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