diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index a850b618df..38ad0efb81 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -1,6 +1,6 @@ //! Helper functions for managing light client key material. -use orchard; -use zcash_address::unified::{self, Container, Encoding}; +use orchard::{self, keys::Scope}; +use zcash_address::unified::{self, Container, Encoding, Typecode}; use zcash_primitives::{ consensus, zip32::{AccountId, DiversifierIndex}, @@ -22,7 +22,6 @@ use { byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}, std::convert::TryFrom, std::io::{Read, Write}, - zcash_address::unified::Typecode, zcash_encoding::CompactSize, zcash_primitives::consensus::BranchId, }; @@ -136,7 +135,7 @@ impl Era { } } -/// A set of spending keys that are all associated with a single +/// A set of viewing keys that are all associated with a single /// ZIP-0032 account identifier. #[derive(Clone, Debug)] #[doc(hidden)] @@ -507,19 +506,31 @@ impl UnifiedFullViewingKey { self.orchard.as_ref() } - /// Attempts to derive the Unified Address for the given diversifier index. + /// Attempts to derive the Unified Address for the given diversifier index and + /// receiver types. /// /// Returns `None` if the specified index does not produce a valid diversifier. - // TODO: Allow filtering down by receiver types? - pub fn address(&self, j: DiversifierIndex) -> Option { - let sapling = if let Some(extfvk) = self.sapling.as_ref() { + pub fn address( + &self, + j: DiversifierIndex, + receiver_types: &[Typecode], + ) -> Option { + let sapling = if let Some(extfvk) = self + .sapling + .as_ref() + .filter(|_| receiver_types.contains(&Typecode::Sapling)) + { Some(extfvk.address(j)?) } else { None }; #[cfg(feature = "transparent-inputs")] - let transparent = if let Some(tfvk) = self.transparent.as_ref() { + let transparent = if let Some(tfvk) = self + .transparent + .as_ref() + .filter(|_| receiver_types.contains(&Typecode::P2pkh)) + { match to_transparent_child_index(j) { Some(transparent_j) => match tfvk .derive_external_ivk() @@ -537,7 +548,13 @@ impl UnifiedFullViewingKey { #[cfg(not(feature = "transparent-inputs"))] let transparent = None; - UnifiedAddress::from_receivers(None, sapling, transparent) + let orchard = self + .orchard + .as_ref() + .filter(|_| receiver_types.contains(&Typecode::Orchard)) + .map(|ofvk| ofvk.address_at(j, Scope::External)); + + UnifiedAddress::from_receivers(orchard, sapling, transparent) } /// Searches the diversifier space starting at diversifier index `j` for one which will @@ -548,7 +565,12 @@ impl UnifiedFullViewingKey { pub fn find_address( &self, mut j: DiversifierIndex, + receiver_types: &[Typecode], ) -> Option<(UnifiedAddress, DiversifierIndex)> { + if receiver_types.is_empty() { + return None; + }; + // If we need to generate a transparent receiver, check that the user has not // specified an invalid transparent child index, from which we can never search to // find a valid index. @@ -559,7 +581,7 @@ impl UnifiedFullViewingKey { // Find a working diversifier and construct the associated address. loop { - let res = self.address(j); + let res = self.address(j, receiver_types); if let Some(ua) = res { break Some((ua, j)); } @@ -572,8 +594,11 @@ impl UnifiedFullViewingKey { /// Returns the Unified Address corresponding to the smallest valid diversifier index, /// along with that index. pub fn default_address(&self) -> (UnifiedAddress, DiversifierIndex) { - self.find_address(DiversifierIndex::new()) - .expect("UFVK should have at least one valid diversifier") + self.find_address( + DiversifierIndex::new(), + &[Typecode::P2pkh, Typecode::Sapling], + ) + .expect("UFVK should have at least one valid diversifier") } } @@ -606,7 +631,7 @@ mod tests { #[cfg(feature = "transparent-inputs")] use { crate::{address::RecipientAddress, encoding::AddressCodec}, - zcash_address::test_vectors, + zcash_address::{test_vectors, unified::Typecode}, zcash_primitives::{ legacy::{ self, @@ -739,7 +764,7 @@ mod tests { continue; } - let ua = ufvk.address(d_idx).unwrap_or_else(|| panic!("diversifier index {} should have produced a valid unified address for account {}", + let ua = ufvk.address(d_idx, &[Typecode::P2pkh, Typecode::Sapling]).unwrap_or_else(|| panic!("diversifier index {} should have produced a valid unified address for account {}", tv.diversifier_index, tv.account)); match RecipientAddress::decode(&MAIN_NETWORK, tv.unified_addr) { diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index b52c47311a..e01da38ea7 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true categories.workspace = true [dependencies] +zcash_address.workspace = true zcash_client_backend = { workspace = true, features = ["unstable-serialization", "unstable-spanning-tree"] } zcash_encoding.workspace = true zcash_primitives.workspace = true diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 6025838b5e..98d6c84ccc 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -42,6 +42,7 @@ use std::{ borrow::Borrow, collections::HashMap, convert::AsRef, fmt, num::NonZeroU32, ops::Range, path::Path, }; +use zcash_address::unified::Typecode; use incrementalmerkletree::Position; use shardtree::{ @@ -412,7 +413,7 @@ impl WalletWrite for WalletDb }; let (addr, diversifier_index) = ufvk - .find_address(search_from) + .find_address(search_from, &[Typecode::P2pkh, Typecode::Sapling]) .ok_or(SqliteClientError::DiversifierIndexOutOfRange)?; wallet::insert_address( diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 8e60ac6292..b2566f7e41 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -133,6 +133,18 @@ impl DiversifierIndex { } } +impl From for DiversifierIndex { + fn from(di: orchard::keys::DiversifierIndex) -> Self { + DiversifierIndex(*di.to_bytes()) + } +} + +impl From for orchard::keys::DiversifierIndex { + fn from(di: DiversifierIndex) -> orchard::keys::DiversifierIndex { + orchard::keys::DiversifierIndex::from(di.0) + } +} + /// The scope of a viewing key or address. /// /// A "scope" narrows the visibility or usage to a level below "full".