alloy_consensus/receipt/
mod.rs1use alloc::vec::Vec;
2use alloy_primitives::Bloom;
3use alloy_rlp::BufMut;
4use core::fmt;
5
6mod envelope;
7pub use envelope::ReceiptEnvelope;
8
9mod receipts;
10pub use receipts::{Receipt, ReceiptWithBloom, Receipts};
11
12mod status;
13pub use status::Eip658Value;
14
15use alloy_eips::Typed2718;
16
17#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
19pub(crate) mod serde_bincode_compat {
20    pub use super::{envelope::serde_bincode_compat::*, receipts::serde_bincode_compat::*};
21}
22
23#[doc(alias = "TransactionReceipt")]
25#[auto_impl::auto_impl(&, Arc)]
26pub trait TxReceipt: Clone + fmt::Debug + PartialEq + Eq + Send + Sync {
27    type Log;
29
30    fn status_or_post_state(&self) -> Eip658Value;
39
40    fn status(&self) -> bool;
55
56    fn bloom(&self) -> Bloom;
59
60    fn bloom_cheap(&self) -> Option<Bloom> {
63        None
64    }
65
66    #[auto_impl(keep_default_for(&, Arc))]
69    fn with_bloom_ref(&self) -> ReceiptWithBloom<&Self> {
70        ReceiptWithBloom { logs_bloom: self.bloom(), receipt: self }
71    }
72
73    #[auto_impl(keep_default_for(&, Arc))]
76    fn into_with_bloom(self) -> ReceiptWithBloom<Self> {
77        ReceiptWithBloom { logs_bloom: self.bloom(), receipt: self }
78    }
79
80    fn cumulative_gas_used(&self) -> u64;
82
83    fn logs(&self) -> &[Self::Log];
85
86    #[auto_impl(keep_default_for(&, Arc))]
88    fn into_logs(self) -> Vec<Self::Log>
89    where
90        Self::Log: Clone,
91    {
92        self.logs().to_vec()
93    }
94}
95
96#[auto_impl::auto_impl(&)]
98pub trait RlpEncodableReceipt {
99    fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;
101
102    fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
104}
105
106pub trait RlpDecodableReceipt: Sized {
108    fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>>;
110}
111
112#[auto_impl::auto_impl(&)]
118pub trait Eip2718EncodableReceipt: RlpEncodableReceipt + Typed2718 {
119    fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;
121
122    fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use alloy_eips::eip2718::Encodable2718;
130    use alloy_primitives::{address, b256, bytes, hex, Log, LogData};
131    use alloy_rlp::{Decodable, Encodable};
132
133    #[test]
135    fn encode_legacy_receipt() {
136        let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
137
138        let mut data = vec![];
139        let receipt =
140            ReceiptEnvelope::Legacy(ReceiptWithBloom {
141                receipt: Receipt {
142                    cumulative_gas_used: 0x1,
143                    logs: vec![Log {
144                        address: address!("0000000000000000000000000000000000000011"),
145                        data: LogData::new_unchecked(
146                            vec![
147                    b256!("000000000000000000000000000000000000000000000000000000000000dead"),
148                    b256!("000000000000000000000000000000000000000000000000000000000000beef"),
149                ],
150                            bytes!("0100ff"),
151                        ),
152                    }],
153                    status: false.into(),
154                },
155                logs_bloom: [0; 256].into(),
156            });
157
158        receipt.network_encode(&mut data);
159
160        assert_eq!(receipt.length(), expected.len());
162        assert_eq!(data, expected);
163    }
164
165    #[test]
167    fn decode_legacy_receipt() {
168        let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
169
170        let expected =
172            ReceiptWithBloom {
173                receipt: Receipt {
174                    cumulative_gas_used: 0x1,
175                    logs: vec![Log {
176                        address: address!("0000000000000000000000000000000000000011"),
177                        data: LogData::new_unchecked(
178                            vec![
179                        b256!("000000000000000000000000000000000000000000000000000000000000dead"),
180                        b256!("000000000000000000000000000000000000000000000000000000000000beef"),
181                    ],
182                            bytes!("0100ff"),
183                        ),
184                    }],
185                    status: false.into(),
186                },
187                logs_bloom: [0; 256].into(),
188            };
189
190        let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
191        assert_eq!(receipt, expected);
192    }
193
194    #[test]
195    fn gigantic_receipt() {
196        let receipt = Receipt {
197            cumulative_gas_used: 16747627,
198            status: true.into(),
199            logs: vec![
200                Log {
201                    address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
202                    data: LogData::new_unchecked(
203                        vec![b256!(
204                            "c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9"
205                        )],
206                        vec![1; 0xffffff].into(),
207                    ),
208                },
209                Log {
210                    address: address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"),
211                    data: LogData::new_unchecked(
212                        vec![b256!(
213                            "8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2"
214                        )],
215                        vec![1; 0xffffff].into(),
216                    ),
217                },
218            ],
219        }
220        .with_bloom();
221
222        let len = receipt.length();
223        let mut data = Vec::with_capacity(receipt.length());
224
225        receipt.encode(&mut data);
226        assert_eq!(data.len(), len);
227        let decoded = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
228
229        assert_eq!(decoded, receipt);
232    }
233
234    #[test]
235    fn can_encode_by_reference() {
236        let receipt: Receipt =
237            Receipt { cumulative_gas_used: 16747627, status: true.into(), logs: vec![] };
238
239        let encoded_ref = alloy_rlp::encode(&ReceiptWithBloom {
240            receipt: &receipt,
241            logs_bloom: receipt.bloom_slow(),
242        });
243        let encoded =
244            alloy_rlp::encode(&ReceiptWithBloom { logs_bloom: receipt.bloom_slow(), receipt });
245
246        assert_eq!(encoded, encoded_ref);
247    }
248}