Skip to content

Commit 13d51a2

Browse files
committed
Merge #13808: wallet: shuffle coins before grouping, where warranted
18f690e wallet: shuffle coins before grouping, where warranted (Karl-Johan Alm) Pull request description: Coins are randomly shuffled in coin selection to avoid unintentional privacy leaks regarding the user's coin set. For the case where a user has a lot of coins with the same destination, these will be grouped into groups of 10 *before* the shuffling. It is unclear whether this has any implications at all, but this PR plugs the potential issue, if there ever is one, by shuffling the coins before they are grouped. Issue brought up in bitcoin/bitcoin#12257 (comment) Tree-SHA512: fb50ed4b5fc03ab4853d45b76e1c64476ad5bcd797497179bc37b9262885c974ed6811159fd8e581f1461b6cc6d0a66146f4b70a2777c0f5e818d1322e0edb89
2 parents b0d3e9b + 18f690e commit 13d51a2

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

src/wallet/wallet.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
#include <boost/algorithm/string/replace.hpp>
3737

38+
static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10;
39+
3840
static CCriticalSection cs_wallets;
3941
static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets);
4042

@@ -2525,6 +2527,12 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
25252527

25262528
// form groups from remaining coins; note that preset coins will not
25272529
// 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+
}
25282536
std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
25292537

25302538
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
44444452
// Limit output groups to no more than 10 entries, to protect
44454453
// against inadvertently creating a too-large transaction
44464454
// when using -avoidpartialspends
4447-
if (gmap[dst].m_outputs.size() >= 10) {
4455+
if (gmap[dst].m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) {
44484456
groups.push_back(gmap[dst]);
44494457
gmap.erase(dst);
44504458
}

0 commit comments

Comments
 (0)