Skip to content

Commit

Permalink
Add receiver type selection to unified address derivation.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Oct 23, 2023
1 parent 84eb082 commit 37e409a
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 16 deletions.
55 changes: 40 additions & 15 deletions zcash_client_backend/src/keys.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -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,
};
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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<UnifiedAddress> {
let sapling = if let Some(extfvk) = self.sapling.as_ref() {
pub fn address(
&self,
j: DiversifierIndex,
receiver_types: &[Typecode],
) -> Option<UnifiedAddress> {
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()
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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));
}
Expand All @@ -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")
}
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions zcash_client_sqlite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion zcash_client_sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -412,7 +413,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
};

let (addr, diversifier_index) = ufvk
.find_address(search_from)
.find_address(search_from, &[Typecode::P2pkh, Typecode::Sapling])
.ok_or(SqliteClientError::DiversifierIndexOutOfRange)?;

wallet::insert_address(
Expand Down
12 changes: 12 additions & 0 deletions zcash_primitives/src/zip32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ impl DiversifierIndex {
}
}

impl From<orchard::keys::DiversifierIndex> for DiversifierIndex {
fn from(di: orchard::keys::DiversifierIndex) -> Self {
DiversifierIndex(*di.to_bytes())
}
}

impl From<DiversifierIndex> 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".
Expand Down

0 comments on commit 37e409a

Please sign in to comment.