-
Notifications
You must be signed in to change notification settings - Fork 27
core: blockchain supports live tracer #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
|
@@ -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. | ||
|
|
@@ -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") | ||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. putting this block of code into
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to put it into |
||
| 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(), | ||
|
|
@@ -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(), | ||
|
|
@@ -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) | ||
|
|
@@ -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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.