-
Notifications
You must be signed in to change notification settings - Fork 374
tests(benchmark): add missing scenario and optimization #1768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
b5c2255
1b44dea
ac154c4
ca5d64a
2b6823a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| """ | ||
|
|
||
| import math | ||
| from typing import Dict | ||
|
|
||
| import pytest | ||
| from execution_testing import ( | ||
|
|
@@ -24,6 +25,7 @@ | |
| While, | ||
| compute_create2_address, | ||
| ) | ||
| from execution_testing.base_types.conversions import NumberConvertible | ||
|
|
||
| from tests.benchmark.compute.helpers import ( | ||
| XOR_TABLE, | ||
|
|
@@ -72,7 +74,6 @@ def test_codesize( | |
| ) | ||
| def test_codecopy( | ||
| benchmark_test: BenchmarkTestFiller, | ||
| pre: Alloc, | ||
| fork: Fork, | ||
| max_code_size_ratio: float, | ||
| fixed_src_dst: bool, | ||
|
|
@@ -86,26 +87,12 @@ def test_codecopy( | |
| src_dst = 0 if fixed_src_dst else Op.MOD(Op.GAS, 7) | ||
| attack_block = Op.CODECOPY(src_dst, src_dst, Op.DUP1) # DUP1 copies size. | ||
|
|
||
| code = JumpLoopGenerator( | ||
| setup=setup, attack_block=attack_block | ||
| ).generate_repeated_code( | ||
| repeated_code=attack_block, setup=setup, fork=fork | ||
| ) | ||
|
|
||
| # Pad the generated code to ensure the contract size matches the maximum | ||
| # The content of the padding bytes is arbitrary. | ||
| code += Op.INVALID * (max_code_size - len(code)) | ||
| assert len(code) == max_code_size, ( | ||
| f"Code size {len(code)} is not equal to max code size {max_code_size}." | ||
| ) | ||
|
|
||
| tx = Transaction( | ||
| to=pre.deploy_contract(code=code), | ||
| sender=pre.fund_eoa(), | ||
| benchmark_test( | ||
| code_generator=JumpLoopGenerator( | ||
| setup=setup, attack_block=attack_block | ||
marioevz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) | ||
| ) | ||
|
|
||
| benchmark_test(tx=tx) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "opcode", | ||
|
|
@@ -361,7 +348,21 @@ def test_extcodecopy_warm( | |
| ], | ||
| ) | ||
| @pytest.mark.parametrize( | ||
| "absent_target", | ||
| "empty_account", | ||
marioevz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [ | ||
| True, | ||
| False, | ||
| ], | ||
| ) | ||
| @pytest.mark.parametrize( | ||
| "initial_balance", | ||
| [ | ||
| True, | ||
| False, | ||
| ], | ||
| ) | ||
| @pytest.mark.parametrize( | ||
| "initial_storage", | ||
| [ | ||
| True, | ||
| False, | ||
|
|
@@ -371,27 +372,40 @@ def test_ext_account_query_warm( | |
| benchmark_test: BenchmarkTestFiller, | ||
| pre: Alloc, | ||
| opcode: Op, | ||
| absent_target: bool, | ||
| empty_account: bool, | ||
| initial_balance: bool, | ||
| initial_storage: bool, | ||
|
||
| ) -> None: | ||
| """ | ||
| Test running a block with as many stateful opcodes doing warm access | ||
| for an account. | ||
| """ | ||
| # Setup | ||
| target_addr = pre.empty_account() | ||
| target_addr = pre.empty_account() if initial_balance else pre.fund_eoa() | ||
| post = {} | ||
| if not absent_target: | ||
| if not empty_account: | ||
| code = Op.STOP + Op.JUMPDEST * 100 | ||
| target_addr = pre.deploy_contract(balance=100, code=code) | ||
| post[target_addr] = Account(balance=100, code=code) | ||
|
|
||
| # Execution | ||
| setup = Op.MSTORE(0, target_addr) | ||
| attack_block = Op.POP(opcode(address=Op.MLOAD(0))) | ||
| storage: Dict[NumberConvertible, NumberConvertible] = ( | ||
| {0: 0x1337} if initial_storage else {0: 0} | ||
| ) | ||
| target_addr = pre.deploy_contract( | ||
| balance=initial_balance, | ||
| code=code, | ||
| storage=storage, | ||
| ) | ||
|
|
||
| post[target_addr] = Account( | ||
| balance=initial_balance, | ||
| code=code, | ||
| storage=storage, | ||
| ) | ||
|
|
||
| benchmark_test( | ||
| post=post, | ||
| code_generator=JumpLoopGenerator( | ||
| setup=setup, attack_block=attack_block | ||
| setup=Op.MSTORE(0, target_addr), | ||
| attack_block=Op.POP(opcode(address=Op.MLOAD(0))), | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,14 +36,29 @@ def test_block_context_ops( | |
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "index", | ||
| [ | ||
| 0, | ||
| 1, | ||
| 256, | ||
| 257, | ||
| None, | ||
marioevz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ], | ||
| ) | ||
| def test_blockhash( | ||
| benchmark_test: BenchmarkTestFiller, | ||
| index: int | None, | ||
| ) -> None: | ||
| """Benchmark BLOCKHASH instruction accessing oldest allowed block.""" | ||
| # Create 256 dummy blocks to fill the blockhash window. | ||
| blocks = [Block()] * 256 | ||
|
|
||
| block_number = Op.AND(Op.GAS, 0xFF) if index is None else index | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding extra BLOCKHASH operation for benchmarking, as it was one of the slowest operations.
|
||
|
|
||
| benchmark_test( | ||
| setup_blocks=blocks, | ||
| code_generator=ExtCallGenerator(attack_block=Op.BLOCKHASH(1)), | ||
| code_generator=ExtCallGenerator( | ||
| attack_block=Op.BLOCKHASH(block_number) | ||
| ), | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,15 @@ def test_gas_op( | |
| ) | ||
|
|
||
|
|
||
| def test_pc_op( | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We miss the benchmark for |
||
| benchmark_test: BenchmarkTestFiller, | ||
| ) -> None: | ||
| """Benchmark PC instruction.""" | ||
| benchmark_test( | ||
| code_generator=ExtCallGenerator(attack_block=Op.PC), | ||
| ) | ||
|
|
||
|
|
||
| def test_jumps( | ||
| benchmark_test: BenchmarkTestFiller, | ||
| pre: Alloc, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| import math | ||
|
|
||
| import pytest | ||
| from execution_testing import ( | ||
| BenchmarkTestFiller, | ||
| Fork, | ||
|
|
@@ -15,15 +16,15 @@ | |
| KECCAK_RATE = 136 | ||
|
|
||
|
|
||
| def test_keccak( | ||
| def test_keccak_max_permutations( | ||
| benchmark_test: BenchmarkTestFiller, | ||
| fork: Fork, | ||
| gas_benchmark_value: int, | ||
| tx_gas_limit: int, | ||
| ) -> None: | ||
| """Benchmark KECCAK256 instruction.""" | ||
| """Benchmark KECCAK256 instruction to maximize permutations per block.""" | ||
| # Intrinsic gas cost is paid once. | ||
| intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator() | ||
| available_gas = gas_benchmark_value - intrinsic_gas_calculator() | ||
| available_gas = tx_gas_limit - intrinsic_gas_calculator() | ||
|
|
||
| gsc = fork.gas_costs() | ||
| mem_exp_gas_calculator = fork.memory_expansion_gas_calculator() | ||
|
|
@@ -60,7 +61,24 @@ def test_keccak( | |
|
|
||
| benchmark_test( | ||
| code_generator=JumpLoopGenerator( | ||
| setup=Op.PUSH20[optimal_input_length], | ||
| setup=Op.PUSH20(optimal_input_length), | ||
marioevz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| attack_block=Op.POP(Op.SHA3(Op.PUSH0, Op.DUP1)), | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("mem_alloc", [b"", b"ff", b"ff" * 32]) | ||
| @pytest.mark.parametrize("offset", [0, 31, 1024]) | ||
| def test_keccak( | ||
|
Comment on lines
+70
to
+72
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of finding the optimal length, add a new case that parametrized initial memory layout and access offset. Request by gas repricing effort. |
||
| benchmark_test: BenchmarkTestFiller, | ||
| offset: int, | ||
| mem_alloc: bytes, | ||
| ) -> None: | ||
| """Benchmark KECCAK256 instruction with diff input data and offsets.""" | ||
| benchmark_test( | ||
| code_generator=JumpLoopGenerator( | ||
| setup=Op.CALLDATACOPY(offset, Op.PUSH0, Op.CALLDATASIZE), | ||
| attack_block=Op.POP(Op.SHA3(offset, Op.CALLDATASIZE)), | ||
| tx_kwargs={"data": mem_alloc}, | ||
| ), | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.