github.com/ethereum/go-ethereum@v1.16.1/core/genesis_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"math/big"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"github.com/davecgh/go-spew/spew"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/consensus/ethash"
    29  	"github.com/ethereum/go-ethereum/core/rawdb"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  	"github.com/ethereum/go-ethereum/ethdb"
    32  	"github.com/ethereum/go-ethereum/params"
    33  	"github.com/ethereum/go-ethereum/triedb"
    34  	"github.com/ethereum/go-ethereum/triedb/pathdb"
    35  )
    36  
    37  func TestSetupGenesis(t *testing.T) {
    38  	testSetupGenesis(t, rawdb.HashScheme)
    39  	testSetupGenesis(t, rawdb.PathScheme)
    40  }
    41  
    42  func testSetupGenesis(t *testing.T, scheme string) {
    43  	var (
    44  		customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
    45  		customg     = Genesis{
    46  			Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3), Ethash: &params.EthashConfig{}},
    47  			Alloc: types.GenesisAlloc{
    48  				{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
    49  			},
    50  		}
    51  		oldcustomg = customg
    52  	)
    53  	oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2), Ethash: &params.EthashConfig{}}
    54  
    55  	tests := []struct {
    56  		name           string
    57  		fn             func(ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error)
    58  		wantConfig     *params.ChainConfig
    59  		wantHash       common.Hash
    60  		wantErr        error
    61  		wantCompactErr *params.ConfigCompatError
    62  	}{
    63  		{
    64  			name: "genesis without ChainConfig",
    65  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
    66  				return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
    67  			},
    68  			wantErr: errGenesisNoConfig,
    69  		},
    70  		{
    71  			name: "no block in DB, genesis == nil",
    72  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
    73  				return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
    74  			},
    75  			wantHash:   params.MainnetGenesisHash,
    76  			wantConfig: params.MainnetChainConfig,
    77  		},
    78  		{
    79  			name: "mainnet block in DB, genesis == nil",
    80  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
    81  				DefaultGenesisBlock().MustCommit(db, triedb.NewDatabase(db, newDbConfig(scheme)))
    82  				return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
    83  			},
    84  			wantHash:   params.MainnetGenesisHash,
    85  			wantConfig: params.MainnetChainConfig,
    86  		},
    87  		{
    88  			name: "custom block in DB, genesis == nil",
    89  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
    90  				tdb := triedb.NewDatabase(db, newDbConfig(scheme))
    91  				customg.Commit(db, tdb)
    92  				return SetupGenesisBlock(db, tdb, nil)
    93  			},
    94  			wantHash:   customghash,
    95  			wantConfig: customg.Config,
    96  		},
    97  		{
    98  			name: "custom block in DB, genesis == sepolia",
    99  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
   100  				tdb := triedb.NewDatabase(db, newDbConfig(scheme))
   101  				customg.Commit(db, tdb)
   102  				return SetupGenesisBlock(db, tdb, DefaultSepoliaGenesisBlock())
   103  			},
   104  			wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
   105  		},
   106  		{
   107  			name: "custom block in DB, genesis == hoodi",
   108  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
   109  				tdb := triedb.NewDatabase(db, newDbConfig(scheme))
   110  				customg.Commit(db, tdb)
   111  				return SetupGenesisBlock(db, tdb, DefaultHoodiGenesisBlock())
   112  			},
   113  			wantErr: &GenesisMismatchError{Stored: customghash, New: params.HoodiGenesisHash},
   114  		},
   115  		{
   116  			name: "compatible config in DB",
   117  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
   118  				tdb := triedb.NewDatabase(db, newDbConfig(scheme))
   119  				oldcustomg.Commit(db, tdb)
   120  				return SetupGenesisBlock(db, tdb, &customg)
   121  			},
   122  			wantHash:   customghash,
   123  			wantConfig: customg.Config,
   124  		},
   125  		{
   126  			name: "incompatible config in DB",
   127  			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
   128  				// Commit the 'old' genesis block with Homestead transition at #2.
   129  				// Advance to block #4, past the homestead transition block of customg.
   130  				tdb := triedb.NewDatabase(db, newDbConfig(scheme))
   131  				oldcustomg.Commit(db, tdb)
   132  
   133  				bc, _ := NewBlockChain(db, &oldcustomg, ethash.NewFullFaker(), DefaultConfig().WithStateScheme(scheme))
   134  				defer bc.Stop()
   135  
   136  				_, blocks, _ := GenerateChainWithGenesis(&oldcustomg, ethash.NewFaker(), 4, nil)
   137  				bc.InsertChain(blocks)
   138  
   139  				// This should return a compatibility error.
   140  				return SetupGenesisBlock(db, tdb, &customg)
   141  			},
   142  			wantHash:   customghash,
   143  			wantConfig: customg.Config,
   144  			wantCompactErr: &params.ConfigCompatError{
   145  				What:          "Homestead fork block",
   146  				StoredBlock:   big.NewInt(2),
   147  				NewBlock:      big.NewInt(3),
   148  				RewindToBlock: 1,
   149  			},
   150  		},
   151  	}
   152  
   153  	for _, test := range tests {
   154  		db := rawdb.NewMemoryDatabase()
   155  		config, hash, compatErr, err := test.fn(db)
   156  		// Check the return values.
   157  		if !reflect.DeepEqual(err, test.wantErr) {
   158  			spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
   159  			t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr))
   160  		}
   161  		if !reflect.DeepEqual(compatErr, test.wantCompactErr) {
   162  			spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
   163  			t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(compatErr), spew.NewFormatter(test.wantCompactErr))
   164  		}
   165  		if !reflect.DeepEqual(config, test.wantConfig) {
   166  			t.Errorf("%s:\nreturned %v\nwant     %v", test.name, config, test.wantConfig)
   167  		}
   168  		if hash != test.wantHash {
   169  			t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex())
   170  		} else if err == nil {
   171  			// Check database content.
   172  			stored := rawdb.ReadBlock(db, test.wantHash, 0)
   173  			if stored.Hash() != test.wantHash {
   174  				t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash)
   175  			}
   176  		}
   177  	}
   178  }
   179  
   180  // TestGenesisHashes checks the congruity of default genesis data to
   181  // corresponding hardcoded genesis hash values.
   182  func TestGenesisHashes(t *testing.T) {
   183  	for i, c := range []struct {
   184  		genesis *Genesis
   185  		want    common.Hash
   186  	}{
   187  		{DefaultGenesisBlock(), params.MainnetGenesisHash},
   188  		{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
   189  		{DefaultHoleskyGenesisBlock(), params.HoleskyGenesisHash},
   190  		{DefaultHoodiGenesisBlock(), params.HoodiGenesisHash},
   191  	} {
   192  		// Test via MustCommit
   193  		db := rawdb.NewMemoryDatabase()
   194  		if have := c.genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)).Hash(); have != c.want {
   195  			t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
   196  		}
   197  		// Test via ToBlock
   198  		if have := c.genesis.ToBlock().Hash(); have != c.want {
   199  			t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
   200  		}
   201  	}
   202  }
   203  
   204  func TestGenesisCommit(t *testing.T) {
   205  	genesis := &Genesis{
   206  		BaseFee: big.NewInt(params.InitialBaseFee),
   207  		Config:  params.TestChainConfig,
   208  		// difficulty is nil
   209  	}
   210  
   211  	db := rawdb.NewMemoryDatabase()
   212  	genesisBlock := genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
   213  
   214  	if genesis.Difficulty != nil {
   215  		t.Fatalf("assumption wrong")
   216  	}
   217  
   218  	// This value should have been set as default in the ToBlock method.
   219  	if genesisBlock.Difficulty().Cmp(params.GenesisDifficulty) != 0 {
   220  		t.Errorf("assumption wrong: want: %d, got: %v", params.GenesisDifficulty, genesisBlock.Difficulty())
   221  	}
   222  }
   223  
   224  func TestReadWriteGenesisAlloc(t *testing.T) {
   225  	var (
   226  		db    = rawdb.NewMemoryDatabase()
   227  		alloc = &types.GenesisAlloc{
   228  			{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
   229  			{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
   230  		}
   231  		hash, _ = hashAlloc(alloc, false)
   232  	)
   233  	blob, _ := json.Marshal(alloc)
   234  	rawdb.WriteGenesisStateSpec(db, hash, blob)
   235  
   236  	var reload types.GenesisAlloc
   237  	err := reload.UnmarshalJSON(rawdb.ReadGenesisStateSpec(db, hash))
   238  	if err != nil {
   239  		t.Fatalf("Failed to load genesis state %v", err)
   240  	}
   241  	if len(reload) != len(*alloc) {
   242  		t.Fatal("Unexpected genesis allocation")
   243  	}
   244  	for addr, account := range reload {
   245  		want, ok := (*alloc)[addr]
   246  		if !ok {
   247  			t.Fatal("Account is not found")
   248  		}
   249  		if !reflect.DeepEqual(want, account) {
   250  			t.Fatal("Unexpected account")
   251  		}
   252  	}
   253  }
   254  
   255  func newDbConfig(scheme string) *triedb.Config {
   256  	if scheme == rawdb.HashScheme {
   257  		return triedb.HashDefaults
   258  	}
   259  	config := *pathdb.Defaults
   260  	config.NoAsyncFlush = true
   261  	return &triedb.Config{PathDB: &config}
   262  }
   263  
   264  func TestVerkleGenesisCommit(t *testing.T) {
   265  	var verkleTime uint64 = 0
   266  	verkleConfig := &params.ChainConfig{
   267  		ChainID:                 big.NewInt(1),
   268  		HomesteadBlock:          big.NewInt(0),
   269  		DAOForkBlock:            nil,
   270  		DAOForkSupport:          false,
   271  		EIP150Block:             big.NewInt(0),
   272  		EIP155Block:             big.NewInt(0),
   273  		EIP158Block:             big.NewInt(0),
   274  		ByzantiumBlock:          big.NewInt(0),
   275  		ConstantinopleBlock:     big.NewInt(0),
   276  		PetersburgBlock:         big.NewInt(0),
   277  		IstanbulBlock:           big.NewInt(0),
   278  		MuirGlacierBlock:        big.NewInt(0),
   279  		BerlinBlock:             big.NewInt(0),
   280  		LondonBlock:             big.NewInt(0),
   281  		ArrowGlacierBlock:       big.NewInt(0),
   282  		GrayGlacierBlock:        big.NewInt(0),
   283  		MergeNetsplitBlock:      nil,
   284  		ShanghaiTime:            &verkleTime,
   285  		CancunTime:              &verkleTime,
   286  		PragueTime:              &verkleTime,
   287  		OsakaTime:               &verkleTime,
   288  		VerkleTime:              &verkleTime,
   289  		TerminalTotalDifficulty: big.NewInt(0),
   290  		EnableVerkleAtGenesis:   true,
   291  		Ethash:                  nil,
   292  		Clique:                  nil,
   293  		BlobScheduleConfig: &params.BlobScheduleConfig{
   294  			Cancun: params.DefaultCancunBlobConfig,
   295  			Prague: params.DefaultPragueBlobConfig,
   296  			Osaka:  params.DefaultOsakaBlobConfig,
   297  			Verkle: params.DefaultPragueBlobConfig,
   298  		},
   299  	}
   300  
   301  	genesis := &Genesis{
   302  		BaseFee:    big.NewInt(params.InitialBaseFee),
   303  		Config:     verkleConfig,
   304  		Timestamp:  verkleTime,
   305  		Difficulty: big.NewInt(0),
   306  		Alloc: types.GenesisAlloc{
   307  			{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
   308  		},
   309  	}
   310  
   311  	expected := common.FromHex("018d20eebb130b5e2b796465fe36aafab650650729a92435aec071bf2386f080")
   312  	got := genesis.ToBlock().Root().Bytes()
   313  	if !bytes.Equal(got, expected) {
   314  		t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
   315  	}
   316  
   317  	db := rawdb.NewMemoryDatabase()
   318  
   319  	config := *pathdb.Defaults
   320  	config.NoAsyncFlush = true
   321  
   322  	triedb := triedb.NewDatabase(db, &triedb.Config{
   323  		IsVerkle: true,
   324  		PathDB:   &config,
   325  	})
   326  	block := genesis.MustCommit(db, triedb)
   327  	if !bytes.Equal(block.Root().Bytes(), expected) {
   328  		t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root())
   329  	}
   330  
   331  	// Test that the trie is verkle
   332  	if !triedb.IsVerkle() {
   333  		t.Fatalf("expected trie to be verkle")
   334  	}
   335  	vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix))
   336  	if !rawdb.HasAccountTrieNode(vdb, nil) {
   337  		t.Fatal("could not find node")
   338  	}
   339  }