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