1919from .fork_types import Address , Authorization , VersionedHash
2020
2121TX_BASE_COST = Uint (21000 )
22- FLOOR_CALLDATA_COST = Uint (10 )
23- STANDARD_CALLDATA_TOKEN_COST = Uint (4 )
22+ TOTAL_COST_FLOOR_PER_TOKEN = Uint (10 )
23+ STANDARD_TOKEN_COST = Uint (4 )
2424TX_CREATE_COST = Uint (32000 )
2525TX_ACCESS_LIST_ADDRESS_COST = Uint (2400 )
2626TX_ACCESS_LIST_STORAGE_KEY_COST = Uint (1900 )
2727
28+ MAX_INIT_CODE_COUNT = 256
29+
2830
2931@slotted_freezable
3032@dataclass
@@ -130,12 +132,35 @@ class SetCodeTransaction:
130132 s : U256
131133
132134
135+ @slotted_freezable
136+ @dataclass
137+ class EofInitCodeTransaction :
138+ """
139+ The transaction type added in EIP-7873.
140+ """
141+
142+ chain_id : U64
143+ nonce : U256
144+ max_priority_fee_per_gas : Uint
145+ max_fee_per_gas : Uint
146+ gas : Uint
147+ to : Union [Bytes0 , Address ]
148+ value : U256
149+ data : Bytes
150+ access_list : Tuple [Tuple [Address , Tuple [Bytes32 , ...]], ...]
151+ init_codes : Tuple [Bytes , ...]
152+ y_parity : U256
153+ r : U256
154+ s : U256
155+
156+
133157Transaction = Union [
134158 LegacyTransaction ,
135159 AccessListTransaction ,
136160 FeeMarketTransaction ,
137161 BlobTransaction ,
138162 SetCodeTransaction ,
163+ EofInitCodeTransaction ,
139164]
140165
141166
@@ -153,6 +178,8 @@ def encode_transaction(tx: Transaction) -> Union[LegacyTransaction, Bytes]:
153178 return b"\x03 " + rlp .encode (tx )
154179 elif isinstance (tx , SetCodeTransaction ):
155180 return b"\x04 " + rlp .encode (tx )
181+ elif isinstance (tx , EofInitCodeTransaction ):
182+ return b"\x05 " + rlp .encode (tx )
156183 else :
157184 raise Exception (f"Unable to encode transaction of type { type (tx )} " )
158185
@@ -170,6 +197,8 @@ def decode_transaction(tx: Union[LegacyTransaction, Bytes]) -> Transaction:
170197 return rlp .decode_to (BlobTransaction , tx [1 :])
171198 elif tx [0 ] == 4 :
172199 return rlp .decode_to (SetCodeTransaction , tx [1 :])
200+ elif tx [0 ] == 5 :
201+ return rlp .decode_to (EofInitCodeTransaction , tx [1 :])
173202 else :
174203 raise TransactionTypeError (tx [0 ])
175204 else :
@@ -211,6 +240,12 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]:
211240 """
212241 from .vm .interpreter import MAX_CODE_SIZE
213242
243+ if isinstance (tx , EofInitCodeTransaction ):
244+ if len (tx .init_codes ) == 0 :
245+ raise InvalidTransaction ("Type 5 tx with no init codes" )
246+ if len (tx .init_codes ) > MAX_INIT_CODE_COUNT :
247+ raise InvalidTransaction ("Type 5 tx with too many init codes" )
248+
214249 intrinsic_gas , calldata_floor_gas_cost = calculate_intrinsic_cost (tx )
215250 if max (intrinsic_gas , calldata_floor_gas_cost ) > tx .gas :
216251 raise InvalidTransaction ("Insufficient gas" )
@@ -222,6 +257,28 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]:
222257 return intrinsic_gas , calldata_floor_gas_cost
223258
224259
260+ def calculate_tokens_in_data (data : Bytes ) -> Uint :
261+ """
262+ Calculate the tokens in a certain data.
263+
264+ Parameters
265+ ----------
266+ data :
267+ Data in which tokens are to be calculated.
268+
269+ Returns
270+ -------
271+ tokens_in_data :
272+ Tokens in the data.
273+ """
274+ zero_bytes = 0
275+ for byte in data :
276+ if byte == 0 :
277+ zero_bytes += 1
278+
279+ return Uint (zero_bytes + (len (data ) - zero_bytes ) * 4 )
280+
281+
225282def calculate_intrinsic_cost (tx : Transaction ) -> Tuple [Uint , Uint ]:
226283 """
227284 Calculates the gas that is charged before execution is started.
@@ -250,19 +307,27 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
250307 """
251308 from .vm .eoa_delegation import PER_EMPTY_ACCOUNT_COST
252309 from .vm .gas import init_code_cost
310+ from .vm .interpreter import MAX_CODE_SIZE
253311
254- zero_bytes = 0
255- for byte in tx .data :
256- if byte == 0 :
257- zero_bytes += 1
312+ tokens_in_tx = Uint (0 )
313+
314+ tokens_in_tx += calculate_tokens_in_data (tx .data )
315+
316+ if isinstance (tx , EofInitCodeTransaction ):
317+ for init_code in tx .init_codes :
318+ if len (init_code ) == 0 :
319+ raise InvalidTransaction (
320+ "Type 5 tx with zero-length init code"
321+ )
322+ if len (init_code ) > 2 * MAX_CODE_SIZE :
323+ raise InvalidTransaction ("Type 5 tx with too large init code" )
324+
325+ tokens_in_tx += calculate_tokens_in_data (init_code )
258326
259- tokens_in_calldata = Uint (zero_bytes + (len (tx .data ) - zero_bytes ) * 4 )
260327 # EIP-7623 floor price (note: no EVM costs)
261- calldata_floor_gas_cost = (
262- tokens_in_calldata * FLOOR_CALLDATA_COST + TX_BASE_COST
263- )
328+ floor_gas_cost = tokens_in_tx * TOTAL_COST_FLOOR_PER_TOKEN + TX_BASE_COST
264329
265- data_cost = tokens_in_calldata * STANDARD_CALLDATA_TOKEN_COST
330+ data_cost = tokens_in_tx * STANDARD_TOKEN_COST
266331
267332 if tx .to == Bytes0 (b"" ):
268333 create_cost = TX_CREATE_COST + init_code_cost (ulen (tx .data ))
@@ -277,6 +342,7 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
277342 FeeMarketTransaction ,
278343 BlobTransaction ,
279344 SetCodeTransaction ,
345+ EofInitCodeTransaction ,
280346 ),
281347 ):
282348 for _address , keys in tx .access_list :
@@ -295,7 +361,7 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
295361 + access_list_cost
296362 + auth_cost
297363 ),
298- calldata_floor_gas_cost ,
364+ floor_gas_cost ,
299365 )
300366
301367
@@ -365,6 +431,10 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
365431 public_key = secp256k1_recover (
366432 r , s , tx .y_parity , signing_hash_7702 (tx )
367433 )
434+ elif isinstance (tx , EofInitCodeTransaction ):
435+ public_key = secp256k1_recover (
436+ r , s , tx .y_parity , signing_hash_7873 (tx )
437+ )
368438
369439 return Address (keccak256 (public_key )[12 :32 ])
370440
@@ -560,6 +630,39 @@ def signing_hash_7702(tx: SetCodeTransaction) -> Hash32:
560630 )
561631
562632
633+ def signing_hash_7873 (tx : EofInitCodeTransaction ) -> Hash32 :
634+ """
635+ Compute the hash of a transaction used in a EIP-7873 signature.
636+
637+ Parameters
638+ ----------
639+ tx :
640+ Transaction of interest.
641+
642+ Returns
643+ -------
644+ hash : `ethereum.crypto.hash.Hash32`
645+ Hash of the transaction.
646+ """
647+ return keccak256 (
648+ b"\x05 "
649+ + rlp .encode (
650+ (
651+ tx .chain_id ,
652+ tx .nonce ,
653+ tx .max_priority_fee_per_gas ,
654+ tx .max_fee_per_gas ,
655+ tx .gas ,
656+ tx .to ,
657+ tx .value ,
658+ tx .data ,
659+ tx .access_list ,
660+ tx .init_codes ,
661+ )
662+ )
663+ )
664+
665+
563666def get_transaction_hash (tx : Union [Bytes , LegacyTransaction ]) -> Hash32 :
564667 """
565668 Parameters
0 commit comments