@@ -321,7 +321,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
321
321
322
322
static CTransactionRef SendMoney (interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount , const CCoinControl& coin_control, mapValue_t mapValue)
323
323
{
324
- CAmount curBalance = pwallet->GetBalance ().m_mine_trusted ;
324
+ CAmount curBalance = pwallet->GetBalance (0 , coin_control. m_avoid_address_reuse ).m_mine_trusted ;
325
325
326
326
// Check amount
327
327
if (nValue <= 0 )
@@ -368,7 +368,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
368
368
return NullUniValue;
369
369
}
370
370
371
- if (request.fHelp || request.params .size () < 2 || request.params .size () > 8 )
371
+ if (request.fHelp || request.params .size () < 2 || request.params .size () > 9 )
372
372
throw std::runtime_error (
373
373
RPCHelpMan{" sendtoaddress" ,
374
374
" \n Send an amount to a given address." +
@@ -389,6 +389,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
389
389
" \" UNSET\"\n "
390
390
" \" ECONOMICAL\"\n "
391
391
" \" CONSERVATIVE\" " },
392
+ {" avoid_reuse" , RPCArg::Type::BOOL, /* default */ pwallet->IsWalletFlagSet (WALLET_FLAG_AVOID_REUSE) ? " true" : " unavailable" , " Avoid spending from dirty addresses; addresses are considered\n "
393
+ " dirty if they have previously been used in a transaction." },
392
394
},
393
395
RPCResult{
394
396
" \" txid\" (string) The transaction id.\n "
@@ -445,6 +447,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
445
447
}
446
448
}
447
449
450
+ coin_control.m_avoid_address_reuse = GetAvoidReuseFlag (pwallet, request.params [8 ]);
448
451
449
452
EnsureWalletIsUnlocked (pwallet);
450
453
@@ -734,7 +737,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
734
737
return NullUniValue;
735
738
}
736
739
737
- if (request.fHelp || ( request.params .size () > 3 ) )
740
+ if (request.fHelp || request.params .size () > 4 )
738
741
throw std::runtime_error (
739
742
RPCHelpMan{" getbalance" ,
740
743
" \n Returns the total available balance.\n "
@@ -744,6 +747,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
744
747
{" dummy" , RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, " Remains for backward compatibility. Must be excluded or set to \" *\" ." },
745
748
{" minconf" , RPCArg::Type::NUM, /* default */ " 0" , " Only include transactions confirmed at least this many times." },
746
749
{" include_watchonly" , RPCArg::Type::BOOL, /* default */ " false" , " Also include balance in watch-only addresses (see 'importaddress')" },
750
+ {" avoid_reuse" , RPCArg::Type::BOOL, /* default */ pwallet->IsWalletFlagSet (WALLET_FLAG_AVOID_REUSE) ? " true" : " unavailable" , " Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction." },
747
751
},
748
752
RPCResult{
749
753
" amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n "
@@ -780,7 +784,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
780
784
include_watchonly = true ;
781
785
}
782
786
783
- const auto bal = pwallet->GetBalance (min_depth);
787
+ bool avoid_reuse = GetAvoidReuseFlag (pwallet, request.params [3 ]);
788
+
789
+ const auto bal = pwallet->GetBalance (min_depth, avoid_reuse);
784
790
785
791
return ValueFromAmount (bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0 ));
786
792
}
@@ -2474,6 +2480,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
2474
2480
" \" paytxfee\" : x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + " /kB\n "
2475
2481
" \" hdseedid\" : \" <hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n "
2476
2482
" \" private_keys_enabled\" : true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n "
2483
+ " \" avoid_reuse\" : true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n "
2477
2484
" \" scanning\" : (json object) current scanning details, or false if no scan is in progress\n "
2478
2485
" {\n "
2479
2486
" \" duration\" : xxxx (numeric) elapsed seconds since scan start\n "
@@ -2522,6 +2529,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
2522
2529
obj.pushKV (" hdseedid" , seed_id.GetHex ());
2523
2530
}
2524
2531
obj.pushKV (" private_keys_enabled" , !pwallet->IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS));
2532
+ obj.pushKV (" avoid_reuse" , pwallet->IsWalletFlagSet (WALLET_FLAG_AVOID_REUSE));
2525
2533
if (pwallet->IsScanning ()) {
2526
2534
UniValue scanning (UniValue::VOBJ);
2527
2535
scanning.pushKV (" duration" , pwallet->ScanningDuration () / 1000 );
@@ -2730,6 +2738,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
2730
2738
{" disable_private_keys" , RPCArg::Type::BOOL, /* default */ " false" , " Disable the possibility of private keys (only watchonlys are possible in this mode)." },
2731
2739
{" blank" , RPCArg::Type::BOOL, /* default */ " false" , " Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed." },
2732
2740
{" passphrase" , RPCArg::Type::STR, RPCArg::Optional::OMITTED, " Encrypt the wallet with this passphrase." },
2741
+ {" avoid_reuse" , RPCArg::Type::BOOL, /* default */ " false" , " Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind." },
2733
2742
},
2734
2743
RPCResult{
2735
2744
" {\n "
@@ -2771,6 +2780,10 @@ static UniValue createwallet(const JSONRPCRequest& request)
2771
2780
flags |= WALLET_FLAG_BLANK_WALLET;
2772
2781
}
2773
2782
2783
+ if (!request.params [4 ].isNull () && request.params [4 ].get_bool ()) {
2784
+ flags |= WALLET_FLAG_AVOID_REUSE;
2785
+ }
2786
+
2774
2787
WalletLocation location (request.params [0 ].get_str ());
2775
2788
if (location.Exists ()) {
2776
2789
throw JSONRPCError (RPC_WALLET_ERROR, " Wallet " + location.GetName () + " already exists." );
@@ -2872,6 +2885,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
2872
2885
return NullUniValue;
2873
2886
}
2874
2887
2888
+ bool avoid_reuse = pwallet->IsWalletFlagSet (WALLET_FLAG_AVOID_REUSE);
2889
+
2875
2890
if (request.fHelp || request.params .size () > 5 )
2876
2891
throw std::runtime_error (
2877
2892
RPCHelpMan{" listunspent" ,
@@ -2911,6 +2926,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
2911
2926
" \" witnessScript\" : \" script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n "
2912
2927
" \" spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n "
2913
2928
" \" solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n "
2929
+ + (avoid_reuse ?
2930
+ " \" reused\" : xxx, (bool) Whether this output is reused/dirty (sent to an address that was previously spent from)\n " :
2931
+ " " ) +
2914
2932
" \" desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n "
2915
2933
" \" safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n "
2916
2934
" from outside keys and unconfirmed replacement transactions are considered unsafe\n "
@@ -2990,9 +3008,11 @@ static UniValue listunspent(const JSONRPCRequest& request)
2990
3008
UniValue results (UniValue::VARR);
2991
3009
std::vector<COutput> vecOutputs;
2992
3010
{
3011
+ CCoinControl cctl;
3012
+ cctl.m_avoid_address_reuse = false ;
2993
3013
auto locked_chain = pwallet->chain ().lock ();
2994
3014
LOCK (pwallet->cs_wallet );
2995
- pwallet->AvailableCoins (*locked_chain, vecOutputs, !include_unsafe, nullptr , nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
3015
+ pwallet->AvailableCoins (*locked_chain, vecOutputs, !include_unsafe, &cctl , nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
2996
3016
}
2997
3017
2998
3018
LOCK (pwallet->cs_wallet );
@@ -3001,6 +3021,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
3001
3021
CTxDestination address;
3002
3022
const CScript& scriptPubKey = out.tx ->tx ->vout [out.i ].scriptPubKey ;
3003
3023
bool fValidAddress = ExtractDestination (scriptPubKey, address);
3024
+ bool reused = avoid_reuse && pwallet->IsUsedDestination (address);
3004
3025
3005
3026
if (destinations.size () && (!fValidAddress || !destinations.count (address)))
3006
3027
continue ;
@@ -3057,6 +3078,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
3057
3078
auto descriptor = InferDescriptor (scriptPubKey, *pwallet);
3058
3079
entry.pushKV (" desc" , descriptor->ToString ());
3059
3080
}
3081
+ if (avoid_reuse) entry.pushKV (" reused" , reused);
3060
3082
entry.pushKV (" safe" , out.fSafe );
3061
3083
results.push_back (entry);
3062
3084
}
@@ -4261,13 +4283,13 @@ static const CRPCCommand commands[] =
4261
4283
{ " wallet" , " addmultisigaddress" , &addmultisigaddress, {" nrequired" ," keys" ," label" ," address_type" } },
4262
4284
{ " wallet" , " backupwallet" , &backupwallet, {" destination" } },
4263
4285
{ " wallet" , " bumpfee" , &bumpfee, {" txid" , " options" } },
4264
- { " wallet" , " createwallet" , &createwallet, {" wallet_name" , " disable_private_keys" , " blank" , " passphrase" } },
4286
+ { " wallet" , " createwallet" , &createwallet, {" wallet_name" , " disable_private_keys" , " blank" , " passphrase" , " avoid_reuse " } },
4265
4287
{ " wallet" , " dumpprivkey" , &dumpprivkey, {" address" } },
4266
4288
{ " wallet" , " dumpwallet" , &dumpwallet, {" filename" } },
4267
4289
{ " wallet" , " encryptwallet" , &encryptwallet, {" passphrase" } },
4268
4290
{ " wallet" , " getaddressesbylabel" , &getaddressesbylabel, {" label" } },
4269
4291
{ " wallet" , " getaddressinfo" , &getaddressinfo, {" address" } },
4270
- { " wallet" , " getbalance" , &getbalance, {" dummy" ," minconf" ," include_watchonly" } },
4292
+ { " wallet" , " getbalance" , &getbalance, {" dummy" ," minconf" ," include_watchonly" , " avoid_reuse " } },
4271
4293
{ " wallet" , " getnewaddress" , &getnewaddress, {" label" ," address_type" } },
4272
4294
{ " wallet" , " getrawchangeaddress" , &getrawchangeaddress, {" address_type" } },
4273
4295
{ " wallet" , " getreceivedbyaddress" , &getreceivedbyaddress, {" address" ," minconf" } },
@@ -4298,7 +4320,7 @@ static const CRPCCommand commands[] =
4298
4320
{ " wallet" , " removeprunedfunds" , &removeprunedfunds, {" txid" } },
4299
4321
{ " wallet" , " rescanblockchain" , &rescanblockchain, {" start_height" , " stop_height" } },
4300
4322
{ " wallet" , " sendmany" , &sendmany, {" dummy" ," amounts" ," minconf" ," comment" ," subtractfeefrom" ," replaceable" ," conf_target" ," estimate_mode" } },
4301
- { " wallet" , " sendtoaddress" , &sendtoaddress, {" address" ," amount" ," comment" ," comment_to" ," subtractfeefromamount" ," replaceable" ," conf_target" ," estimate_mode" } },
4323
+ { " wallet" , " sendtoaddress" , &sendtoaddress, {" address" ," amount" ," comment" ," comment_to" ," subtractfeefromamount" ," replaceable" ," conf_target" ," estimate_mode" , " avoid_reuse " } },
4302
4324
{ " wallet" , " sethdseed" , &sethdseed, {" newkeypool" ," seed" } },
4303
4325
{ " wallet" , " setlabel" , &setlabel, {" address" ," label" } },
4304
4326
{ " wallet" , " settxfee" , &settxfee, {" amount" } },
0 commit comments