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