1use super::constants::DEFAULT_MAX_TX_INPUT_BYTES;
4use crate::{
5 blobstore::BlobStore,
6 error::{
7 Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError,
8 },
9 metrics::TxPoolValidationMetrics,
10 traits::TransactionOrigin,
11 validate::ValidTransaction,
12 Address, BlobTransactionSidecarVariant, EthBlobTransactionSidecar, EthPoolTransaction,
13 LocalTransactionConfig, TransactionValidationOutcome, TransactionValidationTaskExecutor,
14 TransactionValidator,
15};
16
17use alloy_consensus::{
18 constants::{
19 EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID,
20 LEGACY_TX_TYPE_ID,
21 },
22 BlockHeader,
23};
24use alloy_eips::{
25 eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M, eip4844::env_settings::EnvKzgSettings,
26 eip7840::BlobParams, BlockId,
27};
28use alloy_primitives::U256;
29use alloy_rlp::Encodable;
30use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
31use reth_evm::ConfigureEvm;
32use reth_primitives_traits::{
33 transaction::error::InvalidTransactionError, Account, BlockTy, GotExpected, HeaderTy,
34 SealedBlock,
35};
36use reth_storage_api::{
37 errors::ProviderError, AccountInfoReader, BlockReaderIdExt, BytecodeReader, StateProviderBox,
38 StateProviderFactory,
39};
40use reth_tasks::Runtime;
41use revm::context_interface::Cfg;
42use std::{
43 fmt,
44 marker::PhantomData,
45 sync::{
46 atomic::{AtomicBool, AtomicU64, AtomicUsize},
47 Arc,
48 },
49 time::{Instant, SystemTime},
50};
51
52pub type StatelessValidationFn<T> =
57 Arc<dyn Fn(TransactionOrigin, &T) -> Result<(), InvalidPoolTransactionError> + Send + Sync>;
58
59pub type StatefulValidationFn<T> = Arc<
64 dyn Fn(TransactionOrigin, &T, &dyn AccountInfoReader) -> Result<(), InvalidPoolTransactionError>
65 + Send
66 + Sync,
67>;
68
69pub struct EthTransactionValidator<Client, T, Evm> {
84 client: Client,
86 chain_id: u64,
88 blob_store: Box<dyn BlobStore>,
90 fork_tracker: ForkTracker,
92 eip2718: bool,
94 eip1559: bool,
96 eip4844: bool,
98 eip7702: bool,
100 block_gas_limit: AtomicU64,
102 tx_fee_cap: Option<u128>,
104 minimum_priority_fee: Option<u128>,
106 kzg_settings: EnvKzgSettings,
108 local_transactions_config: LocalTransactionConfig,
110 max_tx_input_bytes: usize,
112 max_tx_gas_limit: Option<u64>,
114 disable_balance_check: bool,
116 evm_config: Evm,
118 _marker: PhantomData<T>,
120 validation_metrics: TxPoolValidationMetrics,
122 other_tx_types: U256,
124 eip7594: bool,
128 additional_stateless_validation: Option<StatelessValidationFn<T>>,
131 additional_stateful_validation: Option<StatefulValidationFn<T>>,
134}
135
136impl<Client, Tx, Evm> fmt::Debug for EthTransactionValidator<Client, Tx, Evm> {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.debug_struct("EthTransactionValidator")
139 .field("fork_tracker", &self.fork_tracker)
140 .field("eip2718", &self.eip2718)
141 .field("eip1559", &self.eip1559)
142 .field("eip4844", &self.eip4844)
143 .field("eip7702", &self.eip7702)
144 .field("block_gas_limit", &self.block_gas_limit)
145 .field("tx_fee_cap", &self.tx_fee_cap)
146 .field("minimum_priority_fee", &self.minimum_priority_fee)
147 .field("max_tx_input_bytes", &self.max_tx_input_bytes)
148 .field("max_tx_gas_limit", &self.max_tx_gas_limit)
149 .field("disable_balance_check", &self.disable_balance_check)
150 .field("eip7594", &self.eip7594)
151 .field(
152 "additional_stateless_validation",
153 &self.additional_stateless_validation.as_ref().map(|_| "..."),
154 )
155 .field(
156 "additional_stateful_validation",
157 &self.additional_stateful_validation.as_ref().map(|_| "..."),
158 )
159 .finish()
160 }
161}
162
163impl<Client, Tx, Evm> EthTransactionValidator<Client, Tx, Evm> {
164 pub fn chain_spec(&self) -> Arc<Client::ChainSpec>
166 where
167 Client: ChainSpecProvider,
168 {
169 self.client().chain_spec()
170 }
171
172 pub const fn chain_id(&self) -> u64 {
174 self.chain_id
175 }
176
177 pub const fn client(&self) -> &Client {
179 &self.client
180 }
181
182 pub const fn fork_tracker(&self) -> &ForkTracker {
184 &self.fork_tracker
185 }
186
187 pub const fn evm_config(&self) -> &Evm {
189 &self.evm_config
190 }
191
192 pub const fn eip2718(&self) -> bool {
194 self.eip2718
195 }
196
197 pub const fn eip1559(&self) -> bool {
199 self.eip1559
200 }
201
202 pub const fn eip4844(&self) -> bool {
204 self.eip4844
205 }
206
207 pub const fn eip7702(&self) -> bool {
209 self.eip7702
210 }
211
212 pub const fn tx_fee_cap(&self) -> &Option<u128> {
214 &self.tx_fee_cap
215 }
216
217 pub const fn minimum_priority_fee(&self) -> &Option<u128> {
219 &self.minimum_priority_fee
220 }
221
222 pub const fn kzg_settings(&self) -> &EnvKzgSettings {
224 &self.kzg_settings
225 }
226
227 pub const fn local_transactions_config(&self) -> &LocalTransactionConfig {
229 &self.local_transactions_config
230 }
231
232 pub const fn max_tx_input_bytes(&self) -> usize {
235 self.max_tx_input_bytes
236 }
237
238 pub const fn disable_balance_check(&self) -> bool {
240 self.disable_balance_check
241 }
242
243 pub fn set_additional_stateless_validation<F>(&mut self, f: F)
268 where
269 F: Fn(TransactionOrigin, &Tx) -> Result<(), InvalidPoolTransactionError>
270 + Send
271 + Sync
272 + 'static,
273 {
274 self.additional_stateless_validation = Some(Arc::new(f));
275 }
276
277 pub fn set_additional_stateless_validation_fn(&mut self, f: StatelessValidationFn<Tx>) {
284 self.additional_stateless_validation = Some(f);
285 }
286
287 pub fn set_additional_stateless_validation_fn_opt(
292 &mut self,
293 f: Option<StatelessValidationFn<Tx>>,
294 ) {
295 self.additional_stateless_validation = f;
296 }
297
298 pub fn set_additional_stateful_validation<F>(&mut self, f: F)
323 where
324 F: Fn(
325 TransactionOrigin,
326 &Tx,
327 &dyn AccountInfoReader,
328 ) -> Result<(), InvalidPoolTransactionError>
329 + Send
330 + Sync
331 + 'static,
332 {
333 self.additional_stateful_validation = Some(Arc::new(f));
334 }
335
336 pub fn set_additional_stateful_validation_fn(&mut self, f: StatefulValidationFn<Tx>) {
343 self.additional_stateful_validation = Some(f);
344 }
345
346 pub fn set_additional_stateful_validation_fn_opt(
351 &mut self,
352 f: Option<StatefulValidationFn<Tx>>,
353 ) {
354 self.additional_stateful_validation = f;
355 }
356}
357
358impl<Client, Tx, Evm> EthTransactionValidator<Client, Tx, Evm>
359where
360 Client: ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks> + StateProviderFactory,
361 Tx: EthPoolTransaction,
362 Evm: ConfigureEvm,
363{
364 pub fn block_gas_limit(&self) -> u64 {
366 self.max_gas_limit()
367 }
368
369 pub fn validate_one(
373 &self,
374 origin: TransactionOrigin,
375 transaction: Tx,
376 ) -> TransactionValidationOutcome<Tx> {
377 let mut state: Option<StateProviderBox> = None;
378 self.validate_one_with_provider(origin, transaction, &mut state, || self.client.latest())
379 }
380
381 pub fn validate_one_with_state(
388 &self,
389 origin: TransactionOrigin,
390 transaction: Tx,
391 state: &mut Option<Box<dyn AccountInfoReader + Send>>,
392 ) -> TransactionValidationOutcome<Tx> {
393 self.validate_one_with_provider(origin, transaction, state, || {
394 self.client.latest().map(|state| Box::new(state) as Box<dyn AccountInfoReader + Send>)
395 })
396 }
397
398 fn validate_one_with_provider<P, F>(
402 &self,
403 origin: TransactionOrigin,
404 transaction: Tx,
405 maybe_state: &mut Option<P>,
406 state_provider: F,
407 ) -> TransactionValidationOutcome<Tx>
408 where
409 P: AccountInfoReader,
410 F: FnOnce() -> Result<P, ProviderError>,
411 {
412 match self.validate_stateless(origin, &transaction) {
413 Ok(()) => {
414 if maybe_state.is_none() {
417 match state_provider() {
418 Ok(new_state) => {
419 *maybe_state = Some(new_state);
420 }
421 Err(err) => {
422 return TransactionValidationOutcome::Error(
423 *transaction.hash(),
424 Box::new(err),
425 )
426 }
427 }
428 }
429
430 let state = maybe_state.as_ref().expect("provider is set");
431
432 self.validate_stateful(origin, transaction, state)
433 }
434 Err(err) => TransactionValidationOutcome::Invalid(transaction, err),
435 }
436 }
437
438 pub fn validate_one_with_state_provider(
441 &self,
442 origin: TransactionOrigin,
443 transaction: Tx,
444 state: impl AccountInfoReader,
445 ) -> TransactionValidationOutcome<Tx> {
446 if let Err(err) = self.validate_stateless(origin, &transaction) {
447 return TransactionValidationOutcome::Invalid(transaction, err);
448 }
449 self.validate_stateful(origin, transaction, state)
450 }
451
452 pub fn validate_stateless(
457 &self,
458 origin: TransactionOrigin,
459 transaction: &Tx,
460 ) -> Result<(), InvalidPoolTransactionError> {
461 match transaction.ty() {
463 EIP2930_TX_TYPE_ID if !self.eip2718 => {
465 return Err(InvalidTransactionError::Eip2930Disabled.into())
466 }
467 EIP1559_TX_TYPE_ID if !self.eip1559 => {
469 return Err(InvalidTransactionError::Eip1559Disabled.into())
470 }
471 EIP4844_TX_TYPE_ID if !self.eip4844 => {
473 return Err(InvalidTransactionError::Eip4844Disabled.into())
474 }
475 EIP7702_TX_TYPE_ID if !self.eip7702 => {
477 return Err(InvalidTransactionError::Eip7702Disabled.into())
478 }
479 LEGACY_TX_TYPE_ID | EIP2930_TX_TYPE_ID | EIP1559_TX_TYPE_ID | EIP4844_TX_TYPE_ID |
481 EIP7702_TX_TYPE_ID => {}
482
483 ty if !self.other_tx_types.bit(ty as usize) => {
484 return Err(InvalidTransactionError::TxTypeNotSupported.into())
485 }
486
487 _ => {}
488 };
489
490 let tx_nonce = transaction.nonce();
492 if tx_nonce == u64::MAX {
493 return Err(InvalidPoolTransactionError::Eip2681)
494 }
495
496 if transaction.is_eip4844() {
498 let tx_size = transaction.input().len().saturating_add(
503 transaction
504 .access_list()
505 .map(|access_list| access_list.length())
506 .unwrap_or_default(),
507 );
508 if tx_size > self.max_tx_input_bytes {
509 return Err(InvalidPoolTransactionError::OversizedData {
510 size: tx_size,
511 limit: self.max_tx_input_bytes,
512 })
513 }
514 } else {
515 let tx_size = transaction.encoded_length();
517 if tx_size > self.max_tx_input_bytes {
518 return Err(InvalidPoolTransactionError::OversizedData {
519 size: tx_size,
520 limit: self.max_tx_input_bytes,
521 })
522 }
523 }
524
525 if self.fork_tracker.is_shanghai_activated() {
527 let max_initcode_size =
528 self.fork_tracker.max_initcode_size.load(std::sync::atomic::Ordering::Relaxed);
529 transaction.ensure_max_init_code_size(max_initcode_size)?;
530 }
531
532 let transaction_gas_limit = transaction.gas_limit();
534 let block_gas_limit = self.max_gas_limit();
535 if transaction_gas_limit > block_gas_limit {
536 return Err(InvalidPoolTransactionError::ExceedsGasLimit(
537 transaction_gas_limit,
538 block_gas_limit,
539 ))
540 }
541
542 if let Some(max_tx_gas_limit) = self.max_tx_gas_limit &&
544 transaction_gas_limit > max_tx_gas_limit
545 {
546 return Err(InvalidPoolTransactionError::MaxTxGasLimitExceeded(
547 transaction_gas_limit,
548 max_tx_gas_limit,
549 ))
550 }
551
552 if transaction.max_priority_fee_per_gas() > Some(transaction.max_fee_per_gas()) {
554 return Err(InvalidTransactionError::TipAboveFeeCap.into())
555 }
556
557 let is_local = self.local_transactions_config.is_local(origin, transaction.sender_ref());
559
560 if is_local {
563 match self.tx_fee_cap {
564 Some(0) | None => {} Some(tx_fee_cap_wei) => {
566 let max_tx_fee_wei = transaction.cost().saturating_sub(transaction.value());
567 if max_tx_fee_wei > tx_fee_cap_wei {
568 return Err(InvalidPoolTransactionError::ExceedsFeeCap {
569 max_tx_fee_wei: max_tx_fee_wei.saturating_to(),
570 tx_fee_cap_wei,
571 })
572 }
573 }
574 }
575 }
576
577 if !is_local &&
580 transaction.is_dynamic_fee() &&
581 transaction.max_priority_fee_per_gas() < self.minimum_priority_fee
582 {
583 return Err(InvalidPoolTransactionError::PriorityFeeBelowMinimum {
584 minimum_priority_fee: self
585 .minimum_priority_fee
586 .expect("minimum priority fee is expected inside if statement"),
587 })
588 }
589
590 if let Some(chain_id) = transaction.chain_id() &&
592 chain_id != self.chain_id()
593 {
594 return Err(InvalidTransactionError::ChainIdMismatch.into())
595 }
596
597 if transaction.is_eip7702() {
598 if !self.fork_tracker.is_prague_activated() {
600 return Err(InvalidTransactionError::TxTypeNotSupported.into())
601 }
602
603 if transaction.authorization_list().is_none_or(|l| l.is_empty()) {
604 return Err(Eip7702PoolTransactionError::MissingEip7702AuthorizationList.into())
605 }
606 }
607
608 ensure_intrinsic_gas(transaction, &self.fork_tracker)?;
609
610 if transaction.is_eip4844() {
612 if !self.fork_tracker.is_cancun_activated() {
614 return Err(InvalidTransactionError::TxTypeNotSupported.into())
615 }
616
617 let blob_count = transaction.blob_count().unwrap_or(0);
618 if blob_count == 0 {
619 return Err(InvalidPoolTransactionError::Eip4844(
621 Eip4844PoolTransactionError::NoEip4844Blobs,
622 ))
623 }
624
625 let max_blob_count = self.fork_tracker.max_blob_count();
626 if blob_count > max_blob_count {
627 return Err(InvalidPoolTransactionError::Eip4844(
628 Eip4844PoolTransactionError::TooManyEip4844Blobs {
629 have: blob_count,
630 permitted: max_blob_count,
631 },
632 ))
633 }
634 }
635
636 let tx_gas_limit_cap =
638 self.fork_tracker.tx_gas_limit_cap.load(std::sync::atomic::Ordering::Relaxed);
639 if tx_gas_limit_cap > 0 && transaction.gas_limit() > tx_gas_limit_cap {
640 return Err(InvalidTransactionError::GasLimitTooHigh.into())
641 }
642
643 if let Some(check) = &self.additional_stateless_validation {
645 check(origin, transaction)?;
646 }
647
648 Ok(())
649 }
650
651 pub fn validate_stateful<P>(
656 &self,
657 origin: TransactionOrigin,
658 mut transaction: Tx,
659 state: P,
660 ) -> TransactionValidationOutcome<Tx>
661 where
662 P: AccountInfoReader,
663 {
664 let account = match state.basic_account(transaction.sender_ref()) {
666 Ok(account) => account.unwrap_or_default(),
667 Err(err) => {
668 return TransactionValidationOutcome::Error(*transaction.hash(), Box::new(err))
669 }
670 };
671
672 match self.validate_sender_bytecode(&transaction, &account, &state) {
674 Err(outcome) => return outcome,
675 Ok(Err(err)) => return TransactionValidationOutcome::Invalid(transaction, err),
676 _ => {}
677 };
678
679 if transaction.requires_nonce_check() &&
681 let Err(err) = self.validate_sender_nonce(&transaction, &account)
682 {
683 return TransactionValidationOutcome::Invalid(transaction, err)
684 }
685
686 if let Err(err) = self.validate_sender_balance(&transaction, &account) {
688 return TransactionValidationOutcome::Invalid(transaction, err)
689 }
690
691 let maybe_blob_sidecar = match self.validate_eip4844(&mut transaction) {
693 Err(err) => return TransactionValidationOutcome::Invalid(transaction, err),
694 Ok(sidecar) => sidecar,
695 };
696
697 if let Some(check) = &self.additional_stateful_validation &&
699 let Err(err) = check(origin, &transaction, &state)
700 {
701 return TransactionValidationOutcome::Invalid(transaction, err)
702 }
703
704 let authorities = self.recover_authorities(&transaction);
705 TransactionValidationOutcome::Valid {
707 balance: account.balance,
708 state_nonce: account.nonce,
709 bytecode_hash: account.bytecode_hash,
710 transaction: ValidTransaction::new(transaction, maybe_blob_sidecar),
711 propagate: match origin {
713 TransactionOrigin::External => true,
714 TransactionOrigin::Local => {
715 self.local_transactions_config.propagate_local_transactions
716 }
717 TransactionOrigin::Private => false,
718 },
719 authorities,
720 }
721 }
722
723 pub fn validate_sender_bytecode(
725 &self,
726 transaction: &Tx,
727 sender: &Account,
728 state: impl BytecodeReader,
729 ) -> Result<Result<(), InvalidPoolTransactionError>, TransactionValidationOutcome<Tx>> {
730 if let Some(code_hash) = &sender.bytecode_hash {
737 let is_eip7702 = if self.fork_tracker.is_prague_activated() {
738 match state.bytecode_by_hash(code_hash) {
739 Ok(bytecode) => bytecode.unwrap_or_default().is_eip7702(),
740 Err(err) => {
741 return Err(TransactionValidationOutcome::Error(
742 *transaction.hash(),
743 Box::new(err),
744 ))
745 }
746 }
747 } else {
748 false
749 };
750
751 if !is_eip7702 {
752 return Ok(Err(InvalidTransactionError::SignerAccountHasBytecode.into()))
753 }
754 }
755 Ok(Ok(()))
756 }
757
758 pub fn validate_sender_nonce(
760 &self,
761 transaction: &Tx,
762 sender: &Account,
763 ) -> Result<(), InvalidPoolTransactionError> {
764 let tx_nonce = transaction.nonce();
765
766 if tx_nonce < sender.nonce {
767 return Err(InvalidTransactionError::NonceNotConsistent {
768 tx: tx_nonce,
769 state: sender.nonce,
770 }
771 .into())
772 }
773 Ok(())
774 }
775
776 pub fn validate_sender_balance(
778 &self,
779 transaction: &Tx,
780 sender: &Account,
781 ) -> Result<(), InvalidPoolTransactionError> {
782 let cost = transaction.cost();
783
784 if !self.disable_balance_check && cost > &sender.balance {
785 let expected = *cost;
786 return Err(InvalidTransactionError::InsufficientFunds(
787 GotExpected { got: sender.balance, expected }.into(),
788 )
789 .into())
790 }
791 Ok(())
792 }
793
794 pub fn validate_eip4844(
796 &self,
797 transaction: &mut Tx,
798 ) -> Result<Option<BlobTransactionSidecarVariant>, InvalidPoolTransactionError> {
799 let mut maybe_blob_sidecar = None;
800
801 if transaction.is_eip4844() {
803 match transaction.take_blob() {
805 EthBlobTransactionSidecar::None => {
806 return Err(InvalidTransactionError::TxTypeNotSupported.into())
808 }
809 EthBlobTransactionSidecar::Missing => {
810 if self.blob_store.contains(*transaction.hash()).is_ok_and(|c| c) {
815 } else {
817 return Err(InvalidPoolTransactionError::Eip4844(
818 Eip4844PoolTransactionError::MissingEip4844BlobSidecar,
819 ))
820 }
821 }
822 EthBlobTransactionSidecar::Present(sidecar) => {
823 let now = Instant::now();
824
825 if self.eip7594 {
827 if self.fork_tracker.is_osaka_activated() {
829 if sidecar.is_eip4844() {
830 return Err(InvalidPoolTransactionError::Eip4844(
831 Eip4844PoolTransactionError::UnexpectedEip4844SidecarAfterOsaka,
832 ))
833 }
834 } else if sidecar.is_eip7594() && !self.allow_7594_sidecars() {
835 return Err(InvalidPoolTransactionError::Eip4844(
836 Eip4844PoolTransactionError::UnexpectedEip7594SidecarBeforeOsaka,
837 ))
838 }
839 } else {
840 if sidecar.is_eip7594() {
842 return Err(InvalidPoolTransactionError::Eip4844(
843 Eip4844PoolTransactionError::Eip7594SidecarDisallowed,
844 ))
845 }
846 }
847
848 if let Err(err) = transaction.validate_blob(&sidecar, self.kzg_settings.get()) {
850 return Err(InvalidPoolTransactionError::Eip4844(
851 Eip4844PoolTransactionError::InvalidEip4844Blob(err),
852 ))
853 }
854 self.validation_metrics.blob_validation_duration.record(now.elapsed());
856 maybe_blob_sidecar = Some(sidecar);
858 }
859 }
860 }
861 Ok(maybe_blob_sidecar)
862 }
863
864 fn recover_authorities(&self, transaction: &Tx) -> std::option::Option<Vec<Address>> {
866 transaction
867 .authorization_list()
868 .map(|auths| auths.iter().flat_map(|auth| auth.recover_authority()).collect::<Vec<_>>())
869 }
870
871 fn validate_batch(
873 &self,
874 transactions: impl IntoIterator<Item = (TransactionOrigin, Tx)>,
875 ) -> Vec<TransactionValidationOutcome<Tx>> {
876 let mut provider: Option<StateProviderBox> = None;
877 transactions
878 .into_iter()
879 .map(|(origin, tx)| {
880 self.validate_one_with_provider(origin, tx, &mut provider, || self.client.latest())
881 })
882 .collect()
883 }
884
885 fn validate_batch_with_origin(
887 &self,
888 origin: TransactionOrigin,
889 transactions: impl IntoIterator<Item = Tx> + Send,
890 ) -> Vec<TransactionValidationOutcome<Tx>> {
891 let mut provider: Option<StateProviderBox> = None;
892 transactions
893 .into_iter()
894 .map(|tx| {
895 self.validate_one_with_provider(origin, tx, &mut provider, || self.client.latest())
896 })
897 .collect()
898 }
899
900 fn on_new_head_block(&self, new_tip_block: &HeaderTy<Evm::Primitives>) {
901 if self.chain_spec().is_shanghai_active_at_timestamp(new_tip_block.timestamp()) {
903 self.fork_tracker.shanghai.store(true, std::sync::atomic::Ordering::Relaxed);
904 }
905
906 if self.chain_spec().is_cancun_active_at_timestamp(new_tip_block.timestamp()) {
907 self.fork_tracker.cancun.store(true, std::sync::atomic::Ordering::Relaxed);
908 }
909
910 if self.chain_spec().is_prague_active_at_timestamp(new_tip_block.timestamp()) {
911 self.fork_tracker.prague.store(true, std::sync::atomic::Ordering::Relaxed);
912 }
913
914 if self.chain_spec().is_osaka_active_at_timestamp(new_tip_block.timestamp()) {
915 self.fork_tracker.osaka.store(true, std::sync::atomic::Ordering::Relaxed);
916 }
917
918 if self.chain_spec().is_amsterdam_active_at_timestamp(new_tip_block.timestamp()) {
919 self.fork_tracker.amsterdam.store(true, std::sync::atomic::Ordering::Relaxed);
920 }
921
922 self.fork_tracker
923 .tip_timestamp
924 .store(new_tip_block.timestamp(), std::sync::atomic::Ordering::Relaxed);
925
926 if let Some(blob_params) =
927 self.chain_spec().blob_params_at_timestamp(new_tip_block.timestamp())
928 {
929 self.fork_tracker
930 .max_blob_count
931 .store(blob_params.max_blobs_per_tx, std::sync::atomic::Ordering::Relaxed);
932 }
933
934 self.block_gas_limit.store(new_tip_block.gas_limit(), std::sync::atomic::Ordering::Relaxed);
935
936 let evm_env = self
938 .evm_config
939 .evm_env(new_tip_block)
940 .expect("evm_env should not fail for executed block");
941
942 self.fork_tracker
943 .max_initcode_size
944 .store(evm_env.cfg_env.max_initcode_size(), std::sync::atomic::Ordering::Relaxed);
945 let tx_gas_limit_cap = if evm_env.cfg_env.is_amsterdam_eip8037_enabled() {
949 0
950 } else {
951 evm_env.cfg_env.tx_gas_limit_cap()
952 };
953 self.fork_tracker
954 .tx_gas_limit_cap
955 .store(tx_gas_limit_cap, std::sync::atomic::Ordering::Relaxed);
956 }
957
958 fn max_gas_limit(&self) -> u64 {
959 self.block_gas_limit.load(std::sync::atomic::Ordering::Relaxed)
960 }
961
962 fn allow_7594_sidecars(&self) -> bool {
964 let tip_timestamp = self.fork_tracker.tip_timestamp();
965
966 if self.chain_spec().is_osaka_active_at_timestamp(tip_timestamp.saturating_add(12)) {
968 true
969 } else if self.chain_spec().is_osaka_active_at_timestamp(tip_timestamp.saturating_add(24)) {
970 let current_timestamp =
971 SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
972
973 current_timestamp >= tip_timestamp.saturating_add(4)
975 } else {
976 false
977 }
978 }
979}
980
981impl<Client, Tx, Evm> TransactionValidator for EthTransactionValidator<Client, Tx, Evm>
982where
983 Client: ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks> + StateProviderFactory,
984 Tx: EthPoolTransaction,
985 Evm: ConfigureEvm,
986{
987 type Transaction = Tx;
988 type Block = BlockTy<Evm::Primitives>;
989
990 async fn validate_transaction(
991 &self,
992 origin: TransactionOrigin,
993 transaction: Self::Transaction,
994 ) -> TransactionValidationOutcome<Self::Transaction> {
995 self.validate_one(origin, transaction)
996 }
997
998 async fn validate_transactions(
999 &self,
1000 transactions: impl IntoIterator<Item = (TransactionOrigin, Self::Transaction), IntoIter: Send>
1001 + Send,
1002 ) -> Vec<TransactionValidationOutcome<Self::Transaction>> {
1003 self.validate_batch(transactions)
1004 }
1005
1006 async fn validate_transactions_with_origin(
1007 &self,
1008 origin: TransactionOrigin,
1009 transactions: impl IntoIterator<Item = Self::Transaction, IntoIter: Send> + Send,
1010 ) -> Vec<TransactionValidationOutcome<Self::Transaction>> {
1011 self.validate_batch_with_origin(origin, transactions)
1012 }
1013
1014 fn on_new_head_block(&self, new_tip_block: &SealedBlock<Self::Block>) {
1015 Self::on_new_head_block(self, new_tip_block.header())
1016 }
1017}
1018
1019#[derive(Debug)]
1021pub struct EthTransactionValidatorBuilder<Client, Evm> {
1022 client: Client,
1023 chain_id: u64,
1025 evm_config: Evm,
1027 shanghai: bool,
1029 cancun: bool,
1031 prague: bool,
1033 osaka: bool,
1035 amsterdam: bool,
1037 tip_timestamp: u64,
1039 max_blob_count: u64,
1041 eip2718: bool,
1043 eip1559: bool,
1045 eip4844: bool,
1047 eip7702: bool,
1049 block_gas_limit: AtomicU64,
1051 tx_fee_cap: Option<u128>,
1053 minimum_priority_fee: Option<u128>,
1055 additional_tasks: usize,
1059
1060 kzg_settings: EnvKzgSettings,
1062 local_transactions_config: LocalTransactionConfig,
1064 max_tx_input_bytes: usize,
1066 max_tx_gas_limit: Option<u64>,
1068 disable_balance_check: bool,
1070 other_tx_types: U256,
1072 max_initcode_size: usize,
1074 tx_gas_limit_cap: u64,
1076 eip7594: bool,
1080}
1081
1082impl<Client, Evm> EthTransactionValidatorBuilder<Client, Evm> {
1083 pub fn new(client: Client, evm_config: Evm) -> Self
1093 where
1094 Client: ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>
1095 + BlockReaderIdExt<Header = HeaderTy<Evm::Primitives>>,
1096 Evm: ConfigureEvm,
1097 {
1098 let chain_spec = client.chain_spec();
1099 let tip = client
1100 .header_by_id(BlockId::latest())
1101 .expect("failed to fetch latest header")
1102 .expect("latest header is not found");
1103 let evm_env =
1104 evm_config.evm_env(&tip).expect("evm_env should not fail for existing blocks");
1105
1106 Self {
1107 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M.into(),
1108 client,
1109 chain_id: chain_spec.chain().id(),
1110 evm_config,
1111 minimum_priority_fee: None,
1112 additional_tasks: 1,
1113 kzg_settings: EnvKzgSettings::Default,
1114 local_transactions_config: Default::default(),
1115 max_tx_input_bytes: DEFAULT_MAX_TX_INPUT_BYTES,
1116 tx_fee_cap: Some(1e18 as u128),
1117 max_tx_gas_limit: None,
1118 eip2718: true,
1120 eip1559: true,
1121 eip4844: true,
1122 eip7702: true,
1123
1124 shanghai: chain_spec.is_shanghai_active_at_timestamp(tip.timestamp()),
1125 cancun: chain_spec.is_cancun_active_at_timestamp(tip.timestamp()),
1126 prague: chain_spec.is_prague_active_at_timestamp(tip.timestamp()),
1127 osaka: chain_spec.is_osaka_active_at_timestamp(tip.timestamp()),
1128 amsterdam: chain_spec.is_amsterdam_active_at_timestamp(tip.timestamp()),
1129
1130 tip_timestamp: tip.timestamp(),
1131
1132 max_blob_count: chain_spec
1133 .blob_params_at_timestamp(tip.timestamp())
1134 .unwrap_or_else(BlobParams::prague)
1135 .max_blobs_per_tx,
1136
1137 disable_balance_check: false,
1139
1140 other_tx_types: U256::ZERO,
1142
1143 tx_gas_limit_cap: if evm_env.cfg_env.is_amsterdam_eip8037_enabled() {
1145 0
1146 } else {
1147 evm_env.cfg_env.tx_gas_limit_cap()
1148 },
1149 max_initcode_size: evm_env.cfg_env.max_initcode_size(),
1150
1151 eip7594: true,
1153 }
1154 }
1155
1156 pub const fn no_cancun(self) -> Self {
1158 self.set_cancun(false)
1159 }
1160
1161 pub fn with_local_transactions_config(
1163 mut self,
1164 local_transactions_config: LocalTransactionConfig,
1165 ) -> Self {
1166 self.local_transactions_config = local_transactions_config;
1167 self
1168 }
1169
1170 pub const fn set_cancun(mut self, cancun: bool) -> Self {
1172 self.cancun = cancun;
1173 self
1174 }
1175
1176 pub const fn no_shanghai(self) -> Self {
1178 self.set_shanghai(false)
1179 }
1180
1181 pub const fn set_shanghai(mut self, shanghai: bool) -> Self {
1183 self.shanghai = shanghai;
1184 self
1185 }
1186
1187 pub const fn no_prague(self) -> Self {
1189 self.set_prague(false)
1190 }
1191
1192 pub const fn set_prague(mut self, prague: bool) -> Self {
1194 self.prague = prague;
1195 self
1196 }
1197
1198 pub const fn no_osaka(self) -> Self {
1200 self.set_osaka(false)
1201 }
1202
1203 pub const fn set_osaka(mut self, osaka: bool) -> Self {
1205 self.osaka = osaka;
1206 self
1207 }
1208
1209 pub const fn no_amsterdam(self) -> Self {
1211 self.set_amsterdam(false)
1212 }
1213
1214 pub const fn set_amsterdam(mut self, amsterdam: bool) -> Self {
1216 self.amsterdam = amsterdam;
1217 self
1218 }
1219
1220 pub const fn no_eip2718(self) -> Self {
1222 self.set_eip2718(false)
1223 }
1224
1225 pub const fn set_eip2718(mut self, eip2718: bool) -> Self {
1227 self.eip2718 = eip2718;
1228 self
1229 }
1230
1231 pub const fn no_eip1559(self) -> Self {
1233 self.set_eip1559(false)
1234 }
1235
1236 pub const fn set_eip1559(mut self, eip1559: bool) -> Self {
1238 self.eip1559 = eip1559;
1239 self
1240 }
1241
1242 pub const fn no_eip4844(self) -> Self {
1244 self.set_eip4844(false)
1245 }
1246
1247 pub const fn set_eip4844(mut self, eip4844: bool) -> Self {
1249 self.eip4844 = eip4844;
1250 self
1251 }
1252
1253 pub const fn no_eip7702(self) -> Self {
1255 self.set_eip7702(false)
1256 }
1257
1258 pub const fn set_eip7702(mut self, eip7702: bool) -> Self {
1260 self.eip7702 = eip7702;
1261 self
1262 }
1263
1264 pub const fn no_eip7594(self) -> Self {
1271 self.set_eip7594(false)
1272 }
1273
1274 pub const fn set_eip7594(mut self, eip7594: bool) -> Self {
1279 self.eip7594 = eip7594;
1280 self
1281 }
1282
1283 pub fn kzg_settings(mut self, kzg_settings: EnvKzgSettings) -> Self {
1285 self.kzg_settings = kzg_settings;
1286 self
1287 }
1288
1289 pub const fn with_minimum_priority_fee(mut self, minimum_priority_fee: Option<u128>) -> Self {
1291 self.minimum_priority_fee = minimum_priority_fee;
1292 self
1293 }
1294
1295 pub const fn with_additional_tasks(mut self, additional_tasks: usize) -> Self {
1297 self.additional_tasks = additional_tasks;
1298 self
1299 }
1300
1301 pub const fn with_max_tx_input_bytes(mut self, max_tx_input_bytes: usize) -> Self {
1303 self.max_tx_input_bytes = max_tx_input_bytes;
1304 self
1305 }
1306
1307 pub fn set_block_gas_limit(self, block_gas_limit: u64) -> Self {
1311 self.block_gas_limit.store(block_gas_limit, std::sync::atomic::Ordering::Relaxed);
1312 self
1313 }
1314
1315 pub const fn set_tx_fee_cap(mut self, tx_fee_cap: u128) -> Self {
1319 self.tx_fee_cap = Some(tx_fee_cap);
1320 self
1321 }
1322
1323 pub const fn with_max_tx_gas_limit(mut self, max_tx_gas_limit: Option<u64>) -> Self {
1325 self.max_tx_gas_limit = max_tx_gas_limit;
1326 self
1327 }
1328
1329 pub const fn disable_balance_check(mut self) -> Self {
1331 self.disable_balance_check = true;
1332 self
1333 }
1334
1335 pub const fn with_custom_tx_type(mut self, tx_type: u8) -> Self {
1337 self.other_tx_types.set_bit(tx_type as usize, true);
1338 self
1339 }
1340
1341 pub fn build<Tx, S>(self, blob_store: S) -> EthTransactionValidator<Client, Tx, Evm>
1343 where
1344 S: BlobStore,
1345 {
1346 let Self {
1347 client,
1348 chain_id,
1349 evm_config,
1350 shanghai,
1351 cancun,
1352 prague,
1353 osaka,
1354 amsterdam,
1355 tip_timestamp,
1356 eip2718,
1357 eip1559,
1358 eip4844,
1359 eip7702,
1360 block_gas_limit,
1361 tx_fee_cap,
1362 minimum_priority_fee,
1363 kzg_settings,
1364 local_transactions_config,
1365 max_tx_input_bytes,
1366 max_tx_gas_limit,
1367 disable_balance_check,
1368 max_blob_count,
1369 additional_tasks: _,
1370 other_tx_types,
1371 max_initcode_size,
1372 tx_gas_limit_cap,
1373 eip7594,
1374 } = self;
1375
1376 let fork_tracker = ForkTracker {
1377 shanghai: AtomicBool::new(shanghai),
1378 cancun: AtomicBool::new(cancun),
1379 prague: AtomicBool::new(prague),
1380 osaka: AtomicBool::new(osaka),
1381 amsterdam: AtomicBool::new(amsterdam),
1382 tip_timestamp: AtomicU64::new(tip_timestamp),
1383 max_blob_count: AtomicU64::new(max_blob_count),
1384 max_initcode_size: AtomicUsize::new(max_initcode_size),
1385 tx_gas_limit_cap: AtomicU64::new(tx_gas_limit_cap),
1386 };
1387
1388 EthTransactionValidator {
1389 client,
1390 chain_id,
1391 eip2718,
1392 eip1559,
1393 fork_tracker,
1394 eip4844,
1395 eip7702,
1396 block_gas_limit,
1397 tx_fee_cap,
1398 minimum_priority_fee,
1399 blob_store: Box::new(blob_store),
1400 kzg_settings,
1401 local_transactions_config,
1402 max_tx_input_bytes,
1403 max_tx_gas_limit,
1404 disable_balance_check,
1405 evm_config,
1406 _marker: Default::default(),
1407 validation_metrics: TxPoolValidationMetrics::default(),
1408 other_tx_types,
1409 eip7594,
1410 additional_stateless_validation: None,
1411 additional_stateful_validation: None,
1412 }
1413 }
1414
1415 pub fn build_with_tasks<Tx, S>(
1422 self,
1423 tasks: Runtime,
1424 blob_store: S,
1425 ) -> TransactionValidationTaskExecutor<EthTransactionValidator<Client, Tx, Evm>>
1426 where
1427 S: BlobStore,
1428 {
1429 let additional_tasks = self.additional_tasks;
1430 let validator = self.build::<Tx, S>(blob_store);
1431 TransactionValidationTaskExecutor::spawn(validator, &tasks, additional_tasks)
1432 }
1433}
1434
1435#[derive(Debug)]
1437pub struct ForkTracker {
1438 pub shanghai: AtomicBool,
1440 pub cancun: AtomicBool,
1442 pub prague: AtomicBool,
1444 pub osaka: AtomicBool,
1446 pub amsterdam: AtomicBool,
1448 pub max_blob_count: AtomicU64,
1450 pub tip_timestamp: AtomicU64,
1452 pub max_initcode_size: AtomicUsize,
1454 pub tx_gas_limit_cap: AtomicU64,
1456}
1457
1458impl ForkTracker {
1459 pub fn is_shanghai_activated(&self) -> bool {
1461 self.shanghai.load(std::sync::atomic::Ordering::Relaxed)
1462 }
1463
1464 pub fn is_cancun_activated(&self) -> bool {
1466 self.cancun.load(std::sync::atomic::Ordering::Relaxed)
1467 }
1468
1469 pub fn is_prague_activated(&self) -> bool {
1471 self.prague.load(std::sync::atomic::Ordering::Relaxed)
1472 }
1473
1474 pub fn is_osaka_activated(&self) -> bool {
1476 self.osaka.load(std::sync::atomic::Ordering::Relaxed)
1477 }
1478
1479 pub fn is_amsterdam_activated(&self) -> bool {
1481 self.amsterdam.load(std::sync::atomic::Ordering::Relaxed)
1482 }
1483
1484 pub fn tip_timestamp(&self) -> u64 {
1486 self.tip_timestamp.load(std::sync::atomic::Ordering::Relaxed)
1487 }
1488
1489 pub fn max_blob_count(&self) -> u64 {
1491 self.max_blob_count.load(std::sync::atomic::Ordering::Relaxed)
1492 }
1493}
1494
1495pub fn ensure_intrinsic_gas<T: EthPoolTransaction>(
1499 transaction: &T,
1500 fork_tracker: &ForkTracker,
1501) -> Result<(), InvalidPoolTransactionError> {
1502 use revm::primitives::hardfork::SpecId;
1503 let spec_id = if fork_tracker.is_amsterdam_activated() {
1504 SpecId::AMSTERDAM
1505 } else if fork_tracker.is_prague_activated() {
1506 SpecId::PRAGUE
1507 } else if fork_tracker.is_shanghai_activated() {
1508 SpecId::SHANGHAI
1509 } else {
1510 SpecId::MERGE
1511 };
1512
1513 let eip2780 = fork_tracker.is_amsterdam_activated().then(|| {
1516 revm::context_interface::cfg::gas_params::Eip2780TxInfo {
1517 value: transaction.value(),
1518 is_self_transfer: transaction.kind().to() == Some(&transaction.sender()),
1520 }
1521 });
1522
1523 let gas = revm::interpreter::gas::calculate_initial_tx_gas(
1524 spec_id,
1525 transaction.input(),
1526 transaction.is_create(),
1527 transaction.access_list().map(|l| l.len()).unwrap_or_default() as u64,
1528 transaction
1529 .access_list()
1530 .map(|l| l.iter().map(|i| i.storage_keys.len()).sum::<usize>())
1531 .unwrap_or_default() as u64,
1532 transaction.authorization_list().map(|l| l.len()).unwrap_or_default() as u64,
1533 eip2780,
1534 );
1535
1536 let gas_limit = transaction.gas_limit();
1537 if gas_limit < gas.initial_total_gas() || gas_limit < gas.floor_gas {
1538 Err(InvalidPoolTransactionError::IntrinsicGasTooLow)
1539 } else {
1540 Ok(())
1541 }
1542}
1543
1544#[cfg(test)]
1545mod tests {
1546 use super::*;
1547 use crate::{
1548 blobstore::InMemoryBlobStore, error::PoolErrorKind, test_utils::TransactionBuilder,
1549 traits::PoolTransaction, CoinbaseTipOrdering, EthPooledTransaction, Pool, TransactionPool,
1550 };
1551 use alloy_consensus::Transaction;
1552 use alloy_eips::{
1553 eip2718::{Decodable2718, Encodable2718},
1554 eip2930::{AccessList, AccessListItem},
1555 };
1556 use alloy_primitives::{hex, Address, B256, U256};
1557 use reth_ethereum_primitives::PooledTransactionVariant;
1558 use reth_evm_ethereum::EthEvmConfig;
1559 use reth_primitives_traits::SignedTransaction;
1560 use reth_provider::test_utils::{ExtendedAccount, MockEthProvider};
1561 use revm::primitives::eip3860::MAX_INITCODE_SIZE;
1562
1563 fn test_evm_config() -> EthEvmConfig {
1564 EthEvmConfig::mainnet()
1565 }
1566
1567 fn get_transaction() -> EthPooledTransaction {
1568 let raw = "0x02f914950181ad84b2d05e0085117553845b830f7df88080b9143a6040608081523462000414576200133a803803806200001e8162000419565b9283398101608082820312620004145781516001600160401b03908181116200041457826200004f9185016200043f565b92602092838201519083821162000414576200006d9183016200043f565b8186015190946001600160a01b03821692909183900362000414576060015190805193808511620003145760038054956001938488811c9816801562000409575b89891014620003f3578190601f988981116200039d575b50899089831160011462000336576000926200032a575b505060001982841b1c191690841b1781555b8751918211620003145760049788548481811c9116801562000309575b89821014620002f457878111620002a9575b5087908784116001146200023e5793839491849260009562000232575b50501b92600019911b1c19161785555b6005556007805460ff60a01b19169055600880546001600160a01b0319169190911790553015620001f3575060025469d3c21bcecceda100000092838201809211620001de57506000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9160025530835282815284832084815401905584519384523093a351610e889081620004b28239f35b601190634e487b7160e01b6000525260246000fd5b90606493519262461bcd60e51b845283015260248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b0151935038806200013a565b9190601f198416928a600052848a6000209460005b8c8983831062000291575050501062000276575b50505050811b0185556200014a565b01519060f884600019921b161c191690553880808062000267565b86860151895590970196948501948893500162000253565b89600052886000208880860160051c8201928b8710620002ea575b0160051c019085905b828110620002dd5750506200011d565b60008155018590620002cd565b92508192620002c4565b60228a634e487b7160e01b6000525260246000fd5b90607f16906200010b565b634e487b7160e01b600052604160045260246000fd5b015190503880620000dc565b90869350601f19831691856000528b6000209260005b8d8282106200038657505084116200036d575b505050811b018155620000ee565b015160001983861b60f8161c191690553880806200035f565b8385015186558a979095019493840193016200034c565b90915083600052896000208980850160051c8201928c8610620003e9575b918891869594930160051c01915b828110620003d9575050620000c5565b60008155859450889101620003c9565b92508192620003bb565b634e487b7160e01b600052602260045260246000fd5b97607f1697620000ae565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176200031457604052565b919080601f84011215620004145782516001600160401b038111620003145760209062000475601f8201601f1916830162000419565b92818452828287010111620004145760005b8181106200049d57508260009394955001015290565b85810183015184820184015282016200048756fe608060408181526004918236101561001657600080fd5b600092833560e01c91826306fdde0314610a1c57508163095ea7b3146109f257816318160ddd146109d35781631b4c84d2146109ac57816323b872dd14610833578163313ce5671461081757816339509351146107c357816370a082311461078c578163715018a6146107685781638124f7ac146107495781638da5cb5b1461072057816395d89b411461061d578163a457c2d714610575578163a9059cbb146104e4578163c9567bf914610120575063dd62ed3e146100d557600080fd5b3461011c578060031936011261011c57806020926100f1610b5a565b6100f9610b75565b6001600160a01b0391821683526001865283832091168252845220549051908152f35b5080fd5b905082600319360112610338576008546001600160a01b039190821633036104975760079283549160ff8360a01c1661045557737a250d5630b4cf539739df2c5dacb4c659f2488d92836bffffffffffffffffffffffff60a01b8092161786553087526020938785528388205430156104065730895260018652848920828a52865280858a205584519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925863092a38554835163c45a015560e01b815290861685828581845afa9182156103dd57849187918b946103e7575b5086516315ab88c960e31b815292839182905afa9081156103dd576044879289928c916103c0575b508b83895196879586946364e329cb60e11b8652308c870152166024850152165af19081156103b6579086918991610389575b50169060065416176006558385541660604730895288865260c4858a20548860085416928751958694859363f305d71960e01b8552308a86015260248501528d60448501528d606485015260848401524260a48401525af1801561037f579084929161034c575b50604485600654169587541691888551978894859363095ea7b360e01b855284015260001960248401525af1908115610343575061030c575b5050805460ff60a01b1916600160a01b17905580f35b81813d831161033c575b6103208183610b8b565b8101031261033857518015150361011c5738806102f6565b8280fd5b503d610316565b513d86823e3d90fd5b6060809293503d8111610378575b6103648183610b8b565b81010312610374578290386102bd565b8580fd5b503d61035a565b83513d89823e3d90fd5b6103a99150863d88116103af575b6103a18183610b8b565b810190610e33565b38610256565b503d610397565b84513d8a823e3d90fd5b6103d79150843d86116103af576103a18183610b8b565b38610223565b85513d8b823e3d90fd5b6103ff919450823d84116103af576103a18183610b8b565b92386101fb565b845162461bcd60e51b81528085018790526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b6020606492519162461bcd60e51b8352820152601760248201527f74726164696e6720697320616c7265616479206f70656e0000000000000000006044820152fd5b608490602084519162461bcd60e51b8352820152602160248201527f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6044820152603760f91b6064820152fd5b9050346103385781600319360112610338576104fe610b5a565b9060243593303303610520575b602084610519878633610bc3565b5160018152f35b600594919454808302908382041483151715610562576127109004820391821161054f5750925080602061050b565b634e487b7160e01b815260118552602490fd5b634e487b7160e01b825260118652602482fd5b9050823461061a578260031936011261061a57610590610b5a565b918360243592338152600160205281812060018060a01b03861682526020522054908282106105c9576020856105198585038733610d31565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152fd5b80fd5b83833461011c578160031936011261011c57805191809380549160019083821c92828516948515610716575b6020958686108114610703578589529081156106df5750600114610687575b6106838787610679828c0383610b8b565b5191829182610b11565b0390f35b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106106cc57505050826106839461067992820101948680610668565b80548685018801529286019281016106ae565b60ff19168887015250505050151560051b8301019250610679826106838680610668565b634e487b7160e01b845260228352602484fd5b93607f1693610649565b50503461011c578160031936011261011c5760085490516001600160a01b039091168152602090f35b50503461011c578160031936011261011c576020906005549051908152f35b833461061a578060031936011261061a57600880546001600160a01b031916905580f35b50503461011c57602036600319011261011c5760209181906001600160a01b036107b4610b5a565b16815280845220549051908152f35b82843461061a578160031936011261061a576107dd610b5a565b338252600160209081528383206001600160a01b038316845290528282205460243581019290831061054f57602084610519858533610d31565b50503461011c578160031936011261011c576020905160128152f35b83833461011c57606036600319011261011c5761084e610b5a565b610856610b75565b6044359160018060a01b0381169485815260209560018752858220338352875285822054976000198903610893575b505050906105199291610bc3565b85891061096957811561091a5733156108cc5750948481979861051997845260018a528284203385528a52039120558594938780610885565b865162461bcd60e51b8152908101889052602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b865162461bcd60e51b81529081018890526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b865162461bcd60e51b8152908101889052601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b50503461011c578160031936011261011c5760209060ff60075460a01c1690519015158152f35b50503461011c578160031936011261011c576020906002549051908152f35b50503461011c578060031936011261011c57602090610519610a12610b5a565b6024359033610d31565b92915034610b0d5783600319360112610b0d57600354600181811c9186908281168015610b03575b6020958686108214610af05750848852908115610ace5750600114610a75575b6106838686610679828b0383610b8b565b929550600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b828410610abb575050508261068394610679928201019438610a64565b8054868501880152928601928101610a9e565b60ff191687860152505050151560051b83010192506106798261068338610a64565b634e487b7160e01b845260229052602483fd5b93607f1693610a44565b8380fd5b6020808252825181830181905290939260005b828110610b4657505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610b24565b600435906001600160a01b0382168203610b7057565b600080fd5b602435906001600160a01b0382168203610b7057565b90601f8019910116810190811067ffffffffffffffff821117610bad57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03908116918215610cde5716918215610c8d57600082815280602052604081205491808310610c3957604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815220818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b6001600160a01b03908116918215610de25716918215610d925760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b90816020910312610b7057516001600160a01b0381168103610b70579056fea2646970667358221220285c200b3978b10818ff576bb83f2dc4a2a7c98dfb6a36ea01170de792aa652764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d3fd4f95820a9aa848ce716d6c200eaefb9a2e4900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000003543131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035431310000000000000000000000000000000000000000000000000000000000c001a04e551c75810ffdfe6caff57da9f5a8732449f42f0f4c57f935b05250a76db3b6a046cd47e6d01914270c1ec0d9ac7fae7dfb240ec9a8b6ec7898c4d6aa174388f2";
1569
1570 let data = hex::decode(raw).unwrap();
1571 let tx = PooledTransactionVariant::decode_2718(&mut data.as_ref()).unwrap();
1572
1573 EthPooledTransaction::from_pooled(tx.try_into_recovered().unwrap())
1574 }
1575
1576 fn eip1559_tx(
1577 to: Address,
1578 sender: Address,
1579 value: u64,
1580 gas_limit: u64,
1581 ) -> EthPooledTransaction {
1582 let tx = alloy_consensus::TxEip1559 {
1583 chain_id: 1,
1584 nonce: 0,
1585 gas_limit,
1586 max_fee_per_gas: 1,
1587 max_priority_fee_per_gas: 0,
1588 to: to.into(),
1589 value: U256::from(value),
1590 ..Default::default()
1591 };
1592 let signed = reth_ethereum_primitives::TransactionSigned::new_unhashed(
1593 tx.into(),
1594 alloy_primitives::Signature::test_signature(),
1595 );
1596 EthPooledTransaction::new(
1597 alloy_consensus::transaction::Recovered::new_unchecked(signed, sender),
1598 200,
1599 )
1600 }
1601
1602 #[test]
1606 fn intrinsic_gas_eip2780() {
1607 let sender = Address::repeat_byte(1);
1608 let recipient = Address::repeat_byte(2);
1609
1610 let amsterdam = || ForkTracker {
1611 shanghai: true.into(),
1612 cancun: true.into(),
1613 prague: true.into(),
1614 osaka: true.into(),
1615 amsterdam: true.into(),
1616 tip_timestamp: 0.into(),
1617 max_blob_count: 0.into(),
1618 max_initcode_size: AtomicUsize::new(MAX_INITCODE_SIZE),
1619 tx_gas_limit_cap: AtomicU64::new(0),
1620 };
1621 let pre_amsterdam = || ForkTracker { amsterdam: false.into(), ..amsterdam() };
1622
1623 let self_transfer = eip1559_tx(sender, sender, 1, 15_000);
1625 assert!(ensure_intrinsic_gas(&self_transfer, &amsterdam()).is_ok());
1626 assert!(ensure_intrinsic_gas(&self_transfer, &pre_amsterdam()).is_err());
1627
1628 let zero_value = eip1559_tx(recipient, sender, 0, 15_000);
1630 assert!(ensure_intrinsic_gas(&zero_value, &amsterdam()).is_ok());
1631 assert!(
1632 ensure_intrinsic_gas(&eip1559_tx(recipient, sender, 0, 14_999), &amsterdam()).is_err()
1633 );
1634
1635 assert!(
1637 ensure_intrinsic_gas(&eip1559_tx(recipient, sender, 1, 15_000), &amsterdam()).is_err()
1638 );
1639 assert!(
1640 ensure_intrinsic_gas(&eip1559_tx(recipient, sender, 1, 21_000), &amsterdam()).is_ok()
1641 );
1642 }
1643
1644 #[tokio::test]
1646 async fn validate_transaction() {
1647 let transaction = get_transaction();
1648 let mut fork_tracker = ForkTracker {
1649 shanghai: false.into(),
1650 cancun: false.into(),
1651 prague: false.into(),
1652 osaka: false.into(),
1653 amsterdam: false.into(),
1654 tip_timestamp: 0.into(),
1655 max_blob_count: 0.into(),
1656 max_initcode_size: AtomicUsize::new(MAX_INITCODE_SIZE),
1657 tx_gas_limit_cap: AtomicU64::new(0),
1658 };
1659
1660 let res = ensure_intrinsic_gas(&transaction, &fork_tracker);
1661 assert!(res.is_ok());
1662
1663 fork_tracker.shanghai = true.into();
1664 let res = ensure_intrinsic_gas(&transaction, &fork_tracker);
1665 assert!(res.is_ok());
1666
1667 let provider = MockEthProvider::default().with_genesis_block();
1668 provider.add_account(
1669 transaction.sender(),
1670 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1671 );
1672 let blob_store = InMemoryBlobStore::default();
1673 let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config())
1674 .build(blob_store.clone());
1675
1676 let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());
1677
1678 assert!(outcome.is_valid());
1679
1680 let pool =
1681 Pool::new(validator, CoinbaseTipOrdering::default(), blob_store, Default::default());
1682
1683 let res = pool.add_external_transaction(transaction.clone()).await;
1684 assert!(res.is_ok());
1685 let tx = pool.get(transaction.hash());
1686 assert!(tx.is_some());
1687 }
1688
1689 #[test]
1690 fn validates_configured_chain_id() {
1691 let provider = MockEthProvider::default().with_genesis_block();
1692 let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config())
1693 .build(InMemoryBlobStore::default());
1694 let transaction = |chain_id| {
1695 EthPooledTransaction::try_from_consensus(
1696 TransactionBuilder::default()
1697 .chain_id(chain_id)
1698 .gas_limit(21_000)
1699 .to(Address::ZERO)
1700 .into_eip1559()
1701 .try_into_recovered()
1702 .unwrap(),
1703 )
1704 .unwrap()
1705 };
1706
1707 assert!(validator
1708 .validate_stateless(TransactionOrigin::External, &transaction(validator.chain_id()))
1709 .is_ok());
1710 assert!(matches!(
1711 validator.validate_stateless(
1712 TransactionOrigin::External,
1713 &transaction(validator.chain_id() + 1)
1714 ),
1715 Err(InvalidPoolTransactionError::Consensus(InvalidTransactionError::ChainIdMismatch))
1716 ));
1717 }
1718
1719 #[tokio::test]
1721 async fn invalid_on_gas_limit_too_high() {
1722 let transaction = get_transaction();
1723
1724 let provider = MockEthProvider::default().with_genesis_block();
1725 provider.add_account(
1726 transaction.sender(),
1727 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1728 );
1729
1730 let blob_store = InMemoryBlobStore::default();
1731 let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config())
1732 .set_block_gas_limit(1_000_000) .build(blob_store.clone());
1734
1735 let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());
1736
1737 assert!(outcome.is_invalid());
1738
1739 let pool =
1740 Pool::new(validator, CoinbaseTipOrdering::default(), blob_store, Default::default());
1741
1742 let res = pool.add_external_transaction(transaction.clone()).await;
1743 assert!(res.is_err());
1744 assert!(matches!(
1745 res.unwrap_err().kind,
1746 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::ExceedsGasLimit(
1747 1_015_288, 1_000_000
1748 ))
1749 ));
1750 let tx = pool.get(transaction.hash());
1751 assert!(tx.is_none());
1752 }
1753
1754 #[tokio::test]
1755 async fn invalid_on_fee_cap_exceeded() {
1756 let transaction = get_transaction();
1757 let provider = MockEthProvider::default().with_genesis_block();
1758 provider.add_account(
1759 transaction.sender(),
1760 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1761 );
1762
1763 let blob_store = InMemoryBlobStore::default();
1764 let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config())
1765 .set_tx_fee_cap(100) .build(blob_store.clone());
1767
1768 let outcome = validator.validate_one(TransactionOrigin::Local, transaction.clone());
1769 assert!(outcome.is_invalid());
1770
1771 if let TransactionValidationOutcome::Invalid(_, err) = outcome {
1772 assert!(matches!(
1773 err,
1774 InvalidPoolTransactionError::ExceedsFeeCap { max_tx_fee_wei, tx_fee_cap_wei }
1775 if (max_tx_fee_wei > tx_fee_cap_wei)
1776 ));
1777 }
1778
1779 let pool =
1780 Pool::new(validator, CoinbaseTipOrdering::default(), blob_store, Default::default());
1781 let res = pool.add_transaction(TransactionOrigin::Local, transaction.clone()).await;
1782 assert!(res.is_err());
1783 assert!(matches!(
1784 res.unwrap_err().kind,
1785 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::ExceedsFeeCap { .. })
1786 ));
1787 let tx = pool.get(transaction.hash());
1788 assert!(tx.is_none());
1789 }
1790
1791 #[tokio::test]
1792 async fn valid_on_zero_fee_cap() {
1793 let transaction = get_transaction();
1794 let provider = MockEthProvider::default().with_genesis_block();
1795 provider.add_account(
1796 transaction.sender(),
1797 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1798 );
1799
1800 let blob_store = InMemoryBlobStore::default();
1801 let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet())
1802 .set_tx_fee_cap(0) .build(blob_store);
1804
1805 let outcome = validator.validate_one(TransactionOrigin::Local, transaction);
1806 assert!(outcome.is_valid());
1807 }
1808
1809 #[tokio::test]
1810 async fn valid_on_normal_fee_cap() {
1811 let transaction = get_transaction();
1812 let provider = MockEthProvider::default().with_genesis_block();
1813 provider.add_account(
1814 transaction.sender(),
1815 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1816 );
1817
1818 let blob_store = InMemoryBlobStore::default();
1819 let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet())
1820 .set_tx_fee_cap(2e18 as u128) .build(blob_store);
1822
1823 let outcome = validator.validate_one(TransactionOrigin::Local, transaction);
1824 assert!(outcome.is_valid());
1825 }
1826
1827 #[tokio::test]
1828 async fn invalid_on_max_tx_gas_limit_exceeded() {
1829 let transaction = get_transaction();
1830 let provider = MockEthProvider::default().with_genesis_block();
1831 provider.add_account(
1832 transaction.sender(),
1833 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1834 );
1835
1836 let blob_store = InMemoryBlobStore::default();
1837 let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet())
1838 .with_max_tx_gas_limit(Some(500_000)) .build(blob_store.clone());
1840
1841 let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());
1842 assert!(outcome.is_invalid());
1843
1844 let pool =
1845 Pool::new(validator, CoinbaseTipOrdering::default(), blob_store, Default::default());
1846
1847 let res = pool.add_external_transaction(transaction.clone()).await;
1848 assert!(res.is_err());
1849 assert!(matches!(
1850 res.unwrap_err().kind,
1851 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::MaxTxGasLimitExceeded(
1852 1_015_288, 500_000
1853 ))
1854 ));
1855 let tx = pool.get(transaction.hash());
1856 assert!(tx.is_none());
1857 }
1858
1859 #[tokio::test]
1860 async fn valid_on_max_tx_gas_limit_disabled() {
1861 let transaction = get_transaction();
1862 let provider = MockEthProvider::default().with_genesis_block();
1863 provider.add_account(
1864 transaction.sender(),
1865 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1866 );
1867
1868 let blob_store = InMemoryBlobStore::default();
1869 let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet())
1870 .with_max_tx_gas_limit(None) .build(blob_store);
1872
1873 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
1874 assert!(outcome.is_valid());
1875 }
1876
1877 #[tokio::test]
1878 async fn valid_on_max_tx_gas_limit_within_limit() {
1879 let transaction = get_transaction();
1880 let provider = MockEthProvider::default().with_genesis_block();
1881 provider.add_account(
1882 transaction.sender(),
1883 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1884 );
1885
1886 let blob_store = InMemoryBlobStore::default();
1887 let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet())
1888 .with_max_tx_gas_limit(Some(2_000_000)) .build(blob_store);
1890
1891 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
1892 assert!(outcome.is_valid());
1893 }
1894
1895 fn setup_priority_fee_test() -> (EthPooledTransaction, MockEthProvider) {
1897 let transaction = get_transaction();
1898 let provider = MockEthProvider::default().with_genesis_block();
1899 provider.add_account(
1900 transaction.sender(),
1901 ExtendedAccount::new(transaction.nonce(), U256::MAX),
1902 );
1903 (transaction, provider)
1904 }
1905
1906 fn create_validator_with_minimum_fee(
1908 provider: MockEthProvider,
1909 minimum_priority_fee: Option<u128>,
1910 local_config: Option<LocalTransactionConfig>,
1911 ) -> EthTransactionValidator<MockEthProvider, EthPooledTransaction, EthEvmConfig> {
1912 let blob_store = InMemoryBlobStore::default();
1913 let mut builder = EthTransactionValidatorBuilder::new(provider, test_evm_config())
1914 .with_minimum_priority_fee(minimum_priority_fee);
1915
1916 if let Some(config) = local_config {
1917 builder = builder.with_local_transactions_config(config);
1918 }
1919
1920 builder.build(blob_store)
1921 }
1922
1923 #[tokio::test]
1924 async fn invalid_on_priority_fee_lower_than_configured_minimum() {
1925 let (transaction, provider) = setup_priority_fee_test();
1926
1927 assert!(transaction.is_dynamic_fee());
1929
1930 let minimum_priority_fee =
1932 transaction.max_priority_fee_per_gas().expect("priority fee is expected") * 2;
1933
1934 let validator =
1935 create_validator_with_minimum_fee(provider, Some(minimum_priority_fee), None);
1936
1937 let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());
1939 assert!(outcome.is_invalid());
1940
1941 if let TransactionValidationOutcome::Invalid(_, err) = outcome {
1942 assert!(matches!(
1943 err,
1944 InvalidPoolTransactionError::PriorityFeeBelowMinimum { minimum_priority_fee: min_fee }
1945 if min_fee == minimum_priority_fee
1946 ));
1947 }
1948
1949 let blob_store = InMemoryBlobStore::default();
1951 let pool =
1952 Pool::new(validator, CoinbaseTipOrdering::default(), blob_store, Default::default());
1953
1954 let res = pool.add_external_transaction(transaction.clone()).await;
1955 assert!(res.is_err());
1956 assert!(matches!(
1957 res.unwrap_err().kind,
1958 PoolErrorKind::InvalidTransaction(
1959 InvalidPoolTransactionError::PriorityFeeBelowMinimum { .. }
1960 )
1961 ));
1962 let tx = pool.get(transaction.hash());
1963 assert!(tx.is_none());
1964
1965 let (_, local_provider) = setup_priority_fee_test();
1967 let validator_local =
1968 create_validator_with_minimum_fee(local_provider, Some(minimum_priority_fee), None);
1969
1970 let local_outcome = validator_local.validate_one(TransactionOrigin::Local, transaction);
1971 assert!(local_outcome.is_valid());
1972 }
1973
1974 #[tokio::test]
1975 async fn valid_on_priority_fee_equal_to_minimum() {
1976 let (transaction, provider) = setup_priority_fee_test();
1977
1978 let tx_priority_fee =
1980 transaction.max_priority_fee_per_gas().expect("priority fee is expected");
1981 let validator = create_validator_with_minimum_fee(provider, Some(tx_priority_fee), None);
1982
1983 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
1984 assert!(outcome.is_valid());
1985 }
1986
1987 #[tokio::test]
1988 async fn valid_on_priority_fee_above_minimum() {
1989 let (transaction, provider) = setup_priority_fee_test();
1990
1991 let tx_priority_fee =
1993 transaction.max_priority_fee_per_gas().expect("priority fee is expected");
1994 let minimum_priority_fee = tx_priority_fee / 2; let validator =
1997 create_validator_with_minimum_fee(provider, Some(minimum_priority_fee), None);
1998
1999 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
2000 assert!(outcome.is_valid());
2001 }
2002
2003 #[tokio::test]
2004 async fn valid_on_minimum_priority_fee_disabled() {
2005 let (transaction, provider) = setup_priority_fee_test();
2006
2007 let validator = create_validator_with_minimum_fee(provider, None, None);
2009
2010 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
2011 assert!(outcome.is_valid());
2012 }
2013
2014 #[tokio::test]
2015 async fn priority_fee_validation_applies_to_private_transactions() {
2016 let (transaction, provider) = setup_priority_fee_test();
2017
2018 let minimum_priority_fee =
2020 transaction.max_priority_fee_per_gas().expect("priority fee is expected") * 2;
2021
2022 let validator =
2023 create_validator_with_minimum_fee(provider, Some(minimum_priority_fee), None);
2024
2025 let outcome = validator.validate_one(TransactionOrigin::Private, transaction);
2028 assert!(outcome.is_invalid());
2029
2030 if let TransactionValidationOutcome::Invalid(_, err) = outcome {
2031 assert!(matches!(
2032 err,
2033 InvalidPoolTransactionError::PriorityFeeBelowMinimum { minimum_priority_fee: min_fee }
2034 if min_fee == minimum_priority_fee
2035 ));
2036 }
2037 }
2038
2039 #[tokio::test]
2040 async fn valid_on_local_config_exempts_private_transactions() {
2041 let (transaction, provider) = setup_priority_fee_test();
2042
2043 let minimum_priority_fee =
2045 transaction.max_priority_fee_per_gas().expect("priority fee is expected") * 2;
2046
2047 let local_config =
2049 LocalTransactionConfig { propagate_local_transactions: true, ..Default::default() };
2050
2051 let validator = create_validator_with_minimum_fee(
2052 provider,
2053 Some(minimum_priority_fee),
2054 Some(local_config),
2055 );
2056
2057 let outcome = validator.validate_one(TransactionOrigin::Private, transaction);
2061 assert!(outcome.is_invalid()); }
2063
2064 #[test]
2065 fn reject_oversized_tx() {
2066 let mut transaction = get_transaction();
2067 transaction.encoded_length = DEFAULT_MAX_TX_INPUT_BYTES + 1;
2068 let provider = MockEthProvider::default().with_genesis_block();
2069
2070 let validator = create_validator_with_minimum_fee(provider, None, None);
2072
2073 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
2074 let invalid = outcome.as_invalid().unwrap();
2075 assert!(invalid.is_oversized());
2076 }
2077
2078 #[test]
2079 fn reject_blob_tx_with_oversized_access_list() {
2080 let max_tx_input_bytes = 512;
2081 let provider = MockEthProvider::default().with_genesis_block();
2082 let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config())
2083 .with_max_tx_input_bytes(max_tx_input_bytes)
2084 .build(InMemoryBlobStore::default());
2085
2086 let blob_tx_with_access_list = |storage_keys: usize| {
2087 let access_list = AccessList(vec![AccessListItem {
2088 address: Address::random(),
2089 storage_keys: (0..storage_keys).map(|_| B256::random()).collect(),
2090 }]);
2091 let tx = TransactionBuilder::default()
2092 .access_list(access_list)
2093 .into_eip4844()
2094 .try_into_recovered()
2095 .unwrap();
2096 let encoded_length = tx.encode_2718_len();
2097 EthPooledTransaction::new(tx, encoded_length)
2098 };
2099
2100 let is_oversized = |tx: &EthPooledTransaction| {
2101 matches!(
2102 validator.validate_stateless(TransactionOrigin::External, tx),
2103 Err(InvalidPoolTransactionError::OversizedData { .. })
2104 )
2105 };
2106
2107 let small = blob_tx_with_access_list(1);
2108 assert!(!is_oversized(&small));
2109
2110 let large = blob_tx_with_access_list(64);
2111 assert!(large.input().is_empty());
2112 assert!(is_oversized(&large));
2113 }
2114
2115 #[tokio::test]
2116 async fn valid_with_disabled_balance_check() {
2117 let transaction = get_transaction();
2118 let provider = MockEthProvider::default().with_genesis_block();
2119
2120 provider.add_account(
2122 transaction.sender(),
2123 ExtendedAccount::new(transaction.nonce(), alloy_primitives::U256::ZERO),
2124 );
2125
2126 let validator =
2128 EthTransactionValidatorBuilder::new(provider.clone(), EthEvmConfig::mainnet())
2129 .build(InMemoryBlobStore::default());
2130
2131 let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone());
2132 let expected_cost = *transaction.cost();
2133 if let TransactionValidationOutcome::Invalid(_, err) = outcome {
2134 assert!(matches!(
2135 err,
2136 InvalidPoolTransactionError::Consensus(InvalidTransactionError::InsufficientFunds(ref funds_err))
2137 if funds_err.got == alloy_primitives::U256::ZERO && funds_err.expected == expected_cost
2138 ));
2139 } else {
2140 panic!("Expected Invalid outcome with InsufficientFunds error");
2141 }
2142
2143 let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet())
2145 .disable_balance_check()
2146 .build(InMemoryBlobStore::default());
2147
2148 let outcome = validator.validate_one(TransactionOrigin::External, transaction);
2149 assert!(outcome.is_valid()); }
2151}