Skip to content

Commit

Permalink
zcash_client_sqlite: Return a backend-specific Account type from ge…
Browse files Browse the repository at this point in the history
…t_account_by_ufvk.
  • Loading branch information
nuttycom committed Mar 9, 2024
1 parent 010d282 commit 1fdd0dc
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 16 deletions.
4 changes: 4 additions & 0 deletions zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this library adheres to Rust's notion of
changes related to `Orchard` below are introduced under this feature
flag.
- `zcash_client_backend::data_api`:
- `Account`
- `AccountBalance::with_orchard_balance_mut`
- `AccountBirthday::orchard_frontier`
- `BlockMetadata::orchard_tree_size`
Expand Down Expand Up @@ -48,7 +49,10 @@ and this library adheres to Rust's notion of
- Arguments to `BlockMetadata::from_parts` have changed.
- Arguments to `ScannedBlock::from_parts` have changed.
- Changes to the `WalletRead` trait:
- Added `Account` associated type.
- Added `get_orchard_nullifiers`
- `get_account_for_ufvk` now returns an `Self::Account` instead of a bare
`AccountId`
- Changes to the `InputSource` trait:
- `select_spendable_notes` now takes its `target_value` argument as a
`NonNegativeAmount`. Also, the values of the returned map are also
Expand Down
39 changes: 36 additions & 3 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,35 @@ impl AccountBalance {
}
}

/// A set of capabilities that a client account must provide.
pub trait Account<AccountId: Copy> {
/// Returns the unique identifier for the account.
fn id(&self) -> AccountId;

/// Returns the UFVK that the wallet backend has stored for the account, if any.
fn ufvk(&self) -> Option<&UnifiedFullViewingKey>;
}

impl<A: Copy> Account<A> for (A, UnifiedFullViewingKey) {
fn id(&self) -> A {
self.0
}

fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
Some(&self.1)
}
}

impl<A: Copy> Account<A> for (A, Option<UnifiedFullViewingKey>) {
fn id(&self) -> A {
self.0
}

fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
self.1.as_ref()
}
}

/// A polymorphic ratio type, usually used for rational numbers.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Ratio<T> {
Expand Down Expand Up @@ -492,6 +521,9 @@ pub trait WalletRead {
/// will be interpreted as belonging to that account.
type AccountId: Copy + Debug + Eq + Hash;

/// The concrete account type used by this wallet backend.
type Account: Account<Self::AccountId>;

/// Verifies that the given seed corresponds to the viewing key for the specified account.
///
/// Returns:
Expand Down Expand Up @@ -602,11 +634,11 @@ pub trait WalletRead {
&self,
) -> Result<HashMap<Self::AccountId, UnifiedFullViewingKey>, Self::Error>;

/// Returns the account id corresponding to a given [`UnifiedFullViewingKey`], if any.
/// Returns the account corresponding to a given [`UnifiedFullViewingKey`], if any.
fn get_account_for_ufvk(
&self,
ufvk: &UnifiedFullViewingKey,
) -> Result<Option<Self::AccountId>, Self::Error>;
) -> Result<Option<Self::Account>, Self::Error>;

/// Returns the wallet balances and sync status for an account given the specified minimum
/// number of confirmations, or `Ok(None)` if the wallet has no balance data available.
Expand Down Expand Up @@ -1438,6 +1470,7 @@ pub mod testing {
impl WalletRead for MockWalletDb {
type Error = ();
type AccountId = u32;
type Account = (Self::AccountId, UnifiedFullViewingKey);

fn validate_seed(
&self,
Expand Down Expand Up @@ -1523,7 +1556,7 @@ pub mod testing {
fn get_account_for_ufvk(
&self,
_ufvk: &UnifiedFullViewingKey,
) -> Result<Option<Self::AccountId>, Self::Error> {
) -> Result<Option<Self::Account>, Self::Error> {
Ok(None)
}

Expand Down
11 changes: 6 additions & 5 deletions zcash_client_backend/src/data_api/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ use super::InputSource;
use crate::{
address::Address,
data_api::{
error::Error, SentTransaction, SentTransactionOutput, WalletCommitmentTrees, WalletRead,
WalletWrite,
error::Error, Account, SentTransaction, SentTransactionOutput, WalletCommitmentTrees,
WalletRead, WalletWrite,
},
decrypt_transaction,
fees::{self, DustOutputPolicy},
Expand Down Expand Up @@ -269,7 +269,7 @@ where
wallet_db,
params,
StandardFeeRule::PreZip313,
account,
account.id(),
min_confirmations,
to,
amount,
Expand Down Expand Up @@ -380,7 +380,7 @@ where
let proposal = propose_transfer(
wallet_db,
params,
account,
account.id(),
input_selector,
request,
min_confirmations,
Expand Down Expand Up @@ -694,7 +694,8 @@ where
let account = wallet_db
.get_account_for_ufvk(&usk.to_unified_full_viewing_key())
.map_err(Error::DataSource)?
.ok_or(Error::KeyNotRecognized)?;
.ok_or(Error::KeyNotRecognized)?
.id();

let (sapling_anchor, sapling_inputs) =
if proposal_step.involves(PoolType::Shielded(ShieldedProtocol::Sapling)) {
Expand Down
5 changes: 3 additions & 2 deletions zcash_client_sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> InputSource for
impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for WalletDb<C, P> {
type Error = SqliteClientError;
type AccountId = AccountId;
type Account = (AccountId, Option<UnifiedFullViewingKey>);

fn validate_seed(
&self,
Expand Down Expand Up @@ -373,8 +374,8 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
fn get_account_for_ufvk(
&self,
ufvk: &UnifiedFullViewingKey,
) -> Result<Option<AccountId>, Self::Error> {
wallet::get_account_for_ufvk(self.conn.borrow(), ufvk)
) -> Result<Option<Self::Account>, Self::Error> {
wallet::get_account_for_ufvk(self.conn.borrow(), &self.params, ufvk)
}

fn get_wallet_summary(
Expand Down
27 changes: 21 additions & 6 deletions zcash_client_sqlite/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,31 +693,46 @@ pub(crate) fn get_unified_full_viewing_keys<P: consensus::Parameters>(

/// Returns the account id corresponding to a given [`UnifiedFullViewingKey`],
/// if any.
pub(crate) fn get_account_for_ufvk(
pub(crate) fn get_account_for_ufvk<P: consensus::Parameters>(
conn: &rusqlite::Connection,
params: &P,
ufvk: &UnifiedFullViewingKey,
) -> Result<Option<AccountId>, SqliteClientError> {
) -> Result<Option<(AccountId, Option<UnifiedFullViewingKey>)>, SqliteClientError> {
#[cfg(feature = "transparent-inputs")]
let transparent_item = ufvk.transparent().map(|k| k.serialize());
#[cfg(not(feature = "transparent-inputs"))]
let transparent_item: Option<Vec<u8>> = None;

let mut stmt = conn.prepare(
"SELECT id
"SELECT id, ufvk
FROM accounts
WHERE orchard_fvk_item_cache = :orchard_fvk_item_cache
OR sapling_fvk_item_cache = :sapling_fvk_item_cache
OR p2pkh_fvk_item_cache = :p2pkh_fvk_item_cache",
)?;

let accounts = stmt
.query_and_then::<_, rusqlite::Error, _, _>(
.query_and_then::<_, SqliteClientError, _, _>(
named_params![
":orchard_fvk_item_cache": ufvk.orchard().map(|k| k.to_bytes()),
":sapling_fvk_item_cache": ufvk.sapling().map(|k| k.to_bytes()),
":p2pkh_fvk_item_cache": transparent_item,
],
|row| row.get::<_, u32>(0).map(AccountId),
|row| {
let account_id = row.get::<_, u32>(0).map(AccountId)?;
Ok((
account_id,
row.get::<_, Option<String>>(1)?
.map(|ufvk_str| UnifiedFullViewingKey::decode(params, &ufvk_str))
.transpose()
.map_err(|e| {
SqliteClientError::CorruptedData(format!(
"Could not decode unified full viewing key for account {:?}: {}",
account_id, e
))
})?,
))
},
)?
.collect::<Result<Vec<_>, _>>()?;

Expand All @@ -726,7 +741,7 @@ pub(crate) fn get_account_for_ufvk(
"Mutiple account records correspond to a single UFVK".to_owned(),
))
} else {
Ok(accounts.first().copied())
Ok(accounts.into_iter().next())
}
}

Expand Down

0 comments on commit 1fdd0dc

Please sign in to comment.