From 323292732535601044419ed9e10f151fc798f6d3 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 | 273 +++++++++++++++++++++ core/state/state_override_json.go | 62 +++++ core/state/state_override_test.go | 287 +++++++++++++++++++++++ core/state/statedb.go | 1 - eth/api_backend.go | 23 +- eth/catalyst/api_test.go | 4 +- eth/tracers/api.go | 11 +- eth/tracers/api_test.go | 50 ++-- graphql/graphql.go | 2 +- internal/ethapi/api.go | 98 ++------ internal/ethapi/api_test.go | 83 ++++--- internal/ethapi/backend.go | 4 +- internal/ethapi/transaction_args_test.go | 4 +- 14 files changed, 748 insertions(+), 162 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 6b8dffdcdc..a4fb73474b 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 *map[common.Address]state.OverrideAccount) (*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 0000000000..0c344c57d3 --- /dev/null +++ b/core/state/state_override.go @@ -0,0 +1,273 @@ +// 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" +) + +//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"` +} + +// 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 0000000000..8d6292a9ce --- /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 0000000000..9ffc268f6b --- /dev/null +++ b/core/state/state_override_test.go @@ -0,0 +1,287 @@ +// 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/rawdb" + "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(rawdb.NewMemoryDatabase()) + 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 1a12f519a4..73b44f19ec 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -330,7 +330,6 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 { if stateObject != nil { return stateObject.Nonce() } - return 0 } diff --git a/eth/api_backend.go b/eth/api_backend.go index 8a9898b956..a747720f1a 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 *map[common.Address]state.OverrideAccount) (*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 *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) } 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 0a58e1eaee..1e14d7ff25 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1153,7 +1153,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) } @@ -1687,7 +1687,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 9ee108d0f1..fd1ef07291 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -162,7 +162,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 } @@ -952,11 +952,14 @@ 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 { - if err := config.StateOverrides.Apply(statedb); err != nil { - return nil, err + if len(config.StateOverrides) != 0 { + statedb, err = state.OverrideState(statedb, config.StateOverrides) + if err != nil { + return nil, err + } } config.BlockOverrides.Apply(&vmctx) } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 47e3693495..74743ea0a5 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 f7cf164d31..2592830673 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -87,7 +87,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 aeb3e8adc2..3cbc7e22f5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -26,6 +26,8 @@ import ( "time" "github.com/davecgh/go-spew/spew" + "github.com/tyler-smith/go-bip39" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/scwallet" @@ -36,7 +38,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,8 +49,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" - "github.com/holiman/uint256" - "github.com/tyler-smith/go-bip39" ) // estimateGasErrorRatio is the amount of overestimation eth_estimateGas is @@ -664,7 +663,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 } @@ -717,7 +716,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 } @@ -909,7 +908,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 } @@ -921,7 +920,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 } @@ -957,67 +956,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"` -} - -// StateOverride is the collection of overridden accounts. -type StateOverride map[common.Address]OverrideAccount - -// Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(statedb *state.StateDB) error { - if diff == nil { - return nil - } - for addr, account := range *diff { - // 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 @@ -1093,10 +1034,7 @@ 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) { - if err := overrides.Apply(state); err != nil { - return nil, err - } +func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { // 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 @@ -1144,15 +1082,14 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S 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 *map[common.Address]state.OverrideAccount, 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 } - - return doCall(ctx, b, args, state, header, overrides, blockOverrides, timeout, globalGasCap) + return doCall(ctx, b, args, state, header, blockOverrides, timeout, globalGasCap) } // Call executes the given transaction on the state for the given block number. @@ -1161,7 +1098,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 *map[common.Address]state.OverrideAccount, blockOverrides *BlockOverrides) (hexutil.Bytes, error) { if blockNrOrHash == nil { latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) blockNrOrHash = &latest @@ -1181,15 +1118,12 @@ func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockN // 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 *map[common.Address]state.OverrideAccount, 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); err != nil { - return 0, err - } // Construct the gas estimator option from the user input opts := &gasestimator.Options{ Config: b.ChainConfig(), @@ -1225,7 +1159,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 *map[common.Address]state.OverrideAccount) (hexutil.Uint64, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash @@ -1510,7 +1444,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 } @@ -1639,7 +1573,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 3c5da840d3..3e99c032d6 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -529,7 +529,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") } @@ -540,12 +540,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") } @@ -649,7 +654,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 }{ @@ -685,8 +690,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, @@ -698,8 +703,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, }, @@ -758,7 +763,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) @@ -815,7 +824,7 @@ func TestCall(t *testing.T) { randomAccounts := newAccounts(3) var testSuite = []struct { blockNumber rpc.BlockNumber - overrides StateOverride + overrides map[common.Address]state.OverrideAccount call TransactionArgs blockOverrides BlockOverrides expectErr error @@ -872,21 +881,21 @@ 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", }, // Invalid call without state overriding - { - blockNumber: rpc.LatestBlockNumber, - call: TransactionArgs{ - From: &randomAccounts[0].addr, - To: &randomAccounts[1].addr, - Value: (*hexutil.Big)(big.NewInt(1000)), - }, - 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 @@ -910,9 +919,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))}, }, }, @@ -951,9 +960,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", @@ -977,8 +986,8 @@ 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{}, }, }, @@ -986,7 +995,11 @@ func TestCall(t *testing.T) { }, } for i, tc := range testSuite { - result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) + 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 %d: want error %v, have nothing", i, tc.expectErr) @@ -1343,16 +1356,16 @@ func newAccounts(n int) (accounts []account) { return accounts } -func newRPCBalance(balance *big.Int) *hexutil.Big { - rpcBalance := (*hexutil.Big)(balance) - return rpcBalance -} - func hex2Bytes(str string) *hexutil.Bytes { rpcBytes := hexutil.Bytes(common.Hex2Bytes(str)) return &rpcBytes } +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 2a45ba0921..cf0a4c66ec 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 *map[common.Address]state.OverrideAccount) (*state.StateDB, *types.Header, error) + StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, *types.Header, error) Pending() (*types.Block, types.Receipts, *state.StateDB) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetTd(ctx context.Context, hash common.Hash) *big.Int diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 6750fc07a9..44e5250704 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -354,10 +354,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 }