@ -321,7 +321,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
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 )
{
CAmount curBalance = pwallet - > GetBalance ( ) . m_mine_trusted ;
CAmount curBalance = pwallet - > GetBalance ( 0 , coin_control . m_avoid_address_reuse ) . m_mine_trusted ;
// Check amount
if ( nValue < = 0 )
@ -368,7 +368,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
return NullUniValue ;
}
if ( request . fHelp | | request . params . size ( ) < 2 | | request . params . size ( ) > 8 )
if ( request . fHelp | | request . params . size ( ) < 2 | | request . params . size ( ) > 9 )
throw std : : runtime_error (
RPCHelpMan { " sendtoaddress " ,
" \n Send an amount to a given address. " +
@ -389,6 +389,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
" \" UNSET \" \n "
" \" ECONOMICAL \" \n "
" \" CONSERVATIVE \" " } ,
{ " avoid_reuse " , RPCArg : : Type : : BOOL , /* default */ pwallet - > IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ? " true " : " unavailable " , " Avoid spending from dirty addresses; addresses are considered \n "
" dirty if they have previously been used in a transaction. " } ,
} ,
RPCResult {
" \" txid \" (string) The transaction id. \n "
@ -445,6 +447,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
}
}
coin_control . m_avoid_address_reuse = GetAvoidReuseFlag ( pwallet , request . params [ 8 ] ) ;
EnsureWalletIsUnlocked ( pwallet ) ;
@ -734,7 +737,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
return NullUniValue ;
}
if ( request . fHelp | | ( request . params . size ( ) > 3 ) )
if ( request . fHelp | | request . params . size ( ) > 4 )
throw std : : runtime_error (
RPCHelpMan { " getbalance " ,
" \n Returns the total available balance. \n "
@ -744,6 +747,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
{ " dummy " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Remains for backward compatibility. Must be excluded or set to \" * \" . " } ,
{ " minconf " , RPCArg : : Type : : NUM , /* default */ " 0 " , " Only include transactions confirmed at least this many times. " } ,
{ " include_watchonly " , RPCArg : : Type : : BOOL , /* default */ " false " , " Also include balance in watch-only addresses (see 'importaddress') " } ,
{ " 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. " } ,
} ,
RPCResult {
" amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet. \n "
@ -780,7 +784,9 @@ static UniValue getbalance(const JSONRPCRequest& request)
include_watchonly = true ;
}
const auto bal = pwallet - > GetBalance ( min_depth ) ;
bool avoid_reuse = GetAvoidReuseFlag ( pwallet , request . params [ 3 ] ) ;
const auto bal = pwallet - > GetBalance ( min_depth , avoid_reuse ) ;
return ValueFromAmount ( bal . m_mine_trusted + ( include_watchonly ? bal . m_watchonly_trusted : 0 ) ) ;
}
@ -2474,6 +2480,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
" \" paytxfee \" : x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + " /kB \n "
" \" hdseedid \" : \" <hash160> \" (string, optional) the Hash160 of the HD seed (only present when HD is enabled) \n "
" \" private_keys_enabled \" : true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet) \n "
" \" avoid_reuse \" : true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse \n "
" \" scanning \" : (json object) current scanning details, or false if no scan is in progress \n "
" { \n "
" \" duration \" : xxxx (numeric) elapsed seconds since scan start \n "
@ -2522,6 +2529,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
obj . pushKV ( " hdseedid " , seed_id . GetHex ( ) ) ;
}
obj . pushKV ( " private_keys_enabled " , ! pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
obj . pushKV ( " avoid_reuse " , pwallet - > IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ) ;
if ( pwallet - > IsScanning ( ) ) {
UniValue scanning ( UniValue : : VOBJ ) ;
scanning . pushKV ( " duration " , pwallet - > ScanningDuration ( ) / 1000 ) ;
@ -2730,6 +2738,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
{ " disable_private_keys " , RPCArg : : Type : : BOOL , /* default */ " false " , " Disable the possibility of private keys (only watchonlys are possible in this mode). " } ,
{ " 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. " } ,
{ " passphrase " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " Encrypt the wallet with this passphrase. " } ,
{ " avoid_reuse " , RPCArg : : Type : : BOOL , /* default */ " false " , " Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind. " } ,
} ,
RPCResult {
" { \n "
@ -2771,6 +2780,10 @@ static UniValue createwallet(const JSONRPCRequest& request)
flags | = WALLET_FLAG_BLANK_WALLET ;
}
if ( ! request . params [ 4 ] . isNull ( ) & & request . params [ 4 ] . get_bool ( ) ) {
flags | = WALLET_FLAG_AVOID_REUSE ;
}
WalletLocation location ( request . params [ 0 ] . get_str ( ) ) ;
if ( location . Exists ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Wallet " + location . GetName ( ) + " already exists. " ) ;
@ -2872,6 +2885,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
return NullUniValue ;
}
bool avoid_reuse = pwallet - > IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ;
if ( request . fHelp | | request . params . size ( ) > 5 )
throw std : : runtime_error (
RPCHelpMan { " listunspent " ,
@ -2911,6 +2926,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \" witnessScript \" : \" script \" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH \n "
" \" spendable \" : xxx, (bool) Whether we have the private keys to spend this output \n "
" \" solvable \" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys \n "
+ ( avoid_reuse ?
" \" reused \" : xxx, (bool) Whether this output is reused/dirty (sent to an address that was previously spent from) \n " :
" " ) +
" \" desc \" : xxx, (string, only when solvable) A descriptor for spending this output \n "
" \" safe \" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions \n "
" from outside keys and unconfirmed replacement transactions are considered unsafe \n "
@ -2990,9 +3008,11 @@ static UniValue listunspent(const JSONRPCRequest& request)
UniValue results ( UniValue : : VARR ) ;
std : : vector < COutput > vecOutputs ;
{
CCoinControl cctl ;
cctl . m_avoid_address_reuse = false ;
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
pwallet - > AvailableCoins ( * locked_chain , vecOutputs , ! include_unsafe , nullptr , nMinimumAmount , nMaximumAmount , nMinimumSumAmount , nMaximumCount , nMinDepth , nMaxDepth ) ;
pwallet - > AvailableCoins ( * locked_chain , vecOutputs , ! include_unsafe , & cctl , nMinimumAmount , nMaximumAmount , nMinimumSumAmount , nMaximumCount , nMinDepth , nMaxDepth ) ;
}
LOCK ( pwallet - > cs_wallet ) ;
@ -3001,6 +3021,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
CTxDestination address ;
const CScript & scriptPubKey = out . tx - > tx - > vout [ out . i ] . scriptPubKey ;
bool fValidAddress = ExtractDestination ( scriptPubKey , address ) ;
bool reused = avoid_reuse & & pwallet - > IsUsedDestination ( address ) ;
if ( destinations . size ( ) & & ( ! fValidAddress | | ! destinations . count ( address ) ) )
continue ;
@ -3057,6 +3078,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
auto descriptor = InferDescriptor ( scriptPubKey , * pwallet ) ;
entry . pushKV ( " desc " , descriptor - > ToString ( ) ) ;
}
if ( avoid_reuse ) entry . pushKV ( " reused " , reused ) ;
entry . pushKV ( " safe " , out . fSafe ) ;
results . push_back ( entry ) ;
}
@ -4261,13 +4283,13 @@ static const CRPCCommand commands[] =
{ " wallet " , " addmultisigaddress " , & addmultisigaddress , { " nrequired " , " keys " , " label " , " address_type " } } ,
{ " wallet " , " backupwallet " , & backupwallet , { " destination " } } ,
{ " wallet " , " bumpfee " , & bumpfee , { " txid " , " options " } } ,
{ " wallet " , " createwallet " , & createwallet , { " wallet_name " , " disable_private_keys " , " blank " , " passphrase " } } ,
{ " wallet " , " createwallet " , & createwallet , { " wallet_name " , " disable_private_keys " , " blank " , " passphrase " , " avoid_reuse " } } ,
{ " wallet " , " dumpprivkey " , & dumpprivkey , { " address " } } ,
{ " wallet " , " dumpwallet " , & dumpwallet , { " filename " } } ,
{ " wallet " , " encryptwallet " , & encryptwallet , { " passphrase " } } ,
{ " wallet " , " getaddressesbylabel " , & getaddressesbylabel , { " label " } } ,
{ " wallet " , " getaddressinfo " , & getaddressinfo , { " address " } } ,
{ " wallet " , " getbalance " , & getbalance , { " dummy " , " minconf " , " include_watchonly " } } ,
{ " wallet " , " getbalance " , & getbalance , { " dummy " , " minconf " , " include_watchonly " ," avoid_reuse " } } ,
{ " wallet " , " getnewaddress " , & getnewaddress , { " label " , " address_type " } } ,
{ " wallet " , " getrawchangeaddress " , & getrawchangeaddress , { " address_type " } } ,
{ " wallet " , " getreceivedbyaddress " , & getreceivedbyaddress , { " address " , " minconf " } } ,
@ -4298,7 +4320,7 @@ static const CRPCCommand commands[] =
{ " wallet " , " removeprunedfunds " , & removeprunedfunds , { " txid " } } ,
{ " wallet " , " rescanblockchain " , & rescanblockchain , { " start_height " , " stop_height " } } ,
{ " wallet " , " sendmany " , & sendmany , { " dummy " , " amounts " , " minconf " , " comment " , " subtractfeefrom " , " replaceable " , " conf_target " , " estimate_mode " } } ,
{ " wallet " , " sendtoaddress " , & sendtoaddress , { " address " , " amount " , " comment " , " comment_to " , " subtractfeefromamount " , " replaceable " , " conf_target " , " estimate_mode " } } ,
{ " wallet " , " sendtoaddress " , & sendtoaddress , { " address " , " amount " , " comment " , " comment_to " , " subtractfeefromamount " , " replaceable " , " conf_target " , " estimate_mode " ," avoid_reuse " } } ,
{ " wallet " , " sethdseed " , & sethdseed , { " newkeypool " , " seed " } } ,
{ " wallet " , " setlabel " , & setlabel , { " address " , " label " } } ,
{ " wallet " , " settxfee " , & settxfee , { " amount " } } ,