From 676d353c8e8f9915ae50b78606302f08dbaf687a Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Tue, 4 Jun 2024 20:43:55 +0800 Subject: [PATCH] core, eth, internal: introduce state override reader --- core/blockchain_reader.go | 8 + core/state/state_override.go | 276 ++++++++++++++++++++++ core/state/state_override_json.go | 62 +++++ core/state/state_override_test.go | 286 +++++++++++++++++++++++ core/state/statedb.go | 1 - core/vm/contracts.go | 34 +++ eth/api_backend.go | 23 +- eth/catalyst/api_test.go | 4 +- eth/tracers/api.go | 19 +- eth/tracers/api_test.go | 50 ++-- graphql/graphql.go | 2 +- internal/ethapi/api.go | 139 ++--------- internal/ethapi/api_test.go | 71 ++++-- internal/ethapi/backend.go | 4 +- internal/ethapi/simulate.go | 13 +- internal/ethapi/transaction_args_test.go | 4 +- 16 files changed, 809 insertions(+), 187 deletions(-) create mode 100644 core/state/state_override.go create mode 100644 core/state/state_override_json.go create mode 100644 core/state/state_override_test.go diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 19c1b17f369c..5051ad107b52 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -356,6 +356,14 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { return state.New(root, bc.statedb) } +// StateWithOverrides returns a new mutable state with provided state overrides. +func (bc *BlockChain) StateWithOverrides(root common.Hash, overrides *state.StateOverrides) (*state.StateDB, error) { + if overrides == nil { + return bc.StateAt(root) + } + return state.New(root, state.NewOverrideDatabase(bc.statedb, *overrides)) +} + // Config retrieves the chain's fork configuration. func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig } diff --git a/core/state/state_override.go b/core/state/state_override.go new file mode 100644 index 000000000000..fc74310ff794 --- /dev/null +++ b/core/state/state_override.go @@ -0,0 +1,276 @@ +// 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 ( + "errors" + "maps" + "slices" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" +) + +type StateOverrides map[common.Address]OverrideAccount + +//go:generate go run github.com/fjl/gencodec -type OverrideAccount -field-override overrideMarshaling -out state_override_json.go + +// OverrideAccount specifies the fields of an account that should be overridden +// during the execution of a message call. +// +// Note: The 'state' and 'stateDiff' fields are mutually exclusive and cannot +// be specified simultaneously. If 'state' is set, the message execution will +// only use the data provided in the given state. Otherwise, if 'stateDiff' +// is set, all the differences will be applied first, and then the call message +// will be executed. +type OverrideAccount struct { + Nonce *uint64 `json:"nonce"` + Code *[]byte `json:"code"` + Balance *uint256.Int `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` + MovePrecompileTo *common.Address `json:"movePrecompileToAddress"` +} + +// nolint +type overrideMarshaling struct { + Nonce *hexutil.Uint64 + Code *hexutil.Bytes + Balance *hexutil.U256 +} + +// copy returns a deep-copied override object. +func (o OverrideAccount) copy() OverrideAccount { + var obj OverrideAccount + if o.Nonce != nil { + nonce := *o.Nonce + obj.Nonce = &nonce + } + if o.Code != nil { + code := slices.Clone(*o.Code) + obj.Code = &code + } + if o.Balance != nil { + obj.Balance = new(uint256.Int).Set(o.Balance) + } + if o.State != nil { + obj.State = maps.Clone(o.State) + } + if o.StateDiff != nil { + obj.StateDiff = maps.Clone(o.StateDiff) + } + return obj +} + +// overrideReader implements the Reader interface, serving as a wrapper around a +// provided state reader, but incorporating with overridden states. +type overrideReader struct { + reader Reader + overrides map[common.Address]OverrideAccount +} + +// newOverrideReader creates a reader with customized state overrides. +func newOverrideReader(overrides map[common.Address]OverrideAccount, reader Reader) *overrideReader { + return &overrideReader{ + reader: reader, + overrides: overrides, + } +} + +// Account implementing Reader interface, retrieving the account associated with +// a particular address. +// +// - Returns a nil account if it does not exist +// - Returns an error only if an unexpected issue occurs +// - The returned account is safe to modify after the call +func (r *overrideReader) Account(addr common.Address) (*types.StateAccount, error) { + account, err := r.reader.Account(addr) + if err != nil { + return nil, err + } + // apply the overrides if it's specified + override, ok := r.overrides[addr] + if ok { + if account == nil { + account = types.NewEmptyStateAccount() + } + if override.Nonce != nil { + account.Nonce = *override.Nonce + } + if override.Balance != nil { + account.Balance = new(uint256.Int).Set(override.Balance) + } + if override.Code != nil { + account.CodeHash = crypto.Keccak256(*override.Code) + } + // TODO what about the storage root then, should we compute the + // storage root of overridden state here? + } + return account, nil +} + +// Storage implementing Reader interface, retrieving the storage slot associated +// with a particular account address and slot key. +// +// - Returns an empty slot if it does not exist +// - Returns an error only if an unexpected issue occurs +// - The returned storage slot is safe to modify after the call +func (r *overrideReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { + override, ok := r.overrides[addr] + if ok { + if override.State != nil { + return override.State[slot], nil + } + if override.StateDiff != nil { + if val, ok := override.StateDiff[slot]; ok { + return val, nil + } + } + } + return r.reader.Storage(addr, slot) +} + +// Stats returns the statistics of the reader, specifically detailing the time +// spent on account reading and storage reading. +func (r *overrideReader) Stats() (time.Duration, time.Duration) { return 0, 0 } + +// Copy implementing Reader interface, returning a deep-copied state reader. +func (r *overrideReader) Copy() Reader { + overrides := make(map[common.Address]OverrideAccount) + for addr, override := range r.overrides { + overrides[addr] = override.copy() + } + return &overrideReader{ + overrides: overrides, + reader: r.reader.Copy(), + } +} + +// OverrideDatabase implements the Database interface, serving as a wrapper +// around a standard state database, but incorporating overridden states. +type OverrideDatabase struct { + Database + overrides map[common.Address]OverrideAccount +} + +// NewOverrideDatabase creates a state database with provided state overrides. +func NewOverrideDatabase(db Database, overrides map[common.Address]OverrideAccount) *OverrideDatabase { + // Allocate an empty override set just in case the provided one + // is nil. Don't panic for lazy users. + if overrides == nil { + overrides = make(map[common.Address]OverrideAccount) + } + return &OverrideDatabase{ + Database: db, + overrides: overrides, + } +} + +// Reader returns a state reader associated with the specified state root. +func (db *OverrideDatabase) Reader(root common.Hash) (Reader, error) { + reader, err := db.Database.Reader(root) + if err != nil { + return nil, err + } + return newOverrideReader(db.overrides, reader), nil +} + +// ContractCode retrieves a particular contract's code. +func (db *OverrideDatabase) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) { + override, ok := db.overrides[addr] + if ok && override.Code != nil { + return *override.Code, nil + } + return db.Database.ContractCode(addr, codeHash) +} + +// ContractCodeSize retrieves a particular contracts code's size. +func (db *OverrideDatabase) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) { + override, ok := db.overrides[addr] + if ok && override.Code != nil { + return len(*override.Code), nil + } + return db.Database.ContractCodeSize(addr, codeHash) +} + +// The stateWrapReader wraps a live state database as the state source. +type stateWrapReader struct { + state *StateDB +} + +// Account implementing Reader interface, retrieving the account associated with +// a particular address. +// +// - Returns a nil account if it does not exist +// - Returns an error only if an unexpected issue occurs +// - The returned account is safe to modify after the call +func (r *stateWrapReader) Account(addr common.Address) (*types.StateAccount, error) { + obj := r.state.getStateObject(addr) + if obj == nil { + return nil, nil + } + return obj.data.Copy(), nil +} + +// Storage implementing Reader interface, retrieving the storage slot associated +// with a particular account address and slot key. +// +// - Returns an empty slot if it does not exist +// - Returns an error only if an unexpected issue occurs +// - The returned storage slot is safe to modify after the call +func (r *stateWrapReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { + return r.state.GetState(addr, slot), nil +} + +// Stats returns the statistics of the reader, specifically detailing the time +// spent on account reading and storage reading. +func (r *stateWrapReader) Stats() (time.Duration, time.Duration) { return 0, 0 } + +// Copy implementing Reader interface, returning a deep-copied state reader. +func (r *stateWrapReader) Copy() Reader { + return &stateWrapReader{state: r.state.Copy()} +} + +// stateWrap is an internal struct wrapping a live state instance as the state +// data source. This can be useful in scenarios where we need to create the +// snapshot of the current state and apply some additional overrides on top +// (e.g. state override in RPC call serving). +type stateWrap struct { + Database + state *StateDB +} + +// Reader returns a state reader associated with the specified state root. +func (db *stateWrap) Reader(root common.Hash) (Reader, error) { + if root != db.state.originalRoot { + return nil, errors.New("state root is not matched") + } + return &stateWrapReader{state: db.state}, nil +} + +// OverrideState applies the state overrides into the provided live state database. +func OverrideState(state *StateDB, overrides map[common.Address]OverrideAccount) (*StateDB, error) { + db := NewOverrideDatabase(&stateWrap{ + Database: state.db, + state: state.Copy(), + }, overrides) + return New(state.originalRoot, db) +} diff --git a/core/state/state_override_json.go b/core/state/state_override_json.go new file mode 100644 index 000000000000..8d6292a9ce64 --- /dev/null +++ b/core/state/state_override_json.go @@ -0,0 +1,62 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package state + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/holiman/uint256" +) + +var _ = (*overrideMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (o OverrideAccount) MarshalJSON() ([]byte, error) { + type OverrideAccount struct { + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance *hexutil.U256 `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` + } + var enc OverrideAccount + enc.Nonce = (*hexutil.Uint64)(o.Nonce) + enc.Code = (*hexutil.Bytes)(o.Code) + enc.Balance = (*hexutil.U256)(o.Balance) + enc.State = o.State + enc.StateDiff = o.StateDiff + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (o *OverrideAccount) UnmarshalJSON(input []byte) error { + type OverrideAccount struct { + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance *hexutil.U256 `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` + } + var dec OverrideAccount + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Nonce != nil { + o.Nonce = (*uint64)(dec.Nonce) + } + if dec.Code != nil { + o.Code = (*[]byte)(dec.Code) + } + if dec.Balance != nil { + o.Balance = (*uint256.Int)(dec.Balance) + } + if dec.State != nil { + o.State = dec.State + } + if dec.StateDiff != nil { + o.StateDiff = dec.StateDiff + } + return nil +} diff --git a/core/state/state_override_test.go b/core/state/state_override_test.go new file mode 100644 index 000000000000..de01e3b9aaee --- /dev/null +++ b/core/state/state_override_test.go @@ -0,0 +1,286 @@ +// 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 ( + "bytes" + "testing" + + "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/holiman/uint256" +) + +func initTestState() (common.Hash, *CachingDB) { + db := NewDatabaseForTesting() + state, _ := New(types.EmptyRootHash, db) + state.SetNonce(testAllocAddr, 1) + state.SetBalance(testAllocAddr, uint256.NewInt(100), tracing.BalanceChangeUnspecified) + state.SetCode(testAllocAddr, []byte{0x1}) + state.SetState(testAllocAddr, testAllocSlotKey, common.Hash{0x1}) + root, err := state.Commit(0, false) + if err != nil { + panic("failed to commit state") + } + return root, db +} + +var ( + testAllocAddr = common.HexToAddress("0xdeadbeef") + testAllocAddr2 = common.HexToAddress("0xbabecafe") + testAllocSlotKey = common.HexToHash("0xdeadbeef") + testAllocSlotKey2 = common.HexToHash("0xbabecafe") +) + +func overrideNonce(val uint64) *uint64 { + return &val +} + +func overrideCode(code []byte) *[]byte { + return &code +} + +func TestStateOverride(t *testing.T) { + var cases = []struct { + overrides map[common.Address]OverrideAccount + expectAccounts map[common.Address]types.StateAccount + expectStorages map[common.Address]map[common.Hash]common.Hash + expectCodes map[common.Address][]byte + }{ + // override is nil, it should be allowed + { + nil, + map[common.Address]types.StateAccount{ + testAllocAddr: { + Nonce: 1, + Balance: uint256.NewInt(100), + CodeHash: crypto.Keccak256([]byte{0x1}), + }, + }, + map[common.Address]map[common.Hash]common.Hash{ + testAllocAddr: {testAllocSlotKey: {0x1}}, + }, + map[common.Address][]byte{ + testAllocAddr: {0x1}, + }, + }, + // empty override + { + map[common.Address]OverrideAccount{}, + map[common.Address]types.StateAccount{ + testAllocAddr: { + Nonce: 1, + Balance: uint256.NewInt(100), + CodeHash: crypto.Keccak256([]byte{0x1}), + }, + }, + map[common.Address]map[common.Hash]common.Hash{ + testAllocAddr: {testAllocSlotKey: {0x1}}, + }, + map[common.Address][]byte{ + testAllocAddr: {0x1}, + }, + }, + // empty override, access the non-existent states + { + map[common.Address]OverrideAccount{}, + map[common.Address]types.StateAccount{ + testAllocAddr2: { + Nonce: 0, + Balance: common.U2560, + CodeHash: common.Hash{}.Bytes(), + }, + }, + map[common.Address]map[common.Hash]common.Hash{ + testAllocAddr2: {testAllocSlotKey: {}}, + }, + map[common.Address][]byte{ + testAllocAddr2: nil, + }, + }, + // override account metadata + { + map[common.Address]OverrideAccount{ + testAllocAddr: { + Nonce: overrideNonce(50), + Code: overrideCode([]byte{0x2}), + Balance: uint256.NewInt(200), + }, + testAllocAddr2: { + Nonce: overrideNonce(50), + Code: overrideCode([]byte{0x2}), + Balance: uint256.NewInt(200), + }, + }, + map[common.Address]types.StateAccount{ + testAllocAddr: { + Nonce: 50, + Balance: uint256.NewInt(200), + CodeHash: crypto.Keccak256([]byte{0x2}), + }, + testAllocAddr2: { + Nonce: 50, + Balance: uint256.NewInt(200), + CodeHash: crypto.Keccak256([]byte{0x2}), + }, + }, + map[common.Address]map[common.Hash]common.Hash{ + testAllocAddr: {testAllocSlotKey: {0x1}}, + }, + map[common.Address][]byte{ + testAllocAddr: {0x2}, + testAllocAddr2: {0x2}, + }, + }, + // override storage by diff + { + map[common.Address]OverrideAccount{ + testAllocAddr: { + StateDiff: map[common.Hash]common.Hash{ + testAllocSlotKey: {0x2}, + testAllocSlotKey2: {0x2}, + }, + }, + }, + map[common.Address]types.StateAccount{ + testAllocAddr: { + Nonce: 1, + Balance: uint256.NewInt(100), + CodeHash: crypto.Keccak256([]byte{0x1}), + }, + }, + map[common.Address]map[common.Hash]common.Hash{ + testAllocAddr: { + testAllocSlotKey: {0x2}, + testAllocSlotKey2: {0x2}, + }, + }, + map[common.Address][]byte{ + testAllocAddr: {0x1}, + }, + }, + // override storage by replacing entire storage + { + map[common.Address]OverrideAccount{ + testAllocAddr: { + State: map[common.Hash]common.Hash{ + testAllocSlotKey2: {0x2}, + }, + }, + }, + map[common.Address]types.StateAccount{ + testAllocAddr: { + Nonce: 1, + Balance: uint256.NewInt(100), + CodeHash: crypto.Keccak256([]byte{0x1}), + }, + }, + map[common.Address]map[common.Hash]common.Hash{ + testAllocAddr: { + testAllocSlotKey: {}, + testAllocSlotKey2: {0x2}, + }, + }, + map[common.Address][]byte{ + testAllocAddr: {0x1}, + }, + }, + } + for _, c := range cases { + root, db := initTestState() + stateDb, err := New(root, NewOverrideDatabase(db, c.overrides)) + if err != nil { + t.Fatalf("Failed to initialize state, %v", err) + } + for addr, expect := range c.expectAccounts { + if got := stateDb.GetBalance(addr); got.Cmp(expect.Balance) != 0 { + t.Fatalf("Balance is not matched, got %v, want: %v", got, expect.Balance) + } + if got := stateDb.GetNonce(addr); got != expect.Nonce { + t.Fatalf("Nonce is not matched, got %v, want: %v", got, expect.Nonce) + } + if got := stateDb.GetCodeHash(addr); !bytes.Equal(got.Bytes(), expect.CodeHash) { + t.Fatalf("CodeHash is not matched, got %v, want: %v", got.Bytes(), expect.CodeHash) + } + } + for addr, slots := range c.expectStorages { + for key, val := range slots { + got := stateDb.GetState(addr, key) + if got != val { + t.Fatalf("Storage slot is not matched, got %v, want: %v", got, val) + } + } + } + for addr, code := range c.expectCodes { + if got := stateDb.GetCode(addr); !bytes.Equal(got, code) { + t.Fatalf("Code is not matched, got %v, want: %v", got, code) + } + } + } +} + +func TestRecursiveOverride(t *testing.T) { + root, db := initTestState() + + overrideA := map[common.Address]OverrideAccount{ + testAllocAddr: {Nonce: overrideNonce(2)}, + } + overrideB := map[common.Address]OverrideAccount{ + testAllocAddr: {Nonce: overrideNonce(3)}, + } + overrideC := map[common.Address]OverrideAccount{ + testAllocAddr: { + Nonce: overrideNonce(0), + State: map[common.Hash]common.Hash{ + testAllocSlotKey: {}, + }, + }, + } + odbA := NewOverrideDatabase(db, overrideA) + stateDb, err := New(root, odbA) + if err != nil { + t.Fatalf("Failed to initialize state, %v", err) + } + nonce := stateDb.GetNonce(testAllocAddr) + if nonce != 2 { + t.Fatalf("Unexpected nonce, want: %d, got: %d", 2, nonce) + } + // recursively override + stateDb2, err := OverrideState(stateDb, overrideB) + if err != nil { + t.Fatalf("Failed to initialize state, %v", err) + } + nonce = stateDb2.GetNonce(testAllocAddr) + if nonce != 3 { + t.Fatalf("Unexpected nonce, want: %d, got: %d", 3, nonce) + } + // recursively override again + stateDb3, err := OverrideState(stateDb2, overrideC) + if err != nil { + t.Fatalf("Failed to initialize state, %v", err) + } + nonce = stateDb3.GetNonce(testAllocAddr) + if nonce != 0 { + t.Fatalf("Unexpected nonce, want: %d, got: %d", 0, nonce) + } + val := stateDb3.GetState(testAllocAddr, testAllocSlotKey) + if val != (common.Hash{}) { + t.Fatalf("Unexpected storage slot, want: %v, got: %v", common.Hash{}, val) + } +} diff --git a/core/state/statedb.go b/core/state/statedb.go index d855e5626dc8..a057eb8e704b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -319,7 +319,6 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 { if stateObject != nil { return stateObject.Nonce() } - return 0 } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index f54d5ab86e66..0829c20c74fe 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -30,6 +30,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" @@ -50,6 +51,39 @@ type PrecompiledContract interface { // PrecompiledContracts contains the precompiled contracts supported at the given fork. type PrecompiledContracts map[common.Address]PrecompiledContract +// MovePrecompiles updates the precompile-map as needed, according to the state overrides. +func (precompiles PrecompiledContracts) ApplyOverrides(stateOverrides state.StateOverrides) error { + // Tracks destinations of precompiles that were moved. + dirtyAddrs := make(map[common.Address]struct{}) + for addr, account := range stateOverrides { + // If a precompile was moved to this address already, it can't be overridden. + if _, ok := dirtyAddrs[addr]; ok { + return fmt.Errorf("account %s has already been overridden by a precompile", addr.Hex()) + } + p, isPrecompile := precompiles[addr] + // The MoveTo feature makes it possible to move a precompile + // code to another address. If the target address is another precompile + // the code for the latter is lost for this session. + // Note the destination account is not cleared upon move. + if account.MovePrecompileTo != nil { + if !isPrecompile { + return fmt.Errorf("account %s is not a precompile", addr.Hex()) + } + // Refuse to move a precompile to an address that has been + // or will be overridden. + if _, present := stateOverrides[*account.MovePrecompileTo]; present { + return fmt.Errorf("account %s is already overridden", account.MovePrecompileTo.Hex()) + } + precompiles[*account.MovePrecompileTo] = p + dirtyAddrs[*account.MovePrecompileTo] = struct{}{} + } + if isPrecompile { + delete(precompiles, addr) + } + } + return nil +} + // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum // contracts used in the Frontier and Homestead releases. var PrecompiledContractsHomestead = PrecompiledContracts{ diff --git a/eth/api_backend.go b/eth/api_backend.go index 4e81d68e078f..84b9bc1aed92 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -186,14 +186,21 @@ func (b *EthAPIBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) return b.eth.miner.Pending() } -func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber, overrides *state.StateOverrides) (*state.StateDB, *types.Header, error) { // Pending state is only known by the miner if number == rpc.PendingBlockNumber { - block, _, state := b.eth.miner.Pending() - if block == nil || state == nil { + block, _, stateDb := b.eth.miner.Pending() + if block == nil || stateDb == nil { return nil, nil, errors.New("pending state is not available") } - return state, block.Header(), nil + if overrides == nil { + return stateDb, block.Header(), nil + } + stateDb, err := state.OverrideState(stateDb, *overrides) + if err != nil { + return nil, nil, err + } + return stateDb, block.Header(), nil } // Otherwise resolve the block number and return its state header, err := b.HeaderByNumber(ctx, number) @@ -203,16 +210,16 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B if header == nil { return nil, nil, errors.New("header not found") } - stateDb, err := b.eth.BlockChain().StateAt(header.Root) + stateDb, err := b.eth.BlockChain().StateWithOverrides(header.Root, overrides) if err != nil { return nil, nil, err } return stateDb, header, nil } -func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { +func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, overrides *state.StateOverrides) (*state.StateDB, *types.Header, error) { if blockNr, ok := blockNrOrHash.Number(); ok { - return b.StateAndHeaderByNumber(ctx, blockNr) + return b.StateAndHeaderByNumber(ctx, blockNr, overrides) } if hash, ok := blockNrOrHash.Hash(); ok { header, err := b.HeaderByHash(ctx, hash) @@ -225,7 +232,7 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { return nil, nil, errors.New("hash is not currently canonical") } - stateDb, err := b.eth.BlockChain().StateAt(header.Root) + stateDb, err := b.eth.BlockChain().StateWithOverrides(header.Root, overrides) if err != nil { return nil, nil, err } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 3ac719c23ee1..a51e151fb68c 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1162,7 +1162,7 @@ func TestWithdrawals(t *testing.T) { } // 11: verify withdrawals were processed. - db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number)) + db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number), nil) if err != nil { t.Fatalf("unable to load db: %v", err) } @@ -1683,7 +1683,7 @@ func TestParentBeaconBlockRoot(t *testing.T) { } // 11: verify beacon root was processed. - db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number)) + db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number), nil) if err != nil { t.Fatalf("unable to load db: %v", err) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7d8191c25bd6..8c92e7fad7a3 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -43,6 +43,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "maps" ) const ( @@ -163,7 +164,7 @@ type TraceConfig struct { // field to override the state for tracing. type TraceCallConfig struct { TraceConfig - StateOverrides *ethapi.StateOverride + StateOverrides map[common.Address]state.OverrideAccount BlockOverrides *ethapi.BlockOverrides TxIndex *hexutil.Uint } @@ -955,17 +956,23 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() - vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) // Apply the customization rules if required. + vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) if config != nil { config.BlockOverrides.Apply(&vmctx) - rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) - precompiles := vm.ActivePrecompiledContracts(rules) - if err := config.StateOverrides.Apply(statedb, precompiles); err != nil { - return nil, err + if len(config.StateOverrides) != 0 { + rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) + precompiles := vm.ActivePrecompiledContracts(rules) + precompiles = maps.Clone(precompiles) + precompiles.ApplyOverrides(config.StateOverrides) + if statedb, err = state.OverrideState(statedb, config.StateOverrides); err != nil { + return nil, err + } } + } + // Execute the trace if err := args.CallDefaults(api.backend.RPCGasCap(), vmctx.BaseFee, api.backend.ChainConfig().ChainID); err != nil { return nil, err diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 47e36934953b..74743ea0a525 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -45,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/holiman/uint256" ) var ( @@ -610,8 +611,8 @@ func TestTracingWithOverrides(t *testing.T) { Value: (*hexutil.Big)(big.NewInt(1000)), }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + StateOverrides: map[common.Address]state.OverrideAccount{ + randomAccounts[0].addr: {Balance: uint256.MustFromBig(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, }, want: `{"gas":21000,"failed":false,"returnValue":""}`, @@ -652,9 +653,9 @@ func TestTracingWithOverrides(t *testing.T) { }, config: &TraceCallConfig{ //Tracer: &tracer, - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ - Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), + StateOverrides: map[common.Address]state.OverrideAccount{ + randomAccounts[2].addr: { + Code: newRawBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), }, }, @@ -719,9 +720,9 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ - Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), + StateOverrides: map[common.Address]state.OverrideAccount{ + randomAccounts[2].addr: { + Code: newRawBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), }, }, }, @@ -735,9 +736,9 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ - Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), + StateOverrides: map[common.Address]state.OverrideAccount{ + randomAccounts[2].addr: { + Code: newRawBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), State: newStates([]common.Hash{{}}, []common.Hash{{}}), }, }, @@ -753,9 +754,9 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - storageAccount: ethapi.OverrideAccount{ - Code: newRPCBytes([]byte{ + StateOverrides: map[common.Address]state.OverrideAccount{ + storageAccount: { + Code: newRawBytes([]byte{ // SLOAD(3) + SLOAD(4) (which is 0x77) byte(vm.PUSH1), 0x04, byte(vm.SLOAD), @@ -788,9 +789,9 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - storageAccount: ethapi.OverrideAccount{ - Code: newRPCBytes([]byte{ + StateOverrides: map[common.Address]state.OverrideAccount{ + storageAccount: { + Code: newRawBytes([]byte{ // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00) byte(vm.PUSH1), 0x04, byte(vm.SLOAD), @@ -826,9 +827,9 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - storageAccount: ethapi.OverrideAccount{ - Code: newRPCBytes([]byte{ + StateOverrides: map[common.Address]state.OverrideAccount{ + storageAccount: { + Code: newRawBytes([]byte{ // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44) byte(vm.PUSH1), 0x04, byte(vm.SLOAD), @@ -898,16 +899,15 @@ func newAccounts(n int) (accounts []Account) { return accounts } -func newRPCBalance(balance *big.Int) *hexutil.Big { - rpcBalance := (*hexutil.Big)(balance) - return rpcBalance -} - func newRPCBytes(bytes []byte) *hexutil.Bytes { rpcBytes := hexutil.Bytes(bytes) return &rpcBytes } +func newRawBytes(bytes []byte) *[]byte { + return &bytes +} + func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Hash { if len(keys) != len(vals) { panic("invalid input") diff --git a/graphql/graphql.go b/graphql/graphql.go index 57dfd14ecdb1..ac2cfb63dfb2 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -86,7 +86,7 @@ type Account struct { // getState fetches the StateDB object for an account. func (a *Account) getState(ctx context.Context) (*state.StateDB, error) { - state, _, err := a.r.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash) + state, _, err := a.r.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash, nil) return state, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a508b0ca5b28..7f3505a69066 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core" "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/crypto" @@ -48,7 +47,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" - "github.com/holiman/uint256" ) // estimateGasErrorRatio is the amount of overestimation eth_estimateGas is @@ -325,7 +323,7 @@ func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 { // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { - state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil) if state == nil || err != nil { return nil, err } @@ -378,7 +376,7 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address, return nil, err } } - statedb, header, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + statedb, header, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil) if statedb == nil || err != nil { return nil, err } @@ -570,7 +568,7 @@ func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHas // GetCode returns the code stored at the given address in the state for the given block number. func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { - state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil) if state == nil || err != nil { return nil, err } @@ -582,7 +580,7 @@ func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, b // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { - state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil) if state == nil || err != nil { return nil, err } @@ -618,99 +616,9 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp for i, receipt := range receipts { result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i) } - return result, nil } -// OverrideAccount indicates the overriding fields of account during the execution -// of a message call. -// Note, state and stateDiff can't be specified at the same time. If state is -// set, message execution will only use the data in the given state. Otherwise -// if stateDiff is set, all diff will be applied first and then execute the call -// message. -type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce"` - Code *hexutil.Bytes `json:"code"` - Balance *hexutil.Big `json:"balance"` - State map[common.Hash]common.Hash `json:"state"` - StateDiff map[common.Hash]common.Hash `json:"stateDiff"` - MovePrecompileTo *common.Address `json:"movePrecompileToAddress"` -} - -// StateOverride is the collection of overridden accounts. -type StateOverride map[common.Address]OverrideAccount - -func (diff *StateOverride) has(address common.Address) bool { - _, ok := (*diff)[address] - return ok -} - -// Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error { - if diff == nil { - return nil - } - // Tracks destinations of precompiles that were moved. - dirtyAddrs := make(map[common.Address]struct{}) - for addr, account := range *diff { - // If a precompile was moved to this address already, it can't be overridden. - if _, ok := dirtyAddrs[addr]; ok { - return fmt.Errorf("account %s has already been overridden by a precompile", addr.Hex()) - } - p, isPrecompile := precompiles[addr] - // The MoveTo feature makes it possible to move a precompile - // code to another address. If the target address is another precompile - // the code for the latter is lost for this session. - // Note the destination account is not cleared upon move. - if account.MovePrecompileTo != nil { - if !isPrecompile { - return fmt.Errorf("account %s is not a precompile", addr.Hex()) - } - // Refuse to move a precompile to an address that has been - // or will be overridden. - if diff.has(*account.MovePrecompileTo) { - return fmt.Errorf("account %s is already overridden", account.MovePrecompileTo.Hex()) - } - precompiles[*account.MovePrecompileTo] = p - dirtyAddrs[*account.MovePrecompileTo] = struct{}{} - } - if isPrecompile { - delete(precompiles, addr) - } - // Override account nonce. - if account.Nonce != nil { - statedb.SetNonce(addr, uint64(*account.Nonce)) - } - // Override account(contract) code. - if account.Code != nil { - statedb.SetCode(addr, *account.Code) - } - // Override account balance. - if account.Balance != nil { - u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance)) - statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified) - } - if account.State != nil && account.StateDiff != nil { - return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) - } - // Replace entire state if caller requires. - if account.State != nil { - statedb.SetStorage(addr, account.State) - } - // Apply state diff into specified accounts. - if account.StateDiff != nil { - for key, value := range account.StateDiff { - statedb.SetState(addr, key, value) - } - } - } - // Now finalize the changes. Finalize is normally performed between transactions. - // By using finalize, the overrides are semantically behaving as - // if they were created in a transaction just before the tracing occur. - statedb.Finalise(false) - return nil -} - // BlockOverrides is a set of header fields to override. type BlockOverrides struct { Number *hexutil.Big @@ -819,18 +727,22 @@ func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.H return header } -func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { +func doCall(ctx context.Context, b Backend, args TransactionArgs, statedb *state.StateDB, header *types.Header, stateOverrides *state.StateOverrides, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) if blockOverrides != nil { blockOverrides.Apply(&blockCtx) } rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) - precompiles := maps.Clone(vm.ActivePrecompiledContracts(rules)) - if err := overrides.Apply(state, precompiles); err != nil { - return nil, err + precompiles := vm.ActivePrecompiledContracts(rules) + if stateOverrides != nil && len(*stateOverrides) != 0 { + precompiles = maps.Clone(precompiles) + precompiles.ApplyOverrides(*stateOverrides) + if oState, err := state.OverrideState(statedb, *stateOverrides); err != nil { + return nil, err + } else { + statedb = oState + } } - - // Setup context so it may be cancelled the call has completed // or, in case of unmetered gas, setup a context with a timeout. var cancel context.CancelFunc if timeout > 0 { @@ -847,7 +759,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S } else { gp.AddGas(globalGasCap) } - return applyMessage(ctx, b, args, state, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles, true) + return applyMessage(ctx, b, args, statedb, header, timeout, gp, &blockCtx, &vm.Config{NoBaseFee: true}, precompiles, true) } func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config, precompiles vm.PrecompiledContracts, skipChecks bool) (*core.ExecutionResult, error) { @@ -898,10 +810,10 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, ti return result, nil } -func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { +func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *state.StateOverrides, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) - state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, overrides) if state == nil || err != nil { return nil, err } @@ -914,7 +826,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. -func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) { +func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *state.StateOverrides, blockOverrides *BlockOverrides) (hexutil.Bytes, error) { if blockNrOrHash == nil { latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) blockNrOrHash = &latest @@ -947,7 +859,7 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO n := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) blockNrOrHash = &n } - state, base, err := api.b.StateAndHeaderByNumberOrHash(ctx, *blockNrOrHash) + state, base, err := api.b.StateAndHeaderByNumberOrHash(ctx, *blockNrOrHash, nil) if state == nil || err != nil { return nil, err } @@ -973,15 +885,12 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO // successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if // there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil & // non-zero) and `gasCap` (if non-zero). -func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) { +func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *state.StateOverrides, gasCap uint64) (hexutil.Uint64, error) { // Retrieve the base state and mutate it with any overrides - state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, overrides) if state == nil || err != nil { return 0, err } - if err := overrides.Apply(state, nil); err != nil { - return 0, err - } // Construct the gas estimator option from the user input opts := &gasestimator.Options{ Config: b.ChainConfig(), @@ -1017,7 +926,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap // configuration (if non-zero). // Note: Required blob gas is not computed in this method. -func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) { +func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *state.StateOverrides) (hexutil.Uint64, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash @@ -1281,7 +1190,7 @@ func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args Transaction // If the transaction itself fails, an vmErr is returned. func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrHash, args TransactionArgs) (acl types.AccessList, gasUsed uint64, vmErr error, err error) { // Retrieve the execution context - db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil) if db == nil || err != nil { return nil, 0, nil, err } @@ -1427,7 +1336,7 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm return (*hexutil.Uint64)(&nonce), nil } // Resolve block number and use its state to ask for the nonce - state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil) if state == nil || err != nil { return nil, err } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 758638cb16f1..e6bc48a1a252 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -535,7 +535,7 @@ func (b testBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc. func (b testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { return b.chain.GetBlock(hash, uint64(number.Int64())).Body(), nil } -func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, *types.Header, error) { if number == rpc.PendingBlockNumber { panic("pending state not implemented") } @@ -546,12 +546,17 @@ func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.Bloc if header == nil { return nil, nil, errors.New("header not found") } - stateDb, err := b.chain.StateAt(header.Root) + var stateDb *state.StateDB + if overrides != nil { + stateDb, err = b.chain.StateWithOverrides(header.Root, overrides) + } else { + stateDb, err = b.chain.StateAt(header.Root) + } return stateDb, header, err } -func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { +func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, *types.Header, error) { if blockNr, ok := blockNrOrHash.Number(); ok { - return b.StateAndHeaderByNumber(ctx, blockNr) + return b.StateAndHeaderByNumber(ctx, blockNr, overrides) } panic("only implemented for number") } @@ -652,7 +657,7 @@ func TestEstimateGas(t *testing.T) { var testSuite = []struct { blockNumber rpc.BlockNumber call TransactionArgs - overrides StateOverride + overrides map[common.Address]state.OverrideAccount expectErr error want uint64 }{ @@ -688,8 +693,8 @@ func TestEstimateGas(t *testing.T) { { blockNumber: rpc.LatestBlockNumber, call: TransactionArgs{}, - overrides: StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + overrides: map[common.Address]state.OverrideAccount{ + randomAccounts[0].addr: {Balance: uint256.MustFromBig(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, expectErr: nil, want: 53000, @@ -701,8 +706,8 @@ func TestEstimateGas(t *testing.T) { To: &randomAccounts[1].addr, Value: (*hexutil.Big)(big.NewInt(1000)), }, - overrides: StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))}, + overrides: map[common.Address]state.OverrideAccount{ + randomAccounts[0].addr: {Balance: uint256.MustFromBig(big.NewInt(0))}, }, expectErr: core.ErrInsufficientFunds, }, @@ -761,7 +766,11 @@ func TestEstimateGas(t *testing.T) { }, } for i, tc := range testSuite { - result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides) + var overrides *map[common.Address]state.OverrideAccount + if len(tc.overrides) != 0 { + overrides = &tc.overrides + } + result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, overrides) if tc.expectErr != nil { if err == nil { t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) @@ -819,7 +828,7 @@ func TestCall(t *testing.T) { var testSuite = []struct { name string blockNumber rpc.BlockNumber - overrides StateOverride + overrides map[common.Address]state.OverrideAccount call TransactionArgs blockOverrides BlockOverrides expectErr error @@ -881,8 +890,8 @@ func TestCall(t *testing.T) { To: &randomAccounts[1].addr, Value: (*hexutil.Big)(big.NewInt(1000)), }, - overrides: StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + overrides: map[common.Address]state.OverrideAccount{ + randomAccounts[0].addr: {Balance: uint256.MustFromBig(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, want: "0x", }, @@ -897,6 +906,15 @@ func TestCall(t *testing.T) { }, expectErr: core.ErrInsufficientFunds, }, + //{ + // blockNumber: rpc.LatestBlockNumber, + // call: TransactionArgs{ + // From: &randomAccounts[0].addr, + // To: &randomAccounts[1].addr, + // Value: (*hexutil.Big)(big.NewInt(1000)), + // }, + // expectErr: core.ErrInsufficientFunds, + //}, // Successful simple contract call // // // SPDX-License-Identifier: GPL-3.0 @@ -921,9 +939,9 @@ func TestCall(t *testing.T) { To: &randomAccounts[2].addr, Data: hex2Bytes("8381f58a"), // call number() }, - overrides: StateOverride{ - randomAccounts[2].addr: OverrideAccount{ - Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"), + overrides: map[common.Address]state.OverrideAccount{ + randomAccounts[2].addr: { + Code: hex2RawBytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"), StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))}, }, }, @@ -992,9 +1010,9 @@ func TestCall(t *testing.T) { BlobHashes: []common.Hash{{0x01, 0x22}}, BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), }, - overrides: StateOverride{ + overrides: map[common.Address]state.OverrideAccount{ randomAccounts[2].addr: { - Code: hex2Bytes("60004960005260206000f3"), + Code: hex2RawBytes("60004960005260206000f3"), }, }, want: "0x0122000000000000000000000000000000000000000000000000000000000000", @@ -1018,16 +1036,20 @@ func TestCall(t *testing.T) { // } Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"), }, - overrides: StateOverride{ - dad: OverrideAccount{ + overrides: map[common.Address]state.OverrideAccount{ + dad: { State: map[common.Hash]common.Hash{}, }, }, want: "0x0000000000000000000000000000000000000000000000000000000000000000", }, } - for _, tc := range testSuite { - result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) + for i, tc := range testSuite { + var overrides *map[common.Address]state.OverrideAccount + if len(tc.overrides) > 0 { + overrides = &tc.overrides + } + result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, overrides, &tc.blockOverrides) if tc.expectErr != nil { if err == nil { t.Errorf("test %s: want error %v, have nothing", tc.name, tc.expectErr) @@ -2568,6 +2590,11 @@ func uint256ToBytes(v *uint256.Int) *hexutil.Bytes { return &r } +func hex2RawBytes(str string) *[]byte { + rpcBytes := common.Hex2Bytes(str) + return &rpcBytes +} + func TestRPCMarshalBlock(t *testing.T) { t.Parallel() var ( diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 82465ca7d73d..e4d746cf56e6 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -64,8 +64,8 @@ type Backend interface { BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) - StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) - StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) + StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber, overrides *state.StateOverrides) (*state.StateDB, *types.Header, error) + StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, overrides *state.StateOverrides) (*state.StateDB, *types.Header, error) Pending() (*types.Block, types.Receipts, *state.StateDB) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 81b4633d42cf..cc8325ebe20f 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -51,7 +51,7 @@ const ( // simBlock is a batch of calls to be simulated sequentially. type simBlock struct { BlockOverrides *BlockOverrides - StateOverrides *StateOverride + StateOverrides *state.StateOverrides Calls []TransactionArgs } @@ -170,9 +170,16 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, blockContext.BlobBaseFee = block.BlockOverrides.BlobBaseFee.ToInt() } precompiles := sim.activePrecompiles(sim.base) + stateOverrides := block.StateOverrides // State overrides are applied prior to execution of a block - if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil { - return nil, nil, err + if stateOverrides != nil && len(*stateOverrides) != 0 { + precompiles = maps.Clone(precompiles) + precompiles.ApplyOverrides(*stateOverrides) + if oState, err := state.OverrideState(sim.state, *stateOverrides); err != nil { + return nil, nil, err + } else { + sim.state = oState + } } var ( gasUsed, blobGasUsed uint64 diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 5f59b491e1eb..219d242a0f5c 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -356,10 +356,10 @@ func (b *backendMock) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { return nil, nil } -func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { +func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, *types.Header, error) { return nil, nil, nil } -func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { +func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, *types.Header, error) { return nil, nil, nil } func (b *backendMock) Pending() (*types.Block, types.Receipts, *state.StateDB) { return nil, nil, nil }