reth_execution_types/
execution_outcome.rs

1use crate::{BlockExecutionOutput, BlockExecutionResult};
2use alloc::{vec, vec::Vec};
3use alloy_eips::eip7685::Requests;
4use alloy_primitives::{logs_bloom, map::HashMap, Address, BlockNumber, Bloom, Log, B256, U256};
5use reth_primitives_traits::{Account, Bytecode, Receipt, StorageEntry};
6use reth_trie_common::{HashedPostState, KeyHasher};
7use revm::{
8    database::{states::BundleState, BundleAccount},
9    state::{AccountInfo, FlaggedStorage},
10};
11
12/// Type used to initialize revms bundle state.
13pub type BundleStateInit = HashMap<
14    Address,
15    (Option<Account>, Option<Account>, HashMap<B256, (FlaggedStorage, FlaggedStorage)>),
16>;
17
18/// Types used inside `RevertsInit` to initialize revms reverts.
19pub type AccountRevertInit = (Option<Option<Account>>, Vec<StorageEntry>);
20
21/// Type used to initialize revms reverts.
22pub type RevertsInit = HashMap<BlockNumber, HashMap<Address, AccountRevertInit>>;
23
24/// Represents a changed account
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct ChangedAccount {
27    /// The address of the account.
28    pub address: Address,
29    /// Account nonce.
30    pub nonce: u64,
31    /// Account balance.
32    pub balance: U256,
33}
34
35impl ChangedAccount {
36    /// Creates a new [`ChangedAccount`] with the given address and 0 balance and nonce.
37    pub const fn empty(address: Address) -> Self {
38        Self { address, nonce: 0, balance: U256::ZERO }
39    }
40}
41
42/// Represents the outcome of block execution, including post-execution changes and reverts.
43///
44/// The `ExecutionOutcome` structure aggregates the state changes over an arbitrary number of
45/// blocks, capturing the resulting state, receipts, and requests following the execution.
46#[derive(Debug, Clone, PartialEq, Eq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct ExecutionOutcome<T = reth_ethereum_primitives::Receipt> {
49    /// Bundle state with reverts.
50    pub bundle: BundleState,
51    /// The collection of receipts.
52    /// Outer vector stores receipts for each block sequentially.
53    /// The inner vector stores receipts ordered by transaction number.
54    pub receipts: Vec<Vec<T>>,
55    /// First block of bundle state.
56    pub first_block: BlockNumber,
57    /// The collection of EIP-7685 requests.
58    /// Outer vector stores requests for each block sequentially.
59    /// The inner vector stores requests ordered by transaction number.
60    ///
61    /// A transaction may have zero or more requests, so the length of the inner vector is not
62    /// guaranteed to be the same as the number of transactions.
63    pub requests: Vec<Requests>,
64}
65
66impl<T> Default for ExecutionOutcome<T> {
67    fn default() -> Self {
68        Self {
69            bundle: Default::default(),
70            receipts: Default::default(),
71            first_block: Default::default(),
72            requests: Default::default(),
73        }
74    }
75}
76
77impl<T> ExecutionOutcome<T> {
78    /// Creates a new `ExecutionOutcome`.
79    ///
80    /// This constructor initializes a new `ExecutionOutcome` instance with the provided
81    /// bundle state, receipts, first block number, and EIP-7685 requests.
82    pub const fn new(
83        bundle: BundleState,
84        receipts: Vec<Vec<T>>,
85        first_block: BlockNumber,
86        requests: Vec<Requests>,
87    ) -> Self {
88        Self { bundle, receipts, first_block, requests }
89    }
90
91    /// Creates a new `ExecutionOutcome` from initialization parameters.
92    ///
93    /// This constructor initializes a new `ExecutionOutcome` instance using detailed
94    /// initialization parameters.
95    pub fn new_init(
96        state_init: BundleStateInit,
97        revert_init: RevertsInit,
98        contracts_init: impl IntoIterator<Item = (B256, Bytecode)>,
99        receipts: Vec<Vec<T>>,
100        first_block: BlockNumber,
101        requests: Vec<Requests>,
102    ) -> Self {
103        // sort reverts by block number
104        let mut reverts = revert_init.into_iter().collect::<Vec<_>>();
105        reverts.sort_unstable_by_key(|a| a.0);
106
107        // initialize revm bundle
108        let bundle = BundleState::new(
109            state_init.into_iter().map(|(address, (original, present, storage))| {
110                (
111                    address,
112                    original.map(Into::into),
113                    present.map(Into::into),
114                    storage
115                        .into_iter()
116                        .map(|(k, (orig_value, new_value))| (k.into(), (orig_value, new_value)))
117                        .collect(),
118                )
119            }),
120            reverts.into_iter().map(|(_, reverts)| {
121                // does not needs to be sorted, it is done when taking reverts.
122                reverts.into_iter().map(|(address, (original, storage))| {
123                    (
124                        address,
125                        original.map(|i| i.map(Into::into)),
126                        storage.into_iter().map(|entry| (entry.key.into(), entry.value)),
127                    )
128                })
129            }),
130            contracts_init.into_iter().map(|(code_hash, bytecode)| (code_hash, bytecode.0)),
131        );
132
133        Self { bundle, receipts, first_block, requests }
134    }
135
136    /// Creates a new `ExecutionOutcome` from a single block execution result.
137    pub fn single(block_number: u64, output: BlockExecutionOutput<T>) -> Self {
138        Self {
139            bundle: output.state,
140            receipts: vec![output.result.receipts],
141            first_block: block_number,
142            requests: vec![output.result.requests],
143        }
144    }
145
146    /// Creates a new `ExecutionOutcome` from multiple [`BlockExecutionResult`]s.
147    pub fn from_blocks(
148        first_block: u64,
149        bundle: BundleState,
150        results: Vec<BlockExecutionResult<T>>,
151    ) -> Self {
152        let mut value = Self { bundle, first_block, receipts: Vec::new(), requests: Vec::new() };
153        for result in results {
154            value.receipts.push(result.receipts);
155            value.requests.push(result.requests);
156        }
157        value
158    }
159
160    /// Return revm bundle state.
161    pub const fn state(&self) -> &BundleState {
162        &self.bundle
163    }
164
165    /// Returns mutable revm bundle state.
166    pub const fn state_mut(&mut self) -> &mut BundleState {
167        &mut self.bundle
168    }
169
170    /// Set first block.
171    pub const fn set_first_block(&mut self, first_block: BlockNumber) {
172        self.first_block = first_block;
173    }
174
175    /// Return iterator over all accounts
176    pub fn accounts_iter(&self) -> impl Iterator<Item = (Address, Option<&AccountInfo>)> {
177        self.bundle.state().iter().map(|(a, acc)| (*a, acc.info.as_ref()))
178    }
179
180    /// Return iterator over all [`BundleAccount`]s in the bundle
181    pub fn bundle_accounts_iter(&self) -> impl Iterator<Item = (Address, &BundleAccount)> {
182        self.bundle.state().iter().map(|(a, acc)| (*a, acc))
183    }
184
185    /// Get account if account is known.
186    pub fn account(&self, address: &Address) -> Option<Option<Account>> {
187        self.bundle.account(address).map(|a| a.info.as_ref().map(Into::into))
188    }
189
190    /// Get storage if value is known.
191    ///
192    /// This means that depending on status we can potentially return `U256::ZERO`.
193    pub fn storage(&self, address: &Address, storage_key: U256) -> Option<FlaggedStorage> {
194        self.bundle.account(address).and_then(|a| a.storage_slot(storage_key))
195    }
196
197    /// Return bytecode if known.
198    pub fn bytecode(&self, code_hash: &B256) -> Option<Bytecode> {
199        self.bundle.bytecode(code_hash).map(Bytecode)
200    }
201
202    /// Returns [`HashedPostState`] for this execution outcome.
203    /// See [`HashedPostState::from_bundle_state`] for more info.
204    pub fn hash_state_slow<KH: KeyHasher>(&self) -> HashedPostState {
205        HashedPostState::from_bundle_state::<KH>(&self.bundle.state)
206    }
207
208    /// Transform block number to the index of block.
209    pub fn block_number_to_index(&self, block_number: BlockNumber) -> Option<usize> {
210        if self.first_block > block_number {
211            return None
212        }
213        let index = block_number - self.first_block;
214        if index >= self.receipts.len() as u64 {
215            return None
216        }
217        Some(index as usize)
218    }
219
220    /// Returns the receipt root for all recorded receipts.
221    /// Note: this function calculated Bloom filters for every receipt and created merkle trees
222    /// of receipt. This is a expensive operation.
223    pub fn generic_receipts_root_slow(
224        &self,
225        block_number: BlockNumber,
226        f: impl FnOnce(&[T]) -> B256,
227    ) -> Option<B256> {
228        Some(f(self.receipts.get(self.block_number_to_index(block_number)?)?))
229    }
230
231    /// Returns reference to receipts.
232    pub const fn receipts(&self) -> &Vec<Vec<T>> {
233        &self.receipts
234    }
235
236    /// Returns mutable reference to receipts.
237    pub const fn receipts_mut(&mut self) -> &mut Vec<Vec<T>> {
238        &mut self.receipts
239    }
240
241    /// Return all block receipts
242    pub fn receipts_by_block(&self, block_number: BlockNumber) -> &[T] {
243        let Some(index) = self.block_number_to_index(block_number) else { return &[] };
244        &self.receipts[index]
245    }
246
247    /// Is execution outcome empty.
248    pub fn is_empty(&self) -> bool {
249        self.len() == 0
250    }
251
252    /// Number of blocks in the execution outcome.
253    pub fn len(&self) -> usize {
254        self.receipts.len()
255    }
256
257    /// Return first block of the execution outcome
258    pub const fn first_block(&self) -> BlockNumber {
259        self.first_block
260    }
261
262    /// Return last block of the execution outcome
263    pub fn last_block(&self) -> BlockNumber {
264        (self.first_block + self.len() as u64).saturating_sub(1)
265    }
266
267    /// Revert the state to the given block number.
268    ///
269    /// Returns false if the block number is not in the bundle state.
270    ///
271    /// # Note
272    ///
273    /// The provided block number will stay inside the bundle state.
274    pub fn revert_to(&mut self, block_number: BlockNumber) -> bool {
275        let Some(index) = self.block_number_to_index(block_number) else { return false };
276
277        // +1 is for number of blocks that we have as index is included.
278        let new_len = index + 1;
279        let rm_trx: usize = self.len() - new_len;
280
281        // remove receipts
282        self.receipts.truncate(new_len);
283        // remove requests
284        self.requests.truncate(new_len);
285        // Revert last n reverts.
286        self.bundle.revert(rm_trx);
287
288        true
289    }
290
291    /// Splits the block range state at a given block number.
292    /// Returns two split states ([..at], [at..]).
293    /// The plain state of the 2nd bundle state will contain extra changes
294    /// that were made in state transitions belonging to the lower state.
295    ///
296    /// # Panics
297    ///
298    /// If the target block number is not included in the state block range.
299    pub fn split_at(self, at: BlockNumber) -> (Option<Self>, Self)
300    where
301        T: Clone,
302    {
303        if at == self.first_block {
304            return (None, self)
305        }
306
307        let (mut lower_state, mut higher_state) = (self.clone(), self);
308
309        // Revert lower state to [..at].
310        lower_state.revert_to(at.checked_sub(1).unwrap());
311
312        // Truncate higher state to [at..].
313        let at_idx = higher_state.block_number_to_index(at).unwrap();
314        higher_state.receipts = higher_state.receipts.split_off(at_idx);
315        // Ensure that there are enough requests to truncate.
316        // Sometimes we just have receipts and no requests.
317        if at_idx < higher_state.requests.len() {
318            higher_state.requests = higher_state.requests.split_off(at_idx);
319        }
320        higher_state.bundle.take_n_reverts(at_idx);
321        higher_state.first_block = at;
322
323        (Some(lower_state), higher_state)
324    }
325
326    /// Extend one state from another
327    ///
328    /// For state this is very sensitive operation and should be used only when
329    /// we know that other state was build on top of this one.
330    /// In most cases this would be true.
331    pub fn extend(&mut self, other: Self) {
332        self.bundle.extend(other.bundle);
333        self.receipts.extend(other.receipts);
334        self.requests.extend(other.requests);
335    }
336
337    /// Prepends present the state with the given `BundleState`.
338    /// It adds changes from the given state but does not override any existing changes.
339    ///
340    /// Reverts  and receipts are not updated.
341    pub fn prepend_state(&mut self, mut other: BundleState) {
342        let other_len = other.reverts.len();
343        // take this bundle
344        let this_bundle = core::mem::take(&mut self.bundle);
345        // extend other bundle with this
346        other.extend(this_bundle);
347        // discard other reverts
348        other.take_n_reverts(other_len);
349        // swap bundles
350        core::mem::swap(&mut self.bundle, &mut other)
351    }
352
353    /// Create a new instance with updated receipts.
354    pub fn with_receipts(mut self, receipts: Vec<Vec<T>>) -> Self {
355        self.receipts = receipts;
356        self
357    }
358
359    /// Create a new instance with updated requests.
360    pub fn with_requests(mut self, requests: Vec<Requests>) -> Self {
361        self.requests = requests;
362        self
363    }
364
365    /// Returns an iterator over all changed accounts from the `ExecutionOutcome`.
366    ///
367    /// This method filters the accounts to return only those that have undergone changes
368    /// and maps them into `ChangedAccount` instances, which include the address, nonce, and
369    /// balance.
370    pub fn changed_accounts(&self) -> impl Iterator<Item = ChangedAccount> + '_ {
371        self.accounts_iter().filter_map(|(addr, acc)| acc.map(|acc| (addr, acc))).map(
372            |(address, acc)| ChangedAccount { address, nonce: acc.nonce, balance: acc.balance },
373        )
374    }
375}
376
377impl<T: Receipt<Log = Log>> ExecutionOutcome<T> {
378    /// Returns an iterator over all block logs.
379    pub fn logs(&self, block_number: BlockNumber) -> Option<impl Iterator<Item = &Log>> {
380        let index = self.block_number_to_index(block_number)?;
381        Some(self.receipts[index].iter().flat_map(|r| r.logs()))
382    }
383
384    /// Return blocks logs bloom
385    pub fn block_logs_bloom(&self, block_number: BlockNumber) -> Option<Bloom> {
386        Some(logs_bloom(self.logs(block_number)?))
387    }
388}
389
390impl ExecutionOutcome {
391    /// Returns the ethereum receipt root for all recorded receipts.
392    ///
393    /// Note: this function calculated Bloom filters for every receipt and created merkle trees
394    /// of receipt. This is a expensive operation.
395    pub fn ethereum_receipts_root(&self, block_number: BlockNumber) -> Option<B256> {
396        self.generic_receipts_root_slow(
397            block_number,
398            reth_ethereum_primitives::Receipt::calculate_receipt_root_no_memo,
399        )
400    }
401}
402
403impl<T> From<(BlockExecutionOutput<T>, BlockNumber)> for ExecutionOutcome<T> {
404    fn from((output, block_number): (BlockExecutionOutput<T>, BlockNumber)) -> Self {
405        Self::single(block_number, output)
406    }
407}
408
409#[cfg(feature = "serde-bincode-compat")]
410pub(super) mod serde_bincode_compat {
411    use alloc::{borrow::Cow, vec::Vec};
412    use alloy_eips::eip7685::Requests;
413    use alloy_primitives::BlockNumber;
414    use reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat;
415    use revm::database::BundleState;
416    use serde::{Deserialize, Deserializer, Serialize, Serializer};
417    use serde_with::{DeserializeAs, SerializeAs};
418
419    /// Bincode-compatible [`super::ExecutionOutcome`] serde implementation.
420    ///
421    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
422    /// ```rust
423    /// use reth_execution_types::{serde_bincode_compat, ExecutionOutcome};
424    /// ///
425    /// use reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat;
426    /// use serde::{Deserialize, Serialize};
427    /// use serde_with::serde_as;
428    ///
429    /// #[serde_as]
430    /// #[derive(Serialize, Deserialize)]
431    /// struct Data<T: SerdeBincodeCompat + core::fmt::Debug> {
432    ///     #[serde_as(as = "serde_bincode_compat::ExecutionOutcome<'_, T>")]
433    ///     chain: ExecutionOutcome<T>,
434    /// }
435    /// ```
436    #[derive(Debug, Serialize, Deserialize)]
437    pub struct ExecutionOutcome<'a, T>
438    where
439        T: SerdeBincodeCompat + core::fmt::Debug,
440    {
441        bundle: Cow<'a, BundleState>,
442        receipts: Vec<Vec<T::BincodeRepr<'a>>>,
443        first_block: BlockNumber,
444        #[expect(clippy::owned_cow)]
445        requests: Cow<'a, Vec<Requests>>,
446    }
447
448    impl<'a, T> From<&'a super::ExecutionOutcome<T>> for ExecutionOutcome<'a, T>
449    where
450        T: SerdeBincodeCompat + core::fmt::Debug,
451    {
452        fn from(value: &'a super::ExecutionOutcome<T>) -> Self {
453            ExecutionOutcome {
454                bundle: Cow::Borrowed(&value.bundle),
455                receipts: value
456                    .receipts
457                    .iter()
458                    .map(|vec| vec.iter().map(|receipt| T::as_repr(receipt)).collect())
459                    .collect(),
460                first_block: value.first_block,
461                requests: Cow::Borrowed(&value.requests),
462            }
463        }
464    }
465
466    impl<'a, T> From<ExecutionOutcome<'a, T>> for super::ExecutionOutcome<T>
467    where
468        T: SerdeBincodeCompat + core::fmt::Debug,
469    {
470        fn from(value: ExecutionOutcome<'a, T>) -> Self {
471            Self {
472                bundle: value.bundle.into_owned(),
473                receipts: value
474                    .receipts
475                    .into_iter()
476                    .map(|vec| vec.into_iter().map(|receipt| T::from_repr(receipt)).collect())
477                    .collect(),
478                first_block: value.first_block,
479                requests: value.requests.into_owned(),
480            }
481        }
482    }
483
484    impl<T> SerializeAs<super::ExecutionOutcome<T>> for ExecutionOutcome<'_, T>
485    where
486        T: SerdeBincodeCompat + core::fmt::Debug,
487    {
488        fn serialize_as<S>(
489            source: &super::ExecutionOutcome<T>,
490            serializer: S,
491        ) -> Result<S::Ok, S::Error>
492        where
493            S: Serializer,
494        {
495            ExecutionOutcome::from(source).serialize(serializer)
496        }
497    }
498
499    impl<'de, T> DeserializeAs<'de, super::ExecutionOutcome<T>> for ExecutionOutcome<'de, T>
500    where
501        T: SerdeBincodeCompat + core::fmt::Debug,
502    {
503        fn deserialize_as<D>(deserializer: D) -> Result<super::ExecutionOutcome<T>, D::Error>
504        where
505            D: Deserializer<'de>,
506        {
507            ExecutionOutcome::deserialize(deserializer).map(Into::into)
508        }
509    }
510
511    impl<T: SerdeBincodeCompat + core::fmt::Debug> SerdeBincodeCompat for super::ExecutionOutcome<T> {
512        type BincodeRepr<'a> = ExecutionOutcome<'a, T>;
513
514        fn as_repr(&self) -> Self::BincodeRepr<'_> {
515            self.into()
516        }
517
518        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
519            repr.into()
520        }
521    }
522
523    #[cfg(test)]
524    mod tests {
525        use super::super::{serde_bincode_compat, ExecutionOutcome};
526        use rand::Rng;
527        use reth_ethereum_primitives::Receipt;
528        use reth_primitives_traits::serde_bincode_compat::SerdeBincodeCompat;
529        use serde::{Deserialize, Serialize};
530        use serde_with::serde_as;
531
532        #[test]
533        fn test_chain_bincode_roundtrip() {
534            #[serde_as]
535            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
536            struct Data<T: SerdeBincodeCompat + core::fmt::Debug> {
537                #[serde_as(as = "serde_bincode_compat::ExecutionOutcome<'_, T>")]
538                data: ExecutionOutcome<T>,
539            }
540
541            let mut bytes = [0u8; 1024];
542            rand::rng().fill(bytes.as_mut_slice());
543            let data = Data {
544                data: ExecutionOutcome {
545                    bundle: Default::default(),
546                    receipts: vec![],
547                    first_block: 0,
548                    requests: vec![],
549                },
550            };
551
552            let encoded = bincode::serialize(&data).unwrap();
553            let decoded = bincode::deserialize::<Data<Receipt>>(&encoded).unwrap();
554            assert_eq!(decoded, data);
555        }
556    }
557}
558
559#[cfg(test)]
560mod tests {
561    use super::*;
562    use alloy_consensus::TxType;
563    use alloy_primitives::{bytes, Address, LogData, B256};
564
565    #[test]
566    fn test_initialisation() {
567        // Create a new BundleState object with initial data
568        let bundle = BundleState::new(
569            vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
570            vec![vec![(Address::new([2; 20]), None, vec![])]],
571            vec![],
572        );
573
574        // Create a Receipts object with a vector of receipt vectors
575        let receipts = vec![vec![Some(reth_ethereum_primitives::Receipt {
576            tx_type: TxType::Legacy,
577            cumulative_gas_used: 46913,
578            logs: vec![],
579            success: true,
580        })]];
581
582        // Create a Requests object with a vector of requests
583        let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
584
585        // Define the first block number
586        let first_block = 123;
587
588        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
589        // first_block
590        let exec_res = ExecutionOutcome {
591            bundle: bundle.clone(),
592            receipts: receipts.clone(),
593            requests: requests.clone(),
594            first_block,
595        };
596
597        // Assert that creating a new ExecutionOutcome using the constructor matches exec_res
598        assert_eq!(
599            ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
600            exec_res
601        );
602
603        // Create a BundleStateInit object and insert initial data
604        let mut state_init: BundleStateInit = HashMap::default();
605        state_init
606            .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
607
608        // Create a HashMap for account reverts and insert initial data
609        let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
610        revert_inner.insert(Address::new([2; 20]), (None, vec![]));
611
612        // Create a RevertsInit object and insert the revert_inner data
613        let mut revert_init: RevertsInit = HashMap::default();
614        revert_init.insert(123, revert_inner);
615
616        // Assert that creating a new ExecutionOutcome using the new_init method matches
617        // exec_res
618        assert_eq!(
619            ExecutionOutcome::new_init(
620                state_init,
621                revert_init,
622                vec![],
623                receipts,
624                first_block,
625                requests,
626            ),
627            exec_res
628        );
629    }
630
631    #[test]
632    fn test_block_number_to_index() {
633        // Create a Receipts object with a vector of receipt vectors
634        let receipts = vec![vec![Some(reth_ethereum_primitives::Receipt {
635            tx_type: TxType::Legacy,
636            cumulative_gas_used: 46913,
637            logs: vec![],
638            success: true,
639        })]];
640
641        // Define the first block number
642        let first_block = 123;
643
644        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
645        // first_block
646        let exec_res = ExecutionOutcome {
647            bundle: Default::default(),
648            receipts,
649            requests: vec![],
650            first_block,
651        };
652
653        // Test before the first block
654        assert_eq!(exec_res.block_number_to_index(12), None);
655
656        // Test after the first block but index larger than receipts length
657        assert_eq!(exec_res.block_number_to_index(133), None);
658
659        // Test after the first block
660        assert_eq!(exec_res.block_number_to_index(123), Some(0));
661    }
662
663    #[test]
664    fn test_get_logs() {
665        // Create a Receipts object with a vector of receipt vectors
666        let receipts = vec![vec![reth_ethereum_primitives::Receipt {
667            tx_type: TxType::Legacy,
668            cumulative_gas_used: 46913,
669            logs: vec![Log::<LogData>::default()],
670            success: true,
671        }]];
672
673        // Define the first block number
674        let first_block = 123;
675
676        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
677        // first_block
678        let exec_res = ExecutionOutcome {
679            bundle: Default::default(),
680            receipts,
681            requests: vec![],
682            first_block,
683        };
684
685        // Get logs for block number 123
686        let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
687
688        // Assert that the logs match the expected logs
689        assert_eq!(logs, vec![&Log::<LogData>::default()]);
690    }
691
692    #[test]
693    fn test_receipts_by_block() {
694        // Create a Receipts object with a vector of receipt vectors
695        let receipts = vec![vec![Some(reth_ethereum_primitives::Receipt {
696            tx_type: TxType::Legacy,
697            cumulative_gas_used: 46913,
698            logs: vec![Log::<LogData>::default()],
699            success: true,
700        })]];
701
702        // Define the first block number
703        let first_block = 123;
704
705        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
706        // first_block
707        let exec_res = ExecutionOutcome {
708            bundle: Default::default(), // Default value for bundle
709            receipts,                   // Include the created receipts
710            requests: vec![],           // Empty vector for requests
711            first_block,                // Set the first block number
712        };
713
714        // Get receipts for block number 123 and convert the result into a vector
715        let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
716
717        // Assert that the receipts for block number 123 match the expected receipts
718        assert_eq!(
719            receipts_by_block,
720            vec![&Some(reth_ethereum_primitives::Receipt {
721                tx_type: TxType::Legacy,
722                cumulative_gas_used: 46913,
723                logs: vec![Log::<LogData>::default()],
724                success: true,
725            })]
726        );
727    }
728
729    #[test]
730    fn test_receipts_len() {
731        // Create a Receipts object with a vector of receipt vectors
732        let receipts = vec![vec![Some(reth_ethereum_primitives::Receipt {
733            tx_type: TxType::Legacy,
734            cumulative_gas_used: 46913,
735            logs: vec![Log::<LogData>::default()],
736            success: true,
737        })]];
738
739        // Create an empty Receipts object
740        let receipts_empty = vec![];
741
742        // Define the first block number
743        let first_block = 123;
744
745        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
746        // first_block
747        let exec_res = ExecutionOutcome {
748            bundle: Default::default(), // Default value for bundle
749            receipts,                   // Include the created receipts
750            requests: vec![],           // Empty vector for requests
751            first_block,                // Set the first block number
752        };
753
754        // Assert that the length of receipts in exec_res is 1
755        assert_eq!(exec_res.len(), 1);
756
757        // Assert that exec_res is not empty
758        assert!(!exec_res.is_empty());
759
760        // Create a ExecutionOutcome object with an empty Receipts object
761        let exec_res_empty_receipts: ExecutionOutcome = ExecutionOutcome {
762            bundle: Default::default(), // Default value for bundle
763            receipts: receipts_empty,   // Include the empty receipts
764            requests: vec![],           // Empty vector for requests
765            first_block,                // Set the first block number
766        };
767
768        // Assert that the length of receipts in exec_res_empty_receipts is 0
769        assert_eq!(exec_res_empty_receipts.len(), 0);
770
771        // Assert that exec_res_empty_receipts is empty
772        assert!(exec_res_empty_receipts.is_empty());
773    }
774
775    #[test]
776    fn test_revert_to() {
777        // Create a random receipt object
778        let receipt = reth_ethereum_primitives::Receipt {
779            tx_type: TxType::Legacy,
780            cumulative_gas_used: 46913,
781            logs: vec![],
782            success: true,
783        };
784
785        // Create a Receipts object with a vector of receipt vectors
786        let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
787
788        // Define the first block number
789        let first_block = 123;
790
791        // Create a request.
792        let request = bytes!("deadbeef");
793
794        // Create a vector of Requests containing the request.
795        let requests =
796            vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
797
798        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
799        // first_block
800        let mut exec_res =
801            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
802
803        // Assert that the revert_to method returns true when reverting to the initial block number.
804        assert!(exec_res.revert_to(123));
805
806        // Assert that the receipts are properly cut after reverting to the initial block number.
807        assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
808
809        // Assert that the requests are properly cut after reverting to the initial block number.
810        assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
811
812        // Assert that the revert_to method returns false when attempting to revert to a block
813        // number greater than the initial block number.
814        assert!(!exec_res.revert_to(133));
815
816        // Assert that the revert_to method returns false when attempting to revert to a block
817        // number less than the initial block number.
818        assert!(!exec_res.revert_to(10));
819    }
820
821    #[test]
822    fn test_extend_execution_outcome() {
823        // Create a Receipt object with specific attributes.
824        let receipt = reth_ethereum_primitives::Receipt {
825            tx_type: TxType::Legacy,
826            cumulative_gas_used: 46913,
827            logs: vec![],
828            success: true,
829        };
830
831        // Create a Receipts object containing the receipt.
832        let receipts = vec![vec![Some(receipt.clone())]];
833
834        // Create a request.
835        let request = bytes!("deadbeef");
836
837        // Create a vector of Requests containing the request.
838        let requests = vec![Requests::new(vec![request.clone()])];
839
840        // Define the initial block number.
841        let first_block = 123;
842
843        // Create an ExecutionOutcome object.
844        let mut exec_res =
845            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
846
847        // Extend the ExecutionOutcome object by itself.
848        exec_res.extend(exec_res.clone());
849
850        // Assert the extended ExecutionOutcome matches the expected outcome.
851        assert_eq!(
852            exec_res,
853            ExecutionOutcome {
854                bundle: Default::default(),
855                receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
856                requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
857                first_block: 123,
858            }
859        );
860    }
861
862    #[test]
863    fn test_split_at_execution_outcome() {
864        // Create a random receipt object
865        let receipt = reth_ethereum_primitives::Receipt {
866            tx_type: TxType::Legacy,
867            cumulative_gas_used: 46913,
868            logs: vec![],
869            success: true,
870        };
871
872        // Create a Receipts object with a vector of receipt vectors
873        let receipts = vec![
874            vec![Some(receipt.clone())],
875            vec![Some(receipt.clone())],
876            vec![Some(receipt.clone())],
877        ];
878
879        // Define the first block number
880        let first_block = 123;
881
882        // Create a request.
883        let request = bytes!("deadbeef");
884
885        // Create a vector of Requests containing the request.
886        let requests = vec![
887            Requests::new(vec![request.clone()]),
888            Requests::new(vec![request.clone()]),
889            Requests::new(vec![request.clone()]),
890        ];
891
892        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
893        // first_block
894        let exec_res =
895            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
896
897        // Split the ExecutionOutcome at block number 124
898        let result = exec_res.clone().split_at(124);
899
900        // Define the expected lower ExecutionOutcome after splitting
901        let lower_execution_outcome = ExecutionOutcome {
902            bundle: Default::default(),
903            receipts: vec![vec![Some(receipt.clone())]],
904            requests: vec![Requests::new(vec![request.clone()])],
905            first_block,
906        };
907
908        // Define the expected higher ExecutionOutcome after splitting
909        let higher_execution_outcome = ExecutionOutcome {
910            bundle: Default::default(),
911            receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
912            requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
913            first_block: 124,
914        };
915
916        // Assert that the split result matches the expected lower and higher outcomes
917        assert_eq!(result.0, Some(lower_execution_outcome));
918        assert_eq!(result.1, higher_execution_outcome);
919
920        // Assert that splitting at the first block number returns None for the lower outcome
921        assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
922    }
923
924    #[test]
925    fn test_changed_accounts() {
926        // Set up some sample accounts
927        let address1 = Address::random();
928        let address2 = Address::random();
929        let address3 = Address::random();
930
931        // Set up account info with some changes
932        let account_info1 =
933            AccountInfo { nonce: 1, balance: U256::from(100), code_hash: B256::ZERO, code: None };
934        let account_info2 =
935            AccountInfo { nonce: 2, balance: U256::from(200), code_hash: B256::ZERO, code: None };
936
937        // Set up the bundle state with these accounts
938        let mut bundle_state = BundleState::default();
939        bundle_state.state.insert(
940            address1,
941            BundleAccount {
942                info: Some(account_info1),
943                storage: Default::default(),
944                original_info: Default::default(),
945                status: Default::default(),
946            },
947        );
948        bundle_state.state.insert(
949            address2,
950            BundleAccount {
951                info: Some(account_info2),
952                storage: Default::default(),
953                original_info: Default::default(),
954                status: Default::default(),
955            },
956        );
957
958        // Unchanged account
959        bundle_state.state.insert(
960            address3,
961            BundleAccount {
962                info: None,
963                storage: Default::default(),
964                original_info: Default::default(),
965                status: Default::default(),
966            },
967        );
968
969        let execution_outcome: ExecutionOutcome = ExecutionOutcome {
970            bundle: bundle_state,
971            receipts: Default::default(),
972            first_block: 0,
973            requests: vec![],
974        };
975
976        // Get the changed accounts
977        let changed_accounts: Vec<ChangedAccount> = execution_outcome.changed_accounts().collect();
978
979        // Assert that the changed accounts match the expected ones
980        assert_eq!(changed_accounts.len(), 2);
981
982        assert!(changed_accounts.contains(&ChangedAccount {
983            address: address1,
984            nonce: 1,
985            balance: U256::from(100)
986        }));
987
988        assert!(changed_accounts.contains(&ChangedAccount {
989            address: address2,
990            nonce: 2,
991            balance: U256::from(200)
992        }));
993    }
994}