diff --git a/Cargo.lock b/Cargo.lock index 723a02425..23c8e214f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9122,6 +9122,7 @@ dependencies = [ "pepper-sync", "rustls 0.23.35", "rustyline", + "serde_json", "shellwords", "tokio", "tracing-subscriber", diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 7413859df..a95ec320d 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -61,6 +61,8 @@ const VERIFY_BLOCK_RANGE_SIZE: u32 = 10; pub struct SyncStatus { pub scan_ranges: Vec, pub sync_start_height: BlockHeight, + pub total_blocks: u32, + pub total_outputs: u32, pub session_blocks_scanned: u32, pub total_blocks_scanned: u32, pub percentage_session_blocks_scanned: f32, @@ -98,6 +100,11 @@ impl From for json::JsonValue { }) .collect(); + // Derive a simple boolean completeness flag from integer counts + let sync_complete = value.total_blocks_scanned >= value.total_blocks + && (value.total_sapling_outputs_scanned + value.total_orchard_outputs_scanned) + >= value.total_outputs; + json::object! { "scan_ranges" => scan_ranges, "sync_start_height" => u32::from(value.sync_start_height), @@ -111,6 +118,7 @@ impl From for json::JsonValue { "total_orchard_outputs_scanned" => value.total_orchard_outputs_scanned, "percentage_session_outputs_scanned" => value.percentage_session_outputs_scanned, "percentage_total_outputs_scanned" => value.percentage_total_outputs_scanned, + "sync_complete" => sync_complete, } } } @@ -598,6 +606,8 @@ where return Ok(SyncStatus { scan_ranges: sync_state.scan_ranges.clone(), sync_start_height: 0.into(), + total_blocks: 0, + total_outputs: 0, session_blocks_scanned: 0, total_blocks_scanned: 0, percentage_session_blocks_scanned: 0.0, @@ -671,6 +681,8 @@ where Ok(SyncStatus { scan_ranges: sync_state.scan_ranges.clone(), sync_start_height: sync_state.initial_sync_state.sync_start_height, + total_blocks, + total_outputs, session_blocks_scanned, total_blocks_scanned, percentage_session_blocks_scanned, diff --git a/zingo-cli/Cargo.toml b/zingo-cli/Cargo.toml index 21e210d8c..3a2020f0b 100644 --- a/zingo-cli/Cargo.toml +++ b/zingo-cli/Cargo.toml @@ -8,6 +8,7 @@ regtest = [ "zingolib/regtest", "zingo_common_components/for_test" ] [dependencies] pepper-sync = { workspace = true } +serde_json = { workspace = true } zingo_common_components = { workspace = true, optional = true } zingolib = { workspace = true } diff --git a/zingo-cli/src/lib.rs b/zingo-cli/src/lib.rs index 21da59c3c..2c1f129ad 100644 --- a/zingo-cli/src/lib.rs +++ b/zingo-cli/src/lib.rs @@ -516,37 +516,44 @@ fn dispatch_command_or_start_interactive(cli_config: &ConfigTemplate) { if cli_config.command.is_none() { start_interactive(command_transmitter, resp_receiver); } else { - // Optionally wait for background sync to finish before executing command + // Optionally wait for background sync to finish before executing command. + // Retry requesting status until the channel closes or the percentage field + // indicates completion. if cli_config.sync && cli_config.waitsync { use std::{thread, time::Duration}; + loop { - // Poll sync task status + // Request machine-readable sync status. command_transmitter - .send(("sync".to_string(), vec!["poll".to_string()])) + .send(("sync".to_string(), vec!["status".to_string()])) .unwrap(); + match resp_receiver.recv() { Ok(resp) => { - if resp.starts_with("Error:") { - eprintln!( - "Sync error while waiting: {resp}\nProceeding to execute the command." - ); - break; - } else if resp.starts_with("Sync completed succesfully:") { - // Sync finished; proceed - break; - } else if resp == "Sync task has not been launched." { - // Try to launch sync and continue waiting - command_transmitter - .send(("sync".to_string(), vec!["run".to_string()])) - .unwrap(); - let _ = resp_receiver.recv(); - thread::sleep(Duration::from_millis(500)); - } else { - // Not ready yet - thread::sleep(Duration::from_millis(500)); + // Parse JSON and inspect the numeric completion field. + match serde_json::from_str::(&resp) { + Ok(json_val) => { + // if sync is complete, stop waiting + if let Some(true) = + json_val.get("sync_complete").and_then(|v| v.as_bool()) + { + break; + } + + // Not complete yet; wait a short interval before re-checking. + thread::sleep(Duration::from_millis(500)); + continue; + } + Err(_) => { + // Parse error + break; + } } } - Err(_) => break, + Err(_) => { + // Channel closed; stop waiting. + break; + } } } }