Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Validator for remote L1 without importing blockchain #2607

Merged
merged 29 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fd813de
add validator on remote l1
sukantoraymond Feb 14, 2025
84f8196
fix error
sukantoraymond Feb 18, 2025
62ca8d1
remove print
sukantoraymond Feb 18, 2025
90f1ae1
Merge branch 'main' into add-validator-remote-l1
sukantoraymond Feb 18, 2025
a57ad9e
fix error
sukantoraymond Feb 18, 2025
929f620
get validator manager owner
sukantoraymond Feb 19, 2025
7de4e3f
check if poa
sukantoraymond Feb 20, 2025
afed4c0
fix test
sukantoraymond Feb 20, 2025
044b877
remove print
sukantoraymond Feb 20, 2025
2f511be
fmt
sukantoraymond Feb 20, 2025
4639e58
lint
sukantoraymond Feb 20, 2025
ee7df72
Merge branch 'main' into add-validator-remote-l1
sukantoraymond Feb 20, 2025
e502b7d
revert change
sukantoraymond Feb 20, 2025
6770c15
fix lint
sukantoraymond Feb 20, 2025
b737623
fixes
sukantoraymond Feb 20, 2025
1c86b21
update wait time
sukantoraymond Feb 20, 2025
18af22b
lint
sukantoraymond Feb 21, 2025
670a51f
Merge branch 'main' into add-validator-remote-l1
sukantoraymond Feb 24, 2025
e95c6a7
fix merge
sukantoraymond Feb 24, 2025
66263c1
Merge branch 'main' into add-validator-remote-l1
sukantoraymond Feb 24, 2025
5c7cf59
Merge branch 'main' into add-validator-remote-l1
sukantoraymond Feb 24, 2025
ee40c92
address comments
sukantoraymond Feb 25, 2025
d6a2674
lint
sukantoraymond Feb 25, 2025
6ecef06
address comments
sukantoraymond Feb 26, 2025
86093cb
address comments
sukantoraymond Feb 26, 2025
52b3e02
lint
sukantoraymond Feb 26, 2025
97c4b96
Merge branch 'main' into add-validator-remote-l1
sukantoraymond Feb 26, 2025
e6dfab7
use warp to get blockchain
sukantoraymond Feb 26, 2025
149d8f4
lint
sukantoraymond Feb 26, 2025
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
109 changes: 74 additions & 35 deletions cmd/blockchaincmd/add_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ var (
aggregatorExtraEndpoints []string
aggregatorAllowPrivatePeers bool
clusterNameFlagValue string
createLocalValidator bool
)

createLocalValidator bool
const (
validatorWeightFlag = "weight"
)

// avalanche blockchain addValidator
Expand All @@ -82,12 +85,11 @@ staking token. Both processes will issue a RegisterL1ValidatorTx on the P-Chain.
This command currently only works on Blockchains deployed to either the Fuji
Testnet or Mainnet.`,
RunE: addValidator,
Args: cobrautils.ExactArgs(1),
Args: cobrautils.MaximumNArgs(1),
}
networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, networkoptions.DefaultSupportedNetworkOptions)

cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]")
cmd.Flags().Uint64Var(&weight, "weight", constants.NonBootstrapValidatorWeight, "set the staking weight of the validator to add")
cmd.Flags().Float64Var(
&balanceAVAX,
"balance",
Expand Down Expand Up @@ -119,31 +121,42 @@ Testnet or Mainnet.`,
cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "(for Subnets, not L1s) file path of the add validator tx")
cmd.Flags().BoolVar(&waitForTxAcceptance, "wait-for-tx-acceptance", true, "(for Subnets, not L1s) just issue the add validator tx, without waiting for its acceptance")
cmd.Flags().Uint16Var(&delegationFee, "delegation-fee", 100, "(PoS only) delegation fee (in bips)")
cmd.Flags().StringVar(&subnetIDstr, "subnet-id", "", "subnet ID (only if blockchain name is not provided)")
Copy link
Collaborator

Choose a reason for hiding this comment

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

(Not about this PR) But the strings here, was it a deliberate choice not to move the strings here into constants or more so something that just grew this way?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we can technically move all the flags that we have into a separate file like flags.go and have var for all the flags we use. This is a tech debt

Copy link
Collaborator

@felipemadero felipemadero Feb 26, 2025

Choose a reason for hiding this comment

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

There are some parts of code where the flags names are constants. But overall, it grew this way. We need
to improve (or have) code standards here.

cmd.Flags().StringVar(&validatorManagerOwnerAddress, "validator-manager-owner", "", "validator manager owner address (only if blockchain name is not provided)")
cmd.Flags().Uint64Var(&weight, validatorWeightFlag, uint64(constants.DefaultStakeWeight), "set the weight of the validator")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have we considered refactoring the input validation to be on the flags rather than in the commands?

Similarly, have we considered reusing these flag definitions across all commands rather than defining them in each cmd file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

addressed above, ya this is a tech debt.

Copy link
Collaborator

Choose a reason for hiding this comment

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

For values that have many possible input flags, we have made partial work.
We have for example contract.ChainSpec struct that has methods to validate
itself and to automatically add flags to a command. Same with contract.PrivateKeyFlags.
We also have a function for the network model, on networkoptions.AddNetworkFlagsToCmd.
We may want to unify and expand on this pattern, and also apply to some remaining multiple
flags value, like p-chain input key.

Copy link
Collaborator

@felipemadero felipemadero Feb 26, 2025

Choose a reason for hiding this comment

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

We also have made partial work on avoiding using global variables for command flags.
Best of the worlds IMO is to have each command have its unique flags struct.
Example eg cmd/interchain/messengercmd/deploy.go
Why is that? To avoid interference on default flag value initialization between different
commands (each command make its own default value initialization and we had cases
where one command overwrote the default flag value of another one). And to avoid depending
on global variables among different methods which also introduces secondary effects from time
to time. Finally, this simplifies and cleans calling command from another commands.
We need some code standard here.


return cmd
}

func preAddChecks() error {
func preAddChecks(args []string) error {
if nodeEndpoint != "" && createLocalValidator {
return fmt.Errorf("cannot set both --node-endpoint and --create-local-validator")
}
if createLocalValidator && (nodeIDStr != "" || publicKey != "" || pop != "") {
return fmt.Errorf("cannot set --node-id, --bls-public-key or --bls-proof-of-possession if --create-local-validator used")
}
if len(args) == 0 && createLocalValidator {
return fmt.Errorf("use avalanche addValidator <subnetName> command to use local machine as new validator")
}

return nil
}

func addValidator(_ *cobra.Command, args []string) error {
blockchainName := args[0]
_, err := ValidateSubnetNameAndGetChains([]string{blockchainName})
if err != nil {
return err
}

sc, err := app.LoadSidecar(blockchainName)
if err != nil {
return fmt.Errorf("failed to load sidecar: %w", err)
func addValidator(cmd *cobra.Command, args []string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have any unite tests + e2e tests defined on this?

Copy link
Collaborator Author

@sukantoraymond sukantoraymond Feb 26, 2025

Choose a reason for hiding this comment

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

no unit test as of now for remote validators. To test this, we will have to simulate local network, create an L1 on top of it (using ANR to use local machine for bootstrap validators) and create another validator to add (using local machine to create another instance of validator). This means that we have to create two separate processes of local machine validators, which we can't do currently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's on backlog to do once TMPnet is implement for local machine clusters

var sc models.Sidecar
blockchainName := ""
networkOption := networkoptions.DefaultSupportedNetworkOptions
if len(args) == 1 {
blockchainName = args[0]
_, err := ValidateSubnetNameAndGetChains([]string{blockchainName})
if err != nil {
return err
}
sc, err = app.LoadSidecar(blockchainName)
if err != nil {
return fmt.Errorf("failed to load sidecar: %w", err)
}
networkOption = networkoptions.GetNetworkFromSidecar(sc, networkoptions.DefaultSupportedNetworkOptions)
}

network, err := networkoptions.GetNetworkFromCmdLineFlags(
Expand All @@ -152,7 +165,7 @@ func addValidator(_ *cobra.Command, args []string) error {
globalNetworkFlags,
true,
false,
networkoptions.GetNetworkFromSidecar(sc, networkoptions.DefaultSupportedNetworkOptions),
networkOption,
"",
)
if err != nil {
Expand All @@ -164,7 +177,20 @@ func addValidator(_ *cobra.Command, args []string) error {
network = models.ConvertClusterToNetwork(network)
}

if err := preAddChecks(); err != nil {
if len(args) == 0 {
if rpcURL == "" {
rpcURL, err = app.Prompt.CaptureURL("What is the RPC endpoint?", false)
if err != nil {
return err
}
}
sc, err = importL1(blockchainIDStr, rpcURL, network)
if err != nil {
return err
}
}

if err := preAddChecks(args); err != nil {
return err
}

Expand Down Expand Up @@ -197,19 +223,35 @@ func addValidator(_ *cobra.Command, args []string) error {
}
}

// if we don't have a nodeID or ProofOfPossession by this point, prompt user if we want to add a aditional local node
if (!sovereign && nodeIDStr == "") || (sovereign && !createLocalValidator && nodeIDStr == "" && publicKey == "" && pop == "") {
for {
local := "Use my local machine to spin up an additional validator"
existing := "I have an existing Avalanche node (we will require its NodeID and BLS info)"
if option, err := app.Prompt.CaptureList(
"How would you like to set up the new validator",
[]string{local, existing},
); err != nil {
if sovereign {
if !cmd.Flags().Changed(validatorWeightFlag) {
weight, err = app.Prompt.CaptureWeight(
"What weight would you like to assign to the validator?",
func(uint64) error { return nil },
)
if err != nil {
return err
} else {
createLocalValidator = option == local
break
}
}
}

// if we don't have a nodeID or ProofOfPossession by this point, prompt user if we want to add additional local node
if (!sovereign && nodeIDStr == "") || (sovereign && !createLocalValidator && nodeIDStr == "" && publicKey == "" && pop == "") {
if len(args) == 0 {
createLocalValidator = false
} else {
for {
local := "Use my local machine to spin up an additional validator"
existing := "I have an existing Avalanche node (we will require its NodeID and BLS info)"
if option, err := app.Prompt.CaptureList(
"How would you like to set up the new validator",
[]string{local, existing},
); err != nil {
return err
} else {
createLocalValidator = option == local
break
}
}
}
}
Expand Down Expand Up @@ -312,6 +354,7 @@ func addValidator(_ *cobra.Command, args []string) error {
balanceAVAX,
remainingBalanceOwnerAddr,
disableOwnerAddr,
sc,
)
}

Expand All @@ -335,6 +378,7 @@ func CallAddValidator(
balanceAVAX float64,
remainingBalanceOwnerAddr string,
disableOwnerAddr string,
sc models.Sidecar,
) error {
nodeID, err := ids.NodeIDFromString(nodeIDStr)
if err != nil {
Expand All @@ -350,21 +394,16 @@ func CallAddValidator(
return fmt.Errorf("failed to get blockchain timestamp: %w", err)
}
expiry := uint64(blockchainTimestamp.Add(constants.DefaultValidationIDExpiryDuration).Unix())

chainSpec := contract.ChainSpec{
BlockchainName: blockchainName,
}

sc, err := app.LoadSidecar(chainSpec.BlockchainName)
if err != nil {
return fmt.Errorf("failed to load sidecar: %w", err)
if sc.Networks[network.Name()].BlockchainID.String() != "" {
chainSpec.BlockchainID = sc.Networks[network.Name()].BlockchainID.String()
}

if sc.Networks[network.Name()].ValidatorManagerAddress == "" {
return fmt.Errorf("unable to find Validator Manager address")
}
validatorManagerAddress = sc.Networks[network.Name()].ValidatorManagerAddress

ownerPrivateKeyFound, _, _, ownerPrivateKey, err := contract.SearchForManagedKey(
app,
network,
Expand Down
1 change: 1 addition & 0 deletions cmd/blockchaincmd/change_weight.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,5 +257,6 @@ func setWeight(_ *cobra.Command, args []string) error {
float64(balance)/float64(units.Avax),
remainingBalanceOwnerAddr,
disableOwnerAddr,
sc,
)
}
9 changes: 5 additions & 4 deletions cmd/blockchaincmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ var (
poSWeightToValueFactor uint64
deployBalanceAVAX float64
validatorManagerAddress string
validatorManagerOwnerAddress string
errMutuallyExlusiveControlKeys = errors.New("--control-keys and --same-control-key are mutually exclusive")
ErrMutuallyExlusiveKeyLedger = errors.New("key source flags --key, --ledger/--ledger-addrs are mutually exclusive")
ErrStoredKeyOnMainnet = errors.New("key --key is not available for mainnet operations")
Expand Down Expand Up @@ -574,7 +575,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error {
}

deployBalance := uint64(deployBalanceAVAX * float64(units.Avax))

// whether user has created Avalanche Nodes when blockchain deploy command is called
if sidecar.Sovereign {
if changeOwnerAddress == "" {
// use provided key as change owner unless already set
Expand Down Expand Up @@ -792,13 +793,13 @@ func deployBlockchain(cmd *cobra.Command, args []string) error {
if savePartialTx {
return nil
}

if convertOnly || generateNodeID || (!useLocalMachine && clusterNameFlagValue == "") {
ux.Logger.GreenCheckmarkToUser("Converted blockchain successfully generated")
ux.Logger.PrintToUser("To finish conversion to sovereign L1, create the corresponding Avalanche node(s) with the provided Node ID and BLS Info")
ux.Logger.PrintToUser("and setup them to track subnet ID %s with 'track-subnets' config setting", subnetID)
ux.Logger.PrintToUser(logging.Green.Wrap("Double check the nodes expose the P2P port and have a correct setting for 'public-ip' config value"))
ux.Logger.PrintToUser("Created Node ID and BLS Info can be found at %s", app.GetSidecarPath(blockchainName))
ux.Logger.PrintToUser("==================================================")
ux.Logger.PrintToUser("To enable the nodes to track the L1, set '%s' as the value for 'track-subnets' configuration in ~/.avalanchego/config.json", subnetID)
ux.Logger.PrintToUser("Ensure that the P2P port is exposed and 'public-ip' config value is set")
ux.Logger.PrintToUser("Once the Avalanche Node(s) are created and are tracking the blockchain, call `avalanche contract initValidatorManager %s` to finish conversion to sovereign L1", blockchainName)
return nil
}
Expand Down
82 changes: 78 additions & 4 deletions cmd/blockchaincmd/import_public.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@
package blockchaincmd

import (
"encoding/hex"
"encoding/json"
"fmt"

"github.com/ava-labs/avalanche-cli/pkg/application"
"github.com/ava-labs/avalanche-cli/pkg/blockchain"
"github.com/ava-labs/avalanche-cli/pkg/cobrautils"
"github.com/ava-labs/avalanche-cli/pkg/constants"
"github.com/ava-labs/avalanche-cli/pkg/contract"
"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/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/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"
"github.com/spf13/cobra"
)

var (
blockchainIDstr string
blockchainIDStr string
subnetIDstr string
nodeURL string
useSubnetEvm bool
useCustomVM bool
Expand Down Expand Up @@ -55,7 +62,7 @@ flag.`,
"overwrite the existing configuration if one exists",
)
cmd.Flags().StringVar(
&blockchainIDstr,
&blockchainIDStr,
"blockchain-id",
"",
"the blockchain ID",
Expand Down Expand Up @@ -102,13 +109,13 @@ func importPublic(*cobra.Command, []string) error {
}

var blockchainID ids.ID
if blockchainIDstr == "" {
if blockchainIDStr == "" {
blockchainID, err = app.Prompt.CaptureID("What is the ID of the blockchain?")
if err != nil {
return err
}
} else {
blockchainID, err = ids.FromString(blockchainIDstr)
blockchainID, err = ids.FromString(blockchainIDStr)
if err != nil {
return err
}
Expand Down Expand Up @@ -211,3 +218,70 @@ func importPublic(*cobra.Command, []string) error {

return nil
}

func importL1(blockchainIDStr string, rpcURL string, network models.Network) (models.Sidecar, error) {
var sc models.Sidecar

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
}
} else {
blockchainID, err = ids.FromString(blockchainIDStr)
if err != nil {
return models.Sidecar{}, err
}
}
}
subnetID, err := blockchain.GetSubnetIDFromBlockchainID(blockchainID, network)
if err != nil {
return models.Sidecar{}, err
}

subnetInfo, err := blockchain.GetSubnet(subnetID, network)
if err != nil {
return models.Sidecar{}, err
}
if subnetInfo.IsPermissioned {
return models.Sidecar{}, fmt.Errorf("unable to import non sovereign Subnets")
}
validatorManagerAddress = "0x" + hex.EncodeToString(subnetInfo.ManagerAddress)
fmt.Printf("obtained blockchainid %s \n", blockchainID.String())
fmt.Printf("obtained subnetid %s \n", subnetID.String())

fmt.Printf("obtained validatorManagerAddress %s \n", validatorManagerAddress)

// add validator without blockchain arg is only for l1s
sc = models.Sidecar{
Sovereign: true,
}

isPoA := validatorManagerSDK.ValidatorManagerIsPoA(rpcURL, common.HexToAddress(validatorManagerAddress))
if err != nil {
return models.Sidecar{}, err
}

if isPoA {
sc.ValidatorManagement = models.ProofOfAuthority
owner, err := contract.GetContractOwner(rpcURL, common.HexToAddress(validatorManagerAddress))
if err != nil {
return models.Sidecar{}, err
}
sc.ValidatorManagerOwner = owner.String()
} else {
sc.ValidatorManagement = models.ProofOfStake
}

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
}
2 changes: 1 addition & 1 deletion cmd/keycmd/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func transferF(*cobra.Command, []string) error {
return err
}
if senderChainFlags.BlockchainName != "" || receiverChainFlags.BlockchainName != "" || senderChainFlags.XChain {
return fmt.Errorf("tranfer from %s to %s is not supported", senderDesc, receiverDesc)
return fmt.Errorf("transfer from %s to %s is not supported", senderDesc, receiverDesc)
}

if keyName == "" && ledgerIndex == wrongLedgerIndexVal {
Expand Down
1 change: 0 additions & 1 deletion cmd/validatorcmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ func list(_ *cobra.Command, args []string) error {
if err != nil {
return err
}

managerAddress := common.HexToAddress(validatorManagerAddress)

t := ux.DefaultTable(
Expand Down
Loading
Loading