Skip to content

Commit

Permalink
feat(scanner): Don't scan and log if we are below sapling height (#8121)
Browse files Browse the repository at this point in the history
* do not scan and notify if we are below sapling height

* separate tip logic check into its own loop
  • Loading branch information
oxarbitrage authored Dec 21, 2023
1 parent 141ea89 commit 085bfdc
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 25 deletions.
34 changes: 32 additions & 2 deletions zebra-scan/src/scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,27 @@ 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())
.wait_for_panics()
.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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -400,3 +413,20 @@ fn scanned_block_to_db_result<Nf>(
fn get_min_height(map: &HashMap<String, Height>) -> Option<Height> {
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<Height, Report> {
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"),
}
}
42 changes: 19 additions & 23 deletions zebrad/tests/acceptance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")?;

// Check that scan task started and the first scanning is done.
let output = child.wait_with_output()?;
// Kill the node.
zebrad.kill(false)?;

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(),
)?;
*/
// Check that scan task started and the first scanning is done.
let output = zebrad.wait_with_output()?;

// Make sure the command was killed
output.assert_was_killed()?;
Expand All @@ -2878,18 +2875,17 @@ 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();

// use `UpdateZebraCachedStateNoRpc` as the test type to make sure a zebrad cache state is available.
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.
Expand Down

0 comments on commit 085bfdc

Please sign in to comment.