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 #[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
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use alloy_eips::eip2718::Encodable2718;
136 use alloy_primitives::{address, b256, bytes, hex, Log, LogData};
137 use alloy_rlp::{Decodable, Encodable};
138
139 #[test]
141 fn encode_legacy_receipt() {
142 let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
143
144 let mut data = vec![];
145 let receipt =
146 ReceiptEnvelope::Legacy(ReceiptWithBloom {
147 receipt: Receipt {
148 cumulative_gas_used: 0x1,
149 logs: vec![Log {
150 address: address!("0000000000000000000000000000000000000011"),
151 data: LogData::new_unchecked(
152 vec![
153 b256!("000000000000000000000000000000000000000000000000000000000000dead"),
154 b256!("000000000000000000000000000000000000000000000000000000000000beef"),
155 ],
156 bytes!("0100ff"),
157 ),
158 }],
159 status: false.into(),
160 },
161 logs_bloom: [0; 256].into(),
162 });
163
164 receipt.network_encode(&mut data);
165
166 assert_eq!(receipt.length(), expected.len());
168 assert_eq!(data, expected);
169 }
170
171 #[test]
173 fn decode_legacy_receipt() {
174 let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
175
176 let expected =
178 ReceiptWithBloom {
179 receipt: Receipt {
180 cumulative_gas_used: 0x1,
181 logs: vec![Log {
182 address: address!("0000000000000000000000000000000000000011"),
183 data: LogData::new_unchecked(
184 vec![
185 b256!("000000000000000000000000000000000000000000000000000000000000dead"),
186 b256!("000000000000000000000000000000000000000000000000000000000000beef"),
187 ],
188 bytes!("0100ff"),
189 ),
190 }],
191 status: false.into(),
192 },
193 logs_bloom: [0; 256].into(),
194 };
195
196 let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
197 assert_eq!(receipt, expected);
198 }
199
200 #[test]
201 fn gigantic_receipt() {
202 let receipt = Receipt {
203 cumulative_gas_used: 16747627,
204 status: true.into(),
205 logs: vec![
206 Log {
207 address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
208 data: LogData::new_unchecked(
209 vec![b256!(
210 "c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9"
211 )],
212 vec![1; 0xffffff].into(),
213 ),
214 },
215 Log {
216 address: address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"),
217 data: LogData::new_unchecked(
218 vec![b256!(
219 "8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2"
220 )],
221 vec![1; 0xffffff].into(),
222 ),
223 },
224 ],
225 }
226 .with_bloom();
227
228 let len = receipt.length();
229 let mut data = Vec::with_capacity(receipt.length());
230
231 receipt.encode(&mut data);
232 assert_eq!(data.len(), len);
233 let decoded = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
234
235 assert_eq!(decoded, receipt);
238 }
239
240 #[test]
241 fn can_encode_by_reference() {
242 let receipt: Receipt =
243 Receipt { cumulative_gas_used: 16747627, status: true.into(), logs: vec![] };
244
245 let encoded_ref = alloy_rlp::encode(&ReceiptWithBloom {
246 receipt: &receipt,
247 logs_bloom: receipt.bloom_slow(),
248 });
249 let encoded =
250 alloy_rlp::encode(&ReceiptWithBloom { logs_bloom: receipt.bloom_slow(), receipt });
251
252 assert_eq!(encoded, encoded_ref);
253 }
254}