chore(docs): clarify tree canonical chain docs (#8408)

This commit is contained in:
Matthias Seitz
2024-05-27 14:31:33 +02:00
committed by GitHub
parent 2e47e9fb0d
commit 2bf5c93039
4 changed files with 49 additions and 66 deletions

View File

@ -17,9 +17,9 @@ use std::collections::{btree_map, hash_map, BTreeMap, BTreeSet, HashMap, HashSet
pub struct BlockIndices {
/// Last finalized block.
last_finalized_block: BlockNumber,
/// 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.
/// Non-finalized 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 sidechain specific indices.
canonical_chain: CanonicalChain,
/// Index needed when discarding the chain, so we can remove connected chains from tree.
///
@ -101,14 +101,6 @@ impl BlockIndices {
(canonical_tip.number + 1, pending_blocks)
}
/// Returns the block number of the canonical block with the given hash.
///
/// Returns `None` if no block could be found in the canonical chain.
#[inline]
pub(crate) fn get_canonical_block_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
self.canonical_chain.get_canonical_block_number(self.last_finalized_block, block_hash)
}
/// Last finalized block
pub fn last_finalized_block(&self) -> BlockNumber {
self.last_finalized_block
@ -138,8 +130,8 @@ impl BlockIndices {
self.fork_to_child.entry(first.parent_hash).or_default().insert_if_absent(first.hash());
}
/// Get the chain ID the block belongs to
pub(crate) fn get_blocks_chain_id(&self, block: &BlockHash) -> Option<BlockchainId> {
/// Get the [BlockchainId] the given block belongs to if it exists.
pub(crate) fn get_block_chain_id(&self, block: &BlockHash) -> Option<BlockchainId> {
self.blocks_to_chain.get(block).cloned()
}
@ -370,7 +362,7 @@ impl BlockIndices {
/// Returns the block number of the canonical block with the given hash.
#[inline]
pub fn canonical_number(&self, block_hash: BlockHash) -> Option<BlockNumber> {
pub fn canonical_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
self.canonical_chain.canonical_number(block_hash)
}

View File

@ -215,7 +215,7 @@ where
}
// is block inside chain
if let Some(attachment) = self.is_block_inside_chain(&block) {
if let Some(attachment) = self.is_block_inside_sidechain(&block) {
return Ok(Some(BlockStatus::Valid(attachment)))
}
@ -260,7 +260,7 @@ where
}
/// Returns true if the block is included in a side-chain.
fn is_block_hash_inside_chain(&self, block_hash: BlockHash) -> bool {
fn is_block_hash_inside_sidechain(&self, block_hash: BlockHash) -> bool {
self.block_by_hash(block_hash).is_some()
}
@ -287,7 +287,7 @@ where
let canonical_chain = self.state.block_indices.canonical_chain();
// if it is part of the chain
if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&block_hash) {
if let Some(chain_id) = self.block_indices().get_block_chain_id(&block_hash) {
trace!(target: "blockchain_tree", ?block_hash, "Constructing post state data based on non-canonical chain");
// get block state
let Some(chain) = self.state.chains.get(&chain_id) else {
@ -318,7 +318,7 @@ where
}
// check if there is canonical block
if let Some(canonical_number) = canonical_chain.canonical_number(block_hash) {
if let Some(canonical_number) = canonical_chain.canonical_number(&block_hash) {
trace!(target: "blockchain_tree", %block_hash, "Constructing post state data based on canonical chain");
return Some(BundleStateData {
canonical_fork: ForkBlock { number: canonical_number, hash: block_hash },
@ -345,7 +345,7 @@ where
let parent = block.parent_num_hash();
// check if block parent can be found in any side chain.
if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&parent.hash) {
if let Some(chain_id) = self.block_indices().get_block_chain_id(&parent.hash) {
// found parent in side tree, try to insert there
return self.try_insert_block_into_side_chain(block, chain_id, block_validation_kind)
}
@ -358,7 +358,7 @@ where
// this is another check to ensure that if the block points to a canonical block its block
// is valid
if let Some(canonical_parent_number) =
self.block_indices().canonical_number(block.parent_hash)
self.block_indices().canonical_number(&block.parent_hash)
{
// we found the parent block in canonical chain
if canonical_parent_number != parent.number {
@ -458,7 +458,7 @@ where
/// Try inserting a block into the given side chain.
///
/// WARNING: This expects a valid side chain id, see [BlockIndices::get_blocks_chain_id]
/// WARNING: This expects a valid side chain id, see [BlockIndices::get_block_chain_id]
#[instrument(level = "trace", skip_all, target = "blockchain_tree")]
fn try_insert_block_into_side_chain(
&mut self,
@ -557,8 +557,7 @@ where
}
let fork_block = chain.fork_block();
if let Some(next_chain_id) = self.block_indices().get_blocks_chain_id(&fork_block.hash)
{
if let Some(next_chain_id) = self.block_indices().get_block_chain_id(&fork_block.hash) {
chain_id = next_chain_id;
} else {
// if there is no fork block that point to other chains, break the loop.
@ -582,7 +581,7 @@ where
// chain fork block
fork = self.state.chains.get(&chain_id)?.fork_block();
// get fork block chain
if let Some(fork_chain_id) = self.block_indices().get_blocks_chain_id(&fork.hash) {
if let Some(fork_chain_id) = self.block_indices().get_block_chain_id(&fork.hash) {
chain_id = fork_chain_id;
continue
}
@ -608,7 +607,7 @@ where
while let Some(block) = dependent_block.pop_back() {
// Get chain of dependent block.
let Some(chain_id) = self.block_indices().get_blocks_chain_id(&block) else {
let Some(chain_id) = self.block_indices().get_block_chain_id(&block) else {
debug!(target: "blockchain_tree", ?block, "Block not in tree");
return Default::default();
};
@ -735,15 +734,15 @@ where
Ok(())
}
/// Check if block is found inside chain and its attachment.
/// Check if block is found inside a sidechain and its attachment.
///
/// if it is canonical or extends the canonical chain, return [BlockAttachment::Canonical]
/// if it does not extend the canonical chain, return [BlockAttachment::HistoricalFork]
/// if the block is not in the tree or its chain id is not valid, return None
#[track_caller]
fn is_block_inside_chain(&self, block: &BlockNumHash) -> Option<BlockAttachment> {
fn is_block_inside_sidechain(&self, block: &BlockNumHash) -> Option<BlockAttachment> {
// check if block known and is already in the tree
if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&block.hash) {
if let Some(chain_id) = self.block_indices().get_block_chain_id(&block.hash) {
// find the canonical fork of this chain
let Some(canonical_fork) = self.canonical_fork(chain_id) else {
debug!(target: "blockchain_tree", chain_id=?chain_id, block=?block.hash, "Chain id not valid");
@ -981,22 +980,25 @@ where
///
/// Returns `Ok(None)` if the block hash is not canonical (block hash does not exist, or is
/// included in a sidechain).
///
/// Note: this does not distinguish between a block that is finalized and a block that is not
/// finalized yet, only whether it is part of the canonical chain or not.
pub fn find_canonical_header(
&self,
hash: &BlockHash,
) -> Result<Option<SealedHeader>, ProviderError> {
// if the indices show that the block hash is not canonical, it's either in a sidechain or
// canonical, but in the db. If it is in a sidechain, it is not canonical. If it is not in
// the db, then it is not canonical.
// canonical, but in the db. If it is in a sidechain, it is not canonical. If it is missing
// in the db, then it is also not canonical.
let provider = self.externals.provider_factory.provider()?;
let mut header = None;
if let Some(num) = self.block_indices().get_canonical_block_number(hash) {
if let Some(num) = self.block_indices().canonical_number(hash) {
header = provider.header_by_number(num)?;
}
if header.is_none() && self.is_block_hash_inside_chain(*hash) {
if header.is_none() && self.is_block_hash_inside_sidechain(*hash) {
return Ok(None)
}
@ -1008,6 +1010,9 @@ where
}
/// Determines whether or not a block is canonical, checking the db if necessary.
///
/// Note: this does not distinguish between a block that is finalized and a block that is not
/// finalized yet, only whether it is part of the canonical chain or not.
pub fn is_block_hash_canonical(&self, hash: &BlockHash) -> Result<bool, ProviderError> {
self.find_canonical_header(hash).map(|header| header.is_some())
}
@ -1062,7 +1067,7 @@ where
return Ok(CanonicalOutcome::AlreadyCanonical { header })
}
let Some(chain_id) = self.block_indices().get_blocks_chain_id(&block_hash) else {
let Some(chain_id) = self.block_indices().get_block_chain_id(&block_hash) else {
debug!(target: "blockchain_tree", ?block_hash, "Block hash not found in block indices");
return Err(CanonicalError::from(BlockchainTreeError::BlockHashNotFoundInChain {
block_hash,
@ -1085,7 +1090,7 @@ where
let mut chains_to_promote = vec![canonical];
// loop while fork blocks are found in Tree.
while let Some(chain_id) = self.block_indices().get_blocks_chain_id(&fork_block.hash) {
while let Some(chain_id) = self.block_indices().get_block_chain_id(&fork_block.hash) {
// canonical chain is lower part of the chain.
let Some(canonical) =
self.remove_and_split_chain(chain_id, ChainSplitTarget::Number(fork_block.number))
@ -1200,7 +1205,7 @@ where
.unwrap_or_default()
.into_iter()
.for_each(|child| {
if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&child) {
if let Some(chain_id) = self.block_indices().get_block_chain_id(&child) {
if let Some(chain) = self.state.chains.get_mut(&chain_id) {
chain.clear_trie_updates();
}
@ -1307,8 +1312,8 @@ where
// This should only happen when an optimistic sync target was re-orged.
//
// Static files generally contain finalized data. The blockchain tree only deals
// with unfinalized data. The only scenario where canonical reverts go past the highest
// static file is when an optimistic sync occured and unfinalized data was written to
// with non-finalized data. The only scenario where canonical reverts go past the highest
// static file is when an optimistic sync occurred and non-finalized data was written to
// static files.
if self
.externals
@ -1780,7 +1785,7 @@ mod tests {
);
let block3a_chain_id =
tree.state.block_indices.get_blocks_chain_id(&block3a.hash()).unwrap();
tree.state.block_indices.get_block_chain_id(&block3a.hash()).unwrap();
assert_eq!(
tree.all_chain_hashes(block3a_chain_id),
BTreeMap::from([
@ -1820,7 +1825,7 @@ mod tests {
tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(),
InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical))
);
let block1_chain_id = tree.state.block_indices.get_blocks_chain_id(&block1.hash()).unwrap();
let block1_chain_id = tree.state.block_indices.get_block_chain_id(&block1.hash()).unwrap();
let block1_chain = tree.state.chains.get(&block1_chain_id).unwrap();
assert!(block1_chain.trie_updates().is_some());
@ -1828,7 +1833,7 @@ mod tests {
tree.insert_block(block2.clone(), BlockValidationKind::Exhaustive).unwrap(),
InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical))
);
let block2_chain_id = tree.state.block_indices.get_blocks_chain_id(&block2.hash()).unwrap();
let block2_chain_id = tree.state.block_indices.get_block_chain_id(&block2.hash()).unwrap();
let block2_chain = tree.state.chains.get(&block2_chain_id).unwrap();
assert!(block2_chain.trie_updates().is_none());
@ -1841,7 +1846,7 @@ mod tests {
tree.insert_block(block3.clone(), BlockValidationKind::Exhaustive).unwrap(),
InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical))
);
let block3_chain_id = tree.state.block_indices.get_blocks_chain_id(&block3.hash()).unwrap();
let block3_chain_id = tree.state.block_indices.get_block_chain_id(&block3.hash()).unwrap();
let block3_chain = tree.state.chains.get(&block3_chain_id).unwrap();
assert!(block3_chain.trie_updates().is_some());
@ -1854,7 +1859,7 @@ mod tests {
tree.insert_block(block4.clone(), BlockValidationKind::Exhaustive).unwrap(),
InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical))
);
let block4_chain_id = tree.state.block_indices.get_blocks_chain_id(&block4.hash()).unwrap();
let block4_chain_id = tree.state.block_indices.get_block_chain_id(&block4.hash()).unwrap();
let block4_chain = tree.state.chains.get(&block4_chain_id).unwrap();
assert!(block4_chain.trie_updates().is_some());
@ -1863,7 +1868,7 @@ mod tests {
InsertPayloadOk::Inserted(BlockStatus::Valid(BlockAttachment::Canonical))
);
let block5_chain_id = tree.state.block_indices.get_blocks_chain_id(&block5.hash()).unwrap();
let block5_chain_id = tree.state.block_indices.get_block_chain_id(&block5.hash()).unwrap();
let block5_chain = tree.state.chains.get(&block5_chain_id).unwrap();
assert!(block5_chain.trie_updates().is_none());

View File

@ -1,13 +1,13 @@
use reth_primitives::{BlockHash, BlockNumHash, BlockNumber};
use std::collections::BTreeMap;
/// This keeps track of all blocks of the canonical chain.
/// This keeps track of (non-finalized) 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.
/// canonical chain that is not yet finalized.
#[derive(Debug, Clone, Default)]
pub(crate) struct CanonicalChain {
/// All blocks of the canonical chain in order.
/// All blocks of the canonical chain in order of their block number.
chain: BTreeMap<BlockNumber, BlockHash>,
}
@ -22,18 +22,18 @@ impl CanonicalChain {
self.chain = chain;
}
/// Returns the block hash of the canonical block with the given number.
/// Returns the block hash of the (non-finalized) 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.
/// Returns the block number of the (non-finalized) canonical block with the given hash.
#[inline]
pub(crate) fn canonical_number(&self, block_hash: BlockHash) -> Option<BlockNumber> {
pub(crate) fn canonical_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
self.chain.iter().find_map(
|(number, hash)| {
if *hash == block_hash {
if hash == block_hash {
Some(*number)
} else {
None
@ -42,20 +42,6 @@ impl CanonicalChain {
)
}
/// Returns the block number of the canonical block with the given hash.
///
/// Returns `None` if no block could be found in the canonical chain.
#[inline]
pub(crate) fn get_canonical_block_number(
&self,
last_finalized_block: BlockNumber,
block_hash: &BlockHash,
) -> Option<BlockNumber> {
self.chain
.range(last_finalized_block..)
.find_map(|(num, &h)| (h == *block_hash).then_some(*num))
}
/// Extends all items from the given iterator to the chain.
#[inline]
pub(crate) fn extend(&mut self, blocks: impl Iterator<Item = (BlockNumber, BlockHash)>) {

View File

@ -68,7 +68,7 @@ impl TreeState {
&self,
block_hash: BlockHash,
) -> Option<&SealedBlockWithSenders> {
let id = self.block_indices.get_blocks_chain_id(&block_hash)?;
let id = self.block_indices.get_block_chain_id(&block_hash)?;
let chain = self.chains.get(&id)?;
chain.block_with_senders(block_hash)
}
@ -77,7 +77,7 @@ impl TreeState {
///
/// Caution: This will not return blocks from the canonical chain.
pub(crate) fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<Vec<&Receipt>> {
let id = self.block_indices.get_blocks_chain_id(&block_hash)?;
let id = self.block_indices.get_block_chain_id(&block_hash)?;
let chain = self.chains.get(&id)?;
chain.receipts_by_block_hash(block_hash)
}