@@ -355,7 +355,7 @@ def test_credit(
355355) -> None :
356356 """
357357 Test reserve balance violations for an EOA sending txs with various values,
358- where the exception rules are not enforced based on txs in invalid block .
358+ but also receiving a refill of entire reserve balance in the meantime .
359359 """
360360 # gas spend by transactions send in setup blocks
361361 prepare_tx_gas = (
@@ -461,6 +461,143 @@ def test_credit(
461461 )
462462
463463
464+ @pytest .mark .parametrize ("pre_delegated" , [True , False ])
465+ @pytest .mark .parametrize ("send_pos" , [(0 , 0 ), (2 , 0 )])
466+ @pytest .mark .parametrize ("credit_pos" , [(0 , 0 ), (0 , 1 ), (1 , 0 ), (2 , 1 )])
467+ @pytest .mark .parametrize (
468+ "send_value" ,
469+ [
470+ pytest .param (0 , id = "send_zero" ),
471+ pytest .param (1 , id = "send_one" ),
472+ pytest .param (Spec .RESERVE_BALANCE , id = "send_reserve" ),
473+ ],
474+ )
475+ @pytest .mark .parametrize (
476+ "credit_value" ,
477+ [
478+ pytest .param (0 , id = "credit_zero" ),
479+ pytest .param (1 , id = "credit_one" ),
480+ pytest .param (Spec .RESERVE_BALANCE , id = "credit_reserve" ),
481+ ],
482+ )
483+ @pytest .mark .parametrize ("credit_statically_visible" , [True , False ])
484+ def test_credit_with_value (
485+ blockchain_test : BlockchainTestFiller ,
486+ pre : Alloc ,
487+ pre_delegated : bool ,
488+ send_pos : Tuple [int , int ],
489+ credit_pos : Tuple [int , int ],
490+ send_value : int ,
491+ credit_value : int ,
492+ credit_statically_visible : bool ,
493+ fork : Fork ,
494+ ) -> None :
495+ """
496+ Test reserve balance where sender transfers value in a setup tx
497+ and receives credit of varying amounts via direct transfer or
498+ SELFDESTRUCT contract.
499+
500+ Uses 4 blocks so send in block 0 falls outside the k=3 window,
501+ making the emptying exception reachable for undelegated senders.
502+ """
503+ prepare_tx_gas = (
504+ fork .gas_costs ().G_TRANSACTION + fork .gas_costs ().G_AUTHORIZATION * 2
505+ )
506+ prepare_tx_fee = GAS_PRICE * prepare_tx_gas
507+ initial_balance = Spec .RESERVE_BALANCE + send_value + prepare_tx_fee
508+
509+ target_address = Address (0x1111 )
510+ if pre_delegated :
511+ test_sender = pre .fund_eoa (initial_balance , delegation = target_address )
512+ else :
513+ test_sender = pre .fund_eoa (initial_balance )
514+
515+ contract = Op .SSTORE (slot_code_worked , value_code_worked ) + Op .STOP
516+ contract_address = pre .deploy_contract (contract )
517+
518+ nblocks = 4
519+ blocks = []
520+ test_sender_nonce = int (test_sender .nonce )
521+ for nblock in range (nblocks ):
522+ txs = []
523+ for ntx in range (2 ):
524+ pos = (nblock , ntx )
525+
526+ if send_pos == pos :
527+ sender = test_sender
528+ nonce = test_sender_nonce
529+ test_sender_nonce += 1
530+ tx_value = send_value
531+ else :
532+ sender = pre .fund_eoa ()
533+ nonce = 0
534+ tx_value = 0
535+
536+ prepare_tx = Transaction (
537+ gas_limit = prepare_tx_gas ,
538+ max_fee_per_gas = GAS_PRICE ,
539+ max_priority_fee_per_gas = GAS_PRICE ,
540+ to = Address (0x7873 ),
541+ nonce = nonce ,
542+ sender = sender ,
543+ value = tx_value ,
544+ )
545+ txs .append (prepare_tx )
546+
547+ if credit_pos == pos :
548+ if credit_statically_visible :
549+ credit_tx = Transaction (
550+ gas_limit = prepare_tx_gas ,
551+ max_fee_per_gas = GAS_PRICE ,
552+ max_priority_fee_per_gas = GAS_PRICE ,
553+ to = test_sender ,
554+ value = credit_value ,
555+ sender = pre .fund_eoa (),
556+ )
557+ else :
558+ credit_contract = pre .deploy_contract (
559+ Op .SELFDESTRUCT (address = test_sender ),
560+ balance = credit_value ,
561+ )
562+ credit_tx = Transaction (
563+ gas_limit = generous_gas (fork ),
564+ max_fee_per_gas = GAS_PRICE ,
565+ max_priority_fee_per_gas = GAS_PRICE ,
566+ to = credit_contract ,
567+ sender = pre .fund_eoa (),
568+ )
569+ txs .append (credit_tx )
570+ if nblock < nblocks - 1 :
571+ blocks .append (Block (txs = txs ))
572+ del txs
573+
574+ test_tx = Transaction (
575+ gas_limit = generous_gas (fork ),
576+ max_fee_per_gas = GAS_PRICE ,
577+ max_priority_fee_per_gas = GAS_PRICE ,
578+ to = contract_address ,
579+ nonce = test_sender_nonce ,
580+ value = 1 ,
581+ sender = test_sender ,
582+ )
583+ txs .append (test_tx )
584+ blocks .append (Block (txs = txs ))
585+
586+ # Sender balance at test time: RESERVE_BALANCE + credit_value.
587+ # Test tx sends value=1, so violation iff credit_value == 0.
588+ violation = credit_value == 0
589+ recent_send = send_pos [0 ] > 0
590+ is_exception = not pre_delegated and not recent_send
591+ reverted = violation and not is_exception
592+ storage = {} if reverted else {slot_code_worked : value_code_worked }
593+
594+ blockchain_test (
595+ pre = pre ,
596+ post = {contract_address : Account (storage = storage )},
597+ blocks = blocks ,
598+ )
599+
600+
464601@pytest .mark .parametrize (
465602 ["value" , "balance" , "violation" ],
466603 [
0 commit comments