mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
core, eth, internal: introduce state override reader
This commit is contained in:
parent
623b17ba20
commit
3232927325
14 changed files with 748 additions and 162 deletions
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
273
core/state/state_override.go
Normal file
273
core/state/state_override.go
Normal file
|
|
@ -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)
|
||||
}
|
||||
62
core/state/state_override_json.go
Normal file
62
core/state/state_override_json.go
Normal file
|
|
@ -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
|
||||
}
|
||||
287
core/state/state_override_test.go
Normal file
287
core/state/state_override_test.go
Normal file
|
|
@ -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 {
|
||||
return stateObject.Nonce()
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,12 +952,15 @@ 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 {
|
||||
if len(config.StateOverrides) != 0 {
|
||||
statedb, err = state.OverrideState(statedb, config.StateOverrides)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
config.BlockOverrides.Apply(&vmctx)
|
||||
}
|
||||
// Execute the trace
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
Loading…
Reference in a new issue