Skip to content

Commit

Permalink
Merge pull request #1570 from pacu/feature/transparent-balance
Browse files Browse the repository at this point in the history
[#1411] Refactor AccountBalance to use Balance for transparent funds
  • Loading branch information
daira authored Dec 7, 2024
2 parents f6040a1 + bd45812 commit 3725313
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 77 deletions.
23 changes: 21 additions & 2 deletions zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,37 @@ and this library adheres to Rust's notion of
- `zcash_client_backend::data_api::AccountSource::key_derivation`

### Changed
- `zcash_client_backend::data_api::AccountBalance`: Refactored to use `Balance`
for transparent funds (issue #1411). It now has an `unshielded_balance()`
method that returns `Balance`, allowing the unshielded spendable, unshielded
pending change, and unshielded pending non-change values to be tracked
separately.
- `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.
- Both `Derived` and `Imported` alternatives of `AccountSource` now have an
additional `key_source` field 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.

### Deprecated
- `AccountBalance::unshielded`. Instead use `unshielded_balance` which
provides a `Balance` value. Its `total()` method can be used to obtain the
total of transparent funds.

### Removed
- `zcash_client_backend::AccountBalance::add_unshielded_value`. Instead use
`AccountBalance::with_unshielded_balance_mut` with a closure that calls
the appropriate `add_*_value` method(s) of `Balance` on its argument.
Note that the appropriate method(s) depend on whether the funds are
spendable, pending change, or pending non-change (previously, only the
total unshielded value was tracked).

## [0.15.0] - 2024-11-14

### Added
Expand Down
57 changes: 34 additions & 23 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,35 +231,31 @@ pub struct AccountBalance {
/// The value of unspent Orchard outputs belonging to the account.
orchard_balance: Balance,

/// The value of all unspent transparent outputs belonging to the account, irrespective of
/// confirmation depth.
///
/// Unshielded balances are not subject to confirmation-depth constraints, because the only
/// possible operation on a transparent balance is to shield it, it is possible to create a
/// zero-conf transaction to perform that shielding, and the resulting shielded notes will be
/// subject to normal confirmation rules.
unshielded: NonNegativeAmount,
/// The value of all unspent transparent outputs belonging to the account.
unshielded_balance: Balance,
}

impl AccountBalance {
/// The [`Balance`] value having zero values for all its fields.
pub const ZERO: Self = Self {
sapling_balance: Balance::ZERO,
orchard_balance: Balance::ZERO,
unshielded: NonNegativeAmount::ZERO,
unshielded_balance: Balance::ZERO,
};

fn check_total(&self) -> Result<NonNegativeAmount, BalanceError> {
(self.sapling_balance.total() + self.orchard_balance.total() + self.unshielded)
.ok_or(BalanceError::Overflow)
(self.sapling_balance.total()
+ self.orchard_balance.total()
+ self.unshielded_balance.total())
.ok_or(BalanceError::Overflow)
}

/// Returns the [`Balance`] of Sapling funds in the account.
pub fn sapling_balance(&self) -> &Balance {
&self.sapling_balance
}

/// Provides a `mutable reference to the [`Balance`] of Sapling funds in the account
/// Provides a mutable reference to the [`Balance`] of Sapling funds in the account
/// to the specified callback, checking invariants after the callback's action has been
/// evaluated.
pub fn with_sapling_balance_mut<A, E: From<BalanceError>>(
Expand All @@ -276,7 +272,7 @@ impl AccountBalance {
&self.orchard_balance
}

/// Provides a `mutable reference to the [`Balance`] of Orchard funds in the account
/// Provides a mutable reference to the [`Balance`] of Orchard funds in the account
/// to the specified callback, checking invariants after the callback's action has been
/// evaluated.
pub fn with_orchard_balance_mut<A, E: From<BalanceError>>(
Expand All @@ -289,22 +285,36 @@ impl AccountBalance {
}

/// Returns the total value of unspent transparent transaction outputs belonging to the wallet.
#[deprecated(
note = "this function is deprecated. Please use [`AccountBalance::unshielded_balance`] instead."
)]
pub fn unshielded(&self) -> NonNegativeAmount {
self.unshielded
self.unshielded_balance.total()
}

/// Returns the [`Balance`] of unshielded funds in the account.
pub fn unshielded_balance(&self) -> &Balance {
&self.unshielded_balance
}

/// Adds the specified value to the unshielded total, checking for overflow of
/// the total account balance.
pub fn add_unshielded_value(&mut self, value: NonNegativeAmount) -> Result<(), BalanceError> {
self.unshielded = (self.unshielded + value).ok_or(BalanceError::Overflow)?;
/// Provides a mutable reference to the [`Balance`] of transparent funds in the account
/// to the specified callback, checking invariants after the callback's action has been
/// evaluated.
pub fn with_unshielded_balance_mut<A, E: From<BalanceError>>(
&mut self,
f: impl FnOnce(&mut Balance) -> Result<A, E>,
) -> Result<A, E> {
let result = f(&mut self.unshielded_balance)?;
self.check_total()?;
Ok(())
Ok(result)
}

/// Returns the total value of funds belonging to the account.
pub fn total(&self) -> NonNegativeAmount {
(self.sapling_balance.total() + self.orchard_balance.total() + self.unshielded)
.expect("Account balance cannot overflow MAX_MONEY")
(self.sapling_balance.total()
+ self.orchard_balance.total()
+ self.unshielded_balance.total())
.expect("Account balance cannot overflow MAX_MONEY")
}

/// Returns the total value of shielded (Sapling and Orchard) funds that may immediately be
Expand Down Expand Up @@ -1254,8 +1264,9 @@ pub trait WalletRead {
/// or `Ok(None)` if the wallet has no initialized accounts.
fn get_wallet_birthday(&self) -> Result<Option<BlockHeight>, 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.
/// Returns a [`WalletSummary`] that represents the sync status, and the wallet balances
/// given the specified minimum number of confirmations for all accounts known to the
/// wallet; or `Ok(None)` if the wallet has no summary data available.
fn get_wallet_summary(
&self,
min_confirmations: u32,
Expand Down
Loading

0 comments on commit 3725313

Please sign in to comment.