Skip to content

Commit

Permalink
Merge branch 'main' into add-relayer
Browse files Browse the repository at this point in the history
  • Loading branch information
felipemadero committed Aug 30, 2024
2 parents 29895f6 + 2c429fa commit d316064
Show file tree
Hide file tree
Showing 33 changed files with 1,396 additions and 463 deletions.
185 changes: 176 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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(),
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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())
}

```
4 changes: 0 additions & 4 deletions avalanche/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import (
"os"
)

//
// Public constants
//

const (
// LevelNull sets a logger to show no messages at all.
LevelNull Level = 0
Expand Down
14 changes: 14 additions & 0 deletions avalanche/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (
"fmt"
"strings"

"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
Expand Down Expand Up @@ -104,3 +107,14 @@ func (n Network) BlockchainWSEndpoint(blockchainID string) string {
trimmedURI = strings.TrimPrefix(trimmedURI, "https://")
return fmt.Sprintf("ws://%s/ext/bc/%s/ws", trimmedURI, blockchainID)
}

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
}
2 changes: 1 addition & 1 deletion cloud/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
19 changes: 10 additions & 9 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,24 @@ const (
AvalanchegoMachineMetricsPort = 9100
AvalanchegoLoadTestPort = 8082

// http
APIRequestTimeout = 30 * time.Second
APIRequestLargeTimeout = 2 * time.Minute

// ssh
SSHSleepBetweenChecks = 1 * time.Second
SSHLongRunningScriptTimeout = 10 * time.Minute
SSHFileOpsTimeout = 100 * time.Second
SSHPOSTTimeout = 10 * time.Second
SSHScriptTimeout = 2 * time.Minute
AnsibleSSHUser = "ubuntu"
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"

CloudNodeCLIConfigBasePath = "/home/ubuntu/.avalanche-cli/"
CloudNodeStakingPath = "/home/ubuntu/.avalanchego/staking/"
CloudNodeConfigPath = "/home/ubuntu/.avalanchego/configs/"
ServicesDir = "services"
DashboardsDir = "dashboards"
// services
ServiceAvalanchego = "avalanchego"
ServicePromtail = "promtail"
Expand Down
Loading

0 comments on commit d316064

Please sign in to comment.