mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
perf: introduce BlockValidationKind (#5195)
This commit is contained in:
@ -10,7 +10,7 @@ use reth_db::database::Database;
|
|||||||
use reth_interfaces::{
|
use reth_interfaces::{
|
||||||
blockchain_tree::{
|
blockchain_tree::{
|
||||||
error::{BlockchainTreeError, CanonicalError, InsertBlockError, InsertBlockErrorKind},
|
error::{BlockchainTreeError, CanonicalError, InsertBlockError, InsertBlockErrorKind},
|
||||||
BlockStatus, CanonicalOutcome, InsertPayloadOk,
|
BlockStatus, BlockValidationKind, CanonicalOutcome, InsertPayloadOk,
|
||||||
},
|
},
|
||||||
consensus::{Consensus, ConsensusError},
|
consensus::{Consensus, ConsensusError},
|
||||||
executor::{BlockExecutionError, BlockValidationError},
|
executor::{BlockExecutionError, BlockValidationError},
|
||||||
@ -286,12 +286,13 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
|
|
||||||
/// Try inserting a validated [Self::validate_block] block inside the tree.
|
/// Try inserting a validated [Self::validate_block] block inside the tree.
|
||||||
///
|
///
|
||||||
/// If blocks does not have parent [`BlockStatus::Disconnected`] would be returned, in which
|
/// If the block's parent block is unknown, this returns [`BlockStatus::Disconnected`] and the
|
||||||
/// case it is buffered for future inclusion.
|
/// block will be buffered until the parent block is inserted and then attached.
|
||||||
#[instrument(level = "trace", skip_all, fields(block = ?block.num_hash()), target = "blockchain_tree", ret)]
|
#[instrument(level = "trace", skip_all, fields(block = ?block.num_hash()), target = "blockchain_tree", ret)]
|
||||||
fn try_insert_validated_block(
|
fn try_insert_validated_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> Result<BlockStatus, InsertBlockError> {
|
) -> Result<BlockStatus, InsertBlockError> {
|
||||||
debug_assert!(self.validate_block(&block).is_ok(), "Block must be validated");
|
debug_assert!(self.validate_block(&block).is_ok(), "Block must be validated");
|
||||||
|
|
||||||
@ -300,7 +301,7 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
// check if block parent can be found in Tree
|
// check if block parent can be found in Tree
|
||||||
if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&parent.hash) {
|
if let Some(chain_id) = self.block_indices().get_blocks_chain_id(&parent.hash) {
|
||||||
// found parent in side tree, try to insert there
|
// found parent in side tree, try to insert there
|
||||||
return self.try_insert_block_into_side_chain(block, chain_id)
|
return self.try_insert_block_into_side_chain(block, chain_id, block_validation_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not found, check if the parent can be found inside canonical chain.
|
// if not found, check if the parent can be found inside canonical chain.
|
||||||
@ -308,7 +309,7 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
.is_block_hash_canonical(&parent.hash)
|
.is_block_hash_canonical(&parent.hash)
|
||||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?
|
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?
|
||||||
{
|
{
|
||||||
return self.try_append_canonical_chain(block)
|
return self.try_append_canonical_chain(block, block_validation_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is another check to ensure that if the block points to a canonical block its block
|
// this is another check to ensure that if the block points to a canonical block its block
|
||||||
@ -362,6 +363,7 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
fn try_append_canonical_chain(
|
fn try_append_canonical_chain(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> Result<BlockStatus, InsertBlockError> {
|
) -> Result<BlockStatus, InsertBlockError> {
|
||||||
let parent = block.parent_num_hash();
|
let parent = block.parent_num_hash();
|
||||||
let block_num_hash = block.num_hash();
|
let block_num_hash = block.num_hash();
|
||||||
@ -417,8 +419,15 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
canonical_chain.inner(),
|
canonical_chain.inner(),
|
||||||
parent,
|
parent,
|
||||||
&self.externals,
|
&self.externals,
|
||||||
|
block_validation_kind,
|
||||||
)?;
|
)?;
|
||||||
(BlockStatus::Valid, chain)
|
let status = if block_validation_kind.is_exhaustive() {
|
||||||
|
BlockStatus::Valid
|
||||||
|
} else {
|
||||||
|
BlockStatus::Accepted
|
||||||
|
};
|
||||||
|
|
||||||
|
(status, chain)
|
||||||
} else {
|
} else {
|
||||||
let chain = AppendableChain::new_canonical_fork(
|
let chain = AppendableChain::new_canonical_fork(
|
||||||
block,
|
block,
|
||||||
@ -444,6 +453,7 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
chain_id: BlockChainId,
|
chain_id: BlockChainId,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> Result<BlockStatus, InsertBlockError> {
|
) -> Result<BlockStatus, InsertBlockError> {
|
||||||
debug!(target: "blockchain_tree", "Inserting block into side chain");
|
debug!(target: "blockchain_tree", "Inserting block into side chain");
|
||||||
let block_num_hash = block.num_hash();
|
let block_num_hash = block.num_hash();
|
||||||
@ -495,11 +505,12 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
&self.externals,
|
&self.externals,
|
||||||
canonical_fork,
|
canonical_fork,
|
||||||
block_kind,
|
block_kind,
|
||||||
|
block_validation_kind,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.block_indices_mut().insert_non_fork_block(block_number, block_hash, chain_id);
|
self.block_indices_mut().insert_non_fork_block(block_number, block_hash, chain_id);
|
||||||
|
|
||||||
if block_kind.extends_canonical_head() {
|
if block_kind.extends_canonical_head() && block_validation_kind.is_exhaustive() {
|
||||||
// if the block can be traced back to the canonical head, we were able to fully
|
// if the block can be traced back to the canonical head, we were able to fully
|
||||||
// validate it
|
// validate it
|
||||||
Ok(BlockStatus::Valid)
|
Ok(BlockStatus::Valid)
|
||||||
@ -602,7 +613,7 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
block: SealedBlock,
|
block: SealedBlock,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError> {
|
) -> Result<InsertPayloadOk, InsertBlockError> {
|
||||||
match block.try_seal_with_senders() {
|
match block.try_seal_with_senders() {
|
||||||
Ok(block) => self.insert_block(block),
|
Ok(block) => self.insert_block(block, BlockValidationKind::Exhaustive),
|
||||||
Err(block) => Err(InsertBlockError::sender_recovery_error(block)),
|
Err(block) => Err(InsertBlockError::sender_recovery_error(block)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,6 +692,9 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
/// This means that if the block becomes canonical, we need to fetch the missing blocks over
|
/// This means that if the block becomes canonical, we need to fetch the missing blocks over
|
||||||
/// P2P.
|
/// P2P.
|
||||||
///
|
///
|
||||||
|
/// If the [BlockValidationKind::SkipStateRootValidation] is provided the state root is not
|
||||||
|
/// validated.
|
||||||
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// If the senders have not already been recovered, call
|
/// If the senders have not already been recovered, call
|
||||||
@ -688,6 +702,7 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
pub fn insert_block(
|
pub fn insert_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError> {
|
) -> Result<InsertPayloadOk, InsertBlockError> {
|
||||||
// check if we already have this block
|
// check if we already have this block
|
||||||
match self.is_block_known(block.num_hash()) {
|
match self.is_block_known(block.num_hash()) {
|
||||||
@ -701,7 +716,9 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
return Err(InsertBlockError::consensus_error(err, block.block))
|
return Err(InsertBlockError::consensus_error(err, block.block))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(InsertPayloadOk::Inserted(self.try_insert_validated_block(block)?))
|
Ok(InsertPayloadOk::Inserted(
|
||||||
|
self.try_insert_validated_block(block, block_validation_kind)?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalize blocks up until and including `finalized_block`, and remove them from the tree.
|
/// Finalize blocks up until and including `finalized_block`, and remove them from the tree.
|
||||||
@ -803,17 +820,20 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
|||||||
fn try_connect_buffered_blocks(&mut self, new_block: BlockNumHash) {
|
fn try_connect_buffered_blocks(&mut self, new_block: BlockNumHash) {
|
||||||
trace!(target: "blockchain_tree", ?new_block, "try_connect_buffered_blocks");
|
trace!(target: "blockchain_tree", ?new_block, "try_connect_buffered_blocks");
|
||||||
|
|
||||||
|
// first remove all the children of the new block from the buffer
|
||||||
let include_blocks = self.state.buffered_blocks.remove_with_children(new_block);
|
let include_blocks = self.state.buffered_blocks.remove_with_children(new_block);
|
||||||
// insert block children
|
// then try to reinsert them into the tree
|
||||||
for block in include_blocks.into_iter() {
|
for block in include_blocks.into_iter() {
|
||||||
// dont fail on error, just ignore the block.
|
// dont fail on error, just ignore the block.
|
||||||
let _ = self.try_insert_validated_block(block).map_err(|err| {
|
let _ = self
|
||||||
debug!(
|
.try_insert_validated_block(block, BlockValidationKind::SkipStateRootValidation)
|
||||||
target: "blockchain_tree", ?err,
|
.map_err(|err| {
|
||||||
"Failed to insert buffered block",
|
debug!(
|
||||||
);
|
target: "blockchain_tree", ?err,
|
||||||
err
|
"Failed to insert buffered block",
|
||||||
});
|
);
|
||||||
|
err
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1314,7 +1334,7 @@ mod tests {
|
|||||||
|
|
||||||
// block 2 parent is not known, block2 is buffered.
|
// block 2 parent is not known, block2 is buffered.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block2.clone()).unwrap(),
|
tree.insert_block(block2.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::Inserted(BlockStatus::Disconnected {
|
InsertPayloadOk::Inserted(BlockStatus::Disconnected {
|
||||||
missing_ancestor: block2.parent_num_hash()
|
missing_ancestor: block2.parent_num_hash()
|
||||||
})
|
})
|
||||||
@ -1346,7 +1366,7 @@ mod tests {
|
|||||||
|
|
||||||
// insert block1 and buffered block2 is inserted
|
// insert block1 and buffered block2 is inserted
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block1.clone()).unwrap(),
|
tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::Inserted(BlockStatus::Valid)
|
InsertPayloadOk::Inserted(BlockStatus::Valid)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1369,13 +1389,13 @@ mod tests {
|
|||||||
|
|
||||||
// already inserted block will `InsertPayloadOk::AlreadySeen(_)`
|
// already inserted block will `InsertPayloadOk::AlreadySeen(_)`
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block1.clone()).unwrap(),
|
tree.insert_block(block1.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::AlreadySeen(BlockStatus::Valid)
|
InsertPayloadOk::AlreadySeen(BlockStatus::Valid)
|
||||||
);
|
);
|
||||||
|
|
||||||
// block two is already inserted.
|
// block two is already inserted.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block2.clone()).unwrap(),
|
tree.insert_block(block2.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::AlreadySeen(BlockStatus::Valid)
|
InsertPayloadOk::AlreadySeen(BlockStatus::Valid)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1415,7 +1435,7 @@ mod tests {
|
|||||||
|
|
||||||
// reinsert two blocks that point to canonical chain
|
// reinsert two blocks that point to canonical chain
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block1a.clone()).unwrap(),
|
tree.insert_block(block1a.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::Inserted(BlockStatus::Accepted)
|
InsertPayloadOk::Inserted(BlockStatus::Accepted)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1430,7 +1450,7 @@ mod tests {
|
|||||||
.assert(&tree);
|
.assert(&tree);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block2a.clone()).unwrap(),
|
tree.insert_block(block2a.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::Inserted(BlockStatus::Accepted)
|
InsertPayloadOk::Inserted(BlockStatus::Accepted)
|
||||||
);
|
);
|
||||||
// Trie state:
|
// Trie state:
|
||||||
@ -1620,7 +1640,7 @@ mod tests {
|
|||||||
block2b.parent_hash = B256::new([0x88; 32]);
|
block2b.parent_hash = B256::new([0x88; 32]);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.insert_block(block2b.clone()).unwrap(),
|
tree.insert_block(block2b.clone(), BlockValidationKind::Exhaustive).unwrap(),
|
||||||
InsertPayloadOk::Inserted(BlockStatus::Disconnected {
|
InsertPayloadOk::Inserted(BlockStatus::Disconnected {
|
||||||
missing_ancestor: block2b.parent_num_hash()
|
missing_ancestor: block2b.parent_num_hash()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -6,7 +6,10 @@ use super::externals::TreeExternals;
|
|||||||
use crate::BundleStateDataRef;
|
use crate::BundleStateDataRef;
|
||||||
use reth_db::database::Database;
|
use reth_db::database::Database;
|
||||||
use reth_interfaces::{
|
use reth_interfaces::{
|
||||||
blockchain_tree::error::{BlockchainTreeError, InsertBlockError},
|
blockchain_tree::{
|
||||||
|
error::{BlockchainTreeError, InsertBlockError},
|
||||||
|
BlockValidationKind,
|
||||||
|
},
|
||||||
consensus::{Consensus, ConsensusError},
|
consensus::{Consensus, ConsensusError},
|
||||||
RethResult,
|
RethResult,
|
||||||
};
|
};
|
||||||
@ -54,15 +57,17 @@ impl AppendableChain {
|
|||||||
self.chain
|
self.chain
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new chain that forks off the canonical.
|
/// Create a new chain that forks off the canonical chain.
|
||||||
///
|
///
|
||||||
/// This will also verify the state root of the block extending the canonical chain.
|
/// if [BlockValidationKind::Exhaustive] is provides this will verify the state root of the
|
||||||
|
/// block extending the canonical chain.
|
||||||
pub fn new_canonical_head_fork<DB, EF>(
|
pub fn new_canonical_head_fork<DB, EF>(
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
parent_header: &SealedHeader,
|
parent_header: &SealedHeader,
|
||||||
canonical_block_hashes: &BTreeMap<BlockNumber, BlockHash>,
|
canonical_block_hashes: &BTreeMap<BlockNumber, BlockHash>,
|
||||||
canonical_fork: ForkBlock,
|
canonical_fork: ForkBlock,
|
||||||
externals: &TreeExternals<DB, EF>,
|
externals: &TreeExternals<DB, EF>,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> Result<Self, InsertBlockError>
|
) -> Result<Self, InsertBlockError>
|
||||||
where
|
where
|
||||||
DB: Database,
|
DB: Database,
|
||||||
@ -78,11 +83,13 @@ impl AppendableChain {
|
|||||||
canonical_fork,
|
canonical_fork,
|
||||||
};
|
};
|
||||||
|
|
||||||
let bundle_state = Self::validate_and_execute_canonical_head_descendant(
|
let bundle_state = Self::validate_and_execute(
|
||||||
block.clone(),
|
block.clone(),
|
||||||
parent_header,
|
parent_header,
|
||||||
state_provider,
|
state_provider,
|
||||||
externals,
|
externals,
|
||||||
|
BlockKind::ExtendsCanonicalHead,
|
||||||
|
block_validation_kind,
|
||||||
)
|
)
|
||||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||||
|
|
||||||
@ -170,13 +177,21 @@ impl AppendableChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Validate and execute the given block that _extends the canonical chain_, validating its
|
/// Validate and execute the given block that _extends the canonical chain_, validating its
|
||||||
/// state root after execution.
|
/// state root after execution if possible and requested.
|
||||||
|
///
|
||||||
|
/// Note: State root validation is limited to blocks that extend the canonical chain and is
|
||||||
|
/// optional, see [BlockValidationKind]. So this function takes two parameters to determine
|
||||||
|
/// if the state can and should be validated.
|
||||||
|
/// - [BlockKind] represents if the block extends the canonical chain, and thus if the state
|
||||||
|
/// root __can__ be validated.
|
||||||
|
/// - [BlockValidationKind] determines if the state root __should__ be validated.
|
||||||
fn validate_and_execute<BSDP, DB, EF>(
|
fn validate_and_execute<BSDP, DB, EF>(
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
parent_block: &SealedHeader,
|
parent_block: &SealedHeader,
|
||||||
post_state_data_provider: BSDP,
|
post_state_data_provider: BSDP,
|
||||||
externals: &TreeExternals<DB, EF>,
|
externals: &TreeExternals<DB, EF>,
|
||||||
block_kind: BlockKind,
|
block_kind: BlockKind,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> RethResult<BundleStateWithReceipts>
|
) -> RethResult<BundleStateWithReceipts>
|
||||||
where
|
where
|
||||||
BSDP: BundleStateDataProvider,
|
BSDP: BundleStateDataProvider,
|
||||||
@ -200,8 +215,9 @@ impl AppendableChain {
|
|||||||
executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?;
|
executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?;
|
||||||
let bundle_state = executor.take_output_state();
|
let bundle_state = executor.take_output_state();
|
||||||
|
|
||||||
// check state root if the block extends the canonical chain.
|
// check state root if the block extends the canonical chain __and__ if state root
|
||||||
if block_kind.extends_canonical_head() {
|
// validation was requested.
|
||||||
|
if block_kind.extends_canonical_head() && block_validation_kind.is_exhaustive() {
|
||||||
// check state root
|
// check state root
|
||||||
let state_root = provider.state_root(&bundle_state)?;
|
let state_root = provider.state_root(&bundle_state)?;
|
||||||
if block.state_root != state_root {
|
if block.state_root != state_root {
|
||||||
@ -216,28 +232,6 @@ impl AppendableChain {
|
|||||||
Ok(bundle_state)
|
Ok(bundle_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate and execute the given block that _extends the canonical chain_, validating its
|
|
||||||
/// state root after execution.
|
|
||||||
fn validate_and_execute_canonical_head_descendant<BSDP, DB, EF>(
|
|
||||||
block: SealedBlockWithSenders,
|
|
||||||
parent_block: &SealedHeader,
|
|
||||||
post_state_data_provider: BSDP,
|
|
||||||
externals: &TreeExternals<DB, EF>,
|
|
||||||
) -> RethResult<BundleStateWithReceipts>
|
|
||||||
where
|
|
||||||
BSDP: BundleStateDataProvider,
|
|
||||||
DB: Database,
|
|
||||||
EF: ExecutorFactory,
|
|
||||||
{
|
|
||||||
Self::validate_and_execute(
|
|
||||||
block,
|
|
||||||
parent_block,
|
|
||||||
post_state_data_provider,
|
|
||||||
externals,
|
|
||||||
BlockKind::ExtendsCanonicalHead,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate and execute the given sidechain block, skipping state root validation.
|
/// Validate and execute the given sidechain block, skipping state root validation.
|
||||||
fn validate_and_execute_sidechain<BSDP, DB, EF>(
|
fn validate_and_execute_sidechain<BSDP, DB, EF>(
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
@ -256,6 +250,7 @@ impl AppendableChain {
|
|||||||
post_state_data_provider,
|
post_state_data_provider,
|
||||||
externals,
|
externals,
|
||||||
BlockKind::ForksHistoricalBlock,
|
BlockKind::ForksHistoricalBlock,
|
||||||
|
BlockValidationKind::SkipStateRootValidation,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +266,7 @@ impl AppendableChain {
|
|||||||
/// is the canonical head, or: state root check can't be performed if the given canonical is
|
/// is the canonical head, or: state root check can't be performed if the given canonical is
|
||||||
/// __not__ the canonical head.
|
/// __not__ the canonical head.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn append_block<DB, EF>(
|
pub(crate) fn append_block<DB, EF>(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
@ -279,6 +275,7 @@ impl AppendableChain {
|
|||||||
externals: &TreeExternals<DB, EF>,
|
externals: &TreeExternals<DB, EF>,
|
||||||
canonical_fork: ForkBlock,
|
canonical_fork: ForkBlock,
|
||||||
block_kind: BlockKind,
|
block_kind: BlockKind,
|
||||||
|
block_validation_kind: BlockValidationKind,
|
||||||
) -> Result<(), InsertBlockError>
|
) -> Result<(), InsertBlockError>
|
||||||
where
|
where
|
||||||
DB: Database,
|
DB: Database,
|
||||||
@ -299,6 +296,7 @@ impl AppendableChain {
|
|||||||
post_state_data,
|
post_state_data,
|
||||||
externals,
|
externals,
|
||||||
block_kind,
|
block_kind,
|
||||||
|
block_validation_kind,
|
||||||
)
|
)
|
||||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||||
// extend the state.
|
// extend the state.
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use reth_interfaces::{
|
use reth_interfaces::{
|
||||||
blockchain_tree::{
|
blockchain_tree::{
|
||||||
error::{BlockchainTreeError, InsertBlockError},
|
error::{BlockchainTreeError, InsertBlockError},
|
||||||
BlockchainTreeEngine, BlockchainTreeViewer, CanonicalOutcome, InsertPayloadOk,
|
BlockValidationKind, BlockchainTreeEngine, BlockchainTreeViewer, CanonicalOutcome,
|
||||||
|
InsertPayloadOk,
|
||||||
},
|
},
|
||||||
RethResult,
|
RethResult,
|
||||||
};
|
};
|
||||||
@ -30,6 +31,7 @@ impl BlockchainTreeEngine for NoopBlockchainTree {
|
|||||||
fn insert_block(
|
fn insert_block(
|
||||||
&self,
|
&self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
_validation_kind: BlockValidationKind,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError> {
|
) -> Result<InsertPayloadOk, InsertBlockError> {
|
||||||
Err(InsertBlockError::tree_error(
|
Err(InsertBlockError::tree_error(
|
||||||
BlockchainTreeError::BlockHashNotFoundInChain { block_hash: block.hash },
|
BlockchainTreeError::BlockHashNotFoundInChain { block_hash: block.hash },
|
||||||
|
|||||||
@ -4,8 +4,8 @@ use parking_lot::RwLock;
|
|||||||
use reth_db::database::Database;
|
use reth_db::database::Database;
|
||||||
use reth_interfaces::{
|
use reth_interfaces::{
|
||||||
blockchain_tree::{
|
blockchain_tree::{
|
||||||
error::InsertBlockError, BlockchainTreeEngine, BlockchainTreeViewer, CanonicalOutcome,
|
error::InsertBlockError, BlockValidationKind, BlockchainTreeEngine, BlockchainTreeViewer,
|
||||||
InsertPayloadOk,
|
CanonicalOutcome, InsertPayloadOk,
|
||||||
},
|
},
|
||||||
RethResult,
|
RethResult,
|
||||||
};
|
};
|
||||||
@ -48,10 +48,11 @@ impl<DB: Database, EF: ExecutorFactory> BlockchainTreeEngine for ShareableBlockc
|
|||||||
fn insert_block(
|
fn insert_block(
|
||||||
&self,
|
&self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
validation_kind: BlockValidationKind,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError> {
|
) -> Result<InsertPayloadOk, InsertBlockError> {
|
||||||
trace!(target: "blockchain_tree", hash=?block.hash, number=block.number, parent_hash=?block.parent_hash, "Inserting block");
|
trace!(target: "blockchain_tree", hash=?block.hash, number=block.number, parent_hash=?block.parent_hash, "Inserting block");
|
||||||
let mut tree = self.tree.write();
|
let mut tree = self.tree.write();
|
||||||
let res = tree.insert_block(block);
|
let res = tree.insert_block(block, validation_kind);
|
||||||
tree.update_chains_metrics();
|
tree.update_chains_metrics();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ pub use handle::BeaconConsensusEngineHandle;
|
|||||||
mod forkchoice;
|
mod forkchoice;
|
||||||
use crate::hooks::{EngineHookEvent, EngineHooks, PolledHook};
|
use crate::hooks::{EngineHookEvent, EngineHooks, PolledHook};
|
||||||
pub use forkchoice::ForkchoiceStatus;
|
pub use forkchoice::ForkchoiceStatus;
|
||||||
|
use reth_interfaces::blockchain_tree::BlockValidationKind;
|
||||||
|
|
||||||
mod metrics;
|
mod metrics;
|
||||||
|
|
||||||
@ -1294,7 +1295,9 @@ where
|
|||||||
debug_assert!(self.sync.is_pipeline_idle(), "pipeline must be idle");
|
debug_assert!(self.sync.is_pipeline_idle(), "pipeline must be idle");
|
||||||
|
|
||||||
let block_hash = block.hash;
|
let block_hash = block.hash;
|
||||||
let status = self.blockchain.insert_block_without_senders(block.clone())?;
|
let status = self
|
||||||
|
.blockchain
|
||||||
|
.insert_block_without_senders(block.clone(), BlockValidationKind::Exhaustive)?;
|
||||||
let mut latest_valid_hash = None;
|
let mut latest_valid_hash = None;
|
||||||
let block = Arc::new(block);
|
let block = Arc::new(block);
|
||||||
let status = match status {
|
let status = match status {
|
||||||
@ -1415,7 +1418,10 @@ where
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.blockchain.insert_block_without_senders(block) {
|
match self
|
||||||
|
.blockchain
|
||||||
|
.insert_block_without_senders(block, BlockValidationKind::SkipStateRootValidation)
|
||||||
|
{
|
||||||
Ok(status) => {
|
Ok(status) => {
|
||||||
match status {
|
match status {
|
||||||
InsertPayloadOk::Inserted(BlockStatus::Valid) => {
|
InsertPayloadOk::Inserted(BlockStatus::Valid) => {
|
||||||
|
|||||||
@ -22,9 +22,10 @@ pub trait BlockchainTreeEngine: BlockchainTreeViewer + Send + Sync {
|
|||||||
fn insert_block_without_senders(
|
fn insert_block_without_senders(
|
||||||
&self,
|
&self,
|
||||||
block: SealedBlock,
|
block: SealedBlock,
|
||||||
|
validation_kind: BlockValidationKind,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError> {
|
) -> Result<InsertPayloadOk, InsertBlockError> {
|
||||||
match block.try_seal_with_senders() {
|
match block.try_seal_with_senders() {
|
||||||
Ok(block) => self.insert_block(block),
|
Ok(block) => self.insert_block(block, validation_kind),
|
||||||
Err(block) => Err(InsertBlockError::sender_recovery_error(block)),
|
Err(block) => Err(InsertBlockError::sender_recovery_error(block)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,10 +44,17 @@ pub trait BlockchainTreeEngine: BlockchainTreeViewer + Send + Sync {
|
|||||||
/// Buffer block with senders
|
/// Buffer block with senders
|
||||||
fn buffer_block(&self, block: SealedBlockWithSenders) -> Result<(), InsertBlockError>;
|
fn buffer_block(&self, block: SealedBlockWithSenders) -> Result<(), InsertBlockError>;
|
||||||
|
|
||||||
/// Insert block with senders
|
/// Inserts block with senders
|
||||||
|
///
|
||||||
|
/// The `validation_kind` parameter controls which validation checks are performed.
|
||||||
|
///
|
||||||
|
/// Caution: If the block was received from the consensus layer, this should always be called
|
||||||
|
/// with [BlockValidationKind::Exhaustive] to validate the state root, if possible to adhere to
|
||||||
|
/// the engine API spec.
|
||||||
fn insert_block(
|
fn insert_block(
|
||||||
&self,
|
&self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
validation_kind: BlockValidationKind,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError>;
|
) -> Result<InsertPayloadOk, InsertBlockError>;
|
||||||
|
|
||||||
/// Finalize blocks up until and including `finalized_block`, and remove them from the tree.
|
/// Finalize blocks up until and including `finalized_block`, and remove them from the tree.
|
||||||
@ -92,6 +100,48 @@ pub trait BlockchainTreeEngine: BlockchainTreeViewer + Send + Sync {
|
|||||||
fn unwind(&self, unwind_to: BlockNumber) -> RethResult<()>;
|
fn unwind(&self, unwind_to: BlockNumber) -> RethResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the kind of validation that should be performed when inserting a block.
|
||||||
|
///
|
||||||
|
/// The motivation for this is that the engine API spec requires that block's state root is
|
||||||
|
/// validated when received from the CL.
|
||||||
|
///
|
||||||
|
/// This step is very expensive due to how changesets are stored in the database, so we want to
|
||||||
|
/// avoid doing it if not necessary. Blocks can also originate from the network where this step is
|
||||||
|
/// not required.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum BlockValidationKind {
|
||||||
|
/// All validation checks that can be performed.
|
||||||
|
///
|
||||||
|
/// This includes validating the state root, if possible.
|
||||||
|
///
|
||||||
|
/// Note: This should always be used when inserting blocks that originate from the consensus
|
||||||
|
/// layer.
|
||||||
|
#[default]
|
||||||
|
Exhaustive,
|
||||||
|
/// Perform all validation checks except for state root validation.
|
||||||
|
SkipStateRootValidation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockValidationKind {
|
||||||
|
/// Returns true if the state root should be validated if possible.
|
||||||
|
pub fn is_exhaustive(&self) -> bool {
|
||||||
|
matches!(self, BlockValidationKind::Exhaustive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for BlockValidationKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
BlockValidationKind::Exhaustive => {
|
||||||
|
write!(f, "Exhaustive")
|
||||||
|
}
|
||||||
|
BlockValidationKind::SkipStateRootValidation => {
|
||||||
|
write!(f, "SkipStateRootValidation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// All possible outcomes of a canonicalization attempt of [BlockchainTreeEngine::make_canonical].
|
/// All possible outcomes of a canonicalization attempt of [BlockchainTreeEngine::make_canonical].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum CanonicalOutcome {
|
pub enum CanonicalOutcome {
|
||||||
|
|||||||
@ -44,7 +44,7 @@ pub use bundle_state_provider::BundleStateProvider;
|
|||||||
pub use database::*;
|
pub use database::*;
|
||||||
use reth_db::models::AccountBeforeTx;
|
use reth_db::models::AccountBeforeTx;
|
||||||
use reth_interfaces::blockchain_tree::{
|
use reth_interfaces::blockchain_tree::{
|
||||||
error::InsertBlockError, CanonicalOutcome, InsertPayloadOk,
|
error::InsertBlockError, BlockValidationKind, CanonicalOutcome, InsertPayloadOk,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The main type for interacting with the blockchain.
|
/// The main type for interacting with the blockchain.
|
||||||
@ -574,8 +574,9 @@ where
|
|||||||
fn insert_block(
|
fn insert_block(
|
||||||
&self,
|
&self,
|
||||||
block: SealedBlockWithSenders,
|
block: SealedBlockWithSenders,
|
||||||
|
validation_kind: BlockValidationKind,
|
||||||
) -> Result<InsertPayloadOk, InsertBlockError> {
|
) -> Result<InsertPayloadOk, InsertBlockError> {
|
||||||
self.tree.insert_block(block)
|
self.tree.insert_block(block, validation_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_block(&self, finalized_block: BlockNumber) {
|
fn finalize_block(&self, finalized_block: BlockNumber) {
|
||||||
|
|||||||
Reference in New Issue
Block a user