Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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/add_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func addValidator(cmd *cobra.Command, args []string) error {
}

if len(args) == 0 {
sc, err = importL1(blockchainIDStr, addValidatorFlags.RPC, network)
sc, _, err = importBlockchain(network, addValidatorFlags.RPC, ids.Empty, ux.Logger.PrintToUser)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/blockchaincmd/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ func TestExportImportSubnet(t *testing.T) {
err = os.Remove(sidecarFile)
require.NoError(err)

err = importBlockchain(nil, []string{"this-does-also-not-exist-import-should-fail"})
err = importFile(nil, []string{"this-does-also-not-exist-import-should-fail"})
require.ErrorIs(err, os.ErrNotExist)
err = importBlockchain(nil, []string{exportOutput})
err = importFile(nil, []string{exportOutput})
require.ErrorContains(err, "blockchain already exists")
genFile := filepath.Join(app.GetBaseDir(), constants.SubnetDir, testSubnet, constants.GenesisFileName)
err = os.Remove(genFile)
require.NoError(err)
err = importBlockchain(nil, []string{exportOutput})
err = importFile(nil, []string{exportOutput})
require.NoError(err)
}
4 changes: 2 additions & 2 deletions cmd/blockchaincmd/import_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func newImportFileCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "file [blockchainPath]",
Short: "Import an existing blockchain config",
RunE: importBlockchain,
RunE: importFile,
Args: cobrautils.MaximumNArgs(1),
Long: `The blockchain import command will import a blockchain configuration from a file or a git repository.

Expand Down Expand Up @@ -70,7 +70,7 @@ flag.`,
return cmd
}

func importBlockchain(_ *cobra.Command, args []string) error {
func importFile(_ *cobra.Command, args []string) error {
if len(args) == 1 {
importPath := args[0]
return importFromFile(importPath)
Expand Down
240 changes: 99 additions & 141 deletions cmd/blockchaincmd/import_public.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ import (
"github.com/ava-labs/avalanche-cli/pkg/models"
"github.com/ava-labs/avalanche-cli/pkg/networkoptions"
"github.com/ava-labs/avalanche-cli/pkg/precompiles"
"github.com/ava-labs/avalanche-cli/pkg/prompts"
"github.com/ava-labs/avalanche-cli/pkg/utils"
"github.com/ava-labs/avalanche-cli/pkg/ux"
"github.com/ava-labs/avalanche-cli/pkg/vm"
validatorManagerSDK "github.com/ava-labs/avalanche-cli/sdk/validatormanager"
"github.com/ava-labs/avalanche-cli/sdk/validatormanager/validatormanagertypes"
"github.com/ava-labs/avalanchego/api/info"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/rpc"
"github.com/ava-labs/coreth/core"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -32,9 +31,9 @@ import (
var (
blockchainIDStr string
subnetIDstr string
nodeURL string
useSubnetEvm bool
useCustomVM bool
rpcURL string
)

// avalanche blockchain import public
Expand All @@ -53,8 +52,6 @@ flag.`,

networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, false, networkoptions.DefaultSupportedNetworkOptions)

cmd.Flags().StringVar(&nodeURL, "node-url", "", "[optional] URL of an already running validator")

cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "import a subnet-evm")
cmd.Flags().BoolVar(&useCustomVM, "custom", false, "use a custom VM template")
cmd.Flags().BoolVar(
Expand All @@ -69,6 +66,7 @@ flag.`,
"",
"the blockchain ID",
)
cmd.Flags().StringVar(&rpcURL, "rpc", "", "rpc endpoint for the blockchain")
return cmd
}

Expand All @@ -86,133 +84,52 @@ func importPublic(*cobra.Command, []string) error {
return err
}

var reply *info.GetNodeVersionReply

if nodeURL == "" {
yes, err := app.Prompt.CaptureNoYes("Have validator nodes already been deployed to this blockchain?")
if err != nil {
return err
}
if yes {
nodeURL, err = app.Prompt.CaptureString(
"Please provide an API URL of such a node so we can query its VM version (e.g. http://111.22.33.44:5555)")
if err != nil {
return err
}
ctx, cancel := utils.GetAPIContext()
defer cancel()
infoAPI := info.NewClient(nodeURL)
options := []rpc.Option{}
reply, err = infoAPI.GetNodeVersion(ctx, options...)
if err != nil {
return fmt.Errorf("failed to query node - is it running and reachable? %w", err)
}
}
}

var blockchainID ids.ID
if blockchainIDStr == "" {
blockchainID, err = app.Prompt.CaptureID("What is the ID of the blockchain?")
if err != nil {
return err
}
} else {
if blockchainIDStr != "" {
blockchainID, err = ids.FromString(blockchainIDStr)
if err != nil {
return err
}
}

ux.Logger.PrintToUser("Getting information from the %s network...", network.Name())

createChainTx, err := utils.GetBlockchainTx(network.Endpoint, blockchainID)
sc, genBytes, err := importBlockchain(network, rpcURL, blockchainID, ux.Logger.PrintToUser)
if err != nil {
return err
}

vmID := createChainTx.VMID
subnetID := createChainTx.SubnetID
blockchainName := createChainTx.ChainName
genBytes := createChainTx.GenesisData

ux.Logger.PrintToUser("Retrieved information. BlockchainID: %s, SubnetID: %s, Name: %s, VMID: %s",
blockchainID.String(),
subnetID.String(),
blockchainName,
vmID.String(),
)
// TODO: it's probably possible to deploy VMs with the same name on a public network
// In this case, an import could clash because the tool supports unique names only
sc.TokenName = constants.DefaultTokenName
sc.TokenSymbol = constants.DefaultTokenSymbol

vmType, err := vm.PromptVMType(app, useSubnetEvm, useCustomVM)
sc.VM, err = vm.PromptVMType(app, useSubnetEvm, useCustomVM)
if err != nil {
return err
}

vmIDstr := vmID.String()

sc := &models.Sidecar{
Name: blockchainName,
VM: vmType,
Networks: map[string]models.NetworkData{
network.Name(): {
SubnetID: subnetID,
BlockchainID: blockchainID,
},
},
Subnet: blockchainName,
Version: constants.SidecarVersion,
TokenName: constants.DefaultTokenName,
TokenSymbol: constants.DefaultTokenSymbol,
ImportedVMID: vmIDstr,
}

var versions []string

if reply != nil {
// a node was queried
for _, v := range reply.VMVersions {
if v == vmIDstr {
sc.VMVersion = v
break
}
}
sc.RPCVersion = int(reply.RPCProtocolVersion)
} else {
// no node was queried, ask the user
switch vmType {
case models.SubnetEvm:
versions, err = app.Downloader.GetAllReleasesForRepo(constants.AvaLabsOrg, constants.SubnetEVMRepoName, "", application.All)
if err != nil {
return err
}
sc.VMVersion, err = app.Prompt.CaptureList("Pick the version for this VM", versions)
case models.CustomVM:
return fmt.Errorf("importing custom VMs is not yet implemented, but will be available soon")
default:
return fmt.Errorf("unexpected VM type: %v", vmType)
if sc.VM == models.SubnetEvm {
versions, err := app.Downloader.GetAllReleasesForRepo(constants.AvaLabsOrg, constants.SubnetEVMRepoName, "", application.All)
if err != nil {
return err
}
sc.VMVersion, err = app.Prompt.CaptureList("Pick the version for this VM", versions)
if err != nil {
return err
}
sc.RPCVersion, err = vm.GetRPCProtocolVersion(app, vmType, sc.VMVersion)
sc.RPCVersion, err = vm.GetRPCProtocolVersion(app, sc.VM, sc.VMVersion)
if err != nil {
return fmt.Errorf("failed getting RPCVersion for VM type %s with version %s", vmType, sc.VMVersion)
return fmt.Errorf("failed getting RPCVersion for VM type %s with version %s", sc.VM, sc.VMVersion)
}
}
if vmType == models.SubnetEvm {
var genesis core.Genesis
if err := json.Unmarshal(genBytes, &genesis); err != nil {
return err
}
sc.ChainID = genesis.Config.ChainID.String()
}

if err := app.CreateSidecar(sc); err != nil {
if err := app.CreateSidecar(&sc); err != nil {
return fmt.Errorf("failed creating the sidecar for import: %w", err)
}

if err = app.WriteGenesisFile(blockchainName, genBytes); err != nil {
if err = app.WriteGenesisFile(sc.Name, genBytes); err != nil {
return err
}

Expand All @@ -221,62 +138,103 @@ func importPublic(*cobra.Command, []string) error {
return nil
}

func importL1(blockchainIDStr string, rpcURL string, network models.Network) (models.Sidecar, error) {
var sc models.Sidecar
func importBlockchain(
network models.Network,
rpcURL string,
blockchainID ids.ID,
printFunc func(msg string, args ...interface{}),
) (models.Sidecar, []byte, error) {
var err error

blockchainID, err := precompiles.WarpPrecompileGetBlockchainID(rpcURL)
if err != nil {
if blockchainIDStr == "" {
blockchainID, err = app.Prompt.CaptureID("What is the Blockchain ID?")
if err != nil {
return models.Sidecar{}, err
if rpcURL == "" {
rpcURL, err = app.Prompt.CaptureStringAllowEmpty("What is the RPC endpoint?")
if err != nil {
return models.Sidecar{}, nil, err
}
if rpcURL != "" {
if err := prompts.ValidateURLFormat(rpcURL); err != nil {
return models.Sidecar{}, nil, fmt.Errorf("invalid url format: %w", err)
}
} else {
blockchainID, err = ids.FromString(blockchainIDStr)
}
}

if blockchainID == ids.Empty {
var err error
if rpcURL != "" {
blockchainID, _ = precompiles.WarpPrecompileGetBlockchainID(rpcURL)
}
if blockchainID == ids.Empty {
blockchainID, err = app.Prompt.CaptureID("What is the Blockchain ID?")
if err != nil {
return models.Sidecar{}, err
return models.Sidecar{}, nil, err
}
}
}
subnetID, err := blockchain.GetSubnetIDFromBlockchainID(blockchainID, network)
if err != nil {
return models.Sidecar{}, err
}

subnetInfo, err := blockchain.GetSubnet(subnetID, network)
createChainTx, err := utils.GetBlockchainTx(network.Endpoint, blockchainID)
if err != nil {
return models.Sidecar{}, err
return models.Sidecar{}, nil, err
}
if subnetInfo.IsPermissioned {
return models.Sidecar{}, fmt.Errorf("unable to import non sovereign Subnets")
}
validatorManagerAddress = "0x" + hex.EncodeToString(subnetInfo.ManagerAddress)

// add validator without blockchain arg is only for l1s
sc = models.Sidecar{
Sovereign: true,
}
subnetID := createChainTx.SubnetID
vmID := createChainTx.VMID
blockchainName := createChainTx.ChainName
genBytes := createChainTx.GenesisData

printFunc("Retrieved information:")
printFunc(" Name: %s", blockchainName)
printFunc(" BlockchainID: %s", blockchainID.String())
printFunc(" SubnetID: %s", subnetID.String())
printFunc(" VMID: %s", vmID.String())

sc.ValidatorManagement, err = validatorManagerSDK.GetValidatorManagerType(rpcURL, common.HexToAddress(validatorManagerAddress))
subnetInfo, err := blockchain.GetSubnet(subnetID, network)
if err != nil {
return models.Sidecar{}, fmt.Errorf("could not obtain validator manager type: %w", err)
return models.Sidecar{}, nil, err
}

if sc.ValidatorManagement == validatormanagertypes.ProofOfAuthority {
owner, err := contract.GetContractOwner(rpcURL, common.HexToAddress(validatorManagerAddress))
if err != nil {
return models.Sidecar{}, err
sc := models.Sidecar{
Name: blockchainName,
Networks: map[string]models.NetworkData{
network.Name(): {
SubnetID: subnetID,
BlockchainID: blockchainID,
},
},
Subnet: blockchainName,
Version: constants.SidecarVersion,
ImportedVMID: vmID.String(),
ImportedFromAPM: true,
}

if rpcURL != "" {
e := sc.Networks[network.Name()]
e.RPCEndpoints = []string{rpcURL}
sc.Networks[network.Name()] = e
}

if !subnetInfo.IsPermissioned {
sc.Sovereign = true
validatorManagerAddress = "0x" + hex.EncodeToString(subnetInfo.ManagerAddress)
e := sc.Networks[network.Name()]
e.ValidatorManagerAddress = validatorManagerAddress
sc.Networks[network.Name()] = e
printFunc(" Validator Manager Address: %s", validatorManagerAddress)
if rpcURL != "" {
sc.ValidatorManagement, err = validatorManagerSDK.GetValidatorManagerType(rpcURL, common.HexToAddress(validatorManagerAddress))
if err != nil {
return models.Sidecar{}, nil, fmt.Errorf("could not obtain validator manager type: %w", err)
}
printFunc(" Validation Kind: %s", sc.ValidatorManagement)
if sc.ValidatorManagement == validatormanagertypes.ProofOfAuthority {
owner, err := contract.GetContractOwner(rpcURL, common.HexToAddress(validatorManagerAddress))
if err != nil {
return models.Sidecar{}, nil, err
}
sc.ValidatorManagerOwner = owner.String()
printFunc(" Validator Manager Owner: %s", sc.ValidatorManagerOwner)
}
}
sc.ValidatorManagerOwner = owner.String()
}

sc.Networks = make(map[string]models.NetworkData)

sc.Networks[network.Name()] = models.NetworkData{
SubnetID: subnetID,
BlockchainID: blockchainID,
ValidatorManagerAddress: validatorManagerAddress,
RPCEndpoints: []string{rpcURL},
}
return sc, err
return sc, genBytes, err
}
2 changes: 1 addition & 1 deletion cmd/blockchaincmd/remove_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func removeValidator(_ *cobra.Command, args []string) error {
}
}

validatorKind, err := validatorsdk.IsSovereignValidator(network.SDKNetwork(), subnetID, nodeID)
validatorKind, err := validatorsdk.GetValidatorKind(network.SDKNetwork(), subnetID, nodeID)
if err != nil {
return err
}
Expand Down
Loading
Loading