Skip to content

Commit

Permalink
zcash_client_backend: Add name and key source metadata to accounts.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Dec 3, 2024
1 parent 01b88a7 commit 9bef4ee
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 58 deletions.
13 changes: 13 additions & 0 deletions zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this library adheres to Rust's notion of

## [Unreleased]

### Changed
- `zcash_client_backend::data_api::WalletRead`:
- The `create_account`, `import_account_hd`, and `import_account_ufvk`
methods now each take additional `account_name` and `key_source` arguments.
These allow the wallet backend to store additional metadata that is useful
to applications managing these accounts.
- `zcash_client_backend::data_api::AccountSource`:
- Both the `Derived` and `Importants` now have an additional `key_source`
property that is used to convey application-specific key source metadata.
- The `Copy` impl for this type has been removed.
- `zcash_client_backend::data_api::Account` has an additional `name` method
that returns the human-readable name of the account, if any.

## [0.15.0] - 2024-11-14

### Added
Expand Down
70 changes: 62 additions & 8 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,16 +343,20 @@ pub enum AccountPurpose {
}

/// The kinds of accounts supported by `zcash_client_backend`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum AccountSource {
/// An account derived from a known seed.
Derived {
seed_fingerprint: SeedFingerprint,
account_index: zip32::AccountId,
key_source: Option<String>,
},

/// An account imported from a viewing key.
Imported { purpose: AccountPurpose },
Imported {
purpose: AccountPurpose,
key_source: Option<String>,
},
}

/// A set of capabilities that a client account must provide.
Expand All @@ -362,15 +366,18 @@ pub trait Account {
/// Returns the unique identifier for the account.
fn id(&self) -> Self::AccountId;

/// Returns the human-readable name for the account, if any has been configured.
fn name(&self) -> Option<&str>;

/// Returns whether this account is derived or imported, and the derivation parameters
/// if applicable.
fn source(&self) -> AccountSource;
fn source(&self) -> &AccountSource;

/// Returns whether the account is a spending account or a view-only account.
fn purpose(&self) -> AccountPurpose {
match self.source() {
AccountSource::Derived { .. } => AccountPurpose::Spending,
AccountSource::Imported { purpose } => purpose,
AccountSource::Imported { purpose, .. } => *purpose,

Check warning on line 380 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L380

Added line #L380 was not covered by tests
}
}

Expand All @@ -396,9 +403,10 @@ impl<A: Copy> Account for (A, UnifiedFullViewingKey) {
self.0
}

fn source(&self) -> AccountSource {
AccountSource::Imported {
fn source(&self) -> &AccountSource {
&AccountSource::Imported {

Check warning on line 407 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L406-L407

Added lines #L406 - L407 were not covered by tests
purpose: AccountPurpose::ViewOnly,
key_source: None,

Check warning on line 409 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L409

Added line #L409 was not covered by tests
}
}

Expand All @@ -409,6 +417,10 @@ impl<A: Copy> Account for (A, UnifiedFullViewingKey) {
fn uivk(&self) -> UnifiedIncomingViewingKey {
self.1.to_unified_incoming_viewing_key()
}

fn name(&self) -> Option<&str> {
None

Check warning on line 422 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L421-L422

Added lines #L421 - L422 were not covered by tests
}
}

#[cfg(any(test, feature = "test-dependencies"))]
Expand All @@ -419,9 +431,14 @@ impl<A: Copy> Account for (A, UnifiedIncomingViewingKey) {
self.0
}

fn source(&self) -> AccountSource {
AccountSource::Imported {
fn name(&self) -> Option<&str> {
None

Check warning on line 435 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L434-L435

Added lines #L434 - L435 were not covered by tests
}

fn source(&self) -> &AccountSource {
&AccountSource::Imported {

Check warning on line 439 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L438-L439

Added lines #L438 - L439 were not covered by tests
purpose: AccountPurpose::ViewOnly,
key_source: None,

Check warning on line 441 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L441

Added line #L441 was not covered by tests
}
}

Expand Down Expand Up @@ -2194,6 +2211,15 @@ pub trait WalletWrite: WalletRead {
///
/// The [`WalletWrite`] trait documentation has more details about account creation and import.
///
/// # Arguments
/// - `account_name`: A human-readable name for the account.
/// - `seed`: The 256-byte (at least) HD seed from which to derive the account UFVK.
/// - `birthday`: Metadata about where to start scanning blocks to find transactions intended
/// for the account.
/// - `key_source`: A string identifier or other metadata describing the source of the seed.
/// This is treated as opaque metadata by the wallet backend; it is provided for use by
/// applications which need to track additional identifying information for an account.
///
/// # Implementation notes
///
/// Implementations of this method **MUST NOT** "fill in gaps" by selecting an account index
Expand All @@ -2207,8 +2233,10 @@ pub trait WalletWrite: WalletRead {
/// [ZIP 316]: https://zips.z.cash/zip-0316
fn create_account(
&mut self,
account_name: &str,
seed: &SecretVec<u8>,
birthday: &AccountBirthday,
key_source: Option<&str>,
) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error>;

/// Tells the wallet to track a specific account index for a given seed.
Expand All @@ -2225,16 +2253,29 @@ pub trait WalletWrite: WalletRead {
///
/// The [`WalletWrite`] trait documentation has more details about account creation and import.
///
/// # Arguments
/// - `account_name`: A human-readable name for the account.
/// - `seed`: The 256-byte (at least) HD seed from which to derive the account UFVK.
/// - `account_index`: The ZIP 32 account-level component of the HD derivation path at
/// which to derive the account's UFVK.
/// - `birthday`: Metadata about where to start scanning blocks to find transactions intended
/// for the account.
/// - `key_source`: A string identifier or other metadata describing the source of the seed.
/// This is treated as opaque metadata by the wallet backend; it is provided for use by
/// applications which need to track additional identifying information for an account.
///
/// # Panics
///
/// Panics if the length of the seed is not between 32 and 252 bytes inclusive.
///
/// [ZIP 316]: https://zips.z.cash/zip-0316
fn import_account_hd(
&mut self,
account_name: &str,
seed: &SecretVec<u8>,
account_index: zip32::AccountId,
birthday: &AccountBirthday,
key_source: Option<&str>,
) -> Result<(Self::Account, UnifiedSpendingKey), Self::Error>;

/// Tells the wallet to track an account using a unified full viewing key.
Expand All @@ -2250,14 +2291,27 @@ pub trait WalletWrite: WalletRead {
///
/// The [`WalletWrite`] trait documentation has more details about account creation and import.
///
/// # Arguments
/// - `account_name`: A human-readable name for the account.
/// - `unified_key`: The UFVK used to detect transactions involving the account.
/// - `birthday`: Metadata about where to start scanning blocks to find transactions intended
/// for the account.
/// - `purpose`: Metadata describing whether or not data required for spending should be
/// tracked by the wallet.
/// - `key_source`: A string identifier or other metadata describing the source of the seed.
/// This is treated as opaque metadata by the wallet backend; it is provided for use by
/// applications which need to track additional identifying information for an account.
///
/// # Panics
///
/// Panics if the length of the seed is not between 32 and 252 bytes inclusive.
fn import_account_ufvk(
&mut self,
account_name: &str,
unified_key: &UnifiedFullViewingKey,
birthday: &AccountBirthday,
purpose: AccountPurpose,
key_source: Option<&str>,
) -> Result<Self::Account, Self::Error>;

/// Generates and persists the next available diversified address, given the current
Expand Down
18 changes: 15 additions & 3 deletions zcash_client_backend/src/data_api/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,11 @@ impl<A: Account> Account for TestAccount<A> {
self.account.id()
}

fn source(&self) -> AccountSource {
fn name(&self) -> Option<&str> {
self.account.name()

Check warning on line 378 in zcash_client_backend/src/data_api/testing.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api/testing.rs#L377-L378

Added lines #L377 - L378 were not covered by tests
}

fn source(&self) -> &AccountSource {

Check warning on line 381 in zcash_client_backend/src/data_api/testing.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api/testing.rs#L381

Added line #L381 was not covered by tests
self.account.source()
}

Expand Down Expand Up @@ -1594,10 +1598,12 @@ impl<Cache, DsFactory: DataStoreFactory> TestBuilder<Cache, DsFactory> {
let seed = Secret::new(vec![0u8; 32]);
let (account, usk) = match self.account_index {
Some(index) => wallet_data
.import_account_hd(&seed, index, &birthday)
.import_account_hd("", &seed, index, &birthday, None)
.unwrap(),
None => {
let result = wallet_data.create_account(&seed, &birthday).unwrap();
let result = wallet_data
.create_account("", &seed, &birthday, None)
.unwrap();
(
wallet_data.get_account(result.0).unwrap().unwrap(),
result.1,
Expand Down Expand Up @@ -2596,8 +2602,10 @@ impl WalletWrite for MockWalletDb {

fn create_account(
&mut self,
_account_name: &str,
seed: &SecretVec<u8>,
_birthday: &AccountBirthday,
_key_source: Option<&str>,
) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error> {
let account = zip32::AccountId::ZERO;
UnifiedSpendingKey::from_seed(&self.network, seed.expose_secret(), account)
Expand All @@ -2607,18 +2615,22 @@ impl WalletWrite for MockWalletDb {

fn import_account_hd(
&mut self,
_account_name: &str,
_seed: &SecretVec<u8>,
_account_index: zip32::AccountId,
_birthday: &AccountBirthday,
_key_source: Option<&str>,
) -> Result<(Self::Account, UnifiedSpendingKey), Self::Error> {
todo!()
}

fn import_account_ufvk(
&mut self,
_account_name: &str,
_unified_key: &UnifiedFullViewingKey,
_birthday: &AccountBirthday,
_purpose: AccountPurpose,
_key_source: Option<&str>,
) -> Result<Self::Account, Self::Error> {
todo!()
}
Expand Down
20 changes: 16 additions & 4 deletions zcash_client_backend/src/data_api/testing/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1559,10 +1559,16 @@ pub fn external_address_change_spends_detected_in_restore_from_seed<T: ShieldedP
// Add two accounts to the wallet.
let seed = Secret::new([0u8; 32].to_vec());
let birthday = AccountBirthday::from_sapling_activation(st.network(), BlockHash([0; 32]));
let (account1, usk) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
let (account1, usk) = st
.wallet_mut()
.create_account("account1", &seed, &birthday, None)
.unwrap();
let dfvk = T::sk_to_fvk(T::usk_to_sk(&usk));

let (account2, usk2) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
let (account2, usk2) = st
.wallet_mut()
.create_account("account2", &seed, &birthday, None)
.unwrap();
let dfvk2 = T::sk_to_fvk(T::usk_to_sk(&usk2));

// Add funds to the wallet in a single note
Expand Down Expand Up @@ -1623,13 +1629,19 @@ pub fn external_address_change_spends_detected_in_restore_from_seed<T: ShieldedP
st.reset();

// Account creation and DFVK derivation should be deterministic.
let (account1, restored_usk) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
let (account1, restored_usk) = st
.wallet_mut()
.create_account("account1_restored", &seed, &birthday, None)
.unwrap();
assert!(T::fvks_equal(
&T::sk_to_fvk(T::usk_to_sk(&restored_usk)),
&dfvk,
));

let (account2, restored_usk2) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
let (account2, restored_usk2) = st
.wallet_mut()
.create_account("account2_restored", &seed, &birthday, None)
.unwrap();
assert!(T::fvks_equal(
&T::sk_to_fvk(T::usk_to_sk(&restored_usk2)),
&dfvk2,
Expand Down
Loading

0 comments on commit 9bef4ee

Please sign in to comment.