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    /// Attempts to recover signer and constructs a [`crate::transaction::Recovered`] with a
232    /// reference to the transaction `Recovered<&T>`
233    pub fn try_to_recovered_ref(
234        &self,
235    ) -> Result<crate::transaction::Recovered<&T>, alloy_primitives::SignatureError> {
236        let signer = self.recover_signer()?;
237        Ok(crate::transaction::Recovered::new_unchecked(&self.tx, signer))
238    }
239}
240
241#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
242impl<'a, T: SignableTransaction<Signature> + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a>
243    for Signed<T, Signature>
244{
245    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
246        use k256::{
247            ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
248            NonZeroScalar,
249        };
250        use rand::{rngs::StdRng, SeedableRng};
251
252        let rng_seed = u.arbitrary::<[u8; 32]>()?;
253        let mut rand_gen = StdRng::from_seed(rng_seed);
254        let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
255
256        let tx = T::arbitrary(u)?;
257
258        let (recoverable_sig, recovery_id) =
259            signing_key.sign_prehash(tx.signature_hash().as_ref()).unwrap();
260        let signature: Signature = (recoverable_sig, recovery_id).into();
261
262        Ok(tx.into_signed(signature))
263    }
264}
265
266#[cfg(feature = "serde")]
267mod serde {
268    use crate::transaction::RlpEcdsaEncodableTx;
269    use alloc::borrow::Cow;
270    use alloy_primitives::B256;
271    use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
272
273    #[derive(Serialize, Deserialize)]
274    struct Signed<'a, T: Clone, Sig: Clone> {
275        #[serde(flatten)]
276        tx: Cow<'a, T>,
277        #[serde(flatten)]
278        signature: Cow<'a, Sig>,
279        hash: Cow<'a, B256>,
280    }
281
282    impl<T> Serialize for super::Signed<T>
283    where
284        T: Clone + RlpEcdsaEncodableTx + Serialize,
285    {
286        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287        where
288            S: Serializer,
289        {
290            Signed {
291                tx: Cow::Borrowed(&self.tx),
292                signature: Cow::Borrowed(&self.signature),
293                hash: Cow::Borrowed(self.hash()),
294            }
295            .serialize(serializer)
296        }
297    }
298
299    impl<'de, T, Sig> Deserialize<'de> for super::Signed<T, Sig>
300    where
301        T: Clone + DeserializeOwned,
302        Sig: Clone + DeserializeOwned,
303    {
304        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
305        where
306            D: Deserializer<'de>,
307        {
308            Signed::<T, Sig>::deserialize(deserializer).map(|value| {
309                Self::new_unchecked(
310                    value.tx.into_owned(),
311                    value.signature.into_owned(),
312                    value.hash.into_owned(),
313                )
314            })
315        }
316    }
317}