@@ -3940,4 +3940,108 @@ mod tests {
39403940 assert_eq ! ( t2. id( ) , tx2. id( ) ) ;
39413941 assert_eq ! ( t3. id( ) , tx3. id( ) ) ;
39423942 }
3943+
3944+ #[ test]
3945+ fn test_non_4844_blob_fee_bit_invariant ( ) {
3946+ let mut f = MockTransactionFactory :: default ( ) ;
3947+ let mut pool = TxPool :: new ( MockOrdering :: default ( ) , Default :: default ( ) ) ;
3948+
3949+ let non_4844_tx = MockTransaction :: eip1559 ( ) . set_max_fee ( 200 ) . inc_limit ( ) ;
3950+ let validated = f. validated ( non_4844_tx. clone ( ) ) ;
3951+
3952+ assert ! ( !non_4844_tx. is_eip4844( ) ) ;
3953+ pool. add_transaction ( validated. clone ( ) , U256 :: from ( 10_000 ) , 0 , None ) . unwrap ( ) ;
3954+
3955+ // Core invariant: Non-4844 transactions must ALWAYS have ENOUGH_BLOB_FEE_CAP_BLOCK bit
3956+ let tx_meta = pool. all_transactions . txs . get ( validated. id ( ) ) . unwrap ( ) ;
3957+ assert ! ( tx_meta. state. contains( TxState :: ENOUGH_BLOB_FEE_CAP_BLOCK ) ) ;
3958+ assert_eq ! ( tx_meta. subpool, SubPool :: Pending ) ;
3959+ }
3960+
3961+ #[ test]
3962+ fn test_blob_fee_enforcement_only_applies_to_eip4844 ( ) {
3963+ let mut f = MockTransactionFactory :: default ( ) ;
3964+ let mut pool = TxPool :: new ( MockOrdering :: default ( ) , Default :: default ( ) ) ;
3965+
3966+ // Set blob fee higher than EIP-4844 tx can afford
3967+ let mut block_info = pool. block_info ( ) ;
3968+ block_info. pending_blob_fee = Some ( 160 ) ;
3969+ block_info. pending_basefee = 100 ;
3970+ pool. set_block_info ( block_info) ;
3971+
3972+ let eip4844_tx = MockTransaction :: eip4844 ( )
3973+ . with_sender ( address ! ( "0x000000000000000000000000000000000000000a" ) )
3974+ . with_max_fee ( 200 )
3975+ . with_blob_fee ( 150 ) // Less than block blob fee (160)
3976+ . inc_limit ( ) ;
3977+
3978+ let non_4844_tx = MockTransaction :: eip1559 ( )
3979+ . with_sender ( address ! ( "0x000000000000000000000000000000000000000b" ) )
3980+ . set_max_fee ( 200 )
3981+ . inc_limit ( ) ;
3982+
3983+ let validated_4844 = f. validated ( eip4844_tx) ;
3984+ let validated_non_4844 = f. validated ( non_4844_tx) ;
3985+
3986+ pool. add_transaction ( validated_4844. clone ( ) , U256 :: from ( 10_000 ) , 0 , None ) . unwrap ( ) ;
3987+ pool. add_transaction ( validated_non_4844. clone ( ) , U256 :: from ( 10_000 ) , 0 , None ) . unwrap ( ) ;
3988+
3989+ let tx_4844_meta = pool. all_transactions . txs . get ( validated_4844. id ( ) ) . unwrap ( ) ;
3990+ let tx_non_4844_meta = pool. all_transactions . txs . get ( validated_non_4844. id ( ) ) . unwrap ( ) ;
3991+
3992+ // EIP-4844: blob fee enforcement applies - insufficient blob fee removes bit
3993+ assert ! ( !tx_4844_meta. state. contains( TxState :: ENOUGH_BLOB_FEE_CAP_BLOCK ) ) ;
3994+ assert_eq ! ( tx_4844_meta. subpool, SubPool :: Blob ) ;
3995+
3996+ // Non-4844: blob fee enforcement does NOT apply - bit always remains true
3997+ assert ! ( tx_non_4844_meta. state. contains( TxState :: ENOUGH_BLOB_FEE_CAP_BLOCK ) ) ;
3998+ assert_eq ! ( tx_non_4844_meta. subpool, SubPool :: Pending ) ;
3999+ }
4000+
4001+ #[ test]
4002+ fn test_basefee_decrease_preserves_non_4844_blob_fee_bit ( ) {
4003+ let mut f = MockTransactionFactory :: default ( ) ;
4004+ let mut pool = TxPool :: new ( MockOrdering :: default ( ) , Default :: default ( ) ) ;
4005+
4006+ // Create non-4844 transaction with fee that initially can't afford high basefee
4007+ let non_4844_tx = MockTransaction :: eip1559 ( )
4008+ . with_sender ( address ! ( "0x000000000000000000000000000000000000000a" ) )
4009+ . set_max_fee ( 500 ) // Can't afford basefee of 600
4010+ . inc_limit ( ) ;
4011+
4012+ // Set high basefee so transaction goes to BaseFee pool initially
4013+ pool. update_basefee ( 600 ) ;
4014+
4015+ let validated = f. validated ( non_4844_tx) ;
4016+ let tx_id = * validated. id ( ) ;
4017+ pool. add_transaction ( validated, U256 :: from ( 10_000 ) , 0 , None ) . unwrap ( ) ;
4018+
4019+ // Initially should be in BaseFee pool but STILL have blob fee bit (critical invariant)
4020+ let tx_meta = pool. all_transactions . txs . get ( & tx_id) . unwrap ( ) ;
4021+ assert_eq ! ( tx_meta. subpool, SubPool :: BaseFee ) ;
4022+ assert ! (
4023+ tx_meta. state. contains( TxState :: ENOUGH_BLOB_FEE_CAP_BLOCK ) ,
4024+ "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4025+ ) ;
4026+
4027+ // Decrease basefee - transaction should be promoted to Pending
4028+ // This is where PR #18215 bug would manifest: blob fee bit incorrectly removed
4029+ pool. update_basefee ( 400 ) ;
4030+
4031+ // After basefee decrease: should be promoted to Pending with blob fee bit preserved
4032+ let tx_meta = pool. all_transactions . txs . get ( & tx_id) . unwrap ( ) ;
4033+ assert_eq ! (
4034+ tx_meta. subpool,
4035+ SubPool :: Pending ,
4036+ "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4037+ ) ;
4038+ assert ! (
4039+ tx_meta. state. contains( TxState :: ENOUGH_BLOB_FEE_CAP_BLOCK ) ,
4040+ "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4041+ ) ;
4042+ assert ! (
4043+ tx_meta. state. contains( TxState :: ENOUGH_FEE_CAP_BLOCK ) ,
4044+ "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4045+ ) ;
4046+ }
39434047}
0 commit comments