From 459bb4a64739526120f979bbd337a82d19738de7 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Wed, 23 Oct 2024 08:03:36 +0200 Subject: [PATCH 1/5] core/state: move state log mechanism to a separate layer (#30569) This PR moves the logging/tracing-facilities out of `*state.StateDB`, in to a wrapping struct which implements `vm.StateDB` instead. In most places, it is a pretty straight-forward change: - First, hoisting the invocations from state objects up to the statedb. - Then making the mutation-methods simply return the previous value, so that the external logging layer could log everything. Some internal code uses the direct object-accessors to mutate the state, particularly in testing and in setting up state overrides, which means that these changes are unobservable for the hooked layer. Thus, configuring the overrides are not necessarily part of the API we want to publish. The trickiest part about the layering is that when the selfdestructs are finally deleted during `Finalise`, there's the possibility that someone sent some ether to it, which is burnt at that point, and thus needs to be logged. The hooked layer reaches into the inner layer to figure out these events. In package `vm`, the conversion from `state.StateDB + hooks` into a hooked `vm.StateDB` is performed where needed. --------- Co-authored-by: Gary Rong --- consensus/beacon/consensus.go | 3 +- consensus/clique/clique.go | 3 +- consensus/consensus.go | 3 +- consensus/ethash/consensus.go | 5 +- consensus/misc/dao.go | 10 +- core/blockchain.go | 1 - core/state/state_object.go | 41 +-- core/state/state_test.go | 13 +- core/state/statedb.go | 60 ++--- core/state/statedb_hooked.go | 242 ++++++++++++++++++ core/state/statedb_hooked_test.go | 130 ++++++++++ core/state/statedb_test.go | 16 +- core/state/sync_test.go | 3 +- core/state_processor.go | 41 +-- core/vm/instructions.go | 2 +- core/vm/interface.go | 18 +- eth/tracers/api.go | 1 - .../internal/tracetest/calltrace_test.go | 26 +- .../internal/tracetest/flat_calltrace_test.go | 1 - .../internal/tracetest/prestate_test.go | 1 - eth/tracers/internal/tracetest/supply_test.go | 3 +- eth/tracers/logger/logger_test.go | 8 +- internal/ethapi/api.go | 14 +- internal/ethapi/api_test.go | 1 + internal/ethapi/simulate.go | 11 +- 25 files changed, 519 insertions(+), 138 deletions(-) create mode 100644 core/state/statedb_hooked.go create mode 100644 core/state/statedb_hooked_test.go diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index ae4c9f29a1cd..2b824027d4f9 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -349,7 +350,7 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine and processes withdrawals on top. -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) { if !beacon.IsPoSHeader(header) { beacon.ethone.Finalize(chain, header, state, body) return diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index e58bb6203447..d31efd744510 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -573,7 +574,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header // Finalize implements consensus.Engine. There is no post-transaction // consensus rules in clique, do nothing here. -func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) { +func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) { // No block rewards in PoA, so the state remains as is } diff --git a/consensus/consensus.go b/consensus/consensus.go index 9232f7a2c8bf..ff76d31f551f 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -88,7 +89,7 @@ type Engine interface { // // Note: The state database might be updated to reflect any consensus rules // that happen at finalization (e.g. block rewards). - Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) + Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards or process withdrawals) and assembles the final block. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 3d3c9cf91833..4f92f1282b9e 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -503,7 +504,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine, accumulating the block and uncle rewards. -func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) { +func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) { // Accumulate any block and uncle rewards accumulateRewards(chain.Config(), state, header, body.Uncles) } @@ -566,7 +567,7 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { // accumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) { +func accumulateRewards(config *params.ChainConfig, stateDB vm.StateDB, header *types.Header, uncles []*types.Header) { // Select the correct block reward based on chain progression blockReward := FrontierBlockReward if config.IsByzantium(header.Number) { diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go index 45669d0bcec8..b80c1b833a47 100644 --- a/consensus/misc/dao.go +++ b/consensus/misc/dao.go @@ -21,11 +21,10 @@ import ( "errors" "math/big" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" ) var ( @@ -74,7 +73,7 @@ func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) // ApplyDAOHardFork modifies the state database according to the DAO hard-fork // rules, transferring all balances of a set of DAO accounts to a single refund // contract. -func ApplyDAOHardFork(statedb *state.StateDB) { +func ApplyDAOHardFork(statedb vm.StateDB) { // Retrieve the contract to refund balances into if !statedb.Exist(params.DAORefundContract) { statedb.CreateAccount(params.DAORefundContract) @@ -82,7 +81,8 @@ func ApplyDAOHardFork(statedb *state.StateDB) { // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract) - statedb.SetBalance(addr, new(uint256.Int), tracing.BalanceDecreaseDaoAccount) + balance := statedb.GetBalance(addr) + statedb.AddBalance(params.DAORefundContract, balance, tracing.BalanceIncreaseDaoContract) + statedb.SubBalance(addr, balance, tracing.BalanceDecreaseDaoAccount) } } diff --git a/core/blockchain.go b/core/blockchain.go index 1d45a298e4b0..c3da61b28108 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1774,7 +1774,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness if err != nil { return nil, it.index, err } - statedb.SetLogger(bc.logger) // If we are past Byzantium, enable prefetching to pull in trie node paths // while processing transactions. Before Byzantium the prefetcher is mostly diff --git a/core/state/state_object.go b/core/state/state_object.go index 1ab432e96e9b..b659bf7ff208 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -23,7 +23,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -208,19 +207,18 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { } // SetState updates a value in account storage. -func (s *stateObject) SetState(key, value common.Hash) { +// It returns the previous value +func (s *stateObject) SetState(key, value common.Hash) common.Hash { // If the new value is the same as old, don't set. Otherwise, track only the // dirty changes, supporting reverting all of it back to no change. prev, origin := s.getState(key) if prev == value { - return + return prev } // New value is different, update and journal the change s.db.journal.storageChange(s.address, key, prev, origin) s.setState(key, value, origin) - if s.db.logger != nil && s.db.logger.OnStorageChange != nil { - s.db.logger.OnStorageChange(s.address, key, prev, value) - } + return prev } // setState updates a value in account dirty storage. The dirtiness will be @@ -448,33 +446,25 @@ func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) { +// returns the previous balance +func (s *stateObject) AddBalance(amount *uint256.Int) uint256.Int { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. if amount.IsZero() { if s.empty() { s.touch() } - return + return *(s.Balance()) } - s.SetBalance(new(uint256.Int).Add(s.Balance(), amount), reason) + return s.SetBalance(new(uint256.Int).Add(s.Balance(), amount)) } -// SubBalance removes amount from s's balance. -// It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) { - if amount.IsZero() { - return - } - s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount), reason) -} - -func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) { +// SetBalance sets the balance for the object, and returns the previous balance. +func (s *stateObject) SetBalance(amount *uint256.Int) uint256.Int { + prev := *s.data.Balance s.db.journal.balanceChange(s.address, s.data.Balance) - if s.db.logger != nil && s.db.logger.OnBalanceChange != nil { - s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason) - } s.setBalance(amount) + return prev } func (s *stateObject) setBalance(amount *uint256.Int) { @@ -547,10 +537,6 @@ func (s *stateObject) CodeSize() int { func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { s.db.journal.setCode(s.address) - if s.db.logger != nil && s.db.logger.OnCodeChange != nil { - // TODO remove prevcode from this callback - s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), nil, codeHash, code) - } s.setCode(codeHash, code) } @@ -562,9 +548,6 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) { func (s *stateObject) SetNonce(nonce uint64) { s.db.journal.nonceChange(s.address, s.data.Nonce) - if s.db.logger != nil && s.db.logger.OnNonceChange != nil { - s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce) - } s.setNonce(nonce) } diff --git a/core/state/state_test.go b/core/state/state_test.go index 9de50beb12dc..6f54300c37ad 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/triedb" @@ -48,11 +47,11 @@ func TestDump(t *testing.T) { // generate a few entries obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(uint256.NewInt(22), tracing.BalanceChangeUnspecified) + obj1.AddBalance(uint256.NewInt(22)) obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified) + obj3.SetBalance(uint256.NewInt(44)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -106,13 +105,13 @@ func TestIterativeDump(t *testing.T) { // generate a few entries obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(uint256.NewInt(22), tracing.BalanceChangeUnspecified) + obj1.AddBalance(uint256.NewInt(22)) obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified) + obj3.SetBalance(uint256.NewInt(44)) obj4 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(uint256.NewInt(1337), tracing.BalanceChangeUnspecified) + obj4.AddBalance(uint256.NewInt(1337)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -200,7 +199,7 @@ func TestCreateObjectRevert(t *testing.T) { state.CreateAccount(addr) so0 := state.getStateObject(addr) - so0.SetBalance(uint256.NewInt(42), tracing.BalanceChangeUnspecified) + so0.SetBalance(uint256.NewInt(42)) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) state.setStateObject(so0) diff --git a/core/state/statedb.go b/core/state/statedb.go index f7efc199b3dc..0183c14480df 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "maps" - "math/big" "slices" "sync" "sync/atomic" @@ -81,7 +80,6 @@ type StateDB struct { db Database prefetcher *triePrefetcher trie Trie - logger *tracing.Hooks reader Reader // originalRoot is the pre-state root, before any changes were made. @@ -189,11 +187,6 @@ func New(root common.Hash, db Database) (*StateDB, error) { return sdb, nil } -// SetLogger sets the logger for account update hooks. -func (s *StateDB) SetLogger(l *tracing.Hooks) { - s.logger = l -} - // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. @@ -247,9 +240,6 @@ func (s *StateDB) AddLog(log *types.Log) { log.TxHash = s.thash log.TxIndex = uint(s.txIndex) log.Index = s.logSize - if s.logger != nil && s.logger.OnLog != nil { - s.logger.OnLog(log) - } s.logs[s.thash] = append(s.logs[s.thash], log) s.logSize++ } @@ -409,25 +399,30 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { +func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { stateObject := s.getOrNewStateObject(addr) - if stateObject != nil { - stateObject.AddBalance(amount, reason) + if stateObject == nil { + return uint256.Int{} } + return stateObject.AddBalance(amount) } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { +func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { stateObject := s.getOrNewStateObject(addr) - if stateObject != nil { - stateObject.SubBalance(amount, reason) + if stateObject == nil { + return uint256.Int{} + } + if amount.IsZero() { + return *(stateObject.Balance()) } + return stateObject.SetBalance(new(uint256.Int).Sub(stateObject.Balance(), amount)) } func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { - stateObject.SetBalance(amount, reason) + stateObject.SetBalance(amount) } } @@ -445,11 +440,11 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) { } } -func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := s.getOrNewStateObject(addr) - if stateObject != nil { - stateObject.SetState(key, value) +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash { + if stateObject := s.getOrNewStateObject(addr); stateObject != nil { + return stateObject.SetState(key, value) } + return common.Hash{} } // SetStorage replaces the entire storage for the specified account with given @@ -477,7 +472,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common if obj != nil { newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code) newObj.SetNonce(obj.Nonce()) - newObj.SetBalance(obj.Balance(), tracing.BalanceChangeUnspecified) + newObj.SetBalance(obj.Balance()) } } @@ -486,15 +481,17 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common // // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after SelfDestruct. -func (s *StateDB) SelfDestruct(addr common.Address) { +func (s *StateDB) SelfDestruct(addr common.Address) uint256.Int { stateObject := s.getStateObject(addr) + var prevBalance uint256.Int if stateObject == nil { - return + return prevBalance } + prevBalance = *(stateObject.Balance()) // Regardless of whether it is already destructed or not, we do have to // journal the balance-change, if we set it to zero here. if !stateObject.Balance().IsZero() { - stateObject.SetBalance(new(uint256.Int), tracing.BalanceDecreaseSelfdestruct) + stateObject.SetBalance(new(uint256.Int)) } // If it is already marked as self-destructed, we do not need to add it // for journalling a second time. @@ -502,16 +499,18 @@ func (s *StateDB) SelfDestruct(addr common.Address) { s.journal.destruct(addr) stateObject.markSelfdestructed() } + return prevBalance } -func (s *StateDB) Selfdestruct6780(addr common.Address) { +func (s *StateDB) SelfDestruct6780(addr common.Address) (uint256.Int, bool) { stateObject := s.getStateObject(addr) if stateObject == nil { - return + return uint256.Int{}, false } if stateObject.newContract { - s.SelfDestruct(addr) + return s.SelfDestruct(addr), true } + return *(stateObject.Balance()), false } // SetTransientState sets transient storage for a given account. It @@ -735,11 +734,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { delete(s.stateObjects, obj.address) s.markDelete(addr) - - // If ether was sent to account post-selfdestruct it is burnt. - if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { - s.logger.OnBalanceChange(obj.address, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) - } // We need to maintain account deletions explicitly (will remain // set indefinitely). Note only the first occurred self-destruct // event is tracked. diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go new file mode 100644 index 000000000000..55b53ded40ff --- /dev/null +++ b/core/state/statedb_hooked.go @@ -0,0 +1,242 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/holiman/uint256" +) + +// hookedStateDB represents a statedb which emits calls to tracing-hooks +// on state operations. +type hookedStateDB struct { + inner *StateDB + hooks *tracing.Hooks +} + +// NewHookedState wraps the given stateDb with the given hooks +func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB { + s := &hookedStateDB{stateDb, hooks} + if s.hooks == nil { + s.hooks = new(tracing.Hooks) + } + return s +} + +func (s *hookedStateDB) CreateAccount(addr common.Address) { + s.inner.CreateAccount(addr) +} + +func (s *hookedStateDB) CreateContract(addr common.Address) { + s.inner.CreateContract(addr) +} + +func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int { + return s.inner.GetBalance(addr) +} + +func (s *hookedStateDB) GetNonce(addr common.Address) uint64 { + return s.inner.GetNonce(addr) +} + +func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash { + return s.inner.GetCodeHash(addr) +} + +func (s *hookedStateDB) GetCode(addr common.Address) []byte { + return s.inner.GetCode(addr) +} + +func (s *hookedStateDB) GetCodeSize(addr common.Address) int { + return s.inner.GetCodeSize(addr) +} + +func (s *hookedStateDB) AddRefund(u uint64) { + s.inner.AddRefund(u) +} + +func (s *hookedStateDB) SubRefund(u uint64) { + s.inner.SubRefund(u) +} + +func (s *hookedStateDB) GetRefund() uint64 { + return s.inner.GetRefund() +} + +func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + return s.inner.GetCommittedState(addr, hash) +} + +func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + return s.inner.GetState(addr, hash) +} + +func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash { + return s.inner.GetStorageRoot(addr) +} + +func (s *hookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { + return s.inner.GetTransientState(addr, key) +} + +func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) { + s.inner.SetTransientState(addr, key, value) +} + +func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool { + return s.inner.HasSelfDestructed(addr) +} + +func (s *hookedStateDB) Exist(addr common.Address) bool { + return s.inner.Exist(addr) +} + +func (s *hookedStateDB) Empty(addr common.Address) bool { + return s.inner.Empty(addr) +} + +func (s *hookedStateDB) AddressInAccessList(addr common.Address) bool { + return s.inner.AddressInAccessList(addr) +} + +func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) { + return s.inner.SlotInAccessList(addr, slot) +} + +func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) { + s.inner.AddAddressToAccessList(addr) +} + +func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + s.inner.AddSlotToAccessList(addr, slot) +} + +func (s *hookedStateDB) PointCache() *utils.PointCache { + return s.inner.PointCache() +} + +func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) { + s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses) +} + +func (s *hookedStateDB) RevertToSnapshot(i int) { + s.inner.RevertToSnapshot(i) +} + +func (s *hookedStateDB) Snapshot() int { + return s.inner.Snapshot() +} + +func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) { + s.inner.Snapshot() +} + +func (s *hookedStateDB) Witness() *stateless.Witness { + return s.inner.Witness() +} + +func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { + prev := s.inner.SubBalance(addr, amount, reason) + if s.hooks.OnBalanceChange != nil && !amount.IsZero() { + newBalance := new(uint256.Int).Sub(&prev, amount) + s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason) + } + return prev +} + +func (s *hookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { + prev := s.inner.AddBalance(addr, amount, reason) + if s.hooks.OnBalanceChange != nil && !amount.IsZero() { + newBalance := new(uint256.Int).Add(&prev, amount) + s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason) + } + return prev +} + +func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) { + s.inner.SetNonce(address, nonce) + if s.hooks.OnNonceChange != nil { + s.hooks.OnNonceChange(address, nonce-1, nonce) + } +} + +func (s *hookedStateDB) SetCode(address common.Address, code []byte) { + s.inner.SetCode(address, code) + if s.hooks.OnCodeChange != nil { + s.hooks.OnCodeChange(address, types.EmptyCodeHash, nil, crypto.Keccak256Hash(code), code) + } +} + +func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash { + prev := s.inner.SetState(address, key, value) + if s.hooks.OnStorageChange != nil && prev != value { + s.hooks.OnStorageChange(address, key, prev, value) + } + return prev +} + +func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int { + prev := s.inner.SelfDestruct(address) + if !prev.IsZero() { + if s.hooks.OnBalanceChange != nil { + s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) + } + } + return prev +} + +func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, bool) { + prev, changed := s.inner.SelfDestruct6780(address) + if !prev.IsZero() && changed { + if s.hooks.OnBalanceChange != nil { + s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) + } + } + return prev, changed +} + +func (s *hookedStateDB) AddLog(log *types.Log) { + // The inner will modify the log (add fields), so invoke that first + s.inner.AddLog(log) + if s.hooks.OnLog != nil { + s.hooks.OnLog(log) + } +} + +func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) { + defer s.inner.Finalise(deleteEmptyObjects) + if s.hooks.OnBalanceChange == nil { + return + } + for addr := range s.inner.journal.dirties { + obj := s.inner.stateObjects[addr] + if obj != nil && obj.selfDestructed { + // If ether was sent to account post-selfdestruct it is burnt. + if bal := obj.Balance(); bal.Sign() != 0 { + s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) + } + } + } +} diff --git a/core/state/statedb_hooked_test.go b/core/state/statedb_hooked_test.go new file mode 100644 index 000000000000..9abd76b02db8 --- /dev/null +++ b/core/state/statedb_hooked_test.go @@ -0,0 +1,130 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" +) + +// This method tests that the 'burn' from sending-to-selfdestructed accounts +// is accounted for. +// (There is also a higher-level test in eth/tracers: TestSupplySelfDestruct ) +func TestBurn(t *testing.T) { + // Note: burn can happen even after EIP-6780, if within one single transaction, + // the following occur: + // 1. contract B creates contract A + // 2. contract A is destructed + // 3. constract B sends ether to A + + var burned = new(uint256.Int) + s, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) + hooked := NewHookedState(s, &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + if reason == tracing.BalanceDecreaseSelfdestructBurn { + burned.Add(burned, uint256.MustFromBig(prev)) + } + }, + }) + createAndDestroy := func(addr common.Address) { + hooked.AddBalance(addr, uint256.NewInt(100), tracing.BalanceChangeUnspecified) + hooked.CreateContract(addr) + hooked.SelfDestruct(addr) + // sanity-check that balance is now 0 + if have, want := hooked.GetBalance(addr), new(uint256.Int); !have.Eq(want) { + t.Fatalf("post-destruct balance wrong: have %v want %v", have, want) + } + } + addA := common.Address{0xaa} + addB := common.Address{0xbb} + addC := common.Address{0xcc} + + // Tx 1: create and destroy address A and B in one tx + createAndDestroy(addA) + createAndDestroy(addB) + hooked.AddBalance(addA, uint256.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.AddBalance(addB, uint256.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.Finalise(true) + + // Tx 2: create and destroy address C, then commit + createAndDestroy(addC) + hooked.AddBalance(addC, uint256.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.Finalise(true) + + s.Commit(0, false) + if have, want := burned, uint256.NewInt(600); !have.Eq(want) { + t.Fatalf("burn-count wrong, have %v want %v", have, want) + } +} + +// TestHooks is a basic sanity-check of all hooks +func TestHooks(t *testing.T) { + inner, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) + inner.SetTxContext(common.Hash{0x11}, 100) // For the log + var result []string + var wants = []string{ + "0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)", + "0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)", + "0xaa00000000000000000000000000000000000000.nonce: 1336->1337", + "0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)", + "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011", + "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022", + "log 100", + } + emitF := func(format string, a ...any) { + result = append(result, fmt.Sprintf(format, a...)) + } + sdb := NewHookedState(inner, &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + emitF("%v.balance: %v->%v (%v)", addr, prev, new, reason) + }, + OnNonceChange: func(addr common.Address, prev, new uint64) { + emitF("%v.nonce: %v->%v", addr, prev, new) + }, + OnCodeChange: func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) { + emitF("%v.code: %#x (%v) ->%#x (%v)", addr, prevCode, prevCodeHash, code, codeHash) + }, + OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) { + emitF("%v.storage slot %v: %v ->%v", addr, slot, prev, new) + }, + OnLog: func(log *types.Log) { + emitF("log %v", log.TxIndex) + }, + }) + sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified) + sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer) + sdb.SetNonce(common.Address{0xaa}, 1337) + sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37}) + sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11")) + sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22")) + sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01")) + sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x02")) + sdb.AddLog(&types.Log{ + Address: common.Address{0xbb}, + }) + for i, want := range wants { + if have := result[i]; have != want { + t.Fatalf("error event %d, have\n%v\nwant%v\n", i, have, want) + } + } +} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 3c19ec0591f5..3647397df6f8 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -170,7 +170,7 @@ func TestCopy(t *testing.T) { for i := byte(0); i < 255; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(i))) orig.updateStateObject(obj) } orig.Finalise(false) @@ -187,9 +187,9 @@ func TestCopy(t *testing.T) { copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(uint256.NewInt(2*uint64(i)), tracing.BalanceChangeUnspecified) - copyObj.AddBalance(uint256.NewInt(3*uint64(i)), tracing.BalanceChangeUnspecified) - ccopyObj.AddBalance(uint256.NewInt(4*uint64(i)), tracing.BalanceChangeUnspecified) + origObj.AddBalance(uint256.NewInt(2 * uint64(i))) + copyObj.AddBalance(uint256.NewInt(3 * uint64(i))) + ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i))) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -236,7 +236,7 @@ func TestCopyWithDirtyJournal(t *testing.T) { // Fill up the initial states for i := byte(0); i < 255; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(i))) obj.data.Root = common.HexToHash("0xdeadbeef") orig.updateStateObject(obj) } @@ -246,7 +246,9 @@ func TestCopyWithDirtyJournal(t *testing.T) { // modify all in memory without finalizing for i := byte(0); i < 255; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.SubBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + amount := uint256.NewInt(uint64(i)) + obj.SetBalance(new(uint256.Int).Sub(obj.Balance(), amount)) + orig.updateStateObject(obj) } cpy := orig.Copy() @@ -280,7 +282,7 @@ func TestCopyObjectState(t *testing.T) { // Fill up the initial states for i := byte(0); i < 5; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(i))) obj.data.Root = common.HexToHash("0xdeadbeef") orig.updateStateObject(obj) } diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 2416cda873db..b2c75e72fe33 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -62,7 +61,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c obj := state.getOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} - obj.AddBalance(uint256.NewInt(uint64(11*i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(11 * i))) acc.balance = uint256.NewInt(uint64(11 * i)) obj.SetNonce(uint64(42 * i)) diff --git a/core/state_processor.go b/core/state_processor.go index fe0304e81d67..ec499f892875 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -75,6 +75,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Apply pre-execution system calls. context = NewEVMBlockContext(header, p.chain, nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) @@ -98,7 +99,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } - + var tracingStateDB = vm.StateDB(statedb) + if hooks := cfg.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + } // Read requests if Prague is enabled. var requests [][]byte if p.config.IsPrague(block.Number(), block.Time()) { @@ -109,15 +113,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } requests = append(requests, depositRequests) // EIP-7002 withdrawals - withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb) + withdrawalRequests := ProcessWithdrawalQueue(vmenv, tracingStateDB) requests = append(requests, withdrawalRequests) // EIP-7251 consolidations - consolidationRequests := ProcessConsolidationQueue(vmenv, statedb) + consolidationRequests := ProcessConsolidationQueue(vmenv, tracingStateDB) requests = append(requests, consolidationRequests) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.chain.engine.Finalize(p.chain, header, statedb, block.Body()) + p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body()) return &ProcessResult{ Receipts: receipts, @@ -131,17 +135,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { - if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { - evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) - if evm.Config.Tracer.OnTxEnd != nil { - defer func() { - evm.Config.Tracer.OnTxEnd(receipt, err) - }() + var tracingStateDB = vm.StateDB(statedb) + if hooks := evm.Config.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + if hooks.OnTxStart != nil { + hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + if hooks.OnTxEnd != nil { + defer func() { hooks.OnTxEnd(receipt, err) }() } } + // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) - evm.Reset(txContext, statedb) + evm.Reset(txContext, tracingStateDB) // Apply the transaction to the current state (included in the env). result, err := ApplyMessage(evm, msg, gp) @@ -152,7 +159,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo // Update the state with pending changes. var root []byte if config.IsByzantium(blockNumber) { - statedb.Finalise(true) + tracingStateDB.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() } @@ -217,7 +224,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // contract. This method is exported to be used in tests. -func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) { +func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb vm.StateDB) { if tracer := vmenv.Config.Tracer; tracer != nil { if tracer.OnSystemCallStart != nil { tracer.OnSystemCallStart() @@ -243,7 +250,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat // ProcessParentBlockHash stores the parent block hash in the history storage contract // as per EIP-2935. -func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state.StateDB) { +func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb vm.StateDB) { if tracer := vmenv.Config.Tracer; tracer != nil { if tracer.OnSystemCallStart != nil { tracer.OnSystemCallStart() @@ -269,17 +276,17 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state. // ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract. // It returns the opaque request data returned by the contract. -func ProcessWithdrawalQueue(vmenv *vm.EVM, statedb *state.StateDB) []byte { +func ProcessWithdrawalQueue(vmenv *vm.EVM, statedb vm.StateDB) []byte { return processRequestsSystemCall(vmenv, statedb, 0x01, params.WithdrawalQueueAddress) } // ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract. // It returns the opaque request data returned by the contract. -func ProcessConsolidationQueue(vmenv *vm.EVM, statedb *state.StateDB) []byte { +func ProcessConsolidationQueue(vmenv *vm.EVM, statedb vm.StateDB) []byte { return processRequestsSystemCall(vmenv, statedb, 0x02, params.ConsolidationQueueAddress) } -func processRequestsSystemCall(vmenv *vm.EVM, statedb *state.StateDB, requestType byte, addr common.Address) []byte { +func processRequestsSystemCall(vmenv *vm.EVM, statedb vm.StateDB, requestType byte, addr common.Address) []byte { if tracer := vmenv.Config.Tracer; tracer != nil { if tracer.OnSystemCallStart != nil { tracer.OnSystemCallStart() diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 35d6393fba0e..0e2fd52b14cf 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -919,7 +919,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) - interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) + interpreter.evm.StateDB.SelfDestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig()) diff --git a/core/vm/interface.go b/core/vm/interface.go index 5f426435650d..9229f4d2cd95 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -33,8 +33,8 @@ type StateDB interface { CreateAccount(common.Address) CreateContract(common.Address) - SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) - AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) + SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int + AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int GetBalance(common.Address) *uint256.Int GetNonce(common.Address) uint64 @@ -51,16 +51,21 @@ type StateDB interface { GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) + SetState(common.Address, common.Hash, common.Hash) common.Hash GetStorageRoot(addr common.Address) common.Hash GetTransientState(addr common.Address, key common.Hash) common.Hash SetTransientState(addr common.Address, key, value common.Hash) - SelfDestruct(common.Address) + SelfDestruct(common.Address) uint256.Int HasSelfDestructed(common.Address) bool - Selfdestruct6780(common.Address) + // SelfDestruct6780 is post-EIP6780 selfdestruct, which means that it's a + // send-all-to-beneficiary, unless the contract was created in this same + // transaction, in which case it will be destructed. + // This method returns the prior balance, along with a boolean which is + // true iff the object was indeed destructed. + SelfDestruct6780(common.Address) (uint256.Int, bool) // Exist reports whether the given account exists in state. // Notably this should also return true for self-destructed accounts. @@ -90,6 +95,9 @@ type StateDB interface { AddPreimage(common.Hash, []byte) Witness() *stateless.Witness + + // Finalise must be invoked at the end of a transaction + Finalise(bool) } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 5b6945f54f0c..7d8191c25bd6 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -1018,7 +1018,6 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor } // The actual TxContext will be created as part of ApplyTransactionWithEVM. vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) - statedb.SetLogger(tracer.Hooks) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index d21e589f3dfc..5b9c80959662 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -116,21 +117,23 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = test.Context.toBlockContext(test.Genesis) - state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + st = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) - state.Close() + st.Close() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - - state.StateDB.SetLogger(tracer.Hooks) + logState := vm.StateDB(st.StateDB) + if tracer.Hooks != nil { + logState = state.NewHookedState(st.StateDB, tracer.Hooks) + } msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { @@ -349,7 +352,7 @@ func TestInternals(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - state := tests.MakePreState(rawdb.NewMemoryDatabase(), + st := tests.MakePreState(rawdb.NewMemoryDatabase(), types.GenesisAlloc{ to: types.Account{ Code: tc.code, @@ -358,8 +361,13 @@ func TestInternals(t *testing.T) { Balance: big.NewInt(500000000000000), }, }, false, rawdb.HashScheme) - defer state.Close() - state.StateDB.SetLogger(tc.tracer.Hooks) + defer st.Close() + + logState := vm.StateDB(st.StateDB) + if hooks := tc.tracer.Hooks; hooks != nil { + logState = state.NewHookedState(st.StateDB, hooks) + } + tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ To: &to, Value: big.NewInt(0), @@ -373,7 +381,7 @@ func TestInternals(t *testing.T) { Origin: origin, GasPrice: tx.GasPrice(), } - evm := vm.NewEVM(context, txContext, state.StateDB, config, vm.Config{Tracer: tc.tracer.Hooks}) + evm := vm.NewEVM(context, txContext, logState, config, vm.Config{Tracer: tc.tracer.Hooks}) msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 7a6e1751e87d..0ec3c367bc5b 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -94,7 +94,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to create call tracer: %v", err) } - state.StateDB.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 90f59225dfd0..c6cf10a48346 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -102,7 +102,6 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } - state.StateDB.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 2cddcae67d8a..2391add91b95 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -597,6 +597,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG } func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { + t.Helper() want, err := json.Marshal(expected) if err != nil { t.Fatalf("failed to marshal expected value to JSON: %v", err) @@ -608,6 +609,6 @@ func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { } if !bytes.Equal(want, have) { - t.Fatalf("incorrect supply info: expected %s, got %s", string(want), string(have)) + t.Fatalf("incorrect supply info:\nwant %s\nhave %s", string(want), string(have)) } } diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 137608f8847d..fb1a0154e311 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -49,9 +49,11 @@ type dummyStatedb struct { state.StateDB } -func (*dummyStatedb) GetRefund() uint64 { return 1337 } -func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } -func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} +func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } +func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) common.Hash { + return common.Hash{} +} func TestStoreCapture(t *testing.T) { var ( diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a1f4304690a4..10d79c85ae06 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1209,11 +1209,16 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if precompiles != nil { evm.SetPrecompiles(precompiles) } - - return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp) + res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp) + // If an internal state error occurred, let that have precedence. Otherwise, + // a "trie root missing" type of error will masquerade as e.g. "insufficient gas" + if err := state.Error(); err != nil { + return nil, err + } + return res, err } -func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { +func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) go func() { @@ -1223,9 +1228,6 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, st // Execute the message. result, err := core.ApplyMessage(evm, msg, gp) - if err := state.Error(); err != nil { - return nil, err - } // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index b5b7b628a40e..758638cb16f1 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -2192,6 +2192,7 @@ func TestSimulateV1(t *testing.T) { t.Fatalf("failed to unmarshal result: %v", err) } if !reflect.DeepEqual(have, tc.want) { + t.Log(string(resBytes)) t.Errorf("test %s, result mismatch, have\n%v\n, want\n%v\n", tc.name, have, tc.want) } }) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 4371a4246480..81b4633d42cf 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -187,7 +187,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, } evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, sim.chainConfig, *vmConfig) ) - sim.state.SetLogger(tracer.Hooks()) + var tracingStateDB = vm.StateDB(sim.state) + if hooks := tracer.Hooks(); hooks != nil { + tracingStateDB = state.NewHookedState(sim.state, hooks) + } // It is possible to override precompiles with EVM bytecode, or // move them to another address. if precompiles != nil { @@ -205,8 +208,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracer.reset(tx.Hash(), uint(i)) // EoA check is always skipped, even in validation mode. msg := call.ToMessage(header.BaseFee, !sim.validate, true) - evm.Reset(core.NewEVMTxContext(msg), sim.state) - result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, sim.gp) + evm.Reset(core.NewEVMTxContext(msg), tracingStateDB) + result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) return nil, nil, txErr @@ -214,7 +217,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, // Update the state with pending changes. var root []byte if sim.chainConfig.IsByzantium(blockContext.BlockNumber) { - sim.state.Finalise(true) + tracingStateDB.Finalise(true) } else { root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() } From 74461aecf68788abb198ba9bc42f119a4f98bc68 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Wed, 23 Oct 2024 07:11:25 +0100 Subject: [PATCH 2/5] crypto, tests/fuzzers: add gnark bn254 precompile methods for fuzzing (#30585) Makes the gnark precompile methods more amenable to fuzzing --- crypto/bn256/gnark/g1.go | 51 +++++++++++++++++++++ crypto/bn256/gnark/g2.go | 38 ++++++++++++++++ crypto/bn256/gnark/gt.go | 65 +++++++++++++++++++++++++++ crypto/bn256/gnark/pairing.go | 73 +++++++++++++++++++++++++++++++ tests/fuzzers/bn256/bn256_fuzz.go | 64 ++++++++++++++------------- 5 files changed, 261 insertions(+), 30 deletions(-) create mode 100644 crypto/bn256/gnark/g1.go create mode 100644 crypto/bn256/gnark/g2.go create mode 100644 crypto/bn256/gnark/gt.go create mode 100644 crypto/bn256/gnark/pairing.go diff --git a/crypto/bn256/gnark/g1.go b/crypto/bn256/gnark/g1.go new file mode 100644 index 000000000000..2f933dd53601 --- /dev/null +++ b/crypto/bn256/gnark/g1.go @@ -0,0 +1,51 @@ +package bn256 + +import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// G1 is the affine representation of a G1 group element. +// +// Since this code is used for precompiles, using Jacobian +// points are not beneficial because there are no intermediate +// points to allow us to save on inversions. +// +// Note: We also use this struct so that we can conform to the existing API +// that the precompiles want. +type G1 struct { + inner bn254.G1Affine +} + +// Add adds `a` and `b` together, storing the result in `g` +func (g *G1) Add(a, b *G1) { + g.inner.Add(&a.inner, &b.inner) +} + +// ScalarMult computes the scalar multiplication between `a` and +// `scalar`, storing the result in `g` +func (g *G1) ScalarMult(a *G1, scalar *big.Int) { + g.inner.ScalarMultiplication(&a.inner, scalar) +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: whether the deserialization is of a compressed +// or an uncompressed point, is encoded in the bytes. +// +// For our purpose, the point will always be serialized +// as uncompressed, ie 64 bytes. +// +// This method also checks whether the point is on the +// curve and in the prime order subgroup. +func (g *G1) Unmarshal(buf []byte) (int, error) { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: The point is serialized as uncompressed. +func (p *G1) Marshal() []byte { + return p.inner.Marshal() +} diff --git a/crypto/bn256/gnark/g2.go b/crypto/bn256/gnark/g2.go new file mode 100644 index 000000000000..205373a59194 --- /dev/null +++ b/crypto/bn256/gnark/g2.go @@ -0,0 +1,38 @@ +package bn256 + +import ( + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// G2 is the affine representation of a G2 group element. +// +// Since this code is used for precompiles, using Jacobian +// points are not beneficial because there are no intermediate +// points and G2 in particular is only used for the pairing input. +// +// Note: We also use this struct so that we can conform to the existing API +// that the precompiles want. +type G2 struct { + inner bn254.G2Affine +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: whether the deserialization is of a compressed +// or an uncompressed point, is encoded in the bytes. +// +// For our purpose, the point will always be serialized +// as uncompressed, ie 128 bytes. +// +// This method also checks whether the point is on the +// curve and in the prime order subgroup. +func (g *G2) Unmarshal(buf []byte) (int, error) { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: The point is serialized as uncompressed. +func (g *G2) Marshal() []byte { + return g.inner.Marshal() +} diff --git a/crypto/bn256/gnark/gt.go b/crypto/bn256/gnark/gt.go new file mode 100644 index 000000000000..c30022c5f898 --- /dev/null +++ b/crypto/bn256/gnark/gt.go @@ -0,0 +1,65 @@ +package bn256 + +import ( + "fmt" + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// GT is the affine representation of a GT field element. +// +// Note: GT is not explicitly used in mainline code. +// It is needed for fuzzing. +type GT struct { + inner bn254.GT +} + +// Pair compute the optimal Ate pairing between a G1 and +// G2 element. +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. It should also be noted, +// that the output of this function may not match other +func Pair(a_ *G1, b_ *G2) *GT { + a := a_.inner + b := b_.inner + + pairingOutput, err := bn254.Pair([]bn254.G1Affine{a}, []bn254.G2Affine{b}) + + if err != nil { + // Since this method is only called during fuzzing, it is okay to panic here. + // We do not return an error to match the interface of the other bn256 libraries. + panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) + } + + return >{ + inner: pairingOutput, + } +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Unmarshal(buf []byte) error { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Marshal() []byte { + bytes := g.inner.Bytes() + return bytes[:] +} + +// Exp raises `base` to the power of `exponent` +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Exp(base GT, exponent *big.Int) *GT { + g.inner.Exp(base.inner, exponent) + return g +} diff --git a/crypto/bn256/gnark/pairing.go b/crypto/bn256/gnark/pairing.go new file mode 100644 index 000000000000..39e8a657f466 --- /dev/null +++ b/crypto/bn256/gnark/pairing.go @@ -0,0 +1,73 @@ +package bn256 + +import ( + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// Computes the following relation: ∏ᵢ e(Pᵢ, Qᵢ) =? 1 +// +// To explain why gnark returns a (bool, error): +// +// - If the function `e` does not return a result then internally +// an error is returned. +// - If `e` returns a result, then error will be nil, +// but if this value is not `1` then the boolean value will be false +// +// We therefore check for an error, and return false if its non-nil and +// then return the value of the boolean if not. +func PairingCheck(a_ []*G1, b_ []*G2) bool { + a := getInnerG1s(a_) + b := getInnerG2s(b_) + + // Assume that len(a) == len(b) + // + // The pairing function will return + // false, if this is not the case. + size := len(a) + + // Check if input is empty -- gnark will + // return false on an empty input, however + // the ossified behavior is to return true + // on an empty input, so we add this if statement. + if size == 0 { + return true + } + + ok, err := bn254.PairingCheck(a, b) + if err != nil { + return false + } + return ok +} + +// getInnerG1s gets the inner gnark G1 elements. +// +// These methods are used for two reasons: +// +// - We use a new type `G1`, so we need to convert from +// []*G1 to []*bn254.G1Affine +// - The gnark API accepts slices of values and not slices of +// pointers to values, so we need to return []bn254.G1Affine +// instead of []*bn254.G1Affine. +func getInnerG1s(pointerSlice []*G1) []bn254.G1Affine { + gnarkValues := make([]bn254.G1Affine, 0, len(pointerSlice)) + for _, ptr := range pointerSlice { + if ptr != nil { + gnarkValues = append(gnarkValues, ptr.inner) + } + } + return gnarkValues +} + +// getInnerG2s gets the inner gnark G2 elements. +// +// The rationale for this method is the same as `getInnerG1s`. +func getInnerG2s(pointerSlice []*G2) []bn254.G2Affine { + gnarkValues := make([]bn254.G2Affine, 0, len(pointerSlice)) + for _, ptr := range pointerSlice { + if ptr != nil { + gnarkValues = append(gnarkValues, ptr.inner) + } + } + return gnarkValues +} diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 75f7d59deeef..4521f6b0dbca 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -22,12 +22,12 @@ import ( "io" "math/big" - "github.com/consensys/gnark-crypto/ecc/bn254" cloudflare "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + gnark "github.com/ethereum/go-ethereum/crypto/bn256/gnark" google "github.com/ethereum/go-ethereum/crypto/bn256/google" ) -func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) { +func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gnark.G1) { _, xc, err := cloudflare.RandomG1(input) if err != nil { // insufficient input @@ -37,14 +37,14 @@ func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(bn254.G1Affine) - if err := xs.Unmarshal(xc.Marshal()); err != nil { + xs := new(gnark.G1) + if _, err := xs.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) { +func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gnark.G2) { _, xc, err := cloudflare.RandomG2(input) if err != nil { // insufficient input @@ -54,14 +54,14 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(bn254.G2Affine) - if err := xs.Unmarshal(xc.Marshal()); err != nil { + xs := new(gnark.G2) + if _, err := xs.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. +// fuzzAdd fuzzes bn256 addition between the Google, Cloudflare and Gnark libraries. func fuzzAdd(data []byte) int { input := bytes.NewReader(data) xc, xg, xs := getG1Points(input) @@ -72,7 +72,7 @@ func fuzzAdd(data []byte) int { if yc == nil { return 0 } - // Ensure both libs can parse the second curve point + // Ensure libs can parse the second curve point // Add the two points and ensure they result in the same output rc := new(cloudflare.G1) rc.Add(xc, yc) @@ -80,9 +80,8 @@ func fuzzAdd(data []byte) int { rg := new(google.G1) rg.Add(xg, yg) - tmpX := new(bn254.G1Jac).FromAffine(xs) - tmpY := new(bn254.G1Jac).FromAffine(ys) - rs := new(bn254.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) + rs := new(gnark.G1) + rs.Add(xs, ys) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("add mismatch: cloudflare/google") @@ -94,8 +93,8 @@ func fuzzAdd(data []byte) int { return 1 } -// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare -// libraries. +// fuzzMul fuzzes bn256 scalar multiplication between the Google, Cloudflare +// and Gnark libraries. func fuzzMul(data []byte) int { input := bytes.NewReader(data) pc, pg, ps := getG1Points(input) @@ -122,15 +121,13 @@ func fuzzMul(data []byte) int { rg := new(google.G1) rg.ScalarMult(pg, new(big.Int).SetBytes(buf)) - rs := new(bn254.G1Jac) - psJac := new(bn254.G1Jac).FromAffine(ps) - rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf)) - rsAffine := new(bn254.G1Affine).FromJacobian(rs) + rs := new(gnark.G1) + rs.ScalarMult(ps, new(big.Int).SetBytes(buf)) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("scalar mul mismatch: cloudflare/google") } - if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) { + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { panic("scalar mul mismatch: cloudflare/gnark") } return 1 @@ -150,17 +147,26 @@ func fuzzPair(data []byte) int { // Pair the two points and ensure they result in the same output clPair := cloudflare.Pair(pc, tc).Marshal() gPair := google.Pair(pg, tg).Marshal() + sPair := gnark.Pair(ps, ts).Marshal() + if !bytes.Equal(clPair, gPair) { panic("pairing mismatch: cloudflare/google") } - cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts}) - if err != nil { - panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) + + normalizedClPair := normalizeGTToGnark(clPair).Marshal() + if !bytes.Equal(normalizedClPair, sPair) { + panic("pairing mismatch: cloudflare/gnark") } - // gnark uses a different pairing algorithm which might produce - // different but also correct outputs, we need to scale the output by s + return 1 +} +// normalizeGTToGnark scales a Cloudflare/Google GT element by `s` +// so that it can be compared with a gnark GT point. +// +// For the definition of `s` see 3.5 in https://eprint.iacr.org/2015/192.pdf +func normalizeGTToGnark(cloudflareOrGoogleGT []byte) *gnark.GT { + // Compute s = 2*u(6*u^2 + 3*u + 1) u, _ := new(big.Int).SetString("0x44e992b44a6909f1", 0) u_exp2 := new(big.Int).Exp(u, big.NewInt(2), nil) // u^2 u_6_exp2 := new(big.Int).Mul(big.NewInt(6), u_exp2) // 6*u^2 @@ -170,14 +176,12 @@ func fuzzPair(data []byte) int { u_2 := new(big.Int).Mul(big.NewInt(2), u) // 2*u s := u_2.Mul(u_2, inner) // 2*u(6*u^2 + 3*u + 1) - gRes := new(bn254.GT) - if err := gRes.SetBytes(clPair); err != nil { + // Scale the Cloudflare/Google GT element by `s` + gRes := new(gnark.GT) + if err := gRes.Unmarshal(cloudflareOrGoogleGT); err != nil { panic(err) } gRes = gRes.Exp(*gRes, s) - if !bytes.Equal(cPair.Marshal(), gRes.Marshal()) { - panic("pairing mismatch: cloudflare/gnark") - } - return 1 + return gRes } From 478012ab2391add45e6ceb8e61bb954b2ca101c7 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Wed, 23 Oct 2024 13:26:18 +0700 Subject: [PATCH 3/5] all: remove TerminalTotalDifficultyPassed (#30609) rebased https://github.com/ethereum/go-ethereum/pull/29766 . The downstream branch appears to have been deleted and I don't have perms to push to that fork. `TerminalTotalDifficultyPassed` is removed. `TerminalTotalDifficulty` must now be non-nil, and it is expected that networks are already merged: we can only import PoW/Clique chains, not produce blocks on them. --------- Co-authored-by: stevemilk --- .../internal/ethtest/testdata/genesis.json | 1 - cmd/geth/genesis_test.go | 12 +- cmd/geth/testdata/clique.json | 4 +- cmd/utils/flags.go | 3 - consensus/beacon/consensus.go | 6 - core/block_validator_test.go | 5 +- core/blockchain_test.go | 1 - core/chain_makers_test.go | 1 - core/forkid/forkid_test.go | 39 +- core/genesis_test.go | 49 +- core/state_processor_test.go | 78 ++- core/vm/runtime/runtime.go | 46 +- eth/catalyst/api_test.go | 1 - eth/ethconfig/config.go | 8 +- eth/protocols/eth/handler_test.go | 41 +- .../testdata/call_tracer/blob_tx.json | 3 +- .../nested_pointer_issue.json | 3 +- .../call_tracer_withLog/calldata.json | 1 - .../call_tracer_withLog/delegatecall.json | 1 - .../frontier_create_outofstorage.json | 3 +- .../call_tracer_withLog/multi_contracts.json | 1 - .../call_tracer_withLog/multilogs.json | 1 - .../testdata/call_tracer_withLog/notopic.json | 1 - .../testdata/call_tracer_withLog/simple.json | 1 - .../call_tracer_withLog/tx_failed.json | 1 - .../tx_partial_failed.json | 1 - .../call_tracer_withLog/with_onlyTopCall.json | 1 - .../testdata/prestate_tracer/blob_tx.json | 3 +- .../prestate_tracer/create_create.json | 3 +- .../create_existing_contract.json | 1 - .../create_failed.json | 1 - graphql/graphql_test.go | 10 +- params/config.go | 452 ++++++++---------- tests/block_test_util.go | 6 + tests/difficulty_test.go | 31 +- 35 files changed, 378 insertions(+), 442 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/testdata/genesis.json b/cmd/devp2p/internal/ethtest/testdata/genesis.json index e8bb66bb3c1a..4cfebdcac10d 100644 --- a/cmd/devp2p/internal/ethtest/testdata/genesis.json +++ b/cmd/devp2p/internal/ethtest/testdata/genesis.json @@ -18,7 +18,6 @@ "shanghaiTime": 780, "cancunTime": 840, "terminalTotalDifficulty": 9454784, - "terminalTotalDifficultyPassed": true, "ethash": {} }, "nonce": "0x0", diff --git a/cmd/geth/genesis_test.go b/cmd/geth/genesis_test.go index ffe8176b0138..f5d21cc00b4d 100644 --- a/cmd/geth/genesis_test.go +++ b/cmd/geth/genesis_test.go @@ -29,7 +29,7 @@ var customGenesisTests = []struct { query string result string }{ - // Genesis file with an empty chain configuration (ensure missing fields work) + // Genesis file with a mostly-empty chain configuration (ensure missing fields work) { genesis: `{ "alloc" : {}, @@ -41,8 +41,8 @@ var customGenesisTests = []struct { "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", - "config" : { - "terminalTotalDifficultyPassed": true + "config": { + "terminalTotalDifficulty": 0 } }`, query: "eth.getBlock(0).nonce", @@ -64,7 +64,7 @@ var customGenesisTests = []struct { "homesteadBlock" : 42, "daoForkBlock" : 141, "daoForkSupport" : true, - "terminalTotalDifficultyPassed" : true + "terminalTotalDifficulty": 0 } }`, query: "eth.getBlock(0).nonce", @@ -114,8 +114,8 @@ func TestCustomBackend(t *testing.T) { "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", - "config" : { - "terminalTotalDifficultyPassed": true + "config": { + "terminalTotalDifficulty": 0 } }` type backendTest struct { diff --git a/cmd/geth/testdata/clique.json b/cmd/geth/testdata/clique.json index 36f5c3105703..d318f4c16612 100644 --- a/cmd/geth/testdata/clique.json +++ b/cmd/geth/testdata/clique.json @@ -8,7 +8,7 @@ "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, - "terminalTotalDifficultyPassed": true, + "terminalTotalDifficulty": 0, "clique": { "period": 5, "epoch": 30000 @@ -22,4 +22,4 @@ "balance": "300000" } } -} \ No newline at end of file +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ea9724b9176f..e98d5cc8e3e0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1867,9 +1867,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if err != nil { Fatalf("Could not read genesis from database: %v", err) } - if !genesis.Config.TerminalTotalDifficultyPassed { - Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true") - } if genesis.Config.TerminalTotalDifficulty == nil { Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified") } else if genesis.Config.TerminalTotalDifficulty.Cmp(big.NewInt(0)) != 0 { diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 2b824027d4f9..4ee19c7d4d6e 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -116,9 +116,6 @@ func errOut(n int, err error) chan error { func (beacon *Beacon) splitHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) ([]*types.Header, []*types.Header, error) { // TTD is not defined yet, all headers should be in legacy format. ttd := chain.Config().TerminalTotalDifficulty - if ttd == nil { - return headers, nil, nil - } ptd := chain.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) if ptd == nil { return nil, nil, consensus.ErrUnknownAncestor @@ -495,9 +492,6 @@ func (beacon *Beacon) SetThreads(threads int) { // It depends on the parentHash already being stored in the database. // If the parentHash is not stored in the database a UnknownAncestor error is returned. func IsTTDReached(chain consensus.ChainHeaderReader, parentHash common.Hash, parentNumber uint64) (bool, error) { - if chain.Config().TerminalTotalDifficulty == nil { - return false, nil - } td := chain.GetTd(parentHash, parentNumber) if td == nil { return false, consensus.ErrUnknownAncestor diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 80e682066163..8af4057693e9 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -113,8 +113,12 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { } copy(gspec.ExtraData[32:], addr[:]) + // chain_maker has no blockchain to retrieve the TTD from, setting to nil + // is a hack to signal it to generate pre-merge blocks + gspec.Config.TerminalTotalDifficulty = nil td := 0 genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 8, nil) + for i, block := range blocks { header := block.Header() if i > 0 { @@ -145,7 +149,6 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { } preBlocks = blocks gspec.Config.TerminalTotalDifficulty = big.NewInt(int64(td)) - t.Logf("Set ttd to %v\n", gspec.Config.TerminalTotalDifficulty) postBlocks, _ = GenerateChain(gspec.Config, preBlocks[len(preBlocks)-1], engine, genDb, 8, func(i int, gen *BlockGen) { gen.SetPoS() }) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index b8c4678ac801..05629cd19f89 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4092,7 +4092,6 @@ func TestEIP3651(t *testing.T) { gspec.Config.BerlinBlock = common.Big0 gspec.Config.LondonBlock = common.Big0 gspec.Config.TerminalTotalDifficulty = common.Big0 - gspec.Config.TerminalTotalDifficultyPassed = true gspec.Config.ShanghaiTime = u64(0) signer := types.LatestSigner(gspec.Config) diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 61d09117bddc..f72e6285dfa3 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -57,7 +57,6 @@ func TestGeneratePOSChain(t *testing.T) { db = rawdb.NewMemoryDatabase() ) - config.TerminalTotalDifficultyPassed = true config.TerminalTotalDifficulty = common.Big0 config.ShanghaiTime = u64(0) config.CancunTime = u64(0) diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index ec6c27b48dbe..de589c0d44f6 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -378,26 +378,25 @@ func TestTimeBasedForkInGenesis(t *testing.T) { forkidHash = checksumToBytes(crc32.ChecksumIEEE(genesis.Hash().Bytes())) config = func(shanghai, cancun uint64) *params.ChainConfig { return ¶ms.ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: big.NewInt(0), - ShanghaiTime: &shanghai, - CancunTime: &cancun, - Ethash: new(params.EthashConfig), + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: &shanghai, + CancunTime: &cancun, + Ethash: new(params.EthashConfig), } } ) diff --git a/core/genesis_test.go b/core/genesis_test.go index 0fee8741386a..9eacf2024c68 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -257,31 +257,30 @@ func newDbConfig(scheme string) *triedb.Config { func TestVerkleGenesisCommit(t *testing.T) { var verkleTime uint64 = 0 verkleConfig := ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: nil, - ShanghaiTime: &verkleTime, - CancunTime: &verkleTime, - PragueTime: &verkleTime, - VerkleTime: &verkleTime, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - Ethash: nil, - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: &verkleTime, + CancunTime: &verkleTime, + PragueTime: &verkleTime, + VerkleTime: &verkleTime, + TerminalTotalDifficulty: big.NewInt(0), + Ethash: nil, + Clique: nil, } genesis := &Genesis{ diff --git a/core/state_processor_test.go b/core/state_processor_test.go index b701ee651b50..2a16ef2cfb8f 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -51,23 +51,22 @@ func u64(val uint64) *uint64 { return &val } func TestStateProcessorErrors(t *testing.T) { var ( config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - ShanghaiTime: new(uint64), - CancunTime: new(uint64), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: new(uint64), + CancunTime: new(uint64), } signer = types.LatestSigner(config) key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -257,7 +256,7 @@ func TestStateProcessorErrors(t *testing.T) { want: "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 1, baseFee: 875000000", }, } { - block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config) + block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -306,7 +305,7 @@ func TestStateProcessorErrors(t *testing.T) { want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported", }, } { - block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config) + block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config, true) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -345,7 +344,7 @@ func TestStateProcessorErrors(t *testing.T) { want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", }, } { - block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config) + block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -361,9 +360,9 @@ func TestStateProcessorErrors(t *testing.T) { // valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently // valid to be considered for import: // - valid pow (fake), ancestry, difficulty, gaslimit etc -func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block { +func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig, isPOW bool) *types.Block { difficulty := big.NewInt(0) - if !config.TerminalTotalDifficultyPassed { + if isPOW { fakeChainReader := newChainMaker(nil, config, engine) difficulty = engine.CalcDifficulty(fakeChainReader, parent.Time()+10, &types.Header{ Number: parent.Number(), @@ -441,25 +440,22 @@ var ( func TestProcessVerkle(t *testing.T) { var ( config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - ShanghaiTime: u64(0), - VerkleTime: u64(0), - TerminalTotalDifficulty: common.Big0, - TerminalTotalDifficultyPassed: true, - // TODO uncomment when proof generation is merged - // ProofInBlocks: true, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + VerkleTime: u64(0), + TerminalTotalDifficulty: common.Big0, } signer = types.LatestSigner(config) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index f83ed682cd13..2ad991ee41f3 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -61,27 +61,26 @@ func setDefaults(cfg *Config) { cancunTime = uint64(0) ) cfg.ChainConfig = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: new(big.Int), - DAOForkBlock: new(big.Int), - DAOForkSupport: false, - EIP150Block: new(big.Int), - EIP155Block: new(big.Int), - EIP158Block: new(big.Int), - ByzantiumBlock: new(big.Int), - ConstantinopleBlock: new(big.Int), - PetersburgBlock: new(big.Int), - IstanbulBlock: new(big.Int), - MuirGlacierBlock: new(big.Int), - BerlinBlock: new(big.Int), - LondonBlock: new(big.Int), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: nil, - ShanghaiTime: &shanghaiTime, - CancunTime: &cancunTime} + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + PetersburgBlock: new(big.Int), + IstanbulBlock: new(big.Int), + MuirGlacierBlock: new(big.Int), + BerlinBlock: new(big.Int), + LondonBlock: new(big.Int), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: &shanghaiTime, + CancunTime: &cancunTime} } if cfg.Difficulty == nil { cfg.Difficulty = new(big.Int) @@ -109,10 +108,7 @@ func setDefaults(cfg *Config) { if cfg.BlobBaseFee == nil { cfg.BlobBaseFee = big.NewInt(params.BlobTxMinBlobGasprice) } - // Merge indicators - if t := cfg.ChainConfig.ShanghaiTime; cfg.ChainConfig.TerminalTotalDifficultyPassed || (t != nil && *t == 0) { - cfg.Random = &(common.Hash{}) - } + cfg.Random = &(common.Hash{}) } // Execute executes the code using the input as call data during the execution. diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 74a5a66ed1c9..7bb31329c6ce 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -66,7 +66,6 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker())) if merged { config.TerminalTotalDifficulty = common.Big0 - config.TerminalTotalDifficultyPassed = true engine = beaconConsensus.NewFaker() } genesis := &core.Genesis{ diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index c781a639408a..c325e5010c4d 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,7 +18,7 @@ package ethconfig import ( - "errors" + "fmt" "time" "github.com/ethereum/go-ethereum/common" @@ -162,10 +162,8 @@ type Config struct { // Clique is allowed for now to live standalone, but ethash is forbidden and can // only exist on already merged networks. func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (consensus.Engine, error) { - // Geth v1.14.0 dropped support for non-merged networks in any consensus - // mode. If such a network is requested, reject startup. - if !config.TerminalTotalDifficultyPassed { - return nil, errors.New("only PoS networks are supported, please transition old ones with Geth v1.13.x") + if config.TerminalTotalDifficulty == nil { + return nil, fmt.Errorf("only PoS networks are supported, please transition old ones with Geth v1.13.x") } // Wrap previously supported consensus engines into their post-merge counterpart if config.Clique != nil { diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index fc82b42947f3..faf360cc53e3 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -75,27 +75,26 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, if shanghai { config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: big.NewInt(0), - ShanghaiTime: u64(0), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - Ethash: new(params.EthashConfig), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: u64(0), + TerminalTotalDifficulty: big.NewInt(0), + Ethash: new(params.EthashConfig), } engine = beacon.NewFaker() } diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json index b974151c1b36..05da3b42e194 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json @@ -41,8 +41,7 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true + "terminalTotalDifficulty": 0 } }, "context": { diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json index 89f642db20bf..b8b20e1b8c63 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json @@ -61,7 +61,6 @@ "berlinBlock": 4460644, "londonBlock": 5062605, "terminalTotalDifficulty": 10790000, - "terminalTotalDifficultyPassed": true, "clique": { "period": 15, "epoch": 30000 @@ -185,4 +184,4 @@ "type": "call" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json index cef0c1c5a80a..30991edafb2c 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json @@ -59,7 +59,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json index 0ae76e92f9bb..cdb0dda5f82b 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json @@ -116,7 +116,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json index 049f24d9328f..e562affb1576 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json @@ -60,7 +60,6 @@ "grayGlacierBlock": 15050000, "shanghaiTime": 1681338455, "terminalTotalDifficulty": 7797655526461000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, @@ -186,4 +185,4 @@ "value": "0x0", "type": "CREATE" } -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json index 5cc3013f8076..43d6be2be9c9 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json @@ -281,7 +281,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json index c931ed63fc37..3434dd31031d 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json @@ -149,7 +149,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json index 42fc87255480..814189dc6b35 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json @@ -84,7 +84,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json index 5300d341b824..c00f0831599e 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json @@ -45,7 +45,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json index d5c2f864c168..293575a98910 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json @@ -119,7 +119,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json index 1267160d1338..aba652ede631 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json @@ -57,7 +57,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json index 38d96a6bd08e..ed518faf5ed3 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json @@ -59,7 +59,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json index 444eba450bec..f8adbabf6377 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json @@ -41,8 +41,7 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true + "terminalTotalDifficulty": 0 } }, "context": { diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json index b013d520c1ad..489a1ae6b538 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json @@ -38,7 +38,6 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true, "isDev": true } }, @@ -59,4 +58,4 @@ "balance": "0x8ac7230489e80000" } } -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json index 64a3b16cb1fe..abbc104f3282 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json @@ -49,7 +49,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json index ae7f7e97f5a9..b5bccf7bd05d 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json @@ -55,7 +55,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index a6be589a0f12..231170c273e2 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "math" "math/big" "net/http" "strings" @@ -459,13 +460,14 @@ func newGQLService(t *testing.T, stack *node.Node, shanghai bool, gspec *core.Ge var engine consensus.Engine = ethash.NewFaker() if shanghai { engine = beacon.NewFaker() - chainCfg := gspec.Config - chainCfg.TerminalTotalDifficultyPassed = true - chainCfg.TerminalTotalDifficulty = common.Big0 + gspec.Config.TerminalTotalDifficulty = common.Big0 // GenerateChain will increment timestamps by 10. // Shanghai upgrade at block 1. shanghaiTime := uint64(5) - chainCfg.ShanghaiTime = &shanghaiTime + gspec.Config.ShanghaiTime = &shanghaiTime + } else { + // set an arbitrary large ttd as chains are required to be known to be merged + gspec.Config.TerminalTotalDifficulty = big.NewInt(math.MaxInt64) } ethBackend, err := eth.New(stack, ethConf) if err != nil { diff --git a/params/config.go b/params/config.go index cac948bf2df5..9b3b92484ab2 100644 --- a/params/config.go +++ b/params/config.go @@ -18,6 +18,7 @@ package params import ( "fmt" + "math" "math/big" "github.com/ethereum/go-ethereum/common" @@ -38,249 +39,240 @@ var ( // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(1_150_000), - DAOForkBlock: big.NewInt(1_920_000), - DAOForkSupport: true, - EIP150Block: big.NewInt(2_463_000), - EIP155Block: big.NewInt(2_675_000), - EIP158Block: big.NewInt(2_675_000), - ByzantiumBlock: big.NewInt(4_370_000), - ConstantinopleBlock: big.NewInt(7_280_000), - PetersburgBlock: big.NewInt(7_280_000), - IstanbulBlock: big.NewInt(9_069_000), - MuirGlacierBlock: big.NewInt(9_200_000), - BerlinBlock: big.NewInt(12_244_000), - LondonBlock: big.NewInt(12_965_000), - ArrowGlacierBlock: big.NewInt(13_773_000), - GrayGlacierBlock: big.NewInt(15_050_000), - TerminalTotalDifficulty: MainnetTerminalTotalDifficulty, // 58_750_000_000_000_000_000_000 - TerminalTotalDifficultyPassed: true, - ShanghaiTime: newUint64(1681338455), - CancunTime: newUint64(1710338135), - DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), - Ethash: new(EthashConfig), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(1_150_000), + DAOForkBlock: big.NewInt(1_920_000), + DAOForkSupport: true, + EIP150Block: big.NewInt(2_463_000), + EIP155Block: big.NewInt(2_675_000), + EIP158Block: big.NewInt(2_675_000), + ByzantiumBlock: big.NewInt(4_370_000), + ConstantinopleBlock: big.NewInt(7_280_000), + PetersburgBlock: big.NewInt(7_280_000), + IstanbulBlock: big.NewInt(9_069_000), + MuirGlacierBlock: big.NewInt(9_200_000), + BerlinBlock: big.NewInt(12_244_000), + LondonBlock: big.NewInt(12_965_000), + ArrowGlacierBlock: big.NewInt(13_773_000), + GrayGlacierBlock: big.NewInt(15_050_000), + TerminalTotalDifficulty: MainnetTerminalTotalDifficulty, // 58_750_000_000_000_000_000_000 + ShanghaiTime: newUint64(1681338455), + CancunTime: newUint64(1710338135), + DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), + Ethash: new(EthashConfig), } // HoleskyChainConfig contains the chain parameters to run a node on the Holesky test network. HoleskyChainConfig = &ChainConfig{ - ChainID: big.NewInt(17000), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: nil, - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: nil, - ShanghaiTime: newUint64(1696000704), - CancunTime: newUint64(1707305664), - Ethash: new(EthashConfig), + ChainID: big.NewInt(17000), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: nil, + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: newUint64(1696000704), + CancunTime: newUint64(1707305664), + Ethash: new(EthashConfig), } // SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network. SepoliaChainConfig = &ChainConfig{ - ChainID: big.NewInt(11155111), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - TerminalTotalDifficulty: big.NewInt(17_000_000_000_000_000), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: big.NewInt(1735371), - ShanghaiTime: newUint64(1677557088), - CancunTime: newUint64(1706655072), - Ethash: new(EthashConfig), + ChainID: big.NewInt(11155111), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(17_000_000_000_000_000), + MergeNetsplitBlock: big.NewInt(1735371), + ShanghaiTime: newUint64(1677557088), + CancunTime: newUint64(1706655072), + Ethash: new(EthashConfig), } // AllEthashProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Ethash consensus. AllEthashProtocolChanges = &ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: true, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + Ethash: new(EthashConfig), + Clique: nil, } AllDevChainProtocolChanges = &ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - ShanghaiTime: newUint64(0), - CancunTime: newUint64(0), - PragueTime: newUint64(0), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + CancunTime: newUint64(0), + TerminalTotalDifficulty: big.NewInt(0), + PragueTime: newUint64(0), } // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. AllCliqueProtocolChanges = &ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: false, - Ethash: nil, - Clique: &CliqueConfig{Period: 0, Epoch: 30000}, + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + Ethash: nil, + Clique: &CliqueConfig{Period: 0, Epoch: 30000}, } // TestChainConfig contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers for testing purposes. TestChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: false, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + Ethash: new(EthashConfig), + Clique: nil, } // MergedTestChainConfig contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers for testing purposes. MergedTestChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: big.NewInt(0), - ShanghaiTime: newUint64(0), - CancunTime: newUint64(0), - PragueTime: newUint64(0), - VerkleTime: nil, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + CancunTime: newUint64(0), + PragueTime: newUint64(0), + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(0), + Ethash: new(EthashConfig), + Clique: nil, } // NonActivatedConfig defines the chain configuration without activating // any protocol change (EIPs). NonActivatedConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: nil, - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: nil, - EIP155Block: nil, - EIP158Block: nil, - ByzantiumBlock: nil, - ConstantinopleBlock: nil, - PetersburgBlock: nil, - IstanbulBlock: nil, - MuirGlacierBlock: nil, - BerlinBlock: nil, - LondonBlock: nil, - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: false, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: nil, + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: nil, + EIP155Block: nil, + EIP158Block: nil, + ByzantiumBlock: nil, + ConstantinopleBlock: nil, + PetersburgBlock: nil, + IstanbulBlock: nil, + MuirGlacierBlock: nil, + BerlinBlock: nil, + LondonBlock: nil, + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + Ethash: new(EthashConfig), + Clique: nil, } TestRules = TestChainConfig.Rules(new(big.Int), false, 0) ) @@ -332,13 +324,6 @@ type ChainConfig struct { // the network that triggers the consensus upgrade. TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` - // TerminalTotalDifficultyPassed is a flag specifying that the network already - // passed the terminal total difficulty. Its purpose is to disable legacy sync - // even without having seen the TTD locally (safer long term). - // - // TODO(karalabe): Drop this field eventually (always assuming PoS mode) - TerminalTotalDifficultyPassed bool `json:"terminalTotalDifficultyPassed,omitempty"` - DepositContractAddress common.Address `json:"depositContractAddress,omitempty"` // Various consensus engines @@ -377,21 +362,9 @@ func (c *ChainConfig) Description() string { banner += fmt.Sprintf("Chain ID: %v (%s)\n", c.ChainID, network) switch { case c.Ethash != nil: - if c.TerminalTotalDifficulty == nil { - banner += "Consensus: Ethash (proof-of-work)\n" - } else if !c.TerminalTotalDifficultyPassed { - banner += "Consensus: Beacon (proof-of-stake), merging from Ethash (proof-of-work)\n" - } else { - banner += "Consensus: Beacon (proof-of-stake), merged from Ethash (proof-of-work)\n" - } + banner += "Consensus: Beacon (proof-of-stake), merged from Ethash (proof-of-work)\n" case c.Clique != nil: - if c.TerminalTotalDifficulty == nil { - banner += "Consensus: Clique (proof-of-authority)\n" - } else if !c.TerminalTotalDifficultyPassed { - banner += "Consensus: Beacon (proof-of-stake), merging from Clique (proof-of-authority)\n" - } else { - banner += "Consensus: Beacon (proof-of-stake), merged from Clique (proof-of-authority)\n" - } + banner += "Consensus: Beacon (proof-of-stake), merged from Clique (proof-of-authority)\n" default: banner += "Consensus: unknown\n" } @@ -426,17 +399,12 @@ func (c *ChainConfig) Description() string { banner += "\n" // Add a special section for the merge as it's non-obvious - if c.TerminalTotalDifficulty == nil { - banner += "The Merge is not yet available for this network!\n" - banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" - } else { - banner += "Merge configured:\n" - banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" - banner += fmt.Sprintf(" - Network known to be merged: %v\n", c.TerminalTotalDifficultyPassed) - banner += fmt.Sprintf(" - Total terminal difficulty: %v\n", c.TerminalTotalDifficulty) - if c.MergeNetsplitBlock != nil { - banner += fmt.Sprintf(" - Merge netsplit block: #%-8v\n", c.MergeNetsplitBlock) - } + banner += "Merge configured:\n" + banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" + banner += " - Network known to be merged\n" + banner += fmt.Sprintf(" - Total terminal difficulty: %v\n", c.TerminalTotalDifficulty) + if c.MergeNetsplitBlock != nil { + banner += fmt.Sprintf(" - Merge netsplit block: #%-8v\n", c.MergeNetsplitBlock) } banner += "\n" diff --git a/tests/block_test_util.go b/tests/block_test_util.go index b0a31a69720b..77bf945e40e7 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -22,6 +22,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + stdmath "math" "math/big" "os" "reflect" @@ -129,6 +130,11 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t } // Commit genesis state gspec := t.genesis(config) + + // if ttd is not specified, set an arbitrary huge value + if gspec.Config.TerminalTotalDifficulty == nil { + gspec.Config.TerminalTotalDifficulty = big.NewInt(stdmath.MaxInt64) + } triedb := triedb.NewDatabase(db, tconf) gblock, err := gspec.Commit(db, triedb) if err != nil { diff --git a/tests/difficulty_test.go b/tests/difficulty_test.go index 03e14df7c4df..74ac28363bac 100644 --- a/tests/difficulty_test.go +++ b/tests/difficulty_test.go @@ -36,22 +36,21 @@ var ( } ropstenChainConfig = params.ChainConfig{ - ChainID: big.NewInt(3), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(10), - EIP158Block: big.NewInt(10), - ByzantiumBlock: big.NewInt(1_700_000), - ConstantinopleBlock: big.NewInt(4_230_000), - PetersburgBlock: big.NewInt(4_939_394), - IstanbulBlock: big.NewInt(6_485_846), - MuirGlacierBlock: big.NewInt(7_117_117), - BerlinBlock: big.NewInt(9_812_189), - LondonBlock: big.NewInt(10_499_401), - TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000), - TerminalTotalDifficultyPassed: true, + ChainID: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), + ByzantiumBlock: big.NewInt(1_700_000), + ConstantinopleBlock: big.NewInt(4_230_000), + PetersburgBlock: big.NewInt(4_939_394), + IstanbulBlock: big.NewInt(6_485_846), + MuirGlacierBlock: big.NewInt(7_117_117), + BerlinBlock: big.NewInt(9_812_189), + LondonBlock: big.NewInt(10_499_401), + TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000), } ) From f8f5609b8eced8421110a9193bbd6cefee40b4af Mon Sep 17 00:00:00 2001 From: Shude Li Date: Wed, 23 Oct 2024 14:33:14 +0800 Subject: [PATCH 4/5] eth/tracers/internal/tracertest: add missing Random to call context (#30652) Fixes a configuration issue in a test-helper, so that we can do call tracing-tests post-merge --- eth/tracers/internal/tracetest/util.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eth/tracers/internal/tracetest/util.go b/eth/tracers/internal/tracetest/util.go index a74a96f8a489..abc2699498d4 100644 --- a/eth/tracers/internal/tracetest/util.go +++ b/eth/tracers/internal/tracetest/util.go @@ -47,6 +47,11 @@ func (c *callContext) toBlockContext(genesis *core.Genesis) vm.BlockContext { if genesis.Config.IsLondon(context.BlockNumber) { context.BaseFee = (*big.Int)(c.BaseFee) } + + if genesis.Config.TerminalTotalDifficulty != nil && genesis.Config.TerminalTotalDifficulty.Sign() == 0 { + context.Random = &genesis.Mixhash + } + if genesis.ExcessBlobGas != nil && genesis.BlobGasUsed != nil { excessBlobGas := eip4844.CalcExcessBlobGas(*genesis.ExcessBlobGas, *genesis.BlobGasUsed) context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) From 3e567b8b2901611f004b5a6070a9b6d286be128d Mon Sep 17 00:00:00 2001 From: Fredrik Svantes Date: Wed, 23 Oct 2024 15:12:56 +0200 Subject: [PATCH 5/5] docs: update security policy (#30606) previous key expired 2023-07-27, the new one expires 2026-02-22: pub rsa4096 2016-11-11 [SC] [expires: 2026-02-22] AE96ED969E479B0084F3E17FE88D3334FA5F6A0A uid Ethereum Foundation Security Team uid Ethereum Foundation Bug Bounty sub rsa4096 2016-11-11 [E] [expires: 2026-02-22] --- SECURITY.md | 204 ++++++++++++++++------------------------------------ 1 file changed, 63 insertions(+), 141 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 1c99ab59505a..0b497b44aece 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -29,147 +29,69 @@ Fingerprint: `AE96 ED96 9E47 9B00 84F3 E17F E88D 3334 FA5F 6A0A` ``` -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: SKS 1.1.6 -Comment: Hostname: pgp.mit.edu -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 -=arte +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 +6F7hEeTW+gnXEI2gPjfb5w== +=b5eA -----END PGP PUBLIC KEY BLOCK----- ```