github.com/ava-labs/subnet-evm@v0.6.4/core/genesis_test.go (about) 1 // (c) 2019-2021, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package core 28 29 import ( 30 _ "embed" 31 "math/big" 32 "reflect" 33 "testing" 34 35 "github.com/ava-labs/subnet-evm/consensus/dummy" 36 "github.com/ava-labs/subnet-evm/core/rawdb" 37 "github.com/ava-labs/subnet-evm/core/state" 38 "github.com/ava-labs/subnet-evm/core/types" 39 "github.com/ava-labs/subnet-evm/core/vm" 40 "github.com/ava-labs/subnet-evm/params" 41 "github.com/ava-labs/subnet-evm/precompile/allowlist" 42 "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" 43 "github.com/ava-labs/subnet-evm/trie" 44 "github.com/ava-labs/subnet-evm/trie/triedb/pathdb" 45 "github.com/ava-labs/subnet-evm/utils" 46 "github.com/davecgh/go-spew/spew" 47 "github.com/ethereum/go-ethereum/common" 48 "github.com/ethereum/go-ethereum/ethdb" 49 "github.com/stretchr/testify/assert" 50 "github.com/stretchr/testify/require" 51 ) 52 53 func setupGenesisBlock(db ethdb.Database, triedb *trie.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, common.Hash, error) { 54 return SetupGenesisBlock(db, triedb, genesis, lastAcceptedHash, false) 55 } 56 57 func TestGenesisBlockForTesting(t *testing.T) { 58 genesisBlockForTestingHash := common.HexToHash("0x114ce61b50051f70768f982f7b59e82dd73b7bbd768e310c9d9f508d492e687b") 59 block := GenesisBlockForTesting(rawdb.NewMemoryDatabase(), common.Address{1}, big.NewInt(1)) 60 if block.Hash() != genesisBlockForTestingHash { 61 t.Errorf("wrong testing genesis hash, got %v, want %v", block.Hash(), genesisBlockForTestingHash) 62 } 63 } 64 65 func TestSetupGenesis(t *testing.T) { 66 testSetupGenesis(t, rawdb.HashScheme) 67 testSetupGenesis(t, rawdb.PathScheme) 68 } 69 70 func testSetupGenesis(t *testing.T, scheme string) { 71 preSubnetConfig := *params.TestPreSubnetEVMConfig 72 preSubnetConfig.SubnetEVMTimestamp = utils.NewUint64(100) 73 var ( 74 customghash = common.HexToHash("0x4a12fe7bf8d40d152d7e9de22337b115186a4662aa3a97217b36146202bbfc66") 75 customg = Genesis{ 76 Config: &preSubnetConfig, 77 Alloc: GenesisAlloc{ 78 {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, 79 }, 80 GasLimit: preSubnetConfig.FeeConfig.GasLimit.Uint64(), 81 } 82 oldcustomg = customg 83 ) 84 85 rollbackpreSubnetConfig := preSubnetConfig 86 rollbackpreSubnetConfig.SubnetEVMTimestamp = utils.NewUint64(90) 87 oldcustomg.Config = &rollbackpreSubnetConfig 88 89 tests := []struct { 90 name string 91 fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error) 92 wantConfig *params.ChainConfig 93 wantHash common.Hash 94 wantErr error 95 }{ 96 { 97 name: "genesis without ChainConfig", 98 fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { 99 return setupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis), common.Hash{}) 100 }, 101 wantErr: errGenesisNoConfig, 102 wantConfig: nil, 103 }, 104 { 105 name: "no block in DB, genesis == nil", 106 fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { 107 return setupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil, common.Hash{}) 108 }, 109 wantErr: ErrNoGenesis, 110 wantConfig: nil, 111 }, 112 { 113 name: "custom block in DB, genesis == nil", 114 fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { 115 tdb := trie.NewDatabase(db, newDbConfig(scheme)) 116 customg.Commit(db, tdb) 117 return setupGenesisBlock(db, tdb, nil, common.Hash{}) 118 }, 119 wantErr: ErrNoGenesis, 120 wantConfig: nil, 121 }, 122 { 123 name: "compatible config in DB", 124 fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { 125 tdb := trie.NewDatabase(db, newDbConfig(scheme)) 126 oldcustomg.Commit(db, tdb) 127 return setupGenesisBlock(db, tdb, &customg, customghash) 128 }, 129 wantHash: customghash, 130 wantConfig: customg.Config, 131 }, 132 { 133 name: "incompatible config for avalanche fork in DB", 134 fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { 135 // Commit the 'old' genesis block with SubnetEVM transition at 90. 136 // Advance to block #4, past the SubnetEVM transition block of customg. 137 tdb := trie.NewDatabase(db, newDbConfig(scheme)) 138 genesis, err := oldcustomg.Commit(db, tdb) 139 if err != nil { 140 t.Fatal(err) 141 } 142 143 bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, dummy.NewFullFaker(), vm.Config{}, genesis.Hash(), false) 144 defer bc.Stop() 145 146 _, blocks, _, err := GenerateChainWithGenesis(&oldcustomg, dummy.NewFullFaker(), 4, 25, nil) 147 if err != nil { 148 t.Fatal(err) 149 } 150 bc.InsertChain(blocks) 151 152 for _, block := range blocks { 153 if err := bc.Accept(block); err != nil { 154 t.Fatal(err) 155 } 156 } 157 158 // This should return a compatibility error. 159 return setupGenesisBlock(db, tdb, &customg, bc.lastAccepted.Hash()) 160 }, 161 wantHash: customghash, 162 wantConfig: customg.Config, 163 wantErr: ¶ms.ConfigCompatError{ 164 What: "SubnetEVM fork block timestamp", 165 StoredTime: u64(90), 166 NewTime: u64(100), 167 RewindToTime: 89, 168 }, 169 }, 170 } 171 172 for _, test := range tests { 173 t.Run(test.name, func(t *testing.T) { 174 db := rawdb.NewMemoryDatabase() 175 config, hash, err := test.fn(db) 176 // Check the return values. 177 if !reflect.DeepEqual(err, test.wantErr) { 178 spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true} 179 t.Errorf("returned error %#v, want %#v", spew.NewFormatter(err), spew.NewFormatter(test.wantErr)) 180 } 181 if !reflect.DeepEqual(config, test.wantConfig) { 182 t.Errorf("returned %v\nwant %v", config, test.wantConfig) 183 } 184 if hash != test.wantHash { 185 t.Errorf("returned hash %s, want %s", hash.Hex(), test.wantHash.Hex()) 186 } else if err == nil { 187 // Check database content. 188 stored := rawdb.ReadBlock(db, test.wantHash, 0) 189 if stored.Hash() != test.wantHash { 190 t.Errorf("block in DB has hash %s, want %s", stored.Hash(), test.wantHash) 191 } 192 } 193 }) 194 } 195 } 196 197 func TestStatefulPrecompilesConfigure(t *testing.T) { 198 type test struct { 199 getConfig func() *params.ChainConfig // Return the config that enables the stateful precompile at the genesis for the test 200 assertState func(t *testing.T, sdb *state.StateDB) // Check that the stateful precompiles were configured correctly 201 } 202 203 addr := common.HexToAddress("0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC") 204 205 // Test suite to ensure that stateful precompiles are configured correctly in the genesis. 206 for name, test := range map[string]test{ 207 "allow list enabled in genesis": { 208 getConfig: func() *params.ChainConfig { 209 config := *params.TestChainConfig 210 config.GenesisPrecompiles = params.Precompiles{ 211 deployerallowlist.ConfigKey: deployerallowlist.NewConfig(utils.NewUint64(0), []common.Address{addr}, nil, nil), 212 } 213 return &config 214 }, 215 assertState: func(t *testing.T, sdb *state.StateDB) { 216 assert.Equal(t, allowlist.AdminRole, deployerallowlist.GetContractDeployerAllowListStatus(sdb, addr), "unexpected allow list status for modified address") 217 assert.Equal(t, uint64(1), sdb.GetNonce(deployerallowlist.ContractAddress)) 218 }, 219 }, 220 } { 221 t.Run(name, func(t *testing.T) { 222 config := test.getConfig() 223 224 genesis := &Genesis{ 225 Config: config, 226 Alloc: GenesisAlloc{ 227 {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, 228 }, 229 GasLimit: config.FeeConfig.GasLimit.Uint64(), 230 } 231 232 db := rawdb.NewMemoryDatabase() 233 234 genesisBlock := genesis.ToBlock() 235 genesisRoot := genesisBlock.Root() 236 237 _, _, err := setupGenesisBlock(db, trie.NewDatabase(db, trie.HashDefaults), genesis, genesisBlock.Hash()) 238 if err != nil { 239 t.Fatal(err) 240 } 241 242 statedb, err := state.New(genesisRoot, state.NewDatabase(db), nil) 243 if err != nil { 244 t.Fatal(err) 245 } 246 247 if test.assertState != nil { 248 test.assertState(t, statedb) 249 } 250 }) 251 } 252 } 253 254 // regression test for precompile activation after header block 255 func TestPrecompileActivationAfterHeaderBlock(t *testing.T) { 256 db := rawdb.NewMemoryDatabase() 257 customg := Genesis{ 258 Config: params.TestChainConfig, 259 Alloc: GenesisAlloc{ 260 {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, 261 }, 262 GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(), 263 } 264 bc, _ := NewBlockChain(db, DefaultCacheConfig, &customg, dummy.NewFullFaker(), vm.Config{}, common.Hash{}, false) 265 defer bc.Stop() 266 267 // Advance header to block #4, past the ContractDeployerAllowListConfig. 268 _, blocks, _, _ := GenerateChainWithGenesis(&customg, dummy.NewFullFaker(), 4, 25, nil) 269 270 require := require.New(t) 271 _, err := bc.InsertChain(blocks) 272 require.NoError(err) 273 274 // accept up to block #2 275 for _, block := range blocks[:2] { 276 require.NoError(bc.Accept(block)) 277 } 278 block := bc.CurrentBlock() 279 280 require.Equal(blocks[1].Hash(), bc.lastAccepted.Hash()) 281 // header must be bigger than last accepted 282 require.Greater(block.Time, bc.lastAccepted.Time()) 283 284 activatedGenesisConfig := *customg.Config 285 contractDeployerConfig := deployerallowlist.NewConfig(utils.NewUint64(51), nil, nil, nil) 286 activatedGenesisConfig.UpgradeConfig.PrecompileUpgrades = []params.PrecompileUpgrade{ 287 { 288 Config: contractDeployerConfig, 289 }, 290 } 291 customg.Config = &activatedGenesisConfig 292 293 // assert block is after the activation block 294 require.Greater(block.Time, *contractDeployerConfig.Timestamp()) 295 // assert last accepted block is before the activation block 296 require.Less(bc.lastAccepted.Time(), *contractDeployerConfig.Timestamp()) 297 298 // This should not return any error since the last accepted block is before the activation block. 299 config, _, err := setupGenesisBlock(db, trie.NewDatabase(db, nil), &customg, bc.lastAccepted.Hash()) 300 require.NoError(err) 301 if !reflect.DeepEqual(config, customg.Config) { 302 t.Errorf("returned %v\nwant %v", config, customg.Config) 303 } 304 } 305 306 func TestGenesisWriteUpgradesRegression(t *testing.T) { 307 require := require.New(t) 308 config := *params.TestChainConfig 309 genesis := &Genesis{ 310 Config: &config, 311 Alloc: GenesisAlloc{ 312 {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, 313 }, 314 GasLimit: config.FeeConfig.GasLimit.Uint64(), 315 } 316 317 db := rawdb.NewMemoryDatabase() 318 trieDB := trie.NewDatabase(db, trie.HashDefaults) 319 genesisBlock := genesis.MustCommit(db, trieDB) 320 321 _, _, err := SetupGenesisBlock(db, trieDB, genesis, genesisBlock.Hash(), false) 322 require.NoError(err) 323 324 genesis.Config.UpgradeConfig.PrecompileUpgrades = []params.PrecompileUpgrade{ 325 { 326 Config: deployerallowlist.NewConfig(utils.NewUint64(51), nil, nil, nil), 327 }, 328 } 329 _, _, err = SetupGenesisBlock(db, trieDB, genesis, genesisBlock.Hash(), false) 330 require.NoError(err) 331 332 timestamp := uint64(100) 333 lastAcceptedBlock := types.NewBlock(&types.Header{ 334 ParentHash: common.Hash{1, 2, 3}, 335 Number: big.NewInt(100), 336 GasLimit: 8_000_000, 337 Extra: nil, 338 Time: timestamp, 339 }, nil, nil, nil, trie.NewStackTrie(nil)) 340 rawdb.WriteBlock(db, lastAcceptedBlock) 341 342 // Attempt restart after the chain has advanced past the activation of the precompile upgrade. 343 // This tests a regression where the UpgradeConfig would not be written to disk correctly. 344 _, _, err = SetupGenesisBlock(db, trieDB, genesis, lastAcceptedBlock.Hash(), false) 345 require.NoError(err) 346 } 347 348 func newDbConfig(scheme string) *trie.Config { 349 if scheme == rawdb.HashScheme { 350 return trie.HashDefaults 351 } 352 return &trie.Config{PathDB: pathdb.Defaults} 353 }