Skip to content

Commit 789de47

Browse files
authored
tests/4844: Add EIP-7516, BLOBBASEFEE, verification to excess blob gas tests (#294)
* vm/opcode: add BLOBBASEFEE * tests/4844: check BLOBBASEFEE on excess blob gas tests * tests/cancun: More EIP-7516 coverage * tests: tox fixes
1 parent e7a0603 commit 789de47

File tree

5 files changed

+234
-17
lines changed

5 files changed

+234
-17
lines changed

src/ethereum_test_tools/vm/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ class Opcodes(Opcode, Enum):
249249
SELFBALANCE = Opcode(0x47, pushed_stack_items=1)
250250
BASEFEE = Opcode(0x48, pushed_stack_items=1)
251251
BLOBHASH = Opcode(0x49, popped_stack_items=1, pushed_stack_items=1)
252+
BLOBBASEFEE = Opcode(0x4A, popped_stack_items=0, pushed_stack_items=1)
252253

253254
POP = Opcode(0x50, popped_stack_items=1)
254255
MLOAD = Opcode(0x51, popped_stack_items=1, pushed_stack_items=1)

tests/cancun/eip4844_blobs/test_excess_blob_gas.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,9 @@
2727

2828
import pytest
2929

30+
from ethereum_test_tools import Account, Block, BlockchainTestFiller, Environment, Header
31+
from ethereum_test_tools import Opcodes as Op
3032
from ethereum_test_tools import (
31-
Account,
32-
Block,
33-
BlockchainTestFiller,
34-
Environment,
35-
Header,
3633
TestAddress,
3734
TestAddress2,
3835
Transaction,
@@ -156,17 +153,21 @@ def tx_value() -> int: # noqa: D103
156153

157154

158155
@pytest.fixture
159-
def tx_exact_cost(tx_value: int, tx_max_fee_per_gas: int, tx_data_cost: int) -> int: # noqa: D103
160-
tx_gas = 21000
161-
return (tx_gas * tx_max_fee_per_gas) + tx_value + tx_data_cost
156+
def tx_gas_limit() -> int: # noqa: D103
157+
return 45000
162158

163159

164160
@pytest.fixture
165-
def pre(tx_exact_cost: int) -> Mapping[str, Account]: # noqa: D103
166-
return {
167-
TestAddress: Account(balance=tx_exact_cost),
168-
TestAddress2: Account(balance=10**40),
169-
}
161+
def tx_exact_cost( # noqa: D103
162+
tx_value: int, tx_max_fee_per_gas: int, tx_data_cost: int, tx_gas_limit: int
163+
) -> int:
164+
return (tx_gas_limit * tx_max_fee_per_gas) + tx_value + tx_data_cost
165+
166+
167+
@pytest.fixture
168+
def destination_account_bytecode() -> bytes: # noqa: D103
169+
# Verify that the BLOBBASEFEE opcode reflects the current blob gas cost
170+
return Op.SSTORE(0, Op.BLOBBASEFEE)
170171

171172

172173
@pytest.fixture
@@ -175,9 +176,25 @@ def destination_account() -> str: # noqa: D103
175176

176177

177178
@pytest.fixture
178-
def post(destination_account: str, tx_value: int) -> Mapping[str, Account]: # noqa: D103
179+
def pre( # noqa: D103
180+
destination_account: str, destination_account_bytecode: bytes, tx_exact_cost: int
181+
) -> Mapping[str, Account]:
179182
return {
180-
destination_account: Account(balance=tx_value),
183+
TestAddress: Account(balance=tx_exact_cost),
184+
TestAddress2: Account(balance=10**40),
185+
destination_account: Account(balance=0, code=destination_account_bytecode),
186+
}
187+
188+
189+
@pytest.fixture
190+
def post( # noqa: D103
191+
destination_account: str, tx_value: int, block_fee_per_blob_gas: int
192+
) -> Mapping[str, Account]:
193+
return {
194+
destination_account: Account(
195+
storage={0: block_fee_per_blob_gas},
196+
balance=tx_value,
197+
),
181198
}
182199

183200

@@ -186,6 +203,7 @@ def tx( # noqa: D103
186203
new_blobs: int,
187204
tx_max_fee_per_gas: int,
188205
tx_max_fee_per_blob_gas: int,
206+
tx_gas_limit: int,
189207
destination_account: str,
190208
):
191209
if new_blobs == 0:
@@ -195,7 +213,7 @@ def tx( # noqa: D103
195213
nonce=0,
196214
to=destination_account,
197215
value=1,
198-
gas_limit=21000,
216+
gas_limit=tx_gas_limit,
199217
max_fee_per_gas=tx_max_fee_per_gas,
200218
max_priority_fee_per_gas=0,
201219
access_list=[],
@@ -206,7 +224,7 @@ def tx( # noqa: D103
206224
nonce=0,
207225
to=destination_account,
208226
value=1,
209-
gas_limit=21000,
227+
gas_limit=tx_gas_limit,
210228
max_fee_per_gas=tx_max_fee_per_gas,
211229
max_priority_fee_per_gas=0,
212230
max_fee_per_blob_gas=tx_max_fee_per_blob_gas,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Tests for EIP-7516: BLOBBASEFEE opcode
3+
"""
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
"""
2+
abstract: Tests [EIP-7516: BLOBBASEFEE opcode](https://eips.ethereum.org/EIPS/eip-7516)
3+
4+
Test BLOBGASFEE opcode [EIP-7516: BLOBBASEFEE opcode](https://eips.ethereum.org/EIPS/eip-7516)
5+
6+
""" # noqa: E501
7+
from itertools import count
8+
from typing import Dict
9+
10+
import pytest
11+
12+
from ethereum_test_tools import Account, Block, BlockchainTestFiller, Environment
13+
from ethereum_test_tools import Opcodes as Op
14+
from ethereum_test_tools import StateTestFiller, Storage, TestAddress, Transaction
15+
16+
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-7516.md"
17+
REFERENCE_SPEC_VERSION = "2ade0452efe8124378f35284676ddfd16dd56ecd"
18+
19+
# Code address used to call the test bytecode on every test case.
20+
code_caller_address = 0x100
21+
code_callee_address = 0x200
22+
23+
BLOBBASEFEE_GAS = 2
24+
25+
26+
@pytest.fixture
27+
def call_gas() -> int:
28+
"""
29+
Amount of gas to use when calling the callee code.
30+
"""
31+
return 0xFFFF
32+
33+
34+
@pytest.fixture
35+
def caller_code(
36+
call_gas: int,
37+
) -> bytes:
38+
"""
39+
Bytecode used to call the bytecode containing the BLOBBASEFEE opcode.
40+
"""
41+
return Op.SSTORE(Op.NUMBER, Op.CALL(call_gas, Op.PUSH20(code_callee_address), 0, 0, 0, 0, 0))
42+
43+
44+
@pytest.fixture
45+
def callee_code() -> bytes:
46+
"""
47+
Bytecode under test, by default, only call the BLOBBASEFEE opcode.
48+
"""
49+
return bytes(Op.BLOBBASEFEE + Op.STOP)
50+
51+
52+
@pytest.fixture
53+
def pre(
54+
caller_code: bytes,
55+
callee_code: bytes,
56+
) -> Dict:
57+
"""
58+
Prepares the pre state of all test cases, by setting the balance of the
59+
source account of all test transactions, and the required code.
60+
"""
61+
return {
62+
TestAddress: Account(balance=10**40),
63+
code_caller_address: Account(
64+
balance=0,
65+
code=caller_code,
66+
),
67+
code_callee_address: Account(
68+
balance=0,
69+
code=callee_code,
70+
),
71+
}
72+
73+
74+
@pytest.fixture
75+
def tx() -> Transaction:
76+
"""
77+
Prepares the test transaction, by setting the destination account, the
78+
transaction value, the transaction gas limit, and the transaction data.
79+
"""
80+
return Transaction(
81+
gas_price=1_000_000_000,
82+
gas_limit=1_000_000,
83+
to=code_caller_address,
84+
value=0,
85+
data=b"",
86+
)
87+
88+
89+
@pytest.mark.parametrize(
90+
"callee_code,call_fails",
91+
[
92+
pytest.param(Op.BLOBBASEFEE * 1024, False, id="no_stack_overflow"),
93+
pytest.param(Op.BLOBBASEFEE * 1025, True, id="stack_overflow"),
94+
],
95+
)
96+
@pytest.mark.valid_from("Cancun")
97+
def test_blobbasefee_stack_overflow(
98+
state_test: StateTestFiller,
99+
pre: Dict,
100+
tx: Transaction,
101+
call_fails: bool,
102+
):
103+
"""
104+
Tests that the BLOBBASEFEE opcode produces an stack overflow by using it repatedly.
105+
"""
106+
post = {
107+
code_caller_address: Account(
108+
storage={1: 0 if call_fails else 1},
109+
),
110+
code_callee_address: Account(
111+
balance=0,
112+
),
113+
}
114+
state_test(
115+
env=Environment(),
116+
pre=pre,
117+
txs=[tx],
118+
post=post,
119+
)
120+
121+
122+
@pytest.mark.parametrize(
123+
"call_gas,call_fails",
124+
[
125+
pytest.param(BLOBBASEFEE_GAS, False, id="enough_gas"),
126+
pytest.param(BLOBBASEFEE_GAS - 1, True, id="out_of_gas"),
127+
],
128+
)
129+
@pytest.mark.valid_from("Cancun")
130+
def test_blobbasefee_out_of_gas(
131+
state_test: StateTestFiller,
132+
pre: Dict,
133+
tx: Transaction,
134+
call_fails: bool,
135+
):
136+
"""
137+
Tests that the BLOBBASEFEE opcode produces an stack overflow by using it repatedly.
138+
"""
139+
post = {
140+
code_caller_address: Account(
141+
storage={1: 0 if call_fails else 1},
142+
),
143+
code_callee_address: Account(
144+
balance=0,
145+
),
146+
}
147+
state_test(
148+
env=Environment(),
149+
pre=pre,
150+
txs=[tx],
151+
post=post,
152+
)
153+
154+
155+
@pytest.mark.valid_at_transition_to("Cancun")
156+
def test_blobbasefee_before_fork(
157+
blockchain_test: BlockchainTestFiller,
158+
pre: Dict,
159+
tx: Transaction,
160+
):
161+
"""
162+
Tests that the BLOBBASEFEE opcode results on exception when called before the fork.
163+
"""
164+
code_caller_storage = Storage()
165+
166+
nonce = count(0)
167+
168+
timestamps = [7_500, 14_999, 15_000]
169+
170+
blocks = []
171+
172+
for number, timestamp in enumerate(timestamps):
173+
blocks.append(
174+
Block(
175+
txs=[tx.with_nonce(next(nonce))],
176+
timestamp=timestamp,
177+
),
178+
)
179+
code_caller_storage[number + 1] = 0 if timestamp < 15_000 else 1
180+
181+
post = {
182+
code_caller_address: Account(
183+
storage=code_caller_storage,
184+
),
185+
code_callee_address: Account(
186+
balance=0,
187+
),
188+
}
189+
blockchain_test(
190+
genesis_environment=Environment(),
191+
pre=pre,
192+
blocks=blocks,
193+
post=post,
194+
)

whitelist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ selfdestruct
479479
selfdestructing
480480
sendall
481481
blobhash
482+
blobbasefee
482483
mcopy
483484
precompile
484485
precompiles

0 commit comments

Comments
 (0)