Skip to content

Commit feabe6d

Browse files
committed
Remove complicated code to calculate the number of dust spends.
See zcash#1316 for re-adding it. The previous code was essentially dead because it had no side effects other than potentially returning an error. That error is unnecessary and incorrect when we are not actually performing dust spends. When we re-enable them, we should not try to make dust spends in any transaction with non-default `ephemeral_parameters`, or if the `dust_output_policy` is `DustAction::AddDustToFee`. That would guarantee that we will always have a shielded change output, which makes the calculations more tractable and excludes some potentially complicated interactions. Signed-off-by: Daira-Emma Hopwood <[email protected]>
1 parent f0e5aab commit feabe6d

File tree

1 file changed

+3
-132
lines changed

1 file changed

+3
-132
lines changed

zcash_client_backend/src/fees/zip317.rs

+3-132
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use zcash_primitives::{
1616
use crate::ShieldedProtocol;
1717

1818
use super::{
19-
common::{calculate_net_flows, single_change_output_balance, single_change_output_policy},
20-
sapling as sapling_fees, ChangeError, ChangeStrategy, DustOutputPolicy, TransactionBalance,
19+
common::single_change_output_balance, sapling as sapling_fees, ChangeError, ChangeStrategy,
20+
DustOutputPolicy, TransactionBalance,
2121
};
2222

2323
#[cfg(feature = "transparent-inputs")]
@@ -72,136 +72,7 @@ impl ChangeStrategy for SingleOutputChangeStrategy {
7272
dust_output_policy: &DustOutputPolicy,
7373
#[cfg(feature = "transparent-inputs")] ephemeral_parameters: &EphemeralParameters,
7474
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>> {
75-
// We intentionally never count ephemeral inputs as dust.
76-
let mut transparent_dust: Vec<_> = transparent_inputs
77-
.iter()
78-
.filter_map(|i| {
79-
// for now, we're just assuming p2pkh inputs, so we don't check the size of the input
80-
// script
81-
if i.coin().value < self.fee_rule.marginal_fee() {
82-
Some(i.outpoint().clone())
83-
} else {
84-
None
85-
}
86-
})
87-
.collect();
88-
89-
let mut sapling_dust: Vec<_> = sapling
90-
.inputs()
91-
.iter()
92-
.filter_map(|i| {
93-
if sapling_fees::InputView::<NoteRefT>::value(i) < self.fee_rule.marginal_fee() {
94-
Some(sapling_fees::InputView::<NoteRefT>::note_id(i).clone())
95-
} else {
96-
None
97-
}
98-
})
99-
.collect();
100-
101-
#[cfg(feature = "orchard")]
102-
let mut orchard_dust: Vec<NoteRefT> = orchard
103-
.inputs()
104-
.iter()
105-
.filter_map(|i| {
106-
if orchard_fees::InputView::<NoteRefT>::value(i) < self.fee_rule.marginal_fee() {
107-
Some(orchard_fees::InputView::<NoteRefT>::note_id(i).clone())
108-
} else {
109-
None
110-
}
111-
})
112-
.collect();
113-
#[cfg(not(feature = "orchard"))]
114-
let mut orchard_dust: Vec<NoteRefT> = vec![];
115-
116-
// Depending on the shape of the transaction, we may be able to spend up to
117-
// `grace_actions - 1` dust inputs. If we don't have any dust inputs though,
118-
// we don't need to worry about any of that.
119-
if !(transparent_dust.is_empty() && sapling_dust.is_empty() && orchard_dust.is_empty()) {
120-
let t_non_dust = transparent_inputs.len() - transparent_dust.len();
121-
let t_allowed_dust = transparent_outputs.len().saturating_sub(t_non_dust);
122-
123-
// We add one to either the Sapling or Orchard outputs for the (single)
124-
// change output. Note that this means that wallet-internal shielding
125-
// transactions are an opportunity to spend a dust note.
126-
let net_flows = calculate_net_flows::<NoteRefT, Self::FeeRule, Self::Error>(
127-
transparent_inputs,
128-
transparent_outputs,
129-
sapling,
130-
#[cfg(feature = "orchard")]
131-
orchard,
132-
#[cfg(feature = "transparent-inputs")]
133-
ephemeral_parameters.ephemeral_input_amount(),
134-
#[cfg(feature = "transparent-inputs")]
135-
ephemeral_parameters.ephemeral_output_amount(),
136-
)?;
137-
let (_, sapling_change, orchard_change) =
138-
single_change_output_policy(&net_flows, self.fallback_change_pool);
139-
140-
let s_non_dust = sapling.inputs().len() - sapling_dust.len();
141-
let s_allowed_dust =
142-
(sapling.outputs().len() + sapling_change).saturating_sub(s_non_dust);
143-
144-
#[cfg(feature = "orchard")]
145-
let (orchard_inputs_len, orchard_outputs_len) =
146-
(orchard.inputs().len(), orchard.outputs().len());
147-
#[cfg(not(feature = "orchard"))]
148-
let (orchard_inputs_len, orchard_outputs_len) = (0, 0);
149-
150-
let o_non_dust = orchard_inputs_len - orchard_dust.len();
151-
let o_allowed_dust = (orchard_outputs_len + orchard_change).saturating_sub(o_non_dust);
152-
153-
let available_grace_inputs = self
154-
.fee_rule
155-
.grace_actions()
156-
.saturating_sub(t_non_dust)
157-
.saturating_sub(s_non_dust)
158-
.saturating_sub(o_non_dust);
159-
160-
let mut t_disallowed_dust = transparent_dust.len().saturating_sub(t_allowed_dust);
161-
let mut s_disallowed_dust = sapling_dust.len().saturating_sub(s_allowed_dust);
162-
let mut o_disallowed_dust = orchard_dust.len().saturating_sub(o_allowed_dust);
163-
164-
if available_grace_inputs > 0 {
165-
// If we have available grace inputs, allocate them first to transparent dust
166-
// and then to Sapling dust followed by Orchard dust. The caller has provided
167-
// inputs that it is willing to spend, so we don't need to consider privacy
168-
// effects at this layer.
169-
let t_grace_dust = available_grace_inputs.saturating_sub(t_disallowed_dust);
170-
t_disallowed_dust = t_disallowed_dust.saturating_sub(t_grace_dust);
171-
172-
let s_grace_dust = available_grace_inputs
173-
.saturating_sub(t_grace_dust)
174-
.saturating_sub(s_disallowed_dust);
175-
s_disallowed_dust = s_disallowed_dust.saturating_sub(s_grace_dust);
176-
177-
let o_grace_dust = available_grace_inputs
178-
.saturating_sub(t_grace_dust)
179-
.saturating_sub(s_grace_dust)
180-
.saturating_sub(o_disallowed_dust);
181-
o_disallowed_dust = o_disallowed_dust.saturating_sub(o_grace_dust);
182-
}
183-
184-
// Truncate the lists of inputs to be disregarded in input selection to just the
185-
// disallowed lengths. This has the effect of prioritizing inputs for inclusion by the
186-
// order of the original input slices, with the most preferred inputs first.
187-
transparent_dust.reverse();
188-
transparent_dust.truncate(t_disallowed_dust);
189-
sapling_dust.reverse();
190-
sapling_dust.truncate(s_disallowed_dust);
191-
orchard_dust.reverse();
192-
orchard_dust.truncate(o_disallowed_dust);
193-
194-
if !(transparent_dust.is_empty() && sapling_dust.is_empty() && orchard_dust.is_empty())
195-
{
196-
return Err(ChangeError::DustInputs {
197-
transparent: transparent_dust,
198-
sapling: sapling_dust,
199-
#[cfg(feature = "orchard")]
200-
orchard: orchard_dust,
201-
});
202-
}
203-
}
204-
75+
// TODO: consider opportunistic dust spends (#1316).
20576
single_change_output_balance(
20677
params,
20778
&self.fee_rule,

0 commit comments

Comments
 (0)