feat: abstract EthTransactionValidator over ChainSpec (#14162)

This commit is contained in:
Arsenii Kulikov
2025-02-03 23:26:18 +04:00
committed by GitHub
parent 440e6695b5
commit e3106889a8
13 changed files with 236 additions and 169 deletions

View File

@ -82,12 +82,14 @@
//! use reth_chainspec::MAINNET;
//! use reth_storage_api::StateProviderFactory;
//! use reth_tasks::TokioTaskExecutor;
//! use reth_chainspec::ChainSpecProvider;
//! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool, TransactionPool};
//! use reth_transaction_pool::blobstore::InMemoryBlobStore;
//! async fn t<C>(client: C) where C: StateProviderFactory + Clone + 'static{
//! use reth_chainspec::EthereumHardforks;
//! async fn t<C>(client: C) where C: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory + Clone + 'static{
//! let blob_store = InMemoryBlobStore::default();
//! let pool = Pool::eth_pool(
//! TransactionValidationTaskExecutor::eth(client, MAINNET.clone(), blob_store.clone(), TokioTaskExecutor::default()),
//! TransactionValidationTaskExecutor::eth(client, blob_store.clone(), TokioTaskExecutor::default()),
//! blob_store,
//! Default::default(),
//! );
@ -126,7 +128,7 @@
//! let manager = TaskManager::new(rt.handle().clone());
//! let executor = manager.executor();
//! let pool = Pool::eth_pool(
//! TransactionValidationTaskExecutor::eth(client.clone(), MAINNET.clone(), blob_store.clone(), executor.clone()),
//! TransactionValidationTaskExecutor::eth(client.clone(), blob_store.clone(), executor.clone()),
//! blob_store,
//! Default::default(),
//! );
@ -174,6 +176,7 @@ use crate::{identifier::TransactionId, pool::PoolInner};
use alloy_eips::eip4844::{BlobAndProofV1, BlobTransactionSidecar};
use alloy_primitives::{Address, TxHash, B256, U256};
use aquamarine as _;
use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
use reth_eth_wire_types::HandleMempoolData;
use reth_execution_types::ChangedAccount;
use reth_primitives::Recovered;
@ -280,7 +283,8 @@ where
impl<Client, S> EthTransactionPool<Client, S>
where
Client: StateProviderFactory + Clone + 'static,
Client:
ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory + Clone + 'static,
S: BlobStore,
{
/// Returns a new [`Pool`] that uses the default [`TransactionValidationTaskExecutor`] when
@ -292,15 +296,16 @@ where
/// use reth_chainspec::MAINNET;
/// use reth_storage_api::StateProviderFactory;
/// use reth_tasks::TokioTaskExecutor;
/// use reth_chainspec::ChainSpecProvider;
/// use reth_transaction_pool::{
/// blobstore::InMemoryBlobStore, Pool, TransactionValidationTaskExecutor,
/// };
/// # fn t<C>(client: C) where C: StateProviderFactory + Clone + 'static {
/// use reth_chainspec::EthereumHardforks;
/// # fn t<C>(client: C) where C: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory + Clone + 'static {
/// let blob_store = InMemoryBlobStore::default();
/// let pool = Pool::eth_pool(
/// TransactionValidationTaskExecutor::eth(
/// client,
/// MAINNET.clone(),
/// blob_store.clone(),
/// TokioTaskExecutor::default(),
/// ),

View File

@ -677,7 +677,6 @@ mod tests {
};
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::{hex, U256};
use reth_chainspec::MAINNET;
use reth_fs_util as fs;
use reth_primitives::{PooledTransaction, TransactionSigned};
use reth_provider::test_utils::{ExtendedAccount, MockEthProvider};
@ -706,8 +705,7 @@ mod tests {
let sender = hex!("1f9090aaE28b8a3dCeaDf281B0F12828e676c326").into();
provider.add_account(sender, ExtendedAccount::new(42, U256::MAX));
let blob_store = InMemoryBlobStore::default();
let validator = EthTransactionValidatorBuilder::new(MAINNET.clone())
.build(provider, blob_store.clone());
let validator = EthTransactionValidatorBuilder::new(provider).build(blob_store.clone());
let txpool = Pool::new(
validator.clone(),

View File

@ -22,7 +22,7 @@ use alloy_eips::{
eip1559::ETHEREUM_BLOCK_GAS_LIMIT,
eip4844::{env_settings::EnvKzgSettings, MAX_BLOBS_PER_BLOCK},
};
use reth_chainspec::{ChainSpec, EthereumHardforks};
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
use reth_primitives::{InvalidTransactionError, SealedBlock};
use reth_primitives_traits::{Block, GotExpected};
use reth_storage_api::{StateProvider, StateProviderFactory};
@ -45,8 +45,11 @@ pub struct EthTransactionValidator<Client, T> {
impl<Client, Tx> EthTransactionValidator<Client, Tx> {
/// Returns the configured chain spec
pub fn chain_spec(&self) -> &Arc<ChainSpec> {
&self.inner.chain_spec
pub fn chain_spec(&self) -> Arc<Client::ChainSpec>
where
Client: ChainSpecProvider,
{
self.client().chain_spec()
}
/// Returns the configured client
@ -57,7 +60,7 @@ impl<Client, Tx> EthTransactionValidator<Client, Tx> {
impl<Client, Tx> EthTransactionValidator<Client, Tx>
where
Client: StateProviderFactory,
Client: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory,
Tx: EthPoolTransaction,
{
/// Validates a single transaction.
@ -86,7 +89,7 @@ where
impl<Client, Tx> TransactionValidator for EthTransactionValidator<Client, Tx>
where
Client: StateProviderFactory,
Client: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory,
Tx: EthPoolTransaction,
{
type Transaction = Tx;
@ -130,8 +133,6 @@ where
/// And adheres to the configured [`LocalTransactionConfig`].
#[derive(Debug)]
pub(crate) struct EthTransactionValidatorInner<Client, T> {
/// Spec of the chain
chain_spec: Arc<ChainSpec>,
/// This type fetches account info from the db
client: Client,
/// Blobstore used for fetching re-injected blob transactions.
@ -162,18 +163,23 @@ pub(crate) struct EthTransactionValidatorInner<Client, T> {
// === impl EthTransactionValidatorInner ===
impl<Client, Tx> EthTransactionValidatorInner<Client, Tx> {
impl<Client: ChainSpecProvider, Tx> EthTransactionValidatorInner<Client, Tx> {
/// Returns the configured chain id
pub(crate) fn chain_id(&self) -> u64 {
self.chain_spec.chain().id()
self.client.chain_spec().chain().id()
}
}
impl<Client, Tx> EthTransactionValidatorInner<Client, Tx>
where
Client: StateProviderFactory,
Client: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory,
Tx: EthPoolTransaction,
{
/// Returns the configured chain spec
fn chain_spec(&self) -> Arc<Client::ChainSpec> {
self.client.chain_spec()
}
/// Validates a single transaction using an optional cached state provider.
/// If no provider is passed, a new one will be created. This allows reusing
/// the same provider across multiple txs.
@ -509,15 +515,15 @@ where
fn on_new_head_block<T: BlockHeader>(&self, new_tip_block: &T) {
// update all forks
if self.chain_spec.is_cancun_active_at_timestamp(new_tip_block.timestamp()) {
if self.chain_spec().is_cancun_active_at_timestamp(new_tip_block.timestamp()) {
self.fork_tracker.cancun.store(true, std::sync::atomic::Ordering::Relaxed);
}
if self.chain_spec.is_shanghai_active_at_timestamp(new_tip_block.timestamp()) {
if self.chain_spec().is_shanghai_active_at_timestamp(new_tip_block.timestamp()) {
self.fork_tracker.shanghai.store(true, std::sync::atomic::Ordering::Relaxed);
}
if self.chain_spec.is_prague_active_at_timestamp(new_tip_block.timestamp()) {
if self.chain_spec().is_prague_active_at_timestamp(new_tip_block.timestamp()) {
self.fork_tracker.prague.store(true, std::sync::atomic::Ordering::Relaxed);
}
@ -531,8 +537,8 @@ where
/// A builder for [`TransactionValidationTaskExecutor`]
#[derive(Debug)]
pub struct EthTransactionValidatorBuilder {
chain_spec: Arc<ChainSpec>,
pub struct EthTransactionValidatorBuilder<Client> {
client: Client,
/// Fork indicator whether we are in the Shanghai stage.
shanghai: bool,
/// Fork indicator whether we are in the Cancun hardfork.
@ -564,8 +570,8 @@ pub struct EthTransactionValidatorBuilder {
max_tx_input_bytes: usize,
}
impl EthTransactionValidatorBuilder {
/// Creates a new builder for the given [`ChainSpec`]
impl<Client> EthTransactionValidatorBuilder<Client> {
/// Creates a new builder for the given client
///
/// By default this assumes the network is on the `Cancun` hardfork and the following
/// transactions are allowed:
@ -573,10 +579,10 @@ impl EthTransactionValidatorBuilder {
/// - EIP-2718
/// - EIP-1559
/// - EIP-4844
pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
pub fn new(client: Client) -> Self {
Self {
block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT.into(),
chain_spec,
client,
minimum_priority_fee: None,
additional_tasks: 1,
kzg_settings: EnvKzgSettings::Default,
@ -696,10 +702,13 @@ impl EthTransactionValidatorBuilder {
/// Configures validation rules based on the head block's timestamp.
///
/// For example, whether the Shanghai and Cancun hardfork is activated at launch.
pub fn with_head_timestamp(mut self, timestamp: u64) -> Self {
self.cancun = self.chain_spec.is_cancun_active_at_timestamp(timestamp);
self.shanghai = self.chain_spec.is_shanghai_active_at_timestamp(timestamp);
self.prague = self.chain_spec.is_prague_active_at_timestamp(timestamp);
pub fn with_head_timestamp(mut self, timestamp: u64) -> Self
where
Client: ChainSpecProvider<ChainSpec: EthereumHardforks>,
{
self.cancun = self.client.chain_spec().is_cancun_active_at_timestamp(timestamp);
self.shanghai = self.client.chain_spec().is_shanghai_active_at_timestamp(timestamp);
self.prague = self.client.chain_spec().is_prague_active_at_timestamp(timestamp);
self
}
@ -718,16 +727,12 @@ impl EthTransactionValidatorBuilder {
}
/// Builds a the [`EthTransactionValidator`] without spawning validator tasks.
pub fn build<Client, Tx, S>(
self,
client: Client,
blob_store: S,
) -> EthTransactionValidator<Client, Tx>
pub fn build<Tx, S>(self, blob_store: S) -> EthTransactionValidator<Client, Tx>
where
S: BlobStore,
{
let Self {
chain_spec,
client,
shanghai,
cancun,
prague,
@ -750,7 +755,6 @@ impl EthTransactionValidatorBuilder {
};
let inner = EthTransactionValidatorInner {
chain_spec,
client,
eip2718,
eip1559,
@ -775,9 +779,8 @@ impl EthTransactionValidatorBuilder {
/// The validator will spawn `additional_tasks` additional tasks for validation.
///
/// By default this will spawn 1 additional task.
pub fn build_with_tasks<Client, Tx, T, S>(
pub fn build_with_tasks<Tx, T, S>(
self,
client: Client,
tasks: T,
blob_store: S,
) -> TransactionValidationTaskExecutor<EthTransactionValidator<Client, Tx>>
@ -786,7 +789,7 @@ impl EthTransactionValidatorBuilder {
S: BlobStore,
{
let additional_tasks = self.additional_tasks;
let validator = self.build(client, blob_store);
let validator = self.build(blob_store);
let (tx, task) = ValidationTask::new();
@ -882,7 +885,6 @@ mod tests {
};
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::{hex, U256};
use reth_chainspec::MAINNET;
use reth_primitives::{transaction::SignedTransactionIntoRecoveredExt, PooledTransaction};
use reth_provider::test_utils::{ExtendedAccount, MockEthProvider};
@ -915,8 +917,7 @@ mod tests {
ExtendedAccount::new(transaction.nonce(), U256::MAX),
);
let blob_store = InMemoryBlobStore::default();
let validator = EthTransactionValidatorBuilder::new(MAINNET.clone())
.build(provider, blob_store.clone());
let validator = EthTransactionValidatorBuilder::new(provider).build(blob_store.clone());
let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());
@ -943,9 +944,9 @@ mod tests {
);
let blob_store = InMemoryBlobStore::default();
let validator = EthTransactionValidatorBuilder::new(MAINNET.clone())
let validator = EthTransactionValidatorBuilder::new(provider)
.set_block_gas_limit(1_000_000) // tx gas limit is 1_015_288
.build(provider, blob_store.clone());
.build(blob_store.clone());
let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());

View File

@ -7,7 +7,6 @@ use crate::{
TransactionValidator,
};
use futures_util::{lock::Mutex, StreamExt};
use reth_chainspec::ChainSpec;
use reth_primitives::SealedBlock;
use reth_primitives_traits::Block;
use reth_tasks::TaskSpawner;
@ -93,8 +92,8 @@ pub struct TransactionValidationTaskExecutor<V> {
impl TransactionValidationTaskExecutor<()> {
/// Convenience method to create a [`EthTransactionValidatorBuilder`]
pub fn eth_builder(chain_spec: Arc<ChainSpec>) -> EthTransactionValidatorBuilder {
EthTransactionValidatorBuilder::new(chain_spec)
pub fn eth_builder<Client>(client: Client) -> EthTransactionValidatorBuilder<Client> {
EthTransactionValidatorBuilder::new(client)
}
}
@ -112,23 +111,18 @@ impl<V> TransactionValidationTaskExecutor<V> {
}
impl<Client, Tx> TransactionValidationTaskExecutor<EthTransactionValidator<Client, Tx>> {
/// Creates a new instance for the given [`ChainSpec`]
/// Creates a new instance for the given client
///
/// This will spawn a single validation tasks that performs the actual validation.
/// See [`TransactionValidationTaskExecutor::eth_with_additional_tasks`]
pub fn eth<T, S: BlobStore>(
client: Client,
chain_spec: Arc<ChainSpec>,
blob_store: S,
tasks: T,
) -> Self
pub fn eth<T, S: BlobStore>(client: Client, blob_store: S, tasks: T) -> Self
where
T: TaskSpawner,
{
Self::eth_with_additional_tasks(client, chain_spec, blob_store, tasks, 0)
Self::eth_with_additional_tasks(client, blob_store, tasks, 0)
}
/// Creates a new instance for the given [`ChainSpec`]
/// Creates a new instance for the given client
///
/// By default this will enable support for:
/// - shanghai
@ -139,7 +133,6 @@ impl<Client, Tx> TransactionValidationTaskExecutor<EthTransactionValidator<Clien
/// `num_additional_tasks` additional tasks.
pub fn eth_with_additional_tasks<T, S: BlobStore>(
client: Client,
chain_spec: Arc<ChainSpec>,
blob_store: S,
tasks: T,
num_additional_tasks: usize,
@ -147,9 +140,9 @@ impl<Client, Tx> TransactionValidationTaskExecutor<EthTransactionValidator<Clien
where
T: TaskSpawner,
{
EthTransactionValidatorBuilder::new(chain_spec)
EthTransactionValidatorBuilder::new(client)
.with_additional_tasks(num_additional_tasks)
.build_with_tasks::<Client, Tx, T, S>(client, tasks, blob_store)
.build_with_tasks::<Tx, T, S>(tasks, blob_store)
}
}