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::{eip2718::Eip2718Result, 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 #[auto_impl(keep_default_for(&, Arc))]
82 fn into_with_bloom_unchecked(self, logs_bloom: Bloom) -> ReceiptWithBloom<Self> {
83 ReceiptWithBloom { logs_bloom, receipt: self }
84 }
85
86 fn cumulative_gas_used(&self) -> u64;
88
89 fn logs(&self) -> &[Self::Log];
91
92 #[auto_impl(keep_default_for(&, Arc))]
94 fn into_logs(self) -> Vec<Self::Log>
95 where
96 Self::Log: Clone,
97 {
98 self.logs().to_vec()
99 }
100}
101
102#[auto_impl::auto_impl(&)]
104pub trait RlpEncodableReceipt {
105 fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;
107
108 fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
110}
111
112pub trait RlpDecodableReceipt: Sized {
114 fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>>;
116}
117
118#[auto_impl::auto_impl(&)]
124pub trait Eip2718EncodableReceipt: RlpEncodableReceipt + Typed2718 {
125 fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;
127
128 fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
130}
131
132pub trait Eip2718DecodableReceipt: Sized {
137 fn typed_decode_with_bloom(ty: u8, buf: &mut &[u8]) -> Eip2718Result<ReceiptWithBloom<Self>>;
139
140 fn fallback_decode_with_bloom(buf: &mut &[u8]) -> Eip2718Result<ReceiptWithBloom<Self>>;
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use alloy_eips::eip2718::Encodable2718;
148 use alloy_primitives::{address, b256, bytes, hex, Log, LogData};
149 use alloy_rlp::{Decodable, Encodable};
150
151 #[test]
153 fn encode_legacy_receipt() {
154 let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
155
156 let mut data = vec![];
157 let receipt =
158 ReceiptEnvelope::Legacy(ReceiptWithBloom {
159 receipt: Receipt {
160 cumulative_gas_used: 0x1,
161 logs: vec![Log {
162 address: address!("0000000000000000000000000000000000000011"),
163 data: LogData::new_unchecked(
164 vec![
165 b256!("000000000000000000000000000000000000000000000000000000000000dead"),
166 b256!("000000000000000000000000000000000000000000000000000000000000beef"),
167 ],
168 bytes!("0100ff"),
169 ),
170 }],
171 status: false.into(),
172 },
173 logs_bloom: [0; 256].into(),
174 });
175
176 receipt.network_encode(&mut data);
177
178 assert_eq!(receipt.length(), expected.len());
180 assert_eq!(data, expected);
181 }
182
183 #[test]
185 fn decode_legacy_receipt() {
186 let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
187
188 let expected =
190 ReceiptWithBloom {
191 receipt: Receipt {
192 cumulative_gas_used: 0x1,
193 logs: vec![Log {
194 address: address!("0000000000000000000000000000000000000011"),
195 data: LogData::new_unchecked(
196 vec![
197 b256!("000000000000000000000000000000000000000000000000000000000000dead"),
198 b256!("000000000000000000000000000000000000000000000000000000000000beef"),
199 ],
200 bytes!("0100ff"),
201 ),
202 }],
203 status: false.into(),
204 },
205 logs_bloom: [0; 256].into(),
206 };
207
208 let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
209 assert_eq!(receipt, expected);
210 }
211
212 #[test]
213 fn gigantic_receipt() {
214 let receipt = Receipt {
215 cumulative_gas_used: 16747627,
216 status: true.into(),
217 logs: vec![
218 Log {
219 address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
220 data: LogData::new_unchecked(
221 vec![b256!(
222 "c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9"
223 )],
224 vec![1; 0xffffff].into(),
225 ),
226 },
227 Log {
228 address: address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"),
229 data: LogData::new_unchecked(
230 vec![b256!(
231 "8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2"
232 )],
233 vec![1; 0xffffff].into(),
234 ),
235 },
236 ],
237 }
238 .with_bloom();
239
240 let len = receipt.length();
241 let mut data = Vec::with_capacity(receipt.length());
242
243 receipt.encode(&mut data);
244 assert_eq!(data.len(), len);
245 let decoded = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
246
247 assert_eq!(decoded, receipt);
250 }
251
252 #[test]
253 fn can_encode_by_reference() {
254 let receipt: Receipt =
255 Receipt { cumulative_gas_used: 16747627, status: true.into(), logs: vec![] };
256
257 let encoded_ref = alloy_rlp::encode(&ReceiptWithBloom {
258 receipt: &receipt,
259 logs_bloom: receipt.bloom_slow(),
260 });
261 let encoded =
262 alloy_rlp::encode(&ReceiptWithBloom { logs_bloom: receipt.bloom_slow(), receipt });
263
264 assert_eq!(encoded, encoded_ref);
265 }
266}