Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 123 additions & 60 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
Expand Down Expand Up @@ -259,6 +260,7 @@ type BlockChain struct {
prefetcher Prefetcher
processor Processor // Block transaction processor interface
vmConfig vm.Config
logger *tracing.Hooks

shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
shouldStoreInternalTxs bool
Expand Down Expand Up @@ -456,6 +458,25 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
}
}

if bc.logger != nil && bc.logger.OnBlockchainInit != nil {
bc.logger.OnBlockchainInit(chainConfig)
}

if bc.logger != nil && bc.logger.OnGenesisBlock != nil {
if block := bc.CurrentBlock(); block.Number().Uint64() == 0 {
alloc, err := getGenesisState(bc.db, block.Hash())
if err != nil {
return nil, fmt.Errorf("failed to get genesis state: %w", err)
}

if alloc == nil {
return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set")
}

bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
}
}

// Load any existing snapshot, regenerating it if loading failed
if bc.cacheConfig.SnapshotLimit > 0 {
// If the chain was rewound past the snapshot persistent layer (causing
Expand Down Expand Up @@ -1971,6 +1992,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool, sidecars
return it.index, err
}
stats.processed++
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
bc.logger.OnSkippedBlock(tracing.BlockEvent{
Block: block,
TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1),
Finalized: bc.CurrentFinalBlock(),
})
}

// 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.
Expand All @@ -1988,6 +2016,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool, sidecars
if err != nil {
return it.index, err
}
statedb.SetLogger(bc.logger)

// Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain")
Expand All @@ -2011,73 +2040,19 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool, sidecars
}
}

// Process block using the parent state as reference point
substart := time.Now()
receipts, logs, internalTxs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, bc.OpEvents()...)
if err != nil {
bc.reportBlock(block, receipts, err)
atomic.StoreUint32(&followupInterrupt, 1)
return it.index, err
}

// store internal txs to db and send them to internalTxFeed
if bc.enableAdditionalChainEvent && len(internalTxs) > 0 {
bc.WriteInternalTransactions(block.Hash(), internalTxs)
bc.internalTxFeed.Send(internalTxs)
}

// Update the metrics touched during block processing
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete, we can mark them
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete, we can mark them
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete, we can mark them
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete, we can mark them
triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation
trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates
trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates

blockExecutionTimer.Update(time.Since(substart) - trieproc - triehash)

// Validate the state using the default validator
substart = time.Now()
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
bc.reportBlock(block, receipts, err)
atomic.StoreUint32(&followupInterrupt, 1)
return it.index, err
}
proctime := time.Since(start)

// Update the metrics touched during block validation
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete, we can mark them
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete, we can mark them

blockValidationTimer.Update(time.Since(substart) - (statedb.AccountHashes + statedb.StorageHashes - triehash))

// Write the block to the chain and get the status.
substart = time.Now()
var blockSidecars []*types.BlobTxSidecar
if len(sidecars) > 0 {
blockSidecars = sidecars[it.index]
}
Comment on lines 2043 to 2046
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

putting this block of code into processBlock() as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to put it into processBlock, but doing so would require processBlock to take more parameters like sidecars and it, and I though it's unclear

status, err := bc.writeBlockWithState(block, receipts, logs, internalTxs, statedb, false, blockSidecars)

// The traced section of block import.
res, err := bc.processBlock(block, statedb, start, blockSidecars)
atomic.StoreUint32(&followupInterrupt, 1)
if err != nil {
return it.index, 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) // Triedb commits are complete, we can mark them

blockWriteTimer.Update(time.Since(substart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
blockInsertTimer.UpdateSince(start)
blockTxsGauge.Update(int64(len(block.Transactions())))
blockGasUsedGauge.Update(int64(block.GasUsed()))

switch status {
switch res.status {
case CanonStatTy:
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
Expand All @@ -2087,7 +2062,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool, sidecars
lastCanon = block

// Only count canonical blocks for GC processing time
bc.gcproc += proctime
bc.gcproc += res.procTime

case SideStatTy:
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
Expand All @@ -2104,7 +2079,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool, sidecars
"root", block.Root())
}
stats.processed++
stats.usedGas += usedGas
stats.usedGas += res.usedGas

dirty, _ := bc.triedb.Size()
stats.report(chain, it.index, dirty)
Expand Down Expand Up @@ -2137,6 +2112,94 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool, sidecars
return it.index, err
}

// blockProcessingResult is a summary of block processing
// used for updating the stats.
type blockProcessingResult struct {
usedGas uint64
procTime time.Duration
status WriteStatus
}

// processBlock executes and validates the given block. If there was no error
// it writes the block and associated state to database.
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, blockSidecars []*types.BlobTxSidecar) (_ *blockProcessingResult, blockEndErr error) {
if bc.logger != nil && bc.logger.OnBlockStart != nil {
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
bc.logger.OnBlockStart(tracing.BlockEvent{
Block: block,
TD: td,
Finalized: bc.CurrentFinalBlock(),
})
}
if bc.logger != nil && bc.logger.OnBlockEnd != nil {
defer func() {
bc.logger.OnBlockEnd(blockEndErr)
}()
}

// Process block using the parent state as reference point
substart := time.Now()
receipts, logs, internalTxs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, bc.OpEvents()...)
if err != nil {
bc.reportBlock(block, receipts, err)
return nil, err
}

// store internal txs to db and send them to internalTxFeed
if bc.enableAdditionalChainEvent && len(internalTxs) > 0 {
bc.WriteInternalTransactions(block.Hash(), internalTxs)
bc.internalTxFeed.Send(internalTxs)
}

// Update the metrics touched during block processing
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete, we can mark them
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete, we can mark them
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete, we can mark them
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete, we can mark them
triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation
trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates
trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates

blockExecutionTimer.Update(time.Since(substart) - trieproc - triehash)

// Validate the state using the default validator
substart = time.Now()
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
bc.reportBlock(block, receipts, err)
return nil, err
}
proctime := time.Since(start)

// Update the metrics touched during block validation
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete, we can mark them
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete, we can mark them

blockValidationTimer.Update(time.Since(substart) - (statedb.AccountHashes + statedb.StorageHashes - triehash))

// Write the block to the chain and get the status.
substart = time.Now()
status, err := bc.writeBlockWithState(block, receipts, logs, internalTxs, statedb, false, blockSidecars)
if err != nil {
bc.reportBlock(block, receipts, err)
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) // Triedb commits are complete, we can mark them

blockWriteTimer.Update(time.Since(substart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
blockInsertTimer.UpdateSince(start)
blockTxsGauge.Update(int64(len(block.Transactions())))
blockGasUsedGauge.Update(int64(block.GasUsed()))

return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status}, nil
}

// insertSideChain is called when an import batch hits upon a pruned ancestor
// error, which happens when a sidechain with a sufficiently old fork-block is
// found.
Expand Down
31 changes: 31 additions & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,37 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *trie.Database
return nil
}

func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
if len(blob) != 0 {
if err := alloc.UnmarshalJSON(blob); err != nil {
return nil, err
}

return alloc, nil
}

// Genesis allocation is missing and there are several possibilities:
// the node is legacy which doesn't persist the genesis allocation or
// the persisted allocation is just lost.
// - supported networks(mainnet, testnets), recover with defined allocations
// - private network, can't recover
var genesis *Genesis
switch blockhash {
case params.MainnetGenesisHash:
genesis = DefaultGenesisBlock()
case params.GoerliGenesisHash:
genesis = DefaultGoerliGenesisBlock()
case params.SepoliaGenesisHash:
genesis = DefaultSepoliaGenesisBlock()
Comment on lines +148 to +153
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like ETH related

}
if genesis != nil {
return genesis.Alloc, nil
}

return nil, nil
}

// field type overrides for gencodec
type genesisSpecMarshaling struct {
Nonce math.HexOrDecimal64
Expand Down
2 changes: 1 addition & 1 deletion core/tracing/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ type VMContext struct {
// It contains the block as well as consensus related information.
type BlockEvent struct {
Block *types.Block
TD *big.Int
Finalized *types.Header
Safe *types.Header
}

type (
Expand Down