Skip to content

Update tmpnet fixture to include Proof-of-Possession for initial stakers #2391

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 34 additions & 39 deletions tests/fixture/tmpnet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
44 changes: 37 additions & 7 deletions tests/fixture/tmpnet/local/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}

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