|  | 
| 1 | 1 | use alloy_consensus::BlockHeader; | 
| 2 |  | -use alloy_primitives::{keccak256, B256}; | 
|  | 2 | +use alloy_primitives::{keccak256, Address, B256, U256}; | 
| 3 | 3 | use alloy_rpc_types_debug::ExecutionWitness; | 
| 4 | 4 | use pretty_assertions::Comparison; | 
| 5 | 5 | use reth_chainspec::{EthChainSpec, EthereumHardforks}; | 
| 6 | 6 | use reth_engine_primitives::InvalidBlockHook; | 
| 7 | 7 | use reth_evm::execute::{BlockExecutorProvider, Executor}; | 
| 8 | 8 | use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedHeader}; | 
| 9 | 9 | use reth_provider::{BlockExecutionOutput, ChainSpecProvider, StateProviderFactory}; | 
| 10 |  | -use reth_revm::database::StateProviderDatabase; | 
|  | 10 | +use reth_revm::{database::StateProviderDatabase, db::BundleState, state::AccountInfo}; | 
| 11 | 11 | use reth_rpc_api::DebugApiClient; | 
| 12 | 12 | use reth_tracing::tracing::warn; | 
| 13 | 13 | use reth_trie::{updates::TrieUpdates, HashedStorage}; | 
|  | 14 | +use revm_bytecode::Bytecode; | 
|  | 15 | +use revm_database::states::{ | 
|  | 16 | +    reverts::{AccountInfoRevert, RevertToSlot}, | 
|  | 17 | +    AccountStatus, StorageSlot, | 
|  | 18 | +}; | 
| 14 | 19 | use serde::Serialize; | 
| 15 |  | -use std::{fmt::Debug, fs::File, io::Write, path::PathBuf}; | 
|  | 20 | +use std::{collections::BTreeMap, fmt::Debug, fs::File, io::Write, path::PathBuf}; | 
|  | 21 | + | 
|  | 22 | +#[derive(Debug, PartialEq, Eq)] | 
|  | 23 | +struct AccountRevertSorted { | 
|  | 24 | +    pub account: AccountInfoRevert, | 
|  | 25 | +    pub storage: BTreeMap<U256, RevertToSlot>, | 
|  | 26 | +    pub previous_status: AccountStatus, | 
|  | 27 | +    pub wipe_storage: bool, | 
|  | 28 | +} | 
|  | 29 | + | 
|  | 30 | +#[derive(Debug, PartialEq, Eq)] | 
|  | 31 | +struct BundleAccountSorted { | 
|  | 32 | +    pub info: Option<AccountInfo>, | 
|  | 33 | +    pub original_info: Option<AccountInfo>, | 
|  | 34 | +    /// Contains both original and present state. | 
|  | 35 | +    /// When extracting changeset we compare if original value is different from present value. | 
|  | 36 | +    /// If it is different we add it to changeset. | 
|  | 37 | +    /// If Account was destroyed we ignore original value and compare present state with | 
|  | 38 | +    /// `U256::ZERO`. | 
|  | 39 | +    pub storage: BTreeMap<U256, StorageSlot>, | 
|  | 40 | +    /// Account status. | 
|  | 41 | +    pub status: AccountStatus, | 
|  | 42 | +} | 
|  | 43 | + | 
|  | 44 | +#[derive(Debug, PartialEq, Eq)] | 
|  | 45 | +struct BundleStateSorted { | 
|  | 46 | +    /// Account state | 
|  | 47 | +    pub state: BTreeMap<Address, BundleAccountSorted>, | 
|  | 48 | +    /// All created contracts in this block. | 
|  | 49 | +    pub contracts: BTreeMap<B256, Bytecode>, | 
|  | 50 | +    /// Changes to revert | 
|  | 51 | +    /// | 
|  | 52 | +    /// **Note**: Inside vector is *not* sorted by address. | 
|  | 53 | +    /// | 
|  | 54 | +    /// But it is unique by address. | 
|  | 55 | +    pub reverts: Vec<Vec<(Address, AccountRevertSorted)>>, | 
|  | 56 | +    /// The size of the plain state in the bundle state | 
|  | 57 | +    pub state_size: usize, | 
|  | 58 | +    /// The size of reverts in the bundle state | 
|  | 59 | +    pub reverts_size: usize, | 
|  | 60 | +} | 
|  | 61 | + | 
|  | 62 | +impl BundleStateSorted { | 
|  | 63 | +    fn from_bundle_state(bundle_state: &BundleState) -> Self { | 
|  | 64 | +        let state = bundle_state | 
|  | 65 | +            .state | 
|  | 66 | +            .clone() | 
|  | 67 | +            .into_iter() | 
|  | 68 | +            .map(|(address, account)| { | 
|  | 69 | +                { | 
|  | 70 | +                    ( | 
|  | 71 | +                        address, | 
|  | 72 | +                        BundleAccountSorted { | 
|  | 73 | +                            info: account.info, | 
|  | 74 | +                            original_info: account.original_info, | 
|  | 75 | +                            status: account.status, | 
|  | 76 | +                            storage: BTreeMap::from_iter(account.storage), | 
|  | 77 | +                        }, | 
|  | 78 | +                    ) | 
|  | 79 | +                } | 
|  | 80 | +            }) | 
|  | 81 | +            .collect(); | 
|  | 82 | + | 
|  | 83 | +        let contracts = BTreeMap::from_iter(bundle_state.contracts.clone()); | 
|  | 84 | + | 
|  | 85 | +        let reverts = bundle_state | 
|  | 86 | +            .reverts | 
|  | 87 | +            .iter() | 
|  | 88 | +            .map(|block| { | 
|  | 89 | +                block | 
|  | 90 | +                    .iter() | 
|  | 91 | +                    .map(|(address, account_revert)| { | 
|  | 92 | +                        ( | 
|  | 93 | +                            *address, | 
|  | 94 | +                            AccountRevertSorted { | 
|  | 95 | +                                account: account_revert.account.clone(), | 
|  | 96 | +                                previous_status: account_revert.previous_status, | 
|  | 97 | +                                wipe_storage: account_revert.wipe_storage, | 
|  | 98 | +                                storage: BTreeMap::from_iter(account_revert.storage.clone()), | 
|  | 99 | +                            }, | 
|  | 100 | +                        ) | 
|  | 101 | +                    }) | 
|  | 102 | +                    .collect() | 
|  | 103 | +            }) | 
|  | 104 | +            .collect(); | 
|  | 105 | + | 
|  | 106 | +        let state_size = bundle_state.state_size; | 
|  | 107 | +        let reverts_size = bundle_state.reverts_size; | 
|  | 108 | + | 
|  | 109 | +        Self { state, contracts, reverts, state_size, reverts_size } | 
|  | 110 | +    } | 
|  | 111 | +} | 
| 16 | 112 | 
 | 
| 17 | 113 | /// Generates a witness for the given block and saves it to a file. | 
| 18 | 114 | #[derive(Debug)] | 
| @@ -184,7 +280,12 @@ where | 
| 184 | 280 |             )?; | 
| 185 | 281 | 
 | 
| 186 | 282 |             let filename = format!("{}_{}.bundle_state.diff", block.number(), block.hash()); | 
| 187 |  | -            let diff_path = self.save_diff(filename, &bundle_state, &output.state)?; | 
|  | 283 | +            // Convert bundle state to sorted struct which has BTreeMap instead of HashMap to | 
|  | 284 | +            // have deterministric ordering | 
|  | 285 | +            let bundle_state_sorted = BundleStateSorted::from_bundle_state(&bundle_state); | 
|  | 286 | +            let output_state_sorted = BundleStateSorted::from_bundle_state(&output.state); | 
|  | 287 | + | 
|  | 288 | +            let diff_path = self.save_diff(filename, &bundle_state_sorted, &output_state_sorted)?; | 
| 188 | 289 | 
 | 
| 189 | 290 |             warn!( | 
| 190 | 291 |                 target: "engine::invalid_block_hooks::witness", | 
|  | 
0 commit comments