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
9pub(crate) mod receipt2;
10pub use receipt2::{EthereumReceipt, TxTy};
11
12mod receipts;
13pub use receipts::{Receipt, ReceiptWithBloom, Receipts};
14
15mod status;
16pub use status::Eip658Value;
17
18use alloy_eips::{eip2718::Eip2718Result, Typed2718};
19
20#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
22pub(crate) mod serde_bincode_compat {
23 pub use super::{
24 envelope::serde_bincode_compat::*, receipt2::serde_bincode_compat::*,
25 receipts::serde_bincode_compat::*,
26 };
27}
28
29#[doc(alias = "TransactionReceipt")]
31#[auto_impl::auto_impl(&, Arc)]
32pub trait TxReceipt: Clone + fmt::Debug + PartialEq + Eq + Send + Sync {
33 type Log;
35
36 fn status_or_post_state(&self) -> Eip658Value;
45
46 fn status(&self) -> bool;
61
62 fn bloom(&self) -> Bloom;
65
66 fn bloom_cheap(&self) -> Option<Bloom> {
69 None
70 }
71
72 #[auto_impl(keep_default_for(&, Arc))]
75 fn with_bloom_ref(&self) -> ReceiptWithBloom<&Self> {
76 ReceiptWithBloom { logs_bloom: self.bloom(), receipt: self }
77 }
78
79 #[auto_impl(keep_default_for(&, Arc))]
82 fn into_with_bloom(self) -> ReceiptWithBloom<Self> {
83 ReceiptWithBloom { logs_bloom: self.bloom(), receipt: self }
84 }
85
86 #[auto_impl(keep_default_for(&, Arc))]
88 fn into_with_bloom_unchecked(self, logs_bloom: Bloom) -> ReceiptWithBloom<Self> {
89 ReceiptWithBloom { logs_bloom, receipt: self }
90 }
91
92 fn cumulative_gas_used(&self) -> u64;
94
95 fn logs(&self) -> &[Self::Log];
97
98 #[auto_impl(keep_default_for(&, Arc))]
100 fn into_logs(self) -> Vec<Self::Log>
101 where
102 Self::Log: Clone,
103 {
104 self.logs().to_vec()
105 }
106}
107
108#[auto_impl::auto_impl(&)]
110pub trait RlpEncodableReceipt {
111 fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;
113
114 fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
116}
117
118pub trait RlpDecodableReceipt: Sized {
120 fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>>;
122}
123
124#[auto_impl::auto_impl(&)]
130pub trait Eip2718EncodableReceipt: RlpEncodableReceipt + Typed2718 {
131 fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;
133
134 fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
136}
137
138pub trait Eip2718DecodableReceipt: Sized {
143 fn typed_decode_with_bloom(ty: u8, buf: &mut &[u8]) -> Eip2718Result<ReceiptWithBloom<Self>>;
145
146 fn fallback_decode_with_bloom(buf: &mut &[u8]) -> Eip2718Result<ReceiptWithBloom<Self>>;
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use alloy_eips::eip2718::Encodable2718;
154 use alloy_primitives::{address, b256, bytes, hex, Log, LogData};
155 use alloy_rlp::{Decodable, Encodable};
156
157 #[test]
159 fn encode_legacy_receipt() {
160 let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
161
162 let mut data = vec![];
163 let receipt =
164 ReceiptEnvelope::Legacy(ReceiptWithBloom {
165 receipt: Receipt {
166 cumulative_gas_used: 0x1,
167 logs: vec![Log {
168 address: address!("0000000000000000000000000000000000000011"),
169 data: LogData::new_unchecked(
170 vec![
171 b256!("000000000000000000000000000000000000000000000000000000000000dead"),
172 b256!("000000000000000000000000000000000000000000000000000000000000beef"),
173 ],
174 bytes!("0100ff"),
175 ),
176 }],
177 status: false.into(),
178 },
179 logs_bloom: [0; 256].into(),
180 });
181
182 receipt.network_encode(&mut data);
183
184 assert_eq!(receipt.length(), expected.len());
186 assert_eq!(data, expected);
187 }
188
189 #[test]
191 fn decode_legacy_receipt() {
192 let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
193
194 let expected =
196 ReceiptWithBloom {
197 receipt: Receipt {
198 cumulative_gas_used: 0x1,
199 logs: vec![Log {
200 address: address!("0000000000000000000000000000000000000011"),
201 data: LogData::new_unchecked(
202 vec![
203 b256!("000000000000000000000000000000000000000000000000000000000000dead"),
204 b256!("000000000000000000000000000000000000000000000000000000000000beef"),
205 ],
206 bytes!("0100ff"),
207 ),
208 }],
209 status: false.into(),
210 },
211 logs_bloom: [0; 256].into(),
212 };
213
214 let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
215 assert_eq!(receipt, expected);
216 }
217
218 #[test]
219 fn gigantic_receipt() {
220 let receipt = Receipt {
221 cumulative_gas_used: 16747627,
222 status: true.into(),
223 logs: vec![
224 Log {
225 address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
226 data: LogData::new_unchecked(
227 vec![b256!(
228 "c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9"
229 )],
230 vec![1; 0xffffff].into(),
231 ),
232 },
233 Log {
234 address: address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"),
235 data: LogData::new_unchecked(
236 vec![b256!(
237 "8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2"
238 )],
239 vec![1; 0xffffff].into(),
240 ),
241 },
242 ],
243 }
244 .with_bloom();
245
246 let len = receipt.length();
247 let mut data = Vec::with_capacity(receipt.length());
248
249 receipt.encode(&mut data);
250 assert_eq!(data.len(), len);
251 let decoded = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
252
253 assert_eq!(decoded, receipt);
256 }
257
258 #[test]
259 fn can_encode_by_reference() {
260 let receipt: Receipt =
261 Receipt { cumulative_gas_used: 16747627, status: true.into(), logs: vec![] };
262
263 let encoded_ref = alloy_rlp::encode(&ReceiptWithBloom {
264 receipt: &receipt,
265 logs_bloom: receipt.bloom_slow(),
266 });
267 let encoded =
268 alloy_rlp::encode(&ReceiptWithBloom { logs_bloom: receipt.bloom_slow(), receipt });
269
270 assert_eq!(encoded, encoded_ref);
271 }
272}