1use 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#[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 pub Vec<B256>,
18);
19
20#[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 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#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)]
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: TxReceipt> Receipts69<T> {
59 pub fn into_with_bloom(self) -> Receipts<T> {
63 Receipts(
64 self.0
65 .into_iter()
66 .map(|receipts| receipts.into_iter().map(|r| r.into_with_bloom()).collect())
67 .collect(),
68 )
69 }
70}
71
72impl<T: TxReceipt> From<Receipts69<T>> for Receipts<T> {
73 fn from(receipts: Receipts69<T>) -> Self {
74 receipts.into_with_bloom()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::{message::RequestPair, GetReceipts, Receipts};
82 use alloy_consensus::TxType;
83 use alloy_primitives::{hex, Log};
84 use alloy_rlp::{Decodable, Encodable};
85
86 #[test]
87 fn roundtrip_eip1559() {
88 let receipts = Receipts(vec![vec![ReceiptWithBloom {
89 receipt: Receipt { tx_type: TxType::Eip1559, ..Default::default() },
90 logs_bloom: Default::default(),
91 }]]);
92
93 let mut out = vec![];
94 receipts.encode(&mut out);
95
96 let mut out = out.as_slice();
97 let decoded = Receipts::decode(&mut out).unwrap();
98
99 assert_eq!(receipts, decoded);
100 }
101
102 #[test]
103 fn encode_get_receipts() {
105 let expected = hex!(
106 "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
107 );
108 let mut data = vec![];
109 let request = RequestPair {
110 request_id: 1111,
111 message: GetReceipts(vec![
112 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
113 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
114 ]),
115 };
116 request.encode(&mut data);
117 assert_eq!(data, expected);
118 }
119
120 #[test]
121 fn decode_get_receipts() {
123 let data = hex!(
124 "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
125 );
126 let request = RequestPair::<GetReceipts>::decode(&mut &data[..]).unwrap();
127 assert_eq!(
128 request,
129 RequestPair {
130 request_id: 1111,
131 message: GetReceipts(vec![
132 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
133 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
134 ]),
135 }
136 );
137 }
138
139 #[test]
141 fn encode_receipts() {
142 let expected = hex!(
143 "f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
144 );
145 let mut data = vec![];
146 let request = RequestPair {
147 request_id: 1111,
148 message: Receipts(vec![vec![
149 ReceiptWithBloom {
150 receipt: Receipt {
151 tx_type: TxType::Legacy,
152 cumulative_gas_used: 0x1u64,
153 logs: vec![
154 Log::new_unchecked(
155 hex!("0000000000000000000000000000000000000011").into(),
156 vec![
157 hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
158 hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
159 ],
160 hex!("0100ff")[..].into(),
161 ),
162 ],
163 success: false,
164 },
165 logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
166 },
167 ]]),
168 };
169 request.encode(&mut data);
170 assert_eq!(data, expected);
171 }
172
173 #[test]
175 fn decode_receipts() {
176 let data = hex!(
177 "f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"
178 );
179 let request = RequestPair::<Receipts>::decode(&mut &data[..]).unwrap();
180 assert_eq!(
181 request,
182 RequestPair {
183 request_id: 1111,
184 message: Receipts(vec![
185 vec![
186 ReceiptWithBloom {
187 receipt: Receipt {
188 tx_type: TxType::Legacy,
189 cumulative_gas_used: 0x1u64,
190 logs: vec![
191 Log::new_unchecked(
192 hex!("0000000000000000000000000000000000000011").into(),
193 vec![
194 hex!("000000000000000000000000000000000000000000000000000000000000dead").into(),
195 hex!("000000000000000000000000000000000000000000000000000000000000beef").into(),
196 ],
197 hex!("0100ff")[..].into(),
198 ),
199 ],
200 success: false,
201 },
202 logs_bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
203 },
204 ],
205 ]),
206 }
207 );
208 }
209
210 #[test]
211 fn decode_receipts_69() {
212 let data = hex!("0xf9026605f90262f9025fc60201826590c0c7800183013cd9c0c702018301a2a5c0c7010183027a36c0c702018302e03ec0c7010183034646c0c702018303ac30c0c78001830483b8c0c702018304e9a2c0c780018305c17fc0c7020183062769c0c7800183068d71c0c702018306f35bc0c702018307cb77c0c701018308a382c0c7020183097ab6c0c78080830b0156c0c70101830b6740c0c70201830bcd48c0c70101830c32f6c0c70101830c98e0c0c70201830cfecac0c70201830d64b4c0c70280830dca9ec0c70101830e30a6c0c70201830f080dc0c70201830f6e15c0c78080830fd41dc0c702018310abbac0c701018310fdc2c0c7020183116370c0c780018311c95ac0c7010183122f44c0c701808312952ec0c7020183136c7dc0c70201831443c0c0c702018314a9c8c0c7020183150f94c0c7018083169634c0c7020183176d68c0c702808317d370c0c70201831838c4c0c701808319bf64c0c70201831a256cc0c78080831bac0cc0c70201831c11d8c0c70201831c77c2c0c78080831cdd34c0c70201831db57bc0c70101831e8d07c0c70101831ef2d3c0c70201831fcb37c0c70180832030e5c0c70201832096cfc0c701018320fcb9c0c70201832162c1c0c702018321c8abc0c7020183229ffac0c70201832305c6c0c7028083236bcec0c702808323d1d6c0c702018324a91cc0c7020183250f06c0c70201832574d2c0c7020183264c15c0c70201832723b6c0c70201832789a0c0c702018327ef8ac0c7020183285574c0c702018328bb40c0c702018329212ac0c7028083298714c0c70201832a5e4ec0c70201832ac438c0c70201832b9b72c0c70201832c017ac0");
213
214 let request = RequestPair::<Receipts69>::decode(&mut &data[..]).unwrap();
215 assert_eq!(
216 request.message.0[0][0],
217 Receipt {
218 tx_type: TxType::Eip1559,
219 success: true,
220 cumulative_gas_used: 26000,
221 logs: vec![],
222 }
223 );
224
225 let encoded = alloy_rlp::encode(&request);
226 assert_eq!(encoded, data);
227 }
228}