From 7349abd12639cebc7a5377a841376b0fd4781bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Wed, 25 Jun 2025 23:16:09 +0200 Subject: [PATCH] refactor(examples): Use `TransactionEnvelope` macro from `alloy` for `CustomTransaction` in the `custom-node` example (#17057) --- examples/custom-node/src/evm/env.rs | 36 ++--- examples/custom-node/src/evm/executor.rs | 12 +- examples/custom-node/src/pool.rs | 25 +++- examples/custom-node/src/primitives/tx.rs | 129 +++++++++++++++--- .../custom-node/src/primitives/tx_custom.rs | 12 +- .../custom-node/src/primitives/tx_type.rs | 40 ++---- 6 files changed, 171 insertions(+), 83 deletions(-) diff --git a/examples/custom-node/src/evm/env.rs b/examples/custom-node/src/evm/env.rs index 91e7293b92..dfe0fe93f9 100644 --- a/examples/custom-node/src/evm/env.rs +++ b/examples/custom-node/src/evm/env.rs @@ -257,28 +257,6 @@ impl TransactionEnv for CustomTxEnv { } } -impl FromRecoveredTx for PaymentTxEnv { - fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self { - PaymentTxEnv(match tx { - CustomTransaction::BuiltIn(tx) => { - OpTransaction::::from_recovered_tx(tx, sender).base - } - CustomTransaction::Other(tx) => TxEnv::from_recovered_tx(tx, sender), - }) - } -} - -impl FromTxWithEncoded for PaymentTxEnv { - fn from_encoded_tx(tx: &CustomTransaction, sender: Address, encoded: Bytes) -> Self { - PaymentTxEnv(match tx { - CustomTransaction::BuiltIn(tx) => { - OpTransaction::::from_encoded_tx(tx, sender, encoded).base - } - CustomTransaction::Other(tx) => TxEnv::from_encoded_tx(tx, sender, encoded), - }) - } -} - impl FromRecoveredTx for TxEnv { fn from_recovered_tx(tx: &TxPayment, caller: Address) -> Self { let TxPayment { @@ -317,6 +295,12 @@ impl FromTxWithEncoded for TxEnv { } } +impl FromTxWithEncoded for TxEnv { + fn from_encoded_tx(tx: &TxPayment, sender: Address, _encoded: Bytes) -> Self { + Self::from_recovered_tx(tx, sender) + } +} + impl FromRecoveredTx for CustomTxEnv { fn from_recovered_tx(tx: &OpTxEnvelope, sender: Address) -> Self { Self::Op(OpTransaction::from_recovered_tx(tx, sender)) @@ -332,8 +316,8 @@ impl FromTxWithEncoded for CustomTxEnv { impl FromRecoveredTx for CustomTxEnv { fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self { match tx { - CustomTransaction::BuiltIn(tx) => Self::from_recovered_tx(tx, sender), - CustomTransaction::Other(tx) => { + CustomTransaction::Op(tx) => Self::from_recovered_tx(tx, sender), + CustomTransaction::Payment(tx) => { Self::Payment(PaymentTxEnv(TxEnv::from_recovered_tx(tx, sender))) } } @@ -343,8 +327,8 @@ impl FromRecoveredTx for CustomTxEnv { impl FromTxWithEncoded for CustomTxEnv { fn from_encoded_tx(tx: &CustomTransaction, sender: Address, encoded: Bytes) -> Self { match tx { - CustomTransaction::BuiltIn(tx) => Self::from_encoded_tx(tx, sender, encoded), - CustomTransaction::Other(tx) => { + CustomTransaction::Op(tx) => Self::from_encoded_tx(tx, sender, encoded), + CustomTransaction::Payment(tx) => { Self::Payment(PaymentTxEnv(TxEnv::from_encoded_tx(tx, sender, encoded))) } } diff --git a/examples/custom-node/src/evm/executor.rs b/examples/custom-node/src/evm/executor.rs index 976c45ef52..2c5a58d758 100644 --- a/examples/custom-node/src/evm/executor.rs +++ b/examples/custom-node/src/evm/executor.rs @@ -43,13 +43,11 @@ where f: impl FnOnce(&ExecutionResult<::HaltReason>) -> CommitChanges, ) -> Result, BlockExecutionError> { match tx.tx() { - CustomTransaction::BuiltIn(op_tx) => { - self.inner.execute_transaction_with_commit_condition( - Recovered::new_unchecked(op_tx, *tx.signer()), - f, - ) - } - CustomTransaction::Other(..) => todo!(), + CustomTransaction::Op(op_tx) => self.inner.execute_transaction_with_commit_condition( + Recovered::new_unchecked(op_tx, *tx.signer()), + f, + ), + CustomTransaction::Payment(..) => todo!(), } } diff --git a/examples/custom-node/src/pool.rs b/examples/custom-node/src/pool.rs index 8fda09d712..09f0b667c7 100644 --- a/examples/custom-node/src/pool.rs +++ b/examples/custom-node/src/pool.rs @@ -1,5 +1,28 @@ -use crate::primitives::CustomTransactionEnvelope; +use crate::primitives::{CustomTransaction, CustomTransactionEnvelope}; +use alloy_consensus::error::ValueError; use op_alloy_consensus::OpPooledTransaction; use reth_ethereum::primitives::Extended; pub type CustomPooledTransaction = Extended; + +impl From for CustomTransaction { + fn from(tx: CustomPooledTransaction) -> Self { + match tx { + CustomPooledTransaction::BuiltIn(tx) => Self::Op(tx.into()), + CustomPooledTransaction::Other(tx) => Self::Payment(tx), + } + } +} + +impl TryFrom for CustomPooledTransaction { + type Error = ValueError; + + fn try_from(tx: CustomTransaction) -> Result { + match tx { + CustomTransaction::Op(op) => Ok(Self::BuiltIn( + OpPooledTransaction::try_from(op).map_err(|op| op.map(CustomTransaction::Op))?, + )), + CustomTransaction::Payment(payment) => Ok(Self::Other(payment)), + } + } +} diff --git a/examples/custom-node/src/primitives/tx.rs b/examples/custom-node/src/primitives/tx.rs index b96ffed76b..48348f6839 100644 --- a/examples/custom-node/src/primitives/tx.rs +++ b/examples/custom-node/src/primitives/tx.rs @@ -1,14 +1,17 @@ -use super::{TxPayment, TxTypeCustom}; +use super::TxPayment; use alloy_consensus::{ crypto::{ secp256k1::{recover_signer, recover_signer_unchecked}, RecoveryError, }, transaction::SignerRecoverable, - SignableTransaction, Signed, Transaction, + SignableTransaction, Signed, Transaction, TransactionEnvelope, }; -use alloy_eips::{eip2718::Eip2718Result, Decodable2718, Encodable2718, Typed2718}; -use alloy_primitives::{keccak256, Sealed, Signature, TxHash}; +use alloy_eips::{ + eip2718::{Eip2718Result, IsTyped2718}, + Decodable2718, Encodable2718, Typed2718, +}; +use alloy_primitives::{bytes::Buf, keccak256, Sealed, Signature, TxHash, B256}; use alloy_rlp::{BufMut, Decodable, Encodable, Result as RlpResult}; use op_alloy_consensus::{OpTxEnvelope, TxDeposit}; use reth_codecs::{ @@ -16,19 +19,21 @@ use reth_codecs::{ Compact, }; use reth_ethereum::primitives::{serde_bincode_compat::RlpBincode, InMemorySize}; -use reth_op::{ - primitives::{Extended, SignedTransaction}, - OpTransaction, -}; +use reth_op::{primitives::SignedTransaction, OpTransaction}; use revm_primitives::{Address, Bytes}; use serde::{Deserialize, Serialize}; -/// An [`OpTxEnvelope`] that is [`Extended`] by one more variant of [`CustomTransactionEnvelope`]. -pub type CustomTransaction = ExtendedOpTxEnvelope; - -/// A [`SignedTransaction`] implementation that combines the [`OpTxEnvelope`] and another -/// transaction type. -pub type ExtendedOpTxEnvelope = Extended; +/// Either [`OpTxEnvelope`] or [`CustomTransactionEnvelope`]. +#[derive(Debug, Clone, TransactionEnvelope)] +#[envelope(tx_type_name = TxTypeCustom)] +pub enum CustomTransaction { + /// A regular Optimism transaction as defined by [`OpTxEnvelope`]. + #[envelope(flatten)] + Op(OpTxEnvelope), + /// A [`TxPayment`] tagged with type 0x7E. + #[envelope(ty = 42)] + Payment(CustomTransactionEnvelope), +} #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] pub struct CustomTransactionEnvelope { @@ -98,7 +103,7 @@ impl Transaction for CustomTransactionEnvelope { self.inner.tx().access_list() } - fn blob_versioned_hashes(&self) -> Option<&[revm_primitives::B256]> { + fn blob_versioned_hashes(&self) -> Option<&[B256]> { self.inner.tx().blob_versioned_hashes() } @@ -199,6 +204,7 @@ impl ToTxCompact for CustomTransactionEnvelope { } impl RlpBincode for CustomTransactionEnvelope {} +impl RlpBincode for CustomTransaction {} impl reth_codecs::alloy::transaction::Envelope for CustomTransactionEnvelope { fn signature(&self) -> &Signature { @@ -206,14 +212,14 @@ impl reth_codecs::alloy::transaction::Envelope for CustomTransactionEnvelope { } fn tx_type(&self) -> Self::TxType { - TxTypeCustom::Custom + TxTypeCustom::Payment } } impl Compact for CustomTransactionEnvelope { fn to_compact(&self, buf: &mut B) -> usize where - B: alloy_rlp::bytes::BufMut + AsMut<[u8]>, + B: BufMut + AsMut<[u8]>, { self.inner.tx().to_compact(buf) } @@ -226,6 +232,31 @@ impl Compact for CustomTransactionEnvelope { } } +impl reth_codecs::Compact for CustomTransaction { + fn to_compact(&self, buf: &mut Buf) -> usize + where + Buf: BufMut + AsMut<[u8]>, + { + buf.put_u8(self.ty()); + match self { + Self::Op(tx) => tx.to_compact(buf), + Self::Payment(tx) => tx.to_compact(buf), + } + } + + fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { + let type_byte = buf.get_u8(); + + if ::is_type(type_byte) { + let (tx, remaining) = OpTxEnvelope::from_compact(buf, len); + return (Self::Op(tx), remaining); + } + + let (tx, remaining) = CustomTransactionEnvelope::from_compact(buf, len); + (Self::Payment(tx), remaining) + } +} + impl OpTransaction for CustomTransactionEnvelope { fn is_deposit(&self) -> bool { false @@ -235,3 +266,67 @@ impl OpTransaction for CustomTransactionEnvelope { None } } + +impl OpTransaction for CustomTransaction { + fn is_deposit(&self) -> bool { + match self { + CustomTransaction::Op(op) => op.is_deposit(), + CustomTransaction::Payment(payment) => payment.is_deposit(), + } + } + + fn as_deposit(&self) -> Option<&Sealed> { + match self { + CustomTransaction::Op(op) => op.as_deposit(), + CustomTransaction::Payment(payment) => payment.as_deposit(), + } + } +} + +impl SignerRecoverable for CustomTransaction { + fn recover_signer(&self) -> Result { + match self { + CustomTransaction::Op(tx) => SignerRecoverable::recover_signer(tx), + CustomTransaction::Payment(tx) => SignerRecoverable::recover_signer(tx), + } + } + + fn recover_signer_unchecked(&self) -> Result { + match self { + CustomTransaction::Op(tx) => SignerRecoverable::recover_signer_unchecked(tx), + CustomTransaction::Payment(tx) => SignerRecoverable::recover_signer_unchecked(tx), + } + } +} + +impl SignedTransaction for CustomTransaction { + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { + match self { + CustomTransaction::Op(tx) => { + SignedTransaction::recover_signer_unchecked_with_buf(tx, buf) + } + CustomTransaction::Payment(tx) => { + SignedTransaction::recover_signer_unchecked_with_buf(tx, buf) + } + } + } + + fn tx_hash(&self) -> &B256 { + match self { + CustomTransaction::Op(tx) => SignedTransaction::tx_hash(tx), + CustomTransaction::Payment(tx) => SignedTransaction::tx_hash(tx), + } + } +} + +impl InMemorySize for CustomTransaction { + fn size(&self) -> usize { + match self { + CustomTransaction::Op(tx) => InMemorySize::size(tx), + CustomTransaction::Payment(tx) => InMemorySize::size(tx), + } + } +} diff --git a/examples/custom-node/src/primitives/tx_custom.rs b/examples/custom-node/src/primitives/tx_custom.rs index 8ff92d2f62..8729378bd5 100644 --- a/examples/custom-node/src/primitives/tx_custom.rs +++ b/examples/custom-node/src/primitives/tx_custom.rs @@ -1,4 +1,4 @@ -use crate::primitives::{TxTypeCustom, TRANSFER_TX_TYPE_ID}; +use crate::primitives::PAYMENT_TX_TYPE_ID; use alloy_consensus::{ transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx}, SignableTransaction, Transaction, @@ -71,8 +71,8 @@ pub struct TxPayment { impl TxPayment { /// Get the transaction type #[doc(alias = "transaction_type")] - pub const fn tx_type() -> TxTypeCustom { - TxTypeCustom::Custom + pub const fn tx_type() -> super::tx::TxTypeCustom { + super::tx::TxTypeCustom::Payment } /// Calculates a heuristic for the in-memory size of the [TxPayment] @@ -115,7 +115,7 @@ impl RlpEcdsaEncodableTx for TxPayment { } impl RlpEcdsaDecodableTx for TxPayment { - const DEFAULT_TX_TYPE: u8 = { Self::tx_type() as u8 }; + const DEFAULT_TX_TYPE: u8 = { PAYMENT_TX_TYPE_ID }; /// Decodes the inner [TxPayment] fields from RLP bytes. /// @@ -244,7 +244,7 @@ impl Transaction for TxPayment { impl Typed2718 for TxPayment { fn ty(&self) -> u8 { - TRANSFER_TX_TYPE_ID + PAYMENT_TX_TYPE_ID } } @@ -254,7 +254,7 @@ impl SignableTransaction for TxPayment { } fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { - out.put_u8(Self::tx_type() as u8); + out.put_u8(Self::tx_type().ty()); self.encode(out) } diff --git a/examples/custom-node/src/primitives/tx_type.rs b/examples/custom-node/src/primitives/tx_type.rs index 3616002479..20b9e4be4c 100644 --- a/examples/custom-node/src/primitives/tx_type.rs +++ b/examples/custom-node/src/primitives/tx_type.rs @@ -1,21 +1,8 @@ +use crate::primitives::TxTypeCustom; use alloy_primitives::bytes::{Buf, BufMut}; use reth_codecs::{txtype::COMPACT_EXTENDED_IDENTIFIER_FLAG, Compact}; -use serde::{Deserialize, Serialize}; -pub const TRANSFER_TX_TYPE_ID: u8 = 42; - -/// An enum for the custom transaction type(s) -#[repr(u8)] -#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] -pub enum TxTypeCustom { - Custom = TRANSFER_TX_TYPE_ID, -} - -impl From for u8 { - fn from(value: TxTypeCustom) -> Self { - value as Self - } -} +pub const PAYMENT_TX_TYPE_ID: u8 = 42; impl Compact for TxTypeCustom { fn to_compact(&self, buf: &mut B) -> usize @@ -23,26 +10,27 @@ impl Compact for TxTypeCustom { B: BufMut + AsMut<[u8]>, { match self { - Self::Custom => { - buf.put_u8(TRANSFER_TX_TYPE_ID); + Self::Op(ty) => ty.to_compact(buf), + Self::Payment => { + buf.put_u8(PAYMENT_TX_TYPE_ID); COMPACT_EXTENDED_IDENTIFIER_FLAG } } } fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { - ( - match identifier { - COMPACT_EXTENDED_IDENTIFIER_FLAG => { + match identifier { + COMPACT_EXTENDED_IDENTIFIER_FLAG => ( + { let extended_identifier = buf.get_u8(); match extended_identifier { - TRANSFER_TX_TYPE_ID => Self::Custom, + PAYMENT_TX_TYPE_ID => Self::Payment, _ => panic!("Unsupported TxType identifier: {extended_identifier}"), } - } - _ => panic!("Unknown identifier for TxType: {identifier}"), - }, - buf, - ) + }, + buf, + ), + v => Self::from_compact(buf, v), + } } }