From 65420f59df6e554f016ee4c100274ee009c4c993 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 6 Sep 2023 08:47:51 -0600 Subject: [PATCH] zcash_client_backend: Add `WalletRead::block_max_scanned` Fixes #941 --- zcash_client_backend/CHANGELOG.md | 6 +++-- zcash_client_backend/src/data_api.rs | 11 ++++++++ zcash_client_sqlite/src/lib.rs | 4 +++ zcash_client_sqlite/src/wallet.rs | 31 ++++++++++++++++++++--- zcash_client_sqlite/src/wallet/sapling.rs | 9 ++++++- 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/zcash_client_backend/CHANGELOG.md b/zcash_client_backend/CHANGELOG.md index 2859d2824..e81dbab77 100644 --- a/zcash_client_backend/CHANGELOG.md +++ b/zcash_client_backend/CHANGELOG.md @@ -34,8 +34,10 @@ and this library adheres to Rust's notion of - `ShieldedProtocol` - `WalletCommitmentTrees` - `WalletSummary` - - `WalletRead::{chain_height, block_metadata, block_fully_scanned, suggest_scan_ranges, - get_wallet_birthday, get_account_birthday, get_wallet_summary}` + - `WalletRead::{ + chain_height, block_metadata, block_max_scanned, block_fully_scanned, + suggest_scan_ranges, get_wallet_birthday, get_account_birthday, get_wallet_summary + }` - `WalletWrite::{put_blocks, update_chain_tip}` - `chain::CommitmentTreeRoot` - `scanning` A new module containing types required for `suggest_scan_ranges` diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index d42b0da95..d65cf718a 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -233,6 +233,13 @@ pub trait WalletRead { /// block. fn block_fully_scanned(&self) -> Result, Self::Error>; + /// Returns block metadata for the maximum height that the wallet has scanned. + /// + /// If the wallet is fully synced, this will be equivalent to `block_fully_scanned`; + /// otherwise the maximal scanned height is likely to be greater than the fully scanned height + /// due to the fact that out-of-order scanning can leave gaps. + fn block_max_scanned(&self) -> Result, Self::Error>; + /// Returns a vector of suggested scan ranges based upon the current wallet state. /// /// This method should only be used in cases where the [`CompactBlock`] data that will be made @@ -945,6 +952,10 @@ pub mod testing { Ok(None) } + fn block_max_scanned(&self) -> Result, Self::Error> { + Ok(None) + } + fn suggest_scan_ranges(&self) -> Result, Self::Error> { Ok(vec![]) } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 670f5f97b..22f0ff8c6 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -180,6 +180,10 @@ impl, P: consensus::Parameters> WalletRead for W wallet::block_fully_scanned(self.conn.borrow()) } + fn block_max_scanned(&self) -> Result, Self::Error> { + wallet::block_max_scanned(self.conn.borrow()) + } + fn suggest_scan_ranges(&self) -> Result, Self::Error> { wallet::scanning::suggest_scan_ranges(self.conn.borrow(), ScanPriority::Historic) .map_err(SqliteClientError::from) diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 5f4faedbb..97460ede8 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -1086,6 +1086,33 @@ pub(crate) fn block_fully_scanned( } } +pub(crate) fn block_max_scanned( + conn: &rusqlite::Connection, +) -> Result, SqliteClientError> { + conn.query_row( + "SELECT blocks.height, hash, sapling_commitment_tree_size, sapling_tree + FROM blocks + JOIN (SELECT MAX(height) AS height FROM blocks) blocks_max + ON blocks.height = blocks_max.height", + [], + |row| { + let height: u32 = row.get(0)?; + let block_hash: Vec = row.get(1)?; + let sapling_tree_size: Option = row.get(2)?; + let sapling_tree: Vec = row.get(3)?; + Ok(( + BlockHeight::from(height), + block_hash, + sapling_tree_size, + sapling_tree, + )) + }, + ) + .optional() + .map_err(SqliteClientError::from) + .and_then(|meta_row| meta_row.map(parse_block_metadata).transpose()) +} + /// Returns the block height at which the specified transaction was mined, /// if any. pub(crate) fn get_tx_height( @@ -1892,8 +1919,6 @@ pub(crate) fn prune_nullifier_map( mod tests { use std::num::NonZeroU32; - use zcash_primitives::transaction::components::Amount; - use zcash_client_backend::data_api::{AccountBirthday, WalletRead}; use crate::{testing::TestBuilder, AccountId}; @@ -1906,7 +1931,7 @@ mod tests { }, zcash_primitives::{ consensus::BlockHeight, - transaction::components::{OutPoint, TxOut}, + transaction::components::{Amount, OutPoint, TxOut}, }, }; diff --git a/zcash_client_sqlite/src/wallet/sapling.rs b/zcash_client_sqlite/src/wallet/sapling.rs index d2a5271c1..867c02012 100644 --- a/zcash_client_sqlite/src/wallet/sapling.rs +++ b/zcash_client_sqlite/src/wallet/sapling.rs @@ -465,7 +465,7 @@ pub(crate) mod tests { use crate::{ error::SqliteClientError, testing::{AddressType, BlockCache, TestBuilder, TestState}, - wallet::commitment_tree, + wallet::{block_max_scanned, commitment_tree}, AccountId, NoteId, ReceivedNoteId, }; @@ -501,6 +501,13 @@ pub(crate) mod tests { // Verified balance matches total balance assert_eq!(st.get_total_balance(account), value); + assert_eq!( + block_max_scanned(&st.wallet().conn) + .unwrap() + .unwrap() + .block_height(), + h + ); let to_extsk = ExtendedSpendingKey::master(&[]); let to: RecipientAddress = to_extsk.default_address().1.into();