77import json
88import os
99from contextlib import AbstractContextManager
10- from typing import Any , Final , TextIO , Tuple , Type , TypeVar
10+ from dataclasses import astuple , dataclass
11+ from typing import Any , Final , TextIO , Type , TypeVar
1112
1213from ethereum_rlp import rlp
1314from ethereum_types .numeric import U64 , U256 , Uint
4041from .t8n_types import Alloc , Result , Txs
4142
4243T = TypeVar ("T" )
44+ ForkCriteriaArgument = ByBlockNumber | ByTimestamp | Unscheduled | None
4345
4446
4547def t8n_arguments (subparsers : argparse ._SubParsersAction ) -> None :
@@ -97,12 +99,95 @@ def t8n_arguments(subparsers: argparse._SubParsersAction) -> None:
9799 t8n_parser .add_argument ("--state-test" , action = "store_true" )
98100
99101
102+ @dataclass (frozen = True )
103+ class _ForkOverrides :
104+ """Store temporary hardfork override values."""
105+
106+ fork_criteria : ForkCriteriaArgument = None
107+ blob_target_gas_per_block : U64 | None = None
108+ gas_per_blob : U64 | None = None
109+ blob_min_gasprice : Uint | None = None
110+ blob_base_fee_update_fraction : Uint | None = None
111+ max_blob_gas_per_block : U64 | None = None
112+ blob_schedule_target : U64 | None = None
113+ blob_schedule_max : U64 | None = None
114+
115+ def is_empty (self ) -> bool :
116+ """Return true when all override values are unset."""
117+ return all (value is None for value in astuple (self ))
118+
119+ @staticmethod
120+ def _matches_field (override : object | None , on : object , name : str ) -> bool :
121+ if override is None :
122+ return True
123+
124+ try :
125+ default = getattr (on , name )
126+ except AttributeError :
127+ return False
128+
129+ return override == default
130+
131+ def matches_template (
132+ self ,
133+ template : Hardfork ,
134+ ) -> bool :
135+ """Return true when the requested overrides match the template."""
136+ if self .is_empty ():
137+ return True
138+
139+ if (
140+ self .fork_criteria is not None
141+ and self .fork_criteria != template .criteria
142+ ):
143+ return False
144+
145+ fork_mod = template .module ("fork" )
146+ gas_costs = template .module ("vm.gas" ).GasCosts
147+
148+ checks = (
149+ (
150+ self .max_blob_gas_per_block ,
151+ fork_mod ,
152+ "MAX_BLOB_GAS_PER_BLOCK" ,
153+ ),
154+ (
155+ self .blob_target_gas_per_block ,
156+ gas_costs ,
157+ "BLOB_TARGET_GAS_PER_BLOCK" ,
158+ ),
159+ (self .gas_per_blob , gas_costs , "PER_BLOB" ),
160+ (
161+ self .blob_min_gasprice ,
162+ gas_costs ,
163+ "BLOB_MIN_GASPRICE" ,
164+ ),
165+ (
166+ self .blob_base_fee_update_fraction ,
167+ gas_costs ,
168+ "BLOB_BASE_FEE_UPDATE_FRACTION" ,
169+ ),
170+ (
171+ self .blob_schedule_target ,
172+ gas_costs ,
173+ "BLOB_SCHEDULE_TARGET" ,
174+ ),
175+ (
176+ self .blob_schedule_max ,
177+ gas_costs ,
178+ "BLOB_SCHEDULE_MAX" ,
179+ ),
180+ )
181+
182+ return all (self ._matches_field (* x ) for x in checks )
183+
184+
100185class ForkCache (AbstractContextManager ):
101186 """
102187 Stores references to temporary hardforks and cleans them up when exited.
103188 """
104189
105- _cache : Final [dict [Tuple [ object , ... ], TemporaryHardfork ]]
190+ _cache : Final [dict [tuple [ str , _ForkOverrides ], TemporaryHardfork ]]
106191
107192 def __init__ (self ) -> None :
108193 self ._cache = {}
@@ -129,35 +214,37 @@ def get(
129214 Search the cache for a matching hardfork, or create one if it doesn't
130215 exist.
131216 """
132- cache_key = (
133- template .short_name ,
134- fork_criteria ,
135- blob_target_gas_per_block ,
136- gas_per_blob ,
137- blob_min_gasprice ,
138- blob_base_fee_update_fraction ,
139- max_blob_gas_per_block ,
140- blob_schedule_target ,
141- blob_schedule_max ,
217+ overrides = _ForkOverrides (
218+ fork_criteria = fork_criteria ,
219+ blob_target_gas_per_block = blob_target_gas_per_block ,
220+ gas_per_blob = gas_per_blob ,
221+ blob_min_gasprice = blob_min_gasprice ,
222+ blob_base_fee_update_fraction = blob_base_fee_update_fraction ,
223+ max_blob_gas_per_block = max_blob_gas_per_block ,
224+ blob_schedule_target = blob_schedule_target ,
225+ blob_schedule_max = blob_schedule_max ,
142226 )
143- if all ( x is None for x in cache_key [ 1 :] ):
227+ if overrides . matches_template ( template ):
144228 return template
145229
230+ cache_key = (template .short_name , overrides )
146231 try :
147232 return self ._cache [cache_key ]
148233 except KeyError :
149234 pass
150235
151236 clone = Hardfork .clone (
152237 template = template ,
153- fork_criteria = fork_criteria ,
154- blob_target_gas_per_block = blob_target_gas_per_block ,
155- gas_per_blob = gas_per_blob ,
156- blob_min_gasprice = blob_min_gasprice ,
157- blob_base_fee_update_fraction = blob_base_fee_update_fraction ,
158- max_blob_gas_per_block = max_blob_gas_per_block ,
159- blob_schedule_target = blob_schedule_target ,
160- blob_schedule_max = blob_schedule_max ,
238+ fork_criteria = overrides .fork_criteria ,
239+ blob_target_gas_per_block = overrides .blob_target_gas_per_block ,
240+ gas_per_blob = overrides .gas_per_blob ,
241+ blob_min_gasprice = overrides .blob_min_gasprice ,
242+ blob_base_fee_update_fraction = (
243+ overrides .blob_base_fee_update_fraction
244+ ),
245+ max_blob_gas_per_block = overrides .max_blob_gas_per_block ,
246+ blob_schedule_target = overrides .blob_schedule_target ,
247+ blob_schedule_max = overrides .blob_schedule_max ,
161248 )
162249 self ._cache [cache_key ] = clone
163250 return clone
0 commit comments