diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index f932ae1fb..6455b728e 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -100,7 +100,7 @@ impl> Command { provider .block(best_number.into())? .expect("the header for the latest block is missing, database is corrupt") - .seal(best_hash), + .seal_unchecked(best_hash), )) } @@ -166,7 +166,7 @@ impl> Command { for tx_bytes in &self.transactions { debug!(target: "reth::cli", bytes = ?tx_bytes, "Decoding transaction"); let transaction = TransactionSigned::decode(&mut &Bytes::from_str(tx_bytes)?[..])? - .try_ecrecovered() + .try_clone_into_recovered() .map_err(|e| eyre::eyre!("failed to recover tx: {e}"))?; let encoded_length = match &transaction.transaction { diff --git a/crates/engine/util/src/reorg.rs b/crates/engine/util/src/reorg.rs index 90c69bfcd..b2a232f6d 100644 --- a/crates/engine/util/src/reorg.rs +++ b/crates/engine/util/src/reorg.rs @@ -322,7 +322,7 @@ where } // Configure the environment for the block. - let tx_recovered = tx.clone().try_into_ecrecovered().map_err(|_| { + let tx_recovered = tx.try_clone_into_recovered().map_err(|_| { BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError) })?; let tx_env = evm_config.tx_env(&tx_recovered, tx_recovered.signer()); diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index b290a10e2..e162c8557 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -998,7 +998,7 @@ mod tests { let BlockExecutionOutput { receipts, requests, .. } = executor .execute( &Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } } - .with_recovered_senders() + .try_into_recovered() .unwrap(), ) .unwrap(); @@ -1074,7 +1074,7 @@ mod tests { // Execute the block and capture the result let exec_result = executor.execute( &Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } } - .with_recovered_senders() + .try_into_recovered() .unwrap(), ); diff --git a/crates/exex/exex/src/backfill/test_utils.rs b/crates/exex/exex/src/backfill/test_utils.rs index f64a09ab7..19527fdd6 100644 --- a/crates/exex/exex/src/backfill/test_utils.rs +++ b/crates/exex/exex/src/backfill/test_utils.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use alloy_consensus::{constants::ETH_TO_WEI, BlockHeader, Header, TxEip2930}; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{b256, Address, TxKind, U256}; -use eyre::OptionExt; use reth_chainspec::{ChainSpec, ChainSpecBuilder, EthereumHardfork, MAINNET, MIN_TRANSACTION_GAS}; use reth_evm::execute::{BatchExecutor, BlockExecutionOutput, BlockExecutorProvider, Executor}; use reth_evm_ethereum::execute::EthExecutorProvider; @@ -121,8 +120,7 @@ fn blocks( ..Default::default() }, } - .with_recovered_senders() - .ok_or_eyre("failed to recover senders")?; + .try_into_recovered()?; // Second block resends the same transaction with increased nonce let block2 = Block { @@ -153,8 +151,7 @@ fn blocks( ..Default::default() }, } - .with_recovered_senders() - .ok_or_eyre("failed to recover senders")?; + .try_into_recovered()?; Ok((block1, block2)) } diff --git a/crates/net/network/src/transactions/mod.rs b/crates/net/network/src/transactions/mod.rs index a1c6ceb5d..0be1f1746 100644 --- a/crates/net/network/src/transactions/mod.rs +++ b/crates/net/network/src/transactions/mod.rs @@ -1217,7 +1217,7 @@ where let mut new_txs = Vec::with_capacity(transactions.len()); for tx in transactions { // recover transaction - let tx = match tx.try_into_ecrecovered() { + let tx = match tx.try_into_recovered() { Ok(tx) => tx, Err(badtx) => { trace!(target: "net::tx", diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 76c6b5528..2d9fd4d81 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -789,10 +789,9 @@ where // purely for the purposes of utilizing the `evm_config.tx_env`` function. // Deposit transactions do not have signatures, so if the tx is a deposit, this // will just pull in its `from` address. - let sequencer_tx = - sequencer_tx.value().clone().try_into_ecrecovered().map_err(|_| { - PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed) - })?; + let sequencer_tx = sequencer_tx.value().try_clone_into_recovered().map_err(|_| { + PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed) + })?; // Cache the depositor account prior to the state transition for the deposit nonce. // diff --git a/crates/primitives-traits/src/block/mod.rs b/crates/primitives-traits/src/block/mod.rs index 67e197e15..464d5080f 100644 --- a/crates/primitives-traits/src/block/mod.rs +++ b/crates/primitives-traits/src/block/mod.rs @@ -16,8 +16,8 @@ use alloy_primitives::{Address, B256}; use alloy_rlp::{Decodable, Encodable}; use crate::{ - transaction::signed::RecoveryError, BlockBody, BlockHeader, FullBlockBody, FullBlockHeader, - InMemorySize, MaybeSerde, SealedHeader, SignedTransaction, + block::error::BlockRecoveryError, transaction::signed::RecoveryError, BlockBody, BlockHeader, + FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde, SealedHeader, SignedTransaction, }; /// Bincode-compatible header type serde implementations. @@ -81,7 +81,7 @@ pub trait Block: /// Seal the block with a known hash. /// /// WARNING: This method does not perform validation whether the hash is correct. - fn seal(self, hash: B256) -> SealedBlock { + fn seal_unchecked(self, hash: B256) -> SealedBlock { SealedBlock::new_unchecked(self, hash) } @@ -121,37 +121,23 @@ pub trait Block: } /// Expensive operation that recovers transaction signer. - fn senders(&self) -> Result, RecoveryError> + fn recover_signers(&self) -> Result, RecoveryError> where ::Transaction: SignedTransaction, { self.body().recover_signers() } - /// Transform into a [`RecoveredBlock`]. - /// - /// # Panics - /// - /// If the number of senders does not match the number of transactions in the block - /// and the signer recovery for one of the transactions fails. - /// - /// Note: this is expected to be called with blocks read from disk. - #[track_caller] - fn with_senders_unchecked(self, senders: Vec
) -> RecoveredBlock - where - ::Transaction: SignedTransaction, - { - self.try_with_senders_unchecked(senders).expect("stored block is valid") - } - - /// Transform into a [`RecoveredBlock`] using the given senders. + /// Transform the block into a [`RecoveredBlock`] using the given senders. /// /// If the number of senders does not match the number of transactions in the block, this falls /// back to manually recovery, but _without ensuring that the signature has a low `s` value_. /// - /// Returns an error if a signature is invalid. - #[track_caller] - fn try_with_senders_unchecked(self, senders: Vec
) -> Result, Self> + /// Returns the block as error if a signature is invalid. + fn try_into_recovered_unchecked( + self, + senders: Vec
, + ) -> Result, BlockRecoveryError> where ::Transaction: SignedTransaction, { @@ -159,22 +145,36 @@ pub trait Block: senders } else { // Fall back to recovery if lengths don't match - let Ok(senders) = self.body().recover_signers_unchecked() else { return Err(self) }; + let Ok(senders) = self.body().recover_signers_unchecked() else { + return Err(BlockRecoveryError::new(self)) + }; senders }; Ok(RecoveredBlock::new_unhashed(self, senders)) } - /// **Expensive**. Transform into a [`RecoveredBlock`] by recovering senders in the contained - /// transactions. + /// Transform the block into a [`RecoveredBlock`] using the given signers. /// - /// Returns `None` if a transaction is invalid. - fn with_recovered_senders(self) -> Option> + /// Note: This method assumes the signers are correct and does not validate them. + fn into_recovered_with_signers(self, signers: Vec
) -> RecoveredBlock where ::Transaction: SignedTransaction, { - let senders = self.body().recover_signers().ok()?; - Some(RecoveredBlock::new_unhashed(self, senders)) + RecoveredBlock::new_unhashed(self, signers) + } + + /// **Expensive**. Transform into a [`RecoveredBlock`] by recovering senders in the contained + /// transactions. + /// + /// Returns the block as error if a signature is invalid. + fn try_into_recovered(self) -> Result, BlockRecoveryError> + where + ::Transaction: SignedTransaction, + { + let Ok(signers) = self.body().recover_signers() else { + return Err(BlockRecoveryError::new(self)) + }; + Ok(RecoveredBlock::new_unhashed(self, signers)) } } diff --git a/crates/primitives-traits/src/transaction/signed.rs b/crates/primitives-traits/src/transaction/signed.rs index 1370d9d59..272951de3 100644 --- a/crates/primitives-traits/src/transaction/signed.rs +++ b/crates/primitives-traits/src/transaction/signed.rs @@ -187,7 +187,7 @@ impl SignedTransaction for op_alloy_consensus::OpPooledTransaction { /// 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) -> Result, RecoveryError> { + fn try_clone_into_recovered(&self) -> Result, RecoveryError> { self.recover_signer().map(|signer| Recovered::new_unchecked(self.clone(), signer)) } @@ -195,7 +195,7 @@ pub trait SignedTransactionIntoRecoveredExt: SignedTransaction { /// /// Returns `Err(Self)` if the transaction's signature is invalid, see also /// [`SignedTransaction::recover_signer`]. - fn try_into_ecrecovered(self) -> Result, Self> { + fn try_into_recovered(self) -> Result, Self> { match self.recover_signer() { Ok(signer) => Ok(Recovered::new_unchecked(self, signer)), Err(_) => Err(self), @@ -206,11 +206,13 @@ pub trait SignedTransactionIntoRecoveredExt: SignedTransaction { /// 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) -> Result, RecoveryError> { + fn into_recovered_unchecked(self) -> Result, RecoveryError> { self.recover_signer_unchecked().map(|signer| Recovered::new_unchecked(self, signer)) } /// Returns the [`Recovered`] transaction with the given sender. + /// + /// Note: assumes the given signer is the signer of this transaction. fn with_signer(self, signer: Address) -> Recovered { Recovered::new_unchecked(self, signer) } diff --git a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs index ff69fbde1..5c855cb8f 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs @@ -485,7 +485,7 @@ pub trait LoadTransaction: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt { // part of pending block) and already. We don't need to // check for pre EIP-2 because this transaction could be pre-EIP-2. let transaction = tx - .into_ecrecovered_unchecked() + .into_recovered_unchecked() .map_err(|_| EthApiError::InvalidTransactionSignature)?; let tx = TransactionSource::Block { diff --git a/crates/rpc/rpc-eth-types/src/utils.rs b/crates/rpc/rpc-eth-types/src/utils.rs index 95cb2d72d..5d498ffaf 100644 --- a/crates/rpc/rpc-eth-types/src/utils.rs +++ b/crates/rpc/rpc-eth-types/src/utils.rs @@ -19,7 +19,7 @@ pub fn recover_raw_transaction(mut data: &[u8]) -> EthResu let transaction = T::decode_2718(&mut data).map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?; - transaction.try_into_ecrecovered().or(Err(EthApiError::InvalidTransactionSignature)) + transaction.try_into_recovered().or(Err(EthApiError::InvalidTransactionSignature)) } /// Performs a binary search within a given block range to find the desired block number. diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 90c473a20..fa0da3dec 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -180,7 +180,7 @@ where .collect() }; - self.trace_block(Arc::new(block.with_senders_unchecked(senders)), evm_env, opts).await + self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await } /// Replays a block and returns the trace of each transaction. diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 3cd0200ab..d61f94214 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1229,7 +1229,7 @@ impl BlockReader for DatabaseProvid // Note: we're using unchecked here because we know the block contains valid txs // wrt to its height and can ignore the s value check so pre // EIP-2 txs are allowed - .try_with_senders_unchecked(senders) + .try_into_recovered_unchecked(senders) .map(Some) .map_err(|_| ProviderError::SenderRecoveryError) }, @@ -1274,7 +1274,7 @@ impl BlockReader for DatabaseProvid |range| self.headers_range(range), |header, body, senders| { Self::Block::new(header, body) - .try_with_senders_unchecked(senders) + .try_into_recovered_unchecked(senders) .map_err(|_| ProviderError::SenderRecoveryError) }, ) diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index e1f4dbf4b..7e968ce19 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -577,7 +577,7 @@ where let pool_transactions = txs_signed .into_iter() - .filter_map(|tx| tx.try_ecrecovered().ok()) + .filter_map(|tx| tx.try_clone_into_recovered().ok()) .filter_map(|tx| { // Filter out errors ::try_from_consensus(tx).ok() @@ -695,7 +695,7 @@ mod tests { let tx_bytes = hex!("02f87201830655c2808505ef61f08482565f94388c818ca8b9251b393131c08a736a67ccb192978801049e39c4b5b1f580c001a01764ace353514e8abdfb92446de356b260e3c1225b73fc4c8876a6258d12a129a04f02294aa61ca7676061cd99f29275491218b4754b46a0248e5e42bc5091f507"); let tx = PooledTransaction::decode_2718(&mut &tx_bytes[..]).unwrap(); let provider = MockEthProvider::default(); - let transaction: EthPooledTransaction = tx.try_into_ecrecovered().unwrap().into(); + let transaction: EthPooledTransaction = tx.try_into_recovered().unwrap().into(); let tx_to_cmp = transaction.clone(); let sender = hex!("1f9090aaE28b8a3dCeaDf281B0F12828e676c326").into(); provider.add_account(sender, ExtendedAccount::new(42, U256::MAX)); diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index feb50050c..4944dd110 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -101,12 +101,12 @@ impl TransactionGenerator { /// Generates and returns a pooled EIP-1559 transaction with a random signer. pub fn gen_eip1559_pooled(&mut self) -> EthPooledTransaction { - self.gen_eip1559().try_into_ecrecovered().unwrap().try_into().unwrap() + self.gen_eip1559().try_into_recovered().unwrap().try_into().unwrap() } /// Generates and returns a pooled EIP-4844 transaction with a random signer. pub fn gen_eip4844_pooled(&mut self) -> EthPooledTransaction { - let tx = self.gen_eip4844().try_into_ecrecovered().unwrap(); + let tx = self.gen_eip4844().try_into_recovered().unwrap(); let encoded_length = tx.encode_2718_len(); EthPooledTransaction::new(tx, encoded_length) } diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index ba5b38c48..d659c5930 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -892,7 +892,7 @@ mod tests { let data = hex::decode(raw).unwrap(); let tx = PooledTransaction::decode_2718(&mut data.as_ref()).unwrap(); - tx.try_into_ecrecovered().unwrap().into() + tx.try_into_recovered().unwrap().into() } // diff --git a/examples/custom-payload-builder/src/generator.rs b/examples/custom-payload-builder/src/generator.rs index 76222e0cc..dfb973a96 100644 --- a/examples/custom-payload-builder/src/generator.rs +++ b/examples/custom-payload-builder/src/generator.rs @@ -80,7 +80,7 @@ where .ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent()))?; // we already know the hash, so we can seal it - block.seal(attributes.parent()) + block.seal_unchecked(attributes.parent()) }; let hash = parent_block.hash(); let header = SealedHeader::new(parent_block.header().clone(), hash);