@pytest.mark.parametrize("out_of_gas", [True, False])
def test_nested_creates(
benchmark_test: BenchmarkTestFiller,
pre: Alloc,
fork: Fork,
tx_gas_limit: int,
gas_benchmark_value: int,
out_of_gas: bool,
) -> None:
"""Benchmark chains of nested CREATE frames."""
initcode_size = 32
copy_self = Op.CODECOPY(
dest_offset=0,
offset=0,
size=Op.CODESIZE,
# gas accounting
data_size=initcode_size,
old_memory_size=0,
new_memory_size=initcode_size,
)
create_self = Op.POP(
Op.CREATE(
value=0,
offset=0,
size=Op.CODESIZE,
# gas accounting
init_code_size=initcode_size,
old_memory_size=initcode_size,
new_memory_size=initcode_size,
)
)
initcode = copy_self + create_self
if not out_of_gas:
initcode = copy_self + Conditional(
condition=Op.GT(Op.GAS, 2 * initcode.gas_cost(fork)),
if_true=create_self,
)
assert len(initcode) <= initcode_size, "initcode outgrew its padding"
initcode += Op.STOP * (initcode_size - len(initcode))
setup = Om.MSTORE(bytes(initcode), 0)
launch = Op.POP(
Op.CREATE(
value=0,
offset=0,
size=initcode_size,
# gas accounting
init_code_size=initcode_size,
old_memory_size=initcode_size,
new_memory_size=initcode_size,
)
)
if out_of_gas:
benchmark_test(
target_opcode=Op.CREATE,
code_generator=JumpLoopGenerator(setup=setup, attack_block=launch),
)
else:
driver_address = pre.deploy_contract(
code=setup + WhileGas(body=launch, fork=fork)
)
# Every level spends fifteen times more state gas than execution
# gas, so a single transaction asking for the whole budget goes
# deeper than several could: the state gas the chain spends counts
# against the budget whether a reservoir or execution gas paid it.
gas_limit = min(tx_gas_limit, gas_benchmark_value)
if fork.state_gas_reservoir_enabled():
gas_limit = gas_benchmark_value
benchmark_test(
target_opcode=Op.CREATE,
skip_gas_used_validation=True,
post={
compute_create_address(
address=driver_address, nonce=1
): Account(nonce=2, code=b""),
},
blocks=[
Block(
txs=[
Transaction(
to=driver_address,
gas_limit=gas_limit,
sender=pre.fund_eoa(),
)
]
)
],
)