88
99import pytest
1010
11- from ethereum_test_base_types import AccessList , Address , HexNumber , ZeroPaddedHexNumber
11+ from ethereum_test_base_types import Address , HexNumber , ZeroPaddedHexNumber
1212from ethereum_test_fixtures .blockchain import (
1313 FixtureBlockBase ,
1414 FixtureHeader ,
2323 Transaction ,
2424)
2525from ethereum_test_tools import Opcodes as Op
26- from ethereum_test_types import EOA , AuthorizationTuple , Environment , add_kzg_version
26+ from ethereum_test_types import EOA , Environment
2727
2828from .spec import Spec , ref_spec_7934
2929
@@ -103,45 +103,13 @@ def get_block_rlp_size(transactions: List[Transaction], gas_used: int) -> int:
103103 return len (test_block .with_rlp (txs = transactions ).rlp )
104104
105105
106- def create_typed_transaction (tx_type : int ) -> Transaction :
107- """Create a transaction for the given transaction type."""
108- if tx_type == 0 :
109- return Transaction (type = tx_type , gas_price = 10 ** 9 )
110- else :
111- access_list = [
112- AccessList (address = 0x1234 , storage_keys = [0 , 1 ]),
113- AccessList (address = 0x5678 , storage_keys = [2 , 3 ]),
114- ]
115- tx = Transaction (type = tx_type , access_list = access_list )
116- if tx_type == 1 :
117- tx .gas_price = HexNumber (10 ** 9 )
118- if tx_type >= 2 :
119- tx .max_fee_per_gas = HexNumber (10 ** 9 )
120- tx .max_priority_fee_per_gas = HexNumber (10 ** 9 )
121- if tx_type == 3 :
122- tx .max_fee_per_blob_gas = HexNumber (10 ** 9 )
123- tx .blob_versioned_hashes = add_kzg_version (
124- [1 ],
125- Spec .BLOB_COMMITMENT_VERSION_KZG ,
126- )
127- elif tx_type == 4 :
128- tx .authorization_list = [
129- AuthorizationTuple (
130- address = Address (0x1234 ), nonce = 0 , signer = EOA ("0x" + "11" * 20 , key = 123 )
131- )
132- ]
133- elif tx_type >= 5 :
134- raise NotImplementedError (f"Update test to support new transaction type { tx_type } " )
135- return tx
136-
137-
138106def exact_size_transactions (
139107 sender : EOA ,
140108 block_size_limit : int ,
141109 fork : Fork ,
142110 pre : Optional [Alloc ],
143- tx_type : Optional [int ] = None ,
144111 emit_logs : bool = False ,
112+ specific_transaction_to_include : Optional [Transaction ] = None ,
145113) -> Tuple [List [Transaction ], int ]:
146114 """
147115 Generate transactions that fill a block to exactly the RLP size limit.
@@ -153,9 +121,9 @@ def exact_size_transactions(
153121 sender: The sender account
154122 block_size_limit: The target block RLP size limit
155123 fork: The fork to generate transactions for
156- tx_type: Optional transaction type to include
157- emit_logs: If True, transactions will call a contract that emits logs
158124 pre: Required if emit_logs is True, used to deploy the log contract
125+ emit_logs: If True, transactions will call a contract that emits logs
126+ specific_transaction_to_include: If provided, this transaction will be included
159127
160128 """
161129 log_contract = None
@@ -183,9 +151,19 @@ def exact_size_transactions(
183151 log_contract_code += Op .LOG4
184152 log_contract = pre .deploy_contract (log_contract_code )
185153
186- stubbed_transactions , gas_used = _exact_size_transactions_calculation (
187- block_size_limit , fork , tx_type , emit_logs_contract = log_contract
188- )
154+ if not specific_transaction_to_include :
155+ # use cached version when possible for performance
156+ stubbed_transactions , gas_used = _exact_size_transactions_cached (
157+ block_size_limit , fork , emit_logs_contract = log_contract
158+ )
159+ else :
160+ # Direct calculation, no cache, since `Transaction` is not hashable
161+ stubbed_transactions , gas_used = _exact_size_transactions_impl (
162+ block_size_limit ,
163+ fork ,
164+ specific_transaction_to_include = specific_transaction_to_include ,
165+ )
166+
189167 test_transactions = []
190168 for tx in stubbed_transactions :
191169 # Create a new transaction with the correct sender, preserving all other fields
@@ -199,16 +177,28 @@ def exact_size_transactions(
199177
200178
201179@lru_cache (maxsize = 128 )
202- def _exact_size_transactions_calculation (
180+ def _exact_size_transactions_cached (
203181 block_size_limit : int ,
204182 fork : Fork ,
205- tx_type : Optional [int ] = None ,
206183 emit_logs_contract : Optional [Address ] = None ,
207184) -> Tuple [List [Transaction ], int ]:
208185 """
209186 Generate transactions that fill a block to exactly the RLP size limit. Abstracted
210187 with hashable arguments for caching block calculations.
211188 """
189+ return _exact_size_transactions_impl (block_size_limit , fork , None , emit_logs_contract )
190+
191+
192+ def _exact_size_transactions_impl (
193+ block_size_limit : int ,
194+ fork : Fork ,
195+ specific_transaction_to_include : Optional [Transaction ] = None ,
196+ emit_logs_contract : Optional [Address ] = None ,
197+ ) -> Tuple [List [Transaction ], int ]:
198+ """
199+ Calculate the exact size of transactions to be included. Shared by both cached and
200+ non-cached paths.
201+ """
212202 transactions = []
213203 sender = EOA ("0x" + "00" * 20 , key = 123 )
214204 nonce = 0
@@ -223,7 +213,9 @@ def _exact_size_transactions_calculation(
223213 # block with 16 transactions + large calldata remains safely below the limit
224214 # add 15 generic transactions to fill the block and one typed transaction
225215 # if tx_type is specified, otherwise just add 16 generic transactions
226- not_all_generic_txs = any (kwarg is not None for kwarg in {tx_type , emit_logs_contract })
216+ not_all_generic_txs = any (
217+ kwarg is not None for kwarg in [specific_transaction_to_include , emit_logs_contract ]
218+ )
227219
228220 generic_tx_num = 15 if not_all_generic_txs else 16
229221 for _ in range (generic_tx_num ):
@@ -239,24 +231,23 @@ def _exact_size_transactions_calculation(
239231 total_gas_used += gas_limit_large
240232 nonce += 1
241233
242- # append a typed transaction to fill the block if tx_type is specified
234+ # append a typed transaction to fill the block
243235 if not_all_generic_txs :
244- if tx_type is not None :
245- last_tx = create_typed_transaction (tx_type )
246- last_tx .sender = sender
247- last_tx .nonce = HexNumber (nonce )
248- last_tx .data = Bytes (b"\x00 " * 200_000 )
249- last_tx .gas_limit = HexNumber (
236+ if specific_transaction_to_include is not None :
237+ tx_dict = specific_transaction_to_include .model_dump (exclude_unset = True )
238+ data = Bytes (b"\x00 " * 200_000 )
239+ gas_limit = HexNumber (
250240 calculator (
251- calldata = last_tx .data ,
252- access_list = last_tx .access_list if hasattr (last_tx , "access_list" ) else None ,
253- authorization_list_or_count = (
254- last_tx .authorization_list
255- if hasattr (last_tx , "authorization_list" )
256- else None
257- ),
241+ calldata = data ,
242+ access_list = specific_transaction_to_include .access_list ,
243+ authorization_list_or_count = len (tx_dict .get ("authorization_list" , [])),
258244 )
259245 )
246+ tx_dict ["sender" ] = sender
247+ tx_dict ["nonce" ] = nonce
248+ tx_dict ["data" ] = data
249+ tx_dict ["gas_limit" ] = gas_limit
250+ last_tx = Transaction (** tx_dict )
260251 elif emit_logs_contract is not None :
261252 last_tx = Transaction (
262253 sender = sender ,
@@ -266,6 +257,10 @@ def _exact_size_transactions_calculation(
266257 gas_limit = calculator (calldata = b"" ),
267258 to = emit_logs_contract ,
268259 )
260+ else :
261+ raise ValueError (
262+ "Either specific_transaction_to_include or emit_logs_contract must be provided."
263+ )
269264
270265 transactions .append (last_tx )
271266 nonce += 1
@@ -400,7 +395,6 @@ def test_block_at_rlp_size_limit_boundary(
400395 block_size_limit ,
401396 fork ,
402397 pre ,
403- tx_type = None ,
404398 )
405399 block_rlp_size = get_block_rlp_size (transactions , gas_used = gas_used )
406400 assert block_rlp_size == block_size_limit , (
@@ -430,24 +424,24 @@ def test_block_at_rlp_size_limit_boundary(
430424 )
431425
432426
433- @pytest .mark .with_all_tx_types
434- def test_block_rlp_size_at_limit_with_all_tx_types (
427+ @pytest .mark .with_all_typed_transactions
428+ def test_block_rlp_size_at_limit_with_all_typed_transactions (
435429 blockchain_test : BlockchainTestFiller ,
436430 pre : Alloc ,
437431 post : Alloc ,
438432 fork : Fork ,
439433 sender : EOA ,
440434 block_size_limit : int ,
441435 env : Environment ,
442- tx_type : int ,
436+ typed_transaction : Transaction ,
443437) -> None :
444438 """Test the block RLP size limit with all transaction types."""
445439 transactions , gas_used = exact_size_transactions (
446440 sender ,
447441 block_size_limit ,
448442 fork ,
449443 pre ,
450- tx_type = tx_type ,
444+ specific_transaction_to_include = typed_transaction ,
451445 )
452446 block_rlp_size = get_block_rlp_size (transactions , gas_used = gas_used )
453447 assert block_rlp_size == block_size_limit , (
@@ -483,7 +477,6 @@ def test_block_at_rlp_limit_with_logs(
483477 block_size_limit ,
484478 fork ,
485479 pre ,
486- tx_type = None ,
487480 emit_logs = True ,
488481 )
489482
0 commit comments