reth_ethereum_primitives/
receipt.rs1use alloc::vec::Vec;
2use alloy_consensus::{
3 Eip2718EncodableReceipt, Eip658Value, ReceiptWithBloom, RlpDecodableReceipt,
4 RlpEncodableReceipt, TxReceipt, TxType, Typed2718,
5};
6use alloy_primitives::{Bloom, Log};
7use alloy_rlp::{BufMut, Decodable, Encodable, Header};
8use reth_primitives_traits::InMemorySize;
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15#[cfg_attr(feature = "reth-codec", derive(reth_codecs::CompactZstd))]
16#[cfg_attr(feature = "reth-codec", reth_codecs::add_arbitrary_tests)]
17#[cfg_attr(feature = "reth-codec", reth_zstd(
18 compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR,
19 decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR
20))]
21pub struct Receipt {
22 #[serde(with = "tx_type_serde")]
24 pub tx_type: TxType,
25 pub success: bool,
29 pub cumulative_gas_used: u64,
31 pub logs: Vec<Log>,
33}
34
35impl Receipt {
36 pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize {
38 self.success.length() +
39 self.cumulative_gas_used.length() +
40 bloom.length() +
41 self.logs.length()
42 }
43
44 pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) {
46 self.success.encode(out);
47 self.cumulative_gas_used.encode(out);
48 bloom.encode(out);
49 self.logs.encode(out);
50 }
51
52 pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header {
54 Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) }
55 }
56
57 pub fn rlp_decode_inner(
60 buf: &mut &[u8],
61 tx_type: TxType,
62 ) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
63 let header = Header::decode(buf)?;
64 if !header.list {
65 return Err(alloy_rlp::Error::UnexpectedString);
66 }
67
68 let remaining = buf.len();
69
70 let success = Decodable::decode(buf)?;
71 let cumulative_gas_used = Decodable::decode(buf)?;
72 let logs_bloom = Decodable::decode(buf)?;
73 let logs = Decodable::decode(buf)?;
74
75 if buf.len() + header.payload_length != remaining {
76 return Err(alloy_rlp::Error::UnexpectedLength);
77 }
78
79 Ok(ReceiptWithBloom {
80 receipt: Self { cumulative_gas_used, tx_type, success, logs },
81 logs_bloom,
82 })
83 }
84}
85
86impl Eip2718EncodableReceipt for Receipt {
87 fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
88 !self.tx_type.is_legacy() as usize + self.rlp_header_inner(bloom).length_with_payload()
89 }
90
91 fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
92 if !self.tx_type.is_legacy() {
93 out.put_u8(self.tx_type as u8);
94 }
95 self.rlp_header_inner(bloom).encode(out);
96 self.rlp_encode_fields(bloom, out);
97 }
98}
99
100impl RlpEncodableReceipt for Receipt {
101 fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
102 let mut len = self.eip2718_encoded_length_with_bloom(bloom);
103 if !self.tx_type.is_legacy() {
104 len += Header {
105 list: false,
106 payload_length: self.eip2718_encoded_length_with_bloom(bloom),
107 }
108 .length();
109 }
110
111 len
112 }
113
114 fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
115 if !self.tx_type.is_legacy() {
116 Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) }
117 .encode(out);
118 }
119 self.eip2718_encode_with_bloom(bloom, out);
120 }
121}
122
123impl RlpDecodableReceipt for Receipt {
124 fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
125 let header_buf = &mut &**buf;
126 let header = Header::decode(header_buf)?;
127
128 if header.list {
130 return Self::rlp_decode_inner(buf, TxType::Legacy)
131 }
132
133 *buf = *header_buf;
135
136 let remaining = buf.len();
137 let tx_type = TxType::decode(buf)?;
138 let this = Self::rlp_decode_inner(buf, tx_type)?;
139
140 if buf.len() + header.payload_length != remaining {
141 return Err(alloy_rlp::Error::UnexpectedLength);
142 }
143
144 Ok(this)
145 }
146}
147
148impl TxReceipt for Receipt {
149 type Log = Log;
150
151 fn status_or_post_state(&self) -> Eip658Value {
152 self.success.into()
153 }
154
155 fn status(&self) -> bool {
156 self.success
157 }
158
159 fn bloom(&self) -> Bloom {
160 alloy_primitives::logs_bloom(self.logs())
161 }
162
163 fn cumulative_gas_used(&self) -> u128 {
164 self.cumulative_gas_used as u128
165 }
166
167 fn logs(&self) -> &[Log] {
168 &self.logs
169 }
170}
171
172impl Typed2718 for Receipt {
173 fn ty(&self) -> u8 {
174 self.tx_type as u8
175 }
176}
177
178impl InMemorySize for Receipt {
179 fn size(&self) -> usize {
180 self.tx_type.size() +
181 core::mem::size_of::<bool>() +
182 core::mem::size_of::<u64>() +
183 self.logs.capacity() * core::mem::size_of::<Log>()
184 }
185}
186
187impl reth_primitives_traits::Receipt for Receipt {}
188
189mod tx_type_serde {
191 use alloy_primitives::{U64, U8};
192
193 use super::*;
194
195 pub(crate) fn serialize<S>(tx_type: &TxType, serializer: S) -> Result<S::Ok, S::Error>
196 where
197 S: serde::Serializer,
198 {
199 let value: U8 = (*tx_type).into();
200 value.serialize(serializer)
201 }
202
203 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<TxType, D::Error>
204 where
205 D: serde::Deserializer<'de>,
206 {
207 U64::deserialize(deserializer)?.try_into().map_err(serde::de::Error::custom)
208 }
209}