github.com/haliliceylan/bsc@v1.1.10-0.20220501224556-eb78d644ebcb/core/state_prefetcher.go (about) 1 // Copyright 2019 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 core 18 19 import ( 20 "sync/atomic" 21 22 "github.com/ethereum/go-ethereum/consensus" 23 "github.com/ethereum/go-ethereum/core/state" 24 "github.com/ethereum/go-ethereum/core/types" 25 "github.com/ethereum/go-ethereum/core/vm" 26 "github.com/ethereum/go-ethereum/params" 27 ) 28 29 const prefetchThread = 3 30 const checkInterval = 10 31 32 // statePrefetcher is a basic Prefetcher, which blindly executes a block on top 33 // of an arbitrary state with the goal of prefetching potentially useful state 34 // data from disk before the main block processor start executing. 35 type statePrefetcher struct { 36 config *params.ChainConfig // Chain configuration options 37 bc *BlockChain // Canonical block chain 38 engine consensus.Engine // Consensus engine used for block rewards 39 } 40 41 // NewStatePrefetcher initialises a new statePrefetcher. 42 func NewStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *statePrefetcher { 43 return &statePrefetcher{ 44 config: config, 45 bc: bc, 46 engine: engine, 47 } 48 } 49 50 // Prefetch processes the state changes according to the Ethereum rules by running 51 // the transaction messages using the statedb, but any changes are discarded. The 52 // only goal is to pre-cache transaction signatures and snapshot clean state. 53 func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) { 54 var ( 55 header = block.Header() 56 signer = types.MakeSigner(p.config, header.Number) 57 ) 58 transactions := block.Transactions() 59 sortTransactions := make([][]*types.Transaction, prefetchThread) 60 for i := 0; i < prefetchThread; i++ { 61 sortTransactions[i] = make([]*types.Transaction, 0, len(transactions)/prefetchThread) 62 } 63 for idx := range transactions { 64 threadIdx := idx % prefetchThread 65 sortTransactions[threadIdx] = append(sortTransactions[threadIdx], transactions[idx]) 66 } 67 // No need to execute the first batch, since the main processor will do it. 68 for i := 0; i < prefetchThread; i++ { 69 go func(idx int) { 70 newStatedb := statedb.Copy() 71 newStatedb.EnableWriteOnSharedStorage() 72 gaspool := new(GasPool).AddGas(block.GasLimit()) 73 blockContext := NewEVMBlockContext(header, p.bc, nil) 74 evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) 75 // Iterate over and process the individual transactions 76 for i, tx := range sortTransactions[idx] { 77 // If block precaching was interrupted, abort 78 if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { 79 return 80 } 81 // Convert the transaction into an executable message and pre-cache its sender 82 msg, err := tx.AsMessage(signer) 83 if err != nil { 84 return // Also invalid block, bail out 85 } 86 newStatedb.Prepare(tx.Hash(), header.Hash(), i) 87 precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm) 88 } 89 }(i) 90 } 91 } 92 93 // PrefetchMining processes the state changes according to the Ethereum rules by running 94 // the transaction messages using the statedb, but any changes are discarded. The 95 // only goal is to pre-cache transaction signatures and snapshot clean state. Only used for mining stage 96 func (p *statePrefetcher) PrefetchMining(txs *types.TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction) { 97 var signer = types.MakeSigner(p.config, header.Number) 98 99 txCh := make(chan *types.Transaction, 2*prefetchThread) 100 for i := 0; i < prefetchThread; i++ { 101 go func(startCh <-chan *types.Transaction, stopCh <-chan struct{}) { 102 idx := 0 103 newStatedb := statedb.Copy() 104 newStatedb.EnableWriteOnSharedStorage() 105 gaspool := new(GasPool).AddGas(gasLimit) 106 blockContext := NewEVMBlockContext(header, p.bc, nil) 107 evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) 108 // Iterate over and process the individual transactions 109 for { 110 select { 111 case tx := <-startCh: 112 // Convert the transaction into an executable message and pre-cache its sender 113 msg, err := tx.AsMessageNoNonceCheck(signer) 114 if err != nil { 115 return // Also invalid block, bail out 116 } 117 idx++ 118 newStatedb.Prepare(tx.Hash(), header.Hash(), idx) 119 precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm) 120 gaspool = new(GasPool).AddGas(gasLimit) 121 case <-stopCh: 122 return 123 } 124 } 125 }(txCh, interruptCh) 126 } 127 go func(txset *types.TransactionsByPriceAndNonce) { 128 count := 0 129 for { 130 select { 131 case <-interruptCh: 132 return 133 default: 134 if count++; count%checkInterval == 0 { 135 txset.Forward(*txCurr) 136 } 137 tx := txset.Peek() 138 if tx == nil { 139 return 140 } 141 txCh <- tx 142 txset.Shift() 143 144 } 145 } 146 }(txs) 147 } 148 149 // precacheTransaction attempts to apply a transaction to the given state database 150 // and uses the input parameters for its environment. The goal is not to execute 151 // the transaction successfully, rather to warm up touched data slots. 152 func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) { 153 // Update the evm with the new transaction context. 154 evm.Reset(NewEVMTxContext(msg), statedb) 155 // Add addresses to access list if applicable 156 ApplyMessage(evm, msg, gaspool) 157 }