44""" # noqa: E501
55
66from enum import Enum
7- from typing import List , Sequence
7+ from typing import Callable , List , Sequence
88
99import pytest
1010
@@ -75,11 +75,24 @@ def access_list() -> List[AccessList] | None:
7575
7676
7777@pytest .fixture
78- def authorization_list () -> List [AuthorizationTuple ] | None :
78+ def authorization_list (
79+ request : pytest .FixtureRequest ,
80+ pre : Alloc ,
81+ ) -> List [AuthorizationTuple ] | None :
7982 """
8083 Authorization list for the transaction.
84+
85+ This fixture needs to be parametrized indirectly in order to generate the authorizations with
86+ valid signers using `pre` in this function, and the parametrized value should be a list of
87+ addresses.
8188 """
82- return None
89+ if not hasattr (request , "param" ):
90+ return None
91+ if request .param is None :
92+ return None
93+ return [
94+ AuthorizationTuple (signer = pre .fund_eoa (), address = address ) for address in request .param
95+ ]
8396
8497
8598@pytest .fixture
@@ -98,6 +111,44 @@ def contract_creating_tx(to: Address | None) -> bool:
98111 return to is None
99112
100113
114+ def floor_cost_find (
115+ floor_data_gas_cost_calculator : Callable [[int ], int ],
116+ intrinsic_gas_cost_calculator : Callable [[int ], int ],
117+ ) -> int :
118+ """
119+ Find the minimum amount of tokens that will trigger the floor gas cost, by using a binary
120+ search and the intrinsic gas cost and floor data calculators.
121+ """
122+ # Start with 1000 tokens and if the intrinsic gas cost is greater than the floor gas cost,
123+ # multiply the number of tokens by 2 until it's not.
124+ tokens = 1000
125+ while floor_data_gas_cost_calculator (tokens ) < intrinsic_gas_cost_calculator (tokens ):
126+ tokens *= 2
127+
128+ # Binary search to find the minimum number of tokens that will trigger the floor gas cost.
129+ left = 0
130+ right = tokens
131+ while left < right :
132+ tokens = (left + right ) // 2
133+ if floor_data_gas_cost_calculator (tokens ) < intrinsic_gas_cost_calculator (tokens ):
134+ left = tokens + 1
135+ else :
136+ right = tokens
137+ tokens = left
138+
139+ if floor_data_gas_cost_calculator (tokens ) > intrinsic_gas_cost_calculator (tokens ):
140+ tokens -= 1
141+
142+ # Verify that increasing the tokens by one would always trigger the floor gas cost.
143+ assert (
144+ floor_data_gas_cost_calculator (tokens ) <= intrinsic_gas_cost_calculator (tokens )
145+ ) and floor_data_gas_cost_calculator (tokens + 1 ) > intrinsic_gas_cost_calculator (
146+ tokens + 1
147+ ), "invalid case"
148+
149+ return tokens
150+
151+
101152@pytest .fixture
102153def tx_data (
103154 fork : Fork ,
@@ -118,13 +169,37 @@ def tx_data(
118169 - FLOOR_GAS_COST_GREATER_THAN_INTRINSIC_GAS: The floor gas cost is greater than the intrinsic
119170 gas cost, which means that the size of the tokens in the data are enough to trigger the
120171 floor gas cost.
172+
173+ E.g. Given a transaction with a single access list and a single storage key, its intrinsic gas
174+ cost (as of Prague fork) can be calculated as:
175+ - 21,000 gas for the transaction
176+ - 2,400 gas for the access list
177+ - 1,900 gas for the storage key
178+ - 16 gas for each non-zero byte in the data
179+ - 4 gas for each zero byte in the data
180+
181+ Its floor data gas cost can be calculated as:
182+ - 21,000 gas for the transaction
183+ - 40 gas for each non-zero byte in the data
184+ - 10 gas for each zero byte in the data
185+
186+ Notice that the data included in the transaction affects both the intrinsic gas cost and the
187+ floor data cost, but at different rates.
188+
189+ The purpose of this function is to find the exact amount of data where the floor data gas
190+ cost starts exceeding the intrinsic gas cost.
191+
192+ After a binary search we find that adding 717 tokens of data (179 non-zero bytes +
193+ 1 zero byte) triggers the floor gas cost.
194+
195+ Therefore, this function will return a Bytes object with 179 non-zero bytes and 1 zero byte
196+ for `FLOOR_GAS_COST_GREATER_THAN_INTRINSIC_GAS` and a Bytes object with 179 non-zero bytes
197+ and no zero bytes for `FLOOR_GAS_COST_LESS_THAN_OR_EQUAL_TO_INTRINSIC_GAS`
121198 """
122199
123200 def tokens_to_data (tokens : int ) -> Bytes :
124201 return Bytes (b"\x01 " * (tokens // 4 ) + b"\x00 " * (tokens % 4 ))
125202
126- # Start with zero data and check the difference in the gas calculator between the
127- # intrinsic gas cost and the floor gas cost.
128203 fork_intrinsic_cost_calculator = fork .transaction_intrinsic_cost_calculator ()
129204
130205 def transaction_intrinsic_cost_calculator (tokens : int ) -> int :
@@ -141,47 +216,20 @@ def transaction_intrinsic_cost_calculator(tokens: int) -> int:
141216 def transaction_data_floor_cost_calculator (tokens : int ) -> int :
142217 return fork_data_floor_cost_calculator (data = tokens_to_data (tokens ))
143218
219+ # Start with zero data and check the difference in the gas calculator between the
220+ # intrinsic gas cost and the floor gas cost.
144221 if transaction_data_floor_cost_calculator (0 ) >= transaction_intrinsic_cost_calculator (0 ):
145222 # Special case which is a transaction with no extra intrinsic gas costs other than the
146- # data cost.
223+ # data cost, any data will trigger the floor gas cost .
147224 if data_test_type == DataTestType .FLOOR_GAS_COST_LESS_THAN_OR_EQUAL_TO_INTRINSIC_GAS :
148225 return Bytes (b"" )
149226 else :
150227 return Bytes (b"\0 " )
151228
152- # Start with 1000 tokens and if the intrinsic gas cost is greater than the floor gas cost,
153- # multiply the number of tokens by 2 until it's not.
154- tokens = 1000
155- while transaction_data_floor_cost_calculator (tokens ) < transaction_intrinsic_cost_calculator (
156- tokens
157- ):
158- tokens *= 2
159-
160- # Binary search to find the minimum number of tokens that will trigger the floor gas cost.
161- left = 0
162- right = tokens
163- while left < right :
164- tokens = (left + right ) // 2
165- if transaction_data_floor_cost_calculator (tokens ) < transaction_intrinsic_cost_calculator (
166- tokens
167- ):
168- left = tokens + 1
169- else :
170- right = tokens
171- tokens = left
172- if transaction_data_floor_cost_calculator (tokens ) > transaction_intrinsic_cost_calculator (
173- tokens
174- ):
175- tokens -= 1
176- # Verify that increasing the tokens by one would always trigger the floor gas cost.
177- assert (
178- transaction_data_floor_cost_calculator (tokens )
179- <= transaction_intrinsic_cost_calculator (tokens )
180- ) and transaction_data_floor_cost_calculator (
181- tokens + 1
182- ) > transaction_intrinsic_cost_calculator (
183- tokens + 1
184- ), "invalid case"
229+ tokens = floor_cost_find (
230+ floor_data_gas_cost_calculator = transaction_data_floor_cost_calculator ,
231+ intrinsic_gas_cost_calculator = transaction_intrinsic_cost_calculator ,
232+ )
185233
186234 if data_test_type == DataTestType .FLOOR_GAS_COST_GREATER_THAN_INTRINSIC_GAS :
187235 return tokens_to_data (tokens + 1 )
@@ -191,7 +239,15 @@ def transaction_data_floor_cost_calculator(tokens: int) -> int:
191239@pytest .fixture
192240def tx_gas_delta () -> int :
193241 """
194- Gas delta for the transaction, used to generate an invalid transaction.
242+ Gas delta to modify the gas amount included with the transaction.
243+
244+ If negative, the transaction will be invalid because the intrinsic gas cost is greater than the
245+ gas limit.
246+
247+ This value operates regardless of whether the floor data gas cost is reached or not.
248+
249+ If the value is greater than zero, the transaction will also be valid and the test will check
250+ that transaction processing does not consume more gas than it should.
195251 """
196252 return 0
197253
@@ -207,6 +263,11 @@ def tx_gas(
207263) -> int :
208264 """
209265 Gas limit for the transaction.
266+
267+ The calculated value takes into account the normal intrinsic gas cost and the floor data gas
268+ cost.
269+
270+ The gas delta is added to the intrinsic gas cost to generate different test scenarios.
210271 """
211272 intrinsic_gas_cost_calculator = fork .transaction_intrinsic_cost_calculator ()
212273 return (
@@ -223,7 +284,7 @@ def tx_gas(
223284@pytest .fixture
224285def tx_error (tx_gas_delta : int ) -> TransactionException | None :
225286 """
226- Transaction error.
287+ Transaction error, only expected if the gas delta is negative .
227288 """
228289 return TransactionException .INTRINSIC_GAS_TOO_LOW if tx_gas_delta < 0 else None
229290
@@ -263,6 +324,9 @@ def tx(
263324 pytest .mark .parametrize (
264325 "tx_gas_delta" ,
265326 [
327+ # Test the case where the included gas is greater than the intrinsic gas to verify that
328+ # the data floor does not consume more gas than it should.
329+ pytest .param (1 , id = "extra_gas" ),
266330 pytest .param (0 , id = "exact_gas" ),
267331 pytest .param (- 1 , id = "insufficient_gas" ),
268332 ],
@@ -495,14 +559,15 @@ def test_transaction_validity_type_3(
495559 "authorization_list" ,
496560 [
497561 pytest .param (
498- [AuthorizationTuple ( address = Address (1 ) )],
562+ [Address (1 )],
499563 id = "single_authorization" ,
500564 ),
501565 pytest .param (
502- [AuthorizationTuple ( address = Address (1 )) for _ in range (10 )],
566+ [Address (i + 1 ) for i in range (10 )],
503567 id = "multiple_authorizations" ,
504568 ),
505569 ],
570+ indirect = True ,
506571)
507572@pytest .mark .parametrize (
508573 "ty" ,
0 commit comments