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_encoded, ordered_trie_root_with_encoder, state_root,
12 state_root_ref_unhashed, state_root_unhashed, state_root_unsorted, storage_root,
13 storage_root_unhashed, 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 Eip2718DecodableReceipt, Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptWithBloom,
61 RlpDecodableReceipt, RlpEncodableReceipt, TxType, Typed2718,
62 };
63 use alloy_eips::{eip2718::Eip2718Result, Decodable2718, Encodable2718};
64 use alloy_primitives::{b256, bloom, Address, Log, LogData};
65
66 #[derive(Debug, PartialEq, Eq)]
67 struct TypedReceipt {
68 ty: TxType,
69 receipt: Receipt,
70 }
71
72 impl RlpEncodableReceipt for TypedReceipt {
73 fn rlp_encoded_length_with_bloom(&self, bloom: &alloy_primitives::Bloom) -> usize {
74 let mut payload_length = self.eip2718_encoded_length_with_bloom(bloom);
75
76 if !self.ty.is_legacy() {
77 payload_length += alloy_rlp::Header {
78 list: false,
79 payload_length: self.eip2718_encoded_length_with_bloom(bloom),
80 }
81 .length();
82 }
83
84 payload_length
85 }
86
87 fn rlp_encode_with_bloom(
88 &self,
89 bloom: &alloy_primitives::Bloom,
90 out: &mut dyn alloy_rlp::BufMut,
91 ) {
92 if !self.ty.is_legacy() {
93 alloy_rlp::Header {
94 list: false,
95 payload_length: self.eip2718_encoded_length_with_bloom(bloom),
96 }
97 .encode(out)
98 }
99 self.eip2718_encode_with_bloom(bloom, out);
100 }
101 }
102
103 impl Eip2718EncodableReceipt for TypedReceipt {
104 fn eip2718_encoded_length_with_bloom(&self, bloom: &alloy_primitives::Bloom) -> usize {
105 self.receipt.rlp_encoded_length_with_bloom(bloom) + (!self.ty.is_legacy()) as usize
106 }
107
108 fn eip2718_encode_with_bloom(
109 &self,
110 bloom: &alloy_primitives::Bloom,
111 out: &mut dyn alloy_rlp::BufMut,
112 ) {
113 if !self.ty.is_legacy() {
114 out.put_u8(self.ty.ty());
115 }
116 self.receipt.rlp_encode_with_bloom(bloom, out);
117 }
118 }
119
120 impl Eip2718DecodableReceipt for TypedReceipt {
121 fn typed_decode_with_bloom(
122 ty: u8,
123 buf: &mut &[u8],
124 ) -> Eip2718Result<ReceiptWithBloom<Self>> {
125 let ty =
126 TxType::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("Unexpected type"))?;
127
128 Ok(Receipt::rlp_decode_with_bloom(buf)?.map_receipt(|receipt| Self { ty, receipt }))
129 }
130
131 fn fallback_decode_with_bloom(buf: &mut &[u8]) -> Eip2718Result<ReceiptWithBloom<Self>> {
132 Ok(Receipt::rlp_decode_with_bloom(buf)?
133 .map_receipt(|receipt| Self { ty: TxType::Legacy, receipt }))
134 }
135 }
136
137 impl Typed2718 for TypedReceipt {
138 fn ty(&self) -> u8 {
139 self.ty.ty()
140 }
141 }
142
143 #[test]
144 fn check_receipt_root_optimism() {
145 let logs = vec![Log {
146 address: Address::ZERO,
147 data: LogData::new_unchecked(vec![], Default::default()),
148 }];
149 let logs_bloom = bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
150 let receipt = ReceiptWithBloom {
151 receipt: TypedReceipt {
152 receipt: Receipt {
153 status: Eip658Value::success(),
154 cumulative_gas_used: 102068,
155 logs,
156 },
157 ty: TxType::Eip2930,
158 },
159 logs_bloom,
160 };
161 let root = calculate_receipt_root(&[&receipt]);
162 assert_eq!(root, b256!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0"));
163
164 let encoded = receipt.encoded_2718();
165 let decoded = ReceiptWithBloom::<TypedReceipt>::decode_2718_exact(&encoded).unwrap();
166
167 assert_eq!(decoded, receipt);
168 }
169}