Skip to content

Commit

Permalink
zcash_client_backend: Require wallet metadata for balance calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Oct 23, 2024
1 parent fde0fc9 commit 47b1065
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 78 deletions.
118 changes: 60 additions & 58 deletions zcash_client_backend/src/data_api/wallet/input_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,61 +482,6 @@ impl<DbT: InputSource> InputSelector for GreedyInputSelector<DbT> {
}
}

#[cfg(not(feature = "transparent-inputs"))]
let ephemeral_balance = None;

#[cfg(feature = "transparent-inputs")]
let (ephemeral_balance, tr1_balance_opt) = {
if tr1_transparent_outputs.is_empty() {
(None, None)
} else {
// The ephemeral input going into transaction 1 must be able to pay that
// transaction's fee, as well as the TEX address payments.

// First compute the required total with an additional zero input,
// catching the `InsufficientFunds` error to obtain the required amount
// given the provided change strategy. Ignore the change memo in order
// to avoid adding a change output.
let tr1_required_input_value = match change_strategy
.compute_balance::<_, DbT::NoteRef>(
params,
target_height,
&[] as &[WalletTransparentOutput],
&tr1_transparent_outputs,
&sapling::EmptyBundleView,
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
Some(&EphemeralBalance::Input(NonNegativeAmount::ZERO)),
None,
) {
Err(ChangeError::InsufficientFunds { required, .. }) => required,
Err(ChangeError::DustInputs { .. }) => unreachable!("no inputs were supplied"),
Err(other) => return Err(InputSelectorError::Change(other)),
Ok(_) => NonNegativeAmount::ZERO, // shouldn't happen
};

// Now recompute to obtain the `TransactionBalance` and verify that it
// fully accounts for the required fees.
let tr1_balance = change_strategy.compute_balance::<_, DbT::NoteRef>(
params,
target_height,
&[] as &[WalletTransparentOutput],
&tr1_transparent_outputs,
&sapling::EmptyBundleView,
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
Some(&EphemeralBalance::Input(tr1_required_input_value)),
None,
)?;
assert_eq!(tr1_balance.total(), tr1_balance.fee_required());

(
Some(EphemeralBalance::Output(tr1_required_input_value)),
Some(tr1_balance),
)
}
};

let mut shielded_inputs = SpendableNotes::empty();
let mut prior_available = NonNegativeAmount::ZERO;
let mut amount_required = NonNegativeAmount::ZERO;
Expand Down Expand Up @@ -598,6 +543,63 @@ impl<DbT: InputSource> InputSelector for GreedyInputSelector<DbT> {
.fetch_wallet_meta(wallet_db, account, &selected_input_ids)
.map_err(InputSelectorError::DataSource)?;

#[cfg(not(feature = "transparent-inputs"))]
let ephemeral_balance = None;

#[cfg(feature = "transparent-inputs")]
let (ephemeral_balance, tr1_balance_opt) = {
if tr1_transparent_outputs.is_empty() {
(None, None)
} else {
// The ephemeral input going into transaction 1 must be able to pay that
// transaction's fee, as well as the TEX address payments.

// First compute the required total with an additional zero input,
// catching the `InsufficientFunds` error to obtain the required amount
// given the provided change strategy. Ignore the change memo in order
// to avoid adding a change output.
let tr1_required_input_value = match change_strategy
.compute_balance::<_, DbT::NoteRef>(
params,
target_height,
&[] as &[WalletTransparentOutput],
&tr1_transparent_outputs,
&sapling::EmptyBundleView,
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
Some(&EphemeralBalance::Input(NonNegativeAmount::ZERO)),
&wallet_meta,
) {
Err(ChangeError::InsufficientFunds { required, .. }) => required,
Err(ChangeError::DustInputs { .. }) => {
unreachable!("no inputs were supplied")
}
Err(other) => return Err(InputSelectorError::Change(other)),
Ok(_) => NonNegativeAmount::ZERO, // shouldn't happen
};

// Now recompute to obtain the `TransactionBalance` and verify that it
// fully accounts for the required fees.
let tr1_balance = change_strategy.compute_balance::<_, DbT::NoteRef>(
params,
target_height,
&[] as &[WalletTransparentOutput],
&tr1_transparent_outputs,
&sapling::EmptyBundleView,
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
Some(&EphemeralBalance::Input(tr1_required_input_value)),
&wallet_meta,
)?;
assert_eq!(tr1_balance.total(), tr1_balance.fee_required());

(
Some(EphemeralBalance::Output(tr1_required_input_value)),
Some(tr1_balance),
)
}
};

// In the ZIP 320 case, this is the balance for transaction 0, taking into account
// the ephemeral output.
let balance = change_strategy.compute_balance(
Expand All @@ -617,7 +619,7 @@ impl<DbT: InputSource> InputSelector for GreedyInputSelector<DbT> {
&orchard_outputs[..],
),
ephemeral_balance.as_ref(),
Some(&wallet_meta),
&wallet_meta,
);

match balance {
Expand Down Expand Up @@ -819,7 +821,7 @@ impl<DbT: InputSource> ShieldingSelector for GreedyInputSelector<DbT> {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
Some(&wallet_meta),
&wallet_meta,
);

let balance = match trial_balance {
Expand All @@ -837,7 +839,7 @@ impl<DbT: InputSource> ShieldingSelector for GreedyInputSelector<DbT> {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
Some(&wallet_meta),
&wallet_meta,
)?
}
Err(other) => return Err(InputSelectorError::Change(other)),
Expand Down
2 changes: 1 addition & 1 deletion zcash_client_backend/src/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ pub trait ChangeStrategy {
sapling: &impl sapling::BundleView<NoteRefT>,
#[cfg(feature = "orchard")] orchard: &impl orchard::BundleView<NoteRefT>,
ephemeral_balance: Option<&EphemeralBalance>,
wallet_meta: Option<&Self::WalletMetaT>,
wallet_meta: &Self::WalletMetaT,
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>>;
}

Expand Down
6 changes: 3 additions & 3 deletions zcash_client_backend/src/fees/fixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<I: InputSource> ChangeStrategy for SingleOutputChangeStrategy<I> {
sapling: &impl sapling_fees::BundleView<NoteRefT>,
#[cfg(feature = "orchard")] orchard: &impl orchard_fees::BundleView<NoteRefT>,
ephemeral_balance: Option<&EphemeralBalance>,
_wallet_meta: Option<&Self::WalletMetaT>,
_wallet_meta: &Self::WalletMetaT,
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>> {
let split_policy = SplitPolicy::single_output();
let cfg = SinglePoolBalanceConfig::new(
Expand Down Expand Up @@ -168,7 +168,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -218,7 +218,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down
2 changes: 1 addition & 1 deletion zcash_client_backend/src/fees/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl<I: InputSource> ChangeStrategy for SingleOutputChangeStrategy<I> {
sapling: &impl sapling_fees::BundleView<NoteRefT>,
#[cfg(feature = "orchard")] orchard: &impl orchard_fees::BundleView<NoteRefT>,
ephemeral_balance: Option<&EphemeralBalance>,
wallet_meta: Option<&Self::WalletMetaT>,
wallet_meta: &Self::WalletMetaT,
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>> {
#[allow(deprecated)]
match self.fee_rule() {
Expand Down
30 changes: 15 additions & 15 deletions zcash_client_backend/src/fees/zip317.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl<I: InputSource> ChangeStrategy for SingleOutputChangeStrategy<I> {
sapling: &impl sapling_fees::BundleView<NoteRefT>,
#[cfg(feature = "orchard")] orchard: &impl orchard_fees::BundleView<NoteRefT>,
ephemeral_balance: Option<&EphemeralBalance>,
_wallet_meta: Option<&Self::WalletMetaT>,
_wallet_meta: &Self::WalletMetaT,
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>> {
let split_policy = SplitPolicy::single_output();
let cfg = SinglePoolBalanceConfig::new(
Expand Down Expand Up @@ -189,7 +189,7 @@ impl<I: InputSource> ChangeStrategy for MultiOutputChangeStrategy<I> {
sapling: &impl sapling_fees::BundleView<NoteRefT>,
#[cfg(feature = "orchard")] orchard: &impl orchard_fees::BundleView<NoteRefT>,
ephemeral_balance: Option<&EphemeralBalance>,
wallet_meta: Option<&Self::WalletMetaT>,
wallet_meta: &Self::WalletMetaT,
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>> {
let cfg = SinglePoolBalanceConfig::new(
params,
Expand All @@ -204,7 +204,7 @@ impl<I: InputSource> ChangeStrategy for MultiOutputChangeStrategy<I> {

single_pool_output_balance(
cfg,
wallet_meta,
Some(wallet_meta),
target_height,
transparent_inputs,
transparent_outputs,
Expand Down Expand Up @@ -277,7 +277,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -324,11 +324,11 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
Some(&WalletMeta::new(
&WalletMeta::new(
existing_notes,
#[cfg(feature = "orchard")]
0,
)),
),
)
};

Expand Down Expand Up @@ -380,11 +380,11 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
Some(&WalletMeta::new(
&WalletMeta::new(
0,
#[cfg(feature = "orchard")]
0,
)),
),
);

assert_matches!(
Expand Down Expand Up @@ -435,7 +435,7 @@ mod tests {
))][..],
),
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -489,7 +489,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -534,7 +534,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -579,7 +579,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -630,7 +630,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -692,7 +692,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

assert_matches!(
Expand Down Expand Up @@ -744,7 +744,7 @@ mod tests {
#[cfg(feature = "orchard")]
&orchard_fees::EmptyBundleView,
None,
None,
&(),
);

// We will get an error here, because the dust input isn't free to add
Expand Down

0 comments on commit 47b1065

Please sign in to comment.