alloy_consensus/
proofs.rs1use crate::EMPTY_OMMER_ROOT_HASH;
4use alloc::vec::Vec;
5use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawal};
6use alloy_primitives::{keccak256, B256};
7use alloy_rlp::Encodable;
8
9#[doc(inline)]
10pub use alloy_trie::root::{
11 ordered_trie_root, ordered_trie_root_with_encoder, state_root, state_root_ref_unhashed,
12 state_root_unhashed, state_root_unsorted, storage_root, storage_root_unhashed,
13 storage_root_unsorted,
14};
15
16pub fn calculate_transaction_root<T>(transactions: &[T]) -> B256
20where
21 T: Encodable2718,
22{
23 ordered_trie_root_with_encoder(transactions, |tx: &T, buf| tx.encode_2718(buf))
24}
25
26pub fn calculate_withdrawals_root(withdrawals: &[Withdrawal]) -> B256 {
28 ordered_trie_root(withdrawals)
29}
30
31pub fn calculate_ommers_root<T>(ommers: &[T]) -> B256
35where
36 T: Encodable,
37{
38 if ommers.is_empty() {
40 return EMPTY_OMMER_ROOT_HASH;
41 }
42 let mut ommers_rlp = Vec::new();
44 alloy_rlp::encode_list(ommers, &mut ommers_rlp);
45 keccak256(ommers_rlp)
46}
47
48pub fn calculate_receipt_root<T>(receipts: &[T]) -> B256
50where
51 T: Encodable2718,
52{
53 ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_2718(buf))
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use crate::{
60 Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptWithBloom, RlpEncodableReceipt,
61 TxType, Typed2718,
62 };
63 use alloy_primitives::{b256, bloom, Address, Log, LogData};
64
65 struct TypedReceipt {
66 ty: TxType,
67 receipt: Receipt,
68 }
69
70 impl RlpEncodableReceipt for TypedReceipt {
71 fn rlp_encoded_length_with_bloom(&self, bloom: &alloy_primitives::Bloom) -> usize {
72 let mut payload_length = self.eip2718_encoded_length_with_bloom(bloom);
73
74 if !self.ty.is_legacy() {
75 payload_length += alloy_rlp::Header {
76 list: false,
77 payload_length: self.eip2718_encoded_length_with_bloom(bloom),
78 }
79 .length();
80 }
81
82 payload_length
83 }
84
85 fn rlp_encode_with_bloom(
86 &self,
87 bloom: &alloy_primitives::Bloom,
88 out: &mut dyn alloy_rlp::BufMut,
89 ) {
90 if !self.ty.is_legacy() {
91 alloy_rlp::Header {
92 list: false,
93 payload_length: self.eip2718_encoded_length_with_bloom(bloom),
94 }
95 .encode(out)
96 }
97 self.eip2718_encode_with_bloom(bloom, out);
98 }
99 }
100
101 impl Eip2718EncodableReceipt for TypedReceipt {
102 fn eip2718_encoded_length_with_bloom(&self, bloom: &alloy_primitives::Bloom) -> usize {
103 self.receipt.rlp_encoded_length_with_bloom(bloom) + (!self.ty.is_legacy()) as usize
104 }
105
106 fn eip2718_encode_with_bloom(
107 &self,
108 bloom: &alloy_primitives::Bloom,
109 out: &mut dyn alloy_rlp::BufMut,
110 ) {
111 if !self.ty.is_legacy() {
112 out.put_u8(self.ty.ty());
113 }
114 self.receipt.rlp_encode_with_bloom(bloom, out);
115 }
116 }
117
118 impl Typed2718 for TypedReceipt {
119 fn ty(&self) -> u8 {
120 self.ty.ty()
121 }
122 }
123
124 #[test]
125 fn check_receipt_root_optimism() {
126 let logs = vec![Log {
127 address: Address::ZERO,
128 data: LogData::new_unchecked(vec![], Default::default()),
129 }];
130 let logs_bloom = bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
131 let receipt = ReceiptWithBloom {
132 receipt: TypedReceipt {
133 receipt: Receipt {
134 status: Eip658Value::success(),
135 cumulative_gas_used: 102068,
136 logs,
137 },
138 ty: TxType::Eip2930,
139 },
140 logs_bloom,
141 };
142 let receipt = vec![receipt];
143 let root = calculate_receipt_root(&receipt);
144 assert_eq!(root, b256!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0"));
145 }
146}