1use alloc::vec::Vec;
4use alloy_consensus::{ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt};
5use alloy_primitives::B256;
6use alloy_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
7use derive_more::{Deref, IntoIterator};
8use reth_codecs_derive::add_arbitrary_tests;
9use reth_ethereum_primitives::Receipt;
10
11#[derive(
13 Clone,
14 Debug,
15 PartialEq,
16 Eq,
17 RlpEncodableWrapper,
18 RlpDecodableWrapper,
19 Default,
20 Deref,
21 IntoIterator,
22)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
25#[add_arbitrary_tests(rlp)]
26pub struct GetReceipts(
27 pub Vec<B256>,
29);
30
31#[derive(Clone, Debug, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
41pub struct GetReceipts70 {
42 pub first_block_receipt_index: u64,
44 pub block_hashes: Vec<B256>,
46}
47
48impl alloy_rlp::Encodable for GetReceipts70 {
49 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
50 self.first_block_receipt_index.encode(out);
51 self.block_hashes.encode(out);
52 }
53
54 fn length(&self) -> usize {
55 self.first_block_receipt_index.length() + self.block_hashes.length()
56 }
57}
58
59impl alloy_rlp::Decodable for GetReceipts70 {
60 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
61 let first_block_receipt_index = u64::decode(buf)?;
62 let block_hashes = Vec::<B256>::decode(buf)?;
63 Ok(Self { first_block_receipt_index, block_hashes })
64 }
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Default, Deref, IntoIterator)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
72#[add_arbitrary_tests(rlp)]
73pub struct Receipts<T = Receipt>(
74 pub Vec<Vec<ReceiptWithBloom<T>>>,
76);
77
78impl<T: RlpEncodableReceipt> alloy_rlp::Encodable for Receipts<T> {
79 #[inline]
80 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
81 self.0.encode(out)
82 }
83 #[inline]
84 fn length(&self) -> usize {
85 self.0.length()
86 }
87}
88
89impl<T: RlpDecodableReceipt> alloy_rlp::Decodable for Receipts<T> {
90 #[inline]
91 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
92 alloy_rlp::Decodable::decode(buf).map(Self)
93 }
94}
95
96#[derive(
100 Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Deref, IntoIterator,
101)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
104#[add_arbitrary_tests(rlp)]
105pub struct Receipts69<T = Receipt>(pub Vec<Vec<T>>);
106
107impl<T: TxReceipt> Receipts69<T> {
108 pub fn into_with_bloom(self) -> Receipts<T> {
118 Receipts(
119 self.into_iter()
120 .map(|receipts| receipts.into_iter().map(|r| r.into_with_bloom()).collect())
121 .collect(),
122 )
123 }
124}
125
126impl<T: TxReceipt> From<Receipts69<T>> for Receipts<T> {
127 fn from(receipts: Receipts69<T>) -> Self {
128 receipts.into_with_bloom()
129 }
130}
131
132#[derive(Clone, Debug, PartialEq, Eq)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
139pub struct Receipts70<T = Receipt> {
140 pub last_block_incomplete: bool,
142 pub receipts: Vec<Vec<T>>,
144}
145
146impl<T> alloy_rlp::Encodable for Receipts70<T>
147where
148 T: alloy_rlp::Encodable,
149{
150 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
151 self.last_block_incomplete.encode(out);
152 self.receipts.encode(out);
153 }
154
155 fn length(&self) -> usize {
156 self.last_block_incomplete.length() + self.receipts.length()
157 }
158}
159
160impl<T> alloy_rlp::Decodable for Receipts70<T>
161where
162 T: alloy_rlp::Decodable,
163{
164 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
165 let last_block_incomplete = bool::decode(buf)?;
166 let receipts = Vec::<Vec<T>>::decode(buf)?;
167 Ok(Self { last_block_incomplete, receipts })
168 }
169}
170
171impl<T: TxReceipt> Receipts70<T> {
172 pub fn into_with_bloom(self) -> Receipts<T> {
182 Receipts69(self.receipts).into_with_bloom()
185 }
186}
187
188impl<T: TxReceipt> From<Receipts70<T>> for Receipts<T> {
189 fn from(receipts: Receipts70<T>) -> Self {
190 receipts.into_with_bloom()
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::{message::RequestPair, GetReceipts, Receipts};
198 use alloy_consensus::TxType;
199 use alloy_primitives::{hex, Log};
200 use alloy_rlp::{Decodable, Encodable};
201
202 #[test]
203 fn roundtrip_eip1559() {
204 let receipts = Receipts(vec![vec![ReceiptWithBloom {
205 receipt: Receipt { tx_type: TxType::Eip1559, ..Default::default() },
206 logs_bloom: Default::default(),
207 }]]);
208
209 let mut out = vec![];
210 receipts.encode(&mut out);
211
212 let mut out = out.as_slice();
213 let decoded = Receipts::decode(&mut out).unwrap();
214
215 assert_eq!(receipts, decoded);
216 }
217
218 #[test]
219 fn encode_get_receipts() {
221 let expected = hex!(
222 "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
223 );
224 let mut data = vec![];
225 let request = RequestPair {
226 request_id: 1111,
227 message: GetReceipts(vec![
228 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
229 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
230 ]),
231 };
232 request.encode(&mut data);
233 assert_eq!(data, expected);
234 }
235
236 #[test]
237 fn decode_get_receipts() {
239 let data = hex!(
240 "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
241 );
242 let request = RequestPair::<GetReceipts>::decode(&mut &data[..]).unwrap();
243 assert_eq!(
244 request,
245 RequestPair {
246 request_id: 1111,
247 message: GetReceipts(vec![
248 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
249 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
250 ]),
251 }
252 );
253 }
254
255 #[test]
257 fn encode_receipts() {
258 let expected = hex!(
259 "f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
260 );
261 let mut data = vec![];
262 let request = RequestPair {
263 request_id: 1111,
264 message: Receipts(vec![vec![
265 ReceiptWithBloom {
266 receipt: Receipt {
267 tx_type: TxType::Legacy,
268 cumulative_gas_used: 0x1u64,
269 logs: vec![
270 Log::new_unchecked(
271 hex!("0000000000000000000000000000000000000011").into(),
272 vec![
273 hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
274 hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
275 ],
276 hex!("0100ff")[..].into(),
277 ),
278 ],
279 success: false,
280 },
281 logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
282 },
283 ]]),
284 };
285 request.encode(&mut data);
286 assert_eq!(data, expected);
287 }
288
289 #[test]
291 fn decode_receipts() {
292 let data = hex!(
293 "f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
294 );
295 let request = RequestPair::<Receipts>::decode(&mut &data[..]).unwrap();
296 assert_eq!(
297 request,
298 RequestPair {
299 request_id: 1111,
300 message: Receipts(vec![
301 vec![
302 ReceiptWithBloom {
303 receipt: Receipt {
304 tx_type: TxType::Legacy,
305 cumulative_gas_used: 0x1u64,
306 logs: vec![
307 Log::new_unchecked(
308 hex!("0000000000000000000000000000000000000011").into(),
309 vec![
310 hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
311 hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
312 ],
313 hex!("0100ff")[..].into(),
314 ),
315 ],
316 success: false,
317 },
318 logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
319 },
320 ],
321 ]),
322 }
323 );
324 }
325
326 #[test]
327 fn decode_receipts_69() {
328 let data = hex!("0xf9026605f90262f9025fc60201826590c0c7800183013cd9c0c702018301a2a5c0c7010183027a36c0c702018302e03ec0c7010183034646c0c702018303ac30c0c78001830483b8c0c702018304e9a2c0c780018305c17fc0c7020183062769c0c7800183068d71c0c702018306f35bc0c702018307cb77c0c701018308a382c0c7020183097ab6c0c78080830b0156c0c70101830b6740c0c70201830bcd48c0c70101830c32f6c0c70101830c98e0c0c70201830cfecac0c70201830d64b4c0c70280830dca9ec0c70101830e30a6c0c70201830f080dc0c70201830f6e15c0c78080830fd41dc0c702018310abbac0c701018310fdc2c0c7020183116370c0c780018311c95ac0c7010183122f44c0c701808312952ec0c7020183136c7dc0c70201831443c0c0c702018314a9c8c0c7020183150f94c0c7018083169634c0c7020183176d68c0c702808317d370c0c70201831838c4c0c701808319bf64c0c70201831a256cc0c78080831bac0cc0c70201831c11d8c0c70201831c77c2c0c78080831cdd34c0c70201831db57bc0c70101831e8d07c0c70101831ef2d3c0c70201831fcb37c0c70180832030e5c0c70201832096cfc0c701018320fcb9c0c70201832162c1c0c702018321c8abc0c7020183229ffac0c70201832305c6c0c7028083236bcec0c702808323d1d6c0c702018324a91cc0c7020183250f06c0c70201832574d2c0c7020183264c15c0c70201832723b6c0c70201832789a0c0c702018327ef8ac0c7020183285574c0c702018328bb40c0c702018329212ac0c7028083298714c0c70201832a5e4ec0c70201832ac438c0c70201832b9b72c0c70201832c017ac0");
329
330 let request = RequestPair::<Receipts69>::decode(&mut &data[..]).unwrap();
331 assert_eq!(
332 request.message.0[0][0],
333 Receipt {
334 tx_type: TxType::Eip1559,
335 success: true,
336 cumulative_gas_used: 26000,
337 logs: vec![],
338 }
339 );
340
341 let encoded = alloy_rlp::encode(&request);
342 assert_eq!(encoded, data);
343 }
344
345 #[test]
346 fn encode_get_receipts70_inline_shape() {
347 let req = RequestPair {
348 request_id: 1111,
349 message: GetReceipts70 {
350 first_block_receipt_index: 0,
351 block_hashes: vec![
352 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
353 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
354 ],
355 },
356 };
357
358 let mut out = vec![];
359 req.encode(&mut out);
360
361 let mut buf = out.as_slice();
362 let header = alloy_rlp::Header::decode(&mut buf).unwrap();
363 let payload_start = buf.len();
364 let request_id = u64::decode(&mut buf).unwrap();
365 let first_block_receipt_index = u64::decode(&mut buf).unwrap();
366 let block_hashes = Vec::<B256>::decode(&mut buf).unwrap();
367
368 assert!(buf.is_empty(), "buffer not fully consumed");
369 assert_eq!(request_id, 1111);
370 assert_eq!(first_block_receipt_index, 0);
371 assert_eq!(block_hashes.len(), 2);
372 assert_eq!(payload_start - buf.len(), header.payload_length);
374
375 let mut buf = out.as_slice();
376 let decoded = RequestPair::<GetReceipts70>::decode(&mut buf).unwrap();
377 assert!(buf.is_empty(), "buffer not fully consumed on decode");
378 assert_eq!(decoded, req);
379 }
380
381 #[test]
382 fn encode_receipts70_inline_shape() {
383 let payload: Receipts70<Receipt> =
384 Receipts70 { last_block_incomplete: true, receipts: vec![vec![Receipt::default()]] };
385
386 let resp = RequestPair { request_id: 7, message: payload };
387
388 let mut out = vec![];
389 resp.encode(&mut out);
390
391 let mut buf = out.as_slice();
392 let header = alloy_rlp::Header::decode(&mut buf).unwrap();
393 let payload_start = buf.len();
394 let request_id = u64::decode(&mut buf).unwrap();
395 let last_block_incomplete = bool::decode(&mut buf).unwrap();
396 let receipts = Vec::<Vec<Receipt>>::decode(&mut buf).unwrap();
397
398 assert!(buf.is_empty(), "buffer not fully consumed");
399 assert_eq!(payload_start - buf.len(), header.payload_length);
400 assert_eq!(request_id, 7);
401 assert!(last_block_incomplete);
402 assert_eq!(receipts.len(), 1);
403 assert_eq!(receipts[0].len(), 1);
404
405 let mut buf = out.as_slice();
406 let decoded = RequestPair::<Receipts70>::decode(&mut buf).unwrap();
407 assert!(buf.is_empty(), "buffer not fully consumed on decode");
408 assert_eq!(decoded, resp);
409 }
410}