reth_codecs/alloy/transaction/
ethereum.rs

1use crate::{Compact, Vec};
2use alloy_consensus::{
3    transaction::RlpEcdsaEncodableTx, EthereumTxEnvelope, Signed, Transaction, TxEip1559,
4    TxEip2930, TxEip7702, TxLegacy, TxType,
5};
6use alloy_primitives::PrimitiveSignature;
7use bytes::{Buf, BufMut};
8
9/// A trait for extracting transaction without type and signature and serializing it using
10/// [`Compact`] encoding.
11///
12/// It is not a responsibility of this trait to encode transaction type and signature. Likely this
13/// will be a part of a serialization scenario with a greater scope where these values are
14/// serialized separately.
15///
16/// See [`ToTxCompact::to_tx_compact`].
17pub(super) trait ToTxCompact {
18    /// Serializes inner transaction using [`Compact`] encoding. Writes the result into `buf`.
19    ///
20    /// The written bytes do not contain signature and transaction type. This information be needs
21    /// to be serialized extra if needed.
22    fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>));
23}
24
25/// A trait for deserializing transaction without type and signature using [`Compact`] encoding.
26///
27/// It is not a responsibility of this trait to extract transaction type and signature, but both
28/// are needed to create the value. While these values can come from anywhere, likely this will be
29/// a part of a deserialization scenario with a greater scope where these values are deserialized
30/// separately.
31///
32/// See [`FromTxCompact::from_tx_compact`].
33pub(super) trait FromTxCompact {
34    type TxType;
35
36    /// Deserializes inner transaction using [`Compact`] encoding. The concrete type is determined
37    /// by `tx_type`. The `signature` is added to create typed and signed transaction.
38    ///
39    /// Returns a tuple of 2 elements. The first element is the deserialized value and the second
40    /// is a byte slice created from `buf` with a starting position advanced by the exact amount
41    /// of bytes consumed for this process.  
42    fn from_tx_compact(
43        buf: &[u8],
44        tx_type: Self::TxType,
45        signature: PrimitiveSignature,
46    ) -> (Self, &[u8])
47    where
48        Self: Sized;
49}
50
51impl<Eip4844: Compact + Transaction> ToTxCompact for EthereumTxEnvelope<Eip4844> {
52    fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>)) {
53        match self {
54            Self::Legacy(tx) => tx.tx().to_compact(buf),
55            Self::Eip2930(tx) => tx.tx().to_compact(buf),
56            Self::Eip1559(tx) => tx.tx().to_compact(buf),
57            Self::Eip4844(tx) => tx.tx().to_compact(buf),
58            Self::Eip7702(tx) => tx.tx().to_compact(buf),
59        };
60    }
61}
62
63impl<Eip4844: Compact + Transaction> FromTxCompact for EthereumTxEnvelope<Eip4844> {
64    type TxType = TxType;
65
66    fn from_tx_compact(
67        buf: &[u8],
68        tx_type: TxType,
69        signature: PrimitiveSignature,
70    ) -> (Self, &[u8]) {
71        match tx_type {
72            TxType::Legacy => {
73                let (tx, buf) = TxLegacy::from_compact(buf, buf.len());
74                let tx = Signed::new_unhashed(tx, signature);
75                (Self::Legacy(tx), buf)
76            }
77            TxType::Eip2930 => {
78                let (tx, buf) = TxEip2930::from_compact(buf, buf.len());
79                let tx = Signed::new_unhashed(tx, signature);
80                (Self::Eip2930(tx), buf)
81            }
82            TxType::Eip1559 => {
83                let (tx, buf) = TxEip1559::from_compact(buf, buf.len());
84                let tx = Signed::new_unhashed(tx, signature);
85                (Self::Eip1559(tx), buf)
86            }
87            TxType::Eip4844 => {
88                let (tx, buf) = Eip4844::from_compact(buf, buf.len());
89                let tx = Signed::new_unhashed(tx, signature);
90                (Self::Eip4844(tx), buf)
91            }
92            TxType::Eip7702 => {
93                let (tx, buf) = TxEip7702::from_compact(buf, buf.len());
94                let tx = Signed::new_unhashed(tx, signature);
95                (Self::Eip7702(tx), buf)
96            }
97        }
98    }
99}
100
101pub(super) trait Envelope: FromTxCompact<TxType: Compact> {
102    fn signature(&self) -> &PrimitiveSignature;
103    fn tx_type(&self) -> Self::TxType;
104}
105
106impl<Eip4844: Compact + Transaction + RlpEcdsaEncodableTx> Envelope
107    for EthereumTxEnvelope<Eip4844>
108{
109    fn signature(&self) -> &PrimitiveSignature {
110        Self::signature(self)
111    }
112
113    fn tx_type(&self) -> Self::TxType {
114        Self::tx_type(self)
115    }
116}
117
118pub(super) trait CompactEnvelope: Sized {
119    /// Takes a buffer which can be written to. *Ideally*, it returns the length written to.
120    fn to_compact<B>(&self, buf: &mut B) -> usize
121    where
122        B: BufMut + AsMut<[u8]>;
123
124    /// Takes a buffer which can be read from. Returns the object and `buf` with its internal cursor
125    /// advanced (eg.`.advance(len)`).
126    ///
127    /// `len` can either be the `buf` remaining length, or the length of the compacted type.
128    ///
129    /// It will panic, if `len` is smaller than `buf.len()`.
130    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]);
131}
132
133impl<T: Envelope + ToTxCompact + Transaction + Send + Sync> CompactEnvelope for T {
134    fn to_compact<B>(&self, buf: &mut B) -> usize
135    where
136        B: BufMut + AsMut<[u8]>,
137    {
138        let start = buf.as_mut().len();
139
140        // Placeholder for bitflags.
141        // The first byte uses 4 bits as flags: IsCompressed[1bit], TxType[2bits], Signature[1bit]
142        buf.put_u8(0);
143
144        let sig_bit = self.signature().to_compact(buf) as u8;
145        let zstd_bit = self.input().len() >= 32;
146        let tx_bits = self.tx_type().to_compact(buf) as u8;
147        let flags = sig_bit | (tx_bits << 1) | ((zstd_bit as u8) << 3);
148
149        buf.as_mut()[start] = flags;
150
151        if zstd_bit {
152            let mut tx_buf = Vec::with_capacity(256);
153
154            self.to_tx_compact(&mut tx_buf);
155
156            buf.put_slice(
157                &{
158                    #[cfg(feature = "std")]
159                    {
160                        reth_zstd_compressors::TRANSACTION_COMPRESSOR.with(|compressor| {
161                            let mut compressor = compressor.borrow_mut();
162                            compressor.compress(&tx_buf)
163                        })
164                    }
165                    #[cfg(not(feature = "std"))]
166                    {
167                        let mut compressor = reth_zstd_compressors::create_tx_compressor();
168                        compressor.compress(&tx_buf)
169                    }
170                }
171                .expect("Failed to compress"),
172            );
173        } else {
174            self.to_tx_compact(buf);
175        };
176
177        buf.as_mut().len() - start
178    }
179
180    fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
181        let flags = buf.get_u8() as usize;
182
183        let sig_bit = flags & 1;
184        let tx_bits = (flags & 0b110) >> 1;
185        let zstd_bit = flags >> 3;
186
187        let (signature, buf) = PrimitiveSignature::from_compact(buf, sig_bit);
188        let (tx_type, buf) = T::TxType::from_compact(buf, tx_bits);
189
190        let (transaction, buf) = if zstd_bit != 0 {
191            #[cfg(feature = "std")]
192            {
193                reth_zstd_compressors::TRANSACTION_DECOMPRESSOR.with(|decompressor| {
194                    let mut decompressor = decompressor.borrow_mut();
195
196                    let (tx, _) =
197                        Self::from_tx_compact(decompressor.decompress(buf), tx_type, signature);
198
199                    (tx, buf)
200                })
201            }
202            #[cfg(not(feature = "std"))]
203            {
204                let mut decompressor = reth_zstd_compressors::create_tx_decompressor();
205
206                let (tx, _) =
207                    Self::from_tx_compact(decompressor.decompress(buf), tx_type, signature);
208
209                (tx, buf)
210            }
211        } else {
212            Self::from_tx_compact(buf, tx_type, signature)
213        };
214
215        (transaction, buf)
216    }
217}
218
219impl<Eip4844: Compact + RlpEcdsaEncodableTx + Transaction + Send + Sync> Compact
220    for EthereumTxEnvelope<Eip4844>
221{
222    fn to_compact<B>(&self, buf: &mut B) -> usize
223    where
224        B: BufMut + AsMut<[u8]>,
225    {
226        <Self as CompactEnvelope>::to_compact(self, buf)
227    }
228
229    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
230        <Self as CompactEnvelope>::from_compact(buf, len)
231    }
232}