Files

222 lines
7.8 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package dependencies
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/luxfi/cli/v2/pkg/ux"
"golang.org/x/mod/semver"
"github.com/luxfi/cli/v2/pkg/models"
"github.com/luxfi/cli/v2/pkg/application"
"github.com/luxfi/cli/v2/pkg/constants"
)
var ErrNoLuxdVersion = errors.New("unable to find a compatible luxd version")
func GetLatestLuxdByProtocolVersion(app *application.Lux, rpcVersion int) (string, error) {
useVersion, err := GetAvailableLuxdVersions(app, rpcVersion, constants.LuxdCompatibilityURL)
if err != nil {
return "", err
}
return useVersion[0], nil
}
func GetLatestCLISupportedDependencyVersion(app *application.Lux, dependencyName string, network models.Network, rpcVersion *int) (string, error) {
dependencyBytes, err := app.Downloader.Download(constants.CLILatestDependencyURL)
if err != nil {
return "", err
}
var parsedDependency models.CLIDependencyMap
if err = json.Unmarshal(dependencyBytes, &parsedDependency); err != nil {
return "", err
}
networkDependencies, ok := parsedDependency[network.String()]
if !ok {
return "", fmt.Errorf("no dependencies found for network: %s", network.String())
}
switch dependencyName {
case constants.LuxdRepoName:
// if the user is using RPC that is lower than the latest RPC supported by CLI, user will get latest Luxd version for that RPC
// based on "https://raw.githubusercontent.com/luxfi/luxd/master/version/compatibility.json"
if rpcVersion != nil && networkDependencies.LuxdRPC > *rpcVersion {
return GetLatestLuxdByProtocolVersion(
app,
*rpcVersion,
)
}
return networkDependencies.Luxd, nil
default:
return "", fmt.Errorf("unsupported dependency: %s", dependencyName)
}
}
// GetLuxdVersionsForRPC returns list of compatible lux go versions for a specified rpcVersion
func GetLuxdVersionsForRPC(app *application.Lux, rpcVersion int, url string) ([]string, error) {
compatibilityBytes, err := app.Downloader.Download(url)
if err != nil {
return nil, err
}
var parsedCompat models.LuxdCompatiblity
if err = json.Unmarshal(compatibilityBytes, &parsedCompat); err != nil {
return nil, err
}
eligibleVersions, ok := parsedCompat.RPCChainVMProtocolVersion[strconv.Itoa(rpcVersion)]
if !ok {
return nil, ErrNoLuxdVersion
}
// versions are not necessarily sorted, so we need to sort them, tho this puts them in ascending order
semver.Sort(eligibleVersions)
return eligibleVersions, nil
}
// GetAvailableLuxdVersions returns list of only available for download lux go versions,
// with latest version in first index
func GetAvailableLuxdVersions(app *application.Lux, rpcVersion int, url string) ([]string, error) {
eligibleVersions, err := GetLuxdVersionsForRPC(app, rpcVersion, url)
if err != nil {
return nil, ErrNoLuxdVersion
}
// get latest luxd release to make sure we're not picking a release currently in progress but not available for download
releaseURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest",
constants.LuxOrg,
constants.LuxdRepoName,
)
latestLuxdVersion, err := app.Downloader.GetLatestReleaseVersion(releaseURL)
if err != nil {
return nil, err
}
var availableVersions []string
for i := len(eligibleVersions) - 1; i >= 0; i-- {
versionComparison := semver.Compare(eligibleVersions[i], latestLuxdVersion)
if versionComparison != 1 {
availableVersions = append(availableVersions, eligibleVersions[i])
}
}
if len(availableVersions) == 0 {
return nil, ErrNoLuxdVersion
}
return availableVersions, nil
}
type LuxdVersionSettings struct {
UseCustomLuxgoVersion string
UseLatestLuxgoReleaseVersion bool
UseLatestLuxgoPreReleaseVersion bool
UseLuxgoVersionFromSubnet string
}
// GetLuxdVersion asks users whether they want to install the newest Lux Go version
// or if they want to use the newest Lux Go Version that is still compatible with Subnet EVM
// version of their choice
func GetLuxdVersion(app *application.Lux, luxdVersion LuxdVersionSettings, network models.Network) (string, error) {
// skip this logic if custom-luxd-version flag is set
if luxdVersion.UseCustomLuxgoVersion != "" {
return luxdVersion.UseCustomLuxgoVersion, nil
}
latestReleaseVersion, err := GetLatestCLISupportedDependencyVersion(app, constants.LuxdRepoName, network, nil)
if err != nil {
return "", err
}
// Get all releases and find the latest prerelease
allReleases, err := app.Downloader.GetAllReleasesForRepo(
constants.LuxOrg,
constants.LuxdRepoName,
)
if err != nil {
return "", err
}
if len(allReleases) == 0 {
return "", fmt.Errorf("no releases found")
}
latestPreReleaseVersion := allReleases[0]
if !luxdVersion.UseLatestLuxgoReleaseVersion && !luxdVersion.UseLatestLuxgoPreReleaseVersion && luxdVersion.UseCustomLuxgoVersion == "" && luxdVersion.UseLuxgoVersionFromSubnet == "" {
luxdVersion, err = promptLuxdVersionChoice(app, latestReleaseVersion, latestPreReleaseVersion)
if err != nil {
return "", err
}
}
var version string
switch {
case luxdVersion.UseLatestLuxgoReleaseVersion:
version = latestReleaseVersion
case luxdVersion.UseLatestLuxgoPreReleaseVersion:
version = latestPreReleaseVersion
case luxdVersion.UseCustomLuxgoVersion != "":
version = luxdVersion.UseCustomLuxgoVersion
case luxdVersion.UseLuxgoVersionFromSubnet != "":
sc, err := app.LoadSidecar(luxdVersion.UseLuxgoVersionFromSubnet)
if err != nil {
return "", err
}
version, err = GetLatestCLISupportedDependencyVersion(app, constants.LuxdRepoName, network, &sc.RPCVersion)
if err != nil {
return "", err
}
}
return version, nil
}
// promptLuxdVersionChoice sets flags for either using the latest Lux Go
// version or using the latest Lux Go version that is still compatible with the subnet that user
// wants the cloud server to track
func promptLuxdVersionChoice(app *application.Lux, latestReleaseVersion string, latestPreReleaseVersion string) (LuxdVersionSettings, error) {
versionComments := map[string]string{
"v1.11.0-testnet": " (recommended for testnet durango)",
}
latestReleaseVersionOption := "Use latest Lux Go Release Version" + versionComments[latestReleaseVersion]
latestPreReleaseVersionOption := "Use latest Lux Go Pre-release Version" + versionComments[latestPreReleaseVersion]
subnetBasedVersionOption := "Use the deployed Subnet's VM version that the node will be validating"
customOption := "Custom"
txt := "What version of Lux Go would you like to install in the node?"
versionOptions := []string{latestReleaseVersionOption, subnetBasedVersionOption, customOption}
if latestPreReleaseVersion != latestReleaseVersion {
versionOptions = []string{latestPreReleaseVersionOption, latestReleaseVersionOption, subnetBasedVersionOption, customOption}
}
versionOption, err := app.Prompt.CaptureList(txt, versionOptions)
if err != nil {
return LuxdVersionSettings{}, err
}
switch versionOption {
case latestReleaseVersionOption:
return LuxdVersionSettings{UseLatestLuxgoReleaseVersion: true}, nil
case latestPreReleaseVersionOption:
return LuxdVersionSettings{UseLatestLuxgoPreReleaseVersion: true}, nil
case customOption:
useCustomLuxgoVersion, err := app.Prompt.CaptureVersion("Which version of Luxd would you like to install? (Use format v1.10.13)")
if err != nil {
return LuxdVersionSettings{}, err
}
return LuxdVersionSettings{UseCustomLuxgoVersion: useCustomLuxgoVersion}, nil
default:
useLuxgoVersionFromSubnet := ""
for {
useLuxgoVersionFromSubnet, err = app.Prompt.CaptureString("Which Subnet would you like to use to choose the lux go version?")
if err != nil {
return LuxdVersionSettings{}, err
}
// TODO: Add subnet validation when available
break
ux.Logger.PrintToUser(fmt.Sprintf("no blockchain named as %s found", useLuxgoVersionFromSubnet))
}
return LuxdVersionSettings{UseLuxgoVersionFromSubnet: useLuxgoVersionFromSubnet}, nil
}
}