1use alloc::vec::Vec;
4use alloy_consensus::ReceiptWithBloom;
5use alloy_eips::eip2718::Encodable2718;
6use alloy_primitives::B256;
7use alloy_trie::root::ordered_trie_root_with_encoder;
8use reth_optimism_forks::OpHardforks;
9use reth_optimism_primitives::DepositReceipt;
10
11pub(crate) fn calculate_receipt_root_optimism<R: DepositReceipt>(
13 receipts: &[ReceiptWithBloom<&R>],
14 chain_spec: impl OpHardforks,
15 timestamp: u64,
16) -> B256 {
17 if chain_spec.is_regolith_active_at_timestamp(timestamp) &&
23 !chain_spec.is_canyon_active_at_timestamp(timestamp)
24 {
25 let receipts = receipts
26 .iter()
27 .map(|receipt| {
28 let mut receipt = receipt.clone().map_receipt(|r| r.clone());
29 if let Some(receipt) = receipt.receipt.as_deposit_receipt_mut() {
30 receipt.deposit_nonce = None;
31 }
32 receipt
33 })
34 .collect::<Vec<_>>();
35
36 return ordered_trie_root_with_encoder(receipts.as_slice(), |r, buf| r.encode_2718(buf))
37 }
38
39 ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_2718(buf))
40}
41
42pub fn calculate_receipt_root_no_memo_optimism<R: DepositReceipt>(
46 receipts: &[R],
47 chain_spec: impl OpHardforks,
48 timestamp: u64,
49) -> B256 {
50 if chain_spec.is_regolith_active_at_timestamp(timestamp) &&
56 !chain_spec.is_canyon_active_at_timestamp(timestamp)
57 {
58 let receipts = receipts
59 .iter()
60 .map(|r| {
61 let mut r = (*r).clone();
62 if let Some(receipt) = r.as_deposit_receipt_mut() {
63 receipt.deposit_nonce = None;
64 }
65 r
66 })
67 .collect::<Vec<_>>();
68
69 return ordered_trie_root_with_encoder(&receipts, |r, buf| {
70 r.with_bloom_ref().encode_2718(buf);
71 })
72 }
73
74 ordered_trie_root_with_encoder(receipts, |r, buf| {
75 r.with_bloom_ref().encode_2718(buf);
76 })
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use alloy_consensus::{Receipt, ReceiptWithBloom, TxReceipt};
83 use alloy_primitives::{b256, bloom, hex, Address, Bytes, Log, LogData};
84 use op_alloy_consensus::OpDepositReceipt;
85 use reth_optimism_chainspec::BASE_SEPOLIA;
86 use reth_optimism_primitives::OpReceipt;
87
88 #[test]
95 fn check_optimism_receipt_root() {
96 let cases = [
97 (
100 "bedrock",
101 1679079599,
102 b256!("0xe255fed45eae7ede0556fe4fabc77b0d294d18781a5a581cab09127bc4cd9ffb"),
103 ),
104 (
107 "regolith",
108 1679079600,
109 b256!("0xe255fed45eae7ede0556fe4fabc77b0d294d18781a5a581cab09127bc4cd9ffb"),
110 ),
111 (
114 "canyon",
115 1699981200,
116 b256!("0x6eefbb5efb95235476654a8bfbf8cb64a4f5f0b0c80b700b0c5964550beee6d7"),
117 ),
118 ];
119
120 for case in cases {
121 let receipts = vec![
122 OpReceipt::Deposit(OpDepositReceipt {
124 inner: Receipt {
125 status: true.into(),
126 cumulative_gas_used: 46913,
127 logs: vec![],
128 },
129 deposit_nonce: Some(4012991u64),
130 deposit_receipt_version: None,
131 }),
132 OpReceipt::Eip1559(Receipt {
134 status: true.into(),
135 cumulative_gas_used: 118083,
136 logs: vec![
137 Log {
138 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
139 data: LogData::new_unchecked(
140 vec![
141 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
142 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
143 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
144 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
145 ],
146 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001"))
147 )
148 },
149 Log {
150 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
151 data: LogData::new_unchecked(
152 vec![
153 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
154 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
155 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
156 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
157 ],
158 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001"))
159 )
160 },
161 Log {
162 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
163 data: LogData::new_unchecked(
164 vec![
165 b256!("0x0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"),
166 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
167 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
168 ], Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")))
169 },
170 ]}),
171 OpReceipt::Eip1559(Receipt {
173 status: true.into(),
174 cumulative_gas_used: 189253,
175 logs: vec![
176 Log {
177 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
178 data: LogData::new_unchecked(vec![
179 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
180 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
181 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
182 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
183 ],
184 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")))
185 },
186 Log {
187 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
188 data: LogData::new_unchecked(vec![
189 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
190 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
191 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
192 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
193 ],
194 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")))
195 },
196 Log {
197 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
198 data: LogData::new_unchecked(vec![
199 b256!("0x0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"),
200 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
201 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
202 ],
203 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")))
204 },
205 ],
206 }),
207 OpReceipt::Eip1559(Receipt {
209 status: true.into(),
210 cumulative_gas_used: 346969,
211 logs: vec![
212 Log {
213 address: hex!("4200000000000000000000000000000000000006").into(),
214 data: LogData::new_unchecked( vec![
215 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
216 b256!("0x000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
217 b256!("0x0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"),
218 ],
219 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d8000")))
220 },
221 Log {
222 address: hex!("cf8e7e6b26f407dee615fc4db18bf829e7aa8c09").into(),
223 data: LogData::new_unchecked( vec![
224 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
225 b256!("0x0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"),
226 b256!("0x0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
227 ],
228 Bytes::from_static(&hex!("000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")))
229 },
230 Log {
231 address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(),
232 data: LogData::new_unchecked( vec![
233 b256!("0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"),
234 ],
235 Bytes::from_static(&hex!("000000000000000000000000000000000000000000000009bd50642785c15736000000000000000000000000000000000000000000011bb7ac324f724a29bbbf")))
236 },
237 Log {
238 address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(),
239 data: LogData::new_unchecked( vec![
240 b256!("0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"),
241 b256!("0x00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"),
242 b256!("0x0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
243 ],
244 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")))
245 },
246 Log {
247 address: hex!("6d0f8d488b669aa9ba2d0f0b7b75a88bf5051cd3").into(),
248 data: LogData::new_unchecked( vec![
249 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
250 b256!("0x0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
251 b256!("0x000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
252 ],
253 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000014bc73062aea8093")))
254 },
255 Log {
256 address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(),
257 data: LogData::new_unchecked( vec![
258 b256!("0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"),
259 ],
260 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000002f122cfadc1ca82a35000000000000000000000000000000000000000000000665879dc0609945d6d1")))
261 },
262 Log {
263 address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(),
264 data: LogData::new_unchecked( vec![
265 b256!("0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"),
266 b256!("0x00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"),
267 b256!("0x000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
268 ],
269 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e200000000000000000000000000000000000000000000000014bc73062aea80930000000000000000000000000000000000000000000000000000000000000000")))
270 },
271 ],
272 }),
273 OpReceipt::Eip1559(Receipt {
275 status: true.into(),
276 cumulative_gas_used: 623249,
277 logs: vec![
278 Log {
279 address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
280 data: LogData::new_unchecked( vec![
281 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
282 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
283 b256!("0x000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
284 b256!("0x000000000000000000000000000000000000000000000000000000000011a1d3"),
285 ],
286 Default::default())
287 },
288 Log {
289 address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
290 data: LogData::new_unchecked( vec![
291 b256!("0x9d89e36eadf856db0ad9ffb5a569e07f95634dddd9501141ecf04820484ad0dc"),
292 b256!("0x000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
293 b256!("0x000000000000000000000000000000000000000000000000000000000011a1d3"),
294 ],
295 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")))
296 },
297 Log {
298 address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
299 data: LogData::new_unchecked( vec![
300 b256!("0x110d160a1bedeea919a88fbc4b2a9fb61b7e664084391b6ca2740db66fef80fe"),
301 b256!("0x00000000000000000000000084d47f6eea8f8d87910448325519d1bb45c2972a"),
302 b256!("0x000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
303 b256!("0x000000000000000000000000000000000000000000000000000000000011a1d3"),
304 ],
305 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007717500762343034303661353035646234633961386163316433306335633332303265370000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")))
306 },
307 ],
308 }),
309 ];
310 let root = calculate_receipt_root_optimism(
311 &receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>(),
312 BASE_SEPOLIA.as_ref(),
313 case.1,
314 );
315 assert_eq!(root, case.2);
316 }
317 }
318
319 #[test]
320 fn check_receipt_root_optimism() {
321 let logs = vec![Log {
322 address: Address::ZERO,
323 data: LogData::new_unchecked(vec![], Default::default()),
324 }];
325 let logs_bloom = bloom!(
326 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"
327 );
328 let inner =
329 OpReceipt::Eip2930(Receipt { status: true.into(), cumulative_gas_used: 102068, logs });
330 let receipt = ReceiptWithBloom { receipt: &inner, logs_bloom };
331 let receipt = vec![receipt];
332 let root = calculate_receipt_root_optimism(&receipt, BASE_SEPOLIA.as_ref(), 0);
333 assert_eq!(
334 root,
335 b256!("0xfe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0")
336 );
337 }
338}