@@ -13,9 +13,10 @@ use alloy_evm::{
1313 } ,
1414 eth:: { EthBlockExecutor , EthTxResult } ,
1515} ;
16- use alloy_sol_types:: SolEvent as _;
16+ use alloy_sol_types:: { SolCall as _ , SolEvent as _} ;
1717use reth_evm:: block:: StateDB ;
1818use reth_revm:: { Inspector , context:: result:: ResultAndState } ;
19+ use tempo_chainspec:: hardfork:: TempoHardfork ;
1920use tempo_evm:: { TempoBlockExecutionCtx , TempoReceiptBuilder } ;
2021use tempo_primitives:: { TempoReceipt , TempoTxEnvelope , TempoTxType } ;
2122use tempo_revm:: evm:: TempoContext ;
@@ -41,15 +42,19 @@ enum ZoneBlockPhase {
4142}
4243
4344impl ZoneBlockPhase {
44- fn validate_transaction ( self , tx : & TempoTxEnvelope ) -> Result < Self , BlockExecutionError > {
45+ fn validate_transaction (
46+ self ,
47+ tx : & TempoTxEnvelope ,
48+ spec : TempoHardfork ,
49+ ) -> Result < Self , BlockExecutionError > {
4550 if tx. subblock_proposer ( ) . is_some ( ) {
4651 return Err ( BlockValidationError :: msg (
4752 "subblock transactions are not supported in zone blocks" ,
4853 )
4954 . into ( ) ) ;
5055 }
5156
52- let tx_kind = ZoneTransactionKind :: classify ( tx) ;
57+ let tx_kind = ZoneTransactionKind :: classify ( tx, spec ) ;
5358
5459 match ( self , tx_kind) {
5560 ( Self :: AwaitingAdvanceTempo , ZoneTransactionKind :: AdvanceTempo ) => Ok ( Self :: Executing ) ,
@@ -94,7 +99,7 @@ enum ZoneTransactionKind {
9499}
95100
96101impl ZoneTransactionKind {
97- fn classify ( tx : & TempoTxEnvelope ) -> Self {
102+ fn classify ( tx : & TempoTxEnvelope , spec : TempoHardfork ) -> Self {
98103 if !tx. is_system_tx ( ) {
99104 return Self :: Regular ;
100105 }
@@ -106,7 +111,13 @@ impl ZoneTransactionKind {
106111 }
107112
108113 if tx. calls ( ) . any ( |( kind, input) | {
109- kind. to ( ) == Some ( & ZONE_OUTBOX_ADDRESS ) && is_finalize_withdrawal_batch_calldata ( input)
114+ kind. to ( ) == Some ( & ZONE_OUTBOX_ADDRESS )
115+ && if spec. is_t11 ( ) {
116+ input. get ( ..4 )
117+ == Some ( IZoneOutbox :: finalizeWithdrawalBatchCall:: SELECTOR . as_slice ( ) )
118+ } else {
119+ is_finalize_withdrawal_batch_calldata ( input)
120+ }
110121 } ) {
111122 return Self :: FinalizeWithdrawalBatch ;
112123 }
@@ -208,7 +219,8 @@ where
208219 tempo_tx_env. expiring_nonce_idx = None ;
209220 }
210221
211- let next_phase = self . phase . validate_transaction ( recovered. tx ( ) ) ?;
222+ let spec = self . evm ( ) . ctx ( ) . cfg . spec ;
223+ let next_phase = self . phase . validate_transaction ( recovered. tx ( ) , spec) ?;
212224
213225 let result = self
214226 . inner
@@ -285,7 +297,7 @@ mod tests {
285297 use reth_chainspec:: EthChainSpec as _;
286298 use reth_primitives_traits:: Recovered ;
287299 use revm:: database:: { CacheDB , EmptyDB } ;
288- use tempo_chainspec:: spec:: DEV ;
300+ use tempo_chainspec:: { hardfork :: TempoHardfork , spec:: DEV } ;
289301 use tempo_evm:: TempoBlockExecutionCtx ;
290302 use tempo_precompiles:: {
291303 DEFAULT_FEE_TOKEN , TIP_FEE_MANAGER_ADDRESS ,
@@ -399,7 +411,9 @@ mod tests {
399411 let advance_tempo = advance_tempo_tx ( ) ;
400412 let mut phase = ZoneBlockPhase :: AwaitingAdvanceTempo ;
401413
402- let next_phase = phase. validate_transaction ( & advance_tempo) . unwrap ( ) ;
414+ let next_phase = phase
415+ . validate_transaction ( & advance_tempo, TempoHardfork :: T11 )
416+ . unwrap ( ) ;
403417 assert_eq ! ( phase, ZoneBlockPhase :: AwaitingAdvanceTempo ) ;
404418 assert_eq ! ( next_phase, ZoneBlockPhase :: Executing ) ;
405419
@@ -414,15 +428,15 @@ mod tests {
414428 let ordinary = system_tx ( Address :: ZERO , Bytes :: new ( ) ) ;
415429
416430 let missing_first = ZoneBlockPhase :: AwaitingAdvanceTempo
417- . validate_transaction ( & ordinary)
431+ . validate_transaction ( & ordinary, TempoHardfork :: T11 )
418432 . unwrap_err ( ) ;
419433 assert_eq ! (
420434 missing_first. to_string( ) ,
421435 "advanceTempo must be the first transaction in a zone block"
422436 ) ;
423437
424438 let duplicate = ZoneBlockPhase :: Executing
425- . validate_transaction ( & advance_tempo)
439+ . validate_transaction ( & advance_tempo, TempoHardfork :: T11 )
426440 . unwrap_err ( ) ;
427441 assert_eq ! (
428442 duplicate. to_string( ) ,
@@ -435,7 +449,7 @@ mod tests {
435449 let finalize = finalize_withdrawal_batch_tx ( ) ;
436450 assert_eq ! (
437451 ZoneBlockPhase :: Executing
438- . validate_transaction( & finalize)
452+ . validate_transaction( & finalize, TempoHardfork :: T11 )
439453 . unwrap( ) ,
440454 ZoneBlockPhase :: WithdrawalsFinalized
441455 ) ;
@@ -449,7 +463,7 @@ mod tests {
449463 . into ( ) ,
450464 ) ;
451465 let error = ZoneBlockPhase :: Executing
452- . validate_transaction ( & setter)
466+ . validate_transaction ( & setter, TempoHardfork :: T11 )
453467 . unwrap_err ( ) ;
454468 assert_eq ! (
455469 error. to_string( ) ,
@@ -462,21 +476,97 @@ mod tests {
462476 Bytes :: copy_from_slice ( & IZoneOutbox :: finalizeWithdrawalBatchCall:: SELECTOR ) ,
463477 ) ;
464478 let error = ZoneBlockPhase :: Executing
465- . validate_transaction ( & malformed_finalize)
479+ . validate_transaction ( & malformed_finalize, TempoHardfork :: T10 )
480+ . unwrap_err ( ) ;
481+ assert_eq ! (
482+ error. to_string( ) ,
483+ "system transactions after advanceTempo must call \
484+ ZoneOutbox.finalizeWithdrawalBatch"
485+ ) ;
486+ assert_eq ! (
487+ ZoneBlockPhase :: Executing
488+ . validate_transaction( & malformed_finalize, TempoHardfork :: T11 )
489+ . unwrap( ) ,
490+ ZoneBlockPhase :: WithdrawalsFinalized
491+ ) ;
492+
493+ let mut trailing_calldata = IZoneOutbox :: finalizeWithdrawalBatchCall {
494+ count : U256 :: ZERO ,
495+ blockNumber : 1 ,
496+ encryptedSenders : vec ! [ ] ,
497+ }
498+ . abi_encode ( ) ;
499+ trailing_calldata. extend_from_slice ( & [ 0 ; 32 ] ) ;
500+ let trailing_finalize = system_tx ( ZONE_OUTBOX_ADDRESS , trailing_calldata. into ( ) ) ;
501+ let error = ZoneBlockPhase :: Executing
502+ . validate_transaction ( & trailing_finalize, TempoHardfork :: T10 )
466503 . unwrap_err ( ) ;
467504 assert_eq ! (
468505 error. to_string( ) ,
469506 "system transactions after advanceTempo must call \
470507 ZoneOutbox.finalizeWithdrawalBatch"
471508 ) ;
509+ assert_eq ! (
510+ ZoneBlockPhase :: Executing
511+ . validate_transaction( & trailing_finalize, TempoHardfork :: T11 )
512+ . unwrap( ) ,
513+ ZoneBlockPhase :: WithdrawalsFinalized
514+ ) ;
515+ }
516+
517+ #[ test]
518+ fn malformed_t11_finalization_does_not_advance_block_phase ( ) {
519+ let mut zone_genesis = DEV . genesis ( ) . clone ( ) ;
520+ zone_genesis. config . chain_id = zone_chain_id ( DEV . chain ( ) . id ( ) , 2 ) . unwrap ( ) ;
521+ let chain_spec = std:: sync:: Arc :: new ( ZoneChainSpec :: from_genesis ( zone_genesis) . unwrap ( ) ) ;
522+ let factory =
523+ ZoneEvmFactory :: new ( chain_spec. clone ( ) , MockL1Reader :: default ( ) , Address :: ZERO ) ;
524+ let mut env = EvmEnv :: default ( ) ;
525+ env. cfg_env . spec = TempoHardfork :: T11 ;
526+ let evm = factory. create_evm ( CacheDB :: new ( EmptyDB :: default ( ) ) , env) ;
527+ let ctx = TempoBlockExecutionCtx {
528+ inner : EthBlockExecutionCtx {
529+ parent_hash : B256 :: ZERO ,
530+ parent_beacon_block_root : None ,
531+ ommers : & [ ] ,
532+ withdrawals : None ,
533+ extra_data : Bytes :: new ( ) ,
534+ tx_count_hint : Some ( 1 ) ,
535+ slot_number : None ,
536+ } ,
537+ general_gas_limit : 0 ,
538+ shared_gas_limit : 0 ,
539+ validator_set : None ,
540+ consensus_context : None ,
541+ subblock_fee_recipients : Default :: default ( ) ,
542+ } ;
543+ let mut executor = ZoneBlockExecutor :: new ( evm, ctx, & chain_spec) ;
544+ executor. phase = ZoneBlockPhase :: Executing ;
545+
546+ let tx = Recovered :: new_unchecked (
547+ system_tx (
548+ ZONE_OUTBOX_ADDRESS ,
549+ Bytes :: copy_from_slice ( & IZoneOutbox :: finalizeWithdrawalBatchCall:: SELECTOR ) ,
550+ ) ,
551+ TEMPO_SYSTEM_TX_SENDER ,
552+ ) ;
553+ let error = executor. execute_transaction_without_commit ( tx) . unwrap_err ( ) ;
554+
555+ assert ! (
556+ error
557+ . to_string( )
558+ . contains( "system transaction execution failed" ) ,
559+ "unexpected error: {error}"
560+ ) ;
561+ assert_eq ! ( executor. phase, ZoneBlockPhase :: Executing ) ;
472562 }
473563
474564 #[ test]
475565 fn ordinary_transactions_are_allowed_after_advance_tempo ( ) {
476566 let ordinary = ordinary_tx ( Address :: ZERO , Bytes :: new ( ) ) ;
477567 assert_eq ! (
478568 ZoneBlockPhase :: Executing
479- . validate_transaction( & ordinary)
569+ . validate_transaction( & ordinary, TempoHardfork :: T11 )
480570 . unwrap( ) ,
481571 ZoneBlockPhase :: Executing
482572 ) ;
@@ -534,7 +624,9 @@ mod tests {
534624 ZoneBlockPhase :: Executing ,
535625 ZoneBlockPhase :: WithdrawalsFinalized ,
536626 ] {
537- let error = phase. validate_transaction ( & subblock) . unwrap_err ( ) ;
627+ let error = phase
628+ . validate_transaction ( & subblock, TempoHardfork :: T11 )
629+ . unwrap_err ( ) ;
538630 assert_eq ! (
539631 error. to_string( ) ,
540632 "subblock transactions are not supported in zone blocks"
@@ -549,12 +641,16 @@ mod tests {
549641 let advance_tempo = advance_tempo_tx ( ) ;
550642 let mut phase = ZoneBlockPhase :: Executing ;
551643
552- let next_phase = phase. validate_transaction ( & finalize) . unwrap ( ) ;
644+ let next_phase = phase
645+ . validate_transaction ( & finalize, TempoHardfork :: T11 )
646+ . unwrap ( ) ;
553647 assert_eq ! ( phase, ZoneBlockPhase :: Executing ) ;
554648
555649 phase. advance_to ( next_phase) ;
556650 for tx in [ & ordinary, & finalize, & advance_tempo] {
557- let error = phase. validate_transaction ( tx) . unwrap_err ( ) ;
651+ let error = phase
652+ . validate_transaction ( tx, TempoHardfork :: T11 )
653+ . unwrap_err ( ) ;
558654 assert_eq ! (
559655 error. to_string( ) ,
560656 "finalizeWithdrawalBatch must be the last transaction in a zone block"
@@ -571,14 +667,14 @@ mod tests {
571667
572668 assert_eq ! (
573669 ZoneBlockPhase :: AwaitingAdvanceTempo
574- . validate_transaction( & advance)
670+ . validate_transaction( & advance, TempoHardfork :: T11 )
575671 . unwrap( ) ,
576672 ZoneBlockPhase :: Executing
577673 ) ;
578674 for tx in [ & regular, & finalize, & unexpected_system] {
579675 assert_eq ! (
580676 ZoneBlockPhase :: AwaitingAdvanceTempo
581- . validate_transaction( tx)
677+ . validate_transaction( tx, TempoHardfork :: T11 )
582678 . unwrap_err( )
583679 . to_string( ) ,
584680 "advanceTempo must be the first transaction in a zone block"
@@ -587,26 +683,26 @@ mod tests {
587683
588684 assert_eq ! (
589685 ZoneBlockPhase :: Executing
590- . validate_transaction( & regular)
686+ . validate_transaction( & regular, TempoHardfork :: T11 )
591687 . unwrap( ) ,
592688 ZoneBlockPhase :: Executing
593689 ) ;
594690 assert_eq ! (
595691 ZoneBlockPhase :: Executing
596- . validate_transaction( & finalize)
692+ . validate_transaction( & finalize, TempoHardfork :: T11 )
597693 . unwrap( ) ,
598694 ZoneBlockPhase :: WithdrawalsFinalized
599695 ) ;
600696 assert_eq ! (
601697 ZoneBlockPhase :: Executing
602- . validate_transaction( & advance)
698+ . validate_transaction( & advance, TempoHardfork :: T11 )
603699 . unwrap_err( )
604700 . to_string( ) ,
605701 "advanceTempo must only execute once per zone block"
606702 ) ;
607703 assert_eq ! (
608704 ZoneBlockPhase :: Executing
609- . validate_transaction( & unexpected_system)
705+ . validate_transaction( & unexpected_system, TempoHardfork :: T11 )
610706 . unwrap_err( )
611707 . to_string( ) ,
612708 "system transactions after advanceTempo must call \
@@ -616,7 +712,7 @@ mod tests {
616712 for tx in [ & advance, & regular, & finalize, & unexpected_system] {
617713 assert_eq ! (
618714 ZoneBlockPhase :: WithdrawalsFinalized
619- . validate_transaction( tx)
715+ . validate_transaction( tx, TempoHardfork :: T11 )
620716 . unwrap_err( )
621717 . to_string( ) ,
622718 "finalizeWithdrawalBatch must be the last transaction in a zone block"
@@ -630,8 +726,12 @@ mod tests {
630726 let ordinary = ordinary_tx ( Address :: ZERO , Bytes :: new ( ) ) ;
631727 let mut phase = ZoneBlockPhase :: Executing ;
632728
633- let ordinary_phase = phase. validate_transaction ( & ordinary) . unwrap ( ) ;
634- let finalized_phase = phase. validate_transaction ( & finalize) . unwrap ( ) ;
729+ let ordinary_phase = phase
730+ . validate_transaction ( & ordinary, TempoHardfork :: T11 )
731+ . unwrap ( ) ;
732+ let finalized_phase = phase
733+ . validate_transaction ( & finalize, TempoHardfork :: T11 )
734+ . unwrap ( ) ;
635735 assert_eq ! ( phase, ZoneBlockPhase :: Executing ) ;
636736
637737 phase. advance_to ( finalized_phase) ;
@@ -657,22 +757,22 @@ mod tests {
657757 ) ;
658758
659759 assert_eq ! (
660- ZoneTransactionKind :: classify( & advance_lookalike) ,
760+ ZoneTransactionKind :: classify( & advance_lookalike, TempoHardfork :: T11 ) ,
661761 ZoneTransactionKind :: Regular
662762 ) ;
663763 assert_eq ! (
664- ZoneTransactionKind :: classify( & finalize_lookalike) ,
764+ ZoneTransactionKind :: classify( & finalize_lookalike, TempoHardfork :: T11 ) ,
665765 ZoneTransactionKind :: Regular
666766 ) ;
667767 assert_eq ! (
668768 ZoneBlockPhase :: Executing
669- . validate_transaction( & advance_lookalike)
769+ . validate_transaction( & advance_lookalike, TempoHardfork :: T11 )
670770 . unwrap( ) ,
671771 ZoneBlockPhase :: Executing
672772 ) ;
673773 assert_eq ! (
674774 ZoneBlockPhase :: Executing
675- . validate_transaction( & finalize_lookalike)
775+ . validate_transaction( & finalize_lookalike, TempoHardfork :: T11 )
676776 . unwrap( ) ,
677777 ZoneBlockPhase :: Executing
678778 ) ;
0 commit comments