88
99import pytest
1010
11- from ethereum_test_forks import Cancun , Fork
11+ from ethereum_test_forks import Fork
1212from ethereum_test_tools import (
13+ EOA ,
1314 Account ,
1415 Address ,
16+ Alloc ,
1517 Block ,
1618 BlockchainTestFiller ,
1719 BlockException ,
1820 EngineAPIError ,
1921 Environment ,
2022 Hash ,
2123 Header ,
22- TestAddress ,
2324 Transaction ,
2425 add_kzg_version ,
2526)
2930REFERENCE_SPEC_GIT_PATH = ref_spec_4844 .git_path
3031REFERENCE_SPEC_VERSION = ref_spec_4844 .version
3132
32- # All tests run on the transition fork from Shanghai to Cancun
33- pytestmark = pytest .mark .valid_at_transition_to ("Cancun" )
34-
35-
3633# Timestamp of the fork
3734FORK_TIMESTAMP = 15_000
3835
@@ -43,89 +40,171 @@ def env() -> Environment: # noqa: D103
4340
4441
4542@pytest .fixture
46- def pre () -> Mapping [Address , Account ]: # noqa: D103
47- return {
48- TestAddress : Account (balance = 10 ** 40 ),
49- }
43+ def pre_fork_blobs_per_block (fork : Fork ) -> int :
44+ """Amount of blobs to produce with the pre-fork rules."""
45+ if fork .supports_blobs (timestamp = 0 ):
46+ return fork .max_blobs_per_block (timestamp = 0 )
47+ return 0
48+
49+
50+ @pytest .fixture
51+ def sender (pre : Alloc ) -> EOA :
52+ """Sender account."""
53+ return pre .fund_eoa ()
5054
5155
5256@pytest .fixture
53- def pre_fork_blocks ():
57+ def pre_fork_blocks (
58+ pre_fork_blobs_per_block : int ,
59+ destination_account : Address ,
60+ sender : EOA ,
61+ ) -> List [Block ]:
5462 """Generate blocks to reach the fork."""
55- return [Block (timestamp = t ) for t in range (999 , FORK_TIMESTAMP , 1_000 )]
63+ return [
64+ Block (
65+ txs = [
66+ Transaction (
67+ ty = Spec .BLOB_TX_TYPE ,
68+ to = destination_account ,
69+ value = 1 ,
70+ gas_limit = 3_000_000 ,
71+ max_fee_per_gas = 1_000_000 ,
72+ max_priority_fee_per_gas = 10 ,
73+ max_fee_per_blob_gas = 100 ,
74+ access_list = [],
75+ blob_versioned_hashes = add_kzg_version (
76+ [Hash (x ) for x in range (pre_fork_blobs_per_block )],
77+ Spec .BLOB_COMMITMENT_VERSION_KZG ,
78+ ),
79+ sender = sender ,
80+ )
81+ ]
82+ if pre_fork_blobs_per_block > 0
83+ else [],
84+ timestamp = t ,
85+ )
86+ for t in range (999 , FORK_TIMESTAMP , 1_000 )
87+ ]
88+
89+
90+ @pytest .fixture
91+ def pre_fork_excess_blobs (
92+ fork : Fork ,
93+ pre_fork_blobs_per_block : int ,
94+ pre_fork_blocks : List [Block ],
95+ ) -> int :
96+ """
97+ Return the cummulative excess blobs up until the fork given the pre_fork_blobs_per_block
98+ and the target blobs in the fork prior.
99+ """
100+ if not fork .supports_blobs (timestamp = 0 ):
101+ return 0
102+ target_blobs = fork .target_blobs_per_block (timestamp = 0 )
103+ if pre_fork_blobs_per_block > target_blobs :
104+ return (pre_fork_blobs_per_block - target_blobs ) * (len (pre_fork_blocks ) - 1 )
105+ return 0
56106
57107
58108@pytest .fixture
59109def post_fork_block_count (fork : Fork ) -> int :
60110 """Amount of blocks to produce with the post-fork rules."""
61111 return SpecHelpers .get_min_excess_blobs_for_blob_gas_price (fork = fork , blob_gas_price = 2 ) // (
62- fork .max_blobs_per_block () - fork .target_blobs_per_block ()
112+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP )
113+ - fork .target_blobs_per_block (timestamp = FORK_TIMESTAMP )
63114 )
64115
65116
66117@pytest .fixture
67- def blob_count_per_block ( ) -> int :
118+ def post_fork_blobs_per_block ( fork : Fork ) -> int :
68119 """Amount of blocks to produce with the post-fork rules."""
69- return 4
120+ return fork . target_blobs_per_block ( timestamp = FORK_TIMESTAMP ) + 1
70121
71122
72123@pytest .fixture
73- def destination_account () -> Address : # noqa: D103
74- return Address (0x100 )
124+ def destination_account (pre : Alloc ) -> Address : # noqa: D103
125+ # Empty account to receive the blobs
126+ return pre .fund_eoa (amount = 0 )
127+
128+
129+ @pytest .fixture
130+ def fork_block_excess_blob_gas (
131+ fork : Fork ,
132+ pre_fork_excess_blobs : int ,
133+ pre_fork_blobs_per_block : int ,
134+ ) -> int :
135+ """Calculate the expected excess blob gas for the fork block."""
136+ if pre_fork_blobs_per_block == 0 :
137+ return 0
138+ calc_excess_blob_gas_post_fork = fork .excess_blob_gas_calculator (timestamp = FORK_TIMESTAMP )
139+ return calc_excess_blob_gas_post_fork (
140+ parent_excess_blobs = pre_fork_excess_blobs ,
141+ parent_blob_count = pre_fork_blobs_per_block ,
142+ )
75143
76144
77145@pytest .fixture
78146def post_fork_blocks (
79147 destination_account : Address ,
80148 post_fork_block_count : int ,
81- blob_count_per_block : int ,
149+ post_fork_blobs_per_block : int ,
150+ fork_block_excess_blob_gas : int ,
151+ sender : EOA ,
82152):
83153 """Generate blocks past the fork."""
84- return [
85- Block (
86- txs = [
154+ blocks = []
155+ for i in range (post_fork_block_count ):
156+ txs = (
157+ [
87158 Transaction (
88159 ty = Spec .BLOB_TX_TYPE ,
89- nonce = b ,
90160 to = destination_account ,
91161 value = 1 ,
92- gas_limit = 3000000 ,
93- max_fee_per_gas = 1000000 ,
162+ gas_limit = 3_000_000 ,
163+ max_fee_per_gas = 1_000_000 ,
94164 max_priority_fee_per_gas = 10 ,
95165 max_fee_per_blob_gas = 100 ,
96- access_list = [],
97166 blob_versioned_hashes = add_kzg_version (
98- [Hash (x ) for x in range (blob_count_per_block )],
167+ [Hash (x ) for x in range (post_fork_blobs_per_block )],
99168 Spec .BLOB_COMMITMENT_VERSION_KZG ,
100169 ),
170+ sender = sender ,
101171 )
102- if blob_count_per_block > 0
103- else Transaction (
104- ty = 2 ,
105- nonce = b ,
106- to = destination_account ,
107- value = 1 ,
108- gas_limit = 3000000 ,
109- max_fee_per_gas = 1000000 ,
110- max_priority_fee_per_gas = 10 ,
111- access_list = [],
112- )
113- ],
172+ ]
173+ if post_fork_blobs_per_block > 0
174+ else []
114175 )
115- for b in range (post_fork_block_count )
116- ]
176+ if i == 0 :
177+ # Check the excess blob gas on the first block of the new fork
178+ blocks .append (
179+ Block (
180+ txs = txs ,
181+ excess_blob_gas = fork_block_excess_blob_gas ,
182+ )
183+ )
184+ else :
185+ blocks .append (Block (txs = txs ))
186+ return blocks
117187
118188
119189@pytest .fixture
120190def post ( # noqa: D103
191+ pre_fork_blocks : List [Block ],
192+ pre_fork_blobs_per_block : int ,
121193 post_fork_block_count : int ,
194+ post_fork_blobs_per_block : int ,
122195 destination_account : Address ,
123196) -> Mapping [Address , Account ]:
197+ pre_fork_value = len (pre_fork_blocks ) if pre_fork_blobs_per_block > 0 else 0
198+ post_fork_value = post_fork_block_count if post_fork_blobs_per_block > 0 else 0
199+ total_value = pre_fork_value + post_fork_value
200+ if total_value == 0 :
201+ return {}
124202 return {
125- destination_account : Account (balance = post_fork_block_count ),
203+ destination_account : Account (balance = total_value ),
126204 }
127205
128206
207+ @pytest .mark .valid_at_transition_to ("Cancun" )
129208@pytest .mark .parametrize (
130209 "excess_blob_gas_present,blob_gas_used_present" ,
131210 [
@@ -137,7 +216,7 @@ def post( # noqa: D103
137216def test_invalid_pre_fork_block_with_blob_fields (
138217 blockchain_test : BlockchainTestFiller ,
139218 env : Environment ,
140- pre : Mapping [ Address , Account ] ,
219+ pre : Alloc ,
141220 pre_fork_blocks : List [Block ],
142221 excess_blob_gas_present : bool ,
143222 blob_gas_used_present : bool ,
@@ -166,10 +245,10 @@ def test_invalid_pre_fork_block_with_blob_fields(
166245 )
167246 ],
168247 genesis_environment = env ,
169- tag = "invalid_pre_fork_blob_fields" ,
170248 )
171249
172250
251+ @pytest .mark .valid_at_transition_to ("Cancun" )
173252@pytest .mark .parametrize (
174253 "excess_blob_gas_missing,blob_gas_used_missing" ,
175254 [
@@ -181,7 +260,7 @@ def test_invalid_pre_fork_block_with_blob_fields(
181260def test_invalid_post_fork_block_without_blob_fields (
182261 blockchain_test : BlockchainTestFiller ,
183262 env : Environment ,
184- pre : Mapping [ Address , Account ] ,
263+ pre : Alloc ,
185264 pre_fork_blocks : List [Block ],
186265 excess_blob_gas_missing : bool ,
187266 blob_gas_used_missing : bool ,
@@ -211,28 +290,100 @@ def test_invalid_post_fork_block_without_blob_fields(
211290 )
212291 ],
213292 genesis_environment = env ,
214- tag = "blob_fields_missing_post_fork" ,
215293 )
216294
217295
218- @pytest .mark .parametrize (
219- "post_fork_block_count,blob_count_per_block" ,
220- [
221- (
222- SpecHelpers .get_min_excess_blobs_for_blob_gas_price (fork = Cancun , blob_gas_price = 2 )
223- // (Cancun .max_blobs_per_block () - Cancun .target_blobs_per_block ())
296+ @pytest .mark .valid_at_transition_to ("Cancun" , subsequent_forks = False )
297+ @pytest .mark .parametrize_by_fork (
298+ "post_fork_block_count,post_fork_blobs_per_block" ,
299+ lambda fork : [
300+ pytest .param (
301+ SpecHelpers .get_min_excess_blobs_for_blob_gas_price (fork = fork , blob_gas_price = 2 )
302+ // (
303+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP )
304+ - fork .target_blobs_per_block (timestamp = FORK_TIMESTAMP )
305+ )
306+ + 2 ,
307+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP ),
308+ id = "max_blobs" ,
309+ ),
310+ pytest .param (10 , 0 , id = "no_blobs" ),
311+ pytest .param (10 , fork .target_blobs_per_block (timestamp = FORK_TIMESTAMP ), id = "target_blobs" ),
312+ ],
313+ )
314+ def test_fork_transition_excess_blob_gas_at_blob_genesis (
315+ blockchain_test : BlockchainTestFiller ,
316+ env : Environment ,
317+ pre : Alloc ,
318+ pre_fork_blocks : List [Block ],
319+ post_fork_blocks : List [Block ],
320+ post : Mapping [Address , Account ],
321+ ):
322+ """
323+ Test `excessBlobGas` calculation in the header when the fork is activated.
324+
325+ Also produce enough blocks to test the blob gas price increase when the block is full with
326+ `SpecHelpers.max_blobs_per_block()` blobs.
327+ """
328+ blockchain_test (
329+ pre = pre ,
330+ post = post ,
331+ blocks = pre_fork_blocks + post_fork_blocks ,
332+ genesis_environment = env ,
333+ )
334+
335+
336+ @pytest .mark .valid_at_transition_to ("Prague" , subsequent_forks = True )
337+ @pytest .mark .parametrize_by_fork (
338+ "post_fork_block_count,pre_fork_blobs_per_block,post_fork_blobs_per_block" ,
339+ lambda fork : [
340+ pytest .param (
341+ SpecHelpers .get_min_excess_blobs_for_blob_gas_price (fork = fork , blob_gas_price = 2 )
342+ // (
343+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP )
344+ - fork .target_blobs_per_block (timestamp = FORK_TIMESTAMP )
345+ )
224346 + 2 ,
225- Cancun .max_blobs_per_block (),
347+ fork .max_blobs_per_block (timestamp = 0 ),
348+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP ),
349+ id = "max_blobs" ,
350+ ),
351+ pytest .param (
352+ 10 ,
353+ 0 ,
354+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP ),
355+ id = "no_blobs_before" ,
356+ ),
357+ pytest .param (
358+ 10 ,
359+ fork .max_blobs_per_block (timestamp = 0 ),
360+ 0 ,
361+ id = "no_blobs_after" ,
362+ ),
363+ pytest .param (
364+ 10 ,
365+ fork .target_blobs_per_block (timestamp = 0 ),
366+ fork .target_blobs_per_block (timestamp = FORK_TIMESTAMP ),
367+ id = "target_blobs" ,
368+ ),
369+ pytest .param (
370+ 10 ,
371+ 1 ,
372+ fork .max_blobs_per_block (timestamp = FORK_TIMESTAMP ),
373+ id = "single_blob_to_max_blobs" ,
374+ ),
375+ pytest .param (
376+ 10 ,
377+ fork .max_blobs_per_block (timestamp = 0 ),
378+ 1 ,
379+ id = "max_blobs_to_single_blob" ,
226380 ),
227- (10 , 0 ),
228- (10 , Cancun .target_blobs_per_block ()),
229381 ],
230- ids = ["max_blobs" , "no_blobs" , "target_blobs" ],
231382)
232- def test_fork_transition_excess_blob_gas (
383+ def test_fork_transition_excess_blob_gas_post_blob_genesis (
233384 blockchain_test : BlockchainTestFiller ,
234385 env : Environment ,
235- pre : Mapping [ Address , Account ] ,
386+ pre : Alloc ,
236387 pre_fork_blocks : List [Block ],
237388 post_fork_blocks : List [Block ],
238389 post : Mapping [Address , Account ],
@@ -248,5 +399,4 @@ def test_fork_transition_excess_blob_gas(
248399 post = post ,
249400 blocks = pre_fork_blocks + post_fork_blocks ,
250401 genesis_environment = env ,
251- tag = "correct_initial_blob_gas_calc" ,
252402 )
0 commit comments