2012-08-21 10:38:57 -04:00
// Copyright (c) 2010 Satoshi Nakamoto
2020-01-14 16:17:38 -03:00
// Copyright (c) 2009-2020 The Bitcoin Core developers
2014-11-19 23:19:29 -03: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-10 23:29:00 -03:00
# include <consensus/amount.h>
2017-11-09 21:57:53 -03:00
# include <core_io.h>
2017-07-26 10:23:01 -04:00
# include <interfaces/chain.h>
2017-09-19 22:12:25 -03:00
# include <key_io.h>
2019-09-17 19:28:03 -03:00
# include <node/context.h>
2018-07-09 04:15:50 -04:00
# include <outputtype.h>
2017-11-09 21:57:53 -03:00
# include <policy/feerate.h>
# include <policy/fees.h>
2020-08-07 11:36:36 -04:00
# include <policy/policy.h>
2017-11-09 21:57:53 -03:00
# include <policy/rbf.h>
2019-04-02 17:51:32 -03:00
# include <rpc/rawtransaction_util.h>
2017-11-09 21:57:53 -03:00
# include <rpc/server.h>
2017-09-29 01:21:28 -03:00
# include <rpc/util.h>
2018-10-13 17:32:18 -03:00
# include <script/descriptor.h>
2017-11-09 21:57:53 -03:00
# include <script/sign.h>
2019-04-02 18:03:37 -03:00
# include <util/fees.h>
2019-10-06 18:52:05 -03: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 18:03:37 -03:00
# include <util/url.h>
2020-01-09 14:00:57 -03:00
# include <util/vector.h>
2017-11-09 21:57:53 -03:00
# include <wallet/coincontrol.h>
2020-05-28 02:13:19 -04:00
# include <wallet/context.h>
2017-11-09 21:57:53 -03:00
# include <wallet/feebumper.h>
2019-05-01 15:12:44 -04:00
# include <wallet/load.h>
2021-02-12 20:01:22 -03:00
# include <wallet/receive.h>
2017-06-12 21:53:46 -04:00
# include <wallet/rpcwallet.h>
2021-12-01 03:20:33 -03:00
# include <wallet/rpc/util.h>
2021-02-12 20:01:22 -03:00
# include <wallet/spend.h>
2017-11-09 21:57:53 -03:00
# include <wallet/wallet.h>
# include <wallet/walletdb.h>
2017-10-08 17:48:07 -03:00
# include <wallet/walletutil.h>
2013-04-13 02:13:08 -03:00
2021-03-15 00:59:05 -03:00
# include <optional>
2013-04-13 02:13:08 -03:00
# include <stdint.h>
2015-09-04 11:11:34 -03:00
# include <univalue.h>
2012-08-21 10:38:57 -04:00
2019-10-18 20:43:01 -03:00
# include <map>
2018-04-06 16:05:32 -03:00
2019-07-13 11:34:49 -04:00
2019-06-06 10:49:52 -04:00
/** Checks if a CKey is in the given CWallet compressed or otherwise*/
2019-10-07 15:11:34 -03:00
bool HaveKey ( const SigningProvider & wallet , const CKey & key )
2019-06-06 10:49:52 -04:00
{
CKey key2 ;
key2 . Set ( key . begin ( ) , key . end ( ) , ! key . IsCompressed ( ) ) ;
return wallet . HaveKey ( key . GetPubKey ( ) . GetID ( ) ) | | wallet . HaveKey ( key2 . GetPubKey ( ) . GetID ( ) ) ;
}
2020-03-03 23:26:42 -03:00
/**
* Update coin control with fee estimation based on the given parameters
*
2020-11-17 16:22:01 -03:00
* @ param [ in ] wallet Wallet reference
2020-11-12 07:16:48 -03: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 16:08:30 -03:00
* if present , both conf_target and estimate_mode must either be null , or " unset "
2020-11-12 07:16:48 -03: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 09:13:17 -03:00
* @ throws a JSONRPCError if conf_target , estimate_mode , or fee_rate contain invalid values or are in conflict
2020-03-03 23:26:42 -03:00
*/
2020-11-17 16:22:01 -03: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-03 23:26:42 -03:00
{
2020-11-04 09:13:17 -03:00
if ( ! fee_rate . isNull ( ) ) {
2020-11-17 16:08:30 -03:00
if ( ! conf_target . isNull ( ) ) {
2020-11-04 09:13:17 -03: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-03 23:26:42 -03:00
}
2020-11-17 16:08:30 -03:00
if ( ! estimate_mode . isNull ( ) & & estimate_mode . get_str ( ) ! = " unset " ) {
2020-11-04 09:13:17 -03:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both estimate_mode and fee_rate " ) ;
2020-03-03 23:26:42 -03:00
}
2021-04-27 05:04:10 -04: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 14:38:00 -03:00
if ( override_min_fee ) cc . fOverrideFeeRate = true ;
2020-11-04 09:13:17 -03:00
// Default RBF to true for explicit fee_rate, if unset.
2021-03-15 00:59:05 -03:00
if ( ! cc . m_signal_bip125_rbf ) cc . m_signal_bip125_rbf = true ;
2020-11-04 09:13:17 -03:00
return ;
}
if ( ! estimate_mode . isNull ( ) & & ! FeeModeFromString ( estimate_mode . get_str ( ) , cc . m_fee_mode ) ) {
2020-11-10 08:29:01 -03:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , InvalidEstimateModeErrorMessage ( ) ) ;
2020-11-04 09:13:17 -03:00
}
if ( ! conf_target . isNull ( ) ) {
2020-11-17 16:22:01 -03:00
cc . m_confirm_target = ParseConfirmTarget ( conf_target , wallet . chain ( ) . estimateMaxBlocks ( ) ) ;
2020-03-03 23:26:42 -03:00
}
}
2020-02-24 16:12:50 -03: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 16:12:50 -03:00
if ( destinations . count ( dest ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , std : : string ( " Invalid parameter, duplicated address: " ) + address ) ;
}
destinations . insert ( dest ) ;
2014-12-09 10:50:01 -03:00
2020-02-24 16:12:50 -03: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 16:06:45 -03:00
2021-03-04 12:20:13 -03:00
UniValue SendMoney ( CWallet & wallet , const CCoinControl & coin_control , std : : vector < CRecipient > & recipients , mapValue_t map_value , bool verbose )
2020-02-24 16:12:50 -03:00
{
2021-03-04 12:20:13 -03:00
EnsureWalletIsUnlocked ( wallet ) ;
2014-12-01 16:06:45 -03:00
2021-02-16 15:22:49 -03: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 12:20:13 -03:00
if ( wallet . IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) {
2021-02-16 15:22:49 -03:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error: Private keys are disabled for this wallet " ) ;
}
2020-02-24 16:12:50 -03:00
// Shuffle recipient list
std : : shuffle ( recipients . begin ( ) , recipients . end ( ) , FastRandomContext ( ) ) ;
2014-12-01 16:06:45 -03:00
2020-02-24 16:12:50 -03:00
// Send
2020-03-08 22:20:30 -03:00
CAmount nFeeRequired = 0 ;
2014-07-23 08:34:36 -04:00
int nChangePosRet = - 1 ;
2020-02-24 16:12:50 -03:00
bilingual_str error ;
2017-02-02 17:30:03 -03:00
CTransactionRef tx ;
2020-07-12 15:52:02 -04:00
FeeCalculation fee_calc_out ;
2021-02-12 20:01:22 -03:00
const bool fCreated = CreateTransaction ( wallet , recipients , tx , nFeeRequired , nChangePosRet , error , coin_control , fee_calc_out , true ) ;
2020-02-24 16:12:50 -03:00
if ( ! fCreated ) {
throw JSONRPCError ( RPC_WALLET_INSUFFICIENT_FUNDS , error . original ) ;
2014-12-01 16:06:45 -03:00
}
2021-03-04 12:20:13 -03:00
wallet . CommitTransaction ( tx , std : : move ( map_value ) , { } /* orderForm */ ) ;
2020-07-12 15:52:02 -04: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 16:12:50 -03:00
return tx - > GetHash ( ) . GetHex ( ) ;
2014-12-01 16:06:45 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan sendtoaddress ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " sendtoaddress " ,
2018-11-23 13:21:38 -03:00
" \n Send an amount to a given address. " +
2020-03-31 23:31:43 -03:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 09:19:44 -03:00
{
2018-12-10 18:56:51 -03: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 04:20:37 -03:00
" This is not part of the transaction, just kept in your wallet. " } ,
2018-12-10 18:56:51 -03: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 04:20:37 -03:00
" to which you're sending the transaction. This is not part of the \n "
" transaction, just kept in your wallet. " } ,
2021-04-14 10:01:00 -04:00
{ " subtractfeefromamount " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " The fee will be deducted from the amount being sent. \n "
2020-09-30 04:20:37 -03:00
" The recipient will receive less bitcoins than you enter in the amount field. " } ,
2021-04-14 10:01:00 -04: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 10:17:32 -04:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-04-14 10:01:00 -04: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 11:49:21 -03: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 10:01:00 -04: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 14:29:36 -03:00
} ,
2020-07-12 15:52:02 -04: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 13:55:15 -04:00
{ RPCResult : : Type : : STR , " fee_reason " , " The transaction fee reason. " }
2020-07-12 15:52:02 -04:00
} ,
} ,
2018-12-21 14:29:36 -03:00
} ,
RPCExamples {
2020-11-05 02:12:17 -03: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 16:08:30 -03: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 02:12:17 -03: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 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 14:05:28 -03: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 05:46:17 -03:00
2012-08-21 10:38:57 -04:00
// Wallet comments
2017-02-02 17:30:03 -03:00
mapValue_t mapValue ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 2 ] . isNull ( ) & & ! request . params [ 2 ] . get_str ( ) . empty ( ) )
2017-02-02 17:30:03 -03:00
mapValue [ " comment " ] = request . params [ 2 ] . get_str ( ) ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 3 ] . isNull ( ) & & ! request . params [ 3 ] . get_str ( ) . empty ( ) )
2017-02-02 17:30:03 -03:00
mapValue [ " to " ] = request . params [ 3 ] . get_str ( ) ;
2012-08-21 10:38:57 -04:00
2014-07-23 08:34:36 -04:00
bool fSubtractFeeFromAmount = false ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 4 ] . isNull ( ) ) {
2016-09-22 04:46:41 -03:00
fSubtractFeeFromAmount = request . params [ 4 ] . get_bool ( ) ;
2017-06-14 15:15:40 -04:00
}
CCoinControl coin_control ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 5 ] . isNull ( ) ) {
2018-04-07 13:12:46 -03:00
coin_control . m_signal_bip125_rbf = request . params [ 5 ] . get_bool ( ) ;
2017-06-14 15:15:40 -04:00
}
2021-03-04 12:20:13 -03:00
coin_control . m_avoid_address_reuse = GetAvoidReuseFlag ( * pwallet , request . params [ 8 ] ) ;
2019-04-25 00:50:07 -04: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 08:34:36 -04:00
2020-11-17 16:22:01 -03: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-03 23:26:42 -03:00
2021-03-04 12:20:13 -03:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2012-08-21 10:38:57 -04:00
2020-02-24 16:12:50 -03: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 09:13:17 -03:00
const bool verbose { request . params [ 10 ] . isNull ( ) ? false : request . params [ 10 ] . get_bool ( ) } ;
2020-02-24 16:12:50 -03:00
2021-03-04 12:20:13 -03:00
return SendMoney ( * pwallet , coin_control , recipients , mapValue , verbose ) ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan sendmany ( )
2012-08-21 10:38:57 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " sendmany " ,
2018-11-23 13:21:38 -03:00
" \n Send multiple times. Amounts are double-precision floating point numbers. " +
2020-03-31 23:31:43 -03:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 09:19:44 -03:00
{
2018-12-10 18:56:51 -03:00
{ " dummy " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " Must be set to \" \" for backwards compatibility. " , " \" \" " } ,
2021-05-11 06:03:41 -04:00
{ " amounts " , RPCArg : : Type : : OBJ_USER_KEYS , RPCArg : : Optional : : NO , " The addresses and amounts " ,
2018-10-20 09:19:44 -03:00
{
2018-12-10 18:56:51 -03: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 09:19:44 -03:00
} ,
2018-11-23 13:21:38 -03:00
} ,
2019-03-13 15:45:53 -03:00
{ " minconf " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Ignored dummy value " } ,
2018-12-10 18:56:51 -03:00
{ " comment " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " A comment " } ,
2020-03-05 07:36:27 -03:00
{ " subtractfeefrom " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The addresses. \n "
2020-09-30 04:20:37 -03: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 13:21:38 -03:00
{
2018-12-10 18:56:51 -03:00
{ " address " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " Subtract fee from this address " } ,
2018-11-23 13:21:38 -03:00
} ,
} ,
2021-04-14 10:01:00 -04: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 10:17:32 -04:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-04-14 10:01:00 -04: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 15:52:02 -04:00
} ,
{
RPCResult { " if verbose is not set or set to false " ,
2020-09-30 04:20:37 -03:00
RPCResult : : Type : : STR_HEX , " txid " , " The transaction id for the send. Only 1 transaction is created regardless of \n "
2020-07-12 15:52:02 -04: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 13:55:15 -04:00
{ RPCResult : : Type : : STR , " fee_reason " , " The transaction fee reason. " }
2020-07-12 15:52:02 -04:00
} ,
} ,
2018-12-21 14:29:36 -03:00
} ,
RPCExamples {
2018-04-16 15:01:52 -03:00
" \n Send two amounts to two different addresses: \n "
2020-02-25 13:12:04 -03:00
+ HelpExampleCli ( " sendmany " , " \" \" \" { \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" :0.01, \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" :0.02} \" " ) +
2018-04-16 15:01:52 -03:00
" \n Send two amounts to two different addresses setting the confirmation and comment: \n "
2020-02-25 13:12:04 -03:00
+ HelpExampleCli ( " sendmany " , " \" \" \" { \\ \" " + EXAMPLE_ADDRESS [ 0 ] + " \\ \" :0.01, \\ \" " + EXAMPLE_ADDRESS [ 1 ] + " \\ \" :0.02} \" 6 \" testing \" " ) +
2018-04-16 15:01:52 -03:00
" \n Send two amounts to two different addresses, subtract fee from amount: \n "
2020-02-25 13: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 16:49:18 -03:00
" \n As a JSON-RPC call \n "
2020-02-25 13:12:04 -03:00
+ HelpExampleRpc ( " sendmany " , " \" \" , { \" " + EXAMPLE_ADDRESS [ 0 ] + " \" :0.01, \" " + EXAMPLE_ADDRESS [ 1 ] + " \" :0.02}, 6, \" testing \" " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 14:05:28 -03: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 05:46:17 -03:00
2018-07-27 17:05:24 -04:00
if ( ! request . params [ 0 ] . isNull ( ) & & ! request . params [ 0 ] . get_str ( ) . empty ( ) ) {
2018-04-16 15:01:52 -03:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Dummy value must be set to \" \" " ) ;
}
2016-09-22 04:46:41 -03:00
UniValue sendTo = request . params [ 1 ] . get_obj ( ) ;
2012-08-21 10:38:57 -04:00
2017-02-02 17:30:03 -03:00
mapValue_t mapValue ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 3 ] . isNull ( ) & & ! request . params [ 3 ] . get_str ( ) . empty ( ) )
2017-02-02 17:30:03 -03:00
mapValue [ " comment " ] = request . params [ 3 ] . get_str ( ) ;
2012-08-21 10:38:57 -04:00
2015-05-10 09:48:35 -03:00
UniValue subtractFeeFromAmount ( UniValue : : VARR ) ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 4 ] . isNull ( ) )
2016-09-22 04:46:41 -03:00
subtractFeeFromAmount = request . params [ 4 ] . get_array ( ) ;
2014-07-23 08:34:36 -04:00
2017-06-14 15:15:40 -04:00
CCoinControl coin_control ;
2017-08-14 20:38:18 -03:00
if ( ! request . params [ 5 ] . isNull ( ) ) {
2018-04-07 13:12:46 -03:00
coin_control . m_signal_bip125_rbf = request . params [ 5 ] . get_bool ( ) ;
2017-06-14 15:15:40 -04:00
}
2020-11-17 16:22:01 -03: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 16:12:50 -03:00
std : : vector < CRecipient > recipients ;
ParseRecipients ( sendTo , subtractFeeFromAmount , recipients ) ;
2020-11-04 09:13:17 -03:00
const bool verbose { request . params [ 9 ] . isNull ( ) ? false : request . params [ 9 ] . get_bool ( ) } ;
2018-03-16 15:33:29 -03:00
2021-03-04 12:20:13 -03:00
return SendMoney ( * pwallet , coin_control , recipients , std : : move ( mapValue ) , verbose ) ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2012-08-21 10:38:57 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan settxfee ( )
2013-12-13 12:06:32 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " settxfee " ,
2021-05-01 12:29:22 -04: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 12:07:33 -03: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 09:19:44 -03:00
{
2021-05-01 12:29:22 -04:00
{ " amount " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " The transaction fee rate in " + CURRENCY_UNIT + " /kvB " } ,
2018-12-21 14:29:36 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03:00
RPCResult : : Type : : BOOL , " " , " Returns true if successful "
2018-12-21 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " settxfee " , " 0.00001 " )
2013-12-13 12:06:32 -03:00
+ HelpExampleRpc ( " settxfee " , " 0.00001 " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03: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 05:46:17 -03:00
2016-09-22 04:46:41 -03:00
CAmount nAmount = AmountFromValue ( request . params [ 0 ] ) ;
2018-08-08 11:52:42 -04:00
CFeeRate tx_fee_rate ( nAmount , 1000 ) ;
2020-03-30 16:59:04 -03: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-08 11:52:42 -04:00
// automatic selection
2019-03-06 18:47:57 -03: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-08 11:52:42 -04: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 16:59:04 -03: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-08 11:52:42 -04:00
}
2013-12-13 12:06:32 -03:00
2018-08-08 11:52:42 -04:00
pwallet - > m_pay_tx_fee = tx_fee_rate ;
2013-12-13 12:06:32 -03:00
return true ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2013-12-13 12:06:32 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan getwalletinfo ( )
2014-02-21 01:56:04 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " getwalletinfo " ,
2018-12-21 14:29:36 -03:00
" Returns an object containing various wallet state info. \n " ,
{ } ,
RPCResult {
2020-01-09 14:00:57 -03:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{
{ RPCResult : : Type : : STR , " walletname " , " the wallet name " } ,
{ RPCResult : : Type : : NUM , " walletversion " , " the wallet version " } ,
2020-10-11 19:19:44 -03:00
{ RPCResult : : Type : : STR , " format " , " the database format (bdb or sqlite) " } ,
2020-01-09 14:00:57 -03: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 13:55:15 -04: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-09 14:00:57 -03:00
{ RPCResult : : Type : : NUM , " keypoolsize " , " how many new keys are pre-generated (only counts external keys) " } ,
2021-08-25 13:55:15 -04: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 13:27:48 -04: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 15:07:16 -03:00
{ RPCResult : : Type : : STR_AMOUNT , " paytxfee " , " the transaction fee configuration, set in " + CURRENCY_UNIT + " /kvB " } ,
2020-01-09 14:00:57 -03: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-09 14:00:57 -03:00
} } ,
2018-12-21 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " getwalletinfo " , " " )
2014-02-21 01:56:04 -03:00
+ HelpExampleRpc ( " getwalletinfo " , " " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 03:39:04 -04:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 11:44:40 -03:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-09-12 14:05:28 -03: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 05:46:17 -03:00
2015-05-10 09:48:35 -03:00
UniValue obj ( UniValue : : VOBJ ) ;
2017-01-26 16:52:57 -03:00
2017-03-28 04:18:20 -03:00
size_t kpExternalSize = pwallet - > KeypoolCountExternalKeys ( ) ;
2021-02-12 20:01:22 -03:00
const auto bal = GetBalance ( * pwallet ) ;
2017-09-22 15:04:07 -03:00
obj . pushKV ( " walletname " , pwallet - > GetName ( ) ) ;
obj . pushKV ( " walletversion " , pwallet - > GetVersion ( ) ) ;
2020-10-11 19:19:44 -03:00
obj . pushKV ( " format " , pwallet - > GetDatabase ( ) . Format ( ) ) ;
2019-03-11 17:12:58 -03: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 15:04:07 -03:00
obj . pushKV ( " txcount " , ( int ) pwallet - > mapWallet . size ( ) ) ;
2021-10-30 16: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 15:04:07 -03:00
obj . pushKV ( " keypoolsize " , ( int64_t ) kpExternalSize ) ;
2019-10-07 15:11:34 -03: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 11:44:51 -03:00
if ( pwallet - > CanSupportFeature ( FEATURE_HD_SPLIT ) ) {
2017-09-22 15:04:07 -03:00
obj . pushKV ( " keypoolsize_hd_internal " , ( int64_t ) ( pwallet - > GetKeyPoolSize ( ) - kpExternalSize ) ) ;
2017-01-10 12:45:30 -03:00
}
2016-10-25 05:04:23 -03:00
if ( pwallet - > IsCrypted ( ) ) {
2017-09-22 15:04:07 -03:00
obj . pushKV ( " unlocked_until " , pwallet - > nRelockTime ) ;
2016-10-25 05:04:23 -03:00
}
2018-04-07 13:12:46 -03:00
obj . pushKV ( " paytxfee " , ValueFromAmount ( pwallet - > m_pay_tx_fee . GetFeePerK ( ) ) ) ;
2017-05-05 03:53:39 -03:00
obj . pushKV ( " private_keys_enabled " , ! pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ) ;
2018-09-11 03:53:36 -03:00
obj . pushKV ( " avoid_reuse " , pwallet - > IsWalletFlagSet ( WALLET_FLAG_AVOID_REUSE ) ) ;
2019-04-03 12:56:58 -03: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 01:56:04 -03:00
return obj ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2014-02-21 01:56:04 -03:00
}
2015-03-23 14:47:18 -03:00
2020-09-22 13:13:52 -03:00
static RPCHelpMan listwalletdir ( )
2018-09-24 19:55:30 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " listwalletdir " ,
2018-12-21 14:29:36 -03:00
" Returns a list of wallets in the wallet directory. \n " ,
{ } ,
RPCResult {
2020-01-09 14:00:57 -03:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : ARR , " wallets " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " name " , " The wallet name " } ,
} } ,
} } ,
}
2018-12-21 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " listwalletdir " , " " )
2018-09-24 19:55:30 -03:00
+ HelpExampleRpc ( " listwalletdir " , " " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-09-24 19:55:30 -03:00
UniValue wallets ( UniValue : : VARR ) ;
2020-10-30 17:25:56 -03:00
for ( const auto & path : ListDatabases ( GetWalletDir ( ) ) ) {
2018-09-24 19:55:30 -03:00
UniValue wallet ( UniValue : : VOBJ ) ;
2021-09-10 01:17:20 -03:00
wallet . pushKV ( " name " , path . u8string ( ) ) ;
2018-09-24 19:55:30 -03:00
wallets . push_back ( wallet ) ;
}
UniValue result ( UniValue : : VOBJ ) ;
result . pushKV ( " wallets " , wallets ) ;
return result ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2018-09-24 19:55:30 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan listwallets ( )
2017-06-27 09:46:55 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " listwallets " ,
2018-10-20 09:19:44 -03:00
" Returns a list of currently loaded wallets. \n "
" For full information on the wallet, use \" getwalletinfo \" \n " ,
2018-12-21 14:29:36 -03:00
{ } ,
RPCResult {
2020-01-09 14:00:57 -03:00
RPCResult : : Type : : ARR , " " , " " ,
{
{ RPCResult : : Type : : STR , " walletname " , " the wallet name " } ,
}
2018-12-21 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " listwallets " , " " )
2017-06-27 09:46:55 -04:00
+ HelpExampleRpc ( " listwallets " , " " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03: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 11:18:07 -04:00
LOCK ( wallet - > cs_wallet ) ;
obj . push_back ( wallet - > GetName ( ) ) ;
2017-06-27 09:46:55 -04:00
}
return obj ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2017-06-27 09:46:55 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan loadwallet ( )
2018-04-18 17:01:39 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " loadwallet " ,
2018-10-20 09:19:44 -03: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 00:32:07 -03:00
" \n applied to the new wallet. \n " ,
2018-10-20 09:19:44 -03:00
{
2018-12-10 18:56:51 -03:00
{ " filename " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The wallet directory or .dat file. " } ,
2021-05-11 11:49:49 -04: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 14:29:36 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " loadwallet " , " \" test.dat \" " )
2018-04-18 17:01:39 -03:00
+ HelpExampleRpc ( " loadwallet " , " \" test.dat \" " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03: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 17:01:39 -03:00
2021-07-27 13:28:23 -04:00
auto [ wallet , warnings ] = LoadWalletHelper ( context , request . params [ 1 ] , name ) ;
2018-04-18 17:01:39 -03:00
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " name " , wallet - > GetName ( ) ) ;
2020-05-10 14:28:29 -04:00
obj . pushKV ( " warning " , Join ( warnings , Untranslated ( " \n " ) ) . original ) ;
2018-04-18 17:01:39 -03:00
return obj ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2018-04-18 17:01:39 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan setwalletflag ( )
2018-09-13 01:35:10 -03:00
{
2019-06-19 00:59:11 -04:00
std : : string flags = " " ;
for ( auto & it : WALLET_FLAG_MAP )
if ( it . second & MUTABLE_WALLET_FLAGS )
flags + = ( flags = = " " ? " " : " , " ) + it . first ;
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " setwalletflag " ,
2018-09-13 01:35:10 -03: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 10:01:00 -04:00
{ " value " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " The new state. " } ,
2018-09-13 01:35:10 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 01:35:10 -03:00
} ,
RPCExamples {
HelpExampleCli ( " setwalletflag " , " avoid_reuse " )
+ HelpExampleRpc ( " setwalletflag " , " \" avoid_reuse \" " )
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2018-09-13 01:35:10 -03: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 13:13:52 -03:00
} ,
} ;
2018-09-13 01:35:10 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan createwallet ( )
2018-04-23 13:44:22 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan {
2018-12-19 18:30:49 -03: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 10:01:00 -04: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 06:05:51 -04:00
{ " passphrase " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Encrypt the wallet with this passphrase. " } ,
2021-04-14 10:01:00 -04: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 19:05:44 -03: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-11 11:49:49 -04: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 10:01:00 -04: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 18:30:49 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 18:30:49 -03:00
} ,
RPCExamples {
HelpExampleCli ( " createwallet " , " \" testwallet \" " )
2018-04-23 14:14:34 -03:00
+ HelpExampleRpc ( " createwallet " , " \" testwallet \" " )
2021-02-26 11:03:50 -03: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 18:30:49 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2020-05-28 02:13:19 -04:00
WalletContext & context = EnsureWalletContext ( request . context ) ;
2019-02-06 23:26:55 -03: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 18:30:49 -03: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 18:30:49 -03: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 18:30:49 -03:00
}
2018-06-13 14:35:41 -04:00
}
2018-09-11 03:53:36 -03:00
if ( ! request . params [ 4 ] . isNull ( ) & & request . params [ 4 ] . get_bool ( ) ) {
flags | = WALLET_FLAG_AVOID_REUSE ;
}
2021-09-16 19:05:44 -03:00
if ( request . params [ 5 ] . isNull ( ) | | request . params [ 5 ] . get_bool ( ) ) {
2020-10-15 17:20:00 -03: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 11:56:17 -04:00
if ( ! request . params [ 7 ] . isNull ( ) & & request . params [ 7 ] . get_bool ( ) ) {
# ifdef ENABLE_EXTERNAL_SIGNER
flags | = WALLET_FLAG_EXTERNAL_SIGNER ;
# else
2021-04-13 03:08:33 -04:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Compiled without external signing support (required for external signing) " ) ;
2019-08-04 11:56:17 -04:00
# endif
}
2018-09-11 03:53:36 -03:00
2020-10-19 18:34:20 -03: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-14 23:41:30 -03:00
std : : optional < bool > load_on_start = request . params [ 6 ] . isNull ( ) ? std : : nullopt : std : : optional < bool > ( request . params [ 6 ] . get_bool ( ) ) ;
2021-08-25 03:37:14 -04: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 18:30:49 -03:00
}
2018-04-23 13:44:22 -03:00
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " name " , wallet - > GetName ( ) ) ;
2020-05-10 14:28:29 -04:00
obj . pushKV ( " warning " , Join ( warnings , Untranslated ( " \n " ) ) . original ) ;
2018-04-23 13:44:22 -03:00
return obj ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2018-04-23 13:44:22 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan unloadwallet ( )
2018-04-28 18:36:43 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " unloadwallet " ,
2018-10-20 09:19:44 -03: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 10:01:00 -04: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-11 11:49:49 -04: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 14:29:36 -03: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 14:29:36 -03:00
RPCExamples {
HelpExampleCli ( " unloadwallet " , " wallet_name " )
2018-04-28 18:36:43 -03:00
+ HelpExampleRpc ( " unloadwallet " , " wallet_name " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-04-28 18:36:43 -03:00
std : : string wallet_name ;
if ( GetWalletNameFromJSONRPCRequest ( request , wallet_name ) ) {
2020-11-21 15:57:22 -03: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 18:36:43 -03: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 18:36:43 -03: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-14 23:41:30 -03: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 18:36:43 -03:00
throw JSONRPCError ( RPC_MISC_ERROR , " Requested wallet already unloaded " ) ;
}
2018-12-12 20:21:19 -03:00
UnloadWallet ( std : : move ( wallet ) ) ;
2018-04-28 18:36:43 -03: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 13:13:52 -03:00
} ,
} ;
2018-04-28 18:36:43 -03:00
}
2021-10-04 09:25:23 -03: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 08:24:38 -03:00
{ " replaceable " , RPCArg : : Type : : BOOL , RPCArg : : DefaultHint { " wallet default " } , " Marks this transaction as BIP125-replaceable. \n "
2021-10-04 09:25:23 -03: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 12:20:13 -03: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 22:27:30 -03:00
{
2017-09-12 14:05:28 -03: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 12:20:13 -03:00
wallet . BlockUntilSyncedToCurrentChain ( ) ;
2017-09-12 14:05:28 -03:00
2018-06-27 20:02:07 -04:00
change_position = - 1 ;
2016-04-06 11:56:14 -03:00
bool lockUnspents = false ;
2016-12-13 18:36:23 -03:00
UniValue subtractFeeFromOutputs ;
2017-01-26 22:33:45 -03:00
std : : set < int > setSubtractFeeFromOutputs ;
2016-03-29 22:04:22 -03:00
2018-06-27 20:02:07 -04:00
if ( ! options . isNull ( ) ) {
if ( options . type ( ) = = UniValue : : VBOOL ) {
2016-03-29 22:04:22 -03:00
// backward compatibility bool only fallback
2018-06-27 20:02:07 -04:00
coinControl . fAllowWatchOnly = options . get_bool ( ) ;
2016-03-29 22:04:22 -03:00
}
else {
2018-06-27 20:02:07 -04:00
RPCTypeCheckArgument ( options , UniValue : : VOBJ ) ;
2016-06-06 11:50:50 -04:00
RPCTypeCheckObj ( options ,
{
2019-07-12 08:30:10 -04:00
{ " add_inputs " , UniValueType ( UniValue : : VBOOL ) } ,
2021-03-10 11:37:18 -03:00
{ " include_unsafe " , UniValueType ( UniValue : : VBOOL ) } ,
2020-08-07 11:36:36 -04:00
{ " add_to_wallet " , UniValueType ( UniValue : : VBOOL ) } ,
2016-06-06 11:50:50 -04:00
{ " changeAddress " , UniValueType ( UniValue : : VSTR ) } ,
2020-03-02 10:01:27 -03:00
{ " change_address " , UniValueType ( UniValue : : VSTR ) } ,
2016-06-06 11:50:50 -04:00
{ " changePosition " , UniValueType ( UniValue : : VNUM ) } ,
2020-03-02 10:01:27 -03:00
{ " change_position " , UniValueType ( UniValue : : VNUM ) } ,
2018-01-15 21:20:17 -03:00
{ " change_type " , UniValueType ( UniValue : : VSTR ) } ,
2016-06-06 11:50:50 -04:00
{ " includeWatching " , UniValueType ( UniValue : : VBOOL ) } ,
2020-03-02 10:01:27 -03:00
{ " include_watching " , UniValueType ( UniValue : : VBOOL ) } ,
2020-08-07 11:36:36 -04:00
{ " inputs " , UniValueType ( UniValue : : VARR ) } ,
2016-06-06 11:50:50 -04:00
{ " lockUnspents " , UniValueType ( UniValue : : VBOOL ) } ,
2020-03-02 10:01:27 -03:00
{ " lock_unspents " , UniValueType ( UniValue : : VBOOL ) } ,
2020-08-07 11:36:36 -04:00
{ " locktime " , UniValueType ( UniValue : : VNUM ) } ,
2020-11-04 09:13:17 -03:00
{ " fee_rate " , UniValueType ( ) } , // will be checked by AmountFromValue() in SetFeeEstimateMode()
{ " feeRate " , UniValueType ( ) } , // will be checked by AmountFromValue() below
2020-08-07 11:36:36 -04:00
{ " psbt " , UniValueType ( UniValue : : VBOOL ) } ,
2019-10-18 20:43:01 -03:00
{ " solving_data " , UniValueType ( UniValue : : VOBJ ) } ,
2016-12-13 18:36:23 -03:00
{ " subtractFeeFromOutputs " , UniValueType ( UniValue : : VARR ) } ,
2020-03-02 10:01:27 -03: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 11:50:50 -04:00
} ,
true , true ) ;
2016-03-29 22:04:22 -03:00
2019-07-12 08:30:10 -04:00
if ( options . exists ( " add_inputs " ) ) {
coinControl . m_add_inputs = options [ " add_inputs " ] . get_bool ( ) ;
}
2020-03-02 10:01:27 -03: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-29 22:04:22 -03:00
2017-08-22 22:02:33 -03:00
if ( ! IsValidDestination ( dest ) ) {
2020-03-02 10:01:27 -03:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Change address must be a valid bitcoin address " ) ;
2017-08-22 22:02:33 -03:00
}
2016-03-29 22:04:22 -03:00
2017-08-22 22:02:33 -03:00
coinControl . destChange = dest ;
2016-03-29 22:04:22 -03:00
}
2020-03-02 10:01:27 -03:00
if ( options . exists ( " changePosition " ) | | options . exists ( " change_position " ) ) {
change_position = ( options . exists ( " change_position " ) ? options [ " change_position " ] : options [ " changePosition " ] ) . get_int ( ) ;
}
2016-03-29 22:04:22 -03:00
2018-01-15 21:20:17 -03:00
if ( options . exists ( " change_type " ) ) {
2020-03-02 10:01:27 -03: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-15 21:20:17 -03:00
}
2021-08-04 01:38:36 -04:00
if ( std : : optional < OutputType > parsed = ParseOutputType ( options [ " change_type " ] . get_str ( ) ) ) {
coinControl . m_change_type . emplace ( parsed . value ( ) ) ;
} else {
2018-01-15 21:20:17 -03:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " Unknown change type '%s' " , options [ " change_type " ] . get_str ( ) ) ) ;
}
}
2020-03-02 10:01:27 -03:00
const UniValue include_watching_option = options . exists ( " include_watching " ) ? options [ " include_watching " ] : options [ " includeWatching " ] ;
2021-03-04 12:20:13 -03:00
coinControl . fAllowWatchOnly = ParseIncludeWatchonly ( include_watching_option , wallet ) ;
2016-04-06 11:56:14 -03:00
2020-03-02 10:01:27 -03:00
if ( options . exists ( " lockUnspents " ) | | options . exists ( " lock_unspents " ) ) {
lockUnspents = ( options . exists ( " lock_unspents " ) ? options [ " lock_unspents " ] : options [ " lockUnspents " ] ) . get_bool ( ) ;
}
2016-04-28 17:04:07 -03:00
2021-03-10 11:37:18 -03:00
if ( options . exists ( " include_unsafe " ) ) {
coinControl . m_include_unsafe_inputs = options [ " include_unsafe " ] . get_bool ( ) ;
}
2020-11-04 09:13:17 -03: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-03 23:26:42 -03:00
if ( options . exists ( " conf_target " ) ) {
2020-11-04 09:13:17 -03: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-03 23:26:42 -03:00
}
if ( options . exists ( " estimate_mode " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify both estimate_mode and feeRate " ) ;
}
2020-11-19 14:38:00 -03:00
coinControl . m_feerate = CFeeRate ( AmountFromValue ( options [ " feeRate " ] ) ) ;
2017-02-02 19:33:24 -03:00
coinControl . fOverrideFeeRate = true ;
2016-05-06 06:01:50 -03:00
}
2016-12-13 18:36:23 -03:00
2020-03-02 10:01:27 -03: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 19:36:50 -03:00
2017-06-28 17:53:06 -04:00
if ( options . exists ( " replaceable " ) ) {
2018-04-07 13:12:46 -03:00
coinControl . m_signal_bip125_rbf = options [ " replaceable " ] . get_bool ( ) ;
2017-02-02 19:36:50 -03:00
}
2021-03-04 12:20:13 -03:00
SetFeeEstimateMode ( wallet , coinControl , options [ " conf_target " ] , options [ " estimate_mode " ] , options [ " fee_rate " ] , override_min_fee ) ;
2016-03-29 22:04:22 -03:00
}
2019-07-13 11:34:49 -04:00
} else {
// if options is null and not a bool
2021-03-04 12:20:13 -03:00
coinControl . fAllowWatchOnly = ParseIncludeWatchonly ( NullUniValue , wallet ) ;
2016-03-29 22:04:22 -03:00
}
2015-04-24 22:27:30 -03:00
2019-10-18 20:43:01 -03:00
if ( options . exists ( " solving_data " ) ) {
2021-10-05 10:17:22 -03:00
const UniValue solving_data = options [ " solving_data " ] . get_obj ( ) ;
2019-10-18 20:43:01 -03: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 10:17:22 -03:00
const CPubKey pubkey ( data . begin ( ) , data . end ( ) ) ;
2019-10-18 20:43:01 -03: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 19:50:20 -03:00
if ( tx . vout . size ( ) = = 0 )
2015-11-13 17:52:07 -03:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " TX must have at least one output " ) ;
2018-06-27 20:02:07 -04:00
if ( change_position ! = - 1 & & ( change_position < 0 | | ( unsigned int ) change_position > tx . vout . size ( ) ) )
2016-03-29 22:04:22 -03:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " changePosition out of bounds " ) ;
2015-04-24 01:42:49 -03:00
2016-12-13 18:36:23 -03: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 20:43:01 -03: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 10:17:22 -03:00
coinControl . SelectExternal ( coin . first , coin . second . out ) ;
2019-10-18 20:43:01 -03:00
}
}
2020-04-18 15:18:17 -04:00
bilingual_str error ;
2016-03-29 22:04:22 -03:00
2021-02-12 20:01:22 -03: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 05:04:23 -03:00
}
2018-06-27 20:02:07 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan fundrawtransaction ( )
2018-06-27 20:02:07 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " fundrawtransaction " ,
2020-02-20 12:19:59 -03: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 09:19:44 -03: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 18:25:28 -03:00
" The inputs added will not be signed, use signrawtransactionwithkey \n "
2019-10-18 20:43:01 -03: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 09:19:44 -03: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 18:56:51 -03: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 09:25:23 -03:00
Cat < std : : vector < RPCArg > > (
2018-10-20 09:19:44 -03:00
{
2021-04-14 10:01:00 -04: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 11:37:18 -03: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 10:01:00 -04: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 10:47:35 -04: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 10:01:00 -04: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 04:20:37 -03: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 13:21:38 -03:00
{
2018-12-10 18:56:51 -03:00
{ " vout_index " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED , " The zero-based output index, before a change output is added. " } ,
2018-11-23 13:21:38 -03:00
} ,
} ,
} ,
2021-10-04 09:25:23 -03:00
FundTxDoc ( ) ) ,
2018-11-23 13:21:38 -03:00
" options " } ,
2021-04-14 10:01:00 -04: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 14:29:36 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 14:29:36 -03:00
} ,
RPCExamples {
2018-06-27 20:02:07 -04: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 18:25:28 -03:00
+ HelpExampleCli ( " signrawtransactionwithwallet " , " \" fundedtransactionhex \" " ) +
2018-06-27 20:02:07 -04:00
" \n Send the transaction \n "
+ HelpExampleCli ( " sendrawtransaction " , " \" signedtransactionhex \" " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2018-06-27 20:02:07 -04: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 08:30:10 -04:00
CCoinControl coin_control ;
2020-07-11 07:56:13 -04:00
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
2020-02-20 12:19:59 -03:00
coin_control . m_add_inputs = true ;
2021-03-04 12:20:13 -03:00
FundTransaction ( * pwallet , tx , fee , change_position , request . params [ 1 ] , coin_control , /* override_min_fee */ true ) ;
2015-04-24 22:27:30 -03:00
UniValue result ( UniValue : : VOBJ ) ;
2018-12-10 03:03:07 -03:00
result . pushKV ( " hex " , EncodeHexTx ( CTransaction ( tx ) ) ) ;
2018-06-27 20:02:07 -04:00
result . pushKV ( " fee " , ValueFromAmount ( fee ) ) ;
result . pushKV ( " changepos " , change_position ) ;
2015-04-24 22:27:30 -03:00
return result ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2015-04-24 22:27:30 -03:00
}
2016-01-07 04:33:49 -03:00
2020-09-22 13:13:52 -03:00
RPCHelpMan signrawtransactionwithwallet ( )
2017-06-12 15:23:02 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " signrawtransactionwithwallet " ,
2018-10-20 09:19:44 -03: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 13:21:38 -03:00
" this transaction depends on but may not yet be in the block chain. " +
2020-03-31 23:31:43 -03:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03:00
{ " hexstring " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The transaction hex string " } ,
2020-03-05 07:36:27 -03:00
{ " prevtxs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The previous dependent transaction outputs " ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03: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 00:57:18 -03: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 13:39:39 -03:00
{ " amount " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : OMITTED , " (required for Segwit inputs) the amount spent " } ,
2018-10-23 16:22:28 -03:00
} ,
2018-11-23 13:21:38 -03:00
} ,
2018-10-23 16:22:28 -03:00
} ,
2018-11-23 13:21:38 -03:00
} ,
2021-03-04 19:27:20 -03:00
{ " sighashtype " , RPCArg : : Type : : STR , RPCArg : : Default { " DEFAULT " } , " The signature hash type. Must be one of \n "
" \" DEFAULT \" \n "
2017-06-12 15:23:02 -04:00
" \" ALL \" \n "
" \" NONE \" \n "
" \" SINGLE \" \n "
" \" ALL|ANYONECANPAY \" \n "
" \" NONE|ANYONECANPAY \" \n "
2018-11-23 13:21:38 -03:00
" \" SINGLE|ANYONECANPAY \" " } ,
2018-12-21 14:29:36 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 12:35:18 -04:00
{ RPCResult : : Type : : ARR , " errors " , /* optional */ true , " Script verification errors (if there are any) " ,
2020-01-09 14:00:57 -03: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 13:55:15 -04:00
{ RPCResult : : Type : : ARR , " witness " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " witness " , " " } ,
} } ,
2020-01-09 14:00:57 -03: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 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " signrawtransactionwithwallet " , " \" myhex \" " )
2017-06-12 15:23:02 -04:00
+ HelpExampleRpc ( " signrawtransactionwithwallet " , " \" myhex \" " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 03:39:04 -04:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 11:44:40 -03:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2017-06-12 15:23:02 -04:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VARR , UniValue : : VSTR } , true ) ;
CMutableTransaction mtx ;
2020-08-30 04:28:42 -04:00
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_str ( ) ) ) {
2020-10-13 10:21:03 -03:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed. Make sure the tx has at least one input. " ) ;
2017-06-12 15:23:02 -04:00
}
// Sign the transaction
2017-07-26 10:23:01 -04:00
LOCK ( pwallet - > cs_wallet ) ;
2021-03-04 12:20:13 -03:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2018-09-24 12:10:23 -03:00
2019-04-10 09:38:41 -04: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 21:50:52 -03:00
int nHashType = ParseSighashString ( request . params [ 2 ] ) ;
2019-10-07 15:11:34 -03:00
2020-02-10 21:50:52 -03:00
// Script verification errors
2021-06-23 17:28:54 -04:00
std : : map < int , bilingual_str > input_errors ;
2020-02-10 21:50:52 -03:00
bool complete = pwallet - > SignTransaction ( mtx , coins , nHashType , input_errors ) ;
2019-11-18 16:56:52 -03:00
UniValue result ( UniValue : : VOBJ ) ;
2020-02-10 21:50:52 -03:00
SignTransactionResultToJSON ( mtx , complete , coins , input_errors , result ) ;
return result ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2017-06-12 15:23:02 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan bumpfee_helper ( std : : string method_name )
2016-12-09 15:45:27 -03:00
{
2021-03-29 06:43:21 -03:00
const bool want_psbt = method_name = = " psbtbumpfee " ;
2020-11-19 13:31:07 -03: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 13:13:52 -03: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 09:13:17 -03: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-26 23:53:17 -04: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 08:14:17 -03: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 08:14:17 -03: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 09:19:44 -03:00
{
2021-04-14 10:01:00 -04: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 09:13:17 -03:00
" \n Specify a fee rate in " + CURRENCY_ATOM + " /vB instead of relying on the built-in fee estimator. \n "
2020-11-19 13:31:07 -03:00
" Must be at least " + incremental_fee + " higher than the current transaction fee rate. \n "
2020-11-09 08:14:17 -03: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 10:01:00 -04:00
{ " replaceable " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Whether the new transaction should still be \n "
2020-09-30 04:20:37 -03: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 09:13:17 -03:00
" are replaceable). \n " } ,
2021-04-14 10:01:00 -04:00
{ " estimate_mode " , RPCArg : : Type : : STR , RPCArg : : Default { " unset " } , " The fee estimate mode, must be one of (case insensitive): \n "
2021-03-29 06:43:21 -03:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2018-12-21 14:29:36 -03:00
} ,
2020-06-16 17:17:40 -04:00
" options " } ,
} ,
RPCResult {
2021-05-21 15:57:49 -04: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 15:57:49 -04:00
" \n Bump the fee, get the new transaction \' s " + std : : string ( want_psbt ? " psbt " : " txid " ) + " \n " +
2020-09-22 13:13:52 -03:00
HelpExampleCli ( method_name , " <txid> " )
2020-06-16 17:17:40 -04:00
} ,
2021-03-29 06:43:21 -03:00
[ want_psbt ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
2020-09-22 13:13:52 -03:00
{
2021-03-04 11:44:40 -03: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 20:12:57 -03: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 15:15:28 -04:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VOBJ } ) ;
2018-06-08 14:16:07 -04:00
uint256 hash ( ParseHashV ( request . params [ 0 ] , " txid " ) ) ;
2016-12-09 15:45:27 -03:00
2019-11-20 17:01:30 -03:00
CCoinControl coin_control ;
coin_control . fAllowWatchOnly = pwallet - > IsWalletFlagSet ( WALLET_FLAG_DISABLE_PRIVATE_KEYS ) ;
2017-02-02 18:03:55 -03:00
// optional parameters
2018-04-07 13:12:46 -03:00
coin_control . m_signal_bip125_rbf = true ;
2019-11-20 17:01:30 -03:00
2017-07-10 11:44:39 -04:00
if ( ! request . params [ 1 ] . isNull ( ) ) {
2017-02-02 18:03:55 -03:00
UniValue options = request . params [ 1 ] ;
RPCTypeCheckObj ( options ,
{
{ " confTarget " , UniValueType ( UniValue : : VNUM ) } ,
2019-08-14 11:46:13 -04:00
{ " conf_target " , UniValueType ( UniValue : : VNUM ) } ,
2020-11-04 09:13:17 -03:00
{ " fee_rate " , UniValueType ( ) } , // will be checked by AmountFromValue() in SetFeeEstimateMode()
2017-02-02 18:03:55 -03:00
{ " replaceable " , UniValueType ( UniValue : : VBOOL ) } ,
2017-06-14 15:15:40 -04:00
{ " estimate_mode " , UniValueType ( UniValue : : VSTR ) } ,
2017-02-02 18:03:55 -03:00
} ,
true , true ) ;
2019-08-14 11:46:13 -04: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 18:03:55 -03:00
if ( options . exists ( " replaceable " ) ) {
2018-04-07 13:12:46 -03:00
coin_control . m_signal_bip125_rbf = options [ " replaceable " ] . get_bool ( ) ;
2017-02-02 18:03:55 -03:00
}
2020-11-17 16:22:01 -03:00
SetFeeEstimateMode ( * pwallet , coin_control , conf_target , options [ " estimate_mode " ] , options [ " fee_rate " ] , /* override_min_fee */ false ) ;
2017-02-02 18:03:55 -03:00
}
2017-09-12 14:05:28 -03: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 12:20:13 -03:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2016-12-09 15:45:27 -03: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 18:30:00 -03:00
feebumper : : Result res ;
2020-03-10 14:40:23 -03: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 12:15:47 -03:00
switch ( res ) {
2017-09-20 17:19:30 -03: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 12:15:47 -03:00
break ;
2017-09-20 17:19:30 -03: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 12:15:47 -03:00
break ;
2017-09-20 17:19:30 -03: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 12:15:47 -03:00
break ;
2017-09-20 17:19:30 -03: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 12:15:47 -03:00
break ;
default :
2020-04-18 15:18:17 -04:00
throw JSONRPCError ( RPC_MISC_ERROR , errors [ 0 ] . original ) ;
2017-03-03 12:15:47 -03:00
break ;
2016-12-09 15:45:27 -03:00
}
}
UniValue result ( UniValue : : VOBJ ) ;
2019-11-20 17:18:34 -03:00
2021-05-22 03:30:26 -04: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 17:18:34 -03: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 17:18:34 -03:00
}
result . pushKV ( " txid " , txid . GetHex ( ) ) ;
} else {
PartiallySignedTransaction psbtx ( mtx ) ;
bool complete = false ;
2021-03-04 19:27:20 -03:00
const TransactionError err = pwallet - > FillPSBT ( psbtx , complete , SIGHASH_DEFAULT , false /* sign */ , true /* bip32derivs */ ) ;
2019-11-20 17:18:34 -03: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 15:04:07 -03: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 15:04:07 -03:00
result . pushKV ( " errors " , result_errors ) ;
2016-12-09 15:45:27 -03:00
return result ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2016-12-09 15:45:27 -03:00
}
2020-09-22 13:13:52 -03: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 11:36:36 -04:00
static RPCHelpMan send ( )
{
return RPCHelpMan { " send " ,
2020-09-17 10:06:39 -03:00
" \n EXPERIMENTAL warning: this call may be changed in future releases. \n "
2020-08-07 11:36:36 -04:00
" \n Send a transaction. \n " ,
{
2020-09-30 04:20:37 -03:00
{ " outputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The outputs (key-value pairs), where none of the keys are duplicated. \n "
2020-08-07 11:36:36 -04: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 06:03:41 -04:00
{ " " , RPCArg : : Type : : OBJ_USER_KEYS , RPCArg : : Optional : : OMITTED , " " ,
2020-08-07 11:36:36 -04: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 10:01:00 -04: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 11:36:36 -04:00
" \" " + FeeModes ( " \" \n \" " ) + " \" " } ,
2021-04-14 10:01:00 -04: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 11:36:36 -04:00
{ " options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " " ,
2021-10-04 09:25:23 -03:00
Cat < std : : vector < RPCArg > > (
2020-08-07 11:36:36 -04:00
{
2021-04-14 10:01:00 -04:00
{ " add_inputs " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " If inputs are specified, automatically include more if they are not enough. " } ,
2021-03-10 11:37:18 -03: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 10:01:00 -04: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 11:36:36 -04: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 10:01:00 -04: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 11:36:36 -04: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 10:01:00 -04: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 11:36:36 -04: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 09:25:23 -03:00
FundTxDoc ( ) ) ,
2020-08-07 11:36:36 -04:00
" options " } ,
} ,
RPCResult {
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : BOOL , " complete " , " If the transaction has a complete set of signatures " } ,
2021-08-25 13:55:15 -04: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 11:36:36 -04:00
}
} ,
RPCExamples { " "
2020-11-05 02:12:17 -03: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 16:08:30 -03: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 02:12:17 -03:00
" Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + " /vB using the options argument \n "
2020-11-17 16:08:30 -03:00
+ HelpExampleCli ( " send " , " '{ \" " + EXAMPLE_ADDRESS [ 0 ] + " \" : 0.2}' null \" unset \" null '{ \" fee_rate \" : 1}' \n " ) +
2020-11-05 02:12:17 -03: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 11:36:36 -04: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 09:13:17 -03:00
UniValueType ( ) , // outputs (ARR or OBJ, checked later)
UniValue : : VNUM , // conf_target
UniValue : : VSTR , // estimate_mode
2020-12-04 07:20:39 -03:00
UniValueType ( ) , // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
2020-11-04 09:13:17 -03:00
UniValue : : VOBJ , // options
2020-08-07 11:36:36 -04:00
} , true
) ;
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-08-07 11:36:36 -04:00
2020-11-04 09:13:17 -03:00
UniValue options { request . params [ 4 ] . isNull ( ) ? UniValue : : VOBJ : request . params [ 4 ] } ;
if ( options . exists ( " conf_target " ) | | options . exists ( " estimate_mode " ) ) {
2020-08-07 11:36:36 -04:00
if ( ! request . params [ 1 ] . isNull ( ) | | ! request . params [ 2 ] . isNull ( ) ) {
2020-11-04 09:13:17 -03: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 11:36:36 -04:00
}
} else {
options . pushKV ( " conf_target " , request . params [ 1 ] ) ;
options . pushKV ( " estimate_mode " , request . params [ 2 ] ) ;
}
2020-11-04 09:13:17 -03: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 11:36:36 -04: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 09:13:17 -03:00
if ( options . exists ( " feeRate " ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Use fee_rate ( " + CURRENCY_ATOM + " /vB) instead of feeRate " ) ;
}
2020-08-07 11:36:36 -04: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 15:57:51 -03:00
rbf = options [ " replaceable " ] . get_bool ( ) ;
2020-08-07 11:36:36 -04: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-26 23:53:17 -04:00
// be overridden by options.add_inputs.
2020-08-07 11:36:36 -04:00
coin_control . m_add_inputs = rawTx . vin . size ( ) = = 0 ;
2021-03-04 12:20:13 -03:00
FundTransaction ( * pwallet , rawTx , fee , change_position , options , coin_control , /* override_min_fee */ false ) ;
2020-08-07 11:36:36 -04: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 17:26:01 -04:00
// First fill transaction with our data without signing,
// so external signers are not asked sign more than once.
bool complete ;
2021-03-04 19:27:20 -03:00
pwallet - > FillPSBT ( psbtx , complete , SIGHASH_DEFAULT , false , true ) ;
const TransactionError err = pwallet - > FillPSBT ( psbtx , complete , SIGHASH_DEFAULT , true , false ) ;
2020-08-07 11:36:36 -04: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 10:28:59 -03:00
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
2020-08-07 11:36:36 -04: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 13:13:52 -03:00
static RPCHelpMan sethdseed ( )
2017-09-12 18:01:12 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " sethdseed " ,
2018-10-20 09:19:44 -03: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 13:21:38 -03:00
" \n Note that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed. " +
2020-03-31 23:31:43 -03:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 09:19:44 -03:00
{
2021-04-14 10:01:00 -04: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 04:20:37 -03: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 10:01:00 -04:00
{ " seed " , RPCArg : : Type : : STR , RPCArg : : DefaultHint { " random seed " } , " The WIF private key to use as the new HD seed. \n "
2020-09-30 04:20:37 -03:00
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1 " } ,
2018-12-21 14:29:36 -03:00
} ,
2020-03-13 15:40:53 -03:00
RPCResult { RPCResult : : Type : : NONE , " " , " " } ,
2018-12-21 14:29:36 -03:00
RPCExamples {
HelpExampleCli ( " sethdseed " , " " )
2017-09-12 18:01:12 -03:00
+ HelpExampleCli ( " sethdseed " , " false " )
+ HelpExampleCli ( " sethdseed " , " true \" wifkey \" " )
+ HelpExampleRpc ( " sethdseed " , " true, \" wifkey \" " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2019-10-07 15:11:34 -03:00
LegacyScriptPubKeyMan & spk_man = EnsureLegacyScriptPubKeyMan ( * pwallet , true ) ;
2019-10-07 15:11:34 -03:00
2019-01-25 16:38:34 -03: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 15:11:34 -03:00
LOCK2 ( pwallet - > cs_wallet , spk_man . cs_KeyStore ) ;
2017-09-12 18:01:12 -03:00
// Do not do anything to non-HD wallets
2019-02-06 23:26:55 -03:00
if ( ! pwallet - > CanSupportFeature ( FEATURE_HD ) ) {
2020-11-16 14:23:10 -03: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 18:01:12 -03:00
}
2021-03-04 12:20:13 -03:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2017-09-12 18:01:12 -03: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 12:13:43 -03:00
master_pub_key = spk_man . GenerateNewSeed ( ) ;
2017-09-12 18:01:12 -03: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 12:13:43 -03:00
if ( HaveKey ( spk_man , key ) ) {
2017-09-12 18:01:12 -03: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 12:13:43 -03:00
master_pub_key = spk_man . DeriveNewSeed ( key ) ;
2017-09-12 18:01:12 -03:00
}
2019-11-05 12:13:43 -03:00
spk_man . SetHDSeed ( master_pub_key ) ;
if ( flush_key_pool ) spk_man . NewKeyPool ( ) ;
2017-09-12 18:01:12 -03:00
return NullUniValue ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2017-09-12 18:01:12 -03:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan walletprocesspsbt ( )
2018-06-28 22:05:05 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " walletprocesspsbt " ,
2018-10-20 09:19:44 -03:00
" \n Update a PSBT with input information from our wallet and then sign inputs \n "
2018-11-23 13:21:38 -03:00
" that we can sign for. " +
2020-03-31 23:31:43 -03:00
HELP_REQUIRING_PASSPHRASE ,
2018-10-20 09:19:44 -03:00
{
2018-12-10 18:56:51 -03:00
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The transaction base64 string " } ,
2021-09-27 09:24:23 -03:00
{ " sign " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Also sign the transaction when updating (requires wallet to be unlocked) " } ,
2021-03-04 19:27:20 -03: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 22:05:05 -04:00
" \" ALL \" \n "
" \" NONE \" \n "
" \" SINGLE \" \n "
" \" ALL|ANYONECANPAY \" \n "
" \" NONE|ANYONECANPAY \" \n "
2018-11-23 13:21:38 -03:00
" \" SINGLE|ANYONECANPAY \" " } ,
2021-04-14 10:01:00 -04: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 14:29:36 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 14:29:36 -03:00
} ,
RPCExamples {
HelpExampleCli ( " walletprocesspsbt " , " \" psbt \" " )
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-08-25 03:39:04 -04:00
const std : : shared_ptr < const CWallet > pwallet = GetWalletForJSONRPCRequest ( request ) ;
2021-03-04 11:44:40 -03:00
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2021-06-22 04:08:41 -04: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 22:05:05 -04:00
// Unserialize the transaction
PartiallySignedTransaction psbtx ;
std : : string error ;
2019-01-30 02:32:38 -03:00
if ( ! DecodeBase64PSBT ( psbtx , request . params [ 0 ] . get_str ( ) , error ) ) {
2018-06-28 22:05:05 -04: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 07:03:38 -03: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-10 01:51:33 -03:00
bool complete = true ;
2021-09-27 09:24:23 -03: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 12:01:06 -03:00
if ( err ! = TransactionError : : OK ) {
2019-02-10 01:51:33 -03:00
throw JSONRPCTransactionError ( err ) ;
}
2018-06-28 22:05:05 -04:00
UniValue result ( UniValue : : VOBJ ) ;
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
2018-10-26 19:18:52 -03:00
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
2018-08-09 12:08:45 -04:00
result . pushKV ( " complete " , complete ) ;
2018-06-28 22:05:05 -04:00
return result ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2018-06-28 22:05:05 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan walletcreatefundedpsbt ( )
2018-06-28 22:05:05 -04:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " walletcreatefundedpsbt " ,
2019-07-12 08:30:10 -04:00
" \n Creates and funds a transaction in the Partially Signed Transaction format. \n "
2019-10-18 20:43:01 -03: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 16:22:28 -03:00
{
2019-07-11 13:50:45 -04:00
{ " inputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " Leave empty to add inputs automatically. See add_inputs option. " ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03: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 10:01:00 -04:00
{ " sequence " , RPCArg : : Type : : NUM , RPCArg : : DefaultHint { " depends on the value of the 'locktime' and 'options.replaceable' arguments " } , " The sequence number " } ,
2018-10-23 16:22:28 -03:00
} ,
2018-11-23 13:21:38 -03:00
} ,
} ,
2018-10-23 16:22:28 -03:00
} ,
2020-03-05 07:36:27 -03:00
{ " outputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The outputs (key-value pairs), where none of the keys are duplicated. \n "
2018-12-06 18:13:53 -03:00
" That is, each address can only appear once and there can only be one 'data' object. \n "
2018-11-23 13:21:38 -03:00
" For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also \n "
2020-09-30 04:20:37 -03:00
" accepted as second parameter. " ,
2018-10-23 16:22:28 -03:00
{
2021-05-11 06:03:41 -04:00
{ " " , RPCArg : : Type : : OBJ_USER_KEYS , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03: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 16:22:28 -03:00
} ,
} ,
2018-12-10 18:56:51 -03:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 16:22:28 -03:00
{
2018-12-10 18:56:51 -03: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 16:22:28 -03:00
} ,
2018-11-23 13:21:38 -03:00
} ,
2018-10-23 16:22:28 -03:00
} ,
2018-11-23 13:21:38 -03:00
} ,
2021-04-14 10:01:00 -04:00
{ " locktime " , RPCArg : : Type : : NUM , RPCArg : : Default { 0 } , " Raw locktime. Non-0 value also locktime-activates inputs " } ,
2018-12-10 18:56:51 -03:00
{ " options " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED_NAMED_ARG , " " ,
2021-10-04 09:25:23 -03:00
Cat < std : : vector < RPCArg > > (
2018-11-23 13:21:38 -03:00
{
2021-04-14 10:01:00 -04:00
{ " add_inputs " , RPCArg : : Type : : BOOL , RPCArg : : Default { false } , " If inputs are specified, automatically include more if they are not enough. " } ,
2021-03-10 11:37:18 -03: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 10:01:00 -04: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 04:20:37 -03: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 13:21:38 -03:00
{
2018-12-10 18:56:51 -03:00
{ " vout_index " , RPCArg : : Type : : NUM , RPCArg : : Optional : : OMITTED , " The zero-based output index, before a change output is added. " } ,
2018-11-23 13:21:38 -03:00
} ,
} ,
} ,
2021-10-04 09:25:23 -03:00
FundTxDoc ( ) ) ,
2018-11-23 13:21:38 -03:00
" options " } ,
2021-04-14 10:01:00 -04:00
{ " bip32derivs " , RPCArg : : Type : : BOOL , RPCArg : : Default { true } , " Include BIP 32 derivation paths for public keys if we know them " } ,
2018-12-21 14:29:36 -03:00
} ,
RPCResult {
2020-01-09 14:00:57 -03: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 14:29:36 -03:00
} ,
RPCExamples {
2018-06-28 22:05:05 -04:00
" \n Create a transaction with no inputs \n "
+ HelpExampleCli ( " walletcreatefundedpsbt " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" \" [{ \\ \" data \\ \" : \\ \" 00010203 \\ \" }] \" " )
2018-12-21 14:29:36 -03:00
} ,
2020-09-22 13:13:52 -03:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2021-06-22 04:08:41 -04: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 22:05:05 -04:00
RPCTypeCheck ( request . params , {
UniValue : : VARR ,
UniValueType ( ) , // ARR or OBJ, checked later
UniValue : : VNUM ,
2018-08-20 13:57:06 -03:00
UniValue : : VOBJ ,
UniValue : : VBOOL
2018-06-28 22:05:05 -04:00
} , true
) ;
CAmount fee ;
int change_position ;
2021-06-22 04:08:41 -04:00
bool rbf { wallet . m_signal_rbf } ;
2019-04-27 13:44:38 -04: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 08:30:10 -04:00
CCoinControl coin_control ;
// Automatically select coins, unless at least one is manually selected. Can
2020-07-11 07:56:13 -04:00
// be overridden by options.add_inputs.
2019-07-12 08:30:10 -04:00
coin_control . m_add_inputs = rawTx . vin . size ( ) = = 0 ;
2021-06-22 04:08:41 -04:00
FundTransaction ( wallet , rawTx , fee , change_position , request . params [ 3 ] , coin_control , /* override_min_fee */ true ) ;
2018-06-28 22:05:05 -04:00
// Make a blank psbt
2018-10-26 19:26:16 -03:00
PartiallySignedTransaction psbtx ( rawTx ) ;
2018-06-28 22:05:05 -04:00
// Fill transaction with out data but don't sign
2019-10-26 07:03:38 -03:00
bool bip32derivs = request . params [ 4 ] . isNull ( ) ? true : request . params [ 4 ] . get_bool ( ) ;
2019-02-10 01:51:33 -03:00
bool complete = true ;
2021-06-22 04:08:41 -04:00
const TransactionError err { wallet . FillPSBT ( psbtx , complete , 1 , false , bip32derivs ) } ;
2019-02-14 12:01:06 -03:00
if ( err ! = TransactionError : : OK ) {
2019-02-10 01:51:33 -03:00
throw JSONRPCTransactionError ( err ) ;
}
2018-06-28 22:05:05 -04:00
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
UniValue result ( UniValue : : VOBJ ) ;
2018-10-26 19:18:52 -03:00
result . pushKV ( " psbt " , EncodeBase64 ( ssTx . str ( ) ) ) ;
2018-06-28 22:05:05 -04:00
result . pushKV ( " fee " , ValueFromAmount ( fee ) ) ;
result . pushKV ( " changepos " , change_position ) ;
return result ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2018-06-28 22:05:05 -04:00
}
2020-09-22 13:13:52 -03:00
static RPCHelpMan upgradewallet ( )
2019-04-06 13:56:06 -03:00
{
2020-09-22 13:13:52 -03:00
return RPCHelpMan { " upgradewallet " ,
2020-11-16 14:23:10 -03:00
" \n Upgrade the wallet. Upgrades to the latest version if no version number is specified. \n "
2019-04-06 13:56:06 -03:00
" New keys may be generated and a new wallet backup will need to be made. " ,
{
2021-04-14 10:01:00 -04:00
{ " version " , RPCArg : : Type : : NUM , RPCArg : : Default { FEATURE_LATEST } , " The version number to upgrade to. Default is the latest wallet version. " }
2019-04-06 13:56:06 -03:00
} ,
2020-10-18 22:01:42 -03:00
RPCResult {
RPCResult : : Type : : OBJ , " " , " " ,
{
2020-11-16 14:23:10 -03: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 22:01:42 -03:00
{ RPCResult : : Type : : STR , " error " , /* optional */ true , " Error message (if there is one) " }
} ,
} ,
2019-04-06 13:56:06 -03:00
RPCExamples {
HelpExampleCli ( " upgradewallet " , " 169900 " )
+ HelpExampleRpc ( " upgradewallet " , " 169900 " )
2020-09-22 13:13:52 -03:00
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2021-03-04 11:44:40 -03:00
std : : shared_ptr < CWallet > const pwallet = GetWalletForJSONRPCRequest ( request ) ;
if ( ! pwallet ) return NullUniValue ;
2020-06-11 10:23:26 -04:00
2019-04-06 13:56:06 -03:00
RPCTypeCheck ( request . params , { UniValue : : VNUM } , true ) ;
2021-03-04 12:20:13 -03:00
EnsureWalletIsUnlocked ( * pwallet ) ;
2019-04-06 13:56:06 -03: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 14:23:10 -03: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 11:57:14 -03: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 13:56:06 -03:00
}
2020-11-16 14:23:10 -03:00
2020-10-18 22:01:42 -03:00
UniValue obj ( UniValue : : VOBJ ) ;
2020-11-16 14:23:10 -03: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 11:57:14 -03:00
} else {
CHECK_NONFATAL ( ! error . empty ( ) ) ;
2020-10-18 22:01:42 -03:00
obj . pushKV ( " error " , error . original ) ;
}
return obj ;
2020-09-22 13:13:52 -03:00
} ,
} ;
2019-04-06 13:56:06 -03:00
}
2020-08-14 05:22:05 -04: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 04:24:20 -03:00
RPCHelpMan listdescriptors ( ) ;
2021-11-26 09:48:32 -03:00
RPCHelpMan signmessage ( ) ;
2021-11-29 23:19:02 -03:00
RPCHelpMan backupwallet ( ) ;
RPCHelpMan restorewallet ( ) ;
2016-01-07 04:33:49 -03:00
2021-11-30 23:40:40 -03: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 00:09:30 -03:00
// coins
RPCHelpMan getreceivedbyaddress ( ) ;
RPCHelpMan getreceivedbylabel ( ) ;
RPCHelpMan getbalance ( ) ;
RPCHelpMan getunconfirmedbalance ( ) ;
RPCHelpMan lockunspent ( ) ;
RPCHelpMan listlockunspent ( ) ;
RPCHelpMan getbalances ( ) ;
RPCHelpMan listunspent ( ) ;
2021-11-30 22:50:19 -03:00
// encryption
RPCHelpMan walletpassphrase ( ) ;
RPCHelpMan walletpassphrasechange ( ) ;
RPCHelpMan walletlock ( ) ;
RPCHelpMan encryptwallet ( ) ;
2021-11-30 23:06:45 -03: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-05 12:21:33 -04:00
{
2018-08-20 09:19:43 -03:00
// clang-format off
2016-03-29 14:43:02 -03:00
static const CRPCCommand commands [ ] =
2021-01-12 02:41:46 -03:00
{ // category actor (function)
// ------------------ ------------------------
{ " rawtransactions " , & fundrawtransaction , } ,
{ " wallet " , & abandontransaction , } ,
{ " wallet " , & abortrescan , } ,
{ " wallet " , & addmultisigaddress , } ,
{ " wallet " , & backupwallet , } ,
{ " wallet " , & bumpfee , } ,
{ " wallet " , & psbtbumpfee , } ,
{ " wallet " , & createwallet , } ,
2021-07-27 13:28:23 -04:00
{ " wallet " , & restorewallet , } ,
2021-01-12 02:41:46 -03: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-25 19:36:59 -03:00
{ " wallet " , & newkeypool , } ,
2021-01-12 02:41:46 -03: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 10:17:39 -03:00
# ifdef ENABLE_EXTERNAL_SIGNER
{ " wallet " , & walletdisplayaddress , } ,
# endif // ENABLE_EXTERNAL_SIGNER
2021-01-12 02:41:46 -03:00
{ " wallet " , & walletlock , } ,
{ " wallet " , & walletpassphrase , } ,
{ " wallet " , & walletpassphrasechange , } ,
{ " wallet " , & walletprocesspsbt , } ,
2016-01-07 04:33:49 -03:00
} ;
2018-08-20 09:19:43 -03:00
// clang-format on
2021-11-02 11:07:46 -03:00
return commands ;
2016-01-07 04:33:49 -03:00
}