@@ -187,11 +187,24 @@ std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& ut
187187 return std::nullopt ;
188188}
189189
190- static void ApproximateBestSubset (FastRandomContext& insecure_rand, const std::vector<OutputGroup>& groups, const CAmount& nTotalLower, const CAmount& nTargetValue,
190+ /* * Find a subset of the OutputGroups that is at least as large as, but as close as possible to, the
191+ * target amount; solve subset sum.
192+ * param@[in] groups OutputGroups to choose from, sorted by value in descending order.
193+ * param@[in] nTotalLower Total (effective) value of the UTXOs in groups.
194+ * param@[in] nTargetValue Subset sum target, not including change.
195+ * param@[out] vfBest Boolean vector representing the subset chosen that is closest to
196+ * nTargetValue, with indices corresponding to groups. If the ith
197+ * entry is true, that means the ith group in groups was selected.
198+ * param@[out] nBest Total amount of subset chosen that is closest to nTargetValue.
199+ * param@[in] iterations Maximum number of tries.
200+ */
201+ static void ApproximateBestSubset (FastRandomContext& insecure_rand, const std::vector<OutputGroup>& groups,
202+ const CAmount& nTotalLower, const CAmount& nTargetValue,
191203 std::vector<char >& vfBest, CAmount& nBest, int iterations = 1000 )
192204{
193205 std::vector<char > vfIncluded;
194206
207+ // Worst case "best" approximation is just all of the groups.
195208 vfBest.assign (groups.size (), true );
196209 nBest = nTotalLower;
197210
@@ -217,6 +230,8 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
217230 if (nTotal >= nTargetValue)
218231 {
219232 fReachedTarget = true ;
233+ // If the total is between nTargetValue and nBest, it's our new best
234+ // approximation.
220235 if (nTotal < nBest)
221236 {
222237 nBest = nTotal;
@@ -231,12 +246,15 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
231246 }
232247}
233248
234- std::optional<SelectionResult> KnapsackSolver (std::vector<OutputGroup>& groups, const CAmount& nTargetValue, FastRandomContext& rng)
249+ std::optional<SelectionResult> KnapsackSolver (std::vector<OutputGroup>& groups, const CAmount& nTargetValue,
250+ CAmount change_target, FastRandomContext& rng)
235251{
236252 SelectionResult result (nTargetValue);
237253
238254 // List of values less than target
239255 std::optional<OutputGroup> lowest_larger;
256+ // Groups with selection amount smaller than the target and any change we might produce.
257+ // Don't include groups larger than this, because they will only cause us to overshoot.
240258 std::vector<OutputGroup> applicable_groups;
241259 CAmount nTotalLower = 0 ;
242260
@@ -246,7 +264,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
246264 if (group.GetSelectionAmount () == nTargetValue) {
247265 result.AddInput (group);
248266 return result;
249- } else if (group.GetSelectionAmount () < nTargetValue + MIN_CHANGE ) {
267+ } else if (group.GetSelectionAmount () < nTargetValue + change_target ) {
250268 applicable_groups.push_back (group);
251269 nTotalLower += group.GetSelectionAmount ();
252270 } else if (!lowest_larger || group.GetSelectionAmount () < lowest_larger->GetSelectionAmount ()) {
@@ -273,14 +291,14 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
273291 CAmount nBest;
274292
275293 ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue, vfBest, nBest);
276- if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE ) {
277- ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue + MIN_CHANGE , vfBest, nBest);
294+ if (nBest != nTargetValue && nTotalLower >= nTargetValue + change_target ) {
295+ ApproximateBestSubset (rng, applicable_groups, nTotalLower, nTargetValue + change_target , vfBest, nBest);
278296 }
279297
280298 // If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
281299 // or the next bigger coin is closer), return the bigger coin
282300 if (lowest_larger &&
283- ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE ) || lowest_larger->GetSelectionAmount () <= nBest)) {
301+ ((nBest != nTargetValue && nBest < nTargetValue + change_target ) || lowest_larger->GetSelectionAmount () <= nBest)) {
284302 result.AddInput (*lowest_larger);
285303 } else {
286304 for (unsigned int i = 0 ; i < applicable_groups.size (); i++) {
0 commit comments