Skip to main content

reth_codecs/alloy/
receipt.rs

1//! Compact implementation for [`AlloyEthereumReceipt`]
2
3use 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    /// Bitflag fieldset for receipt compact encoding.
15    ///
16    /// Used bytes: 1 | Unused bits: 0
17    #[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        /// Deserializes this fieldset and returns it, alongside the original slice in an advanced
28        /// position.
29        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}