Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/blockchaincmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error {
if vmVersion != latest && vmVersion != preRelease && vmVersion != "" && !semver.IsValid(vmVersion) {
return fmt.Errorf("invalid version string, should be semantic version (ex: v1.1.1): %s", vmVersion)
}
vmVersion, err = vm.PromptVMVersion(app, constants.SubnetEVMRepoName, vmVersion)
vmVersion, err = vm.PromptSubnetEVMVersion(app, vmVersion)
if err != nil {
return err
}
Expand Down
7 changes: 1 addition & 6 deletions cmd/blockchaincmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,12 +479,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error {
avagoVersion := userProvidedAvagoVersion

if avagoVersion == constants.DefaultAvalancheGoVersion && avagoBinaryPath == "" {
// nothing given: get avago version from RPC compat
avagoVersion, err = vm.GetLatestAvalancheGoByProtocolVersion(
app,
sidecar.RPCVersion,
constants.AvalancheGoCompatibilityURL,
)
avagoVersion, err = vm.GetLatestCLISupportedDependencyVersion(app, constants.AvalancheGoRepoName, network, &sidecar.RPCVersion)
if err != nil {
if err != vm.ErrNoAvagoVersion {
return err
Expand Down
4 changes: 0 additions & 4 deletions pkg/application/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,6 @@ func (app *Avalanche) GetDownloader() Downloader {
return app.Downloader
}

func (*Avalanche) GetAvalanchegoCompatibilityURL() string {
return constants.AvalancheGoCompatibilityURL
}

func (app *Avalanche) ReadUpgradeFile(blockchainName string) ([]byte, error) {
upgradeBytesFilePath := app.GetUpgradeBytesFilePath(blockchainName)

Expand Down
2 changes: 2 additions & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
AvalancheGoRepoName = "avalanchego"
SubnetEVMRepoName = "subnet-evm"
CliRepoName = "avalanche-cli"
RpcKey = "rpc"

Check failure on line 192 in pkg/constants/constants.go

View workflow job for this annotation

GitHub Actions / Lint

ST1003: const RpcKey should be RPCKey (stylecheck)
ICMContractsRepoName = "icm-contracts"
ICMServicesRepoName = "icm-services"
ICMRelayerKind = "icm-relayer"
Expand Down Expand Up @@ -295,6 +296,7 @@
FujiAvalancheGoV113 = "v1.13.0-fuji"
AvalancheGoCompatibilityURL = "https://raw.githubusercontent.com/ava-labs/avalanchego/master/version/compatibility.json"
SubnetEVMRPCCompatibilityURL = "https://raw.githubusercontent.com/ava-labs/subnet-evm/master/compatibility.json"
CLILatestDependencyURL = "https://raw.githubusercontent.com/ava-labs/avalanche-cli/control-default-version/versions/latest.json"

YesLabel = "Yes"
NoLabel = "No"
Expand Down
12 changes: 12 additions & 0 deletions pkg/models/compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ type VMCompatibility struct {
}

type AvagoCompatiblity map[string][]string

type NetworkVersion struct {
LatestVersion string `json:"latest-version"`
RequirePrerelease bool `json:"require-prerelease"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for case like #2674, where we had to use a prerelease for fuji only

PrereleaseVersion string `json:"prerelease-version"`
}

type CLIDependencyMap struct {
RPC int `json:"rpc"`
SubnetEVM string `json:"subnet-evm"`
AvalancheGo map[string]NetworkVersion `json:"avalanchego"`
}
36 changes: 36 additions & 0 deletions pkg/vm/compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,39 @@ func GetLatestAvalancheGoByProtocolVersion(app *application.Avalanche, rpcVersio
}
return useVersion[0], nil
}

func GetLatestCLISupportedDependencyVersion(app *application.Avalanche, 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
}

switch dependencyName {
case constants.AvalancheGoRepoName:
if rpcVersion == nil {
return "", fmt.Errorf("RPC version is required to get latest Avalanche Go version supported by CLI")
}
// if the user is using RPC that is lower than the latest RPC supported by CLI, user will get latest AvalancheGo version for that RPC
// based on "https://raw.githubusercontent.com/ava-labs/avalanchego/master/version/compatibility.json"
if parsedDependency.RPC > *rpcVersion {
return GetLatestAvalancheGoByProtocolVersion(
app,
*rpcVersion,
constants.AvalancheGoCompatibilityURL,
)
}
if parsedDependency.AvalancheGo[network.Name()].RequirePrerelease {
return parsedDependency.AvalancheGo[network.Name()].PrereleaseVersion, nil
}
return parsedDependency.AvalancheGo[network.Name()].LatestVersion, nil
case constants.SubnetEVMRepoName:
return parsedDependency.SubnetEVM, nil
default:
return "", fmt.Errorf("unsupported dependency: %s", dependencyName)
}
}
185 changes: 185 additions & 0 deletions pkg/vm/compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ var (
testAvagoCompat2 = []byte("{\"19\": [\"v1.9.2\", \"v1.9.1\"],\"18\": [\"v1.9.0\"]}")
testAvagoCompat3 = []byte("{\"19\": [\"v1.9.1\", \"v1.9.2\"],\"18\": [\"v1.9.0\"]}")
testAvagoCompat4 = []byte("{\"19\": [\"v1.9.1\", \"v1.9.2\", \"v1.9.11\"],\"18\": [\"v1.9.0\"]}")
testAvagoCompat5 = []byte("{\"39\": [\"v1.12.2\", \"v1.13.0\"],\"38\": [\"v1.11.13\", \"v1.12.0\", \"v1.12.1\"]}")
testAvagoCompat6 = []byte("{\"39\": [\"v1.12.2\", \"v1.13.0\", \"v1.13.1\"],\"38\": [\"v1.11.13\", \"v1.12.0\", \"v1.12.1\"]}")
testAvagoCompat7 = []byte("{\"40\": [\"v1.13.2\"],\"39\": [\"v1.12.2\", \"v1.13.0\", \"v1.13.1\"]}")
testCLICompat = []byte(`{"subnet-evm":"v0.7.3","rpc":39,"avalanchego":{"Local Network":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""},"DevNet":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""},"Fuji":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""},"Mainnet":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""}}}`)
testCLICompat2 = []byte(`{"subnet-evm":"v0.7.3","rpc":39,"avalanchego":{"Local Network":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""},"DevNet":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""},"Fuji":{"latest-version":"v1.13.0","require-prerelease":true,"prerelease-version":"v1.13.0-fuji"},"Mainnet":{"latest-version":"v1.13.0","require-prerelease":false,"prerelease-version":""}}}`)
)

func TestGetRPCProtocolVersionSubnetEVM(t *testing.T) {
Expand Down Expand Up @@ -171,3 +176,183 @@ func TestGetLatestAvalancheGoByProtocolVersion(t *testing.T) {
})
}
}

func TestGetLatestCLISupportedDependencyVersion(t *testing.T) {
tests := []struct {
name string
dependency string
expectedError bool
expectedResult string
cliDependencyData []byte
avalancheGoData []byte
latestVersion string
}{
{
name: "avalanchego dependency with cli supporting latest avalanchego release",
dependency: constants.AvalancheGoRepoName,
cliDependencyData: testCLICompat,
avalancheGoData: testAvagoCompat5,
latestVersion: "v1.13.0",
expectedError: false,
expectedResult: "v1.13.0",
},
{
name: "avalanchego dependency with cli not supporting latest avalanchego release, but same rpc",
dependency: constants.AvalancheGoRepoName,
cliDependencyData: testCLICompat,
avalancheGoData: testAvagoCompat6,
latestVersion: "v1.13.1",
expectedError: false,
expectedResult: "v1.13.0",
},
{
name: "avalanchego dependency with cli supporting lower rpc",
dependency: constants.AvalancheGoRepoName,
cliDependencyData: testCLICompat,
avalancheGoData: testAvagoCompat7,
latestVersion: "v1.13.2",
expectedError: false,
expectedResult: "v1.13.0",
},
{
name: "avalanchego dependency with cli requiring a prerelease",
dependency: constants.AvalancheGoRepoName,
cliDependencyData: testCLICompat2,
avalancheGoData: testAvagoCompat7,
latestVersion: "v1.13.2",
expectedError: false,
expectedResult: "v1.13.0-fuji",
},
{
name: "subnet-evm dependency, where cli latest.json doesn't support newest subnet evm version yet",
dependency: constants.SubnetEVMRepoName,
cliDependencyData: testCLICompat,
expectedError: false,
expectedResult: "v0.7.3",
latestVersion: "v0.7.4",
},
{
name: "subnet-evm dependency, where cli supports newest subnet evm version",
dependency: constants.SubnetEVMRepoName,
cliDependencyData: testCLICompat,
expectedError: false,
expectedResult: "v0.7.3",
latestVersion: "v0.7.3",
},
{
name: "empty dependency",
dependency: "",
expectedError: true,
expectedResult: "",
},
{
name: "invalid dependency",
dependency: "invalid",
expectedError: true,
expectedResult: "",
},
}

for _, tt := range tests {
mockDownloader := &mocks.Downloader{}
mockDownloader.On("Download", mock.MatchedBy(func(url string) bool {
return url == constants.CLILatestDependencyURL
})).Return(tt.cliDependencyData, nil)

mockDownloader.On("Download", mock.MatchedBy(func(url string) bool {
return url == constants.AvalancheGoCompatibilityURL
})).Return(tt.avalancheGoData, nil)
mockDownloader.On("GetLatestReleaseVersion", mock.Anything, mock.Anything, mock.Anything).Return(tt.latestVersion, nil)

app := application.New()
app.Downloader = mockDownloader

t.Run(tt.name, func(t *testing.T) {
rpcVersion := 39
result, err := GetLatestCLISupportedDependencyVersion(app, tt.dependency, models.NewFujiNetwork(), &rpcVersion)
if tt.expectedError {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.expectedResult, result)
}
})
}
}

func TestGetLatestCLISupportedDependencyVersionWithLowerRPC(t *testing.T) {
tests := []struct {
name string
dependency string
expectedError bool
expectedResult string
cliDependencyData []byte
avalancheGoData []byte
latestVersion string
}{
{
name: "avalanchego dependency with cli supporting latest avalanchego release, user using lower rpc",
dependency: constants.AvalancheGoRepoName,
cliDependencyData: testCLICompat,
avalancheGoData: testAvagoCompat5,
expectedError: false,
expectedResult: "v1.12.1",
latestVersion: "v1.13.0",
},
{
name: "avalanchego dependency with cli supporting latest avalanchego release, user using lower rpc, prerelease required",
dependency: constants.AvalancheGoRepoName,
cliDependencyData: testCLICompat2,
avalancheGoData: testAvagoCompat6,
expectedError: false,
expectedResult: "v1.12.1",
latestVersion: "v1.13.2",
},
{
name: "subnet-evm dependency, where cli supports newest subnet evm version",
dependency: constants.SubnetEVMRepoName,
cliDependencyData: testCLICompat,
expectedError: false,
expectedResult: "v0.7.3",
latestVersion: "v0.7.3",
},
{
name: "empty dependency",
dependency: "",
expectedError: true,
expectedResult: "",
},
{
name: "invalid dependency",
dependency: "invalid",
expectedError: true,
expectedResult: "",
},
}

for _, tt := range tests {
mockDownloader := &mocks.Downloader{}
mockDownloader.On("Download", mock.MatchedBy(func(url string) bool {
return url == constants.CLILatestDependencyURL
})).Return(tt.cliDependencyData, nil)

mockDownloader.On("Download", mock.MatchedBy(func(url string) bool {
return url == constants.AvalancheGoCompatibilityURL
})).Return(tt.avalancheGoData, nil)
mockDownloader.On("GetLatestReleaseVersion", mock.Anything, mock.Anything, mock.Anything).Return(tt.latestVersion, nil)

app := application.New()
app.Downloader = mockDownloader

t.Run(tt.name, func(t *testing.T) {
rpcVersion := 38
result, err := GetLatestCLISupportedDependencyVersion(app, tt.dependency, models.NewFujiNetwork(), &rpcVersion)
if tt.expectedError {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.expectedResult, result)
}
})
}
}
30 changes: 10 additions & 20 deletions pkg/vm/evm_prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,51 +877,41 @@ func promptPermissioning(
return params, nil
}

func PromptVMVersion(
func PromptSubnetEVMVersion(
app *application.Avalanche,
repoName string,
vmVersion string,
subnetEVMVersion string,
) (string, error) {
switch vmVersion {
switch subnetEVMVersion {
case latest:
return app.Downloader.GetLatestReleaseVersion(
constants.AvaLabsOrg,
repoName,
"",
)
return GetLatestCLISupportedDependencyVersion(app, constants.SubnetEVMRepoName, models.UndefinedNetwork, nil)
case preRelease:
return app.Downloader.GetLatestPreReleaseVersion(
constants.AvaLabsOrg,
repoName,
constants.SubnetEVMRepoName,
"",
)
case "":
return promptUserForVMVersion(app, repoName)
return promptUserForSubnetEVMVersion(app)
}
return vmVersion, nil
return subnetEVMVersion, nil
}

func promptUserForVMVersion(
func promptUserForSubnetEVMVersion(
app *application.Avalanche,
repoName string,
) (string, error) {
var (
latestReleaseVersion string
latestPreReleaseVersion string
err error
)
if os.Getenv(constants.OperateOfflineEnvVarName) == "" {
latestReleaseVersion, err = app.Downloader.GetLatestReleaseVersion(
constants.AvaLabsOrg,
repoName,
"",
)
latestReleaseVersion, err = GetLatestCLISupportedDependencyVersion(app, constants.SubnetEVMRepoName, models.UndefinedNetwork, nil)
if err != nil {
return "", err
}
latestPreReleaseVersion, err = app.Downloader.GetLatestPreReleaseVersion(
constants.AvaLabsOrg,
repoName,
constants.SubnetEVMRepoName,
"",
)
if err != nil {
Expand Down
26 changes: 26 additions & 0 deletions versions/latest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"subnet-evm": "v0.7.3",
"rpc": 39,
"avalanchego": {
"Local Network": {
"latest-version": "v1.13.0",
"require-prerelease": false,
"prerelease-version": ""
},
"DevNet": {
"latest-version": "v1.13.0",
"require-prerelease": false,
"prerelease-version": ""
},
"Fuji": {
"latest-version": "v1.13.0",
"require-prerelease": false,
"prerelease-version": ""
},
"Mainnet": {
"latest-version": "v1.13.0",
"require-prerelease": false,
"prerelease-version": ""
}
}
}
Loading