mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add state root validation on new insert (#2543)
Co-authored-by: Roman Krasiuk <rokrassyuk@gmail.com> Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
This commit is contained in:
@ -158,11 +158,10 @@ 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.canonical_chain().canonical_hash(&block.number) {
|
||||
if hash == block.hash {
|
||||
return Ok(Some(BlockStatus::Valid))
|
||||
}
|
||||
if self.is_block_hash_canonical(&block.hash)? {
|
||||
return Ok(Some(BlockStatus::Valid))
|
||||
}
|
||||
|
||||
// check if block is inside database
|
||||
if self.externals.database().block_number(block.hash)?.is_some() {
|
||||
return Ok(Some(BlockStatus::Valid))
|
||||
@ -315,11 +314,16 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
.map_err(|err| InsertBlockError::consensus_error(err, block.block.clone()))?;
|
||||
}
|
||||
|
||||
// insert block inside unconnected block buffer. Delaying it execution.
|
||||
// insert block inside unconnected block buffer. Delaying its execution.
|
||||
self.buffered_blocks.insert_block(block);
|
||||
Ok(BlockStatus::Disconnected)
|
||||
}
|
||||
|
||||
/// This tries to append the given block to the canonical chain.
|
||||
///
|
||||
/// WARNING: this expects that the block is part of the canonical chain, see
|
||||
/// [Self::is_block_hash_canonical]. Hence, it is expected that the block can be traced back
|
||||
/// to the current canonical block.
|
||||
#[instrument(skip_all, target = "blockchain_tree")]
|
||||
fn try_append_canonical_chain(
|
||||
&mut self,
|
||||
@ -354,13 +358,6 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
))
|
||||
}
|
||||
|
||||
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)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?
|
||||
@ -372,19 +369,36 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
})?
|
||||
.seal(block.parent_hash);
|
||||
|
||||
let chain = AppendableChain::new_canonical_fork(
|
||||
block,
|
||||
&parent_header,
|
||||
canonical_chain.inner(),
|
||||
parent,
|
||||
&self.externals,
|
||||
)?;
|
||||
let canonical_chain = self.canonical_chain();
|
||||
|
||||
let (block_status, chain) = if block.parent_hash == canonical_chain.tip().hash {
|
||||
let chain = AppendableChain::new_canonical_fork_extend(
|
||||
block,
|
||||
&parent_header,
|
||||
canonical_chain.inner(),
|
||||
parent,
|
||||
&self.externals,
|
||||
)?;
|
||||
(BlockStatus::Valid, chain)
|
||||
} else {
|
||||
let chain = AppendableChain::new_canonical_fork(
|
||||
block,
|
||||
&parent_header,
|
||||
canonical_chain.inner(),
|
||||
parent,
|
||||
&self.externals,
|
||||
)?;
|
||||
(BlockStatus::Accepted, chain)
|
||||
};
|
||||
|
||||
self.insert_chain(chain);
|
||||
self.try_connect_buffered_blocks(block_num_hash);
|
||||
Ok(block_status)
|
||||
}
|
||||
|
||||
/// Try inserting a block into the given side chain.
|
||||
///
|
||||
/// WARNING: This expects a valid side chain id, see [BlockIndices::get_blocks_chain_id]
|
||||
#[instrument(skip_all, target = "blockchain_tree")]
|
||||
fn try_insert_block_into_side_chain(
|
||||
&mut self,
|
||||
@ -979,7 +993,7 @@ mod tests {
|
||||
transaction::DbTxMut,
|
||||
};
|
||||
use reth_interfaces::test_utils::TestConsensus;
|
||||
use reth_primitives::{proofs::EMPTY_ROOT, ChainSpecBuilder, H256, MAINNET};
|
||||
use reth_primitives::{proofs::EMPTY_ROOT, ChainSpecBuilder, StageCheckpoint, H256, MAINNET};
|
||||
use reth_provider::{
|
||||
insert_block,
|
||||
post_state::PostState,
|
||||
@ -1018,6 +1032,7 @@ mod tests {
|
||||
for i in 0..10 {
|
||||
tx_mut.put::<tables::CanonicalHeaders>(i, H256([100 + i as u8; 32])).unwrap();
|
||||
}
|
||||
tx_mut.put::<tables::SyncStage>("Finish".to_string(), StageCheckpoint::new(10)).unwrap();
|
||||
tx_mut.commit().unwrap();
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::{post_state::PostState, PostStateDataRef};
|
||||
use reth_db::database::Database;
|
||||
use reth_interfaces::{
|
||||
blockchain_tree::error::{BlockchainTreeError, InsertBlockError},
|
||||
consensus::Consensus,
|
||||
consensus::{Consensus, ConsensusError},
|
||||
Error,
|
||||
};
|
||||
use reth_primitives::{
|
||||
@ -14,6 +14,7 @@ use reth_primitives::{
|
||||
};
|
||||
use reth_provider::{
|
||||
providers::PostStateProvider, BlockExecutor, Chain, ExecutorFactory, PostStateDataProvider,
|
||||
StateRootProvider,
|
||||
};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
@ -57,6 +58,42 @@ impl AppendableChain {
|
||||
self.chain
|
||||
}
|
||||
|
||||
/// Create a new chain that _extends the canonical chain_.
|
||||
///
|
||||
/// This will also verify the state root of the block extending the canonical chain.
|
||||
pub fn new_canonical_fork_extend<DB, C, EF>(
|
||||
block: SealedBlockWithSenders,
|
||||
parent_header: &SealedHeader,
|
||||
canonical_block_hashes: &BTreeMap<BlockNumber, BlockHash>,
|
||||
canonical_fork: ForkBlock,
|
||||
externals: &TreeExternals<DB, C, EF>,
|
||||
) -> Result<Self, InsertBlockError>
|
||||
where
|
||||
DB: Database,
|
||||
C: Consensus,
|
||||
EF: ExecutorFactory,
|
||||
{
|
||||
let state = PostState::default();
|
||||
let empty = BTreeMap::new();
|
||||
|
||||
let state_provider = PostStateDataRef {
|
||||
state: &state,
|
||||
sidechain_block_hashes: &empty,
|
||||
canonical_block_hashes,
|
||||
canonical_fork,
|
||||
};
|
||||
|
||||
let changeset = Self::validate_and_execute_canonical(
|
||||
block.clone(),
|
||||
parent_header,
|
||||
state_provider,
|
||||
externals,
|
||||
)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||
|
||||
Ok(Self { chain: Chain::new(vec![(block, changeset)]) })
|
||||
}
|
||||
|
||||
/// Create a new chain that forks off of the canonical chain.
|
||||
pub fn new_canonical_fork<DB, C, EF>(
|
||||
block: SealedBlockWithSenders,
|
||||
@ -80,14 +117,20 @@ impl AppendableChain {
|
||||
canonical_fork,
|
||||
};
|
||||
|
||||
let changeset =
|
||||
Self::validate_and_execute(block.clone(), parent_header, state_provider, externals)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||
let changeset = Self::validate_and_execute_sidechain(
|
||||
block.clone(),
|
||||
parent_header,
|
||||
state_provider,
|
||||
externals,
|
||||
)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||
|
||||
Ok(Self { chain: Chain::new(vec![(block, changeset)]) })
|
||||
}
|
||||
|
||||
/// Create a new chain that forks off of an existing sidechain.
|
||||
///
|
||||
/// This differs from [AppendableChain::new_canonical_fork] in that this starts a new fork.
|
||||
pub(crate) fn new_chain_fork<DB, C, EF>(
|
||||
&self,
|
||||
block: SealedBlockWithSenders,
|
||||
@ -122,7 +165,7 @@ impl AppendableChain {
|
||||
canonical_fork,
|
||||
};
|
||||
let block_state =
|
||||
Self::validate_and_execute(block.clone(), parent, post_state_data, externals)
|
||||
Self::validate_and_execute_sidechain(block.clone(), parent, post_state_data, externals)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||
state.extend(block_state);
|
||||
|
||||
@ -133,8 +176,9 @@ impl AppendableChain {
|
||||
Ok(chain)
|
||||
}
|
||||
|
||||
/// Validate and execute the given block.
|
||||
fn validate_and_execute<PSDP, DB, C, EF>(
|
||||
/// Validate and execute the given block that _extends the canonical chain_, validating its
|
||||
/// state root after execution.
|
||||
fn validate_and_execute_canonical<PSDP, DB, C, EF>(
|
||||
block: SealedBlockWithSenders,
|
||||
parent_block: &SealedHeader,
|
||||
post_state_data_provider: PSDP,
|
||||
@ -149,8 +193,8 @@ impl AppendableChain {
|
||||
// some checks are done before blocks comes here.
|
||||
externals.consensus.validate_header_against_parent(&block, parent_block)?;
|
||||
|
||||
let (unseal, senders) = block.into_components();
|
||||
let unseal = unseal.unseal();
|
||||
let (block, senders) = block.into_components();
|
||||
let block = block.unseal();
|
||||
|
||||
//get state provider.
|
||||
let db = externals.database();
|
||||
@ -162,10 +206,57 @@ impl AppendableChain {
|
||||
let provider = PostStateProvider::new(state_provider, post_state_data_provider);
|
||||
|
||||
let mut executor = externals.executor_factory.with_sp(&provider);
|
||||
executor.execute_and_verify_receipt(&unseal, U256::MAX, Some(senders)).map_err(Into::into)
|
||||
let post_state = executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?;
|
||||
|
||||
// check state root
|
||||
let state_root = provider.state_root(post_state.clone())?;
|
||||
if block.state_root != state_root {
|
||||
return Err(ConsensusError::BodyStateRootDiff {
|
||||
got: state_root,
|
||||
expected: block.state_root,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
Ok(post_state)
|
||||
}
|
||||
|
||||
/// Validate and execute the given sidechain block, skipping state root validation.
|
||||
fn validate_and_execute_sidechain<PSDP, DB, C, EF>(
|
||||
block: SealedBlockWithSenders,
|
||||
parent_block: &SealedHeader,
|
||||
post_state_data_provider: PSDP,
|
||||
externals: &TreeExternals<DB, C, EF>,
|
||||
) -> Result<PostState, Error>
|
||||
where
|
||||
PSDP: PostStateDataProvider,
|
||||
DB: Database,
|
||||
C: Consensus,
|
||||
EF: ExecutorFactory,
|
||||
{
|
||||
// some checks are done before blocks comes here.
|
||||
externals.consensus.validate_header_against_parent(&block, parent_block)?;
|
||||
|
||||
let (block, senders) = block.into_components();
|
||||
let block = block.unseal();
|
||||
|
||||
//get state provider.
|
||||
let db = externals.database();
|
||||
// TODO, small perf can check if caonical fork is the latest state.
|
||||
let canonical_fork = post_state_data_provider.canonical_fork();
|
||||
let history_provider = db.history_by_block_number(canonical_fork.number)?;
|
||||
let state_provider = history_provider;
|
||||
|
||||
let provider = PostStateProvider::new(state_provider, post_state_data_provider);
|
||||
|
||||
let mut executor = externals.executor_factory.with_sp(&provider);
|
||||
let post_state = executor.execute_and_verify_receipt(&block, U256::MAX, Some(senders))?;
|
||||
|
||||
Ok(post_state)
|
||||
}
|
||||
|
||||
/// Validate and execute the given block, and append it to this chain.
|
||||
#[track_caller]
|
||||
pub(crate) fn append_block<DB, C, EF>(
|
||||
&mut self,
|
||||
block: SealedBlockWithSenders,
|
||||
@ -188,9 +279,13 @@ impl AppendableChain {
|
||||
canonical_fork,
|
||||
};
|
||||
|
||||
let block_state =
|
||||
Self::validate_and_execute(block.clone(), parent_block, post_state_data, externals)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||
let block_state = Self::validate_and_execute_sidechain(
|
||||
block.clone(),
|
||||
parent_block,
|
||||
post_state_data,
|
||||
externals,
|
||||
)
|
||||
.map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?;
|
||||
self.state.extend(block_state);
|
||||
self.blocks.insert(block.number, block);
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user