19
19
from .fork_types import Address , Authorization , VersionedHash
20
20
21
21
TX_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 )
24
24
TX_CREATE_COST = Uint (32000 )
25
25
TX_ACCESS_LIST_ADDRESS_COST = Uint (2400 )
26
26
TX_ACCESS_LIST_STORAGE_KEY_COST = Uint (1900 )
27
27
28
+ MAX_INIT_CODE_COUNT = 256
29
+
28
30
29
31
@slotted_freezable
30
32
@dataclass
@@ -130,12 +132,35 @@ class SetCodeTransaction:
130
132
s : U256
131
133
132
134
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
+
133
157
Transaction = Union [
134
158
LegacyTransaction ,
135
159
AccessListTransaction ,
136
160
FeeMarketTransaction ,
137
161
BlobTransaction ,
138
162
SetCodeTransaction ,
163
+ EofInitCodeTransaction ,
139
164
]
140
165
141
166
@@ -153,6 +178,8 @@ def encode_transaction(tx: Transaction) -> Union[LegacyTransaction, Bytes]:
153
178
return b"\x03 " + rlp .encode (tx )
154
179
elif isinstance (tx , SetCodeTransaction ):
155
180
return b"\x04 " + rlp .encode (tx )
181
+ elif isinstance (tx , EofInitCodeTransaction ):
182
+ return b"\x05 " + rlp .encode (tx )
156
183
else :
157
184
raise Exception (f"Unable to encode transaction of type { type (tx )} " )
158
185
@@ -170,6 +197,8 @@ def decode_transaction(tx: Union[LegacyTransaction, Bytes]) -> Transaction:
170
197
return rlp .decode_to (BlobTransaction , tx [1 :])
171
198
elif tx [0 ] == 4 :
172
199
return rlp .decode_to (SetCodeTransaction , tx [1 :])
200
+ elif tx [0 ] == 5 :
201
+ return rlp .decode_to (EofInitCodeTransaction , tx [1 :])
173
202
else :
174
203
raise TransactionTypeError (tx [0 ])
175
204
else :
@@ -211,6 +240,12 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]:
211
240
"""
212
241
from .vm .interpreter import MAX_CODE_SIZE
213
242
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
+
214
249
intrinsic_gas , calldata_floor_gas_cost = calculate_intrinsic_cost (tx )
215
250
if max (intrinsic_gas , calldata_floor_gas_cost ) > tx .gas :
216
251
raise InvalidTransaction ("Insufficient gas" )
@@ -222,6 +257,28 @@ def validate_transaction(tx: Transaction) -> Tuple[Uint, Uint]:
222
257
return intrinsic_gas , calldata_floor_gas_cost
223
258
224
259
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
+
225
282
def calculate_intrinsic_cost (tx : Transaction ) -> Tuple [Uint , Uint ]:
226
283
"""
227
284
Calculates the gas that is charged before execution is started.
@@ -250,19 +307,27 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
250
307
"""
251
308
from .vm .eoa_delegation import PER_EMPTY_ACCOUNT_COST
252
309
from .vm .gas import init_code_cost
310
+ from .vm .interpreter import MAX_CODE_SIZE
253
311
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 )
258
326
259
- tokens_in_calldata = Uint (zero_bytes + (len (tx .data ) - zero_bytes ) * 4 )
260
327
# 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
264
329
265
- data_cost = tokens_in_calldata * STANDARD_CALLDATA_TOKEN_COST
330
+ data_cost = tokens_in_tx * STANDARD_TOKEN_COST
266
331
267
332
if tx .to == Bytes0 (b"" ):
268
333
create_cost = TX_CREATE_COST + init_code_cost (ulen (tx .data ))
@@ -277,6 +342,7 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
277
342
FeeMarketTransaction ,
278
343
BlobTransaction ,
279
344
SetCodeTransaction ,
345
+ EofInitCodeTransaction ,
280
346
),
281
347
):
282
348
for _address , keys in tx .access_list :
@@ -295,7 +361,7 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]:
295
361
+ access_list_cost
296
362
+ auth_cost
297
363
),
298
- calldata_floor_gas_cost ,
364
+ floor_gas_cost ,
299
365
)
300
366
301
367
@@ -365,6 +431,10 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
365
431
public_key = secp256k1_recover (
366
432
r , s , tx .y_parity , signing_hash_7702 (tx )
367
433
)
434
+ elif isinstance (tx , EofInitCodeTransaction ):
435
+ public_key = secp256k1_recover (
436
+ r , s , tx .y_parity , signing_hash_7873 (tx )
437
+ )
368
438
369
439
return Address (keccak256 (public_key )[12 :32 ])
370
440
@@ -560,6 +630,39 @@ def signing_hash_7702(tx: SetCodeTransaction) -> Hash32:
560
630
)
561
631
562
632
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
+
563
666
def get_transaction_hash (tx : Union [Bytes , LegacyTransaction ]) -> Hash32 :
564
667
"""
565
668
Parameters
0 commit comments