From 732ee01443241f481590669dfb2c29ce967c4b89 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 15 Nov 2023 20:06:23 -0300 Subject: [PATCH] feat(scanner): Add a very basic RAM database to store keys and scan results (#7942) * add a basic RAM database for the scanner * specify a crate version for zebra-chain dependency * add a type for sapling keys * rename everything to reflect sapling * change some storage methods --- zebra-scan/Cargo.toml | 2 +- zebra-scan/src/lib.rs | 2 ++ zebra-scan/src/storage.rs | 54 ++++++++++++++++++++++++++++++++ zebra-scan/src/tests.rs | 66 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 zebra-scan/src/storage.rs diff --git a/zebra-scan/Cargo.toml b/zebra-scan/Cargo.toml index bddd71545..cb1cbd77b 100644 --- a/zebra-scan/Cargo.toml +++ b/zebra-scan/Cargo.toml @@ -19,6 +19,7 @@ categories = ["cryptography::cryptocurrencies"] # Production features that activate extra dependencies, or extra features in dependencies [dependencies] +zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.31" } [dev-dependencies] @@ -35,5 +36,4 @@ group = "0.13.0" tokio = { version = "1.34.0", features = ["test-util"] } zebra-state = { path = "../zebra-state" } -zebra-chain = { path = "../zebra-chain" } zebra-test = { path = "../zebra-test" } diff --git a/zebra-scan/src/lib.rs b/zebra-scan/src/lib.rs index 47a569e8e..b469bb7de 100644 --- a/zebra-scan/src/lib.rs +++ b/zebra-scan/src/lib.rs @@ -4,5 +4,7 @@ #![doc(html_logo_url = "https://zfnd.org/wp-content/uploads/2022/03/zebra-icon.png")] #![doc(html_root_url = "https://docs.rs/zebra_scan")] +mod storage; + #[cfg(test)] mod tests; diff --git a/zebra-scan/src/storage.rs b/zebra-scan/src/storage.rs new file mode 100644 index 000000000..eac776f2b --- /dev/null +++ b/zebra-scan/src/storage.rs @@ -0,0 +1,54 @@ +//! Store viewing keys and results of the scan. + +use std::collections::HashMap; + +use zebra_chain::{block::Height, transaction::Hash}; + +/// The type used in Zebra to store Sapling scanning keys. +/// It can represent a full viewing key or an individual viewing key. +pub type SaplingScanningKey = String; + +/// Store key info and results of the scan. +#[allow(dead_code)] +pub struct Storage { + /// The sapling key and an optional birthday for it. + sapling_keys: HashMap>, + + /// The sapling key and the related transaction id. + sapling_results: HashMap>, +} + +#[allow(dead_code)] +impl Storage { + /// Create a new storage. + pub fn new() -> Self { + Self { + sapling_keys: HashMap::new(), + sapling_results: HashMap::new(), + } + } + + /// Add a sapling key to the storage. + pub fn add_sapling_key(&mut self, key: SaplingScanningKey, birthday: Option) { + self.sapling_keys.insert(key, birthday); + } + + /// Add a sapling result to the storage. + pub fn add_sapling_result(&mut self, key: SaplingScanningKey, txid: Hash) { + if let Some(results) = self.sapling_results.get_mut(&key) { + results.push(txid); + } else { + self.sapling_results.insert(key, vec![txid]); + } + } + + /// Get the results of a sapling key. + pub fn get_sapling_results(&self, key: &str) -> Vec { + self.sapling_results.get(key).cloned().unwrap_or_default() + } + + /// Get all keys and their birthdays. + pub fn get_sapling_keys(&self) -> HashMap> { + self.sapling_keys.clone() + } +} diff --git a/zebra-scan/src/tests.rs b/zebra-scan/src/tests.rs index ec792f445..85c27231d 100644 --- a/zebra-scan/src/tests.rs +++ b/zebra-scan/src/tests.rs @@ -39,7 +39,7 @@ use zebra_chain::{ block::Block, chain_tip::ChainTip, serialization::{ZcashDeserializeInto, ZcashSerialize}, - transaction::Transaction, + transaction::{Hash, Transaction}, }; /// Prove that Zebra blocks can be scanned using the `zcash_client_backend::scanning::scan_block` function: @@ -283,6 +283,70 @@ async fn scanning_zecpages_from_populated_zebra_state() -> Result<()> { Ok(()) } +/// In this test we generate a viewing key and manually add it to the database. Also we send results to the Storage database. +/// The purpose of this test is to check if our database and our scanning code are compatible. +#[tokio::test] +#[allow(deprecated)] +async fn scanning_fake_blocks_store_key_and_results() -> Result<()> { + // Generate a key + let account = AccountId::from(12); + let extsk = ExtendedSpendingKey::master(&[]); + // TODO: find out how to do it with `to_diversifiable_full_viewing_key` as `to_extended_full_viewing_key` is deprecated. + let extfvk = extsk.to_extended_full_viewing_key(); + let dfvk: DiversifiableFullViewingKey = extsk.to_diversifiable_full_viewing_key(); + let key_to_be_stored = + zcash_client_backend::encoding::encode_extended_full_viewing_key("zxviews", &extfvk); + + // Create a database + let mut s = crate::storage::Storage::new(); + + // Insert the generated key to the database + s.add_sapling_key(key_to_be_stored.clone(), None); + + // Check key was added + assert_eq!(s.get_sapling_keys().len(), 1); + assert_eq!(s.get_sapling_keys().get(&key_to_be_stored), Some(&None)); + + let vks: Vec<(&AccountId, &SaplingIvk)> = vec![]; + let nf = Nullifier([7; 32]); + + // Add key to fake block + let cb = fake_compact_block( + 1u32.into(), + BlockHash([0; 32]), + nf, + &dfvk, + 1, + false, + Some(0), + ); + + // Scan with our key + let res = scan_block( + &zcash_primitives::consensus::MainNetwork, + cb.clone(), + &vks[..], + &[(account, nf)], + None, + ) + .unwrap(); + + // Get transaction hash + let found_transaction = res.transactions()[0].txid.as_ref(); + let found_transaction_hash = Hash::from_bytes_in_display_order(found_transaction); + + // Add result to database + s.add_sapling_result(key_to_be_stored.clone(), found_transaction_hash); + + // Check the result was added + assert_eq!( + s.get_sapling_results(key_to_be_stored.as_str())[0], + found_transaction_hash + ); + + Ok(()) +} + /// Convert a zebra block and meta data into a compact block. fn block_to_compact(block: Arc, chain_metadata: ChainMetadata) -> CompactBlock { CompactBlock {