reth_primitives_traits/transaction/
signed.rs1use crate::{
4 crypto::secp256k1::recover_signer_unchecked, InMemorySize, MaybeCompact, MaybeSerde,
5 MaybeSerdeBincodeCompat,
6};
7use alloc::{fmt, vec::Vec};
8use alloy_consensus::{
9 transaction::{Recovered, RlpEcdsaEncodableTx, SignerRecoverable},
10 EthereumTxEnvelope, SignableTransaction,
11};
12use alloy_eips::eip2718::{Decodable2718, Encodable2718};
13use alloy_primitives::{keccak256, Address, Signature, TxHash, B256};
14use alloy_rlp::{Decodable, Encodable};
15use core::hash::Hash;
16
17pub use alloy_consensus::crypto::RecoveryError;
18
19pub trait FullSignedTx: SignedTransaction + MaybeCompact + MaybeSerdeBincodeCompat {}
21impl<T> FullSignedTx for T where T: SignedTransaction + MaybeCompact + MaybeSerdeBincodeCompat {}
22
23#[auto_impl::auto_impl(&, Arc)]
25pub trait SignedTransaction:
26 Send
27 + Sync
28 + Unpin
29 + Clone
30 + fmt::Debug
31 + PartialEq
32 + Eq
33 + Hash
34 + Encodable
35 + Decodable
36 + Encodable2718
37 + Decodable2718
38 + alloy_consensus::Transaction
39 + MaybeSerde
40 + InMemorySize
41 + SignerRecoverable
42{
43 fn tx_hash(&self) -> &TxHash;
45
46 fn is_broadcastable_in_full(&self) -> bool {
52 !self.is_eip4844()
54 }
55
56 fn try_recover(&self) -> Result<Address, RecoveryError> {
60 self.recover_signer()
61 }
62
63 fn try_recover_unchecked(&self) -> Result<Address, RecoveryError> {
68 self.recover_signer_unchecked()
69 }
70
71 fn recover_signer_unchecked_with_buf(
75 &self,
76 buf: &mut Vec<u8>,
77 ) -> Result<Address, RecoveryError>;
78
79 fn recalculate_hash(&self) -> B256 {
82 keccak256(self.encoded_2718())
83 }
84
85 #[auto_impl(keep_default_for(&, Arc))]
87 fn try_clone_into_recovered(&self) -> Result<Recovered<Self>, RecoveryError> {
88 self.recover_signer().map(|signer| Recovered::new_unchecked(self.clone(), signer))
89 }
90
91 #[auto_impl(keep_default_for(&, Arc))]
96 fn try_into_recovered(self) -> Result<Recovered<Self>, Self> {
97 match self.recover_signer() {
98 Ok(signer) => Ok(Recovered::new_unchecked(self, signer)),
99 Err(_) => Err(self),
100 }
101 }
102
103 #[deprecated(note = "Use try_into_recovered_unchecked instead")]
108 #[auto_impl(keep_default_for(&, Arc))]
109 fn into_recovered_unchecked(self) -> Result<Recovered<Self>, RecoveryError> {
110 self.recover_signer_unchecked().map(|signer| Recovered::new_unchecked(self, signer))
111 }
112
113 #[auto_impl(keep_default_for(&, Arc))]
117 fn with_signer(self, signer: Address) -> Recovered<Self> {
118 Recovered::new_unchecked(self, signer)
119 }
120
121 #[auto_impl(keep_default_for(&, Arc))]
125 fn with_signer_ref(&self, signer: Address) -> Recovered<&Self> {
126 Recovered::new_unchecked(self, signer)
127 }
128}
129
130impl<T> SignedTransaction for EthereumTxEnvelope<T>
131where
132 T: RlpEcdsaEncodableTx + SignableTransaction<Signature> + Unpin,
133 Self: Clone + PartialEq + Eq + Decodable + Decodable2718 + MaybeSerde + InMemorySize,
134{
135 fn tx_hash(&self) -> &TxHash {
136 match self {
137 Self::Legacy(tx) => tx.hash(),
138 Self::Eip2930(tx) => tx.hash(),
139 Self::Eip1559(tx) => tx.hash(),
140 Self::Eip7702(tx) => tx.hash(),
141 Self::Eip4844(tx) => tx.hash(),
142 }
143 }
144
145 fn recover_signer_unchecked_with_buf(
146 &self,
147 buf: &mut Vec<u8>,
148 ) -> Result<Address, RecoveryError> {
149 match self {
150 Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
151 Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
152 Self::Eip1559(tx) => tx.tx().encode_for_signing(buf),
153 Self::Eip7702(tx) => tx.tx().encode_for_signing(buf),
154 Self::Eip4844(tx) => tx.tx().encode_for_signing(buf),
155 }
156 let signature_hash = keccak256(buf);
157 recover_signer_unchecked(self.signature(), signature_hash)
158 }
159}
160
161impl SignedTransaction for seismic_alloy_consensus::SeismicTxEnvelope {
162 fn tx_hash(&self) -> &TxHash {
163 match self {
164 Self::Legacy(tx) => tx.hash(),
165 Self::Eip2930(tx) => tx.hash(),
166 Self::Eip1559(tx) => tx.hash(),
167 Self::Eip4844(tx) => tx.hash(),
168 Self::Eip7702(tx) => tx.hash(),
169 Self::Seismic(tx) => tx.hash(),
170 }
171 }
172
173 fn recover_signer_unchecked_with_buf(
174 &self,
175 buf: &mut Vec<u8>,
176 ) -> Result<Address, RecoveryError> {
177 match self {
178 Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
179 Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
180 Self::Eip1559(tx) => tx.tx().encode_for_signing(buf),
181 Self::Eip4844(tx) => tx.tx().encode_for_signing(buf),
182 Self::Eip7702(tx) => tx.tx().encode_for_signing(buf),
183 Self::Seismic(tx) => tx.tx().encode_for_signing(buf),
184 }
185 let signature_hash = keccak256(buf);
186 recover_signer_unchecked(self.signature(), signature_hash)
187 }
188}
189
190#[cfg(feature = "op")]
191mod op {
192 use super::*;
193 use op_alloy_consensus::{OpPooledTransaction, OpTxEnvelope};
194
195 impl SignedTransaction for OpPooledTransaction {
196 fn tx_hash(&self) -> &TxHash {
197 match self {
198 Self::Legacy(tx) => tx.hash(),
199 Self::Eip2930(tx) => tx.hash(),
200 Self::Eip1559(tx) => tx.hash(),
201 Self::Eip7702(tx) => tx.hash(),
202 }
203 }
204
205 fn recover_signer_unchecked_with_buf(
206 &self,
207 buf: &mut Vec<u8>,
208 ) -> Result<Address, RecoveryError> {
209 match self {
210 Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
211 Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
212 Self::Eip1559(tx) => tx.tx().encode_for_signing(buf),
213 Self::Eip7702(tx) => tx.tx().encode_for_signing(buf),
214 }
215 let signature_hash = keccak256(buf);
216 recover_signer_unchecked(self.signature(), signature_hash)
217 }
218 }
219
220 impl SignedTransaction for OpTxEnvelope {
221 fn tx_hash(&self) -> &TxHash {
222 match self {
223 Self::Legacy(tx) => tx.hash(),
224 Self::Eip2930(tx) => tx.hash(),
225 Self::Eip1559(tx) => tx.hash(),
226 Self::Eip7702(tx) => tx.hash(),
227 Self::Deposit(tx) => tx.hash_ref(),
228 }
229 }
230
231 fn recover_signer_unchecked_with_buf(
232 &self,
233 buf: &mut Vec<u8>,
234 ) -> Result<Address, RecoveryError> {
235 match self {
236 Self::Deposit(tx) => return Ok(tx.from),
237 Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
238 Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
239 Self::Eip1559(tx) => tx.tx().encode_for_signing(buf),
240 Self::Eip7702(tx) => tx.tx().encode_for_signing(buf),
241 }
242 let signature_hash = keccak256(buf);
243 let signature = match self {
244 Self::Legacy(tx) => tx.signature(),
245 Self::Eip2930(tx) => tx.signature(),
246 Self::Eip1559(tx) => tx.signature(),
247 Self::Eip7702(tx) => tx.signature(),
248 Self::Deposit(_) => unreachable!("Deposit transactions should not be handled here"),
249 };
250 recover_signer_unchecked(signature, signature_hash)
251 }
252 }
253}