reth_execution_types/
chain.rs

1//! Contains [Chain], a chain of blocks and their final state.
2
3use crate::ExecutionOutcome;
4use alloc::{borrow::Cow, collections::BTreeMap, vec::Vec};
5use alloy_consensus::{transaction::Recovered, BlockHeader};
6use alloy_eips::{eip1898::ForkBlock, eip2718::Encodable2718, BlockNumHash};
7use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash};
8use core::{fmt, ops::RangeInclusive};
9use reth_primitives_traits::{
10    transaction::signed::SignedTransaction, Block, BlockBody, NodePrimitives, RecoveredBlock,
11    SealedHeader,
12};
13use reth_trie_common::updates::TrieUpdates;
14use revm_database::BundleState;
15
16/// A chain of blocks and their final state.
17///
18/// The chain contains the state of accounts after execution of its blocks,
19/// changesets for those blocks (and their transactions), as well as the blocks themselves.
20///
21/// Used inside the `BlockchainTree`.
22///
23/// # Warning
24///
25/// A chain of blocks should not be empty.
26#[derive(Clone, Debug, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Chain<N: NodePrimitives = reth_ethereum_primitives::EthPrimitives> {
29    /// All blocks in this chain.
30    blocks: BTreeMap<BlockNumber, RecoveredBlock<N::Block>>,
31    /// The outcome of block execution for this chain.
32    ///
33    /// This field contains the state of all accounts after the execution of all blocks in this
34    /// chain, ranging from the [`Chain::first`] block to the [`Chain::tip`] block, inclusive.
35    ///
36    /// Additionally, it includes the individual state changes that led to the current state.
37    execution_outcome: ExecutionOutcome<N::Receipt>,
38    /// State trie updates after block is added to the chain.
39    /// NOTE: Currently, trie updates are present only for
40    /// single-block chains that extend the canonical chain.
41    trie_updates: Option<TrieUpdates>,
42}
43
44impl<N: NodePrimitives> Default for Chain<N> {
45    fn default() -> Self {
46        Self {
47            blocks: Default::default(),
48            execution_outcome: Default::default(),
49            trie_updates: Default::default(),
50        }
51    }
52}
53
54impl<N: NodePrimitives> Chain<N> {
55    /// Create new Chain from blocks and state.
56    ///
57    /// # Warning
58    ///
59    /// A chain of blocks should not be empty.
60    pub fn new(
61        blocks: impl IntoIterator<Item = RecoveredBlock<N::Block>>,
62        execution_outcome: ExecutionOutcome<N::Receipt>,
63        trie_updates: Option<TrieUpdates>,
64    ) -> Self {
65        let blocks =
66            blocks.into_iter().map(|b| (b.header().number(), b)).collect::<BTreeMap<_, _>>();
67        debug_assert!(!blocks.is_empty(), "Chain should have at least one block");
68
69        Self { blocks, execution_outcome, trie_updates }
70    }
71
72    /// Create new Chain from a single block and its state.
73    pub fn from_block(
74        block: RecoveredBlock<N::Block>,
75        execution_outcome: ExecutionOutcome<N::Receipt>,
76        trie_updates: Option<TrieUpdates>,
77    ) -> Self {
78        Self::new([block], execution_outcome, trie_updates)
79    }
80
81    /// Get the blocks in this chain.
82    pub const fn blocks(&self) -> &BTreeMap<BlockNumber, RecoveredBlock<N::Block>> {
83        &self.blocks
84    }
85
86    /// Consumes the type and only returns the blocks in this chain.
87    pub fn into_blocks(self) -> BTreeMap<BlockNumber, RecoveredBlock<N::Block>> {
88        self.blocks
89    }
90
91    /// Returns an iterator over all headers in the block with increasing block numbers.
92    pub fn headers(&self) -> impl Iterator<Item = SealedHeader<N::BlockHeader>> + '_ {
93        self.blocks.values().map(|block| block.clone_sealed_header())
94    }
95
96    /// Get cached trie updates for this chain.
97    pub const fn trie_updates(&self) -> Option<&TrieUpdates> {
98        self.trie_updates.as_ref()
99    }
100
101    /// Remove cached trie updates for this chain.
102    pub fn clear_trie_updates(&mut self) {
103        self.trie_updates.take();
104    }
105
106    /// Get execution outcome of this chain
107    pub const fn execution_outcome(&self) -> &ExecutionOutcome<N::Receipt> {
108        &self.execution_outcome
109    }
110
111    /// Get mutable execution outcome of this chain
112    pub fn execution_outcome_mut(&mut self) -> &mut ExecutionOutcome<N::Receipt> {
113        &mut self.execution_outcome
114    }
115
116    /// Prepends the given state to the current state.
117    pub fn prepend_state(&mut self, state: BundleState) {
118        self.execution_outcome.prepend_state(state);
119        self.trie_updates.take(); // invalidate cached trie updates
120    }
121
122    /// Return true if chain is empty and has no blocks.
123    pub fn is_empty(&self) -> bool {
124        self.blocks.is_empty()
125    }
126
127    /// Return block number of the block hash.
128    pub fn block_number(&self, block_hash: BlockHash) -> Option<BlockNumber> {
129        self.blocks.iter().find_map(|(num, block)| (block.hash() == block_hash).then_some(*num))
130    }
131
132    /// Returns the block with matching hash.
133    pub fn recovered_block(&self, block_hash: BlockHash) -> Option<&RecoveredBlock<N::Block>> {
134        self.blocks.iter().find_map(|(_num, block)| (block.hash() == block_hash).then_some(block))
135    }
136
137    /// Return execution outcome at the `block_number` or None if block is not known
138    pub fn execution_outcome_at_block(
139        &self,
140        block_number: BlockNumber,
141    ) -> Option<ExecutionOutcome<N::Receipt>> {
142        if self.tip().number() == block_number {
143            return Some(self.execution_outcome.clone())
144        }
145
146        if self.blocks.contains_key(&block_number) {
147            let mut execution_outcome = self.execution_outcome.clone();
148            execution_outcome.revert_to(block_number);
149            return Some(execution_outcome)
150        }
151        None
152    }
153
154    /// Destructure the chain into its inner components:
155    /// 1. The blocks contained in the chain.
156    /// 2. The execution outcome representing the final state.
157    /// 3. The optional trie updates.
158    pub fn into_inner(
159        self,
160    ) -> (ChainBlocks<'static, N::Block>, ExecutionOutcome<N::Receipt>, Option<TrieUpdates>) {
161        (ChainBlocks { blocks: Cow::Owned(self.blocks) }, self.execution_outcome, self.trie_updates)
162    }
163
164    /// Destructure the chain into its inner components:
165    /// 1. A reference to the blocks contained in the chain.
166    /// 2. A reference to the execution outcome representing the final state.
167    pub const fn inner(&self) -> (ChainBlocks<'_, N::Block>, &ExecutionOutcome<N::Receipt>) {
168        (ChainBlocks { blocks: Cow::Borrowed(&self.blocks) }, &self.execution_outcome)
169    }
170
171    /// Returns an iterator over all the receipts of the blocks in the chain.
172    pub fn block_receipts_iter(&self) -> impl Iterator<Item = &Vec<N::Receipt>> + '_ {
173        self.execution_outcome.receipts().iter()
174    }
175
176    /// Returns an iterator over all blocks in the chain with increasing block number.
177    pub fn blocks_iter(&self) -> impl Iterator<Item = &RecoveredBlock<N::Block>> + '_ {
178        self.blocks().iter().map(|block| block.1)
179    }
180
181    /// Returns an iterator over all blocks and their receipts in the chain.
182    pub fn blocks_and_receipts(
183        &self,
184    ) -> impl Iterator<Item = (&RecoveredBlock<N::Block>, &Vec<N::Receipt>)> + '_ {
185        self.blocks_iter().zip(self.block_receipts_iter())
186    }
187
188    /// Get the block at which this chain forked.
189    #[track_caller]
190    pub fn fork_block(&self) -> ForkBlock {
191        let first = self.first();
192        ForkBlock {
193            number: first.header().number().saturating_sub(1),
194            hash: first.header().parent_hash(),
195        }
196    }
197
198    /// Get the first block in this chain.
199    ///
200    /// # Panics
201    ///
202    /// If chain doesn't have any blocks.
203    #[track_caller]
204    pub fn first(&self) -> &RecoveredBlock<N::Block> {
205        self.blocks.first_key_value().expect("Chain should have at least one block").1
206    }
207
208    /// Get the tip of the chain.
209    ///
210    /// # Panics
211    ///
212    /// If chain doesn't have any blocks.
213    #[track_caller]
214    pub fn tip(&self) -> &RecoveredBlock<N::Block> {
215        self.blocks.last_key_value().expect("Chain should have at least one block").1
216    }
217
218    /// Returns length of the chain.
219    pub fn len(&self) -> usize {
220        self.blocks.len()
221    }
222
223    /// Returns the range of block numbers in the chain.
224    ///
225    /// # Panics
226    ///
227    /// If chain doesn't have any blocks.
228    pub fn range(&self) -> RangeInclusive<BlockNumber> {
229        self.first().header().number()..=self.tip().header().number()
230    }
231
232    /// Get all receipts for the given block.
233    pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<Vec<&N::Receipt>> {
234        let num = self.block_number(block_hash)?;
235        Some(self.execution_outcome.receipts_by_block(num).iter().collect())
236    }
237
238    /// Get all receipts with attachment.
239    ///
240    /// Attachment includes block number, block hash, transaction hash and transaction index.
241    pub fn receipts_with_attachment(&self) -> Vec<BlockReceipts<N::Receipt>>
242    where
243        N::SignedTx: Encodable2718,
244    {
245        let mut receipt_attach = Vec::with_capacity(self.blocks().len());
246        for ((block_num, block), receipts) in
247            self.blocks().iter().zip(self.execution_outcome.receipts().iter())
248        {
249            let mut tx_receipts = Vec::with_capacity(receipts.len());
250            for (tx, receipt) in block.body().transactions().iter().zip(receipts.iter()) {
251                tx_receipts.push((tx.trie_hash(), receipt.clone()));
252            }
253            let block_num_hash = BlockNumHash::new(*block_num, block.hash());
254            receipt_attach.push(BlockReceipts { block: block_num_hash, tx_receipts });
255        }
256        receipt_attach
257    }
258
259    /// Append a single block with state to the chain.
260    /// This method assumes that blocks attachment to the chain has already been validated.
261    pub fn append_block(
262        &mut self,
263        block: RecoveredBlock<N::Block>,
264        execution_outcome: ExecutionOutcome<N::Receipt>,
265    ) {
266        self.blocks.insert(block.header().number(), block);
267        self.execution_outcome.extend(execution_outcome);
268        self.trie_updates.take(); // reset
269    }
270
271    /// Merge two chains by appending the given chain into the current one.
272    ///
273    /// The state of accounts for this chain is set to the state of the newest chain.
274    ///
275    /// Returns the passed `other` chain in [`Result::Err`] variant if the chains could not be
276    /// connected.
277    pub fn append_chain(&mut self, other: Self) -> Result<(), Self> {
278        let chain_tip = self.tip();
279        let other_fork_block = other.fork_block();
280        if chain_tip.hash() != other_fork_block.hash {
281            return Err(other)
282        }
283
284        // Insert blocks from other chain
285        self.blocks.extend(other.blocks);
286        self.execution_outcome.extend(other.execution_outcome);
287        self.trie_updates.take(); // reset
288
289        Ok(())
290    }
291
292    /// Split this chain at the given block.
293    ///
294    /// The given block will be the last block in the first returned chain.
295    ///
296    /// If the given block is not found, [`ChainSplit::NoSplitPending`] is returned.
297    /// Split chain at the number or hash, block with given number will be included at first chain.
298    /// If any chain is empty (Does not have blocks) None will be returned.
299    ///
300    /// # Note
301    ///
302    /// The plain state is only found in the second chain, making it
303    /// impossible to perform any state reverts on the first chain.
304    ///
305    /// The second chain only contains the changes that were reverted on the first chain; however,
306    /// it retains the up to date state as if the chains were one, i.e. the second chain is an
307    /// extension of the first.
308    ///
309    /// # Panics
310    ///
311    /// If chain doesn't have any blocks.
312    #[track_caller]
313    pub fn split(mut self, split_at: ChainSplitTarget) -> ChainSplit<N> {
314        let chain_tip = *self.blocks.last_entry().expect("chain is never empty").key();
315        let block_number = match split_at {
316            ChainSplitTarget::Hash(block_hash) => {
317                let Some(block_number) = self.block_number(block_hash) else {
318                    return ChainSplit::NoSplitPending(self)
319                };
320                // If block number is same as tip whole chain is becoming canonical.
321                if block_number == chain_tip {
322                    return ChainSplit::NoSplitCanonical(self)
323                }
324                block_number
325            }
326            ChainSplitTarget::Number(block_number) => {
327                if block_number > chain_tip {
328                    return ChainSplit::NoSplitPending(self)
329                }
330                if block_number == chain_tip {
331                    return ChainSplit::NoSplitCanonical(self)
332                }
333                if block_number < *self.blocks.first_entry().expect("chain is never empty").key() {
334                    return ChainSplit::NoSplitPending(self)
335                }
336                block_number
337            }
338        };
339
340        let split_at = block_number + 1;
341        let higher_number_blocks = self.blocks.split_off(&split_at);
342
343        let execution_outcome = core::mem::take(&mut self.execution_outcome);
344        let (canonical_block_exec_outcome, pending_block_exec_outcome) =
345            execution_outcome.split_at(split_at);
346
347        // TODO: Currently, trie updates are reset on chain split.
348        // Add tests ensuring that it is valid to leave updates in the pending chain.
349        ChainSplit::Split {
350            canonical: Self {
351                execution_outcome: canonical_block_exec_outcome.expect("split in range"),
352                blocks: self.blocks,
353                trie_updates: None,
354            },
355            pending: Self {
356                execution_outcome: pending_block_exec_outcome,
357                blocks: higher_number_blocks,
358                trie_updates: None,
359            },
360        }
361    }
362}
363
364/// Wrapper type for `blocks` display in `Chain`
365#[derive(Debug)]
366pub struct DisplayBlocksChain<'a, B: reth_primitives_traits::Block>(
367    pub &'a BTreeMap<BlockNumber, RecoveredBlock<B>>,
368);
369
370impl<B: reth_primitives_traits::Block> fmt::Display for DisplayBlocksChain<'_, B> {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        let mut list = f.debug_list();
373        let mut values = self.0.values().map(|block| block.num_hash());
374        if values.len() <= 3 {
375            list.entries(values);
376        } else {
377            list.entry(&values.next().unwrap());
378            list.entry(&format_args!("..."));
379            list.entry(&values.next_back().unwrap());
380        }
381        list.finish()
382    }
383}
384
385/// All blocks in the chain
386#[derive(Clone, Debug, Default, PartialEq, Eq)]
387pub struct ChainBlocks<'a, B: Block> {
388    blocks: Cow<'a, BTreeMap<BlockNumber, RecoveredBlock<B>>>,
389}
390
391impl<B: Block<Body: BlockBody<Transaction: SignedTransaction>>> ChainBlocks<'_, B> {
392    /// Creates a consuming iterator over all blocks in the chain with increasing block number.
393    ///
394    /// Note: this always yields at least one block.
395    #[inline]
396    pub fn into_blocks(self) -> impl Iterator<Item = RecoveredBlock<B>> {
397        self.blocks.into_owned().into_values()
398    }
399
400    /// Creates an iterator over all blocks in the chain with increasing block number.
401    #[inline]
402    pub fn iter(&self) -> impl Iterator<Item = (&BlockNumber, &RecoveredBlock<B>)> {
403        self.blocks.iter()
404    }
405
406    /// Get the tip of the chain.
407    ///
408    /// # Note
409    ///
410    /// Chains always have at least one block.
411    #[inline]
412    pub fn tip(&self) -> &RecoveredBlock<B> {
413        self.blocks.last_key_value().expect("Chain should have at least one block").1
414    }
415
416    /// Get the _first_ block of the chain.
417    ///
418    /// # Note
419    ///
420    /// Chains always have at least one block.
421    #[inline]
422    pub fn first(&self) -> &RecoveredBlock<B> {
423        self.blocks.first_key_value().expect("Chain should have at least one block").1
424    }
425
426    /// Returns an iterator over all transactions in the chain.
427    #[inline]
428    pub fn transactions(&self) -> impl Iterator<Item = &<B::Body as BlockBody>::Transaction> + '_ {
429        self.blocks.values().flat_map(|block| block.body().transactions_iter())
430    }
431
432    /// Returns an iterator over all transactions and their senders.
433    #[inline]
434    pub fn transactions_with_sender(
435        &self,
436    ) -> impl Iterator<Item = (&Address, &<B::Body as BlockBody>::Transaction)> + '_ {
437        self.blocks.values().flat_map(|block| block.transactions_with_sender())
438    }
439
440    /// Returns an iterator over all [`Recovered`] in the blocks
441    ///
442    /// Note: This clones the transactions since it is assumed this is part of a shared [Chain].
443    #[inline]
444    pub fn transactions_ecrecovered(
445        &self,
446    ) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> + '_ {
447        self.transactions_with_sender().map(|(signer, tx)| tx.clone().with_signer(*signer))
448    }
449
450    /// Returns an iterator over all transaction hashes in the block
451    #[inline]
452    pub fn transaction_hashes(&self) -> impl Iterator<Item = TxHash> + '_ {
453        self.blocks
454            .values()
455            .flat_map(|block| block.body().transactions_iter().map(|tx| tx.trie_hash()))
456    }
457}
458
459impl<B: Block> IntoIterator for ChainBlocks<'_, B> {
460    type Item = (BlockNumber, RecoveredBlock<B>);
461    type IntoIter = alloc::collections::btree_map::IntoIter<BlockNumber, RecoveredBlock<B>>;
462
463    fn into_iter(self) -> Self::IntoIter {
464        #[allow(clippy::unnecessary_to_owned)]
465        self.blocks.into_owned().into_iter()
466    }
467}
468
469/// Used to hold receipts and their attachment.
470#[derive(Default, Clone, Debug, PartialEq, Eq)]
471pub struct BlockReceipts<T = reth_ethereum_primitives::Receipt> {
472    /// Block identifier
473    pub block: BlockNumHash,
474    /// Transaction identifier and receipt.
475    pub tx_receipts: Vec<(TxHash, T)>,
476}
477
478/// The target block where the chain should be split.
479#[derive(Clone, Copy, Debug, PartialEq, Eq)]
480pub enum ChainSplitTarget {
481    /// Split at block number.
482    Number(BlockNumber),
483    /// Split at block hash.
484    Hash(BlockHash),
485}
486
487impl From<BlockNumber> for ChainSplitTarget {
488    fn from(number: BlockNumber) -> Self {
489        Self::Number(number)
490    }
491}
492
493impl From<BlockHash> for ChainSplitTarget {
494    fn from(hash: BlockHash) -> Self {
495        Self::Hash(hash)
496    }
497}
498
499/// Result of a split chain.
500#[derive(Clone, Debug, PartialEq, Eq)]
501#[allow(clippy::large_enum_variant)]
502pub enum ChainSplit<N: NodePrimitives = reth_ethereum_primitives::EthPrimitives> {
503    /// Chain is not split. Pending chain is returned.
504    /// Given block split is higher than last block.
505    /// Or in case of split by hash when hash is unknown.
506    NoSplitPending(Chain<N>),
507    /// Chain is not split. Canonical chain is returned.
508    /// Given block split is lower than first block.
509    NoSplitCanonical(Chain<N>),
510    /// Chain is split into two: `[canonical]` and `[pending]`
511    /// The target of this chain split [`ChainSplitTarget`] belongs to the `canonical` chain.
512    Split {
513        /// Contains lower block numbers that are considered canonicalized. It ends with
514        /// the [`ChainSplitTarget`] block. The state of this chain is now empty and no longer
515        /// usable.
516        canonical: Chain<N>,
517        /// Right contains all subsequent blocks __after__ the [`ChainSplitTarget`] that are still
518        /// pending.
519        ///
520        /// The state of the original chain is moved here.
521        pending: Chain<N>,
522    },
523}
524
525/// Bincode-compatible [`Chain`] serde implementation.
526#[cfg(feature = "serde-bincode-compat")]
527pub(super) mod serde_bincode_compat {
528    use crate::{serde_bincode_compat, ExecutionOutcome};
529    use alloc::borrow::Cow;
530    use alloy_primitives::BlockNumber;
531    use reth_ethereum_primitives::EthPrimitives;
532    use reth_primitives_traits::{
533        serde_bincode_compat::{RecoveredBlock, SerdeBincodeCompat},
534        Block, NodePrimitives,
535    };
536    use reth_trie_common::serde_bincode_compat::updates::TrieUpdates;
537    use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
538    use serde_with::{DeserializeAs, SerializeAs};
539    use std::collections::BTreeMap;
540
541    /// Bincode-compatible [`super::Chain`] serde implementation.
542    ///
543    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
544    /// ```rust
545    /// use reth_execution_types::{serde_bincode_compat, Chain};
546    /// use serde::{Deserialize, Serialize};
547    /// use serde_with::serde_as;
548    ///
549    /// #[serde_as]
550    /// #[derive(Serialize, Deserialize)]
551    /// struct Data {
552    ///     #[serde_as(as = "serde_bincode_compat::Chain")]
553    ///     chain: Chain,
554    /// }
555    /// ```
556    #[derive(Debug, Serialize, Deserialize)]
557    pub struct Chain<'a, N = EthPrimitives>
558    where
559        N: NodePrimitives<
560            Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
561        >,
562    {
563        blocks: RecoveredBlocks<'a, N::Block>,
564        execution_outcome: serde_bincode_compat::ExecutionOutcome<'a, N::Receipt>,
565        trie_updates: Option<TrieUpdates<'a>>,
566    }
567
568    #[derive(Debug)]
569    struct RecoveredBlocks<
570        'a,
571        B: reth_primitives_traits::Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat>
572            + 'static,
573    >(Cow<'a, BTreeMap<BlockNumber, reth_primitives_traits::RecoveredBlock<B>>>);
574
575    impl<B> Serialize for RecoveredBlocks<'_, B>
576    where
577        B: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
578    {
579        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
580        where
581            S: Serializer,
582        {
583            let mut state = serializer.serialize_map(Some(self.0.len()))?;
584
585            for (block_number, block) in self.0.iter() {
586                state.serialize_entry(block_number, &RecoveredBlock::<'_, B>::from(block))?;
587            }
588
589            state.end()
590        }
591    }
592
593    impl<'de, B> Deserialize<'de> for RecoveredBlocks<'_, B>
594    where
595        B: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
596    {
597        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
598        where
599            D: Deserializer<'de>,
600        {
601            Ok(Self(Cow::Owned(
602                BTreeMap::<BlockNumber, RecoveredBlock<'_, B>>::deserialize(deserializer)
603                    .map(|blocks| blocks.into_iter().map(|(n, b)| (n, b.into())).collect())?,
604            )))
605        }
606    }
607
608    impl<'a, N> From<&'a super::Chain<N>> for Chain<'a, N>
609    where
610        N: NodePrimitives<
611            Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
612        >,
613    {
614        fn from(value: &'a super::Chain<N>) -> Self {
615            Self {
616                blocks: RecoveredBlocks(Cow::Borrowed(&value.blocks)),
617                execution_outcome: value.execution_outcome.as_repr(),
618                trie_updates: value.trie_updates.as_ref().map(Into::into),
619            }
620        }
621    }
622
623    impl<'a, N> From<Chain<'a, N>> for super::Chain<N>
624    where
625        N: NodePrimitives<
626            Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
627        >,
628    {
629        fn from(value: Chain<'a, N>) -> Self {
630            Self {
631                blocks: value.blocks.0.into_owned(),
632                execution_outcome: ExecutionOutcome::from_repr(value.execution_outcome),
633                trie_updates: value.trie_updates.map(Into::into),
634            }
635        }
636    }
637
638    impl<N> SerializeAs<super::Chain<N>> for Chain<'_, N>
639    where
640        N: NodePrimitives<
641            Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
642        >,
643    {
644        fn serialize_as<S>(source: &super::Chain<N>, serializer: S) -> Result<S::Ok, S::Error>
645        where
646            S: Serializer,
647        {
648            Chain::from(source).serialize(serializer)
649        }
650    }
651
652    impl<'de, N> DeserializeAs<'de, super::Chain<N>> for Chain<'de, N>
653    where
654        N: NodePrimitives<
655            Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
656        >,
657    {
658        fn deserialize_as<D>(deserializer: D) -> Result<super::Chain<N>, D::Error>
659        where
660            D: Deserializer<'de>,
661        {
662            Chain::deserialize(deserializer).map(Into::into)
663        }
664    }
665
666    #[cfg(test)]
667    mod tests {
668        use super::super::{serde_bincode_compat, Chain};
669        use arbitrary::Arbitrary;
670        use rand::Rng;
671        use reth_primitives_traits::RecoveredBlock;
672        use serde::{Deserialize, Serialize};
673        use serde_with::serde_as;
674
675        #[test]
676        fn test_chain_bincode_roundtrip() {
677            #[serde_as]
678            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
679            struct Data {
680                #[serde_as(as = "serde_bincode_compat::Chain")]
681                chain: Chain,
682            }
683
684            let mut bytes = [0u8; 1024];
685            rand::thread_rng().fill(bytes.as_mut_slice());
686            let data = Data {
687                chain: Chain::new(
688                    vec![RecoveredBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
689                        .unwrap()],
690                    Default::default(),
691                    None,
692                ),
693            };
694
695            let encoded = bincode::serialize(&data).unwrap();
696            let decoded: Data = bincode::deserialize(&encoded).unwrap();
697            assert_eq!(decoded, data);
698        }
699    }
700}
701
702#[cfg(test)]
703mod tests {
704    use super::*;
705    use alloy_consensus::TxType;
706    use alloy_primitives::{Address, B256};
707    use reth_ethereum_primitives::Receipt;
708    use revm::{primitives::HashMap, state::AccountInfo};
709
710    #[test]
711    fn chain_append() {
712        let block: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
713        let block1_hash = B256::new([0x01; 32]);
714        let block2_hash = B256::new([0x02; 32]);
715        let block3_hash = B256::new([0x03; 32]);
716        let block4_hash = B256::new([0x04; 32]);
717
718        let mut block1 = block.clone();
719        let mut block2 = block.clone();
720        let mut block3 = block.clone();
721        let mut block4 = block;
722
723        block1.set_hash(block1_hash);
724        block2.set_hash(block2_hash);
725        block3.set_hash(block3_hash);
726        block4.set_hash(block4_hash);
727
728        block3.set_parent_hash(block2_hash);
729
730        let mut chain1: Chain =
731            Chain { blocks: BTreeMap::from([(1, block1), (2, block2)]), ..Default::default() };
732
733        let chain2 =
734            Chain { blocks: BTreeMap::from([(3, block3), (4, block4)]), ..Default::default() };
735
736        assert!(chain1.append_chain(chain2.clone()).is_ok());
737
738        // chain1 got changed so this will fail
739        assert!(chain1.append_chain(chain2).is_err());
740    }
741
742    #[test]
743    fn test_number_split() {
744        let execution_outcome1: ExecutionOutcome = ExecutionOutcome::new(
745            BundleState::new(
746                vec![(
747                    Address::new([2; 20]),
748                    None,
749                    Some(AccountInfo::default()),
750                    HashMap::default(),
751                )],
752                vec![vec![(Address::new([2; 20]), None, vec![])]],
753                vec![],
754            ),
755            vec![vec![]],
756            1,
757            vec![],
758        );
759
760        let execution_outcome2 = ExecutionOutcome::new(
761            BundleState::new(
762                vec![(
763                    Address::new([3; 20]),
764                    None,
765                    Some(AccountInfo::default()),
766                    HashMap::default(),
767                )],
768                vec![vec![(Address::new([3; 20]), None, vec![])]],
769                vec![],
770            ),
771            vec![vec![]],
772            2,
773            vec![],
774        );
775
776        let mut block1: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
777        let block1_hash = B256::new([15; 32]);
778        block1.set_block_number(1);
779        block1.set_hash(block1_hash);
780        block1.push_sender(Address::new([4; 20]));
781
782        let mut block2: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
783        let block2_hash = B256::new([16; 32]);
784        block2.set_block_number(2);
785        block2.set_hash(block2_hash);
786        block2.push_sender(Address::new([4; 20]));
787
788        let mut block_state_extended = execution_outcome1;
789        block_state_extended.extend(execution_outcome2);
790
791        let chain: Chain =
792            Chain::new(vec![block1.clone(), block2.clone()], block_state_extended, None);
793
794        let (split1_execution_outcome, split2_execution_outcome) =
795            chain.execution_outcome.clone().split_at(2);
796
797        let chain_split1 = Chain {
798            execution_outcome: split1_execution_outcome.unwrap(),
799            blocks: BTreeMap::from([(1, block1.clone())]),
800            trie_updates: None,
801        };
802
803        let chain_split2 = Chain {
804            execution_outcome: split2_execution_outcome,
805            blocks: BTreeMap::from([(2, block2.clone())]),
806            trie_updates: None,
807        };
808
809        // return tip state
810        assert_eq!(
811            chain.execution_outcome_at_block(block2.number),
812            Some(chain.execution_outcome.clone())
813        );
814        assert_eq!(
815            chain.execution_outcome_at_block(block1.number),
816            Some(chain_split1.execution_outcome.clone())
817        );
818        // state at unknown block
819        assert_eq!(chain.execution_outcome_at_block(100), None);
820
821        // split in two
822        assert_eq!(
823            chain.clone().split(block1_hash.into()),
824            ChainSplit::Split { canonical: chain_split1, pending: chain_split2 }
825        );
826
827        // split at unknown block hash
828        assert_eq!(
829            chain.clone().split(B256::new([100; 32]).into()),
830            ChainSplit::NoSplitPending(chain.clone())
831        );
832
833        // split at higher number
834        assert_eq!(chain.clone().split(10u64.into()), ChainSplit::NoSplitPending(chain.clone()));
835
836        // split at lower number
837        assert_eq!(chain.clone().split(0u64.into()), ChainSplit::NoSplitPending(chain));
838    }
839
840    #[test]
841    fn receipts_by_block_hash() {
842        // Create a default RecoveredBlock object
843        let block: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
844
845        // Define block hashes for block1 and block2
846        let block1_hash = B256::new([0x01; 32]);
847        let block2_hash = B256::new([0x02; 32]);
848
849        // Clone the default block into block1 and block2
850        let mut block1 = block.clone();
851        let mut block2 = block;
852
853        // Set the hashes of block1 and block2
854        block1.set_hash(block1_hash);
855        block2.set_hash(block2_hash);
856
857        // Create a random receipt object, receipt1
858        let receipt1 = Receipt {
859            tx_type: TxType::Legacy,
860            cumulative_gas_used: 46913,
861            logs: vec![],
862            success: true,
863        };
864
865        // Create another random receipt object, receipt2
866        let receipt2 = Receipt {
867            tx_type: TxType::Legacy,
868            cumulative_gas_used: 1325345,
869            logs: vec![],
870            success: true,
871        };
872
873        // Create a Receipts object with a vector of receipt vectors
874        let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
875
876        // Create an ExecutionOutcome object with the created bundle, receipts, an empty requests
877        // vector, and first_block set to 10
878        let execution_outcome = ExecutionOutcome {
879            bundle: Default::default(),
880            receipts,
881            requests: vec![],
882            first_block: 10,
883        };
884
885        // Create a Chain object with a BTreeMap of blocks mapped to their block numbers,
886        // including block1_hash and block2_hash, and the execution_outcome
887        let chain: Chain = Chain {
888            blocks: BTreeMap::from([(10, block1), (11, block2)]),
889            execution_outcome: execution_outcome.clone(),
890            ..Default::default()
891        };
892
893        // Assert that the proper receipt vector is returned for block1_hash
894        assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
895
896        // Create an ExecutionOutcome object with a single receipt vector containing receipt1
897        let execution_outcome1 = ExecutionOutcome {
898            bundle: Default::default(),
899            receipts: vec![vec![receipt1]],
900            requests: vec![],
901            first_block: 10,
902        };
903
904        // Assert that the execution outcome at the first block contains only the first receipt
905        assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
906
907        // Assert that the execution outcome at the tip block contains the whole execution outcome
908        assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
909    }
910}