alloy_consensus/
proofs.rs

1//! Helper function for calculating Merkle proofs and hashes.
2
3use 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
16/// Calculate a transaction root.
17///
18/// `(rlp(index), encoded(tx))` pairs.
19pub 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
26/// Calculates the root hash of the withdrawals.
27pub fn calculate_withdrawals_root(withdrawals: &[Withdrawal]) -> B256 {
28    ordered_trie_root(withdrawals)
29}
30
31/// Calculates the root hash for ommer/uncle headers.
32///
33/// See [`Header`](crate::Header).
34pub fn calculate_ommers_root<T>(ommers: &[T]) -> B256
35where
36    T: Encodable,
37{
38    // Check if `ommers` list is empty
39    if ommers.is_empty() {
40        return EMPTY_OMMER_ROOT_HASH;
41    }
42    // RLP Encode
43    let mut ommers_rlp = Vec::new();
44    alloy_rlp::encode_list(ommers, &mut ommers_rlp);
45    keccak256(ommers_rlp)
46}
47
48/// Calculates the receipt root.
49pub 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}