@@ -2,11 +2,11 @@ use assert_matches::assert_matches;
22use blockifier:: blockifier:: config:: ContractClassManagerConfig ;
33use blockifier:: bouncer:: { BouncerConfig , BouncerWeights } ;
44use blockifier:: state:: contract_class_manager:: ContractClassManager ;
5- use blockifier_reexecution:: state_reader:: rpc_objects:: BlockId ;
5+ use blockifier_reexecution:: state_reader:: rpc_objects:: { BlockHeader , BlockId } ;
66use blockifier_reexecution:: utils:: get_chain_info;
77use rstest:: rstest;
88use starknet_api:: abi:: abi_utils:: { get_storage_var_address, selector_from_name} ;
9- use starknet_api:: block:: BlockNumber ;
9+ use starknet_api:: block:: { BlockNumber , GasPrice , GasPricePerToken , StarknetVersion } ;
1010use starknet_api:: core:: { ChainId , ContractAddress , Nonce } ;
1111use starknet_api:: test_utils:: invoke:: invoke_tx;
1212use starknet_api:: transaction:: fields:: ValidResourceBounds ;
@@ -15,6 +15,8 @@ use starknet_api::{calldata, felt, invoke_tx_args};
1515
1616use crate :: errors:: VirtualBlockExecutorError ;
1717use crate :: running:: virtual_block_executor:: {
18+ starknet_version_or_latest,
19+ BaseBlockInfo ,
1820 RpcVirtualBlockExecutor ,
1921 RpcVirtualBlockExecutorConfig ,
2022 VirtualBlockExecutor ,
@@ -267,3 +269,91 @@ fn test_execute_rejected_by_tight_bouncer_limits(
267269 if msg. contains( "Transaction size exceeds the maximum block capacity" )
268270 ) ;
269271}
272+
273+ /// Returns a block header with the given version string and nonzero gas prices (zero gas prices
274+ /// fail the `BlockInfo` conversion).
275+ fn block_header_with_version ( starknet_version : & str ) -> BlockHeader {
276+ let nonzero_gas_price =
277+ GasPricePerToken { price_in_wei : GasPrice ( 1 ) , price_in_fri : GasPrice ( 1 ) } ;
278+ BlockHeader {
279+ starknet_version : starknet_version. to_string ( ) ,
280+ l1_gas_price : nonzero_gas_price,
281+ l1_data_gas_price : nonzero_gas_price,
282+ l2_gas_price : nonzero_gas_price,
283+ ..Default :: default ( )
284+ }
285+ }
286+
287+ /// Verifies that the base block info keeps the raw version string for the OS while falling back
288+ /// to the latest known version for execution when the block's version is newer than this binary.
289+ #[ rstest]
290+ #[ case:: known_version( "0.14.2" , StarknetVersion :: V0_14_2 ) ]
291+ #[ case:: unknown_newer_version( "0.20.0" , StarknetVersion :: LATEST ) ]
292+ fn test_base_block_info_starknet_version_handling (
293+ #[ case] version_string : & str ,
294+ #[ case] expected_execution_version : StarknetVersion ,
295+ #[ values( true , false ) ] use_latest_versioned_constants : bool ,
296+ ) {
297+ let base_block_info = BaseBlockInfo :: new (
298+ block_header_with_version ( version_string) ,
299+ get_chain_info ( & ChainId :: Mainnet , None ) ,
300+ use_latest_versioned_constants,
301+ )
302+ . unwrap ( ) ;
303+
304+ assert_eq ! ( base_block_info. raw_starknet_version, version_string) ;
305+ assert_eq ! (
306+ base_block_info. block_context. block_info( ) . starknet_version,
307+ expected_execution_version
308+ ) ;
309+ }
310+
311+ #[ rstest]
312+ #[ case:: non_numeric_version( "not.a.version" ) ]
313+ #[ case:: unknown_old_version( "0.13.7" ) ]
314+ fn test_base_block_info_rejects_invalid_version ( #[ case] version_string : & str ) {
315+ let base_block_info_result = BaseBlockInfo :: new (
316+ block_header_with_version ( version_string) ,
317+ get_chain_info ( & ChainId :: Mainnet , None ) ,
318+ true ,
319+ ) ;
320+ assert ! ( base_block_info_result. is_err( ) ) ;
321+ }
322+
323+ /// Verifies the version-string parsing of `starknet_version_or_latest`: known version strings
324+ /// parse to their exact variant, and strings strictly newer than `LATEST` fall back to `LATEST`.
325+ #[ rstest]
326+ #[ case:: known_three_segment_version( "0.13.2" , StarknetVersion :: V0_13_2 ) ]
327+ #[ case:: known_four_segment_version( "0.13.1.1" , StarknetVersion :: V0_13_1_1 ) ]
328+ #[ case:: newer_minor_version( "0.15.0" , StarknetVersion :: LATEST ) ]
329+ #[ case:: newer_major_version( "1.0.0" , StarknetVersion :: LATEST ) ]
330+ fn test_starknet_version_or_latest_parsing (
331+ #[ case] version_string : & str ,
332+ #[ case] expected_version : StarknetVersion ,
333+ ) {
334+ assert_eq ! ( starknet_version_or_latest( version_string) . unwrap( ) , expected_version) ;
335+ }
336+
337+ /// The exact `LATEST` version string must parse back to `LATEST`, regardless of which version
338+ /// that currently is.
339+ #[ test]
340+ fn test_starknet_version_or_latest_roundtrips_latest ( ) {
341+ assert_eq ! (
342+ starknet_version_or_latest( & StarknetVersion :: LATEST . to_string( ) ) . unwrap( ) ,
343+ StarknetVersion :: LATEST
344+ ) ;
345+ }
346+
347+ /// Verifies that `starknet_version_or_latest` rejects malformed version strings and unknown
348+ /// versions that are not newer than `LATEST` (only newer-than-`LATEST` versions fall back).
349+ #[ rstest]
350+ #[ case:: unknown_older_version( "0.13.7" ) ]
351+ #[ case:: truncated_latest_version( "0.14" ) ]
352+ #[ case:: non_numeric_version( "not.a.version" ) ]
353+ #[ case:: empty_string( "" ) ]
354+ #[ case:: segment_exceeding_u8( "0.14.300" ) ]
355+ #[ case:: trailing_dot( "0.14.3." ) ]
356+ #[ case:: release_candidate_suffix( "0.15.0-rc.1" ) ]
357+ fn test_starknet_version_or_latest_rejects_invalid_version ( #[ case] version_string : & str ) {
358+ assert ! ( starknet_version_or_latest( version_string) . is_err( ) ) ;
359+ }
0 commit comments