core, eth, internal: introduce state override reader

pull/29950/head
Gary Rong 4 months ago
parent 623b17ba20
commit 3232927325
  1. 8
      core/blockchain_reader.go
  2. 273
      core/state/state_override.go
  3. 62
      core/state/state_override_json.go
  4. 287
      core/state/state_override_test.go
  5. 1
      core/state/statedb.go
  6. 23
      eth/api_backend.go
  7. 4
      eth/catalyst/api_test.go
  8. 11
      eth/tracers/api.go
  9. 50
      eth/tracers/api_test.go
  10. 2
      graphql/graphql.go
  11. 98
      internal/ethapi/api.go
  12. 83
      internal/ethapi/api_test.go
  13. 4
      internal/ethapi/backend.go
  14. 4
      internal/ethapi/transaction_args_test.go

@ -356,6 +356,14 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, bc.statedb) 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. // Config retrieves the chain's fork configuration.
func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig } func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }

@ -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 <http://www.gnu.org/licenses/>.
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)
}

@ -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
}

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}

@ -330,7 +330,6 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 {
if stateObject != nil { if stateObject != nil {
return stateObject.Nonce() return stateObject.Nonce()
} }
return 0 return 0
} }

@ -186,14 +186,21 @@ func (b *EthAPIBackend) Pending() (*types.Block, types.Receipts, *state.StateDB)
return b.eth.miner.Pending() 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 // Pending state is only known by the miner
if number == rpc.PendingBlockNumber { if number == rpc.PendingBlockNumber {
block, _, state := b.eth.miner.Pending() block, _, stateDb := b.eth.miner.Pending()
if block == nil || state == nil { if block == nil || stateDb == nil {
return nil, nil, errors.New("pending state is not available") 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 // Otherwise resolve the block number and return its state
header, err := b.HeaderByNumber(ctx, number) header, err := b.HeaderByNumber(ctx, number)
@ -203,16 +210,16 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
if header == nil { if header == nil {
return nil, nil, errors.New("header not found") 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
return stateDb, header, nil 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 { if blockNr, ok := blockNrOrHash.Number(); ok {
return b.StateAndHeaderByNumber(ctx, blockNr) return b.StateAndHeaderByNumber(ctx, blockNr, overrides)
} }
if hash, ok := blockNrOrHash.Hash(); ok { if hash, ok := blockNrOrHash.Hash(); ok {
header, err := b.HeaderByHash(ctx, hash) 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 { if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
return nil, nil, errors.New("hash is not currently canonical") 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }

@ -1153,7 +1153,7 @@ func TestWithdrawals(t *testing.T) {
} }
// 11: verify withdrawals were processed. // 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 { if err != nil {
t.Fatalf("unable to load db: %v", err) t.Fatalf("unable to load db: %v", err)
} }
@ -1687,7 +1687,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
} }
// 11: verify beacon root was processed. // 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 { if err != nil {
t.Fatalf("unable to load db: %v", err) t.Fatalf("unable to load db: %v", err)
} }

@ -162,7 +162,7 @@ type TraceConfig struct {
// field to override the state for tracing. // field to override the state for tracing.
type TraceCallConfig struct { type TraceCallConfig struct {
TraceConfig TraceConfig
StateOverrides *ethapi.StateOverride StateOverrides map[common.Address]state.OverrideAccount
BlockOverrides *ethapi.BlockOverrides BlockOverrides *ethapi.BlockOverrides
TxIndex *hexutil.Uint TxIndex *hexutil.Uint
} }
@ -952,11 +952,14 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
} }
defer release() defer release()
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
// Apply the customization rules if required. // Apply the customization rules if required.
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
if config != nil { if config != nil {
if err := config.StateOverrides.Apply(statedb); err != nil { if len(config.StateOverrides) != 0 {
return nil, err statedb, err = state.OverrideState(statedb, config.StateOverrides)
if err != nil {
return nil, err
}
} }
config.BlockOverrides.Apply(&vmctx) config.BlockOverrides.Apply(&vmctx)
} }

@ -45,6 +45,7 @@ import (
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
) )
var ( var (
@ -610,8 +611,8 @@ func TestTracingWithOverrides(t *testing.T) {
Value: (*hexutil.Big)(big.NewInt(1000)), Value: (*hexutil.Big)(big.NewInt(1000)),
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, randomAccounts[0].addr: {Balance: uint256.MustFromBig(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
}, },
}, },
want: `{"gas":21000,"failed":false,"returnValue":""}`, want: `{"gas":21000,"failed":false,"returnValue":""}`,
@ -652,9 +653,9 @@ func TestTracingWithOverrides(t *testing.T) {
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
//Tracer: &tracer, //Tracer: &tracer,
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
randomAccounts[2].addr: ethapi.OverrideAccount{ randomAccounts[2].addr: {
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), Code: newRawBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), 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")), // Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
randomAccounts[2].addr: ethapi.OverrideAccount{ randomAccounts[2].addr: {
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), Code: newRawBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
}, },
}, },
}, },
@ -735,9 +736,9 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
randomAccounts[2].addr: ethapi.OverrideAccount{ randomAccounts[2].addr: {
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), Code: newRawBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")),
State: newStates([]common.Hash{{}}, []common.Hash{{}}), State: newStates([]common.Hash{{}}, []common.Hash{{}}),
}, },
}, },
@ -753,9 +754,9 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
storageAccount: ethapi.OverrideAccount{ storageAccount: {
Code: newRPCBytes([]byte{ Code: newRawBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is 0x77) // SLOAD(3) + SLOAD(4) (which is 0x77)
byte(vm.PUSH1), 0x04, byte(vm.PUSH1), 0x04,
byte(vm.SLOAD), byte(vm.SLOAD),
@ -788,9 +789,9 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
storageAccount: ethapi.OverrideAccount{ storageAccount: {
Code: newRPCBytes([]byte{ Code: newRawBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00) // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00)
byte(vm.PUSH1), 0x04, byte(vm.PUSH1), 0x04,
byte(vm.SLOAD), byte(vm.SLOAD),
@ -826,9 +827,9 @@ func TestTracingWithOverrides(t *testing.T) {
Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), //
}, },
config: &TraceCallConfig{ config: &TraceCallConfig{
StateOverrides: &ethapi.StateOverride{ StateOverrides: map[common.Address]state.OverrideAccount{
storageAccount: ethapi.OverrideAccount{ storageAccount: {
Code: newRPCBytes([]byte{ Code: newRawBytes([]byte{
// SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44) // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44)
byte(vm.PUSH1), 0x04, byte(vm.PUSH1), 0x04,
byte(vm.SLOAD), byte(vm.SLOAD),
@ -898,16 +899,15 @@ func newAccounts(n int) (accounts []Account) {
return accounts return accounts
} }
func newRPCBalance(balance *big.Int) *hexutil.Big {
rpcBalance := (*hexutil.Big)(balance)
return rpcBalance
}
func newRPCBytes(bytes []byte) *hexutil.Bytes { func newRPCBytes(bytes []byte) *hexutil.Bytes {
rpcBytes := hexutil.Bytes(bytes) rpcBytes := hexutil.Bytes(bytes)
return &rpcBytes return &rpcBytes
} }
func newRawBytes(bytes []byte) *[]byte {
return &bytes
}
func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Hash { func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Hash {
if len(keys) != len(vals) { if len(keys) != len(vals) {
panic("invalid input") panic("invalid input")

@ -87,7 +87,7 @@ type Account struct {
// getState fetches the StateDB object for an account. // getState fetches the StateDB object for an account.
func (a *Account) getState(ctx context.Context) (*state.StateDB, error) { 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 return state, err
} }

@ -26,6 +26,8 @@ import (
"time" "time"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/tyler-smith/go-bip39"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/accounts/scwallet" "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/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "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/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -48,8 +49,6 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie" "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 // 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 // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
// block numbers are also allowed. // block numbers are also allowed.
func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { 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 { if state == nil || err != nil {
return nil, err return nil, err
} }
@ -717,7 +716,7 @@ func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address,
return nil, err 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 { if statedb == nil || err != nil {
return nil, err 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. // 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) { 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 { if state == nil || err != nil {
return nil, err 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 // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed. // numbers are also allowed.
func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { 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 { if state == nil || err != nil {
return nil, err return nil, err
} }
@ -957,67 +956,9 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
for i, receipt := range receipts { for i, receipt := range receipts {
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i) result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i)
} }
return result, nil 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. // BlockOverrides is a set of header fields to override.
type BlockOverrides struct { type BlockOverrides struct {
Number *hexutil.Big Number *hexutil.Big
@ -1093,10 +1034,7 @@ func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.H
return header 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, state *state.StateDB, header *types.Header, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
if err := overrides.Apply(state); err != nil {
return nil, err
}
// Setup context so it may be cancelled the call has completed // Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout. // or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc var cancel context.CancelFunc
@ -1144,15 +1082,14 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
return result, nil 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()) 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 { if state == nil || err != nil {
return nil, err return nil, err
} }
return doCall(ctx, b, args, state, header, blockOverrides, timeout, globalGasCap)
return doCall(ctx, b, args, state, header, overrides, blockOverrides, timeout, globalGasCap)
} }
// Call executes the given transaction on the state for the given block number. // 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 // Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values. // 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 { if blockNrOrHash == nil {
latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
blockNrOrHash = &latest 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 // 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 & // there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
// non-zero) and `gasCap` (if non-zero). // 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 // 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 { if state == nil || err != nil {
return 0, err return 0, err
} }
if err = overrides.Apply(state); err != nil {
return 0, err
}
// Construct the gas estimator option from the user input // Construct the gas estimator option from the user input
opts := &gasestimator.Options{ opts := &gasestimator.Options{
Config: b.ChainConfig(), 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 // value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
// configuration (if non-zero). // configuration (if non-zero).
// Note: Required blob gas is not computed in this method. // 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) bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
if blockNrOrHash != nil { if blockNrOrHash != nil {
bNrOrHash = *blockNrOrHash bNrOrHash = *blockNrOrHash
@ -1510,7 +1444,7 @@ func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args Transaction
// If the transaction itself fails, an vmErr is returned. // 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) { 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 // Retrieve the execution context
db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) db, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash, nil)
if db == nil || err != nil { if db == nil || err != nil {
return nil, 0, nil, err return nil, 0, nil, err
} }
@ -1639,7 +1573,7 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm
return (*hexutil.Uint64)(&nonce), nil return (*hexutil.Uint64)(&nonce), nil
} }
// Resolve block number and use its state to ask for the nonce // 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 { if state == nil || err != nil {
return nil, err return nil, err
} }

@ -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) { 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 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 { if number == rpc.PendingBlockNumber {
panic("pending state not implemented") panic("pending state not implemented")
} }
@ -540,12 +540,17 @@ func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.Bloc
if header == nil { if header == nil {
return nil, nil, errors.New("header not found") 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 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 { if blockNr, ok := blockNrOrHash.Number(); ok {
return b.StateAndHeaderByNumber(ctx, blockNr) return b.StateAndHeaderByNumber(ctx, blockNr, overrides)
} }
panic("only implemented for number") panic("only implemented for number")
} }
@ -649,7 +654,7 @@ func TestEstimateGas(t *testing.T) {
var testSuite = []struct { var testSuite = []struct {
blockNumber rpc.BlockNumber blockNumber rpc.BlockNumber
call TransactionArgs call TransactionArgs
overrides StateOverride overrides map[common.Address]state.OverrideAccount
expectErr error expectErr error
want uint64 want uint64
}{ }{
@ -685,8 +690,8 @@ func TestEstimateGas(t *testing.T) {
{ {
blockNumber: rpc.LatestBlockNumber, blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{}, call: TransactionArgs{},
overrides: StateOverride{ overrides: map[common.Address]state.OverrideAccount{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, randomAccounts[0].addr: {Balance: uint256.MustFromBig(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
}, },
expectErr: nil, expectErr: nil,
want: 53000, want: 53000,
@ -698,8 +703,8 @@ func TestEstimateGas(t *testing.T) {
To: &randomAccounts[1].addr, To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)), Value: (*hexutil.Big)(big.NewInt(1000)),
}, },
overrides: StateOverride{ overrides: map[common.Address]state.OverrideAccount{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))}, randomAccounts[0].addr: {Balance: uint256.MustFromBig(big.NewInt(0))},
}, },
expectErr: core.ErrInsufficientFunds, expectErr: core.ErrInsufficientFunds,
}, },
@ -758,7 +763,11 @@ func TestEstimateGas(t *testing.T) {
}, },
} }
for i, tc := range testSuite { 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 tc.expectErr != nil {
if err == nil { if err == nil {
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
@ -815,7 +824,7 @@ func TestCall(t *testing.T) {
randomAccounts := newAccounts(3) randomAccounts := newAccounts(3)
var testSuite = []struct { var testSuite = []struct {
blockNumber rpc.BlockNumber blockNumber rpc.BlockNumber
overrides StateOverride overrides map[common.Address]state.OverrideAccount
call TransactionArgs call TransactionArgs
blockOverrides BlockOverrides blockOverrides BlockOverrides
expectErr error expectErr error
@ -872,21 +881,21 @@ func TestCall(t *testing.T) {
To: &randomAccounts[1].addr, To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)), Value: (*hexutil.Big)(big.NewInt(1000)),
}, },
overrides: StateOverride{ overrides: map[common.Address]state.OverrideAccount{
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, randomAccounts[0].addr: {Balance: uint256.MustFromBig(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
}, },
want: "0x", want: "0x",
}, },
// Invalid call without state overriding // Invalid call without state overriding
{ //{
blockNumber: rpc.LatestBlockNumber, // blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{ // call: TransactionArgs{
From: &randomAccounts[0].addr, // From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr, // To: &randomAccounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)), // Value: (*hexutil.Big)(big.NewInt(1000)),
}, // },
expectErr: core.ErrInsufficientFunds, // expectErr: core.ErrInsufficientFunds,
}, //},
// Successful simple contract call // Successful simple contract call
// //
// // SPDX-License-Identifier: GPL-3.0 // // SPDX-License-Identifier: GPL-3.0
@ -910,9 +919,9 @@ func TestCall(t *testing.T) {
To: &randomAccounts[2].addr, To: &randomAccounts[2].addr,
Data: hex2Bytes("8381f58a"), // call number() Data: hex2Bytes("8381f58a"), // call number()
}, },
overrides: StateOverride{ overrides: map[common.Address]state.OverrideAccount{
randomAccounts[2].addr: OverrideAccount{ randomAccounts[2].addr: {
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"), Code: hex2RawBytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))}, 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}}, BlobHashes: []common.Hash{{0x01, 0x22}},
BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
}, },
overrides: StateOverride{ overrides: map[common.Address]state.OverrideAccount{
randomAccounts[2].addr: { randomAccounts[2].addr: {
Code: hex2Bytes("60004960005260206000f3"), Code: hex2RawBytes("60004960005260206000f3"),
}, },
}, },
want: "0x0122000000000000000000000000000000000000000000000000000000000000", want: "0x0122000000000000000000000000000000000000000000000000000000000000",
@ -977,8 +986,8 @@ func TestCall(t *testing.T) {
// } // }
Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"), Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"),
}, },
overrides: StateOverride{ overrides: map[common.Address]state.OverrideAccount{
dad: OverrideAccount{ dad: {
State: map[common.Hash]common.Hash{}, State: map[common.Hash]common.Hash{},
}, },
}, },
@ -986,7 +995,11 @@ func TestCall(t *testing.T) {
}, },
} }
for i, tc := range testSuite { 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 tc.expectErr != nil {
if err == nil { if err == nil {
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
@ -1343,16 +1356,16 @@ func newAccounts(n int) (accounts []account) {
return accounts return accounts
} }
func newRPCBalance(balance *big.Int) *hexutil.Big {
rpcBalance := (*hexutil.Big)(balance)
return rpcBalance
}
func hex2Bytes(str string) *hexutil.Bytes { func hex2Bytes(str string) *hexutil.Bytes {
rpcBytes := hexutil.Bytes(common.Hex2Bytes(str)) rpcBytes := hexutil.Bytes(common.Hex2Bytes(str))
return &rpcBytes return &rpcBytes
} }
func hex2RawBytes(str string) *[]byte {
rpcBytes := common.Hex2Bytes(str)
return &rpcBytes
}
func TestRPCMarshalBlock(t *testing.T) { func TestRPCMarshalBlock(t *testing.T) {
t.Parallel() t.Parallel()
var ( var (

@ -64,8 +64,8 @@ type Backend interface {
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*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) 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) (*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) Pending() (*types.Block, types.Receipts, *state.StateDB)
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
GetTd(ctx context.Context, hash common.Hash) *big.Int GetTd(ctx context.Context, hash common.Hash) *big.Int

@ -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) { func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
return nil, nil 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 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 return nil, nil, nil
} }
func (b *backendMock) Pending() (*types.Block, types.Receipts, *state.StateDB) { return nil, nil, nil } func (b *backendMock) Pending() (*types.Block, types.Receipts, *state.StateDB) { return nil, nil, nil }

Loading…
Cancel
Save