reth_codecs/alloy/
receipt.rs1use crate::Compact;
4use alloc::vec::Vec;
5use alloy_consensus::EthereumReceipt as AlloyEthereumReceipt;
6use alloy_primitives::Log;
7use bytes::Buf;
8use modular_bitfield::prelude::*;
9
10#[allow(non_snake_case)]
11mod flags {
12 use super::*;
13
14 #[bitfield]
18 #[derive(Clone, Copy, Debug, Default)]
19 pub struct ReceiptFlags {
20 pub tx_type_len: B2,
21 pub success_len: B1,
22 pub cumulative_gas_used_len: B4,
23 pub __zstd: B1,
24 }
25
26 impl ReceiptFlags {
27 pub fn from(mut buf: &[u8]) -> (Self, &[u8]) {
30 (Self::from_bytes([buf.get_u8()]), buf)
31 }
32 }
33}
34
35pub(crate) use flags::ReceiptFlags;
36
37impl<T: Compact> Compact for AlloyEthereumReceipt<T> {
38 fn to_compact<B>(&self, buf: &mut B) -> usize
39 where
40 B: bytes::BufMut + AsMut<[u8]>,
41 {
42 let mut flags = ReceiptFlags::default();
43 let mut total_length = 0;
44 let mut buffer = bytes::BytesMut::new();
45
46 let tx_type_len = self.tx_type.to_compact(&mut buffer);
47 flags.set_tx_type_len(tx_type_len as u8);
48 let success_len = self.success.to_compact(&mut buffer);
49 flags.set_success_len(success_len as u8);
50 let cumulative_gas_used_len = self.cumulative_gas_used.to_compact(&mut buffer);
51 flags.set_cumulative_gas_used_len(cumulative_gas_used_len as u8);
52 self.logs.to_compact(&mut buffer);
53
54 let zstd = buffer.len() > 7;
55 if zstd {
56 flags.set___zstd(1);
57 }
58
59 let flags = flags.into_bytes();
60 total_length += flags.len() + buffer.len();
61 buf.put_slice(&flags);
62 if zstd {
63 reth_zstd_compressors::with_receipt_compressor(|compressor| {
64 let compressed = compressor.compress(&buffer).expect("Failed to compress.");
65 buf.put(compressed.as_slice());
66 });
67 } else {
68 buf.put(buffer);
69 }
70 total_length
71 }
72
73 fn from_compact(buf: &[u8], _len: usize) -> (Self, &[u8]) {
74 let (flags, mut buf) = ReceiptFlags::from(buf);
75 if flags.__zstd() != 0 {
76 reth_zstd_compressors::with_receipt_decompressor(|decompressor| {
77 let decompressed = decompressor.decompress(buf);
78 let original_buf = buf;
79 let mut buf: &[u8] = decompressed;
80 let (tx_type, new_buf) = T::from_compact(buf, flags.tx_type_len() as usize);
81 buf = new_buf;
82 let (success, new_buf) = bool::from_compact(buf, flags.success_len() as usize);
83 buf = new_buf;
84 let (cumulative_gas_used, new_buf) =
85 u64::from_compact(buf, flags.cumulative_gas_used_len() as usize);
86 buf = new_buf;
87 let (logs, _) = Vec::<Log>::from_compact(buf, buf.len());
88 (Self { tx_type, success, cumulative_gas_used, logs }, original_buf)
89 })
90 } else {
91 let (tx_type, new_buf) = T::from_compact(buf, flags.tx_type_len() as usize);
92 buf = new_buf;
93 let (success, new_buf) = bool::from_compact(buf, flags.success_len() as usize);
94 buf = new_buf;
95 let (cumulative_gas_used, new_buf) =
96 u64::from_compact(buf, flags.cumulative_gas_used_len() as usize);
97 buf = new_buf;
98 let (logs, new_buf) = Vec::<Log>::from_compact(buf, buf.len());
99 buf = new_buf;
100 let obj = Self { tx_type, success, cumulative_gas_used, logs };
101 (obj, buf)
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use alloy_consensus::TxType;
110 use proptest::proptest;
111 use proptest_arbitrary_interop::arb;
112
113 proptest! {
114 #[test]
115 fn roundtrip_receipt(receipt in arb::<AlloyEthereumReceipt<TxType>>()) {
116 let mut compacted_receipt = Vec::<u8>::new();
117 let len = receipt.to_compact(&mut compacted_receipt);
118 let (decoded, _) = AlloyEthereumReceipt::<TxType>::from_compact(&compacted_receipt, len);
119 assert_eq!(receipt, decoded)
120 }
121 }
122}