Skip to content

Commit 903f7ef

Browse files
committed
feat(forks): New methods for EIP-7691+EIP-7762
1 parent 0a5e37e commit 903f7ef

File tree

3 files changed

+247
-34
lines changed

3 files changed

+247
-34
lines changed

src/ethereum_test_forks/base_fork.py

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __call__(self, block_number: int = 0, timestamp: int = 0) -> Any:
2929

3030
class MemoryExpansionGasCalculator(Protocol):
3131
"""
32-
A protocol to calculate the gas cost of memory expansion for a given fork.
32+
A protocol to calculate the gas cost of memory expansion at a given fork.
3333
"""
3434

3535
def __call__(self, *, new_bytes: int, previous_bytes: int = 0) -> int:
@@ -41,7 +41,7 @@ def __call__(self, *, new_bytes: int, previous_bytes: int = 0) -> int:
4141

4242
class CalldataGasCalculator(Protocol):
4343
"""
44-
A protocol to calculate the transaction gas cost of calldata for a given fork.
44+
A protocol to calculate the transaction gas cost of calldata at a given fork.
4545
"""
4646

4747
def __call__(self, *, data: BytesConvertible) -> int:
@@ -53,7 +53,7 @@ def __call__(self, *, data: BytesConvertible) -> int:
5353

5454
class TransactionIntrinsicCostCalculator(Protocol):
5555
"""
56-
A protocol to calculate the intrinsic gas cost of a transaction for a given fork.
56+
A protocol to calculate the intrinsic gas cost of a transaction at a given fork.
5757
"""
5858

5959
def __call__(
@@ -70,6 +70,37 @@ def __call__(
7070
pass
7171

7272

73+
class BlobGasPriceCalculator(Protocol):
74+
"""
75+
A protocol to calculate the blob gas price given the excess blob gas at a given fork.
76+
"""
77+
78+
def __call__(self, *, excess_blob_gas: int) -> int:
79+
"""
80+
Returns the blob gas price given the excess blob gas.
81+
"""
82+
pass
83+
84+
85+
class ExcessBlobGasCalculator(Protocol):
86+
"""
87+
A protocol to calculate the excess blob gas for a block at a given fork.
88+
"""
89+
90+
def __call__(
91+
self,
92+
*,
93+
parent_excess_blob_gas: int | None = None,
94+
parent_excess_blobs: int | None = None,
95+
parent_blob_gas_used: int | None = None,
96+
parent_blob_count: int | None = None,
97+
) -> int:
98+
"""
99+
Returns the excess blob gas given the parent's excess blob gas and blob gas used.
100+
"""
101+
pass
102+
103+
73104
class BaseForkMeta(ABCMeta):
74105
"""
75106
Metaclass for BaseFork
@@ -191,23 +222,25 @@ def header_blob_gas_used_required(cls, block_number: int = 0, timestamp: int = 0
191222

192223
@classmethod
193224
@abstractmethod
194-
def header_beacon_root_required(cls, block_number: int, timestamp: int) -> bool:
225+
def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
195226
"""
196227
Returns true if the header must contain parent beacon block root
197228
"""
198229
pass
199230

200231
@classmethod
201232
@abstractmethod
202-
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
233+
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
203234
"""
204235
Returns true if the header must contain beacon chain requests
205236
"""
206237
pass
207238

208239
@classmethod
209240
@abstractmethod
210-
def header_target_blobs_per_block_required(cls, block_number: int, timestamp: int) -> bool:
241+
def header_target_blobs_per_block_required(
242+
cls, block_number: int = 0, timestamp: int = 0
243+
) -> bool:
211244
"""
212245
Returns true if the header must contain target blobs per block.
213246
"""
@@ -256,25 +289,61 @@ def transaction_intrinsic_cost_calculator(
256289

257290
@classmethod
258291
@abstractmethod
259-
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
292+
def blob_gas_price_calculator(
293+
cls, block_number: int = 0, timestamp: int = 0
294+
) -> BlobGasPriceCalculator:
295+
"""
296+
Returns a callable that calculates the blob gas price at a given fork.
297+
"""
298+
pass
299+
300+
@classmethod
301+
@abstractmethod
302+
def excess_blob_gas_calculator(
303+
cls, block_number: int = 0, timestamp: int = 0
304+
) -> ExcessBlobGasCalculator:
305+
"""
306+
Returns a callable that calculates the excess blob gas for a block at a given fork.
307+
"""
308+
pass
309+
310+
@classmethod
311+
@abstractmethod
312+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
313+
"""
314+
Returns the minimum base fee per blob gas at a given fork.
315+
"""
316+
pass
317+
318+
@classmethod
319+
@abstractmethod
320+
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
321+
"""
322+
Returns the amount of blob gas used per blob at a given fork.
323+
"""
324+
pass
325+
326+
@classmethod
327+
@abstractmethod
328+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
260329
"""
261-
Returns the amount of blob gas used per blob for a given fork.
330+
Returns the blob base fee update fraction at a given fork.
262331
"""
263332
pass
264333

265334
@classmethod
266335
@abstractmethod
267-
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
336+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
268337
"""
269-
Returns the target blobs per block for a given fork.
338+
Returns the target blobs per block at a given fork.
270339
"""
271340
pass
272341

273342
@classmethod
274343
@abstractmethod
275-
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
344+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
276345
"""
277-
Returns the max blobs per block for a given fork.
346+
Returns the max blobs per block at a given fork.
278347
"""
279348
pass
280349

src/ethereum_test_forks/forks/forks.py

Lines changed: 141 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,19 @@
1616

1717
from ..base_fork import (
1818
BaseFork,
19+
BlobGasPriceCalculator,
1920
CalldataGasCalculator,
21+
ExcessBlobGasCalculator,
2022
MemoryExpansionGasCalculator,
2123
TransactionIntrinsicCostCalculator,
2224
)
2325
from ..gas_costs import GasCosts
26+
from .helpers import ceiling_division, fake_exponential
2427

2528
CURRENT_FILE = Path(realpath(__file__))
2629
CURRENT_FOLDER = CURRENT_FILE.parent
2730

2831

29-
def ceiling_division(a: int, b: int) -> int:
30-
"""
31-
Calculates the ceil without using floating point.
32-
Used by many of the EVM's formulas
33-
"""
34-
return -(a // -b)
35-
36-
3732
# All forks must be listed here !!! in the order they were introduced !!!
3833
class Frontier(BaseFork, solc_name="homestead"):
3934
"""
@@ -228,28 +223,60 @@ def fn(
228223
return fn
229224

230225
@classmethod
231-
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
226+
def blob_gas_price_calculator(
227+
cls, block_number: int = 0, timestamp: int = 0
228+
) -> BlobGasPriceCalculator:
232229
"""
233-
Returns the amount of blob gas used per blob for a given fork.
230+
Returns a callable that calculates the blob gas price at a given fork.
234231
"""
235-
return 0
232+
raise NotImplementedError("Blob gas price calculator is not supported in Frontier")
236233

237234
@classmethod
238-
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
235+
def excess_blob_gas_calculator(
236+
cls, block_number: int = 0, timestamp: int = 0
237+
) -> ExcessBlobGasCalculator:
239238
"""
240-
Returns the target number of blobs per block for a given fork.
239+
Returns a callable that calculates the excess blob gas for a block at a given fork.
241240
"""
242-
return 0
241+
raise NotImplementedError("Excess blob gas calculator is not supported in Frontier")
243242

244243
@classmethod
245-
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
244+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
246245
"""
247-
Returns the max number of blobs per block for a given fork.
246+
Returns the amount of blob gas used per blob at a given fork.
248247
"""
249-
return 0
248+
raise NotImplementedError("Base fee per blob gas is not supported in Frontier")
249+
250+
@classmethod
251+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
252+
"""
253+
Returns the blob base fee update fraction at a given fork.
254+
"""
255+
raise NotImplementedError("Blob base fee update fraction is not supported in Frontier")
256+
257+
@classmethod
258+
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
259+
"""
260+
Returns the amount of blob gas used per blob at a given fork.
261+
"""
262+
raise NotImplementedError("Blob gas per blob is not supported in Frontier")
263+
264+
@classmethod
265+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
266+
"""
267+
Returns the target number of blobs per block at a given fork.
268+
"""
269+
raise NotImplementedError("Target blobs per block is not supported in Frontier")
250270

251271
@classmethod
252-
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
272+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
273+
"""
274+
Returns the max number of blobs per block at a given fork.
275+
"""
276+
raise NotImplementedError("Max blobs per block is not supported in Frontier")
277+
278+
@classmethod
279+
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
253280
"""
254281
At genesis, header must not contain beacon chain requests.
255282
"""
@@ -992,21 +1019,85 @@ def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0)
9921019
return True
9931020

9941021
@classmethod
995-
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
1022+
def blob_gas_price_calculator(
1023+
cls, block_number: int = 0, timestamp: int = 0
1024+
) -> BlobGasPriceCalculator:
1025+
"""
1026+
Returns a callable that calculates the blob gas price at Cancun.
1027+
"""
1028+
min_base_fee_per_blob_gas = cls.min_base_fee_per_blob_gas(block_number, timestamp)
1029+
blob_base_fee_update_fraction = cls.blob_base_fee_update_fraction(block_number, timestamp)
1030+
1031+
def fn(*, excess_blob_gas) -> int:
1032+
return fake_exponential(
1033+
min_base_fee_per_blob_gas,
1034+
excess_blob_gas,
1035+
blob_base_fee_update_fraction,
1036+
)
1037+
1038+
return fn
1039+
1040+
@classmethod
1041+
def excess_blob_gas_calculator(
1042+
cls, block_number: int = 0, timestamp: int = 0
1043+
) -> ExcessBlobGasCalculator:
1044+
"""
1045+
Returns a callable that calculates the excess blob gas for a block at Cancun.
1046+
"""
1047+
target_blobs_per_block = cls.target_blobs_per_block(block_number, timestamp)
1048+
blob_gas_per_blob = cls.blob_gas_per_blob(block_number, timestamp)
1049+
target_blob_gas_per_block = target_blobs_per_block * blob_gas_per_blob
1050+
1051+
def fn(
1052+
*,
1053+
parent_excess_blob_gas: int | None = None,
1054+
parent_excess_blobs: int | None = None,
1055+
parent_blob_gas_used: int | None = None,
1056+
parent_blob_count: int | None = None,
1057+
) -> int:
1058+
if parent_excess_blob_gas is None:
1059+
assert parent_excess_blobs is not None, "Parent excess blobs are required"
1060+
parent_excess_blob_gas = parent_excess_blobs * blob_gas_per_blob
1061+
if parent_blob_gas_used is None:
1062+
assert parent_blob_count is not None, "Parent blob count is required"
1063+
parent_blob_gas_used = parent_blob_count * blob_gas_per_blob
1064+
if parent_excess_blob_gas + parent_blob_gas_used < target_blob_gas_per_block:
1065+
return 0
1066+
else:
1067+
return parent_excess_blob_gas + parent_blob_gas_used - target_blob_gas_per_block
1068+
1069+
return fn
1070+
1071+
@classmethod
1072+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
1073+
"""
1074+
Returns the minimum base fee per blob gas for Cancun.
1075+
"""
1076+
return 1
1077+
1078+
@classmethod
1079+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
1080+
"""
1081+
Returns the blob base fee update fraction for Cancun.
1082+
"""
1083+
return 3338477
1084+
1085+
@classmethod
1086+
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
9961087
"""
9971088
Blobs are enabled starting from Cancun.
9981089
"""
9991090
return 2**17
10001091

10011092
@classmethod
1002-
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
1093+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
10031094
"""
10041095
Blobs are enabled starting from Cancun, with a static target of 3 blobs.
10051096
"""
10061097
return 3
10071098

10081099
@classmethod
1009-
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
1100+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
10101101
"""
10111102
Blobs are enabled starting from Cancun, with a static max of 6 blobs.
10121103
"""
@@ -1178,6 +1269,34 @@ def fn(
11781269

11791270
return fn
11801271

1272+
@classmethod
1273+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
1274+
"""
1275+
Returns the minimum base fee per blob gas for Prague.
1276+
"""
1277+
return 2**25
1278+
1279+
@classmethod
1280+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
1281+
"""
1282+
Returns the blob base fee update fraction for Prague.
1283+
"""
1284+
return 5007716
1285+
1286+
@classmethod
1287+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
1288+
"""
1289+
Target blob count of 6 for Prague.
1290+
"""
1291+
return 6
1292+
1293+
@classmethod
1294+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
1295+
"""
1296+
Max blob count of 9 for Prague.
1297+
"""
1298+
return 9
1299+
11811300
@classmethod
11821301
def pre_allocation_blockchain(cls) -> Mapping:
11831302
"""
@@ -1241,7 +1360,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
12411360
return new_allocation | super(Prague, cls).pre_allocation_blockchain() # type: ignore
12421361

12431362
@classmethod
1244-
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
1363+
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
12451364
"""
12461365
Prague requires that the execution layer header contains the beacon
12471366
chain requests hash.

0 commit comments

Comments
 (0)