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