From d7fee0eed7c67e4a89e16ca77446a432621dd926 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 28 Aug 2024 13:41:57 +0100 Subject: [PATCH] scan takes ufvk instead of scanning keys --- zingo-sync/src/keys.rs | 46 ++++--------------- zingo-sync/src/scan.rs | 9 ++-- zingo-sync/src/scan/compact_blocks.rs | 7 ++- zingo-sync/src/scan/task.rs | 9 ++-- zingo-sync/src/scan/transactions.rs | 65 ++++++++++++++++----------- zingo-sync/src/sync.rs | 20 ++++----- 6 files changed, 71 insertions(+), 85 deletions(-) diff --git a/zingo-sync/src/keys.rs b/zingo-sync/src/keys.rs index 7418f2dca..0e2538aa0 100644 --- a/zingo-sync/src/keys.rs +++ b/zingo-sync/src/keys.rs @@ -56,9 +56,6 @@ pub trait ScanningKeyOps { /// IVK-based implementations of this trait cannot successfully derive /// nullifiers, in which this function will always return `None`. fn nf(&self, note: &D::Note, note_position: Position) -> Option; - - /// Returns the outgtoing viewing key - fn ovk(&self) -> D::OutgoingViewingKey; } impl> ScanningKeyOps for &K { fn prepare(&self) -> D::IncomingViewingKey { @@ -76,21 +73,16 @@ impl> ScanningKeyOps for &K { fn nf(&self, note: &D::Note, note_position: Position) -> Option { (*self).nf(note, note_position) } - - fn ovk(&self) -> D::OutgoingViewingKey { - (*self).ovk() - } } -pub(crate) struct ScanningKey { +pub(crate) struct ScanningKey { key_id: KeyId, ivk: Ivk, nk: Option, - ovk: Ovk, } impl ScanningKeyOps - for ScanningKey + for ScanningKey { fn prepare(&self) -> sapling::note_encryption::PreparedIncomingViewingKey { sapling_crypto::note_encryption::PreparedIncomingViewingKey::new(&self.ivk) @@ -100,10 +92,6 @@ impl ScanningKeyOps self.nk.as_ref().map(|key| note.nf(key, position.into())) } - fn ovk(&self) -> ::OutgoingViewingKey { - self.ovk - } - fn account_id(&self) -> &zcash_primitives::zip32::AccountId { &self.key_id.account_id } @@ -114,7 +102,7 @@ impl ScanningKeyOps } impl ScanningKeyOps - for ScanningKey + for ScanningKey { fn prepare(&self) -> orchard::keys::PreparedIncomingViewingKey { orchard::keys::PreparedIncomingViewingKey::new(&self.ivk) @@ -128,10 +116,6 @@ impl ScanningKeyOps self.nk.as_ref().map(|key| note.nullifier(key)) } - fn ovk(&self) -> ::OutgoingViewingKey { - self.ovk.clone() - } - fn account_id(&self) -> &zcash_primitives::zip32::AccountId { &self.key_id.account_id } @@ -145,14 +129,8 @@ impl ScanningKeyOps #[derive(Getters)] #[getset(get = "pub(crate)")] pub(crate) struct ScanningKeys { - sapling: HashMap< - KeyId, - ScanningKey, - >, - orchard: HashMap< - KeyId, - ScanningKey, - >, + sapling: HashMap>, + orchard: HashMap>, } impl ScanningKeys { @@ -163,14 +141,10 @@ impl ScanningKeys { ) -> Self { #![allow(clippy::type_complexity)] - let mut sapling: HashMap< - KeyId, - ScanningKey, - > = HashMap::new(); - let mut orchard: HashMap< - KeyId, - ScanningKey, - > = HashMap::new(); + let mut sapling: HashMap> = + HashMap::new(); + let mut orchard: HashMap> = + HashMap::new(); for (account_id, ufvk) in ufvks { if let Some(dfvk) = ufvk.sapling() { @@ -182,7 +156,6 @@ impl ScanningKeys { key_id, ivk: dfvk.to_ivk(scope), nk: Some(dfvk.to_nk(scope)), - ovk: dfvk.to_ovk(scope), }, ); } @@ -197,7 +170,6 @@ impl ScanningKeys { key_id, ivk: fvk.to_ivk(scope), nk: Some(fvk.clone()), - ovk: fvk.to_ovk(scope), }, ); } diff --git a/zingo-sync/src/scan.rs b/zingo-sync/src/scan.rs index 8f342806a..b4c87e004 100644 --- a/zingo-sync/src/scan.rs +++ b/zingo-sync/src/scan.rs @@ -6,14 +6,15 @@ use std::{ use incrementalmerkletree::Position; use tokio::sync::mpsc; use zcash_client_backend::{data_api::scanning::ScanRange, proto::compact_formats::CompactBlock}; +use zcash_keys::keys::UnifiedFullViewingKey; use zcash_primitives::{ consensus::{BlockHeight, NetworkUpgrade, Parameters}, transaction::TxId, + zip32::AccountId, }; use crate::{ client::{self, FetchRequest}, - keys::ScanningKeys, primitives::{NullifierMap, OutputId, WalletBlock, WalletTransaction}, witness::ShardTreeData, }; @@ -139,7 +140,7 @@ impl DecryptedNoteData { pub(crate) async fn scan

( fetch_request_sender: mpsc::UnboundedSender, parameters: &P, - scanning_keys: &ScanningKeys, + ufvks: &HashMap, scan_range: ScanRange, previous_wallet_block: Option, ) -> Result @@ -165,7 +166,7 @@ where .unwrap(); let scan_data = - scan_compact_blocks(compact_blocks, parameters, scanning_keys, initial_scan_data).unwrap(); + scan_compact_blocks(compact_blocks, parameters, ufvks, initial_scan_data).unwrap(); let ScanData { nullifiers, @@ -178,7 +179,7 @@ where let wallet_transactions = scan_transactions( fetch_request_sender, parameters, - scanning_keys, + ufvks, relevant_txids, decrypted_note_data, &wallet_blocks, diff --git a/zingo-sync/src/scan/compact_blocks.rs b/zingo-sync/src/scan/compact_blocks.rs index 4e9721c57..6e6ab75d8 100644 --- a/zingo-sync/src/scan/compact_blocks.rs +++ b/zingo-sync/src/scan/compact_blocks.rs @@ -6,11 +6,13 @@ use sapling_crypto::{note_encryption::CompactOutputDescription, Node}; use zcash_client_backend::proto::compact_formats::{ CompactBlock, CompactOrchardAction, CompactSaplingOutput, CompactTx, }; +use zcash_keys::keys::UnifiedFullViewingKey; use zcash_note_encryption::Domain; use zcash_primitives::{ block::BlockHash, consensus::{BlockHeight, Parameters}, transaction::TxId, + zip32::AccountId, }; use crate::{ @@ -28,7 +30,7 @@ mod runners; pub(crate) fn scan_compact_blocks

( compact_blocks: Vec, parameters: &P, - scanning_keys: &ScanningKeys, + ufvks: &HashMap, initial_scan_data: InitialScanData, ) -> Result where @@ -36,7 +38,8 @@ where { check_continuity(&compact_blocks, initial_scan_data.previous_block.as_ref()).unwrap(); - let mut runners = trial_decrypt(parameters, scanning_keys, &compact_blocks).unwrap(); + let scanning_keys = ScanningKeys::from_account_ufvks(ufvks.clone()); + let mut runners = trial_decrypt(parameters, &scanning_keys, &compact_blocks).unwrap(); let mut wallet_blocks: BTreeMap = BTreeMap::new(); let mut nullifiers = NullifierMap::new(); diff --git a/zingo-sync/src/scan/task.rs b/zingo-sync/src/scan/task.rs index 4b0fa0998..9ec52d890 100644 --- a/zingo-sync/src/scan/task.rs +++ b/zingo-sync/src/scan/task.rs @@ -12,7 +12,7 @@ use zcash_client_backend::data_api::scanning::ScanRange; use zcash_keys::keys::UnifiedFullViewingKey; use zcash_primitives::{consensus::Parameters, zip32::AccountId}; -use crate::{client::FetchRequest, keys::ScanningKeys, primitives::WalletBlock}; +use crate::{client::FetchRequest, primitives::WalletBlock}; use super::{scan, ScanResults}; @@ -107,7 +107,7 @@ struct ScanWorker

{ scan_results_sender: mpsc::UnboundedSender<(ScanRange, ScanResults)>, fetch_request_sender: mpsc::UnboundedSender, parameters: P, - scanning_keys: ScanningKeys, + ufvks: HashMap, } impl

ScanWorker

@@ -121,14 +121,13 @@ where parameters: P, ufvks: HashMap, ) -> Self { - let scanning_keys = ScanningKeys::from_account_ufvks(ufvks); Self { is_scanning: Arc::new(AtomicBool::new(false)), scan_task_receiver, scan_results_sender, fetch_request_sender, parameters, - scanning_keys, + ufvks, } } @@ -139,7 +138,7 @@ where let scan_results = scan( self.fetch_request_sender.clone(), &self.parameters.clone(), - &self.scanning_keys, + &self.ufvks, scan_task.scan_range.clone(), scan_task.previous_wallet_block, ) diff --git a/zingo-sync/src/scan/transactions.rs b/zingo-sync/src/scan/transactions.rs index 60f6ff880..24d962e12 100644 --- a/zingo-sync/src/scan/transactions.rs +++ b/zingo-sync/src/scan/transactions.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use incrementalmerkletree::Position; use orchard::{ + keys::Scope, note_encryption::OrchardDomain, primitives::redpallas::{Signature, SpendAuth}, Action, @@ -12,18 +13,21 @@ use sapling_crypto::{ }; use tokio::sync::mpsc; -use zcash_keys::{address::UnifiedAddress, encoding::encode_payment_address}; +use zcash_keys::{ + address::UnifiedAddress, encoding::encode_payment_address, keys::UnifiedFullViewingKey, +}; use zcash_note_encryption::{BatchDomain, Domain, ShieldedOutput, ENC_CIPHERTEXT_SIZE}; use zcash_primitives::{ consensus::{BlockHeight, NetworkConstants, Parameters}, memo::Memo, transaction::{Transaction, TxId}, + zip32::AccountId, }; use zingo_memo::ParsedMemo; use crate::{ client::{self, FetchRequest}, - keys::{KeyId, ScanningKeyOps as _, ScanningKeys}, + keys::KeyId, primitives::{ OrchardNote, OutgoingNote, OutgoingOrchardNote, OutgoingSaplingNote, OutputId, SaplingNote, SyncOutgoingNotes, WalletBlock, WalletNote, WalletTransaction, @@ -62,7 +66,7 @@ impl ShieldedOutputExt for OutputDescription { pub(crate) async fn scan_transactions( fetch_request_sender: mpsc::UnboundedSender, parameters: &P, - scanning_keys: &ScanningKeys, + ufvks: &HashMap, relevant_txids: HashSet, decrypted_note_data: DecryptedNoteData, wallet_blocks: &BTreeMap, @@ -90,7 +94,7 @@ pub(crate) async fn scan_transactions( let wallet_transaction = scan_transaction( parameters, - scanning_keys, + ufvks, transaction, block_height, &decrypted_note_data, @@ -104,7 +108,7 @@ pub(crate) async fn scan_transactions( fn scan_transaction( parameters: &P, - scanning_keys: &ScanningKeys, + ufvks: &HashMap, transaction: Transaction, block_height: BlockHeight, decrypted_note_data: &DecryptedNoteData, @@ -120,6 +124,36 @@ fn scan_transaction( let mut outgoing_orchard_notes: Vec = Vec::new(); let mut encoded_memos = Vec::new(); + let mut sapling_ivks = Vec::new(); + let mut sapling_ovks = Vec::new(); + let mut orchard_ivks = Vec::new(); + let mut orchard_ovks = Vec::new(); + for (account_id, ufvk) in ufvks { + if let Some(dfvk) = ufvk.sapling() { + for scope in [Scope::External, Scope::Internal] { + let key_id = KeyId::from_parts(*account_id, scope); + sapling_ivks.push(( + key_id, + sapling_crypto::note_encryption::PreparedIncomingViewingKey::new( + &dfvk.to_ivk(scope), + ), + )); + sapling_ovks.push((key_id, dfvk.to_ovk(scope))); + } + } + + if let Some(fvk) = ufvk.orchard() { + for scope in [Scope::External, Scope::Internal] { + let key_id = KeyId::from_parts(*account_id, scope); + orchard_ivks.push(( + key_id, + orchard::keys::PreparedIncomingViewingKey::new(&fvk.to_ivk(scope)), + )); + orchard_ovks.push((key_id, fvk.to_ovk(scope))); + } + } + } + // TODO: scan transparent bundle if let Some(bundle) = transaction.sapling_bundle() { @@ -129,12 +163,6 @@ fn scan_transaction( .map(|output| (SaplingDomain::new(zip212_enforcement), output.clone())) .collect(); - let sapling_ivks: Vec<(KeyId, sapling_crypto::keys::PreparedIncomingViewingKey)> = - scanning_keys - .sapling() - .iter() - .map(|(key_id, key)| (*key_id, key.prepare())) - .collect(); scan_incoming_notes::< SaplingDomain, OutputDescription, @@ -149,11 +177,6 @@ fn scan_transaction( ) .unwrap(); - let sapling_ovks: Vec<(KeyId, sapling_crypto::keys::OutgoingViewingKey)> = scanning_keys - .sapling() - .iter() - .map(|(key_id, key)| (*key_id, key.ovk())) - .collect(); scan_outgoing_notes( &mut outgoing_sapling_notes, transaction.txid(), @@ -172,11 +195,6 @@ fn scan_transaction( .map(|action| (OrchardDomain::for_action(action), action.clone())) .collect(); - let orchard_ivks: Vec<(KeyId, orchard::keys::PreparedIncomingViewingKey)> = scanning_keys - .orchard() - .iter() - .map(|(key_id, key)| (*key_id, key.prepare())) - .collect(); scan_incoming_notes::< OrchardDomain, Action>, @@ -191,11 +209,6 @@ fn scan_transaction( ) .unwrap(); - let orchard_ovks: Vec<(KeyId, orchard::keys::OutgoingViewingKey)> = scanning_keys - .orchard() - .iter() - .map(|(key_id, key)| (*key_id, key.ovk())) - .collect(); scan_outgoing_notes( &mut outgoing_orchard_notes, transaction.txid(), diff --git a/zingo-sync/src/sync.rs b/zingo-sync/src/sync.rs index 6512f47b4..b9b3ea92d 100644 --- a/zingo-sync/src/sync.rs +++ b/zingo-sync/src/sync.rs @@ -1,13 +1,12 @@ //! Entrypoint for sync engine use std::cmp; -use std::collections::{BTreeMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::ops::Range; use std::time::Duration; use crate::client::fetch::fetch; use crate::client::{self, FetchRequest}; -use crate::keys::ScanningKeys; use crate::primitives::SyncState; use crate::scan::task::{ScanTask, Scanner}; use crate::scan::transactions::scan_transactions; @@ -19,11 +18,13 @@ use zcash_client_backend::{ data_api::scanning::{ScanPriority, ScanRange}, proto::service::compact_tx_streamer_client::CompactTxStreamerClient, }; +use zcash_keys::keys::UnifiedFullViewingKey; use zcash_primitives::consensus::{BlockHeight, NetworkUpgrade, Parameters}; use futures::future::try_join_all; use tokio::sync::mpsc; use zcash_primitives::transaction::TxId; +use zcash_primitives::zip32::AccountId; // TODO; replace fixed batches with orchard shard ranges (block ranges containing all note commitments to an orchard shard or fragment of a shard) const BATCH_SIZE: u32 = 10; @@ -68,9 +69,6 @@ where ); scanner.spawn_workers(); - // TODO: replace scanning keys with ufvk for scan_tx - let scanning_keys = ScanningKeys::from_account_ufvks(ufvks); - let mut interval = tokio::time::interval(Duration::from_millis(30)); loop { interval.tick().await; // TODO: tokio select to recieve scan results before tick @@ -97,7 +95,7 @@ where wallet, fetch_request_sender.clone(), parameters, - &scanning_keys, + &ufvks, scan_range, scan_results, ) @@ -114,7 +112,7 @@ where wallet, fetch_request_sender.clone(), parameters, - &scanning_keys, + &ufvks, scan_range, scan_results, ) @@ -221,7 +219,7 @@ async fn process_scan_results( wallet: &mut W, fetch_request_sender: mpsc::UnboundedSender, parameters: &P, - scanning_keys: &ScanningKeys, + ufvks: &HashMap, scan_range: ScanRange, scan_results: ScanResults, ) -> Result<(), ()> @@ -230,7 +228,7 @@ where W: SyncWallet + SyncBlocks + SyncTransactions + SyncNullifiers + SyncShardTrees, { update_wallet_data(wallet, scan_results).unwrap(); - link_nullifiers(wallet, fetch_request_sender, parameters, scanning_keys) + link_nullifiers(wallet, fetch_request_sender, parameters, ufvks) .await .unwrap(); remove_irrelevant_data(wallet, &scan_range).unwrap(); @@ -266,7 +264,7 @@ async fn link_nullifiers( wallet: &mut W, fetch_request_sender: mpsc::UnboundedSender, parameters: &P, - scanning_keys: &ScanningKeys, + ufvks: &HashMap, ) -> Result<(), ()> where P: Parameters, @@ -319,7 +317,7 @@ where let spending_transactions = scan_transactions( fetch_request_sender, parameters, - scanning_keys, + ufvks, spending_txids, DecryptedNoteData::new(), &wallet_blocks,