alloy_consensus/transaction/
rlp.rs

1use crate::Signed;
2use alloc::vec::Vec;
3use alloy_eips::{
4    eip2718::{Eip2718Error, Eip2718Result},
5    Typed2718,
6};
7use alloy_primitives::{keccak256, Signature, TxHash};
8use alloy_rlp::{Buf, BufMut, Decodable, Encodable, Header};
9
10/// Helper trait for managing RLP encoding of transactions inside 2718
11#[doc(hidden)]
12#[doc(alias = "RlpEncodableTx", alias = "RlpTxEncoding")]
13#[auto_impl::auto_impl(&, Arc)]
14pub trait RlpEcdsaEncodableTx: Sized + Typed2718 {
15    /// Calculate the encoded length of the transaction's fields, without a RLP
16    /// header.
17    fn rlp_encoded_fields_length(&self) -> usize;
18
19    /// Encodes only the transaction's fields into the desired buffer, without
20    /// a RLP header.
21    fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut);
22
23    /// Create an list rlp header for the unsigned transaction.
24    fn rlp_header(&self) -> Header {
25        Header { list: true, payload_length: self.rlp_encoded_fields_length() }
26    }
27
28    /// Get the length of the transaction when RLP encoded.
29    fn rlp_encoded_length(&self) -> usize {
30        self.rlp_header().length_with_payload()
31    }
32
33    /// RLP encodes the transaction.
34    fn rlp_encode(&self, out: &mut dyn BufMut) {
35        self.rlp_header().encode(out);
36        self.rlp_encode_fields(out);
37    }
38
39    /// Create an rlp list header for the signed transaction.
40    fn rlp_header_signed(&self, signature: &Signature) -> Header {
41        let payload_length =
42            self.rlp_encoded_fields_length() + signature.rlp_rs_len() + signature.v().length();
43        Header { list: true, payload_length }
44    }
45
46    /// Get the length of the transaction when RLP encoded with the given
47    /// signature.
48    fn rlp_encoded_length_with_signature(&self, signature: &Signature) -> usize {
49        self.rlp_header_signed(signature).length_with_payload()
50    }
51
52    /// RLP encodes the transaction with the given signature.
53    fn rlp_encode_signed(&self, signature: &Signature, out: &mut dyn BufMut) {
54        self.rlp_header_signed(signature).encode(out);
55        self.rlp_encode_fields(out);
56        signature.write_rlp_vrs(out, signature.v());
57    }
58
59    /// Get the length of the transaction when EIP-2718 encoded. This is the
60    /// 1 byte type flag + the length of the RLP encoded transaction.
61    fn eip2718_encoded_length(&self, signature: &Signature) -> usize {
62        self.rlp_encoded_length_with_signature(signature) + 1
63    }
64
65    /// EIP-2718 encode the transaction with the given signature and type flag.
66    fn eip2718_encode_with_type(&self, signature: &Signature, ty: u8, out: &mut dyn BufMut) {
67        out.put_u8(ty);
68        self.rlp_encode_signed(signature, out);
69    }
70
71    /// EIP-2718 encode the transaction with the given signature and the default
72    /// type flag.
73    fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
74        self.eip2718_encode_with_type(signature, self.ty(), out);
75    }
76
77    /// Create an rlp header for the network encoded transaction. This will
78    /// usually be a string header, however, legacy transactions' network
79    /// encoding is a list.
80    fn network_header(&self, signature: &Signature) -> Header {
81        let payload_length = self.eip2718_encoded_length(signature);
82        Header { list: false, payload_length }
83    }
84
85    /// Get the length of the transaction when network encoded. This is the
86    /// EIP-2718 encoded length with an outer RLP header.
87    fn network_encoded_length(&self, signature: &Signature) -> usize {
88        self.network_header(signature).length_with_payload()
89    }
90
91    /// Network encode the transaction with the given signature.
92    fn network_encode_with_type(&self, signature: &Signature, ty: u8, out: &mut dyn BufMut) {
93        self.network_header(signature).encode(out);
94        self.eip2718_encode_with_type(signature, ty, out);
95    }
96
97    /// Network encode the transaction with the given signature and the default
98    /// type flag.
99    fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
100        self.network_encode_with_type(signature, self.ty(), out);
101    }
102
103    /// Calculate the transaction hash for the given signature and type.
104    fn tx_hash_with_type(&self, signature: &Signature, ty: u8) -> TxHash {
105        let mut buf = Vec::with_capacity(self.eip2718_encoded_length(signature));
106        self.eip2718_encode_with_type(signature, ty, &mut buf);
107        keccak256(&buf)
108    }
109
110    /// Calculate the transaction hash for the given signature.
111    fn tx_hash(&self, signature: &Signature) -> TxHash {
112        self.tx_hash_with_type(signature, self.ty())
113    }
114}
115
116/// Helper trait for managing RLP decoding of transactions inside 2718 envelopes.
117#[doc(hidden)]
118#[doc(alias = "RlpDecodableTx", alias = "RlpTxDecoding")]
119pub trait RlpEcdsaDecodableTx: RlpEcdsaEncodableTx {
120    /// The default transaction type for this transaction.
121    const DEFAULT_TX_TYPE: u8;
122
123    /// Decodes the fields of the transaction from RLP bytes. Do not decode a
124    /// header. You may assume the buffer is long enough to contain the
125    /// transaction.
126    fn rlp_decode_fields(buf: &mut &[u8]) -> alloy_rlp::Result<Self>;
127
128    /// Decodes the transaction from RLP bytes.
129    fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
130        let header = Header::decode(buf)?;
131        if !header.list {
132            return Err(alloy_rlp::Error::UnexpectedString);
133        }
134        let remaining = buf.len();
135
136        if header.payload_length > remaining {
137            return Err(alloy_rlp::Error::InputTooShort);
138        }
139
140        let this = Self::rlp_decode_fields(buf)?;
141
142        if buf.len() + header.payload_length != remaining {
143            return Err(alloy_rlp::Error::UnexpectedLength);
144        }
145
146        Ok(this)
147    }
148
149    /// Decodes the transaction from RLP bytes, including the signature.
150    fn rlp_decode_with_signature(buf: &mut &[u8]) -> alloy_rlp::Result<(Self, Signature)> {
151        let header = Header::decode(buf)?;
152        if !header.list {
153            return Err(alloy_rlp::Error::UnexpectedString);
154        }
155
156        let remaining = buf.len();
157        let tx = Self::rlp_decode_fields(buf)?;
158        let signature = Signature::decode_rlp_vrs(buf, bool::decode)?;
159
160        if buf.len() + header.payload_length != remaining {
161            return Err(alloy_rlp::Error::ListLengthMismatch {
162                expected: header.payload_length,
163                got: remaining - buf.len(),
164            });
165        }
166
167        Ok((tx, signature))
168    }
169
170    /// Decodes the transaction from RLP bytes, including the signature
171    /// Produces a [`Signed`].
172    fn rlp_decode_signed(buf: &mut &[u8]) -> alloy_rlp::Result<Signed<Self>> {
173        Self::rlp_decode_with_signature(buf)
174            .map(|(tx, signature)| Signed::new_unhashed(tx, signature))
175    }
176
177    /// Decodes the transaction from eip2718 bytes, expecting the given type
178    /// flag.
179    fn eip2718_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Signed<Self>> {
180        let original_buf = *buf;
181
182        if buf.remaining() < 1 {
183            return Err(alloy_rlp::Error::InputTooShort.into());
184        }
185        let actual = buf.get_u8();
186        if actual != ty {
187            return Err(Eip2718Error::UnexpectedType(actual));
188        }
189
190        // OPT: We avoid re-serializing by calculating the hash directly
191        // from the original buffer contents.
192        let (tx, signature) = Self::rlp_decode_with_signature(buf)?;
193        let total_len = tx.eip2718_encoded_length(&signature);
194        let hash = keccak256(&original_buf[..total_len]);
195
196        Ok(Signed::new_unchecked(tx, signature, hash))
197    }
198
199    /// Decodes the transaction from eip2718 bytes, expecting the default type
200    /// flag.
201    fn eip2718_decode(buf: &mut &[u8]) -> Eip2718Result<Signed<Self>> {
202        Self::eip2718_decode_with_type(buf, Self::DEFAULT_TX_TYPE)
203    }
204
205    /// Decodes the transaction from network bytes.
206    fn network_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Signed<Self>> {
207        let header = Header::decode(buf)?;
208        if header.list {
209            return Err(alloy_rlp::Error::UnexpectedList.into());
210        }
211
212        let remaining = buf.len();
213        let res = Self::eip2718_decode_with_type(buf, ty)?;
214
215        if buf.len() + header.payload_length != remaining {
216            return Err(alloy_rlp::Error::UnexpectedLength.into());
217        }
218
219        Ok(res)
220    }
221
222    /// Decodes the transaction from network bytes, expecting the default type
223    /// flag.
224    fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Signed<Self>> {
225        Self::network_decode_with_type(buf, Self::DEFAULT_TX_TYPE)
226    }
227}
228
229/// Helper trait for managing RLP encoding and decoding of transactions inside 2718
230/// envelopes.
231#[doc(hidden)]
232pub trait RlpEcdsaTx: RlpEcdsaEncodableTx + RlpEcdsaDecodableTx {}
233
234// Implement RlpEcdsaTx for all types that implement both required traits
235impl<T> RlpEcdsaTx for T where T: RlpEcdsaEncodableTx + RlpEcdsaDecodableTx {}