alloy_consensus/
signed.rs

1use crate::transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, SignableTransaction};
2use alloy_eips::eip2718::Eip2718Result;
3use alloy_primitives::{Signature, B256};
4use alloy_rlp::BufMut;
5use core::hash::{Hash, Hasher};
6#[cfg(not(feature = "std"))]
7use once_cell::race::OnceBox as OnceLock;
8#[cfg(feature = "std")]
9use std::sync::OnceLock;
10
11/// A transaction with a signature and hash seal.
12#[derive(Debug, Clone)]
13pub struct Signed<T, Sig = Signature> {
14    #[doc(alias = "transaction")]
15    tx: T,
16    signature: Sig,
17    #[doc(alias = "tx_hash", alias = "transaction_hash")]
18    hash: OnceLock<B256>,
19}
20
21impl<T, Sig> Signed<T, Sig> {
22    /// Instantiate from a transaction and signature. Does not verify the signature.
23    pub fn new_unchecked(tx: T, signature: Sig, hash: B256) -> Self {
24        let value = OnceLock::new();
25        #[allow(clippy::useless_conversion)]
26        value.get_or_init(|| hash.into());
27        Self { tx, signature, hash: value }
28    }
29
30    /// Instantiate from a transaction and signature. Does not verify the signature.
31    pub const fn new_unhashed(tx: T, signature: Sig) -> Self {
32        Self { tx, signature, hash: OnceLock::new() }
33    }
34
35    /// Returns a reference to the transaction.
36    #[doc(alias = "transaction")]
37    pub const fn tx(&self) -> &T {
38        &self.tx
39    }
40
41    /// Returns a mutable reference to the transaction.
42    pub fn tx_mut(&mut self) -> &mut T {
43        &mut self.tx
44    }
45
46    /// Returns a reference to the signature.
47    pub const fn signature(&self) -> &Sig {
48        &self.signature
49    }
50
51    /// Returns the transaction without signature.
52    pub fn strip_signature(self) -> T {
53        self.tx
54    }
55
56    /// Converts the transaction type to the given alternative that is `From<T>`
57    ///
58    /// Caution: This is only intended for converting transaction types that are structurally
59    /// equivalent (produce the same hash).
60    pub fn convert<U>(self) -> Signed<U, Sig>
61    where
62        U: From<T>,
63    {
64        self.map(U::from)
65    }
66
67    /// Converts the transaction to the given alternative that is `TryFrom<T>`
68    ///
69    /// Returns the transaction with the new transaction type if all conversions were successful.
70    ///
71    /// Caution: This is only intended for converting transaction types that are structurally
72    /// equivalent (produce the same hash).
73    pub fn try_convert<U>(self) -> Result<Signed<U, Sig>, U::Error>
74    where
75        U: TryFrom<T>,
76    {
77        self.try_map(U::try_from)
78    }
79
80    /// Applies the given closure to the inner transaction type.
81    ///
82    /// Caution: This is only intended for converting transaction types that are structurally
83    /// equivalent (produce the same hash).
84    pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Signed<Tx, Sig> {
85        let Self { tx, signature, hash } = self;
86        Signed { tx: f(tx), signature, hash }
87    }
88
89    /// Applies the given fallible closure to the inner transactions.
90    ///
91    /// Caution: This is only intended for converting transaction types that are structurally
92    /// equivalent (produce the same hash).
93    pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Signed<Tx, Sig>, E> {
94        let Self { tx, signature, hash } = self;
95        Ok(Signed { tx: f(tx)?, signature, hash })
96    }
97}
98
99impl<T: SignableTransaction<Sig>, Sig> Signed<T, Sig> {
100    /// Calculate the signing hash for the transaction.
101    pub fn signature_hash(&self) -> B256 {
102        self.tx.signature_hash()
103    }
104}
105
106impl<T> Signed<T>
107where
108    T: RlpEcdsaEncodableTx,
109{
110    /// Returns a reference to the transaction hash.
111    #[doc(alias = "tx_hash", alias = "transaction_hash")]
112    pub fn hash(&self) -> &B256 {
113        #[allow(clippy::useless_conversion)]
114        self.hash.get_or_init(|| self.tx.tx_hash(&self.signature).into())
115    }
116
117    /// Splits the transaction into parts.
118    pub fn into_parts(self) -> (T, Signature, B256) {
119        let hash = *self.hash();
120        (self.tx, self.signature, hash)
121    }
122
123    /// Get the length of the transaction when RLP encoded.
124    pub fn rlp_encoded_length(&self) -> usize {
125        self.tx.rlp_encoded_length_with_signature(&self.signature)
126    }
127
128    /// RLP encode the signed transaction.
129    pub fn rlp_encode(&self, out: &mut dyn BufMut) {
130        self.tx.rlp_encode_signed(&self.signature, out);
131    }
132
133    /// Get the length of the transaction when EIP-2718 encoded.
134    pub fn eip2718_encoded_length(&self) -> usize {
135        self.tx.eip2718_encoded_length(&self.signature)
136    }
137
138    /// EIP-2718 encode the signed transaction with a specified type flag.
139    pub fn eip2718_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
140        self.tx.eip2718_encode_with_type(&self.signature, ty, out);
141    }
142
143    /// EIP-2718 encode the signed transaction.
144    pub fn eip2718_encode(&self, out: &mut dyn BufMut) {
145        self.tx.eip2718_encode(&self.signature, out);
146    }
147
148    /// Get the length of the transaction when network encoded.
149    pub fn network_encoded_length(&self) -> usize {
150        self.tx.network_encoded_length(&self.signature)
151    }
152
153    /// Network encode the signed transaction with a specified type flag.
154    pub fn network_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
155        self.tx.network_encode_with_type(&self.signature, ty, out);
156    }
157
158    /// Network encode the signed transaction.
159    pub fn network_encode(&self, out: &mut dyn BufMut) {
160        self.tx.network_encode(&self.signature, out);
161    }
162}
163
164impl<T> Signed<T>
165where
166    T: RlpEcdsaDecodableTx,
167{
168    /// RLP decode the signed transaction.
169    pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
170        T::rlp_decode_signed(buf)
171    }
172
173    /// EIP-2718 decode the signed transaction with a specified type flag.
174    pub fn eip2718_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
175        T::eip2718_decode_with_type(buf, ty)
176    }
177
178    /// EIP-2718 decode the signed transaction.
179    pub fn eip2718_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
180        T::eip2718_decode(buf)
181    }
182
183    /// Network decode the signed transaction with a specified type flag.
184    pub fn network_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
185        T::network_decode_with_type(buf, ty)
186    }
187
188    /// Network decode the signed transaction.
189    pub fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
190        T::network_decode(buf)
191    }
192}
193
194impl<T> Hash for Signed<T>
195where
196    T: RlpEcdsaDecodableTx + Hash,
197{
198    fn hash<H: Hasher>(&self, state: &mut H) {
199        self.hash().hash(state);
200        self.tx.hash(state);
201        self.signature.hash(state);
202    }
203}
204
205impl<T: RlpEcdsaEncodableTx + PartialEq> PartialEq for Signed<T> {
206    fn eq(&self, other: &Self) -> bool {
207        self.hash() == other.hash() && self.tx == other.tx && self.signature == other.signature
208    }
209}
210
211impl<T: RlpEcdsaEncodableTx + PartialEq> Eq for Signed<T> {}
212
213#[cfg(feature = "k256")]
214impl<T: SignableTransaction<Signature>> Signed<T, Signature> {
215    /// Recover the signer of the transaction
216    pub fn recover_signer(
217        &self,
218    ) -> Result<alloy_primitives::Address, alloy_primitives::SignatureError> {
219        let sighash = self.tx.signature_hash();
220        self.signature.recover_address_from_prehash(&sighash)
221    }
222
223    /// Attempts to recover signer and constructs a [`crate::transaction::Recovered`] object.
224    pub fn try_into_recovered(
225        self,
226    ) -> Result<crate::transaction::Recovered<T>, alloy_primitives::SignatureError> {
227        let signer = self.recover_signer()?;
228        Ok(crate::transaction::Recovered::new_unchecked(self.tx, signer))
229    }
230}
231
232#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
233impl<'a, T: SignableTransaction<Signature> + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a>
234    for Signed<T, Signature>
235{
236    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
237        use k256::{
238            ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
239            NonZeroScalar,
240        };
241        use rand::{rngs::StdRng, SeedableRng};
242
243        let rng_seed = u.arbitrary::<[u8; 32]>()?;
244        let mut rand_gen = StdRng::from_seed(rng_seed);
245        let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
246
247        let tx = T::arbitrary(u)?;
248
249        let (recoverable_sig, recovery_id) =
250            signing_key.sign_prehash(tx.signature_hash().as_ref()).unwrap();
251        let signature: Signature = (recoverable_sig, recovery_id).into();
252
253        Ok(tx.into_signed(signature))
254    }
255}
256
257#[cfg(feature = "serde")]
258mod serde {
259    use crate::transaction::RlpEcdsaEncodableTx;
260    use alloc::borrow::Cow;
261    use alloy_primitives::B256;
262    use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
263
264    #[derive(Serialize, Deserialize)]
265    struct Signed<'a, T: Clone, Sig: Clone> {
266        #[serde(flatten)]
267        tx: Cow<'a, T>,
268        #[serde(flatten)]
269        signature: Cow<'a, Sig>,
270        hash: Cow<'a, B256>,
271    }
272
273    impl<T> Serialize for super::Signed<T>
274    where
275        T: Clone + RlpEcdsaEncodableTx + Serialize,
276    {
277        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
278        where
279            S: Serializer,
280        {
281            Signed {
282                tx: Cow::Borrowed(&self.tx),
283                signature: Cow::Borrowed(&self.signature),
284                hash: Cow::Borrowed(self.hash()),
285            }
286            .serialize(serializer)
287        }
288    }
289
290    impl<'de, T, Sig> Deserialize<'de> for super::Signed<T, Sig>
291    where
292        T: Clone + DeserializeOwned,
293        Sig: Clone + DeserializeOwned,
294    {
295        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
296        where
297            D: Deserializer<'de>,
298        {
299            Signed::<T, Sig>::deserialize(deserializer).map(|value| {
300                Self::new_unchecked(
301                    value.tx.into_owned(),
302                    value.signature.into_owned(),
303                    value.hash.into_owned(),
304                )
305            })
306        }
307    }
308}