mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
feat(primitives-traits): add try_recover_signers for parallel batch recovery (#21103)
This commit is contained in:
@@ -1,68 +1,137 @@
|
||||
//! Helpers for recovering signers from a set of transactions
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
pub use rayon::*;
|
||||
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
pub use iter::*;
|
||||
use crate::{transaction::signed::RecoveryError, Recovered, SignedTransaction};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::transaction::SignerRecoverable;
|
||||
use alloy_primitives::Address;
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
mod rayon {
|
||||
use crate::{transaction::signed::RecoveryError, SignedTransaction};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_primitives::Address;
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator.
|
||||
///
|
||||
/// 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: IntoParallelIterator<Item = &'a T>,
|
||||
{
|
||||
txes.into_par_iter().map(|tx| tx.recover_signer()).collect()
|
||||
}
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator _without ensuring that the
|
||||
/// signature has a low `s` value_.
|
||||
///
|
||||
/// 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: IntoParallelIterator<Item = &'a T>,
|
||||
{
|
||||
txes.into_par_iter().map(|tx| tx.recover_signer_unchecked()).collect()
|
||||
}
|
||||
/// Recovers a list of signers from a transaction list iterator.
|
||||
///
|
||||
/// Returns `Err(RecoveryError)`, if some transaction's signature is invalid.
|
||||
///
|
||||
/// When the `rayon` feature is enabled, recovery is performed in parallel.
|
||||
#[cfg(feature = "rayon")]
|
||||
pub fn recover_signers<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
|
||||
where
|
||||
T: SignedTransaction,
|
||||
I: IntoParallelIterator<Item = &'a T>,
|
||||
{
|
||||
txes.into_par_iter().map(|tx| tx.recover_signer()).collect()
|
||||
}
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator.
|
||||
///
|
||||
/// Returns `Err(RecoveryError)`, if some transaction's signature is invalid.
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
pub fn recover_signers<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
|
||||
where
|
||||
T: SignedTransaction,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
{
|
||||
txes.into_iter().map(|tx| tx.recover_signer()).collect()
|
||||
}
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator _without ensuring that the
|
||||
/// signature has a low `s` value_.
|
||||
///
|
||||
/// Returns `Err(RecoveryError)`, if some transaction's signature is invalid.
|
||||
///
|
||||
/// When the `rayon` feature is enabled, recovery is performed in parallel.
|
||||
#[cfg(feature = "rayon")]
|
||||
pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
|
||||
where
|
||||
T: SignedTransaction,
|
||||
I: IntoParallelIterator<Item = &'a T>,
|
||||
{
|
||||
txes.into_par_iter().map(|tx| tx.recover_signer_unchecked()).collect()
|
||||
}
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator _without ensuring that the
|
||||
/// signature has a low `s` value_.
|
||||
///
|
||||
/// Returns `Err(RecoveryError)`, if some transaction's signature is invalid.
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Result<Vec<Address>, RecoveryError>
|
||||
where
|
||||
T: SignedTransaction,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
{
|
||||
txes.into_iter().map(|tx| tx.recover_signer_unchecked()).collect()
|
||||
}
|
||||
|
||||
/// Trait for items that can be used with [`try_recover_signers`].
|
||||
#[cfg(feature = "rayon")]
|
||||
pub trait TryRecoverItems: IntoParallelIterator {}
|
||||
|
||||
/// Trait for items that can be used with [`try_recover_signers`].
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
pub trait TryRecoverItems: IntoIterator {}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<I: IntoParallelIterator> TryRecoverItems for I {}
|
||||
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
mod iter {
|
||||
use crate::{transaction::signed::RecoveryError, SignedTransaction};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_primitives::Address;
|
||||
impl<I: IntoIterator> TryRecoverItems for I {}
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator.
|
||||
///
|
||||
/// 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>,
|
||||
{
|
||||
txes.into_iter().map(|tx| tx.recover_signer()).collect()
|
||||
}
|
||||
/// Trait for decode functions that can be used with [`try_recover_signers`].
|
||||
#[cfg(feature = "rayon")]
|
||||
pub trait TryRecoverFn<Item, T>: Fn(Item) -> Result<T, RecoveryError> + Sync {}
|
||||
|
||||
/// Recovers a list of signers from a transaction list iterator _without ensuring that the
|
||||
/// signature has a low `s` value_.
|
||||
///
|
||||
/// 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>,
|
||||
{
|
||||
txes.into_iter().map(|tx| tx.recover_signer_unchecked()).collect()
|
||||
}
|
||||
/// Trait for decode functions that can be used with [`try_recover_signers`].
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
pub trait TryRecoverFn<Item, T>: Fn(Item) -> Result<T, RecoveryError> {}
|
||||
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<Item, T, F: Fn(Item) -> Result<T, RecoveryError> + Sync> TryRecoverFn<Item, T> for F {}
|
||||
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
impl<Item, T, F: Fn(Item) -> Result<T, RecoveryError>> TryRecoverFn<Item, T> for F {}
|
||||
|
||||
/// Decodes and recovers a list of [`Recovered`] transactions from an iterator.
|
||||
///
|
||||
/// The `decode` closure transforms each item into a [`SignedTransaction`], which is then
|
||||
/// recovered.
|
||||
///
|
||||
/// Returns an error if decoding or signature recovery fails for any transaction.
|
||||
///
|
||||
/// When the `rayon` feature is enabled, recovery is performed in parallel.
|
||||
#[cfg(feature = "rayon")]
|
||||
pub fn try_recover_signers<I, F, T>(items: I, decode: F) -> Result<Vec<Recovered<T>>, RecoveryError>
|
||||
where
|
||||
I: IntoParallelIterator,
|
||||
F: Fn(I::Item) -> Result<T, RecoveryError> + Sync,
|
||||
T: SignedTransaction,
|
||||
{
|
||||
items
|
||||
.into_par_iter()
|
||||
.map(|item| {
|
||||
let tx = decode(item)?;
|
||||
SignerRecoverable::try_into_recovered(tx)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Decodes and recovers a list of [`Recovered`] transactions from an iterator.
|
||||
///
|
||||
/// The `decode` closure transforms each item into a [`SignedTransaction`], which is then
|
||||
/// recovered.
|
||||
///
|
||||
/// Returns an error if decoding or signature recovery fails for any transaction.
|
||||
#[cfg(not(feature = "rayon"))]
|
||||
pub fn try_recover_signers<I, F, T>(items: I, decode: F) -> Result<Vec<Recovered<T>>, RecoveryError>
|
||||
where
|
||||
I: IntoIterator,
|
||||
F: Fn(I::Item) -> Result<T, RecoveryError>,
|
||||
T: SignedTransaction,
|
||||
{
|
||||
items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
let tx = decode(item)?;
|
||||
SignerRecoverable::try_into_recovered(tx)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//! on public-facing RPC endpoints without proper authentication.
|
||||
|
||||
use alloy_consensus::{Header, Transaction};
|
||||
use alloy_eips::eip2718::Decodable2718;
|
||||
use alloy_evm::Evm;
|
||||
use alloy_primitives::{map::HashSet, Address, U256};
|
||||
use alloy_rpc_types_engine::ExecutionPayloadEnvelopeV5;
|
||||
@@ -24,11 +25,14 @@ use reth_errors::RethError;
|
||||
use reth_ethereum_engine_primitives::EthBuiltPayload;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_evm::{execute::BlockBuilder, ConfigureEvm, NextBlockEnvAttributes};
|
||||
use reth_primitives_traits::{AlloyBlockHeader as BlockTrait, Recovered, TxTy};
|
||||
use reth_primitives_traits::{
|
||||
transaction::{recover::try_recover_signers, signed::RecoveryError},
|
||||
AlloyBlockHeader as BlockTrait, TxTy,
|
||||
};
|
||||
use reth_revm::{database::StateProviderDatabase, db::State};
|
||||
use reth_rpc_api::{TestingApiServer, TestingBuildBlockRequestV1};
|
||||
use reth_rpc_eth_api::{helpers::Call, FromEthApiError};
|
||||
use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError};
|
||||
use reth_rpc_eth_types::EthApiError;
|
||||
use reth_storage_api::{BlockReader, HeaderProvider};
|
||||
use revm::context::Block;
|
||||
use revm_primitives::map::DefaultHashBuilder;
|
||||
@@ -106,11 +110,16 @@ where
|
||||
|
||||
let mut invalid_senders: HashSet<Address, DefaultHashBuilder> = HashSet::default();
|
||||
|
||||
for (idx, tx) in request.transactions.iter().enumerate() {
|
||||
let tx: Recovered<TxTy<Evm::Primitives>> = recover_raw_transaction(tx)?;
|
||||
let sender = tx.signer();
|
||||
// Decode and recover all transactions in parallel
|
||||
let recovered_txs = try_recover_signers(&request.transactions, |tx| {
|
||||
TxTy::<Evm::Primitives>::decode_2718_exact(tx.as_ref())
|
||||
.map_err(RecoveryError::from_source)
|
||||
})
|
||||
.or(Err(EthApiError::InvalidTransactionSignature))?;
|
||||
|
||||
if skip_invalid_transactions && invalid_senders.contains(&sender) {
|
||||
for (idx, tx) in recovered_txs.into_iter().enumerate() {
|
||||
let signer = tx.signer();
|
||||
if skip_invalid_transactions && invalid_senders.contains(&signer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -122,17 +131,17 @@ where
|
||||
debug!(
|
||||
target: "rpc::testing",
|
||||
tx_idx = idx,
|
||||
?sender,
|
||||
?signer,
|
||||
error = ?err,
|
||||
"Skipping invalid transaction"
|
||||
);
|
||||
invalid_senders.insert(sender);
|
||||
invalid_senders.insert(signer);
|
||||
continue;
|
||||
}
|
||||
debug!(
|
||||
target: "rpc::testing",
|
||||
tx_idx = idx,
|
||||
?sender,
|
||||
?signer,
|
||||
error = ?err,
|
||||
"Transaction execution failed"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user