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::Signature;
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 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 trait FromTxCompact {
34    /// The transaction type that represents the set of transactions.
35    type TxType;
36
37    /// Deserializes inner transaction using [`Compact`] encoding. The concrete type is determined
38    /// by `tx_type`. The `signature` is added to create typed and signed transaction.
39    ///
40    /// Returns a tuple of 2 elements. The first element is the deserialized value and the second
41    /// is a byte slice created from `buf` with a starting position advanced by the exact amount
42    /// of bytes consumed for this process.  
43    fn from_tx_compact(buf: &[u8], tx_type: Self::TxType, signature: Signature) -> (Self, &[u8])
44    where
45        Self: Sized;
46}
47
48impl<Eip4844: Compact + Transaction> ToTxCompact for EthereumTxEnvelope<Eip4844> {
49    fn to_tx_compact(&self, buf: &mut (impl BufMut + AsMut<[u8]>)) {
50        match self {
51            Self::Legacy(tx) => tx.tx().to_compact(buf),
52            Self::Eip2930(tx) => tx.tx().to_compact(buf),
53            Self::Eip1559(tx) => tx.tx().to_compact(buf),
54            Self::Eip4844(tx) => tx.tx().to_compact(buf),
55            Self::Eip7702(tx) => tx.tx().to_compact(buf),
56        };
57    }
58}
59
60impl<Eip4844: Compact + Transaction> FromTxCompact for EthereumTxEnvelope<Eip4844> {
61    type TxType = TxType;
62
63    fn from_tx_compact(buf: &[u8], tx_type: TxType, signature: Signature) -> (Self, &[u8]) {
64        match tx_type {
65            TxType::Legacy => {
66                let (tx, buf) = TxLegacy::from_compact(buf, buf.len());
67                let tx = Signed::new_unhashed(tx, signature);
68                (Self::Legacy(tx), buf)
69            }
70            TxType::Eip2930 => {
71                let (tx, buf) = TxEip2930::from_compact(buf, buf.len());
72                let tx = Signed::new_unhashed(tx, signature);
73                (Self::Eip2930(tx), buf)
74            }
75            TxType::Eip1559 => {
76                let (tx, buf) = TxEip1559::from_compact(buf, buf.len());
77                let tx = Signed::new_unhashed(tx, signature);
78                (Self::Eip1559(tx), buf)
79            }
80            TxType::Eip4844 => {
81                let (tx, buf) = Eip4844::from_compact(buf, buf.len());
82                let tx = Signed::new_unhashed(tx, signature);
83                (Self::Eip4844(tx), buf)
84            }
85            TxType::Eip7702 => {
86                let (tx, buf) = TxEip7702::from_compact(buf, buf.len());
87                let tx = Signed::new_unhashed(tx, signature);
88                (Self::Eip7702(tx), buf)
89            }
90        }
91    }
92}
93
94/// A trait for types convertible from a compact transaction type.
95pub trait Envelope: FromTxCompact<TxType: Compact> {
96    ///Returns the signature
97    fn signature(&self) -> &Signature;
98
99    ///Returns the tx type
100    fn tx_type(&self) -> Self::TxType;
101}
102
103impl<Eip4844: Compact + Transaction + RlpEcdsaEncodableTx> Envelope
104    for EthereumTxEnvelope<Eip4844>
105{
106    fn signature(&self) -> &Signature {
107        Self::signature(self)
108    }
109
110    fn tx_type(&self) -> Self::TxType {
111        Self::tx_type(self)
112    }
113}
114
115/// Compact serialization for transaction envelopes with compression and bitfield packing.
116pub trait CompactEnvelope: Sized {
117    /// Takes a buffer which can be written to. *Ideally*, it returns the length written to.
118    fn to_compact<B>(&self, buf: &mut B) -> usize
119    where
120        B: BufMut + AsMut<[u8]>;
121
122    /// Takes a buffer which can be read from. Returns the object and `buf` with its internal cursor
123    /// advanced (eg.`.advance(len)`).
124    ///
125    /// `len` can either be the `buf` remaining length, or the length of the compacted type.
126    ///
127    /// It will panic, if `len` is smaller than `buf.len()`.
128    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]);
129}
130
131impl<T: Envelope + ToTxCompact + Transaction + Send + Sync> CompactEnvelope for T {
132    fn to_compact<B>(&self, buf: &mut B) -> usize
133    where
134        B: BufMut + AsMut<[u8]>,
135    {
136        let start = buf.as_mut().len();
137
138        // Placeholder for bitflags.
139        // The first byte uses 4 bits as flags: IsCompressed[1bit], TxType[2bits], Signature[1bit]
140        buf.put_u8(0);
141
142        let sig_bit = self.signature().to_compact(buf) as u8;
143        let zstd_bit = self.input().len() >= 32;
144
145        let tx_bits = if zstd_bit {
146            // compress the tx prefixed with txtype
147            let mut tx_buf = Vec::with_capacity(256);
148            let tx_bits = self.tx_type().to_compact(&mut tx_buf) as u8;
149            self.to_tx_compact(&mut tx_buf);
150
151            buf.put_slice(
152                &{
153                    #[cfg(feature = "std")]
154                    {
155                        reth_zstd_compressors::TRANSACTION_COMPRESSOR.with(|compressor| {
156                            let mut compressor = compressor.borrow_mut();
157                            compressor.compress(&tx_buf)
158                        })
159                    }
160                    #[cfg(not(feature = "std"))]
161                    {
162                        let mut compressor = reth_zstd_compressors::create_tx_compressor();
163                        compressor.compress(&tx_buf)
164                    }
165                }
166                .expect("Failed to compress"),
167            );
168            tx_bits
169        } else {
170            let tx_bits = self.tx_type().to_compact(buf) as u8;
171            self.to_tx_compact(buf);
172            tx_bits
173        };
174
175        let flags = sig_bit | (tx_bits << 1) | ((zstd_bit as u8) << 3);
176        buf.as_mut()[start] = flags;
177
178        buf.as_mut().len() - start
179    }
180
181    fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
182        let flags = buf.get_u8() as usize;
183
184        let sig_bit = flags & 1;
185        let tx_bits = (flags & 0b110) >> 1;
186        let zstd_bit = flags >> 3;
187
188        let (signature, buf) = Signature::from_compact(buf, sig_bit);
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                    let decompressed = decompressor.decompress(buf);
196
197                    let (tx_type, tx_buf) = T::TxType::from_compact(decompressed, tx_bits);
198                    let (tx, _) = Self::from_tx_compact(tx_buf, tx_type, signature);
199
200                    (tx, buf)
201                })
202            }
203            #[cfg(not(feature = "std"))]
204            {
205                let mut decompressor = reth_zstd_compressors::create_tx_decompressor();
206                let decompressed = decompressor.decompress(buf);
207                let (tx_type, tx_buf) = T::TxType::from_compact(decompressed, tx_bits);
208                let (tx, _) = Self::from_tx_compact(tx_buf, tx_type, signature);
209
210                (tx, buf)
211            }
212        } else {
213            let (tx_type, buf) = T::TxType::from_compact(buf, tx_bits);
214            Self::from_tx_compact(buf, tx_type, signature)
215        };
216
217        (transaction, buf)
218    }
219}
220
221impl<Eip4844: Compact + RlpEcdsaEncodableTx + Transaction + Send + Sync> Compact
222    for EthereumTxEnvelope<Eip4844>
223{
224    fn to_compact<B>(&self, buf: &mut B) -> usize
225    where
226        B: BufMut + AsMut<[u8]>,
227    {
228        <Self as CompactEnvelope>::to_compact(self, buf)
229    }
230
231    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
232        <Self as CompactEnvelope>::from_compact(buf, len)
233    }
234}