reth_eth_wire_types/
receipts.rs

1//! Implements the `GetReceipts` and `Receipts` message types.
2
3use alloc::vec::Vec;
4use alloy_consensus::{ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt};
5use alloy_primitives::B256;
6use alloy_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
7use reth_codecs_derive::add_arbitrary_tests;
8use reth_ethereum_primitives::Receipt;
9
10/// A request for transaction receipts from the given block hashes.
11#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
14#[add_arbitrary_tests(rlp)]
15pub struct GetReceipts(
16    /// The block hashes to request receipts for.
17    pub Vec<B256>,
18);
19
20/// The response to [`GetReceipts`], containing receipt lists that correspond to each block
21/// requested.
22#[derive(Clone, Debug, PartialEq, Eq, Default)]
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 Receipts<T = Receipt>(
27    /// Each receipt hash should correspond to a block hash in the request.
28    pub Vec<Vec<ReceiptWithBloom<T>>>,
29);
30
31impl<T: RlpEncodableReceipt> alloy_rlp::Encodable for Receipts<T> {
32    #[inline]
33    fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
34        self.0.encode(out)
35    }
36    #[inline]
37    fn length(&self) -> usize {
38        self.0.length()
39    }
40}
41
42impl<T: RlpDecodableReceipt> alloy_rlp::Decodable for Receipts<T> {
43    #[inline]
44    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
45        alloy_rlp::Decodable::decode(buf).map(Self)
46    }
47}
48
49/// Eth/69 receipt response type that removes bloom filters from the protocol.
50///
51/// This is effectively a subset of [`Receipts`].
52#[derive(Clone, Debug, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
55#[add_arbitrary_tests(rlp)]
56pub struct Receipts69<T = Receipt>(pub Vec<Vec<T>>);
57
58impl<T: RlpEncodableReceipt + alloy_rlp::Encodable> alloy_rlp::Encodable for Receipts69<T> {
59    #[inline]
60    fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
61        self.0.encode(out)
62    }
63    #[inline]
64    fn length(&self) -> usize {
65        self.0.length()
66    }
67}
68
69impl<T: RlpDecodableReceipt + alloy_rlp::Decodable> alloy_rlp::Decodable for Receipts69<T> {
70    #[inline]
71    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
72        alloy_rlp::Decodable::decode(buf).map(Self)
73    }
74}
75
76impl<T: TxReceipt> Receipts69<T> {
77    /// Encodes all receipts with the bloom filter.
78    ///
79    /// Note: This is an expensive operation that recalculates the bloom for each receipt.
80    pub fn into_with_bloom(self) -> Receipts<T> {
81        Receipts(
82            self.0
83                .into_iter()
84                .map(|receipts| receipts.into_iter().map(|r| r.into_with_bloom()).collect())
85                .collect(),
86        )
87    }
88}
89
90impl<T: TxReceipt> From<Receipts69<T>> for Receipts<T> {
91    fn from(receipts: Receipts69<T>) -> Self {
92        receipts.into_with_bloom()
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use crate::{message::RequestPair, GetReceipts, Receipts};
100    use alloy_consensus::TxType;
101    use alloy_primitives::{hex, Log};
102    use alloy_rlp::{Decodable, Encodable};
103
104    #[test]
105    fn roundtrip_eip1559() {
106        let receipts = Receipts(vec![vec![ReceiptWithBloom {
107            receipt: Receipt { tx_type: TxType::Eip1559, ..Default::default() },
108            logs_bloom: Default::default(),
109        }]]);
110
111        let mut out = vec![];
112        receipts.encode(&mut out);
113
114        let mut out = out.as_slice();
115        let decoded = Receipts::decode(&mut out).unwrap();
116
117        assert_eq!(receipts, decoded);
118    }
119
120    #[test]
121    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
122    fn encode_get_receipts() {
123        let expected = hex!(
124            "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
125        );
126        let mut data = vec![];
127        let request = RequestPair {
128            request_id: 1111,
129            message: GetReceipts(vec![
130                hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
131                hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
132            ]),
133        };
134        request.encode(&mut data);
135        assert_eq!(data, expected);
136    }
137
138    #[test]
139    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
140    fn decode_get_receipts() {
141        let data = hex!(
142            "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
143        );
144        let request = RequestPair::<GetReceipts>::decode(&mut &data[..]).unwrap();
145        assert_eq!(
146            request,
147            RequestPair {
148                request_id: 1111,
149                message: GetReceipts(vec![
150                    hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
151                    hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
152                ]),
153            }
154        );
155    }
156
157    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
158    #[test]
159    fn encode_receipts() {
160        let expected = hex!(
161            "f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
162        );
163        let mut data = vec![];
164        let request = RequestPair {
165            request_id: 1111,
166            message: Receipts(vec![vec![
167                ReceiptWithBloom {
168                    receipt: Receipt {
169                        tx_type: TxType::Legacy,
170                        cumulative_gas_used: 0x1u64,
171                        logs: vec![
172                            Log::new_unchecked(
173                                hex!("0000000000000000000000000000000000000011").into(),
174                                vec![
175                                    hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
176                                    hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
177                                ],
178                                hex!("0100ff")[..].into(),
179                            ),
180                        ],
181                        success: false,
182                    },
183                    logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
184                },
185            ]]),
186        };
187        request.encode(&mut data);
188        assert_eq!(data, expected);
189    }
190
191    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
192    #[test]
193    fn decode_receipts() {
194        let data = hex!(
195            "f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
196        );
197        let request = RequestPair::<Receipts>::decode(&mut &data[..]).unwrap();
198        assert_eq!(
199            request,
200            RequestPair {
201                request_id: 1111,
202                message: Receipts(vec![
203                    vec![
204                        ReceiptWithBloom {
205                            receipt: Receipt {
206                                tx_type: TxType::Legacy,
207                                cumulative_gas_used: 0x1u64,
208                                logs: vec![
209                                    Log::new_unchecked(
210                                        hex!("0000000000000000000000000000000000000011").into(),
211                                        vec![
212                                            hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
213                                            hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
214                                        ],
215                                        hex!("0100ff")[..].into(),
216                                    ),
217                                ],
218                                success: false,
219                            },
220                            logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
221                        },
222                    ],
223                ]),
224            }
225        );
226    }
227}