11use assert_matches:: assert_matches;
2- use zksync_test_contracts:: TestContract ;
3- use zksync_types:: { u256_to_h256, AccountTreeId , Address , StorageKey } ;
4- use zksync_vm_interface:: tracer:: ViolatedValidationRule ;
2+ use zksync_test_contracts:: { Account , TestContract } ;
3+ use zksync_types:: { address_to_h256, fee:: Fee , AccountTreeId , Address , StorageKey , H256 } ;
54
65use super :: {
7- get_empty_storage, require_eip712:: make_aa_transaction, tester :: VmTesterBuilder ,
8- ContractToDeploy , TestedVm , TestedVmForValidation ,
6+ default_system_env , get_empty_storage, require_eip712:: make_aa_transaction,
7+ tester :: VmTesterBuilder , ContractToDeploy , TestedVm , TestedVmForValidation ,
98} ;
10- use crate :: interface:: TxExecutionMode ;
9+ use crate :: interface:: {
10+ tracer:: ViolatedValidationRule , ExecutionResult , Halt , InspectExecutionMode , SystemEnv ,
11+ TxExecutionMode , VmExecutionResultAndLogs , VmInterfaceExt ,
12+ } ;
13+
14+ /// Corresponds to test cases in the `ValidationRuleBreaker` contract.
15+ #[ derive( Debug , Clone , Copy ) ]
16+ #[ repr( u32 ) ]
17+ enum TestCase {
18+ Baseline = 0 ,
19+ ReadBootloaderBalance = 1 ,
20+ CallEoa = 2 ,
21+ ReadFromTrustedAddressSlot = 3 ,
22+ RecursiveOutOfGas = 4 ,
23+ PlainOutOfGas = 5 ,
24+ PlainOutOfGasWithCatch = 6 ,
25+ }
1126
1227/// Checks that every limitation imposed on account validation results in an appropriate error.
1328/// The actual misbehavior cases are found in "validation-rule-breaker.sol".
1429pub ( crate ) fn test_account_validation_rules < VM : TestedVm + TestedVmForValidation > ( ) {
15- assert_matches ! ( test_rule:: <VM >( 0 ) , None ) ;
30+ let ( result, violated_rule) = test_rule :: < VM > ( u32:: MAX , TestCase :: Baseline ) ;
31+ assert ! ( !result. result. is_failed( ) , "{result:#?}" ) ;
32+ assert_matches ! ( violated_rule, None ) ;
33+
34+ let ( result, violated_rule) = test_rule :: < VM > ( u32:: MAX , TestCase :: ReadBootloaderBalance ) ;
35+ assert_matches ! (
36+ & result. result,
37+ ExecutionResult :: Halt {
38+ reason: Halt :: TracerCustom ( _)
39+ }
40+ ) ;
1641 assert_matches ! (
17- test_rule :: < VM > ( 1 ) ,
42+ violated_rule ,
1843 Some ( ViolatedValidationRule :: TouchedDisallowedStorageSlots ( _, _) )
1944 ) ;
45+
46+ let ( result, violated_rule) = test_rule :: < VM > ( u32:: MAX , TestCase :: CallEoa ) ;
2047 assert_matches ! (
21- test_rule:: <VM >( 2 ) ,
22- Some ( ViolatedValidationRule :: CalledContractWithNoCode ( _) )
48+ & result. result,
49+ ExecutionResult :: Halt {
50+ reason: Halt :: TracerCustom ( _)
51+ }
2352 ) ;
24- assert_matches ! ( test_rule:: <VM >( 3 ) , None ) ;
2553 assert_matches ! (
26- test_rule:: <VM >( 4 ) ,
27- Some ( ViolatedValidationRule :: TookTooManyComputationalGas ( _) )
28- )
54+ violated_rule,
55+ Some ( ViolatedValidationRule :: CalledContractWithNoCode ( _) )
56+ ) ;
57+
58+ let ( result, violated_rule) = test_rule :: < VM > ( u32:: MAX , TestCase :: ReadFromTrustedAddressSlot ) ;
59+ assert ! ( !result. result. is_failed( ) , "{result:#?}" ) ;
60+ assert_matches ! ( violated_rule, None ) ;
61+
62+ for test_case in [ TestCase :: RecursiveOutOfGas , TestCase :: PlainOutOfGas ] {
63+ let ( result, violated_rule) = test_rule :: < VM > ( u32:: MAX , test_case) ;
64+ assert_matches ! (
65+ & result. result,
66+ ExecutionResult :: Halt {
67+ reason: Halt :: TracerCustom ( _)
68+ }
69+ ) ;
70+ assert_matches ! (
71+ violated_rule,
72+ Some ( ViolatedValidationRule :: TookTooManyComputationalGas ( _) )
73+ ) ;
74+ }
2975}
3076
31- fn test_rule < VM : TestedVm + TestedVmForValidation > ( rule : u32 ) -> Option < ViolatedValidationRule > {
77+ fn test_rule < VM : TestedVmForValidation > (
78+ validation_gas_limit : u32 ,
79+ test_case : TestCase ,
80+ ) -> ( VmExecutionResultAndLogs , Option < ViolatedValidationRule > ) {
3281 let aa_address = Address :: repeat_byte ( 0x10 ) ;
3382 let beneficiary_address = Address :: repeat_byte ( 0x20 ) ;
3483
3584 // Set the type of misbehaviour of the AA contract
3685 let mut storage_with_rule_break_set = get_empty_storage ( ) ;
3786 storage_with_rule_break_set. set_value (
38- StorageKey :: new ( AccountTreeId :: new ( aa_address) , u256_to_h256 ( 0 . into ( ) ) ) ,
39- u256_to_h256 ( rule. into ( ) ) ,
87+ StorageKey :: new ( AccountTreeId :: new ( aa_address) , H256 :: zero ( ) ) ,
88+ H256 :: from_low_u64_be ( test_case as u64 ) ,
89+ ) ;
90+ // Set the trusted address.
91+ storage_with_rule_break_set. set_value (
92+ StorageKey :: new ( AccountTreeId :: new ( aa_address) , H256 :: from_low_u64_be ( 1 ) ) ,
93+ address_to_h256 ( & Address :: from_low_u64_be ( 0x800a ) ) ,
4094 ) ;
4195
4296 let bytecode = TestContract :: validation_test ( ) . bytecode . to_vec ( ) ;
4397 let mut vm = VmTesterBuilder :: new ( )
44- . with_empty_in_memory_storage ( )
98+ . with_system_env ( SystemEnv {
99+ default_validation_computational_gas_limit : validation_gas_limit,
100+ ..default_system_env ( )
101+ } )
45102 . with_custom_contracts ( vec ! [
46103 ContractToDeploy :: account( bytecode, aa_address) . funded( )
47104 ] )
@@ -50,10 +107,102 @@ fn test_rule<VM: TestedVm + TestedVmForValidation>(rule: u32) -> Option<Violated
50107 . with_rich_accounts ( 1 )
51108 . build :: < VM > ( ) ;
52109
53- let private_account = vm. rich_accounts [ 0 ] . clone ( ) ;
110+ let private_account = & mut vm. rich_accounts [ 0 ] ;
111+ let tx = make_aa_transaction ( aa_address, beneficiary_address, private_account, None ) ;
112+ vm. vm . run_validation ( tx, 55 )
113+ }
114+
115+ const OUT_OF_GAS_CASES : [ TestCase ; 3 ] = [
116+ TestCase :: PlainOutOfGas ,
117+ TestCase :: PlainOutOfGasWithCatch ,
118+ TestCase :: RecursiveOutOfGas ,
119+ ] ;
120+
121+ pub ( crate ) fn test_validation_out_of_gas_with_full_tracer < VM : TestedVmForValidation > ( ) {
122+ for test_case in OUT_OF_GAS_CASES {
123+ println ! ( "Testing case: {test_case:?}" ) ;
124+ let ( result, violated_rule) = test_rule :: < VM > ( 300_000 , test_case) ;
125+ assert_matches ! (
126+ & result. result,
127+ ExecutionResult :: Halt {
128+ reason: Halt :: TracerCustom ( _)
129+ }
130+ ) ;
131+ assert_matches ! (
132+ violated_rule,
133+ Some ( ViolatedValidationRule :: TookTooManyComputationalGas ( _) )
134+ ) ;
135+ }
136+ }
137+
138+ pub ( crate ) fn test_validation_out_of_gas_with_fast_tracer < VM : TestedVm > ( ) {
139+ for test_case in OUT_OF_GAS_CASES {
140+ println ! ( "Testing case: {test_case:?}" ) ;
141+
142+ // Large tx gas limit should lead to a validation-specific halt reason.
143+ let tx_gas_limits: & [ _ ] = if matches ! ( test_case, TestCase :: RecursiveOutOfGas ) {
144+ & [ 100_000_000 , 200_000_000 , 500_000_000 , 1_000_000_000 ]
145+ } else {
146+ & [ 1_000_000 , 10_000_000 , 100_000_000 , 1_000_000_000 ]
147+ } ;
148+
149+ for & tx_gas_limit in tx_gas_limits {
150+ println ! ( "Testing tx with gas limit: {tx_gas_limit}" ) ;
151+ let result = run_validation_with_gas_limit :: < VM > ( test_case, tx_gas_limit) ;
152+ assert_matches ! (
153+ & result. result,
154+ ExecutionResult :: Halt {
155+ reason: Halt :: ValidationOutOfGas ,
156+ }
157+ ) ;
158+ }
159+
160+ // If the tx gas limit is lower than the validation gas limit, the bootloader should exit super-early.
161+ println ! ( "Testing tx with low gas limit" ) ;
162+ let result = run_validation_with_gas_limit :: < VM > ( test_case, 250_000 ) ;
163+ assert_matches ! (
164+ & result. result,
165+ ExecutionResult :: Halt {
166+ reason: Halt :: ValidationFailed ( _)
167+ }
168+ ) ;
169+ }
170+ }
171+
172+ fn run_validation_with_gas_limit < VM : TestedVm > (
173+ test_case : TestCase ,
174+ tx_gas_limit : u32 ,
175+ ) -> VmExecutionResultAndLogs {
176+ let aa_address = Address :: repeat_byte ( 0x10 ) ;
177+ let beneficiary_address = Address :: repeat_byte ( 0x20 ) ;
178+ let bytecode = TestContract :: validation_test ( ) . bytecode . to_vec ( ) ;
179+
180+ // Configure the AA to run out of gas during validation.
181+ let mut storage_with_rule_break_set = get_empty_storage ( ) ;
182+ storage_with_rule_break_set. set_value (
183+ StorageKey :: new ( AccountTreeId :: new ( aa_address) , H256 :: zero ( ) ) ,
184+ H256 :: from_low_u64_be ( test_case as u64 ) ,
185+ ) ;
186+
187+ let mut vm = VmTesterBuilder :: new ( )
188+ . with_system_env ( SystemEnv {
189+ default_validation_computational_gas_limit : 300_000 ,
190+ ..default_system_env ( )
191+ } )
192+ . with_custom_contracts ( vec ! [
193+ ContractToDeploy :: account( bytecode, aa_address) . funded( )
194+ ] )
195+ . with_storage ( storage_with_rule_break_set)
196+ . with_execution_mode ( TxExecutionMode :: VerifyExecute )
197+ . with_rich_accounts ( 1 )
198+ . build :: < VM > ( ) ;
54199
55- vm. vm . run_validation (
56- make_aa_transaction ( aa_address, beneficiary_address, & private_account) ,
57- 55 ,
58- )
200+ let private_account = & mut vm. rich_accounts [ 0 ] ;
201+ let fee = Fee {
202+ gas_limit : tx_gas_limit. into ( ) ,
203+ ..Account :: default_fee ( )
204+ } ;
205+ let tx = make_aa_transaction ( aa_address, beneficiary_address, private_account, Some ( fee) ) ;
206+ vm. vm . push_transaction ( tx. into ( ) ) ;
207+ vm. vm . execute ( InspectExecutionMode :: OneTx )
59208}
0 commit comments