Skip to content

Commit 64a691b

Browse files
committed
chore.
1 parent 8a9be4b commit 64a691b

File tree

5 files changed

+256
-188
lines changed

5 files changed

+256
-188
lines changed
Lines changed: 80 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
"""Shared pytest definitions local to EIP-7883 tests."""
1+
"""Shared pytest definitions for EIP-7883 tests."""
22

3-
from typing import Dict
3+
from typing import Dict, Tuple
44

55
import pytest
66

@@ -9,12 +9,15 @@
99
Account,
1010
Address,
1111
Alloc,
12+
Bytecode,
13+
CodeGasMeasure,
1214
Environment,
1315
Storage,
1416
Transaction,
1517
)
1618
from ethereum_test_tools.vm.opcode import Opcodes as Op
1719

20+
from .helpers import parse_modexp_input
1821
from .spec import Spec
1922

2023

@@ -25,74 +28,102 @@ def env() -> Environment:
2528

2629

2730
@pytest.fixture
28-
def sender(pre: Alloc):
31+
def parsed_input(input_data: bytes) -> Tuple[bytes, bytes, bytes, int]:
32+
"""Parse the ModExp input data."""
33+
return parse_modexp_input(input_data)
34+
35+
36+
@pytest.fixture
37+
def base(parsed_input: Tuple[bytes, bytes, bytes, int]) -> bytes:
38+
"""Get the base value from the parsed input."""
39+
return parsed_input[0]
40+
41+
42+
@pytest.fixture
43+
def exponent_bytes(parsed_input: Tuple[bytes, bytes, bytes, int]) -> bytes:
44+
"""Get the exponent bytes from the parsed input."""
45+
return parsed_input[1]
46+
47+
48+
@pytest.fixture
49+
def modulus(parsed_input: Tuple[bytes, bytes, bytes, int]) -> bytes:
50+
"""Get the modulus value from the parsed input."""
51+
return parsed_input[2]
52+
53+
54+
@pytest.fixture
55+
def exponent(parsed_input: Tuple[bytes, bytes, bytes, int]) -> int:
56+
"""Get the exponent value from the parsed input."""
57+
return parsed_input[3]
58+
59+
60+
@pytest.fixture
61+
def sender(pre: Alloc) -> EOA:
2962
"""Create and fund an EOA to be used as the transaction sender."""
3063
return pre.fund_eoa()
3164

3265

3366
@pytest.fixture
34-
def gas_measure_contract(pre: Alloc):
35-
"""Deploys a simple contract to call ModExp and store its success."""
36-
# TODO: fix the gas accounting
37-
measured_code = Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + Op.SSTORE(
38-
0,
39-
Op.CALL(
40-
address=Spec.MODEXP_ADDRESS,
41-
value=0,
42-
args_offset=0,
43-
args_size=Op.CALLDATASIZE,
44-
ret_offset=0,
45-
ret_size=0x80,
46-
),
47-
)
48-
return pre.deploy_contract(measured_code)
67+
def call_opcode() -> Op:
68+
"""Return default call used to call the precompile."""
69+
return Op.CALL
4970

5071

5172
@pytest.fixture
52-
def base(base_length, request):
53-
"""Generate base bytes with the specified length."""
54-
return b"\xff" * base_length # arbitrary bytes
73+
def modexp_call_code(call_opcode: Op, input_data: bytes) -> Bytecode:
74+
"""Create bytecode to call the ModExp precompile."""
75+
call_code = call_opcode(
76+
address=Spec.MODEXP_ADDRESS,
77+
value=0,
78+
args_offset=0,
79+
args_size=Op.CALLDATASIZE,
80+
ret_offset=0,
81+
ret_size=0x80,
82+
)
83+
call_code += Op.SSTORE(0, Op.ISZERO(Op.ISZERO))
84+
return call_code
5585

5686

5787
@pytest.fixture
58-
def modulus(modulus_length, request):
59-
"""Generate modulus bytes with the specified length."""
60-
return b"\xff" * modulus_length # arbitrary bytes
88+
def gas_measure_contract(pre: Alloc, modexp_call_code: Bytecode) -> Address:
89+
"""Deploys a contract that measures ModExp gas consumption."""
90+
calldata_copy = Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE)
91+
measured_code = CodeGasMeasure(
92+
code=calldata_copy + modexp_call_code,
93+
overhead_cost=12, # TODO: Calculate overhead cost
94+
extra_stack_items=0,
95+
sstore_key=1,
96+
stop=True,
97+
)
98+
return pre.deploy_contract(measured_code)
6199

62100

63101
@pytest.fixture
64-
def exponent_bytes(exponent_length, exponent, request):
65-
"""Convert the integer exponent to bytes with the specified length."""
66-
# Truncate to the lowest exponent length bytes to avoid overflow
67-
mask = (1 << (8 * exponent_length)) - 1
68-
truncated = exponent & mask
69-
return truncated.to_bytes(exponent_length, byteorder="big")
102+
def precompile_gas_modifier() -> int:
103+
"""Modify the gas passed to the precompile, for negative testing purposes."""
104+
return 0
70105

71106

72107
@pytest.fixture
73-
def expected_gas(
74-
base: bytes,
75-
exponent_bytes: bytes,
76-
modulus: bytes,
77-
) -> int:
78-
"""Return expected gas consumption of the ModExp precompile."""
108+
def precompile_gas(base: bytes, exponent_bytes: bytes, modulus: bytes, expected_gas: int) -> int:
109+
"""Calculate gas cost for the ModExp precompile and verify it matches expected gas."""
79110
base_length = len(base)
80111
exponent_length = len(exponent_bytes)
81112
modulus_length = len(modulus)
82113
exponent_value = int.from_bytes(exponent_bytes, byteorder="big")
83-
# return Spec.calculate_new_gas_cost(
84-
# base_length, modulus_length, exponent_length, exponent_value
85-
# )
86-
# Temporarily use the old (EIP-2565) gas schedule
87-
return Spec.calculate_old_gas_cost(
114+
calculated_gas = Spec.calculate_new_gas_cost(
88115
base_length, modulus_length, exponent_length, exponent_value
89116
)
117+
assert (
118+
calculated_gas == expected_gas
119+
), f"Calculated gas {calculated_gas} != Vector gas {expected_gas}"
120+
return calculated_gas
90121

91122

92123
@pytest.fixture
93-
def modexp_input_data(base: bytes, exponent_bytes: bytes, modulus: bytes) -> bytes:
94-
"""ModExp input data."""
95-
return Spec.create_modexp_input(base, exponent_bytes, modulus)
124+
def modexp_input_data(input_data: bytes) -> bytes:
125+
"""ModExp input data, directly use the input from the test vector."""
126+
return input_data
96127

97128

98129
@pytest.fixture
@@ -106,20 +137,21 @@ def tx(
106137
sender=sender,
107138
to=gas_measure_contract,
108139
data=modexp_input_data,
109-
gas_limit=100_000,
140+
gas_limit=1_000_000,
110141
)
111142

112143

113144
@pytest.fixture
114145
def post(
115146
gas_measure_contract: Address,
116-
expected_gas: int,
147+
precompile_gas: int,
117148
) -> Dict[Address, Dict[str, Storage]]:
118-
"""Return expected post state."""
149+
"""Return expected post state with gas consumption check."""
119150
return {
120151
gas_measure_contract: Account(
121152
storage={
122-
0: 1,
153+
0: 1, # Call should succeed
154+
1: precompile_gas,
123155
}
124156
)
125157
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Helper functions for the EIP-7883 ModExp gas cost increase tests."""
2+
3+
import json
4+
import os
5+
from typing import List, Tuple
6+
7+
import pytest
8+
9+
10+
def current_python_script_directory(*args: str) -> str:
11+
"""Get the current Python script directory."""
12+
return os.path.join(os.path.dirname(os.path.realpath(__file__)), *args)
13+
14+
15+
def vectors_from_file(filename: str) -> List[Tuple]:
16+
"""Load test vectors from a file."""
17+
with open(current_python_script_directory(filename), "r") as f:
18+
vectors_json = json.load(f)
19+
result = []
20+
for vector in vectors_json:
21+
input_hex = vector["Input"]
22+
name = vector["Name"]
23+
gas_new = vector["GasNew"]
24+
param = pytest.param(
25+
bytes.fromhex(input_hex),
26+
gas_new,
27+
id=name,
28+
)
29+
result.append(param)
30+
return result
31+
32+
33+
def parse_modexp_input(input_data: bytes) -> Tuple[bytes, bytes, bytes, int]:
34+
"""Parse ModExp input data into base, exponent bytes, modulus, and exponent value."""
35+
base_length = int.from_bytes(input_data[0:32], byteorder="big")
36+
exponent_length = int.from_bytes(input_data[32:64], byteorder="big")
37+
modulus_length = int.from_bytes(input_data[64:96], byteorder="big")
38+
39+
base_start = 96
40+
base_end = base_start + base_length
41+
base = input_data[base_start:base_end]
42+
43+
exponent_start = base_end
44+
exponent_end = exponent_start + exponent_length
45+
exponent_bytes = input_data[exponent_start:exponent_end]
46+
exponent_value = int.from_bytes(exponent_bytes, byteorder="big")
47+
48+
modulus_start = exponent_end
49+
modulus_end = modulus_start + modulus_length
50+
modulus = input_data[modulus_start:modulus_end]
51+
52+
return base, exponent_bytes, modulus, exponent_value

0 commit comments

Comments
 (0)