feat: unify recover fn result type (#13897)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
DevOrbitlabs
2025-01-22 21:58:36 +07:00
committed by GitHub
parent 5170112c1f
commit 926ad2a639
24 changed files with 129 additions and 98 deletions

View File

@ -167,7 +167,7 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>> Command<C> {
debug!(target: "reth::cli", bytes = ?tx_bytes, "Decoding transaction");
let transaction = TransactionSigned::decode(&mut &Bytes::from_str(tx_bytes)?[..])?
.try_ecrecovered()
.ok_or_else(|| eyre::eyre!("failed to recover tx"))?;
.map_err(|e| eyre::eyre!("failed to recover tx: {e}"))?;
let encoded_length = match &transaction.transaction {
Transaction::Eip4844(TxEip4844 { blob_versioned_hashes, .. }) => {

View File

@ -1,7 +1,6 @@
use alloy_consensus::BlockHeader;
use alloy_primitives::{keccak256, B256};
use alloy_rpc_types_debug::ExecutionWitness;
use eyre::OptionExt;
use pretty_assertions::Comparison;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_engine_primitives::InvalidBlockHook;
@ -87,7 +86,8 @@ where
// Re-execute all of the transactions in the block to load all touched accounts into
// the cache DB.
for tx in block.body().transactions() {
let signer = tx.recover_signer().ok_or_eyre("failed to recover sender")?;
let signer =
tx.recover_signer().map_err(|_| eyre::eyre!("failed to recover sender"))?;
evm.transact_commit(self.evm_config.tx_env(tx, signer))?;
}

View File

@ -19,7 +19,7 @@ use once_cell as _;
use once_cell::sync::OnceCell as OnceLock;
use reth_primitives_traits::{
crypto::secp256k1::{recover_signer, recover_signer_unchecked},
transaction::error::TransactionConversionError,
transaction::{error::TransactionConversionError, signed::RecoveryError},
FillTxEnv, InMemorySize, SignedTransaction,
};
use revm_primitives::{AuthorizationList, TxEnv};
@ -719,12 +719,15 @@ impl SignedTransaction for TransactionSigned {
&self.signature
}
fn recover_signer(&self) -> Option<Address> {
fn recover_signer(&self) -> Result<Address, RecoveryError> {
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> {
fn recover_signer_unchecked_with_buf(
&self,
buf: &mut Vec<u8>,
) -> Result<Address, RecoveryError> {
self.encode_for_signing(buf);
let signature_hash = keccak256(buf);
recover_signer_unchecked(&self.signature, signature_hash)
@ -940,10 +943,10 @@ mod tests {
// recover signer, expect failure
let hash = *tx.tx_hash();
assert!(recover_signer(signature, hash).is_none());
assert!(recover_signer(signature, hash).is_err());
// use unchecked, ensure it succeeds (the signature is valid if not for EIP-2)
assert!(recover_signer_unchecked(signature, hash).is_some());
assert!(recover_signer_unchecked(signature, hash).is_ok());
}
#[test]
@ -1002,8 +1005,8 @@ mod tests {
let decoded = TransactionSigned::decode_2718(&mut &tx_bytes[..]).unwrap();
assert_eq!(
decoded.recover_signer(),
Some(Address::from_str("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5").unwrap())
decoded.recover_signer().unwrap(),
Address::from_str("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5").unwrap()
);
}
@ -1018,8 +1021,10 @@ mod tests {
let decoded = TransactionSigned::decode_2718(&mut raw_tx.as_slice()).unwrap();
assert!(alloy_consensus::Typed2718::is_eip4844(&decoded));
let from = decoded.recover_signer();
assert_eq!(from, Some(address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2")));
assert_eq!(
decoded.recover_signer().ok(),
Some(address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2"))
);
let tx = decoded.transaction;
@ -1182,7 +1187,8 @@ mod tests {
let mut pointer = raw.as_ref();
let tx = TransactionSigned::decode(&mut pointer).unwrap();
assert_eq!(*tx.tx_hash(), hash, "Expected same hash");
assert_eq!(tx.recover_signer(), Some(signer), "Recovering signer should pass.");
let recovered = tx.recover_signer().expect("Recovering signer should pass");
assert_eq!(recovered, signer);
}
#[test]
@ -1252,7 +1258,7 @@ mod tests {
let data = hex!("f8ea0c850ba43b7400832dc6c0942935aa0a2d2fbb791622c29eb1c117b65b7a908580b884590528a9000000000000000000000001878ace42092b7f1ae1f28d16c1272b1aa80ca4670000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000557fe293cabc08cf1ca05bfaf3fda0a56b49cc78b22125feb5ae6a99d2b4781f00507d8b02c173771c85a0b5da0dbe6c5bc53740d0071fc83eb17ba0f709e49e9ae7df60dee625ef51afc5");
let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap();
let sender = tx.recover_signer();
assert!(sender.is_none());
assert!(sender.is_err());
let sender = tx.recover_signer_unchecked().unwrap();
assert_eq!(sender, address!("7e9e359edf0dbacf96a9952fa63092d919b0842b"));

View File

@ -26,7 +26,7 @@ use proptest as _;
use reth_primitives_traits::{
crypto::secp256k1::{recover_signer, recover_signer_unchecked},
sync::OnceLock,
transaction::error::TransactionConversionError,
transaction::{error::TransactionConversionError, signed::RecoveryError},
InMemorySize, SignedTransaction,
};
@ -79,11 +79,11 @@ impl SignedTransaction for OpTransactionSigned {
&self.signature
}
fn recover_signer(&self) -> Option<Address> {
fn recover_signer(&self) -> Result<Address, RecoveryError> {
// Optimism's Deposit transaction does not have a signature. Directly return the
// `from` address.
if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = self.transaction {
return Some(from)
return Ok(from)
}
let Self { transaction, signature, .. } = self;
@ -91,11 +91,11 @@ impl SignedTransaction for OpTransactionSigned {
recover_signer(signature, signature_hash)
}
fn recover_signer_unchecked(&self) -> Option<Address> {
fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
// Optimism's Deposit transaction does not have a signature. Directly return the
// `from` address.
if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = &self.transaction {
return Some(*from)
return Ok(*from)
}
let Self { transaction, signature, .. } = self;
@ -103,11 +103,14 @@ impl SignedTransaction for OpTransactionSigned {
recover_signer_unchecked(signature, signature_hash)
}
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address> {
fn recover_signer_unchecked_with_buf(
&self,
buf: &mut Vec<u8>,
) -> Result<Address, RecoveryError> {
match &self.transaction {
// Optimism's Deposit transaction does not have a signature. Directly return the
// `from` address.
OpTypedTransaction::Deposit(tx) => return Some(tx.from),
OpTypedTransaction::Deposit(tx) => return Ok(tx.from),
OpTypedTransaction::Legacy(tx) => tx.encode_for_signing(buf),
OpTypedTransaction::Eip2930(tx) => tx.encode_for_signing(buf),
OpTypedTransaction::Eip1559(tx) => tx.encode_for_signing(buf),

View File

@ -123,11 +123,11 @@ pub trait BlockBody:
}
/// Recover signer addresses for all transactions in the block body.
fn recover_signers(&self) -> Option<Vec<Address>>
fn recover_signers(&self) -> Result<Vec<Address>, RecoveryError>
where
Self::Transaction: SignedTransaction,
{
crate::transaction::recover::recover_signers(self.transactions())
crate::transaction::recover::recover_signers(self.transactions()).map_err(|_| RecoveryError)
}
/// Recover signer addresses for all transactions in the block body.
@ -137,14 +137,14 @@ pub trait BlockBody:
where
Self::Transaction: SignedTransaction,
{
self.recover_signers().ok_or(RecoveryError)
self.recover_signers()
}
/// Recover signer addresses for all transactions in the block body _without ensuring that the
/// signature has a low `s` value_.
///
/// Returns `None`, if some transaction's signature is invalid.
fn recover_signers_unchecked(&self) -> Option<Vec<Address>>
fn recover_signers_unchecked(&self) -> Result<Vec<Address>, RecoveryError>
where
Self::Transaction: SignedTransaction,
{
@ -159,7 +159,7 @@ pub trait BlockBody:
where
Self::Transaction: SignedTransaction,
{
self.recover_signers_unchecked().ok_or(RecoveryError)
self.recover_signers_unchecked()
}
}

View File

@ -16,8 +16,8 @@ use alloy_primitives::{Address, B256};
use alloy_rlp::{Decodable, Encodable};
use crate::{
BlockBody, BlockHeader, FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde, SealedHeader,
SignedTransaction,
transaction::signed::RecoveryError, BlockBody, BlockHeader, FullBlockBody, FullBlockHeader,
InMemorySize, MaybeSerde, SealedHeader, SignedTransaction,
};
/// Bincode-compatible header type serde implementations.
@ -121,7 +121,7 @@ pub trait Block:
}
/// Expensive operation that recovers transaction signer.
fn senders(&self) -> Option<Vec<Address>>
fn senders(&self) -> Result<Vec<Address>, RecoveryError>
where
<Self::Body as BlockBody>::Transaction: SignedTransaction,
{
@ -158,10 +158,10 @@ pub trait Block:
let senders = if self.body().transactions().len() == senders.len() {
senders
} else {
let Some(senders) = self.body().recover_signers_unchecked() else { return Err(self) };
// Fall back to recovery if lengths don't match
let Ok(senders) = self.body().recover_signers_unchecked() else { return Err(self) };
senders
};
Ok(RecoveredBlock::new_unhashed(self, senders))
}
@ -173,7 +173,7 @@ pub trait Block:
where
<Self::Body as BlockBody>::Transaction: SignedTransaction,
{
let senders = self.senders()?;
let senders = self.body().recover_signers().ok()?;
Some(RecoveredBlock::new_unhashed(self, senders))
}
}

View File

@ -2,6 +2,7 @@
use crate::{
block::{error::BlockRecoveryError, RecoveredBlock},
transaction::signed::RecoveryError,
Block, BlockBody, GotExpected, InMemorySize, SealedHeader,
};
use alloc::vec::Vec;
@ -179,7 +180,7 @@ impl<B: Block> SealedBlock<B> {
/// Recovers all senders from the transactions in the block.
///
/// Returns `None` if any of the transactions fail to recover the sender.
pub fn senders(&self) -> Option<Vec<Address>> {
pub fn senders(&self) -> Result<Vec<Address>, RecoveryError> {
self.body().recover_signers()
}

View File

@ -22,6 +22,7 @@ pub mod secp256k1 {
#[cfg(feature = "secp256k1")]
use super::impl_secp256k1 as imp;
use crate::transaction::signed::RecoveryError;
pub use imp::{public_key_to_address, sign_message};
/// Recover signer from message hash, _without ensuring that the signature has a low `s`
@ -30,7 +31,10 @@ pub mod secp256k1 {
/// Using this for signature validation will succeed, even if the signature is malleable or not
/// compliant with EIP-2. This is provided for compatibility with old signatures which have
/// large `s` values.
pub fn recover_signer_unchecked(signature: &Signature, hash: B256) -> Option<Address> {
pub fn recover_signer_unchecked(
signature: &Signature,
hash: B256,
) -> Result<Address, RecoveryError> {
let mut sig: [u8; 65] = [0; 65];
sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
@ -39,7 +43,7 @@ pub mod secp256k1 {
// NOTE: we are removing error from underlying crypto library as it will restrain primitive
// errors and we care only if recovery is passing or not.
imp::recover_signer_unchecked(&sig, &hash.0).ok()
imp::recover_signer_unchecked(&sig, &hash.0).map_err(|_| RecoveryError)
}
/// Recover signer address from message hash. This ensures that the signature S value is
@ -47,11 +51,10 @@ pub mod secp256k1 {
/// [EIP-2](https://eips.ethereum.org/EIPS/eip-2).
///
/// If the S value is too large, then this will return `None`
pub fn recover_signer(signature: &Signature, hash: B256) -> Option<Address> {
pub fn recover_signer(signature: &Signature, hash: B256) -> Result<Address, RecoveryError> {
if signature.s() > SECP256K1N_HALF {
return None
return Err(RecoveryError)
}
recover_signer_unchecked(signature, hash)
}
}

View File

@ -8,7 +8,7 @@ pub use iter::*;
#[cfg(feature = "rayon")]
mod rayon {
use crate::SignedTransaction;
use crate::{transaction::signed::RecoveryError, SignedTransaction};
use alloc::vec::Vec;
use alloy_primitives::Address;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
@ -16,7 +16,7 @@ mod rayon {
/// Recovers a list of signers from a transaction list iterator.
///
/// Returns `None`, if some transaction's signature is invalid
pub fn recover_signers<'a, I, T>(txes: I) -> Option<Vec<Address>>
pub fn recover_signers<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
where
T: SignedTransaction,
I: IntoParallelIterator<Item = &'a T> + IntoIterator<Item = &'a T> + Send,
@ -28,7 +28,7 @@ mod rayon {
/// signature has a low `s` value_.
///
/// Returns `None`, if some transaction's signature is invalid.
pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Option<Vec<Address>>
pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
where
T: SignedTransaction,
I: IntoParallelIterator<Item = &'a T> + IntoIterator<Item = &'a T> + Send,
@ -39,14 +39,14 @@ mod rayon {
#[cfg(not(feature = "rayon"))]
mod iter {
use crate::SignedTransaction;
use crate::{transaction::signed::RecoveryError, SignedTransaction};
use alloc::vec::Vec;
use alloy_primitives::Address;
/// Recovers a list of signers from a transaction list iterator.
///
/// Returns `None`, if some transaction's signature is invalid
pub fn recover_signers<'a, I, T>(txes: I) -> Option<Vec<Address>>
/// Returns `Err(RecoveryError)`, if some transaction's signature is invalid
pub fn recover_signers<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
where
T: SignedTransaction,
I: IntoIterator<Item = &'a T> + IntoIterator<Item = &'a T>,
@ -57,8 +57,8 @@ mod iter {
/// Recovers a list of signers from a transaction list iterator _without ensuring that the
/// signature has a low `s` value_.
///
/// Returns `None`, if some transaction's signature is invalid.
pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Option<Vec<Address>>
/// Returns `Err(RecoveryError)`, if some transaction's signature is invalid.
pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
where
T: SignedTransaction,
I: IntoIterator<Item = &'a T> + IntoIterator<Item = &'a T>,

View File

@ -62,13 +62,13 @@ pub trait SignedTransaction:
/// 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>;
fn recover_signer(&self) -> Result<Address, RecoveryError>;
/// Recover signer from signature and hash.
///
/// Returns an error if the transaction's signature is invalid.
fn try_recover(&self) -> Result<Address, RecoveryError> {
self.recover_signer().ok_or(RecoveryError)
self.recover_signer().map_err(|_| RecoveryError)
}
/// Recover signer from signature and hash _without ensuring that the signature has a low `s`
@ -76,8 +76,8 @@ pub trait SignedTransaction:
///
/// 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())
fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
self.recover_signer_unchecked_with_buf(&mut Vec::new()).map_err(|_| RecoveryError)
}
/// Recover signer from signature and hash _without ensuring that the signature has a low `s`
@ -85,12 +85,15 @@ pub trait SignedTransaction:
///
/// Returns an error if the transaction's signature is invalid.
fn try_recover_unchecked(&self) -> Result<Address, RecoveryError> {
self.recover_signer_unchecked().ok_or(RecoveryError)
self.recover_signer_unchecked()
}
/// 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>;
fn recover_signer_unchecked_with_buf(
&self,
buf: &mut Vec<u8>,
) -> Result<Address, RecoveryError>;
/// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with
/// tx type.
@ -120,12 +123,15 @@ impl SignedTransaction for PooledTransaction {
}
}
fn recover_signer(&self) -> Option<Address> {
fn recover_signer(&self) -> Result<Address, RecoveryError> {
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> {
fn recover_signer_unchecked_with_buf(
&self,
buf: &mut Vec<u8>,
) -> Result<Address, RecoveryError> {
match self {
Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
@ -158,12 +164,15 @@ impl SignedTransaction for op_alloy_consensus::OpPooledTransaction {
}
}
fn recover_signer(&self) -> Option<Address> {
fn recover_signer(&self) -> Result<Address, RecoveryError> {
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> {
fn recover_signer_unchecked_with_buf(
&self,
buf: &mut Vec<u8>,
) -> Result<Address, RecoveryError> {
match self {
Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
@ -178,9 +187,8 @@ 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) -> Option<Recovered<Self>> {
let signer = self.recover_signer()?;
Some(Recovered::new_unchecked(self.clone(), signer))
fn try_ecrecovered(&self) -> Result<Recovered<Self>, RecoveryError> {
self.recover_signer().map(|signer| Recovered::new_unchecked(self.clone(), signer))
}
/// Tries to recover signer and return [`Recovered`].
@ -189,8 +197,8 @@ pub trait SignedTransactionIntoRecoveredExt: SignedTransaction {
/// [`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)),
Ok(signer) => Ok(Recovered::new_unchecked(self, signer)),
Err(_) => Err(self),
}
}
@ -198,9 +206,8 @@ 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) -> Option<Recovered<Self>> {
let signer = self.recover_signer_unchecked()?;
Some(Recovered::new_unchecked(self, signer))
fn into_ecrecovered_unchecked(self) -> Result<Recovered<Self>, RecoveryError> {
self.recover_signer_unchecked().map(|signer| Recovered::new_unchecked(self, signer))
}
/// Returns the [`Recovered`] transaction with the given sender.

View File

@ -13,7 +13,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let raw =hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2");
let mut pointer = raw.as_ref();
let tx = TransactionSigned::decode(&mut pointer).unwrap();
tx.recover_signer();
tx.recover_signer().unwrap();
}
)
});

View File

@ -486,7 +486,7 @@ pub trait LoadTransaction: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt {
// check for pre EIP-2 because this transaction could be pre-EIP-2.
let transaction = tx
.into_ecrecovered_unchecked()
.ok_or(EthApiError::InvalidTransactionSignature)?;
.map_err(|_| EthApiError::InvalidTransactionSignature)?;
let tx = TransactionSource::Block {
transaction,

View File

@ -10,6 +10,7 @@ use alloy_primitives::{Address, Bytes, U256};
use alloy_rpc_types_eth::{error::EthRpcErrorCode, request::TransactionInputError, BlockError};
use alloy_sol_types::decode_revert_reason;
use reth_errors::RethError;
use reth_primitives_traits::transaction::signed::RecoveryError;
use reth_rpc_server_types::result::{
block_id_to_str, internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code,
};
@ -285,6 +286,12 @@ where
}
}
impl From<RecoveryError> for EthApiError {
fn from(_: RecoveryError) -> Self {
Self::InvalidTransactionSignature
}
}
/// An error due to invalid transaction.
///
/// The only reason this exists is to maintain compatibility with other clients de-facto standard

View File

@ -251,7 +251,7 @@ where
}
// check if the sender was the coinbase, if so, ignore
if let Some(sender) = tx.recover_signer() {
if let Ok(sender) = tx.recover_signer() {
if sender == block.beneficiary() {
continue
}

View File

@ -1,6 +1,6 @@
//! RPC receipt response builder, extends a layer one receipt with layer two data.
use super::{EthApiError, EthResult};
use super::EthResult;
use alloy_consensus::{transaction::TransactionMeta, ReceiptEnvelope, TxReceipt};
use alloy_primitives::{Address, TxKind};
use alloy_rpc_types_eth::{Log, ReceiptWithBloom, TransactionReceipt};
@ -21,8 +21,7 @@ where
{
// Note: we assume this transaction is valid, because it's mined (or part of pending block)
// and we don't need to check for pre EIP-2
let from =
transaction.recover_signer_unchecked().ok_or(EthApiError::InvalidTransactionSignature)?;
let from = transaction.recover_signer_unchecked()?;
// get the previous transaction cumulative gas used
let gas_used = if meta.index == 0 {

View File

@ -166,23 +166,19 @@ where
.body()
.transactions()
.iter()
.map(|tx| {
tx.recover_signer()
.ok_or(EthApiError::InvalidTransactionSignature)
.map_err(Eth::Error::from_eth_err)
})
.collect::<Result<Vec<_>, Eth::Error>>()?
.map(|tx| tx.recover_signer().map_err(Eth::Error::from_eth_err))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.collect()
} else {
block
.body()
.transactions()
.iter()
.map(|tx| {
tx.recover_signer_unchecked()
.ok_or(EthApiError::InvalidTransactionSignature)
.map_err(Eth::Error::from_eth_err)
})
.collect::<Result<Vec<_>, Eth::Error>>()?
.map(|tx| tx.recover_signer_unchecked().map_err(Eth::Error::from_eth_err))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.collect()
};
self.trace_block(Arc::new(block.with_senders_unchecked(senders)), evm_env, opts).await

View File

@ -313,9 +313,9 @@ fn recover_sender<T: SignedTransaction>(
// value is greater than `secp256k1n / 2` if past EIP-2. There are transactions
// pre-homestead which have large `s` values, so using [Signature::recover_signer] here
// would not be backwards-compatible.
let sender = tx
.recover_signer_unchecked_with_buf(rlp_buf)
.ok_or(SenderRecoveryStageError::FailedRecovery(FailedSenderRecoveryError { tx: tx_id }))?;
let sender = tx.recover_signer_unchecked_with_buf(rlp_buf).map_err(|_| {
SenderRecoveryStageError::FailedRecovery(FailedSenderRecoveryError { tx: tx_id })
})?;
Ok((tx_id, sender))
}

View File

@ -3,7 +3,7 @@ use alloc::{boxed::Box, string::String};
use alloy_eips::{BlockHashOrNumber, HashOrNumber};
use alloy_primitives::{Address, BlockHash, BlockNumber, TxNumber, B256};
use derive_more::Display;
use reth_primitives_traits::GotExpected;
use reth_primitives_traits::{transaction::signed::RecoveryError, GotExpected};
use reth_prune_types::PruneSegmentError;
use reth_static_file_types::StaticFileSegment;
@ -151,6 +151,12 @@ impl From<alloy_rlp::Error> for ProviderError {
}
}
impl From<RecoveryError> for ProviderError {
fn from(_: RecoveryError) -> Self {
Self::SenderRecoveryError
}
}
/// A root mismatch error at a given block height.
#[derive(Clone, Debug, PartialEq, Eq, Display)]
#[display("root mismatch at #{block_number} ({block_hash}): {root}")]

View File

@ -2640,7 +2640,7 @@ mod tests {
transaction_sender,
|block: &SealedBlock, tx_num: TxNumber, _: B256, _: &Vec<Vec<Receipt>>| (
tx_num,
block.body().transactions[test_tx_index].recover_signer()
block.body().transactions[test_tx_index].recover_signer().ok()
),
u64::MAX
),

View File

@ -693,9 +693,7 @@ impl<TX: DbTx + 'static, N: NodeTypesForProvider> DatabaseProvider<TX, N> {
match known_senders.get(&tx_num) {
None => {
// recover the sender from the transaction if not found
let sender = tx
.recover_signer_unchecked()
.ok_or(ProviderError::SenderRecoveryError)?;
let sender = tx.recover_signer_unchecked()?;
senders.push(sender);
}
Some(sender) => senders.push(*sender),

View File

@ -299,15 +299,14 @@ impl<N: NodePrimitives<SignedTx: Decompress + SignedTransaction>> TransactionsPr
range: impl RangeBounds<TxNumber>,
) -> ProviderResult<Vec<Address>> {
let txs = self.transactions_by_tx_range(range)?;
reth_primitives_traits::transaction::recover::recover_signers(&txs)
.ok_or(ProviderError::SenderRecoveryError)
Ok(reth_primitives_traits::transaction::recover::recover_signers(&txs)?)
}
fn transaction_sender(&self, num: TxNumber) -> ProviderResult<Option<Address>> {
Ok(self
.cursor()?
.get_one::<TransactionMask<Self::Transaction>>(num.into())?
.and_then(|tx| tx.recover_signer()))
.and_then(|tx| tx.recover_signer().ok()))
}
}

View File

@ -1568,12 +1568,14 @@ impl<N: NodePrimitives<SignedTx: Decompress + SignedTransaction>> TransactionsPr
range: impl RangeBounds<TxNumber>,
) -> ProviderResult<Vec<Address>> {
let txes = self.transactions_by_tx_range(range)?;
reth_primitives_traits::transaction::recover::recover_signers(&txes)
.ok_or(ProviderError::SenderRecoveryError)
Ok(reth_primitives_traits::transaction::recover::recover_signers(&txes)?)
}
fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
Ok(self.transaction_by_id_unhashed(id)?.and_then(|tx| tx.recover_signer()))
match self.transaction_by_id_unhashed(id)? {
Some(tx) => Ok(tx.recover_signer().ok()),
None => Ok(None),
}
}
}

View File

@ -368,7 +368,11 @@ impl TransactionsProvider for MockEthProvider {
.flat_map(|block| &block.body.transactions)
.enumerate()
.filter_map(|(tx_number, tx)| {
range.contains(&(tx_number as TxNumber)).then(|| tx.recover_signer()).flatten()
if range.contains(&(tx_number as TxNumber)) {
tx.recover_signer().ok()
} else {
None
}
})
.collect();

View File

@ -577,7 +577,7 @@ where
let pool_transactions = txs_signed
.into_iter()
.filter_map(|tx| tx.try_ecrecovered())
.filter_map(|tx| tx.try_ecrecovered().ok())
.filter_map(|tx| {
// Filter out errors
<P::Transaction as PoolTransaction>::try_from_consensus(tx).ok()