zcash_client_backend: Expose the kind of an `Account`

This commit is contained in:
Jack Grigg 2024-03-13 19:04:45 +00:00
parent bc6aa955ff
commit 65093487c3
6 changed files with 61 additions and 42 deletions

View File

@ -16,6 +16,7 @@ and this library adheres to Rust's notion of
- `Account`
- `AccountBalance::with_orchard_balance_mut`
- `AccountBirthday::orchard_frontier`
- `AccountKind`
- `BlockMetadata::orchard_tree_size`
- `DecryptedTransaction::{new, tx(), orchard_outputs()}`
- `ScannedBlock::orchard`

View File

@ -315,12 +315,32 @@ impl AccountBalance {
}
}
/// The kinds of accounts supported by `zcash_client_backend`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AccountKind {
/// An account derived from a known seed.
Derived {
seed_fingerprint: HdSeedFingerprint,
account_index: zip32::AccountId,
},
/// An account imported from a viewing key.
Imported,
}
/// 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 whether this account is derived or imported, and the derivation parameters
/// if applicable.
fn kind(&self) -> AccountKind;
/// Returns the UFVK that the wallet backend has stored for the account, if any.
///
/// Accounts for which this returns `None` cannot be used in wallet contexts, because
/// they are unable to maintain an accurate balance.
fn ufvk(&self) -> Option<&UnifiedFullViewingKey>;
}
@ -329,6 +349,10 @@ impl<A: Copy> Account<A> for (A, UnifiedFullViewingKey) {
self.0
}
fn kind(&self) -> AccountKind {
AccountKind::Imported
}
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
Some(&self.1)
}
@ -339,6 +363,10 @@ impl<A: Copy> Account<A> for (A, Option<UnifiedFullViewingKey>) {
self.0
}
fn kind(&self) -> AccountKind {
AccountKind::Imported
}
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
self.1.as_ref()
}

View File

@ -60,9 +60,9 @@ use zcash_client_backend::{
self,
chain::{BlockSource, ChainState, CommitmentTreeRoot},
scanning::{ScanPriority, ScanRange},
AccountBirthday, BlockMetadata, DecryptedTransaction, InputSource, NullifierQuery,
ScannedBlock, SentTransaction, WalletCommitmentTrees, WalletRead, WalletSummary,
WalletWrite, SAPLING_SHARD_HEIGHT,
Account, AccountBirthday, AccountKind, BlockMetadata, DecryptedTransaction, InputSource,
NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, WalletRead,
WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
},
keys::{
AddressGenerationError, UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey,
@ -100,7 +100,7 @@ pub mod error;
pub mod wallet;
use wallet::{
commitment_tree::{self, put_shard_roots},
AccountType, SubtreeScanProgress,
SubtreeScanProgress,
};
#[cfg(test)]
@ -307,10 +307,10 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
seed: &SecretVec<u8>,
) -> Result<bool, Self::Error> {
if let Some(account) = wallet::get_account(self, account_id)? {
if let AccountType::Derived {
if let AccountKind::Derived {
seed_fingerprint,
account_index,
} = account.kind
} = account.kind()
{
let seed_fingerprint_match = HdSeedFingerprint::from_seed(seed) == seed_fingerprint;
@ -507,7 +507,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
let account_id = wallet::add_account(
wdb.conn.0,
&wdb.params,
AccountType::Derived {
AccountKind::Derived {
seed_fingerprint,
account_index,
},

View File

@ -81,7 +81,7 @@ use zcash_client_backend::{
address::{Address, UnifiedAddress},
data_api::{
scanning::{ScanPriority, ScanRange},
AccountBalance, AccountBirthday, BlockMetadata, Ratio, SentTransactionOutput,
AccountBalance, AccountBirthday, AccountKind, BlockMetadata, Ratio, SentTransactionOutput,
WalletSummary, SAPLING_SHARD_HEIGHT,
},
encoding::AddressCodec,
@ -138,27 +138,13 @@ pub(crate) mod scanning;
pub(crate) const BLOCK_SAPLING_FRONTIER_ABSENT: &[u8] = &[0x0];
/// This tracks the allowed values of the `account_type` column of the `accounts` table
/// and should not be made public.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum AccountType {
/// An account derived from a known seed.
Derived {
seed_fingerprint: HdSeedFingerprint,
account_index: zip32::AccountId,
},
/// An account imported from a viewing key.
Imported,
}
fn parse_account_kind(
account_type: u32,
hd_seed_fingerprint: Option<[u8; 32]>,
hd_account_index: Option<u32>,
) -> Result<AccountType, SqliteClientError> {
) -> Result<AccountKind, SqliteClientError> {
match (account_type, hd_seed_fingerprint, hd_account_index) {
(0, Some(seed_fp), Some(account_index)) => Ok(AccountType::Derived {
(0, Some(seed_fp), Some(account_index)) => Ok(AccountKind::Derived {
seed_fingerprint: HdSeedFingerprint::from_bytes(seed_fp),
account_index: zip32::AccountId::try_from(account_index).map_err(|_| {
SqliteClientError::CorruptedData(
@ -166,7 +152,7 @@ fn parse_account_kind(
)
})?,
}),
(1, None, None) => Ok(AccountType::Imported),
(1, None, None) => Ok(AccountKind::Imported),
(0, None, None) | (1, Some(_), Some(_)) => Err(SqliteClientError::CorruptedData(
"Wallet DB account_type constraint violated".to_string(),
)),
@ -176,10 +162,10 @@ fn parse_account_kind(
}
}
fn account_kind_code(value: AccountType) -> u32 {
fn account_kind_code(value: AccountKind) -> u32 {
match value {
AccountType::Derived { .. } => 0,
AccountType::Imported => 1,
AccountKind::Derived { .. } => 0,
AccountKind::Imported => 1,
}
}
@ -203,7 +189,7 @@ pub(crate) enum ViewingKey {
#[derive(Debug, Clone)]
pub(crate) struct Account {
account_id: AccountId,
pub(crate) kind: AccountType,
kind: AccountKind,
viewing_key: ViewingKey,
}
@ -229,6 +215,10 @@ impl zcash_client_backend::data_api::Account<AccountId> for Account {
self.account_id
}
fn kind(&self) -> AccountKind {
self.kind
}
fn ufvk(&self) -> Option<&UnifiedFullViewingKey> {
self.viewing_key.ufvk()
}
@ -333,16 +323,16 @@ pub(crate) fn ufvk_to_uivk<P: consensus::Parameters>(
pub(crate) fn add_account<P: consensus::Parameters>(
conn: &rusqlite::Transaction,
params: &P,
kind: AccountType,
kind: AccountKind,
viewing_key: ViewingKey,
birthday: AccountBirthday,
) -> Result<AccountId, SqliteClientError> {
let (hd_seed_fingerprint, hd_account_index) = match kind {
AccountType::Derived {
AccountKind::Derived {
seed_fingerprint,
account_index,
} => (Some(seed_fingerprint), Some(account_index)),
AccountType::Imported => (None, None),
AccountKind::Imported => (None, None),
};
let orchard_item = viewing_key
@ -2695,12 +2685,12 @@ mod tests {
use std::num::NonZeroU32;
use sapling::zip32::ExtendedSpendingKey;
use zcash_client_backend::data_api::{AccountBirthday, WalletRead};
use zcash_client_backend::data_api::{AccountBirthday, AccountKind, WalletRead};
use zcash_primitives::{block::BlockHash, transaction::components::amount::NonNegativeAmount};
use crate::{
testing::{AddressType, BlockCache, TestBuilder, TestState},
wallet::{get_account, AccountType},
wallet::get_account,
AccountId,
};
@ -2853,7 +2843,7 @@ mod tests {
let expected_account_index = zip32::AccountId::try_from(0).unwrap();
assert_matches!(
account_parameters.kind,
AccountType::Derived{account_index, ..} if account_index == expected_account_index
AccountKind::Derived{account_index, ..} if account_index == expected_account_index
);
}

View File

@ -1282,9 +1282,9 @@ mod tests {
#[test]
#[cfg(feature = "transparent-inputs")]
fn account_produces_expected_ua_sequence() {
use zcash_client_backend::data_api::AccountBirthday;
use zcash_client_backend::data_api::{AccountBirthday, AccountKind};
use crate::wallet::{get_account, AccountType};
use crate::wallet::get_account;
let network = Network::MainNetwork;
let data_file = NamedTempFile::new().unwrap();
@ -1303,7 +1303,7 @@ mod tests {
get_account(&db_data, account_id),
Ok(Some(account)) if matches!(
account.kind,
AccountType::Derived{account_index, ..} if account_index == zip32::AccountId::ZERO,
AccountKind::Derived{account_index, ..} if account_index == zip32::AccountId::ZERO,
)
);

View File

@ -1,11 +1,11 @@
use std::{collections::HashSet, rc::Rc};
use crate::wallet::{account_kind_code, init::WalletMigrationError, ufvk_to_uivk, AccountType};
use crate::wallet::{account_kind_code, init::WalletMigrationError, ufvk_to_uivk};
use rusqlite::{named_params, Transaction};
use schemer_rusqlite::RusqliteMigration;
use secrecy::{ExposeSecret, SecretVec};
use uuid::Uuid;
use zcash_client_backend::keys::UnifiedSpendingKey;
use zcash_client_backend::{data_api::AccountKind, keys::UnifiedSpendingKey};
use zcash_keys::keys::{HdSeedFingerprint, UnifiedFullViewingKey};
use zcash_primitives::consensus;
@ -44,11 +44,11 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
type Error = WalletMigrationError;
fn up(&self, transaction: &Transaction) -> Result<(), WalletMigrationError> {
let account_type_derived = account_kind_code(AccountType::Derived {
let account_type_derived = account_kind_code(AccountKind::Derived {
seed_fingerprint: HdSeedFingerprint::from_bytes([0; 32]),
account_index: zip32::AccountId::ZERO,
});
let account_type_imported = account_kind_code(AccountType::Imported);
let account_type_imported = account_kind_code(AccountKind::Imported);
transaction.execute_batch(
&format!(r#"
PRAGMA foreign_keys = OFF;