reth_blockchain_tree/
chain.rs

1//! A chain in a [`BlockchainTree`][super::BlockchainTree].
2//!
3//! A [`Chain`] contains the state of accounts for the chain after execution of its constituent
4//! blocks, as well as a list of the blocks the chain is composed of.
5
6use super::externals::TreeExternals;
7use crate::BundleStateDataRef;
8use alloy_eips::ForkBlock;
9use alloy_primitives::{BlockHash, BlockNumber, U256};
10use reth_blockchain_tree_api::{
11    error::{BlockchainTreeError, InsertBlockErrorKind},
12    BlockAttachment, BlockValidationKind,
13};
14use reth_consensus::{ConsensusError, PostExecutionInput};
15use reth_evm::execute::{BlockExecutorProvider, Executor};
16use reth_execution_errors::BlockExecutionError;
17use reth_execution_types::{Chain, ExecutionOutcome};
18use reth_primitives::{GotExpected, SealedBlockWithSenders, SealedHeader};
19use reth_provider::{
20    providers::{BundleStateProvider, ConsistentDbView, TreeNodeTypes},
21    DBProvider, FullExecutionDataProvider, HashedPostStateProvider, ProviderError,
22    StateRootProvider, TryIntoHistoricalStateProvider,
23};
24use reth_revm::database::StateProviderDatabase;
25use reth_trie::{updates::TrieUpdates, TrieInput};
26use reth_trie_parallel::root::ParallelStateRoot;
27use std::{
28    collections::BTreeMap,
29    ops::{Deref, DerefMut},
30    time::Instant,
31};
32
33/// A chain in the blockchain tree that has functionality to execute blocks and append them to
34/// itself.
35#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct AppendableChain {
37    chain: Chain,
38}
39
40impl Deref for AppendableChain {
41    type Target = Chain;
42
43    fn deref(&self) -> &Self::Target {
44        &self.chain
45    }
46}
47
48impl DerefMut for AppendableChain {
49    fn deref_mut(&mut self) -> &mut Self::Target {
50        &mut self.chain
51    }
52}
53
54impl AppendableChain {
55    /// Create a new appendable chain from a given chain.
56    pub const fn new(chain: Chain) -> Self {
57        Self { chain }
58    }
59
60    /// Get the chain.
61    pub fn into_inner(self) -> Chain {
62        self.chain
63    }
64
65    /// Create a new chain that forks off of the canonical chain.
66    ///
67    /// if [`BlockValidationKind::Exhaustive`] is specified, the method will verify the state root
68    /// of the block.
69    pub fn new_canonical_fork<N, E>(
70        block: SealedBlockWithSenders,
71        parent_header: &SealedHeader,
72        canonical_block_hashes: &BTreeMap<BlockNumber, BlockHash>,
73        canonical_fork: ForkBlock,
74        externals: &TreeExternals<N, E>,
75        block_attachment: BlockAttachment,
76        block_validation_kind: BlockValidationKind,
77    ) -> Result<Self, InsertBlockErrorKind>
78    where
79        N: TreeNodeTypes,
80        E: BlockExecutorProvider<Primitives = N::Primitives>,
81    {
82        let execution_outcome = ExecutionOutcome::default();
83        let empty = BTreeMap::new();
84
85        let state_provider = BundleStateDataRef {
86            execution_outcome: &execution_outcome,
87            sidechain_block_hashes: &empty,
88            canonical_block_hashes,
89            canonical_fork,
90        };
91
92        let (bundle_state, trie_updates) = Self::validate_and_execute(
93            block.clone(),
94            parent_header,
95            state_provider,
96            externals,
97            block_attachment,
98            block_validation_kind,
99        )?;
100
101        Ok(Self::new(Chain::new(vec![block], bundle_state, trie_updates)))
102    }
103
104    /// Create a new chain that forks off of an existing sidechain.
105    ///
106    /// This differs from [`AppendableChain::new_canonical_fork`] in that this starts a new fork.
107    pub(crate) fn new_chain_fork<N, E>(
108        &self,
109        block: SealedBlockWithSenders,
110        side_chain_block_hashes: BTreeMap<BlockNumber, BlockHash>,
111        canonical_block_hashes: &BTreeMap<BlockNumber, BlockHash>,
112        canonical_fork: ForkBlock,
113        externals: &TreeExternals<N, E>,
114        block_validation_kind: BlockValidationKind,
115    ) -> Result<Self, InsertBlockErrorKind>
116    where
117        N: TreeNodeTypes,
118        E: BlockExecutorProvider<Primitives = N::Primitives>,
119    {
120        let parent_number =
121            block.number.checked_sub(1).ok_or(BlockchainTreeError::GenesisBlockHasNoParent)?;
122        let parent = self.blocks().get(&parent_number).ok_or(
123            BlockchainTreeError::BlockNumberNotFoundInChain { block_number: parent_number },
124        )?;
125
126        let mut execution_outcome = self.execution_outcome().clone();
127
128        // Revert state to the state after execution of the parent block
129        execution_outcome.revert_to(parent.number);
130
131        // Revert changesets to get the state of the parent that we need to apply the change.
132        let bundle_state_data = BundleStateDataRef {
133            execution_outcome: &execution_outcome,
134            sidechain_block_hashes: &side_chain_block_hashes,
135            canonical_block_hashes,
136            canonical_fork,
137        };
138        let (block_state, _) = Self::validate_and_execute(
139            block.clone(),
140            parent,
141            bundle_state_data,
142            externals,
143            BlockAttachment::HistoricalFork,
144            block_validation_kind,
145        )?;
146        // extending will also optimize few things, mostly related to selfdestruct and wiping of
147        // storage.
148        execution_outcome.extend(block_state);
149
150        // remove all receipts and reverts (except the last one), as they belong to the chain we
151        // forked from and not the new chain we are creating.
152        let size = execution_outcome.receipts().len();
153        execution_outcome.receipts_mut().drain(0..size - 1);
154        execution_outcome.state_mut().take_n_reverts(size - 1);
155        execution_outcome.set_first_block(block.number);
156
157        // If all is okay, return new chain back. Present chain is not modified.
158        Ok(Self::new(Chain::from_block(block, execution_outcome, None)))
159    }
160
161    /// Validate and execute the given block that _extends the canonical chain_, validating its
162    /// state root after execution if possible and requested.
163    ///
164    /// Note: State root validation is limited to blocks that extend the canonical chain and is
165    /// optional, see [`BlockValidationKind`]. So this function takes two parameters to determine
166    /// if the state can and should be validated.
167    ///   - [`BlockAttachment`] represents if the block extends the canonical chain, and thus we can
168    ///     cache the trie state updates.
169    ///   - [`BlockValidationKind`] determines if the state root __should__ be validated.
170    fn validate_and_execute<EDP, N, E>(
171        block: SealedBlockWithSenders,
172        parent_block: &SealedHeader,
173        bundle_state_data_provider: EDP,
174        externals: &TreeExternals<N, E>,
175        block_attachment: BlockAttachment,
176        block_validation_kind: BlockValidationKind,
177    ) -> Result<(ExecutionOutcome, Option<TrieUpdates>), BlockExecutionError>
178    where
179        EDP: FullExecutionDataProvider,
180        N: TreeNodeTypes,
181        E: BlockExecutorProvider<Primitives = N::Primitives>,
182    {
183        // some checks are done before blocks comes here.
184        externals.consensus.validate_header_against_parent(&block, parent_block)?;
185
186        // get the state provider.
187        let canonical_fork = bundle_state_data_provider.canonical_fork();
188
189        // SAFETY: For block execution and parallel state root computation below we open multiple
190        // independent database transactions. Upon opening the database transaction the consistent
191        // view will check a current tip in the database and throw an error if it doesn't match
192        // the one recorded during initialization.
193        // It is safe to use consistent view without any special error handling as long as
194        // we guarantee that plain state cannot change during processing of new payload.
195        // The usage has to be re-evaluated if that was ever to change.
196        let consistent_view =
197            ConsistentDbView::new_with_latest_tip(externals.provider_factory.clone())?;
198        let state_provider = consistent_view
199            .provider_ro()?
200            // State root calculation can take a while, and we're sure no write transaction
201            // will be open in parallel. See https://github.com/paradigmxyz/reth/issues/7509.
202            .disable_long_read_transaction_safety()
203            .try_into_history_at_block(canonical_fork.number)?;
204
205        let provider = BundleStateProvider::new(state_provider, bundle_state_data_provider);
206
207        let db = StateProviderDatabase::new(&provider);
208        let executor = externals.executor_factory.executor(db);
209        let block_hash = block.hash();
210        let block = block.unseal();
211
212        let state = executor.execute((&block, U256::MAX).into())?;
213        externals.consensus.validate_block_post_execution(
214            &block,
215            PostExecutionInput::new(&state.receipts, &state.requests),
216        )?;
217
218        let initial_execution_outcome = ExecutionOutcome::from((state, block.number));
219
220        // check state root if the block extends the canonical chain __and__ if state root
221        // validation was requested.
222        if block_validation_kind.is_exhaustive() {
223            // calculate and check state root
224            let start = Instant::now();
225            let (state_root, trie_updates) = if block_attachment.is_canonical() {
226                let mut execution_outcome =
227                    provider.block_execution_data_provider.execution_outcome().clone();
228                execution_outcome.extend(initial_execution_outcome.clone());
229                ParallelStateRoot::new(
230                    consistent_view,
231                    TrieInput::from_state(provider.hashed_post_state(execution_outcome.state())),
232                )
233                .incremental_root_with_updates()
234                .map(|(root, updates)| (root, Some(updates)))
235                .map_err(ProviderError::from)?
236            } else {
237                let hashed_state = provider.hashed_post_state(initial_execution_outcome.state());
238                let state_root = provider.state_root(hashed_state)?;
239                (state_root, None)
240            };
241            if block.state_root != state_root {
242                return Err(ConsensusError::BodyStateRootDiff(
243                    GotExpected { got: state_root, expected: block.state_root }.into(),
244                )
245                .into())
246            }
247
248            tracing::debug!(
249                target: "blockchain_tree::chain",
250                number = block.number,
251                hash = %block_hash,
252                elapsed = ?start.elapsed(),
253                "Validated state root"
254            );
255
256            Ok((initial_execution_outcome, trie_updates))
257        } else {
258            Ok((initial_execution_outcome, None))
259        }
260    }
261
262    /// Validate and execute the given block, and append it to this chain.
263    ///
264    /// This expects that the block's ancestors can be traced back to the `canonical_fork` (the
265    /// first parent block of the `block`'s chain that is in the canonical chain).
266    ///
267    /// In other words, expects a gap less (side-) chain:  [`canonical_fork..block`] in order to be
268    /// able to __execute__ the block.
269    ///
270    /// CAUTION: This will only perform state root check if it's possible: if the `canonical_fork`
271    /// is the canonical head, or: state root check can't be performed if the given canonical is
272    /// __not__ the canonical head.
273    #[track_caller]
274    #[allow(clippy::too_many_arguments)]
275    pub(crate) fn append_block<N, E>(
276        &mut self,
277        block: SealedBlockWithSenders,
278        side_chain_block_hashes: BTreeMap<BlockNumber, BlockHash>,
279        canonical_block_hashes: &BTreeMap<BlockNumber, BlockHash>,
280        externals: &TreeExternals<N, E>,
281        canonical_fork: ForkBlock,
282        block_attachment: BlockAttachment,
283        block_validation_kind: BlockValidationKind,
284    ) -> Result<(), InsertBlockErrorKind>
285    where
286        N: TreeNodeTypes,
287        E: BlockExecutorProvider<Primitives = N::Primitives>,
288    {
289        let parent_block = self.chain.tip();
290
291        let bundle_state_data = BundleStateDataRef {
292            execution_outcome: self.execution_outcome(),
293            sidechain_block_hashes: &side_chain_block_hashes,
294            canonical_block_hashes,
295            canonical_fork,
296        };
297
298        let (block_state, _) = Self::validate_and_execute(
299            block.clone(),
300            parent_block,
301            bundle_state_data,
302            externals,
303            block_attachment,
304            block_validation_kind,
305        )?;
306        // extend the state.
307        self.chain.append_block(block, block_state);
308
309        Ok(())
310    }
311}