Skip to content

Commit 69e8489

Browse files
committed
feat(forks): New methods for EIP-7691+EIP-7762
1 parent 8948a84 commit 69e8489

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, floor: bool = False) -> int:
@@ -65,7 +65,7 @@ def __call__(self, *, data: BytesConvertible) -> int:
6565

6666
class TransactionIntrinsicCostCalculator(Protocol):
6767
"""
68-
A protocol to calculate the intrinsic gas cost of a transaction for a given fork.
68+
A protocol to calculate the intrinsic gas cost of a transaction at a given fork.
6969
"""
7070

7171
def __call__(
@@ -97,6 +97,37 @@ def __call__(
9797
pass
9898

9999

100+
class BlobGasPriceCalculator(Protocol):
101+
"""
102+
A protocol to calculate the blob gas price given the excess blob gas at a given fork.
103+
"""
104+
105+
def __call__(self, *, excess_blob_gas: int) -> int:
106+
"""
107+
Returns the blob gas price given the excess blob gas.
108+
"""
109+
pass
110+
111+
112+
class ExcessBlobGasCalculator(Protocol):
113+
"""
114+
A protocol to calculate the excess blob gas for a block at a given fork.
115+
"""
116+
117+
def __call__(
118+
self,
119+
*,
120+
parent_excess_blob_gas: int | None = None,
121+
parent_excess_blobs: int | None = None,
122+
parent_blob_gas_used: int | None = None,
123+
parent_blob_count: int | None = None,
124+
) -> int:
125+
"""
126+
Returns the excess blob gas given the parent's excess blob gas and blob gas used.
127+
"""
128+
pass
129+
130+
100131
class BaseForkMeta(ABCMeta):
101132
"""
102133
Metaclass for BaseFork
@@ -218,23 +249,25 @@ def header_blob_gas_used_required(cls, block_number: int = 0, timestamp: int = 0
218249

219250
@classmethod
220251
@abstractmethod
221-
def header_beacon_root_required(cls, block_number: int, timestamp: int) -> bool:
252+
def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
222253
"""
223254
Returns true if the header must contain parent beacon block root
224255
"""
225256
pass
226257

227258
@classmethod
228259
@abstractmethod
229-
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
260+
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
230261
"""
231262
Returns true if the header must contain beacon chain requests
232263
"""
233264
pass
234265

235266
@classmethod
236267
@abstractmethod
237-
def header_target_blobs_per_block_required(cls, block_number: int, timestamp: int) -> bool:
268+
def header_target_blobs_per_block_required(
269+
cls, block_number: int = 0, timestamp: int = 0
270+
) -> bool:
238271
"""
239272
Returns true if the header must contain target blobs per block.
240273
"""
@@ -293,25 +326,61 @@ def transaction_intrinsic_cost_calculator(
293326

294327
@classmethod
295328
@abstractmethod
296-
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
329+
def blob_gas_price_calculator(
330+
cls, block_number: int = 0, timestamp: int = 0
331+
) -> BlobGasPriceCalculator:
332+
"""
333+
Returns a callable that calculates the blob gas price at a given fork.
334+
"""
335+
pass
336+
337+
@classmethod
338+
@abstractmethod
339+
def excess_blob_gas_calculator(
340+
cls, block_number: int = 0, timestamp: int = 0
341+
) -> ExcessBlobGasCalculator:
342+
"""
343+
Returns a callable that calculates the excess blob gas for a block at a given fork.
344+
"""
345+
pass
346+
347+
@classmethod
348+
@abstractmethod
349+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
350+
"""
351+
Returns the minimum base fee per blob gas at a given fork.
352+
"""
353+
pass
354+
355+
@classmethod
356+
@abstractmethod
357+
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
358+
"""
359+
Returns the amount of blob gas used per blob at a given fork.
360+
"""
361+
pass
362+
363+
@classmethod
364+
@abstractmethod
365+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
297366
"""
298-
Returns the amount of blob gas used per blob for a given fork.
367+
Returns the blob base fee update fraction at a given fork.
299368
"""
300369
pass
301370

302371
@classmethod
303372
@abstractmethod
304-
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
373+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
305374
"""
306-
Returns the target blobs per block for a given fork.
375+
Returns the target blobs per block at a given fork.
307376
"""
308377
pass
309378

310379
@classmethod
311380
@abstractmethod
312-
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
381+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
313382
"""
314-
Returns the max blobs per block for a given fork.
383+
Returns the max blobs per block at a given fork.
315384
"""
316385
pass
317386

src/ethereum_test_forks/forks/forks.py

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

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

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

2932

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

247242
@classmethod
248-
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
243+
def blob_gas_price_calculator(
244+
cls, block_number: int = 0, timestamp: int = 0
245+
) -> BlobGasPriceCalculator:
249246
"""
250-
Returns the amount of blob gas used per blob for a given fork.
247+
Returns a callable that calculates the blob gas price at a given fork.
251248
"""
252-
return 0
249+
raise NotImplementedError("Blob gas price calculator is not supported in Frontier")
253250

254251
@classmethod
255-
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
252+
def excess_blob_gas_calculator(
253+
cls, block_number: int = 0, timestamp: int = 0
254+
) -> ExcessBlobGasCalculator:
256255
"""
257-
Returns the target number of blobs per block for a given fork.
256+
Returns a callable that calculates the excess blob gas for a block at a given fork.
258257
"""
259-
return 0
258+
raise NotImplementedError("Excess blob gas calculator is not supported in Frontier")
260259

261260
@classmethod
262-
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
261+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
263262
"""
264-
Returns the max number of blobs per block for a given fork.
263+
Returns the amount of blob gas used per blob at a given fork.
265264
"""
266-
return 0
265+
raise NotImplementedError("Base fee per blob gas is not supported in Frontier")
266+
267+
@classmethod
268+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
269+
"""
270+
Returns the blob base fee update fraction at a given fork.
271+
"""
272+
raise NotImplementedError("Blob base fee update fraction is not supported in Frontier")
273+
274+
@classmethod
275+
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
276+
"""
277+
Returns the amount of blob gas used per blob at a given fork.
278+
"""
279+
raise NotImplementedError("Blob gas per blob is not supported in Frontier")
280+
281+
@classmethod
282+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
283+
"""
284+
Returns the target number of blobs per block at a given fork.
285+
"""
286+
raise NotImplementedError("Target blobs per block is not supported in Frontier")
267287

268288
@classmethod
269-
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
289+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
290+
"""
291+
Returns the max number of blobs per block at a given fork.
292+
"""
293+
raise NotImplementedError("Max blobs per block is not supported in Frontier")
294+
295+
@classmethod
296+
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
270297
"""
271298
At genesis, header must not contain beacon chain requests.
272299
"""
@@ -1011,21 +1038,85 @@ def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0)
10111038
return True
10121039

10131040
@classmethod
1014-
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
1041+
def blob_gas_price_calculator(
1042+
cls, block_number: int = 0, timestamp: int = 0
1043+
) -> BlobGasPriceCalculator:
1044+
"""
1045+
Returns a callable that calculates the blob gas price at Cancun.
1046+
"""
1047+
min_base_fee_per_blob_gas = cls.min_base_fee_per_blob_gas(block_number, timestamp)
1048+
blob_base_fee_update_fraction = cls.blob_base_fee_update_fraction(block_number, timestamp)
1049+
1050+
def fn(*, excess_blob_gas) -> int:
1051+
return fake_exponential(
1052+
min_base_fee_per_blob_gas,
1053+
excess_blob_gas,
1054+
blob_base_fee_update_fraction,
1055+
)
1056+
1057+
return fn
1058+
1059+
@classmethod
1060+
def excess_blob_gas_calculator(
1061+
cls, block_number: int = 0, timestamp: int = 0
1062+
) -> ExcessBlobGasCalculator:
1063+
"""
1064+
Returns a callable that calculates the excess blob gas for a block at Cancun.
1065+
"""
1066+
target_blobs_per_block = cls.target_blobs_per_block(block_number, timestamp)
1067+
blob_gas_per_blob = cls.blob_gas_per_blob(block_number, timestamp)
1068+
target_blob_gas_per_block = target_blobs_per_block * blob_gas_per_blob
1069+
1070+
def fn(
1071+
*,
1072+
parent_excess_blob_gas: int | None = None,
1073+
parent_excess_blobs: int | None = None,
1074+
parent_blob_gas_used: int | None = None,
1075+
parent_blob_count: int | None = None,
1076+
) -> int:
1077+
if parent_excess_blob_gas is None:
1078+
assert parent_excess_blobs is not None, "Parent excess blobs are required"
1079+
parent_excess_blob_gas = parent_excess_blobs * blob_gas_per_blob
1080+
if parent_blob_gas_used is None:
1081+
assert parent_blob_count is not None, "Parent blob count is required"
1082+
parent_blob_gas_used = parent_blob_count * blob_gas_per_blob
1083+
if parent_excess_blob_gas + parent_blob_gas_used < target_blob_gas_per_block:
1084+
return 0
1085+
else:
1086+
return parent_excess_blob_gas + parent_blob_gas_used - target_blob_gas_per_block
1087+
1088+
return fn
1089+
1090+
@classmethod
1091+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
1092+
"""
1093+
Returns the minimum base fee per blob gas for Cancun.
1094+
"""
1095+
return 1
1096+
1097+
@classmethod
1098+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
1099+
"""
1100+
Returns the blob base fee update fraction for Cancun.
1101+
"""
1102+
return 3338477
1103+
1104+
@classmethod
1105+
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
10151106
"""
10161107
Blobs are enabled starting from Cancun.
10171108
"""
10181109
return 2**17
10191110

10201111
@classmethod
1021-
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
1112+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
10221113
"""
10231114
Blobs are enabled starting from Cancun, with a static target of 3 blobs.
10241115
"""
10251116
return 3
10261117

10271118
@classmethod
1028-
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
1119+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
10291120
"""
10301121
Blobs are enabled starting from Cancun, with a static max of 6 blobs.
10311122
"""
@@ -1256,6 +1347,34 @@ def fn(
12561347

12571348
return fn
12581349

1350+
@classmethod
1351+
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
1352+
"""
1353+
Returns the minimum base fee per blob gas for Prague.
1354+
"""
1355+
return 2**25
1356+
1357+
@classmethod
1358+
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
1359+
"""
1360+
Returns the blob base fee update fraction for Prague.
1361+
"""
1362+
return 5007716
1363+
1364+
@classmethod
1365+
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
1366+
"""
1367+
Target blob count of 6 for Prague.
1368+
"""
1369+
return 6
1370+
1371+
@classmethod
1372+
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
1373+
"""
1374+
Max blob count of 9 for Prague.
1375+
"""
1376+
return 9
1377+
12591378
@classmethod
12601379
def pre_allocation_blockchain(cls) -> Mapping:
12611380
"""
@@ -1319,7 +1438,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
13191438
return new_allocation | super(Prague, cls).pre_allocation_blockchain() # type: ignore
13201439

13211440
@classmethod
1322-
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
1441+
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
13231442
"""
13241443
Prague requires that the execution layer header contains the beacon
13251444
chain requests hash.

0 commit comments

Comments
 (0)