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
115pub(super) trait CompactEnvelope: Sized {
116    /// Takes a buffer which can be written to. *Ideally*, it returns the length written to.
117    fn to_compact<B>(&self, buf: &mut B) -> usize
118    where
119        B: BufMut + AsMut<[u8]>;
120
121    /// Takes a buffer which can be read from. Returns the object and `buf` with its internal cursor
122    /// advanced (eg.`.advance(len)`).
123    ///
124    /// `len` can either be the `buf` remaining length, or the length of the compacted type.
125    ///
126    /// It will panic, if `len` is smaller than `buf.len()`.
127    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]);
128}
129
130impl<T: Envelope + ToTxCompact + Transaction + Send + Sync> CompactEnvelope for T {
131    fn to_compact<B>(&self, buf: &mut B) -> usize
132    where
133        B: BufMut + AsMut<[u8]>,
134    {
135        let start = buf.as_mut().len();
136
137        // Placeholder for bitflags.
138        // The first byte uses 4 bits as flags: IsCompressed[1bit], TxType[2bits], Signature[1bit]
139        buf.put_u8(0);
140
141        let sig_bit = self.signature().to_compact(buf) as u8;
142        let zstd_bit = self.input().len() >= 32;
143
144        let tx_bits = if zstd_bit {
145            // compress the tx prefixed with txtype
146            let mut tx_buf = Vec::with_capacity(256);
147            let tx_bits = self.tx_type().to_compact(&mut tx_buf) as u8;
148            self.to_tx_compact(&mut tx_buf);
149
150            buf.put_slice(
151                &{
152                    #[cfg(feature = "std")]
153                    {
154                        reth_zstd_compressors::TRANSACTION_COMPRESSOR.with(|compressor| {
155                            let mut compressor = compressor.borrow_mut();
156                            compressor.compress(&tx_buf)
157                        })
158                    }
159                    #[cfg(not(feature = "std"))]
160                    {
161                        let mut compressor = reth_zstd_compressors::create_tx_compressor();
162                        compressor.compress(&tx_buf)
163                    }
164                }
165                .expect("Failed to compress"),
166            );
167            tx_bits
168        } else {
169            let tx_bits = self.tx_type().to_compact(buf) as u8;
170            self.to_tx_compact(buf);
171            tx_bits
172        };
173
174        let flags = sig_bit | (tx_bits << 1) | ((zstd_bit as u8) << 3);
175        buf.as_mut()[start] = flags;
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) = Signature::from_compact(buf, sig_bit);
188
189        let (transaction, buf) = if zstd_bit != 0 {
190            #[cfg(feature = "std")]
191            {
192                reth_zstd_compressors::TRANSACTION_DECOMPRESSOR.with(|decompressor| {
193                    let mut decompressor = decompressor.borrow_mut();
194                    let decompressed = decompressor.decompress(buf);
195
196                    let (tx_type, tx_buf) = T::TxType::from_compact(decompressed, tx_bits);
197                    let (tx, _) = Self::from_tx_compact(tx_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                let decompressed = decompressor.decompress(buf);
206                let (tx_type, tx_buf) = T::TxType::from_compact(decompressed, tx_bits);
207                let (tx, _) = Self::from_tx_compact(tx_buf, tx_type, signature);
208
209                (tx, buf)
210            }
211        } else {
212            let (tx_type, buf) = T::TxType::from_compact(buf, tx_bits);
213            Self::from_tx_compact(buf, tx_type, signature)
214        };
215
216        (transaction, buf)
217    }
218}
219
220impl<Eip4844: Compact + RlpEcdsaEncodableTx + Transaction + Send + Sync> Compact
221    for EthereumTxEnvelope<Eip4844>
222{
223    fn to_compact<B>(&self, buf: &mut B) -> usize
224    where
225        B: BufMut + AsMut<[u8]>,
226    {
227        <Self as CompactEnvelope>::to_compact(self, buf)
228    }
229
230    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
231        <Self as CompactEnvelope>::from_compact(buf, len)
232    }
233}