alloy_consensus/transaction/
recovered.rs1use crate::crypto::RecoveryError;
2use alloy_eips::{
3 eip2718::{Encodable2718, WithEncoded},
4 Typed2718,
5};
6use alloy_primitives::{bytes, Address, Bytes, B256};
7use alloy_rlp::{Decodable, Encodable};
8use derive_more::{AsRef, Deref};
9
10#[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: Address,
17 #[deref]
19 #[as_ref]
20 #[cfg_attr(feature = "serde", serde(flatten))]
21 inner: T,
22}
23
24impl<T> Recovered<T> {
25 pub const fn signer(&self) -> Address {
27 self.signer
28 }
29
30 pub const fn signer_ref(&self) -> &Address {
32 &self.signer
33 }
34
35 pub const fn inner(&self) -> &T {
37 &self.inner
38 }
39
40 pub fn inner_mut(&mut self) -> &mut T {
42 &mut self.inner
43 }
44
45 pub fn into_inner(self) -> T {
47 self.inner
48 }
49
50 pub fn clone_inner(&self) -> T
52 where
53 T: Clone,
54 {
55 self.inner.clone()
56 }
57
58 #[doc(alias = "transaction")]
60 #[deprecated = "Use `inner` instead"]
61 pub const fn tx(&self) -> &T {
62 &self.inner
63 }
64
65 #[doc(alias = "into_transaction")]
67 #[deprecated = "Use `into_inner` instead"]
68 pub fn into_tx(self) -> T {
69 self.inner
70 }
71
72 #[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 #[doc(alias = "split")]
84 pub fn into_parts(self) -> (T, Address) {
85 (self.inner, self.signer)
86 }
87
88 pub const fn as_recovered_ref(&self) -> Recovered<&T> {
90 Recovered { inner: &self.inner, signer: self.signer() }
91 }
92
93 #[inline]
97 pub const fn new_unchecked(inner: T, signer: Address) -> Self {
98 Self { inner, signer }
99 }
100
101 pub fn convert<Tx>(self) -> Recovered<Tx>
103 where
104 Tx: From<T>,
105 {
106 self.map(Tx::from)
107 }
108
109 #[deprecated = "Use `convert_inner` 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 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 #[deprecated = "Use `try_convert_inner` 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 pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Recovered<Tx> {
137 Recovered::new_unchecked(f(self.inner), self.signer)
138 }
139
140 #[deprecated = "Use `map_inner` 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 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 #[deprecated = "Use `try_map_inner` 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 pub fn into_encoded_with(self, encoding: impl Into<Bytes>) -> WithEncoded<Self> {
162 WithEncoded::new(encoding.into(), self)
163 }
164
165 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 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
188impl<T: Encodable> Encodable for Recovered<T> {
189 fn encode(&self, out: &mut dyn bytes::BufMut) {
191 self.inner.encode(out)
192 }
193
194 fn length(&self) -> usize {
195 self.inner.length()
196 }
197}
198
199impl<T: Decodable + SignerRecoverable> Decodable for Recovered<T> {
200 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
201 let tx = T::decode(buf)?;
202 let signer = tx.recover_signer().map_err(|_| {
203 alloy_rlp::Error::Custom("Unable to recover decoded transaction signer.")
204 })?;
205 Ok(Self::new_unchecked(tx, signer))
206 }
207}
208
209impl<T: Typed2718> Typed2718 for Recovered<T> {
210 fn ty(&self) -> u8 {
211 self.inner.ty()
212 }
213}
214
215impl<T: Encodable2718> Encodable2718 for Recovered<T> {
216 fn encode_2718_len(&self) -> usize {
217 self.inner.encode_2718_len()
218 }
219
220 fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
221 self.inner.encode_2718(out)
222 }
223
224 fn trie_hash(&self) -> B256 {
225 self.inner.trie_hash()
226 }
227}
228
229pub trait SignerRecoverable {
234 fn recover_signer(&self) -> Result<Address, RecoveryError>;
244
245 fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError>;
250
251 fn try_into_recovered(self) -> Result<Recovered<Self>, RecoveryError>
254 where
255 Self: Sized,
256 {
257 let signer = self.recover_signer()?;
258 Ok(Recovered::new_unchecked(self, signer))
259 }
260
261 fn try_into_recovered_unchecked(self) -> Result<Recovered<Self>, RecoveryError>
264 where
265 Self: Sized,
266 {
267 let signer = self.recover_signer_unchecked()?;
268 Ok(Recovered::new_unchecked(self, signer))
269 }
270
271 fn try_to_recovered_ref(&self) -> Result<Recovered<&Self>, RecoveryError> {
274 let signer = self.recover_signer()?;
275 Ok(Recovered::new_unchecked(self, signer))
276 }
277
278 fn try_to_recovered_ref_unchecked(&self) -> Result<Recovered<&Self>, RecoveryError> {
281 let signer = self.recover_signer_unchecked()?;
282 Ok(Recovered::new_unchecked(self, signer))
283 }
284}
285
286impl<T> SignerRecoverable for WithEncoded<T>
287where
288 T: SignerRecoverable,
289{
290 fn recover_signer(&self) -> Result<Address, RecoveryError> {
291 self.1.recover_signer()
292 }
293
294 fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
295 self.1.recover_signer_unchecked()
296 }
297}