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 .cloned()
28 .map(|mut r| {
29 if let Some(receipt) = r.receipt.as_deposit_receipt_mut() {
30 receipt.deposit_nonce = None;
31 }
32 r
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};
83 use alloy_primitives::{b256, bloom, hex, Address, Bloom, 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 ReceiptWithBloom {
124 receipt: OpReceipt::Deposit(OpDepositReceipt {
125 inner: Receipt {
126 status: true.into(),
127 cumulative_gas_used: 46913,
128 logs: vec![],
129 },
130 deposit_nonce: Some(4012991u64),
131 deposit_receipt_version: None,
132 }),
133 logs_bloom: Bloom(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
134 },
135 ReceiptWithBloom {
137 receipt: OpReceipt::Eip1559(Receipt {
138 status: true.into(),
139 cumulative_gas_used: 118083,
140 logs: vec![
141 Log {
142 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
143 data: LogData::new_unchecked(
144 vec![
145 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
146 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
147 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
148 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
149 ],
150 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001"))
151 )
152 },
153 Log {
154 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
155 data: LogData::new_unchecked(
156 vec![
157 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
158 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
159 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
160 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
161 ],
162 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001"))
163 )
164 },
165 Log {
166 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
167 data: LogData::new_unchecked(
168 vec![
169 b256!("0x0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"),
170 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
171 b256!("0x000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
172 ], Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")))
173 },
174 ]}),
175 logs_bloom: Bloom(hex!("00001000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000010000").into()),
176 },
177 ReceiptWithBloom {
179 receipt: OpReceipt::Eip1559(Receipt {
180 status: true.into(),
181 cumulative_gas_used: 189253,
182 logs: vec![
183 Log {
184 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
185 data: LogData::new_unchecked(vec![
186 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
187 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
188 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
189 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
190 ],
191 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")))
192 },
193 Log {
194 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
195 data: LogData::new_unchecked(vec![
196 b256!("0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
197 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
198 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
199 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
200 ],
201 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")))
202 },
203 Log {
204 address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
205 data: LogData::new_unchecked(vec![
206 b256!("0x0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"),
207 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
208 b256!("0x0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
209 ],
210 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")))
211 },
212 ],
213 }),
214 logs_bloom: Bloom(hex!("00000000000000000000200000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000000000").into()),
215 },
216 ReceiptWithBloom {
218 receipt: OpReceipt::Eip1559(Receipt {
219 status: true.into(),
220 cumulative_gas_used: 346969,
221 logs: vec![
222 Log {
223 address: hex!("4200000000000000000000000000000000000006").into(),
224 data: LogData::new_unchecked( vec![
225 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
226 b256!("0x000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
227 b256!("0x0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"),
228 ],
229 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d8000")))
230 },
231 Log {
232 address: hex!("cf8e7e6b26f407dee615fc4db18bf829e7aa8c09").into(),
233 data: LogData::new_unchecked( vec![
234 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
235 b256!("0x0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"),
236 b256!("0x0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
237 ],
238 Bytes::from_static(&hex!("000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")))
239 },
240 Log {
241 address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(),
242 data: LogData::new_unchecked( vec![
243 b256!("0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"),
244 ],
245 Bytes::from_static(&hex!("000000000000000000000000000000000000000000000009bd50642785c15736000000000000000000000000000000000000000000011bb7ac324f724a29bbbf")))
246 },
247 Log {
248 address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(),
249 data: LogData::new_unchecked( vec![
250 b256!("0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"),
251 b256!("0x00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"),
252 b256!("0x0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
253 ],
254 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")))
255 },
256 Log {
257 address: hex!("6d0f8d488b669aa9ba2d0f0b7b75a88bf5051cd3").into(),
258 data: LogData::new_unchecked( vec![
259 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
260 b256!("0x0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
261 b256!("0x000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
262 ],
263 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000014bc73062aea8093")))
264 },
265 Log {
266 address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(),
267 data: LogData::new_unchecked( vec![
268 b256!("0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"),
269 ],
270 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000002f122cfadc1ca82a35000000000000000000000000000000000000000000000665879dc0609945d6d1")))
271 },
272 Log {
273 address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(),
274 data: LogData::new_unchecked( vec![
275 b256!("0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"),
276 b256!("0x00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"),
277 b256!("0x000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
278 ],
279 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e200000000000000000000000000000000000000000000000014bc73062aea80930000000000000000000000000000000000000000000000000000000000000000")))
280 },
281 ],
282 }),
283 logs_bloom: Bloom(hex!("00200000000000000000000080000000000000000000000000040000100004000000000000000000000000100000000000000000000000000000100000000000000000000000000002000008000000200000000200000000020000000000000040000000000000000400000200000000000000000000000000000010000000000400000000010400000000000000000000000000002000c80000004080002000000000000000400200000000800000000000000000000000000000000000000000000002000000000000000000000000000000000100001000000000000000000000002000000000000000000000010000000000000000000000800000800000").into()),
284 },
285 ReceiptWithBloom {
287 receipt: OpReceipt::Eip1559(Receipt {
288 status: true.into(),
289 cumulative_gas_used: 623249,
290 logs: vec![
291 Log {
292 address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
293 data: LogData::new_unchecked( vec![
294 b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
295 b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
296 b256!("0x000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
297 b256!("0x000000000000000000000000000000000000000000000000000000000011a1d3"),
298 ],
299 Default::default())
300 },
301 Log {
302 address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
303 data: LogData::new_unchecked( vec![
304 b256!("0x9d89e36eadf856db0ad9ffb5a569e07f95634dddd9501141ecf04820484ad0dc"),
305 b256!("0x000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
306 b256!("0x000000000000000000000000000000000000000000000000000000000011a1d3"),
307 ],
308 Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")))
309 },
310 Log {
311 address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
312 data: LogData::new_unchecked( vec![
313 b256!("0x110d160a1bedeea919a88fbc4b2a9fb61b7e664084391b6ca2740db66fef80fe"),
314 b256!("0x00000000000000000000000084d47f6eea8f8d87910448325519d1bb45c2972a"),
315 b256!("0x000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
316 b256!("0x000000000000000000000000000000000000000000000000000000000011a1d3"),
317 ],
318 Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007717500762343034303661353035646234633961386163316433306335633332303265370000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")))
319 },
320 ],
321 }),
322 logs_bloom: Bloom(hex!("00000000000000000000000000000000400000000000000000000000000000000000004000000000000001000000000000000002000000000100000000000000000000000000000000000008000000000000000000000000000000000000000004000000020000000000000000000800000000000000000000000010200100200008000002000000000000000000800000000000000000000002000000000000000000000000000000080000000000000000000000004000000000000000000000000002000000000000000000000000000000000000200000000000000020002000000000000000002000000000000000000000000000000000000000000000").into()),
323 },
324 ];
325 let root = calculate_receipt_root_optimism(&receipts, BASE_SEPOLIA.as_ref(), case.1);
326 assert_eq!(root, case.2);
327 }
328 }
329
330 #[test]
331 fn check_receipt_root_optimism() {
332 let logs = vec![Log {
333 address: Address::ZERO,
334 data: LogData::new_unchecked(vec![], Default::default()),
335 }];
336 let logs_bloom = bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
337 let receipt = ReceiptWithBloom {
338 receipt: OpReceipt::Eip2930(Receipt {
339 status: true.into(),
340 cumulative_gas_used: 102068,
341 logs,
342 }),
343 logs_bloom,
344 };
345 let receipt = vec![receipt];
346 let root = calculate_receipt_root_optimism(&receipt, BASE_SEPOLIA.as_ref(), 0);
347 assert_eq!(
348 root,
349 b256!("0xfe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0")
350 );
351 }
352}