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: &params.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  }