Skip to content

Commit 7706c82

Browse files
refactor(benchmark): update code generator interface
1 parent a2f2841 commit 7706c82

File tree

2 files changed

+16
-12
lines changed

2 files changed

+16
-12
lines changed

src/ethereum_test_benchmark/benchmark_code_generator.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Benchmark code generator classes for creating optimized bytecode patterns."""
22

3+
from ethereum_test_base_types import Address
34
from ethereum_test_forks import Fork
45
from ethereum_test_specs.benchmark import BenchmarkCodeGenerator
56
from ethereum_test_types import Alloc, Transaction
@@ -10,17 +11,18 @@
1011
class JumpLoopGenerator(BenchmarkCodeGenerator):
1112
"""Generates bytecode that loops execution using JUMP operations."""
1213

13-
def deploy_contracts(self, pre: Alloc, fork: Fork) -> None:
14+
def deploy_contracts(self, pre: Alloc, fork: Fork) -> Address:
1415
"""Deploy the looping contract."""
1516
# Benchmark Test Structure:
1617
# setup + JUMPDEST + attack + attack + ... + attack + JUMP(setup_length)
17-
code = self.generate_repeated_code(self.attack_block, self.setup, fork)
18+
code = self.generate_repeated_code(self.attack_block, self.setup, self.cleanup, fork)
1819
self._contract_address = pre.deploy_contract(code=code)
20+
return self._contract_address
1921

2022
def generate_transaction(self, pre: Alloc, gas_limit: int, fork: Fork) -> Transaction:
2123
"""Generate transaction that executes the looping contract."""
2224
if not hasattr(self, "_contract_address"):
23-
raise ValueError("deploy_contracts must be called before generate_transaction")
25+
self.deploy_contracts(pre, fork)
2426

2527
return Transaction(
2628
to=self._contract_address,
@@ -32,7 +34,7 @@ def generate_transaction(self, pre: Alloc, gas_limit: int, fork: Fork) -> Transa
3234
class ExtCallGenerator(BenchmarkCodeGenerator):
3335
"""Generates bytecode that fills the contract to maximum allowed code size."""
3436

35-
def deploy_contracts(self, pre: Alloc, fork: Fork) -> None:
37+
def deploy_contracts(self, pre: Alloc, fork: Fork) -> Address:
3638
"""Deploy both target and caller contracts."""
3739
# Benchmark Test Structure:
3840
# There are two contracts:
@@ -45,21 +47,22 @@ def deploy_contracts(self, pre: Alloc, fork: Fork) -> None:
4547

4648
# Deploy target contract that contains the actual attack block
4749
self._target_contract_address = pre.deploy_contract(
48-
code=self.attack_block * max_iterations
50+
code=self.setup + self.attack_block * max_iterations
4951
)
5052

5153
# Create caller contract that repeatedly calls the target contract
5254
# attack = POP(STATICCALL(GAS, target_contract_address, 0, 0, 0, 0))
5355
# setup + JUMPDEST + attack + attack + ... + attack + JUMP(setup_length)
5456
code_sequence = Op.POP(Op.STATICCALL(Op.GAS, self._target_contract_address, 0, 0, 0, 0))
5557

56-
caller_code = self.generate_repeated_code(code_sequence, Bytecode(), fork)
58+
caller_code = self.generate_repeated_code(code_sequence, Bytecode(), self.cleanup, fork)
5759
self._contract_address = pre.deploy_contract(code=caller_code)
60+
return self._contract_address
5861

5962
def generate_transaction(self, pre: Alloc, gas_limit: int, fork: Fork) -> Transaction:
6063
"""Generate transaction that executes the caller contract."""
6164
if not hasattr(self, "_contract_address"):
62-
raise ValueError("deploy_contracts must be called before generate_transaction")
65+
self.deploy_contracts(pre, fork)
6366

6467
return Transaction(
6568
to=self._contract_address,

src/ethereum_test_specs/benchmark.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from pydantic import ConfigDict, Field
1010

1111
from ethereum_clis import TransitionTool
12-
from ethereum_test_base_types import HexNumber
12+
from ethereum_test_base_types import Address, HexNumber
1313
from ethereum_test_exceptions import BlockException, TransactionException
1414
from ethereum_test_execution import (
1515
BaseExecute,
@@ -40,9 +40,10 @@ class BenchmarkCodeGenerator(ABC):
4040

4141
attack_block: Bytecode
4242
setup: Bytecode = field(default_factory=Bytecode)
43+
cleanup: Bytecode = field(default_factory=Bytecode)
4344

4445
@abstractmethod
45-
def deploy_contracts(self, pre: Alloc, fork: Fork) -> None:
46+
def deploy_contracts(self, pre: Alloc, fork: Fork) -> Address:
4647
"""Deploy any contracts needed for the benchmark."""
4748
...
4849

@@ -52,17 +53,17 @@ def generate_transaction(self, pre: Alloc, gas_limit: int, fork: Fork) -> Transa
5253
...
5354

5455
def generate_repeated_code(
55-
self, repeated_code: Bytecode, setup: Bytecode, fork: Fork
56+
self, repeated_code: Bytecode, setup: Bytecode, cleanup: Bytecode, fork: Fork
5657
) -> Bytecode:
5758
"""Calculate the maximum number of iterations that can fit in the code size limit."""
5859
assert len(repeated_code) > 0, "repeated_code cannot be empty"
5960
max_code_size = fork.max_code_size()
6061

61-
overhead = len(setup) + len(Op.JUMPDEST) + len(Op.JUMP(len(setup)))
62+
overhead = len(setup) + len(Op.JUMPDEST) + len(cleanup) + len(Op.JUMP(len(setup)))
6263
available_space = max_code_size - overhead
6364
max_iterations = available_space // len(repeated_code)
6465

65-
code = setup + Op.JUMPDEST + repeated_code * max_iterations + Op.JUMP(len(setup))
66+
code = setup + Op.JUMPDEST + repeated_code * max_iterations + cleanup + Op.JUMP(len(setup))
6667
self._validate_code_size(code, fork)
6768

6869
return code

0 commit comments

Comments
 (0)