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
17package core
18
19import (
20	"math/big"
21	"reflect"
22	"testing"
23
24	"github.com/davecgh/go-spew/spew"
25	"github.com/ethereum/go-ethereum/common"
26	"github.com/ethereum/go-ethereum/consensus/ethash"
27	"github.com/ethereum/go-ethereum/core/rawdb"
28	"github.com/ethereum/go-ethereum/core/vm"
29	"github.com/ethereum/go-ethereum/ethdb"
30	"github.com/ethereum/go-ethereum/params"
31)
32
33func TestInvalidCliqueConfig(t *testing.T) {
34	block := DefaultGoerliGenesisBlock()
35	block.ExtraData = []byte{}
36	if _, err := block.Commit(nil); err == nil {
37		t.Fatal("Expected error on invalid clique config")
38	}
39}
40
41func TestSetupGenesis(t *testing.T) {
42	var (
43		customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
44		customg     = Genesis{
45			Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3)},
46			Alloc: GenesisAlloc{
47				{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
48			},
49		}
50		oldcustomg = customg
51	)
52	oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)}
53	tests := []struct {
54		name       string
55		fn         func(ethdb.Database) (*params.ChainConfig, common.Hash, error)
56		wantConfig *params.ChainConfig
57		wantHash   common.Hash
58		wantErr    error
59	}{
60		{
61			name: "genesis without ChainConfig",
62			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
63				return SetupGenesisBlock(db, new(Genesis))
64			},
65			wantErr:    errGenesisNoConfig,
66			wantConfig: params.AllEthashProtocolChanges,
67		},
68		{
69			name: "no block in DB, genesis == nil",
70			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
71				return SetupGenesisBlock(db, nil)
72			},
73			wantHash:   params.MainnetGenesisHash,
74			wantConfig: params.MainnetChainConfig,
75		},
76		{
77			name: "mainnet block in DB, genesis == nil",
78			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
79				DefaultGenesisBlock().MustCommit(db)
80				return SetupGenesisBlock(db, nil)
81			},
82			wantHash:   params.MainnetGenesisHash,
83			wantConfig: params.MainnetChainConfig,
84		},
85		{
86			name: "custom block in DB, genesis == nil",
87			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
88				customg.MustCommit(db)
89				return SetupGenesisBlock(db, nil)
90			},
91			wantHash:   customghash,
92			wantConfig: customg.Config,
93		},
94		{
95			name: "custom block in DB, genesis == ropsten",
96			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
97				customg.MustCommit(db)
98				return SetupGenesisBlock(db, DefaultRopstenGenesisBlock())
99			},
100			wantErr:    &GenesisMismatchError{Stored: customghash, New: params.RopstenGenesisHash},
101			wantHash:   params.RopstenGenesisHash,
102			wantConfig: params.RopstenChainConfig,
103		},
104		{
105			name: "compatible config in DB",
106			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
107				oldcustomg.MustCommit(db)
108				return SetupGenesisBlock(db, &customg)
109			},
110			wantHash:   customghash,
111			wantConfig: customg.Config,
112		},
113		{
114			name: "incompatible config in DB",
115			fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
116				// Commit the 'old' genesis block with Homestead transition at #2.
117				// Advance to block #4, past the homestead transition block of customg.
118				genesis := oldcustomg.MustCommit(db)
119
120				bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
121				defer bc.Stop()
122
123				blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
124				bc.InsertChain(blocks)
125				bc.CurrentBlock()
126				// This should return a compatibility error.
127				return SetupGenesisBlock(db, &customg)
128			},
129			wantHash:   customghash,
130			wantConfig: customg.Config,
131			wantErr: &params.ConfigCompatError{
132				What:         "Homestead fork block",
133				StoredConfig: big.NewInt(2),
134				NewConfig:    big.NewInt(3),
135				RewindTo:     1,
136			},
137		},
138	}
139
140	for _, test := range tests {
141		db := rawdb.NewMemoryDatabase()
142		config, hash, err := test.fn(db)
143		// Check the return values.
144		if !reflect.DeepEqual(err, test.wantErr) {
145			spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
146			t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr))
147		}
148		if !reflect.DeepEqual(config, test.wantConfig) {
149			t.Errorf("%s:\nreturned %v\nwant     %v", test.name, config, test.wantConfig)
150		}
151		if hash != test.wantHash {
152			t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex())
153		} else if err == nil {
154			// Check database content.
155			stored := rawdb.ReadBlock(db, test.wantHash, 0)
156			if stored.Hash() != test.wantHash {
157				t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash)
158			}
159		}
160	}
161}
162
163// TestGenesisHashes checks the congruity of default genesis data to
164// corresponding hardcoded genesis hash values.
165func TestGenesisHashes(t *testing.T) {
166	for i, c := range []struct {
167		genesis *Genesis
168		want    common.Hash
169	}{
170		{DefaultGenesisBlock(), params.MainnetGenesisHash},
171		{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
172		{DefaultRopstenGenesisBlock(), params.RopstenGenesisHash},
173		{DefaultRinkebyGenesisBlock(), params.RinkebyGenesisHash},
174		{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
175	} {
176		// Test via MustCommit
177		if have := c.genesis.MustCommit(rawdb.NewMemoryDatabase()).Hash(); have != c.want {
178			t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
179		}
180		// Test via ToBlock
181		if have := c.genesis.ToBlock(nil).Hash(); have != c.want {
182			t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
183		}
184	}
185}
186
187func TestGenesis_Commit(t *testing.T) {
188	genesis := &Genesis{
189		BaseFee: big.NewInt(params.InitialBaseFee),
190		Config:  params.TestChainConfig,
191		// difficulty is nil
192	}
193
194	db := rawdb.NewMemoryDatabase()
195	genesisBlock, err := genesis.Commit(db)
196	if err != nil {
197		t.Fatal(err)
198	}
199
200	if genesis.Difficulty != nil {
201		t.Fatalf("assumption wrong")
202	}
203
204	// This value should have been set as default in the ToBlock method.
205	if genesisBlock.Difficulty().Cmp(params.GenesisDifficulty) != 0 {
206		t.Errorf("assumption wrong: want: %d, got: %v", params.GenesisDifficulty, genesisBlock.Difficulty())
207	}
208
209	// Expect the stored total difficulty to be the difficulty of the genesis block.
210	stored := rawdb.ReadTd(db, genesisBlock.Hash(), genesisBlock.NumberU64())
211
212	if stored.Cmp(genesisBlock.Difficulty()) != 0 {
213		t.Errorf("inequal difficulty; stored: %v, genesisBlock: %v", stored, genesisBlock.Difficulty())
214	}
215}
216