mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 17:18:08 -05:00
chore(tree): a few refactors and instrumentations (#2549)
This commit is contained in:
@@ -11,9 +11,10 @@ pub type BufferedBlocks = BTreeMap<BlockNumber, HashMap<BlockHash, SealedBlockWi
|
||||
/// It allows us to store unconnected blocks for potential inclusion.
|
||||
///
|
||||
/// It has three main functionality:
|
||||
/// * `insert_block` for inserting blocks inside the buffer.
|
||||
/// * `take_all_childrens` for connecting blocks if the parent gets received and inserted.
|
||||
/// * `clean_old_blocks` to clear old blocks that are below finalized line.
|
||||
/// * [BlockBuffer::insert_block] for inserting blocks inside the buffer.
|
||||
/// * [BlockBuffer::take_all_children] for connecting blocks if the parent gets received and
|
||||
/// inserted.
|
||||
/// * [BlockBuffer::clean_old_blocks] to clear old blocks that are below finalized line.
|
||||
///
|
||||
/// Note: Buffer is limited by number of blocks that it can contains and eviction of the block
|
||||
/// is done by last recently used block.
|
||||
@@ -67,7 +68,7 @@ impl BlockBuffer {
|
||||
///
|
||||
/// Note: that order of returned blocks is important and the blocks with lower block number
|
||||
/// in the chain will come first so that they can be executed in the correct order.
|
||||
pub fn take_all_childrens(&mut self, parent: BlockNumHash) -> Vec<SealedBlockWithSenders> {
|
||||
pub fn take_all_children(&mut self, parent: BlockNumHash) -> Vec<SealedBlockWithSenders> {
|
||||
// remove parent block if present
|
||||
let mut taken = Vec::new();
|
||||
if let Some(block) = self.remove_from_blocks(&parent) {
|
||||
@@ -211,7 +212,7 @@ mod tests {
|
||||
buffer.insert_block(block4);
|
||||
|
||||
assert_eq!(buffer.len(), 4);
|
||||
assert_eq!(buffer.take_all_childrens(main_parent), vec![block1, block2, block3]);
|
||||
assert_eq!(buffer.take_all_children(main_parent), vec![block1, block2, block3]);
|
||||
assert_eq!(buffer.len(), 1);
|
||||
}
|
||||
|
||||
@@ -233,7 +234,7 @@ mod tests {
|
||||
assert_eq!(buffer.len(), 4);
|
||||
assert_eq!(
|
||||
buffer
|
||||
.take_all_childrens(main_parent)
|
||||
.take_all_children(main_parent)
|
||||
.into_iter()
|
||||
.map(|b| (b.hash, b))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
@@ -265,7 +266,7 @@ mod tests {
|
||||
assert_eq!(buffer.len(), 4);
|
||||
assert_eq!(
|
||||
buffer
|
||||
.take_all_childrens(block1.num_hash())
|
||||
.take_all_children(block1.num_hash())
|
||||
.into_iter()
|
||||
.map(|b| (b.hash, b))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Implementation of [`BlockIndices`] related to [`super::BlockchainTree`]
|
||||
|
||||
use super::chain::BlockChainId;
|
||||
use crate::canonical_chain::CanonicalChain;
|
||||
use reth_primitives::{BlockHash, BlockNumHash, BlockNumber, SealedBlockWithSenders};
|
||||
use reth_provider::Chain;
|
||||
use std::collections::{btree_map, hash_map, BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
@@ -18,7 +19,7 @@ pub struct BlockIndices {
|
||||
/// Canonical chain. Contains N number (depends on `finalization_depth`) of blocks.
|
||||
/// These blocks are found in fork_to_child but not inside `blocks_to_chain` or
|
||||
/// `number_to_block` as those are chain specific indices.
|
||||
canonical_chain: BTreeMap<BlockNumber, BlockHash>,
|
||||
canonical_chain: CanonicalChain,
|
||||
/// Index needed when discarding the chain, so we can remove connected chains from tree.
|
||||
/// NOTE: It contains just a blocks that are forks as a key and not all blocks.
|
||||
fork_to_child: HashMap<BlockHash, HashSet<BlockHash>>,
|
||||
@@ -37,7 +38,7 @@ impl BlockIndices {
|
||||
) -> Self {
|
||||
Self {
|
||||
last_finalized_block,
|
||||
canonical_chain,
|
||||
canonical_chain: CanonicalChain::new(canonical_chain),
|
||||
fork_to_child: Default::default(),
|
||||
blocks_to_chain: Default::default(),
|
||||
index_number_to_block: Default::default(),
|
||||
@@ -83,7 +84,7 @@ impl BlockIndices {
|
||||
|
||||
/// Check if block hash belongs to canonical chain.
|
||||
pub fn is_block_hash_canonical(&self, block_hash: &BlockHash) -> bool {
|
||||
self.canonical_chain.range(self.last_finalized_block..).any(|(_, &h)| h == *block_hash)
|
||||
self.canonical_chain.is_block_hash_canonical(self.last_finalized_block, block_hash)
|
||||
}
|
||||
|
||||
/// Last finalized block
|
||||
@@ -92,7 +93,7 @@ impl BlockIndices {
|
||||
}
|
||||
|
||||
/// Insert non fork block.
|
||||
pub fn insert_non_fork_block(
|
||||
pub(crate) fn insert_non_fork_block(
|
||||
&mut self,
|
||||
block_number: BlockNumber,
|
||||
block_hash: BlockHash,
|
||||
@@ -103,7 +104,7 @@ impl BlockIndices {
|
||||
}
|
||||
|
||||
/// Insert block to chain and fork child indices of the new chain
|
||||
pub fn insert_chain(&mut self, chain_id: BlockChainId, chain: &Chain) {
|
||||
pub(crate) fn insert_chain(&mut self, chain_id: BlockChainId, chain: &Chain) {
|
||||
for (number, block) in chain.blocks().iter() {
|
||||
// add block -> chain_id index
|
||||
self.blocks_to_chain.insert(block.hash(), chain_id);
|
||||
@@ -116,19 +117,19 @@ impl BlockIndices {
|
||||
}
|
||||
|
||||
/// Get the chain ID the block belongs to
|
||||
pub fn get_blocks_chain_id(&self, block: &BlockHash) -> Option<BlockChainId> {
|
||||
pub(crate) fn get_blocks_chain_id(&self, block: &BlockHash) -> Option<BlockChainId> {
|
||||
self.blocks_to_chain.get(block).cloned()
|
||||
}
|
||||
|
||||
/// Update all block hashes. iterate over present and new list of canonical hashes and compare
|
||||
/// them. Remove all missmatches, disconnect them and return all chains that needs to be
|
||||
/// removed.
|
||||
pub fn update_block_hashes(
|
||||
pub(crate) fn update_block_hashes(
|
||||
&mut self,
|
||||
hashes: BTreeMap<u64, BlockHash>,
|
||||
) -> (BTreeSet<BlockChainId>, Vec<BlockNumHash>) {
|
||||
// set new canonical hashes.
|
||||
self.canonical_chain = hashes.clone();
|
||||
self.canonical_chain.replace(hashes.clone());
|
||||
|
||||
let mut new_hashes = hashes.into_iter();
|
||||
let mut old_hashes = self.canonical_chain().clone().into_iter();
|
||||
@@ -249,7 +250,7 @@ impl BlockIndices {
|
||||
let first_number = *blocks.first_key_value().unwrap().0;
|
||||
|
||||
// this will remove all blocks numbers that are going to be replaced.
|
||||
self.canonical_chain.retain(|num, _| *num < first_number);
|
||||
self.canonical_chain.retain(|&number, _| number < first_number);
|
||||
|
||||
// remove them from block to chain_id index
|
||||
blocks.iter().map(|(_, b)| (b.number, b.hash(), b.parent_hash)).for_each(
|
||||
@@ -308,8 +309,8 @@ impl BlockIndices {
|
||||
let finalized_blocks: Vec<BlockHash> = self
|
||||
.canonical_chain
|
||||
.iter()
|
||||
.filter(|(&number, _)| number >= self.last_finalized_block && number < finalized_block)
|
||||
.map(|(_, hash)| *hash)
|
||||
.filter(|(number, _)| *number >= self.last_finalized_block && *number < finalized_block)
|
||||
.map(|(_, hash)| hash)
|
||||
.collect();
|
||||
|
||||
// remove unneeded canonical hashes.
|
||||
@@ -338,33 +339,26 @@ impl BlockIndices {
|
||||
}
|
||||
|
||||
/// Returns the block hash of the canonical block with the given number.
|
||||
pub fn canonical_hash(&self, block_number: BlockNumber) -> Option<BlockHash> {
|
||||
self.canonical_chain.get(&block_number).cloned()
|
||||
#[inline]
|
||||
pub fn canonical_hash(&self, block_number: &BlockNumber) -> Option<BlockHash> {
|
||||
self.canonical_chain.canonical_hash(block_number)
|
||||
}
|
||||
|
||||
/// Returns the block number of the canonical block with the given hash.
|
||||
#[inline]
|
||||
pub fn canonical_number(&self, block_hash: BlockHash) -> Option<BlockNumber> {
|
||||
self.canonical_chain.iter().find_map(
|
||||
|(number, hash)| {
|
||||
if *hash == block_hash {
|
||||
Some(*number)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
self.canonical_chain.canonical_number(block_hash)
|
||||
}
|
||||
|
||||
/// get canonical tip
|
||||
#[inline]
|
||||
pub fn canonical_tip(&self) -> BlockNumHash {
|
||||
self.canonical_chain
|
||||
.last_key_value()
|
||||
.map(|(&number, &hash)| BlockNumHash { number, hash })
|
||||
.unwrap_or_default()
|
||||
self.canonical_chain.tip()
|
||||
}
|
||||
|
||||
/// Canonical chain needed for execution of EVM. It should contains last 256 block hashes.
|
||||
pub fn canonical_chain(&self) -> &BTreeMap<BlockNumber, BlockHash> {
|
||||
#[inline]
|
||||
pub(crate) fn canonical_chain(&self) -> &CanonicalChain {
|
||||
&self.canonical_chain
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Implementation of [`BlockchainTree`]
|
||||
use crate::{
|
||||
chain::BlockChainId, AppendableChain, BlockBuffer, BlockIndices, BlockchainTreeConfig,
|
||||
PostStateData, TreeExternals,
|
||||
canonical_chain::CanonicalChain, chain::BlockChainId, AppendableChain, BlockBuffer,
|
||||
BlockIndices, BlockchainTreeConfig, PostStateData, TreeExternals,
|
||||
};
|
||||
use reth_db::{cursor::DbCursorRO, database::Database, tables, transaction::DbTx};
|
||||
use reth_interfaces::{
|
||||
@@ -24,7 +24,7 @@ use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
use tracing::{error, info, trace};
|
||||
use tracing::{debug, error, info, instrument, trace};
|
||||
|
||||
#[cfg_attr(doc, aquamarine::aquamarine)]
|
||||
/// Tree of chains and its identifications.
|
||||
@@ -148,8 +148,8 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
// check db if block is finalized.
|
||||
if block.number <= last_finalized_block {
|
||||
// check if block is canonical
|
||||
if let Some(hash) = self.block_indices.canonical_chain().get(&block.number) {
|
||||
if *hash == block.hash {
|
||||
if let Some(hash) = self.canonical_chain().canonical_hash(&block.number) {
|
||||
if hash == block.hash {
|
||||
return Ok(Some(BlockStatus::Valid))
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
|
||||
// check if block is part of canonical chain
|
||||
if self.block_indices.canonical_hash(block.number) == Some(block.hash) {
|
||||
if self.block_indices.canonical_hash(&block.number) == Some(block.hash) {
|
||||
return Ok(Some(BlockStatus::Valid))
|
||||
}
|
||||
|
||||
@@ -185,10 +185,16 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
|
||||
/// Expose internal indices of the BlockchainTree.
|
||||
#[inline]
|
||||
pub fn block_indices(&self) -> &BlockIndices {
|
||||
&self.block_indices
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn canonical_chain(&self) -> &CanonicalChain {
|
||||
self.block_indices.canonical_chain()
|
||||
}
|
||||
|
||||
/// Returns the block with matching hash.
|
||||
pub fn block_by_hash(&self, block_hash: BlockHash) -> Option<&SealedBlock> {
|
||||
let id = self.block_indices.get_blocks_chain_id(&block_hash)?;
|
||||
@@ -225,10 +231,8 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
let first_pending_block_number =
|
||||
*parent_block_hashed.first_key_value().expect("There is at least one block hash").0;
|
||||
let canonical_chain = self
|
||||
.block_indices
|
||||
.canonical_chain()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|&(key, _)| key < first_pending_block_number)
|
||||
.collect::<Vec<_>>();
|
||||
parent_block_hashed.extend(canonical_chain.into_iter());
|
||||
@@ -239,14 +243,12 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
|
||||
// check if there is canonical block
|
||||
if let Some(canonical_fork) =
|
||||
self.block_indices().canonical_chain().iter().find(|(_, value)| **value == block_hash)
|
||||
{
|
||||
if let Some(canonical_number) = self.canonical_chain().canonical_number(block_hash) {
|
||||
trace!(target: "blockchain_tree", ?block_hash, "Constructing post state data based on canonical chain");
|
||||
return Some(PostStateData {
|
||||
canonical_fork: ForkBlock { number: *canonical_fork.0, hash: *canonical_fork.1 },
|
||||
canonical_fork: ForkBlock { number: canonical_number, hash: block_hash },
|
||||
state: PostState::new(),
|
||||
parent_block_hashed: self.block_indices().canonical_chain().clone(),
|
||||
parent_block_hashed: self.canonical_chain().inner().clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -255,108 +257,24 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
|
||||
/// Try inserting block inside the tree.
|
||||
///
|
||||
/// If blocks does not have parent [`BlockStatus::Disconnected`] would be returned.
|
||||
/// If blocks does not have parent [`BlockStatus::Disconnected`] would be returned, in which
|
||||
/// case it is buffered for future inclusion.
|
||||
#[instrument(skip_all, fields(block = ?block.num_hash()), target = "blockchain-tree", ret)]
|
||||
pub fn try_insert_block(
|
||||
&mut self,
|
||||
block: SealedBlockWithSenders,
|
||||
) -> Result<BlockStatus, Error> {
|
||||
let parent = block.parent_num_hash();
|
||||
let block_num_hash = block.num_hash();
|
||||
|
||||
// check if block parent can be found in Tree
|
||||
if let Some(chain_id) = self.block_indices.get_blocks_chain_id(&parent.hash) {
|
||||
// Create a new sidechain by forking the given chain, or append the block if the parent
|
||||
// block is the top of the given chain.
|
||||
let block_hashes = self.all_chain_hashes(chain_id);
|
||||
|
||||
// get canonical fork.
|
||||
let canonical_fork = self
|
||||
.canonical_fork(chain_id)
|
||||
.ok_or(ExecError::BlockChainIdConsistency { chain_id })?;
|
||||
|
||||
// get chain that block needs to join to.
|
||||
let parent_chain = self
|
||||
.chains
|
||||
.get_mut(&chain_id)
|
||||
.ok_or(ExecError::BlockChainIdConsistency { chain_id })?;
|
||||
let chain_tip = parent_chain.tip().hash();
|
||||
|
||||
let canonical_block_hashes = self.block_indices.canonical_chain();
|
||||
|
||||
// append the block if it is continuing the chain.
|
||||
let status = if chain_tip == block.parent_hash {
|
||||
let block_hash = block.hash();
|
||||
let block_number = block.number;
|
||||
parent_chain.append_block(
|
||||
block,
|
||||
block_hashes,
|
||||
canonical_block_hashes,
|
||||
canonical_fork,
|
||||
&self.externals,
|
||||
)?;
|
||||
|
||||
self.block_indices.insert_non_fork_block(block_number, block_hash, chain_id);
|
||||
Ok(BlockStatus::Valid)
|
||||
} else {
|
||||
let chain = parent_chain.new_chain_fork(
|
||||
block,
|
||||
block_hashes,
|
||||
canonical_block_hashes,
|
||||
canonical_fork,
|
||||
&self.externals,
|
||||
)?;
|
||||
self.insert_chain(chain);
|
||||
Ok(BlockStatus::Accepted)
|
||||
};
|
||||
self.try_connect_buffered_blocks(block_num_hash);
|
||||
return status
|
||||
// found parent in side tree, try to insert there
|
||||
return self.try_insert_block_into_side_chain(block, chain_id)
|
||||
}
|
||||
|
||||
let canonical_parent_block = self.block_indices.canonical_hash(parent.number);
|
||||
|
||||
// if not found, check if the parent can be found inside canonical chain.
|
||||
if Some(parent.hash) == canonical_parent_block {
|
||||
// create new chain that points to that block
|
||||
//return self.fork_canonical_chain(block.clone());
|
||||
// TODO save pending block to database
|
||||
// https://github.com/paradigmxyz/reth/issues/1713
|
||||
|
||||
let db = self.externals.shareable_db();
|
||||
|
||||
// Validate that the block is post merge
|
||||
let parent_td = db
|
||||
.header_td(&block.parent_hash)?
|
||||
.ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })?;
|
||||
// Pass the parent total difficulty to short-circuit unnecessary calculations.
|
||||
if !self.externals.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO)
|
||||
{
|
||||
return Err(ExecError::BlockPreMerge { hash: block.hash }.into())
|
||||
}
|
||||
|
||||
// Create state provider
|
||||
let canonical_block_hashes = self.block_indices.canonical_chain();
|
||||
let canonical_tip =
|
||||
canonical_block_hashes.last_key_value().map(|(_, hash)| *hash).unwrap_or_default();
|
||||
let block_status = if block.parent_hash == canonical_tip {
|
||||
BlockStatus::Valid
|
||||
} else {
|
||||
BlockStatus::Accepted
|
||||
};
|
||||
|
||||
let parent_header = db
|
||||
.header(&block.parent_hash)?
|
||||
.ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })?
|
||||
.seal(block.parent_hash);
|
||||
let chain = AppendableChain::new_canonical_fork(
|
||||
&block,
|
||||
&parent_header,
|
||||
canonical_block_hashes,
|
||||
parent,
|
||||
&self.externals,
|
||||
)?;
|
||||
self.insert_chain(chain);
|
||||
self.try_connect_buffered_blocks(block_num_hash);
|
||||
return Ok(block_status)
|
||||
if Some(parent.hash) == self.block_indices.canonical_hash(&parent.number) {
|
||||
return self.try_append_canonical_chain(block, parent)
|
||||
}
|
||||
|
||||
// this is another check to ensure that if the block points to a canonical block its block
|
||||
@@ -383,6 +301,115 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
Ok(BlockStatus::Disconnected)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, target = "blockchain_tree")]
|
||||
fn try_append_canonical_chain(
|
||||
&mut self,
|
||||
block: SealedBlockWithSenders,
|
||||
parent: BlockNumHash,
|
||||
) -> Result<BlockStatus, Error> {
|
||||
let block_num_hash = block.num_hash();
|
||||
debug!(target: "blockchain_tree", ?parent, "Appending block to canonical chain");
|
||||
// create new chain that points to that block
|
||||
//return self.fork_canonical_chain(block.clone());
|
||||
// TODO save pending block to database
|
||||
// https://github.com/paradigmxyz/reth/issues/1713
|
||||
|
||||
let db = self.externals.shareable_db();
|
||||
|
||||
// Validate that the block is post merge
|
||||
let parent_td = db
|
||||
.header_td(&block.parent_hash)?
|
||||
.ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })?;
|
||||
// Pass the parent total difficulty to short-circuit unnecessary calculations.
|
||||
if !self.externals.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO) {
|
||||
return Err(ExecError::BlockPreMerge { hash: block.hash }.into())
|
||||
}
|
||||
|
||||
let canonical_chain = self.canonical_chain();
|
||||
let block_status = if block.parent_hash == canonical_chain.tip().hash {
|
||||
BlockStatus::Valid
|
||||
} else {
|
||||
BlockStatus::Accepted
|
||||
};
|
||||
|
||||
let parent_header = db
|
||||
.header(&block.parent_hash)?
|
||||
.ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })?
|
||||
.seal(block.parent_hash);
|
||||
let chain = AppendableChain::new_canonical_fork(
|
||||
&block,
|
||||
&parent_header,
|
||||
canonical_chain.inner(),
|
||||
parent,
|
||||
&self.externals,
|
||||
)?;
|
||||
self.insert_chain(chain);
|
||||
self.try_connect_buffered_blocks(block_num_hash);
|
||||
Ok(block_status)
|
||||
}
|
||||
|
||||
/// Try inserting a block into the given side chain.
|
||||
#[instrument(skip_all, target = "blockchain_tree")]
|
||||
fn try_insert_block_into_side_chain(
|
||||
&mut self,
|
||||
block: SealedBlockWithSenders,
|
||||
chain_id: BlockChainId,
|
||||
) -> Result<BlockStatus, Error> {
|
||||
debug!(target: "blockchain_tree", "Inserting block into side chain");
|
||||
let block_num_hash = block.num_hash();
|
||||
// Create a new sidechain by forking the given chain, or append the block if the parent
|
||||
// block is the top of the given chain.
|
||||
let block_hashes = self.all_chain_hashes(chain_id);
|
||||
|
||||
// get canonical fork.
|
||||
let canonical_fork = self
|
||||
.canonical_fork(chain_id)
|
||||
.ok_or(ExecError::BlockSideChainIdConsistency { chain_id })?;
|
||||
|
||||
// get chain that block needs to join to.
|
||||
let parent_chain = self
|
||||
.chains
|
||||
.get_mut(&chain_id)
|
||||
.ok_or(ExecError::BlockSideChainIdConsistency { chain_id })?;
|
||||
let chain_tip = parent_chain.tip().hash();
|
||||
|
||||
let canonical_block_hashes = self.block_indices.canonical_chain();
|
||||
|
||||
// append the block if it is continuing the side chain.
|
||||
let status = if chain_tip == block.parent_hash {
|
||||
debug!(target: "blockchain_tree", "Appending block to side chain");
|
||||
let block_hash = block.hash();
|
||||
let block_number = block.number;
|
||||
parent_chain.append_block(
|
||||
block,
|
||||
block_hashes,
|
||||
canonical_block_hashes.inner(),
|
||||
canonical_fork,
|
||||
&self.externals,
|
||||
)?;
|
||||
|
||||
self.block_indices.insert_non_fork_block(block_number, block_hash, chain_id);
|
||||
Ok(BlockStatus::Valid)
|
||||
} else {
|
||||
debug!(target: "blockchain_tree", ?canonical_fork, "Starting new fork from side chain");
|
||||
// the block starts a new fork
|
||||
let chain = parent_chain.new_chain_fork(
|
||||
block,
|
||||
block_hashes,
|
||||
canonical_block_hashes.inner(),
|
||||
canonical_fork,
|
||||
&self.externals,
|
||||
)?;
|
||||
self.insert_chain(chain);
|
||||
Ok(BlockStatus::Accepted)
|
||||
};
|
||||
|
||||
// After we inserted the block, we try to connect any buffered blocks
|
||||
self.try_connect_buffered_blocks(block_num_hash);
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
/// Get all block hashes from a sidechain that are not part of the canonical chain.
|
||||
///
|
||||
/// This is a one time operation per block.
|
||||
@@ -429,7 +456,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
break
|
||||
}
|
||||
(self.block_indices.canonical_hash(fork.number) == Some(fork.hash)).then_some(fork)
|
||||
(self.block_indices.canonical_hash(&fork.number) == Some(fork.hash)).then_some(fork)
|
||||
}
|
||||
|
||||
/// Insert a chain into the tree.
|
||||
@@ -462,13 +489,16 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
|
||||
/// Insert block for future execution.
|
||||
///
|
||||
/// Returns an error if the block is invalid.
|
||||
pub fn buffer_block(&mut self, block: SealedBlockWithSenders) -> Result<(), Error> {
|
||||
self.validate_block(&block)?;
|
||||
self.buffered_blocks.insert_block(block);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate if block is correct and if i
|
||||
/// Validate if block is correct and satisfies all the consensus rules that concern the header
|
||||
/// and block body itself.
|
||||
fn validate_block(&self, block: &SealedBlockWithSenders) -> Result<(), Error> {
|
||||
if let Err(e) =
|
||||
self.externals.consensus.validate_header_with_total_difficulty(block, U256::MAX)
|
||||
@@ -496,6 +526,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
/// Check if block is found inside chain and if the chain extends the canonical chain
|
||||
/// if it does extends the canonical chain, return `BlockStatus::Valid`
|
||||
/// if it does not extends the canonical chain, return `BlockStatus::Accepted`
|
||||
#[track_caller]
|
||||
fn is_block_inside_chain(&self, block: &BlockNumHash) -> Option<BlockStatus> {
|
||||
// check if block known and is already inside Tree
|
||||
if let Some(chain_id) = self.block_indices.get_blocks_chain_id(&block.hash) {
|
||||
@@ -533,7 +564,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
self.insert_block_inner(block, true)
|
||||
}
|
||||
|
||||
/// Insert a block (with senders recovered) in the tree. Check [`BlockchainTree::insert_block`]
|
||||
/// Insert a block (with recovered senders) in the tree. Check [`BlockchainTree::insert_block`]
|
||||
/// for more info
|
||||
pub(crate) fn insert_block_inner(
|
||||
&mut self,
|
||||
@@ -628,13 +659,19 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Connect unconnected blocks
|
||||
/// Connect unconnected,buffered blocks if the new block closes a gap.
|
||||
fn try_connect_buffered_blocks(&mut self, new_block: BlockNumHash) {
|
||||
let include_blocks = self.buffered_blocks.take_all_childrens(new_block);
|
||||
// insert child blocks
|
||||
let include_blocks = self.buffered_blocks.take_all_children(new_block);
|
||||
// insert block children
|
||||
for block in include_blocks.into_iter() {
|
||||
// dont fail on error, just ignore the block.
|
||||
let _ = self.insert_block_inner(block, false);
|
||||
let _ = self.insert_block_inner(block, false).map_err(|err| {
|
||||
debug!(
|
||||
target: "blockchain_tree", ?err,
|
||||
"Failed to insert buffered block",
|
||||
);
|
||||
err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,7 +687,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
let chain = chain.into_inner();
|
||||
match chain.split(split_at) {
|
||||
ChainSplit::Split { canonical, pending } => {
|
||||
// rest of splited chain is inserted back with same chain_id.
|
||||
// rest of split chain is inserted back with same chain_id.
|
||||
self.block_indices.insert_chain(chain_id, &pending);
|
||||
self.chains.insert(chain_id, AppendableChain::new(pending));
|
||||
canonical
|
||||
@@ -662,7 +699,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a block and its parent part of the canonical chain.
|
||||
/// Make a block and its parent(s) part of the canonical chain.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@@ -672,10 +709,12 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `Ok` if the blocks were canonicalized, or if the blocks were already canonical.
|
||||
#[track_caller]
|
||||
#[instrument(skip(self), target = "blockchain_tree")]
|
||||
pub fn make_canonical(&mut self, block_hash: &BlockHash) -> Result<(), Error> {
|
||||
// If block is already canonical don't return error.
|
||||
if self.block_indices.is_block_hash_canonical(block_hash) {
|
||||
trace!(target: "blockchain_tree", ?block_hash, "Block is already canonical");
|
||||
trace!(target: "blockchain_tree", "Block is already canonical");
|
||||
let td = self
|
||||
.externals
|
||||
.shareable_db()
|
||||
@@ -688,11 +727,12 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
}
|
||||
|
||||
let Some(chain_id) = self.block_indices.get_blocks_chain_id(block_hash) else {
|
||||
debug!(target: "blockchain_tree", "Block hash not found in block indices");
|
||||
return Err(ExecError::BlockHashNotFoundInChain { block_hash: *block_hash }.into())
|
||||
};
|
||||
let chain = self.chains.remove(&chain_id).expect("To be present");
|
||||
|
||||
// we are spliting chain as there is possibility that only part of chain get canonicalized.
|
||||
// we are splitting chain as there is possibility that only part of chain get canonicalized.
|
||||
let canonical = self.split_chain(chain_id, chain, SplitAt::Hash(*block_hash));
|
||||
|
||||
let mut block_fork = canonical.fork_block();
|
||||
@@ -718,11 +758,12 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
// update canonical index
|
||||
self.block_indices.canonicalize_blocks(new_canon_chain.blocks());
|
||||
|
||||
let chain_action;
|
||||
// event about new canonical chain.
|
||||
let chain_notification;
|
||||
|
||||
// if joins to the tip;
|
||||
if new_canon_chain.fork_block_hash() == old_tip.hash {
|
||||
chain_action =
|
||||
chain_notification =
|
||||
CanonStateNotification::Commit { new: Arc::new(new_canon_chain.clone()) };
|
||||
// append to database
|
||||
self.commit_canonical(new_canon_chain)?;
|
||||
@@ -731,7 +772,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
|
||||
let canon_fork = new_canon_chain.fork_block();
|
||||
// sanity check
|
||||
if self.block_indices.canonical_hash(canon_fork.number) != Some(canon_fork.hash) {
|
||||
if self.block_indices.canonical_hash(&canon_fork.number) != Some(canon_fork.hash) {
|
||||
unreachable!("all chains should point to canonical chain.");
|
||||
}
|
||||
|
||||
@@ -742,7 +783,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
|
||||
if let Some(old_canon_chain) = old_canon_chain {
|
||||
// state action
|
||||
chain_action = CanonStateNotification::Reorg {
|
||||
chain_notification = CanonStateNotification::Reorg {
|
||||
old: Arc::new(old_canon_chain.clone()),
|
||||
new: Arc::new(new_canon_chain.clone()),
|
||||
};
|
||||
@@ -752,12 +793,13 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
// error here to confirm that we are reverting nothing from db.
|
||||
error!("Reverting nothing from db on block: #{:?}", block_hash);
|
||||
|
||||
chain_action = CanonStateNotification::Commit { new: Arc::new(new_canon_chain) };
|
||||
chain_notification =
|
||||
CanonStateNotification::Commit { new: Arc::new(new_canon_chain) };
|
||||
}
|
||||
}
|
||||
|
||||
// send notification
|
||||
let _ = self.canon_state_notification_sender.send(chain_action);
|
||||
// send notification about new canonical chain.
|
||||
let _ = self.canon_state_notification_sender.send(chain_notification);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
92
crates/blockchain-tree/src/canonical_chain.rs
Normal file
92
crates/blockchain-tree/src/canonical_chain.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use reth_primitives::{BlockHash, BlockNumHash, BlockNumber};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// This keeps track of all blocks of the canonical chain.
|
||||
///
|
||||
/// This is a wrapper type around an ordered set of block numbers and hashes that belong to the
|
||||
/// canonical chain.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct CanonicalChain {
|
||||
/// All blocks of the canonical chain in order.
|
||||
chain: BTreeMap<BlockNumber, BlockHash>,
|
||||
}
|
||||
|
||||
impl CanonicalChain {
|
||||
pub(crate) fn new(chain: BTreeMap<BlockNumber, BlockHash>) -> Self {
|
||||
Self { chain }
|
||||
}
|
||||
|
||||
/// Replaces the current chain with the given one.
|
||||
#[inline]
|
||||
pub(crate) fn replace(&mut self, chain: BTreeMap<BlockNumber, BlockHash>) {
|
||||
self.chain = chain;
|
||||
}
|
||||
|
||||
/// Returns the block hash of the canonical block with the given number.
|
||||
#[inline]
|
||||
pub(crate) fn canonical_hash(&self, number: &BlockNumber) -> Option<BlockHash> {
|
||||
self.chain.get(number).cloned()
|
||||
}
|
||||
|
||||
/// Returns the block number of the canonical block with the given hash.
|
||||
#[inline]
|
||||
pub(crate) fn canonical_number(&self, block_hash: BlockHash) -> Option<BlockNumber> {
|
||||
self.chain.iter().find_map(
|
||||
|(number, hash)| {
|
||||
if *hash == block_hash {
|
||||
Some(*number)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if block hash belongs to canonical chain.
|
||||
#[inline]
|
||||
pub(crate) fn is_block_hash_canonical(
|
||||
&self,
|
||||
last_finalized_block: BlockNumber,
|
||||
block_hash: &BlockHash,
|
||||
) -> bool {
|
||||
self.chain.range(last_finalized_block..).any(|(_, &h)| h == *block_hash)
|
||||
}
|
||||
|
||||
/// Extends all items from the given iterator to the chain.
|
||||
#[inline]
|
||||
pub(crate) fn extend(&mut self, blocks: impl Iterator<Item = (BlockNumber, BlockHash)>) {
|
||||
self.chain.extend(blocks)
|
||||
}
|
||||
|
||||
/// Retains only the elements specified by the predicate.
|
||||
#[inline]
|
||||
pub(crate) fn retain<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnMut(&BlockNumber, &mut BlockHash) -> bool,
|
||||
{
|
||||
self.chain.retain(f)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn inner(&self) -> &BTreeMap<BlockNumber, BlockHash> {
|
||||
&self.chain
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn tip(&self) -> BlockNumHash {
|
||||
self.chain
|
||||
.last_key_value()
|
||||
.map(|(&number, &hash)| BlockNumHash { number, hash })
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (BlockNumber, BlockHash)> + '_ {
|
||||
self.chain.iter().map(|(&number, &hash)| (number, hash))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (BlockNumber, BlockHash)> {
|
||||
self.chain.into_iter()
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,13 @@ use std::sync::Arc;
|
||||
#[derive(Debug)]
|
||||
pub struct TreeExternals<DB, C, EF> {
|
||||
/// The database, used to commit the canonical chain, or unwind it.
|
||||
pub db: DB,
|
||||
pub(crate) db: DB,
|
||||
/// The consensus engine.
|
||||
pub consensus: C,
|
||||
pub(crate) consensus: C,
|
||||
/// The executor factory to execute blocks with.
|
||||
pub executor_factory: EF,
|
||||
pub(crate) executor_factory: EF,
|
||||
/// The chain spec.
|
||||
pub chain_spec: Arc<ChainSpec>,
|
||||
pub(crate) chain_spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
impl<DB, C, EF> TreeExternals<DB, C, EF> {
|
||||
|
||||
@@ -32,4 +32,6 @@ pub use post_state_data::{PostStateData, PostStateDataRef};
|
||||
|
||||
/// Buffer of not executed blocks.
|
||||
pub mod block_buffer;
|
||||
mod canonical_chain;
|
||||
|
||||
pub use block_buffer::BlockBuffer;
|
||||
|
||||
@@ -92,7 +92,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTreeViewer
|
||||
|
||||
fn canonical_blocks(&self) -> BTreeMap<BlockNumber, BlockHash> {
|
||||
trace!(target: "blockchain_tree", "Returning canonical blocks in tree");
|
||||
self.tree.read().block_indices().canonical_chain().clone()
|
||||
self.tree.read().block_indices().canonical_chain().inner().clone()
|
||||
}
|
||||
|
||||
fn find_canonical_ancestor(&self, hash: BlockHash) -> Option<BlockHash> {
|
||||
|
||||
@@ -35,7 +35,7 @@ pub enum Error {
|
||||
#[error("Provider error")]
|
||||
ProviderError,
|
||||
#[error("BlockChainId can't be found in BlockchainTree with internal index {chain_id}")]
|
||||
BlockChainIdConsistency { chain_id: u64 },
|
||||
BlockSideChainIdConsistency { chain_id: u64 },
|
||||
#[error(
|
||||
"Appending chain on fork (other_chain_fork:?) is not possible as the tip is {chain_tip:?}"
|
||||
)]
|
||||
|
||||
Reference in New Issue
Block a user