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
This commit is contained in:
parent
461c5aa680
commit
732ee01443
|
@ -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" }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<SaplingScanningKey, Option<Height>>,
|
||||
|
||||
/// The sapling key and the related transaction id.
|
||||
sapling_results: HashMap<SaplingScanningKey, Vec<Hash>>,
|
||||
}
|
||||
|
||||
#[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<Height>) {
|
||||
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<Hash> {
|
||||
self.sapling_results.get(key).cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Get all keys and their birthdays.
|
||||
pub fn get_sapling_keys(&self) -> HashMap<String, Option<Height>> {
|
||||
self.sapling_keys.clone()
|
||||
}
|
||||
}
|
|
@ -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<Block>, chain_metadata: ChainMetadata) -> CompactBlock {
|
||||
CompactBlock {
|
||||
|
|
Loading…
Reference in New Issue