mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 05:36:46 +00:00
feat: N-blocks gas limit
This commit is contained in:
parent
a94d9ee531
commit
f2c21d5814
10 changed files with 367 additions and 284 deletions
|
|
@ -439,7 +439,7 @@ func showMetrics() {
|
||||||
blockWriteTimer := metrics.GetOrRegisterResettingTimer("chain/write", nil)
|
blockWriteTimer := metrics.GetOrRegisterResettingTimer("chain/write", nil)
|
||||||
|
|
||||||
blockPrefetchExecuteTimer := metrics.GetOrRegisterResettingTimer("chain/prefetch/executes", nil)
|
blockPrefetchExecuteTimer := metrics.GetOrRegisterResettingTimer("chain/prefetch/executes", nil)
|
||||||
mgaspsHist := metrics.GetOrRegisterHistogram("chain/execution/mgasps", nil, metrics.NewUniformSample(2000))
|
chainMgaspsMeter := metrics.GetOrRegisterResettingTimer("chain/mgasps", nil)
|
||||||
|
|
||||||
// not important
|
// not important
|
||||||
fmt.Println("accountReadSingleTimer", accountReadSingleTimer.Total())
|
fmt.Println("accountReadSingleTimer", accountReadSingleTimer.Total())
|
||||||
|
|
@ -475,14 +475,16 @@ func showMetrics() {
|
||||||
fmt.Println("PrefetchBALtime: ", core.PrefetchBALTime)
|
fmt.Println("PrefetchBALtime: ", core.PrefetchBALTime)
|
||||||
fmt.Println("PrefetchMergeTime:", core.PrefetchMergeBALTime)
|
fmt.Println("PrefetchMergeTime:", core.PrefetchMergeBALTime)
|
||||||
fmt.Println("ParallelExeTime: ", core.ParallelExeTime)
|
fmt.Println("ParallelExeTime: ", core.ParallelExeTime)
|
||||||
fmt.Println("PostMergeTime: ", core.PostMergeTime)
|
|
||||||
fmt.Println("PrefetchTrieWallTime: ", core.PrefetchTrieTimer)
|
fmt.Println("PrefetchTrieWallTime: ", core.PrefetchTrieTimer)
|
||||||
// fmt.Println("PrefetchTrieCPUtime: ", state.PrefetchTrieCPUTime)
|
// fmt.Println("PrefetchTrieCPUtime: ", state.PrefetchTrieCPUTime)
|
||||||
|
|
||||||
|
// Sync state time
|
||||||
|
fmt.Println("PrefetchChTime: ", core.PrefetchChTime)
|
||||||
|
|
||||||
// total
|
// total
|
||||||
fmt.Println("blockInsertTimer", blockInsertTimer.Total())
|
fmt.Println("blockInsertTimer", blockInsertTimer.Total())
|
||||||
|
|
||||||
mgasps := mgaspsHist.Snapshot()
|
mgasps := chainMgaspsMeter.Snapshot()
|
||||||
fmt.Println("mgasps,mean,max,min:", int64(mgasps.Mean()), mgasps.Max(), mgasps.Min())
|
fmt.Println("mgasps,mean,max,min:", int64(mgasps.Mean()), mgasps.Max(), mgasps.Min())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
importBatchSize = 1
|
importBatchSize = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrImportInterrupted is returned when the user interrupts the import process.
|
// ErrImportInterrupted is returned when the user interrupts the import process.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -108,12 +107,13 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ancestor block must be known.
|
// Ancestor block must be known.
|
||||||
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
|
// Don't check intermediate blocks for N-blocks
|
||||||
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
// if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
|
||||||
return consensus.ErrUnknownAncestor
|
// if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||||
}
|
// return consensus.ErrUnknownAncestor
|
||||||
return consensus.ErrPrunedAncestor
|
// }
|
||||||
}
|
// return consensus.ErrPrunedAncestor
|
||||||
|
// }
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,19 +148,20 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||||
}
|
}
|
||||||
// Validate the parsed requests match the expected header value.
|
// Validate the parsed requests match the expected header value.
|
||||||
if header.RequestsHash != nil {
|
// if header.RequestsHash != nil {
|
||||||
reqhash := types.CalcRequestsHash(res.Requests)
|
// reqhash := types.CalcRequestsHash(res.Requests)
|
||||||
if reqhash != *header.RequestsHash {
|
// if reqhash != *header.RequestsHash {
|
||||||
return fmt.Errorf("invalid requests hash (remote: %x local: %x)", *header.RequestsHash, reqhash)
|
// return fmt.Errorf("invalid requests hash (remote: %x local: %x)", *header.RequestsHash, reqhash)
|
||||||
}
|
// }
|
||||||
} else if res.Requests != nil {
|
// } else if res.Requests != nil {
|
||||||
return errors.New("block has requests before prague fork")
|
// return errors.New("block has requests before prague fork")
|
||||||
}
|
// }
|
||||||
// Validate the state root against the received state root and throw
|
// Validate the state root against the received state root and throw
|
||||||
// an error if they don't match.
|
// an error if they don't match.
|
||||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
// Commented for N-blocks
|
||||||
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
|
// if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
||||||
}
|
// return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
|
||||||
|
// }
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,6 @@ var (
|
||||||
blockPrefetchTxsInvalidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/invalid", nil)
|
blockPrefetchTxsInvalidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/invalid", nil)
|
||||||
blockPrefetchTxsValidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/valid", nil)
|
blockPrefetchTxsValidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/valid", nil)
|
||||||
|
|
||||||
mgaspsHist = metrics.NewRegisteredHistogram("chain/execution/mgasps", nil, metrics.NewUniformSample(2000))
|
|
||||||
|
|
||||||
errInsertionInterrupted = errors.New("insertion is interrupted")
|
errInsertionInterrupted = errors.New("insertion is interrupted")
|
||||||
errChainStopped = errors.New("blockchain is stopped")
|
errChainStopped = errors.New("blockchain is stopped")
|
||||||
errInvalidOldChain = errors.New("invalid old chain")
|
errInvalidOldChain = errors.New("invalid old chain")
|
||||||
|
|
@ -1712,7 +1710,8 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
|
// _, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
|
||||||
|
_, n, err := bc.insertChainN(chain, true, false)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1946,6 +1945,9 @@ type blockProcessingResult struct {
|
||||||
procTime time.Duration
|
procTime time.Duration
|
||||||
status WriteStatus
|
status WriteStatus
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
// For merged N-blocks
|
||||||
|
receipts types.Receipts
|
||||||
|
logs []*types.Log
|
||||||
}
|
}
|
||||||
|
|
||||||
// processBlock executes and validates the given block. If there was no error
|
// processBlock executes and validates the given block. If there was no error
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package core
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -35,6 +36,13 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PrefetchBALTime = time.Duration(0)
|
||||||
|
PostMergeTime = time.Duration(0)
|
||||||
|
PrefetchTrieTimer = time.Duration(0)
|
||||||
|
PrefetchChTime = time.Duration(0)
|
||||||
|
)
|
||||||
|
|
||||||
func (bc *BlockChain) insertChainN(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
func (bc *BlockChain) insertChainN(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
||||||
// If the chain is terminating, don't even bother starting up.
|
// If the chain is terminating, don't even bother starting up.
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
|
|
@ -133,125 +141,228 @@ func (bc *BlockChain) insertChainN(chain types.Blocks, setHead bool, makeWitness
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
// Track the singleton witness from this chain insertion (if any)
|
// Track the singleton witness from this chain insertion (if any)
|
||||||
var witness *stateless.Witness
|
var (
|
||||||
|
witness *stateless.Witness
|
||||||
|
wg sync.WaitGroup
|
||||||
|
allReceipts types.Receipts
|
||||||
|
allLogs []*types.Log
|
||||||
|
totalGasUsed uint64
|
||||||
|
headerTime time.Duration
|
||||||
|
)
|
||||||
|
|
||||||
for ; block != nil && err == nil || errors.Is(err, ErrKnownBlock); block, err = it.next() {
|
// All blocks share the same stateDB to simulate commiting after processing multiple blocks
|
||||||
// If the chain is terminating, stop processing blocks
|
startBlock := block
|
||||||
if bc.insertStopped() {
|
endBlock := chain[len(chain)-1]
|
||||||
log.Debug("Abort during block processing")
|
parent := it.previous()
|
||||||
break
|
if parent == nil {
|
||||||
|
parent = bc.GetHeader(startBlock.ParentHash(), startBlock.NumberU64()-1)
|
||||||
|
}
|
||||||
|
statedb, err := state.New(parent.Root, bc.statedb)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("failed to initailzied state", "error", err, "root:", parent.Root.Hex())
|
||||||
|
}
|
||||||
|
// Prefetch pre-N-blocks state with merged pre-block BALs for N-blocks
|
||||||
|
// Here pre-block BALs are not merged, but we skipped allready fetch state to simulate merged operations.
|
||||||
|
prefetchStart := time.Now()
|
||||||
|
for _, blk := range chain {
|
||||||
|
statedb.PrefetchStateBAL(blk.NumberU64())
|
||||||
|
}
|
||||||
|
PrefetchBALTime += time.Since(prefetchStart)
|
||||||
|
|
||||||
|
// pre-block state for block N
|
||||||
|
prestateCh := make(chan *state.StateDB, len(chain))
|
||||||
|
|
||||||
|
wg.Add(2)
|
||||||
|
// process post-N-blocks state with merged post-BALs for N-blocks
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
mstart := time.Now()
|
||||||
|
// Stop prefetcher cause we'll directly fetch tries in parallel
|
||||||
|
statedb.StopPrefetcher()
|
||||||
|
|
||||||
|
for _, blk := range chain {
|
||||||
|
// We don't need to worry about blockNumber is not changed, because it'll be set during process block
|
||||||
|
prestateCh <- statedb.CopyState()
|
||||||
|
statedb.MergePostBalStates(blk.NumberU64())
|
||||||
}
|
}
|
||||||
// If the block is known (in the middle of the chain), it's a special case for
|
PostMergeTime += time.Since(mstart)
|
||||||
// Clique blocks where they can share state among each other, so importing an
|
|
||||||
// older block might complete the state of the subsequent one. In this case,
|
|
||||||
// just skip the block (we already validated it once fully (and crashed), since
|
|
||||||
// its header and body was already in the database). But if the corresponding
|
|
||||||
// snapshot layer is missing, forcibly rerun the execution to build it.
|
|
||||||
if bc.skipBlock(err, it) {
|
|
||||||
logger := log.Debug
|
|
||||||
if bc.chainConfig.Clique == nil {
|
|
||||||
logger = log.Warn
|
|
||||||
}
|
|
||||||
logger("Inserted known block", "number", block.Number(), "hash", block.Hash(),
|
|
||||||
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
|
|
||||||
"root", block.Root())
|
|
||||||
|
|
||||||
// Special case. Commit the empty receipt slice if we meet the known
|
// Prewarm the trie for future committing
|
||||||
// block in the middle. It can only happen in the clique chain. Whenever
|
pstart := time.Now()
|
||||||
// we insert blocks via `insertSideChain`, we only commit `td`, `header`
|
statedb.PrefetchTrie()
|
||||||
// and `body` if it's non-existent. Since we don't have receipts without
|
PrefetchTrieTimer += time.Since(pstart)
|
||||||
// reexecution, so nothing to commit. But if the sidechain will be adopted
|
}()
|
||||||
// as the canonical chain eventually, it needs to be reexecuted for missing
|
|
||||||
// state, but if it's this special case here(skip reexecution) we will lose
|
go func() {
|
||||||
// the empty receipt entry.
|
defer wg.Done()
|
||||||
if len(block.Transactions()) == 0 {
|
|
||||||
rawdb.WriteReceipts(bc.db, block.Hash(), block.NumberU64(), nil)
|
hstart := time.Now()
|
||||||
} else {
|
// Write all headers then parallel executing to validate post-tx BALs
|
||||||
log.Error("Please file an issue, skip known block execution without receipt",
|
bc.writeNBlockHeaders(chain)
|
||||||
"hash", block.Hash(), "number", block.NumberU64())
|
headerTime = time.Since(hstart)
|
||||||
|
|
||||||
|
for ; block != nil && err == nil || errors.Is(err, ErrKnownBlock); block, err = it.next() {
|
||||||
|
// If the chain is terminating, stop processing blocks
|
||||||
|
if bc.insertStopped() {
|
||||||
|
log.Debug("Abort during block processing")
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if err := bc.writeKnownBlock(block); err != nil {
|
// If the block is known (in the middle of the chain), it's a special case for
|
||||||
return nil, it.index, err
|
// Clique blocks where they can share state among each other, so importing an
|
||||||
|
// older block might complete the state of the subsequent one. In this case,
|
||||||
|
// just skip the block (we already validated it once fully (and crashed), since
|
||||||
|
// its header and body was already in the database). But if the corresponding
|
||||||
|
// snapshot layer is missing, forcibly rerun the execution to build it.
|
||||||
|
if bc.skipBlock(err, it) {
|
||||||
|
logger := log.Debug
|
||||||
|
if bc.chainConfig.Clique == nil {
|
||||||
|
logger = log.Warn
|
||||||
|
}
|
||||||
|
logger("Inserted known block", "number", block.Number(), "hash", block.Hash(),
|
||||||
|
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
|
||||||
|
"root", block.Root())
|
||||||
|
|
||||||
|
// Special case. Commit the empty receipt slice if we meet the known
|
||||||
|
// block in the middle. It can only happen in the clique chain. Whenever
|
||||||
|
// we insert blocks via `insertSideChain`, we only commit `td`, `header`
|
||||||
|
// and `body` if it's non-existent. Since we don't have receipts without
|
||||||
|
// reexecution, so nothing to commit. But if the sidechain will be adopted
|
||||||
|
// as the canonical chain eventually, it needs to be reexecuted for missing
|
||||||
|
// state, but if it's this special case here(skip reexecution) we will lose
|
||||||
|
// the empty receipt entry.
|
||||||
|
if len(block.Transactions()) == 0 {
|
||||||
|
rawdb.WriteReceipts(bc.db, block.Hash(), block.NumberU64(), nil)
|
||||||
|
} else {
|
||||||
|
log.Error("Please file an issue, skip known block execution without receipt",
|
||||||
|
"hash", block.Hash(), "number", block.NumberU64())
|
||||||
|
}
|
||||||
|
if err := bc.writeKnownBlock(block); err != nil {
|
||||||
|
witness = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stats.processed++
|
||||||
|
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
||||||
|
bc.logger.OnSkippedBlock(tracing.BlockEvent{
|
||||||
|
Block: block,
|
||||||
|
Finalized: bc.CurrentFinalBlock(),
|
||||||
|
Safe: bc.CurrentSafeBlock(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// We can assume that logs are empty here, since the only way for consecutive
|
||||||
|
// Clique blocks to have the same state is if there are no transactions.
|
||||||
|
lastCanon = block
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
// Retrieve the parent block and it's state to execute on top
|
||||||
|
parent := it.previous()
|
||||||
|
if parent == nil {
|
||||||
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
}
|
||||||
|
// The traced section of block import.
|
||||||
|
pstart := time.Now()
|
||||||
|
statedbForBlock := <-prestateCh
|
||||||
|
PrefetchChTime += time.Since(pstart)
|
||||||
|
|
||||||
|
res, err := bc.processBlockWithState(parent.Root, block, setHead, makeWitness && len(chain) == 1, statedbForBlock)
|
||||||
|
if err != nil {
|
||||||
|
witness = nil
|
||||||
|
log.Crit("Failed to processBlock", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect logs and receipts for N-blocks
|
||||||
|
allReceipts = append(allReceipts, res.receipts...)
|
||||||
|
allLogs = append(allLogs, res.logs...)
|
||||||
|
totalGasUsed += res.usedGas
|
||||||
|
// Report the import stats before returning the various results
|
||||||
stats.processed++
|
stats.processed++
|
||||||
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
stats.usedGas += res.usedGas
|
||||||
bc.logger.OnSkippedBlock(tracing.BlockEvent{
|
witness = res.witness
|
||||||
Block: block,
|
|
||||||
Finalized: bc.CurrentFinalBlock(),
|
var snapDiffItems, snapBufItems common.StorageSize
|
||||||
Safe: bc.CurrentSafeBlock(),
|
if bc.snaps != nil {
|
||||||
})
|
snapDiffItems, snapBufItems = bc.snaps.Size()
|
||||||
|
}
|
||||||
|
trieDiffNodes, trieBufNodes, _ := bc.triedb.Size()
|
||||||
|
stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, setHead)
|
||||||
|
|
||||||
|
// Print confirmation that a future fork is scheduled, but not yet active.
|
||||||
|
bc.logForkReadiness(block)
|
||||||
|
|
||||||
|
if !setHead {
|
||||||
|
// After merge we expect few side chains. Simply count
|
||||||
|
// all blocks the CL gives us for GC processing time
|
||||||
|
bc.gcproc += res.procTime
|
||||||
|
witness = nil
|
||||||
|
return // Direct block insertion of a single block
|
||||||
}
|
}
|
||||||
// We can assume that logs are empty here, since the only way for consecutive
|
|
||||||
// Clique blocks to have the same state is if there are no transactions.
|
|
||||||
lastCanon = block
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
// Retrieve the parent block and it's state to execute on top
|
|
||||||
parent := it.previous()
|
|
||||||
if parent == nil {
|
|
||||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
|
||||||
}
|
|
||||||
// The traced section of block import.
|
|
||||||
start := time.Now()
|
|
||||||
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, it.index, err
|
|
||||||
}
|
|
||||||
// Report the import stats before returning the various results
|
|
||||||
stats.processed++
|
|
||||||
stats.usedGas += res.usedGas
|
|
||||||
witness = res.witness
|
|
||||||
|
|
||||||
var snapDiffItems, snapBufItems common.StorageSize
|
stats.ignored += it.remaining()
|
||||||
if bc.snaps != nil {
|
}()
|
||||||
snapDiffItems, snapBufItems = bc.snaps.Size()
|
wg.Wait()
|
||||||
}
|
|
||||||
trieDiffNodes, trieBufNodes, _ := bc.triedb.Size()
|
|
||||||
stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, setHead)
|
|
||||||
|
|
||||||
// Print confirmation that a future fork is scheduled, but not yet active.
|
// Validate stateRoot for N-blocks at once.
|
||||||
bc.logForkReadiness(block)
|
xvtime := time.Now()
|
||||||
|
header := endBlock.Header()
|
||||||
|
if root := statedb.IntermediateRoot(true); header.Root != root {
|
||||||
|
log.Crit("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
|
||||||
|
}
|
||||||
|
blockCrossValidationTimer.Update(time.Since(xvtime))
|
||||||
|
|
||||||
if !setHead {
|
// Commit N-blocks together to the chain and get the status.
|
||||||
// After merge we expect few side chains. Simply count
|
var (
|
||||||
// all blocks the CL gives us for GC processing time
|
wstart = time.Now()
|
||||||
bc.gcproc += res.procTime
|
status WriteStatus
|
||||||
return witness, it.index, nil // Direct block insertion of a single block
|
)
|
||||||
}
|
if !setHead {
|
||||||
switch res.status {
|
// Don't set the head, only insert the block
|
||||||
case CanonStatTy:
|
err = bc.writeNBlocksWithState(startBlock, endBlock, allReceipts, statedb)
|
||||||
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
|
} else {
|
||||||
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
|
status, err = bc.writeNBlocksAndSetHead(startBlock, endBlock, allReceipts, allLogs, statedb, false)
|
||||||
"elapsed", common.PrettyDuration(time.Since(start)),
|
}
|
||||||
"root", block.Root())
|
if err != nil {
|
||||||
|
return nil, it.index, err
|
||||||
lastCanon = block
|
|
||||||
|
|
||||||
// Only count canonical blocks for GC processing time
|
|
||||||
bc.gcproc += res.procTime
|
|
||||||
|
|
||||||
case SideStatTy:
|
|
||||||
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
|
|
||||||
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
|
||||||
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
|
|
||||||
"root", block.Root())
|
|
||||||
|
|
||||||
default:
|
|
||||||
// This in theory is impossible, but lets be nice to our future selves and leave
|
|
||||||
// a log, instead of trying to track down blocks imports that don't emit logs.
|
|
||||||
log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(),
|
|
||||||
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
|
||||||
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
|
|
||||||
"root", block.Root())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.ignored += it.remaining()
|
// Update the metrics touched during N-blocks commit
|
||||||
|
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
||||||
|
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
|
||||||
|
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
|
||||||
|
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
||||||
|
|
||||||
|
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits + headerTime)
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case CanonStatTy:
|
||||||
|
log.Debug("Inserted new blocks", "numberStart", startBlock.Number(), "numberEnd", endBlock.Number(),
|
||||||
|
"elapsed", common.PrettyDuration(time.Since(prefetchStart)),
|
||||||
|
"root", endBlock.Root())
|
||||||
|
|
||||||
|
lastCanon = endBlock
|
||||||
|
|
||||||
|
default:
|
||||||
|
// This in theory is impossible, but lets be nice to our future selves and leave
|
||||||
|
// a log, instead of trying to track down blocks imports that don't emit logs.
|
||||||
|
log.Warn("Inserted block with unknown status", "number", endBlock.Number(), "hash", endBlock.Hash(),
|
||||||
|
"diff", endBlock.Difficulty(), "elapsed", common.PrettyDuration(time.Since(prefetchStart)),
|
||||||
|
"txs", len(endBlock.Transactions()), "gas", endBlock.GasUsed(), "uncles", len(endBlock.Uncles()),
|
||||||
|
"root", endBlock.Root())
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Since(prefetchStart) + 1 // prevent zero division
|
||||||
|
blockInsertTimer.Update(elapsed)
|
||||||
|
|
||||||
|
// TODO(rjl493456442) generalize the ResettingTimer
|
||||||
|
mgasps := float64(totalGasUsed) * 1000 / float64(elapsed)
|
||||||
|
chainMgaspsMeter.Update(time.Duration(mgasps))
|
||||||
|
|
||||||
return witness, it.index, err
|
return witness, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) writeNBlocksWithState(block *types.Block, receipts []*types.Receipt, statedb *state.StateDB) error {
|
func (bc *BlockChain) writeNBlocksWithState(startBlock, endBlock *types.Block, receipts []*types.Receipt, statedb *state.StateDB) error {
|
||||||
if !bc.HasHeader(block.ParentHash(), block.NumberU64()-1) {
|
if !bc.HasHeader(startBlock.ParentHash(), startBlock.NumberU64()-1) {
|
||||||
return consensus.ErrUnknownAncestor
|
return consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
// Irrelevant of the canonical status, write the block itself to the database.
|
// Irrelevant of the canonical status, write the block itself to the database.
|
||||||
|
|
@ -259,14 +370,14 @@ func (bc *BlockChain) writeNBlocksWithState(block *types.Block, receipts []*type
|
||||||
// Note all the components of block(hash->number map, header, body, receipts)
|
// Note all the components of block(hash->number map, header, body, receipts)
|
||||||
// should be written atomically. BlockBatch is used for containing all components.
|
// should be written atomically. BlockBatch is used for containing all components.
|
||||||
blockBatch := bc.db.NewBatch()
|
blockBatch := bc.db.NewBatch()
|
||||||
rawdb.WriteBlock(blockBatch, block)
|
rawdb.WriteBlock(blockBatch, endBlock)
|
||||||
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
|
rawdb.WriteReceipts(blockBatch, endBlock.Hash(), endBlock.NumberU64(), receipts)
|
||||||
rawdb.WritePreimages(blockBatch, statedb.Preimages())
|
rawdb.WritePreimages(blockBatch, statedb.Preimages())
|
||||||
if err := blockBatch.Write(); err != nil {
|
if err := blockBatch.Write(); err != nil {
|
||||||
log.Crit("Failed to write block into disk", "err", err)
|
log.Crit("Failed to write block into disk", "err", err)
|
||||||
}
|
}
|
||||||
// Commit all cached state changes into underlying memory database.
|
// Commit all cached state changes into underlying memory database.
|
||||||
root, err := statedb.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.chainConfig.IsCancun(block.Number(), block.Time()))
|
root, err := statedb.Commit(endBlock.NumberU64(), bc.chainConfig.IsEIP158(endBlock.Number()), bc.chainConfig.IsCancun(endBlock.Number(), endBlock.Time()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -281,10 +392,10 @@ func (bc *BlockChain) writeNBlocksWithState(block *types.Block, receipts []*type
|
||||||
}
|
}
|
||||||
// Full but not archive node, do proper garbage collection
|
// Full but not archive node, do proper garbage collection
|
||||||
bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||||
bc.triegc.Push(root, -int64(block.NumberU64()))
|
bc.triegc.Push(root, -int64(endBlock.NumberU64()))
|
||||||
|
|
||||||
// Flush limits are not considered for the first TriesInMemory blocks.
|
// Flush limits are not considered for the first TriesInMemory blocks.
|
||||||
current := block.NumberU64()
|
current := endBlock.NumberU64()
|
||||||
if current <= state.TriesInMemory {
|
if current <= state.TriesInMemory {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -330,23 +441,34 @@ func (bc *BlockChain) writeNBlocksWithState(block *types.Block, receipts []*type
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) writeNBlocksAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
// Due to blockHash opCode, blockHeaders must be written to DB before commit.
|
||||||
if err := bc.writeBlockWithState(block, receipts, state); err != nil {
|
func (bc *BlockChain) writeNBlockHeaders(chain types.Blocks) {
|
||||||
|
blockBatch := bc.db.NewBatch()
|
||||||
|
for _, block := range chain {
|
||||||
|
rawdb.WriteHeader(blockBatch, block.Header())
|
||||||
|
}
|
||||||
|
if err := blockBatch.Write(); err != nil {
|
||||||
|
log.Crit("Failed to write block into disk", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *BlockChain) writeNBlocksAndSetHead(startBlock, endBlock *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||||
|
if err := bc.writeBlockWithState(endBlock, receipts, state); err != nil {
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
}
|
}
|
||||||
currentBlock := bc.CurrentBlock()
|
currentBlock := bc.CurrentBlock()
|
||||||
|
|
||||||
// Reorganise the chain if the parent is not the head block
|
// Reorganise the chain if the parent is not the head block
|
||||||
if block.ParentHash() != currentBlock.Hash() {
|
if startBlock.ParentHash() != currentBlock.Hash() {
|
||||||
if err := bc.reorg(currentBlock, block.Header()); err != nil {
|
if err := bc.reorg(currentBlock, endBlock.Header()); err != nil {
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set new head.
|
// Set new head.
|
||||||
bc.writeHeadBlock(block)
|
bc.writeHeadBlock(endBlock)
|
||||||
|
|
||||||
bc.chainFeed.Send(ChainEvent{Header: block.Header()})
|
bc.chainFeed.Send(ChainEvent{Header: endBlock.Header()})
|
||||||
if len(logs) > 0 {
|
if len(logs) > 0 {
|
||||||
bc.logsFeed.Send(logs)
|
bc.logsFeed.Send(logs)
|
||||||
}
|
}
|
||||||
|
|
@ -356,33 +478,24 @@ func (bc *BlockChain) writeNBlocksAndSetHead(block *types.Block, receipts []*typ
|
||||||
// we will fire an accumulated ChainHeadEvent and disable fire
|
// we will fire an accumulated ChainHeadEvent and disable fire
|
||||||
// event here.
|
// event here.
|
||||||
if emitHeadEvent {
|
if emitHeadEvent {
|
||||||
bc.chainHeadFeed.Send(ChainHeadEvent{Header: block.Header()})
|
bc.chainHeadFeed.Send(ChainHeadEvent{Header: endBlock.Header()})
|
||||||
}
|
}
|
||||||
return CanonStatTy, nil
|
return CanonStatTy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) processBlockWithState(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
|
func (bc *BlockChain) processBlockWithState(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool, statedb *state.StateDB) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
statedb *state.StateDB
|
|
||||||
interrupt atomic.Bool
|
interrupt atomic.Bool
|
||||||
)
|
)
|
||||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
if !bc.cfg.NoPrefetch {
|
||||||
|
panic("Must enable --cache.noprefetch to perf N-blocks")
|
||||||
|
}
|
||||||
|
|
||||||
if bc.cfg.NoPrefetch {
|
if bc.cfg.NoPrefetch {
|
||||||
statedb, err = state.New(parentRoot, bc.statedb)
|
// Use stateDB snapshotted from statedb.MergePostBalStates
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// reader, err := bc.statedb.ReaderWithCache(parentRoot)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// statedb, err = state.NewWithReader(parentRoot, bc.statedb, reader)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("prefetch enabled===========================")
|
fmt.Println("prefetch enabled===========================")
|
||||||
// If prefetching is enabled, run that against the current state to pre-cache
|
// If prefetching is enabled, run that against the current state to pre-cache
|
||||||
|
|
@ -443,8 +556,9 @@ func (bc *BlockChain) processBlockWithState(parentRoot common.Hash, block *types
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb.StartPrefetcher("chain", witness)
|
// Don't start the prefetcher, as it can significantly degrade performance. We've already prefetched the trie in insertChainN.
|
||||||
defer statedb.StopPrefetcher()
|
// statedb.StartPrefetcher("chain", witness)
|
||||||
|
// defer statedb.StopPrefetcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||||
|
|
@ -525,38 +639,10 @@ func (bc *BlockChain) processBlockWithState(parentRoot common.Hash, block *types
|
||||||
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
||||||
blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation
|
blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation
|
||||||
|
|
||||||
// Write the block to the chain and get the status.
|
|
||||||
var (
|
|
||||||
wstart = time.Now()
|
|
||||||
status WriteStatus
|
|
||||||
)
|
|
||||||
if !setHead {
|
|
||||||
// Don't set the head, only insert the block
|
|
||||||
err = bc.writeBlockWithState(block, res.Receipts, statedb)
|
|
||||||
} else {
|
|
||||||
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Update the metrics touched during block commit
|
|
||||||
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
|
||||||
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
|
|
||||||
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
|
|
||||||
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
|
||||||
|
|
||||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
|
||||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
|
||||||
blockInsertTimer.Update(elapsed)
|
|
||||||
|
|
||||||
// TODO(rjl493456442) generalize the ResettingTimer
|
|
||||||
mgasps := float64(res.GasUsed) * 1000 / float64(elapsed)
|
|
||||||
chainMgaspsMeter.Update(time.Duration(mgasps))
|
|
||||||
|
|
||||||
return &blockProcessingResult{
|
return &blockProcessingResult{
|
||||||
usedGas: res.GasUsed,
|
usedGas: res.GasUsed,
|
||||||
procTime: proctime,
|
procTime: proctime,
|
||||||
status: status,
|
status: CanonStatTy, // Assue write status is always correct
|
||||||
witness: witness,
|
witness: witness,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,6 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
||||||
mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
|
mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
|
||||||
)
|
)
|
||||||
// Update the Mgas per second gauge
|
|
||||||
mgaspsHist.Update(int64(mgasps))
|
|
||||||
|
|
||||||
// If we're at the last block of the batch or report period reached, log
|
// If we're at the last block of the batch or report period reached, log
|
||||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -17,11 +16,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PrefetchBALTime = time.Duration(0)
|
|
||||||
PrefetchMergeBALTime = time.Duration(0)
|
|
||||||
ParallelExeTime = time.Duration(0)
|
ParallelExeTime = time.Duration(0)
|
||||||
PostMergeTime = time.Duration(0)
|
PrefetchMergeBALTime = time.Duration(0)
|
||||||
PrefetchTrieTimer = time.Duration(0)
|
EmptyStatedb = state.NewEmptyDB()
|
||||||
)
|
)
|
||||||
|
|
||||||
type ParallelStateProcessor struct {
|
type ParallelStateProcessor struct {
|
||||||
|
|
@ -43,10 +40,8 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
context vm.BlockContext
|
context vm.BlockContext
|
||||||
gp = new(GasPool).AddGas(block.GasLimit())
|
gp = new(GasPool).AddGas(block.GasLimit())
|
||||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
initialdb = statedb.Copy()
|
initialdb = EmptyStatedb
|
||||||
postState *state.StateDB
|
|
||||||
|
|
||||||
wg sync.WaitGroup
|
|
||||||
result *ProcessResult
|
result *ProcessResult
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
@ -56,14 +51,9 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
misc.ApplyDAOHardFork(statedb)
|
misc.ApplyDAOHardFork(statedb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if preStateType == BALPreState {
|
preCalPostStart := time.Now()
|
||||||
// copy initialdb before PrefetchStateBAL to avoid redundant copy
|
statedb.PreComputePostState(block.NumberU64(), runtime.NumCPU()/2)
|
||||||
start := time.Now()
|
PrefetchMergeBALTime += time.Since(preCalPostStart)
|
||||||
// Must prefetch bal before syscall to avoid overriding syscall's state, thus merkle root might mismatch
|
|
||||||
statedb.PrefetchStateBAL(block.NumberU64())
|
|
||||||
// statedb.SetBlocknumber(block.NumberU64())
|
|
||||||
PrefetchBALTime += time.Since(start)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply pre-execution system calls.
|
// Apply pre-execution system calls.
|
||||||
var tracingStateDB = vm.StateDB(statedb)
|
var tracingStateDB = vm.StateDB(statedb)
|
||||||
|
|
@ -80,43 +70,9 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
ProcessParentBlockHash(block.ParentHash(), evm)
|
ProcessParentBlockHash(block.ParentHash(), evm)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(2)
|
exeStart := time.Now()
|
||||||
go func() {
|
result, err = p.executeParallel(block, statedb, cfg, gp, signer, context, initialdb)
|
||||||
defer wg.Done()
|
ParallelExeTime += time.Since(exeStart)
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
postState = statedb.CopyState()
|
|
||||||
// Stop prefetcher cause we'll directly fetch tries in parallel
|
|
||||||
postState.StopPrefetcher()
|
|
||||||
|
|
||||||
postState.MergePostBalStates()
|
|
||||||
PostMergeTime += time.Since(start)
|
|
||||||
|
|
||||||
// Prewarm the updating trie
|
|
||||||
start = time.Now()
|
|
||||||
postState.PrefetchTrie()
|
|
||||||
PrefetchTrieTimer += time.Since(start)
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
preCalPostStart := time.Now()
|
|
||||||
statedb.PreComputePostState(block.NumberU64(), runtime.NumCPU()/2)
|
|
||||||
PrefetchMergeBALTime += time.Since(preCalPostStart)
|
|
||||||
|
|
||||||
exeStart := time.Now()
|
|
||||||
result, err = p.executeParallel(block, statedb, cfg, gp, signer, context, initialdb)
|
|
||||||
ParallelExeTime += time.Since(exeStart)
|
|
||||||
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
// Last tx alreadly includes the state change in requests after all txs
|
|
||||||
if preStateType == BALPreState {
|
|
||||||
*statedb = *postState
|
|
||||||
}
|
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,15 +91,16 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
|
||||||
)
|
)
|
||||||
|
|
||||||
// leave some cpus for prefetching
|
// leave some cpus for prefetching
|
||||||
workers.SetLimit(runtime.NumCPU() / 2)
|
|
||||||
|
|
||||||
switch preStateType {
|
switch preStateType {
|
||||||
case BALPreState:
|
case BALPreState:
|
||||||
{
|
{
|
||||||
|
workers.SetLimit(runtime.NumCPU() / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SeqPreState: // must set workers limit = 1
|
case SeqPreState: // must set workers limit = 1
|
||||||
{
|
{
|
||||||
|
workers.SetLimit(1)
|
||||||
preStatedb := statedb.Copy()
|
preStatedb := statedb.Copy()
|
||||||
gpcp := *gp
|
gpcp := *gp
|
||||||
preStateProvider = &SequentialPrestateProvider{
|
preStateProvider = &SequentialPrestateProvider{
|
||||||
|
|
@ -297,7 +254,7 @@ func ApplyTransactionWithParallelEVM(msg *Message, gp *GasPool, statedb *state.S
|
||||||
|
|
||||||
// Merge the tx-local access event into the "block-local" one, in order to collect
|
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||||
// all values, so that the witness can be built.
|
// all values, so that the witness can be built.
|
||||||
if statedb.GetTrie().IsVerkle() {
|
if statedb.GetTrie() != nil && statedb.GetTrie().IsVerkle() {
|
||||||
statedb.AccessEvents().Merge(evm.AccessEvents)
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,23 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
return NewWithReader(root, db, reader)
|
return NewWithReader(root, db, reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empty statedb without tr
|
||||||
|
func NewEmptyDB() *StateDB {
|
||||||
|
sdb := &StateDB{
|
||||||
|
stateObjects: make(map[common.Address]*stateObject),
|
||||||
|
stateObjectsDestruct: make(map[common.Address]*stateObject),
|
||||||
|
mutations: make(map[common.Address]*mutation),
|
||||||
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
|
preimages: make(map[common.Hash][]byte),
|
||||||
|
journal: newJournal(),
|
||||||
|
updateJournal: newJournal(),
|
||||||
|
accessList: newAccessList(),
|
||||||
|
transientStorage: newTransientStorage(),
|
||||||
|
postStates: make(map[int]*StateDB),
|
||||||
|
}
|
||||||
|
return sdb
|
||||||
|
}
|
||||||
|
|
||||||
// NewWithReader creates a new state for the specified state root. Unlike New,
|
// NewWithReader creates a new state for the specified state root. Unlike New,
|
||||||
// this function accepts an additional Reader which is bound to the given root.
|
// this function accepts an additional Reader which is bound to the given root.
|
||||||
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
|
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
|
||||||
|
|
@ -357,7 +374,7 @@ func (s *StateDB) PrefetchStateBAL(blockNumber uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) prefetchBalPreblockKeys() {
|
func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
log.Info("PrefetchBalPreblockKeys...")
|
log.Info("PrefetchBalPreblockKeys for:", "block:", s.blockNumber)
|
||||||
type StorageKV struct {
|
type StorageKV struct {
|
||||||
addr *common.Address
|
addr *common.Address
|
||||||
key *common.Hash
|
key *common.Hash
|
||||||
|
|
@ -370,12 +387,17 @@ func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
workers errgroup.Group
|
workers errgroup.Group
|
||||||
)
|
)
|
||||||
|
|
||||||
workers.SetLimit(runtime.NumCPU() - 2)
|
workers.SetLimit(runtime.NumCPU() / 2)
|
||||||
// Fetch pre-block acccount state
|
// Fetch pre-block acccount state
|
||||||
accounts := make(chan *stateObject, len(preBal))
|
accounts := make(chan *stateObject, len(preBal))
|
||||||
|
lenAcctCh := 0
|
||||||
for _, acl := range preBal {
|
for _, acl := range preBal {
|
||||||
addr := acl.Address
|
addr := acl.Address
|
||||||
lenMaxSlots += len(acl.StorageKeys)
|
lenMaxSlots += len(acl.StorageKeys)
|
||||||
|
if s.stateObjects[addr] != nil { // Skip already fetched pre-block bals or newObject will override existing storage!
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lenAcctCh++
|
||||||
|
|
||||||
workers.Go(func() error {
|
workers.Go(func() error {
|
||||||
acctBal, err := s.reader.Account(addr)
|
acctBal, err := s.reader.Account(addr)
|
||||||
|
|
@ -393,9 +415,8 @@ func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
workers.Wait()
|
|
||||||
|
|
||||||
for range len(preBal) {
|
for range lenAcctCh {
|
||||||
obj := <-accounts
|
obj := <-accounts
|
||||||
// must set it first to avoid accounts read later in storage fetching
|
// must set it first to avoid accounts read later in storage fetching
|
||||||
if obj != nil {
|
if obj != nil {
|
||||||
|
|
@ -404,7 +425,6 @@ func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
}
|
}
|
||||||
|
|
||||||
close(accounts)
|
close(accounts)
|
||||||
|
|
||||||
// Fetch pre-block storage state
|
// Fetch pre-block storage state
|
||||||
storages := make(chan *StorageKV, lenMaxSlots)
|
storages := make(chan *StorageKV, lenMaxSlots)
|
||||||
for _, acl := range preBal {
|
for _, acl := range preBal {
|
||||||
|
|
@ -417,9 +437,13 @@ func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
lenSlots += len(acl.StorageKeys)
|
|
||||||
|
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
|
// Skip already fetched slots to simulate merged N-blocks BALs
|
||||||
|
if _, cached := obj.originStorage[key]; cached {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lenSlots++
|
||||||
|
|
||||||
workers.Go(func() error {
|
workers.Go(func() error {
|
||||||
val, err := s.reader.Storage(addr, key)
|
val, err := s.reader.Storage(addr, key)
|
||||||
kv := &StorageKV{&addr, &key, &val}
|
kv := &StorageKV{&addr, &key, &val}
|
||||||
|
|
@ -433,7 +457,6 @@ func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
workers.Wait()
|
|
||||||
|
|
||||||
for range lenSlots {
|
for range lenSlots {
|
||||||
kv := <-storages
|
kv := <-storages
|
||||||
|
|
@ -443,7 +466,6 @@ func (s *StateDB) prefetchBalPreblockKeys() {
|
||||||
s.setStateObject(account)
|
s.setStateObject(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(storages)
|
close(storages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,16 +705,17 @@ func (s *StateDB) MergeState(entries []JournalEntry) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) MergePostBalStates() {
|
func (s *StateDB) MergePostBalStates(blockNumber uint64) {
|
||||||
if balType != BalPreblockKeysPostValues {
|
if balType != BalPreblockKeysPostValues {
|
||||||
panic("MergePostBal is only supported with BalPreblockKeysPostValues")
|
panic("MergePostBal is only supported with BalPreblockKeysPostValues")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
postBal = AllBlockTxPostValues[s.blockNumber]
|
postBal = AllBlockTxPostValues[blockNumber]
|
||||||
)
|
)
|
||||||
|
s.blockNumber = blockNumber
|
||||||
|
|
||||||
// -1 and len(postBal)-1 (not used) are syscalls pre and post all txs.
|
// -1 and len(postBal)-1 (used for N-blocks) are syscalls pre and post all txs.
|
||||||
for txIndex := -1; txIndex < len(postBal)-1; txIndex++ {
|
for txIndex := -1; txIndex < len(postBal); txIndex++ {
|
||||||
postVals := postBal[txIndex]
|
postVals := postBal[txIndex]
|
||||||
|
|
||||||
for addr, acct := range postVals {
|
for addr, acct := range postVals {
|
||||||
|
|
@ -1241,23 +1264,26 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) CopyState() *StateDB {
|
func (s *StateDB) CopyState() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Only copy all the basic fields for N-blocks state, initialize the memory ones
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
prefetcher: s.prefetcher,
|
// prefetcher: s.prefetcher, // don't need prefetcher for BAL
|
||||||
trie: s.trie,
|
trie: s.trie,
|
||||||
reader: s.reader,
|
reader: s.reader,
|
||||||
originalRoot: s.originalRoot,
|
originalRoot: s.originalRoot,
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
||||||
mutations: make(map[common.Address]*mutation, len(s.mutations)),
|
// mutations: make(map[common.Address]*mutation, len(s.mutations)),
|
||||||
dbErr: s.dbErr,
|
mutations: make(map[common.Address]*mutation),
|
||||||
refund: s.refund,
|
dbErr: s.dbErr,
|
||||||
thash: s.thash,
|
refund: s.refund,
|
||||||
txIndex: s.txIndex,
|
thash: s.thash,
|
||||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
txIndex: s.txIndex,
|
||||||
logSize: s.logSize,
|
// logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
preimages: maps.Clone(s.preimages),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
|
|
||||||
|
logSize: s.logSize,
|
||||||
|
// preimages: maps.Clone(s.preimages),
|
||||||
|
|
||||||
// Do we need to copy the access list and transient storage?
|
// Do we need to copy the access list and transient storage?
|
||||||
// In practice: No. At the start of a transaction, these two lists are empty.
|
// In practice: No. At the start of a transaction, these two lists are empty.
|
||||||
|
|
@ -1265,9 +1291,12 @@ func (s *StateDB) CopyState() *StateDB {
|
||||||
// in the middle of a transaction. However, it doesn't cost us much to copy
|
// in the middle of a transaction. However, it doesn't cost us much to copy
|
||||||
// empty lists, so we do it anyway to not blow up if we ever decide copy them
|
// empty lists, so we do it anyway to not blow up if we ever decide copy them
|
||||||
// in the middle of a transaction.
|
// in the middle of a transaction.
|
||||||
accessList: s.accessList.Copy(),
|
// accessList: s.accessList.Copy(),
|
||||||
transientStorage: s.transientStorage.Copy(),
|
// transientStorage: s.transientStorage.Copy(),
|
||||||
journal: s.journal.copy(),
|
// journal: s.journal.copy(),
|
||||||
|
accessList: newAccessList(),
|
||||||
|
transientStorage: newTransientStorage(),
|
||||||
|
journal: newJournal(),
|
||||||
// The update journal is not copies to avoid duplicated updates in later transactions.
|
// The update journal is not copies to avoid duplicated updates in later transactions.
|
||||||
blockNumber: s.blockNumber,
|
blockNumber: s.blockNumber,
|
||||||
updateJournal: newJournal(),
|
updateJournal: newJournal(),
|
||||||
|
|
@ -1277,9 +1306,9 @@ func (s *StateDB) CopyState() *StateDB {
|
||||||
if s.witness != nil {
|
if s.witness != nil {
|
||||||
state.witness = s.witness.Copy()
|
state.witness = s.witness.Copy()
|
||||||
}
|
}
|
||||||
if s.accessEvents != nil {
|
// if s.accessEvents != nil {
|
||||||
state.accessEvents = s.accessEvents.Copy()
|
// state.accessEvents = s.accessEvents.Copy()
|
||||||
}
|
// }
|
||||||
// Deep copy cached state objects.
|
// Deep copy cached state objects.
|
||||||
for addr, obj := range s.stateObjects {
|
for addr, obj := range s.stateObjects {
|
||||||
state.stateObjects[addr] = obj.simpleCopy(state)
|
state.stateObjects[addr] = obj.simpleCopy(state)
|
||||||
|
|
@ -1289,18 +1318,19 @@ func (s *StateDB) CopyState() *StateDB {
|
||||||
state.stateObjectsDestruct[addr] = obj.simpleCopy(state)
|
state.stateObjectsDestruct[addr] = obj.simpleCopy(state)
|
||||||
}
|
}
|
||||||
// Deep copy the object state markers.
|
// Deep copy the object state markers.
|
||||||
for addr, op := range s.mutations {
|
// for addr, op := range s.mutations {
|
||||||
state.mutations[addr] = op.copy()
|
// state.mutations[addr] = op.copy()
|
||||||
}
|
// }
|
||||||
// Deep copy the logs occurred in the scope of block
|
// Deep copy the logs occurred in the scope of block
|
||||||
for hash, logs := range s.logs {
|
// for hash, logs := range s.logs {
|
||||||
cpy := make([]*types.Log, len(logs))
|
// cpy := make([]*types.Log, len(logs))
|
||||||
for i, l := range logs {
|
// for i, l := range logs {
|
||||||
cpy[i] = new(types.Log)
|
// cpy[i] = new(types.Log)
|
||||||
*cpy[i] = *l
|
// *cpy[i] = *l
|
||||||
}
|
// }
|
||||||
state.logs[hash] = cpy
|
// state.logs[hash] = cpy
|
||||||
}
|
// }
|
||||||
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,13 @@ func (s *StateDB) CodeSize(addr common.Address, codeHash common.Hash) (int, erro
|
||||||
func (s *StateDB) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
func (s *StateDB) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
stateObject := s.stateObjects[addr]
|
stateObject := s.stateObjects[addr]
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
// Must use dirtyStorage and pendingStorage first, because statedb.MergePostBalStates will modify originStorage during N-blocks scenerio.
|
||||||
|
if value, cached := stateObject.dirtyStorage[slot]; cached {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
if value, cached := stateObject.pendingStorage[slot]; cached {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
if value, cached := stateObject.originStorage[slot]; cached {
|
if value, cached := stateObject.originStorage[slot]; cached {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
|
||||||
|
|
||||||
// Merge the tx-local access event into the "block-local" one, in order to collect
|
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||||
// all values, so that the witness can be built.
|
// all values, so that the witness can be built.
|
||||||
if statedb.Database().TrieDB().IsVerkle() {
|
if statedb.Database() != nil && statedb.Database().TrieDB().IsVerkle() {
|
||||||
statedb.AccessEvents().Merge(evm.AccessEvents)
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), nil
|
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), nil
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue