From 4b0b63766e7f25729be5a8bc614dfdcf17db97c0 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Tue, 8 Aug 2023 10:53:12 +0100 Subject: [PATCH] feat: add `TransactionSigned::recover_signers` (#4098) Co-authored-by: Matthias Seitz --- Cargo.lock | 1 + crates/consensus/auto-seal/src/lib.rs | 6 ++---- crates/primitives/Cargo.toml | 1 + crates/primitives/src/block.rs | 2 +- crates/primitives/src/transaction/mod.rs | 20 +++++++++++++++++++ crates/revm/src/executor.rs | 7 ++----- .../src/providers/database/provider.rs | 19 +++++++++--------- 7 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 984cb8c1a0..997d83087b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5820,6 +5820,7 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.8.5", + "rayon", "reth-codecs", "reth-rlp", "reth-rlp-derive", diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 7b14f544a4..afd47662f1 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -349,10 +349,8 @@ impl StorageInner { let block = Block { header, body: transactions, ommers: vec![], withdrawals: None }; - let senders = - block.body.iter().map(|tx| tx.recover_signer()).collect::>>().ok_or( - BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError), - )?; + let senders = TransactionSigned::recover_signers(block.body.iter(), block.body.len()) + .ok_or(BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError))?; trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index e73b59d885..8394559219 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -60,6 +60,7 @@ impl-serde = "0.4.0" once_cell = "1.17.0" zstd = { version = "0.12", features = ["experimental"] } paste = "1.0" +rayon = "1.7" # proof related triehash = "0.8" diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 0c9866ba28..6ae1a8e062 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -163,7 +163,7 @@ impl SealedBlock { /// Expensive operation that recovers transaction signer. See [SealedBlockWithSenders]. pub fn senders(&self) -> Option> { - self.body.iter().map(|tx| tx.recover_signer()).collect::>>() + TransactionSigned::recover_signers(self.body.iter(), self.body.len()) } /// Seal sealed block with recovered transaction senders. diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index d3f03e0820..fae91a3318 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -7,6 +7,7 @@ use bytes::{Buf, BytesMut}; use derive_more::{AsRef, Deref}; pub use error::InvalidTransactionError; pub use meta::TransactionMeta; +use rayon::prelude::{ParallelBridge, ParallelIterator}; use reth_codecs::{add_arbitrary_tests, derive_arbitrary, Compact}; use reth_rlp::{ length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE, @@ -32,6 +33,10 @@ mod signature; mod tx_type; pub(crate) mod util; +// Expected number of transactions where we can expect a speed-up by recovering the senders in +// parallel. +const PARALLEL_SENDER_RECOVERY_THRESHOLD: usize = 10; + /// A raw transaction. /// /// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). @@ -937,6 +942,21 @@ impl TransactionSigned { self.signature.recover_signer(signature_hash) } + /// Recovers a list of signers from a transaction list iterator + /// + /// Returns `None`, if some transaction's signature is invalid, see also + /// [Self::recover_signer]. + pub fn recover_signers<'a>( + txes: impl Iterator + Send, + num_txes: usize, + ) -> Option> { + if num_txes < PARALLEL_SENDER_RECOVERY_THRESHOLD { + txes.map(|tx| tx.recover_signer()).collect() + } else { + txes.cloned().par_bridge().map(|tx| tx.recover_signer()).collect() + } + } + /// Consumes the type, recover signer and return [`TransactionSignedEcRecovered`] /// /// Returns `None` if the transaction's signature is invalid, see also [Self::recover_signer]. diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 5ce714521f..e52bdb4bc1 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -84,11 +84,8 @@ where Err(BlockValidationError::SenderRecoveryError.into()) } } else { - body.iter() - .map(|tx| { - tx.recover_signer().ok_or(BlockValidationError::SenderRecoveryError.into()) - }) - .collect() + TransactionSigned::recover_signers(body.iter(), body.len()) + .ok_or(BlockValidationError::SenderRecoveryError.into()) } } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 56d4c0f392..fe691b0441 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -22,7 +22,10 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, BlockNumberList, DatabaseError, }; -use reth_interfaces::Result; +use reth_interfaces::{ + executor::{BlockExecutionError, BlockValidationError}, + Result, +}; use reth_primitives::{ keccak256, stage::{StageCheckpoint, StageId}, @@ -1910,14 +1913,12 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> BlockWriter for DatabaseProvider<' let tx_iter = if Some(block.body.len()) == senders_len { block.body.into_iter().zip(senders.unwrap()).collect::>() } else { - block - .body - .into_iter() - .map(|tx| { - let signer = tx.recover_signer(); - (tx, signer.unwrap_or_default()) - }) - .collect::>() + let senders = TransactionSigned::recover_signers(block.body.iter(), block.body.len()) + .ok_or(BlockExecutionError::Validation( + BlockValidationError::SenderRecoveryError, + ))?; + + block.body.into_iter().zip(senders).collect() }; for (transaction, sender) in tx_iter {