Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions tests/benchmark/compute/instruction/test_account_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def test_codesize(
)


@pytest.mark.repricing(max_code_size_ratio=0.25, fixed_src_dst=True)
@pytest.mark.parametrize(
"max_code_size_ratio",
[
Expand Down Expand Up @@ -311,6 +312,7 @@ def test_extcode_ops(
)


@pytest.mark.repricing(copied_size=512)
@pytest.mark.parametrize(
"copied_size",
[
Expand All @@ -323,31 +325,25 @@ def test_extcodecopy_warm(
benchmark_test: BenchmarkTestFiller,
pre: Alloc,
copied_size: int,
gas_benchmark_value: int,
) -> None:
"""Benchmark EXTCODECOPY instruction."""
copied_contract_address = pre.deploy_contract(
code=Op.JUMPDEST * copied_size,
)

execution_code = (
Op.PUSH10(copied_size)
+ Op.PUSH20(copied_contract_address)
+ While(
body=Op.EXTCODECOPY(Op.DUP4, 0, 0, Op.DUP2),
)
)
execution_code_address = pre.deploy_contract(code=execution_code)
tx = Transaction(
to=execution_code_address,
gas_limit=gas_benchmark_value,
sender=pre.fund_eoa(),
benchmark_test(
code_generator=JumpLoopGenerator(
setup=Op.PUSH10(copied_size) + Op.PUSH20(copied_contract_address),
attack_block=Op.EXTCODECOPY(Op.DUP4, 0, 0, Op.DUP2),
),
)

benchmark_test(tx=tx)


@pytest.mark.repricing(absent_target=False)
@pytest.mark.repricing(
empty_code=True,
initial_balance=True,
initial_storage=True,
)
@pytest.mark.parametrize(
"opcode",
[
Expand Down
169 changes: 110 additions & 59 deletions tests/benchmark/compute/instruction/test_call_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,44 +64,52 @@ def test_calldatasize(
)


@pytest.mark.repricing(non_zero_value=True, from_origin=True)
@pytest.mark.repricing(non_zero_value=True)
@pytest.mark.parametrize("non_zero_value", [True, False])
@pytest.mark.parametrize("from_origin", [True, False])
def test_callvalue(
def test_callvalue_from_origin(
benchmark_test: BenchmarkTestFiller,
pre: Alloc,
fork: Fork,
non_zero_value: bool,
from_origin: bool,
) -> None:
"""
Benchmark CALLVALUE instruction.

- non_zero_value: whether opcode must return non-zero value.
- from_origin: whether the call frame is the immediate one
from the transaction or a previous CALL.
Benchmark CALLVALUE instruction from origin.
"""
code_address = JumpLoopGenerator(
attack_block=Op.POP(Op.CALLVALUE)
).deploy_contracts(pre=pre, fork=fork)
benchmark_test(
code_generator=JumpLoopGenerator(
attack_block=Op.POP(Op.CALLVALUE),
tx_kwargs={"value": int(non_zero_value)},
),
)

if from_origin:
tx_to = code_address
else:
entry_code = (
Op.JUMPDEST
+ Op.CALL(address=code_address, value=1 if non_zero_value else 0)
+ Op.JUMP(Op.PUSH0)
)
tx_to = pre.deploy_contract(code=entry_code, balance=1_000_000)

tx = Transaction(
to=tx_to,
value=1 if non_zero_value and from_origin else 0,
sender=pre.fund_eoa(),
@pytest.mark.repricing(non_zero_value=True)
@pytest.mark.parametrize("non_zero_value", [True, False])
def test_callvalue_from_call(
benchmark_test: BenchmarkTestFiller,
pre: Alloc,
non_zero_value: bool,
fork: Fork,
) -> None:
"""
Benchmark CALLVALUE instruction from call.
"""
code_address = pre.deploy_contract(
code=Op.CALLVALUE * fork.max_stack_height()
)
benchmark_test(
code_generator=JumpLoopGenerator(
attack_block=Op.POP(
Op.CALL(
address=code_address,
value=int(non_zero_value),
args_offset=Op.PUSH0,
args_size=Op.PUSH0,
ret_offset=Op.PUSH0,
ret_size=Op.PUSH0,
)
),
tx_kwargs={"value": 10**18},
),
)

benchmark_test(tx=tx)


@pytest.mark.repricing(calldata=b"")
Expand All @@ -128,12 +136,76 @@ def test_calldataload(


@pytest.mark.parametrize(
"origin",
"size",
[
pytest.param(0, id="0 bytes"),
pytest.param(100, id="100 bytes"),
pytest.param(10 * 1024, id="10KiB"),
pytest.param(1024 * 1024, id="1MiB"),
],
)
@pytest.mark.parametrize(
"fixed_src_dst",
[
pytest.param(CallDataOrigin.TRANSACTION, id="transaction"),
pytest.param(CallDataOrigin.CALL, id="call"),
True,
False,
],
)
@pytest.mark.parametrize(
"non_zero_data",
[
True,
False,
],
)
def test_calldatacopy_from_origin(
benchmark_test: BenchmarkTestFiller,
fork: Fork,
size: int,
fixed_src_dst: bool,
non_zero_data: bool,
tx_gas_limit: int,
) -> None:
"""Benchmark CALLDATACOPY instruction."""
if size == 0 and non_zero_data:
pytest.skip("Non-zero data with size 0 is not applicable.")

# If `non_zero_data` is True, we fill the calldata with deterministic
# random data. Note that if `size == 0` and `non_zero_data` is a skipped
# case.
data = Bytes([i % 256 for i in range(size)]) if non_zero_data else Bytes()

intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
min_gas = intrinsic_gas_calculator(calldata=data)
if min_gas > tx_gas_limit:
pytest.skip(
"Minimum gas required for calldata ({min_gas}) is greater "
"than the gas limit"
)

# We create the contract that will be doing the CALLDATACOPY multiple
# times.
#
# If `non_zero_data` is True, we leverage CALLDATASIZE for the copy
# length. Otherwise, since we
# don't send zero data explicitly via calldata, PUSH the target size and
# use DUP1 to copy it.
setup = Bytecode() if non_zero_data or size == 0 else Op.PUSH3(size)
src_dst = 0 if fixed_src_dst else Op.MOD(Op.GAS, 7)
attack_block = Op.CALLDATACOPY(
src_dst,
src_dst,
Op.CALLDATASIZE if non_zero_data or size == 0 else Op.DUP1,
)

benchmark_test(
code_generator=JumpLoopGenerator(
setup=setup,
attack_block=attack_block,
)
)


@pytest.mark.parametrize(
"size",
[
Expand All @@ -157,11 +229,10 @@ def test_calldataload(
False,
],
)
def test_calldatacopy(
def test_calldatacopy_from_call(
benchmark_test: BenchmarkTestFiller,
pre: Alloc,
fork: Fork,
origin: CallDataOrigin,
size: int,
fixed_src_dst: bool,
non_zero_data: bool,
Expand Down Expand Up @@ -203,35 +274,15 @@ def test_calldatacopy(
setup=setup, attack_block=attack_block
).deploy_contracts(pre=pre, fork=fork)

tx_target = code_address

# If the origin is CALL, we need to create a contract that will call the
# target contract with the calldata.
if origin == CallDataOrigin.CALL:
# If `non_zero_data` is False we leverage just using zeroed memory.
# Otherwise, we copy the calldata received from the transaction.
setup = (
Op.CALLDATACOPY(Op.PUSH0, Op.PUSH0, Op.CALLDATASIZE)
if non_zero_data
else Bytecode()
) + Op.JUMPDEST
arg_size = Op.CALLDATASIZE if non_zero_data else size
attack_block = Op.STATICCALL(
address=code_address, args_offset=Op.PUSH0, args_size=arg_size
)
arg_size = Op.CALLDATASIZE if non_zero_data else size
attack_block = Op.STATICCALL(address=code_address, args_size=arg_size)

tx_target = JumpLoopGenerator(
benchmark_test(
code_generator=JumpLoopGenerator(
setup=setup, attack_block=attack_block
).deploy_contracts(pre=pre, fork=fork)

tx = Transaction(
to=tx_target,
data=data,
sender=pre.fund_eoa(),
)
)

benchmark_test(tx=tx)


@pytest.mark.repricing(
returned_size=1,
Expand Down
1 change: 1 addition & 0 deletions tests/benchmark/compute/instruction/test_control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def test_gas_op(
)


@pytest.mark.repricing
def test_pc_op(
benchmark_test: BenchmarkTestFiller,
) -> None:
Expand Down
1 change: 1 addition & 0 deletions tests/benchmark/compute/instruction/test_keccak.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def test_keccak_max_permutations(
)


@pytest.mark.repricing(mem_alloc=b"ff" * 32, offset=31)
@pytest.mark.parametrize("mem_alloc", [b"", b"ff", b"ff" * 32])
@pytest.mark.parametrize("offset", [0, 31, 1024])
def test_keccak(
Expand Down
2 changes: 2 additions & 0 deletions tests/benchmark/compute/instruction/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ def test_xcall(
)


@pytest.mark.repricing(max_code_size_ratio=0.25, non_zero_data=True, value=0)
@pytest.mark.parametrize(
"opcode",
[
Expand Down Expand Up @@ -353,6 +354,7 @@ def test_create(
)


@pytest.mark.repricing
@pytest.mark.parametrize(
"opcode",
[
Expand Down
Loading