chore(tree): a few refactors and instrumentations (#2549)

This commit is contained in:
Matthias Seitz
2023-05-04 14:08:53 +02:00
committed by GitHub
parent 010b600f39
commit dfd35fabb4
8 changed files with 294 additions and 163 deletions

View File

@@ -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<_, _>>(),

View File

@@ -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
}
}

View File

@@ -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(())
}

View 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()
}
}

View File

@@ -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> {

View File

@@ -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;

View File

@@ -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> {

View File

@@ -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:?}"
)]