From c96ce75a7cc51d159d6a8cfe55d2c112f0c9aebb Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 8 Aug 2024 20:26:26 +0700 Subject: [PATCH 01/56] add primary validator example --- node/add_validator_primary_network.go | 388 +++++++++++++++++++++ node/add_validator_primary_network_test.go | 233 +++++++++++++ node/cloud.go | 2 +- node/node.go | 5 + node/ssh.go | 2 + node/staking.go | 5 + 6 files changed, 634 insertions(+), 1 deletion(-) create mode 100644 node/add_validator_primary_network.go create mode 100644 node/add_validator_primary_network_test.go diff --git a/node/add_validator_primary_network.go b/node/add_validator_primary_network.go new file mode 100644 index 0000000..860c444 --- /dev/null +++ b/node/add_validator_primary_network.go @@ -0,0 +1,388 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package node + +import ( + "errors" + "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/utils" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + "os" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "golang.org/x/net/context" +) + +type ValidatorParams struct { + NodeID ids.NodeID + + Duration time.Duration + + Weight uint64 +} + +var ( + ErrEmptyValidatorNodeID = errors.New("validator node id is not provided") + ErrEmptyValidatorDuration = errors.New("validator duration is not provided") + ErrEmptyValidatorWeight = errors.New("validator weight is not provided") + ErrEmptySubnetID = errors.New("subnet ID is not provided") +) + +func GetMinStakingAmount(network avalanche.Network) (uint64, error) { + pClient := platformvm.NewClient(network.Endpoint) + ctx, cancel := utils.GetAPIContext() + defer cancel() + minValStake, _, err := pClient.GetMinStake(ctx, ids.Empty) + if err != nil { + return 0, err + } + return minValStake, nil +} + +func (h *Node) SetNodeBLSKey(signingKeyPath string) error { + blsKeyBytes, err := os.ReadFile(signingKeyPath) + if err != nil { + return err + } + blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) + if err != nil { + return err + } + h.BlsSecretKey = blsSk + return nil +} + +// AddNodeAsPrimaryNetworkValidator returns bool if node is added as primary network validator +// as it impacts the output in adding node as subnet validator in the next steps +func (h *Node) AddNodeAsPrimaryNetworkValidator( + network avalanche.Network, + validatorInput ValidatorParams, + wallet wallet.Wallet, +) (ids.ID, error) { + //if isValidator, err := checkNodeIsPrimaryNetworkValidator(nodeID, network); err != nil { + // return err + //} else if !isValidator { + // signingKeyPath := app.GetNodeBLSSecretKeyPath(instanceID) + // return joinAsPrimaryNetworkValidator(deployer, network, kc, nodeID, nodeIndex, signingKeyPath, true) + //} + minValStake, err := GetMinStakingAmount(network) + if err != nil { + return ids.Empty, err + } + if validatorInput.Weight < minValStake { + return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validatorInput.Weight) + } + + //recipientAddr := kc.Addresses().List()[0] + // we set the starting time for node to be a Primary Network Validator to be in 1 minute + // we use min delegation fee as default + delegationFee := network.GenesisParams().MinDelegationFee + /// + //blsKeyBytes, err := os.ReadFile(signingKeyPath) + //if err != nil { + // return ids.Empty, err + //} + //blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) + //if err != nil { + // return ids.Empty, err + //} + /// + //if _, err := deployer.AddPermissionlessValidator( + // ids.Empty, + // ids.Empty, + // nodeID, + // weight, + // uint64(start.Unix()), + // uint64(start.Add(duration).Unix()), + // recipientAddr, + // delegationFee, + // nil, + // signer.NewProofOfPossession(blsSk), + //); err != nil { + // return err + //} + + // popBytes is a marshalled json object containing publicKey and proofOfPossession of the node's BLS info + //txID, err := d.issueAddPermissionlessValidatorTX(recipientAddr, stakeAmount, subnetID, nodeID, subnetAssetID, startTime, endTime, wallet, delegationFee, popBytes, proofOfPossession) + //if err != nil { + // return ids.Empty, err + //} + + wallet.SetSubnetAuthMultisig([]ids.ShortID{}) + + //owner := &secp256k1fx.OutputOwners{ + // Threshold: 1, + // Addrs: []ids.ShortID{ + // recipientAddr, + // }, + //} + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + wallet.Addresses()[0], + }, + } + proofOfPossession := signer.NewProofOfPossession(h.BlsSecretKey) + nodeID, err := ids.NodeIDFromString(h.NodeID) + if err != nil { + return ids.Empty, err + } + fmt.Printf("building transaction") + unsignedTx, err := wallet.P().Builder().NewAddPermissionlessValidatorTx( + &txs.SubnetValidator{ + Validator: txs.Validator{ + NodeID: nodeID, + End: uint64(time.Now().Add(validatorInput.Duration).Unix()), + Wght: validatorInput.Weight, + }, + Subnet: ids.Empty, + }, + proofOfPossession, + wallet.P().Builder().Context().AVAXAssetID, + owner, + owner, + delegationFee, + ) + if err != nil { + return ids.Empty, fmt.Errorf("error building tx: %w", err) + } + + tx := txs.Tx{Unsigned: unsignedTx} + fmt.Printf("signing with wallet") + if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + return ids.Empty, fmt.Errorf("error signing tx: %w", err) + } + fmt.Printf("successfully signed with wallet") + + ctx, cancel := utils.GetAPIContext() + defer cancel() + err = wallet.P().IssueTx( + &tx, + common.WithContext(ctx), + ) + if err != nil { + if ctx.Err() != nil { + err = fmt.Errorf("timeout issuing/verifying tx with ID %s: %w", tx.ID(), err) + } else { + err = fmt.Errorf("error issuing tx with ID %s: %w", tx.ID(), err) + } + return ids.Empty, err + } + + return tx.ID(), nil +} + +//func joinAsPrimaryNetworkValidator( +// deployer *subnet.PublicDeployer, +// network models.Network, +// kc *keychain.Keychain, +// nodeID ids.NodeID, +// nodeIndex int, +// signingKeyPath string, +// nodeCmd bool, +//) error { +// var ( +// start time.Time +// err error +// ) +// minValStake, err := GetMinStakingAmount(network) +// if err != nil { +// return err +// } +// if weight == 0 { +// weight, err = PromptWeightPrimaryNetwork(network) +// if err != nil { +// return err +// } +// } +// if weight < minValStake { +// return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, weight) +// } +// start, duration, err = GetTimeParametersPrimaryNetwork(network, nodeIndex, duration, startTimeStr, nodeCmd) +// if err != nil { +// return err +// } +// +// recipientAddr := kc.Addresses().List()[0] +// // we set the starting time for node to be a Primary Network Validator to be in 1 minute +// // we use min delegation fee as default +// delegationFee := network.GenesisParams().MinDelegationFee +// blsKeyBytes, err := os.ReadFile(signingKeyPath) +// if err != nil { +// return err +// } +// blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) +// if err != nil { +// return err +// } +// if _, err := deployer.AddPermissionlessValidator( +// ids.Empty, +// ids.Empty, +// nodeID, +// weight, +// uint64(start.Unix()), +// uint64(start.Add(duration).Unix()), +// recipientAddr, +// delegationFee, +// nil, +// signer.NewProofOfPossession(blsSk), +// ); err != nil { +// return err +// } +// return nil +//} +// +//func (d *PublicDeployer) AddPermissionlessValidator( +// subnetID ids.ID, +// subnetAssetID ids.ID, +// nodeID ids.NodeID, +// stakeAmount uint64, +// startTime uint64, +// endTime uint64, +// recipientAddr ids.ShortID, +// delegationFee uint32, +// popBytes []byte, +// proofOfPossession *signer.ProofOfPossession, +//) (ids.ID, error) { +// wallet, err := d.loadWallet(subnetID) +// if err != nil { +// return ids.Empty, err +// } +// if subnetAssetID == ids.Empty { +// subnetAssetID = wallet.P().Builder().Context().AVAXAssetID +// } +// // popBytes is a marshalled json object containing publicKey and proofOfPossession of the node's BLS info +// txID, err := d.issueAddPermissionlessValidatorTX(recipientAddr, stakeAmount, subnetID, nodeID, subnetAssetID, startTime, endTime, wallet, delegationFee, popBytes, proofOfPossession) +// if err != nil { +// return ids.Empty, err +// } +// return txID, nil +//} +// +//func (c *Subnet) issueAddPermissionlessValidatorTX( +// recipientAddr ids.ShortID, +// stakeAmount uint64, +// subnetID ids.ID, +// nodeID ids.NodeID, +// assetID ids.ID, +// startTime uint64, +// endTime uint64, +// wallet primary.Wallet, +// delegationFee uint32, +// popBytes []byte, +// blsProof *signer.ProofOfPossession, +//) (ids.ID, error) { +// options := d.getMultisigTxOptions([]ids.ShortID{}) +// owner := &secp256k1fx.OutputOwners{ +// Threshold: 1, +// Addrs: []ids.ShortID{ +// recipientAddr, +// }, +// } +// var proofOfPossession signer.Signer +// if subnetID == ids.Empty { +// if popBytes != nil { +// pop := &signer.ProofOfPossession{} +// err := pop.UnmarshalJSON(popBytes) +// if err != nil { +// return ids.Empty, err +// } +// proofOfPossession = pop +// } else { +// proofOfPossession = blsProof +// } +// } else { +// proofOfPossession = &signer.Empty{} +// } +// +// unsignedTx, err := wallet.P().Builder().NewAddPermissionlessValidatorTx( +// &txs.SubnetValidator{ +// Validator: txs.Validator{ +// NodeID: nodeID, +// Start: startTime, +// End: endTime, +// Wght: stakeAmount, +// }, +// Subnet: subnetID, +// }, +// proofOfPossession, +// assetID, +// owner, +// owner, +// delegationFee, +// options..., +// ) +// if err != nil { +// return ids.Empty, fmt.Errorf("error building tx: %w", err) +// } +// tx := txs.Tx{Unsigned: unsignedTx} +// if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { +// return ids.Empty, fmt.Errorf("error signing tx: %w", err) +// } +// +// ctx, cancel := utils.GetAPIContext() +// defer cancel() +// err = wallet.P().IssueTx( +// &tx, +// common.WithContext(ctx), +// ) +// if err != nil { +// if ctx.Err() != nil { +// err = fmt.Errorf("timeout issuing/verifying tx with ID %s: %w", tx.ID(), err) +// } else { +// err = fmt.Errorf("error issuing tx with ID %s: %w", tx.ID(), err) +// } +// return ids.Empty, err +// } +// +// return tx.ID(), nil +//} +// +//// AddValidator adds validator to subnet +//// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be +//// tracking the subnet +//// TODO: add more description once node join subnet sdk is done +//func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput ValidatorParams) (*multisig.Multisig, error) { +// if validatorInput.NodeID == ids.EmptyNodeID { +// return nil, ErrEmptyValidatorNodeID +// } +// if validatorInput.Duration == 0 { +// return nil, ErrEmptyValidatorDuration +// } +// if validatorInput.Weight == 0 { +// return nil, ErrEmptyValidatorWeight +// } +// if c.SubnetID == ids.Empty { +// return nil, ErrEmptySubnetID +// } +// +// wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) +// +// validator := &txs.SubnetValidator{ +// Validator: txs.Validator{ +// NodeID: validatorInput.NodeID, +// End: uint64(time.Now().Add(validatorInput.Duration).Unix()), +// Wght: validatorInput.Weight, +// }, +// Subnet: c.SubnetID, +// } +// +// unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) +// if err != nil { +// return nil, fmt.Errorf("error building tx: %w", err) +// } +// tx := txs.Tx{Unsigned: unsignedTx} +// if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { +// return nil, fmt.Errorf("error signing tx: %w", err) +// } +// return multisig.New(&tx), nil +//} diff --git a/node/add_validator_primary_network_test.go b/node/add_validator_primary_network_test.go new file mode 100644 index 0000000..8130c17 --- /dev/null +++ b/node/add_validator_primary_network_test.go @@ -0,0 +1,233 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package node + +import ( + "context" + "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "testing" + "time" +) + +//func TestNodesValidatePrimaryNetwork(_ *testing.T) { +// ctx := context.Background() +// // Get the default cloud parameters for AWS +// cp, err := GetDefaultCloudParams(ctx, AWSCloud) +// if err != nil { +// panic(err) +// } +// +// //securityGroupName := "SECURITY_GROUP_NAME" +// securityGroupName := "rs_security_group_sdk_new" +// //sgID := "sg-024ef9cde0f63fe5a" +// sgID, err := awsAPI.CreateSecurityGroup(ctx, securityGroupName, cp.AWSConfig.AWSProfile, cp.Region) +// if err != nil { +// panic(err) +// } +// // Set the security group we are using when creating our Avalanche Nodes +// cp.AWSConfig.AWSSecurityGroupID = sgID +// cp.AWSConfig.AWSSecurityGroupName = securityGroupName +// +// //keyPairName := "KEY_PAIR_NAME" +// keyPairName := "rs_key_pair_sdk" +// //sshPrivateKeyPath := utils.ExpandHome("PRIVATE_KEY_FILEPATH") +// sshPrivateKeyPath := utils.ExpandHome("/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem") +// //if err := awsAPI.CreateSSHKeyPair(ctx, cp.AWSConfig.AWSProfile, cp.Region, keyPairName, sshPrivateKeyPath); err != nil { +// // panic(err) +// //} +// // Set the key pair we are using when creating our Avalanche Nodes +// cp.AWSConfig.AWSKeyPair = keyPairName +// +// // Avalanche-CLI is installed in nodes to enable them to join subnets as validators +// // Avalanche-CLI dependency by Avalanche nodes will be deprecated in the next release +// // of Avalanche Tooling SDK +// const ( +// avalancheGoVersion = "v1.11.8" +// ) +// +// // Create two new Avalanche Validator nodes on Fuji Network on AWS without Elastic IPs +// // attached. Once CreateNodes is completed, the validators will begin bootstrapping process +// // to Primary Network in Fuji Network. Nodes need to finish bootstrapping process +// // before they can validate Avalanche Primary Network / Subnet. +// // +// // SDK function for nodes to start validating Primary Network / Subnet will be available +// // in the next Avalanche Tooling SDK release. +// hosts, err := CreateNodes(ctx, +// &NodeParams{ +// CloudParams: cp, +// Count: 2, +// Roles: []SupportedRole{Validator}, +// Network: avalanche.FujiNetwork(), +// AvalancheGoVersion: avalancheGoVersion, +// UseStaticIP: false, +// SSHPrivateKeyPath: sshPrivateKeyPath, +// }) +// if err != nil { +// panic(err) +// } +// +// fmt.Println("Successfully created Avalanche Validators") +// +// const ( +// sshTimeout = 120 * time.Second +// sshCommandTimeout = 10 * time.Second +// ) +// +// // Examples showing how to run ssh commands on the created nodes +// for _, h := range hosts { +// // Wait for the host to be ready (only needs to be done once for newly created nodes) +// fmt.Println("Waiting for SSH shell") +// if err := h.WaitForSSHShell(sshTimeout); err != nil { +// panic(err) +// } +// fmt.Println("SSH shell ready to execute commands") +// // Run a command on the host +// if output, err := h.Commandf(nil, sshCommandTimeout, "echo 'Hello, %s!'", "World"); err != nil { +// panic(err) +// } else { +// fmt.Println(string(output)) +// } +// // sleep for 10 seconds allowing AvalancheGo container to start +// time.Sleep(10 * time.Second) +// // check if avalanchego is running +// if output, err := h.Commandf(nil, sshCommandTimeout, "docker ps"); err != nil { +// panic(err) +// } else { +// fmt.Println(string(output)) +// } +// } +// +// // Create a monitoring node. +// // Monitoring node enables you to have a centralized Grafana Dashboard where you can view +// // metrics relevant to any Validator & API nodes that the monitoring node is linked to as well +// // as a centralized logs for the X/P/C Chain and Subnet logs for the Validator & API nodes. +// // An example on how the dashboard and logs look like can be found at https://docs.avax.network/tooling/cli-create-nodes/create-a-validator-aws +// monitoringHosts, err := CreateNodes(ctx, +// &NodeParams{ +// CloudParams: cp, +// Count: 1, +// Roles: []SupportedRole{Monitor}, +// UseStaticIP: false, +// SSHPrivateKeyPath: sshPrivateKeyPath, +// }) +// if err != nil { +// panic(err) +// } +// +// fmt.Println("Successfully created monitoring node") +// fmt.Println("Linking monitoring node with Avalanche Validator nodes ...") +// // Link the 2 validator nodes previously created with the monitoring host so that +// // the monitoring host can start tracking the validator nodes metrics and collecting their logs +// if err := monitoringHosts[0].MonitorNodes(ctx, hosts, ""); err != nil { +// panic(err) +// } +// fmt.Println("Successfully linked monitoring node with Avalanche Validator nodes") +// // +// //fmt.Println("Terminating all created nodes ...") +// //// Destroy all created nodes +// //for _, h := range hosts { +// // err = h.Destroy(ctx) +// // if err != nil { +// // panic(err) +// // } +// //} +// //err = monitoringHosts[0].Destroy(ctx) +// //if err != nil { +// // panic(err) +// //} +// //fmt.Println("All nodes terminated") +//} + +func TestNodesValidatePrimaryNetwork(_ *testing.T) { + ctx := context.Background() + cp, err := GetDefaultCloudParams(ctx, AWSCloud) + if err != nil { + panic(err) + } + + node := Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: SSHConfig{ + User: constants.AnsibleSSHUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: AWSCloud, + + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + + Roles: []SupportedRole{Validator}, + } + + //node := Node{ + // // NodeID is Avalanche Node ID of the node + // NodeID: "NodeID-mF1xjcfTNmB3FikUhZg4Lyfpj8htSmZ1", + // // IP address of the node + // IP: "52.53.210.128", + // // SSH configuration for the node + // SSHConfig: SSHConfig{ + // User: constants.AnsibleSSHUser, + // PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + // }, + // // Cloud is the cloud service that the node is on + // Cloud: AWSCloud, + // + // // CloudConfig is the cloud specific configuration for the node + // CloudConfig: *cp, + // + // Roles: []SupportedRole{Validator}, + //} + + err = node.ProvideStakingCertAndKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) + if err != nil { + panic(err) + } + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + validator := ValidatorParams{ + NodeID: nodeID, + Duration: 48 * time.Hour, + Weight: 20, + } + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: nil, + }, + ) + if err != nil { + panic(err) + } + err = node.SetNodeBLSKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s/signer.key", node.NodeID)) + if err != nil { + panic(err) + } + txID, err := node.AddNodeAsPrimaryNetworkValidator(avalanche.FujiNetwork(), validator, wallet) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} diff --git a/node/cloud.go b/node/cloud.go index fe49128..f947e4a 100644 --- a/node/cloud.go +++ b/node/cloud.go @@ -122,7 +122,7 @@ func GetDefaultCloudParams(ctx context.Context, cloud SupportedCloud) (*CloudPar AWSVolumeIOPS: 1000, AWSVolumeType: "gp3", }, - Region: "us-east-1", + Region: "us-west-1", InstanceType: constants.AWSDefaultInstanceType, } awsSvc, err := awsAPI.NewAwsCloud(ctx, cp.AWSConfig.AWSProfile, cp.Region) diff --git a/node/node.go b/node/node.go index ab67085..5f86e3a 100644 --- a/node/node.go +++ b/node/node.go @@ -8,6 +8,7 @@ import ( "bytes" "context" "fmt" + "github.com/ava-labs/avalanchego/utils/crypto/bls" "io" "net" "net/http" @@ -75,6 +76,8 @@ type Node struct { // Logger for node Logger avalanche.LeveledLogger + + BlsSecretKey *bls.SecretKey } // NewNodeConnection creates a new SSH connection to the node @@ -168,9 +171,11 @@ func (h *Node) Disconnect() error { func (h *Node) Upload(localFile string, remoteFile string, timeout time.Duration) error { if !h.Connected() { if err := h.Connect(0); err != nil { + fmt.Printf("we are not connected") return err } } + fmt.Printf("we are connected") _, err := utils.TimedFunction( func() (interface{}, error) { return nil, h.connection.Upload(localFile, remoteFile) diff --git a/node/ssh.go b/node/ssh.go index fca99ea..6b24dd4 100644 --- a/node/ssh.go +++ b/node/ssh.go @@ -257,6 +257,7 @@ func (h *Node) RunSSHGetNewSubnetEVMRelease(subnetEVMReleaseURL, subnetEVMArchiv // RunSSHUploadStakingFiles uploads staking files to a remote host via SSH. func (h *Node) RunSSHUploadStakingFiles(keyPath string) error { + fmt.Printf("we are starting RunSSHUploadStakingFiles ") if err := h.MkdirAll( constants.CloudNodeStakingPath, constants.SSHFileOpsTimeout, @@ -268,6 +269,7 @@ func (h *Node) RunSSHUploadStakingFiles(keyPath string) error { filepath.Join(constants.CloudNodeStakingPath, constants.StakerCertFileName), constants.SSHFileOpsTimeout, ); err != nil { + fmt.Printf("we have error uploading staker file") return err } if err := h.Upload( diff --git a/node/staking.go b/node/staking.go index 2f60992..14dc61d 100644 --- a/node/staking.go +++ b/node/staking.go @@ -4,6 +4,7 @@ package node import ( + "fmt" "os" "path/filepath" @@ -15,8 +16,10 @@ import ( func (h *Node) ProvideStakingCertAndKey(keyPath string) error { if nodeID, err := GenerateNodeCertAndKeys(keyPath); err != nil { + fmt.Printf("we have err here GenerateNodeCertAndKeys ") return err } else { + fmt.Printf("we are all good here GenerateNodeCertAndKeys") h.Logger.Infof("Generated Staking Cert and Key for NodeID: %s in folder %s", nodeID.String(), keyPath) } return h.RunSSHUploadStakingFiles(keyPath) @@ -25,6 +28,7 @@ func (h *Node) ProvideStakingCertAndKey(keyPath string) error { // GenerateNodeCertAndKeys generates a node certificate and keys and return nodeID func GenerateNodeCertAndKeys(keyPath string) (ids.NodeID, error) { if err := os.MkdirAll(keyPath, constants.DefaultPerms755); err != nil { + fmt.Printf("we have err beginning") return ids.EmptyNodeID, err } stakerCertFilePath := filepath.Join(keyPath, constants.StakerCertFileName) @@ -40,6 +44,7 @@ func GenerateNodeCertAndKeys(keyPath string) (ids.NodeID, error) { return ids.EmptyNodeID, err } if err := os.MkdirAll(filepath.Dir(stakerCertFilePath), constants.DefaultPerms755); err != nil { + fmt.Printf("we have err stakerCertFilePath") return ids.EmptyNodeID, err } if err := os.WriteFile(stakerCertFilePath, certBytes, constants.WriteReadUserOnlyPerms); err != nil { From 235d6d7b1fbf032043c57c162686b98e868a905a Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 8 Aug 2024 20:40:02 +0700 Subject: [PATCH 02/56] remove print --- node/node.go | 3 ++- node/ssh.go | 1 - node/staking.go | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/node/node.go b/node/node.go index 5f86e3a..ab73a16 100644 --- a/node/node.go +++ b/node/node.go @@ -96,6 +96,7 @@ func NewNodeConnection(h *Node, port uint) (*goph.Client, error) { auth, err = goph.Key(h.SSHConfig.PrivateKeyPath, "") } if err != nil { + fmt.Printf("we have err connecting private key") return nil, err } cl, err := goph.NewConn(&goph.Config{ @@ -108,6 +109,7 @@ func NewNodeConnection(h *Node, port uint) (*goph.Client, error) { Callback: ssh.InsecureIgnoreHostKey(), // we don't verify node key ( similar to ansible) }) if err != nil { + fmt.Printf("we have err NewConn") return nil, err } return cl, nil @@ -171,7 +173,6 @@ func (h *Node) Disconnect() error { func (h *Node) Upload(localFile string, remoteFile string, timeout time.Duration) error { if !h.Connected() { if err := h.Connect(0); err != nil { - fmt.Printf("we are not connected") return err } } diff --git a/node/ssh.go b/node/ssh.go index 6b24dd4..8112417 100644 --- a/node/ssh.go +++ b/node/ssh.go @@ -257,7 +257,6 @@ func (h *Node) RunSSHGetNewSubnetEVMRelease(subnetEVMReleaseURL, subnetEVMArchiv // RunSSHUploadStakingFiles uploads staking files to a remote host via SSH. func (h *Node) RunSSHUploadStakingFiles(keyPath string) error { - fmt.Printf("we are starting RunSSHUploadStakingFiles ") if err := h.MkdirAll( constants.CloudNodeStakingPath, constants.SSHFileOpsTimeout, diff --git a/node/staking.go b/node/staking.go index 14dc61d..664cad5 100644 --- a/node/staking.go +++ b/node/staking.go @@ -16,10 +16,8 @@ import ( func (h *Node) ProvideStakingCertAndKey(keyPath string) error { if nodeID, err := GenerateNodeCertAndKeys(keyPath); err != nil { - fmt.Printf("we have err here GenerateNodeCertAndKeys ") return err } else { - fmt.Printf("we are all good here GenerateNodeCertAndKeys") h.Logger.Infof("Generated Staking Cert and Key for NodeID: %s in folder %s", nodeID.String(), keyPath) } return h.RunSSHUploadStakingFiles(keyPath) From 8594732dbb091325a096d66e1a1a837819d51706 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 9 Aug 2024 19:04:54 +0700 Subject: [PATCH 03/56] add example of validate primary network --- node/add_validator_primary_network.go | 257 +-------------------- node/add_validator_primary_network_test.go | 168 ++------------ node/node.go | 5 +- node/ssh.go | 2 +- 4 files changed, 20 insertions(+), 412 deletions(-) diff --git a/node/add_validator_primary_network.go b/node/add_validator_primary_network.go index 860c444..037449c 100644 --- a/node/add_validator_primary_network.go +++ b/node/add_validator_primary_network.go @@ -68,12 +68,6 @@ func (h *Node) AddNodeAsPrimaryNetworkValidator( validatorInput ValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { - //if isValidator, err := checkNodeIsPrimaryNetworkValidator(nodeID, network); err != nil { - // return err - //} else if !isValidator { - // signingKeyPath := app.GetNodeBLSSecretKeyPath(instanceID) - // return joinAsPrimaryNetworkValidator(deployer, network, kc, nodeID, nodeIndex, signingKeyPath, true) - //} minValStake, err := GetMinStakingAmount(network) if err != nil { return ids.Empty, err @@ -82,61 +76,23 @@ func (h *Node) AddNodeAsPrimaryNetworkValidator( return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validatorInput.Weight) } - //recipientAddr := kc.Addresses().List()[0] - // we set the starting time for node to be a Primary Network Validator to be in 1 minute - // we use min delegation fee as default delegationFee := network.GenesisParams().MinDelegationFee - /// - //blsKeyBytes, err := os.ReadFile(signingKeyPath) - //if err != nil { - // return ids.Empty, err - //} - //blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) - //if err != nil { - // return ids.Empty, err - //} - /// - //if _, err := deployer.AddPermissionlessValidator( - // ids.Empty, - // ids.Empty, - // nodeID, - // weight, - // uint64(start.Unix()), - // uint64(start.Add(duration).Unix()), - // recipientAddr, - // delegationFee, - // nil, - // signer.NewProofOfPossession(blsSk), - //); err != nil { - // return err - //} - - // popBytes is a marshalled json object containing publicKey and proofOfPossession of the node's BLS info - //txID, err := d.issueAddPermissionlessValidatorTX(recipientAddr, stakeAmount, subnetID, nodeID, subnetAssetID, startTime, endTime, wallet, delegationFee, popBytes, proofOfPossession) - //if err != nil { - // return ids.Empty, err - //} wallet.SetSubnetAuthMultisig([]ids.ShortID{}) - //owner := &secp256k1fx.OutputOwners{ - // Threshold: 1, - // Addrs: []ids.ShortID{ - // recipientAddr, - // }, - //} owner := &secp256k1fx.OutputOwners{ Threshold: 1, Addrs: []ids.ShortID{ wallet.Addresses()[0], }, } + proofOfPossession := signer.NewProofOfPossession(h.BlsSecretKey) nodeID, err := ids.NodeIDFromString(h.NodeID) if err != nil { return ids.Empty, err } - fmt.Printf("building transaction") + unsignedTx, err := wallet.P().Builder().NewAddPermissionlessValidatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -157,11 +113,9 @@ func (h *Node) AddNodeAsPrimaryNetworkValidator( } tx := txs.Tx{Unsigned: unsignedTx} - fmt.Printf("signing with wallet") if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { return ids.Empty, fmt.Errorf("error signing tx: %w", err) } - fmt.Printf("successfully signed with wallet") ctx, cancel := utils.GetAPIContext() defer cancel() @@ -169,6 +123,7 @@ func (h *Node) AddNodeAsPrimaryNetworkValidator( &tx, common.WithContext(ctx), ) + if err != nil { if ctx.Err() != nil { err = fmt.Errorf("timeout issuing/verifying tx with ID %s: %w", tx.ID(), err) @@ -180,209 +135,3 @@ func (h *Node) AddNodeAsPrimaryNetworkValidator( return tx.ID(), nil } - -//func joinAsPrimaryNetworkValidator( -// deployer *subnet.PublicDeployer, -// network models.Network, -// kc *keychain.Keychain, -// nodeID ids.NodeID, -// nodeIndex int, -// signingKeyPath string, -// nodeCmd bool, -//) error { -// var ( -// start time.Time -// err error -// ) -// minValStake, err := GetMinStakingAmount(network) -// if err != nil { -// return err -// } -// if weight == 0 { -// weight, err = PromptWeightPrimaryNetwork(network) -// if err != nil { -// return err -// } -// } -// if weight < minValStake { -// return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, weight) -// } -// start, duration, err = GetTimeParametersPrimaryNetwork(network, nodeIndex, duration, startTimeStr, nodeCmd) -// if err != nil { -// return err -// } -// -// recipientAddr := kc.Addresses().List()[0] -// // we set the starting time for node to be a Primary Network Validator to be in 1 minute -// // we use min delegation fee as default -// delegationFee := network.GenesisParams().MinDelegationFee -// blsKeyBytes, err := os.ReadFile(signingKeyPath) -// if err != nil { -// return err -// } -// blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) -// if err != nil { -// return err -// } -// if _, err := deployer.AddPermissionlessValidator( -// ids.Empty, -// ids.Empty, -// nodeID, -// weight, -// uint64(start.Unix()), -// uint64(start.Add(duration).Unix()), -// recipientAddr, -// delegationFee, -// nil, -// signer.NewProofOfPossession(blsSk), -// ); err != nil { -// return err -// } -// return nil -//} -// -//func (d *PublicDeployer) AddPermissionlessValidator( -// subnetID ids.ID, -// subnetAssetID ids.ID, -// nodeID ids.NodeID, -// stakeAmount uint64, -// startTime uint64, -// endTime uint64, -// recipientAddr ids.ShortID, -// delegationFee uint32, -// popBytes []byte, -// proofOfPossession *signer.ProofOfPossession, -//) (ids.ID, error) { -// wallet, err := d.loadWallet(subnetID) -// if err != nil { -// return ids.Empty, err -// } -// if subnetAssetID == ids.Empty { -// subnetAssetID = wallet.P().Builder().Context().AVAXAssetID -// } -// // popBytes is a marshalled json object containing publicKey and proofOfPossession of the node's BLS info -// txID, err := d.issueAddPermissionlessValidatorTX(recipientAddr, stakeAmount, subnetID, nodeID, subnetAssetID, startTime, endTime, wallet, delegationFee, popBytes, proofOfPossession) -// if err != nil { -// return ids.Empty, err -// } -// return txID, nil -//} -// -//func (c *Subnet) issueAddPermissionlessValidatorTX( -// recipientAddr ids.ShortID, -// stakeAmount uint64, -// subnetID ids.ID, -// nodeID ids.NodeID, -// assetID ids.ID, -// startTime uint64, -// endTime uint64, -// wallet primary.Wallet, -// delegationFee uint32, -// popBytes []byte, -// blsProof *signer.ProofOfPossession, -//) (ids.ID, error) { -// options := d.getMultisigTxOptions([]ids.ShortID{}) -// owner := &secp256k1fx.OutputOwners{ -// Threshold: 1, -// Addrs: []ids.ShortID{ -// recipientAddr, -// }, -// } -// var proofOfPossession signer.Signer -// if subnetID == ids.Empty { -// if popBytes != nil { -// pop := &signer.ProofOfPossession{} -// err := pop.UnmarshalJSON(popBytes) -// if err != nil { -// return ids.Empty, err -// } -// proofOfPossession = pop -// } else { -// proofOfPossession = blsProof -// } -// } else { -// proofOfPossession = &signer.Empty{} -// } -// -// unsignedTx, err := wallet.P().Builder().NewAddPermissionlessValidatorTx( -// &txs.SubnetValidator{ -// Validator: txs.Validator{ -// NodeID: nodeID, -// Start: startTime, -// End: endTime, -// Wght: stakeAmount, -// }, -// Subnet: subnetID, -// }, -// proofOfPossession, -// assetID, -// owner, -// owner, -// delegationFee, -// options..., -// ) -// if err != nil { -// return ids.Empty, fmt.Errorf("error building tx: %w", err) -// } -// tx := txs.Tx{Unsigned: unsignedTx} -// if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { -// return ids.Empty, fmt.Errorf("error signing tx: %w", err) -// } -// -// ctx, cancel := utils.GetAPIContext() -// defer cancel() -// err = wallet.P().IssueTx( -// &tx, -// common.WithContext(ctx), -// ) -// if err != nil { -// if ctx.Err() != nil { -// err = fmt.Errorf("timeout issuing/verifying tx with ID %s: %w", tx.ID(), err) -// } else { -// err = fmt.Errorf("error issuing tx with ID %s: %w", tx.ID(), err) -// } -// return ids.Empty, err -// } -// -// return tx.ID(), nil -//} -// -//// AddValidator adds validator to subnet -//// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be -//// tracking the subnet -//// TODO: add more description once node join subnet sdk is done -//func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput ValidatorParams) (*multisig.Multisig, error) { -// if validatorInput.NodeID == ids.EmptyNodeID { -// return nil, ErrEmptyValidatorNodeID -// } -// if validatorInput.Duration == 0 { -// return nil, ErrEmptyValidatorDuration -// } -// if validatorInput.Weight == 0 { -// return nil, ErrEmptyValidatorWeight -// } -// if c.SubnetID == ids.Empty { -// return nil, ErrEmptySubnetID -// } -// -// wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) -// -// validator := &txs.SubnetValidator{ -// Validator: txs.Validator{ -// NodeID: validatorInput.NodeID, -// End: uint64(time.Now().Add(validatorInput.Duration).Unix()), -// Wght: validatorInput.Weight, -// }, -// Subnet: c.SubnetID, -// } -// -// unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) -// if err != nil { -// return nil, fmt.Errorf("error building tx: %w", err) -// } -// tx := txs.Tx{Unsigned: unsignedTx} -// if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { -// return nil, fmt.Errorf("error signing tx: %w", err) -// } -// return multisig.New(&tx), nil -//} diff --git a/node/add_validator_primary_network_test.go b/node/add_validator_primary_network_test.go index 8130c17..79a2b4f 100644 --- a/node/add_validator_primary_network_test.go +++ b/node/add_validator_primary_network_test.go @@ -17,135 +17,6 @@ import ( "time" ) -//func TestNodesValidatePrimaryNetwork(_ *testing.T) { -// ctx := context.Background() -// // Get the default cloud parameters for AWS -// cp, err := GetDefaultCloudParams(ctx, AWSCloud) -// if err != nil { -// panic(err) -// } -// -// //securityGroupName := "SECURITY_GROUP_NAME" -// securityGroupName := "rs_security_group_sdk_new" -// //sgID := "sg-024ef9cde0f63fe5a" -// sgID, err := awsAPI.CreateSecurityGroup(ctx, securityGroupName, cp.AWSConfig.AWSProfile, cp.Region) -// if err != nil { -// panic(err) -// } -// // Set the security group we are using when creating our Avalanche Nodes -// cp.AWSConfig.AWSSecurityGroupID = sgID -// cp.AWSConfig.AWSSecurityGroupName = securityGroupName -// -// //keyPairName := "KEY_PAIR_NAME" -// keyPairName := "rs_key_pair_sdk" -// //sshPrivateKeyPath := utils.ExpandHome("PRIVATE_KEY_FILEPATH") -// sshPrivateKeyPath := utils.ExpandHome("/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem") -// //if err := awsAPI.CreateSSHKeyPair(ctx, cp.AWSConfig.AWSProfile, cp.Region, keyPairName, sshPrivateKeyPath); err != nil { -// // panic(err) -// //} -// // Set the key pair we are using when creating our Avalanche Nodes -// cp.AWSConfig.AWSKeyPair = keyPairName -// -// // Avalanche-CLI is installed in nodes to enable them to join subnets as validators -// // Avalanche-CLI dependency by Avalanche nodes will be deprecated in the next release -// // of Avalanche Tooling SDK -// const ( -// avalancheGoVersion = "v1.11.8" -// ) -// -// // Create two new Avalanche Validator nodes on Fuji Network on AWS without Elastic IPs -// // attached. Once CreateNodes is completed, the validators will begin bootstrapping process -// // to Primary Network in Fuji Network. Nodes need to finish bootstrapping process -// // before they can validate Avalanche Primary Network / Subnet. -// // -// // SDK function for nodes to start validating Primary Network / Subnet will be available -// // in the next Avalanche Tooling SDK release. -// hosts, err := CreateNodes(ctx, -// &NodeParams{ -// CloudParams: cp, -// Count: 2, -// Roles: []SupportedRole{Validator}, -// Network: avalanche.FujiNetwork(), -// AvalancheGoVersion: avalancheGoVersion, -// UseStaticIP: false, -// SSHPrivateKeyPath: sshPrivateKeyPath, -// }) -// if err != nil { -// panic(err) -// } -// -// fmt.Println("Successfully created Avalanche Validators") -// -// const ( -// sshTimeout = 120 * time.Second -// sshCommandTimeout = 10 * time.Second -// ) -// -// // Examples showing how to run ssh commands on the created nodes -// for _, h := range hosts { -// // Wait for the host to be ready (only needs to be done once for newly created nodes) -// fmt.Println("Waiting for SSH shell") -// if err := h.WaitForSSHShell(sshTimeout); err != nil { -// panic(err) -// } -// fmt.Println("SSH shell ready to execute commands") -// // Run a command on the host -// if output, err := h.Commandf(nil, sshCommandTimeout, "echo 'Hello, %s!'", "World"); err != nil { -// panic(err) -// } else { -// fmt.Println(string(output)) -// } -// // sleep for 10 seconds allowing AvalancheGo container to start -// time.Sleep(10 * time.Second) -// // check if avalanchego is running -// if output, err := h.Commandf(nil, sshCommandTimeout, "docker ps"); err != nil { -// panic(err) -// } else { -// fmt.Println(string(output)) -// } -// } -// -// // Create a monitoring node. -// // Monitoring node enables you to have a centralized Grafana Dashboard where you can view -// // metrics relevant to any Validator & API nodes that the monitoring node is linked to as well -// // as a centralized logs for the X/P/C Chain and Subnet logs for the Validator & API nodes. -// // An example on how the dashboard and logs look like can be found at https://docs.avax.network/tooling/cli-create-nodes/create-a-validator-aws -// monitoringHosts, err := CreateNodes(ctx, -// &NodeParams{ -// CloudParams: cp, -// Count: 1, -// Roles: []SupportedRole{Monitor}, -// UseStaticIP: false, -// SSHPrivateKeyPath: sshPrivateKeyPath, -// }) -// if err != nil { -// panic(err) -// } -// -// fmt.Println("Successfully created monitoring node") -// fmt.Println("Linking monitoring node with Avalanche Validator nodes ...") -// // Link the 2 validator nodes previously created with the monitoring host so that -// // the monitoring host can start tracking the validator nodes metrics and collecting their logs -// if err := monitoringHosts[0].MonitorNodes(ctx, hosts, ""); err != nil { -// panic(err) -// } -// fmt.Println("Successfully linked monitoring node with Avalanche Validator nodes") -// // -// //fmt.Println("Terminating all created nodes ...") -// //// Destroy all created nodes -// //for _, h := range hosts { -// // err = h.Destroy(ctx) -// // if err != nil { -// // panic(err) -// // } -// //} -// //err = monitoringHosts[0].Destroy(ctx) -// //if err != nil { -// // panic(err) -// //} -// //fmt.Println("All nodes terminated") -//} - func TestNodesValidatePrimaryNetwork(_ *testing.T) { ctx := context.Background() cp, err := GetDefaultCloudParams(ctx, AWSCloud) @@ -165,50 +36,37 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { }, // Cloud is the cloud service that the node is on Cloud: AWSCloud, - // CloudConfig is the cloud specific configuration for the node CloudConfig: *cp, - + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor Roles: []SupportedRole{Validator}, } - //node := Node{ - // // NodeID is Avalanche Node ID of the node - // NodeID: "NodeID-mF1xjcfTNmB3FikUhZg4Lyfpj8htSmZ1", - // // IP address of the node - // IP: "52.53.210.128", - // // SSH configuration for the node - // SSHConfig: SSHConfig{ - // User: constants.AnsibleSSHUser, - // PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - // }, - // // Cloud is the cloud service that the node is on - // Cloud: AWSCloud, - // - // // CloudConfig is the cloud specific configuration for the node - // CloudConfig: *cp, - // - // Roles: []SupportedRole{Validator}, + //err = node.ProvideStakingCertAndKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) + //if err != nil { + // panic(err) //} - err = node.ProvideStakingCertAndKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) - if err != nil { - panic(err) - } nodeID, err := ids.NodeIDFromString(node.NodeID) if err != nil { panic(err) } + validator := ValidatorParams{ - NodeID: nodeID, + NodeID: nodeID, + // Validate Primary Network for 48 hours Duration: 48 * time.Hour, - Weight: 20, + // 1 billion in weight is equivalent to 1 AVAX + Weight: 2000000000, } + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) if err != nil { panic(err) } + wallet, err := wallet.New( context.Background(), &primary.WalletConfig{ @@ -221,10 +79,12 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { if err != nil { panic(err) } + err = node.SetNodeBLSKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s/signer.key", node.NodeID)) if err != nil { panic(err) } + txID, err := node.AddNodeAsPrimaryNetworkValidator(avalanche.FujiNetwork(), validator, wallet) if err != nil { panic(err) diff --git a/node/node.go b/node/node.go index ab73a16..559cd36 100644 --- a/node/node.go +++ b/node/node.go @@ -96,7 +96,6 @@ func NewNodeConnection(h *Node, port uint) (*goph.Client, error) { auth, err = goph.Key(h.SSHConfig.PrivateKeyPath, "") } if err != nil { - fmt.Printf("we have err connecting private key") return nil, err } cl, err := goph.NewConn(&goph.Config{ @@ -109,7 +108,6 @@ func NewNodeConnection(h *Node, port uint) (*goph.Client, error) { Callback: ssh.InsecureIgnoreHostKey(), // we don't verify node key ( similar to ansible) }) if err != nil { - fmt.Printf("we have err NewConn") return nil, err } return cl, nil @@ -176,7 +174,8 @@ func (h *Node) Upload(localFile string, remoteFile string, timeout time.Duration return err } } - fmt.Printf("we are connected") + fmt.Printf("we are connected \n") + fmt.Printf("we are uploading from localfile %s to remotefile %s \n", localFile, remoteFile) _, err := utils.TimedFunction( func() (interface{}, error) { return nil, h.connection.Upload(localFile, remoteFile) diff --git a/node/ssh.go b/node/ssh.go index 8112417..c7bea46 100644 --- a/node/ssh.go +++ b/node/ssh.go @@ -268,7 +268,7 @@ func (h *Node) RunSSHUploadStakingFiles(keyPath string) error { filepath.Join(constants.CloudNodeStakingPath, constants.StakerCertFileName), constants.SSHFileOpsTimeout, ); err != nil { - fmt.Printf("we have error uploading staker file") + fmt.Printf("we have error uploading staker file \n") return err } if err := h.Upload( From 53acf86215a943b7555528a78132f8b633076ec4 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 9 Aug 2024 19:05:51 +0700 Subject: [PATCH 04/56] remove fmt print --- node/staking.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/node/staking.go b/node/staking.go index 664cad5..2f60992 100644 --- a/node/staking.go +++ b/node/staking.go @@ -4,7 +4,6 @@ package node import ( - "fmt" "os" "path/filepath" @@ -26,7 +25,6 @@ func (h *Node) ProvideStakingCertAndKey(keyPath string) error { // GenerateNodeCertAndKeys generates a node certificate and keys and return nodeID func GenerateNodeCertAndKeys(keyPath string) (ids.NodeID, error) { if err := os.MkdirAll(keyPath, constants.DefaultPerms755); err != nil { - fmt.Printf("we have err beginning") return ids.EmptyNodeID, err } stakerCertFilePath := filepath.Join(keyPath, constants.StakerCertFileName) @@ -42,7 +40,6 @@ func GenerateNodeCertAndKeys(keyPath string) (ids.NodeID, error) { return ids.EmptyNodeID, err } if err := os.MkdirAll(filepath.Dir(stakerCertFilePath), constants.DefaultPerms755); err != nil { - fmt.Printf("we have err stakerCertFilePath") return ids.EmptyNodeID, err } if err := os.WriteFile(stakerCertFilePath, certBytes, constants.WriteReadUserOnlyPerms); err != nil { From 0ab6af86da661e4fa6890b2df340f6ca9d8f56a7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 9 Aug 2024 21:58:15 +0700 Subject: [PATCH 05/56] add subnet valdiator --- node/add_validator_primary_network_test.go | 2 +- subnet/subnet.go | 4 ++ subnet/subnet_test.go | 82 ++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/node/add_validator_primary_network_test.go b/node/add_validator_primary_network_test.go index 79a2b4f..1d18d78 100644 --- a/node/add_validator_primary_network_test.go +++ b/node/add_validator_primary_network_test.go @@ -55,7 +55,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { validator := ValidatorParams{ NodeID: nodeID, // Validate Primary Network for 48 hours - Duration: 48 * time.Hour, + Duration: 72 * time.Hour, // 1 billion in weight is equivalent to 1 AVAX Weight: 2000000000, } diff --git a/subnet/subnet.go b/subnet/subnet.go index 2a6fbc3..8b5a526 100644 --- a/subnet/subnet.go +++ b/subnet/subnet.go @@ -183,6 +183,10 @@ func New(subnetParams *SubnetParams) (*Subnet, error) { return &subnet, nil } +func (c *Subnet) SetSubnetID(subnetID ids.ID) { + c.SubnetID = subnetID +} + func createEvmGenesis( subnetEVMParams *SubnetEVMParams, ) ([]byte, error) { diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index e4d4897..8b9d1b1 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,6 +6,8 @@ package subnet import ( "context" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -227,3 +229,83 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } + +func TestValidateSubnet(t *testing.T) { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + newSubnet, err := New(&subnetParams) + if err != nil { + panic(err) + } + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.AnsibleSSHUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + network := avalanche.FujiNetwork() + + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validator := ValidatorParams{ + NodeID: nodeID, + // Validate Primary Network for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + if err != nil { + panic(err) + } + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} From e4a7022d2dbbee64ca353c79831a62c15cc8e457 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Sat, 10 Aug 2024 13:21:34 +0700 Subject: [PATCH 06/56] fix lint --- node/add_validator_primary_network.go | 5 +++-- node/node.go | 5 ++--- node/ssh.go | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/node/add_validator_primary_network.go b/node/add_validator_primary_network.go index 037449c..5ec47d1 100644 --- a/node/add_validator_primary_network.go +++ b/node/add_validator_primary_network.go @@ -6,6 +6,9 @@ package node import ( "errors" "fmt" + "os" + "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" "github.com/ava-labs/avalanchego/utils/crypto/bls" @@ -13,8 +16,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - "os" - "time" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" diff --git a/node/node.go b/node/node.go index 559cd36..ac323ad 100644 --- a/node/node.go +++ b/node/node.go @@ -8,7 +8,6 @@ import ( "bytes" "context" "fmt" - "github.com/ava-labs/avalanchego/utils/crypto/bls" "io" "net" "net/http" @@ -18,6 +17,8 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/melbahja/goph" "golang.org/x/crypto/ssh" @@ -174,8 +175,6 @@ func (h *Node) Upload(localFile string, remoteFile string, timeout time.Duration return err } } - fmt.Printf("we are connected \n") - fmt.Printf("we are uploading from localfile %s to remotefile %s \n", localFile, remoteFile) _, err := utils.TimedFunction( func() (interface{}, error) { return nil, h.connection.Upload(localFile, remoteFile) diff --git a/node/ssh.go b/node/ssh.go index c7bea46..fca99ea 100644 --- a/node/ssh.go +++ b/node/ssh.go @@ -268,7 +268,6 @@ func (h *Node) RunSSHUploadStakingFiles(keyPath string) error { filepath.Join(constants.CloudNodeStakingPath, constants.StakerCertFileName), constants.SSHFileOpsTimeout, ); err != nil { - fmt.Printf("we have error uploading staker file \n") return err } if err := h.Upload( From d90e2a01b1a3584b12277e2a4ad60179b2e31a7f Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 19 Aug 2024 12:41:33 +0700 Subject: [PATCH 07/56] change file name --- ...ry_network.go => add_validator_primary.go} | 26 +++++++------------ ..._test.go => add_validator_primary_test.go} | 14 +++++----- 2 files changed, 17 insertions(+), 23 deletions(-) rename node/{add_validator_primary_network.go => add_validator_primary.go} (78%) rename node/{add_validator_primary_network_test.go => add_validator_primary_test.go} (87%) diff --git a/node/add_validator_primary_network.go b/node/add_validator_primary.go similarity index 78% rename from node/add_validator_primary_network.go rename to node/add_validator_primary.go index 5ec47d1..86bcd2a 100644 --- a/node/add_validator_primary_network.go +++ b/node/add_validator_primary.go @@ -4,7 +4,6 @@ package node import ( - "errors" "fmt" "os" "time" @@ -31,13 +30,6 @@ type ValidatorParams struct { Weight uint64 } -var ( - ErrEmptyValidatorNodeID = errors.New("validator node id is not provided") - ErrEmptyValidatorDuration = errors.New("validator duration is not provided") - ErrEmptyValidatorWeight = errors.New("validator weight is not provided") - ErrEmptySubnetID = errors.New("subnet ID is not provided") -) - func GetMinStakingAmount(network avalanche.Network) (uint64, error) { pClient := platformvm.NewClient(network.Endpoint) ctx, cancel := utils.GetAPIContext() @@ -62,19 +54,21 @@ func (h *Node) SetNodeBLSKey(signingKeyPath string) error { return nil } -// AddNodeAsPrimaryNetworkValidator returns bool if node is added as primary network validator -// as it impacts the output in adding node as subnet validator in the next steps -func (h *Node) AddNodeAsPrimaryNetworkValidator( +// ValidatePrimaryNetwork adds node as primary network validator. +// It adds the node in the specified network (Fuji / Mainnet / Devnet) +// and uses the wallet provided in the argument to pay for the transaction fee +func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validatorInput ValidatorParams, + validator ValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { minValStake, err := GetMinStakingAmount(network) if err != nil { return ids.Empty, err } - if validatorInput.Weight < minValStake { - return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validatorInput.Weight) + + if validator.Weight < minValStake { + return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validator.Weight) } delegationFee := network.GenesisParams().MinDelegationFee @@ -98,8 +92,8 @@ func (h *Node) AddNodeAsPrimaryNetworkValidator( &txs.SubnetValidator{ Validator: txs.Validator{ NodeID: nodeID, - End: uint64(time.Now().Add(validatorInput.Duration).Unix()), - Wght: validatorInput.Weight, + End: uint64(time.Now().Add(validator.Duration).Unix()), + Wght: validator.Weight, }, Subnet: ids.Empty, }, diff --git a/node/add_validator_primary_network_test.go b/node/add_validator_primary_test.go similarity index 87% rename from node/add_validator_primary_network_test.go rename to node/add_validator_primary_test.go index 79a2b4f..b74b04a 100644 --- a/node/add_validator_primary_network_test.go +++ b/node/add_validator_primary_test.go @@ -26,9 +26,9 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { node := Node{ // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + NodeID: "NodeID-F63677rCUsAzGWVfwqYBdtVcgNGTXs2Vj", // IP address of the node - IP: "18.144.79.215", + IP: "54.67.114.200", // SSH configuration for the node SSHConfig: SSHConfig{ User: constants.AnsibleSSHUser, @@ -42,10 +42,10 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { Roles: []SupportedRole{Validator}, } - //err = node.ProvideStakingCertAndKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) - //if err != nil { - // panic(err) - //} + err = node.ProvideStakingCertAndKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) + if err != nil { + panic(err) + } nodeID, err := ids.NodeIDFromString(node.NodeID) if err != nil { @@ -85,7 +85,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { panic(err) } - txID, err := node.AddNodeAsPrimaryNetworkValidator(avalanche.FujiNetwork(), validator, wallet) + txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validator, wallet) if err != nil { panic(err) } From d94684404b29249258c68f2de40de6887631f2b7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 19 Aug 2024 12:48:09 +0700 Subject: [PATCH 08/56] change validatorparams to primary network validator params --- node/add_validator_primary.go | 6 ++++-- node/add_validator_primary_test.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 86bcd2a..63d9a06 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -22,12 +22,14 @@ import ( "golang.org/x/net/context" ) -type ValidatorParams struct { +type PrimaryNetworkValidatorParams struct { NodeID ids.NodeID Duration time.Duration Weight uint64 + + DelegationFee uint32 } func GetMinStakingAmount(network avalanche.Network) (uint64, error) { @@ -59,7 +61,7 @@ func (h *Node) SetNodeBLSKey(signingKeyPath string) error { // and uses the wallet provided in the argument to pay for the transaction fee func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validator ValidatorParams, + validator PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { minValStake, err := GetMinStakingAmount(network) diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index b74b04a..7c27fb4 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -52,7 +52,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { panic(err) } - validator := ValidatorParams{ + validator := PrimaryNetworkValidatorParams{ NodeID: nodeID, // Validate Primary Network for 48 hours Duration: 48 * time.Hour, From bf6aa31081a2ed0facff1e05c0127b6f21b6e762 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 19 Aug 2024 16:23:12 +0700 Subject: [PATCH 09/56] update generate staking files --- node/add_validator_primary.go | 8 ++++++++ node/add_validator_primary_test.go | 2 +- node/node.go | 3 +++ node/staking.go | 16 ++++++++++++---- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 63d9a06..9cad79a 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -5,6 +5,7 @@ package node import ( "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" "os" "time" @@ -23,6 +24,7 @@ import ( ) type PrimaryNetworkValidatorParams struct { + // NodeID is the unique identifier of the node to be added as a validator on the Primary Network. NodeID ids.NodeID Duration time.Duration @@ -64,6 +66,12 @@ func (h *Node) ValidatePrimaryNetwork( validator PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { + if validator.NodeID == ids.EmptyNodeID { + return ids.Empty, subnet.ErrEmptyValidatorNodeID + } + if validator.Duration == 0 { + return ids.Empty, subnet.ErrEmptyValidatorDuration + } minValStake, err := GetMinStakingAmount(network) if err != nil { return ids.Empty, err diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index 7c27fb4..eb490fa 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -42,7 +42,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { Roles: []SupportedRole{Validator}, } - err = node.ProvideStakingCertAndKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) + err = node.ProvideStakingFiles(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) if err != nil { panic(err) } diff --git a/node/node.go b/node/node.go index ac323ad..4beeb3f 100644 --- a/node/node.go +++ b/node/node.go @@ -78,6 +78,9 @@ type Node struct { // Logger for node Logger avalanche.LeveledLogger + // BLS provides a way to aggregate signatures off chain into a single signature that can be efficiently verified on chain. + // To set BlsSecretKey of a node, use SetNodeBLSKey + // For more information about how BLS is used on the P-Chain, please head to https://docs.avax.network/cross-chain/avalanche-warp-messaging/deep-dive#bls-multi-signatures-with-public-key-aggregation BlsSecretKey *bls.SecretKey } diff --git a/node/staking.go b/node/staking.go index 2f60992..2cf085f 100644 --- a/node/staking.go +++ b/node/staking.go @@ -13,8 +13,15 @@ import ( "github.com/ava-labs/avalanchego/staking" ) -func (h *Node) ProvideStakingCertAndKey(keyPath string) error { - if nodeID, err := GenerateNodeCertAndKeys(keyPath); err != nil { +// ProvideStakingFiles generates the files needed to validate the primary network: +// - staker.crt, staker.key, more information can be found at https://docs.avax.network/nodes/validate/how-to-stake#secret-management +// - The file containing the node's BLS information: signer.key (more information can be found at https://docs.avax.network/cross-chain/avalanche-warp-messaging/deep-dive#bls-multi-signatures-with-public-key-aggregation) +// +// and stores them in the provided directory in argument in local machine +// and subsequently uploads these files into the remote host in /home/ubuntu/.avalanchego/staking/ +// directory +func (h *Node) ProvideStakingFiles(keyPath string) error { + if nodeID, err := GenerateStakingFiles(keyPath); err != nil { return err } else { h.Logger.Infof("Generated Staking Cert and Key for NodeID: %s in folder %s", nodeID.String(), keyPath) @@ -22,8 +29,9 @@ func (h *Node) ProvideStakingCertAndKey(keyPath string) error { return h.RunSSHUploadStakingFiles(keyPath) } -// GenerateNodeCertAndKeys generates a node certificate and keys and return nodeID -func GenerateNodeCertAndKeys(keyPath string) (ids.NodeID, error) { +// GenerateStakingFiles generates the following files: staker.crt, staker.key and signer.key +// and stores them in the provided directory in argument in local machine +func GenerateStakingFiles(keyPath string) (ids.NodeID, error) { if err := os.MkdirAll(keyPath, constants.DefaultPerms755); err != nil { return ids.EmptyNodeID, err } From e25876257537a9f37f41964d91066ceb03f405f1 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 20 Aug 2024 16:41:14 +0700 Subject: [PATCH 10/56] fix example --- cloud/gcp/gcp.go | 2 +- constants/constants.go | 4 +- node/add_validator_primary.go | 116 +++++++++++++++++++++-------- node/add_validator_primary_test.go | 30 +++----- node/create.go | 4 +- utils/ssh.go | 2 +- 6 files changed, 102 insertions(+), 56 deletions(-) diff --git a/cloud/gcp/gcp.go b/cloud/gcp/gcp.go index b57ee3a..584bec0 100644 --- a/cloud/gcp/gcp.go +++ b/cloud/gcp/gcp.go @@ -238,7 +238,7 @@ func (c *GcpCloud) SetupInstances( } instances := make([]*compute.Instance, numNodes) instancesChan := make(chan *compute.Instance, numNodes) - sshKey := fmt.Sprintf("%s:%s", constants.AnsibleSSHUser, strings.TrimSuffix(sshPublicKey, "\n")) + sshKey := fmt.Sprintf("%s:%s", constants.RemoteHostUser, strings.TrimSuffix(sshPublicKey, "\n")) automaticRestart := true instancePrefix := utils.RandomString(5) diff --git a/constants/constants.go b/constants/constants.go index 9b7f88d..847ac03 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -42,7 +42,7 @@ const ( SSHFileOpsTimeout = 100 * time.Second SSHPOSTTimeout = 10 * time.Second SSHScriptTimeout = 2 * time.Minute - AnsibleSSHUser = "ubuntu" + RemoteHostUser = "ubuntu" // node CloudNodeCLIConfigBasePath = "/home/ubuntu/.avalanche-cli/" @@ -52,7 +52,7 @@ const ( CloudNodeConfigPath = "/home/ubuntu/.avalanchego/configs/" ServicesDir = "services" DashboardsDir = "dashboards" - + LocalTmpDir = ".avalanche-tooling-sdk-go" // services ServiceAvalanchego = "avalanchego" ServicePromtail = "promtail" diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 9cad79a..022809d 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -5,10 +5,14 @@ package node import ( "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" "os" + "os/user" + "path/filepath" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" "github.com/ava-labs/avalanchego/utils/crypto/bls" @@ -27,37 +31,22 @@ type PrimaryNetworkValidatorParams struct { // NodeID is the unique identifier of the node to be added as a validator on the Primary Network. NodeID ids.NodeID + // Duration is how long the node will be staking the Primary Network + // Duration has to be greater than or equal to minimum duration for the specified network + // (Fuji / Mainnet) Duration time.Duration - Weight uint64 + // StakeAmount is the amount of Avalanche tokens (AVAX) to stake in this validator + // StakeAmount is in the amount of nAVAX + // StakeAmount has to be greater than or equal to minimum stake required for the specified network + StakeAmount uint64 + // DelegationFee is the percent fee this validator will charge when others delegate stake to it + // When DelegationFee is not set, the minimum delegation fee for the specified network will be set + // For more information on delegation fee, please head to https://docs.avax.network/nodes/validate/node-validator#delegation-fee-rate DelegationFee uint32 } -func GetMinStakingAmount(network avalanche.Network) (uint64, error) { - pClient := platformvm.NewClient(network.Endpoint) - ctx, cancel := utils.GetAPIContext() - defer cancel() - minValStake, _, err := pClient.GetMinStake(ctx, ids.Empty) - if err != nil { - return 0, err - } - return minValStake, nil -} - -func (h *Node) SetNodeBLSKey(signingKeyPath string) error { - blsKeyBytes, err := os.ReadFile(signingKeyPath) - if err != nil { - return err - } - blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) - if err != nil { - return err - } - h.BlsSecretKey = blsSk - return nil -} - // ValidatePrimaryNetwork adds node as primary network validator. // It adds the node in the specified network (Fuji / Mainnet / Devnet) // and uses the wallet provided in the argument to pay for the transaction fee @@ -69,19 +58,27 @@ func (h *Node) ValidatePrimaryNetwork( if validator.NodeID == ids.EmptyNodeID { return ids.Empty, subnet.ErrEmptyValidatorNodeID } + if validator.Duration == 0 { return ids.Empty, subnet.ErrEmptyValidatorDuration } + minValStake, err := GetMinStakingAmount(network) if err != nil { return ids.Empty, err } - if validator.Weight < minValStake { - return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validator.Weight) + if validator.StakeAmount < minValStake { + return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validator.StakeAmount) } - delegationFee := network.GenesisParams().MinDelegationFee + if validator.DelegationFee == 0 { + validator.DelegationFee = network.GenesisParams().MinDelegationFee + } + + if err = h.HandleBLSKey(); err != nil { + return ids.Empty, fmt.Errorf("unable to set BLS key of node due to %w", err) + } wallet.SetSubnetAuthMultisig([]ids.ShortID{}) @@ -103,7 +100,7 @@ func (h *Node) ValidatePrimaryNetwork( Validator: txs.Validator{ NodeID: nodeID, End: uint64(time.Now().Add(validator.Duration).Unix()), - Wght: validator.Weight, + Wght: validator.StakeAmount, }, Subnet: ids.Empty, }, @@ -111,7 +108,7 @@ func (h *Node) ValidatePrimaryNetwork( wallet.P().Builder().Context().AVAXAssetID, owner, owner, - delegationFee, + validator.DelegationFee, ) if err != nil { return ids.Empty, fmt.Errorf("error building tx: %w", err) @@ -140,3 +137,60 @@ func (h *Node) ValidatePrimaryNetwork( return tx.ID(), nil } + +func GetMinStakingAmount(network avalanche.Network) (uint64, error) { + pClient := platformvm.NewClient(network.Endpoint) + ctx, cancel := utils.GetAPIContext() + defer cancel() + minValStake, _, err := pClient.GetMinStake(ctx, ids.Empty) + if err != nil { + return 0, err + } + return minValStake, nil +} + +func (h *Node) SetNodeBLSKey(signingKeyPath string) error { + blsKeyBytes, err := os.ReadFile(signingKeyPath) + if err != nil { + return err + } + blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) + if err != nil { + return err + } + h.BlsSecretKey = blsSk + return nil +} + +func RemoveTmpSDKDir() error { + usr, err := user.Current() + if err != nil { + return fmt.Errorf("unable to get system user %s", err) + } + return os.RemoveAll(filepath.Join(usr.HomeDir, constants.LocalTmpDir)) +} + +func (h *Node) GetBLSKeyFromRemoteHost() error { + usr, err := user.Current() + if err != nil { + return fmt.Errorf("unable to get system user %s", err) + } + filePath := filepath.Join(constants.CloudNodeStakingPath, constants.BLSKeyFileName) + localFilePath := filepath.Join(usr.HomeDir, constants.LocalTmpDir, h.NodeID, constants.BLSKeyFileName) + return h.Download(filePath, localFilePath, constants.SSHFileOpsTimeout) +} + +// HandleBLSKey gets BLS information from remote host and sets the BlsSecretKey value in Node object +func (h *Node) HandleBLSKey() error { + if err := h.GetBLSKeyFromRemoteHost(); err != nil { + return err + } + usr, err := user.Current() + if err != nil { + return fmt.Errorf("unable to get system user %s", err) + } + if err := h.SetNodeBLSKey(filepath.Join(usr.HomeDir, constants.LocalTmpDir, h.NodeID, constants.BLSKeyFileName)); err != nil { + return err + } + return RemoveTmpSDKDir() +} diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index eb490fa..8a1940b 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -6,15 +6,17 @@ package node import ( "context" "fmt" + "testing" + "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary" - "testing" - "time" ) func TestNodesValidatePrimaryNetwork(_ *testing.T) { @@ -26,13 +28,13 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { node := Node{ // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-F63677rCUsAzGWVfwqYBdtVcgNGTXs2Vj", + NodeID: "NODE_ID", // IP address of the node - IP: "54.67.114.200", + IP: "NODE_IP_ADDRESS", // SSH configuration for the node SSHConfig: SSHConfig{ - User: constants.AnsibleSSHUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + User: constants.RemoteHostUser, + PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", }, // Cloud is the cloud service that the node is on Cloud: AWSCloud, @@ -42,11 +44,6 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { Roles: []SupportedRole{Validator}, } - err = node.ProvideStakingFiles(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s", node.NodeID)) - if err != nil { - panic(err) - } - nodeID, err := ids.NodeIDFromString(node.NodeID) if err != nil { panic(err) @@ -56,13 +53,13 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { NodeID: nodeID, // Validate Primary Network for 48 hours Duration: 48 * time.Hour, - // 1 billion in weight is equivalent to 1 AVAX - Weight: 2000000000, + // Stake 2 AVAX + StakeAmount: 2 * units.Avax, } network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) if err != nil { panic(err) } @@ -80,11 +77,6 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { panic(err) } - err = node.SetNodeBLSKey(fmt.Sprintf("/Users/raymondsukanto/.avalanche-cli/nodes/%s/signer.key", node.NodeID)) - if err != nil { - panic(err) - } - txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validator, wallet) if err != nil { panic(err) diff --git a/node/create.go b/node/create.go index e02b13c..218e377 100644 --- a/node/create.go +++ b/node/create.go @@ -187,7 +187,7 @@ func createCloudInstances(ctx context.Context, cp CloudParams, count int, useSta Cloud: cp.Cloud(), CloudConfig: cp, SSHConfig: SSHConfig{ - User: constants.AnsibleSSHUser, + User: constants.RemoteHostUser, PrivateKeyPath: sshPrivateKeyPath, }, Roles: nil, @@ -233,7 +233,7 @@ func createCloudInstances(ctx context.Context, cp CloudParams, count int, useSta Cloud: cp.Cloud(), CloudConfig: cp, SSHConfig: SSHConfig{ - User: constants.AnsibleSSHUser, + User: constants.RemoteHostUser, PrivateKeyPath: sshPrivateKeyPath, }, Roles: nil, diff --git a/utils/ssh.go b/utils/ssh.go index fd9ca41..35799c7 100644 --- a/utils/ssh.go +++ b/utils/ssh.go @@ -20,7 +20,7 @@ func GetSCPTargetPath(ip, path string) string { if ip == "" { return path } - return fmt.Sprintf("%s@%s:%s", constants.AnsibleSSHUser, ip, path) + return fmt.Sprintf("%s@%s:%s", constants.RemoteHostUser, ip, path) } // SplitSCPPath splits the given path into node and path. From b85398d091520a72acb028a7e526573746fcdce4 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 20 Aug 2024 16:41:50 +0700 Subject: [PATCH 11/56] fix example --- node/cloud.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/cloud.go b/node/cloud.go index f947e4a..fe49128 100644 --- a/node/cloud.go +++ b/node/cloud.go @@ -122,7 +122,7 @@ func GetDefaultCloudParams(ctx context.Context, cloud SupportedCloud) (*CloudPar AWSVolumeIOPS: 1000, AWSVolumeType: "gp3", }, - Region: "us-west-1", + Region: "us-east-1", InstanceType: constants.AWSDefaultInstanceType, } awsSvc, err := awsAPI.NewAwsCloud(ctx, cp.AWSConfig.AWSProfile, cp.Region) From f9e1c492380160d51587307ee6f9f27b07334da2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 20 Aug 2024 16:43:16 +0700 Subject: [PATCH 12/56] fix example --- node/add_validator_primary.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 022809d..26cc936 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -165,7 +165,7 @@ func (h *Node) SetNodeBLSKey(signingKeyPath string) error { func RemoveTmpSDKDir() error { usr, err := user.Current() if err != nil { - return fmt.Errorf("unable to get system user %s", err) + return fmt.Errorf("unable to get system user %w", err) } return os.RemoveAll(filepath.Join(usr.HomeDir, constants.LocalTmpDir)) } @@ -173,7 +173,7 @@ func RemoveTmpSDKDir() error { func (h *Node) GetBLSKeyFromRemoteHost() error { usr, err := user.Current() if err != nil { - return fmt.Errorf("unable to get system user %s", err) + return fmt.Errorf("unable to get system user %w", err) } filePath := filepath.Join(constants.CloudNodeStakingPath, constants.BLSKeyFileName) localFilePath := filepath.Join(usr.HomeDir, constants.LocalTmpDir, h.NodeID, constants.BLSKeyFileName) @@ -187,7 +187,7 @@ func (h *Node) HandleBLSKey() error { } usr, err := user.Current() if err != nil { - return fmt.Errorf("unable to get system user %s", err) + return fmt.Errorf("unable to get system user %w", err) } if err := h.SetNodeBLSKey(filepath.Join(usr.HomeDir, constants.LocalTmpDir, h.NodeID, constants.BLSKeyFileName)); err != nil { return err From 4e05bf811a3dfb973a42defcea6ad1f6717eedfd Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 20 Aug 2024 16:50:19 +0700 Subject: [PATCH 13/56] fix example --- node/add_validator_primary.go | 1 - 1 file changed, 1 deletion(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 26cc936..6b02328 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -125,7 +125,6 @@ func (h *Node) ValidatePrimaryNetwork( &tx, common.WithContext(ctx), ) - if err != nil { if ctx.Err() != nil { err = fmt.Errorf("timeout issuing/verifying tx with ID %s: %w", tx.ID(), err) From 71868f00d70075e48a7264a5393945c7d78ebcf3 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 21 Aug 2024 22:38:01 +0700 Subject: [PATCH 14/56] add subnet validator --- node/add_validator_primary.go | 24 +----- node/add_validator_primary_test.go | 4 +- subnet/add_validator_subnet.go | 13 +--- subnet/add_validator_subnet_test.go | 112 ++++++++++++++++++++++++++++ subnet/deploy_subnet.go | 2 +- subnet/subnet_test.go | 82 -------------------- validator/validator.go | 41 ++++++++++ 7 files changed, 163 insertions(+), 115 deletions(-) create mode 100644 subnet/add_validator_subnet_test.go create mode 100644 validator/validator.go diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 6b02328..5a5aee0 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -10,6 +10,8 @@ import ( "path/filepath" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" @@ -27,32 +29,12 @@ import ( "golang.org/x/net/context" ) -type PrimaryNetworkValidatorParams struct { - // NodeID is the unique identifier of the node to be added as a validator on the Primary Network. - NodeID ids.NodeID - - // Duration is how long the node will be staking the Primary Network - // Duration has to be greater than or equal to minimum duration for the specified network - // (Fuji / Mainnet) - Duration time.Duration - - // StakeAmount is the amount of Avalanche tokens (AVAX) to stake in this validator - // StakeAmount is in the amount of nAVAX - // StakeAmount has to be greater than or equal to minimum stake required for the specified network - StakeAmount uint64 - - // DelegationFee is the percent fee this validator will charge when others delegate stake to it - // When DelegationFee is not set, the minimum delegation fee for the specified network will be set - // For more information on delegation fee, please head to https://docs.avax.network/nodes/validate/node-validator#delegation-fee-rate - DelegationFee uint32 -} - // ValidatePrimaryNetwork adds node as primary network validator. // It adds the node in the specified network (Fuji / Mainnet / Devnet) // and uses the wallet provided in the argument to pay for the transaction fee func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validator PrimaryNetworkValidatorParams, + validator validator.PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { if validator.NodeID == ids.EmptyNodeID { diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index 8a1940b..6a882e4 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" @@ -49,7 +51,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { panic(err) } - validator := PrimaryNetworkValidatorParams{ + validator := validator.PrimaryNetworkValidatorParams{ NodeID: nodeID, // Validate Primary Network for 48 hours Duration: 48 * time.Hour, diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index f18838e..2c8c876 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -6,6 +6,7 @@ package subnet import ( "errors" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "time" "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" @@ -15,14 +16,6 @@ import ( "golang.org/x/net/context" ) -type ValidatorParams struct { - NodeID ids.NodeID - - Duration time.Duration - - Weight uint64 -} - var ( ErrEmptyValidatorNodeID = errors.New("validator node id is not provided") ErrEmptyValidatorDuration = errors.New("validator duration is not provided") @@ -34,7 +27,7 @@ var ( // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be // tracking the subnet // TODO: add more description once node join subnet sdk is done -func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput ValidatorParams) (*multisig.Multisig, error) { +func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput validator.SubnetValidatorParams) (*multisig.Multisig, error) { if validatorInput.NodeID == ids.EmptyNodeID { return nil, ErrEmptyValidatorNodeID } @@ -48,7 +41,7 @@ func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput ValidatorPara return nil, ErrEmptySubnetID } - wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) + wallet.SetSubnetAuthMultisig([]ids.ShortID{}) validator := &txs.SubnetValidator{ Validator: txs.Validator{ diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go new file mode 100644 index 0000000..092dc00 --- /dev/null +++ b/subnet/add_validator_subnet_test.go @@ -0,0 +1,112 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package subnet + +import ( + "context" + "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "testing" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/set" + + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +func TestValidateSubnet(t *testing.T) { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + + newSubnet, err := New(&subnetParams) + if err != nil { + panic(err) + } + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + time.Sleep(2 * time.Second) + + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validator := validator.SubnetValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + if err != nil { + panic(err) + } + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} diff --git a/subnet/deploy_subnet.go b/subnet/deploy_subnet.go index 9d7ea65..9ac1865 100644 --- a/subnet/deploy_subnet.go +++ b/subnet/deploy_subnet.go @@ -61,7 +61,7 @@ func (c *Subnet) CreateBlockchainTx(wallet wallet.Wallet) (*multisig.Multisig, e if c.Name == "" { return nil, fmt.Errorf("subnet name is not provided") } - wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) + wallet.SetSubnetAuthMultisig([]ids.ShortID{}) // create tx fxIDs := make([]ids.ID, 0) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 8b9d1b1..e4d4897 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,8 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -229,83 +227,3 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } - -func TestValidateSubnet(t *testing.T) { - ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } - - subnetParams := SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", - } - newSubnet, err := New(&subnetParams) - if err != nil { - panic(err) - } - node := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.AnsibleSSHUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - - network := avalanche.FujiNetwork() - - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) - if err != nil { - panic(err) - } - - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - - wallet, err := wallet.New( - context.Background(), - &primary.WalletConfig{ - URI: network.Endpoint, - AVAXKeychain: keychain.Keychain, - EthKeychain: secp256k1fx.NewKeychain(), - PChainTxsToFetch: set.Of(subnetID), - }, - ) - if err != nil { - panic(err) - } - - nodeID, err := ids.NodeIDFromString(node.NodeID) - if err != nil { - panic(err) - } - - validator := ValidatorParams{ - NodeID: nodeID, - // Validate Primary Network for 48 hours - Duration: 48 * time.Hour, - Weight: 20, - } - fmt.Printf("adding subnet validator") - - newSubnet.SetSubnetID(subnetID) - addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - if err != nil { - panic(err) - } - txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - if err != nil { - panic(err) - } - fmt.Printf("obtained tx id %s", txID.String()) -} diff --git a/validator/validator.go b/validator/validator.go new file mode 100644 index 0000000..4aa76ec --- /dev/null +++ b/validator/validator.go @@ -0,0 +1,41 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package validator + +import ( + "time" + + "github.com/ava-labs/avalanchego/ids" +) + +type PrimaryNetworkValidatorParams struct { + // NodeID is the unique identifier of the node to be added as a validator on the Primary Network. + NodeID ids.NodeID + + // Duration is how long the node will be staking the Primary Network + // Duration has to be greater than or equal to minimum duration for the specified network + // (Fuji / Mainnet) + Duration time.Duration + + // StakeAmount is the amount of Avalanche tokens (AVAX) to stake in this validator + // StakeAmount is in the amount of nAVAX + // StakeAmount has to be greater than or equal to minimum stake required for the specified network + StakeAmount uint64 + + // DelegationFee is the percent fee this validator will charge when others delegate stake to it + // When DelegationFee is not set, the minimum delegation fee for the specified network will be set + // For more information on delegation fee, please head to https://docs.avax.network/nodes/validate/node-validator#delegation-fee-rate + DelegationFee uint32 +} + +type SubnetValidatorParams struct { + // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. + NodeID ids.NodeID + // Duration is how long the node will be staking the Subnet + // Duration has to be less than or equal to the duration that the node will be validating the Primary + // Network + Duration time.Duration + // Weight is the validator's weight when sampling validators. + Weight uint64 +} From 3e16c3200ec6f9555d0e29c4957de6de10a51580 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 14:43:06 +0700 Subject: [PATCH 15/56] revert SetSubnetAuthMultisig --- subnet/add_validator_subnet.go | 2 +- subnet/deploy_subnet.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 2c8c876..bb6193c 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -41,7 +41,7 @@ func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput validator.Sub return nil, ErrEmptySubnetID } - wallet.SetSubnetAuthMultisig([]ids.ShortID{}) + wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) validator := &txs.SubnetValidator{ Validator: txs.Validator{ diff --git a/subnet/deploy_subnet.go b/subnet/deploy_subnet.go index 9ac1865..9d7ea65 100644 --- a/subnet/deploy_subnet.go +++ b/subnet/deploy_subnet.go @@ -61,7 +61,7 @@ func (c *Subnet) CreateBlockchainTx(wallet wallet.Wallet) (*multisig.Multisig, e if c.Name == "" { return nil, fmt.Errorf("subnet name is not provided") } - wallet.SetSubnetAuthMultisig([]ids.ShortID{}) + wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) // create tx fxIDs := make([]ids.ID, 0) From c7f74d40189f18602291e5a3e34bd82a9be97546 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:08:41 +0700 Subject: [PATCH 16/56] address comments --- node/add_validator_primary.go | 35 ++++++++--------------------------- node/config/avalanche.go | 4 ++++ 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 6b02328..a47e4e3 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -6,10 +6,10 @@ package node import ( "fmt" "os" - "os/user" - "path/filepath" "time" + remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" @@ -76,7 +76,7 @@ func (h *Node) ValidatePrimaryNetwork( validator.DelegationFee = network.GenesisParams().MinDelegationFee } - if err = h.HandleBLSKey(); err != nil { + if err = h.GetBLSKeyFromRemoteHost(); err != nil { return ids.Empty, fmt.Errorf("unable to set BLS key of node due to %w", err) } @@ -161,35 +161,16 @@ func (h *Node) SetNodeBLSKey(signingKeyPath string) error { return nil } -func RemoveTmpSDKDir() error { - usr, err := user.Current() - if err != nil { - return fmt.Errorf("unable to get system user %w", err) - } - return os.RemoveAll(filepath.Join(usr.HomeDir, constants.LocalTmpDir)) -} - +// GetBLSKeyFromRemoteHost gets BLS information from remote host and sets the BlsSecretKey value in Node object func (h *Node) GetBLSKeyFromRemoteHost() error { - usr, err := user.Current() + blsKeyBytes, err := h.ReadFileBytes(remoteconfig.GetRemoteBLSKeyFile(), constants.SSHFileOpsTimeout) if err != nil { - return fmt.Errorf("unable to get system user %w", err) - } - filePath := filepath.Join(constants.CloudNodeStakingPath, constants.BLSKeyFileName) - localFilePath := filepath.Join(usr.HomeDir, constants.LocalTmpDir, h.NodeID, constants.BLSKeyFileName) - return h.Download(filePath, localFilePath, constants.SSHFileOpsTimeout) -} - -// HandleBLSKey gets BLS information from remote host and sets the BlsSecretKey value in Node object -func (h *Node) HandleBLSKey() error { - if err := h.GetBLSKeyFromRemoteHost(); err != nil { return err } - usr, err := user.Current() + blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) if err != nil { - return fmt.Errorf("unable to get system user %w", err) - } - if err := h.SetNodeBLSKey(filepath.Join(usr.HomeDir, constants.LocalTmpDir, h.NodeID, constants.BLSKeyFileName)); err != nil { return err } - return RemoveTmpSDKDir() + h.BlsSecretKey = blsSk + return nil } diff --git a/node/config/avalanche.go b/node/config/avalanche.go index d3a6482..73516d7 100644 --- a/node/config/avalanche.go +++ b/node/config/avalanche.go @@ -75,6 +75,10 @@ func RenderAvalancheCChainConfig(config AvalancheConfigInputs) ([]byte, error) { } } +func GetRemoteBLSKeyFile() string { + return filepath.Join(constants.CloudNodeStakingPath, constants.BLSKeyFileName) +} + func GetRemoteAvalancheNodeConfig() string { return filepath.Join(constants.CloudNodeConfigPath, "node.json") } From 2041101c111f2f62bc7f04ce75811e4ad5f1db39 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:09:13 +0700 Subject: [PATCH 17/56] remove unused constants --- constants/constants.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/constants/constants.go b/constants/constants.go index 847ac03..70e8ee4 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -45,14 +45,11 @@ const ( RemoteHostUser = "ubuntu" // node - CloudNodeCLIConfigBasePath = "/home/ubuntu/.avalanche-cli/" - CloudNodeConfigBasePath = "/home/ubuntu/.avalanchego/" - CloudNodeSubnetVMBinaryPath = "/home/ubuntu/.avalanchego/plugins/%s" - CloudNodeStakingPath = "/home/ubuntu/.avalanchego/staking/" - CloudNodeConfigPath = "/home/ubuntu/.avalanchego/configs/" - ServicesDir = "services" - DashboardsDir = "dashboards" - LocalTmpDir = ".avalanche-tooling-sdk-go" + CloudNodeCLIConfigBasePath = "/home/ubuntu/.avalanche-cli/" + CloudNodeStakingPath = "/home/ubuntu/.avalanchego/staking/" + CloudNodeConfigPath = "/home/ubuntu/.avalanchego/configs/" + ServicesDir = "services" + DashboardsDir = "dashboards" // services ServiceAvalanchego = "avalanchego" ServicePromtail = "promtail" From 424a6f323eda346e5b01d7ae59a5ef912b0fe175 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:20:18 +0700 Subject: [PATCH 18/56] update example --- avalanche/network.go | 14 ++++++++++ node/add_validator_primary.go | 50 +++++++++-------------------------- node/node.go | 1 - 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/avalanche/network.go b/avalanche/network.go index c8b9a5d..c996520 100644 --- a/avalanche/network.go +++ b/avalanche/network.go @@ -4,8 +4,11 @@ package avalanche import ( + "github.com/ava-labs/avalanche-tooling-sdk-go/utils" "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/platformvm" ) type NetworkKind int64 @@ -90,3 +93,14 @@ func (n Network) GenesisParams() *genesis.Params { } return nil } + +func (n Network) GetMinStakingAmount() (uint64, error) { + pClient := platformvm.NewClient(n.Endpoint) + ctx, cancel := utils.GetAPIContext() + defer cancel() + minValStake, _, err := pClient.GetMinStake(ctx, ids.Empty) + if err != nil { + return 0, err + } + return minValStake, nil +} diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index a47e4e3..590baa1 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -5,7 +5,6 @@ package node import ( "fmt" - "os" "time" remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" @@ -16,7 +15,6 @@ import ( "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" @@ -52,32 +50,32 @@ type PrimaryNetworkValidatorParams struct { // and uses the wallet provided in the argument to pay for the transaction fee func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validator PrimaryNetworkValidatorParams, + validatorParams PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { - if validator.NodeID == ids.EmptyNodeID { + if validatorParams.NodeID == ids.EmptyNodeID { return ids.Empty, subnet.ErrEmptyValidatorNodeID } - if validator.Duration == 0 { + if validatorParams.Duration == 0 { return ids.Empty, subnet.ErrEmptyValidatorDuration } - minValStake, err := GetMinStakingAmount(network) + minValStake, err := network.GetMinStakingAmount() if err != nil { return ids.Empty, err } - if validator.StakeAmount < minValStake { - return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validator.StakeAmount) + if validatorParams.StakeAmount < minValStake { + return ids.Empty, fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", minValStake, validatorParams.StakeAmount) } - if validator.DelegationFee == 0 { - validator.DelegationFee = network.GenesisParams().MinDelegationFee + if validatorParams.DelegationFee == 0 { + validatorParams.DelegationFee = network.GenesisParams().MinDelegationFee } if err = h.GetBLSKeyFromRemoteHost(); err != nil { - return ids.Empty, fmt.Errorf("unable to set BLS key of node due to %w", err) + return ids.Empty, fmt.Errorf("unable to set BLS key of node from remote host due to %w", err) } wallet.SetSubnetAuthMultisig([]ids.ShortID{}) @@ -99,8 +97,8 @@ func (h *Node) ValidatePrimaryNetwork( &txs.SubnetValidator{ Validator: txs.Validator{ NodeID: nodeID, - End: uint64(time.Now().Add(validator.Duration).Unix()), - Wght: validator.StakeAmount, + End: uint64(time.Now().Add(validatorParams.Duration).Unix()), + Wght: validatorParams.StakeAmount, }, Subnet: ids.Empty, }, @@ -108,7 +106,7 @@ func (h *Node) ValidatePrimaryNetwork( wallet.P().Builder().Context().AVAXAssetID, owner, owner, - validator.DelegationFee, + validatorParams.DelegationFee, ) if err != nil { return ids.Empty, fmt.Errorf("error building tx: %w", err) @@ -137,30 +135,6 @@ func (h *Node) ValidatePrimaryNetwork( return tx.ID(), nil } -func GetMinStakingAmount(network avalanche.Network) (uint64, error) { - pClient := platformvm.NewClient(network.Endpoint) - ctx, cancel := utils.GetAPIContext() - defer cancel() - minValStake, _, err := pClient.GetMinStake(ctx, ids.Empty) - if err != nil { - return 0, err - } - return minValStake, nil -} - -func (h *Node) SetNodeBLSKey(signingKeyPath string) error { - blsKeyBytes, err := os.ReadFile(signingKeyPath) - if err != nil { - return err - } - blsSk, err := bls.SecretKeyFromBytes(blsKeyBytes) - if err != nil { - return err - } - h.BlsSecretKey = blsSk - return nil -} - // GetBLSKeyFromRemoteHost gets BLS information from remote host and sets the BlsSecretKey value in Node object func (h *Node) GetBLSKeyFromRemoteHost() error { blsKeyBytes, err := h.ReadFileBytes(remoteconfig.GetRemoteBLSKeyFile(), constants.SSHFileOpsTimeout) diff --git a/node/node.go b/node/node.go index 4beeb3f..1007e05 100644 --- a/node/node.go +++ b/node/node.go @@ -79,7 +79,6 @@ type Node struct { Logger avalanche.LeveledLogger // BLS provides a way to aggregate signatures off chain into a single signature that can be efficiently verified on chain. - // To set BlsSecretKey of a node, use SetNodeBLSKey // For more information about how BLS is used on the P-Chain, please head to https://docs.avax.network/cross-chain/avalanche-warp-messaging/deep-dive#bls-multi-signatures-with-public-key-aggregation BlsSecretKey *bls.SecretKey } From 75133a9dff55ade9715934e5b3d8bda3e7fe0b1b Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:26:22 +0700 Subject: [PATCH 19/56] fix lint --- subnet/add_validator_subnet.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index bb6193c..392efc4 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -6,9 +6,10 @@ package subnet import ( "errors" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" From 53aeb738ccd760813e3038d8ebc0d1a82029f669 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:30:36 +0700 Subject: [PATCH 20/56] fix merge --- validator/validator.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/validator.go b/validator/validator.go index 4aa76ec..0587cef 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -37,5 +37,6 @@ type SubnetValidatorParams struct { // Network Duration time.Duration // Weight is the validator's weight when sampling validators. + // Weight for subnet validators is set to 20 by default Weight uint64 } From d295cf504df9b229fd36ed639e232c24dd4d4888 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:31:12 +0700 Subject: [PATCH 21/56] fix merge --- node/add_validator_primary.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 81b7934..49e8f14 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -5,6 +5,7 @@ package node import ( "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "time" remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" @@ -30,7 +31,7 @@ import ( // and uses the wallet provided in the argument to pay for the transaction fee func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validatorParams PrimaryNetworkValidatorParams, + validatorParams validator.PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { if validatorParams.NodeID == ids.EmptyNodeID { From d0ddb61cde089c1f27c6cd0520b9bb272c901706 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:35:25 +0700 Subject: [PATCH 22/56] fix merge --- node/add_validator_primary.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index 49e8f14..bb4609a 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -5,9 +5,10 @@ package node import ( "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" From a638db799076ebe145f4ff424419941f2e1c1ad5 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:41:03 +0700 Subject: [PATCH 23/56] move validator --- {validator => avalanche}/validator.go | 2 +- node/add_validator_primary.go | 4 +--- node/add_validator_primary_test.go | 4 +--- subnet/add_validator_subnet.go | 13 +++++++------ subnet/add_validator_subnet_test.go | 3 +-- 5 files changed, 11 insertions(+), 15 deletions(-) rename {validator => avalanche}/validator.go (98%) diff --git a/validator/validator.go b/avalanche/validator.go similarity index 98% rename from validator/validator.go rename to avalanche/validator.go index 0587cef..bdf65bc 100644 --- a/validator/validator.go +++ b/avalanche/validator.go @@ -1,7 +1,7 @@ // Copyright (C) 2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package validator +package avalanche import ( "time" diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index bb4609a..b83c236 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -7,8 +7,6 @@ import ( "fmt" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" - remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" @@ -32,7 +30,7 @@ import ( // and uses the wallet provided in the argument to pay for the transaction fee func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validatorParams validator.PrimaryNetworkValidatorParams, + validatorParams avalanche.PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { if validatorParams.NodeID == ids.EmptyNodeID { diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index 6a882e4..7cc76ff 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" - "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" @@ -51,7 +49,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { panic(err) } - validator := validator.PrimaryNetworkValidatorParams{ + validator := avalanche.PrimaryNetworkValidatorParams{ NodeID: nodeID, // Validate Primary Network for 48 hours Duration: 48 * time.Hour, diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 392efc4..da65119 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -6,10 +6,9 @@ package subnet import ( "errors" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" - "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" @@ -20,15 +19,14 @@ import ( var ( ErrEmptyValidatorNodeID = errors.New("validator node id is not provided") ErrEmptyValidatorDuration = errors.New("validator duration is not provided") - ErrEmptyValidatorWeight = errors.New("validator weight is not provided") ErrEmptySubnetID = errors.New("subnet ID is not provided") + ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) // AddValidator adds validator to subnet // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be // tracking the subnet -// TODO: add more description once node join subnet sdk is done -func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput validator.SubnetValidatorParams) (*multisig.Multisig, error) { +func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput avalanche.SubnetValidatorParams) (*multisig.Multisig, error) { if validatorInput.NodeID == ids.EmptyNodeID { return nil, ErrEmptyValidatorNodeID } @@ -36,11 +34,14 @@ func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput validator.Sub return nil, ErrEmptyValidatorDuration } if validatorInput.Weight == 0 { - return nil, ErrEmptyValidatorWeight + validatorInput.Weight = 20 } if c.SubnetID == ids.Empty { return nil, ErrEmptySubnetID } + if len(c.DeployInfo.SubnetAuthKeys) == 0 { + return nil, ErrEmptySubnetAuth + } wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 092dc00..3c69c57 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -6,7 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "testing" "time" @@ -91,7 +90,7 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - validator := validator.SubnetValidatorParams{ + validator := avalanche.SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, From 87c9443482ab7094aa58ce8dcf3a1c14f2928052 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 16:43:38 +0700 Subject: [PATCH 24/56] move subnet validator --- avalanche/validator.go | 12 ------------ subnet/add_validator_subnet.go | 17 ++++++++++++++--- subnet/add_validator_subnet_test.go | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/avalanche/validator.go b/avalanche/validator.go index bdf65bc..a85f701 100644 --- a/avalanche/validator.go +++ b/avalanche/validator.go @@ -28,15 +28,3 @@ type PrimaryNetworkValidatorParams struct { // For more information on delegation fee, please head to https://docs.avax.network/nodes/validate/node-validator#delegation-fee-rate DelegationFee uint32 } - -type SubnetValidatorParams struct { - // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. - NodeID ids.NodeID - // Duration is how long the node will be staking the Subnet - // Duration has to be less than or equal to the duration that the node will be validating the Primary - // Network - Duration time.Duration - // Weight is the validator's weight when sampling validators. - // Weight for subnet validators is set to 20 by default - Weight uint64 -} diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index da65119..2edaed0 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -6,7 +6,6 @@ package subnet import ( "errors" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "time" "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" @@ -23,10 +22,22 @@ var ( ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) +type SubnetValidatorParams struct { + // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. + NodeID ids.NodeID + // Duration is how long the node will be staking the Subnet + // Duration has to be less than or equal to the duration that the node will be validating the Primary + // Network + Duration time.Duration + // Weight is the validator's weight when sampling validators. + // Weight for subnet validators is set to 20 by default + Weight uint64 +} + // AddValidator adds validator to subnet // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be -// tracking the subnet -func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput avalanche.SubnetValidatorParams) (*multisig.Multisig, error) { +// tracking the subnet, which can be done by calling SyncSubnets in node package +func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { if validatorInput.NodeID == ids.EmptyNodeID { return nil, ErrEmptyValidatorNodeID } diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 3c69c57..8d315a4 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -90,7 +90,7 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - validator := avalanche.SubnetValidatorParams{ + validator := SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, From e59ca3ef9ed9f2b670e6ef7cc3d11eb77e0428f1 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:23:05 +0700 Subject: [PATCH 25/56] update example --- examples/subnet.go | 8 ++++---- examples/subnet_ multisig.go | 4 ++-- subnet/add_validator_subnet_test.go | 4 ++++ subnet/subnet.go | 10 ++++++++-- subnet/subnet_test.go | 12 ++++++------ 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/examples/subnet.go b/examples/subnet.go index f197b3c..4a055ab 100644 --- a/examples/subnet.go +++ b/examples/subnet.go @@ -67,7 +67,7 @@ func DeploySubnet() { // can be committed on chain subnetAuthKeys := keychain.Addresses().List() threshold := 1 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) wallet, _ := wallet.New( context.Background(), @@ -86,7 +86,7 @@ func DeploySubnet() { // we need to wait to allow the transaction to reach other nodes in Fuji time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) deployChainTx, _ := newSubnet.CreateBlockchainTx(wallet) // since we are using the fee paying key as control key too, we can commit the transaction // on chain immediately since the number of signatures has been reached @@ -139,7 +139,7 @@ func DeploySubnetWithLedger() { controlKeys := addressesIDs subnetAuthKeys := addressesIDs threshold := 1 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) // Pay and Sign CreateSubnet Tx with fee paying key A using Ledger deploySubnetTx, _ := newSubnet.CreateSubnetTx(walletA) @@ -149,7 +149,7 @@ func DeploySubnetWithLedger() { // we need to wait to allow the transaction to reach other nodes in Fuji time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) // Pay and sign CreateChain Tx with fee paying key A using Ledger deployChainTx, _ := newSubnet.CreateBlockchainTx(walletA) diff --git a/examples/subnet_ multisig.go b/examples/subnet_ multisig.go index 21151b4..5486754 100644 --- a/examples/subnet_ multisig.go +++ b/examples/subnet_ multisig.go @@ -55,7 +55,7 @@ func DeploySubnetMultiSig() { // at least two signatures are required to be able to send the CreateChain transaction on-chain // note that threshold does not apply to CreateSubnet transaction threshold := 2 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) // Key A will be used for paying the transaction fees of CreateSubnetTx and CreateChainTx walletA, _ := wallet.New( @@ -75,7 +75,7 @@ func DeploySubnetMultiSig() { // we need to wait to allow the transaction to reach other nodes in Fuji time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) deployChainTx, err := newSubnet.CreateBlockchainTx(walletA) if err != nil { fmt.Errorf("error signing tx walletA: %w", err) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 8d315a4..33482ee 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -38,6 +38,7 @@ func TestValidateSubnet(t *testing.T) { if err != nil { panic(err) } + node := node.Node{ // NodeID is Avalanche Node ID of the node NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", @@ -99,6 +100,9 @@ func TestValidateSubnet(t *testing.T) { fmt.Printf("adding subnet validator") newSubnet.SetSubnetID(subnetID) + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) if err != nil { panic(err) diff --git a/subnet/subnet.go b/subnet/subnet.go index 8b5a526..aa359e6 100644 --- a/subnet/subnet.go +++ b/subnet/subnet.go @@ -114,12 +114,18 @@ func (c *Subnet) SetParams(controlKeys []ids.ShortID, subnetAuthKeys []ids.Short } } -func (c *Subnet) SetSubnetCreateParams(controlKeys []ids.ShortID, threshold uint32) { +// SetSubnetControlParams sets: +// - control keys, which are keys that are allowed to make changes to a Subnet +// - threshold, which is the number of keys that need to sign a transaction that changes +// a Subnet +func (c *Subnet) SetSubnetControlParams(controlKeys []ids.ShortID, threshold uint32) { c.DeployInfo.ControlKeys = controlKeys c.DeployInfo.Threshold = threshold } -func (c *Subnet) SetBlockchainCreateParams(subnetAuthKeys []ids.ShortID) { +// SetSubnetAuthKeys sets subnetAuthKeys, which are keys that are being used to sign a transaction +// that changes a Subnet +func (c *Subnet) SetSubnetAuthKeys(subnetAuthKeys []ids.ShortID) { c.DeployInfo.SubnetAuthKeys = subnetAuthKeys } diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index e4d4897..ca1359b 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -57,7 +57,7 @@ func TestSubnetDeploy(t *testing.T) { controlKeys := keychain.Addresses().List() subnetAuthKeys := keychain.Addresses().List() threshold := 1 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) wallet, err := wallet.New( context.Background(), &primary.WalletConfig{ @@ -74,7 +74,7 @@ func TestSubnetDeploy(t *testing.T) { require.NoError(err) fmt.Printf("subnetID %s \n", subnetID.String()) time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) deployChainTx, err := newSubnet.CreateBlockchainTx(wallet) require.NoError(err) blockchainID, err := newSubnet.Commit(*deployChainTx, wallet, true) @@ -104,7 +104,7 @@ func TestSubnetDeployMultiSig(t *testing.T) { subnetAuthKeys = append(subnetAuthKeys, keychainA.Addresses().List()[0]) subnetAuthKeys = append(subnetAuthKeys, keychainB.Addresses().List()[0]) threshold := 2 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) walletA, err := wallet.New( context.Background(), @@ -126,7 +126,7 @@ func TestSubnetDeployMultiSig(t *testing.T) { // we need to wait to allow the transaction to reach other nodes in Fuji time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) // first signature of CreateChainTx using keychain A deployChainTx, err := newSubnet.CreateBlockchainTx(walletA) require.NoError(err) @@ -173,7 +173,7 @@ func TestSubnetDeployLedger(t *testing.T) { subnetAuthKeys := addressesIDs threshold := 1 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) walletA, err := wallet.New( context.Background(), @@ -194,7 +194,7 @@ func TestSubnetDeployLedger(t *testing.T) { time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) deployChainTx, err := newSubnet.CreateBlockchainTx(walletA) require.NoError(err) From bab121f7a82880389e075b480088f5bad3d5ac37 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:28:32 +0700 Subject: [PATCH 26/56] update example --- subnet/add_validator_subnet_test.go | 202 +++++++++++++--------------- subnet/subnet_test.go | 94 +++++++++++++ 2 files changed, 186 insertions(+), 110 deletions(-) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 33482ee..239e517 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -3,113 +3,95 @@ package subnet -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/set" - - "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" - "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" - "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/avalanchego/wallet/subnet/primary" -) - -func TestValidateSubnet(t *testing.T) { - ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } - - subnetParams := SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", - } - - newSubnet, err := New(&subnetParams) - if err != nil { - panic(err) - } - - node := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - - subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - panic(err) - } - - time.Sleep(2 * time.Second) - - network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) - if err != nil { - panic(err) - } - - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - - wallet, err := wallet.New( - context.Background(), - &primary.WalletConfig{ - URI: network.Endpoint, - AVAXKeychain: keychain.Keychain, - EthKeychain: secp256k1fx.NewKeychain(), - PChainTxsToFetch: set.Of(subnetID), - }, - ) - if err != nil { - panic(err) - } - - nodeID, err := ids.NodeIDFromString(node.NodeID) - if err != nil { - panic(err) - } - - validator := SubnetValidatorParams{ - NodeID: nodeID, - // Validate Subnet for 48 hours - Duration: 48 * time.Hour, - Weight: 20, - } - fmt.Printf("adding subnet validator") - - newSubnet.SetSubnetID(subnetID) - subnetAuthKeys := keychain.Addresses().List() - newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - - addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - if err != nil { - panic(err) - } - txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - if err != nil { - panic(err) - } - fmt.Printf("obtained tx id %s", txID.String()) -} +// +//func TestValidateSubnet(t *testing.T) { +// ctx := context.Background() +// cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) +// if err != nil { +// panic(err) +// } +// +// subnetParams := SubnetParams{ +// GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", +// Name: "sdkSubnetNew", +// } +// +// newSubnet, err := New(&subnetParams) +// if err != nil { +// panic(err) +// } +// +// node := node.Node{ +// // NodeID is Avalanche Node ID of the node +// NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", +// // IP address of the node +// IP: "18.144.79.215", +// // SSH configuration for the node +// SSHConfig: node.SSHConfig{ +// User: constants.RemoteHostUser, +// PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", +// }, +// // Cloud is the cloud service that the node is on +// Cloud: node.AWSCloud, +// // CloudConfig is the cloud specific configuration for the node +// CloudConfig: *cp, +// // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor +// Roles: []node.SupportedRole{node.Validator}, +// } +// +// subnetIDsToValidate := []string{newSubnet.SubnetID.String()} +// fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) +// if err := node.SyncSubnets(subnetIDsToValidate); err != nil { +// panic(err) +// } +// +// time.Sleep(2 * time.Second) +// +// network := avalanche.FujiNetwork() +// keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) +// if err != nil { +// panic(err) +// } +// +// subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") +// +// wallet, err := wallet.New( +// context.Background(), +// &primary.WalletConfig{ +// URI: network.Endpoint, +// AVAXKeychain: keychain.Keychain, +// EthKeychain: secp256k1fx.NewKeychain(), +// PChainTxsToFetch: set.Of(subnetID), +// }, +// ) +// if err != nil { +// panic(err) +// } +// +// nodeID, err := ids.NodeIDFromString(node.NodeID) +// if err != nil { +// panic(err) +// } +// +// validator := SubnetValidatorParams{ +// NodeID: nodeID, +// // Validate Subnet for 48 hours +// Duration: 48 * time.Hour, +// Weight: 20, +// } +// fmt.Printf("adding subnet validator") +// +// newSubnet.SetSubnetID(subnetID) +// subnetAuthKeys := keychain.Addresses().List() +// newSubnet.SetSubnetAuthKeys(subnetAuthKeys) +// +// addValidatorTx, err := newSubnet.AddValidator(wallet, validator) +// if err != nil { +// panic(err) +// } +// txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) +// if err != nil { +// panic(err) +// } +// fmt.Printf("obtained tx id %s", txID.String()) +//} diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index ca1359b..de3e7b2 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,6 +6,8 @@ package subnet import ( "context" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -227,3 +229,95 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } + +func TestValidateSubnet(t *testing.T) { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + + newSubnet, err := New(&subnetParams) + if err != nil { + panic(err) + } + + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + time.Sleep(2 * time.Second) + + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validator := SubnetValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + if err != nil { + panic(err) + } + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} From 5fd58e4a89714c6d8895b205d2ce07d0a2797c53 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:44:07 +0700 Subject: [PATCH 27/56] update example --- subnet/add_validator_subnet.go | 88 ++++++++++++++++------------------ subnet/subnet_test.go | 22 ++++----- 2 files changed, 53 insertions(+), 57 deletions(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 2edaed0..f32a08d 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -5,14 +5,9 @@ package subnet import ( "errors" - "fmt" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" - "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" - "golang.org/x/net/context" ) var ( @@ -34,44 +29,45 @@ type SubnetValidatorParams struct { Weight uint64 } -// AddValidator adds validator to subnet -// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be -// tracking the subnet, which can be done by calling SyncSubnets in node package -func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { - if validatorInput.NodeID == ids.EmptyNodeID { - return nil, ErrEmptyValidatorNodeID - } - if validatorInput.Duration == 0 { - return nil, ErrEmptyValidatorDuration - } - if validatorInput.Weight == 0 { - validatorInput.Weight = 20 - } - if c.SubnetID == ids.Empty { - return nil, ErrEmptySubnetID - } - if len(c.DeployInfo.SubnetAuthKeys) == 0 { - return nil, ErrEmptySubnetAuth - } - - wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) - - validator := &txs.SubnetValidator{ - Validator: txs.Validator{ - NodeID: validatorInput.NodeID, - End: uint64(time.Now().Add(validatorInput.Duration).Unix()), - Wght: validatorInput.Weight, - }, - Subnet: c.SubnetID, - } - - unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) - if err != nil { - return nil, fmt.Errorf("error building tx: %w", err) - } - tx := txs.Tx{Unsigned: unsignedTx} - if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { - return nil, fmt.Errorf("error signing tx: %w", err) - } - return multisig.New(&tx), nil -} +// +//// AddValidator adds validator to subnet +//// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be +//// tracking the subnet, which can be done by calling SyncSubnets in node package +//func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { +// if validatorInput.NodeID == ids.EmptyNodeID { +// return nil, ErrEmptyValidatorNodeID +// } +// if validatorInput.Duration == 0 { +// return nil, ErrEmptyValidatorDuration +// } +// if validatorInput.Weight == 0 { +// validatorInput.Weight = 20 +// } +// if c.SubnetID == ids.Empty { +// return nil, ErrEmptySubnetID +// } +// if len(c.DeployInfo.SubnetAuthKeys) == 0 { +// return nil, ErrEmptySubnetAuth +// } +// +// wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) +// +// validator := &txs.SubnetValidator{ +// Validator: txs.Validator{ +// NodeID: validatorInput.NodeID, +// End: uint64(time.Now().Add(validatorInput.Duration).Unix()), +// Wght: validatorInput.Weight, +// }, +// Subnet: c.SubnetID, +// } +// +// unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) +// if err != nil { +// return nil, fmt.Errorf("error building tx: %w", err) +// } +// tx := txs.Tx{Unsigned: unsignedTx} +// if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { +// return nil, fmt.Errorf("error signing tx: %w", err) +// } +// return multisig.New(&tx), nil +//} diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index de3e7b2..b299272 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -281,7 +281,7 @@ func TestValidateSubnet(t *testing.T) { subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - wallet, err := wallet.New( + _, err = wallet.New( context.Background(), &primary.WalletConfig{ URI: network.Endpoint, @@ -299,7 +299,7 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - validator := SubnetValidatorParams{ + _ = SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, @@ -311,13 +311,13 @@ func TestValidateSubnet(t *testing.T) { subnetAuthKeys := keychain.Addresses().List() newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - if err != nil { - panic(err) - } - txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - if err != nil { - panic(err) - } - fmt.Printf("obtained tx id %s", txID.String()) + //addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + //if err != nil { + // panic(err) + //} + //txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + //if err != nil { + // panic(err) + //} + //fmt.Printf("obtained tx id %s", txID.String()) } From 2b552160be3c4212ed53216b0002198c3f67bf19 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:47:15 +0700 Subject: [PATCH 28/56] update example --- subnet/add_validator_subnet.go | 32 +++--- subnet/subnet_test.go | 185 ++++++++++++++++----------------- 2 files changed, 106 insertions(+), 111 deletions(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index f32a08d..f0bd410 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -5,9 +5,6 @@ package subnet import ( "errors" - "time" - - "github.com/ava-labs/avalanchego/ids" ) var ( @@ -17,22 +14,21 @@ var ( ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) -type SubnetValidatorParams struct { - // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. - NodeID ids.NodeID - // Duration is how long the node will be staking the Subnet - // Duration has to be less than or equal to the duration that the node will be validating the Primary - // Network - Duration time.Duration - // Weight is the validator's weight when sampling validators. - // Weight for subnet validators is set to 20 by default - Weight uint64 -} +//type SubnetValidatorParams struct { +// // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. +// NodeID ids.NodeID +// // Duration is how long the node will be staking the Subnet +// // Duration has to be less than or equal to the duration that the node will be validating the Primary +// // Network +// Duration time.Duration +// // Weight is the validator's weight when sampling validators. +// // Weight for subnet validators is set to 20 by default +// Weight uint64 +//} -// -//// AddValidator adds validator to subnet -//// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be -//// tracking the subnet, which can be done by calling SyncSubnets in node package +// AddValidator adds validator to subnet +// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be +// tracking the subnet, which can be done by calling SyncSubnets in node package //func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { // if validatorInput.NodeID == ids.EmptyNodeID { // return nil, ErrEmptyValidatorNodeID diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index b299272..6855853 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,8 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -230,94 +228,95 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } -func TestValidateSubnet(t *testing.T) { - ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } - - subnetParams := SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", - } - - newSubnet, err := New(&subnetParams) - if err != nil { - panic(err) - } - - node := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - - subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - panic(err) - } - - time.Sleep(2 * time.Second) - - network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) - if err != nil { - panic(err) - } - - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - - _, err = wallet.New( - context.Background(), - &primary.WalletConfig{ - URI: network.Endpoint, - AVAXKeychain: keychain.Keychain, - EthKeychain: secp256k1fx.NewKeychain(), - PChainTxsToFetch: set.Of(subnetID), - }, - ) - if err != nil { - panic(err) - } - - nodeID, err := ids.NodeIDFromString(node.NodeID) - if err != nil { - panic(err) - } - - _ = SubnetValidatorParams{ - NodeID: nodeID, - // Validate Subnet for 48 hours - Duration: 48 * time.Hour, - Weight: 20, - } - fmt.Printf("adding subnet validator") - - newSubnet.SetSubnetID(subnetID) - subnetAuthKeys := keychain.Addresses().List() - newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - - //addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - //if err != nil { - // panic(err) - //} - //txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - //if err != nil { - // panic(err) - //} - //fmt.Printf("obtained tx id %s", txID.String()) -} +// +//func TestValidateSubnet(t *testing.T) { +// ctx := context.Background() +// cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) +// if err != nil { +// panic(err) +// } +// +// subnetParams := SubnetParams{ +// GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", +// Name: "sdkSubnetNew", +// } +// +// newSubnet, err := New(&subnetParams) +// if err != nil { +// panic(err) +// } +// +// node := node.Node{ +// // NodeID is Avalanche Node ID of the node +// NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", +// // IP address of the node +// IP: "18.144.79.215", +// // SSH configuration for the node +// SSHConfig: node.SSHConfig{ +// User: constants.RemoteHostUser, +// PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", +// }, +// // Cloud is the cloud service that the node is on +// Cloud: node.AWSCloud, +// // CloudConfig is the cloud specific configuration for the node +// CloudConfig: *cp, +// // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor +// Roles: []node.SupportedRole{node.Validator}, +// } +// +// subnetIDsToValidate := []string{newSubnet.SubnetID.String()} +// fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) +// if err := node.SyncSubnets(subnetIDsToValidate); err != nil { +// panic(err) +// } +// +// time.Sleep(2 * time.Second) +// +// network := avalanche.FujiNetwork() +// keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) +// if err != nil { +// panic(err) +// } +// +// subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") +// +// wallet, err := wallet.New( +// context.Background(), +// &primary.WalletConfig{ +// URI: network.Endpoint, +// AVAXKeychain: keychain.Keychain, +// EthKeychain: secp256k1fx.NewKeychain(), +// PChainTxsToFetch: set.Of(subnetID), +// }, +// ) +// if err != nil { +// panic(err) +// } +// +// nodeID, err := ids.NodeIDFromString(node.NodeID) +// if err != nil { +// panic(err) +// } +// +// validator := SubnetValidatorParams{ +// NodeID: nodeID, +// // Validate Subnet for 48 hours +// Duration: 48 * time.Hour, +// Weight: 20, +// } +// fmt.Printf("adding subnet validator") +// +// newSubnet.SetSubnetID(subnetID) +// subnetAuthKeys := keychain.Addresses().List() +// newSubnet.SetSubnetAuthKeys(subnetAuthKeys) +// +// addValidatorTx, err := newSubnet.AddValidator(wallet, validator) +// if err != nil { +// panic(err) +// } +// txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) +// if err != nil { +// panic(err) +// } +// fmt.Printf("obtained tx id %s", txID.String()) +//} From b96e2849651c2aaf4527a9999d16e633f626d874 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:48:33 +0700 Subject: [PATCH 29/56] update example --- subnet/add_validator_subnet.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index f0bd410..6bc1d0d 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -14,18 +14,6 @@ var ( ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) -//type SubnetValidatorParams struct { -// // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. -// NodeID ids.NodeID -// // Duration is how long the node will be staking the Subnet -// // Duration has to be less than or equal to the duration that the node will be validating the Primary -// // Network -// Duration time.Duration -// // Weight is the validator's weight when sampling validators. -// // Weight for subnet validators is set to 20 by default -// Weight uint64 -//} - // AddValidator adds validator to subnet // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be // tracking the subnet, which can be done by calling SyncSubnets in node package From 723aa480913f186ebb038c141f8e99548bf97678 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:52:45 +0700 Subject: [PATCH 30/56] update example --- subnet/add_validator_subnet.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 6bc1d0d..791fbd4 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -5,6 +5,8 @@ package subnet import ( "errors" + "github.com/ava-labs/avalanchego/ids" + "time" ) var ( @@ -14,6 +16,18 @@ var ( ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) +type ValidatorParams struct { + // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. + NodeID ids.NodeID + // Duration is how long the node will be staking the Subnet + // Duration has to be less than or equal to the duration that the node will be validating the Primary + // Network + Duration time.Duration + // Weight is the validator's weight when sampling validators. + // Weight for subnet validators is set to 20 by default + Weight uint64 +} + // AddValidator adds validator to subnet // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be // tracking the subnet, which can be done by calling SyncSubnets in node package From dd0fc423fe08c064f57c672324ad4afdb1786edd Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:54:03 +0700 Subject: [PATCH 31/56] update example --- subnet/add_validator_subnet.go | 80 +++++++------- subnet/subnet_test.go | 185 +++++++++++++++++---------------- 2 files changed, 135 insertions(+), 130 deletions(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 791fbd4..7357181 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -5,7 +5,11 @@ package subnet import ( "errors" + "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" "time" ) @@ -31,41 +35,41 @@ type ValidatorParams struct { // AddValidator adds validator to subnet // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be // tracking the subnet, which can be done by calling SyncSubnets in node package -//func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { -// if validatorInput.NodeID == ids.EmptyNodeID { -// return nil, ErrEmptyValidatorNodeID -// } -// if validatorInput.Duration == 0 { -// return nil, ErrEmptyValidatorDuration -// } -// if validatorInput.Weight == 0 { -// validatorInput.Weight = 20 -// } -// if c.SubnetID == ids.Empty { -// return nil, ErrEmptySubnetID -// } -// if len(c.DeployInfo.SubnetAuthKeys) == 0 { -// return nil, ErrEmptySubnetAuth -// } -// -// wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) -// -// validator := &txs.SubnetValidator{ -// Validator: txs.Validator{ -// NodeID: validatorInput.NodeID, -// End: uint64(time.Now().Add(validatorInput.Duration).Unix()), -// Wght: validatorInput.Weight, -// }, -// Subnet: c.SubnetID, -// } -// -// unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) -// if err != nil { -// return nil, fmt.Errorf("error building tx: %w", err) -// } -// tx := txs.Tx{Unsigned: unsignedTx} -// if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { -// return nil, fmt.Errorf("error signing tx: %w", err) -// } -// return multisig.New(&tx), nil -//} +func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput ValidatorParams) (*multisig.Multisig, error) { + if validatorInput.NodeID == ids.EmptyNodeID { + return nil, ErrEmptyValidatorNodeID + } + if validatorInput.Duration == 0 { + return nil, ErrEmptyValidatorDuration + } + if validatorInput.Weight == 0 { + validatorInput.Weight = 20 + } + if c.SubnetID == ids.Empty { + return nil, ErrEmptySubnetID + } + if len(c.DeployInfo.SubnetAuthKeys) == 0 { + return nil, ErrEmptySubnetAuth + } + + wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) + + validator := &txs.SubnetValidator{ + Validator: txs.Validator{ + NodeID: validatorInput.NodeID, + End: uint64(time.Now().Add(validatorInput.Duration).Unix()), + Wght: validatorInput.Weight, + }, + Subnet: c.SubnetID, + } + + unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) + if err != nil { + return nil, fmt.Errorf("error building tx: %w", err) + } + tx := txs.Tx{Unsigned: unsignedTx} + if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + return nil, fmt.Errorf("error signing tx: %w", err) + } + return multisig.New(&tx), nil +} diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 6855853..9801ed8 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,6 +6,8 @@ package subnet import ( "context" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -228,95 +230,94 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } -// -//func TestValidateSubnet(t *testing.T) { -// ctx := context.Background() -// cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) -// if err != nil { -// panic(err) -// } -// -// subnetParams := SubnetParams{ -// GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", -// Name: "sdkSubnetNew", -// } -// -// newSubnet, err := New(&subnetParams) -// if err != nil { -// panic(err) -// } -// -// node := node.Node{ -// // NodeID is Avalanche Node ID of the node -// NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", -// // IP address of the node -// IP: "18.144.79.215", -// // SSH configuration for the node -// SSHConfig: node.SSHConfig{ -// User: constants.RemoteHostUser, -// PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", -// }, -// // Cloud is the cloud service that the node is on -// Cloud: node.AWSCloud, -// // CloudConfig is the cloud specific configuration for the node -// CloudConfig: *cp, -// // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor -// Roles: []node.SupportedRole{node.Validator}, -// } -// -// subnetIDsToValidate := []string{newSubnet.SubnetID.String()} -// fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) -// if err := node.SyncSubnets(subnetIDsToValidate); err != nil { -// panic(err) -// } -// -// time.Sleep(2 * time.Second) -// -// network := avalanche.FujiNetwork() -// keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) -// if err != nil { -// panic(err) -// } -// -// subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") -// -// wallet, err := wallet.New( -// context.Background(), -// &primary.WalletConfig{ -// URI: network.Endpoint, -// AVAXKeychain: keychain.Keychain, -// EthKeychain: secp256k1fx.NewKeychain(), -// PChainTxsToFetch: set.Of(subnetID), -// }, -// ) -// if err != nil { -// panic(err) -// } -// -// nodeID, err := ids.NodeIDFromString(node.NodeID) -// if err != nil { -// panic(err) -// } -// -// validator := SubnetValidatorParams{ -// NodeID: nodeID, -// // Validate Subnet for 48 hours -// Duration: 48 * time.Hour, -// Weight: 20, -// } -// fmt.Printf("adding subnet validator") -// -// newSubnet.SetSubnetID(subnetID) -// subnetAuthKeys := keychain.Addresses().List() -// newSubnet.SetSubnetAuthKeys(subnetAuthKeys) -// -// addValidatorTx, err := newSubnet.AddValidator(wallet, validator) -// if err != nil { -// panic(err) -// } -// txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) -// if err != nil { -// panic(err) -// } -// fmt.Printf("obtained tx id %s", txID.String()) -//} +func TestValidateSubnet(t *testing.T) { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + + newSubnet, err := New(&subnetParams) + if err != nil { + panic(err) + } + + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + time.Sleep(2 * time.Second) + + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validator := ValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + if err != nil { + panic(err) + } + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} From 2c74d45fb925aef0540aeae6bcb2e78516f0f2bc Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:54:31 +0700 Subject: [PATCH 32/56] update example --- subnet/add_validator_subnet.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 7357181..2c5bf1e 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -6,11 +6,12 @@ package subnet import ( "errors" "fmt" + "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/platformvm/txs" - "time" ) var ( From 40738f3993fa8b8217f0e53fbf81db491af374a5 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:56:30 +0700 Subject: [PATCH 33/56] update example --- subnet/add_validator_subnet.go | 46 ----------------- subnet/subnet_test.go | 94 ---------------------------------- 2 files changed, 140 deletions(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 2c5bf1e..459d043 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -5,13 +5,9 @@ package subnet import ( "errors" - "fmt" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" - "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) var ( @@ -32,45 +28,3 @@ type ValidatorParams struct { // Weight for subnet validators is set to 20 by default Weight uint64 } - -// AddValidator adds validator to subnet -// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be -// tracking the subnet, which can be done by calling SyncSubnets in node package -func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput ValidatorParams) (*multisig.Multisig, error) { - if validatorInput.NodeID == ids.EmptyNodeID { - return nil, ErrEmptyValidatorNodeID - } - if validatorInput.Duration == 0 { - return nil, ErrEmptyValidatorDuration - } - if validatorInput.Weight == 0 { - validatorInput.Weight = 20 - } - if c.SubnetID == ids.Empty { - return nil, ErrEmptySubnetID - } - if len(c.DeployInfo.SubnetAuthKeys) == 0 { - return nil, ErrEmptySubnetAuth - } - - wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) - - validator := &txs.SubnetValidator{ - Validator: txs.Validator{ - NodeID: validatorInput.NodeID, - End: uint64(time.Now().Add(validatorInput.Duration).Unix()), - Wght: validatorInput.Weight, - }, - Subnet: c.SubnetID, - } - - unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) - if err != nil { - return nil, fmt.Errorf("error building tx: %w", err) - } - tx := txs.Tx{Unsigned: unsignedTx} - if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { - return nil, fmt.Errorf("error signing tx: %w", err) - } - return multisig.New(&tx), nil -} diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 9801ed8..ca1359b 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,8 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -229,95 +227,3 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } - -func TestValidateSubnet(t *testing.T) { - ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } - - subnetParams := SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", - } - - newSubnet, err := New(&subnetParams) - if err != nil { - panic(err) - } - - node := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - - subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - panic(err) - } - - time.Sleep(2 * time.Second) - - network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) - if err != nil { - panic(err) - } - - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - - wallet, err := wallet.New( - context.Background(), - &primary.WalletConfig{ - URI: network.Endpoint, - AVAXKeychain: keychain.Keychain, - EthKeychain: secp256k1fx.NewKeychain(), - PChainTxsToFetch: set.Of(subnetID), - }, - ) - if err != nil { - panic(err) - } - - nodeID, err := ids.NodeIDFromString(node.NodeID) - if err != nil { - panic(err) - } - - validator := ValidatorParams{ - NodeID: nodeID, - // Validate Subnet for 48 hours - Duration: 48 * time.Hour, - Weight: 20, - } - fmt.Printf("adding subnet validator") - - newSubnet.SetSubnetID(subnetID) - subnetAuthKeys := keychain.Addresses().List() - newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - - addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - if err != nil { - panic(err) - } - txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - if err != nil { - panic(err) - } - fmt.Printf("obtained tx id %s", txID.String()) -} From 5078b2f049dbf2063572c2164122c1aac2f6fffb Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 17:57:35 +0700 Subject: [PATCH 34/56] update example --- subnet/add_validator_subnet_test.go | 97 ----------------------------- 1 file changed, 97 deletions(-) delete mode 100644 subnet/add_validator_subnet_test.go diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go deleted file mode 100644 index 239e517..0000000 --- a/subnet/add_validator_subnet_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package subnet - -// -//func TestValidateSubnet(t *testing.T) { -// ctx := context.Background() -// cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) -// if err != nil { -// panic(err) -// } -// -// subnetParams := SubnetParams{ -// GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", -// Name: "sdkSubnetNew", -// } -// -// newSubnet, err := New(&subnetParams) -// if err != nil { -// panic(err) -// } -// -// node := node.Node{ -// // NodeID is Avalanche Node ID of the node -// NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", -// // IP address of the node -// IP: "18.144.79.215", -// // SSH configuration for the node -// SSHConfig: node.SSHConfig{ -// User: constants.RemoteHostUser, -// PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", -// }, -// // Cloud is the cloud service that the node is on -// Cloud: node.AWSCloud, -// // CloudConfig is the cloud specific configuration for the node -// CloudConfig: *cp, -// // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor -// Roles: []node.SupportedRole{node.Validator}, -// } -// -// subnetIDsToValidate := []string{newSubnet.SubnetID.String()} -// fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) -// if err := node.SyncSubnets(subnetIDsToValidate); err != nil { -// panic(err) -// } -// -// time.Sleep(2 * time.Second) -// -// network := avalanche.FujiNetwork() -// keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) -// if err != nil { -// panic(err) -// } -// -// subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") -// -// wallet, err := wallet.New( -// context.Background(), -// &primary.WalletConfig{ -// URI: network.Endpoint, -// AVAXKeychain: keychain.Keychain, -// EthKeychain: secp256k1fx.NewKeychain(), -// PChainTxsToFetch: set.Of(subnetID), -// }, -// ) -// if err != nil { -// panic(err) -// } -// -// nodeID, err := ids.NodeIDFromString(node.NodeID) -// if err != nil { -// panic(err) -// } -// -// validator := SubnetValidatorParams{ -// NodeID: nodeID, -// // Validate Subnet for 48 hours -// Duration: 48 * time.Hour, -// Weight: 20, -// } -// fmt.Printf("adding subnet validator") -// -// newSubnet.SetSubnetID(subnetID) -// subnetAuthKeys := keychain.Addresses().List() -// newSubnet.SetSubnetAuthKeys(subnetAuthKeys) -// -// addValidatorTx, err := newSubnet.AddValidator(wallet, validator) -// if err != nil { -// panic(err) -// } -// txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) -// if err != nil { -// panic(err) -// } -// fmt.Printf("obtained tx id %s", txID.String()) -//} From c5fce65527109e0f76d3f9bd8f5f904cc34120c2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 18:00:17 +0700 Subject: [PATCH 35/56] update example --- subnet/subnet_test.go | 94 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index ca1359b..c4b172e 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,6 +6,8 @@ package subnet import ( "context" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -227,3 +229,95 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } + +func TestValidateSubnet(t *testing.T) { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + + newSubnet, err := New(&subnetParams) + if err != nil { + panic(err) + } + + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + time.Sleep(2 * time.Second) + + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + _, err = wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + _ = ValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + + //addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + //if err != nil { + // panic(err) + //} + //txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + //if err != nil { + // panic(err) + //} + //fmt.Printf("obtained tx id %s", txID.String()) +} From 12f0e862ae1140533d4aecee43dcd6499bc808e3 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 18:02:10 +0700 Subject: [PATCH 36/56] update example --- subnet/subnet_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index c4b172e..6f8cf5c 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -267,11 +267,11 @@ func TestValidateSubnet(t *testing.T) { subnetIDsToValidate := []string{newSubnet.SubnetID.String()} fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - panic(err) - } - - time.Sleep(2 * time.Second) + //if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + // panic(err) + //} + // + //time.Sleep(2 * time.Second) network := avalanche.FujiNetwork() keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) From fb71900c1902deb4b107f11350ffc7865a430c32 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 18:03:38 +0700 Subject: [PATCH 37/56] update example --- subnet/subnet_test.go | 123 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 6f8cf5c..56ca731 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,7 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" @@ -232,7 +231,7 @@ func TestSubnetDeployLedger(t *testing.T) { func TestValidateSubnet(t *testing.T) { ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + _, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) if err != nil { panic(err) } @@ -242,74 +241,74 @@ func TestValidateSubnet(t *testing.T) { Name: "sdkSubnetNew", } - newSubnet, err := New(&subnetParams) + _, err = New(&subnetParams) if err != nil { panic(err) } - - node := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - - subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + // + //node := node.Node{ + // // NodeID is Avalanche Node ID of the node + // NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // // IP address of the node + // IP: "18.144.79.215", + // // SSH configuration for the node + // SSHConfig: node.SSHConfig{ + // User: constants.RemoteHostUser, + // PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + // }, + // // Cloud is the cloud service that the node is on + // Cloud: node.AWSCloud, + // // CloudConfig is the cloud specific configuration for the node + // CloudConfig: *cp, + // // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + // Roles: []node.SupportedRole{node.Validator}, + //} + // + //subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + //fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) //if err := node.SyncSubnets(subnetIDsToValidate); err != nil { // panic(err) //} // //time.Sleep(2 * time.Second) - - network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) - if err != nil { - panic(err) - } - - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - - _, err = wallet.New( - context.Background(), - &primary.WalletConfig{ - URI: network.Endpoint, - AVAXKeychain: keychain.Keychain, - EthKeychain: secp256k1fx.NewKeychain(), - PChainTxsToFetch: set.Of(subnetID), - }, - ) - if err != nil { - panic(err) - } - - nodeID, err := ids.NodeIDFromString(node.NodeID) - if err != nil { - panic(err) - } - - _ = ValidatorParams{ - NodeID: nodeID, - // Validate Subnet for 48 hours - Duration: 48 * time.Hour, - Weight: 20, - } - fmt.Printf("adding subnet validator") - - newSubnet.SetSubnetID(subnetID) - subnetAuthKeys := keychain.Addresses().List() - newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + // + //network := avalanche.FujiNetwork() + //keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + //if err != nil { + // panic(err) + //} + // + //subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + // + //_, err = wallet.New( + // context.Background(), + // &primary.WalletConfig{ + // URI: network.Endpoint, + // AVAXKeychain: keychain.Keychain, + // EthKeychain: secp256k1fx.NewKeychain(), + // PChainTxsToFetch: set.Of(subnetID), + // }, + //) + //if err != nil { + // panic(err) + //} + // + //nodeID, err := ids.NodeIDFromString(node.NodeID) + //if err != nil { + // panic(err) + //} + // + //_ = ValidatorParams{ + // NodeID: nodeID, + // // Validate Subnet for 48 hours + // Duration: 48 * time.Hour, + // Weight: 20, + //} + //fmt.Printf("adding subnet validator") + // + //newSubnet.SetSubnetID(subnetID) + //subnetAuthKeys := keychain.Addresses().List() + //newSubnet.SetSubnetAuthKeys(subnetAuthKeys) //addValidatorTx, err := newSubnet.AddValidator(wallet, validator) //if err != nil { From 461440f6656140bcb74398fa00e12784003f3108 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 18:05:34 +0700 Subject: [PATCH 38/56] update example --- subnet/subnet_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 56ca731..57c6520 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,7 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -230,18 +229,18 @@ func TestSubnetDeployLedger(t *testing.T) { } func TestValidateSubnet(t *testing.T) { - ctx := context.Background() - _, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } + //ctx := context.Background() + //_, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + //if err != nil { + // panic(err) + //} subnetParams := SubnetParams{ GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", Name: "sdkSubnetNew", } - _, err = New(&subnetParams) + _, err := New(&subnetParams) if err != nil { panic(err) } From c6f9dd09941a161a89b5ec46950a1117fdf53001 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 18:07:29 +0700 Subject: [PATCH 39/56] update example --- subnet/subnet_test.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 57c6520..6b96ffd 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,6 +6,8 @@ package subnet import ( "context" "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -244,24 +246,22 @@ func TestValidateSubnet(t *testing.T) { if err != nil { panic(err) } - // - //node := node.Node{ - // // NodeID is Avalanche Node ID of the node - // NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // // IP address of the node - // IP: "18.144.79.215", - // // SSH configuration for the node - // SSHConfig: node.SSHConfig{ - // User: constants.RemoteHostUser, - // PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - // }, - // // Cloud is the cloud service that the node is on - // Cloud: node.AWSCloud, - // // CloudConfig is the cloud specific configuration for the node - // CloudConfig: *cp, - // // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - // Roles: []node.SupportedRole{node.Validator}, - //} + + _ := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } // //subnetIDsToValidate := []string{newSubnet.SubnetID.String()} //fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) From c1f17308b7563546e85f0501ac28239b1eb7d42b Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 18:07:38 +0700 Subject: [PATCH 40/56] update example --- subnet/subnet_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 6b96ffd..aebe406 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -231,12 +231,6 @@ func TestSubnetDeployLedger(t *testing.T) { } func TestValidateSubnet(t *testing.T) { - //ctx := context.Background() - //_, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - //if err != nil { - // panic(err) - //} - subnetParams := SubnetParams{ GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", Name: "sdkSubnetNew", From a51d19973e5bef3c1e085f86193d70fddd0a6bde Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 20:04:48 +0700 Subject: [PATCH 41/56] revert changes --- avalanche/log.go | 4 - subnet/add_validator_subnet.go | 49 +++++++++++- subnet/add_validator_subnet_test.go | 114 ++++++++++++++++++++++++++++ subnet/subnet_test.go | 86 --------------------- 4 files changed, 162 insertions(+), 91 deletions(-) create mode 100644 subnet/add_validator_subnet_test.go diff --git a/avalanche/log.go b/avalanche/log.go index db44b8a..0b0b42a 100644 --- a/avalanche/log.go +++ b/avalanche/log.go @@ -9,10 +9,6 @@ import ( "os" ) -// -// Public constants -// - const ( // LevelNull sets a logger to show no messages at all. LevelNull Level = 0 diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 459d043..d3e0d3c 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -5,9 +5,14 @@ package subnet import ( "errors" + "fmt" + "golang.org/x/net/context" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) var ( @@ -17,7 +22,7 @@ var ( ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) -type ValidatorParams struct { +type SubnetValidatorParams struct { // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. NodeID ids.NodeID // Duration is how long the node will be staking the Subnet @@ -28,3 +33,45 @@ type ValidatorParams struct { // Weight for subnet validators is set to 20 by default Weight uint64 } + +// AddValidator adds validator to subnet +// Before an Avalanche Node can be added as a validator to a Subnet, the node must already be +// tracking the subnet, which can be done by calling SyncSubnets in node package +func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { + if validatorInput.NodeID == ids.EmptyNodeID { + return nil, ErrEmptyValidatorNodeID + } + if validatorInput.Duration == 0 { + return nil, ErrEmptyValidatorDuration + } + if validatorInput.Weight == 0 { + validatorInput.Weight = 20 + } + if c.SubnetID == ids.Empty { + return nil, ErrEmptySubnetID + } + if len(c.DeployInfo.SubnetAuthKeys) == 0 { + return nil, ErrEmptySubnetAuth + } + + wallet.SetSubnetAuthMultisig(c.DeployInfo.SubnetAuthKeys) + + validator := &txs.SubnetValidator{ + Validator: txs.Validator{ + NodeID: validatorInput.NodeID, + End: uint64(time.Now().Add(validatorInput.Duration).Unix()), + Wght: validatorInput.Weight, + }, + Subnet: c.SubnetID, + } + + unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) + if err != nil { + return nil, fmt.Errorf("error building tx: %w", err) + } + tx := txs.Tx{Unsigned: unsignedTx} + if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + return nil, fmt.Errorf("error signing tx: %w", err) + } + return multisig.New(&tx), nil +} diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go new file mode 100644 index 0000000..c0cf74b --- /dev/null +++ b/subnet/add_validator_subnet_test.go @@ -0,0 +1,114 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package subnet + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/set" + + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +func TestValidateSubnet(t *testing.T) { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + + newSubnet, err := New(&subnetParams) + if err != nil { + panic(err) + } + + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + time.Sleep(2 * time.Second) + + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validator := SubnetValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + if err != nil { + panic(err) + } + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index aebe406..ca1359b 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -6,8 +6,6 @@ package subnet import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" "math/big" "testing" "time" @@ -229,87 +227,3 @@ func TestSubnetDeployLedger(t *testing.T) { fmt.Printf("blockchainID %s \n", blockchainID.String()) } - -func TestValidateSubnet(t *testing.T) { - subnetParams := SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", - } - - _, err := New(&subnetParams) - if err != nil { - panic(err) - } - - _ := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - // - //subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - //fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - //if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - // panic(err) - //} - // - //time.Sleep(2 * time.Second) - // - //network := avalanche.FujiNetwork() - //keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) - //if err != nil { - // panic(err) - //} - // - //subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - // - //_, err = wallet.New( - // context.Background(), - // &primary.WalletConfig{ - // URI: network.Endpoint, - // AVAXKeychain: keychain.Keychain, - // EthKeychain: secp256k1fx.NewKeychain(), - // PChainTxsToFetch: set.Of(subnetID), - // }, - //) - //if err != nil { - // panic(err) - //} - // - //nodeID, err := ids.NodeIDFromString(node.NodeID) - //if err != nil { - // panic(err) - //} - // - //_ = ValidatorParams{ - // NodeID: nodeID, - // // Validate Subnet for 48 hours - // Duration: 48 * time.Hour, - // Weight: 20, - //} - //fmt.Printf("adding subnet validator") - // - //newSubnet.SetSubnetID(subnetID) - //subnetAuthKeys := keychain.Addresses().List() - //newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - - //addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - //if err != nil { - // panic(err) - //} - //txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - //if err != nil { - // panic(err) - //} - //fmt.Printf("obtained tx id %s", txID.String()) -} From 939ca4e40939e3b33e7730c2b2453e59c62df33e Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 23:20:20 +0700 Subject: [PATCH 42/56] fix lint --- subnet/add_validator_subnet_test.go | 62 ++++++++++++++--------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index c0cf74b..f1e252b 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -9,9 +9,6 @@ import ( "testing" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/set" @@ -23,11 +20,11 @@ import ( ) func TestValidateSubnet(t *testing.T) { - ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } + //ctx := context.Background() + //cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + //if err != nil { + // panic(err) + //} subnetParams := SubnetParams{ GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", @@ -39,29 +36,29 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - node := node.Node{ - // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // IP address of the node - IP: "18.144.79.215", - // SSH configuration for the node - SSHConfig: node.SSHConfig{ - User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - Roles: []node.SupportedRole{node.Validator}, - } - - subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - panic(err) - } + //node := node.Node{ + // // NodeID is Avalanche Node ID of the node + // NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // // IP address of the node + // IP: "18.144.79.215", + // // SSH configuration for the node + // SSHConfig: node.SSHConfig{ + // User: constants.RemoteHostUser, + // PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + // }, + // // Cloud is the cloud service that the node is on + // Cloud: node.AWSCloud, + // // CloudConfig is the cloud specific configuration for the node + // CloudConfig: *cp, + // // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + // Roles: []node.SupportedRole{node.Validator}, + //} + + //subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + //fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + //if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + // panic(err) + //} time.Sleep(2 * time.Second) @@ -86,7 +83,8 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - nodeID, err := ids.NodeIDFromString(node.NodeID) + //nodeID, err := ids.NodeIDFromString(node.NodeID) + nodeID, err := ids.NodeIDFromString("NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPH") if err != nil { panic(err) } From 0adfc47e0a5f5cfda7dc091ad167f63e92f5bded Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 23:22:03 +0700 Subject: [PATCH 43/56] fix lint --- subnet/add_validator_subnet.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index d3e0d3c..5c1808c 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -6,9 +6,10 @@ package subnet import ( "errors" "fmt" - "golang.org/x/net/context" "time" + "golang.org/x/net/context" + "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" From efb998bd4881835c09c88a3194544997138db8d7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 23:32:36 +0700 Subject: [PATCH 44/56] add subnet validator example --- examples/subnet_ addValidator.go | 113 +++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 examples/subnet_ addValidator.go diff --git a/examples/subnet_ addValidator.go b/examples/subnet_ addValidator.go new file mode 100644 index 0000000..20bfdf5 --- /dev/null +++ b/examples/subnet_ addValidator.go @@ -0,0 +1,113 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package examples + +import ( + "context" + "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +func AddSubnetValidator() { + ctx := context.Background() + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + subnetParams := subnet.SubnetParams{ + GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", + Name: "sdkSubnetNew", + } + + newSubnet, err := subnet.New(&subnetParams) + if err != nil { + panic(err) + } + + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + // IP address of the node + IP: "18.144.79.215", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + }, + // Cloud is the cloud service that the node is on + Cloud: node.AWSCloud, + // CloudConfig is the cloud specific configuration for the node + CloudConfig: *cp, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + time.Sleep(2 * time.Second) + + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + //nodeID, err := ids.NodeIDFromString(node.NodeID) + nodeID, err := ids.NodeIDFromString("NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPH") + if err != nil { + panic(err) + } + + validator := subnet.SubnetValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + fmt.Printf("adding subnet validator") + + newSubnet.SetSubnetID(subnetID) + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + if err != nil { + panic(err) + } + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} From ae06017b92fd4b309759b438716b263d0c1a5caf Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 22 Aug 2024 23:37:55 +0700 Subject: [PATCH 45/56] fix lint --- node/add_validator_primary.go | 4 +++- node/add_validator_primary_test.go | 4 +++- subnet/add_validator_subnet.go | 16 +++------------- subnet/add_validator_subnet_test.go | 6 ++++-- {avalanche => validator}/validator.go | 20 ++++++++++++++++---- 5 files changed, 29 insertions(+), 21 deletions(-) rename {avalanche => validator}/validator.go (59%) diff --git a/node/add_validator_primary.go b/node/add_validator_primary.go index b83c236..bb4609a 100644 --- a/node/add_validator_primary.go +++ b/node/add_validator_primary.go @@ -7,6 +7,8 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" @@ -30,7 +32,7 @@ import ( // and uses the wallet provided in the argument to pay for the transaction fee func (h *Node) ValidatePrimaryNetwork( network avalanche.Network, - validatorParams avalanche.PrimaryNetworkValidatorParams, + validatorParams validator.PrimaryNetworkValidatorParams, wallet wallet.Wallet, ) (ids.ID, error) { if validatorParams.NodeID == ids.EmptyNodeID { diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index 7cc76ff..6a882e4 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" @@ -49,7 +51,7 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { panic(err) } - validator := avalanche.PrimaryNetworkValidatorParams{ + validator := validator.PrimaryNetworkValidatorParams{ NodeID: nodeID, // Validate Primary Network for 48 hours Duration: 48 * time.Hour, diff --git a/subnet/add_validator_subnet.go b/subnet/add_validator_subnet.go index 5c1808c..964acd9 100644 --- a/subnet/add_validator_subnet.go +++ b/subnet/add_validator_subnet.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "golang.org/x/net/context" "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" @@ -23,22 +25,10 @@ var ( ErrEmptySubnetAuth = errors.New("no subnet auth keys is provided") ) -type SubnetValidatorParams struct { - // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. - NodeID ids.NodeID - // Duration is how long the node will be staking the Subnet - // Duration has to be less than or equal to the duration that the node will be validating the Primary - // Network - Duration time.Duration - // Weight is the validator's weight when sampling validators. - // Weight for subnet validators is set to 20 by default - Weight uint64 -} - // AddValidator adds validator to subnet // Before an Avalanche Node can be added as a validator to a Subnet, the node must already be // tracking the subnet, which can be done by calling SyncSubnets in node package -func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput SubnetValidatorParams) (*multisig.Multisig, error) { +func (c *Subnet) AddValidator(wallet wallet.Wallet, validatorInput validator.SubnetValidatorParams) (*multisig.Multisig, error) { if validatorInput.NodeID == ids.EmptyNodeID { return nil, ErrEmptyValidatorNodeID } diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index f1e252b..7dc0a68 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/set" @@ -83,13 +85,13 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - //nodeID, err := ids.NodeIDFromString(node.NodeID) + // nodeID, err := ids.NodeIDFromString(node.NodeID) nodeID, err := ids.NodeIDFromString("NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPH") if err != nil { panic(err) } - validator := SubnetValidatorParams{ + validator := validator.SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, diff --git a/avalanche/validator.go b/validator/validator.go similarity index 59% rename from avalanche/validator.go rename to validator/validator.go index a85f701..f60c208 100644 --- a/avalanche/validator.go +++ b/validator/validator.go @@ -1,7 +1,7 @@ // Copyright (C) 2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package avalanche +package validator import ( "time" @@ -18,9 +18,9 @@ type PrimaryNetworkValidatorParams struct { // (Fuji / Mainnet) Duration time.Duration - // StakeAmount is the amount of Avalanche tokens (AVAX) to stake in this validator - // StakeAmount is in the amount of nAVAX - // StakeAmount has to be greater than or equal to minimum stake required for the specified network + // StakeAmount is the amount of Avalanche tokens (AVAX) to stake in this validator, which is + // denominated in nAVAX. StakeAmount has to be greater than or equal to minimum stake required + // for the specified network StakeAmount uint64 // DelegationFee is the percent fee this validator will charge when others delegate stake to it @@ -28,3 +28,15 @@ type PrimaryNetworkValidatorParams struct { // For more information on delegation fee, please head to https://docs.avax.network/nodes/validate/node-validator#delegation-fee-rate DelegationFee uint32 } + +type SubnetValidatorParams struct { + // NodeID is the unique identifier of the node to be added as a validator on the specified Subnet. + NodeID ids.NodeID + // Duration is how long the node will be staking the Subnet + // Duration has to be less than or equal to the duration that the node will be validating the Primary + // Network + Duration time.Duration + // Weight is the validator's weight when sampling validators. + // Weight for subnet validators is set to 20 by default + Weight uint64 +} From b1843024fa9605b07b21172dbe331e61cf64d1b2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 00:32:12 +0700 Subject: [PATCH 46/56] add example --- examples/subnet_ addValidator.go | 3 +- subnet/add_validator_subnet_test.go | 46 ++++++++--------------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/examples/subnet_ addValidator.go b/examples/subnet_ addValidator.go index 20bfdf5..7d8db6d 100644 --- a/examples/subnet_ addValidator.go +++ b/examples/subnet_ addValidator.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/node" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "time" "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" @@ -90,7 +91,7 @@ func AddSubnetValidator() { panic(err) } - validator := subnet.SubnetValidatorParams{ + validator := validator.SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 7dc0a68..0c08a31 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -22,12 +22,6 @@ import ( ) func TestValidateSubnet(t *testing.T) { - //ctx := context.Background() - //cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - //if err != nil { - // panic(err) - //} - subnetParams := SubnetParams{ GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", Name: "sdkSubnetNew", @@ -38,32 +32,6 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - //node := node.Node{ - // // NodeID is Avalanche Node ID of the node - // NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", - // // IP address of the node - // IP: "18.144.79.215", - // // SSH configuration for the node - // SSHConfig: node.SSHConfig{ - // User: constants.RemoteHostUser, - // PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", - // }, - // // Cloud is the cloud service that the node is on - // Cloud: node.AWSCloud, - // // CloudConfig is the cloud specific configuration for the node - // CloudConfig: *cp, - // // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor - // Roles: []node.SupportedRole{node.Validator}, - //} - - //subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - //fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) - //if err := node.SyncSubnets(subnetIDsToValidate); err != nil { - // panic(err) - //} - - time.Sleep(2 * time.Second) - network := avalanche.FujiNetwork() keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) if err != nil { @@ -85,7 +53,6 @@ func TestValidateSubnet(t *testing.T) { panic(err) } - // nodeID, err := ids.NodeIDFromString(node.NodeID) nodeID, err := ids.NodeIDFromString("NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPH") if err != nil { panic(err) @@ -100,12 +67,25 @@ func TestValidateSubnet(t *testing.T) { fmt.Printf("adding subnet validator") newSubnet.SetSubnetID(subnetID) + + // We need to set Subnet Auth Keys for this transaction since Subnet AddValidator is + // a Subnet-changing transaction + // + // In this example, the example Subnet was created with only 1 key as control key with a threshold of 1 + // and the control key is the key contained in the keychain object, so we are going to use the + // key contained in the keychain object as the Subnet Auth Key for Subnet AddValidator tx subnetAuthKeys := keychain.Addresses().List() newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + + // In this example, we are assuming that the specified node has already tracked the Subnet + // which can be done in Avalanche Tooling SDK through node.Sync function + // For an example, head to examples/subnet_addValidator.go addValidatorTx, err := newSubnet.AddValidator(wallet, validator) if err != nil { panic(err) } + + // Since it has the required signatures, we will now commit the transaction on chain txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) if err != nil { panic(err) From 16e9f2f3dbaebe420354bdfa007c054f798ed96e Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 00:33:54 +0700 Subject: [PATCH 47/56] add example --- subnet/add_validator_subnet_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 0c08a31..2b2d826 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -21,7 +21,7 @@ import ( "github.com/ava-labs/avalanchego/wallet/subnet/primary" ) -func TestValidateSubnet(t *testing.T) { +func TestValidateSubnet(_ *testing.T) { subnetParams := SubnetParams{ GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", Name: "sdkSubnetNew", @@ -39,6 +39,9 @@ func TestValidateSubnet(t *testing.T) { } subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + if err != nil { + panic(err) + } wallet, err := wallet.New( context.Background(), From df386b951d277a3f8a79f448e4e0be494c013950 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 15:50:25 +0700 Subject: [PATCH 48/56] update example --- examples/node.go | 2 +- examples/subnet_ addValidator.go | 68 ++++++++++++++++------------- examples/subnet_ multisig.go | 6 +-- node/add_validator_primary_test.go | 8 ---- subnet/add_validator_subnet_test.go | 34 +++++++-------- 5 files changed, 57 insertions(+), 61 deletions(-) diff --git a/examples/node.go b/examples/node.go index c500619..01d13fa 100644 --- a/examples/node.go +++ b/examples/node.go @@ -105,7 +105,7 @@ func CreateNodes() { // examle of how to reconfigure the created nodes to track a subnet subnetIDsToValidate := []string{"xxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyzzzzzzzzzzzzzzz"} for _, h := range hosts { - fmt.Println("Reconfiguring node %s to track subnet %s", h.NodeID, subnetIDsToValidate) + fmt.Printf("Reconfiguring node %s to track subnet %s", h.NodeID, subnetIDsToValidate) if err := h.SyncSubnets(subnetIDsToValidate); err != nil { panic(err) } diff --git a/examples/subnet_ addValidator.go b/examples/subnet_ addValidator.go index 7d8db6d..07e45a0 100644 --- a/examples/subnet_ addValidator.go +++ b/examples/subnet_ addValidator.go @@ -6,31 +6,24 @@ package examples import ( "context" "fmt" - "github.com/ava-labs/avalanche-tooling-sdk-go/constants" - "github.com/ava-labs/avalanche-tooling-sdk-go/node" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" - "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" "github.com/ava-labs/avalanche-tooling-sdk-go/subnet" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "time" ) func AddSubnetValidator() { - ctx := context.Background() - cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) - if err != nil { - panic(err) - } - subnetParams := subnet.SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", + GenesisFilePath: "GENESIS_FILE_PATH", + Name: "SUBNET_NAME", } newSubnet, err := subnet.New(&subnetParams) @@ -38,40 +31,49 @@ func AddSubnetValidator() { panic(err) } + subnetID, err := ids.FromString("SUBNET_ID") + if err != nil { + panic(err) + } + newSubnet.SetSubnetID(subnetID) + node := node.Node{ // NodeID is Avalanche Node ID of the node - NodeID: "NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPHu", + NodeID: "NODE_ID", // IP address of the node - IP: "18.144.79.215", + IP: "NODE_IP_ADDRESS", // SSH configuration for the node SSHConfig: node.SSHConfig{ User: constants.RemoteHostUser, - PrivateKeyPath: "/Users/raymondsukanto/.ssh/rs_key_pair_sdk.pem", + PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", }, // Cloud is the cloud service that the node is on Cloud: node.AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, - // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, } + // Here we are assuming that the node is currently validating the Primary Network, which is + // a requirement before the node can start validating a Subnet. + // To have a node validate the Primary Network, call node.ValidatePrimaryNetwork + // Now we are calling the node to start tracking the Subnet subnetIDsToValidate := []string{newSubnet.SubnetID.String()} - fmt.Printf("Reconfiguring node %s to track subnet %s\n", node.NodeID, subnetIDsToValidate) if err := node.SyncSubnets(subnetIDsToValidate); err != nil { panic(err) } - time.Sleep(2 * time.Second) + // Node is now tracking the Subnet + // Key that will be used for paying the transaction fees of Subnet AddValidator Tx + // + // In our example, this Key is also the control Key to the Subnet, so we are going to use + // this key to also sign the Subnet AddValidator tx network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) if err != nil { panic(err) } - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") - wallet, err := wallet.New( context.Background(), &primary.WalletConfig{ @@ -85,27 +87,33 @@ func AddSubnetValidator() { panic(err) } - //nodeID, err := ids.NodeIDFromString(node.NodeID) - nodeID, err := ids.NodeIDFromString("NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPH") + nodeID, err := ids.NodeIDFromString(node.NodeID) if err != nil { panic(err) } - validator := validator.SubnetValidatorParams{ + validatorParams := validator.SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, Weight: 20, } - fmt.Printf("adding subnet validator") - newSubnet.SetSubnetID(subnetID) + // We need to set Subnet Auth Keys for this transaction since Subnet AddValidator is + // a Subnet-changing transaction + // + // In this example, the example Subnet was created with only 1 key as control key with a threshold of 1 + // and the control key is the key contained in the keychain object, so we are going to use the + // key contained in the keychain object as the Subnet Auth Key for Subnet AddValidator tx subnetAuthKeys := keychain.Addresses().List() newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + + addValidatorTx, err := newSubnet.AddValidator(wallet, validatorParams) if err != nil { panic(err) } + + // Since it has the required signatures, we will now commit the transaction on chain txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) if err != nil { panic(err) diff --git a/examples/subnet_ multisig.go b/examples/subnet_ multisig.go index 5486754..1fbc0c6 100644 --- a/examples/subnet_ multisig.go +++ b/examples/subnet_ multisig.go @@ -28,9 +28,9 @@ func DeploySubnetMultiSig() { // Create three keys that will be used as control keys of the subnet // NewKeychain will generate a new key pair in the provided path if no .pk file currently // exists in the provided path - keychainA, _ := keychain.NewKeychain(network, "KEY_PATH_A") - keychainB, _ := keychain.NewKeychain(network, "KEY_PATH_B") - keychainC, _ := keychain.NewKeychain(network, "KEY_PATH_C") + keychainA, _ := keychain.NewKeychain(network, "KEY_PATH_A", nil) + keychainB, _ := keychain.NewKeychain(network, "KEY_PATH_B", nil) + keychainC, _ := keychain.NewKeychain(network, "KEY_PATH_C", nil) // In this example, we are using the fee-paying key generated above also as control key // and subnet auth key diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index 6a882e4..f4889f9 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -22,12 +22,6 @@ import ( ) func TestNodesValidatePrimaryNetwork(_ *testing.T) { - ctx := context.Background() - cp, err := GetDefaultCloudParams(ctx, AWSCloud) - if err != nil { - panic(err) - } - node := Node{ // NodeID is Avalanche Node ID of the node NodeID: "NODE_ID", @@ -40,8 +34,6 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { }, // Cloud is the cloud service that the node is on Cloud: AWSCloud, - // CloudConfig is the cloud specific configuration for the node - CloudConfig: *cp, // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor Roles: []SupportedRole{Validator}, } diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 2b2d826..076687d 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -9,22 +9,20 @@ import ( "testing" "time" - "github.com/ava-labs/avalanche-tooling-sdk-go/validator" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary" ) -func TestValidateSubnet(_ *testing.T) { +func TestValidateSubnet(t *testing.T) { subnetParams := SubnetParams{ - GenesisFilePath: "/Users/raymondsukanto/.avalanche-cli/subnets/sdkSubnetNew/genesis.json", - Name: "sdkSubnetNew", + GenesisFilePath: "GENESIS_FILE_PATH", + Name: "SUBNET_NAME", } newSubnet, err := New(&subnetParams) @@ -32,13 +30,15 @@ func TestValidateSubnet(_ *testing.T) { panic(err) } - network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "/Users/raymondsukanto/.avalanche-cli/key/newTestKeyNew.pk", nil) + // Genesis doesn't contain the deployed Subnet's SubnetID, we need to first set the Subnet ID + subnetID, err := ids.FromString("SUBNET_ID") if err != nil { panic(err) } + newSubnet.SetSubnetID(subnetID) - subnetID, err := ids.FromString("2VsqBt64W9qayKttmGTiAmtsQVnp9e9U4gSHF1yuLKHuquck5j") + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) if err != nil { panic(err) } @@ -56,7 +56,7 @@ func TestValidateSubnet(_ *testing.T) { panic(err) } - nodeID, err := ids.NodeIDFromString("NodeID-Mb3AwcUpWysCWLP6mSpzzJVgYawJWzPH") + nodeID, err := ids.NodeIDFromString("VALIDATOR_NODEID") if err != nil { panic(err) } @@ -65,11 +65,9 @@ func TestValidateSubnet(_ *testing.T) { NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, - Weight: 20, + // Setting weight of subnet validator to 20 (default value) + Weight: 20, } - fmt.Printf("adding subnet validator") - - newSubnet.SetSubnetID(subnetID) // We need to set Subnet Auth Keys for this transaction since Subnet AddValidator is // a Subnet-changing transaction @@ -80,9 +78,6 @@ func TestValidateSubnet(_ *testing.T) { subnetAuthKeys := keychain.Addresses().List() newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - // In this example, we are assuming that the specified node has already tracked the Subnet - // which can be done in Avalanche Tooling SDK through node.Sync function - // For an example, head to examples/subnet_addValidator.go addValidatorTx, err := newSubnet.AddValidator(wallet, validator) if err != nil { panic(err) @@ -93,5 +88,6 @@ func TestValidateSubnet(_ *testing.T) { if err != nil { panic(err) } + fmt.Printf("obtained tx id %s", txID.String()) } From ce57100bab3ce0f3b4b8e939a4881f4b56ffff11 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 15:53:44 +0700 Subject: [PATCH 49/56] update example --- subnet/add_validator_subnet_test.go | 31 ++++++++++------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 076687d..1488800 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -6,6 +6,7 @@ package subnet import ( "context" "fmt" + "github.com/stretchr/testify/require" "testing" "time" @@ -20,28 +21,24 @@ import ( ) func TestValidateSubnet(t *testing.T) { + require := require.New(t) subnetParams := SubnetParams{ GenesisFilePath: "GENESIS_FILE_PATH", Name: "SUBNET_NAME", } newSubnet, err := New(&subnetParams) - if err != nil { - panic(err) - } + require.NoError(err) // Genesis doesn't contain the deployed Subnet's SubnetID, we need to first set the Subnet ID subnetID, err := ids.FromString("SUBNET_ID") - if err != nil { - panic(err) - } + require.NoError(err) + newSubnet.SetSubnetID(subnetID) network := avalanche.FujiNetwork() keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) - if err != nil { - panic(err) - } + require.NoError(err) wallet, err := wallet.New( context.Background(), @@ -52,14 +49,10 @@ func TestValidateSubnet(t *testing.T) { PChainTxsToFetch: set.Of(subnetID), }, ) - if err != nil { - panic(err) - } + require.NoError(err) nodeID, err := ids.NodeIDFromString("VALIDATOR_NODEID") - if err != nil { - panic(err) - } + require.NoError(err) validator := validator.SubnetValidatorParams{ NodeID: nodeID, @@ -79,15 +72,11 @@ func TestValidateSubnet(t *testing.T) { newSubnet.SetSubnetAuthKeys(subnetAuthKeys) addValidatorTx, err := newSubnet.AddValidator(wallet, validator) - if err != nil { - panic(err) - } + require.NoError(err) // Since it has the required signatures, we will now commit the transaction on chain txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) - if err != nil { - panic(err) - } + require.NoError(err) fmt.Printf("obtained tx id %s", txID.String()) } From e17cde5888ffd9dc0c4155aef5c3b49d4b2a24f7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 16:36:04 +0700 Subject: [PATCH 50/56] update example --- examples/node.go | 9 --- examples/node_validate_primary.go | 76 +++++++++++++++++++ ...dValidator.go => subnet_ add_validator.go} | 9 ++- node/add_validator_primary_test.go | 25 +++--- 4 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 examples/node_validate_primary.go rename examples/{subnet_ addValidator.go => subnet_ add_validator.go} (92%) diff --git a/examples/node.go b/examples/node.go index 01d13fa..ed1a457 100644 --- a/examples/node.go +++ b/examples/node.go @@ -102,15 +102,6 @@ func CreateNodes() { } } - // examle of how to reconfigure the created nodes to track a subnet - subnetIDsToValidate := []string{"xxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyzzzzzzzzzzzzzzz"} - for _, h := range hosts { - fmt.Printf("Reconfiguring node %s to track subnet %s", h.NodeID, subnetIDsToValidate) - if err := h.SyncSubnets(subnetIDsToValidate); err != nil { - panic(err) - } - } - // Create a monitoring node. // Monitoring node enables you to have a centralized Grafana Dashboard where you can view // metrics relevant to any Validator & API nodes that the monitoring node is linked to as well diff --git a/examples/node_validate_primary.go b/examples/node_validate_primary.go new file mode 100644 index 0000000..ea108d9 --- /dev/null +++ b/examples/node_validate_primary.go @@ -0,0 +1,76 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package examples + +import ( + "context" + "fmt" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" +) + +func ValidatePrimaryNetwork() { + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NODE_ID", + // IP address of the node + IP: "NODE_IP_ADDRESS", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", + }, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validatorParams := validator.PrimaryNetworkValidatorParams{ + NodeID: nodeID, + // Validate Primary Network for 48 hours + Duration: 48 * time.Hour, + // Stake 2 AVAX + StakeAmount: 2 * units.Avax, + } + + // Key that will be used for paying the transaction fee of AddValidator Tx + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) + if err != nil { + panic(err) + } + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: nil, + }, + ) + if err != nil { + panic(err) + } + + txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validatorParams, wallet) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} diff --git a/examples/subnet_ addValidator.go b/examples/subnet_ add_validator.go similarity index 92% rename from examples/subnet_ addValidator.go rename to examples/subnet_ add_validator.go index 07e45a0..1cfba70 100644 --- a/examples/subnet_ addValidator.go +++ b/examples/subnet_ add_validator.go @@ -21,6 +21,7 @@ import ( ) func AddSubnetValidator() { + // We are using existing Subnet that we have already deployed on Fuji subnetParams := subnet.SubnetParams{ GenesisFilePath: "GENESIS_FILE_PATH", Name: "SUBNET_NAME", @@ -35,8 +36,11 @@ func AddSubnetValidator() { if err != nil { panic(err) } + + // Genesis doesn't contain the deployed Subnet's SubnetID, we need to first set the Subnet ID newSubnet.SetSubnetID(subnetID) + // We are using existing host node := node.Node{ // NodeID is Avalanche Node ID of the node NodeID: "NODE_ID", @@ -47,9 +51,8 @@ func AddSubnetValidator() { User: constants.RemoteHostUser, PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", }, - // Cloud is the cloud service that the node is on - Cloud: node.AWSCloud, - + // Role is the role that we expect the host to be (Validator, API, AWMRelayer, Loadtest or + // Monitor) Roles: []node.SupportedRole{node.Validator}, } diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index f4889f9..c070232 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -6,6 +6,7 @@ package node import ( "context" "fmt" + "github.com/stretchr/testify/require" "testing" "time" @@ -21,7 +22,9 @@ import ( "github.com/ava-labs/avalanchego/wallet/subnet/primary" ) -func TestNodesValidatePrimaryNetwork(_ *testing.T) { +func TestNodesValidatePrimaryNetwork(t *testing.T) { + require := require.New(t) + // We are using an existing host node := Node{ // NodeID is Avalanche Node ID of the node NodeID: "NODE_ID", @@ -32,16 +35,12 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { User: constants.RemoteHostUser, PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", }, - // Cloud is the cloud service that the node is on - Cloud: AWSCloud, // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor Roles: []SupportedRole{Validator}, } nodeID, err := ids.NodeIDFromString(node.NodeID) - if err != nil { - panic(err) - } + require.NoError(err) validator := validator.PrimaryNetworkValidatorParams{ NodeID: nodeID, @@ -52,11 +51,8 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { } network := avalanche.FujiNetwork() - keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) - if err != nil { - panic(err) - } + require.NoError(err) wallet, err := wallet.New( context.Background(), @@ -67,13 +63,10 @@ func TestNodesValidatePrimaryNetwork(_ *testing.T) { PChainTxsToFetch: nil, }, ) - if err != nil { - panic(err) - } + require.NoError(err) txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validator, wallet) - if err != nil { - panic(err) - } + require.NoError(err) + fmt.Printf("obtained tx id %s", txID.String()) } From fe59f1e588cbdd7eefabe74bdd9f81070f7290a2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 16:42:34 +0700 Subject: [PATCH 51/56] fix lint --- examples/node_validate_primary.go | 3 ++- node/add_validator_primary_test.go | 3 ++- subnet/add_validator_subnet_test.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/node_validate_primary.go b/examples/node_validate_primary.go index ea108d9..6d682ae 100644 --- a/examples/node_validate_primary.go +++ b/examples/node_validate_primary.go @@ -6,6 +6,8 @@ package examples import ( "context" "fmt" + "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" "github.com/ava-labs/avalanche-tooling-sdk-go/validator" @@ -14,7 +16,6 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary" - "time" "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/node" diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index c070232..b06d08c 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -6,10 +6,11 @@ package node import ( "context" "fmt" - "github.com/stretchr/testify/require" "testing" "time" + "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanche-tooling-sdk-go/validator" "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index 1488800..a0e6846 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -6,10 +6,11 @@ package subnet import ( "context" "fmt" - "github.com/stretchr/testify/require" "testing" "time" + "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" "github.com/ava-labs/avalanche-tooling-sdk-go/validator" From 69f2cb43540da373cbf953d68e89a829d96a1481 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 16:44:57 +0700 Subject: [PATCH 52/56] update example --- examples/node_validate_primary.go | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/node_validate_primary.go b/examples/node_validate_primary.go index 6d682ae..449c91e 100644 --- a/examples/node_validate_primary.go +++ b/examples/node_validate_primary.go @@ -22,6 +22,7 @@ import ( ) func ValidatePrimaryNetwork() { + // We are using existing host node := node.Node{ // NodeID is Avalanche Node ID of the node NodeID: "NODE_ID", From 5a207c680ab3fd18319b37dfa5c480463d2ab154 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 16:54:26 +0700 Subject: [PATCH 53/56] update example --- node/create_test.go | 46 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/node/create_test.go b/node/create_test.go index 7fc9eaf..32ed51b 100644 --- a/node/create_test.go +++ b/node/create_test.go @@ -6,6 +6,7 @@ package node import ( "context" "fmt" + "github.com/stretchr/testify/require" "testing" "time" @@ -15,28 +16,26 @@ import ( "github.com/ava-labs/avalanche-tooling-sdk-go/utils" ) -func TestCreateNodes(_ *testing.T) { +func TestCreateNodes(t *testing.T) { + require := require.New(t) ctx := context.Background() // Get the default cloud parameters for AWS cp, err := GetDefaultCloudParams(ctx, AWSCloud) - if err != nil { - panic(err) - } + require.NoError(err) securityGroupName := "SECURITY_GROUP_NAME" sgID, err := awsAPI.CreateSecurityGroup(ctx, securityGroupName, cp.AWSConfig.AWSProfile, cp.Region) - if err != nil { - panic(err) - } + require.NoError(err) + // Set the security group we are using when creating our Avalanche Nodes cp.AWSConfig.AWSSecurityGroupID = sgID cp.AWSConfig.AWSSecurityGroupName = securityGroupName keyPairName := "KEY_PAIR_NAME" sshPrivateKeyPath := utils.ExpandHome("PRIVATE_KEY_FILEPATH") - if err := awsAPI.CreateSSHKeyPair(ctx, cp.AWSConfig.AWSProfile, cp.Region, keyPairName, sshPrivateKeyPath); err != nil { - panic(err) - } + err = awsAPI.CreateSSHKeyPair(ctx, cp.AWSConfig.AWSProfile, cp.Region, keyPairName, sshPrivateKeyPath) + require.NoError(err) + // Set the key pair we are using when creating our Avalanche Nodes cp.AWSConfig.AWSKeyPair = keyPairName @@ -64,9 +63,7 @@ func TestCreateNodes(_ *testing.T) { UseStaticIP: false, SSHPrivateKeyPath: sshPrivateKeyPath, }) - if err != nil { - panic(err) - } + require.NoError(err) fmt.Println("Successfully created Avalanche Validators") @@ -80,12 +77,12 @@ func TestCreateNodes(_ *testing.T) { // Wait for the host to be ready (only needs to be done once for newly created nodes) fmt.Println("Waiting for SSH shell") if err := h.WaitForSSHShell(sshTimeout); err != nil { - panic(err) + require.NoError(err) } fmt.Println("SSH shell ready to execute commands") // Run a command on the host if output, err := h.Commandf(nil, sshCommandTimeout, "echo 'Hello, %s!'", "World"); err != nil { - panic(err) + require.NoError(err) } else { fmt.Println(string(output)) } @@ -93,7 +90,7 @@ func TestCreateNodes(_ *testing.T) { time.Sleep(10 * time.Second) // check if avalanchego is running if output, err := h.Commandf(nil, sshCommandTimeout, "docker ps"); err != nil { - panic(err) + require.NoError(err) } else { fmt.Println(string(output)) } @@ -112,30 +109,23 @@ func TestCreateNodes(_ *testing.T) { UseStaticIP: false, SSHPrivateKeyPath: sshPrivateKeyPath, }) - if err != nil { - panic(err) - } + require.NoError(err) fmt.Println("Successfully created monitoring node") fmt.Println("Linking monitoring node with Avalanche Validator nodes ...") // Link the 2 validator nodes previously created with the monitoring host so that // the monitoring host can start tracking the validator nodes metrics and collecting their logs - if err := monitoringHosts[0].MonitorNodes(ctx, hosts, ""); err != nil { - panic(err) - } + err = monitoringHosts[0].MonitorNodes(ctx, hosts, "") + require.NoError(err) fmt.Println("Successfully linked monitoring node with Avalanche Validator nodes") fmt.Println("Terminating all created nodes ...") // Destroy all created nodes for _, h := range hosts { err = h.Destroy(ctx) - if err != nil { - panic(err) - } + require.NoError(err) } err = monitoringHosts[0].Destroy(ctx) - if err != nil { - panic(err) - } + require.NoError(err) fmt.Println("All nodes terminated") } From a3da517ab39bd5cd5c80e1b60cfe24e8d3fe1974 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 17:06:48 +0700 Subject: [PATCH 54/56] fix lint --- node/create_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/create_test.go b/node/create_test.go index 32ed51b..9030f14 100644 --- a/node/create_test.go +++ b/node/create_test.go @@ -6,10 +6,11 @@ package node import ( "context" "fmt" - "github.com/stretchr/testify/require" "testing" "time" + "github.com/stretchr/testify/require" + awsAPI "github.com/ava-labs/avalanche-tooling-sdk-go/cloud/aws" "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" From ef4471bb9c3dfe69275de2fff158a9a23c078bb5 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 19:11:13 +0700 Subject: [PATCH 55/56] change validator name --- node/add_validator_primary_test.go | 4 ++-- subnet/add_validator_subnet_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/add_validator_primary_test.go b/node/add_validator_primary_test.go index b06d08c..45b01ed 100644 --- a/node/add_validator_primary_test.go +++ b/node/add_validator_primary_test.go @@ -43,7 +43,7 @@ func TestNodesValidatePrimaryNetwork(t *testing.T) { nodeID, err := ids.NodeIDFromString(node.NodeID) require.NoError(err) - validator := validator.PrimaryNetworkValidatorParams{ + validatorParams := validator.PrimaryNetworkValidatorParams{ NodeID: nodeID, // Validate Primary Network for 48 hours Duration: 48 * time.Hour, @@ -66,7 +66,7 @@ func TestNodesValidatePrimaryNetwork(t *testing.T) { ) require.NoError(err) - txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validator, wallet) + txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validatorParams, wallet) require.NoError(err) fmt.Printf("obtained tx id %s", txID.String()) diff --git a/subnet/add_validator_subnet_test.go b/subnet/add_validator_subnet_test.go index a0e6846..d885c8c 100644 --- a/subnet/add_validator_subnet_test.go +++ b/subnet/add_validator_subnet_test.go @@ -55,7 +55,7 @@ func TestValidateSubnet(t *testing.T) { nodeID, err := ids.NodeIDFromString("VALIDATOR_NODEID") require.NoError(err) - validator := validator.SubnetValidatorParams{ + validatorParams := validator.SubnetValidatorParams{ NodeID: nodeID, // Validate Subnet for 48 hours Duration: 48 * time.Hour, @@ -72,7 +72,7 @@ func TestValidateSubnet(t *testing.T) { subnetAuthKeys := keychain.Addresses().List() newSubnet.SetSubnetAuthKeys(subnetAuthKeys) - addValidatorTx, err := newSubnet.AddValidator(wallet, validator) + addValidatorTx, err := newSubnet.AddValidator(wallet, validatorParams) require.NoError(err) // Since it has the required signatures, we will now commit the transaction on chain From a638afe984e65e35dfbe89181061eb3413993686 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 23 Aug 2024 22:31:28 +0700 Subject: [PATCH 56/56] update readme --- README.md | 185 ++++++++++++++++++++++++++++++++++++++++++--- examples/subnet.go | 20 +++-- 2 files changed, 189 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 19d9422..7935159 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,21 @@ The official Avalanche Tooling Go SDK library. *** Please note that this SDK is in experimental mode, major changes to the SDK are to be expected in between releases *** -Current version (v0.2.0) currently supports: +Current version (v0.3.0) currently supports: - Create Subnet and Create Blockchain in a Subnet in Fuji / Mainnet. - Create Avalanche Node (Validator / API / Monitoring / Load Test Node) & install all required dependencies (AvalancheGo, gcc, Promtail, Grafana, etc). +- Enable Avalanche nodes to validate Primary Network +- Adding Validators to a Subnet +- Ledger SDK Currently, only stored keys are supported for transaction building and signing, ledger support is coming soon. Future SDK releases will contain the following features (in order of priority): -- Additional Nodes SDK features (Have Avalanche nodes validate a Subnet & Primary Network) -- Additional Subnet SDK features (i.e. Adding Validators to a Subnet, Custom Subnets) +- Additional Nodes SDK features (Devnet) +- Additional Subnet SDK features (Custom Subnets) - Teleporter SDK -- Ledger SDK ## Getting Started @@ -41,6 +43,8 @@ This examples also shows how to create a key pair to pay for transactions, how t object that will be used to build and sign CreateSubnetTx and CreateChainTx and how to commit these transactions on chain. +More examples can be found at examples directory. + ```go package main @@ -74,7 +78,7 @@ func DeploySubnet() { // Key that will be used for paying the transaction fees of CreateSubnetTx and CreateChainTx // NewKeychain will generate a new key pair in the provided path if no .pk file currently // exists in the provided path - keychain, _ := keychain.NewKeychain(network, "KEY_PATH") + keychain, _ := keychain.NewKeychain(network, "KEY_PATH", nil) // In this example, we are using the fee-paying key generated above also as control key // and subnet auth key @@ -91,10 +95,9 @@ func DeploySubnet() { // CreateSubnetTx. // // All keys in subnetAuthKeys have to sign the transaction before the transaction - // can be committed on chain - subnetAuthKeys := controlKeys + subnetAuthKeys := keychain.Addresses().List() threshold := 1 - newSubnet.SetSubnetCreateParams(controlKeys, uint32(threshold)) + newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) wallet, _ := wallet.New( context.Background(), @@ -115,7 +118,7 @@ func DeploySubnet() { // we need to wait to allow the transaction to reach other nodes in Fuji time.Sleep(2 * time.Second) - newSubnet.SetBlockchainCreateParams(subnetAuthKeys) + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) // Build and Sign CreateChainTx with our fee paying key (which is also our subnet auth key) deployChainTx, _ := newSubnet.CreateBlockchainTx(wallet) // Commit our CreateChainTx on chain @@ -125,6 +128,111 @@ func DeploySubnet() { fmt.Printf("blockchainID %s \n", blockchainID.String()) } +// Add a validator to Subnet +func AddSubnetValidator() { + // We are using existing Subnet that we have already deployed on Fuji + subnetParams := subnet.SubnetParams{ + GenesisFilePath: "GENESIS_FILE_PATH", + Name: "SUBNET_NAME", + } + + newSubnet, err := subnet.New(&subnetParams) + if err != nil { + panic(err) + } + + subnetID, err := ids.FromString("SUBNET_ID") + if err != nil { + panic(err) + } + + // Genesis doesn't contain the deployed Subnet's SubnetID, we need to first set the Subnet ID + newSubnet.SetSubnetID(subnetID) + + // We are using existing host + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NODE_ID", + // IP address of the node + IP: "NODE_IP_ADDRESS", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", + }, + // Role is the role that we expect the host to be (Validator, API, AWMRelayer, Loadtest or + // Monitor) + Roles: []node.SupportedRole{node.Validator}, + } + + // Here we are assuming that the node is currently validating the Primary Network, which is + // a requirement before the node can start validating a Subnet. + // To have a node validate the Primary Network, call node.ValidatePrimaryNetwork + // Now we are calling the node to start tracking the Subnet + subnetIDsToValidate := []string{newSubnet.SubnetID.String()} + if err := node.SyncSubnets(subnetIDsToValidate); err != nil { + panic(err) + } + + // Node is now tracking the Subnet + + // Key that will be used for paying the transaction fees of Subnet AddValidator Tx + // + // In our example, this Key is also the control Key to the Subnet, so we are going to use + // this key to also sign the Subnet AddValidator tx + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) + if err != nil { + panic(err) + } + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: set.Of(subnetID), + }, + ) + if err != nil { + panic(err) + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validatorParams := validator.SubnetValidatorParams{ + NodeID: nodeID, + // Validate Subnet for 48 hours + Duration: 48 * time.Hour, + Weight: 20, + } + + // We need to set Subnet Auth Keys for this transaction since Subnet AddValidator is + // a Subnet-changing transaction + // + // In this example, the example Subnet was created with only 1 key as control key with a threshold of 1 + // and the control key is the key contained in the keychain object, so we are going to use the + // key contained in the keychain object as the Subnet Auth Key for Subnet AddValidator tx + subnetAuthKeys := keychain.Addresses().List() + newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + + addValidatorTx, err := newSubnet.AddValidator(wallet, validatorParams) + if err != nil { + panic(err) + } + + // Since it has the required signatures, we will now commit the transaction on chain + txID, err := newSubnet.Commit(*addValidatorTx, wallet, true) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} + func getDefaultSubnetEVMGenesis() subnet.SubnetParams { allocation := core.GenesisAlloc{} defaultAmount, _ := new(big.Int).SetString(vm.DefaultEvmAirdropAmount, 10) @@ -153,6 +261,8 @@ centralized Grafana Dashboard where you can view metrics relevant to any Validat the monitoring node is linked to as well as a centralized logs for the X/P/C Chain and Subnet logs for the Validator & API nodes. An example on how the dashboard and logs look like can be found at https://docs.avax.network/tooling/cli-create-nodes/create-a-validator-aws +More examples can be found at examples directory. + ```go package main @@ -278,4 +388,61 @@ func CreateNodes() { panic(err) } } + +func ValidatePrimaryNetwork() { + // We are using existing host + node := node.Node{ + // NodeID is Avalanche Node ID of the node + NodeID: "NODE_ID", + // IP address of the node + IP: "NODE_IP_ADDRESS", + // SSH configuration for the node + SSHConfig: node.SSHConfig{ + User: constants.RemoteHostUser, + PrivateKeyPath: "NODE_KEYPAIR_PRIVATE_KEY_PATH", + }, + // Role of the node can be Validator, API, AWMRelayer, Loadtest, or Monitor + Roles: []node.SupportedRole{node.Validator}, + } + + nodeID, err := ids.NodeIDFromString(node.NodeID) + if err != nil { + panic(err) + } + + validatorParams := validator.PrimaryNetworkValidatorParams{ + NodeID: nodeID, + // Validate Primary Network for 48 hours + Duration: 48 * time.Hour, + // Stake 2 AVAX + StakeAmount: 2 * units.Avax, + } + + // Key that will be used for paying the transaction fee of AddValidator Tx + network := avalanche.FujiNetwork() + keychain, err := keychain.NewKeychain(network, "PRIVATE_KEY_FILEPATH", nil) + if err != nil { + panic(err) + } + + wallet, err := wallet.New( + context.Background(), + &primary.WalletConfig{ + URI: network.Endpoint, + AVAXKeychain: keychain.Keychain, + EthKeychain: secp256k1fx.NewKeychain(), + PChainTxsToFetch: nil, + }, + ) + if err != nil { + panic(err) + } + + txID, err := node.ValidatePrimaryNetwork(avalanche.FujiNetwork(), validatorParams, wallet) + if err != nil { + panic(err) + } + fmt.Printf("obtained tx id %s", txID.String()) +} + ``` diff --git a/examples/subnet.go b/examples/subnet.go index 4a055ab..b6d7ecf 100644 --- a/examples/subnet.go +++ b/examples/subnet.go @@ -55,16 +55,18 @@ func DeploySubnet() { // In this example, we are using the fee-paying key generated above also as control key // and subnet auth key - // control keys are a list of keys that are permitted to make changes to a Subnet + // Control keys are a list of keys that are permitted to make changes to a Subnet // such as creating a blockchain in the Subnet and adding validators to the Subnet controlKeys := keychain.Addresses().List() - // subnet auth keys are a subset of control keys + // Subnet auth keys are a subset of control keys that will be used to sign transactions that + // modify a Subnet (such as creating a blockchain in the Subnet and adding validators to the + // Subnet) // - // they are the keys that will be used to sign transactions that modify a Subnet - // number of keys in subnetAuthKeys has to be more than or equal to threshold - // all keys in subnetAuthKeys have to sign the transaction before the transaction - // can be committed on chain + // Number of keys in subnetAuthKeys has to be equal to the threshold value provided during + // CreateSubnetTx. + // + // All keys in subnetAuthKeys have to sign the transaction before the transaction subnetAuthKeys := keychain.Addresses().List() threshold := 1 newSubnet.SetSubnetControlParams(controlKeys, uint32(threshold)) @@ -79,7 +81,9 @@ func DeploySubnet() { }, ) + // Build and Sign CreateSubnetTx with our fee paying key deploySubnetTx, _ := newSubnet.CreateSubnetTx(wallet) + // Commit our CreateSubnetTx on chain subnetID, _ := newSubnet.Commit(*deploySubnetTx, wallet, true) fmt.Printf("subnetID %s \n", subnetID.String()) @@ -87,8 +91,10 @@ func DeploySubnet() { time.Sleep(2 * time.Second) newSubnet.SetSubnetAuthKeys(subnetAuthKeys) + // Build and Sign CreateChainTx with our fee paying key (which is also our subnet auth key) deployChainTx, _ := newSubnet.CreateBlockchainTx(wallet) - // since we are using the fee paying key as control key too, we can commit the transaction + // Commit our CreateChainTx on chain + // Since we are using the fee paying key as control key too, we can commit the transaction // on chain immediately since the number of signatures has been reached blockchainID, _ := newSubnet.Commit(*deployChainTx, wallet, true) fmt.Printf("blockchainID %s \n", blockchainID.String())