Skip to content

Commit

Permalink
feat: integrate Recovered into more types (#2151)
Browse files Browse the repository at this point in the history
* feat: integrate Recovered into more types

* fix

* add helper TxEnvelope

* fix
  • Loading branch information
klkvr authored Mar 6, 2025
1 parent fd39777 commit 9959e7a
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 46 deletions.
8 changes: 8 additions & 0 deletions crates/consensus/src/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ impl<T: SignableTransaction<Signature>> Signed<T, Signature> {
let sighash = self.tx.signature_hash();
self.signature.recover_address_from_prehash(&sighash)
}

/// Attempts to recover signer and constructs a [`crate::transaction::Recovered`] object.
pub fn try_into_recovered(
self,
) -> Result<crate::transaction::Recovered<T>, alloy_primitives::SignatureError> {
let signer = self.recover_signer()?;
Ok(crate::transaction::Recovered::new_unchecked(self.tx, signer))
}
}

#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
Expand Down
9 changes: 9 additions & 0 deletions crates/consensus/src/transaction/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,15 @@ impl TxEnvelope {
}
}

/// Recover the signer of the transaction.
#[cfg(feature = "k256")]
pub fn try_into_recovered(
self,
) -> Result<crate::transaction::Recovered<Self>, alloy_primitives::SignatureError> {
let signer = self.recover_signer()?;
Ok(crate::transaction::Recovered::new_unchecked(self, signer))
}

/// Calculate the signing hash for the transaction.
pub fn signature_hash(&self) -> B256 {
match self {
Expand Down
1 change: 1 addition & 0 deletions crates/consensus/src/transaction/recovered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use derive_more::{AsRef, Deref};

/// Signed transaction with recovered signer.
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, AsRef, Deref)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Recovered<T> {
/// Signer of the transaction
signer: Address,
Expand Down
2 changes: 1 addition & 1 deletion crates/network/src/any/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ impl TransactionResponse for AnyRpcTransaction {
}

fn from(&self) -> alloy_primitives::Address {
self.inner.from
self.inner.from()
}

fn gas_price(&self) -> Option<u128> {
Expand Down
2 changes: 1 addition & 1 deletion crates/network/src/any/unknowns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ mod tests {

let tx: AnyRpcTransaction = serde_json::from_str(input).unwrap();

let AnyTxEnvelope::Unknown(inner) = tx.inner.inner.clone() else {
let AnyTxEnvelope::Unknown(inner) = tx.inner.inner.tx().clone() else {
panic!("expected unknown envelope");
};

Expand Down
85 changes: 41 additions & 44 deletions crates/rpc-types-eth/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use request::{TransactionInput, TransactionRequest};
///
/// This represents a transaction in RPC format (`eth_getTransactionByHash`) and contains the full
/// transaction object and additional block metadata if the transaction has been mined.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))]
#[cfg_attr(
Expand All @@ -46,7 +46,7 @@ pub use request::{TransactionInput, TransactionRequest};
#[doc(alias = "Tx")]
pub struct Transaction<T = TxEnvelope> {
/// The inner transaction object
pub inner: T,
pub inner: Recovered<T>,

/// Hash of block where transaction was included, `None` if pending
pub block_hash: Option<BlockHash>,
Expand All @@ -59,25 +59,37 @@ pub struct Transaction<T = TxEnvelope> {

/// Deprecated effective gas price value.
pub effective_gas_price: Option<u128>,
}

/// Sender
pub from: Address,
impl<T> Default for Transaction<T>
where
T: Default,
{
fn default() -> Self {
Self {
inner: Recovered::new_unchecked(Default::default(), Default::default()),
block_hash: Default::default(),
block_number: Default::default(),
transaction_index: Default::default(),
effective_gas_price: Default::default(),
}
}
}

impl<T> Transaction<T> {
/// Consumes the type and returns the wrapped transaction.
pub fn into_inner(self) -> T {
self.inner
self.inner.into_tx()
}

/// Consumes the type and returns a [`Recovered`] transaction with the sender
pub fn into_recovered(self) -> Recovered<T> {
Recovered::new_unchecked(self.inner, self.from)
self.inner
}

/// Returns a `Recovered<&T>` with the transaction and the sender.
pub fn as_recovered(&self) -> Recovered<&T> {
Recovered::new_unchecked(&self.inner, self.from)
self.inner.as_recovered_ref()
}

/// Converts the transaction type to the given alternative that is `From<T>`
Expand All @@ -100,29 +112,25 @@ impl<T> Transaction<T> {

/// Applies the given closure to the inner transaction type.
pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Transaction<Tx> {
let Self { inner, block_hash, block_number, transaction_index, effective_gas_price, from } =
self;
let Self { inner, block_hash, block_number, transaction_index, effective_gas_price } = self;
Transaction {
inner: f(inner),
inner: inner.map_transaction(f),
block_hash,
block_number,
transaction_index,
effective_gas_price,
from,
}
}

/// Applies the given fallible closure to the inner transactions.
pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Transaction<Tx>, E> {
let Self { inner, block_hash, block_number, transaction_index, effective_gas_price, from } =
self;
let Self { inner, block_hash, block_number, transaction_index, effective_gas_price } = self;
Ok(Transaction {
inner: f(inner)?,
inner: inner.try_map_transaction(f)?,
block_hash,
block_number,
transaction_index,
effective_gas_price,
from,
})
}
}
Expand Down Expand Up @@ -172,15 +180,15 @@ where
/// During this conversion data for [TransactionRequest::sidecar] is not
/// populated as it is not part of [Transaction].
pub fn into_request(self) -> TransactionRequest {
self.inner.into()
self.inner.into_tx().into()
}
}

impl Transaction {
/// Consumes the transaction and returns it as [`Signed`] with [`TypedTransaction`] as the
/// transaction type.
pub fn into_signed(self) -> Signed<TypedTransaction> {
self.inner.into_signed()
self.inner.into_tx().into_signed()
}

/// Consumes the transaction and returns it a [`Recovered`] signed [`TypedTransaction`].
Expand Down Expand Up @@ -208,11 +216,9 @@ impl TryFrom<Transaction> for Signed<TxLegacy> {
type Error = ConversionError;

fn try_from(tx: Transaction) -> Result<Self, Self::Error> {
match tx.inner {
match tx.inner.into_tx() {
TxEnvelope::Legacy(tx) => Ok(tx),
_ => {
Err(ConversionError::Custom(format!("expected Legacy, got {}", tx.inner.tx_type())))
}
tx => Err(ConversionError::Custom(format!("expected Legacy, got {}", tx.tx_type()))),
}
}
}
Expand All @@ -221,12 +227,9 @@ impl TryFrom<Transaction> for Signed<TxEip1559> {
type Error = ConversionError;

fn try_from(tx: Transaction) -> Result<Self, Self::Error> {
match tx.inner {
match tx.inner.into_tx() {
TxEnvelope::Eip1559(tx) => Ok(tx),
_ => Err(ConversionError::Custom(format!(
"expected Eip1559, got {}",
tx.inner.tx_type()
))),
tx => Err(ConversionError::Custom(format!("expected Eip1559, got {}", tx.tx_type()))),
}
}
}
Expand All @@ -235,12 +238,9 @@ impl TryFrom<Transaction> for Signed<TxEip2930> {
type Error = ConversionError;

fn try_from(tx: Transaction) -> Result<Self, Self::Error> {
match tx.inner {
match tx.inner.into_tx() {
TxEnvelope::Eip2930(tx) => Ok(tx),
_ => Err(ConversionError::Custom(format!(
"expected Eip2930, got {}",
tx.inner.tx_type()
))),
tx => Err(ConversionError::Custom(format!("expected Eip2930, got {}", tx.tx_type()))),
}
}
}
Expand All @@ -261,11 +261,11 @@ impl TryFrom<Transaction> for Signed<TxEip4844Variant> {
type Error = ConversionError;

fn try_from(tx: Transaction) -> Result<Self, Self::Error> {
match tx.inner {
match tx.inner.into_tx() {
TxEnvelope::Eip4844(tx) => Ok(tx),
_ => Err(ConversionError::Custom(format!(
tx => Err(ConversionError::Custom(format!(
"expected TxEip4844Variant, got {}",
tx.inner.tx_type()
tx.tx_type()
))),
}
}
Expand All @@ -275,19 +275,16 @@ impl TryFrom<Transaction> for Signed<TxEip7702> {
type Error = ConversionError;

fn try_from(tx: Transaction) -> Result<Self, Self::Error> {
match tx.inner {
match tx.inner.into_tx() {
TxEnvelope::Eip7702(tx) => Ok(tx),
_ => Err(ConversionError::Custom(format!(
"expected Eip7702, got {}",
tx.inner.tx_type()
))),
tx => Err(ConversionError::Custom(format!("expected Eip7702, got {}", tx.tx_type()))),
}
}
}

impl From<Transaction> for TxEnvelope {
fn from(tx: Transaction) -> Self {
tx.inner
tx.inner.into_tx()
}
}

Expand Down Expand Up @@ -385,7 +382,7 @@ impl<T: TransactionTrait + Encodable2718> TransactionResponse for Transaction<T>
}

fn from(&self) -> Address {
self.from
self.inner.signer()
}
}

Expand Down Expand Up @@ -443,9 +440,10 @@ mod tx_serde {
block_number,
transaction_index,
effective_gas_price,
from,
} = value;

let (inner, from) = inner.into_parts();

// if inner transaction has its own `gasPrice` don't serialize it in this struct.
let effective_gas_price = effective_gas_price.filter(|_| inner.gas_price().is_none());

Expand Down Expand Up @@ -478,11 +476,10 @@ mod tx_serde {
let effective_gas_price = inner.gas_price().or(gas_price.effective_gas_price);

Ok(Self {
inner,
inner: Recovered::new_unchecked(inner, from),
block_hash,
block_number,
transaction_index,
from,
effective_gas_price,
})
}
Expand Down

0 comments on commit 9959e7a

Please sign in to comment.