feat: add missing canonical checks for safe+finalized block (#9765)

This commit is contained in:
Matthias Seitz
2024-07-24 17:52:20 +02:00
committed by GitHub
parent 9de9b7cb32
commit d395df29fc
2 changed files with 61 additions and 14 deletions

View File

@ -133,6 +133,11 @@ impl CanonicalInMemoryState {
Self { inner: Arc::new(inner) } Self { inner: Arc::new(inner) }
} }
/// Returns in the header corresponding to the given hash.
pub fn header_by_hash(&self, hash: B256) -> Option<SealedHeader> {
self.state_by_hash(hash).map(|block| block.block().block.header.clone())
}
/// Append new blocks to the in memory state. /// Append new blocks to the in memory state.
fn update_blocks<I>(&self, new_blocks: I, reorged: I) fn update_blocks<I>(&self, new_blocks: I, reorged: I)
where where

View File

@ -23,10 +23,11 @@ use reth_payload_primitives::{PayloadAttributes, PayloadBuilderAttributes, Paylo
use reth_payload_validator::ExecutionPayloadValidator; use reth_payload_validator::ExecutionPayloadValidator;
use reth_primitives::{ use reth_primitives::{
Block, BlockNumHash, BlockNumber, GotExpected, Header, Receipts, Requests, SealedBlock, Block, BlockNumHash, BlockNumber, GotExpected, Header, Receipts, Requests, SealedBlock,
SealedBlockWithSenders, B256, U256, SealedBlockWithSenders, SealedHeader, B256, U256,
}; };
use reth_provider::{ use reth_provider::{
BlockReader, ExecutionOutcome, StateProvider, StateProviderFactory, StateRootProvider, BlockReader, ExecutionOutcome, ProviderError, StateProvider, StateProviderFactory,
StateRootProvider,
}; };
use reth_revm::database::StateProviderDatabase; use reth_revm::database::StateProviderDatabase;
use reth_rpc_types::{ use reth_rpc_types::{
@ -1077,19 +1078,62 @@ where
Ok(InsertPayloadOk::Inserted(BlockStatus::Valid(attachment))) Ok(InsertPayloadOk::Inserted(BlockStatus::Valid(attachment)))
} }
/// Updates the tracked finalized block if we have it. /// Attempts to find the header for the given block hash if it is canonical.
fn update_finalized_block(&self, finalized_block_hash: B256) { pub fn find_canonical_header(&self, hash: B256) -> Result<Option<SealedHeader>, ProviderError> {
if finalized_block_hash.is_zero() {} let mut canonical = self.canonical_in_memory_state.header_by_hash(hash);
// TODO find finalized block and ensure it's canonical if canonical.is_none() {
// TODO tree cleanup canonical = self.provider.header(&hash)?.map(|header| header.seal(hash));
}
Ok(canonical)
}
/// Updates the tracked finalized block if we have it.
fn update_finalized_block(
&self,
finalized_block_hash: B256,
) -> Result<(), OnForkChoiceUpdated> {
if finalized_block_hash.is_zero() {
return Ok(())
}
match self.find_canonical_header(finalized_block_hash) {
Ok(None) => {
// if the finalized block is not known, we can't update the finalized block
return Err(OnForkChoiceUpdated::invalid_state())
}
Ok(Some(finalized)) => {
self.canonical_in_memory_state.set_finalized(finalized);
}
Err(err) => {
error!(%err, "Failed to fetch finalized block header");
}
}
Ok(())
} }
/// Updates the tracked safe block if we have it /// Updates the tracked safe block if we have it
fn update_safe_block(&self, safe_block_hash: B256) { fn update_safe_block(&self, safe_block_hash: B256) -> Result<(), OnForkChoiceUpdated> {
if safe_block_hash.is_zero() {} if safe_block_hash.is_zero() {
return Ok(())
}
// TODO find safe block and ensure it's canonical match self.find_canonical_header(safe_block_hash) {
Ok(None) => {
// if the safe block is not known, we can't update the safe block
return Err(OnForkChoiceUpdated::invalid_state())
}
Ok(Some(finalized)) => {
self.canonical_in_memory_state.set_safe(finalized);
}
Err(err) => {
error!(%err, "Failed to fetch safe block header");
}
}
Ok(())
} }
/// Ensures that the given forkchoice state is consistent, assuming the head block has been /// Ensures that the given forkchoice state is consistent, assuming the head block has been
@ -1109,16 +1153,14 @@ where
// //
// This ensures that the finalized block is consistent with the head block, i.e. the // This ensures that the finalized block is consistent with the head block, i.e. the
// finalized block is an ancestor of the head block. // finalized block is an ancestor of the head block.
self.update_finalized_block(state.finalized_block_hash); self.update_finalized_block(state.finalized_block_hash)?;
// Also ensure that the safe block, if not zero, is known and in the canonical chain // Also ensure that the safe block, if not zero, is known and in the canonical chain
// after the head block is canonicalized. // after the head block is canonicalized.
// //
// This ensures that the safe block is consistent with the head block, i.e. the safe // This ensures that the safe block is consistent with the head block, i.e. the safe
// block is an ancestor of the head block. // block is an ancestor of the head block.
self.update_safe_block(state.safe_block_hash); self.update_safe_block(state.safe_block_hash)
Ok(())
} }
/// Pre-validate forkchoice update and check whether it can be processed. /// Pre-validate forkchoice update and check whether it can be processed.