@@ -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