From 159aafbf32a4ae219d0394a7d80af19a11244507 Mon Sep 17 00:00:00 2001 From: marun Date: Sun, 10 Dec 2023 16:53:13 -0800 Subject: [PATCH] Update tmpnet fixture to include Proof-of-Possession for initial stakers (#2391) --- tests/fixture/tmpnet/config.go | 73 +++++++++++++-------------- tests/fixture/tmpnet/local/network.go | 44 +++++++++++++--- 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/tests/fixture/tmpnet/config.go b/tests/fixture/tmpnet/config.go index f504eb84d20d..a86b2f26c51a 100644 --- a/tests/fixture/tmpnet/config.go +++ b/tests/fixture/tmpnet/config.go @@ -29,7 +29,7 @@ import ( "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" ) const ( @@ -49,14 +49,13 @@ var ( // Arbitrarily large amount of AVAX (10^12) to fund keys on the C-Chain for testing DefaultFundedKeyCChainAmount = new(big.Int).Exp(big.NewInt(10), big.NewInt(30), nil) - errEmptyValidatorsForGenesis = errors.New("failed to generate genesis: empty validator IDs") - errNoKeysForGenesis = errors.New("failed to generate genesis: no keys to fund") - errInvalidNetworkIDForGenesis = errors.New("network ID can't be mainnet, testnet or local network ID") - errMissingValidatorsForGenesis = errors.New("no genesis validators provided") - errMissingBalancesForGenesis = errors.New("no genesis balances given") - errMissingTLSKeyForNodeID = fmt.Errorf("failed to ensure node ID: missing value for %q", config.StakingTLSKeyContentKey) - errMissingCertForNodeID = fmt.Errorf("failed to ensure node ID: missing value for %q", config.StakingCertContentKey) - errInvalidKeypair = fmt.Errorf("%q and %q must be provided together or not at all", config.StakingTLSKeyContentKey, config.StakingCertContentKey) + errNoKeysForGenesis = errors.New("failed to generate genesis: no keys to fund") + errInvalidNetworkIDForGenesis = errors.New("network ID can't be mainnet, testnet or local network ID") + errMissingStakersForGenesis = errors.New("no genesis stakers provided") + errMissingBalancesForGenesis = errors.New("no genesis balances given") + errMissingTLSKeyForNodeID = fmt.Errorf("failed to ensure node ID: missing value for %q", config.StakingTLSKeyContentKey) + errMissingCertForNodeID = fmt.Errorf("failed to ensure node ID: missing value for %q", config.StakingCertContentKey) + errInvalidKeypair = fmt.Errorf("%q and %q must be provided together or not at all", config.StakingTLSKeyContentKey, config.StakingCertContentKey) ) // Defines a mapping of flag keys to values intended to be supplied to @@ -129,14 +128,11 @@ type NetworkConfig struct { } // Ensure genesis is generated if not already present. -func (c *NetworkConfig) EnsureGenesis(networkID uint32, validatorIDs []ids.NodeID) error { +func (c *NetworkConfig) EnsureGenesis(networkID uint32, initialStakers []genesis.UnparsedStaker) error { if c.Genesis != nil { return nil } - if len(validatorIDs) == 0 { - return errEmptyValidatorsForGenesis - } if len(c.FundedKeys) == 0 { return errNoKeysForGenesis } @@ -151,7 +147,7 @@ func (c *NetworkConfig) EnsureGenesis(networkID uint32, validatorIDs []ids.NodeI } } - genesis, err := NewTestGenesis(networkID, xChainBalances, cChainBalances, validatorIDs) + genesis, err := NewTestGenesis(networkID, xChainBalances, cChainBalances, initialStakers) if err != nil { return err } @@ -206,6 +202,24 @@ func (nc *NodeConfig) EnsureKeys() error { return nc.EnsureNodeID() } +// Derives the nodes proof-of-possession. Requires the node to have a +// BLS signing key. +func (nc *NodeConfig) GetProofOfPossession() (*signer.ProofOfPossession, error) { + signingKey, err := nc.Flags.GetStringVal(config.StakingSignerKeyContentKey) + if err != nil { + return nil, err + } + signingKeyBytes, err := base64.StdEncoding.DecodeString(signingKey) + if err != nil { + return nil, err + } + secretKey, err := bls.SecretKeyFromBytes(signingKeyBytes) + if err != nil { + return nil, err + } + return signer.NewProofOfPossession(secretKey), nil +} + // Ensures a BLS signing key is generated if not already present. func (nc *NodeConfig) EnsureBLSSigningKey() error { // Attempt to retrieve an existing key @@ -312,15 +326,15 @@ func NewTestGenesis( networkID uint32, xChainBalances XChainBalanceMap, cChainBalances core.GenesisAlloc, - validatorIDs []ids.NodeID, + initialStakers []genesis.UnparsedStaker, ) (*genesis.UnparsedConfig, error) { // Validate inputs switch networkID { case constants.TestnetID, constants.MainnetID, constants.LocalID: return nil, errInvalidNetworkIDForGenesis } - if len(validatorIDs) == 0 { - return nil, errMissingValidatorsForGenesis + if len(initialStakers) == 0 { + return nil, errMissingStakersForGenesis } if len(xChainBalances) == 0 || len(cChainBalances) == 0 { return nil, errMissingBalancesForGenesis @@ -336,8 +350,8 @@ func NewTestGenesis( return nil, fmt.Errorf("failed to format stake address: %w", err) } - // Ensure the total stake allows a MegaAvax per validator - totalStake := uint64(len(validatorIDs)) * units.MegaAvax + // Ensure the total stake allows a MegaAvax per staker + totalStake := uint64(len(initialStakers)) * units.MegaAvax // The eth address is only needed to link pre-mainnet assets. Until that capability // becomes necessary for testing, use a bogus address. @@ -367,6 +381,7 @@ func NewTestGenesis( InitialStakeDuration: 365 * 24 * 60 * 60, // 1 year InitialStakeDurationOffset: 90 * 60, // 90 minutes Message: "hello avalanche!", + InitialStakers: initialStakers, } // Set X-Chain balances @@ -409,25 +424,5 @@ func NewTestGenesis( } config.CChainGenesis = string(cChainGenesisBytes) - // Give staking rewards for initial validators to a random address. Any testing of staking rewards - // will be easier to perform with nodes other than the initial validators since the timing of - // staking can be more easily controlled. - rewardAddr, err := address.Format("X", constants.GetHRP(networkID), ids.GenerateTestShortID().Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to format reward address: %w", err) - } - - // Configure provided validator node IDs as initial stakers - for _, validatorID := range validatorIDs { - config.InitialStakers = append( - config.InitialStakers, - genesis.UnparsedStaker{ - NodeID: validatorID, - RewardAddress: rewardAddr, - DelegationFee: .01 * reward.PercentDenominator, - }, - ) - } - return config, nil } diff --git a/tests/fixture/tmpnet/local/network.go b/tests/fixture/tmpnet/local/network.go index 70411d4afcde..ea55f5f5968c 100644 --- a/tests/fixture/tmpnet/local/network.go +++ b/tests/fixture/tmpnet/local/network.go @@ -21,8 +21,10 @@ import ( "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/utils/perms" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" ) const ( @@ -238,18 +240,18 @@ func (ln *LocalNetwork) PopulateLocalNetworkConfig(networkID uint32, nodeCount i } // Ensure each node has keys and an associated node ID. This - // ensures the availability of validator node IDs for genesis - // generation. + // ensures the availability of node IDs and proofs of possession + // for genesis generation. for _, node := range ln.Nodes { if err := node.EnsureKeys(); err != nil { return err } } - // Assume all initial nodes are validator ids - validatorIDs := make([]ids.NodeID, 0, len(ln.Nodes)) - for _, node := range ln.Nodes { - validatorIDs = append(validatorIDs, node.NodeID) + // Assume all the initial nodes are stakers + initialStakers, err := stakersForNodes(networkID, ln.Nodes) + if err != nil { + return err } if keyCount > 0 { @@ -265,7 +267,7 @@ func (ln *LocalNetwork) PopulateLocalNetworkConfig(networkID uint32, nodeCount i ln.FundedKeys = keys } - if err := ln.EnsureGenesis(networkID, validatorIDs); err != nil { + if err := ln.EnsureGenesis(networkID, initialStakers); err != nil { return err } @@ -713,3 +715,31 @@ func (ln *LocalNetwork) GetBootstrapIPsAndIDs() ([]string, []string, error) { return bootstrapIPs, bootstrapIDs, nil } + +// Returns staker configuration for the given set of nodes. +func stakersForNodes(networkID uint32, nodes []*LocalNode) ([]genesis.UnparsedStaker, error) { + // Give staking rewards for initial validators to a random address. Any testing of staking rewards + // will be easier to perform with nodes other than the initial validators since the timing of + // staking can be more easily controlled. + rewardAddr, err := address.Format("X", constants.GetHRP(networkID), ids.GenerateTestShortID().Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to format reward address: %w", err) + } + + // Configure provided nodes as initial stakers + initialStakers := make([]genesis.UnparsedStaker, len(nodes)) + for i, node := range nodes { + pop, err := node.GetProofOfPossession() + if err != nil { + return nil, fmt.Errorf("failed to derive proof of possession: %w", err) + } + initialStakers[i] = genesis.UnparsedStaker{ + NodeID: node.NodeID, + RewardAddress: rewardAddr, + DelegationFee: .01 * reward.PercentDenominator, + Signer: pop, + } + } + + return initialStakers, nil +}