@@ -8,10 +8,9 @@ pub mod transaction_fuzzer {
88 pub mod generator;
99 pub mod invariants;
1010 pub mod mutator;
11-
1211 use binprot:: {
1312 macros:: { BinProtRead , BinProtWrite } ,
14- BinProtRead , BinProtSize , BinProtWrite ,
13+ BinProtRead , BinProtSize , BinProtWrite , SmallString1k ,
1514 } ;
1615 use context:: { ApplyTxResult , FuzzerCtx , FuzzerCtxBuilder } ;
1716 use coverage:: {
@@ -20,14 +19,15 @@ pub mod transaction_fuzzer {
2019 stats:: Stats ,
2120 } ;
2221 use ledger:: {
23- scan_state:: transaction_logic:: { zkapp_command :: ZkAppCommand , Transaction , UserCommand } ,
22+ scan_state:: transaction_logic:: { Transaction , UserCommand } ,
2423 sparse_ledger:: LedgerIntf ,
2524 Account , BaseLedger ,
2625 } ;
2726 use mina_hasher:: Fp ;
2827 use mina_p2p_messages:: bigint:: BigInt ;
2928 use openmina_core:: constants:: ConstraintConstantsUnversioned ;
3029 use std:: io:: { Read , Write } ;
30+ use std:: panic;
3131 use std:: {
3232 env,
3333 process:: { ChildStdin , ChildStdout } ,
@@ -105,6 +105,11 @@ pub mod transaction_fuzzer {
105105 . filter_path( ".rustup/" )
106106 . filter_path( "mina-p2p-messages/" )
107107 . filter_path( "core/" )
108+ . filter_path( "tools/" )
109+ . filter_path( "p2p/" )
110+ . filter_path( "node/" )
111+ . filter_path( "vrf/" )
112+ . filter_path( "snark/" )
108113 . filter_path( "proofs/" )
109114 ) ;
110115 }
@@ -115,6 +120,8 @@ pub mod transaction_fuzzer {
115120 enum Action {
116121 SetConstraintConstants ( ConstraintConstantsUnversioned ) ,
117122 SetInitialAccounts ( Vec < Account > ) ,
123+ SetupPool ,
124+ PoolVerify ( UserCommand ) ,
118125 GetAccounts ,
119126 ApplyTx ( UserCommand ) ,
120127 #[ allow( dead_code) ]
@@ -125,11 +132,39 @@ pub mod transaction_fuzzer {
125132 enum ActionOutput {
126133 ConstraintConstantsSet ,
127134 InitialAccountsSet ( BigInt ) ,
135+ SetupPool ,
136+ PoolVerify ( Result < Vec < UserCommand > , SmallString1k > ) ,
128137 Accounts ( Vec < Account > ) ,
129138 TxApplied ( ApplyTxResult ) ,
130139 ExitAck ,
131140 }
132141
142+ #[ coverage( off) ]
143+ fn ocaml_setup_pool ( stdin : & mut ChildStdin , stdout : & mut ChildStdout ) {
144+ let action = Action :: SetupPool ;
145+ serialize ( & action, stdin) ;
146+ let output: ActionOutput = deserialize ( stdout) ;
147+ match output {
148+ ActionOutput :: SetupPool => ( ) ,
149+ _ => panic ! ( "Expected SetupPool" ) ,
150+ }
151+ }
152+
153+ #[ coverage( off) ]
154+ fn ocaml_pool_verify (
155+ stdin : & mut ChildStdin ,
156+ stdout : & mut ChildStdout ,
157+ user_command : UserCommand ,
158+ ) -> Result < Vec < UserCommand > , SmallString1k > {
159+ let action = Action :: PoolVerify ( user_command) ;
160+ serialize ( & action, stdin) ;
161+ let output: ActionOutput = deserialize ( stdout) ;
162+ match output {
163+ ActionOutput :: PoolVerify ( result) => result,
164+ _ => panic ! ( "Expected SetupPool" ) ,
165+ }
166+ }
167+
133168 #[ coverage( off) ]
134169 fn ocaml_set_initial_accounts (
135170 ctx : & mut FuzzerCtx ,
@@ -197,6 +232,8 @@ pub mod transaction_fuzzer {
197232 break_on_invariant : bool ,
198233 seed : u64 ,
199234 minimum_fee : u64 ,
235+ pool_fuzzing : bool ,
236+ transaction_application_fuzzing : bool ,
200237 ) {
201238 * invariants:: BREAK . write ( ) . unwrap ( ) = break_on_invariant;
202239 let mut cov_stats = CoverageStats :: new ( ) ;
@@ -210,6 +247,10 @@ pub mod transaction_fuzzer {
210247 ocaml_set_constraint_constants ( & mut ctx, stdin, stdout) ;
211248 ocaml_set_initial_accounts ( & mut ctx, stdin, stdout) ;
212249
250+ if pool_fuzzing {
251+ ocaml_setup_pool ( stdin, stdout) ;
252+ }
253+
213254 let mut fuzzer_made_progress = false ;
214255
215256 for iteration in 0 .. {
@@ -238,86 +279,145 @@ pub mod transaction_fuzzer {
238279 }
239280
240281 let user_command: UserCommand = ctx. random_user_command ( ) ;
241- let ocaml_apply_result = ocaml_apply_transaction ( stdin, stdout, user_command. clone ( ) ) ;
242- let mut ledger = ctx. get_ledger_inner ( ) . make_child ( ) ;
243-
244- // Apply transaction on the Rust side
245- if let Err ( error) =
246- ctx. apply_transaction ( & mut ledger, & user_command, & ocaml_apply_result)
247- {
248- println ! ( "!!! {error}" ) ;
249282
250- // Diff generated command form serialized version (detect hash inconsitencies)
251- if let Transaction :: Command ( ocaml_user_command) =
252- ocaml_apply_result. apply_result [ 0 ] . transaction ( ) . data
253- {
254- if let UserCommand :: ZkAppCommand ( command) = & ocaml_user_command {
255- command. account_updates . ensure_hashed ( ) ;
283+ if pool_fuzzing {
284+ let ocaml_pool_verify_result =
285+ ocaml_pool_verify ( stdin, stdout, user_command. clone ( ) ) ;
286+
287+ match panic:: catch_unwind (
288+ #[ coverage( off) ]
289+ || ctx. pool_verify ( & user_command, & ocaml_pool_verify_result) ,
290+ ) {
291+ Ok ( mismatch) => {
292+ if mismatch {
293+ let mut ledger = ctx. get_ledger_inner ( ) . make_child ( ) ;
294+ let bigint: num_bigint:: BigUint =
295+ LedgerIntf :: merkle_root ( & mut ledger) . into ( ) ;
296+ ctx. save_fuzzcase ( & user_command, & bigint. to_string ( ) ) ;
297+
298+ std:: process:: exit ( 0 ) ;
299+ } else {
300+ if let Err ( _error) = ocaml_pool_verify_result {
301+ //println!("Skipping application: {:?}", _error);
302+ continue ;
303+ }
304+ }
305+ }
306+ Err ( _) => {
307+ println ! ( "!!! PANIC detected" ) ;
308+ let mut ledger = ctx. get_ledger_inner ( ) . make_child ( ) ;
309+ let bigint: num_bigint:: BigUint =
310+ LedgerIntf :: merkle_root ( & mut ledger) . into ( ) ;
311+ ctx. save_fuzzcase ( & user_command, & bigint. to_string ( ) ) ;
312+
313+ std:: process:: exit ( 0 ) ;
256314 }
257-
258- println ! ( "{}" , ctx. diagnostic( & user_command, & ocaml_user_command) ) ;
259315 }
316+ }
317+
318+ if transaction_application_fuzzing {
319+ let ocaml_apply_result =
320+ ocaml_apply_transaction ( stdin, stdout, user_command. clone ( ) ) ;
321+ let mut ledger = ctx. get_ledger_inner ( ) . make_child ( ) ;
260322
261- let ocaml_accounts = ocaml_get_accounts ( stdin, stdout) ;
262- let rust_accounts = ledger. to_list ( ) ;
323+ // Apply transaction on the Rust side
324+ if let Err ( error) =
325+ ctx. apply_transaction ( & mut ledger, & user_command, & ocaml_apply_result)
326+ {
327+ println ! ( "!!! {error}" ) ;
328+
329+ // Diff generated command form serialized version (detect hash inconsitencies)
330+ if let Transaction :: Command ( ocaml_user_command) =
331+ ocaml_apply_result. apply_result [ 0 ] . transaction ( ) . data
332+ {
333+ if let UserCommand :: ZkAppCommand ( command) = & ocaml_user_command {
334+ command. account_updates . ensure_hashed ( ) ;
335+ }
336+
337+ println ! ( "{}" , ctx. diagnostic( & user_command, & ocaml_user_command) ) ;
338+ }
263339
264- for ocaml_account in ocaml_accounts. iter ( ) {
265- match rust_accounts. iter ( ) . find (
266- #[ coverage( off) ]
267- |account| account. public_key == ocaml_account. public_key ,
268- ) {
269- Some ( rust_account) => {
270- if rust_account != ocaml_account {
340+ let ocaml_accounts = ocaml_get_accounts ( stdin, stdout) ;
341+ let rust_accounts = ledger. to_list ( ) ;
342+
343+ for ocaml_account in ocaml_accounts. iter ( ) {
344+ match rust_accounts. iter ( ) . find (
345+ #[ coverage( off) ]
346+ |account| account. public_key == ocaml_account. public_key ,
347+ ) {
348+ Some ( rust_account) => {
349+ if rust_account != ocaml_account {
350+ println ! (
351+ "Content mismatch between OCaml and Rust account:\n {}" ,
352+ ctx. diagnostic( rust_account, ocaml_account)
353+ ) ;
354+ }
355+ }
356+ None => {
271357 println ! (
272- "Content mismatch between OCaml and Rust account: \n { }" ,
273- ctx . diagnostic ( rust_account , ocaml_account)
358+ "OCaml account not present in Rust ledger: {:? }" ,
359+ ocaml_account
274360 ) ;
275361 }
276362 }
277- None => {
363+ }
364+
365+ for rust_account in rust_accounts. iter ( ) {
366+ if !ocaml_accounts. iter ( ) . any (
367+ #[ coverage( off) ]
368+ |account| account. public_key == rust_account. public_key ,
369+ ) {
278370 println ! (
279- "OCaml account not present in Rust ledger: {:?}" ,
280- ocaml_account
371+ "Rust account not present in Ocaml ledger: {:?}" ,
372+ rust_account
281373 ) ;
282374 }
283375 }
284- }
285376
286- for rust_account in rust_accounts. iter ( ) {
287- if !ocaml_accounts. iter ( ) . any (
288- #[ coverage( off) ]
289- |account| account. public_key == rust_account. public_key ,
290- ) {
291- println ! (
292- "Rust account not present in Ocaml ledger: {:?}" ,
293- rust_account
294- ) ;
295- }
296- }
377+ let bigint: num_bigint:: BigUint = LedgerIntf :: merkle_root ( & mut ledger) . into ( ) ;
378+ ctx. save_fuzzcase ( & user_command, & bigint. to_string ( ) ) ;
297379
298- let bigint: num_bigint:: BigUint = LedgerIntf :: merkle_root ( & mut ledger) . into ( ) ;
299- ctx. save_fuzzcase ( & user_command, & bigint. to_string ( ) ) ;
300-
301- // Exiting due to inconsistent state
302- std:: process:: exit ( 0 ) ;
380+ // Exiting due to inconsistent state
381+ std:: process:: exit ( 0 ) ;
382+ }
303383 }
304384 }
305385 }
306386
307387 #[ coverage( off) ]
308- pub fn reproduce ( stdin : & mut ChildStdin , stdout : & mut ChildStdout , fuzzcase : & String ) {
388+ pub fn reproduce (
389+ stdin : & mut ChildStdin ,
390+ stdout : & mut ChildStdout ,
391+ fuzzcase : & String ,
392+ pool_fuzzing : bool ,
393+ transaction_application_fuzzing : bool ,
394+ ) {
309395 let mut ctx = FuzzerCtxBuilder :: new ( ) . build ( ) ;
310396 let user_command = ctx. load_fuzzcase ( fuzzcase) ;
311397
312398 ocaml_set_constraint_constants ( & mut ctx, stdin, stdout) ;
313399 ocaml_set_initial_accounts ( & mut ctx, stdin, stdout) ;
314400
315- let mut ledger = ctx. get_ledger_inner ( ) . make_child ( ) ;
316- let ocaml_apply_result = ocaml_apply_transaction ( stdin, stdout, user_command. clone ( ) ) ;
317- let rust_apply_result =
318- ctx. apply_transaction ( & mut ledger, & user_command, & ocaml_apply_result) ;
401+ if pool_fuzzing {
402+ ocaml_setup_pool ( stdin, stdout) ;
319403
320- println ! ( "apply_transaction: {:?}" , rust_apply_result) ;
404+ let ocaml_pool_verify_result = ocaml_pool_verify ( stdin, stdout, user_command. clone ( ) ) ;
405+
406+ println ! ( "OCaml pool verify: {:?}" , ocaml_pool_verify_result) ;
407+
408+ if ctx. pool_verify ( & user_command, & ocaml_pool_verify_result) {
409+ return ;
410+ }
411+ }
412+
413+ if transaction_application_fuzzing {
414+ let mut ledger = ctx. get_ledger_inner ( ) . make_child ( ) ;
415+ let ocaml_apply_result = ocaml_apply_transaction ( stdin, stdout, user_command. clone ( ) ) ;
416+ let rust_apply_result =
417+ ctx. apply_transaction ( & mut ledger, & user_command, & ocaml_apply_result) ;
418+
419+ println ! ( "apply_transaction: {:?}" , rust_apply_result) ;
420+ }
321421 }
322422}
323423
@@ -340,6 +440,18 @@ fn main() {
340440 . default_value ( "42" )
341441 . value_parser ( clap:: value_parser!( u64 ) ) ,
342442 )
443+ . arg (
444+ clap:: Arg :: new ( "pool-fuzzing" )
445+ . long ( "pool-fuzzing" )
446+ . default_value ( "true" )
447+ . value_parser ( clap:: value_parser!( bool ) ) ,
448+ )
449+ . arg (
450+ clap:: Arg :: new ( "transaction-application-fuzzing" )
451+ . long ( "transaction-application-fuzzing" )
452+ . default_value ( "true" )
453+ . value_parser ( clap:: value_parser!( bool ) ) ,
454+ )
343455 . get_matches ( ) ;
344456
345457 let mut child = Command :: new (
@@ -363,16 +475,33 @@ fn main() {
363475 let stdin = child. stdin . as_mut ( ) . expect ( "Failed to open stdin" ) ;
364476 let stdout = child. stdout . as_mut ( ) . expect ( "Failed to open stdout" ) ;
365477
478+ let pool_fuzzing = * matches. get_one :: < bool > ( "pool-fuzzing" ) . unwrap ( ) ;
479+ let transaction_application_fuzzing = * matches
480+ . get_one :: < bool > ( "transaction-application-fuzzing" )
481+ . unwrap ( ) ;
482+
366483 if let Some ( fuzzcase) = matches. get_one :: < String > ( "fuzzcase" ) {
367484 println ! ( "Reproducing fuzzcase from file: {}" , fuzzcase) ;
368- transaction_fuzzer:: reproduce ( stdin, stdout, fuzzcase) ;
485+ transaction_fuzzer:: reproduce (
486+ stdin,
487+ stdout,
488+ fuzzcase,
489+ pool_fuzzing,
490+ transaction_application_fuzzing,
491+ ) ;
369492 } else {
370- let Some ( seed) = matches. get_one :: < u64 > ( "seed" ) else {
371- unreachable ! ( )
372- } ;
373-
374- println ! ( "Running the fuzzer with seed {seed}..." ) ;
375- transaction_fuzzer:: fuzz ( stdin, stdout, true , * seed, 1000 ) ;
493+ let seed = * matches. get_one :: < u64 > ( "seed" ) . unwrap ( ) ;
494+ println ! ( "Fuzzing [seed: {seed}] [transaction application: {transaction_application_fuzzing} ] [pool: {pool_fuzzing}]..." ) ;
495+
496+ transaction_fuzzer:: fuzz (
497+ stdin,
498+ stdout,
499+ true ,
500+ seed,
501+ 1000 ,
502+ pool_fuzzing,
503+ transaction_application_fuzzing,
504+ ) ;
376505 }
377506 }
378507}
0 commit comments