Skip to main content

reth_execution_types/
execution_outcome.rs

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