diff --git a/zebra-scan/src/scan.rs b/zebra-scan/src/scan.rs index 158fadadd..754daaa01 100644 --- a/zebra-scan/src/scan.rs +++ b/zebra-scan/src/scan.rs @@ -63,6 +63,19 @@ pub async fn start( storage: Storage, ) -> Result<(), Report> { let network = storage.network(); + let sapling_activation_height = storage.min_sapling_birthday_height(); + + // Do not scan and notify if we are below sapling activation height. + loop { + let tip_height = tip_height(state.clone()).await?; + if tip_height < sapling_activation_height { + info!("scanner is waiting for sapling activation. Current tip: {}, Sapling activation: {}", tip_height.0, sapling_activation_height.0); + tokio::time::sleep(CHECK_INTERVAL).await; + continue; + } + break; + } + // Read keys from the storage on disk, which can block async execution. let key_storage = storage.clone(); let key_heights = tokio::task::spawn_blocking(move || key_storage.sapling_keys_last_heights()) @@ -70,7 +83,7 @@ pub async fn start( .await; let key_heights = Arc::new(key_heights); - let mut height = get_min_height(&key_heights).unwrap_or(storage.min_sapling_birthday_height()); + let mut height = get_min_height(&key_heights).unwrap_or(sapling_activation_height); // Parse and convert keys once, then use them to scan all blocks. // There is some cryptography here, but it should be fast even with thousands of keys. @@ -149,7 +162,7 @@ pub async fn scan_height_and_store_results( let block = match block { zebra_state::Response::Block(Some(block)) => block, zebra_state::Response::Block(None) => return Ok(None), - _ => unreachable!("unmatched response to a state::Tip request"), + _ => unreachable!("unmatched response to a state::Block request"), }; // Scan it with all the keys. @@ -400,3 +413,20 @@ fn scanned_block_to_db_result( fn get_min_height(map: &HashMap) -> Option { map.values().cloned().min() } + +/// Get tip height or return genesis block height if no tip is available. +async fn tip_height(mut state: State) -> Result { + let tip = state + .ready() + .await + .map_err(|e| eyre!(e))? + .call(zebra_state::Request::Tip) + .await + .map_err(|e| eyre!(e))?; + + match tip { + zebra_state::Response::Tip(Some((height, _hash))) => Ok(height), + zebra_state::Response::Tip(None) => Ok(Height(0)), + _ => unreachable!("unmatched response to a state::Tip request"), + } +} diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 787c8097f..93ab9f3cd 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -2820,39 +2820,36 @@ async fn fully_synced_rpc_z_getsubtreesbyindex_snapshot_test() -> Result<()> { #[cfg(feature = "shielded-scan")] fn scan_task_starts() -> Result<()> { use indexmap::IndexMap; - use zebra_scan::tests::ZECPAGES_SAPLING_VIEWING_KEY; let _init_guard = zebra_test::init(); + let test_type = TestType::LaunchWithEmptyState { + launches_lightwalletd: false, + }; let mut config = default_test_config(Mainnet)?; let mut keys = IndexMap::new(); keys.insert(ZECPAGES_SAPLING_VIEWING_KEY.to_string(), 1); config.shielded_scan.sapling_keys_to_scan = keys; - let testdir = testdir()?.with_config(&mut config)?; - let testdir = &testdir; + // Start zebra with the config. + let mut zebrad = testdir()? + .with_exact_config(&config)? + .spawn_child(args!["start"])? + .with_timeout(test_type.zebrad_timeout()); - let mut child = testdir.spawn_child(args!["start"])?; + // Check scanner was started. + zebrad.expect_stdout_line_matches("loaded Zebra scanner cache")?; - // Run the program and kill it after the scanner starts and the first scanning is done. - std::thread::sleep(LAUNCH_DELAY * 2); - child.kill(false)?; + // Look for 2 scanner notices indicating we are below sapling activation. + zebrad.expect_stdout_line_matches("scanner is waiting for sapling activation. Current tip: [0-9]{1,4}, Sapling activation: 419200")?; + zebrad.expect_stdout_line_matches("scanner is waiting for sapling activation. Current tip: [0-9]{1,4}, Sapling activation: 419200")?; + + // Kill the node. + zebrad.kill(false)?; // Check that scan task started and the first scanning is done. - let output = child.wait_with_output()?; - - output.stdout_line_contains("spawning shielded scanner with configured viewing keys")?; - /* - TODO: these lines only happen when we reach sapling activation height - output.stdout_line_contains("Scanning the blockchain: now at block")?; - output.stdout_line_contains( - format!( - "Scanning the blockchain for key 0, started at block", - ) - .as_str(), - )?; - */ + let output = zebrad.wait_with_output()?; // Make sure the command was killed output.assert_was_killed()?; @@ -2878,7 +2875,7 @@ fn scan_task_starts() -> Result<()> { #[cfg(feature = "shielded-scan")] fn scan_start_where_left() -> Result<()> { use indexmap::IndexMap; - use zebra_scan::storage::db::SCANNER_DATABASE_KIND; + use zebra_scan::{storage::db::SCANNER_DATABASE_KIND, tests::ZECPAGES_SAPLING_VIEWING_KEY}; let _init_guard = zebra_test::init(); @@ -2886,10 +2883,9 @@ fn scan_start_where_left() -> Result<()> { let test_type = TestType::UpdateZebraCachedStateNoRpc; if let Some(cache_dir) = test_type.zebrad_state_path("scan test") { // Add a key to the config - const ZECPAGES_VIEWING_KEY: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz"; let mut config = default_test_config(Mainnet)?; let mut keys = IndexMap::new(); - keys.insert(ZECPAGES_VIEWING_KEY.to_string(), 1); + keys.insert(ZECPAGES_SAPLING_VIEWING_KEY.to_string(), 1); config.shielded_scan.sapling_keys_to_scan = keys; // Add the cache dir to shielded scan, make it the same as the zebrad cache state.