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}