@@ -390,6 +390,84 @@ impl<'a> DataDir<'a> {
390390 }
391391}
392392
393+ fn next_wallet_deriv_index_for_challenge (
394+ base_dir : & Option < String > ,
395+ challenge_id : & str ,
396+ data_dir_for_path : & DataDir
397+ ) -> Result < u32 , String > {
398+
399+ // Always start from 0 for the check
400+ const START_INDEX : u32 = 0 ;
401+
402+ Ok ( if let Some ( data_base_dir) = base_dir {
403+
404+ let temp_data_dir_mnemonic = match data_dir_for_path {
405+ DataDir :: Mnemonic ( wallet) => DataDir :: Mnemonic ( DataDirMnemonic {
406+ mnemonic : wallet. mnemonic ,
407+ account : wallet. account ,
408+ deriv_index : 0 ,
409+ } ) ,
410+ _ => return Err ( "next_wallet_deriv_index_for_challenge called with non-Mnemonic DataDir" . to_string ( ) ) ,
411+ } ;
412+
413+ let mut account_dir = temp_data_dir_mnemonic. receipt_dir ( data_base_dir, challenge_id) ?;
414+ account_dir. pop ( ) ;
415+
416+ // --- GATHER AND PARSE INDICES ---
417+ let mut parsed_indices: Vec < u32 > = std:: fs:: read_dir ( & account_dir)
418+ . map_err ( |e| format ! ( "Could not read the mnemonic's account dir: {}" , e) ) ?
419+ . filter_map ( |entry| entry. ok ( ) . map ( |e| e. path ( ) ) )
420+ . filter ( |path| {
421+ // Check if the directory path points to a valid index directory
422+ path. file_stem ( ) . and_then ( OsStr :: to_str) . and_then ( |s| s. parse :: < u32 > ( ) . ok ( ) ) . is_some ( )
423+ } )
424+ . filter ( |path| {
425+ // Check if the receipt file exists inside this index directory
426+ let mut receipt_path = path. clone ( ) ;
427+ receipt_path. push ( FILE_NAME_RECEIPT ) ;
428+
429+ match std:: fs:: exists ( & receipt_path) {
430+ Err ( e) => {
431+ eprintln ! ( "Could not check for receipt at {:?}: {}" , path, e) ;
432+ true // Treat error as receipt existing to avoid retrying a potentially problematic index
433+ } ,
434+ Ok ( exists) => exists,
435+ }
436+ } )
437+ . filter_map ( |path| path. file_stem ( )
438+ . and_then ( OsStr :: to_str)
439+ . and_then ( |s| s. parse :: < u32 > ( ) . ok ( ) ) )
440+ . collect ( ) ;
441+
442+ parsed_indices. sort ( ) ;
443+
444+ if parsed_indices. is_empty ( ) {
445+ eprintln ! ( "no highest index: using {}" , START_INDEX ) ;
446+ START_INDEX
447+ } else {
448+ // --- CHECK FOR GAPS ---
449+ let mut expected_index = START_INDEX ;
450+ for & index in parsed_indices. iter ( ) {
451+ if index > expected_index {
452+ // Gap found: an index is missing a receipt. Return the missing index.
453+ eprintln ! ( "Gap found in receipts. Highest continuous index is {}. Retrying missing index {}." , expected_index. wrapping_sub( 1 ) , expected_index) ;
454+ return Ok ( expected_index) ;
455+ }
456+ // Move to the next expected index
457+ expected_index = index. wrapping_add ( 1 ) ;
458+ }
459+
460+ // --- NO GAPS FOUND ---
461+ // The next index to mine is the one that was *expected* after the last saved index.
462+ expected_index
463+ }
464+ } else {
465+ // If no data_dir is provided, always start at 0.
466+ START_INDEX
467+ } )
468+ }
469+
470+
393471/// Runs the main application logic based on CLI flags.
394472fn run_app ( cli : Cli ) -> Result < ( ) , String > {
395473 // 1. Check for --api-url
@@ -510,8 +588,8 @@ fn run_app(cli: Cli) -> Result<(), String> {
510588 Ok ( Some ( params) ) => params,
511589 Ok ( None ) => continue , // Continue polling after sleep/wait
512590 Err ( e) => {
513- eprintln ! ( "⚠️ Critical API Error during challenge check: {}. Retrying in 5 minutes ..." , e) ;
514- thread:: sleep ( Duration :: from_secs ( 5 * 60 ) ) ;
591+ eprintln ! ( "⚠️ Critical API Error during challenge check: {}. Retrying in 1 minute ..." , e) ;
592+ thread:: sleep ( Duration :: from_secs ( 60 ) ) ;
515593 continue ;
516594 }
517595 } ;
@@ -595,8 +673,8 @@ fn run_app(cli: Cli) -> Result<(), String> {
595673 if let Some ( ref mnemonic_phrase) = mnemonic {
596674
597675 let reg_message = tc_response. message . clone ( ) ;
598- // Start at derivation index 0 or CLI flag passed in if resuming
599- let mut wallet_deriv_index: u32 = cli . mnemonic_starting_index ;
676+ // ⭐ REMOVED cli.mnemonic_starting_index, now initialized to 0 implicitly via gap check
677+ let mut wallet_deriv_index: u32 = 0 ;
600678 let mut first_run = true ;
601679 let mut max_registered_index = None ;
602680 let mut backoff_challenge = Backoff :: new ( 5 , 300 , 2.0 ) ;
@@ -613,6 +691,7 @@ fn run_app(cli: Cli) -> Result<(), String> {
613691
614692 // Outer Polling loop (robustly checks for challenge changes)
615693 loop {
694+ // DataDir is constructed with the current index, used for the current wallet and for path generation in the gap check.
616695 let data_dir = DataDir :: Mnemonic ( DataDirMnemonic {
617696 mnemonic : mnemonic_phrase,
618697 account : cli. mnemonic_account ,
@@ -627,66 +706,20 @@ fn run_app(cli: Cli) -> Result<(), String> {
627706 // which is exactly the point of increasing the wallet derivation path index.
628707 current_challenge_id. clear ( ) ;
629708
630- fn next_wallet_deriv_index_for_challenge ( base_dir : & Option < String > , data_dir : & DataDir , challenge_id : & str ) -> Result < u32 , String > {
631- let initial_deriv_index = match data_dir {
632- DataDir :: Mnemonic ( wallet) => wallet. deriv_index ,
633- // Handle other DataDir variants if necessary, or panic/return an error
634- // for cases where deriv_index is not applicable/available.
635- // For this specific logic, we assume it's only called with DataDir::Mnemonic.
636- _ => return Err ( "next_wallet_deriv_index_for_challenge called with non-Mnemonic DataDir" . to_string ( ) ) ,
637- } ;
638- Ok ( if let Some ( data_base_dir) = base_dir {
639- let mut account_dir = data_dir. receipt_dir ( data_base_dir, challenge_id) ?;
640- account_dir. pop ( ) ;
641-
642- let mut indices: Vec < String > = std:: fs:: read_dir ( & account_dir)
643- . map_err ( |e| format ! ( "Could not read the mnemonic's account dir: {}" , e) ) ?
644- . filter_map ( |entry| entry. ok ( ) . map ( |e| e. path ( ) ) )
645- . filter ( |path| {
646- let mut receipt_path = path. clone ( ) ;
647- receipt_path. push ( FILE_NAME_RECEIPT ) ;
648-
649- match std:: fs:: exists ( & receipt_path) {
650- Err ( e) => {
651- eprintln ! ( "Could not check for receipt: {}" , e) ;
652- true // better to skip an index than to solve the challenge twice
653- } ,
654- Ok ( exists) => exists,
655- }
656- } )
657- . filter_map ( |path| path. file_stem ( )
658- . and_then ( OsStr :: to_str)
659- . map ( ToOwned :: to_owned) )
660- . collect ( ) ;
661-
662- indices. sort ( ) ;
663-
664- if let Some ( highest_index_string) = indices. pop ( ) {
665- let highest_index = highest_index_string. parse :: < u32 > ( )
666- . map_err ( |e| format ! ( "Wallet derivation index directory name is not a positive integer: {}" , e) ) ?;
667- if initial_deriv_index > highest_index {
668- eprintln ! ( "Using initial deriv_index {} which is higher than highest existing index {}" , initial_deriv_index, highest_index) ;
669- initial_deriv_index
670- } else {
671- highest_index + 1
672- }
673- } else {
674- eprintln ! ( "no highest index: using {}" , initial_deriv_index) ;
675- initial_deriv_index
676- }
677- } else {
678- 0
679- } )
680- }
681-
682709 // Get challenge parameters (fixed or dynamic)
683710 let challenge_params = match get_challenge_params ( & client, & api_url, cli_challenge_ref, & mut current_challenge_id) {
684711 Ok ( Some ( params) ) => {
685712 backoff_challenge. reset ( ) ;
686713
687- // Reset index only if we saw a new challenge
714+ // ⭐ Index reset logic simplified: Always check for the next index from 0 on a new challenge
688715 if first_run || ( cli_challenge_ref. is_none ( ) && params. challenge_id != old_challenge_id) {
689- wallet_deriv_index = next_wallet_deriv_index_for_challenge ( & cli. data_dir , & data_dir, & params. challenge_id ) ?;
716+
717+ // ⭐ Simplified call: default_start_index is always 0, as per request
718+ wallet_deriv_index = next_wallet_deriv_index_for_challenge (
719+ & cli. data_dir ,
720+ & params. challenge_id ,
721+ & data_dir
722+ ) ?;
690723 }
691724
692725 // Update last seen only on success
@@ -778,10 +811,13 @@ fn run_app(cli: Cli) -> Result<(), String> {
778811 if let Some ( ref base_dir) = cli. data_dir {
779812 data_dir. save_receipt ( base_dir, & challenge_params. challenge_id , & receipt, & donation) ?;
780813 }
814+ // Index must be incremented after a successful submit
815+ wallet_deriv_index = wallet_deriv_index. wrapping_add ( 1 ) ;
816+ println ! ( "\n ✅ Solution submitted. Incrementing index to {}." , wallet_deriv_index) ;
781817 } ,
782818 MiningResult :: AlreadySolved => {
783819 wallet_deriv_index = wallet_deriv_index. wrapping_add ( 1 ) ;
784- println ! ( "\n ✅ Cycle complete . Incrementing index to {}." , wallet_deriv_index) ;
820+ println ! ( "\n ✅ Challenge already solved . Incrementing index to {}." , wallet_deriv_index) ;
785821 // The outer loop restarts, calling get_challenge_params again.
786822 }
787823 MiningResult :: MiningFailed => {
0 commit comments