|
35 | 35 |
|
36 | 36 | #include <boost/algorithm/string/replace.hpp>
|
37 | 37 |
|
| 38 | +static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; |
| 39 | + |
38 | 40 | static CCriticalSection cs_wallets;
|
39 | 41 | static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets);
|
40 | 42 |
|
@@ -2525,6 +2527,12 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
|
2525 | 2527 |
|
2526 | 2528 | // form groups from remaining coins; note that preset coins will not
|
2527 | 2529 | // automatically have their associated (same address) coins included
|
| 2530 | + if (coin_control.m_avoid_partial_spends && vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) { |
| 2531 | + // Cases where we have 11+ outputs all pointing to the same destination may result in |
| 2532 | + // privacy leaks as they will potentially be deterministically sorted. We solve that by |
| 2533 | + // explicitly shuffling the outputs before processing |
| 2534 | + std::shuffle(vCoins.begin(), vCoins.end(), FastRandomContext()); |
| 2535 | + } |
2528 | 2536 | std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
|
2529 | 2537 |
|
2530 | 2538 | size_t max_ancestors = (size_t)std::max<int64_t>(1, gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT));
|
@@ -4444,7 +4452,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
|
4444 | 4452 | // Limit output groups to no more than 10 entries, to protect
|
4445 | 4453 | // against inadvertently creating a too-large transaction
|
4446 | 4454 | // when using -avoidpartialspends
|
4447 |
| - if (gmap[dst].m_outputs.size() >= 10) { |
| 4455 | + if (gmap[dst].m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) { |
4448 | 4456 | groups.push_back(gmap[dst]);
|
4449 | 4457 | gmap.erase(dst);
|
4450 | 4458 | }
|
|
0 commit comments