Skip to content

Commit 460ba4d

Browse files
committed
core, eth, internal: introduce state override reader
1 parent 409a8e0 commit 460ba4d

File tree

14 files changed

+741
-185
lines changed

14 files changed

+741
-185
lines changed

core/blockchain_reader.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,14 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
356356
return state.New(root, bc.stateDb)
357357
}
358358

359+
// StateWithOverrides returns a new mutable state with provided state overrides.
360+
func (bc *BlockChain) StateWithOverrides(root common.Hash, overrides *map[common.Address]state.OverrideAccount) (*state.StateDB, error) {
361+
if overrides == nil {
362+
return bc.StateAt(root)
363+
}
364+
return state.New(root, state.NewOverrideDatabase(bc.stateDb, *overrides))
365+
}
366+
359367
// Config retrieves the chain's fork configuration.
360368
func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
361369

core/state/state_override.go

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright 2024 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package state
18+
19+
import (
20+
"errors"
21+
"maps"
22+
"slices"
23+
24+
"github.com/ethereum/go-ethereum/common"
25+
"github.com/ethereum/go-ethereum/common/hexutil"
26+
"github.com/ethereum/go-ethereum/core/types"
27+
"github.com/ethereum/go-ethereum/crypto"
28+
"github.com/holiman/uint256"
29+
)
30+
31+
//go:generate go run github.com/fjl/gencodec -type OverrideAccount -field-override overrideMarshaling -out state_override_json.go
32+
33+
// OverrideAccount specifies the fields of an account that should be overridden
34+
// during the execution of a message call.
35+
//
36+
// Note: The 'state' and 'stateDiff' fields are mutually exclusive and cannot
37+
// be specified simultaneously. If 'state' is set, the message execution will
38+
// only use the data provided in the given state. Otherwise, if 'stateDiff'
39+
// is set, all the differences will be applied first, and then the call message
40+
// will be executed.
41+
type OverrideAccount struct {
42+
Nonce *uint64 `json:"nonce"`
43+
Code *[]byte `json:"code"`
44+
Balance *uint256.Int `json:"balance"`
45+
State map[common.Hash]common.Hash `json:"state"`
46+
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
47+
}
48+
49+
// nolint
50+
type overrideMarshaling struct {
51+
Nonce *hexutil.Uint64
52+
Code *hexutil.Bytes
53+
Balance *hexutil.U256
54+
}
55+
56+
// copy returns a deep-copied override object.
57+
func (o OverrideAccount) copy() OverrideAccount {
58+
var obj OverrideAccount
59+
if o.Nonce != nil {
60+
nonce := *o.Nonce
61+
obj.Nonce = &nonce
62+
}
63+
if o.Code != nil {
64+
code := slices.Clone(*o.Code)
65+
obj.Code = &code
66+
}
67+
if o.Balance != nil {
68+
obj.Balance = new(uint256.Int).Set(o.Balance)
69+
}
70+
if o.State != nil {
71+
obj.State = maps.Clone(o.State)
72+
}
73+
if o.StateDiff != nil {
74+
obj.StateDiff = maps.Clone(o.StateDiff)
75+
}
76+
return obj
77+
}
78+
79+
// overrideReader implements the Reader interface, serving as a wrapper around a
80+
// provided state reader, but incorporating with overridden states.
81+
type overrideReader struct {
82+
reader Reader
83+
overrides map[common.Address]OverrideAccount
84+
}
85+
86+
// newOverrideReader creates a reader with customized state overrides.
87+
func newOverrideReader(overrides map[common.Address]OverrideAccount, reader Reader) *overrideReader {
88+
return &overrideReader{
89+
reader: reader,
90+
overrides: overrides,
91+
}
92+
}
93+
94+
// Account implementing Reader interface, retrieving the account associated with
95+
// a particular address.
96+
//
97+
// - Returns a nil account if it does not exist
98+
// - Returns an error only if an unexpected issue occurs
99+
// - The returned account is safe to modify after the call
100+
func (r *overrideReader) Account(addr common.Address) (*types.StateAccount, error) {
101+
account, err := r.reader.Account(addr)
102+
if err != nil {
103+
return nil, err
104+
}
105+
// apply the overrides if it's specified
106+
override, ok := r.overrides[addr]
107+
if ok {
108+
if account == nil {
109+
account = types.NewEmptyStateAccount()
110+
}
111+
if override.Nonce != nil {
112+
account.Nonce = *override.Nonce
113+
}
114+
if override.Balance != nil {
115+
account.Balance = new(uint256.Int).Set(override.Balance)
116+
}
117+
if override.Code != nil {
118+
account.CodeHash = crypto.Keccak256(*override.Code)
119+
}
120+
// TODO what about the storage root then, should we compute the
121+
// storage root of overridden state here?
122+
}
123+
return account, nil
124+
}
125+
126+
// Storage implementing Reader interface, retrieving the storage slot associated
127+
// with a particular account address and slot key.
128+
//
129+
// - Returns an empty slot if it does not exist
130+
// - Returns an error only if an unexpected issue occurs
131+
// - The returned storage slot is safe to modify after the call
132+
func (r *overrideReader) Storage(addr common.Address, storageRoot common.Hash, slot common.Hash) (common.Hash, error) {
133+
override, ok := r.overrides[addr]
134+
if ok {
135+
if override.State != nil {
136+
return override.State[slot], nil
137+
}
138+
if override.StateDiff != nil {
139+
if val, ok := override.StateDiff[slot]; ok {
140+
return val, nil
141+
}
142+
}
143+
}
144+
return r.reader.Storage(addr, storageRoot, slot)
145+
}
146+
147+
// Copy implementing Reader interface, returning a deep-copied state reader.
148+
func (r *overrideReader) Copy() Reader {
149+
overrides := make(map[common.Address]OverrideAccount)
150+
for addr, override := range r.overrides {
151+
overrides[addr] = override.copy()
152+
}
153+
return &overrideReader{
154+
overrides: overrides,
155+
reader: r.reader.Copy(),
156+
}
157+
}
158+
159+
// OverrideDatabase implements the Database interface, serving as a wrapper
160+
// around a standard state database, but incorporating overridden states.
161+
type OverrideDatabase struct {
162+
Database
163+
overrides map[common.Address]OverrideAccount
164+
}
165+
166+
// NewOverrideDatabase creates a state database with provided state overrides.
167+
func NewOverrideDatabase(db Database, overrides map[common.Address]OverrideAccount) *OverrideDatabase {
168+
// Allocate an empty override set just in case the provided one
169+
// is nil. Don't panic for lazy users.
170+
if overrides == nil {
171+
overrides = make(map[common.Address]OverrideAccount)
172+
}
173+
return &OverrideDatabase{
174+
Database: db,
175+
overrides: overrides,
176+
}
177+
}
178+
179+
// Reader returns a state reader associated with the specified state root.
180+
func (db *OverrideDatabase) Reader(root common.Hash) (Reader, error) {
181+
reader, err := db.Database.Reader(root)
182+
if err != nil {
183+
return nil, err
184+
}
185+
return newOverrideReader(db.overrides, reader), nil
186+
}
187+
188+
// ContractCode retrieves a particular contract's code.
189+
func (db *OverrideDatabase) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) {
190+
override, ok := db.overrides[addr]
191+
if ok && override.Code != nil {
192+
return *override.Code, nil
193+
}
194+
return db.Database.ContractCode(addr, codeHash)
195+
}
196+
197+
// ContractCodeSize retrieves a particular contracts code's size.
198+
func (db *OverrideDatabase) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
199+
override, ok := db.overrides[addr]
200+
if ok && override.Code != nil {
201+
return len(*override.Code), nil
202+
}
203+
return db.Database.ContractCodeSize(addr, codeHash)
204+
}
205+
206+
// The stateWrapReader wraps a live state database as the state source.
207+
type stateWrapReader struct {
208+
state *StateDB
209+
}
210+
211+
// Account implementing Reader interface, retrieving the account associated with
212+
// a particular address.
213+
//
214+
// - Returns a nil account if it does not exist
215+
// - Returns an error only if an unexpected issue occurs
216+
// - The returned account is safe to modify after the call
217+
func (r *stateWrapReader) Account(addr common.Address) (*types.StateAccount, error) {
218+
obj := r.state.getStateObject(addr)
219+
if obj == nil {
220+
return nil, nil
221+
}
222+
return obj.data.Copy(), nil
223+
}
224+
225+
// Storage implementing Reader interface, retrieving the storage slot associated
226+
// with a particular account address and slot key.
227+
//
228+
// - Returns an empty slot if it does not exist
229+
// - Returns an error only if an unexpected issue occurs
230+
// - The returned storage slot is safe to modify after the call
231+
func (r *stateWrapReader) Storage(addr common.Address, storageRoot common.Hash, slot common.Hash) (common.Hash, error) {
232+
return r.state.GetState(addr, slot), nil
233+
}
234+
235+
// Copy implementing Reader interface, returning a deep-copied state reader.
236+
func (r *stateWrapReader) Copy() Reader {
237+
return &stateWrapReader{state: r.state.Copy()}
238+
}
239+
240+
// stateWrap is an internal struct wrapping a live state instance as the state
241+
// data source. This can be useful in scenarios where we need to create the
242+
// snapshot of the current state and apply some additional overrides on top
243+
// (e.g. state override in RPC call serving).
244+
type stateWrap struct {
245+
Database
246+
state *StateDB
247+
}
248+
249+
// Reader returns a state reader associated with the specified state root.
250+
func (db *stateWrap) Reader(root common.Hash) (Reader, error) {
251+
if root != db.state.originalRoot {
252+
return nil, errors.New("state root is not matched")
253+
}
254+
return &stateWrapReader{state: db.state}, nil
255+
}
256+
257+
// OverrideState applies the state overrides into the provided live state database.
258+
func OverrideState(state *StateDB, overrides map[common.Address]OverrideAccount) (*StateDB, error) {
259+
db := NewOverrideDatabase(&stateWrap{
260+
Database: state.db,
261+
state: state.Copy(),
262+
}, overrides)
263+
return New(state.originalRoot, db)
264+
}

core/state/state_override_json.go

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)