github.com/MetalBlockchain/metalgo@v1.11.9/tests/fixture/tmpnet/genesis.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package tmpnet 5 6 import ( 7 "encoding/json" 8 "errors" 9 "fmt" 10 "math/big" 11 "time" 12 13 "github.com/MetalBlockchain/coreth/core" 14 "github.com/MetalBlockchain/coreth/params" 15 "github.com/MetalBlockchain/coreth/plugin/evm" 16 17 "github.com/MetalBlockchain/metalgo/genesis" 18 "github.com/MetalBlockchain/metalgo/ids" 19 "github.com/MetalBlockchain/metalgo/utils/constants" 20 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 21 "github.com/MetalBlockchain/metalgo/utils/formatting/address" 22 "github.com/MetalBlockchain/metalgo/utils/units" 23 "github.com/MetalBlockchain/metalgo/vms/platformvm/reward" 24 ) 25 26 const ( 27 defaultGasLimit = uint64(100_000_000) // Gas limit is arbitrary 28 29 // Arbitrarily large amount of AVAX to fund keys on the X-Chain for testing 30 defaultFundedKeyXChainAmount = 30 * units.MegaAvax 31 ) 32 33 var ( 34 // Arbitrarily large amount of AVAX (10^12) to fund keys on the C-Chain for testing 35 defaultFundedKeyCChainAmount = new(big.Int).Exp(big.NewInt(10), big.NewInt(30), nil) 36 37 errNoKeysForGenesis = errors.New("no keys to fund for genesis") 38 errInvalidNetworkIDForGenesis = errors.New("network ID can't be mainnet, testnet or local network ID for genesis") 39 errMissingStakersForGenesis = errors.New("no stakers provided for genesis") 40 ) 41 42 // Helper type to simplify configuring X-Chain genesis balances 43 type XChainBalanceMap map[ids.ShortID]uint64 44 45 // Create a genesis struct valid for bootstrapping a test 46 // network. Note that many of the genesis fields (e.g. reward 47 // addresses) are randomly generated or hard-coded. 48 func NewTestGenesis( 49 networkID uint32, 50 nodes []*Node, 51 keysToFund []*secp256k1.PrivateKey, 52 ) (*genesis.UnparsedConfig, error) { 53 // Validate inputs 54 switch networkID { 55 case constants.TahoeID, constants.MainnetID, constants.LocalID: 56 return nil, errInvalidNetworkIDForGenesis 57 } 58 if len(nodes) == 0 { 59 return nil, errMissingStakersForGenesis 60 } 61 if len(keysToFund) == 0 { 62 return nil, errNoKeysForGenesis 63 } 64 65 initialStakers, err := stakersForNodes(networkID, nodes) 66 if err != nil { 67 return nil, fmt.Errorf("failed to configure stakers for nodes: %w", err) 68 } 69 70 // Address that controls stake doesn't matter -- generate it randomly 71 stakeAddress, err := address.Format( 72 "X", 73 constants.GetHRP(networkID), 74 ids.GenerateTestShortID().Bytes(), 75 ) 76 if err != nil { 77 return nil, fmt.Errorf("failed to format stake address: %w", err) 78 } 79 80 // Ensure the total stake allows a MegaAvax per staker 81 totalStake := uint64(len(initialStakers)) * units.MegaAvax 82 83 // The eth address is only needed to link pre-mainnet assets. Until that capability 84 // becomes necessary for testing, use a bogus address. 85 // 86 // Reference: https://github.com/MetalBlockchain/metalgo/issues/1365#issuecomment-1511508767 87 ethAddress := "0x0000000000000000000000000000000000000000" 88 89 now := time.Now() 90 91 config := &genesis.UnparsedConfig{ 92 NetworkID: networkID, 93 Allocations: []genesis.UnparsedAllocation{ 94 { 95 ETHAddr: ethAddress, 96 AVAXAddr: stakeAddress, 97 InitialAmount: 0, 98 UnlockSchedule: []genesis.LockedAmount{ // Provides stake to validators 99 { 100 Amount: totalStake, 101 Locktime: uint64(now.Add(7 * 24 * time.Hour).Unix()), // 1 Week 102 }, 103 }, 104 }, 105 }, 106 StartTime: uint64(now.Unix()), 107 InitialStakedFunds: []string{stakeAddress}, 108 InitialStakeDuration: 365 * 24 * 60 * 60, // 1 year 109 InitialStakeDurationOffset: 90 * 60, // 90 minutes 110 Message: "hello avalanche!", 111 InitialStakers: initialStakers, 112 } 113 114 // Ensure pre-funded keys have arbitrary large balances on both chains to support testing 115 xChainBalances := make(XChainBalanceMap, len(keysToFund)) 116 cChainBalances := make(core.GenesisAlloc, len(keysToFund)) 117 for _, key := range keysToFund { 118 xChainBalances[key.Address()] = defaultFundedKeyXChainAmount 119 cChainBalances[evm.GetEthAddress(key)] = core.GenesisAccount{ 120 Balance: defaultFundedKeyCChainAmount, 121 } 122 } 123 124 // Set X-Chain balances 125 for xChainAddress, balance := range xChainBalances { 126 avaxAddr, err := address.Format("X", constants.GetHRP(networkID), xChainAddress[:]) 127 if err != nil { 128 return nil, fmt.Errorf("failed to format X-Chain address: %w", err) 129 } 130 config.Allocations = append( 131 config.Allocations, 132 genesis.UnparsedAllocation{ 133 ETHAddr: ethAddress, 134 AVAXAddr: avaxAddr, 135 InitialAmount: balance, 136 UnlockSchedule: []genesis.LockedAmount{ 137 { 138 Amount: 20 * units.MegaAvax, 139 }, 140 { 141 Amount: totalStake, 142 Locktime: uint64(now.Add(7 * 24 * time.Hour).Unix()), // 1 Week 143 }, 144 }, 145 }, 146 ) 147 } 148 149 // Define C-Chain genesis 150 cChainGenesis := &core.Genesis{ 151 Config: params.AvalancheLocalChainConfig, 152 Difficulty: big.NewInt(0), // Difficulty is a mandatory field 153 GasLimit: defaultGasLimit, 154 Alloc: cChainBalances, 155 } 156 cChainGenesisBytes, err := json.Marshal(cChainGenesis) 157 if err != nil { 158 return nil, fmt.Errorf("failed to marshal C-Chain genesis: %w", err) 159 } 160 config.CChainGenesis = string(cChainGenesisBytes) 161 162 return config, nil 163 } 164 165 // Returns staker configuration for the given set of nodes. 166 func stakersForNodes(networkID uint32, nodes []*Node) ([]genesis.UnparsedStaker, error) { 167 // Give staking rewards for initial validators to a random address. Any testing of staking rewards 168 // will be easier to perform with nodes other than the initial validators since the timing of 169 // staking can be more easily controlled. 170 rewardAddr, err := address.Format("X", constants.GetHRP(networkID), ids.GenerateTestShortID().Bytes()) 171 if err != nil { 172 return nil, fmt.Errorf("failed to format reward address: %w", err) 173 } 174 175 // Configure provided nodes as initial stakers 176 initialStakers := make([]genesis.UnparsedStaker, len(nodes)) 177 for i, node := range nodes { 178 pop, err := node.GetProofOfPossession() 179 if err != nil { 180 return nil, fmt.Errorf("failed to derive proof of possession for node %s: %w", node.NodeID, err) 181 } 182 initialStakers[i] = genesis.UnparsedStaker{ 183 NodeID: node.NodeID, 184 RewardAddress: rewardAddr, 185 DelegationFee: .01 * reward.PercentDenominator, 186 Signer: pop, 187 } 188 } 189 190 return initialStakers, nil 191 }