Skip to content

Commit 6ac43e7

Browse files
committed
core, eth, internal: introduce state override reader
1 parent 5ad9c1e commit 6ac43e7

File tree

14 files changed

+748
-162
lines changed

14 files changed

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

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)