@@ -16,8 +16,8 @@ use zcash_primitives::{
16
16
use crate :: ShieldedProtocol ;
17
17
18
18
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 ,
21
21
} ;
22
22
23
23
#[ cfg( feature = "transparent-inputs" ) ]
@@ -72,136 +72,7 @@ impl ChangeStrategy for SingleOutputChangeStrategy {
72
72
dust_output_policy : & DustOutputPolicy ,
73
73
#[ cfg( feature = "transparent-inputs" ) ] ephemeral_parameters : & EphemeralParameters ,
74
74
) -> 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).
205
76
single_change_output_balance (
206
77
params,
207
78
& self . fee_rule ,
0 commit comments