alloy_consensus/transaction/
recovered.rs

1use alloy_eips::{eip2718::Encodable2718, Typed2718};
2use alloy_primitives::{bytes, Address, B256};
3use alloy_rlp::{Decodable, Encodable};
4use derive_more::{AsRef, Deref};
5
6/// Signed object with recovered signer.
7#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, AsRef, Deref)]
8#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct Recovered<T> {
11    /// Signer of the type
12    signer: Address,
13    /// Signed object
14    #[deref]
15    #[as_ref]
16    #[cfg_attr(feature = "serde", serde(flatten))]
17    inner: T,
18}
19
20impl<T> Recovered<T> {
21    /// Signer of the object recovered from signature
22    pub const fn signer(&self) -> Address {
23        self.signer
24    }
25
26    /// Reference to the signer of the object recovered from signature
27    pub const fn signer_ref(&self) -> &Address {
28        &self.signer
29    }
30
31    /// Reference to the inner recovered object.
32    pub const fn inner(&self) -> &T {
33        &self.inner
34    }
35
36    /// Reference to the inner recovered object.
37    pub fn inner_mut(&mut self) -> &mut T {
38        &mut self.inner
39    }
40
41    /// Reference to the inner signed object.
42    pub fn into_inner(self) -> T {
43        self.inner
44    }
45
46    /// Clone the inner signed object.
47    pub fn clone_inner(&self) -> T
48    where
49        T: Clone,
50    {
51        self.inner.clone()
52    }
53
54    /// Returns a reference to the transaction.
55    #[doc(alias = "transaction")]
56    #[deprecated = "Use `inner` instead"]
57    pub const fn tx(&self) -> &T {
58        &self.inner
59    }
60
61    /// Transform back to the transaction.
62    #[doc(alias = "into_transaction")]
63    #[deprecated = "Use `into_inner` instead"]
64    pub fn into_tx(self) -> T {
65        self.inner
66    }
67
68    /// Clone the inner transaction.
69    #[doc(alias = "clone_transaction")]
70    #[deprecated = "Use `clone_inner` instead"]
71    pub fn clone_tx(&self) -> T
72    where
73        T: Clone,
74    {
75        self.inner.clone()
76    }
77
78    /// Dissolve Self to its component
79    #[doc(alias = "split")]
80    pub fn into_parts(self) -> (T, Address) {
81        (self.inner, self.signer)
82    }
83
84    /// Converts from `&Recovered<T>` to `Recovered<&T>`.
85    pub const fn as_recovered_ref(&self) -> Recovered<&T> {
86        Recovered { inner: &self.inner, signer: self.signer() }
87    }
88
89    /// Create [`Recovered`] from the given transaction and [`Address`] of the signer.
90    ///
91    /// Note: This does not check if the signer is the actual signer of the transaction.
92    #[inline]
93    pub const fn new_unchecked(inner: T, signer: Address) -> Self {
94        Self { inner, signer }
95    }
96
97    /// Converts the inner signed object to the given alternative that is `From<T>`
98    pub fn convert<Tx>(self) -> Recovered<Tx>
99    where
100        Tx: From<T>,
101    {
102        self.map(Tx::from)
103    }
104
105    /// Converts the transaction type to the given alternative that is `From<T>`
106    #[deprecated = "Use `convert_inner` instead"]
107    pub fn convert_transaction<Tx>(self) -> Recovered<Tx>
108    where
109        Tx: From<T>,
110    {
111        self.map(Tx::from)
112    }
113
114    /// Converts the inner signed object to the given alternative that is `TryFrom<T>`
115    pub fn try_convert<Tx, E>(self) -> Result<Recovered<Tx>, Tx::Error>
116    where
117        Tx: TryFrom<T>,
118    {
119        self.try_map(Tx::try_from)
120    }
121
122    /// Converts the transaction to the given alternative that is `TryFrom<T>`
123    #[deprecated = "Use `try_convert_inner` instead"]
124    pub fn try_convert_transaction<Tx, E>(self) -> Result<Recovered<Tx>, Tx::Error>
125    where
126        Tx: TryFrom<T>,
127    {
128        self.try_map(Tx::try_from)
129    }
130
131    /// Applies the given closure to the inner signed object.
132    pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Recovered<Tx> {
133        Recovered::new_unchecked(f(self.inner), self.signer)
134    }
135
136    /// Applies the given closure to the inner transaction type.
137    #[deprecated = "Use `map_inner` instead"]
138    pub fn map_transaction<Tx>(self, f: impl FnOnce(T) -> Tx) -> Recovered<Tx> {
139        Recovered::new_unchecked(f(self.inner), self.signer)
140    }
141
142    /// Applies the given fallible closure to the inner signed object.
143    pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Recovered<Tx>, E> {
144        Ok(Recovered::new_unchecked(f(self.inner)?, self.signer))
145    }
146
147    /// Applies the given fallible closure to the inner transaction type.
148    #[deprecated = "Use `try_map_inner` instead"]
149    pub fn try_map_transaction<Tx, E>(
150        self,
151        f: impl FnOnce(T) -> Result<Tx, E>,
152    ) -> Result<Recovered<Tx>, E> {
153        Ok(Recovered::new_unchecked(f(self.inner)?, self.signer))
154    }
155}
156
157impl<T> Recovered<&T> {
158    /// Maps a `Recovered<&T>` to a `Recovered<T>` by cloning the transaction.
159    pub fn cloned(self) -> Recovered<T>
160    where
161        T: Clone,
162    {
163        let Self { inner, signer } = self;
164        Recovered::new_unchecked(inner.clone(), signer)
165    }
166}
167
168impl<T: Encodable> Encodable for Recovered<T> {
169    /// This encodes the transaction _with_ the signature, and an rlp header.
170    fn encode(&self, out: &mut dyn bytes::BufMut) {
171        self.inner.encode(out)
172    }
173
174    fn length(&self) -> usize {
175        self.inner.length()
176    }
177}
178
179impl<T: Decodable + SignerRecoverable> Decodable for Recovered<T> {
180    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
181        let tx = T::decode(buf)?;
182        let signer = tx.recover_signer().map_err(|_| {
183            alloy_rlp::Error::Custom("Unable to recover decoded transaction signer.")
184        })?;
185        Ok(Self::new_unchecked(tx, signer))
186    }
187}
188
189impl<T: Typed2718> Typed2718 for Recovered<T> {
190    fn ty(&self) -> u8 {
191        self.inner.ty()
192    }
193}
194
195impl<T: Encodable2718> Encodable2718 for Recovered<T> {
196    fn encode_2718_len(&self) -> usize {
197        self.inner.encode_2718_len()
198    }
199
200    fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
201        self.inner.encode_2718(out)
202    }
203
204    fn trie_hash(&self) -> B256 {
205        self.inner.trie_hash()
206    }
207}
208
209/// A type that can recover the signer of a transaction.
210///
211/// This is a helper trait that only provides the ability to recover the signer (address) of a
212/// transaction.
213pub trait SignerRecoverable {
214    /// Recover signer from signature and hash.
215    ///
216    /// Returns an error if the transaction's signature is invalid following [EIP-2](https://eips.ethereum.org/EIPS/eip-2).
217    ///
218    /// Note:
219    ///
220    /// This can fail for some early ethereum mainnet transactions pre EIP-2, use
221    /// [`Self::recover_signer_unchecked`] if you want to recover the signer without ensuring that
222    /// the signature has a low `s` value.
223    fn recover_signer(&self) -> Result<Address, alloy_primitives::SignatureError>;
224
225    /// Recover signer from signature and hash _without ensuring that the signature has a low `s`
226    /// value_.
227    ///
228    /// Returns an error if the transaction's signature is invalid.
229    fn recover_signer_unchecked(&self) -> Result<Address, alloy_primitives::SignatureError>;
230}