mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
198 lines
7.1 KiB
Rust
198 lines
7.1 KiB
Rust
//! API of a signed transaction.
|
|
|
|
use crate::{
|
|
crypto::secp256k1::{recover_signer, recover_signer_unchecked},
|
|
FillTxEnv, InMemorySize, MaybeCompact, MaybeSerde,
|
|
};
|
|
use alloc::{fmt, vec::Vec};
|
|
use alloy_consensus::{
|
|
transaction::{PooledTransaction, Recovered},
|
|
SignableTransaction,
|
|
};
|
|
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
|
|
use alloy_primitives::{keccak256, Address, PrimitiveSignature as Signature, TxHash, B256};
|
|
use core::hash::Hash;
|
|
|
|
/// Helper trait that unifies all behaviour required by block to support full node operations.
|
|
pub trait FullSignedTx: SignedTransaction + FillTxEnv + MaybeCompact {}
|
|
|
|
impl<T> FullSignedTx for T where T: SignedTransaction + FillTxEnv + MaybeCompact {}
|
|
|
|
/// A signed transaction.
|
|
#[auto_impl::auto_impl(&, Arc)]
|
|
pub trait SignedTransaction:
|
|
Send
|
|
+ Sync
|
|
+ Unpin
|
|
+ Clone
|
|
+ fmt::Debug
|
|
+ PartialEq
|
|
+ Eq
|
|
+ Hash
|
|
+ alloy_rlp::Encodable
|
|
+ alloy_rlp::Decodable
|
|
+ Encodable2718
|
|
+ Decodable2718
|
|
+ alloy_consensus::Transaction
|
|
+ MaybeSerde
|
|
+ InMemorySize
|
|
{
|
|
/// Returns reference to transaction hash.
|
|
fn tx_hash(&self) -> &TxHash;
|
|
|
|
/// Returns reference to signature.
|
|
fn signature(&self) -> &Signature;
|
|
|
|
/// Returns whether this transaction type can be __broadcasted__ as full transaction over the
|
|
/// network.
|
|
///
|
|
/// Some transactions are not broadcastable as objects and only allowed to be broadcasted as
|
|
/// hashes, e.g. because they missing context (e.g. blob sidecar).
|
|
fn is_broadcastable_in_full(&self) -> bool {
|
|
// EIP-4844 transactions are not broadcastable in full, only hashes are allowed.
|
|
!self.is_eip4844()
|
|
}
|
|
|
|
/// Recover signer from signature and hash.
|
|
///
|
|
/// Returns `None` if the transaction's signature is invalid following [EIP-2](https://eips.ethereum.org/EIPS/eip-2), see also `reth_primitives::transaction::recover_signer`.
|
|
///
|
|
/// Note:
|
|
///
|
|
/// This can fail for some early ethereum mainnet transactions pre EIP-2, use
|
|
/// [`Self::recover_signer_unchecked`] if you want to recover the signer without ensuring that
|
|
/// the signature has a low `s` value.
|
|
fn recover_signer(&self) -> Option<Address>;
|
|
|
|
/// Recover signer from signature and hash _without ensuring that the signature has a low `s`
|
|
/// value_.
|
|
///
|
|
/// Returns `None` if the transaction's signature is invalid, see also
|
|
/// `reth_primitives::transaction::recover_signer_unchecked`.
|
|
fn recover_signer_unchecked(&self) -> Option<Address> {
|
|
self.recover_signer_unchecked_with_buf(&mut Vec::new())
|
|
}
|
|
|
|
/// Same as [`Self::recover_signer_unchecked`] but receives a buffer to operate on. This is used
|
|
/// during batch recovery to avoid allocating a new buffer for each transaction.
|
|
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address>;
|
|
|
|
/// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with
|
|
/// tx type.
|
|
fn recalculate_hash(&self) -> B256 {
|
|
keccak256(self.encoded_2718())
|
|
}
|
|
}
|
|
|
|
impl SignedTransaction for PooledTransaction {
|
|
fn tx_hash(&self) -> &TxHash {
|
|
match self {
|
|
Self::Legacy(tx) => tx.hash(),
|
|
Self::Eip2930(tx) => tx.hash(),
|
|
Self::Eip1559(tx) => tx.hash(),
|
|
Self::Eip7702(tx) => tx.hash(),
|
|
Self::Eip4844(tx) => tx.hash(),
|
|
}
|
|
}
|
|
|
|
fn signature(&self) -> &Signature {
|
|
match self {
|
|
Self::Legacy(tx) => tx.signature(),
|
|
Self::Eip2930(tx) => tx.signature(),
|
|
Self::Eip1559(tx) => tx.signature(),
|
|
Self::Eip7702(tx) => tx.signature(),
|
|
Self::Eip4844(tx) => tx.signature(),
|
|
}
|
|
}
|
|
|
|
fn recover_signer(&self) -> Option<Address> {
|
|
let signature_hash = self.signature_hash();
|
|
recover_signer(self.signature(), signature_hash)
|
|
}
|
|
|
|
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address> {
|
|
match self {
|
|
Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip1559(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip7702(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip4844(tx) => tx.tx().encode_for_signing(buf),
|
|
}
|
|
let signature_hash = keccak256(buf);
|
|
recover_signer_unchecked(self.signature(), signature_hash)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "op")]
|
|
impl SignedTransaction for op_alloy_consensus::OpPooledTransaction {
|
|
fn tx_hash(&self) -> &TxHash {
|
|
match self {
|
|
Self::Legacy(tx) => tx.hash(),
|
|
Self::Eip2930(tx) => tx.hash(),
|
|
Self::Eip1559(tx) => tx.hash(),
|
|
Self::Eip7702(tx) => tx.hash(),
|
|
}
|
|
}
|
|
|
|
fn signature(&self) -> &Signature {
|
|
match self {
|
|
Self::Legacy(tx) => tx.signature(),
|
|
Self::Eip2930(tx) => tx.signature(),
|
|
Self::Eip1559(tx) => tx.signature(),
|
|
Self::Eip7702(tx) => tx.signature(),
|
|
}
|
|
}
|
|
|
|
fn recover_signer(&self) -> Option<Address> {
|
|
let signature_hash = self.signature_hash();
|
|
recover_signer(self.signature(), signature_hash)
|
|
}
|
|
|
|
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address> {
|
|
match self {
|
|
Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip1559(tx) => tx.tx().encode_for_signing(buf),
|
|
Self::Eip7702(tx) => tx.tx().encode_for_signing(buf),
|
|
}
|
|
let signature_hash = keccak256(buf);
|
|
recover_signer_unchecked(self.signature(), signature_hash)
|
|
}
|
|
}
|
|
|
|
/// Extension trait for [`SignedTransaction`] to convert it into [`Recovered`].
|
|
pub trait SignedTransactionIntoRecoveredExt: SignedTransaction {
|
|
/// Tries to recover signer and return [`Recovered`] by cloning the type.
|
|
fn try_ecrecovered(&self) -> Option<Recovered<Self>> {
|
|
let signer = self.recover_signer()?;
|
|
Some(Recovered::new_unchecked(self.clone(), signer))
|
|
}
|
|
|
|
/// Tries to recover signer and return [`Recovered`].
|
|
///
|
|
/// Returns `Err(Self)` if the transaction's signature is invalid, see also
|
|
/// [`SignedTransaction::recover_signer`].
|
|
fn try_into_ecrecovered(self) -> Result<Recovered<Self>, Self> {
|
|
match self.recover_signer() {
|
|
None => Err(self),
|
|
Some(signer) => Ok(Recovered::new_unchecked(self, signer)),
|
|
}
|
|
}
|
|
|
|
/// Consumes the type, recover signer and return [`Recovered`] _without
|
|
/// ensuring that the signature has a low `s` value_ (EIP-2).
|
|
///
|
|
/// Returns `None` if the transaction's signature is invalid.
|
|
fn into_ecrecovered_unchecked(self) -> Option<Recovered<Self>> {
|
|
let signer = self.recover_signer_unchecked()?;
|
|
Some(Recovered::new_unchecked(self, signer))
|
|
}
|
|
|
|
/// Returns the [`Recovered`] transaction with the given sender.
|
|
fn with_signer(self, signer: Address) -> Recovered<Self> {
|
|
Recovered::new_unchecked(self, signer)
|
|
}
|
|
}
|
|
|
|
impl<T> SignedTransactionIntoRecoveredExt for T where T: SignedTransaction {}
|