alloy_consensus/transaction/
recovered.rs

1use crate::crypto::RecoveryError;
2use alloy_eips::{
3    eip2718::{Encodable2718, WithEncoded},
4    Typed2718,
5};
6use alloy_primitives::{bytes, Address, Bytes, Sealed, B256};
7use alloy_rlp::{Decodable, Encodable};
8use derive_more::{AsRef, Deref};
9
10/// Signed object with recovered signer.
11#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, AsRef, Deref)]
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Recovered<T> {
15    /// Signer of the type
16    signer: Address,
17    /// Signed object
18    #[deref]
19    #[as_ref]
20    #[cfg_attr(feature = "serde", serde(flatten))]
21    inner: T,
22}
23
24impl<T> Recovered<T> {
25    /// Signer of the object recovered from signature
26    pub const fn signer(&self) -> Address {
27        self.signer
28    }
29
30    /// Reference to the signer of the object recovered from signature
31    pub const fn signer_ref(&self) -> &Address {
32        &self.signer
33    }
34
35    /// Reference to the inner recovered object.
36    pub const fn inner(&self) -> &T {
37        &self.inner
38    }
39
40    /// Reference to the inner recovered object.
41    pub const fn inner_mut(&mut self) -> &mut T {
42        &mut self.inner
43    }
44
45    /// Reference to the inner signed object.
46    pub fn into_inner(self) -> T {
47        self.inner
48    }
49
50    /// Clone the inner signed object.
51    pub fn clone_inner(&self) -> T
52    where
53        T: Clone,
54    {
55        self.inner.clone()
56    }
57
58    /// Returns a reference to the transaction.
59    #[doc(alias = "transaction")]
60    #[deprecated = "Use `inner` instead"]
61    pub const fn tx(&self) -> &T {
62        &self.inner
63    }
64
65    /// Transform back to the transaction.
66    #[doc(alias = "into_transaction")]
67    #[deprecated = "Use `into_inner` instead"]
68    pub fn into_tx(self) -> T {
69        self.inner
70    }
71
72    /// Clone the inner transaction.
73    #[doc(alias = "clone_transaction")]
74    #[deprecated = "Use `clone_inner` instead"]
75    pub fn clone_tx(&self) -> T
76    where
77        T: Clone,
78    {
79        self.inner.clone()
80    }
81
82    /// Dissolve Self to its component
83    #[doc(alias = "split")]
84    pub fn into_parts(self) -> (T, Address) {
85        (self.inner, self.signer)
86    }
87
88    /// Converts from `&Recovered<T>` to `Recovered<&T>`.
89    pub const fn as_recovered_ref(&self) -> Recovered<&T> {
90        Recovered { inner: &self.inner, signer: self.signer() }
91    }
92
93    /// Create [`Recovered`] from the given transaction and [`Address`] of the signer.
94    ///
95    /// Note: This does not check if the signer is the actual signer of the transaction.
96    #[inline]
97    pub const fn new_unchecked(inner: T, signer: Address) -> Self {
98        Self { inner, signer }
99    }
100
101    /// Converts the inner signed object to the given alternative that is `From<T>`
102    pub fn convert<Tx>(self) -> Recovered<Tx>
103    where
104        Tx: From<T>,
105    {
106        self.map(Tx::from)
107    }
108
109    /// Converts the transaction type to the given alternative that is `From<T>`
110    #[deprecated = "Use `convert` instead"]
111    pub fn convert_transaction<Tx>(self) -> Recovered<Tx>
112    where
113        Tx: From<T>,
114    {
115        self.map(Tx::from)
116    }
117
118    /// Converts the inner signed object to the given alternative that is `TryFrom<T>`
119    pub fn try_convert<Tx, E>(self) -> Result<Recovered<Tx>, Tx::Error>
120    where
121        Tx: TryFrom<T>,
122    {
123        self.try_map(Tx::try_from)
124    }
125
126    /// Converts the transaction to the given alternative that is `TryFrom<T>`
127    #[deprecated = "Use `try_convert` instead"]
128    pub fn try_convert_transaction<Tx, E>(self) -> Result<Recovered<Tx>, Tx::Error>
129    where
130        Tx: TryFrom<T>,
131    {
132        self.try_map(Tx::try_from)
133    }
134
135    /// Applies the given closure to the inner signed object.
136    pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Recovered<Tx> {
137        Recovered::new_unchecked(f(self.inner), self.signer)
138    }
139
140    /// Applies the given closure to the inner transaction type.
141    #[deprecated = "Use `map` instead"]
142    pub fn map_transaction<Tx>(self, f: impl FnOnce(T) -> Tx) -> Recovered<Tx> {
143        Recovered::new_unchecked(f(self.inner), self.signer)
144    }
145
146    /// Applies the given fallible closure to the inner signed object.
147    pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Recovered<Tx>, E> {
148        Ok(Recovered::new_unchecked(f(self.inner)?, self.signer))
149    }
150
151    /// Applies the given fallible closure to the inner transaction type.
152    #[deprecated = "Use `try_map` instead"]
153    pub fn try_map_transaction<Tx, E>(
154        self,
155        f: impl FnOnce(T) -> Result<Tx, E>,
156    ) -> Result<Recovered<Tx>, E> {
157        Ok(Recovered::new_unchecked(f(self.inner)?, self.signer))
158    }
159
160    /// Returns the [`WithEncoded`] representation of [`Recovered`] with the given encoding.
161    pub fn into_encoded_with(self, encoding: impl Into<Bytes>) -> WithEncoded<Self> {
162        WithEncoded::new(encoding.into(), self)
163    }
164
165    /// Encodes the inner type and returns the [`WithEncoded`] representation of [`Recovered`].
166    pub fn into_encoded(self) -> WithEncoded<Self>
167    where
168        T: Encodable2718,
169    {
170        let mut out = alloc::vec![];
171        self.inner.encode_2718(&mut out);
172
173        self.into_encoded_with(out)
174    }
175}
176
177impl<T> Recovered<&T> {
178    /// Maps a `Recovered<&T>` to a `Recovered<T>` by cloning the transaction.
179    pub fn cloned(self) -> Recovered<T>
180    where
181        T: Clone,
182    {
183        let Self { inner, signer } = self;
184        Recovered::new_unchecked(inner.clone(), signer)
185    }
186
187    /// Helper function to explicitly create a new copy of `Recovered<&T>`
188    pub const fn copied(&self) -> Self {
189        *self
190    }
191}
192
193impl<T: Encodable> Encodable for Recovered<T> {
194    /// This encodes the transaction _with_ the signature, and an rlp header.
195    fn encode(&self, out: &mut dyn bytes::BufMut) {
196        self.inner.encode(out)
197    }
198
199    fn length(&self) -> usize {
200        self.inner.length()
201    }
202}
203
204impl<T: Decodable + SignerRecoverable> Decodable for Recovered<T> {
205    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
206        let tx = T::decode(buf)?;
207        let signer = tx.recover_signer().map_err(|_| {
208            alloy_rlp::Error::Custom("Unable to recover decoded transaction signer.")
209        })?;
210        Ok(Self::new_unchecked(tx, signer))
211    }
212}
213
214impl<T: Typed2718> Typed2718 for Recovered<T> {
215    fn ty(&self) -> u8 {
216        self.inner.ty()
217    }
218}
219
220impl<T: Encodable2718> Encodable2718 for Recovered<T> {
221    fn encode_2718_len(&self) -> usize {
222        self.inner.encode_2718_len()
223    }
224
225    fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
226        self.inner.encode_2718(out)
227    }
228
229    fn trie_hash(&self) -> B256 {
230        self.inner.trie_hash()
231    }
232}
233
234impl<T> AsRef<Self> for Recovered<T> {
235    fn as_ref(&self) -> &Self {
236        self
237    }
238}
239
240/// A type that can recover the signer of a transaction.
241///
242/// This is a helper trait that only provides the ability to recover the signer (address) of a
243/// transaction.
244pub trait SignerRecoverable {
245    /// Recover signer from signature and hash.
246    ///
247    /// Returns an error if the transaction's signature is invalid following [EIP-2](https://eips.ethereum.org/EIPS/eip-2).
248    ///
249    /// Note:
250    ///
251    /// This can fail for some early ethereum mainnet transactions pre EIP-2, use
252    /// [`Self::recover_signer_unchecked`] if you want to recover the signer without ensuring that
253    /// the signature has a low `s` value.
254    fn recover_signer(&self) -> Result<Address, RecoveryError>;
255
256    /// Recover signer from signature and hash _without ensuring that the signature has a low `s`
257    /// value_.
258    ///
259    /// Returns an error if the transaction's signature is invalid.
260    fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError>;
261
262    /// Same as [`SignerRecoverable::recover_signer_unchecked`] but receives a buffer to operate on
263    /// for encoding. This is useful during batch recovery of historical transactions to avoid
264    /// allocating a new buffer for each transaction.
265    ///
266    /// Caution: it is expected that implementations clear this buffer.
267    fn recover_unchecked_with_buf(
268        &self,
269        buf: &mut alloc::vec::Vec<u8>,
270    ) -> Result<Address, RecoveryError> {
271        let _ = buf;
272        self.recover_signer()
273    }
274
275    /// Recover the signer via [`SignerRecoverable::recover_signer`] and returns a
276    /// `Recovered<Self>`
277    fn try_into_recovered(self) -> Result<Recovered<Self>, RecoveryError>
278    where
279        Self: Sized,
280    {
281        let signer = self.recover_signer()?;
282        Ok(Recovered::new_unchecked(self, signer))
283    }
284
285    /// Recover the signer via [`SignerRecoverable::recover_signer_unchecked`] and returns a
286    /// `Recovered<&Self>`
287    fn try_into_recovered_unchecked(self) -> Result<Recovered<Self>, RecoveryError>
288    where
289        Self: Sized,
290    {
291        let signer = self.recover_signer_unchecked()?;
292        Ok(Recovered::new_unchecked(self, signer))
293    }
294
295    /// Recover the signer via [`SignerRecoverable::recover_signer`] and returns a
296    /// `Recovered<&Self>`
297    fn try_to_recovered_ref(&self) -> Result<Recovered<&Self>, RecoveryError> {
298        let signer = self.recover_signer()?;
299        Ok(Recovered::new_unchecked(self, signer))
300    }
301
302    /// Recover the signer via [`SignerRecoverable::recover_signer_unchecked`] and returns a
303    /// `Recovered<&Self>`
304    fn try_to_recovered_ref_unchecked(&self) -> Result<Recovered<&Self>, RecoveryError> {
305        let signer = self.recover_signer_unchecked()?;
306        Ok(Recovered::new_unchecked(self, signer))
307    }
308}
309
310impl<T> SignerRecoverable for WithEncoded<T>
311where
312    T: SignerRecoverable,
313{
314    fn recover_signer(&self) -> Result<Address, RecoveryError> {
315        self.1.recover_signer()
316    }
317
318    fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
319        self.1.recover_signer_unchecked()
320    }
321
322    fn recover_unchecked_with_buf(
323        &self,
324        buf: &mut alloc::vec::Vec<u8>,
325    ) -> Result<Address, RecoveryError> {
326        self.1.recover_unchecked_with_buf(buf)
327    }
328}
329
330impl<T> SignerRecoverable for Sealed<T>
331where
332    T: SignerRecoverable,
333{
334    fn recover_signer(&self) -> Result<Address, RecoveryError> {
335        self.inner().recover_signer()
336    }
337
338    fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
339        self.inner().recover_signer_unchecked()
340    }
341
342    fn recover_unchecked_with_buf(
343        &self,
344        buf: &mut alloc::vec::Vec<u8>,
345    ) -> Result<Address, RecoveryError> {
346        self.inner().recover_unchecked_with_buf(buf)
347    }
348}