2012-08-21 10:38:57 -04:00
// Copyright (c) 2010 Satoshi Nakamoto
2020-01-15 02:17:38 +07:00
// Copyright (c) 2009-2020 The Bitcoin Core developers
2014-11-20 10:19:29 +08:00
// Distributed under the MIT software license, see the accompanying
2012-08-21 10:38:57 -04:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2021-09-11 10:29:00 +08:00
# include <consensus/amount.h>
2017-11-10 13:57:53 +13:00
# include <core_io.h>
2017-07-26 10:23:01 -04:00
# include <interfaces/chain.h>
2017-09-19 18:12:25 -07:00
# include <key_io.h>
2019-09-17 18:28:03 -04:00
# include <node/context.h>
2018-07-09 18:15:50 +10:00
# include <outputtype.h>
2017-11-10 13:57:53 +13:00
# include <policy/feerate.h>
# include <policy/fees.h>
2020-08-07 17:36:36 +02:00
# include <policy/policy.h>
2017-11-10 13:57:53 +13:00
# include <policy/rbf.h>
2019-04-02 16:51:32 -04:00
# include <rpc/rawtransaction_util.h>
2017-11-10 13:57:53 +13:00
# include <rpc/server.h>
2017-09-29 00:21:28 -04:00
# include <rpc/util.h>
2018-10-13 13:32:18 -07:00
# include <script/descriptor.h>
2017-11-10 13:57:53 +13:00
# include <script/sign.h>
2019-04-02 17:03:37 -04:00
# include <util/fees.h>
2018-10-22 15:51:11 -07:00
# include <util/moneystr.h>
2019-10-06 17:52:05 -04:00
# include <util/string.h>
2019-04-09 09:10:08 -04:00
# include <util/system.h>
2019-08-19 18:12:35 -04:00
# include <util/translation.h>
2019-04-02 17:03:37 -04:00
# include <util/url.h>
2020-01-10 00:00:57 +07:00
# include <util/vector.h>
2017-11-10 13:57:53 +13:00
# include <wallet/coincontrol.h>
2020-05-28 02:13:19 -04:00
# include <wallet/context.h>
2017-11-10 13:57:53 +13:00
# include <wallet/feebumper.h>
2019-05-01 15:12:44 -04:00
# include <wallet/load.h>
2021-02-12 18:01:22 -05:00
# include <wallet/receive.h>
2017-06-12 18:53:46 -07:00
# include <wallet/rpcwallet.h>
2021-12-01 19:20:33 +13:00
# include <wallet/rpc/util.h>
2021-02-12 18:01:22 -05:00
# include <wallet/spend.h>
2017-11-10 13:57:53 +13:00
# include <wallet/wallet.h>
# include <wallet/walletdb.h>
2017-10-09 09:48:07 +13:00
# include <wallet/walletutil.h>
2013-04-13 00:13:08 -05:00
2021-03-15 11:59:05 +08:00
# include <optional>
2013-04-13 00:13:08 -05:00
# include <stdint.h>
2015-09-04 16:11:34 +02:00
# include <univalue.h>
2012-08-21 10:38:57 -04:00
2019-10-18 19:43:01 -04:00
# include <map>
2018-04-06 15:05:32 -04:00
2019-07-13 08:34:49 -07:00
2019-06-06 16:49:52 +02:00
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
2019-10-07 14:11:34 -04:00
bool HaveKey ( const SigningProvider & wallet , const CKey & key )
2019-06-06 16:49:52 +02:00
{
CKey key2 ;
key2 . Set ( key . begin ( ) , key . end ( ) , ! key . IsCompressed ( ) ) ;
return wallet . HaveKey ( key . GetPubKey ( ) . GetID ( ) ) | | wallet . HaveKey ( key2 . GetPubKey ( ) . GetID ( ) ) ;
}
2020-03-04 11:26:42 +09:00
/**
* Update coin control with fee estimation based on the given parameters
*
2020-11-17 20:22:01 +01:00
* @ param [ in ] wallet Wallet reference
2020-11-12 11:16:48 +01:00
* @ param [ in , out ] cc Coin control to be updated
* @ param [ in ] conf_target UniValue integer ; confirmation target in blocks , values between 1 and 1008 are valid per policy / fees . h ;
* @ param [ in ] estimate_mode UniValue string ; fee estimation mode , valid values are " unset " , " economical " or " conservative " ;
* @ param [ in ] fee_rate UniValue real ; fee rate in sat / vB ;
2020-11-17 20:08:30 +01:00
* if present , both conf_target and estimate_mode must either be null , or " unset "
2020-11-12 11:16:48 +01:00
* @ param [ in ] override_min_fee bool ; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
* verify only that fee_rate is greater than 0
2020-11-04 13:13:17 +01:00
* @ throws a JSONRPCError if conf_target , estimate_mode , or fee_rate contain invalid values or are in conflict
2020-03-04 11:26:42 +09:00
*/
2020-11-17 20:22:01 +01:00
static void SetFeeEstimateMode ( const CWallet & wallet , CCoinControl & cc , const UniValue & conf_target , const UniValue & estimate_mode , const UniValue & fee_rate , bool override_min_fee )
2020-03-04 11:26:42 +09:00
{
2020-11-04 13:13:17 +01:00
if ( ! fee_rate . isNull ( ) ) {
2020-11-17 20:08:30 +01:00
if ( ! conf_target . isNull ( ) ) {
2020-11-04 13:13:17 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate. " ) ;
2020-03-04 11:26:42 +09:00
}
2020-11-17 20:08:30 +01:00
if ( ! estimate_mode . isNull ( ) & & estimate_mode . get_str ( ) ! = " unset " ) {
2020-11-04 13:13:17 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both estimate_mode and fee_rate " ) ;
2020-03-04 11:26:42 +09:00
}
2021-04-27 11:04:10 +02:00
// Fee rates in sat/vB cannot represent more than 3 significant digits.
cc . m_feerate = CFeeRate { AmountFromValue ( fee_rate , /* decimals */ 3 ) } ;
2020-11-19 18:38:00 +01:00
if ( override_min_fee ) cc . fOverrideFeeRate = true ;
2020-11-04 13:13:17 +01:00
// Default RBF to true for explicit fee_rate, if unset.
2021-03-15 11:59:05 +08:00
if ( ! cc . m_signal_bip125_rbf ) cc . m_signal_bip125_rbf = true ;
2020-11-04 13:13:17 +01:00
return ;
}
if ( ! estimate_mode . isNull ( ) & & ! FeeModeFromString ( estimate_mode . get_str ( ) , cc . m_fee_mode ) ) {
2020-11-10 12:29:01 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , InvalidEstimateModeErrorMessage ( ) ) ;
2020-11-04 13:13:17 +01:00
}
if ( ! conf_target . isNull ( ) ) {
2020-11-17 20:22:01 +01:00
cc . m_confirm_target = ParseConfirmTarget ( conf_target , wallet . chain ( ) . estimateMaxBlocks ( ) ) ;
2020-03-04 11:26:42 +09:00
}
}
2020-02-24 20:12:50 +01:00
void ParseRecipients ( const UniValue & address_amounts , const UniValue & subtract_fee_outputs , std : : vector < CRecipient > & recipients ) {
std : : set < CTxDestination > destinations ;
int i = 0 ;
for ( const std : : string & address : address_amounts . getKeys ( ) ) {
CTxDestination dest = DecodeDestination ( address ) ;
if ( ! IsValidDestination ( dest ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , std : : string ( " Invalid Bitcoin address: " ) + address ) ;
}
2012-08-21 10:38:57 -04:00
2020-02-24 20:12:50 +01:00
if ( destinations . count ( dest ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , std : : string ( " Invalid parameter, duplicated address: " ) + address ) ;
}
destinations . insert ( dest ) ;
2014-12-09 14:50:01 +01:00
2020-02-24 20:12:50 +01:00
CScript script_pub_key = GetScriptForDestination ( dest ) ;
CAmount amount = AmountFromValue ( address_amounts [ i + + ] ) ;
bool subtract_fee = false ;
for ( unsigned int idx = 0 ; idx < subtract_fee_outputs . size ( ) ; idx + + ) {
const UniValue & addr = subtract_fee_outputs [ idx ] ;
if ( addr . get_str ( ) = = address ) {
subtract_fee = true ;
}
}
CRecipient recipient = { script_pub_key , amount , subtract_fee } ;
recipients . push_back ( recipient ) ;
}
}
2014-12-01 20:06:45 +01:00
2021-03-04 23:20:13 +08:00
UniValue SendMoney ( CWallet & wallet , const CCoinControl & coin_control , std : : vector < CRecipient > & recipients , mapValue_t map_value , bool verbose )
2020-02-24 20:12:50 +01:00
{
2021-03-04 23:20:13 +08:00
EnsureWalletIsUnlocked ( wallet ) ;
2014-12-01 20:06:45 +01:00
2021-02-16 13:22:49 -05:00
// This function is only used by sendtoaddress and sendmany.
// This should always try to sign, if we don't have private keys, don't try to do anything here.
2021-03-04 23:20:13 +08:00
if ( wallet . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
2021-02-16 13:22:49 -05:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error: Private keys are disabled for this wallet " ) ;
}
2020-02-24 20:12:50 +01:00
// Shuffle recipient list
std : : shuffle ( recipients . begin ( ) , recipients . end ( ) , FastRandomContext ( ) ) ;
2014-12-01 20:06:45 +01:00
2020-02-24 20:12:50 +01:00
// Send
2020-03-09 10:20:30 +09:00
CAmount nFeeRequired = 0 ;
2014-07-23 14:34:36 +02:00
int nChangePosRet = - 1 ;
2020-02-24 20:12:50 +01:00
bilingual_str error ;
2017-02-02 15:30:03 -05:00
CTransactionRef tx ;
2020-07-12 12:52:02 -07:00
FeeCalculation fee_calc_out ;
2021-02-12 18:01:22 -05:00
const bool fCreated = CreateTransaction ( wallet , recipients , tx , nFeeRequired , nChangePosRet , error , coin_control , fee_calc_out , true ) ;
2020-02-24 20:12:50 +01:00
if ( ! fCreated ) {
throw JSONRPCError ( RPC_WALLET_INSUFFICIENT_FUNDS , error . original ) ;
2014-12-01 20:06:45 +01:00
}
2021-03-04 23:20:13 +08:00
wallet . CommitTransaction ( tx , std : : move ( map_value ) , { } /* orderForm */ ) ;
2020-07-12 12:52:02 -07:00
if ( verbose ) {
UniValue entry ( UniValue : : VOBJ ) ;
entry . pushKV ( " txid " , tx - > GetHash ( ) . GetHex ( ) ) ;
entry . pushKV ( " fee_reason " , StringForFeeReason ( fee_calc_out . reason ) ) ;
return entry ;
}
2020-02-24 20:12:50 +01:00
return tx - > GetHash ( ) . GetHex ( ) ;
2014-12-01 20:06:45 +01:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan sendtoaddress ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " sendtoaddress " ,
2018-11-23 11:21:38 -05:00
" \n Send an amount to a given address. " +
2020-04-01 10:31:43 +08:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 08:19:44 -04:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The bitcoin address to send to. " } ,
{ " amount " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " The amount in " + CURRENCY_UNIT + " to send. eg 0.1 " } ,
{ " comment " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " A comment used to store what the transaction is for. \n "
2020-09-30 09:20:37 +02:00
" This is not part of the transaction, just kept in your wallet. " } ,
2018-12-10 16:56:51 -05:00
{ " comment_to " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " A comment to store the name of the person or organization \n "
2020-09-30 09:20:37 +02:00
" to which you're sending the transaction. This is not part of the \n "
" transaction, just kept in your wallet. " } ,
2021-04-14 15:01:00 +01:00
{ " subtractfeefromamount " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " The fee will be deducted from the amount being sent. \n "
2020-09-30 09:20:37 +02:00
" The recipient will receive less bitcoins than you enter in the amount field. " } ,
2021-04-14 15:01:00 +01:00
{ " replaceable " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " wallet default " } , " Allow this transaction to be replaced by a transaction with higher fees via BIP 125 " } ,
{ " conf_target " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " wallet -txconfirmtarget " } , " Confirmation target in blocks " } ,
{ " estimate_mode " , RPCArg : : Type : : STR , RPCArg : : Default { " unset " } , std : : string ( ) + " The fee estimate mode, must be one of (case insensitive): \n "
2019-08-21 23:17:32 +09:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-04-14 15:01:00 +01:00
{ " avoid_reuse " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " (only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered \n "
2020-03-24 15:49:21 +01:00
" dirty if they have previously been used in a transaction. If true, this also activates avoidpartialspends, grouping outputs by their addresses. " } ,
2021-04-14 15:01:00 +01:00
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_ATOM + " /vB. " } ,
{ " verbose " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " If true, return extra information about the transaction. " } ,
2018-12-21 12:29:36 -05:00
} ,
2020-07-12 12:52:02 -07:00
{
RPCResult { " if verbose is not set or set to false " ,
RPCResult : : Type : : STR_HEX , " txid " , " The transaction id. "
} ,
RPCResult { " if verbose is set to true " ,
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id. " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : STR , " fee_reason " , " The transaction fee reason. " }
2020-07-12 12:52:02 -07:00
} ,
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2020-11-05 06:12:17 +01:00
" \n Send 0.1 BTC \n "
+ HelpExampleCli ( " sendtoaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 0.1 " ) +
" \n Send 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments \n "
+ HelpExampleCli ( " sendtoaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 0.1 \" donation \" \" sean's outpost \" false true 6 economical " ) +
2020-11-17 20:08:30 +01:00
" \n Send 0.1 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + " /vB, subtract fee from amount, BIP125-replaceable, using positional arguments \n "
+ HelpExampleCli ( " sendtoaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 0.1 \" drinks \" \" room77 \" true true null \" unset \" null 1.1 " ) +
2020-11-05 06:12:17 +01:00
" \n Send 0.2 BTC with a confirmation target of 6 blocks in economical fee estimate mode using named arguments \n "
+ HelpExampleCli ( " -named sendtoaddress " , " address= \" " + EXAMPLE_ADDRESS [ 0 ] + " \" amount=0.2 conf_target=6 estimate_mode= \" economical \" " ) +
" \n Send 0.5 BTC with a fee rate of 25 " + CURRENCY_ATOM + " /vB using named arguments \n "
+ HelpExampleCli ( " -named sendtoaddress " , " address= \" " + EXAMPLE_ADDRESS [ 0 ] + " \" amount=0.5 fee_rate=25 " )
+ HelpExampleCli ( " -named sendtoaddress " , " address= \" " + EXAMPLE_ADDRESS [ 0 ] + " \" amount=0.5 fee_rate=25 subtractfeefromamount=false replaceable=true avoid_reuse=true comment= \" 2 pizzas \" comment_to= \" jeremy \" verbose=true " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2012-08-21 10:38:57 -04:00
// Wallet comments
2017-02-02 15:30:03 -05:00
mapValue_t mapValue ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 2 ] . isNull ( ) & & ! request . params [ 2 ] . get_str ( ) . empty ( ) )
2017-02-02 15:30:03 -05:00
mapValue [ " comment " ] = request . params [ 2 ] . get_str ( ) ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 3 ] . isNull ( ) & & ! request . params [ 3 ] . get_str ( ) . empty ( ) )
2017-02-02 15:30:03 -05:00
mapValue [ " to " ] = request . params [ 3 ] . get_str ( ) ;
2012-08-21 10:38:57 -04:00
2014-07-23 14:34:36 +02:00
bool fSubtractFeeFromAmount = false ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 4 ] . isNull ( ) ) {
2016-09-22 09:46:41 +02:00
fSubtractFeeFromAmount = request . params [ 4 ] . get_bool ( ) ;
2017-06-14 15:15:40 -04:00
}
CCoinControl coin_control ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 5 ] . isNull ( ) ) {
2018-04-07 12:12:46 -04:00
coin_control . m_signal_bip125_rbf = request . params [ 5 ] . get_bool ( ) ;
2017-06-14 15:15:40 -04:00
}
2021-03-04 23:20:13 +08:00
coin_control . m_avoid_address_reuse = GetAvoidReuseFlag ( * pwallet , request . params [ 8 ] ) ;
2019-04-25 13:50:07 +09:00
// We also enable partial spend avoidance if reuse avoidance is set.
coin_control . m_avoid_partial_spends | = coin_control . m_avoid_address_reuse ;
2014-07-23 14:34:36 +02:00
2020-11-17 20:22:01 +01:00
SetFeeEstimateMode ( * pwallet , coin_control , /* conf_target */ request . params [ 6 ] , /* estimate_mode */ request . params [ 7 ] , /* fee_rate */ request . params [ 9 ] , /* override_min_fee */ false ) ;
2020-03-04 11:26:42 +09:00
2021-03-04 23:20:13 +08:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2012-08-21 10:38:57 -04:00
2020-02-24 20:12:50 +01:00
UniValue address_amounts ( UniValue : : VOBJ ) ;
const std : : string address = request . params [ 0 ] . get_str ( ) ;
address_amounts . pushKV ( address , request . params [ 1 ] ) ;
UniValue subtractFeeFromAmount ( UniValue : : VARR ) ;
if ( fSubtractFeeFromAmount ) {
subtractFeeFromAmount . push_back ( address ) ;
}
std : : vector < CRecipient > recipients ;
ParseRecipients ( address_amounts , subtractFeeFromAmount , recipients ) ;
2020-11-04 13:13:17 +01:00
const bool verbose { request . params [ 10 ] . isNull ( ) ? false : request . params [ 10 ] . get_bool ( ) } ;
2020-02-24 20:12:50 +01:00
2021-03-04 23:20:13 +08:00
return SendMoney ( * pwallet , coin_control , recipients , mapValue , verbose ) ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2019-07-16 17:01:46 -04:00
static CAmount GetReceived ( const CWallet & wallet , const UniValue & params , bool by_label ) EXCLUSIVE_LOCKS_REQUIRED ( wallet . cs_wallet )
2020-03-27 14:25:40 -04:00
{
std : : set < CTxDestination > address_set ;
if ( by_label ) {
// Get the set of addresses assigned to label
std : : string label = LabelFromValue ( params [ 0 ] ) ;
address_set = wallet . GetLabelAddresses ( label ) ;
} else {
// Get the address
CTxDestination dest = DecodeDestination ( params [ 0 ] . get_str ( ) ) ;
if ( ! IsValidDestination ( dest ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid Bitcoin address " ) ;
}
CScript script_pub_key = GetScriptForDestination ( dest ) ;
if ( ! wallet . IsMine ( script_pub_key ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Address not found in wallet " ) ;
}
address_set . insert ( dest ) ;
}
// Minimum confirmations
int min_depth = 1 ;
if ( ! params [ 1 ] . isNull ( ) )
min_depth = params [ 1 ] . get_int ( ) ;
2021-12-06 12:30:58 -05:00
const bool include_immature_coinbase { params [ 2 ] . isNull ( ) ? false : params [ 2 ] . get_bool ( ) } ;
// Excluding coinbase outputs is deprecated
// It can be enabled by setting deprecatedrpc=exclude_coinbase
const bool include_coinbase { ! wallet . chain ( ) . rpcEnableDeprecated ( " exclude_coinbase " ) } ;
if ( include_immature_coinbase & & ! include_coinbase ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " include_immature_coinbase is incompatible with deprecated exclude_coinbase " ) ;
}
2020-03-27 14:25:40 -04:00
// Tally
CAmount amount = 0 ;
for ( const std : : pair < const uint256 , CWalletTx > & wtx_pair : wallet . mapWallet ) {
const CWalletTx & wtx = wtx_pair . second ;
2021-12-06 12:30:58 -05:00
int depth { wallet . GetTxDepthInMainChain ( wtx ) } ;
if ( depth < min_depth
// Coinbase with less than 1 confirmation is no longer in the main chain
| | ( wtx . IsCoinBase ( ) & & ( depth < 1 | | ! include_coinbase ) )
| | ( wallet . IsTxImmatureCoinBase ( wtx ) & & ! include_immature_coinbase )
| | ! wallet . chain ( ) . checkFinalTx ( * wtx . tx ) ) {
2020-03-27 14:25:40 -04:00
continue ;
}
for ( const CTxOut & txout : wtx . tx - > vout ) {
CTxDestination address ;
if ( ExtractDestination ( txout . scriptPubKey , address ) & & wallet . IsMine ( address ) & & address_set . count ( address ) ) {
amount + = txout . nValue ;
}
}
}
return amount ;
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan getreceivedbyaddress ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " getreceivedbyaddress " ,
2018-10-20 08:19:44 -04:00
" \n Returns the total amount received by the given address in transactions with at least minconf confirmations. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The bitcoin address for transactions. " } ,
2021-04-14 15:01:00 +01:00
{ " minconf " , RPCArg : : Type : : NUM , RPCArg : : Default { 1 } , " Only include transactions confirmed at least this many times. " } ,
2021-12-06 12:30:58 -05:00
{ " include_immature_coinbase " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Include immature coinbase transactions. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR_AMOUNT , " amount " , " The total amount in " + CURRENCY_UNIT + " received at this address. "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2013-10-29 22:29:44 +11:00
" \n The amount from transactions with at least 1 confirmation \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " getreceivedbyaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" " ) +
2013-10-29 22:29:44 +11:00
" \n The amount including unconfirmed transactions, zero confirmations \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " getreceivedbyaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 0 " ) +
2017-07-19 16:50:26 -04:00
" \n The amount with at least 6 confirmations \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " getreceivedbyaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 6 " ) +
2021-12-06 12:30:58 -05:00
" \n The amount with at least 6 confirmations including immature coinbase outputs \n "
+ HelpExampleCli ( " getreceivedbyaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" 6 true " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleRpc ( " getreceivedbyaddress " , " \" " + EXAMPLE_ADDRESS [ 0 ] + " \" , 6 " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2019-07-16 17:01:46 -04:00
return ValueFromAmount ( GetReceived ( * pwallet , request . params , /* by_label */ false ) ) ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan getreceivedbylabel ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " getreceivedbylabel " ,
2018-10-20 08:19:44 -04:00
" \n Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " label " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The selected label, may be the default label using \" \" . " } ,
2021-04-14 15:01:00 +01:00
{ " minconf " , RPCArg : : Type : : NUM , RPCArg : : Default { 1 } , " Only include transactions confirmed at least this many times. " } ,
2021-12-06 12:30:58 -05:00
{ " include_immature_coinbase " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Include immature coinbase transactions. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR_AMOUNT , " amount " , " The total amount in " + CURRENCY_UNIT + " received for this label. "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2017-10-20 13:27:55 -04:00
" \n Amount received by the default label with at least 1 confirmation \n "
+ HelpExampleCli ( " getreceivedbylabel " , " \" \" " ) +
" \n Amount received at the tabby label including unconfirmed amounts with zero confirmations \n "
+ HelpExampleCli ( " getreceivedbylabel " , " \" tabby \" 0 " ) +
2017-07-19 16:50:26 -04:00
" \n The amount with at least 6 confirmations \n "
2017-10-20 13:27:55 -04:00
+ HelpExampleCli ( " getreceivedbylabel " , " \" tabby \" 6 " ) +
2021-12-06 12:30:58 -05:00
" \n The amount with at least 6 confirmations including immature coinbase outputs \n "
+ HelpExampleCli ( " getreceivedbylabel " , " \" tabby \" 6 true " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2021-12-06 12:30:58 -05:00
+ HelpExampleRpc ( " getreceivedbylabel " , " \" tabby \" , 6, true " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2019-07-16 17:01:46 -04:00
return ValueFromAmount ( GetReceived ( * pwallet , request . params , /* by_label */ true ) ) ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan getbalance ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " getbalance " ,
2018-10-20 08:19:44 -04:00
" \n Returns the total available balance. \n "
" The available balance is what the wallet considers currently spendable, and is \n "
" thus affected by options which limit spendability such as -spendzeroconfchange. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " dummy " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Remains for backward compatibility. Must be excluded or set to \" * \" . " } ,
2021-04-14 15:01:00 +01:00
{ " minconf " , RPCArg : : Type : : NUM , RPCArg : : Default { 0 } , " Only include transactions confirmed at least this many times. " } ,
{ " include_watchonly " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " true for watch-only wallets, otherwise false " } , " Also include balance in watch-only addresses (see 'importaddress') " } ,
{ " avoid_reuse " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " (only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR_AMOUNT , " amount " , " The total amount in " + CURRENCY_UNIT + " received for this wallet. "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2020-04-13 16:22:30 +08:00
" \n The total amount in the wallet with 0 or more confirmations \n "
2013-10-29 22:29:44 +11:00
+ HelpExampleCli ( " getbalance " , " " ) +
2020-04-13 16:22:30 +08:00
" \n The total amount in the wallet with at least 6 confirmations \n "
2014-01-29 13:41:34 +01:00
+ HelpExampleCli ( " getbalance " , " \" * \" 6 " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2014-12-30 14:32:07 +00:00
+ HelpExampleRpc ( " getbalance " , " \" * \" , 6 " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2018-07-27 17:05:24 -04:00
const UniValue & dummy_value = request . params [ 0 ] ;
if ( ! dummy_value . isNull ( ) & & dummy_value . get_str ( ) ! = " * " ) {
throw JSONRPCError ( RPC_METHOD_DEPRECATED , " dummy first argument must be excluded or set to \" * \" . " ) ;
}
2017-08-15 15:47:27 -04:00
2018-06-26 17:21:30 -04:00
int min_depth = 0 ;
if ( ! request . params [ 1 ] . isNull ( ) ) {
min_depth = request . params [ 1 ] . get_int ( ) ;
}
2019-07-13 08:34:49 -07:00
bool include_watchonly = ParseIncludeWatchonly ( request . params [ 2 ] , * pwallet ) ;
2018-06-26 17:21:30 -04:00
2021-03-04 23:20:13 +08:00
bool avoid_reuse = GetAvoidReuseFlag ( * pwallet , request . params [ 3 ] ) ;
2018-09-11 15:53:36 +09:00
2021-02-12 18:01:22 -05:00
const auto bal = GetBalance ( * pwallet , min_depth , avoid_reuse ) ;
2019-03-11 16:12:58 -04:00
return ValueFromAmount ( bal . m_mine_trusted + ( include_watchonly ? bal . m_watchonly_trusted : 0 ) ) ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan getunconfirmedbalance ( )
2013-11-24 12:48:52 +01:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " getunconfirmedbalance " ,
2019-05-01 10:39:18 -04:00
" DEPRECATED \n Identical to getbalances().mine.untrusted_pending \n " ,
2018-12-21 12:29:36 -05:00
{ } ,
2020-03-13 14:40:53 -04:00
RPCResult { RPCResult : : Type : : NUM , " " , " The balance " } ,
2018-12-21 12:29:36 -05:00
RPCExamples { " " } ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2021-02-12 18:01:22 -05:00
return ValueFromAmount ( GetBalance ( * pwallet ) . m_mine_untrusted_pending ) ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2013-11-24 12:48:52 +01:00
}
2012-08-21 10:38:57 -04:00
2020-09-22 18:13:52 +02:00
static RPCHelpMan sendmany ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " sendmany " ,
2018-11-23 11:21:38 -05:00
" \n Send multiple times. Amounts are double-precision floating point numbers. " +
2020-04-01 10:31:43 +08:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 08:19:44 -04:00
{
2018-12-10 16:56:51 -05:00
{ " dummy " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " Must be set to \" \" for backwards compatibility. " , " \" \" " } ,
2021-05-11 19:03:41 +09:00
{ " amounts " , RPCArg : : Type : : OBJ_USER_KEYS , RPCArg : : Optional : : NO , " The addresses and amounts " ,
2018-10-20 08:19:44 -04:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value " } ,
2018-10-20 08:19:44 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2019-03-13 14:45:53 -04:00
{ " minconf " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Ignored dummy value " } ,
2018-12-10 16:56:51 -05:00
{ " comment " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " A comment " } ,
2020-03-05 10:36:27 +00:00
{ " subtractfeefrom " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The addresses. \n "
2020-09-30 09:20:37 +02:00
" The fee will be equally deducted from the amount of each selected address. \n "
" Those recipients will receive less bitcoins than you enter in their corresponding amount field. \n "
" If no addresses are specified here, the sender pays the fee. " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " Subtract fee from this address " } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
2021-04-14 15:01:00 +01:00
{ " replaceable " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " wallet default " } , " Allow this transaction to be replaced by a transaction with higher fees via BIP 125 " } ,
{ " conf_target " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " wallet -txconfirmtarget " } , " Confirmation target in blocks " } ,
{ " estimate_mode " , RPCArg : : Type : : STR , RPCArg : : Default { " unset " } , std : : string ( ) + " The fee estimate mode, must be one of (case insensitive): \n "
2019-08-21 23:17:32 +09:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-04-14 15:01:00 +01:00
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_ATOM + " /vB. " } ,
{ " verbose " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " If true, return extra infomration about the transaction. " } ,
2020-07-12 12:52:02 -07:00
} ,
{
RPCResult { " if verbose is not set or set to false " ,
2020-09-30 09:20:37 +02:00
RPCResult : : Type : : STR_HEX , " txid " , " The transaction id for the send. Only 1 transaction is created regardless of \n "
2020-07-12 12:52:02 -07:00
" the number of addresses. "
} ,
RPCResult { " if verbose is set to true " ,
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id for the send. Only 1 transaction is created regardless of \n "
" the number of addresses. " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : STR , " fee_reason " , " The transaction fee reason. " }
2020-07-12 12:52:02 -07:00
} ,
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2018-04-16 14:01:52 -04:00
" \n Send two amounts to two different addresses: \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " sendmany " , " \" \" \" { \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" :0.01, \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" :0.02} \" " ) +
2018-04-16 14:01:52 -04:00
" \n Send two amounts to two different addresses setting the confirmation and comment: \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " sendmany " , " \" \" \" { \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" :0.01, \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" :0.02} \" 6 \" testing \" " ) +
2018-04-16 14:01:52 -04:00
" \n Send two amounts to two different addresses, subtract fee from amount: \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " sendmany " , " \" \" \" { \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" :0.01, \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" :0.02} \" 1 \" \" \" [ \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" , \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" ] \" " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2020-02-25 19:12:04 +03:00
+ HelpExampleRpc ( " sendmany " , " \" \" , { \" " + EXAMPLE_ADDRESS [ 0 ] + " \" :0.01, \" " + EXAMPLE_ADDRESS [ 1 ] + " \" :0.02}, 6, \" testing \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2018-07-27 17:05:24 -04:00
if ( ! request . params [ 0 ] . isNull ( ) & & ! request . params [ 0 ] . get_str ( ) . empty ( ) ) {
2018-04-16 14:01:52 -04:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Dummy value must be set to \" \" " ) ;
}
2016-09-22 09:46:41 +02:00
UniValue sendTo = request . params [ 1 ] . get_obj ( ) ;
2012-08-21 10:38:57 -04:00
2017-02-02 15:30:03 -05:00
mapValue_t mapValue ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 3 ] . isNull ( ) & & ! request . params [ 3 ] . get_str ( ) . empty ( ) )
2017-02-02 15:30:03 -05:00
mapValue [ " comment " ] = request . params [ 3 ] . get_str ( ) ;
2012-08-21 10:38:57 -04:00
2015-05-10 14:48:35 +02:00
UniValue subtractFeeFromAmount ( UniValue : : VARR ) ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 4 ] . isNull ( ) )
2016-09-22 09:46:41 +02:00
subtractFeeFromAmount = request . params [ 4 ] . get_array ( ) ;
2014-07-23 14:34:36 +02:00
2017-06-14 15:15:40 -04:00
CCoinControl coin_control ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 5 ] . isNull ( ) ) {
2018-04-07 12:12:46 -04:00
coin_control . m_signal_bip125_rbf = request . params [ 5 ] . get_bool ( ) ;
2017-06-14 15:15:40 -04:00
}
2020-11-17 20:22:01 +01:00
SetFeeEstimateMode ( * pwallet , coin_control , /* conf_target */ request . params [ 6 ] , /* estimate_mode */ request . params [ 7 ] , /* fee_rate */ request . params [ 8 ] , /* override_min_fee */ false ) ;
2017-06-14 15:15:40 -04:00
2020-02-24 20:12:50 +01:00
std : : vector < CRecipient > recipients ;
ParseRecipients ( sendTo , subtractFeeFromAmount , recipients ) ;
2020-11-04 13:13:17 +01:00
const bool verbose { request . params [ 9 ] . isNull ( ) ? false : request . params [ 9 ] . get_bool ( ) } ;
2018-03-16 14:33:29 -04:00
2021-03-04 23:20:13 +08:00
return SendMoney ( * pwallet , coin_control , recipients , std : : move ( mapValue ) , verbose ) ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan lockunspent ( )
2012-09-27 13:52:09 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " lockunspent " ,
2018-10-20 08:19:44 -04:00
" \n Updates list of temporarily unspendable outputs. \n "
" Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs. \n "
" If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked. \n "
" A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins. \n "
2020-08-07 13:53:51 +02:00
" Manually selected coins are automatically unlocked. \n "
2021-09-23 00:18:39 +12:00
" Locks are stored in memory only, unless persistent=true, in which case they will be written to the \n "
" wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared \n "
" (by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not. \n "
2018-10-20 08:19:44 -04:00
" Also see the listunspent call \n " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " unlock " , RPCArg : : Type : : BOOL , RPCArg : : Optional : : NO , " Whether to unlock (true) or lock (false) the specified transactions " } ,
2021-04-14 15:01:00 +01:00
{ " transactions " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " The transaction outputs and within each, the txid (string) vout (numeric). " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2021-09-23 00:18:39 +12:00
{ " persistent " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Whether to write/erase this lock in the wallet database, or keep the change in memory only. Ignored for unlocking. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : BOOL , " " , " Whether the command was successful or not "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2013-10-29 22:29:44 +11:00
" \n List the unspent transactions \n "
+ HelpExampleCli ( " listunspent " , " " ) +
" \n Lock an unspent transaction \n "
+ HelpExampleCli ( " lockunspent " , " false \" [{ \\ \" txid \\ \" : \\ \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \\ \" , \\ \" vout \\ \" :1}] \" " ) +
" \n List the locked transactions \n "
+ HelpExampleCli ( " listlockunspent " , " " ) +
" \n Unlock the transaction again \n "
+ HelpExampleCli ( " lockunspent " , " true \" [{ \\ \" txid \\ \" : \\ \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \\ \" , \\ \" vout \\ \" :1}] \" " ) +
2021-09-23 00:18:39 +12:00
" \n Lock the transaction persistently in the wallet database \n "
+ HelpExampleCli ( " lockunspent " , " false \" [{ \\ \" txid \\ \" : \\ \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \\ \" , \\ \" vout \\ \" :1}] \" true " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2013-10-29 22:29:44 +11:00
+ HelpExampleRpc ( " lockunspent " , " false, \" [{ \\ \" txid \\ \" : \\ \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \\ \" , \\ \" vout \\ \" :1}] \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2017-08-14 19:44:02 -04:00
RPCTypeCheckArgument ( request . params [ 0 ] , UniValue : : VBOOL ) ;
2012-09-27 13:52:09 -04:00
2016-09-22 09:46:41 +02:00
bool fUnlock = request . params [ 0 ] . get_bool ( ) ;
2012-09-27 13:52:09 -04:00
2021-09-23 00:18:39 +12:00
const bool persistent { request . params [ 2 ] . isNull ( ) ? false : request . params [ 2 ] . get_bool ( ) } ;
2017-08-14 19:44:02 -04:00
if ( request . params [ 1 ] . isNull ( ) ) {
2021-09-23 00:18:39 +12:00
if ( fUnlock ) {
if ( ! pwallet - > UnlockAllCoins ( ) )
throw JSONRPCError ( RPC_WALLET_ERROR , " Unlocking coins failed " ) ;
}
2012-09-27 13:52:09 -04:00
return true ;
}
2017-08-14 19:44:02 -04:00
RPCTypeCheckArgument ( request . params [ 1 ] , UniValue : : VARR ) ;
2017-08-18 14:21:40 +02:00
const UniValue & output_params = request . params [ 1 ] ;
// Create and validate the COutPoints first.
std : : vector < COutPoint > outputs ;
outputs . reserve ( output_params . size ( ) ) ;
for ( unsigned int idx = 0 ; idx < output_params . size ( ) ; idx + + ) {
const UniValue & o = output_params [ idx ] . get_obj ( ) ;
2012-09-27 13:52:09 -04:00
2016-06-06 17:50:50 +02:00
RPCTypeCheckObj ( o ,
{
{ " txid " , UniValueType ( UniValue : : VSTR ) } ,
{ " vout " , UniValueType ( UniValue : : VNUM ) } ,
} ) ;
2012-09-27 13:52:09 -04:00
2018-06-08 11:16:07 -07:00
const uint256 txid ( ParseHashO ( o , " txid " ) ) ;
2017-08-18 14:21:40 +02:00
const int nOutput = find_value ( o , " vout " ) . get_int ( ) ;
if ( nOutput < 0 ) {
2020-09-30 20:43:05 +03:30
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, vout cannot be negative " ) ;
2017-08-18 14:21:40 +02:00
}
2012-09-27 13:52:09 -04:00
2018-06-08 11:16:07 -07:00
const COutPoint outpt ( txid , nOutput ) ;
2012-09-27 13:52:09 -04:00
2017-08-18 14:21:40 +02:00
const auto it = pwallet - > mapWallet . find ( outpt . hash ) ;
if ( it = = pwallet - > mapWallet . end ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, unknown transaction " ) ;
}
const CWalletTx & trans = it - > second ;
if ( outpt . n > = trans . tx - > vout . size ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, vout index out of bounds " ) ;
}
2019-04-29 09:52:01 -04:00
if ( pwallet - > IsSpent ( outpt . hash , outpt . n ) ) {
2017-08-18 14:21:40 +02:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, expected unspent output " ) ;
}
const bool is_locked = pwallet - > IsLockedCoin ( outpt . hash , outpt . n ) ;
if ( fUnlock & & ! is_locked ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, expected locked output " ) ;
}
2021-09-23 00:18:39 +12:00
if ( ! fUnlock & & is_locked & & ! persistent ) {
2017-08-18 14:21:40 +02:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, output already locked " ) ;
}
outputs . push_back ( outpt ) ;
}
2021-09-23 00:18:39 +12:00
std : : unique_ptr < WalletBatch > batch = nullptr ;
// Unlock is always persistent
if ( fUnlock | | persistent ) batch = std : : make_unique < WalletBatch > ( pwallet - > GetDatabase ( ) ) ;
2017-08-18 14:21:40 +02:00
// Atomically set (un)locked status for the outputs.
for ( const COutPoint & outpt : outputs ) {
2021-09-23 00:18:39 +12:00
if ( fUnlock ) {
if ( ! pwallet - > UnlockCoin ( outpt , batch . get ( ) ) ) throw JSONRPCError ( RPC_WALLET_ERROR , " Unlocking coin failed " ) ;
} else {
if ( ! pwallet - > LockCoin ( outpt , batch . get ( ) ) ) throw JSONRPCError ( RPC_WALLET_ERROR , " Locking coin failed " ) ;
}
2012-09-27 13:52:09 -04:00
}
return true ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-09-27 13:52:09 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan listlockunspent ( )
2012-09-27 13:52:09 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " listlockunspent " ,
2018-10-20 08:19:44 -04:00
" \n Returns list of temporarily unspendable outputs. \n "
" See the lockunspent call to lock and unlock transactions for spending. \n " ,
2018-12-21 12:29:36 -05:00
{ } ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : ARR , " " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id locked " } ,
{ RPCResult : : Type : : NUM , " vout " , " The vout value " } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2013-10-29 22:29:44 +11:00
" \n List the unspent transactions \n "
+ HelpExampleCli ( " listunspent " , " " ) +
" \n Lock an unspent transaction \n "
+ HelpExampleCli ( " lockunspent " , " false \" [{ \\ \" txid \\ \" : \\ \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \\ \" , \\ \" vout \\ \" :1}] \" " ) +
" \n List the locked transactions \n "
+ HelpExampleCli ( " listlockunspent " , " " ) +
" \n Unlock the transaction again \n "
+ HelpExampleCli ( " lockunspent " , " true \" [{ \\ \" txid \\ \" : \\ \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \\ \" , \\ \" vout \\ \" :1}] \" " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2013-10-29 22:29:44 +11:00
+ HelpExampleRpc ( " listlockunspent " , " " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2017-01-27 10:33:45 +09:00
std : : vector < COutPoint > vOutpts ;
2016-10-25 07:45:57 +00:00
pwallet - > ListLockedCoins ( vOutpts ) ;
2012-09-27 13:52:09 -04:00
2015-05-10 14:48:35 +02:00
UniValue ret ( UniValue : : VARR ) ;
2012-09-27 13:52:09 -04:00
2018-06-18 07:58:28 +02:00
for ( const COutPoint & outpt : vOutpts ) {
2015-05-10 14:48:35 +02:00
UniValue o ( UniValue : : VOBJ ) ;
2012-09-27 13:52:09 -04:00
2017-09-22 20:04:07 +02:00
o . pushKV ( " txid " , outpt . hash . GetHex ( ) ) ;
o . pushKV ( " vout " , ( int ) outpt . n ) ;
2012-09-27 13:52:09 -04:00
ret . push_back ( o ) ;
}
return ret ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2012-09-27 13:52:09 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan settxfee ( )
2013-12-13 16:06:32 +01:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " settxfee " ,
2021-05-01 18:29:22 +02:00
" \n Set the transaction fee rate in " + CURRENCY_UNIT + " /kvB for this wallet. Overrides the global -paytxfee command line parameter. \n "
2020-03-31 17:07:33 +02:00
" Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default. \n " ,
2018-10-20 08:19:44 -04:00
{
2021-05-01 18:29:22 +02:00
{ " amount " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " The transaction fee rate in " + CURRENCY_UNIT + " /kvB " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : BOOL , " " , " Returns true if successful "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " settxfee " , " 0.00001 " )
2013-12-13 16:06:32 +01:00
+ HelpExampleRpc ( " settxfee " , " 0.00001 " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2016-09-22 09:46:41 +02:00
CAmount nAmount = AmountFromValue ( request . params [ 0 ] ) ;
2018-08-09 01:52:42 +10:00
CFeeRate tx_fee_rate ( nAmount , 1000 ) ;
2020-03-30 21:59:04 +02:00
CFeeRate max_tx_fee_rate ( pwallet - > m_default_max_tx_fee , 1000 ) ;
2019-08-20 09:58:35 -04:00
if ( tx_fee_rate = = CFeeRate ( 0 ) ) {
2018-08-09 01:52:42 +10:00
// automatic selection
2019-03-06 16:47:57 -05:00
} else if ( tx_fee_rate < pwallet - > chain ( ) . relayMinFee ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " txfee cannot be less than min relay tx fee (%s) " , pwallet->chain().relayMinFee().ToString())) ;
2018-08-09 01:52:42 +10:00
} else if ( tx_fee_rate < pwallet - > m_min_fee ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " txfee cannot be less than wallet min fee (%s) " , pwallet->m_min_fee.ToString())) ;
2020-03-30 21:59:04 +02:00
} else if ( tx_fee_rate > max_tx_fee_rate ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " txfee cannot be more than wallet max tx fee (%s) " , max_tx_fee_rate.ToString())) ;
2018-08-09 01:52:42 +10:00
}
2013-12-13 16:06:32 +01:00
2018-08-09 01:52:42 +10:00
pwallet - > m_pay_tx_fee = tx_fee_rate ;
2013-12-13 16:06:32 +01:00
return true ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2013-12-13 16:06:32 +01:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan getbalances ( )
2019-04-09 09:10:08 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan {
2019-04-09 09:10:08 -04:00
" getbalances " ,
" Returns an object with all balances in " + CURRENCY_UNIT + " . \n " ,
{ } ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : OBJ , " mine " , " balances from outputs that the wallet can sign " ,
{
{ RPCResult : : Type : : STR_AMOUNT , " trusted " , " trusted balance (outputs created by the wallet or confirmed outputs) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " untrusted_pending " , " untrusted pending balance (outputs created by others that are in the mempool) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " immature " , " balance from immature coinbase outputs " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : STR_AMOUNT , " used " , /* optional */ true , " (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating) " } ,
2020-01-10 00:00:57 +07:00
} } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : OBJ , " watchonly " , /* optional */ true , " watchonly balances (not present if wallet does not watch anything) " ,
2020-01-10 00:00:57 +07:00
{
{ RPCResult : : Type : : STR_AMOUNT , " trusted " , " trusted balance (outputs created by the wallet or confirmed outputs) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " untrusted_pending " , " untrusted pending balance (outputs created by others that are in the mempool) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " immature " , " balance from immature coinbase outputs " } ,
} } ,
}
} ,
2019-04-09 09:10:08 -04:00
RPCExamples {
HelpExampleCli ( " getbalances " , " " ) +
HelpExampleRpc ( " getbalances " , " " ) } ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > rpc_wallet = GetWalletForJSONRPCRequest ( request ) ;
2020-06-11 10:23:26 -04:00
if ( ! rpc_wallet ) return NullUniValue ;
2021-08-25 16:39:04 +09:00
const CWallet & wallet = * rpc_wallet ;
2020-06-11 10:23:26 -04:00
2019-04-09 09:10:08 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet . BlockUntilSyncedToCurrentChain ( ) ;
LOCK ( wallet . cs_wallet ) ;
2021-02-12 18:01:22 -05:00
const auto bal = GetBalance ( wallet ) ;
2019-04-09 09:10:08 -04:00
UniValue balances { UniValue : : VOBJ } ;
{
UniValue balances_mine { UniValue : : VOBJ } ;
balances_mine . pushKV ( " trusted " , ValueFromAmount ( bal . m_mine_trusted ) ) ;
balances_mine . pushKV ( " untrusted_pending " , ValueFromAmount ( bal . m_mine_untrusted_pending ) ) ;
balances_mine . pushKV ( " immature " , ValueFromAmount ( bal . m_mine_immature ) ) ;
2019-06-19 11:43:51 +09:00
if ( wallet . IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ) {
// If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
// the total balance, and then subtract bal to get the reused address balance.
2021-02-12 18:01:22 -05:00
const auto full_bal = GetBalance ( wallet , 0 , false ) ;
2019-06-19 11:43:51 +09:00
balances_mine . pushKV ( " used " , ValueFromAmount ( full_bal . m_mine_trusted + full_bal . m_mine_untrusted_pending - bal . m_mine_trusted - bal . m_mine_untrusted_pending ) ) ;
}
2019-04-09 09:10:08 -04:00
balances . pushKV ( " mine " , balances_mine ) ;
}
2019-10-07 14:11:34 -04:00
auto spk_man = wallet . GetLegacyScriptPubKeyMan ( ) ;
if ( spk_man & & spk_man - > HaveWatchOnly ( ) ) {
2019-04-09 09:10:08 -04:00
UniValue balances_watchonly { UniValue : : VOBJ } ;
balances_watchonly . pushKV ( " trusted " , ValueFromAmount ( bal . m_watchonly_trusted ) ) ;
balances_watchonly . pushKV ( " untrusted_pending " , ValueFromAmount ( bal . m_watchonly_untrusted_pending ) ) ;
balances_watchonly . pushKV ( " immature " , ValueFromAmount ( bal . m_watchonly_immature ) ) ;
balances . pushKV ( " watchonly " , balances_watchonly ) ;
}
return balances ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2019-04-09 09:10:08 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan getwalletinfo ( )
2014-02-21 17:56:04 +13:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " getwalletinfo " ,
2018-12-21 12:29:36 -05:00
" Returns an object containing various wallet state info. \n " ,
{ } ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{
{ RPCResult : : Type : : STR , " walletname " , " the wallet name " } ,
{ RPCResult : : Type : : NUM , " walletversion " , " the wallet version " } ,
2020-10-11 23:19:44 +01:00
{ RPCResult : : Type : : STR , " format " , " the database format (bdb or sqlite) " } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : STR_AMOUNT , " balance " , " DEPRECATED. Identical to getbalances().mine.trusted " } ,
{ RPCResult : : Type : : STR_AMOUNT , " unconfirmed_balance " , " DEPRECATED. Identical to getbalances().mine.untrusted_pending " } ,
{ RPCResult : : Type : : STR_AMOUNT , " immature_balance " , " DEPRECATED. Identical to getbalances().mine.immature " } ,
{ RPCResult : : Type : : NUM , " txcount " , " the total number of transactions in the wallet " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : NUM_TIME , " keypoololdest " , /* optional */ true , " the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only. " } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : NUM , " keypoolsize " , " how many new keys are pre-generated (only counts external keys) " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : NUM , " keypoolsize_hd_internal " , /* optional */ true , " how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used) " } ,
2020-07-31 12:27:48 -05:00
{ RPCResult : : Type : : NUM_TIME , " unlocked_until " , /* optional */ true , " the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets) " } ,
2020-11-06 19:07:16 +01:00
{ RPCResult : : Type : : STR_AMOUNT , " paytxfee " , " the transaction fee configuration, set in " + CURRENCY_UNIT + " /kvB " } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : STR_HEX , " hdseedid " , /* optional */ true , " the Hash160 of the HD seed (only present when HD is enabled) " } ,
{ RPCResult : : Type : : BOOL , " private_keys_enabled " , " false if privatekeys are disabled for this wallet (enforced watch-only wallet) " } ,
{ RPCResult : : Type : : BOOL , " avoid_reuse " , " whether this wallet tracks clean/dirty coins in terms of reuse " } ,
{ RPCResult : : Type : : OBJ , " scanning " , " current scanning details, or false if no scan is in progress " ,
{
{ RPCResult : : Type : : NUM , " duration " , " elapsed seconds since scan start " } ,
{ RPCResult : : Type : : NUM , " progress " , " scanning progress percentage [0.0, 1.0] " } ,
} } ,
2019-07-05 22:32:59 -04:00
{ RPCResult : : Type : : BOOL , " descriptors " , " whether this wallet uses descriptors for scriptPubKey management " } ,
2020-01-10 00:00:57 +07:00
} } ,
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " getwalletinfo " , " " )
2014-02-21 17:56:04 +13:00
+ HelpExampleRpc ( " getwalletinfo " , " " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 04:46:17 -04:00
2015-05-10 14:48:35 +02:00
UniValue obj ( UniValue : : VOBJ ) ;
2017-01-26 20:52:57 +01:00
2017-03-28 09:18:20 +02:00
size_t kpExternalSize = pwallet - > KeypoolCountExternalKeys ( ) ;
2021-02-12 18:01:22 -05:00
const auto bal = GetBalance ( * pwallet ) ;
2017-09-22 20:04:07 +02:00
obj . pushKV ( " walletname " , pwallet - > GetName ( ) ) ;
obj . pushKV ( " walletversion " , pwallet - > GetVersion ( ) ) ;
2020-10-11 23:19:44 +01:00
obj . pushKV ( " format " , pwallet - > GetDatabase ( ) . Format ( ) ) ;
2019-03-11 16:12:58 -04:00
obj . pushKV ( " balance " , ValueFromAmount ( bal . m_mine_trusted ) ) ;
obj . pushKV ( " unconfirmed_balance " , ValueFromAmount ( bal . m_mine_untrusted_pending ) ) ;
obj . pushKV ( " immature_balance " , ValueFromAmount ( bal . m_mine_immature ) ) ;
2017-09-22 20:04:07 +02:00
obj . pushKV ( " txcount " , ( int ) pwallet - > mapWallet . size ( ) ) ;
2021-10-30 22:19:22 +03:00
const auto kp_oldest = pwallet - > GetOldestKeyPoolTime ( ) ;
if ( kp_oldest . has_value ( ) ) {
obj . pushKV ( " keypoololdest " , kp_oldest . value ( ) ) ;
2019-07-11 16:14:17 -04:00
}
2017-09-22 20:04:07 +02:00
obj . pushKV ( " keypoolsize " , ( int64_t ) kpExternalSize ) ;
2019-10-07 14:11:34 -04:00
LegacyScriptPubKeyMan * spk_man = pwallet - > GetLegacyScriptPubKeyMan ( ) ;
if ( spk_man ) {
CKeyID seed_id = spk_man - > GetHDChain ( ) . seed_id ;
if ( ! seed_id . IsNull ( ) ) {
obj . pushKV ( " hdseedid " , seed_id . GetHex ( ) ) ;
}
}
2018-11-06 09:44:51 -05:00
if ( pwallet - > CanSupportFeature ( FEATURE_HD_SPLIT ) ) {
2017-09-22 20:04:07 +02:00
obj . pushKV ( " keypoolsize_hd_internal " , ( int64_t ) ( pwallet - > GetKeyPoolSize ( ) - kpExternalSize ) ) ;
2017-01-10 16:45:30 +01:00
}
2016-10-25 08:04:23 +00:00
if ( pwallet - > IsCrypted ( ) ) {
2017-09-22 20:04:07 +02:00
obj . pushKV ( " unlocked_until " , pwallet - > nRelockTime ) ;
2016-10-25 08:04:23 +00:00
}
2018-04-07 12:12:46 -04:00
obj . pushKV ( " paytxfee " , ValueFromAmount ( pwallet - > m_pay_tx_fee . GetFeePerK ( ) ) ) ;
2017-05-05 08:53:39 +02:00
obj . pushKV ( " private_keys_enabled " , ! pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
2018-09-11 15:53:36 +09:00
obj . pushKV ( " avoid_reuse " , pwallet - > IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ) ;
2019-04-03 16:56:58 +01:00
if ( pwallet - > IsScanning ( ) ) {
UniValue scanning ( UniValue : : VOBJ ) ;
scanning . pushKV ( " duration " , pwallet - > ScanningDuration ( ) / 1000 ) ;
scanning . pushKV ( " progress " , pwallet - > ScanningProgress ( ) ) ;
obj . pushKV ( " scanning " , scanning ) ;
} else {
obj . pushKV ( " scanning " , false ) ;
}
2019-07-05 22:32:59 -04:00
obj . pushKV ( " descriptors " , pwallet - > IsWalletFlagSet ( WALLET_FLAG_DESCRIPTORS ) ) ;
2014-02-21 17:56:04 +13:00
return obj ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2014-02-21 17:56:04 +13:00
}
2015-03-23 13:47:18 -04:00
2020-09-22 18:13:52 +02:00
static RPCHelpMan listwalletdir ( )
2018-09-24 23:55:30 +01:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " listwalletdir " ,
2018-12-21 12:29:36 -05:00
" Returns a list of wallets in the wallet directory. \n " ,
{ } ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : ARR , " wallets " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " name " , " The wallet name " } ,
} } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " listwalletdir " , " " )
2018-09-24 23:55:30 +01:00
+ HelpExampleRpc ( " listwalletdir " , " " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-09-24 23:55:30 +01:00
UniValue wallets ( UniValue : : VARR ) ;
2020-10-30 16:25:56 -04:00
for ( const auto & path : ListDatabases ( GetWalletDir ( ) ) ) {
2018-09-24 23:55:30 +01:00
UniValue wallet ( UniValue : : VOBJ ) ;
2021-09-10 00:17:20 -04:00
wallet . pushKV ( " name " , path . u8string ( ) ) ;
2018-09-24 23:55:30 +01:00
wallets . push_back ( wallet ) ;
}
UniValue result ( UniValue : : VOBJ ) ;
result . pushKV ( " wallets " , wallets ) ;
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-09-24 23:55:30 +01:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan listwallets ( )
2017-06-27 09:46:55 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " listwallets " ,
2018-10-20 08:19:44 -04:00
" Returns a list of currently loaded wallets. \n "
" For full information on the wallet, use \" getwalletinfo \" \n " ,
2018-12-21 12:29:36 -05:00
{ } ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : ARR , " " , " " ,
{
{ RPCResult : : Type : : STR , " walletname " , " the wallet name " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " listwallets " , " " )
2017-06-27 09:46:55 -04:00
+ HelpExampleRpc ( " listwallets " , " " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2017-06-27 09:46:55 -04:00
UniValue obj ( UniValue : : VARR ) ;
2020-05-28 13:06:43 -04:00
WalletContext & context = EnsureWalletContext ( request . context ) ;
for ( const std : : shared_ptr < CWallet > & wallet : GetWallets ( context ) ) {
2018-05-22 16:18:07 +01:00
LOCK ( wallet - > cs_wallet ) ;
obj . push_back ( wallet - > GetName ( ) ) ;
2017-06-27 09:46:55 -04:00
}
return obj ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2017-06-27 09:46:55 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan loadwallet ( )
2018-04-18 16:01:39 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " loadwallet " ,
2018-10-20 08:19:44 -04:00
" \n Loads a wallet from a wallet file or directory. "
" \n Note that all wallet command-line options used when starting bitcoind will be "
2021-09-29 16:32:07 +13:00
" \n applied to the new wallet. \n " ,
2018-10-20 08:19:44 -04:00
{
2018-12-10 16:56:51 -05:00
{ " filename " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The wallet directory or .dat file. " } ,
2021-05-12 00:49:49 +09:00
{ " load_on_startup " , RPCArg : : Type : : BOOL , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " name " , " The wallet name if loaded successfully. " } ,
{ RPCResult : : Type : : STR , " warning " , " Warning message if wallet was not loaded cleanly. " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " loadwallet " , " \" test.dat \" " )
2018-04-18 16:01:39 -04:00
+ HelpExampleRpc ( " loadwallet " , " \" test.dat \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2020-05-28 02:13:19 -04:00
WalletContext & context = EnsureWalletContext ( request . context ) ;
2020-07-28 19:25:14 -04:00
const std : : string name ( request . params [ 0 ] . get_str ( ) ) ;
2018-04-18 16:01:39 -04:00
2021-07-27 14:28:23 -03:00
auto [ wallet , warnings ] = LoadWalletHelper ( context , request . params [ 1 ] , name ) ;
2018-04-18 16:01:39 -04:00
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " name " , wallet - > GetName ( ) ) ;
2020-05-10 21:28:29 +03:00
obj . pushKV ( " warning " , Join ( warnings , Untranslated ( " \n " ) ) . original ) ;
2018-04-18 16:01:39 -04:00
return obj ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-04-18 16:01:39 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan setwalletflag ( )
2018-09-13 13:35:10 +09:00
{
2019-06-19 13:59:11 +09:00
std : : string flags = " " ;
for ( auto & it : WALLET_FLAG_MAP )
if ( it . second & MUTABLE_WALLET_FLAGS )
flags + = ( flags = = " " ? " " : " , " ) + it . first ;
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " setwalletflag " ,
2018-09-13 13:35:10 +09:00
" \n Change the state of the given wallet flag for a wallet. \n " ,
{
{ " flag " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The name of the flag to change. Current available flags: " + flags } ,
2021-04-14 15:01:00 +01:00
{ " value " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " The new state. " } ,
2018-09-13 13:35:10 +09:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " flag_name " , " The name of the flag that was modified " } ,
{ RPCResult : : Type : : BOOL , " flag_state " , " The new state of the flag " } ,
{ RPCResult : : Type : : STR , " warnings " , " Any warnings associated with the change " } ,
}
2018-09-13 13:35:10 +09:00
} ,
RPCExamples {
HelpExampleCli ( " setwalletflag " , " avoid_reuse " )
+ HelpExampleRpc ( " setwalletflag " , " \" avoid_reuse \" " )
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2018-09-13 13:35:10 +09:00
std : : string flag_str = request . params [ 0 ] . get_str ( ) ;
bool value = request . params [ 1 ] . isNull ( ) | | request . params [ 1 ] . get_bool ( ) ;
if ( ! WALLET_FLAG_MAP . count ( flag_str ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Unknown wallet flag: %s " , flag_str ) ) ;
}
auto flag = WALLET_FLAG_MAP . at ( flag_str ) ;
if ( ! ( flag & MUTABLE_WALLET_FLAGS ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Wallet flag is immutable: %s " , flag_str ) ) ;
}
UniValue res ( UniValue : : VOBJ ) ;
if ( pwallet - > IsWalletFlagSet ( flag ) = = value ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Wallet flag is already set to %s: %s " , value ? " true " : " false " , flag_str ) ) ;
}
res . pushKV ( " flag_name " , flag_str ) ;
res . pushKV ( " flag_state " , value ) ;
if ( value ) {
pwallet - > SetWalletFlag ( flag ) ;
} else {
pwallet - > UnsetWalletFlag ( flag ) ;
}
if ( flag & & value & & WALLET_FLAG_CAVEATS . count ( flag ) ) {
res . pushKV ( " warnings " , WALLET_FLAG_CAVEATS . at ( flag ) ) ;
}
return res ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-09-13 13:35:10 +09:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan createwallet ( )
2018-04-23 12:44:22 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan {
2018-12-19 16:30:49 -05:00
" createwallet " ,
" \n Creates and loads a new wallet. \n " ,
{
{ " wallet_name " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The name for the new wallet. If this is a path, the wallet will be created at the path location. " } ,
2021-04-14 15:01:00 +01:00
{ " disable_private_keys " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Disable the possibility of private keys (only watchonlys are possible in this mode). " } ,
{ " blank " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed. " } ,
2021-05-11 19:05:51 +09:00
{ " passphrase " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Encrypt the wallet with this passphrase. " } ,
2021-04-14 15:01:00 +01:00
{ " avoid_reuse " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind. " } ,
2021-09-16 18:05:44 -04:00
{ " descriptors " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation " } ,
2021-05-12 00:49:49 +09:00
{ " load_on_startup " , RPCArg : : Type : : BOOL , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged. " } ,
2021-04-14 15:01:00 +01:00
{ " external_signer " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true. " } ,
2018-12-19 16:30:49 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " name " , " The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path. " } ,
{ RPCResult : : Type : : STR , " warning " , " Warning message if wallet was not loaded cleanly. " } ,
}
2018-12-19 16:30:49 -05:00
} ,
RPCExamples {
HelpExampleCli ( " createwallet " , " \" testwallet \" " )
2018-04-23 13:14:34 -04:00
+ HelpExampleRpc ( " createwallet " , " \" testwallet \" " )
2021-02-26 15:03:50 +01:00
+ HelpExampleCliNamed ( " createwallet " , { { " wallet_name " , " descriptors " } , { " avoid_reuse " , true } , { " descriptors " , true } , { " load_on_startup " , true } } )
+ HelpExampleRpcNamed ( " createwallet " , { { " wallet_name " , " descriptors " } , { " avoid_reuse " , true } , { " descriptors " , true } , { " load_on_startup " , true } } )
2018-12-19 16:30:49 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2020-05-28 02:13:19 -04:00
WalletContext & context = EnsureWalletContext ( request . context ) ;
2019-02-06 21:26:55 -05:00
uint64_t flags = 0 ;
if ( ! request . params [ 1 ] . isNull ( ) & & request . params [ 1 ] . get_bool ( ) ) {
flags | = WALLET_FLAG_DISABLE_PRIVATE_KEYS ;
}
if ( ! request . params [ 2 ] . isNull ( ) & & request . params [ 2 ] . get_bool ( ) ) {
2018-12-19 16:30:49 -05:00
flags | = WALLET_FLAG_BLANK_WALLET ;
}
SecureString passphrase ;
passphrase . reserve ( 100 ) ;
2019-08-19 18:12:35 -04:00
std : : vector < bilingual_str > warnings ;
2018-12-19 16:30:49 -05:00
if ( ! request . params [ 3 ] . isNull ( ) ) {
passphrase = request . params [ 3 ] . get_str ( ) . c_str ( ) ;
if ( passphrase . empty ( ) ) {
2019-07-15 15:33:56 -04:00
// Empty string means unencrypted
2019-08-19 18:12:35 -04:00
warnings . emplace_back ( Untranslated ( " Empty string given as passphrase, wallet will not be encrypted. " ) ) ;
2018-12-19 16:30:49 -05:00
}
2018-06-13 20:35:41 +02:00
}
2018-09-11 15:53:36 +09:00
if ( ! request . params [ 4 ] . isNull ( ) & & request . params [ 4 ] . get_bool ( ) ) {
flags | = WALLET_FLAG_AVOID_REUSE ;
}
2021-09-16 18:05:44 -04:00
if ( request . params [ 5 ] . isNull ( ) | | request . params [ 5 ] . get_bool ( ) ) {
2020-10-15 20:20:00 +00:00
# ifndef USE_SQLITE
throw JSONRPCError ( RPC_WALLET_ERROR , " Compiled without sqlite support (required for descriptor wallets) " ) ;
# endif
2019-07-11 18:21:21 -04:00
flags | = WALLET_FLAG_DESCRIPTORS ;
}
2019-08-04 17:56:17 +02:00
if ( ! request . params [ 7 ] . isNull ( ) & & request . params [ 7 ] . get_bool ( ) ) {
# ifdef ENABLE_EXTERNAL_SIGNER
flags | = WALLET_FLAG_EXTERNAL_SIGNER ;
# else
2021-04-13 15:08:33 +08:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Compiled without external signing support (required for external signing) " ) ;
2019-08-04 17:56:17 +02:00
# endif
}
2018-09-11 15:53:36 +09:00
2020-10-19 17:34:20 -04:00
# ifndef USE_BDB
if ( ! ( flags & WALLET_FLAG_DESCRIPTORS ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Compiled without bdb support (required for legacy wallets) " ) ;
}
# endif
2020-08-04 17:55:13 -04:00
DatabaseOptions options ;
DatabaseStatus status ;
2020-08-04 21:03:27 -04:00
options . require_create = true ;
2020-08-04 17:55:13 -04:00
options . create_flags = flags ;
options . create_passphrase = passphrase ;
2019-08-19 18:12:35 -04:00
bilingual_str error ;
2021-03-15 10:41:30 +08:00
std : : optional < bool > load_on_start = request . params [ 6 ] . isNull ( ) ? std : : nullopt : std : : optional < bool > ( request . params [ 6 ] . get_bool ( ) ) ;
2021-08-25 16:37:14 +09:00
const std : : shared_ptr < CWallet > wallet = CreateWallet ( context , request . params [ 0 ] . get_str ( ) , load_on_start , options , status , error , warnings ) ;
2020-08-04 17:55:13 -04:00
if ( ! wallet ) {
RPCErrorCode code = status = = DatabaseStatus : : FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR ;
throw JSONRPCError ( code , error . original ) ;
2018-12-19 16:30:49 -05:00
}
2018-04-23 12:44:22 -04:00
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " name " , wallet - > GetName ( ) ) ;
2020-05-10 21:28:29 +03:00
obj . pushKV ( " warning " , Join ( warnings , Untranslated ( " \n " ) ) . original ) ;
2018-04-23 12:44:22 -04:00
return obj ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-04-23 12:44:22 -04:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan unloadwallet ( )
2018-04-28 22:36:43 +01:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " unloadwallet " ,
2018-10-20 08:19:44 -04:00
" Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument. \n "
" Specifying the wallet name on a wallet endpoint is invalid. " ,
{
2021-04-14 15:01:00 +01:00
{ " wallet_name " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " the wallet name from the RPC endpoint " } , " The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical. " } ,
2021-05-12 00:49:49 +09:00
{ " load_on_startup " , RPCArg : : Type : : BOOL , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged. " } ,
2018-12-21 12:29:36 -05:00
} ,
2019-05-01 15:12:44 -04:00
RPCResult { RPCResult : : Type : : OBJ , " " , " " , {
{ RPCResult : : Type : : STR , " warning " , " Warning message if wallet was not unloaded cleanly. " } ,
} } ,
2018-12-21 12:29:36 -05:00
RPCExamples {
HelpExampleCli ( " unloadwallet " , " wallet_name " )
2018-04-28 22:36:43 +01:00
+ HelpExampleRpc ( " unloadwallet " , " wallet_name " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-04-28 22:36:43 +01:00
std : : string wallet_name ;
if ( GetWalletNameFromJSONRPCRequest ( request , wallet_name ) ) {
2020-11-21 18:57:22 +00:00
if ( ! ( request . params [ 0 ] . isNull ( ) | | request . params [ 0 ] . get_str ( ) = = wallet_name ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " RPC endpoint wallet and wallet_name parameter specify different wallets " ) ;
2018-04-28 22:36:43 +01:00
}
} else {
wallet_name = request . params [ 0 ] . get_str ( ) ;
}
2020-05-28 13:06:43 -04:00
WalletContext & context = EnsureWalletContext ( request . context ) ;
std : : shared_ptr < CWallet > wallet = GetWallet ( context , wallet_name ) ;
2018-04-28 22:36:43 +01:00
if ( ! wallet ) {
throw JSONRPCError ( RPC_WALLET_NOT_FOUND , " Requested wallet does not exist or is not loaded " ) ;
}
// Release the "main" shared pointer and prevent further notifications.
// Note that any attempt to load the same wallet would fail until the wallet
// is destroyed (see CheckUniqueFileid).
2020-08-17 15:21:50 -04:00
std : : vector < bilingual_str > warnings ;
2021-03-15 10:41:30 +08:00
std : : optional < bool > load_on_start = request . params [ 1 ] . isNull ( ) ? std : : nullopt : std : : optional < bool > ( request . params [ 1 ] . get_bool ( ) ) ;
2020-05-28 13:06:43 -04:00
if ( ! RemoveWallet ( context , wallet , load_on_start , warnings ) ) {
2018-04-28 22:36:43 +01:00
throw JSONRPCError ( RPC_MISC_ERROR , " Requested wallet already unloaded " ) ;
}
2018-12-12 23:21:19 +00:00
UnloadWallet ( std : : move ( wallet ) ) ;
2018-04-28 22:36:43 +01:00
2019-05-01 15:12:44 -04:00
UniValue result ( UniValue : : VOBJ ) ;
result . pushKV ( " warning " , Join ( warnings , Untranslated ( " \n " ) ) . original ) ;
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-04-28 22:36:43 +01:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan listunspent ( )
2015-04-12 17:56:32 +02:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan {
2019-06-19 11:55:41 +09:00
" listunspent " ,
2018-10-20 08:19:44 -04:00
" \n Returns array of unspent transaction outputs \n "
" with between minconf and maxconf (inclusive) confirmations. \n "
" Optionally filter to only include txouts paid to specified addresses. \n " ,
2018-10-23 15:22:28 -04:00
{
2021-04-14 15:01:00 +01:00
{ " minconf " , RPCArg : : Type : : NUM , RPCArg : : Default { 1 } , " The minimum confirmations to filter " } ,
{ " maxconf " , RPCArg : : Type : : NUM , RPCArg : : Default { 9999999 } , " The maximum confirmations to filter " } ,
{ " addresses " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " The bitcoin addresses to filter " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " bitcoin address " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2021-04-14 15:01:00 +01:00
{ " include_unsafe " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Include outputs that are not safe to spend \n "
2020-09-30 09:20:37 +02:00
" See description of \" safe \" attribute below. " } ,
2018-12-10 16:56:51 -05:00
{ " query_options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " JSON with query options " ,
2018-10-23 15:22:28 -04:00
{
2021-04-14 15:01:00 +01:00
{ " minimumAmount " , RPCArg : : Type : : AMOUNT , RPCArg : : Default { FormatMoney ( 0 ) } , " Minimum value of each UTXO in " + CURRENCY_UNIT + " " } ,
{ " maximumAmount " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " unlimited " } , " Maximum value of each UTXO in " + CURRENCY_UNIT + " " } ,
{ " maximumCount " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " unlimited " } , " Maximum number of UTXOs " } ,
{ " minimumSumAmount " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " unlimited " } , " Minimum sum value of all UTXOs in " + CURRENCY_UNIT + " " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
" query_options " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : ARR , " " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " the transaction id " } ,
{ RPCResult : : Type : : NUM , " vout " , " the vout value " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : STR , " address " , /* optional */ true , " the bitcoin address " } ,
{ RPCResult : : Type : : STR , " label " , /* optional */ true , " The associated label, or \" \" for the default label " } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : STR , " scriptPubKey " , " the script key " } ,
{ RPCResult : : Type : : STR_AMOUNT , " amount " , " the transaction output amount in " + CURRENCY_UNIT } ,
{ RPCResult : : Type : : NUM , " confirmations " , " The number of confirmations " } ,
2018-03-12 17:59:50 +00:00
{ RPCResult : : Type : : NUM , " ancestorcount " , /* optional */ true , " The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool) " } ,
{ RPCResult : : Type : : NUM , " ancestorsize " , /* optional */ true , " The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " ancestorfees " , /* optional */ true , " The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool) " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : STR_HEX , " redeemScript " , /* optional */ true , " The redeemScript if scriptPubKey is P2SH " } ,
{ RPCResult : : Type : : STR , " witnessScript " , /* optional */ true , " witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH " } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : BOOL , " spendable " , " Whether we have the private keys to spend this output " } ,
{ RPCResult : : Type : : BOOL , " solvable " , " Whether we know how to spend this output, ignoring the lack of keys " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : BOOL , " reused " , /* optional */ true , " (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from) " } ,
{ RPCResult : : Type : : STR , " desc " , /* optional */ true , " (only when solvable) A descriptor for spending this output " } ,
2020-03-13 15:44:11 -04:00
{ RPCResult : : Type : : BOOL , " safe " , " Whether this output is considered safe to spend. Unconfirmed transactions \n "
" from outside keys and unconfirmed replacement transactions are considered unsafe \n "
" and are not eligible for spending by fundrawtransaction and sendtoaddress. " } ,
2020-01-10 00:00:57 +07:00
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " listunspent " , " " )
2020-02-25 19:12:04 +03:00
+ HelpExampleCli ( " listunspent " , " 6 9999999 \" [ \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" , \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" ] \" " )
+ HelpExampleRpc ( " listunspent " , " 6, 9999999 \" [ \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" , \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" ] \" " )
2017-03-07 18:17:32 +00:00
+ HelpExampleCli ( " listunspent " , " 6 9999999 '[]' true '{ \" minimumAmount \" : 0.005 }' " )
+ HelpExampleRpc ( " listunspent " , " 6, 9999999, [] , true, { \" minimumAmount \" : 0.005 } " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2015-04-12 17:56:32 +02:00
int nMinDepth = 1 ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 0 ] . isNull ( ) ) {
2017-01-17 10:40:41 -05:00
RPCTypeCheckArgument ( request . params [ 0 ] , UniValue : : VNUM ) ;
2016-09-22 09:46:41 +02:00
nMinDepth = request . params [ 0 ] . get_int ( ) ;
2017-01-17 10:40:41 -05:00
}
2015-04-13 15:04:08 +02:00
2015-04-12 17:56:32 +02:00
int nMaxDepth = 9999999 ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 1 ] . isNull ( ) ) {
2017-01-17 10:40:41 -05:00
RPCTypeCheckArgument ( request . params [ 1 ] , UniValue : : VNUM ) ;
2016-09-22 09:46:41 +02:00
nMaxDepth = request . params [ 1 ] . get_int ( ) ;
2017-01-17 10:40:41 -05:00
}
2015-04-13 15:04:08 +02:00
2017-08-22 18:02:33 -07:00
std : : set < CTxDestination > destinations ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 2 ] . isNull ( ) ) {
2017-01-17 10:40:41 -05:00
RPCTypeCheckArgument ( request . params [ 2 ] , UniValue : : VARR ) ;
2016-09-22 09:46:41 +02:00
UniValue inputs = request . params [ 2 ] . get_array ( ) ;
2015-05-10 13:35:44 +02:00
for ( unsigned int idx = 0 ; idx < inputs . size ( ) ; idx + + ) {
2015-05-18 14:02:18 +02:00
const UniValue & input = inputs [ idx ] ;
2017-08-22 18:02:33 -07:00
CTxDestination dest = DecodeDestination ( input . get_str ( ) ) ;
if ( ! IsValidDestination ( dest ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , std : : string ( " Invalid Bitcoin address: " ) + input . get_str ( ) ) ;
}
if ( ! destinations . insert ( dest ) . second ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , std : : string ( " Invalid parameter, duplicated address: " ) + input . get_str ( ) ) ;
}
2015-04-12 17:56:32 +02:00
}
}
2015-04-13 15:04:08 +02:00
2017-01-17 10:40:41 -05:00
bool include_unsafe = true ;
2017-08-14 19:38:18 -04:00
if ( ! request . params [ 3 ] . isNull ( ) ) {
2017-01-17 10:40:41 -05:00
RPCTypeCheckArgument ( request . params [ 3 ] , UniValue : : VBOOL ) ;
include_unsafe = request . params [ 3 ] . get_bool ( ) ;
}
2017-03-07 18:17:32 +00:00
CAmount nMinimumAmount = 0 ;
CAmount nMaximumAmount = MAX_MONEY ;
CAmount nMinimumSumAmount = MAX_MONEY ;
uint64_t nMaximumCount = 0 ;
2017-07-10 11:44:39 -04:00
if ( ! request . params [ 4 ] . isNull ( ) ) {
2017-03-07 18:17:32 +00:00
const UniValue & options = request . params [ 4 ] . get_obj ( ) ;
2020-06-05 13:40:02 -05:00
RPCTypeCheckObj ( options ,
{
{ " minimumAmount " , UniValueType ( ) } ,
{ " maximumAmount " , UniValueType ( ) } ,
{ " minimumSumAmount " , UniValueType ( ) } ,
{ " maximumCount " , UniValueType ( UniValue : : VNUM ) } ,
} ,
true , true ) ;
2017-03-07 18:17:32 +00:00
if ( options . exists ( " minimumAmount " ) )
nMinimumAmount = AmountFromValue ( options [ " minimumAmount " ] ) ;
if ( options . exists ( " maximumAmount " ) )
nMaximumAmount = AmountFromValue ( options [ " maximumAmount " ] ) ;
if ( options . exists ( " minimumSumAmount " ) )
nMinimumSumAmount = AmountFromValue ( options [ " minimumSumAmount " ] ) ;
if ( options . exists ( " maximumCount " ) )
nMaximumCount = options [ " maximumCount " ] . get_int64 ( ) ;
}
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2015-05-10 14:48:35 +02:00
UniValue results ( UniValue : : VARR ) ;
2017-01-27 10:33:45 +09:00
std : : vector < COutput > vecOutputs ;
2018-05-03 11:31:21 +01:00
{
2018-09-11 15:53:36 +09:00
CCoinControl cctl ;
cctl . m_avoid_address_reuse = false ;
2019-05-09 18:34:38 -07:00
cctl . m_min_depth = nMinDepth ;
cctl . m_max_depth = nMaxDepth ;
2021-05-11 09:58:13 +02:00
cctl . m_include_unsafe_inputs = include_unsafe ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2021-02-12 18:01:22 -05:00
AvailableCoins ( * pwallet , vecOutputs , & cctl , nMinimumAmount , nMaximumAmount , nMinimumSumAmount , nMaximumCount ) ;
2018-05-03 11:31:21 +01:00
}
LOCK ( pwallet - > cs_wallet ) ;
2015-04-13 15:04:08 +02:00
2019-06-19 11:55:41 +09:00
const bool avoid_reuse = pwallet - > IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ;
2017-06-02 03:18:57 +02:00
for ( const COutput & out : vecOutputs ) {
2016-03-08 10:37:18 +00:00
CTxDestination address ;
2016-11-11 16:54:51 -08:00
const CScript & scriptPubKey = out . tx - > tx - > vout [ out . i ] . scriptPubKey ;
2016-03-08 10:37:18 +00:00
bool fValidAddress = ExtractDestination ( scriptPubKey , address ) ;
2020-02-21 21:15:13 +00:00
bool reused = avoid_reuse & & pwallet - > IsSpentKey ( out . tx - > GetHash ( ) , out . i ) ;
2015-04-13 15:04:08 +02:00
2017-08-22 18:02:33 -07:00
if ( destinations . size ( ) & & ( ! fValidAddress | | ! destinations . count ( address ) ) )
2016-03-08 10:37:18 +00:00
continue ;
2015-04-13 15:04:08 +02:00
2015-05-10 14:48:35 +02:00
UniValue entry ( UniValue : : VOBJ ) ;
2017-09-22 20:04:07 +02:00
entry . pushKV ( " txid " , out . tx - > GetHash ( ) . GetHex ( ) ) ;
entry . pushKV ( " vout " , out . i ) ;
2016-03-08 10:37:18 +00:00
if ( fValidAddress ) {
2017-09-22 20:04:07 +02:00
entry . pushKV ( " address " , EncodeDestination ( address ) ) ;
2016-03-08 10:37:18 +00:00
2020-02-22 04:16:36 +00:00
const auto * address_book_entry = pwallet - > FindAddressBookEntry ( address ) ;
if ( address_book_entry ) {
2020-04-03 03:02:16 +00:00
entry . pushKV ( " label " , address_book_entry - > GetLabel ( ) ) ;
2016-10-25 08:04:23 +00:00
}
2016-03-08 10:37:18 +00:00
2020-02-10 21:27:59 -05:00
std : : unique_ptr < SigningProvider > provider = pwallet - > GetSolvingProvider ( scriptPubKey ) ;
2019-10-07 14:11:34 -04:00
if ( provider ) {
if ( scriptPubKey . IsPayToScriptHash ( ) ) {
2021-01-04 11:20:02 +01:00
const CScriptID & hash = CScriptID ( std : : get < ScriptHash > ( address ) ) ;
2019-10-07 14:11:34 -04:00
CScript redeemScript ;
if ( provider - > GetCScript ( hash , redeemScript ) ) {
2020-06-24 15:48:26 +02:00
entry . pushKV ( " redeemScript " , HexStr ( redeemScript ) ) ;
2019-10-07 14:11:34 -04:00
// Now check if the redeemScript is actually a P2WSH script
CTxDestination witness_destination ;
if ( redeemScript . IsPayToWitnessScriptHash ( ) ) {
bool extracted = ExtractDestination ( redeemScript , witness_destination ) ;
CHECK_NONFATAL ( extracted ) ;
// Also return the witness script
2021-01-04 11:20:02 +01:00
const WitnessV0ScriptHash & whash = std : : get < WitnessV0ScriptHash > ( witness_destination ) ;
2019-10-07 14:11:34 -04:00
CScriptID id ;
CRIPEMD160 ( ) . Write ( whash . begin ( ) , whash . size ( ) ) . Finalize ( id . begin ( ) ) ;
CScript witnessScript ;
if ( provider - > GetCScript ( id , witnessScript ) ) {
2020-06-24 15:48:26 +02:00
entry . pushKV ( " witnessScript " , HexStr ( witnessScript ) ) ;
2019-10-07 14:11:34 -04:00
}
2018-10-15 16:57:18 +13:00
}
}
2019-10-07 14:11:34 -04:00
} else if ( scriptPubKey . IsPayToWitnessScriptHash ( ) ) {
2021-01-04 11:20:02 +01:00
const WitnessV0ScriptHash & whash = std : : get < WitnessV0ScriptHash > ( address ) ;
2019-10-07 14:11:34 -04:00
CScriptID id ;
CRIPEMD160 ( ) . Write ( whash . begin ( ) , whash . size ( ) ) . Finalize ( id . begin ( ) ) ;
CScript witnessScript ;
if ( provider - > GetCScript ( id , witnessScript ) ) {
2020-06-24 15:48:26 +02:00
entry . pushKV ( " witnessScript " , HexStr ( witnessScript ) ) ;
2019-10-07 14:11:34 -04:00
}
2016-10-25 08:04:23 +00:00
}
2015-04-12 17:56:32 +02:00
}
}
2016-03-08 10:37:18 +00:00
2020-06-24 15:48:26 +02:00
entry . pushKV ( " scriptPubKey " , HexStr ( scriptPubKey ) ) ;
2017-09-22 20:04:07 +02:00
entry . pushKV ( " amount " , ValueFromAmount ( out . tx - > tx - > vout [ out . i ] . nValue ) ) ;
entry . pushKV ( " confirmations " , out . nDepth ) ;
2018-03-12 17:59:50 +00:00
if ( ! out . nDepth ) {
size_t ancestor_count , descendant_count , ancestor_size ;
CAmount ancestor_fees ;
pwallet - > chain ( ) . getTransactionAncestry ( out . tx - > GetHash ( ) , ancestor_count , descendant_count , & ancestor_size , & ancestor_fees ) ;
if ( ancestor_count ) {
entry . pushKV ( " ancestorcount " , uint64_t ( ancestor_count ) ) ;
entry . pushKV ( " ancestorsize " , uint64_t ( ancestor_size ) ) ;
entry . pushKV ( " ancestorfees " , uint64_t ( ancestor_fees ) ) ;
}
}
2017-09-22 20:04:07 +02:00
entry . pushKV ( " spendable " , out . fSpendable ) ;
entry . pushKV ( " solvable " , out . fSolvable ) ;
2018-10-13 13:32:18 -07:00
if ( out . fSolvable ) {
2020-02-10 21:27:59 -05:00
std : : unique_ptr < SigningProvider > provider = pwallet - > GetSolvingProvider ( scriptPubKey ) ;
2019-10-07 14:11:34 -04:00
if ( provider ) {
auto descriptor = InferDescriptor ( scriptPubKey , * provider ) ;
entry . pushKV ( " desc " , descriptor - > ToString ( ) ) ;
}
2018-10-13 13:32:18 -07:00
}
2018-09-11 15:53:36 +09:00
if ( avoid_reuse ) entry . pushKV ( " reused " , reused ) ;
2017-09-22 20:04:07 +02:00
entry . pushKV ( " safe " , out . fSafe ) ;
2015-04-12 17:56:32 +02:00
results . push_back ( entry ) ;
}
2015-04-13 15:04:08 +02:00
2015-04-12 17:56:32 +02:00
return results ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2015-06-06 09:41:12 +02:00
}
2015-04-24 18:27:30 -07:00
2021-10-04 14:25:23 +02:00
// Only includes key documentation where the key is snake_case in all RPC methods. MixedCase keys can be added later.
static std : : vector < RPCArg > FundTxDoc ( )
{
return {
{ " conf_target " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " wallet -txconfirmtarget " } , " Confirmation target in blocks " } ,
{ " estimate_mode " , RPCArg : : Type : : STR , RPCArg : : Default { " unset " } , std : : string ( ) + " The fee estimate mode, must be one of (case insensitive): \n "
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-12-01 19:24:38 +08:00
{ " replaceable " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " wallet default " } , " Marks this transaction as BIP125-replaceable. \n "
2021-10-04 14:25:23 +02:00
" Allows this transaction to be replaced by a transaction with higher fees " } ,
{ " solving_data " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Keys and scripts needed for producing a final transaction with a dummy signature. \n "
" Used for fee estimation during coin selection. " ,
{
{ " pubkeys " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " Public keys involved in this transaction. " ,
{
{ " pubkey " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " A public key " } ,
} } ,
{ " scripts " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " Scripts involved in this transaction. " ,
{
{ " script " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " A script " } ,
} } ,
{ " descriptors " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " Descriptors that provide solving data for this transaction. " ,
{
{ " descriptor " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " A descriptor " } ,
} } ,
} } ,
} ;
}
2021-03-04 23:20:13 +08:00
void FundTransaction ( CWallet & wallet , CMutableTransaction & tx , CAmount & fee_out , int & change_position , const UniValue & options , CCoinControl & coinControl , bool override_min_fee )
2015-04-24 18:27:30 -07:00
{
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
2021-03-04 23:20:13 +08:00
wallet . BlockUntilSyncedToCurrentChain ( ) ;
2017-09-12 13:05:28 -04:00
2018-06-27 17:02:07 -07:00
change_position = - 1 ;
2016-04-06 15:56:14 +01:00
bool lockUnspents = false ;
2016-12-13 13:36:23 -08:00
UniValue subtractFeeFromOutputs ;
2017-01-27 10:33:45 +09:00
std : : set < int > setSubtractFeeFromOutputs ;
2016-03-30 02:04:22 +01:00
2018-06-27 17:02:07 -07:00
if ( ! options . isNull ( ) ) {
if ( options . type ( ) = = UniValue : : VBOOL ) {
2016-03-30 02:04:22 +01:00
// backward compatibility bool only fallback
2018-06-27 17:02:07 -07:00
coinControl . fAllowWatchOnly = options . get_bool ( ) ;
2016-03-30 02:04:22 +01:00
}
else {
2018-06-27 17:02:07 -07:00
RPCTypeCheckArgument ( options , UniValue : : VOBJ ) ;
2016-06-06 17:50:50 +02:00
RPCTypeCheckObj ( options ,
{
2019-07-12 13:30:10 +01:00
{ " add_inputs " , UniValueType ( UniValue : : VBOOL ) } ,
2021-03-10 15:37:18 +01:00
{ " include_unsafe " , UniValueType ( UniValue : : VBOOL ) } ,
2020-08-07 17:36:36 +02:00
{ " add_to_wallet " , UniValueType ( UniValue : : VBOOL ) } ,
2016-06-06 17:50:50 +02:00
{ " changeAddress " , UniValueType ( UniValue : : VSTR ) } ,
2020-03-02 14:01:27 +01:00
{ " change_address " , UniValueType ( UniValue : : VSTR ) } ,
2016-06-06 17:50:50 +02:00
{ " changePosition " , UniValueType ( UniValue : : VNUM ) } ,
2020-03-02 14:01:27 +01:00
{ " change_position " , UniValueType ( UniValue : : VNUM ) } ,
2018-01-16 00:20:17 +00:00
{ " change_type " , UniValueType ( UniValue : : VSTR ) } ,
2016-06-06 17:50:50 +02:00
{ " includeWatching " , UniValueType ( UniValue : : VBOOL ) } ,
2020-03-02 14:01:27 +01:00
{ " include_watching " , UniValueType ( UniValue : : VBOOL ) } ,
2020-08-07 17:36:36 +02:00
{ " inputs " , UniValueType ( UniValue : : VARR ) } ,
2016-06-06 17:50:50 +02:00
{ " lockUnspents " , UniValueType ( UniValue : : VBOOL ) } ,
2020-03-02 14:01:27 +01:00
{ " lock_unspents " , UniValueType ( UniValue : : VBOOL ) } ,
2020-08-07 17:36:36 +02:00
{ " locktime " , UniValueType ( UniValue : : VNUM ) } ,
2020-11-04 13:13:17 +01:00
{ " fee_rate " , UniValueType ( ) } , // will be checked by AmountFromValue() in SetFeeEstimateMode()
{ " feeRate " , UniValueType ( ) } , // will be checked by AmountFromValue() below
2020-08-07 17:36:36 +02:00
{ " psbt " , UniValueType ( UniValue : : VBOOL ) } ,
2019-10-18 19:43:01 -04:00
{ " solving_data " , UniValueType ( UniValue : : VOBJ ) } ,
2016-12-13 13:36:23 -08:00
{ " subtractFeeFromOutputs " , UniValueType ( UniValue : : VARR ) } ,
2020-03-02 14:01:27 +01:00
{ " subtract_fee_from_outputs " , UniValueType ( UniValue : : VARR ) } ,
2017-06-28 17:53:06 -04:00
{ " replaceable " , UniValueType ( UniValue : : VBOOL ) } ,
2017-06-14 15:15:40 -04:00
{ " conf_target " , UniValueType ( UniValue : : VNUM ) } ,
{ " estimate_mode " , UniValueType ( UniValue : : VSTR ) } ,
2016-06-06 17:50:50 +02:00
} ,
true , true ) ;
2016-03-30 02:04:22 +01:00
2019-07-12 13:30:10 +01:00
if ( options . exists ( " add_inputs " ) ) {
coinControl . m_add_inputs = options [ " add_inputs " ] . get_bool ( ) ;
}
2020-03-02 14:01:27 +01:00
if ( options . exists ( " changeAddress " ) | | options . exists ( " change_address " ) ) {
const std : : string change_address_str = ( options . exists ( " change_address " ) ? options [ " change_address " ] : options [ " changeAddress " ] ) . get_str ( ) ;
CTxDestination dest = DecodeDestination ( change_address_str ) ;
2016-03-30 02:04:22 +01:00
2017-08-22 18:02:33 -07:00
if ( ! IsValidDestination ( dest ) ) {
2020-03-02 14:01:27 +01:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Change address must be a valid bitcoin address " ) ;
2017-08-22 18:02:33 -07:00
}
2016-03-30 02:04:22 +01:00
2017-08-22 18:02:33 -07:00
coinControl . destChange = dest ;
2016-03-30 02:04:22 +01:00
}
2020-03-02 14:01:27 +01:00
if ( options . exists ( " changePosition " ) | | options . exists ( " change_position " ) ) {
change_position = ( options . exists ( " change_position " ) ? options [ " change_position " ] : options [ " changePosition " ] ) . get_int ( ) ;
}
2016-03-30 02:04:22 +01:00
2018-01-16 00:20:17 +00:00
if ( options . exists ( " change_type " ) ) {
2020-03-02 14:01:27 +01:00
if ( options . exists ( " changeAddress " ) | | options . exists ( " change_address " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both change address and address type options " ) ;
2018-01-16 00:20:17 +00:00
}
2021-08-04 13:38:36 +08:00
if ( std : : optional < OutputType > parsed = ParseOutputType ( options [ " change_type " ] . get_str ( ) ) ) {
coinControl . m_change_type . emplace ( parsed . value ( ) ) ;
} else {
2018-01-16 00:20:17 +00:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " Unknown change type '%s' " , options [ " change_type " ] . get_str ( ) ) ) ;
}
}
2020-03-02 14:01:27 +01:00
const UniValue include_watching_option = options . exists ( " include_watching " ) ? options [ " include_watching " ] : options [ " includeWatching " ] ;
2021-03-04 23:20:13 +08:00
coinControl . fAllowWatchOnly = ParseIncludeWatchonly ( include_watching_option , wallet ) ;
2016-04-06 15:56:14 +01:00
2020-03-02 14:01:27 +01:00
if ( options . exists ( " lockUnspents " ) | | options . exists ( " lock_unspents " ) ) {
lockUnspents = ( options . exists ( " lock_unspents " ) ? options [ " lock_unspents " ] : options [ " lockUnspents " ] ) . get_bool ( ) ;
}
2016-04-28 22:04:07 +02:00
2021-03-10 15:37:18 +01:00
if ( options . exists ( " include_unsafe " ) ) {
coinControl . m_include_unsafe_inputs = options [ " include_unsafe " ] . get_bool ( ) ;
}
2020-11-04 13:13:17 +01:00
if ( options . exists ( " feeRate " ) ) {
if ( options . exists ( " fee_rate " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both fee_rate ( " + CURRENCY_ATOM + " /vB) and feeRate ( " + CURRENCY_UNIT + " / kvB ) " ) ;
}
2020-03-04 11:26:42 +09:00
if ( options . exists ( " conf_target " ) ) {
2020-11-04 13:13:17 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both conf_target and feeRate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate. " ) ;
2020-03-04 11:26:42 +09:00
}
if ( options . exists ( " estimate_mode " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both estimate_mode and feeRate " ) ;
}
2020-11-19 18:38:00 +01:00
coinControl . m_feerate = CFeeRate ( AmountFromValue ( options [ " feeRate " ] ) ) ;
2017-02-02 22:33:24 +00:00
coinControl . fOverrideFeeRate = true ;
2016-05-06 11:01:50 +02:00
}
2016-12-13 13:36:23 -08:00
2020-03-02 14:01:27 +01:00
if ( options . exists ( " subtractFeeFromOutputs " ) | | options . exists ( " subtract_fee_from_outputs " ) )
subtractFeeFromOutputs = ( options . exists ( " subtract_fee_from_outputs " ) ? options [ " subtract_fee_from_outputs " ] : options [ " subtractFeeFromOutputs " ] ) . get_array ( ) ;
2017-02-02 22:36:50 +00:00
2017-06-28 17:53:06 -04:00
if ( options . exists ( " replaceable " ) ) {
2018-04-07 12:12:46 -04:00
coinControl . m_signal_bip125_rbf = options [ " replaceable " ] . get_bool ( ) ;
2017-02-02 22:36:50 +00:00
}
2021-03-04 23:20:13 +08:00
SetFeeEstimateMode ( wallet , coinControl , options [ " conf_target " ] , options [ " estimate_mode " ] , options [ " fee_rate " ] , override_min_fee ) ;
2016-03-30 02:04:22 +01:00
}
2019-07-13 08:34:49 -07:00
} else {
// if options is null and not a bool
2021-03-04 23:20:13 +08:00
coinControl . fAllowWatchOnly = ParseIncludeWatchonly ( NullUniValue , wallet ) ;
2016-03-30 02:04:22 +01:00
}
2015-04-24 18:27:30 -07:00
2019-10-18 19:43:01 -04:00
if ( options . exists ( " solving_data " ) ) {
2021-10-05 21:17:22 +08:00
const UniValue solving_data = options [ " solving_data " ] . get_obj ( ) ;
2019-10-18 19:43:01 -04:00
if ( solving_data . exists ( " pubkeys " ) ) {
for ( const UniValue & pk_univ : solving_data [ " pubkeys " ] . get_array ( ) . getValues ( ) ) {
const std : : string & pk_str = pk_univ . get_str ( ) ;
if ( ! IsHex ( pk_str ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " '%s' is not hex " , pk_str ) ) ;
}
const std : : vector < unsigned char > data ( ParseHex ( pk_str ) ) ;
2021-10-05 21:17:22 +08:00
const CPubKey pubkey ( data . begin ( ) , data . end ( ) ) ;
2019-10-18 19:43:01 -04:00
if ( ! pubkey . IsFullyValid ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " '%s' is not a valid public key " , pk_str ) ) ;
}
coinControl . m_external_provider . pubkeys . emplace ( pubkey . GetID ( ) , pubkey ) ;
// Add witness script for pubkeys
const CScript wit_script = GetScriptForDestination ( WitnessV0KeyHash ( pubkey ) ) ;
coinControl . m_external_provider . scripts . emplace ( CScriptID ( wit_script ) , wit_script ) ;
}
}
if ( solving_data . exists ( " scripts " ) ) {
for ( const UniValue & script_univ : solving_data [ " scripts " ] . get_array ( ) . getValues ( ) ) {
const std : : string & script_str = script_univ . get_str ( ) ;
if ( ! IsHex ( script_str ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " '%s' is not hex " , script_str ) ) ;
}
std : : vector < unsigned char > script_data ( ParseHex ( script_str ) ) ;
const CScript script ( script_data . begin ( ) , script_data . end ( ) ) ;
coinControl . m_external_provider . scripts . emplace ( CScriptID ( script ) , script ) ;
}
}
if ( solving_data . exists ( " descriptors " ) ) {
for ( const UniValue & desc_univ : solving_data [ " descriptors " ] . get_array ( ) . getValues ( ) ) {
const std : : string & desc_str = desc_univ . get_str ( ) ;
FlatSigningProvider desc_out ;
std : : string error ;
std : : vector < CScript > scripts_temp ;
std : : unique_ptr < Descriptor > desc = Parse ( desc_str , desc_out , error , true ) ;
if ( ! desc ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Unable to parse descriptor '%s': %s " , desc_str , error ) ) ;
}
desc - > Expand ( 0 , desc_out , scripts_temp , desc_out ) ;
coinControl . m_external_provider = Merge ( coinControl . m_external_provider , desc_out ) ;
}
}
}
2016-11-30 14:50:20 -08:00
if ( tx . vout . size ( ) = = 0 )
2015-11-13 15:52:07 -05:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " TX must have at least one output " ) ;
2018-06-27 17:02:07 -07:00
if ( change_position ! = - 1 & & ( change_position < 0 | | ( unsigned int ) change_position > tx . vout . size ( ) ) )
2016-03-30 02:04:22 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " changePosition out of bounds " ) ;
2015-04-23 21:42:49 -07:00
2016-12-13 13:36:23 -08:00
for ( unsigned int idx = 0 ; idx < subtractFeeFromOutputs . size ( ) ; idx + + ) {
int pos = subtractFeeFromOutputs [ idx ] . get_int ( ) ;
if ( setSubtractFeeFromOutputs . count ( pos ) )
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Invalid parameter, duplicated position: %d " , pos ) ) ;
if ( pos < 0 )
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Invalid parameter, negative position: %d " , pos ) ) ;
if ( pos > = int ( tx . vout . size ( ) ) )
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Invalid parameter, position too large: %d " , pos ) ) ;
setSubtractFeeFromOutputs . insert ( pos ) ;
}
2019-10-18 19:43:01 -04:00
// Fetch specified UTXOs from the UTXO set to get the scriptPubKeys and values of the outputs being selected
// and to match with the given solving_data. Only used for non-wallet outputs.
std : : map < COutPoint , Coin > coins ;
for ( const CTxIn & txin : tx . vin ) {
coins [ txin . prevout ] ; // Create empty map entry keyed by prevout.
}
wallet . chain ( ) . findCoins ( coins ) ;
for ( const auto & coin : coins ) {
if ( ! coin . second . out . IsNull ( ) ) {
2021-10-05 21:17:22 +08:00
coinControl . SelectExternal ( coin . first , coin . second . out ) ;
2019-10-18 19:43:01 -04:00
}
}
2020-04-18 15:18:17 -04:00
bilingual_str error ;
2016-03-30 02:04:22 +01:00
2021-02-12 18:01:22 -05:00
if ( ! FundTransaction ( wallet , tx , fee_out , change_position , error , lockUnspents , setSubtractFeeFromOutputs , coinControl ) ) {
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_WALLET_ERROR , error . original ) ;
2016-10-25 08:04:23 +00:00
}
2018-06-27 17:02:07 -07:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan fundrawtransaction ( )
2018-06-27 17:02:07 -07:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " fundrawtransaction " ,
2020-02-20 16:19:59 +01:00
" \n If the transaction has no inputs, they will be automatically selected to meet its out value. \n "
" It will add at most one change output to the outputs. \n "
2018-10-20 08:19:44 -04:00
" No existing outputs will be modified unless \" subtractFeeFromOutputs \" is specified. \n "
" Note that inputs which were signed may need to be resigned after completion since in/outputs have been added. \n "
2019-01-24 16:25:28 -05:00
" The inputs added will not be signed, use signrawtransactionwithkey \n "
2019-10-18 19:43:01 -04:00
" or signrawtransactionwithwallet for that. \n "
" All existing inputs must either have their previous output transaction be in the wallet \n "
" or be in the UTXO set. Solving data must be provided for non-wallet inputs. \n "
2018-10-20 08:19:44 -04:00
" Note that all inputs selected must be of standard form and P2SH scripts must be \n "
" in the wallet using importaddress or addmultisigaddress (to calculate fees). \n "
" You can see whether this is the case by checking the \" solvable \" field in the listunspent output. \n "
" Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only \n " ,
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The hex string of the raw transaction " } ,
{ " options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " for backward compatibility: passing in a true instead of an object will result in { \" includeWatching \" :true} " ,
2021-10-04 14:25:23 +02:00
Cat < std : : vector < RPCArg > > (
2018-10-20 08:19:44 -04:00
{
2021-04-14 15:01:00 +01:00
{ " add_inputs " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " For a transaction with existing inputs, automatically include more if they are not enough. " } ,
2021-03-10 15:37:18 +01:00
{ " include_unsafe " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions). \n "
" Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears. \n "
" If that happens, you will need to fund the transaction with different inputs and republish it. " } ,
2021-04-14 15:01:00 +01:00
{ " changeAddress " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " pool address " } , " The bitcoin address to receive the change " } ,
{ " changePosition " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " random " } , " The index of the change output " } ,
{ " change_type " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " set by -changetype " } , " The output type to use. Only valid if changeAddress is not specified. Options are \" legacy \" , \" p2sh-segwit \" , and \" bech32 \" . " } ,
{ " includeWatching " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " true for watch-only wallets, otherwise false " } , " Also select inputs which are watch only. \n "
2019-07-16 16:47:35 +02:00
" Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported, \n "
" e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field. " } ,
2021-04-14 15:01:00 +01:00
{ " lockUnspents " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Lock selected unspent outputs " } ,
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_ATOM + " /vB. " } ,
{ " feeRate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_UNIT + " /kvB. " } ,
{ " subtractFeeFromOutputs " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " The integers. \n "
2020-09-30 09:20:37 +02:00
" The fee will be equally deducted from the amount of each specified output. \n "
" Those recipients will receive less bitcoins than you enter in their corresponding amount field. \n "
" If no outputs are specified here, the sender pays the fee. " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " vout_index " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED , " The zero-based output index, before a change output is added. " } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
} ,
2021-10-04 14:25:23 +02:00
FundTxDoc ( ) ) ,
2018-11-23 11:21:38 -05:00
" options " } ,
2021-04-14 15:01:00 +01:00
{ " iswitness " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " depends on heuristic tests " } , " Whether the transaction hex is a serialized witness transaction. \n "
2019-04-26 09:04:08 -04:00
" If iswitness is not present, heuristic tests will be used in decoding. \n "
" If true, only witness deserialization will be tried. \n "
" If false, only non-witness deserialization will be tried. \n "
" This boolean should reflect whether the transaction has inputs \n "
" (e.g. fully valid, or on-chain transactions), if known by the caller. "
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " hex " , " The resulting raw transaction (hex-encoded string) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " fee " , " Fee in " + CURRENCY_UNIT + " the resulting transaction pays " } ,
{ RPCResult : : Type : : NUM , " changepos " , " The position of the added change output, or -1 " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2018-06-27 17:02:07 -07:00
" \n Create a transaction with no inputs \n "
+ HelpExampleCli ( " createrawtransaction " , " \" [] \" \" { \\ \" myaddress \\ \" :0.01} \" " ) +
" \n Add sufficient unsigned inputs to meet the output value \n "
+ HelpExampleCli ( " fundrawtransaction " , " \" rawtransactionhex \" " ) +
" \n Sign the transaction \n "
2019-01-24 16:25:28 -05:00
+ HelpExampleCli ( " signrawtransactionwithwallet " , " \" fundedtransactionhex \" " ) +
2018-06-27 17:02:07 -07:00
" \n Send the transaction \n "
+ HelpExampleCli ( " sendrawtransaction " , " \" signedtransactionhex \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2018-06-27 17:02:07 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValueType ( ) , UniValue : : VBOOL } ) ;
// parse hex string from parameter
CMutableTransaction tx ;
bool try_witness = request . params [ 2 ] . isNull ( ) ? true : request . params [ 2 ] . get_bool ( ) ;
bool try_no_witness = request . params [ 2 ] . isNull ( ) ? true : ! request . params [ 2 ] . get_bool ( ) ;
if ( ! DecodeHexTx ( tx , request . params [ 0 ] . get_str ( ) , try_no_witness , try_witness ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
}
CAmount fee ;
int change_position ;
2019-07-12 13:30:10 +01:00
CCoinControl coin_control ;
2020-07-11 13:56:13 +02:00
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
2020-02-20 16:19:59 +01:00
coin_control . m_add_inputs = true ;
2021-03-04 23:20:13 +08:00
FundTransaction ( * pwallet , tx , fee , change_position , request . params [ 1 ] , coin_control , /* override_min_fee */ true ) ;
2015-04-24 18:27:30 -07:00
UniValue result ( UniValue : : VOBJ ) ;
2018-12-09 22:03:07 -08:00
result . pushKV ( " hex " , EncodeHexTx ( CTransaction ( tx ) ) ) ;
2018-06-27 17:02:07 -07:00
result . pushKV ( " fee " , ValueFromAmount ( fee ) ) ;
result . pushKV ( " changepos " , change_position ) ;
2015-04-24 18:27:30 -07:00
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2015-04-24 18:27:30 -07:00
}
2016-01-07 08:33:49 +01:00
2020-09-22 18:13:52 +02:00
RPCHelpMan signrawtransactionwithwallet ( )
2017-06-12 12:23:02 -07:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " signrawtransactionwithwallet " ,
2018-10-20 08:19:44 -04:00
" \n Sign inputs for raw transaction (serialized, hex-encoded). \n "
" The second optional argument (may be null) is an array of previous transaction outputs that \n "
2018-11-23 11:21:38 -05:00
" this transaction depends on but may not yet be in the block chain. " +
2020-04-01 10:31:43 +08:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The transaction hex string " } ,
2020-03-05 10:36:27 +00:00
{ " prevtxs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The previous dependent transaction outputs " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
{ " scriptPubKey " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " script key " } ,
2018-10-15 16:57:18 +13:00
{ " redeemScript " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " (required for P2SH) redeem script " } ,
{ " witnessScript " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " (required for P2WSH or P2SH-P2WSH) witness script " } ,
2019-03-26 17:39:39 +01:00
{ " amount " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : OMITTED , " (required for Segwit inputs) the amount spent " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2021-03-04 14:27:20 -08:00
{ " sighashtype " , RPCArg : : Type : : STR , RPCArg : : Default { " DEFAULT " } , " The signature hash type. Must be one of \n "
" \" DEFAULT \" \n "
2017-06-12 12:23:02 -07:00
" \" ALL \" \n "
" \" NONE \" \n "
" \" SINGLE \" \n "
" \" ALL|ANYONECANPAY \" \n "
" \" NONE|ANYONECANPAY \" \n "
2018-11-23 11:21:38 -05:00
" \" SINGLE|ANYONECANPAY \" " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex-encoded raw transaction with signature(s) " } ,
{ RPCResult : : Type : : BOOL , " complete " , " If the transaction has a complete set of signatures " } ,
2020-08-02 18:35:18 +02:00
{ RPCResult : : Type : : ARR , " errors " , /* optional */ true , " Script verification errors (if there are any) " ,
2020-01-10 00:00:57 +07:00
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The hash of the referenced, previous transaction " } ,
{ RPCResult : : Type : : NUM , " vout " , " The index of the output to spent and used as input " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : ARR , " witness " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " witness " , " " } ,
} } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : STR_HEX , " scriptSig " , " The hex-encoded signature script " } ,
{ RPCResult : : Type : : NUM , " sequence " , " Script sequence number " } ,
{ RPCResult : : Type : : STR , " error " , " Verification or signing error related to the input " } ,
} } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " signrawtransactionwithwallet " , " \" myhex \" " )
2017-06-12 12:23:02 -07:00
+ HelpExampleRpc ( " signrawtransactionwithwallet " , " \" myhex \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-06-12 12:23:02 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VARR , UniValue : : VSTR } , true ) ;
CMutableTransaction mtx ;
2020-08-30 10:28:42 +02:00
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_str ( ) ) ) {
2020-10-13 15:21:03 +02:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed. Make sure the tx has at least one input. " ) ;
2017-06-12 12:23:02 -07:00
}
// Sign the transaction
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2021-03-04 23:20:13 +08:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2018-09-24 16:10:23 +01:00
2019-04-10 13:38:41 +00:00
// Fetch previous transactions (inputs):
std : : map < COutPoint , Coin > coins ;
for ( const CTxIn & txin : mtx . vin ) {
coins [ txin . prevout ] ; // Create empty map entry keyed by prevout.
}
pwallet - > chain ( ) . findCoins ( coins ) ;
2019-07-05 17:39:31 -04:00
// Parse the prevtxs array
ParsePrevouts ( request . params [ 1 ] , nullptr , coins ) ;
2020-02-10 19:50:52 -05:00
int nHashType = ParseSighashString ( request . params [ 2 ] ) ;
2019-10-07 14:11:34 -04:00
2020-02-10 19:50:52 -05:00
// Script verification errors
2021-06-23 17:28:54 -04:00
std : : map < int , bilingual_str > input_errors ;
2020-02-10 19:50:52 -05:00
bool complete = pwallet - > SignTransaction ( mtx , coins , nHashType , input_errors ) ;
2019-11-18 14:56:52 -05:00
UniValue result ( UniValue : : VOBJ ) ;
2020-02-10 19:50:52 -05:00
SignTransactionResultToJSON ( mtx , complete , coins , input_errors , result ) ;
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2017-06-12 12:23:02 -07:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan bumpfee_helper ( std : : string method_name )
2016-12-09 13:45:27 -05:00
{
2021-03-29 11:43:21 +02:00
const bool want_psbt = method_name = = " psbtbumpfee " ;
2020-11-19 17:31:07 +01:00
const std : : string incremental_fee { CFeeRate ( DEFAULT_INCREMENTAL_RELAY_FEE ) . ToString ( FeeEstimateMode : : SAT_VB ) } ;
2020-04-13 14:48:03 -04:00
2020-09-22 18:13:52 +02:00
return RPCHelpMan { method_name ,
2020-06-16 17:17:40 -04:00
" \n Bumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B. \n "
+ std : : string ( want_psbt ? " Returns a PSBT instead of creating and signing a new transaction. \n " : " " ) +
" An opt-in RBF transaction with the given txid must be in the wallet. \n "
2020-11-04 13:13:17 +01:00
" The command will pay the additional fee by reducing change outputs or adding inputs when necessary. \n "
" It may add a new change output if one does not already exist. \n "
2020-06-16 17:17:40 -04:00
" All inputs in the original transaction will be included in the replacement transaction. \n "
" The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs. \n "
2020-06-27 05:53:17 +02:00
" By default, the new fee will be calculated automatically using the estimatesmartfee RPC. \n "
2020-06-16 17:17:40 -04:00
" The user can specify a confirmation target for estimatesmartfee. \n "
2020-11-09 12:14:17 +01:00
" Alternatively, the user can specify a fee rate in " + CURRENCY_ATOM + " /vB for the new transaction. \n "
2020-06-16 17:17:40 -04:00
" At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee \n "
2020-11-09 12:14:17 +01:00
" returned by getnetworkinfo) to enter the node's mempool. \n "
" * WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + " /kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + " /vB. * \n " ,
2020-06-16 17:17:40 -04:00
{
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The txid to be bumped " } ,
{ " options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " " ,
2018-10-20 08:19:44 -04:00
{
2021-04-14 15:01:00 +01:00
{ " conf_target " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " wallet -txconfirmtarget " } , " Confirmation target in blocks \n " } ,
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } ,
2020-11-04 13:13:17 +01:00
" \n Specify a fee rate in " + CURRENCY_ATOM + " /vB instead of relying on the built-in fee estimator. \n "
2020-11-19 17:31:07 +01:00
" Must be at least " + incremental_fee + " higher than the current transaction fee rate. \n "
2020-11-09 12:14:17 +01:00
" WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + " /kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + " /vB. \n " } ,
2021-04-14 15:01:00 +01:00
{ " replaceable " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Whether the new transaction should still be \n "
2020-09-30 09:20:37 +02:00
" marked bip-125 replaceable. If true, the sequence numbers in the transaction will \n "
" be left unchanged from the original. If false, any input sequence numbers in the \n "
" original transaction that were less than 0xfffffffe will be increased to 0xfffffffe \n "
" so the new transaction will not be explicitly bip-125 replaceable (though it may \n "
" still be replaceable in practice, for example if it has unconfirmed ancestors which \n "
2020-11-04 13:13:17 +01:00
" are replaceable). \n " } ,
2021-04-14 15:01:00 +01:00
{ " estimate_mode " , RPCArg : : Type : : STR , RPCArg : : Default { " unset " } , " The fee estimate mode, must be one of (case insensitive): \n "
2021-03-29 11:43:21 +02:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2018-12-21 12:29:36 -05:00
} ,
2020-06-16 17:17:40 -04:00
" options " } ,
} ,
RPCResult {
2021-05-21 21:57:49 +02:00
RPCResult : : Type : : OBJ , " " , " " , Cat (
want_psbt ?
std : : vector < RPCResult > { { RPCResult : : Type : : STR , " psbt " , " The base64-encoded unsigned PSBT of the new transaction. " } } :
std : : vector < RPCResult > { { RPCResult : : Type : : STR_HEX , " txid " , " The id of the new transaction. " } } ,
2020-06-16 17:17:40 -04:00
{
{ RPCResult : : Type : : STR_AMOUNT , " origfee " , " The fee of the replaced transaction. " } ,
{ RPCResult : : Type : : STR_AMOUNT , " fee " , " The fee of the new transaction. " } ,
{ RPCResult : : Type : : ARR , " errors " , " Errors encountered during processing (may be empty). " ,
{
{ RPCResult : : Type : : STR , " " , " " } ,
} } ,
} )
} ,
RPCExamples {
2021-05-21 21:57:49 +02:00
" \n Bump the fee, get the new transaction \' s " + std : : string ( want_psbt ? " psbt " : " txid " ) + " \n " +
2020-09-22 18:13:52 +02:00
HelpExampleCli ( method_name , " <txid> " )
2020-06-16 17:17:40 -04:00
} ,
2021-03-29 11:43:21 +02:00
[ want_psbt ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
2020-09-22 18:13:52 +02:00
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2020-04-13 14:48:03 -04:00
if ( pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) & & ! want_psbt ) {
2021-01-08 18:12:57 -05:00
throw JSONRPCError ( RPC_WALLET_ERROR , " bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead. " ) ;
2020-04-13 14:48:03 -04:00
}
2017-06-06 21:15:28 +02:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VOBJ } ) ;
2018-06-08 11:16:07 -07:00
uint256 hash ( ParseHashV ( request . params [ 0 ] , " txid " ) ) ;
2016-12-09 13:45:27 -05:00
2019-11-20 15:01:30 -05:00
CCoinControl coin_control ;
coin_control . fAllowWatchOnly = pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ;
2017-02-02 22:03:55 +01:00
// optional parameters
2018-04-07 12:12:46 -04:00
coin_control . m_signal_bip125_rbf = true ;
2019-11-20 15:01:30 -05:00
2017-07-10 11:44:39 -04:00
if ( ! request . params [ 1 ] . isNull ( ) ) {
2017-02-02 22:03:55 +01:00
UniValue options = request . params [ 1 ] ;
RPCTypeCheckObj ( options ,
{
{ " confTarget " , UniValueType ( UniValue : : VNUM ) } ,
2019-08-15 00:46:13 +09:00
{ " conf_target " , UniValueType ( UniValue : : VNUM ) } ,
2020-11-04 13:13:17 +01:00
{ " fee_rate " , UniValueType ( ) } , // will be checked by AmountFromValue() in SetFeeEstimateMode()
2017-02-02 22:03:55 +01:00
{ " replaceable " , UniValueType ( UniValue : : VBOOL ) } ,
2017-06-14 15:15:40 -04:00
{ " estimate_mode " , UniValueType ( UniValue : : VSTR ) } ,
2017-02-02 22:03:55 +01:00
} ,
true , true ) ;
2019-08-15 00:46:13 +09:00
if ( options . exists ( " confTarget " ) & & options . exists ( " conf_target " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " confTarget and conf_target options should not both be set. Use conf_target (confTarget is deprecated) . " ) ;
}
auto conf_target = options . exists ( " confTarget " ) ? options [ " confTarget " ] : options [ " conf_target " ] ;
2017-02-02 22:03:55 +01:00
if ( options . exists ( " replaceable " ) ) {
2018-04-07 12:12:46 -04:00
coin_control . m_signal_bip125_rbf = options [ " replaceable " ] . get_bool ( ) ;
2017-02-02 22:03:55 +01:00
}
2020-11-17 20:22:01 +01:00
SetFeeEstimateMode ( * pwallet , coin_control , conf_target , options [ " estimate_mode " ] , options [ " fee_rate " ] , /* override_min_fee */ false ) ;
2017-02-02 22:03:55 +01:00
}
2017-09-12 13:05:28 -04:00
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet - > BlockUntilSyncedToCurrentChain ( ) ;
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2021-03-04 23:20:13 +08:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2016-12-09 13:45:27 -05:00
2017-06-15 10:34:17 -04:00
2020-04-18 15:18:17 -04:00
std : : vector < bilingual_str > errors ;
2017-06-15 10:34:17 -04:00
CAmount old_fee ;
CAmount new_fee ;
CMutableTransaction mtx ;
2019-03-06 16:30:00 -05:00
feebumper : : Result res ;
2020-03-10 18:40:23 +01:00
// Targeting feerate bump.
res = feebumper : : CreateRateBumpTransaction ( * pwallet , hash , coin_control , errors , old_fee , new_fee , mtx ) ;
2017-06-15 10:34:17 -04:00
if ( res ! = feebumper : : Result : : OK ) {
2017-03-03 16:15:47 +01:00
switch ( res ) {
2017-09-20 16:19:30 -04:00
case feebumper : : Result : : INVALID_ADDRESS_OR_KEY :
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , errors [ 0 ] . original ) ;
2017-03-03 16:15:47 +01:00
break ;
2017-09-20 16:19:30 -04:00
case feebumper : : Result : : INVALID_REQUEST :
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_INVALID_REQUEST , errors [ 0 ] . original ) ;
2017-03-03 16:15:47 +01:00
break ;
2017-09-20 16:19:30 -04:00
case feebumper : : Result : : INVALID_PARAMETER :
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , errors [ 0 ] . original ) ;
2017-03-03 16:15:47 +01:00
break ;
2017-09-20 16:19:30 -04:00
case feebumper : : Result : : WALLET_ERROR :
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_WALLET_ERROR , errors [ 0 ] . original ) ;
2017-03-03 16:15:47 +01:00
break ;
default :
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_MISC_ERROR , errors [ 0 ] . original ) ;
2017-03-03 16:15:47 +01:00
break ;
2016-12-09 13:45:27 -05:00
}
}
UniValue result ( UniValue : : VOBJ ) ;
2019-11-20 15:18:34 -05:00
2021-05-22 09:30:26 +02:00
// For bumpfee, return the new transaction id.
// For psbtbumpfee, return the base64-encoded unsigned PSBT of the new transaction.
2020-04-13 14:48:03 -04:00
if ( ! want_psbt ) {
2019-11-20 15:18:34 -05:00
if ( ! feebumper : : SignTransaction ( * pwallet , mtx ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Can't sign transaction. " ) ;
}
uint256 txid ;
if ( feebumper : : CommitTransaction ( * pwallet , hash , std : : move ( mtx ) , errors , txid ) ! = feebumper : : Result : : OK ) {
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_WALLET_ERROR , errors [ 0 ] . original ) ;
2019-11-20 15:18:34 -05:00
}
result . pushKV ( " txid " , txid . GetHex ( ) ) ;
} else {
PartiallySignedTransaction psbtx ( mtx ) ;
bool complete = false ;
2021-03-04 14:27:20 -08:00
const TransactionError err = pwallet - > FillPSBT ( psbtx , complete , SIGHASH_DEFAULT , false /* sign */ , true /* bip32derivs */ ) ;
2019-11-20 15:18:34 -05:00
CHECK_NONFATAL ( err = = TransactionError : : OK ) ;
CHECK_NONFATAL ( ! complete ) ;
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
}
2017-09-22 20:04:07 +02:00
result . pushKV ( " origfee " , ValueFromAmount ( old_fee ) ) ;
result . pushKV ( " fee " , ValueFromAmount ( new_fee ) ) ;
2017-06-15 10:34:17 -04:00
UniValue result_errors ( UniValue : : VARR ) ;
2020-04-18 15:18:17 -04:00
for ( const bilingual_str & error : errors ) {
result_errors . push_back ( error . original ) ;
2017-06-15 10:34:17 -04:00
}
2017-09-22 20:04:07 +02:00
result . pushKV ( " errors " , result_errors ) ;
2016-12-09 13:45:27 -05:00
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2016-12-09 13:45:27 -05:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan bumpfee ( ) { return bumpfee_helper ( " bumpfee " ) ; }
static RPCHelpMan psbtbumpfee ( ) { return bumpfee_helper ( " psbtbumpfee " ) ; }
2020-04-13 14:48:03 -04:00
2020-08-07 17:36:36 +02:00
static RPCHelpMan send ( )
{
return RPCHelpMan { " send " ,
2020-09-17 15:06:39 +02:00
" \n EXPERIMENTAL warning: this call may be changed in future releases. \n "
2020-08-07 17:36:36 +02:00
" \n Send a transaction. \n " ,
{
2020-09-30 09:20:37 +02:00
{ " outputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The outputs (key-value pairs), where none of the keys are duplicated. \n "
2020-08-07 17:36:36 +02:00
" That is, each address can only appear once and there can only be one 'data' object. \n "
" For convenience, a dictionary, which holds the key-value pairs directly, is also accepted. " ,
{
2021-05-11 19:03:41 +09:00
{ " " , RPCArg : : Type : : OBJ_USER_KEYS , RPCArg : : Optional : : OMITTED , " " ,
2020-08-07 17:36:36 +02:00
{
{ " address " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + " " } ,
} ,
} ,
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
{
{ " data " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " A key-value pair. The key must be \" data \" , the value is hex-encoded data " } ,
} ,
} ,
} ,
} ,
2021-04-14 15:01:00 +01:00
{ " conf_target " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " wallet -txconfirmtarget " } , " Confirmation target in blocks " } ,
{ " estimate_mode " , RPCArg : : Type : : STR , RPCArg : : Default { " unset " } , std : : string ( ) + " The fee estimate mode, must be one of (case insensitive): \n "
2020-08-07 17:36:36 +02:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-04-14 15:01:00 +01:00
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_ATOM + " /vB. " } ,
2020-08-07 17:36:36 +02:00
{ " options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " " ,
2021-10-04 14:25:23 +02:00
Cat < std : : vector < RPCArg > > (
2020-08-07 17:36:36 +02:00
{
2021-04-14 15:01:00 +01:00
{ " add_inputs " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " If inputs are specified, automatically include more if they are not enough. " } ,
2021-03-10 15:37:18 +01:00
{ " include_unsafe " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions). \n "
" Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears. \n "
" If that happens, you will need to fund the transaction with different inputs and republish it. " } ,
2021-04-14 15:01:00 +01:00
{ " add_to_wallet " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " When false, returns a serialized transaction which will not be added to the wallet or broadcast " } ,
{ " change_address " , RPCArg : : Type : : STR_HEX , RPCArg : : DefaultHint { " pool address " } , " The bitcoin address to receive the change " } ,
{ " change_position " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " random " } , " The index of the change output " } ,
{ " change_type " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " set by -changetype " } , " The output type to use. Only valid if change_address is not specified. Options are \" legacy \" , \" p2sh-segwit \" , and \" bech32 \" . " } ,
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_ATOM + " /vB. " } ,
{ " include_watching " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " true for watch-only wallets, otherwise false " } , " Also select inputs which are watch only. \n "
2020-08-07 17:36:36 +02:00
" Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported, \n "
" e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field. " } ,
2021-04-14 15:01:00 +01:00
{ " inputs " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " Specify inputs instead of adding them automatically. A JSON array of JSON objects " ,
2020-08-07 17:36:36 +02:00
{
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
{ " sequence " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The sequence number " } ,
} ,
} ,
2021-04-14 15:01:00 +01:00
{ " locktime " , RPCArg : : Type : : NUM , RPCArg : : Default { 0 } , " Raw locktime. Non-0 value also locktime-activates inputs " } ,
{ " lock_unspents " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Lock selected unspent outputs " } ,
{ " psbt " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " automatic " } , " Always return a PSBT, implies add_to_wallet=false. " } ,
{ " subtract_fee_from_outputs " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " Outputs to subtract the fee from, specified as integer indices. \n "
2020-08-07 17:36:36 +02:00
" The fee will be equally deducted from the amount of each specified output. \n "
" Those recipients will receive less bitcoins than you enter in their corresponding amount field. \n "
" If no outputs are specified here, the sender pays the fee. " ,
{
{ " vout_index " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED , " The zero-based output index, before a change output is added. " } ,
} ,
} ,
} ,
2021-10-04 14:25:23 +02:00
FundTxDoc ( ) ) ,
2020-08-07 17:36:36 +02:00
" options " } ,
} ,
RPCResult {
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : BOOL , " complete " , " If the transaction has a complete set of signatures " } ,
2021-08-25 19:55:15 +02:00
{ RPCResult : : Type : : STR_HEX , " txid " , /* optional */ true , " The transaction id for the send. Only 1 transaction is created regardless of the number of addresses. " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , /* optional */ true , " If add_to_wallet is false, the hex-encoded raw transaction with signature(s) " } ,
{ RPCResult : : Type : : STR , " psbt " , /* optional */ true , " If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction " }
2020-08-07 17:36:36 +02:00
}
} ,
RPCExamples { " "
2020-11-05 06:12:17 +01:00
" \n Send 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode \n "
+ HelpExampleCli ( " send " , " '{ \" " + EXAMPLE_ADDRESS [ 0 ] + " \" : 0.1}' 6 economical \n " ) +
2020-11-17 20:08:30 +01:00
" Send 0.2 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + " /vB using positional arguments \n "
+ HelpExampleCli ( " send " , " '{ \" " + EXAMPLE_ADDRESS [ 0 ] + " \" : 0.2}' null \" unset \" 1.1 \n " ) +
2020-11-05 06:12:17 +01:00
" Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + " /vB using the options argument \n "
2020-11-17 20:08:30 +01:00
+ HelpExampleCli ( " send " , " '{ \" " + EXAMPLE_ADDRESS [ 0 ] + " \" : 0.2}' null \" unset \" null '{ \" fee_rate \" : 1}' \n " ) +
2020-11-05 06:12:17 +01:00
" Send 0.3 BTC with a fee rate of 25 " + CURRENCY_ATOM + " /vB using named arguments \n "
+ HelpExampleCli ( " -named send " , " outputs='{ \" " + EXAMPLE_ADDRESS [ 0 ] + " \" : 0.3}' fee_rate=25 \n " ) +
" Create a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network \n "
2020-08-07 17:36:36 +02:00
+ HelpExampleCli ( " send " , " '{ \" " + EXAMPLE_ADDRESS [ 0 ] + " \" : 0.1}' 1 economical '{ \" add_to_wallet \" : false, \" inputs \" : [{ \" txid \" : \" a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0 \" , \" vout \" :1}]}' " )
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
RPCTypeCheck ( request . params , {
2020-11-04 13:13:17 +01:00
UniValueType ( ) , // outputs (ARR or OBJ, checked later)
UniValue : : VNUM , // conf_target
UniValue : : VSTR , // estimate_mode
2020-12-04 11:20:39 +01:00
UniValueType ( ) , // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
2020-11-04 13:13:17 +01:00
UniValue : : VOBJ , // options
2020-08-07 17:36:36 +02:00
} , true
) ;
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-08-07 17:36:36 +02:00
2020-11-04 13:13:17 +01:00
UniValue options { request . params [ 4 ] . isNull ( ) ? UniValue : : VOBJ : request . params [ 4 ] } ;
if ( options . exists ( " conf_target " ) | | options . exists ( " estimate_mode " ) ) {
2020-08-07 17:36:36 +02:00
if ( ! request . params [ 1 ] . isNull ( ) | | ! request . params [ 2 ] . isNull ( ) ) {
2020-11-04 13:13:17 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Pass conf_target and estimate_mode either as arguments or in the options object, but not both " ) ;
2020-08-07 17:36:36 +02:00
}
} else {
options . pushKV ( " conf_target " , request . params [ 1 ] ) ;
options . pushKV ( " estimate_mode " , request . params [ 2 ] ) ;
}
2020-11-04 13:13:17 +01:00
if ( options . exists ( " fee_rate " ) ) {
if ( ! request . params [ 3 ] . isNull ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Pass the fee_rate either as an argument, or in the options object, but not both " ) ;
}
} else {
options . pushKV ( " fee_rate " , request . params [ 3 ] ) ;
}
2020-08-07 17:36:36 +02:00
if ( ! options [ " conf_target " ] . isNull ( ) & & ( options [ " estimate_mode " ] . isNull ( ) | | ( options [ " estimate_mode " ] . get_str ( ) = = " unset " ) ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Specify estimate_mode " ) ;
}
2020-11-04 13:13:17 +01:00
if ( options . exists ( " feeRate " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use fee_rate ( " + CURRENCY_ATOM + " /vB) instead of feeRate " ) ;
}
2020-08-07 17:36:36 +02:00
if ( options . exists ( " changeAddress " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use change_address " ) ;
}
if ( options . exists ( " changePosition " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use change_position " ) ;
}
if ( options . exists ( " includeWatching " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use include_watching " ) ;
}
if ( options . exists ( " lockUnspents " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use lock_unspents " ) ;
}
if ( options . exists ( " subtractFeeFromOutputs " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use subtract_fee_from_outputs " ) ;
}
const bool psbt_opt_in = options . exists ( " psbt " ) & & options [ " psbt " ] . get_bool ( ) ;
CAmount fee ;
int change_position ;
bool rbf = pwallet - > m_signal_rbf ;
if ( options . exists ( " replaceable " ) ) {
2020-09-17 20:57:51 +02:00
rbf = options [ " replaceable " ] . get_bool ( ) ;
2020-08-07 17:36:36 +02:00
}
CMutableTransaction rawTx = ConstructTransaction ( options [ " inputs " ] , request . params [ 0 ] , options [ " locktime " ] , rbf ) ;
CCoinControl coin_control ;
// Automatically select coins, unless at least one is manually selected. Can
2020-06-27 05:53:17 +02:00
// be overridden by options.add_inputs.
2020-08-07 17:36:36 +02:00
coin_control . m_add_inputs = rawTx . vin . size ( ) = = 0 ;
2021-03-04 23:20:13 +08:00
FundTransaction ( * pwallet , rawTx , fee , change_position , options , coin_control , /* override_min_fee */ false ) ;
2020-08-07 17:36:36 +02:00
bool add_to_wallet = true ;
if ( options . exists ( " add_to_wallet " ) ) {
add_to_wallet = options [ " add_to_wallet " ] . get_bool ( ) ;
}
// Make a blank psbt
PartiallySignedTransaction psbtx ( rawTx ) ;
2019-08-04 23:26:01 +02:00
// First fill transaction with our data without signing,
// so external signers are not asked sign more than once.
bool complete ;
2021-03-04 14:27:20 -08:00
pwallet - > FillPSBT ( psbtx , complete , SIGHASH_DEFAULT , false , true ) ;
const TransactionError err = pwallet - > FillPSBT ( psbtx , complete , SIGHASH_DEFAULT , true , false ) ;
2020-08-07 17:36:36 +02:00
if ( err ! = TransactionError : : OK ) {
throw JSONRPCTransactionError ( err ) ;
}
CMutableTransaction mtx ;
complete = FinalizeAndExtractPSBT ( psbtx , mtx ) ;
UniValue result ( UniValue : : VOBJ ) ;
if ( psbt_opt_in | | ! complete | | ! add_to_wallet ) {
2020-09-17 15:28:59 +02:00
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
2020-08-07 17:36:36 +02:00
}
if ( complete ) {
std : : string err_string ;
std : : string hex = EncodeHexTx ( CTransaction ( mtx ) ) ;
CTransactionRef tx ( MakeTransactionRef ( std : : move ( mtx ) ) ) ;
result . pushKV ( " txid " , tx - > GetHash ( ) . GetHex ( ) ) ;
if ( add_to_wallet & & ! psbt_opt_in ) {
pwallet - > CommitTransaction ( tx , { } , { } /* orderForm */ ) ;
} else {
result . pushKV ( " hex " , hex ) ;
}
}
result . pushKV ( " complete " , complete ) ;
return result ;
}
} ;
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan sethdseed ( )
2017-09-12 14:01:12 -07:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " sethdseed " ,
2018-10-20 08:19:44 -04:00
" \n Set or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already \n "
" HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed. \n "
2018-11-23 11:21:38 -05:00
" \n Note that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed. " +
2020-04-01 10:31:43 +08:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 08:19:44 -04:00
{
2021-04-14 15:01:00 +01:00
{ " newkeypool " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it. \n "
2020-09-30 09:20:37 +02:00
" If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed. \n "
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing \n "
" keypool will be used until it has been depleted. " } ,
2021-04-14 15:01:00 +01:00
{ " seed " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " random seed " } , " The WIF private key to use as the new HD seed. \n "
2020-09-30 09:20:37 +02:00
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1 " } ,
2018-12-21 12:29:36 -05:00
} ,
2020-03-13 14:40:53 -04:00
RPCResult { RPCResult : : Type : : NONE , " " , " " } ,
2018-12-21 12:29:36 -05:00
RPCExamples {
HelpExampleCli ( " sethdseed " , " " )
2017-09-12 14:01:12 -07:00
+ HelpExampleCli ( " sethdseed " , " false " )
+ HelpExampleCli ( " sethdseed " , " true \" wifkey \" " )
+ HelpExampleRpc ( " sethdseed " , " true, \" wifkey \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2019-10-07 14:11:34 -04:00
LegacyScriptPubKeyMan & spk_man = EnsureLegacyScriptPubKeyMan ( * pwallet , true ) ;
2019-10-07 14:11:34 -04:00
2019-01-25 14:38:34 -05:00
if ( pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Cannot set a HD seed to a wallet with private keys disabled " ) ;
}
2019-10-07 14:11:34 -04:00
LOCK2 ( pwallet - > cs_wallet , spk_man . cs_KeyStore ) ;
2017-09-12 14:01:12 -07:00
// Do not do anything to non-HD wallets
2019-02-06 21:26:55 -05:00
if ( ! pwallet - > CanSupportFeature ( FEATURE_HD ) ) {
2020-11-16 18:23:10 +01:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD " ) ;
2017-09-12 14:01:12 -07:00
}
2021-03-04 23:20:13 +08:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2017-09-12 14:01:12 -07:00
bool flush_key_pool = true ;
if ( ! request . params [ 0 ] . isNull ( ) ) {
flush_key_pool = request . params [ 0 ] . get_bool ( ) ;
}
CPubKey master_pub_key ;
if ( request . params [ 1 ] . isNull ( ) ) {
2019-11-05 10:13:43 -05:00
master_pub_key = spk_man . GenerateNewSeed ( ) ;
2017-09-12 14:01:12 -07:00
} else {
CKey key = DecodeSecret ( request . params [ 1 ] . get_str ( ) ) ;
if ( ! key . IsValid ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid private key " ) ;
}
2019-11-05 10:13:43 -05:00
if ( HaveKey ( spk_man , key ) ) {
2017-09-12 14:01:12 -07:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Already have this key (either as an HD seed or as a loose private key) " ) ;
}
2019-11-05 10:13:43 -05:00
master_pub_key = spk_man . DeriveNewSeed ( key ) ;
2017-09-12 14:01:12 -07:00
}
2019-11-05 10:13:43 -05:00
spk_man . SetHDSeed ( master_pub_key ) ;
if ( flush_key_pool ) spk_man . NewKeyPool ( ) ;
2017-09-12 14:01:12 -07:00
return NullUniValue ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2017-09-12 14:01:12 -07:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan walletprocesspsbt ( )
2018-06-28 19:05:05 -07:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " walletprocesspsbt " ,
2018-10-20 08:19:44 -04:00
" \n Update a PSBT with input information from our wallet and then sign inputs \n "
2018-11-23 11:21:38 -05:00
" that we can sign for. " +
2020-04-01 10:31:43 +08:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 08:19:44 -04:00
{
2018-12-10 16:56:51 -05:00
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The transaction base64 string " } ,
2021-09-28 01:24:23 +13:00
{ " sign " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Also sign the transaction when updating (requires wallet to be unlocked) " } ,
2021-03-04 14:27:20 -08:00
{ " sighashtype " , RPCArg : : Type : : STR , RPCArg : : Default { " DEFAULT " } , " The signature hash type to sign with if not specified by the PSBT. Must be one of \n "
" \" DEFAULT \" \n "
2018-06-28 19:05:05 -07:00
" \" ALL \" \n "
" \" NONE \" \n "
" \" SINGLE \" \n "
" \" ALL|ANYONECANPAY \" \n "
" \" NONE|ANYONECANPAY \" \n "
2018-11-23 11:21:38 -05:00
" \" SINGLE|ANYONECANPAY \" " } ,
2021-04-14 15:01:00 +01:00
{ " bip32derivs " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Include BIP 32 derivation paths for public keys if we know them " } ,
2021-07-20 21:24:56 -04:00
{ " finalize " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Also finalize inputs if possible " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " psbt " , " The base64-encoded partially signed transaction " } ,
{ RPCResult : : Type : : BOOL , " complete " , " If the transaction has a complete set of signatures " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " walletprocesspsbt " , " \" psbt \" " )
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 16:39:04 +09:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 22:44:40 +08:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2021-06-22 10:08:41 +02:00
const CWallet & wallet { * pwallet } ;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet . BlockUntilSyncedToCurrentChain ( ) ;
2021-07-20 21:24:56 -04:00
RPCTypeCheck ( request . params , { UniValue : : VSTR } ) ;
2018-06-28 19:05:05 -07:00
// Unserialize the transaction
PartiallySignedTransaction psbtx ;
std : : string error ;
2019-01-29 21:32:38 -08:00
if ( ! DecodeBase64PSBT ( psbtx , request . params [ 0 ] . get_str ( ) , error ) ) {
2018-06-28 19:05:05 -07:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
// Get the sighash type
int nHashType = ParseSighashString ( request . params [ 2 ] ) ;
// Fill transaction with our data and also sign
bool sign = request . params [ 1 ] . isNull ( ) ? true : request . params [ 1 ] . get_bool ( ) ;
2019-10-26 12:03:38 +02:00
bool bip32derivs = request . params [ 3 ] . isNull ( ) ? true : request . params [ 3 ] . get_bool ( ) ;
2021-07-20 21:24:56 -04:00
bool finalize = request . params [ 4 ] . isNull ( ) ? true : request . params [ 4 ] . get_bool ( ) ;
2019-02-09 20:51:33 -08:00
bool complete = true ;
2021-09-28 01:24:23 +13:00
if ( sign ) EnsureWalletIsUnlocked ( * pwallet ) ;
2021-07-20 21:24:56 -04:00
const TransactionError err { wallet . FillPSBT ( psbtx , complete , nHashType , sign , bip32derivs , nullptr , finalize ) } ;
2019-02-14 10:01:06 -05:00
if ( err ! = TransactionError : : OK ) {
2019-02-09 20:51:33 -08:00
throw JSONRPCTransactionError ( err ) ;
}
2018-06-28 19:05:05 -07:00
UniValue result ( UniValue : : VOBJ ) ;
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
2018-10-26 15:18:52 -07:00
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
2018-08-09 18:08:45 +02:00
result . pushKV ( " complete " , complete ) ;
2018-06-28 19:05:05 -07:00
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-06-28 19:05:05 -07:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan walletcreatefundedpsbt ( )
2018-06-28 19:05:05 -07:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " walletcreatefundedpsbt " ,
2019-07-12 13:30:10 +01:00
" \n Creates and funds a transaction in the Partially Signed Transaction format. \n "
2019-10-18 19:43:01 -04:00
" Implements the Creator and Updater roles. \n "
" All existing inputs must either have their previous output transaction be in the wallet \n "
" or be in the UTXO set. Solving data must be provided for non-wallet inputs. \n " ,
2018-10-23 15:22:28 -04:00
{
2019-07-11 18:50:45 +01:00
{ " inputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Leave empty to add inputs automatically. See add_inputs option. " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
2021-04-14 15:01:00 +01:00
{ " sequence " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " depends on the value of the 'locktime' and 'options.replaceable' arguments " } , " The sequence number " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
} ,
2018-10-23 15:22:28 -04:00
} ,
2020-03-05 10:36:27 +00:00
{ " outputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The outputs (key-value pairs), where none of the keys are duplicated. \n "
2018-12-06 16:13:53 -05:00
" That is, each address can only appear once and there can only be one 'data' object. \n "
2018-11-23 11:21:38 -05:00
" For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also \n "
2020-09-30 09:20:37 +02:00
" accepted as second parameter. " ,
2018-10-23 15:22:28 -04:00
{
2021-05-11 19:03:41 +09:00
{ " " , RPCArg : : Type : : OBJ_USER_KEYS , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + " " } ,
2018-10-23 15:22:28 -04:00
} ,
} ,
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " data " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " A key-value pair. The key must be \" data \" , the value is hex-encoded data " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2021-04-14 15:01:00 +01:00
{ " locktime " , RPCArg : : Type : : NUM , RPCArg : : Default { 0 } , " Raw locktime. Non-0 value also locktime-activates inputs " } ,
2018-12-10 16:56:51 -05:00
{ " options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " " ,
2021-10-04 14:25:23 +02:00
Cat < std : : vector < RPCArg > > (
2018-11-23 11:21:38 -05:00
{
2021-04-14 15:01:00 +01:00
{ " add_inputs " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " If inputs are specified, automatically include more if they are not enough. " } ,
2021-03-10 15:37:18 +01:00
{ " include_unsafe " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions). \n "
" Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears. \n "
" If that happens, you will need to fund the transaction with different inputs and republish it. " } ,
2021-04-14 15:01:00 +01:00
{ " changeAddress " , RPCArg : : Type : : STR_HEX , RPCArg : : DefaultHint { " pool address " } , " The bitcoin address to receive the change " } ,
{ " changePosition " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " random " } , " The index of the change output " } ,
{ " change_type " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " set by -changetype " } , " The output type to use. Only valid if changeAddress is not specified. Options are \" legacy \" , \" p2sh-segwit \" , and \" bech32 \" . " } ,
{ " includeWatching " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " true for watch-only wallets, otherwise false " } , " Also select inputs which are watch only " } ,
{ " lockUnspents " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " Lock selected unspent outputs " } ,
{ " fee_rate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_ATOM + " /vB. " } ,
{ " feeRate " , RPCArg : : Type : : AMOUNT , RPCArg : : DefaultHint { " not set, fall back to wallet fee estimation " } , " Specify a fee rate in " + CURRENCY_UNIT + " /kvB. " } ,
{ " subtractFeeFromOutputs " , RPCArg : : Type : : ARR , RPCArg : : Default { UniValue : : VARR } , " The outputs to subtract the fee from. \n "
2020-09-30 09:20:37 +02:00
" The fee will be equally deducted from the amount of each specified output. \n "
" Those recipients will receive less bitcoins than you enter in their corresponding amount field. \n "
" If no outputs are specified here, the sender pays the fee. " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " vout_index " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED , " The zero-based output index, before a change output is added. " } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
} ,
2021-10-04 14:25:23 +02:00
FundTxDoc ( ) ) ,
2018-11-23 11:21:38 -05:00
" options " } ,
2021-04-14 15:01:00 +01:00
{ " bip32derivs " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Include BIP 32 derivation paths for public keys if we know them " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " psbt " , " The resulting raw transaction (base64-encoded string) " } ,
{ RPCResult : : Type : : STR_AMOUNT , " fee " , " Fee in " + CURRENCY_UNIT + " the resulting transaction pays " } ,
{ RPCResult : : Type : : NUM , " changepos " , " The position of the added change output, or -1 " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2018-06-28 19:05:05 -07:00
" \n Create a transaction with no inputs \n "
+ HelpExampleCli ( " walletcreatefundedpsbt " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" \" [{ \\ \" data \\ \" : \\ \" 00010203 \\ \" }] \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-09-22 18:13:52 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2021-06-22 10:08:41 +02:00
CWallet & wallet { * pwallet } ;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet . BlockUntilSyncedToCurrentChain ( ) ;
2018-06-28 19:05:05 -07:00
RPCTypeCheck ( request . params , {
UniValue : : VARR ,
UniValueType ( ) , // ARR or OBJ, checked later
UniValue : : VNUM ,
2018-08-20 12:57:06 -04:00
UniValue : : VOBJ ,
UniValue : : VBOOL
2018-06-28 19:05:05 -07:00
} , true
) ;
CAmount fee ;
int change_position ;
2021-06-22 10:08:41 +02:00
bool rbf { wallet . m_signal_rbf } ;
2019-04-27 19:44:38 +02:00
const UniValue & replaceable_arg = request . params [ 3 ] [ " replaceable " ] ;
if ( ! replaceable_arg . isNull ( ) ) {
RPCTypeCheckArgument ( replaceable_arg , UniValue : : VBOOL ) ;
rbf = replaceable_arg . isTrue ( ) ;
}
CMutableTransaction rawTx = ConstructTransaction ( request . params [ 0 ] , request . params [ 1 ] , request . params [ 2 ] , rbf ) ;
2019-07-12 13:30:10 +01:00
CCoinControl coin_control ;
// Automatically select coins, unless at least one is manually selected. Can
2020-07-11 13:56:13 +02:00
// be overridden by options.add_inputs.
2019-07-12 13:30:10 +01:00
coin_control . m_add_inputs = rawTx . vin . size ( ) = = 0 ;
2021-06-22 10:08:41 +02:00
FundTransaction ( wallet , rawTx , fee , change_position , request . params [ 3 ] , coin_control , /* override_min_fee */ true ) ;
2018-06-28 19:05:05 -07:00
// Make a blank psbt
2018-10-26 15:26:16 -07:00
PartiallySignedTransaction psbtx ( rawTx ) ;
2018-06-28 19:05:05 -07:00
// Fill transaction with out data but don't sign
2019-10-26 12:03:38 +02:00
bool bip32derivs = request . params [ 4 ] . isNull ( ) ? true : request . params [ 4 ] . get_bool ( ) ;
2019-02-09 20:51:33 -08:00
bool complete = true ;
2021-06-22 10:08:41 +02:00
const TransactionError err { wallet . FillPSBT ( psbtx , complete , 1 , false , bip32derivs ) } ;
2019-02-14 10:01:06 -05:00
if ( err ! = TransactionError : : OK ) {
2019-02-09 20:51:33 -08:00
throw JSONRPCTransactionError ( err ) ;
}
2018-06-28 19:05:05 -07:00
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
UniValue result ( UniValue : : VOBJ ) ;
2018-10-26 15:18:52 -07:00
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
2018-06-28 19:05:05 -07:00
result . pushKV ( " fee " , ValueFromAmount ( fee ) ) ;
result . pushKV ( " changepos " , change_position ) ;
return result ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2018-06-28 19:05:05 -07:00
}
2020-09-22 18:13:52 +02:00
static RPCHelpMan upgradewallet ( )
2019-04-06 12:56:06 -04:00
{
2020-09-22 18:13:52 +02:00
return RPCHelpMan { " upgradewallet " ,
2020-11-16 18:23:10 +01:00
" \n Upgrade the wallet. Upgrades to the latest version if no version number is specified. \n "
2019-04-06 12:56:06 -04:00
" New keys may be generated and a new wallet backup will need to be made. " ,
{
2021-04-14 15:01:00 +01:00
{ " version " , RPCArg : : Type : : NUM , RPCArg : : Default { FEATURE_LATEST } , " The version number to upgrade to. Default is the latest wallet version. " }
2019-04-06 12:56:06 -04:00
} ,
2020-10-18 18:01:42 -07:00
RPCResult {
RPCResult : : Type : : OBJ , " " , " " ,
{
2020-11-16 18:23:10 +01:00
{ RPCResult : : Type : : STR , " wallet_name " , " Name of wallet this operation was performed on " } ,
{ RPCResult : : Type : : NUM , " previous_version " , " Version of wallet before this operation " } ,
{ RPCResult : : Type : : NUM , " current_version " , " Version of wallet after this operation " } ,
{ RPCResult : : Type : : STR , " result " , /* optional */ true , " Description of result, if no error " } ,
2020-10-18 18:01:42 -07:00
{ RPCResult : : Type : : STR , " error " , /* optional */ true , " Error message (if there is one) " }
} ,
} ,
2019-04-06 12:56:06 -04:00
RPCExamples {
HelpExampleCli ( " upgradewallet " , " 169900 " )
+ HelpExampleRpc ( " upgradewallet " , " 169900 " )
2020-09-22 18:13:52 +02:00
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 22:44:40 +08:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2019-04-06 12:56:06 -04:00
RPCTypeCheck ( request . params , { UniValue : : VNUM } , true ) ;
2021-03-04 23:20:13 +08:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2019-04-06 12:56:06 -04:00
int version = 0 ;
if ( ! request . params [ 0 ] . isNull ( ) ) {
version = request . params [ 0 ] . get_int ( ) ;
}
2019-08-19 18:12:35 -04:00
bilingual_str error ;
2020-11-16 18:23:10 +01:00
const int previous_version { pwallet - > GetVersion ( ) } ;
const bool wallet_upgraded { pwallet - > UpgradeWallet ( version , error ) } ;
const int current_version { pwallet - > GetVersion ( ) } ;
std : : string result ;
2020-11-17 15:57:14 +01:00
if ( wallet_upgraded ) {
if ( previous_version = = current_version ) {
result = " Already at latest version. Wallet version unchanged. " ;
} else {
result = strprintf ( " Wallet upgraded successfully from version %i to version %i. " , previous_version , current_version ) ;
}
2019-04-06 12:56:06 -04:00
}
2020-11-16 18:23:10 +01:00
2020-10-18 18:01:42 -07:00
UniValue obj ( UniValue : : VOBJ ) ;
2020-11-16 18:23:10 +01:00
obj . pushKV ( " wallet_name " , pwallet - > GetName ( ) ) ;
obj . pushKV ( " previous_version " , previous_version ) ;
obj . pushKV ( " current_version " , current_version ) ;
if ( ! result . empty ( ) ) {
obj . pushKV ( " result " , result ) ;
2020-11-17 15:57:14 +01:00
} else {
CHECK_NONFATAL ( ! error . empty ( ) ) ;
2020-10-18 18:01:42 -07:00
obj . pushKV ( " error " , error . original ) ;
}
return obj ;
2020-09-22 18:13:52 +02:00
} ,
} ;
2019-04-06 12:56:06 -04:00
}
2020-08-14 11:22:05 +02:00
RPCHelpMan abortrescan ( ) ;
RPCHelpMan dumpprivkey ( ) ;
RPCHelpMan importprivkey ( ) ;
RPCHelpMan importaddress ( ) ;
RPCHelpMan importpubkey ( ) ;
RPCHelpMan dumpwallet ( ) ;
RPCHelpMan importwallet ( ) ;
RPCHelpMan importprunedfunds ( ) ;
RPCHelpMan removeprunedfunds ( ) ;
RPCHelpMan importmulti ( ) ;
RPCHelpMan importdescriptors ( ) ;
2020-10-09 14:24:20 +07:00
RPCHelpMan listdescriptors ( ) ;
2021-11-26 13:48:32 +01:00
RPCHelpMan signmessage ( ) ;
2021-11-30 15:19:02 +13:00
RPCHelpMan backupwallet ( ) ;
RPCHelpMan restorewallet ( ) ;
2016-01-07 08:33:49 +01:00
2021-12-01 15:40:40 +13:00
// addresses
RPCHelpMan getnewaddress ( ) ;
RPCHelpMan getrawchangeaddress ( ) ;
RPCHelpMan setlabel ( ) ;
RPCHelpMan listaddressgroupings ( ) ;
RPCHelpMan addmultisigaddress ( ) ;
RPCHelpMan keypoolrefill ( ) ;
RPCHelpMan newkeypool ( ) ;
RPCHelpMan getaddressesbylabel ( ) ;
RPCHelpMan listlabels ( ) ;
# ifdef ENABLE_EXTERNAL_SIGNER
RPCHelpMan walletdisplayaddress ( ) ;
# endif // ENABLE_EXTERNAL_SIGNER
2021-12-01 14:50:19 +13:00
// encryption
RPCHelpMan walletpassphrase ( ) ;
RPCHelpMan walletpassphrasechange ( ) ;
RPCHelpMan walletlock ( ) ;
RPCHelpMan encryptwallet ( ) ;
2021-12-01 15:06:45 +13:00
// transactions
RPCHelpMan listreceivedbyaddress ( ) ;
RPCHelpMan listreceivedbylabel ( ) ;
RPCHelpMan listtransactions ( ) ;
RPCHelpMan listsinceblock ( ) ;
RPCHelpMan gettransaction ( ) ;
RPCHelpMan abandontransaction ( ) ;
RPCHelpMan rescanblockchain ( ) ;
2020-05-28 02:13:19 -04:00
Span < const CRPCCommand > GetWalletRPCCommands ( )
2020-04-06 00:21:33 +08:00
{
2018-08-20 14:19:43 +02:00
// clang-format off
2016-03-29 19:43:02 +02:00
static const CRPCCommand commands [ ] =
2021-01-12 06:41:46 +01:00
{ // category actor (function)
// ------------------ ------------------------
{ " rawtransactions " , & fundrawtransaction , } ,
{ " wallet " , & abandontransaction , } ,
{ " wallet " , & abortrescan , } ,
{ " wallet " , & addmultisigaddress , } ,
{ " wallet " , & backupwallet , } ,
{ " wallet " , & bumpfee , } ,
{ " wallet " , & psbtbumpfee , } ,
{ " wallet " , & createwallet , } ,
2021-07-27 14:28:23 -03:00
{ " wallet " , & restorewallet , } ,
2021-01-12 06:41:46 +01:00
{ " wallet " , & dumpprivkey , } ,
{ " wallet " , & dumpwallet , } ,
{ " wallet " , & encryptwallet , } ,
{ " wallet " , & getaddressesbylabel , } ,
{ " wallet " , & getaddressinfo , } ,
{ " wallet " , & getbalance , } ,
{ " wallet " , & getnewaddress , } ,
{ " wallet " , & getrawchangeaddress , } ,
{ " wallet " , & getreceivedbyaddress , } ,
{ " wallet " , & getreceivedbylabel , } ,
{ " wallet " , & gettransaction , } ,
{ " wallet " , & getunconfirmedbalance , } ,
{ " wallet " , & getbalances , } ,
{ " wallet " , & getwalletinfo , } ,
{ " wallet " , & importaddress , } ,
{ " wallet " , & importdescriptors , } ,
{ " wallet " , & importmulti , } ,
{ " wallet " , & importprivkey , } ,
{ " wallet " , & importprunedfunds , } ,
{ " wallet " , & importpubkey , } ,
{ " wallet " , & importwallet , } ,
{ " wallet " , & keypoolrefill , } ,
{ " wallet " , & listaddressgroupings , } ,
{ " wallet " , & listdescriptors , } ,
{ " wallet " , & listlabels , } ,
{ " wallet " , & listlockunspent , } ,
{ " wallet " , & listreceivedbyaddress , } ,
{ " wallet " , & listreceivedbylabel , } ,
{ " wallet " , & listsinceblock , } ,
{ " wallet " , & listtransactions , } ,
{ " wallet " , & listunspent , } ,
{ " wallet " , & listwalletdir , } ,
{ " wallet " , & listwallets , } ,
{ " wallet " , & loadwallet , } ,
{ " wallet " , & lockunspent , } ,
2021-09-26 11:36:59 +13:00
{ " wallet " , & newkeypool , } ,
2021-01-12 06:41:46 +01:00
{ " wallet " , & removeprunedfunds , } ,
{ " wallet " , & rescanblockchain , } ,
{ " wallet " , & send , } ,
{ " wallet " , & sendmany , } ,
{ " wallet " , & sendtoaddress , } ,
{ " wallet " , & sethdseed , } ,
{ " wallet " , & setlabel , } ,
{ " wallet " , & settxfee , } ,
{ " wallet " , & setwalletflag , } ,
{ " wallet " , & signmessage , } ,
{ " wallet " , & signrawtransactionwithwallet , } ,
{ " wallet " , & unloadwallet , } ,
{ " wallet " , & upgradewallet , } ,
{ " wallet " , & walletcreatefundedpsbt , } ,
2021-03-18 14:17:39 +01:00
# ifdef ENABLE_EXTERNAL_SIGNER
{ " wallet " , & walletdisplayaddress , } ,
# endif // ENABLE_EXTERNAL_SIGNER
2021-01-12 06:41:46 +01:00
{ " wallet " , & walletlock , } ,
{ " wallet " , & walletpassphrase , } ,
{ " wallet " , & walletpassphrasechange , } ,
{ " wallet " , & walletprocesspsbt , } ,
2016-01-07 08:33:49 +01:00
} ;
2018-08-20 14:19:43 +02:00
// clang-format on
2021-11-02 10:07:46 -04:00
return commands ;
2016-01-07 08:33:49 +01:00
}