Skip to content

Commit 0a19e7d

Browse files
committed
zkevm: add selfdestruct of contracts deployed in tx
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
1 parent 492a745 commit 0a19e7d

File tree

1 file changed

+95
-21
lines changed

1 file changed

+95
-21
lines changed

tests/zkevm/test_worst_stateful_opcodes.py

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ def test_worst_selfdestruct_existing(
496496
# Create an account that will be used as the beneficiary of the SELFDESTRUCT calls, to avoid
497497
# account creation costs. All SELFDESTRUCT calls will target the same account to avoid
498498
# cold costs.
499-
selfdestruct_beneficiary = pre.fund_eoa(amount=100)
499+
selfdestruct_beneficiary = pre.fund_eoa()
500500

501501
# Template code that will be used to deploy a large number of contracts.
502502
selfdestructable_contract_addr = pre.deploy_contract(
@@ -563,19 +563,7 @@ def test_worst_selfdestruct_existing(
563563
sender=pre.fund_eoa(),
564564
)
565565

566-
post = {}
567-
deployed_contract_addresses = []
568-
for i in range(num_contracts):
569-
deployed_contract_address = compute_create2_address(
570-
address=factory_address,
571-
salt=i,
572-
initcode=initcode,
573-
)
574-
post[deployed_contract_address] = Account(nonce=1)
575-
deployed_contract_addresses.append(deployed_contract_address)
576-
577-
attack_call = Op.POP(Op.CALL(address=Op.SHA3(32 - 20 - 1, 85)))
578-
attack_code = (
566+
code = (
579567
# Setup memory for later CREATE2 address generation loop.
580568
# 0xFF+[Address(20bytes)]+[seed(32bytes)]+[initcode keccak(32bytes)]
581569
Op.MSTORE(0, factory_address)
@@ -584,22 +572,37 @@ def test_worst_selfdestruct_existing(
584572
+ Op.MSTORE(64, initcode.keccak256())
585573
# Main loop
586574
+ While(
587-
body=attack_call + Op.MSTORE(32, Op.ADD(Op.MLOAD(32), 1)),
588-
# The condition is having enough gas for at least one more iteration.
589-
# 2+2600+5000 is the `attack_call` gas cost, and 2* is a rough safety margin.
590-
condition=Op.GT(Op.GAS, 2 * (2 + 2600 + 5000)),
575+
body=Op.POP(Op.CALL(address=Op.SHA3(32 - 20 - 1, 85)))
576+
+ Op.MSTORE(32, Op.ADD(Op.MLOAD(32), 1)),
577+
# Stop before we run out of gas for the whole tx execution.
578+
# The value was discovered practically rounded to the next 1000 multiple.
579+
condition=Op.GT(Op.GAS, 28_000),
591580
)
581+
+ Op.SSTORE(0, 42) # Done for successful tx execution assertion below.
592582
)
593-
assert len(attack_code) <= fork.max_code_size()
583+
assert len(code) <= fork.max_code_size()
594584

595-
attack_code_addr = pre.deploy_contract(code=attack_code)
585+
code_addr = pre.deploy_contract(code=code)
596586
opcode_tx = Transaction(
597-
to=attack_code_addr,
587+
to=code_addr,
598588
gas_limit=attack_gas_limit,
599589
gas_price=10**9,
600590
sender=pre.fund_eoa(),
601591
)
602592

593+
post = {
594+
code_addr: Account(storage={0: 42}) # Check for successful execution.
595+
}
596+
deployed_contract_addresses = []
597+
for i in range(num_contracts):
598+
deployed_contract_address = compute_create2_address(
599+
address=factory_address,
600+
salt=i,
601+
initcode=initcode,
602+
)
603+
post[deployed_contract_address] = Account(nonce=1)
604+
deployed_contract_addresses.append(deployed_contract_address)
605+
603606
blockchain_test(
604607
genesis_environment=env,
605608
pre=pre,
@@ -610,3 +613,74 @@ def test_worst_selfdestruct_existing(
610613
],
611614
exclude_full_post_state_in_output=True,
612615
)
616+
617+
618+
@pytest.mark.valid_from("Cancun")
619+
@pytest.mark.parametrize("value_bearing", [True, False])
620+
def test_worst_selfdestruct_created(
621+
state_test: StateTestFiller,
622+
pre: Alloc,
623+
value_bearing: bool,
624+
):
625+
"""
626+
Test running a block with as SELFDESTRUCT calls as possible for deployed contracts in
627+
the same transaction.
628+
"""
629+
env = Environment()
630+
631+
# Create an account that will be used as the beneficiary of the SELFDESTRUCT calls, to avoid
632+
# account creation costs. All SELFDESTRUCT calls will target the same account to avoid
633+
# cold costs.
634+
selfdestruct_beneficiary = pre.fund_eoa()
635+
636+
# Template code that will be used to deploy a large number of contracts.
637+
selfdestructable_contract_addr = pre.deploy_contract(
638+
code=Op.SELFDESTRUCT(selfdestruct_beneficiary)
639+
)
640+
initcode = Op.EXTCODECOPY(
641+
address=selfdestructable_contract_addr,
642+
dest_offset=0,
643+
offset=0,
644+
size=Op.EXTCODESIZE(selfdestructable_contract_addr),
645+
) + Op.RETURN(0, Op.EXTCODESIZE(selfdestructable_contract_addr))
646+
initcode_address = pre.deploy_contract(code=initcode)
647+
648+
code = (
649+
Op.EXTCODECOPY(
650+
address=initcode_address,
651+
dest_offset=0,
652+
offset=0,
653+
size=Op.EXTCODESIZE(initcode_address),
654+
)
655+
+ While(
656+
body=Op.POP(
657+
Op.CALL(
658+
address=Op.CREATE(
659+
value=1 if value_bearing else 0,
660+
offset=0,
661+
size=Op.EXTCODESIZE(initcode_address),
662+
)
663+
)
664+
),
665+
# Stop before we run out of gas for the whole tx execution.
666+
# The value was discovered practically rounded to the next 1000 multiple.
667+
condition=Op.GT(Op.GAS, 40_000),
668+
)
669+
+ Op.SSTORE(0, 42) # Done for successful tx execution assertion below.
670+
)
671+
code_addr = pre.deploy_contract(code=code)
672+
code_tx = Transaction(
673+
to=code_addr,
674+
gas_limit=env.gas_limit,
675+
gas_price=10**9,
676+
sender=pre.fund_eoa(),
677+
)
678+
679+
post = {code_addr: Account(storage={0: 42})} # Check for successful execution.
680+
681+
state_test(
682+
env=env,
683+
pre=pre,
684+
post=post,
685+
tx=code_tx,
686+
)

0 commit comments

Comments
 (0)