Skip to content

Commit 56fde10

Browse files
committed
feat(tests): test_credit_with_value multi-block scenarios
Co-Authored-By: Claude claude-opus-4-6
1 parent f80dad8 commit 56fde10

1 file changed

Lines changed: 138 additions & 1 deletion

File tree

tests/monad_eight/reserve_balance/test_multi_block.py

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)