@@ -31,39 +31,58 @@ import (
31
31
)
32
32
33
33
// 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
+ }
41
51
}
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
45
59
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 })
55
63
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
+ }
59
78
}
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
+ }
67
86
}
68
87
}
69
88
// State was available at historical point, regenerate
@@ -72,138 +91,62 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64) (statedb *s
72
91
logged time.Time
73
92
parent common.Hash
74
93
)
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 {
81
95
// 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 ))
84
98
logged = time .Now ()
85
99
}
86
100
// 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 )
89
104
}
90
- _ , _ , _ , err := eth .blockchain .Processor ().Process (block , statedb , vm.Config {})
105
+ _ , _ , _ , err := eth .blockchain .Processor ().Process (current , statedb , vm.Config {})
91
106
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 )
93
108
}
94
109
// 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 ()))
96
111
if err != nil {
97
- return nil , nil , err
112
+ return nil , err
98
113
}
99
114
statedb , err = state .New (root , database , nil )
100
115
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 )
102
117
}
103
118
database .TrieDB ().Reference (root , common.Hash {})
104
119
if parent != (common.Hash {}) {
105
120
database .TrieDB ().Dereference (parent )
106
121
}
107
122
parent = root
108
123
}
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 )
186
127
}
187
- return states , release , nil
128
+ return statedb , nil
188
129
}
189
130
190
131
// 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 ) {
192
133
// Short circuit if it's genesis block.
193
134
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" )
195
136
}
196
137
// Create the parent state database
197
138
parent := eth .blockchain .GetBlock (block .ParentHash (), block .NumberU64 ()- 1 )
198
139
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 ())
200
141
}
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 )
202
145
if err != nil {
203
- return nil , vm.BlockContext {}, nil , nil , err
146
+ return nil , vm.BlockContext {}, nil , err
204
147
}
205
148
if txIndex == 0 && len (block .Transactions ()) == 0 {
206
- return nil , vm.BlockContext {}, statedb , release , nil
149
+ return nil , vm.BlockContext {}, statedb , nil
207
150
}
208
151
// Recompute transactions up to the target index.
209
152
signer := types .MakeSigner (eth .blockchain .Config (), block .Number ())
@@ -213,19 +156,17 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
213
156
txContext := core .NewEVMTxContext (msg )
214
157
context := core .NewEVMBlockContext (block .Header (), eth .blockchain , nil )
215
158
if idx == txIndex {
216
- return msg , context , statedb , release , nil
159
+ return msg , context , statedb , nil
217
160
}
218
161
// Not yet the searched for transaction, execute on top of the current state
219
162
vmenv := vm .NewEVM (context , txContext , statedb , eth .blockchain .Config (), vm.Config {})
220
163
statedb .Prepare (tx .Hash (), block .Hash (), idx )
221
164
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 )
224
166
}
225
167
// Ensure any modifications are committed to the state
226
168
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
227
169
statedb .Finalise (vmenv .ChainConfig ().IsEIP158 (block .Number ()))
228
170
}
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 ())
231
172
}
0 commit comments