@@ -128,7 +128,7 @@ pub struct CliChallengeData {
128128// CORE APPLICATION STRUCTS
129129// ===============================================
130130
131- // NEW STRUCT: Holds the common, validated state for the mining loops.
131+ // Holds the common, validated state for the mining loops.
132132#[ derive( Debug ) ]
133133pub struct MiningContext < ' a > {
134134 pub client : blocking:: Client ,
@@ -142,10 +142,20 @@ pub struct MiningContext<'a> {
142142}
143143
144144
145- // NEW: Define a result type for the mining cycle
145+ // Holds the data needed to submit a solution later.
146+ #[ derive( Debug , Deserialize , Serialize , Clone ) ]
147+ pub struct PendingSolution {
148+ pub address : String ,
149+ pub challenge_id : String ,
150+ pub nonce : String ,
151+ pub donation_address : Option < String > , // RE-ADDED this field
152+ }
153+
154+ // Define a result type for the mining cycle
146155#[ derive( Debug , PartialEq ) ]
147156pub enum MiningResult {
148- FoundAndSubmitted ( ( serde_json:: Value , Option < String > ) ) ,
157+ FoundAndQueued , // Solution found and saved to local queue
158+ #[ allow( dead_code) ] // The submitter thread produces this result conceptually when processing a queue item, but the miner never constructs it.
149159 AlreadySolved , // The solution was successfully submitted by someone else
150160 MiningFailed , // General mining or submission error (e.g., hash not found, transient API error)
151161}
@@ -154,6 +164,8 @@ pub enum MiningResult {
154164pub const FILE_NAME_CHALLENGE : & str = "challenge.json" ;
155165pub const FILE_NAME_RECEIPT : & str = "receipt.json" ;
156166pub const FILE_NAME_DONATION : & str = "donation.txt" ;
167+ // Removed: FILE_NAME_PENDING_SOLUTION is unused, path is derived from QUEUE_BASE_DIR
168+ pub const FILE_NAME_FOUND_SOLUTION : & str = "found.json" ; // (Crash recovery file)
157169
158170
159171#[ derive( Debug , Clone , Copy ) ]
@@ -224,31 +236,98 @@ impl<'a> DataDir<'a> {
224236 Ok ( ( ) )
225237 }
226238
227- pub fn save_receipt ( & self , base_dir : & str , challenge_id : & str , receipt : & serde_json:: Value , donation : & Option < String > ) -> Result < ( ) , String > {
239+ // Only saves the receipt, donation logic removed
240+ pub fn save_receipt ( & self , base_dir : & str , challenge_id : & str , receipt : & serde_json:: Value ) -> Result < ( ) , String > {
228241 let mut path = self . receipt_dir ( base_dir, challenge_id) ?;
229242 path. push ( FILE_NAME_RECEIPT ) ;
230243
231244 let receipt_json = receipt. to_string ( ) ;
232245
233- // FIX: Use explicit file handling and sync to guarantee persistence.
234246 let mut file = std:: fs:: File :: create ( & path)
235247 . map_err ( |e| format ! ( "Could not create {}: {}" , FILE_NAME_RECEIPT , e) ) ?;
236248
237249 file. write_all ( receipt_json. as_bytes ( ) )
238250 . map_err ( |e| format ! ( "Could not write to {}: {}" , FILE_NAME_RECEIPT , e) ) ?;
239251
240- // CRITICAL: Force the OS to write the data to disk now.
241252 file. sync_all ( )
242253 . map_err ( |e| format ! ( "Could not sync {}: {}" , FILE_NAME_RECEIPT , e) ) ?;
243254
244- if let Some ( donation_id) = donation {
245- path. pop ( ) ;
246- path. push ( FILE_NAME_DONATION ) ;
255+ // Donation file logic is intentionally removed here.
247256
248- std:: fs:: write ( & path, donation_id. as_bytes ( ) )
249- . map_err ( |e| format ! ( "Could not write {}: {}" , FILE_NAME_DONATION , e) ) ?;
250- }
257+ Ok ( ( ) )
258+ }
259+
260+ // Saves a PendingSolution to the queue directory
261+ pub fn save_pending_solution ( & self , base_dir : & str , solution : & PendingSolution ) -> Result < ( ) , String > {
262+ let mut path = PathBuf :: from ( base_dir) ;
263+ path. push ( "pending_submissions" ) ; // Dedicated directory for the queue
264+ std:: fs:: create_dir_all ( & path)
265+ . map_err ( |e| format ! ( "Could not create pending_submissions directory: {}" , e) ) ?;
266+
267+ // Use a unique file name based on challenge, address, and nonce
268+ path. push ( format ! ( "{}_{}_{}.json" , solution. address, solution. challenge_id, solution. nonce) ) ;
269+
270+ let solution_json = serde_json:: to_string ( solution)
271+ . map_err ( |e| format ! ( "Could not serialize pending solution: {}" , e) ) ?;
272+
273+ std:: fs:: write ( & path, solution_json)
274+ . map_err ( |e| format ! ( "Could not write pending solution file: {}" , e) ) ?;
251275
252276 Ok ( ( ) )
253277 }
278+
279+ // Saves the temporary file indicating a solution was found but not queued/submitted
280+ pub fn save_found_solution ( & self , base_dir : & str , challenge_id : & str , solution : & PendingSolution ) -> Result < ( ) , String > {
281+ let mut path = self . receipt_dir ( base_dir, challenge_id) ?; // Use receipt dir for local persistence
282+ path. push ( FILE_NAME_FOUND_SOLUTION ) ;
283+
284+ let solution_json = serde_json:: to_string ( solution)
285+ . map_err ( |e| format ! ( "Could not serialize found solution: {}" , e) ) ?;
286+
287+ // Use explicit file handling to guarantee persistence before returning success
288+ let mut file = std:: fs:: File :: create ( & path)
289+ . map_err ( |e| format ! ( "Could not create {}: {}" , FILE_NAME_FOUND_SOLUTION , e) ) ?;
290+
291+ file. write_all ( solution_json. as_bytes ( ) )
292+ . map_err ( |e| format ! ( "Could not write to {}: {}" , FILE_NAME_FOUND_SOLUTION , e) ) ?;
293+
294+ file. sync_all ( )
295+ . map_err ( |e| format ! ( "Could not sync {}: {}" , FILE_NAME_FOUND_SOLUTION , e) ) ?;
296+
297+ Ok ( ( ) )
298+ }
299+
300+ // Removes the temporary file
301+ pub fn delete_found_solution ( & self , base_dir : & str , challenge_id : & str ) -> Result < ( ) , String > {
302+ let mut path = self . receipt_dir ( base_dir, challenge_id) ?;
303+ path. push ( FILE_NAME_FOUND_SOLUTION ) ;
304+ if path. exists ( ) {
305+ std:: fs:: remove_file ( & path)
306+ . map_err ( |e| format ! ( "Failed to delete {}: {}" , FILE_NAME_FOUND_SOLUTION , e) ) ?;
307+ }
308+ Ok ( ( ) )
309+ }
310+ }
311+
312+ // Checks if an address/challenge has a pending submission file in the queue dir
313+ pub fn is_solution_pending_in_queue ( base_dir : & str , address : & str , challenge_id : & str ) -> Result < bool , String > {
314+ use std:: path:: PathBuf ;
315+
316+ let mut path = PathBuf :: from ( base_dir) ;
317+ path. push ( "pending_submissions" ) ;
318+
319+ // Scan for any file that matches the address and challenge ID prefix
320+ if let Ok ( entries) = std:: fs:: read_dir ( & path) {
321+ for entry in entries. filter_map ( |e| e. ok ( ) ) {
322+ if let Some ( filename) = entry. file_name ( ) . to_str ( ) {
323+ // Check if the filename starts with the required prefix and is a JSON file
324+ // The filename format is: address_challenge_id_nonce.json
325+ if filename. starts_with ( & format ! ( "{}_{}_" , address, challenge_id) ) && filename. ends_with ( ".json" ) {
326+ return Ok ( true ) ;
327+ }
328+ }
329+ }
330+ }
331+ // If the directory doesn't exist or no matching file is found
332+ Ok ( false )
254333}
0 commit comments