alloy_consensus/transaction/
recovered.rs

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