Skip to content

Commit 3a6469b

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

File tree

1 file changed

+94
-22
lines changed

1 file changed

+94
-22
lines changed

tests/zkevm/test_worst_stateful_opcodes.py

Lines changed: 94 additions & 22 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,35 @@ 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) <= MAX_CONTRACT_SIZE
594-
595-
attack_code_addr = pre.deploy_contract(code=attack_code)
583+
code_addr = pre.deploy_contract(code=code)
596584
opcode_tx = Transaction(
597-
to=attack_code_addr,
585+
to=code_addr,
598586
gas_limit=attack_gas_limit,
599587
gas_price=10**9,
600588
sender=pre.fund_eoa(),
601589
)
602590

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

0 commit comments

Comments
 (0)