Expand description
Tools for blockchain validation & scanning
§Examples
use zcash_primitives::{
consensus::{BlockHeight, Network, Parameters},
};
use zcash_client_backend::{
data_api::{
WalletRead, WalletWrite, WalletCommitmentTrees,
chain::{
BlockSource,
CommitmentTreeRoot,
error::Error,
scan_cached_blocks,
testing as chain_testing,
},
scanning::ScanPriority,
testing,
},
};
let network = Network::TestNetwork;
let block_source = chain_testing::MockBlockSource;
let mut wallet_db = testing::MockWalletDb::new(Network::TestNetwork);
// 1) Download note commitment tree data from lightwalletd
let roots: Vec<CommitmentTreeRoot<sapling::Node>> = unimplemented!();
// 2) Pass the commitment tree data to the database.
wallet_db.put_sapling_subtree_roots(0, &roots).unwrap();
// 3) Download chain tip metadata from lightwalletd
let tip_height: BlockHeight = unimplemented!();
// 4) Notify the wallet of the updated chain tip.
wallet_db.update_chain_tip(tip_height).map_err(Error::Wallet)?;
// 5) Get the suggested scan ranges from the wallet database
let mut scan_ranges = wallet_db.suggest_scan_ranges().map_err(Error::Wallet)?;
// 6) Run the following loop until the wallet's view of the chain tip as of the previous wallet
// session is valid.
loop {
// If there is a range of blocks that needs to be verified, it will always be returned as
// the first element of the vector of suggested ranges.
match scan_ranges.first() {
Some(scan_range) if scan_range.priority() == ScanPriority::Verify => {
// Download the chain state for the block prior to the start of the range you want
// to scan.
let chain_state = unimplemented!("get_chain_state(scan_range.block_range().start - 1)?;");
// Download the blocks in `scan_range` into the block source, overwriting any
// existing blocks in this range.
unimplemented!("cache_blocks(scan_range)?;");
// Scan the downloaded blocks
let scan_result = scan_cached_blocks(
&network,
&block_source,
&mut wallet_db,
scan_range.block_range().start,
chain_state,
scan_range.len()
);
// Check for scanning errors that indicate that the wallet's chain tip is out of
// sync with blockchain history.
match scan_result {
Ok(_) => {
// At this point, the cache and scanned data are locally consistent (though
// not necessarily consistent with the latest chain tip - this would be
// discovered the next time this codepath is executed after new blocks are
// received) so we can break out of the loop.
break;
}
Err(Error::Scan(err)) if err.is_continuity_error() => {
// Pick a height to rewind to, which must be at least one block before
// the height at which the error occurred, but may be an earlier height
// determined based on heuristics such as the platform, available bandwidth,
// size of recent CompactBlocks, etc.
let rewind_height = err.at_height().saturating_sub(10);
// Rewind to the chosen height.
wallet_db.truncate_to_height(rewind_height).map_err(Error::Wallet)?;
// Delete cached blocks from rewind_height onwards.
//
// This does imply that assumed-valid blocks will be re-downloaded, but it
// is also possible that in the intervening time, a chain reorg has
// occurred that orphaned some of those blocks.
unimplemented!();
}
Err(other) => {
// Handle or return other errors
}
}
// In case we updated the suggested scan ranges, now re-request.
scan_ranges = wallet_db.suggest_scan_ranges().map_err(Error::Wallet)?;
}
_ => {
// Nothing to verify; break out of the loop
break;
}
}
}
// 7) Loop over the remaining suggested scan ranges, retrieving the requested data and calling
// `scan_cached_blocks` on each range. Periodically, or if a continuity error is
// encountered, this process should be repeated starting at step (3).
let scan_ranges = wallet_db.suggest_scan_ranges().map_err(Error::Wallet)?;
for scan_range in scan_ranges {
// Download the chain state for the block prior to the start of the range you want
// to scan.
let chain_state = unimplemented!("get_chain_state(scan_range.block_range().start - 1)?;");
// 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. 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!("cache_blocks(scan_range)?;");
// Scan the downloaded blocks.
let scan_result = scan_cached_blocks(
&network,
&block_source,
&mut wallet_db,
scan_range.block_range().start,
chain_state,
scan_range.len()
)?;
// Handle scan errors, etc.
}
Modules§
Structs§
- Chain
State - The final note commitment tree state for each shielded pool, as of a particular block height.
- Commitment
Tree Root - A struct containing metadata about a subtree root of the note commitment tree.
- Scan
Summary - Metadata about modifications to the wallet state made in the course of scanning a set of blocks.
Traits§
- Block
Cache BlockCache
is a trait that extendsBlockSource
and defines methods for managing a cache of compact blocks.- Block
Source - This trait provides sequential access to raw blockchain data via a callback-oriented API.
Functions§
- scan_
cached_ blocks - Scans at most
limit
blocks from the provided block source for in order to find transactions received by the accounts tracked in the provided wallet database.