poc(scanner): add a populated state test for ZECpages viewing key (#7916)

* get started with the blockchain scanner poc

* rustfmt

* fix the tests

* Reads blocks from db

* Adds conversion functions

* scans blocks and counts transactions

* fix bug

* split into 2 tests

* add duplicated dependencies to deny.toml

* upgrade zebra-scanner version

* try removing ecc duplicated dependencies

* try fix deny.toml

* remove unintended paste from deny.toml

* remove duplicated code from the other test

* remove strict version of `zcash_primitives` crate

* change description

* remove feture

* remove tokio features

* change lib doc

* add more documentation

* change expect

* do not use default in compact block creation

* more docs

* add more checks to test

* remove zebra-consensus dependency

* move all deps to dev-deps

* change crate version

* rename crate to zebra-scan

* lock file

* add test for zecpages populated state

* scans all cached blocks for zecpages viewing key expecting Ok results.

* use test blocks

* fixes test

* fix expect messages

* Discard changes to Cargo.lock

* Discard changes to deny.toml

---------

Co-authored-by: arya2 <aryasolhi@gmail.com>
Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Alfredo Garcia 2023-11-08 20:46:47 -03:00 committed by GitHub
parent 2fad3573fd
commit e5e89ec549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 116 additions and 1 deletions

View File

@ -6,6 +6,8 @@
use std::sync::Arc;
use zcash_client_backend::{
data_api::BlockMetadata,
encoding::decode_extended_full_viewing_key,
proto::compact_formats::{
self as compact, ChainMetadata, CompactBlock, CompactSaplingOutput, CompactSaplingSpend,
CompactTx,
@ -16,7 +18,7 @@ use zcash_note_encryption::Domain;
use zcash_primitives::{
block::BlockHash,
consensus::{BlockHeight, Network},
constants::SPENDING_KEY_GENERATOR,
constants::{mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, SPENDING_KEY_GENERATOR},
memo::MemoBytes,
sapling::{
note_encryption::{sapling_note_encryption, SaplingDomain},
@ -168,6 +170,119 @@ async fn scanning_from_fake_generated_blocks() -> Result<()> {
Ok(())
}
/// Scan a populated state for the ZECpages viewing key.
/// This test is very similar to `scanning_from_populated_zebra_state` but with the ZECpages key.
/// There are no zechub transactions in the test data so we should get empty related transactions.
#[tokio::test]
async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> {
/// The extended Sapling viewing key of [ZECpages](https://zecpages.com/boardinfo)
const ZECPAGES_VIEWING_KEY: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz";
// Parse the key from ZECpages
let efvk = decode_extended_full_viewing_key(
HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
ZECPAGES_VIEWING_KEY,
)
.unwrap();
let account = AccountId::from(1);
// Build a vector of viewing keys `vks` to scan for.
let fvk = efvk.fvk;
let ivk = fvk.vk.ivk();
let vks: Vec<(&AccountId, &SaplingIvk)> = vec![(&account, &ivk)];
let network = zebra_chain::parameters::Network::Mainnet;
// Create a continuous chain of mainnet blocks from genesis
let blocks: Vec<Arc<Block>> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS
.iter()
.map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap())
.collect();
// Create a populated state service.
let (_state_service, read_only_state_service, latest_chain_tip, _chain_tip_change) =
zebra_state::populated_state(blocks.clone(), network).await;
let db = read_only_state_service.db();
// use the tip as starting height
let mut height = latest_chain_tip.best_tip_height().unwrap();
let mut transactions_found = 0;
let mut transactions_scanned = 0;
let mut blocks_scanned = 0;
while let Some(block) = db.block(height.into()) {
// zcash_client_backend doesn't support scanning the genesis block, but that's ok, because
// Sapling activates at height 419,200. So we'll never scan these blocks in production code.
let sapling_tree_size = if height.is_min() {
1
} else {
db.sapling_tree_by_hash_or_height(height.into())
.expect("each state block must have a sapling tree")
.count()
};
let orchard_tree_size = db
.orchard_tree_by_hash_or_height(height.into())
.expect("each state block must have a orchard tree")
.count();
let chain_metadata = ChainMetadata {
sapling_commitment_tree_size: sapling_tree_size
.try_into()
.expect("sapling position is limited to u32::MAX"),
orchard_commitment_tree_size: orchard_tree_size
.try_into()
.expect("orchard position is limited to u32::MAX"),
};
let block_metadata = if height.is_min() {
None
} else {
Some(BlockMetadata::from_parts(
height.previous()?.0.into(),
BlockHash(block.header.previous_block_hash.0),
db.sapling_tree_by_hash_or_height(block.header.previous_block_hash.into())
.expect("each state block must have a sapling tree")
.count()
.try_into()
.expect("sapling position is limited to u32::MAX"),
))
};
let compact_block = block_to_compact(block, chain_metadata);
let res = scan_block(
&zcash_primitives::consensus::MainNetwork,
compact_block.clone(),
&vks[..],
&[],
block_metadata.as_ref(),
)
.expect("scanning block for the ZECpages viewing key should work");
transactions_found += res.transactions().len();
transactions_scanned += compact_block.vtx.len();
blocks_scanned += 1;
// scan backwards
if height.is_min() {
break;
}
height = height.previous()?;
}
// make sure all blocks and transactions were scanned
assert_eq!(blocks_scanned, 11);
assert_eq!(transactions_scanned, 11);
// no relevant transactions should be found
assert_eq!(transactions_found, 0);
Ok(())
}
/// Convert a zebra block and meta data into a compact block.
fn block_to_compact(block: Arc<Block>, chain_metadata: ChainMetadata) -> CompactBlock {
CompactBlock {