1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Helper function for calculating Merkle proofs and hashes.

use crate::EMPTY_OMMER_ROOT_HASH;
use alloc::vec::Vec;
use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawal};
use alloy_primitives::{keccak256, B256};
use alloy_rlp::Encodable;

#[doc(inline)]
pub use alloy_trie::root::{
    ordered_trie_root, ordered_trie_root_with_encoder, state_root, state_root_ref_unhashed,
    state_root_unhashed, state_root_unsorted, storage_root, storage_root_unhashed,
    storage_root_unsorted,
};

/// Calculate a transaction root.
///
/// `(rlp(index), encoded(tx))` pairs.
pub fn calculate_transaction_root<T>(transactions: &[T]) -> B256
where
    T: Encodable2718,
{
    ordered_trie_root_with_encoder(transactions, |tx: &T, buf| tx.encode_2718(buf))
}

/// Calculates the root hash of the withdrawals.
pub fn calculate_withdrawals_root(withdrawals: &[Withdrawal]) -> B256 {
    ordered_trie_root(withdrawals)
}

/// Calculates the root hash for ommer/uncle headers.
///
/// See [`Header`](crate::Header).
pub fn calculate_ommers_root<T>(ommers: &[T]) -> B256
where
    T: Encodable,
{
    // Check if `ommers` list is empty
    if ommers.is_empty() {
        return EMPTY_OMMER_ROOT_HASH;
    }
    // RLP Encode
    let mut ommers_rlp = Vec::new();
    alloy_rlp::encode_list(ommers, &mut ommers_rlp);
    keccak256(ommers_rlp)
}

/// Calculates the receipt root.
pub fn calculate_receipt_root<T>(receipts: &[T]) -> B256
where
    T: Encodable2718,
{
    ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_2718(buf))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptWithBloom, RlpEncodableReceipt,
        TxType, Typed2718,
    };
    use alloy_primitives::{b256, bloom, Address, Log, LogData};

    struct TypedReceipt {
        ty: TxType,
        receipt: Receipt,
    }

    impl RlpEncodableReceipt for TypedReceipt {
        fn rlp_encoded_length_with_bloom(&self, bloom: &alloy_primitives::Bloom) -> usize {
            let mut payload_length = self.eip2718_encoded_length_with_bloom(bloom);

            if !self.ty.is_legacy() {
                payload_length += alloy_rlp::Header {
                    list: false,
                    payload_length: self.eip2718_encoded_length_with_bloom(bloom),
                }
                .length();
            }

            payload_length
        }

        fn rlp_encode_with_bloom(
            &self,
            bloom: &alloy_primitives::Bloom,
            out: &mut dyn alloy_rlp::BufMut,
        ) {
            if !self.ty.is_legacy() {
                alloy_rlp::Header {
                    list: false,
                    payload_length: self.eip2718_encoded_length_with_bloom(bloom),
                }
                .encode(out)
            }
            self.eip2718_encode_with_bloom(bloom, out);
        }
    }

    impl Eip2718EncodableReceipt for TypedReceipt {
        fn eip2718_encoded_length_with_bloom(&self, bloom: &alloy_primitives::Bloom) -> usize {
            self.receipt.rlp_encoded_length_with_bloom(bloom) + (!self.ty.is_legacy()) as usize
        }

        fn eip2718_encode_with_bloom(
            &self,
            bloom: &alloy_primitives::Bloom,
            out: &mut dyn alloy_rlp::BufMut,
        ) {
            if !self.ty.is_legacy() {
                out.put_u8(self.ty.ty());
            }
            self.receipt.rlp_encode_with_bloom(bloom, out);
        }
    }

    impl Typed2718 for TypedReceipt {
        fn ty(&self) -> u8 {
            self.ty.ty()
        }
    }

    #[test]
    fn check_receipt_root_optimism() {
        let logs = vec![Log {
            address: Address::ZERO,
            data: LogData::new_unchecked(vec![], Default::default()),
        }];
        let logs_bloom = bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
        let receipt = ReceiptWithBloom {
            receipt: TypedReceipt {
                receipt: Receipt {
                    status: Eip658Value::success(),
                    cumulative_gas_used: 102068,
                    logs,
                },
                ty: TxType::Eip2930,
            },
            logs_bloom,
        };
        let receipt = vec![receipt];
        let root = calculate_receipt_root(&receipt);
        assert_eq!(root, b256!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0"));
    }
}