state_accessor.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright 2021 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. package eth
  17. import (
  18. "errors"
  19. "fmt"
  20. "math/big"
  21. "time"
  22. "github.com/ethereum/go-ethereum/common"
  23. "github.com/ethereum/go-ethereum/consensus"
  24. "github.com/ethereum/go-ethereum/core"
  25. "github.com/ethereum/go-ethereum/core/state"
  26. "github.com/ethereum/go-ethereum/core/types"
  27. "github.com/ethereum/go-ethereum/core/vm"
  28. "github.com/ethereum/go-ethereum/log"
  29. "github.com/ethereum/go-ethereum/trie"
  30. )
  31. // stateAtBlock retrieves the state database associated with a certain block.
  32. // If no state is locally available for the given block, a number of blocks
  33. // are attempted to be reexecuted to generate the desired state. The optional
  34. // base layer statedb can be passed then it's regarded as the statedb of the
  35. // parent block.
  36. // Parameters:
  37. // - block: The block for which we want the state (== state at the stateRoot of the parent)
  38. // - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
  39. // - base: If the caller is tracing multiple blocks, the caller can provide the parent state
  40. // continuously from the callsite.
  41. // - checklive: if true, then the live 'blockchain' state database is used. If the caller want to
  42. // perform Commit or other 'save-to-disk' changes, this should be set to false to avoid
  43. // storing trash persistently
  44. // - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided,
  45. // it would be preferrable to start from a fresh state, if we have it on disk.
  46. func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
  47. var (
  48. current *types.Block
  49. database state.Database
  50. report = true
  51. origin = block.NumberU64()
  52. )
  53. // Check the live database first if we have the state fully available, use that.
  54. if checkLive {
  55. statedb, err = eth.blockchain.StateAt(block.Root())
  56. if err == nil {
  57. return statedb, nil
  58. }
  59. }
  60. if base != nil {
  61. if preferDisk {
  62. // Create an ephemeral trie.Database for isolating the live one. Otherwise
  63. // the internal junks created by tracing will be persisted into the disk.
  64. database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16})
  65. if statedb, err = state.New(block.Root(), database, nil); err == nil {
  66. log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
  67. return statedb, nil
  68. }
  69. }
  70. // The optional base statedb is given, mark the start point as parent block
  71. statedb, database, report = base, base.Database(), false
  72. current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
  73. } else {
  74. // Otherwise try to reexec blocks until we find a state or reach our limit
  75. current = block
  76. // Create an ephemeral trie.Database for isolating the live one. Otherwise
  77. // the internal junks created by tracing will be persisted into the disk.
  78. database = state.NewDatabaseWithConfig(eth.chainDb, &trie.Config{Cache: 16})
  79. // If we didn't check the dirty database, do check the clean one, otherwise
  80. // we would rewind past a persisted block (specific corner case is chain
  81. // tracing from the genesis).
  82. if !checkLive {
  83. statedb, err = state.New(current.Root(), database, nil)
  84. if err == nil {
  85. return statedb, nil
  86. }
  87. }
  88. // Database does not have the state for the given block, try to regenerate
  89. for i := uint64(0); i < reexec; i++ {
  90. if current.NumberU64() == 0 {
  91. return nil, errors.New("genesis state is missing")
  92. }
  93. parent := eth.blockchain.GetBlock(current.ParentHash(), current.NumberU64()-1)
  94. if parent == nil {
  95. return nil, fmt.Errorf("missing block %v %d", current.ParentHash(), current.NumberU64()-1)
  96. }
  97. current = parent
  98. statedb, err = state.New(current.Root(), database, nil)
  99. if err == nil {
  100. break
  101. }
  102. }
  103. if err != nil {
  104. switch err.(type) {
  105. case *trie.MissingNodeError:
  106. return nil, fmt.Errorf("required historical state unavailable (reexec=%d)", reexec)
  107. default:
  108. return nil, err
  109. }
  110. }
  111. }
  112. // State was available at historical point, regenerate
  113. var (
  114. start = time.Now()
  115. logged time.Time
  116. parent common.Hash
  117. )
  118. for current.NumberU64() < origin {
  119. // Print progress logs if long enough time elapsed
  120. if time.Since(logged) > 8*time.Second && report {
  121. log.Info("Regenerating historical state", "block", current.NumberU64()+1, "target", origin, "remaining", origin-current.NumberU64()-1, "elapsed", time.Since(start))
  122. logged = time.Now()
  123. }
  124. // Retrieve the next block to regenerate and process it
  125. next := current.NumberU64() + 1
  126. if current = eth.blockchain.GetBlockByNumber(next); current == nil {
  127. return nil, fmt.Errorf("block #%d not found", next)
  128. }
  129. statedb, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
  130. if err != nil {
  131. return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
  132. }
  133. // Finalize the state so any modifications are written to the trie
  134. root, _, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
  135. if err != nil {
  136. return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
  137. current.NumberU64(), current.Root().Hex(), err)
  138. }
  139. statedb, err = state.New(root, database, nil)
  140. if err != nil {
  141. return nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err)
  142. }
  143. database.TrieDB().Reference(root, common.Hash{})
  144. if parent != (common.Hash{}) {
  145. database.TrieDB().Dereference(parent)
  146. }
  147. parent = root
  148. }
  149. if report {
  150. nodes, imgs := database.TrieDB().Size()
  151. log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
  152. }
  153. return statedb, nil
  154. }
  155. // stateAtTransaction returns the execution environment of a certain transaction.
  156. func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
  157. // Short circuit if it's genesis block.
  158. if block.NumberU64() == 0 {
  159. return nil, vm.BlockContext{}, nil, errors.New("no transaction in genesis")
  160. }
  161. // Create the parent state database
  162. parent := eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
  163. if parent == nil {
  164. return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
  165. }
  166. // Lookup the statedb of parent block from the live database,
  167. // otherwise regenerate it on the flight.
  168. statedb, err := eth.stateAtBlock(parent, reexec, nil, true, false)
  169. if err != nil {
  170. return nil, vm.BlockContext{}, nil, err
  171. }
  172. if txIndex == 0 && len(block.Transactions()) == 0 {
  173. return nil, vm.BlockContext{}, statedb, nil
  174. }
  175. // Recompute transactions up to the target index.
  176. signer := types.MakeSigner(eth.blockchain.Config(), block.Number())
  177. for idx, tx := range block.Transactions() {
  178. // Assemble the transaction call message and return if the requested offset
  179. msg, _ := tx.AsMessage(signer)
  180. txContext := core.NewEVMTxContext(msg)
  181. context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
  182. if idx == txIndex {
  183. return msg, context, statedb, nil
  184. }
  185. // Not yet the searched for transaction, execute on top of the current state
  186. vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
  187. if posa, ok := eth.Engine().(consensus.PoSA); ok && msg.From() == context.Coinbase &&
  188. posa.IsSystemContract(msg.To()) && msg.GasPrice().Cmp(big.NewInt(0)) == 0 {
  189. balance := statedb.GetBalance(consensus.SystemAddress)
  190. if balance.Cmp(common.Big0) > 0 {
  191. statedb.SetBalance(consensus.SystemAddress, big.NewInt(0))
  192. statedb.AddBalance(context.Coinbase, balance)
  193. }
  194. }
  195. statedb.Prepare(tx.Hash(), block.Hash(), idx)
  196. if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
  197. return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
  198. }
  199. // Ensure any modifications are committed to the state
  200. // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
  201. statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
  202. }
  203. return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
  204. }