reth_optimism_consensus/validation/
isthmus.rs
1use crate::OpConsensusError;
4use alloy_consensus::BlockHeader;
5use alloy_primitives::{address, Address, B256};
6use core::fmt::Debug;
7use reth_storage_api::{errors::ProviderResult, StorageRootProvider};
8use reth_trie_common::HashedStorage;
9use revm::database::BundleState;
10
11pub const ADDRESS_L2_TO_L1_MESSAGE_PASSER: Address =
13 address!("0x4200000000000000000000000000000000000016");
14
15pub fn ensure_withdrawals_storage_root_is_some<H: BlockHeader>(
18 header: H,
19) -> Result<(), OpConsensusError> {
20 header.withdrawals_root().ok_or(OpConsensusError::L2WithdrawalsRootMissing)?;
21
22 Ok(())
23}
24
25pub fn withdrawals_root<DB: StorageRootProvider>(
29 state_updates: &BundleState,
30 state: DB,
31) -> ProviderResult<B256> {
32 withdrawals_root_prehashed(
35 state_updates
36 .state()
37 .get(&ADDRESS_L2_TO_L1_MESSAGE_PASSER)
38 .map(|acc| {
39 HashedStorage::from_plain_storage(
40 acc.status,
41 acc.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
42 )
43 })
44 .unwrap_or_default(),
45 state,
46 )
47}
48
49pub fn withdrawals_root_prehashed<DB: StorageRootProvider>(
54 hashed_storage_updates: HashedStorage,
55 state: DB,
56) -> ProviderResult<B256> {
57 state.storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, hashed_storage_updates)
58}
59
60pub fn verify_withdrawals_root<DB, H>(
67 state_updates: &BundleState,
68 state: DB,
69 header: H,
70) -> Result<(), OpConsensusError>
71where
72 DB: StorageRootProvider,
73 H: BlockHeader + Debug,
74{
75 let header_storage_root =
76 header.withdrawals_root().ok_or(OpConsensusError::L2WithdrawalsRootMissing)?;
77
78 let storage_root = withdrawals_root(state_updates, state)
79 .map_err(OpConsensusError::L2WithdrawalsRootCalculationFail)?;
80
81 if header_storage_root != storage_root {
82 return Err(OpConsensusError::L2WithdrawalsRootMismatch {
83 header: header_storage_root,
84 exec_res: storage_root,
85 })
86 }
87
88 Ok(())
89}
90
91pub fn verify_withdrawals_root_prehashed<DB, H>(
99 hashed_storage_updates: HashedStorage,
100 state: DB,
101 header: H,
102) -> Result<(), OpConsensusError>
103where
104 DB: StorageRootProvider,
105 H: BlockHeader + core::fmt::Debug,
106{
107 let header_storage_root =
108 header.withdrawals_root().ok_or(OpConsensusError::L2WithdrawalsRootMissing)?;
109
110 let storage_root = withdrawals_root_prehashed(hashed_storage_updates, state)
111 .map_err(OpConsensusError::L2WithdrawalsRootCalculationFail)?;
112
113 if header_storage_root != storage_root {
114 return Err(OpConsensusError::L2WithdrawalsRootMismatch {
115 header: header_storage_root,
116 exec_res: storage_root,
117 })
118 }
119
120 Ok(())
121}
122
123#[cfg(test)]
124mod test {
125 use super::*;
126 use alloc::sync::Arc;
127 use alloy_chains::Chain;
128 use alloy_consensus::Header;
129 use alloy_primitives::{keccak256, B256, U256};
130 use core::str::FromStr;
131 use reth_db_common::init::init_genesis;
132 use reth_optimism_chainspec::OpChainSpecBuilder;
133 use reth_optimism_node::OpNode;
134 use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
135 use reth_provider::{
136 providers::BlockchainProvider, test_utils::create_test_provider_factory_with_node_types,
137 StateWriter,
138 };
139 use reth_revm::db::BundleState;
140 use reth_storage_api::StateProviderFactory;
141 use reth_trie::{test_utils::storage_root_prehashed, HashedStorage};
142 use reth_trie_common::HashedPostState;
143
144 #[test]
145 fn l2tol1_message_passer_no_withdrawals() {
146 let hashed_address = keccak256(ADDRESS_L2_TO_L1_MESSAGE_PASSER);
147
148 let init_storage = HashedStorage::from_iter(
150 false,
151 [
152 "50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
153 "512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
154 "51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
155 ]
156 .into_iter()
157 .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
158 );
159 let mut state = HashedPostState::default();
160 state.storages.insert(hashed_address, init_storage.clone());
161
162 let provider_factory = create_test_provider_factory_with_node_types::<OpNode>(Arc::new(
166 OpChainSpecBuilder::default().chain(Chain::dev()).genesis(Default::default()).build(),
167 ));
168 let _ = init_genesis(&provider_factory).unwrap();
169
170 let provider_rw = provider_factory.provider_rw().unwrap();
172 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
173 provider_rw.commit().unwrap();
174
175 let header = Header {
177 withdrawals_root: Some(storage_root_prehashed(init_storage.storage)),
178 ..Default::default()
179 };
180
181 let state_provider_factory = BlockchainProvider::new(provider_factory).unwrap();
183
184 verify_withdrawals_root(
186 &BundleState::default(),
187 state_provider_factory.latest().expect("load state"),
188 &header,
189 )
190 .unwrap();
191 }
192}