Add `UnifiedAddress` generation to `UnifiedFullViewingKey`

This commit is contained in:
Jack Grigg 2022-06-09 21:59:53 +00:00
parent ed6016857e
commit 555a621a10
1 changed files with 90 additions and 2 deletions

View File

@ -1,8 +1,16 @@
//! Helper functions for managing light client key material.
use zcash_primitives::{consensus, zip32::AccountId};
use zcash_primitives::{
consensus,
zip32::{AccountId, DiversifierIndex},
};
use crate::address::UnifiedAddress;
#[cfg(feature = "transparent-inputs")]
use zcash_primitives::legacy::keys as legacy;
use std::convert::TryInto;
#[cfg(feature = "transparent-inputs")]
use zcash_primitives::legacy::keys::{self as legacy, IncomingViewingKey};
pub mod sapling {
use zcash_primitives::zip32::{AccountId, ChildIndex};
@ -45,6 +53,17 @@ pub mod sapling {
}
}
#[cfg(feature = "transparent-inputs")]
fn to_transparent_child_index(j: DiversifierIndex) -> Option<u32> {
let (low_4_bytes, rest) = j.0.split_at(4);
let transparent_j = u32::from_le_bytes(low_4_bytes.try_into().unwrap());
if transparent_j > (0x7FFFFFFF) || rest.iter().any(|b| b != &0) {
None
} else {
Some(transparent_j)
}
}
#[derive(Debug)]
#[doc(hidden)]
pub enum DerivationError {
@ -164,6 +183,75 @@ impl UnifiedFullViewingKey {
pub fn sapling(&self) -> Option<&sapling::ExtendedFullViewingKey> {
self.sapling.as_ref()
}
/// Attempts to derive the Unified Address for the given diversifier index.
///
/// 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() {
Some(extfvk.address(j)?)
} else {
None
};
#[cfg(feature = "transparent-inputs")]
let transparent = if let Some(tfvk) = self.transparent.as_ref() {
match to_transparent_child_index(j) {
Some(transparent_j) => match tfvk
.derive_external_ivk()
.and_then(|tivk| tivk.derive_address(transparent_j))
{
Ok(taddr) => Some(taddr),
Err(_) => return None,
},
// Diversifier doesn't generate a valid transparent child index.
None => return None,
}
} else {
None
};
#[cfg(not(feature = "transparent-inputs"))]
let transparent = None;
UnifiedAddress::from_receivers(None, sapling, transparent)
}
/// Searches the diversifier space starting at diversifier index `j` for one which will
/// produce a valid diversifier, and return the Unified Address constructed using that
/// diversifier along with the index at which the valid diversifier was found.
///
/// Returns `None` if no valid diversifier exists
pub fn find_address(
&self,
mut j: DiversifierIndex,
) -> Option<(UnifiedAddress, DiversifierIndex)> {
// 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.
#[cfg(feature = "transparent-inputs")]
if self.transparent.is_some() && to_transparent_child_index(j).is_none() {
return None;
}
// Find a working diversifier and construct the associated address.
loop {
let res = self.address(j);
if let Some(ua) = res {
break Some((ua, j));
}
if j.increment().is_err() {
break None;
}
}
}
/// 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")
}
}
#[cfg(test)]