@@ -209,11 +209,13 @@ std::vector<std::set<std::size_t>> form_preferred_input_candidate_subsets(
209209
210210 // 5. We should prefer to spend non-forward-secret enotes in transactions where all the outputs
211211 // are going back to ourself. Otherwise, if we spend these enotes while transferring money to
212- // another entity, an external observer who A) has a quantum computer, and B) knows one of their
212+ // another entity, an external observer who A) has a quantum computer, and B) knows one of our
213213 // public addresses, will be able to trace the money transfer. Such an observer will always be
214214 // able to tell which view-incoming keys / accounts these non-forward-secrets enotes belong to,
215215 // their amounts, and where they're spent. So since they already know that information, churning
216- // back to oneself doesn't actually reveal that much more additional information.
216+ // back to oneself doesn't actually reveal that much more additional information. Plus it allows
217+ // us to automatically convert non-forward-secret enotes to forward-secret enotes through
218+ // churning.
217219 const bool prefer_non_fs = !is_normal_transfer;
218220 CARROT_CHECK_AND_THROW (!must_use_internal || !prefer_non_fs,
219221 carrot_logic_error, " bug: must_use_internal AND prefer_non_fs are true" );
@@ -250,7 +252,7 @@ std::vector<std::set<std::size_t>> form_preferred_input_candidate_subsets(
250252 if (!must_use_internal || !prefer_non_fs)
251253 {
252254 // Spending non-FS inputs in a normal transfer transaction is not ideal, but at least
253- // when partition it like this, we aren't "dirtying" the carrot with the pre-carrot, and
255+ // when partitioned like this, we aren't "dirtying" the carrot with the pre-carrot, and
254256 // the internal with the external
255257 if (!must_use_carrot)
256258 push_subset (pre_carrot_inputs);
@@ -343,6 +345,8 @@ select_inputs_func_t make_single_transfer_input_selector(
343345 const std::size_t max_n_inputs,
344346 std::set<size_t > *selected_input_indices_out)
345347{
348+ const std::size_t max_n_inputs = max_n_inputs ? max_n_inputs : FCMP_PLUS_PLUS_MAX_INPUTS ;
349+
346350 // input selector :D
347351 return [=](const boost::multiprecision::uint128_t &nominal_output_sum,
348352 const std::map<std::size_t , xmr_amount> &fee_by_input_count,
@@ -354,7 +358,7 @@ select_inputs_func_t make_single_transfer_input_selector(
354358
355359 // 1. Sanity checks valid arguments
356360 const std::size_t n_candidates = input_candidates.size ();
357- CARROT_CHECK_AND_THROW (!fee_by_input_count.empty (), missing_components, " no provided allowed input count" );
361+ CARROT_CHECK_AND_THROW (!fee_by_input_count.empty (), missing_components, " no provided fee by input count" );
358362 CARROT_CHECK_AND_THROW (!policies.empty (), missing_components, " no input selection policies provided" );
359363 CARROT_CHECK_AND_THROW (n_candidates, not_enough_money, " no input candidates provided" );
360364
@@ -388,7 +392,7 @@ select_inputs_func_t make_single_transfer_input_selector(
388392
389393 std::set<std::size_t > all_idxs; for (std::size_t i = 0 ; i < input_candidates.size (); ++i) all_idxs.insert (i);
390394 const std::pair<std::size_t , boost::multiprecision::uint128_t > max_usable_money =
391- input_count_for_max_usable_money (input_candidates, all_idxs, FCMP_PLUS_PLUS_MAX_INPUTS , fee_by_input_count);
395+ input_count_for_max_usable_money (input_candidates, all_idxs, max_n_inputs , fee_by_input_count);
392396 CARROT_CHECK_AND_THROW (max_usable_money.second >= absolute_minimum_required_money,
393397 not_enough_usable_money,
394398 " Not enough usable money in top " << max_usable_money.first << " inputs ("
@@ -413,7 +417,7 @@ select_inputs_func_t make_single_transfer_input_selector(
413417
414418 // Skip if not enough money in this selectable set for max number of tx inputs...
415419 const auto max_usable_money = input_count_for_max_usable_money (input_candidates,
416- input_candidate_subset, FCMP_PLUS_PLUS_MAX_INPUTS , fee_by_input_count);
420+ input_candidate_subset, max_n_inputs , fee_by_input_count);
417421 if (!max_usable_money.first )
418422 continue ;
419423 else if (max_usable_money.second < required_money_by_input_count.at (max_usable_money.first ))
@@ -442,6 +446,16 @@ select_inputs_func_t make_single_transfer_input_selector(
442446 continue ;
443447 else if (max_usable_money.second < required_money)
444448 continue ;
449+ else if (subtract_fee && fee_by_input_count[max_usable_money->first ] >= nominal_output_sum)
450+ {
451+ // Note: we do not check for combinations less than max_usable_money where the fee is lower than
452+ // the output sum
453+ MDEBUG (" Skipping input selection... fee exceeds output sum with 'subtract fee' option set. Fee "
454+ << cryptonote::print_money (fee_by_input_count[max_usable_money->first ])
455+ << " for " << max_usable_money->first
456+ << " inputs is >= output sum " << cryptonote::print_money (nominal_output_sum));
457+ continue ;
458+ }
445459
446460 // After this point, we expect one of the policies to succeed, otherwise all input selection fails
447461
@@ -492,8 +506,9 @@ select_inputs_func_t make_single_transfer_input_selector(
492506 CARROT_CHECK_AND_THROW (!selected_inputs_indices.empty (),
493507 not_enough_usable_money,
494508 " No single allowed subset of candidates had enough money to fund payment proposals and fees for inputs" );
495- CARROT_CHECK_AND_THROW (*selected_inputs_indices.crbegin () < input_candidates.size (),
496- carrot_logic_error, " bug: selected inputs index out of range" );
509+ for (const std::size_t selected_inputs_index : selected_inputs_indices)
510+ CARROT_CHECK_AND_THROW (selected_inputs_index < input_candidates.size (),
511+ carrot_logic_error, " bug: selected inputs index out of range" );
497512
498513 // 9. Check the sum of input amounts is great enough
499514 const std::size_t num_selected = selected_inputs_indices.size ();
@@ -566,17 +581,16 @@ void select_greedy_aging(const epee::span<const InputCandidate> input_candidates
566581 const xmr_amount currently_selected_amount = input_candidates[bi_it->second ].core .amount ;
567582 const xmr_amount lowest_replacement_amount = (currently_selected_amount > surplus)
568583 ? boost::numeric_cast<xmr_amount>(currently_selected_amount - surplus) : 0 ;
569- const auto lower_amount_it = std::lower_bound (selectable_inputs_by_amount.cbegin (),
570- selectable_inputs_by_amount.cend (), lowest_replacement_amount,
571- [&input_candidates](const std::size_t selectable_idx, const xmr_amount lowest_replacement_amount)
572- {
573- CARROT_CHECK_AND_THROW (selectable_idx < input_candidates.size (),
574- std::out_of_range, " input candidate index out of range" );
575- return input_candidates[selectable_idx].core .amount < lowest_replacement_amount;
576- });
577- for (auto amount_it = lower_amount_it; amount_it != selectable_inputs_by_amount.cend (); ++amount_it)
584+ for (size_t i = 0 ; i < selectable_inputs_by_amount.size (); ++i)
578585 {
579- const std::size_t potential_replacement_idx = *amount_it;
586+ // Ignore candidates with amounts too low for replacement
587+ const std::size_t potential_replacement_idx = selectable_inputs_by_amount[i];
588+ CARROT_CHECK_AND_THROW (potential_replacement_idx < input_candidates.size (),
589+ std::out_of_range, " input candidate index out of range" );
590+ if (input_candidates[potential_replacement_idx].core .amount < lowest_replacement_amount)
591+ continue ;
592+
593+ // Replace if candidate's block index is lower
580594 if (selected_inputs_indices_out.count (potential_replacement_idx))
581595 continue ;
582596 const InputCandidate &potential_replacement_input = input_candidates[potential_replacement_idx];
0 commit comments