Skip to content

Commit

Permalink
Add UnifiedIncomingViewingKey struct
Browse files Browse the repository at this point in the history
Also update sqlite to utilize the new struct
  • Loading branch information
AArnott committed Mar 12, 2024
1 parent df93c1c commit 071d7c5
Show file tree
Hide file tree
Showing 4 changed files with 564 additions and 61 deletions.
52 changes: 20 additions & 32 deletions zcash_client_sqlite/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ use std::num::NonZeroU32;
use std::ops::RangeInclusive;
use tracing::debug;
use zcash_address::unified::{Encoding, Ivk, Uivk};
use zcash_keys::keys::{AddressGenerationError, HdSeedFingerprint, UnifiedAddressRequest};
use zcash_keys::keys::{
AddressGenerationError, HdSeedFingerprint, UnifiedAddressRequest, UnifiedIncomingViewingKey,
};

use zcash_client_backend::{
address::{Address, UnifiedAddress},
Expand Down Expand Up @@ -200,7 +202,7 @@ pub(crate) enum ImportedAccount {
/// An account that was imported via its full viewing key.
Full(Box<UnifiedFullViewingKey>),
/// An account that was imported via its incoming viewing key.
Incoming(Uivk),
Incoming(Box<UnifiedIncomingViewingKey>),
}

/// Describes an account in terms of its UVK or ZIP-32 origins.
Expand All @@ -225,7 +227,7 @@ impl Account {
match self {
Account::Zip32(HdSeedAccount(_, _, ufvk)) => ufvk.default_address(request),
Account::Imported(ImportedAccount::Full(ufvk)) => ufvk.default_address(request),
Account::Imported(ImportedAccount::Incoming(_uivk)) => todo!(),
Account::Imported(ImportedAccount::Incoming(uivk)) => uivk.default_address(request),
}
}
}
Expand Down Expand Up @@ -304,50 +306,34 @@ fn get_sql_values_for_account_parameters<'a, P: consensus::Parameters>(
hd_seed_fingerprint: Some(hdaccount.hd_seed_fingerprint().as_bytes()),
hd_account_index: Some(hdaccount.account_index().into()),
ufvk: Some(hdaccount.ufvk().encode(params)),
uivk: ufvk_to_uivk(hdaccount.ufvk(), params)?,
uivk: hdaccount
.ufvk()
.to_unified_incoming_viewing_key()
.map_err(|e| SqliteClientError::CorruptedData(e.to_string()))?
.to_uivk()
.encode(&params.network_type()),
},
Account::Imported(ImportedAccount::Full(ufvk)) => AccountSqlValues {
account_type: AccountType::Imported.into(),
hd_seed_fingerprint: None,
hd_account_index: None,
ufvk: Some(ufvk.encode(params)),
uivk: ufvk_to_uivk(ufvk, params)?,
uivk: ufvk
.to_unified_incoming_viewing_key()
.map_err(|e| SqliteClientError::CorruptedData(e.to_string()))?
.to_uivk()
.encode(&params.network_type()),
},
Account::Imported(ImportedAccount::Incoming(uivk)) => AccountSqlValues {
account_type: AccountType::Imported.into(),
hd_seed_fingerprint: None,
hd_account_index: None,
ufvk: None,
uivk: uivk.encode(&params.network_type()),
uivk: uivk.to_uivk().encode(&params.network_type()),
},
})
}

pub(crate) fn ufvk_to_uivk<P: consensus::Parameters>(
ufvk: &UnifiedFullViewingKey,
params: &P,
) -> Result<String, SqliteClientError> {
let mut ivks: Vec<Ivk> = Vec::new();
if let Some(orchard) = ufvk.orchard() {
ivks.push(Ivk::Orchard(orchard.to_ivk(Scope::External).to_bytes()));
}
if let Some(sapling) = ufvk.sapling() {
let ivk = sapling.to_external_ivk();
ivks.push(Ivk::Sapling(ivk.to_bytes()));
}
#[cfg(feature = "transparent-inputs")]
if let Some(tfvk) = ufvk.transparent() {
let tivk = tfvk.derive_external_ivk()?;
ivks.push(Ivk::P2pkh(tivk.serialize().try_into().map_err(|_| {
SqliteClientError::BadAccountData("Unable to serialize transparent IVK.".to_string())
})?));
}

let uivk = zcash_address::unified::Uivk::try_from_items(ivks)
.map_err(|e| SqliteClientError::BadAccountData(format!("Unable to derive UIVK: {}", e)))?;
Ok(uivk.encode(&params.network_type()))
}

pub(crate) fn add_account<P: consensus::Parameters>(
conn: &rusqlite::Transaction,
params: &P,
Expand Down Expand Up @@ -1202,6 +1188,8 @@ pub(crate) fn get_account<C: Borrow<rusqlite::Connection>, P: Parameters>(
"UIVK network type does not match wallet network type".to_string(),
));
}
let uivk = UnifiedIncomingViewingKey::from_uivk(&uivk)
.map_err(|e| SqliteClientError::CorruptedData(e.to_string()))?;

match account_type {
AccountType::Zip32 => Ok(Some(Account::Zip32(HdSeedAccount::new(
Expand All @@ -1222,7 +1210,7 @@ pub(crate) fn get_account<C: Borrow<rusqlite::Connection>, P: Parameters>(
AccountType::Imported => Ok(Some(Account::Imported(if let Some(ufvk) = ufvk {
ImportedAccount::Full(Box::new(ufvk))
} else {
ImportedAccount::Incoming(uivk)
ImportedAccount::Incoming(Box::new(uivk))
}))),
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::{collections::HashSet, rc::Rc};

use crate::wallet::{init::WalletMigrationError, ufvk_to_uivk, AccountType};
use crate::wallet::{init::WalletMigrationError, AccountType};
use rusqlite::{named_params, Transaction};
use schemer_rusqlite::RusqliteMigration;
use secrecy::{ExposeSecret, SecretVec};
use uuid::Uuid;
use zcash_address::unified::Encoding;
use zcash_client_backend::keys::UnifiedSpendingKey;
use zcash_keys::keys::{HdSeedFingerprint, UnifiedFullViewingKey};
use zcash_primitives::consensus;
Expand Down Expand Up @@ -113,8 +114,11 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
));
}

let uivk = ufvk_to_uivk(&ufvk_parsed, &self.params)
.map_err(|e| WalletMigrationError::CorruptedData(e.to_string()))?;
let uivk = ufvk_parsed
.to_unified_incoming_viewing_key()
.map_err(|e| WalletMigrationError::CorruptedData(e.to_string()))?
.to_uivk()
.encode(&self.params.network_type());

transaction.execute(r#"
INSERT INTO accounts_new (id, account_type, hd_seed_fingerprint, hd_account_index, ufvk, uivk, birthday_height, recover_until_height)
Expand Down
9 changes: 9 additions & 0 deletions zcash_keys/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ and this library adheres to Rust's notion of
- `zcash_keys::address::Address::has_receiver`
- `impl Display for zcash_keys::keys::AddressGenerationError`
- `impl std::error::Error for zcash_keys::keys::AddressGenerationError`
- `zcash_keys::keys::UnifiedError`
- `zcash_keys::keys::UnifiedFullViewingKey::from_ufvk`
- `zcash_keys::keys::UnifiedFullViewingKey::to_ufvk`
- `zcash_keys::keys::UnifiedFullViewingKey::to_unified_incoming_viewing_key`
- `zcash_keys::keys::UnifiedIncomingViewingKey`

### Changed
- `zcash_keys::keys::AddressGenerationError` has a new variant
`DiversifierSpaceExhausted`.
- `zcash_keys::keys::UnifiedFullViewingKey::{find_address, default_address}`
now return `Result<(UnifiedAddress, DiversifierIndex), AddressGenerationError>`
(instead of `Option<(UnifiedAddress, DiversifierIndex)>` for `find_address`).
- `zcash_keys::keys::AddressGenerationError`
- Dropped `Clone` trait
- Added `UnifiedError` variant.
- Added `HDWalletError` variant.

### Fixed
- `UnifiedFullViewingKey::find_address` can now find an address for a diversifier
Expand Down
Loading

0 comments on commit 071d7c5

Please sign in to comment.