github.com/ethereum/go-ethereum@v1.16.1/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 "bytes" 21 "runtime" 22 "sync/atomic" 23 24 "github.com/ethereum/go-ethereum/common" 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/params" 29 "golang.org/x/sync/errgroup" 30 ) 31 32 // statePrefetcher is a basic Prefetcher that executes transactions from a block 33 // on top of the parent state, aiming to prefetch potentially useful state data 34 // from disk. Transactions are executed in parallel to fully leverage the 35 // SSD's read performance. 36 type statePrefetcher struct { 37 config *params.ChainConfig // Chain configuration options 38 chain *HeaderChain // Canonical block chain 39 } 40 41 // newStatePrefetcher initialises a new statePrefetcher. 42 func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePrefetcher { 43 return &statePrefetcher{ 44 config: config, 45 chain: chain, 46 } 47 } 48 49 // Prefetch processes the state changes according to the Ethereum rules by running 50 // the transaction messages using the statedb, but any changes are discarded. The 51 // only goal is to warm the state caches. 52 func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) { 53 var ( 54 fails atomic.Int64 55 header = block.Header() 56 signer = types.MakeSigner(p.config, header.Number, header.Time) 57 workers errgroup.Group 58 reader = statedb.Reader() 59 ) 60 workers.SetLimit(max(1, 4*runtime.NumCPU()/5)) // Aggressively run the prefetching 61 62 // Iterate over and process the individual transactions 63 for i, tx := range block.Transactions() { 64 stateCpy := statedb.Copy() // closure 65 workers.Go(func() error { 66 // If block precaching was interrupted, abort 67 if interrupt != nil && interrupt.Load() { 68 return nil 69 } 70 // Preload the touched accounts and storage slots in advance 71 sender, err := types.Sender(signer, tx) 72 if err != nil { 73 fails.Add(1) 74 return nil 75 } 76 reader.Account(sender) 77 78 if tx.To() != nil { 79 account, _ := reader.Account(*tx.To()) 80 81 // Preload the contract code if the destination has non-empty code 82 if account != nil && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { 83 reader.Code(*tx.To(), common.BytesToHash(account.CodeHash)) 84 } 85 } 86 for _, list := range tx.AccessList() { 87 reader.Account(list.Address) 88 if len(list.StorageKeys) > 0 { 89 for _, slot := range list.StorageKeys { 90 reader.Storage(list.Address, slot) 91 } 92 } 93 } 94 // Execute the message to preload the implicit touched states 95 evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg) 96 97 // Convert the transaction into an executable message and pre-cache its sender 98 msg, err := TransactionToMessage(tx, signer, header.BaseFee) 99 if err != nil { 100 fails.Add(1) 101 return nil // Also invalid block, bail out 102 } 103 // Disable the nonce check 104 msg.SkipNonceChecks = true 105 106 stateCpy.SetTxContext(tx.Hash(), i) 107 108 // We attempt to apply a transaction. The goal is not to execute 109 // the transaction successfully, rather to warm up touched data slots. 110 if _, err := ApplyMessage(evm, msg, new(GasPool).AddGas(block.GasLimit())); err != nil { 111 fails.Add(1) 112 return nil // Ugh, something went horribly wrong, bail out 113 } 114 // Pre-load trie nodes for the intermediate root. 115 // 116 // This operation incurs significant memory allocations due to 117 // trie hashing and node decoding. TODO(rjl493456442): investigate 118 // ways to mitigate this overhead. 119 stateCpy.IntermediateRoot(true) 120 return nil 121 }) 122 } 123 workers.Wait() 124 125 blockPrefetchTxsValidMeter.Mark(int64(len(block.Transactions())) - fails.Load()) 126 blockPrefetchTxsInvalidMeter.Mark(fails.Load()) 127 return 128 }