Documentation updates, fixes, and cleanups

Co-authored-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
Jack Grigg 2023-07-13 18:25:56 +01:00
parent 2a98f94f05
commit a87dca00e2
10 changed files with 54 additions and 42 deletions

View File

@ -85,6 +85,8 @@ and this library adheres to Rust's notion of
feature flag, has been modified by the addition of a `sapling_tree` property.
- `wallet::input_selection`:
- `Proposal::target_height` (use `Proposal::min_target_height` instead).
- `zcash_client_backend::data_api::chain::validate_chain` (logic merged into
`chain::scan_cached_blocks`.
- `zcash_client_backend::data_api::chain::error::{ChainError, Cause}` have been
replaced by `zcash_client_backend::scanning::ScanError`
- `zcash_client_backend::wallet::WalletSaplingOutput::{witness, witness_mut}`

View File

@ -89,6 +89,8 @@ pub trait WalletRead {
/// tree size information for each block; or else the scan is likely to fail if notes belonging
/// to the wallet are detected.
///
/// The returned range(s) may include block heights beyond the current chain tip.
///
/// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock
fn suggest_scan_ranges(&self) -> Result<Vec<ScanRange>, Self::Error>;
@ -502,8 +504,8 @@ pub trait WalletWrite: WalletRead {
/// Updates the wallet's view of the blockchain.
///
/// This method is used to provide the wallet with information about the state of the
/// blockchain, and detect any previously scanned that needs to be re-validated before
/// proceeding with scanning. It should be called at wallet startup prior to calling
/// blockchain, and detect any previously scanned data that needs to be re-validated
/// before proceeding with scanning. It should be called at wallet startup prior to calling
/// [`WalletRead::suggest_scan_ranges`] in order to provide the wallet with the information it
/// needs to correctly prioritize scanning operations.
fn update_chain_tip(&mut self, tip_height: BlockHeight) -> Result<(), Self::Error>;

View File

@ -104,8 +104,7 @@
//! }
//! }
//!
//! // Truncation will have updated the suggested scan ranges, so we now
//! // re_request
//! // In case we updated the suggested scan ranges, now re-request.
//! scan_ranges = wallet_db.suggest_scan_ranges().map_err(Error::Wallet)?;
//! }
//! _ => {
@ -123,10 +122,13 @@
//! // Download the blocks in `scan_range` into the block source. While in this example this
//! // step is performed in-line, it's fine for the download of scan ranges to be asynchronous
//! // and for the scanner to process the downloaded ranges as they become available in a
//! // separate thread.
//! // separate thread. The scan ranges should also be broken down into smaller chunks as
//! // appropriate, and for ranges with priority `Historic` it can be useful to download and
//! // scan the range in reverse order (to discover more recent unspent notes sooner), or from
//! // the start and end of the range inwards.
//! unimplemented!();
//!
//! // Scan the downloaded blocks,
//! // Scan the downloaded blocks.
//! let scan_result = scan_cached_blocks(
//! &network,
//! &block_source,

View File

@ -10,13 +10,14 @@ pub enum ScanPriority {
Scanned,
/// Block ranges to be scanned to advance the fully-scanned height.
Historic,
/// Block ranges adjacent to wallet open heights.
/// Block ranges adjacent to heights at which the user opened the wallet.
OpenAdjacent,
/// Blocks that must be scanned to complete note commitment tree shards adjacent to found notes.
FoundNote,
/// Blocks that must be scanned to complete the latest note commitment tree shard.
ChainTip,
/// A previously-scanned range that must be verified has highest priority.
/// A previously scanned range that must be verified to check it is still in the
/// main chain, has highest priority.
Verify,
}

View File

@ -175,7 +175,7 @@ impl fmt::Display for ScanError {
at_height
),
BlockHeightDiscontinuity { prev_height, new_height } => {
write!(f, "Block height discontinuity at height {}; next height is : {}", prev_height, new_height)
write!(f, "Block height discontinuity at height {}; previous height was: {}", new_height, prev_height)
}
TreeSizeMismatch { protocol, at_height, given, computed } => {
write!(f, "The {:?} note commitment tree size provided by a compact block did not match the expected size at height {}; given {}, expected {}", protocol, at_height, given, computed)

View File

@ -26,10 +26,6 @@ pub mod migrations;
/// Starting at `from_height`, the `with_row` callback is invoked with each block retrieved from
/// the backing store. If the `limit` value provided is `None`, all blocks are traversed up to the
/// maximum height.
///
/// # Panics
///
/// Panics if the provided `limit` value exceeds the range of a u32
pub(crate) fn blockdb_with_blocks<F, DbErrT>(
block_source: &BlockDb,
from_height: Option<BlockHeight>,
@ -200,10 +196,6 @@ pub(crate) fn blockmetadb_find_block(
/// Starting at `from_height`, the `with_row` callback is invoked with each block retrieved from
/// the backing store. If the `limit` value provided is `None`, all blocks are traversed up to the
/// maximum height for which metadata is available.
///
/// # Panics
///
/// Panics if the provided `limit` value exceeds the range of a u32
#[cfg(feature = "unstable")]
pub(crate) fn fsblockdb_with_blocks<F, DbErrT>(
cache: &FsBlockDb,

View File

@ -748,7 +748,7 @@ pub(crate) fn truncate_to_height<P: consensus::Parameters>(
) -> Result<(), SqliteClientError> {
let sapling_activation_height = params
.activation_height(NetworkUpgrade::Sapling)
.expect("Sapling activation height mutst be available.");
.expect("Sapling activation height must be available.");
// Recall where we synced up to previously.
let last_scanned_height = conn.query_row("SELECT MAX(height) FROM blocks", [], |row| {

View File

@ -772,9 +772,9 @@ pub(crate) fn put_shard_roots<
return Ok(());
}
// We treat the cap as a DEPTH-SHARD_HEIGHT tree so that we can make a batch insertion of
// root data using `Position::from(start_index)` as the starting position and treating the
// roots as level-0 leaves.
// We treat the cap as a tree with `DEPTH - SHARD_HEIGHT` levels, so that we can make a
// batch insertion of root data using `Position::from(start_index)` as the starting position
// and treating the roots as level-0 leaves.
#[derive(Clone, Debug, PartialEq, Eq)]
struct LevelShifter<H, const SHARD_HEIGHT: u8>(H);
impl<H: Hashable, const SHARD_HEIGHT: u8> Hashable for LevelShifter<H, SHARD_HEIGHT> {

View File

@ -207,7 +207,8 @@ impl RusqliteMigration for Migration {
}
}
// Establish the scan queue & wallet history table
// Establish the scan queue & wallet history table.
// block_range_end is exclusive.
debug!("Creating table for scan queue");
transaction.execute_batch(
"CREATE TABLE scan_queue (

View File

@ -107,7 +107,8 @@ pub(crate) fn suggest_scan_ranges(
// This implements the dominance rule for range priority. If the inserted range's priority is
// `Verify`, this replaces any existing priority. Otherwise, if the current priority is
// `Scanned`, this overwrites any priority
// `Scanned`, it remains as `Scanned`; and if the new priority is `Scanned`, it
// overrides any existing priority.
fn dominance(current: &ScanPriority, inserted: &ScanPriority, insert: Insert) -> Dominance {
match (current.cmp(inserted), (current, inserted)) {
(Ordering::Equal, _) => Dominance::Equal,
@ -118,14 +119,25 @@ fn dominance(current: &ScanPriority, inserted: &ScanPriority, insert: Insert) ->
}
}
/// In the comments for each alternative, `()` represents the left range and `[]` represents the right range.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum RangeOrdering {
/// `( ) [ ]`
LeftFirstDisjoint,
/// `( [ ) ]`
LeftFirstOverlap,
/// `[ ( ) ]`
LeftContained,
/// ```text
/// ( )
/// [ ]
/// ```
Equal,
/// `( [ ] )`
RightContained,
/// `[ ( ] )`
RightFirstOverlap,
/// `[ ] ( )`
RightFirstDisjoint,
}
@ -307,10 +319,11 @@ impl SpanningTree {
match self {
SpanningTree::Leaf(cur) => Self::from_joined(insert(cur, to_insert)),
SpanningTree::Parent { span, left, right } => {
// TODO: this algorithm always preserves the existing partition point, and does not
// do any rebalancing or unification of ranges within the tree; `into_vec`
// performes such unification and the tree being unbalanced should be fine given
// the relatively small number of ranges we should ordinarily be concerned with.
// This algorithm always preserves the existing partition point, and does not do
// any rebalancing or unification of ranges within the tree. This should be okay
// because `into_vec` performs such unification, and the tree being unbalanced
// should be fine given the relatively small number of ranges we should ordinarily
// be concerned with.
use RangeOrdering::*;
match RangeOrdering::cmp(&span, to_insert.block_range()) {
LeftFirstDisjoint => {
@ -417,7 +430,7 @@ pub(crate) fn insert_queue_entries<'a>(
trace!("Inserting queue entry {}", entry);
if !entry.is_empty() {
stmt.execute(named_params![
":block_range_start": u32::from(entry.block_range().start) ,
":block_range_start": u32::from(entry.block_range().start),
":block_range_end": u32::from(entry.block_range().end),
":priority": priority_code(&entry.priority())
])?;
@ -459,7 +472,7 @@ pub(crate) fn replace_queue_entries(
":end": u32::from(query_range.end),
])?;
// Iterate over the ranges in the scan queue that overlaps the range that we have
// Iterate over the ranges in the scan queue that overlap the range that we have
// identified as needing to be fully scanned. For each such range add it to the
// spanning tree (these should all be nonoverlapping ranges, but we might coalesce
// some in the process).
@ -554,9 +567,9 @@ pub(crate) fn scan_complete<P: consensus::Parameters>(
.flatten())
};
// if no notes belonging to the wallet were found, so don't need to extend the scanning
// If no notes belonging to the wallet were found, we don't need to extend the scanning
// range suggestions to include the associated subtrees, and our bounds are just the
// scanned range
// scanned range.
subtree_bounds
.map(|(min_idx, max_idx)| {
let range_min = if *min_idx > 0 {
@ -617,13 +630,12 @@ pub(crate) fn update_chain_tip<P: consensus::Parameters>(
)?;
// Create a scanning range for the fragment of the last shard leading up to new tip.
// However, only do so if the start of the shard is at a stable height.
let shard_entry = shard_start_height
.filter(|h| h < &chain_end)
.map(|h| ScanRange::from_parts(h..chain_end, ScanPriority::ChainTip));
// Create scanning ranges to either validate potentially invalid blocks at the wallet's view
// of the chain tip,
// of the chain tip, or connect the prior tip to the new tip.
let tip_entry = block_height_extrema(conn)?.map(|(_, prior_tip)| {
// If we don't have shard metadata, this means we're doing linear scanning, so create a
// scan range from the prior tip to the current tip with `Historic` priority.
@ -634,7 +646,7 @@ pub(crate) fn update_chain_tip<P: consensus::Parameters>(
// and not subject to being reorg'ed.
let stable_height = new_tip.saturating_sub(PRUNING_DEPTH);
// if the wallet's prior tip is above the stable height, prioritize the range between
// If the wallet's prior tip is above the stable height, prioritize the range between
// it and the new tip as `ChainTip`. Otherwise, prioritize the `VALIDATION_DEPTH`
// blocks above the wallet's prior tip as `Verify`. Since `scan_cached_blocks`
// retrieves the metadata for the block being connected to, the connectivity to the
@ -954,12 +966,12 @@ mod tests {
let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap();
init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap();
// Add an account to the wallet
// Add an account to the wallet.
let (dfvk, _taddr) = init_test_accounts_table(&mut db_data);
assert_matches!(
// in the following, we don't care what the root hashes are, they just need to be
// distinct
// In the following, we don't care what the root hashes are, they just need to be
// distinct.
db_data.put_sapling_subtree_roots(
0,
&[
@ -1021,7 +1033,7 @@ mod tests {
Ok(())
);
// Verify the that adjacent range needed to make the note spendable has been prioritized
// Verify the that adjacent range needed to make the note spendable has been prioritized.
let sap_active = u32::from(sapling_activation_height());
assert_matches!(
db_data.suggest_scan_ranges(),
@ -1030,7 +1042,7 @@ mod tests {
]
);
// Check that the scanned range has been properly persisted
// Check that the scanned range has been properly persisted.
assert_matches!(
suggest_scan_ranges(&db_data.conn, Scanned),
Ok(scan_ranges) if scan_ranges == vec![
@ -1039,8 +1051,8 @@ mod tests {
]
);
// simulate the wallet going offline for a bit, update the chain tip to 30 blocks in the
// future
// Simulate the wallet going offline for a bit, update the chain tip to 20 blocks in the
// future.
assert_matches!(
db_data.update_chain_tip(sapling_activation_height() + 340),
Ok(())
@ -1056,7 +1068,7 @@ mod tests {
]
);
// Now simulate a jump ahead more than 100 blocks
// Now simulate a jump ahead more than 100 blocks.
assert_matches!(
db_data.update_chain_tip(sapling_activation_height() + 450),
Ok(())