Skip to content

Commit a600dab

Browse files
authored
eth, les: fix tracers (#22473)
* eth, les: fix tracer * eth: isolate live trie database in tracer * eth: fix nil * eth: fix * eth, les: add checkLive param * eth/tracer: fix
1 parent 2d89fe0 commit a600dab

File tree

7 files changed

+155
-241
lines changed

7 files changed

+155
-241
lines changed

eth/api.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,11 +407,10 @@ func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, c
407407
if block == nil {
408408
return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash)
409409
}
410-
_, _, statedb, release, err := api.eth.stateAtTransaction(block, txIndex, 0)
410+
_, _, statedb, err := api.eth.stateAtTransaction(block, txIndex, 0)
411411
if err != nil {
412412
return StorageRangeResult{}, err
413413
}
414-
defer release()
415414
st := statedb.StorageTrie(contractAddress)
416415
if st == nil {
417416
return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress)

eth/api_backend.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,14 +332,10 @@ func (b *EthAPIBackend) StartMining(threads int) error {
332332
return b.eth.StartMining(threads)
333333
}
334334

335-
func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64) (*state.StateDB, func(), error) {
336-
return b.eth.stateAtBlock(block, reexec)
335+
func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) {
336+
return b.eth.stateAtBlock(block, reexec, base, checkLive)
337337
}
338338

339-
func (b *EthAPIBackend) StatesInRange(ctx context.Context, fromBlock *types.Block, toBlock *types.Block, reexec uint64) ([]*state.StateDB, func(), error) {
340-
return b.eth.statesInRange(fromBlock, toBlock, reexec)
341-
}
342-
343-
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
339+
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
344340
return b.eth.stateAtTransaction(block, txIndex, reexec)
345341
}

eth/state_accessor.go

Lines changed: 74 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -31,39 +31,58 @@ import (
3131
)
3232

3333
// stateAtBlock retrieves the state database associated with a certain block.
34-
// If no state is locally available for the given block, a number of blocks are
35-
// attempted to be reexecuted to generate the desired state.
36-
func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64) (statedb *state.StateDB, release func(), err error) {
37-
// If we have the state fully available, use that
38-
statedb, err = eth.blockchain.StateAt(block.Root())
39-
if err == nil {
40-
return statedb, func() {}, nil
34+
// If no state is locally available for the given block, a number of blocks
35+
// are attempted to be reexecuted to generate the desired state. The optional
36+
// base layer statedb can be passed then it's regarded as the statedb of the
37+
// parent block.
38+
func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (statedb *state.StateDB, err error) {
39+
var (
40+
current *types.Block
41+
database state.Database
42+
report = true
43+
origin = block.NumberU64()
44+
)
45+
// Check the live database first if we have the state fully available, use that.
46+
if checkLive {
47+
statedb, err = eth.blockchain.StateAt(block.Root())
48+
if err == nil {
49+
return statedb, nil
50+
}
4151
}
42-
// Otherwise try to reexec blocks until we find a state or reach our limit
43-
origin := block.NumberU64()
44-
database := state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16, Preimages: true})
52+
if base != nil {
53+
// The optional base statedb is given, mark the start point as parent block
54+
statedb, database, report = base, base.Database(), false
55+
current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
56+
} else {
57+
// Otherwise try to reexec blocks until we find a state or reach our limit
58+
current = block
4559

46-
for i := uint64(0); i < reexec; i++ {
47-
if block.NumberU64() == 0 {
48-
return nil, nil, errors.New("genesis state is missing")
49-
}
50-
parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
51-
if parent == nil {
52-
return nil, nil, fmt.Errorf("missing block %v %d", block.ParentHash(), block.NumberU64()-1)
53-
}
54-
block = parent
60+
// Create an ephemeral trie.Database for isolating the live one. Otherwise
61+
// the internal junks created by tracing will be persisted into the disk.
62+
database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16})
5563

56-
statedb, err = state.New(block.Root(), database, nil)
57-
if err == nil {
58-
break
64+
for i := uint64(0); i < reexec; i++ {
65+
if current.NumberU64() == 0 {
66+
return nil, errors.New("genesis state is missing")
67+
}
68+
parent := eth.blockchain.GetBlock(current.ParentHash(), current.NumberU64()-1)
69+
if parent == nil {
70+
return nil, fmt.Errorf("missing block %v %d", current.ParentHash(), current.NumberU64()-1)
71+
}
72+
current = parent
73+
74+
statedb, err = state.New(current.Root(), database, nil)
75+
if err == nil {
76+
break
77+
}
5978
}
60-
}
61-
if err != nil {
62-
switch err.(type) {
63-
case *trie.MissingNodeError:
64-
return nil, nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
65-
default:
66-
return nil, nil, err
79+
if err != nil {
80+
switch err.(type) {
81+
case *trie.MissingNodeError:
82+
return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
83+
default:
84+
return nil, err
85+
}
6786
}
6887
}
6988
// State was available at historical point, regenerate
@@ -72,138 +91,62 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64) (statedb *s
7291
logged time.Time
7392
parent common.Hash
7493
)
75-
defer func() {
76-
if err != nil && parent != (common.Hash{}) {
77-
database.TrieDB().Dereference(parent)
78-
}
79-
}()
80-
for block.NumberU64() < origin {
94+
for current.NumberU64() < origin {
8195
// Print progress logs if long enough time elapsed
82-
if time.Since(logged) > 8*time.Second {
83-
log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "remaining", origin-block.NumberU64()-1, "elapsed", time.Since(start))
96+
if time.Since(logged) > 8*time.Second && report {
97+
log.Info("Regenerating historical state", "block", current.NumberU64()+1, "target", origin, "remaining", origin-current.NumberU64()-1, "elapsed", time.Since(start))
8498
logged = time.Now()
8599
}
86100
// Retrieve the next block to regenerate and process it
87-
if block = eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil {
88-
return nil, nil, fmt.Errorf("block #%d not found", block.NumberU64()+1)
101+
next := current.NumberU64() + 1
102+
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
103+
return nil, fmt.Errorf("block #%d not found", next)
89104
}
90-
_, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{})
105+
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
91106
if err != nil {
92-
return nil, nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
107+
return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
93108
}
94109
// Finalize the state so any modifications are written to the trie
95-
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(block.Number()))
110+
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
96111
if err != nil {
97-
return nil, nil, err
112+
return nil, err
98113
}
99114
statedb, err = state.New(root, database, nil)
100115
if err != nil {
101-
return nil, nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err)
116+
return nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err)
102117
}
103118
database.TrieDB().Reference(root, common.Hash{})
104119
if parent != (common.Hash{}) {
105120
database.TrieDB().Dereference(parent)
106121
}
107122
parent = root
108123
}
109-
nodes, imgs := database.TrieDB().Size()
110-
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
111-
return statedb, func() { database.TrieDB().Dereference(parent) }, nil
112-
}
113-
114-
// statesInRange retrieves a batch of state databases associated with the specific
115-
// block ranges. If no state is locally available for the given range, a number of
116-
// blocks are attempted to be reexecuted to generate the ancestor state.
117-
func (eth *Ethereum) statesInRange(fromBlock, toBlock *types.Block, reexec uint64) (states []*state.StateDB, release func(), err error) {
118-
statedb, err := eth.blockchain.StateAt(fromBlock.Root())
119-
if err != nil {
120-
statedb, _, err = eth.stateAtBlock(fromBlock, reexec)
121-
}
122-
if err != nil {
123-
return nil, nil, err
124-
}
125-
states = append(states, statedb.Copy())
126-
127-
var (
128-
logged time.Time
129-
parent common.Hash
130-
start = time.Now()
131-
refs = []common.Hash{fromBlock.Root()}
132-
database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16, Preimages: true})
133-
)
134-
// Release all resources(including the states referenced by `stateAtBlock`)
135-
// if error is returned.
136-
defer func() {
137-
if err != nil {
138-
for _, ref := range refs {
139-
database.TrieDB().Dereference(ref)
140-
}
141-
}
142-
}()
143-
for i := fromBlock.NumberU64() + 1; i <= toBlock.NumberU64(); i++ {
144-
// Print progress logs if long enough time elapsed
145-
if time.Since(logged) > 8*time.Second {
146-
logged = time.Now()
147-
log.Info("Regenerating historical state", "block", i, "target", fromBlock.NumberU64(), "remaining", toBlock.NumberU64()-i, "elapsed", time.Since(start))
148-
}
149-
// Retrieve the next block to regenerate and process it
150-
block := eth.blockchain.GetBlockByNumber(i)
151-
if block == nil {
152-
return nil, nil, fmt.Errorf("block #%d not found", i)
153-
}
154-
_, _, _, err := eth.blockchain.Processor().Process(block, statedb, vm.Config{})
155-
if err != nil {
156-
return nil, nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
157-
}
158-
// Finalize the state so any modifications are written to the trie
159-
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(block.Number()))
160-
if err != nil {
161-
return nil, nil, err
162-
}
163-
statedb, err := eth.blockchain.StateAt(root)
164-
if err != nil {
165-
return nil, nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err)
166-
}
167-
states = append(states, statedb.Copy())
168-
169-
// Reference the trie twice, once for us, once for the tracer
170-
database.TrieDB().Reference(root, common.Hash{})
171-
database.TrieDB().Reference(root, common.Hash{})
172-
refs = append(refs, root)
173-
174-
// Dereference all past tries we ourselves are done working with
175-
if parent != (common.Hash{}) {
176-
database.TrieDB().Dereference(parent)
177-
}
178-
parent = root
179-
}
180-
// release is handler to release all states referenced, including
181-
// the one referenced in `stateAtBlock`.
182-
release = func() {
183-
for _, ref := range refs {
184-
database.TrieDB().Dereference(ref)
185-
}
124+
if report {
125+
nodes, imgs := database.TrieDB().Size()
126+
log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
186127
}
187-
return states, release, nil
128+
return statedb, nil
188129
}
189130

190131
// stateAtTransaction returns the execution environment of a certain transaction.
191-
func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, func(), error) {
132+
func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
192133
// Short circuit if it's genesis block.
193134
if block.NumberU64() == 0 {
194-
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
135+
return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis")
195136
}
196137
// Create the parent state database
197138
parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
198139
if parent == nil {
199-
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
140+
return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
200141
}
201-
statedb, release, err := eth.stateAtBlock(parent, reexec)
142+
// Lookup the statedb of parent block from the live database,
143+
// otherwise regenerate it on the flight.
144+
statedb, err := eth.stateAtBlock(parent, reexec, nil, true)
202145
if err != nil {
203-
return nil, vm.BlockContext{}, nil, nil, err
146+
return nil, vm.BlockContext{}, nil, err
204147
}
205148
if txIndex == 0 && len(block.Transactions()) == 0 {
206-
return nil, vm.BlockContext{}, statedb, release, nil
149+
return nil, vm.BlockContext{}, statedb, nil
207150
}
208151
// Recompute transactions up to the target index.
209152
signer := types.MakeSigner(eth.blockchain.Config(), block.Number())
@@ -213,19 +156,17 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
213156
txContext := core.NewEVMTxContext(msg)
214157
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
215158
if idx == txIndex {
216-
return msg, context, statedb, release, nil
159+
return msg, context, statedb, nil
217160
}
218161
// Not yet the searched for transaction, execute on top of the current state
219162
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
220163
statedb.Prepare(tx.Hash(), block.Hash(), idx)
221164
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
222-
release()
223-
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
165+
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
224166
}
225167
// Ensure any modifications are committed to the state
226168
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
227169
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
228170
}
229-
release()
230-
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
171+
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
231172
}

0 commit comments

Comments
 (0)