zcash_client_backend: Fix `UnifiedFullViewingKey` Sapling item type

Per ZIP 316, the Sapling FVK Encoding only includes `(ak, nk, ovk, dk)`
which is a subset of the Sapling `ExtendedFullViewingKey`. We therefore
need to use `DiversifiableFullViewingKey` inside `UnifiedFullViewingKey`
in order to make it parseable from the UFVK string encoding.

`zcash_client_sqlite::wallet::get_extended_full_viewing_keys` has been
removed as a consequence of this change: we can no longer reconstruct
the correct `ExtendedFullViewingKey` from the `UnifiedFullViewingKey`.
This commit is contained in:
Jack Grigg 2022-06-14 02:27:55 +00:00
parent d8b860207d
commit 76d015ed11
11 changed files with 149 additions and 140 deletions

View File

@ -84,7 +84,6 @@ use zcash_primitives::{
consensus::{self, BlockHeight, NetworkUpgrade}, consensus::{self, BlockHeight, NetworkUpgrade},
merkle_tree::CommitmentTree, merkle_tree::CommitmentTree,
sapling::Nullifier, sapling::Nullifier,
zip32::{AccountId, ExtendedFullViewingKey},
}; };
use crate::{ use crate::{
@ -214,7 +213,7 @@ where
let ufvks = data.get_unified_full_viewing_keys()?; let ufvks = data.get_unified_full_viewing_keys()?;
// TODO: Change `scan_block` to also scan Orchard. // TODO: Change `scan_block` to also scan Orchard.
// https://github.com/zcash/librustzcash/issues/403 // https://github.com/zcash/librustzcash/issues/403
let extfvks: Vec<(&AccountId, &ExtendedFullViewingKey)> = ufvks let dfvks: Vec<_> = ufvks
.iter() .iter()
.map(|(account, ufvk)| (account, ufvk.sapling().expect("TODO Add Orchard support"))) .map(|(account, ufvk)| (account, ufvk.sapling().expect("TODO Add Orchard support")))
.collect(); .collect();
@ -249,7 +248,7 @@ where
scan_block( scan_block(
params, params,
block, block,
&extfvks, &dfvks,
&nullifiers, &nullifiers,
&mut tree, &mut tree,
&mut witness_refs[..], &mut witness_refs[..],

View File

@ -46,9 +46,9 @@ pub fn decrypt_transaction<P: consensus::Parameters>(
if let Some(bundle) = tx.sapling_bundle() { if let Some(bundle) = tx.sapling_bundle() {
for (account, ufvk) in ufvks.iter() { for (account, ufvk) in ufvks.iter() {
let extfvk = ufvk.sapling().expect("TODO: Add Orchard support"); let dfvk = ufvk.sapling().expect("TODO: Add Orchard support");
let ivk = extfvk.fvk.vk.ivk(); let ivk = dfvk.fvk().vk.ivk();
let ovk = extfvk.fvk.ovk; let ovk = dfvk.fvk().ovk;
for (index, output) in bundle.shielded_outputs.iter().enumerate() { for (index, output) in bundle.shielded_outputs.iter().enumerate() {
let ((note, to, memo), outgoing) = let ((note, to, memo), outgoing) =

View File

@ -1,12 +1,12 @@
//! Helper functions for managing light client key material. //! Helper functions for managing light client key material.
use zcash_primitives::{ use zcash_primitives::{
consensus, consensus,
sapling::keys as sapling_keys,
zip32::{AccountId, DiversifierIndex}, zip32::{AccountId, DiversifierIndex},
}; };
use crate::address::UnifiedAddress; use crate::address::UnifiedAddress;
#[cfg(feature = "transparent-inputs")]
use std::convert::TryInto; use std::convert::TryInto;
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
@ -109,7 +109,7 @@ impl UnifiedSpendingKey {
UnifiedFullViewingKey { UnifiedFullViewingKey {
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent: Some(self.transparent.to_account_pubkey()), transparent: Some(self.transparent.to_account_pubkey()),
sapling: Some(sapling::ExtendedFullViewingKey::from(&self.sapling)), sapling: Some(sapling::ExtendedFullViewingKey::from(&self.sapling).into()),
} }
} }
@ -138,9 +138,7 @@ impl UnifiedSpendingKey {
pub struct UnifiedFullViewingKey { pub struct UnifiedFullViewingKey {
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
transparent: Option<legacy::AccountPubKey>, transparent: Option<legacy::AccountPubKey>,
// TODO: This type is invalid for a UFVK; create a `sapling::DiversifiableFullViewingKey` sapling: Option<sapling_keys::DiversifiableFullViewingKey>,
// to replace it.
sapling: Option<sapling::ExtendedFullViewingKey>,
} }
#[doc(hidden)] #[doc(hidden)]
@ -148,7 +146,7 @@ impl UnifiedFullViewingKey {
/// Construct a new unified full viewing key, if the required components are present. /// Construct a new unified full viewing key, if the required components are present.
pub fn new( pub fn new(
#[cfg(feature = "transparent-inputs")] transparent: Option<legacy::AccountPubKey>, #[cfg(feature = "transparent-inputs")] transparent: Option<legacy::AccountPubKey>,
sapling: Option<sapling::ExtendedFullViewingKey>, sapling: Option<sapling_keys::DiversifiableFullViewingKey>,
) -> Option<UnifiedFullViewingKey> { ) -> Option<UnifiedFullViewingKey> {
if sapling.is_none() { if sapling.is_none() {
None None
@ -185,7 +183,12 @@ impl UnifiedFullViewingKey {
}; };
let sapling = if flag & 2 != 0 { let sapling = if flag & 2 != 0 {
Some(sapling::ExtendedFullViewingKey::read(data).ok()?) if data.len() != 128 {
return None;
}
Some(sapling_keys::DiversifiableFullViewingKey::from_bytes(
data.try_into().unwrap(),
)?)
} else { } else {
None None
}; };
@ -212,7 +215,7 @@ impl UnifiedFullViewingKey {
}; };
if let Some(sapling) = self.sapling.as_ref() { if let Some(sapling) = self.sapling.as_ref() {
sapling.write(&mut ufvk).unwrap(); ufvk.extend_from_slice(&sapling.to_bytes());
} }
format!("DONOTUSEUFVK{}", hex::encode(&ufvk)) format!("DONOTUSEUFVK{}", hex::encode(&ufvk))
@ -225,9 +228,8 @@ impl UnifiedFullViewingKey {
self.transparent.as_ref() self.transparent.as_ref()
} }
/// Returns the Sapling extended full viewing key component of this /// Returns the Sapling diversifiable full viewing key component of this unified key.
/// unified key. pub fn sapling(&self) -> Option<&sapling_keys::DiversifiableFullViewingKey> {
pub fn sapling(&self) -> Option<&sapling::ExtendedFullViewingKey> {
self.sapling.as_ref() self.sapling.as_ref()
} }
@ -348,7 +350,7 @@ mod tests {
let sapling = { let sapling = {
let extsk = sapling::spending_key(&[0; 32], 0, account); let extsk = sapling::spending_key(&[0; 32], 0, account);
Some(ExtendedFullViewingKey::from(&extsk)) Some(ExtendedFullViewingKey::from(&extsk).into())
}; };
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
@ -368,6 +370,9 @@ mod tests {
decoded.transparent.map(|t| t.serialize()), decoded.transparent.map(|t| t.serialize()),
ufvk.transparent.map(|t| t.serialize()), ufvk.transparent.map(|t| t.serialize()),
); );
assert_eq!(decoded.sapling, ufvk.sapling); assert_eq!(
decoded.sapling.map(|s| s.to_bytes()),
ufvk.sapling.map(|s| s.to_bytes()),
);
} }
} }

View File

@ -9,6 +9,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight}, consensus::{self, BlockHeight},
merkle_tree::{CommitmentTree, IncrementalWitness}, merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{ sapling::{
keys::DiversifiableFullViewingKey,
note_encryption::{try_sapling_compact_note_decryption, SaplingDomain}, note_encryption::{try_sapling_compact_note_decryption, SaplingDomain},
Node, Note, Nullifier, PaymentAddress, SaplingIvk, Node, Note, Nullifier, PaymentAddress, SaplingIvk,
}, },
@ -127,6 +128,26 @@ pub trait ScanningKey {
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf; fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf;
} }
impl ScanningKey for DiversifiableFullViewingKey {
type Nf = Nullifier;
fn try_decryption<
P: consensus::Parameters,
Output: ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>,
>(
&self,
params: &P,
height: BlockHeight,
output: &Output,
) -> Option<(Note, PaymentAddress)> {
try_sapling_compact_note_decryption(params, height, &self.fvk().vk.ivk(), output)
}
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf {
note.nf(&self.fvk().vk, witness.position() as u64)
}
}
/// The [`ScanningKey`] implementation for [`ExtendedFullViewingKey`]s. /// The [`ScanningKey`] implementation for [`ExtendedFullViewingKey`]s.
/// Nullifiers may be derived when scanning with these keys. /// Nullifiers may be derived when scanning with these keys.
/// ///

View File

@ -49,6 +49,12 @@ and this library adheres to Rust's notion of
constructed, rather than only in the case that a transaction has constructed, rather than only in the case that a transaction has
been decrypted after being retrieved from the network. been decrypted after being retrieved from the network.
### Removed
- `zcash_client_sqlite::wallet`:
- `get_extended_full_viewing_keys` (use
`zcash_client_backend::data_api::WalletRead::get_unified_full_viewing_keys`
instead).
### Deprecated ### Deprecated
- A number of public API methods that are used internally to support the - A number of public API methods that are used internally to support the
`zcash_client_backend::data_api::{WalletRead, WalletWrite}` interfaces have `zcash_client_backend::data_api::{WalletRead, WalletWrite}` interfaces have

View File

@ -101,7 +101,7 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Empty chain should be valid // Empty chain should be valid
validate_chain( validate_chain(
@ -115,7 +115,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
Amount::from_u64(5).unwrap(), Amount::from_u64(5).unwrap(),
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -144,7 +144,7 @@ mod tests {
let (cb2, _) = fake_compact_block( let (cb2, _) = fake_compact_block(
sapling_activation_height() + 1, sapling_activation_height() + 1,
cb.hash(), cb.hash(),
extfvk, &dfvk,
Amount::from_u64(7).unwrap(), Amount::from_u64(7).unwrap(),
); );
insert_into_cache(&db_cache, &cb2); insert_into_cache(&db_cache, &cb2);
@ -180,19 +180,19 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Create some fake CompactBlocks // Create some fake CompactBlocks
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
Amount::from_u64(5).unwrap(), Amount::from_u64(5).unwrap(),
); );
let (cb2, _) = fake_compact_block( let (cb2, _) = fake_compact_block(
sapling_activation_height() + 1, sapling_activation_height() + 1,
cb.hash(), cb.hash(),
extfvk.clone(), &dfvk,
Amount::from_u64(7).unwrap(), Amount::from_u64(7).unwrap(),
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -214,13 +214,13 @@ mod tests {
let (cb3, _) = fake_compact_block( let (cb3, _) = fake_compact_block(
sapling_activation_height() + 2, sapling_activation_height() + 2,
BlockHash([1; 32]), BlockHash([1; 32]),
extfvk.clone(), &dfvk,
Amount::from_u64(8).unwrap(), Amount::from_u64(8).unwrap(),
); );
let (cb4, _) = fake_compact_block( let (cb4, _) = fake_compact_block(
sapling_activation_height() + 3, sapling_activation_height() + 3,
cb3.hash(), cb3.hash(),
extfvk, &dfvk,
Amount::from_u64(3).unwrap(), Amount::from_u64(3).unwrap(),
); );
insert_into_cache(&db_cache, &cb3); insert_into_cache(&db_cache, &cb3);
@ -250,19 +250,19 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Create some fake CompactBlocks // Create some fake CompactBlocks
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
Amount::from_u64(5).unwrap(), Amount::from_u64(5).unwrap(),
); );
let (cb2, _) = fake_compact_block( let (cb2, _) = fake_compact_block(
sapling_activation_height() + 1, sapling_activation_height() + 1,
cb.hash(), cb.hash(),
extfvk.clone(), &dfvk,
Amount::from_u64(7).unwrap(), Amount::from_u64(7).unwrap(),
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -284,13 +284,13 @@ mod tests {
let (cb3, _) = fake_compact_block( let (cb3, _) = fake_compact_block(
sapling_activation_height() + 2, sapling_activation_height() + 2,
cb2.hash(), cb2.hash(),
extfvk.clone(), &dfvk,
Amount::from_u64(8).unwrap(), Amount::from_u64(8).unwrap(),
); );
let (cb4, _) = fake_compact_block( let (cb4, _) = fake_compact_block(
sapling_activation_height() + 3, sapling_activation_height() + 3,
BlockHash([1; 32]), BlockHash([1; 32]),
extfvk, &dfvk,
Amount::from_u64(3).unwrap(), Amount::from_u64(3).unwrap(),
); );
insert_into_cache(&db_cache, &cb3); insert_into_cache(&db_cache, &cb3);
@ -320,7 +320,7 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Account balance should be zero // Account balance should be zero
assert_eq!( assert_eq!(
@ -334,12 +334,12 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
value, value,
); );
let (cb2, _) = let (cb2, _) =
fake_compact_block(sapling_activation_height() + 1, cb.hash(), extfvk, value2); fake_compact_block(sapling_activation_height() + 1, cb.hash(), &dfvk, value2);
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
insert_into_cache(&db_cache, &cb2); insert_into_cache(&db_cache, &cb2);
@ -389,14 +389,14 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Create a block with height SAPLING_ACTIVATION_HEIGHT // Create a block with height SAPLING_ACTIVATION_HEIGHT
let value = Amount::from_u64(50000).unwrap(); let value = Amount::from_u64(50000).unwrap();
let (cb1, _) = fake_compact_block( let (cb1, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb1); insert_into_cache(&db_cache, &cb1);
@ -405,14 +405,10 @@ mod tests {
assert_eq!(get_balance(&db_data, AccountId::from(0)).unwrap(), value); assert_eq!(get_balance(&db_data, AccountId::from(0)).unwrap(), value);
// We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next // We cannot scan a block of height SAPLING_ACTIVATION_HEIGHT + 2 next
let (cb2, _) = fake_compact_block( let (cb2, _) =
sapling_activation_height() + 1, fake_compact_block(sapling_activation_height() + 1, cb1.hash(), &dfvk, value);
cb1.hash(),
extfvk.clone(),
value,
);
let (cb3, _) = let (cb3, _) =
fake_compact_block(sapling_activation_height() + 2, cb2.hash(), extfvk, value); fake_compact_block(sapling_activation_height() + 2, cb2.hash(), &dfvk, value);
insert_into_cache(&db_cache, &cb3); insert_into_cache(&db_cache, &cb3);
match scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None) { match scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None) {
Err(SqliteClientError::BackendError(e)) => { Err(SqliteClientError::BackendError(e)) => {
@ -448,7 +444,7 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Account balance should be zero // Account balance should be zero
assert_eq!( assert_eq!(
@ -461,7 +457,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -476,7 +472,7 @@ mod tests {
// Create a second fake CompactBlock sending more value to the address // Create a second fake CompactBlock sending more value to the address
let value2 = Amount::from_u64(7).unwrap(); let value2 = Amount::from_u64(7).unwrap();
let (cb2, _) = let (cb2, _) =
fake_compact_block(sapling_activation_height() + 1, cb.hash(), extfvk, value2); fake_compact_block(sapling_activation_height() + 1, cb.hash(), &dfvk, value2);
insert_into_cache(&db_cache, &cb2); insert_into_cache(&db_cache, &cb2);
// Scan the cache again // Scan the cache again
@ -500,7 +496,7 @@ mod tests {
init_wallet_db(&db_data).unwrap(); init_wallet_db(&db_data).unwrap();
// Add an account to the wallet // Add an account to the wallet
let (extfvk, _taddr) = init_test_accounts_table(&db_data); let (dfvk, _taddr) = init_test_accounts_table(&db_data);
// Account balance should be zero // Account balance should be zero
assert_eq!( assert_eq!(
@ -513,7 +509,7 @@ mod tests {
let (cb, nf) = fake_compact_block( let (cb, nf) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -535,7 +531,7 @@ mod tests {
sapling_activation_height() + 1, sapling_activation_height() + 1,
cb.hash(), cb.hash(),
(nf, value), (nf, value),
extfvk, &dfvk,
to2, to2,
value2, value2,
), ),

View File

@ -745,8 +745,8 @@ mod tests {
legacy::TransparentAddress, legacy::TransparentAddress,
memo::MemoBytes, memo::MemoBytes,
sapling::{ sapling::{
note_encryption::sapling_note_encryption, util::generate_random_rseed, Note, Nullifier, keys::DiversifiableFullViewingKey, note_encryption::sapling_note_encryption,
PaymentAddress, util::generate_random_rseed, Note, Nullifier, PaymentAddress,
}, },
transaction::components::Amount, transaction::components::Amount,
zip32::ExtendedFullViewingKey, zip32::ExtendedFullViewingKey,
@ -783,11 +783,11 @@ mod tests {
#[cfg(test)] #[cfg(test)]
pub(crate) fn init_test_accounts_table( pub(crate) fn init_test_accounts_table(
db_data: &WalletDb<Network>, db_data: &WalletDb<Network>,
) -> (ExtendedFullViewingKey, Option<TransparentAddress>) { ) -> (DiversifiableFullViewingKey, Option<TransparentAddress>) {
let seed = [0u8; 32]; let seed = [0u8; 32];
let account = AccountId::from(0); let account = AccountId::from(0);
let extsk = sapling::spending_key(&seed, network().coin_type(), account); let extsk = sapling::spending_key(&seed, network().coin_type(), account);
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let (tkey, taddr) = { let (tkey, taddr) = {
@ -804,13 +804,13 @@ mod tests {
let ufvk = UnifiedFullViewingKey::new( let ufvk = UnifiedFullViewingKey::new(
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
tkey, tkey,
Some(extfvk.clone()), Some(dfvk.clone()),
) )
.unwrap(); .unwrap();
init_accounts_table(db_data, &[ufvk]).unwrap(); init_accounts_table(db_data, &[ufvk]).unwrap();
(extfvk, taddr) (dfvk, taddr)
} }
/// Create a fake CompactBlock at the given height, containing a single output paying /// Create a fake CompactBlock at the given height, containing a single output paying
@ -818,10 +818,10 @@ mod tests {
pub(crate) fn fake_compact_block( pub(crate) fn fake_compact_block(
height: BlockHeight, height: BlockHeight,
prev_hash: BlockHash, prev_hash: BlockHash,
extfvk: ExtendedFullViewingKey, dfvk: &DiversifiableFullViewingKey,
value: Amount, value: Amount,
) -> (CompactBlock, Nullifier) { ) -> (CompactBlock, Nullifier) {
let to = extfvk.default_address().1; let to = dfvk.default_address().1;
// Create a fake Note for the account // Create a fake Note for the account
let mut rng = OsRng; let mut rng = OsRng;
@ -833,7 +833,7 @@ mod tests {
rseed, rseed,
}; };
let encryptor = sapling_note_encryption::<_, Network>( let encryptor = sapling_note_encryption::<_, Network>(
Some(extfvk.fvk.ovk), Some(dfvk.fvk().ovk),
note.clone(), note.clone(),
to, to,
MemoBytes::empty(), MemoBytes::empty(),
@ -859,7 +859,7 @@ mod tests {
rng.fill_bytes(&mut cb.hash); rng.fill_bytes(&mut cb.hash);
cb.prevHash.extend_from_slice(&prev_hash.0); cb.prevHash.extend_from_slice(&prev_hash.0);
cb.vtx.push(ctx); cb.vtx.push(ctx);
(cb, note.nf(&extfvk.fvk.vk, 0)) (cb, note.nf(&dfvk.fvk().vk, 0))
} }
/// Create a fake CompactBlock at the given height, spending a single note from the /// Create a fake CompactBlock at the given height, spending a single note from the
@ -868,7 +868,7 @@ mod tests {
height: BlockHeight, height: BlockHeight,
prev_hash: BlockHash, prev_hash: BlockHash,
(nf, in_value): (Nullifier, Amount), (nf, in_value): (Nullifier, Amount),
extfvk: ExtendedFullViewingKey, dfvk: &DiversifiableFullViewingKey,
to: PaymentAddress, to: PaymentAddress,
value: Amount, value: Amount,
) -> CompactBlock { ) -> CompactBlock {
@ -893,7 +893,7 @@ mod tests {
rseed, rseed,
}; };
let encryptor = sapling_note_encryption::<_, Network>( let encryptor = sapling_note_encryption::<_, Network>(
Some(extfvk.fvk.ovk), Some(dfvk.fvk().ovk),
note.clone(), note.clone(),
to, to,
MemoBytes::empty(), MemoBytes::empty(),
@ -912,7 +912,7 @@ mod tests {
// Create a fake Note for the change // Create a fake Note for the change
ctx.outputs.push({ ctx.outputs.push({
let change_addr = extfvk.default_address().1; let change_addr = dfvk.default_address().1;
let rseed = generate_random_rseed(&network(), height, &mut rng); let rseed = generate_random_rseed(&network(), height, &mut rng);
let note = Note { let note = Note {
g_d: change_addr.diversifier().g_d().unwrap(), g_d: change_addr.diversifier().g_d().unwrap(),
@ -921,7 +921,7 @@ mod tests {
rseed, rseed,
}; };
let encryptor = sapling_note_encryption::<_, Network>( let encryptor = sapling_note_encryption::<_, Network>(
Some(extfvk.fvk.ovk), Some(dfvk.fvk().ovk),
note.clone(), note.clone(),
change_addr, change_addr,
MemoBytes::empty(), MemoBytes::empty(),

View File

@ -17,7 +17,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters}, consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
memo::{Memo, MemoBytes}, memo::{Memo, MemoBytes},
merkle_tree::{CommitmentTree, IncrementalWitness}, merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Node, Note, Nullifier, PaymentAddress}, sapling::{keys::DiversifiableFullViewingKey, Node, Note, Nullifier, PaymentAddress},
transaction::{components::Amount, Transaction, TxId}, transaction::{components::Amount, Transaction, TxId},
zip32::{AccountId, ExtendedFullViewingKey}, zip32::{AccountId, ExtendedFullViewingKey},
}; };
@ -172,28 +172,6 @@ pub fn get_address<P: consensus::Parameters>(
}) })
} }
/// Returns the [`ExtendedFullViewingKey`]s for the wallet.
///
/// [`ExtendedFullViewingKey`]: zcash_primitives::zip32::ExtendedFullViewingKey
#[deprecated(
note = "This function will be removed in a future release. Use zcash_client_backend::data_api::WalletRead::get_unified_full_viewing_keys instead."
)]
pub fn get_extended_full_viewing_keys<P: consensus::Parameters>(
wdb: &WalletDb<P>,
) -> Result<HashMap<AccountId, ExtendedFullViewingKey>, SqliteClientError> {
get_unified_full_viewing_keys(wdb).map(|ufvks| {
ufvks
.into_iter()
.map(|(account, ufvk)| {
(
account,
ufvk.sapling().cloned().expect("TODO: Add Orchard support"),
)
})
.collect()
})
}
/// Returns the [`UnifiedFullViewingKey`]s for the wallet. /// Returns the [`UnifiedFullViewingKey`]s for the wallet.
pub(crate) fn get_unified_full_viewing_keys<P: consensus::Parameters>( pub(crate) fn get_unified_full_viewing_keys<P: consensus::Parameters>(
wdb: &WalletDb<P>, wdb: &WalletDb<P>,
@ -248,7 +226,10 @@ pub fn is_valid_account_extfvk<P: consensus::Parameters>(
.map_err(SqliteClientError::from) .map_err(SqliteClientError::from)
.and_then(|row| { .and_then(|row| {
if let Some(ufvk) = row { if let Some(ufvk) = row {
ufvk.map(|ufvk| ufvk.sapling() == Some(extfvk)) ufvk.map(|ufvk| {
ufvk.sapling().map(|dfvk| dfvk.to_bytes())
== Some(DiversifiableFullViewingKey::from(extfvk.clone()).to_bytes())
})
} else { } else {
Ok(false) Ok(false)
} }

View File

@ -179,8 +179,8 @@ pub fn init_wallet_db<P>(wdb: &WalletDb<P>) -> Result<(), rusqlite::Error> {
/// let seed = [0u8; 32]; // insecure; replace with a strong random seed /// let seed = [0u8; 32]; // insecure; replace with a strong random seed
/// let account = AccountId::from(0); /// let account = AccountId::from(0);
/// let extsk = sapling::spending_key(&seed, Network::TestNetwork.coin_type(), account); /// let extsk = sapling::spending_key(&seed, Network::TestNetwork.coin_type(), account);
/// let extfvk = ExtendedFullViewingKey::from(&extsk); /// let dfvk = ExtendedFullViewingKey::from(&extsk).into();
/// let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk)).unwrap(); /// let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk)).unwrap();
/// init_accounts_table(&db_data, &[ufvk]).unwrap(); /// init_accounts_table(&db_data, &[ufvk]).unwrap();
/// # } /// # }
/// ``` /// ```
@ -293,6 +293,7 @@ mod tests {
use zcash_primitives::{ use zcash_primitives::{
block::BlockHash, block::BlockHash,
consensus::{BlockHeight, Parameters}, consensus::{BlockHeight, Parameters},
sapling::keys::DiversifiableFullViewingKey,
zip32::ExtendedFullViewingKey, zip32::ExtendedFullViewingKey,
}; };
@ -319,7 +320,7 @@ mod tests {
// First call with data should initialise the accounts table // First call with data should initialise the accounts table
let extsk = sapling::spending_key(&seed, network().coin_type(), account); let extsk = sapling::spending_key(&seed, network().coin_type(), account);
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new( let ufvk = UnifiedFullViewingKey::new(
@ -328,12 +329,12 @@ mod tests {
.unwrap() .unwrap()
.to_account_pubkey(), .to_account_pubkey(),
), ),
Some(extfvk), Some(dfvk),
) )
.unwrap(); .unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk), None).unwrap();
init_accounts_table(&db_data, &[ufvk.clone()]).unwrap(); init_accounts_table(&db_data, &[ufvk.clone()]).unwrap();

View File

@ -165,7 +165,10 @@ mod tests {
block::BlockHash, block::BlockHash,
consensus::{BlockHeight, BranchId, Parameters}, consensus::{BlockHeight, BranchId, Parameters},
legacy::TransparentAddress, legacy::TransparentAddress,
sapling::{note_encryption::try_sapling_output_recovery, prover::TxProver}, sapling::{
keys::DiversifiableFullViewingKey, note_encryption::try_sapling_output_recovery,
prover::TxProver,
},
transaction::{components::Amount, Transaction}, transaction::{components::Amount, Transaction},
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
}; };
@ -207,8 +210,8 @@ mod tests {
// Add two accounts to the wallet // Add two accounts to the wallet
let extsk0 = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk0 = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extsk1 = sapling::spending_key(&[1u8; 32], network().coin_type(), AccountId::from(1)); let extsk1 = sapling::spending_key(&[1u8; 32], network().coin_type(), AccountId::from(1));
let extfvk0 = ExtendedFullViewingKey::from(&extsk0); let dfvk0 = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk0));
let extfvk1 = ExtendedFullViewingKey::from(&extsk1); let dfvk1 = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk1));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvks = { let ufvks = {
@ -219,14 +222,14 @@ mod tests {
transparent::AccountPrivKey::from_seed(&network(), &[1u8; 32], AccountId::from(1)) transparent::AccountPrivKey::from_seed(&network(), &[1u8; 32], AccountId::from(1))
.unwrap(); .unwrap();
[ [
UnifiedFullViewingKey::new(Some(tsk0.to_account_pubkey()), Some(extfvk0)).unwrap(), UnifiedFullViewingKey::new(Some(tsk0.to_account_pubkey()), Some(dfvk0)).unwrap(),
UnifiedFullViewingKey::new(Some(tsk1.to_account_pubkey()), Some(extfvk1)).unwrap(), UnifiedFullViewingKey::new(Some(tsk1.to_account_pubkey()), Some(dfvk1)).unwrap(),
] ]
}; };
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvks = [ let ufvks = [
UnifiedFullViewingKey::new(Some(extfvk0)).unwrap(), UnifiedFullViewingKey::new(Some(dfvk0), None).unwrap(),
UnifiedFullViewingKey::new(Some(extfvk1)).unwrap(), UnifiedFullViewingKey::new(Some(dfvk1), None).unwrap(),
]; ];
init_accounts_table(&db_data, &ufvks).unwrap(); init_accounts_table(&db_data, &ufvks).unwrap();
@ -275,12 +278,12 @@ mod tests {
// Add an account to the wallet // Add an account to the wallet
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk)).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk)).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
let to = extsk.default_address().1.into(); let to = extsk.default_address().1.into();
@ -319,11 +322,11 @@ mod tests {
// Add an account to the wallet // Add an account to the wallet
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk)).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk)).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk)).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
let to = extsk.default_address().1.into(); let to = extsk.default_address().1.into();
@ -367,11 +370,11 @@ mod tests {
// Add an account to the wallet // Add an account to the wallet
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -379,7 +382,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -398,12 +401,7 @@ mod tests {
); );
// Add more funds to the wallet in a second note // Add more funds to the wallet in a second note
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(sapling_activation_height() + 1, cb.hash(), &dfvk, value);
sapling_activation_height() + 1,
cb.hash(),
extfvk.clone(),
value,
);
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap();
@ -446,12 +444,8 @@ mod tests {
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second // Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second
// note is verified // note is verified
for i in 2..10 { for i in 2..10 {
let (cb, _) = fake_compact_block( let (cb, _) =
sapling_activation_height() + i, fake_compact_block(sapling_activation_height() + i, cb.hash(), &dfvk, value);
cb.hash(),
extfvk.clone(),
value,
);
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
} }
scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap();
@ -477,8 +471,7 @@ mod tests {
} }
// Mine block 11 so that the second note becomes verified // Mine block 11 so that the second note becomes verified
let (cb, _) = let (cb, _) = fake_compact_block(sapling_activation_height() + 10, cb.hash(), &dfvk, value);
fake_compact_block(sapling_activation_height() + 10, cb.hash(), extfvk, value);
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap(); scan_cached_blocks(&tests::network(), &db_cache, &mut db_write, None).unwrap();
@ -510,11 +503,11 @@ mod tests {
// Add an account to the wallet // Add an account to the wallet
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -522,7 +515,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk, &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -573,7 +566,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height() + i, sapling_activation_height() + i,
cb.hash(), cb.hash(),
ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), &ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])).into(),
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -604,7 +597,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height() + 22, sapling_activation_height() + 22,
cb.hash(), cb.hash(),
ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])), &ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[22])).into(),
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -639,11 +632,11 @@ mod tests {
// Add an account to the wallet // Add an account to the wallet
let extsk = sapling::spending_key(&[0u8; 32], network.coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network.coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -651,7 +644,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk.clone(), &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -708,7 +701,7 @@ mod tests {
try_sapling_output_recovery( try_sapling_output_recovery(
&network, &network,
sapling_activation_height(), sapling_activation_height(),
&extfvk.fvk.ovk, &dfvk.fvk().ovk,
output, output,
) )
}; };
@ -725,7 +718,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height() + i, sapling_activation_height() + i,
cb.hash(), cb.hash(),
ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])), &ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[i as u8])).into(),
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);
@ -749,11 +742,11 @@ mod tests {
// Add an account to the wallet // Add an account to the wallet
let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0)); let extsk = sapling::spending_key(&[0u8; 32], network().coin_type(), AccountId::from(0));
let extfvk = ExtendedFullViewingKey::from(&extsk); let dfvk = DiversifiableFullViewingKey::from(ExtendedFullViewingKey::from(&extsk));
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let ufvk = UnifiedFullViewingKey::new(None, Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(None, Some(dfvk.clone())).unwrap();
#[cfg(not(feature = "transparent-inputs"))] #[cfg(not(feature = "transparent-inputs"))]
let ufvk = UnifiedFullViewingKey::new(Some(extfvk.clone())).unwrap(); let ufvk = UnifiedFullViewingKey::new(Some(dfvk.clone())).unwrap();
init_accounts_table(&db_data, &[ufvk]).unwrap(); init_accounts_table(&db_data, &[ufvk]).unwrap();
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -761,7 +754,7 @@ mod tests {
let (cb, _) = fake_compact_block( let (cb, _) = fake_compact_block(
sapling_activation_height(), sapling_activation_height(),
BlockHash([0; 32]), BlockHash([0; 32]),
extfvk, &dfvk,
value, value,
); );
insert_into_cache(&db_cache, &cb); insert_into_cache(&db_cache, &cb);

View File

@ -276,6 +276,13 @@ impl DiversifiableFullViewingKey {
zip32::sapling_find_address(&self.fvk, &self.dk, j) zip32::sapling_find_address(&self.fvk, &self.dk, j)
} }
/// Returns the payment address corresponding to the smallest valid diversifier index,
/// along with that index.
// TODO: See if this is only used in tests.
pub fn default_address(&self) -> (zip32::DiversifierIndex, PaymentAddress) {
zip32::sapling_default_address(&self.fvk, &self.dk)
}
/// Attempts to decrypt the given address's diversifier with this full viewing key. /// Attempts to decrypt the given address's diversifier with this full viewing key.
/// ///
/// This method extracts the diversifier from the given address and decrypts it as a /// This method extracts the diversifier from the given address and decrypts it as a