Module chain

Source
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§

error
Types for chain scanning error handling.
testingtest-dependencies

Structs§

ChainState
The final note commitment tree state for each shielded pool, as of a particular block height.
CommitmentTreeRoot
A struct containing metadata about a subtree root of the note commitment tree.
ScanSummary
Metadata about modifications to the wallet state made in the course of scanning a set of blocks.

Traits§

BlockCache
BlockCache is a trait that extends BlockSource and defines methods for managing a cache of compact blocks.
BlockSource
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.