2012-05-31 16:01:16 -04:00
// Copyright (c) 2010 Satoshi Nakamoto
2020-04-16 13:14:08 -04:00
// Copyright (c) 2009-2020 The Bitcoin Core developers
2014-11-20 10:19:29 +08:00
// Distributed under the MIT software license, see the accompanying
2012-05-31 16:01:16 -04:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-10 13:57:53 +13:00
# include <chain.h>
# include <coins.h>
2019-02-25 14:54:22 -05:00
# include <consensus/validation.h>
2017-11-10 13:57:53 +13:00
# include <core_io.h>
2017-12-08 11:41:35 -08:00
# include <index/txindex.h>
2017-09-19 18:12:25 -07:00
# include <key_io.h>
2017-11-10 13:57:53 +13:00
# include <merkleblock.h>
2019-04-10 13:38:41 +00:00
# include <node/coin.h>
2019-09-17 18:28:03 -04:00
# include <node/context.h>
2019-04-08 16:33:05 -04:00
# include <node/psbt.h>
2019-01-08 22:16:50 -08:00
# include <node/transaction.h>
2019-08-02 02:03:01 +09:00
# include <policy/policy.h>
2017-11-10 13:57:53 +13:00
# include <policy/rbf.h>
# include <primitives/transaction.h>
2019-01-09 02:06:29 -08:00
# include <psbt.h>
2019-07-31 18:02:24 -04:00
# include <random.h>
2019-09-17 19:05:26 -04:00
# include <rpc/blockchain.h>
2019-04-02 16:51:32 -04:00
# include <rpc/rawtransaction_util.h>
2017-11-10 13:57:53 +13:00
# include <rpc/server.h>
2017-05-30 15:55:17 -04:00
# include <rpc/util.h>
2017-11-10 13:57:53 +13:00
# include <script/script.h>
# include <script/sign.h>
2019-06-06 22:52:24 +02:00
# include <script/signingprovider.h>
2017-11-10 13:57:53 +13:00
# include <script/standard.h>
# include <uint256.h>
2020-07-19 17:25:07 +02:00
# include <util/bip32.h>
2018-06-27 17:21:07 +09:00
# include <util/moneystr.h>
2018-10-22 15:51:11 -07:00
# include <util/strencodings.h>
2020-02-12 23:01:45 -05:00
# include <util/string.h>
2018-11-06 10:40:50 -05:00
# include <validation.h>
# include <validationinterface.h>
2012-05-31 16:01:16 -04:00
2018-07-31 17:58:01 -07:00
# include <numeric>
2013-04-13 00:13:08 -05:00
# include <stdint.h>
2015-09-04 16:11:34 +02:00
# include <univalue.h>
2015-05-18 14:02:18 +02:00
2018-05-02 17:14:48 +02:00
static void TxToJSON ( const CTransaction & tx , const uint256 hashBlock , UniValue & entry )
2012-05-31 16:01:16 -04:00
{
2016-09-21 20:51:00 -04:00
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
// Blockchain contextual information (confirmations and blocktime) is not
// available to code in bitcoin-common, so we query them here and push the
// data into the returned UniValue.
2017-08-11 12:21:14 -07:00
TxToUniv ( tx , uint256 ( ) , entry , true , RPCSerializationFlags ( ) ) ;
2012-05-31 16:01:16 -04:00
2014-12-15 09:11:16 +01:00
if ( ! hashBlock . IsNull ( ) ) {
2017-12-08 11:49:08 -08:00
LOCK ( cs_main ) ;
2017-09-22 20:04:07 +02:00
entry . pushKV ( " blockhash " , hashBlock . GetHex ( ) ) ;
2018-01-12 00:23:09 +00:00
CBlockIndex * pindex = LookupBlockIndex ( hashBlock ) ;
if ( pindex ) {
2019-03-27 11:14:25 -04:00
if ( : : ChainActive ( ) . Contains ( pindex ) ) {
entry . pushKV ( " confirmations " , 1 + : : ChainActive ( ) . Height ( ) - pindex - > nHeight ) ;
2017-09-22 20:04:07 +02:00
entry . pushKV ( " time " , pindex - > GetBlockTime ( ) ) ;
entry . pushKV ( " blocktime " , pindex - > GetBlockTime ( ) ) ;
2012-05-31 16:01:16 -04:00
}
else
2017-09-22 20:04:07 +02:00
entry . pushKV ( " confirmations " , 0 ) ;
2012-05-31 16:01:16 -04:00
}
}
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan getrawtransaction ( )
2012-05-31 16:01:16 -04:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan {
2019-02-02 19:48:54 -08:00
" getrawtransaction " ,
" \n Return the raw transaction data. \n "
2019-01-26 14:34:00 -08:00
" \n By default this function only works for mempool transactions. When called with a blockhash \n "
" argument, getrawtransaction will return the transaction if the specified block is available and \n "
" the transaction is found in that block. When called without a blockhash argument, getrawtransaction \n "
" will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction \n "
" is in a block in the blockchain. \n "
2017-01-11 15:30:50 -08:00
2019-02-25 23:49:20 -05:00
" \n Hint: Use gettransaction for wallet transactions. \n "
2019-02-02 19:48:54 -08:00
" \n If verbose is 'true', returns an Object with information about 'txid'. \n "
" If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'. \n " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " verbose " , RPCArg : : Type : : BOOL , /* default */ " false " , " If false, return a string, otherwise return a json object " } ,
{ " blockhash " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The block in which to look for the transaction " } ,
2018-12-21 12:29:36 -05:00
} ,
{
RPCResult { " if verbose is not set or set to false " ,
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " data " , " The serialized, hex-encoded data for 'txid' "
2018-12-21 12:29:36 -05:00
} ,
RPCResult { " if verbose is set to true " ,
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : BOOL , " in_active_chain " , " Whether specified block is in the active chain or not (only present with explicit \" blockhash \" argument) " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The serialized, hex-encoded data for 'txid' " } ,
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id (same as provided) " } ,
{ RPCResult : : Type : : STR_HEX , " hash " , " The transaction hash (differs from txid for witness transactions) " } ,
{ RPCResult : : Type : : NUM , " size " , " The serialized transaction size " } ,
{ RPCResult : : Type : : NUM , " vsize " , " The virtual transaction size (differs from size for witness transactions) " } ,
{ RPCResult : : Type : : NUM , " weight " , " The transaction's weight (between vsize*4-3 and vsize*4) " } ,
{ RPCResult : : Type : : NUM , " version " , " The version " } ,
{ RPCResult : : Type : : NUM_TIME , " locktime " , " The lock time " } ,
{ RPCResult : : Type : : ARR , " vin " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id " } ,
{ RPCResult : : Type : : STR , " vout " , " " } ,
{ RPCResult : : Type : : OBJ , " scriptSig " , " The script " ,
{
{ RPCResult : : Type : : STR , " asm " , " asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " hex " } ,
} } ,
{ RPCResult : : Type : : NUM , " sequence " , " The script sequence number " } ,
{ RPCResult : : Type : : ARR , " txinwitness " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " hex " , " hex-encoded witness data (if any) " } ,
} } ,
} } ,
} } ,
{ RPCResult : : Type : : ARR , " vout " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : NUM , " value " , " The value in " + CURRENCY_UNIT } ,
{ RPCResult : : Type : : NUM , " n " , " index " } ,
{ RPCResult : : Type : : OBJ , " scriptPubKey " , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " the asm " } ,
{ RPCResult : : Type : : STR , " hex " , " the hex " } ,
{ RPCResult : : Type : : NUM , " reqSigs " , " The required sigs " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
{ RPCResult : : Type : : ARR , " addresses " , " " ,
{
{ RPCResult : : Type : : STR , " address " , " bitcoin address " } ,
} } ,
} } ,
} } ,
} } ,
{ RPCResult : : Type : : STR_HEX , " blockhash " , " the block hash " } ,
{ RPCResult : : Type : : NUM , " confirmations " , " The confirmations " } ,
{ RPCResult : : Type : : NUM_TIME , " blocktime " , " The block time expressed in " + UNIX_EPOCH_TIME } ,
{ RPCResult : : Type : : NUM , " time " , " Same as \" blocktime \" " } ,
}
2018-12-21 12:29:36 -05:00
} ,
} ,
RPCExamples {
HelpExampleCli ( " getrawtransaction " , " \" mytxid \" " )
2016-10-26 13:09:54 +01:00
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" true " )
+ HelpExampleRpc ( " getrawtransaction " , " \" mytxid \" , true " )
2017-04-25 17:29:24 +09:00
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" false \" myblockhash \" " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" true \" myblockhash \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2020-07-26 09:59:11 +02:00
const NodeContext & node = EnsureNodeContext ( request . context ) ;
2017-04-25 17:29:24 +09:00
bool in_active_chain = true ;
2016-09-22 09:46:41 +02:00
uint256 hash = ParseHashV ( request . params [ 0 ] , " parameter 1 " ) ;
2017-04-25 17:29:24 +09:00
CBlockIndex * blockindex = nullptr ;
2012-05-31 16:01:16 -04:00
2018-01-27 20:10:56 +13:00
if ( hash = = Params ( ) . GenesisBlock ( ) . hashMerkleRoot ) {
// Special exception for the genesis block coinbase transaction
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved " ) ;
}
2016-10-26 13:09:54 +01:00
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
2012-05-31 16:01:16 -04:00
bool fVerbose = false ;
2017-07-10 11:44:39 -04:00
if ( ! request . params [ 1 ] . isNull ( ) ) {
2017-05-06 12:49:18 +09:00
fVerbose = request . params [ 1 ] . isNum ( ) ? ( request . params [ 1 ] . get_int ( ) ! = 0 ) : request . params [ 1 ] . get_bool ( ) ;
2016-10-26 13:09:54 +01:00
}
2012-05-31 16:01:16 -04:00
2017-04-25 17:29:24 +09:00
if ( ! request . params [ 2 ] . isNull ( ) ) {
2017-12-08 11:49:08 -08:00
LOCK ( cs_main ) ;
2017-04-25 17:29:24 +09:00
uint256 blockhash = ParseHashV ( request . params [ 2 ] , " parameter 3 " ) ;
2018-01-12 00:23:09 +00:00
blockindex = LookupBlockIndex ( blockhash ) ;
if ( ! blockindex ) {
2017-12-06 10:52:29 -05:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Block hash not found " ) ;
2017-04-25 17:29:24 +09:00
}
2019-03-27 11:14:25 -04:00
in_active_chain = : : ChainActive ( ) . Contains ( blockindex ) ;
2017-04-25 17:29:24 +09:00
}
2017-12-08 11:49:08 -08:00
bool f_txindex_ready = false ;
if ( g_txindex & & ! blockindex ) {
f_txindex_ready = g_txindex - > BlockUntilSyncedToCurrentChain ( ) ;
}
2017-04-25 17:29:24 +09:00
uint256 hash_block ;
2020-07-19 20:30:46 +02:00
const CTransactionRef tx = GetTransaction ( blockindex , node . mempool . get ( ) , hash , Params ( ) . GetConsensus ( ) , hash_block ) ;
2020-07-26 09:59:11 +02:00
if ( ! tx ) {
2017-04-25 17:29:24 +09:00
std : : string errmsg ;
if ( blockindex ) {
if ( ! ( blockindex - > nStatus & BLOCK_HAVE_DATA ) ) {
throw JSONRPCError ( RPC_MISC_ERROR , " Block not available " ) ;
}
errmsg = " No such transaction found in the provided block " ;
2017-12-08 11:41:35 -08:00
} else if ( ! g_txindex ) {
2019-06-14 23:00:37 +02:00
errmsg = " No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries " ;
2017-12-08 11:49:08 -08:00
} else if ( ! f_txindex_ready ) {
errmsg = " No such mempool transaction. Blockchain transactions are still in the process of being indexed " ;
2017-04-25 17:29:24 +09:00
} else {
2017-12-08 11:41:35 -08:00
errmsg = " No such mempool or blockchain transaction " ;
2017-04-25 17:29:24 +09:00
}
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , errmsg + " . Use gettransaction for wallet transactions. " ) ;
}
2012-05-31 16:01:16 -04:00
2017-04-25 17:29:24 +09:00
if ( ! fVerbose ) {
2017-08-10 15:58:25 -07:00
return EncodeHexTx ( * tx , RPCSerializationFlags ( ) ) ;
2017-04-25 17:29:24 +09:00
}
2012-05-31 16:01:16 -04:00
2015-05-10 14:48:35 +02:00
UniValue result ( UniValue : : VOBJ ) ;
2017-09-22 20:04:07 +02:00
if ( blockindex ) result . pushKV ( " in_active_chain " , in_active_chain ) ;
2017-04-25 17:29:24 +09:00
TxToJSON ( * tx , hash_block , result ) ;
2012-05-31 16:01:16 -04:00
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2012-05-31 16:01:16 -04:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan gettxoutproof ( )
2014-11-01 16:01:48 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " gettxoutproof " ,
2018-10-20 08:19:44 -04:00
" \n Returns a hex-encoded proof that \" txid \" was included in a block. \n "
" \n NOTE: By default this function only works sometimes. This is when there is an \n "
" unspent output in the utxo for this transaction. To make it always work, \n "
" you need to maintain a transaction index, using the -txindex command line option or \n "
" specify the block in which the transaction is included manually (by blockhash). \n " ,
2018-10-23 15:22:28 -04:00
{
2020-03-05 10:36:27 +00:00
{ " txids " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The txids to filter " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " A transaction hash " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-12-10 16:56:51 -05:00
{ " blockhash " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED_NAMED_ARG , " If specified, looks for txid in the block with this hash " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " data " , " A string that is a serialized, hex-encoded data for the proof. "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples { " " } ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2017-01-04 13:22:19 +09:00
std : : set < uint256 > setTxids ;
2014-11-01 16:01:48 -07:00
uint256 oneTxid ;
2016-09-22 09:46:41 +02:00
UniValue txids = request . params [ 0 ] . get_array ( ) ;
2015-05-10 13:35:44 +02:00
for ( unsigned int idx = 0 ; idx < txids . size ( ) ; idx + + ) {
2015-05-18 14:02:18 +02:00
const UniValue & txid = txids [ idx ] ;
2018-06-08 11:16:07 -07:00
uint256 hash ( ParseHashV ( txid , " txid " ) ) ;
2020-07-26 09:30:01 +02:00
if ( setTxids . count ( hash ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , std : : string ( " Invalid parameter, duplicated txid: " ) + txid . get_str ( ) ) ;
}
setTxids . insert ( hash ) ;
oneTxid = hash ;
2014-11-01 16:01:48 -07:00
}
2017-08-07 07:36:37 +02:00
CBlockIndex * pblockindex = nullptr ;
2014-11-01 16:01:48 -07:00
uint256 hashBlock ;
2017-12-08 11:49:08 -08:00
if ( ! request . params [ 1 ] . isNull ( ) ) {
LOCK ( cs_main ) ;
2018-06-08 11:16:07 -07:00
hashBlock = ParseHashV ( request . params [ 1 ] , " blockhash " ) ;
2018-01-12 00:23:09 +00:00
pblockindex = LookupBlockIndex ( hashBlock ) ;
if ( ! pblockindex ) {
2014-11-01 16:01:48 -07:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Block not found " ) ;
2018-01-12 00:23:09 +00:00
}
2014-11-01 16:01:48 -07:00
} else {
2017-12-08 11:49:08 -08:00
LOCK ( cs_main ) ;
2017-02-10 11:04:13 -05:00
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
for ( const auto & tx : setTxids ) {
2019-07-24 11:45:04 -04:00
const Coin & coin = AccessByTxid ( : : ChainstateActive ( ) . CoinsTip ( ) , tx ) ;
2017-02-10 11:04:13 -05:00
if ( ! coin . IsSpent ( ) ) {
2019-03-27 11:14:25 -04:00
pblockindex = : : ChainActive ( ) [ coin . nHeight ] ;
2017-02-10 11:04:13 -05:00
break ;
}
2017-04-25 11:29:39 -07:00
}
2014-11-01 16:01:48 -07:00
}
2017-12-08 11:49:08 -08:00
// Allow txindex to catch up if we need to query it and before we acquire cs_main.
if ( g_txindex & & ! pblockindex ) {
g_txindex - > BlockUntilSyncedToCurrentChain ( ) ;
}
LOCK ( cs_main ) ;
2020-07-26 09:30:01 +02:00
if ( pblockindex = = nullptr ) {
2020-07-26 09:59:11 +02:00
const CTransactionRef tx = GetTransaction ( /* block_index */ nullptr , /* mempool */ nullptr , oneTxid , Params ( ) . GetConsensus ( ) , hashBlock ) ;
if ( ! tx | | hashBlock . IsNull ( ) ) {
2014-11-01 16:01:48 -07:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Transaction not yet in block " ) ;
2020-07-26 09:59:11 +02:00
}
2018-01-12 00:23:09 +00:00
pblockindex = LookupBlockIndex ( hashBlock ) ;
if ( ! pblockindex ) {
2014-11-01 16:01:48 -07:00
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Transaction index corrupt " ) ;
2018-01-12 00:23:09 +00:00
}
2014-11-01 16:01:48 -07:00
}
CBlock block ;
2020-07-26 09:30:01 +02:00
if ( ! ReadBlockFromDisk ( block , pblockindex , Params ( ) . GetConsensus ( ) ) ) {
2014-11-01 16:01:48 -07:00
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Can't read block from disk " ) ;
2020-07-26 09:30:01 +02:00
}
2014-11-01 16:01:48 -07:00
unsigned int ntxFound = 0 ;
2020-07-26 09:30:01 +02:00
for ( const auto & tx : block . vtx ) {
if ( setTxids . count ( tx - > GetHash ( ) ) ) {
2014-11-01 16:01:48 -07:00
ntxFound + + ;
2020-07-26 09:30:01 +02:00
}
}
if ( ntxFound ! = setTxids . size ( ) ) {
2017-02-10 11:04:13 -05:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Not all transactions found in specified or retrieved block " ) ;
2020-07-26 09:30:01 +02:00
}
2014-11-01 16:01:48 -07:00
2015-11-06 01:32:04 +01:00
CDataStream ssMB ( SER_NETWORK , PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS ) ;
2014-11-01 16:01:48 -07:00
CMerkleBlock mb ( block , setTxids ) ;
ssMB < < mb ;
2020-06-24 15:48:26 +02:00
std : : string strHex = HexStr ( ssMB ) ;
2014-11-01 16:01:48 -07:00
return strHex ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2014-11-01 16:01:48 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan verifytxoutproof ( )
2014-11-01 16:01:48 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " verifytxoutproof " ,
2018-10-20 08:19:44 -04:00
" \n Verifies that a proof points to a transaction in a block, returning the transaction it commits to \n "
" and throwing an RPC error if the block is not in our best chain \n " ,
{
2018-12-10 16:56:51 -05:00
{ " proof " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The hex-encoded proof generated by gettxoutproof " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : ARR , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The txid(s) which the proof commits to, or empty array if the proof can not be validated. " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples { " " } ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2016-09-22 09:46:41 +02:00
CDataStream ssMB ( ParseHexV ( request . params [ 0 ] , " proof " ) , SER_NETWORK , PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS ) ;
2014-11-01 16:01:48 -07:00
CMerkleBlock merkleBlock ;
ssMB > > merkleBlock ;
2015-05-10 14:48:35 +02:00
UniValue res ( UniValue : : VARR ) ;
2014-11-01 16:01:48 -07:00
2017-01-04 13:22:19 +09:00
std : : vector < uint256 > vMatch ;
std : : vector < unsigned int > vIndex ;
2016-02-18 16:31:12 -08:00
if ( merkleBlock . txn . ExtractMatches ( vMatch , vIndex ) ! = merkleBlock . header . hashMerkleRoot )
2014-11-01 16:01:48 -07:00
return res ;
LOCK ( cs_main ) ;
2018-01-12 00:23:09 +00:00
const CBlockIndex * pindex = LookupBlockIndex ( merkleBlock . header . GetHash ( ) ) ;
2019-03-27 11:14:25 -04:00
if ( ! pindex | | ! : : ChainActive ( ) . Contains ( pindex ) | | pindex - > nTx = = 0 ) {
2014-11-01 16:01:48 -07:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Block not found in chain " ) ;
2018-01-12 00:23:09 +00:00
}
2014-11-01 16:01:48 -07:00
2018-06-12 17:10:21 -04:00
// Check if proof is valid, only add results if so
if ( pindex - > nTx = = merkleBlock . txn . GetNumTransactions ( ) ) {
for ( const uint256 & hash : vMatch ) {
res . push_back ( hash . GetHex ( ) ) ;
}
}
2014-11-01 16:01:48 -07:00
return res ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2014-11-01 16:01:48 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan createrawtransaction ( )
2018-06-27 17:02:07 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " createrawtransaction " ,
2018-11-23 11:21:38 -05:00
" \n Create a transaction spending the given inputs and creating new outputs. \n "
" Outputs can be addresses or data. \n "
" Returns hex-encoded raw transaction. \n "
" Note that the transaction's inputs are not signed, and \n "
" it is not stored in the wallet or transmitted to the network. \n " ,
{
2020-03-05 10:36:27 +00:00
{ " inputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The inputs " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
{ " sequence " , RPCArg : : Type : : NUM , /* default */ " depends on the value of the 'replaceable' and 'locktime' arguments " , " The sequence number " } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
} ,
} ,
2020-03-05 10:36:27 +00:00
{ " outputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The outputs (key-value pairs), where none of the keys are duplicated. \n "
2018-12-06 16:13:53 -05:00
" That is, each address can only appear once and there can only be one 'data' object. \n "
2018-11-23 11:21:38 -05:00
" For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also \n "
" accepted as second parameter. " ,
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-11-23 11:21:38 -05:00
{
2018-12-10 16:56:51 -05:00
{ " data " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " A key-value pair. The key must be \" data \" , the value is hex-encoded data " } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
} ,
} ,
2018-12-10 16:56:51 -05:00
{ " locktime " , RPCArg : : Type : : NUM , /* default */ " 0 " , " Raw locktime. Non-0 value also locktime-activates inputs " } ,
{ " replaceable " , RPCArg : : Type : : BOOL , /* default */ " false " , " Marks this transaction as BIP125-replaceable. \n "
2018-11-23 11:21:38 -05:00
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR_HEX , " transaction " , " hex string of the transaction "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" \" [{ \\ \" address \\ \" :0.01}] \" " )
2018-06-27 17:02:07 -07:00
+ HelpExampleCli ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" \" [{ \\ \" data \\ \" : \\ \" 00010203 \\ \" }] \" " )
+ HelpExampleRpc ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" , \" [{ \\ \" address \\ \" :0.01}] \" " )
+ HelpExampleRpc ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" , \" [{ \\ \" data \\ \" : \\ \" 00010203 \\ \" }] \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-27 17:02:07 -07:00
RPCTypeCheck ( request . params , {
UniValue : : VARR ,
UniValueType ( ) , // ARR or OBJ, checked later
UniValue : : VNUM ,
UniValue : : VBOOL
} , true
) ;
2019-04-27 19:44:38 +02:00
bool rbf = false ;
if ( ! request . params [ 3 ] . isNull ( ) ) {
rbf = request . params [ 3 ] . isTrue ( ) ;
}
CMutableTransaction rawTx = ConstructTransaction ( request . params [ 0 ] , request . params [ 1 ] , request . params [ 2 ] , rbf ) ;
2018-06-27 17:02:07 -07:00
2018-12-09 22:03:07 -08:00
return EncodeHexTx ( CTransaction ( rawTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2012-05-31 16:01:16 -04:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan decoderawtransaction ( )
2012-05-31 16:01:16 -04:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " decoderawtransaction " ,
2018-10-20 08:19:44 -04:00
" \n Return a JSON object representing the serialized, hex-encoded transaction. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction hex string " } ,
2019-04-26 09:04:08 -04:00
{ " iswitness " , RPCArg : : Type : : BOOL , /* default */ " depends on heuristic tests " , " Whether the transaction hex is a serialized witness transaction. \n "
" If iswitness is not present, heuristic tests will be used in decoding. \n "
" If true, only witness deserialization will be tried. \n "
" If false, only non-witness deserialization will be tried. \n "
" This boolean should reflect whether the transaction has inputs \n "
" (e.g. fully valid, or on-chain transactions), if known by the caller. "
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id " } ,
{ RPCResult : : Type : : STR_HEX , " hash " , " The transaction hash (differs from txid for witness transactions) " } ,
{ RPCResult : : Type : : NUM , " size " , " The transaction size " } ,
{ RPCResult : : Type : : NUM , " vsize " , " The virtual transaction size (differs from size for witness transactions) " } ,
{ RPCResult : : Type : : NUM , " weight " , " The transaction's weight (between vsize*4 - 3 and vsize*4) " } ,
{ RPCResult : : Type : : NUM , " version " , " The version " } ,
{ RPCResult : : Type : : NUM_TIME , " locktime " , " The lock time " } ,
{ RPCResult : : Type : : ARR , " vin " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction id " } ,
{ RPCResult : : Type : : NUM , " vout " , " The output number " } ,
{ RPCResult : : Type : : OBJ , " scriptSig " , " The script " ,
{
{ RPCResult : : Type : : STR , " asm " , " asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " hex " } ,
} } ,
{ RPCResult : : Type : : ARR , " txinwitness " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " hex " , " hex-encoded witness data (if any) " } ,
} } ,
{ RPCResult : : Type : : NUM , " sequence " , " The script sequence number " } ,
} } ,
} } ,
{ RPCResult : : Type : : ARR , " vout " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : NUM , " value " , " The value in " + CURRENCY_UNIT } ,
{ RPCResult : : Type : : NUM , " n " , " index " } ,
{ RPCResult : : Type : : OBJ , " scriptPubKey " , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " the asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " the hex " } ,
{ RPCResult : : Type : : NUM , " reqSigs " , " The required sigs " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
{ RPCResult : : Type : : ARR , " addresses " , " " ,
{
{ RPCResult : : Type : : STR , " address " , " bitcoin address " } ,
} } ,
} } ,
} } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " decoderawtransaction " , " \" hexstring \" " )
2013-10-29 22:29:44 +11:00
+ HelpExampleRpc ( " decoderawtransaction " , " \" hexstring \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2017-08-28 18:00:21 +12:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VBOOL } ) ;
2014-06-23 23:10:24 -04:00
2016-11-30 14:50:20 -08:00
CMutableTransaction mtx ;
2014-06-23 23:10:24 -04:00
2017-08-28 18:00:21 +12:00
bool try_witness = request . params [ 1 ] . isNull ( ) ? true : request . params [ 1 ] . get_bool ( ) ;
bool try_no_witness = request . params [ 1 ] . isNull ( ) ? true : ! request . params [ 1 ] . get_bool ( ) ;
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_str ( ) , try_no_witness , try_witness ) ) {
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
2017-08-28 18:00:21 +12:00
}
2012-05-31 16:01:16 -04:00
2015-05-10 13:35:44 +02:00
UniValue result ( UniValue : : VOBJ ) ;
2017-08-11 12:21:14 -07:00
TxToUniv ( CTransaction ( std : : move ( mtx ) ) , uint256 ( ) , result , false ) ;
2012-05-31 16:01:16 -04:00
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2012-05-31 16:01:16 -04:00
}
2019-03-20 15:54:37 -04:00
static std : : string GetAllOutputTypes ( )
{
2020-05-30 10:11:02 -04:00
std : : vector < std : : string > ret ;
2020-05-30 09:16:05 -04:00
using U = std : : underlying_type < TxoutType > : : type ;
for ( U i = ( U ) TxoutType : : NONSTANDARD ; i < = ( U ) TxoutType : : WITNESS_UNKNOWN ; + + i ) {
ret . emplace_back ( GetTxnOutputType ( static_cast < TxoutType > ( i ) ) ) ;
2019-03-20 15:54:37 -04:00
}
2020-05-30 10:11:02 -04:00
return Join ( ret , " , " ) ;
2019-03-20 15:54:37 -04:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan decodescript ( )
2013-07-15 01:22:10 -04:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " decodescript " ,
2018-10-20 08:19:44 -04:00
" \n Decode a hex-encoded script. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " the hex-encoded script " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " Script public key " } ,
{ RPCResult : : Type : : STR , " type " , " The output type (e.g. " + GetAllOutputTypes ( ) + " ) " } ,
{ RPCResult : : Type : : NUM , " reqSigs " , " The required signatures " } ,
{ RPCResult : : Type : : ARR , " addresses " , " " ,
{
{ RPCResult : : Type : : STR , " address " , " bitcoin address " } ,
} } ,
{ RPCResult : : Type : : STR , " p2sh " , " address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH) " } ,
{ RPCResult : : Type : : OBJ , " segwit " , " Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness) " ,
{
{ RPCResult : : Type : : STR , " asm " , " String representation of the script public key " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " Hex string of the script public key " } ,
{ RPCResult : : Type : : STR , " type " , " The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash) " } ,
{ RPCResult : : Type : : NUM , " reqSigs " , " The required signatures (always 1) " } ,
{ RPCResult : : Type : : ARR , " addresses " , " (always length 1) " ,
{
{ RPCResult : : Type : : STR , " address " , " segwit address " } ,
} } ,
{ RPCResult : : Type : : STR , " p2sh-segwit " , " address of the P2SH script wrapping this witness redeem script " } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " decodescript " , " \" hexstring \" " )
2013-10-29 22:29:44 +11:00
+ HelpExampleRpc ( " decodescript " , " \" hexstring \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2017-06-06 21:15:28 +02:00
RPCTypeCheck ( request . params , { UniValue : : VSTR } ) ;
2013-07-15 01:22:10 -04:00
2015-05-10 14:48:35 +02:00
UniValue r ( UniValue : : VOBJ ) ;
2013-07-15 01:22:10 -04:00
CScript script ;
2016-09-22 09:46:41 +02:00
if ( request . params [ 0 ] . get_str ( ) . size ( ) > 0 ) {
2017-01-04 13:22:19 +09:00
std : : vector < unsigned char > scriptData ( ParseHexV ( request . params [ 0 ] , " argument " ) ) ;
2013-07-15 01:22:10 -04:00
script = CScript ( scriptData . begin ( ) , scriptData . end ( ) ) ;
} else {
// Empty scripts are valid
}
2019-03-17 22:27:45 -04:00
ScriptPubKeyToUniv ( script , r , /* fIncludeHex */ false ) ;
2013-07-15 01:22:10 -04:00
2016-09-26 17:01:10 -04:00
UniValue type ;
type = find_value ( r , " type " ) ;
if ( type . isStr ( ) & & type . get_str ( ) ! = " scripthash " ) {
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
2019-02-19 17:00:45 -05:00
r . pushKV ( " p2sh " , EncodeDestination ( ScriptHash ( script ) ) ) ;
2018-02-01 02:40:47 +02:00
// P2SH and witness programs cannot be wrapped in P2WSH, if this script
// is a witness program, don't return addresses for a segwit programs.
2018-04-08 00:03:04 +03:00
if ( type . get_str ( ) = = " pubkey " | | type . get_str ( ) = = " pubkeyhash " | | type . get_str ( ) = = " multisig " | | type . get_str ( ) = = " nonstandard " ) {
2018-02-01 02:40:47 +02:00
std : : vector < std : : vector < unsigned char > > solutions_data ;
2020-05-30 09:16:05 -04:00
TxoutType which_type = Solver ( script , solutions_data ) ;
2018-02-01 02:40:47 +02:00
// Uncompressed pubkeys cannot be used with segwit checksigs.
// If the script contains an uncompressed pubkey, skip encoding of a segwit program.
2020-05-30 09:16:05 -04:00
if ( ( which_type = = TxoutType : : PUBKEY ) | | ( which_type = = TxoutType : : MULTISIG ) ) {
2018-02-01 02:40:47 +02:00
for ( const auto & solution : solutions_data ) {
if ( ( solution . size ( ) ! = 1 ) & & ! CPubKey ( solution ) . IsCompressed ( ) ) {
return r ;
}
}
}
UniValue sr ( UniValue : : VOBJ ) ;
CScript segwitScr ;
2020-05-30 09:16:05 -04:00
if ( which_type = = TxoutType : : PUBKEY ) {
2020-06-26 13:36:41 -07:00
segwitScr = GetScriptForDestination ( WitnessV0KeyHash ( Hash160 ( solutions_data [ 0 ] ) ) ) ;
2020-05-30 09:16:05 -04:00
} else if ( which_type = = TxoutType : : PUBKEYHASH ) {
2020-01-14 22:37:16 -08:00
segwitScr = GetScriptForDestination ( WitnessV0KeyHash ( uint160 { solutions_data [ 0 ] } ) ) ;
2018-02-01 02:40:47 +02:00
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
// Newer segwit program versions should be considered when then become available.
2018-06-17 19:44:50 -07:00
segwitScr = GetScriptForDestination ( WitnessV0ScriptHash ( script ) ) ;
2018-02-01 02:40:47 +02:00
}
2019-03-17 22:27:45 -04:00
ScriptPubKeyToUniv ( segwitScr , sr , /* fIncludeHex */ true ) ;
2019-02-19 17:00:45 -05:00
sr . pushKV ( " p2sh-segwit " , EncodeDestination ( ScriptHash ( segwitScr ) ) ) ;
2018-02-01 02:40:47 +02:00
r . pushKV ( " segwit " , sr ) ;
}
2016-09-26 17:01:10 -04:00
}
2013-07-15 01:22:10 -04:00
return r ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2013-07-15 01:22:10 -04:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan combinerawtransaction ( )
2017-06-09 22:38:06 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " combinerawtransaction " ,
2018-10-20 08:19:44 -04:00
" \n Combine multiple partially signed transactions into one transaction. \n "
" The combined transaction may be another partially signed transaction or a \n "
" fully signed transaction. " ,
2018-10-23 15:22:28 -04:00
{
2020-03-05 10:36:27 +00:00
{ " txs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The hex strings of partially signed transactions " ,
2018-10-23 15:22:28 -04:00
{
2020-09-04 17:21:18 -04:00
{ " hexstring " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " A hex-encoded raw transaction " } ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " " , " The hex-encoded raw transaction with signature(s) "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2019-10-14 13:09:42 -04:00
HelpExampleCli ( " combinerawtransaction " , R " ('[ " myhex1 " , " myhex2 " , " myhex3 " ]') " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2017-06-09 22:38:06 -07:00
UniValue txs = request . params [ 0 ] . get_array ( ) ;
std : : vector < CMutableTransaction > txVariants ( txs . size ( ) ) ;
for ( unsigned int idx = 0 ; idx < txs . size ( ) ; idx + + ) {
if ( ! DecodeHexTx ( txVariants [ idx ] , txs [ idx ] . get_str ( ) , true ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed for tx %d " , idx ) ) ;
}
}
if ( txVariants . empty ( ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Missing transactions " ) ;
}
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
CMutableTransaction mergedTx ( txVariants [ 0 ] ) ;
// Fetch previous transactions (inputs):
CCoinsView viewDummy ;
CCoinsViewCache view ( & viewDummy ) ;
{
2020-04-17 11:28:45 -04:00
const CTxMemPool & mempool = EnsureMemPool ( request . context ) ;
2017-06-09 22:38:06 -07:00
LOCK ( cs_main ) ;
LOCK ( mempool . cs ) ;
2019-07-24 11:45:04 -04:00
CCoinsViewCache & viewChain = : : ChainstateActive ( ) . CoinsTip ( ) ;
2017-06-09 22:38:06 -07:00
CCoinsViewMemPool viewMempool ( & viewChain , mempool ) ;
view . SetBackend ( viewMempool ) ; // temporarily switch cache backend to db+mempool view
for ( const CTxIn & txin : mergedTx . vin ) {
view . AccessCoin ( txin . prevout ) ; // Load entries from viewChain into view; can fail.
}
view . SetBackend ( viewDummy ) ; // switch back to avoid locking mempool for too long
}
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst ( mergedTx ) ;
// Sign what we can:
for ( unsigned int i = 0 ; i < mergedTx . vin . size ( ) ; i + + ) {
CTxIn & txin = mergedTx . vin [ i ] ;
const Coin & coin = view . AccessCoin ( txin . prevout ) ;
if ( coin . IsSpent ( ) ) {
throw JSONRPCError ( RPC_VERIFY_ERROR , " Input not found or already spent " ) ;
}
SignatureData sigdata ;
// ... and merge in other signatures:
for ( const CMutableTransaction & txv : txVariants ) {
if ( txv . vin . size ( ) > i ) {
2018-06-07 21:12:25 -07:00
sigdata . MergeSignatureData ( DataFromTransaction ( txv , i , coin . out ) ) ;
2017-06-09 22:38:06 -07:00
}
}
2018-06-07 21:12:25 -07:00
ProduceSignature ( DUMMY_SIGNING_PROVIDER , MutableTransactionSignatureCreator ( & mergedTx , i , coin . out . nValue , 1 ) , coin . out . scriptPubKey , sigdata ) ;
2017-06-09 22:38:06 -07:00
2018-05-17 17:54:18 -07:00
UpdateInput ( txin , sigdata ) ;
2017-06-09 22:38:06 -07:00
}
2018-12-09 22:03:07 -08:00
return EncodeHexTx ( CTransaction ( mergedTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2017-06-09 22:38:06 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan signrawtransactionwithkey ( )
2017-06-12 12:23:02 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " signrawtransactionwithkey " ,
2018-10-20 08:19:44 -04:00
" \n Sign inputs for raw transaction (serialized, hex-encoded). \n "
" The second argument is an array of base58-encoded private \n "
" keys that will be the only keys used to sign the transaction. \n "
" The third optional argument (may be null) is an array of previous transaction outputs that \n "
" this transaction depends on but may not yet be in the block chain. \n " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The transaction hex string " } ,
2020-03-05 10:36:27 +00:00
{ " privkeys " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The base58-encoded private keys for signing " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " privatekey " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " private key in base58-encoding " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2020-03-05 10:36:27 +00:00
{ " prevtxs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The previous dependent transaction outputs " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
{ " scriptPubKey " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " script key " } ,
2018-10-15 16:57:18 +13:00
{ " redeemScript " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " (required for P2SH) redeem script " } ,
{ " witnessScript " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " (required for P2WSH or P2SH-P2WSH) witness script " } ,
2019-03-26 17:39:39 +01:00
{ " amount " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : OMITTED , " (required for Segwit inputs) the amount spent " } ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-12-10 16:56:51 -05:00
{ " sighashtype " , RPCArg : : Type : : STR , /* default */ " ALL " , " The signature hash type. Must be one of: \n "
2017-06-12 12:23:02 -07:00
" \" ALL \" \n "
" \" NONE \" \n "
" \" SINGLE \" \n "
" \" ALL|ANYONECANPAY \" \n "
" \" NONE|ANYONECANPAY \" \n "
" \" SINGLE|ANYONECANPAY \" \n "
2018-11-23 11:21:38 -05:00
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex-encoded raw transaction with signature(s) " } ,
{ RPCResult : : Type : : BOOL , " complete " , " If the transaction has a complete set of signatures " } ,
2020-08-02 18:35:18 +02:00
{ RPCResult : : Type : : ARR , " errors " , /* optional */ true , " Script verification errors (if there are any) " ,
2020-01-10 00:00:57 +07:00
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The hash of the referenced, previous transaction " } ,
{ RPCResult : : Type : : NUM , " vout " , " The index of the output to spent and used as input " } ,
{ RPCResult : : Type : : STR_HEX , " scriptSig " , " The hex-encoded signature script " } ,
{ RPCResult : : Type : : NUM , " sequence " , " Script sequence number " } ,
{ RPCResult : : Type : : STR , " error " , " Verification or signing error related to the input " } ,
} } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2019-06-13 19:33:28 -07:00
HelpExampleCli ( " signrawtransactionwithkey " , " \" myhex \" \" [ \\ \" key1 \\ \" , \\ \" key2 \\ \" ] \" " )
+ HelpExampleRpc ( " signrawtransactionwithkey " , " \" myhex \" , \" [ \\ \" key1 \\ \" , \\ \" key2 \\ \" ] \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2017-06-12 12:23:02 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VARR , UniValue : : VARR , UniValue : : VSTR } , true ) ;
CMutableTransaction mtx ;
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_str ( ) , true ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
}
2019-06-06 16:33:23 +02:00
FillableSigningProvider keystore ;
2017-06-12 12:23:02 -07:00
const UniValue & keys = request . params [ 1 ] . get_array ( ) ;
for ( unsigned int idx = 0 ; idx < keys . size ( ) ; + + idx ) {
UniValue k = keys [ idx ] ;
2017-09-19 16:49:52 -07:00
CKey key = DecodeSecret ( k . get_str ( ) ) ;
2017-06-12 12:23:02 -07:00
if ( ! key . IsValid ( ) ) {
2017-09-19 16:49:52 -07:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid private key " ) ;
2017-06-12 12:23:02 -07:00
}
keystore . AddKey ( key ) ;
}
2019-04-10 13:38:41 +00:00
// Fetch previous transactions (inputs):
std : : map < COutPoint , Coin > coins ;
for ( const CTxIn & txin : mtx . vin ) {
coins [ txin . prevout ] ; // Create empty map entry keyed by prevout.
}
2020-04-17 11:28:45 -04:00
NodeContext & node = EnsureNodeContext ( request . context ) ;
FindCoins ( node , coins ) ;
2019-04-10 13:38:41 +00:00
2019-07-05 17:39:31 -04:00
// Parse the prevtxs array
ParsePrevouts ( request . params [ 2 ] , & keystore , coins ) ;
2019-11-18 14:56:52 -05:00
UniValue result ( UniValue : : VOBJ ) ;
SignTransaction ( mtx , & keystore , coins , request . params [ 3 ] , result ) ;
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2017-06-12 12:23:02 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan sendrawtransaction ( )
2012-05-31 16:01:16 -04:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " sendrawtransaction " ,
2019-07-26 11:30:51 +02:00
" \n Submit a raw transaction (serialized, hex-encoded) to local node and network. \n "
" \n Note that the transaction will be sent unconditionally to all peers, so using this \n "
" for manual rebroadcast may degrade privacy by leaking the transaction's origin, as \n "
" nodes will normally not rebroadcast non-wallet transactions already in their mempool. \n "
2018-10-20 08:19:44 -04:00
" \n Also see createrawtransaction and signrawtransactionwithkey calls. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The hex string of the raw transaction " } ,
2019-08-02 02:03:01 +09:00
{ " maxfeerate " , RPCArg : : Type : : AMOUNT , /* default */ FormatMoney ( DEFAULT_MAX_RAW_TX_FEE_RATE . GetFeePerK ( ) ) ,
2019-04-05 18:01:28 -04:00
" Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
" /kB. \n Set to 0 to accept any fee rate. \n " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR_HEX , " " , " The transaction hash in hex "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2013-10-29 22:29:44 +11:00
" \n Create a transaction \n "
+ HelpExampleCli ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" mytxid \\ \" , \\ \" vout \\ \" :0}] \" \" { \\ \" myaddress \\ \" :0.01} \" " ) +
" Sign the transaction, and get back the hex \n "
2018-02-20 09:15:28 -05:00
+ HelpExampleCli ( " signrawtransactionwithwallet " , " \" myhex \" " ) +
2013-10-29 22:29:44 +11:00
" \n Send the transaction (signed hex) \n "
+ HelpExampleCli ( " sendrawtransaction " , " \" signedhex \" " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2013-10-29 22:29:44 +11:00
+ HelpExampleRpc ( " sendrawtransaction " , " \" signedhex \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-27 17:21:07 +09:00
RPCTypeCheck ( request . params , {
UniValue : : VSTR ,
2020-04-01 16:15:35 +02:00
UniValueType ( ) , // VNUM or VSTR, checked inside AmountFromValue()
2018-06-27 17:21:07 +09:00
} ) ;
2012-05-31 16:01:16 -04:00
// parse hex string from parameter
2016-11-30 14:50:20 -08:00
CMutableTransaction mtx ;
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_str ( ) ) )
2014-06-23 23:10:24 -04:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
2016-11-10 22:29:19 -08:00
CTransactionRef tx ( MakeTransactionRef ( std : : move ( mtx ) ) ) ;
2013-08-28 15:41:46 -07:00
2020-04-01 16:15:35 +02:00
const CFeeRate max_raw_tx_fee_rate = request . params [ 1 ] . isNull ( ) ?
DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate ( AmountFromValue ( request . params [ 1 ] ) ) ;
2018-06-27 17:21:07 +09:00
2019-08-02 02:03:01 +09:00
int64_t virtual_size = GetVirtualTransactionSize ( * tx ) ;
CAmount max_raw_tx_fee = max_raw_tx_fee_rate . GetFee ( virtual_size ) ;
2019-02-09 20:51:33 -08:00
std : : string err_string ;
2019-04-11 14:37:30 +00:00
AssertLockNotHeld ( cs_main ) ;
2020-04-17 11:28:45 -04:00
NodeContext & node = EnsureNodeContext ( request . context ) ;
const TransactionError err = BroadcastTransaction ( node , tx , err_string , max_raw_tx_fee , /*relay*/ true , /*wait_callback*/ true ) ;
2019-02-14 10:01:06 -05:00
if ( TransactionError : : OK ! = err ) {
2019-02-09 20:51:33 -08:00
throw JSONRPCTransactionError ( err , err_string ) ;
2017-04-25 11:29:30 -07:00
}
2012-05-31 16:01:16 -04:00
2019-04-11 14:37:30 +00:00
return tx - > GetHash ( ) . GetHex ( ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2012-05-31 16:01:16 -04:00
}
2016-03-29 19:43:02 +02:00
2020-08-31 18:29:48 +02:00
static RPCHelpMan testmempoolaccept ( )
2017-11-17 12:54:39 -05:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " testmempoolaccept " ,
2018-11-23 11:21:38 -05:00
" \n Returns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool. \n "
" \n This checks if the transaction violates the consensus or policy rules. \n "
" \n See sendrawtransaction call. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " rawtxs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " An array of hex strings of raw transactions. \n "
2018-11-23 11:21:38 -05:00
" Length must be one for now. " ,
{
2018-12-10 16:56:51 -05:00
{ " rawtx " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED , " " } ,
2018-11-23 11:21:38 -05:00
} ,
} ,
2019-08-02 02:03:01 +09:00
{ " maxfeerate " , RPCArg : : Type : : AMOUNT , /* default */ FormatMoney ( DEFAULT_MAX_RAW_TX_FEE_RATE . GetFeePerK ( ) ) , " Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + " /kB \n " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : ARR , " " , " The result of the mempool acceptance test for each raw transaction in the input array. \n "
" Length is exactly one for now. " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " txid " , " The transaction hash in hex " } ,
{ RPCResult : : Type : : BOOL , " allowed " , " If the mempool allows this tx to be inserted " } ,
2020-05-24 18:28:56 +05:30
{ RPCResult : : Type : : NUM , " vsize " , " Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true) " } ,
{ RPCResult : : Type : : OBJ , " fees " , " Transaction fees (only present if 'allowed' is true) " ,
{
{ RPCResult : : Type : : STR_AMOUNT , " base " , " transaction fee in " + CURRENCY_UNIT } ,
} } ,
2020-01-10 00:00:57 +07:00
{ RPCResult : : Type : : STR , " reject-reason " , " Rejection string (only present when 'allowed' is false) " } ,
} } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2017-11-17 12:54:39 -05:00
" \n Create a transaction \n "
+ HelpExampleCli ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" mytxid \\ \" , \\ \" vout \\ \" :0}] \" \" { \\ \" myaddress \\ \" :0.01} \" " ) +
" Sign the transaction, and get back the hex \n "
2018-02-20 09:15:28 -05:00
+ HelpExampleCli ( " signrawtransactionwithwallet " , " \" myhex \" " ) +
2017-11-17 12:54:39 -05:00
" \n Test acceptance of the transaction (signed hex) \n "
2019-10-14 13:09:42 -04:00
+ HelpExampleCli ( " testmempoolaccept " , R " ('[ " signedhex " ]') " ) +
2018-10-02 14:49:18 -05:00
" \n As a JSON-RPC call \n "
2017-11-17 12:54:39 -05:00
+ HelpExampleRpc ( " testmempoolaccept " , " [ \" signedhex \" ] " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2019-02-14 17:07:29 +09:00
RPCTypeCheck ( request . params , {
UniValue : : VARR ,
2020-04-01 16:15:35 +02:00
UniValueType ( ) , // VNUM or VSTR, checked inside AmountFromValue()
2019-02-14 17:07:29 +09:00
} ) ;
2017-11-17 12:54:39 -05:00
if ( request . params [ 0 ] . get_array ( ) . size ( ) ! = 1 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Array must contain exactly one raw transaction for now " ) ;
}
CMutableTransaction mtx ;
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_array ( ) [ 0 ] . get_str ( ) ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
}
CTransactionRef tx ( MakeTransactionRef ( std : : move ( mtx ) ) ) ;
const uint256 & tx_hash = tx - > GetHash ( ) ;
2020-04-01 16:15:35 +02:00
const CFeeRate max_raw_tx_fee_rate = request . params [ 1 ] . isNull ( ) ?
DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate ( AmountFromValue ( request . params [ 1 ] ) ) ;
2017-11-17 12:54:39 -05:00
2020-04-17 11:28:45 -04:00
CTxMemPool & mempool = EnsureMemPool ( request . context ) ;
2019-08-02 02:03:01 +09:00
int64_t virtual_size = GetVirtualTransactionSize ( * tx ) ;
CAmount max_raw_tx_fee = max_raw_tx_fee_rate . GetFee ( virtual_size ) ;
2017-11-17 12:54:39 -05:00
UniValue result ( UniValue : : VARR ) ;
UniValue result_0 ( UniValue : : VOBJ ) ;
result_0 . pushKV ( " txid " , tx_hash . GetHex ( ) ) ;
2019-10-24 11:35:42 -04:00
TxValidationState state ;
2017-11-17 12:54:39 -05:00
bool test_accept_res ;
2020-05-24 18:28:56 +05:30
CAmount fee ;
2017-11-17 12:54:39 -05:00
{
LOCK ( cs_main ) ;
2019-04-28 16:26:31 -05:00
test_accept_res = AcceptToMemoryPool ( mempool , state , std : : move ( tx ) ,
2020-05-24 18:28:56 +05:30
nullptr /* plTxnReplaced */ , false /* bypass_limits */ , max_raw_tx_fee , /* test_accept */ true , & fee ) ;
2017-11-17 12:54:39 -05:00
}
result_0 . pushKV ( " allowed " , test_accept_res ) ;
2020-05-24 18:28:56 +05:30
// Only return the fee and vsize if the transaction would pass ATMP.
// These can be used to calculate the feerate.
if ( test_accept_res ) {
result_0 . pushKV ( " vsize " , virtual_size ) ;
UniValue fees ( UniValue : : VOBJ ) ;
fees . pushKV ( " base " , ValueFromAmount ( fee ) ) ;
result_0 . pushKV ( " fees " , fees ) ;
} else {
2017-11-17 12:54:39 -05:00
if ( state . IsInvalid ( ) ) {
2019-04-28 16:26:31 -05:00
if ( state . GetResult ( ) = = TxValidationResult : : TX_MISSING_INPUTS ) {
result_0 . pushKV ( " reject-reason " , " missing-inputs " ) ;
} else {
result_0 . pushKV ( " reject-reason " , strprintf ( " %s " , state . GetRejectReason ( ) ) ) ;
}
2017-11-17 12:54:39 -05:00
} else {
result_0 . pushKV ( " reject-reason " , state . GetRejectReason ( ) ) ;
}
}
result . push_back ( std : : move ( result_0 ) ) ;
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2017-11-17 12:54:39 -05:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan decodepsbt ( )
2018-06-28 19:04:40 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " decodepsbt " ,
2018-10-20 08:19:44 -04:00
" \n Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " The PSBT base64 string " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : OBJ , " tx " , " The decoded network-serialized unsigned transaction. " ,
{
{ RPCResult : : Type : : ELISION , " " , " The layout is the same as the output of decoderawtransaction. " } ,
} } ,
{ RPCResult : : Type : : OBJ_DYN , " unknown " , " The unknown global fields " ,
{
{ RPCResult : : Type : : STR_HEX , " key " , " (key-value pair) An unknown key-value pair " } ,
} } ,
{ RPCResult : : Type : : ARR , " inputs " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : OBJ , " non_witness_utxo " , /* optional */ true , " Decoded network transaction for non-witness UTXOs " ,
{
{ RPCResult : : Type : : ELISION , " " , " " } ,
} } ,
{ RPCResult : : Type : : OBJ , " witness_utxo " , /* optional */ true , " Transaction output for witness UTXOs " ,
{
{ RPCResult : : Type : : NUM , " amount " , " The value in " + CURRENCY_UNIT } ,
{ RPCResult : : Type : : OBJ , " scriptPubKey " , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " The asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
{ RPCResult : : Type : : STR , " address " , " Bitcoin address if there is one " } ,
} } ,
} } ,
{ RPCResult : : Type : : OBJ_DYN , " partial_signatures " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR , " pubkey " , " The public key and signature that corresponds to it. " } ,
} } ,
{ RPCResult : : Type : : STR , " sighash " , /* optional */ true , " The sighash type to be used " } ,
{ RPCResult : : Type : : OBJ , " redeem_script " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " The asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
} } ,
{ RPCResult : : Type : : OBJ , " witness_script " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " The asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
} } ,
{ RPCResult : : Type : : ARR , " bip32_derivs " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : OBJ , " pubkey " , /* optional */ true , " The public key with the derivation path as the value. " ,
{
{ RPCResult : : Type : : STR , " master_fingerprint " , " The fingerprint of the master key " } ,
{ RPCResult : : Type : : STR , " path " , " The path " } ,
} } ,
} } ,
{ RPCResult : : Type : : OBJ , " final_scriptsig " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " The asm " } ,
{ RPCResult : : Type : : STR , " hex " , " The hex " } ,
} } ,
{ RPCResult : : Type : : ARR , " final_scriptwitness " , " " ,
{
{ RPCResult : : Type : : STR_HEX , " " , " hex-encoded witness data (if any) " } ,
} } ,
{ RPCResult : : Type : : OBJ_DYN , " unknown " , " The unknown global fields " ,
{
{ RPCResult : : Type : : STR_HEX , " key " , " (key-value pair) An unknown key-value pair " } ,
} } ,
} } ,
} } ,
{ RPCResult : : Type : : ARR , " outputs " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : OBJ , " redeem_script " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " The asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
} } ,
{ RPCResult : : Type : : OBJ , " witness_script " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " The asm " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex " } ,
{ RPCResult : : Type : : STR , " type " , " The type, eg 'pubkeyhash' " } ,
} } ,
{ RPCResult : : Type : : ARR , " bip32_derivs " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " pubkey " , " The public key this path corresponds to " } ,
{ RPCResult : : Type : : STR , " master_fingerprint " , " The fingerprint of the master key " } ,
{ RPCResult : : Type : : STR , " path " , " The path " } ,
} } ,
} } ,
{ RPCResult : : Type : : OBJ_DYN , " unknown " , " The unknown global fields " ,
{
{ RPCResult : : Type : : STR_HEX , " key " , " (key-value pair) An unknown key-value pair " } ,
} } ,
} } ,
} } ,
{ RPCResult : : Type : : STR_AMOUNT , " fee " , /* optional */ true , " The transaction fee paid if all UTXOs slots in the PSBT have been filled. " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " decodepsbt " , " \" psbt \" " )
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-28 19:04:40 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR } ) ;
// Unserialize the transactions
PartiallySignedTransaction psbtx ;
std : : string error ;
2019-01-29 21:32:38 -08:00
if ( ! DecodeBase64PSBT ( psbtx , request . params [ 0 ] . get_str ( ) , error ) ) {
2018-06-28 19:04:40 -07:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
UniValue result ( UniValue : : VOBJ ) ;
// Add the decoded tx
UniValue tx_univ ( UniValue : : VOBJ ) ;
TxToUniv ( CTransaction ( * psbtx . tx ) , uint256 ( ) , tx_univ , false ) ;
result . pushKV ( " tx " , tx_univ ) ;
// Unknown data
UniValue unknowns ( UniValue : : VOBJ ) ;
for ( auto entry : psbtx . unknown ) {
unknowns . pushKV ( HexStr ( entry . first ) , HexStr ( entry . second ) ) ;
}
result . pushKV ( " unknown " , unknowns ) ;
// inputs
CAmount total_in = 0 ;
bool have_all_utxos = true ;
UniValue inputs ( UniValue : : VARR ) ;
for ( unsigned int i = 0 ; i < psbtx . inputs . size ( ) ; + + i ) {
const PSBTInput & input = psbtx . inputs [ i ] ;
UniValue in ( UniValue : : VOBJ ) ;
// UTXOs
2020-06-04 23:43:25 -04:00
bool have_a_utxo = false ;
2020-07-14 12:16:18 -04:00
CTxOut txout ;
2018-06-28 19:04:40 -07:00
if ( ! input . witness_utxo . IsNull ( ) ) {
2020-07-14 12:16:18 -04:00
txout = input . witness_utxo ;
2018-06-28 19:04:40 -07:00
UniValue o ( UniValue : : VOBJ ) ;
ScriptToUniv ( txout . scriptPubKey , o , true ) ;
2020-07-14 12:16:18 -04:00
UniValue out ( UniValue : : VOBJ ) ;
out . pushKV ( " amount " , ValueFromAmount ( txout . nValue ) ) ;
2018-06-28 19:04:40 -07:00
out . pushKV ( " scriptPubKey " , o ) ;
2020-07-14 12:16:18 -04:00
2018-06-28 19:04:40 -07:00
in . pushKV ( " witness_utxo " , out ) ;
2020-07-14 12:16:18 -04:00
2020-06-04 23:43:25 -04:00
have_a_utxo = true ;
}
if ( input . non_witness_utxo ) {
2020-07-14 12:16:18 -04:00
txout = input . non_witness_utxo - > vout [ psbtx . tx - > vin [ i ] . prevout . n ] ;
2018-06-28 19:04:40 -07:00
UniValue non_wit ( UniValue : : VOBJ ) ;
TxToUniv ( * input . non_witness_utxo , uint256 ( ) , non_wit , false ) ;
in . pushKV ( " non_witness_utxo " , non_wit ) ;
2020-07-14 12:16:18 -04:00
have_a_utxo = true ;
}
if ( have_a_utxo ) {
if ( MoneyRange ( txout . nValue ) & & MoneyRange ( total_in + txout . nValue ) ) {
total_in + = txout . nValue ;
2019-10-15 17:15:22 -04:00
} else {
// Hack to just not show fee later
have_all_utxos = false ;
}
2020-07-14 12:16:18 -04:00
} else {
2018-06-28 19:04:40 -07:00
have_all_utxos = false ;
}
// Partial sigs
if ( ! input . partial_sigs . empty ( ) ) {
UniValue partial_sigs ( UniValue : : VOBJ ) ;
for ( const auto & sig : input . partial_sigs ) {
partial_sigs . pushKV ( HexStr ( sig . second . first ) , HexStr ( sig . second . second ) ) ;
}
in . pushKV ( " partial_signatures " , partial_sigs ) ;
}
// Sighash
if ( input . sighash_type > 0 ) {
in . pushKV ( " sighash " , SighashToStr ( ( unsigned char ) input . sighash_type ) ) ;
}
// Redeem script and witness script
if ( ! input . redeem_script . empty ( ) ) {
UniValue r ( UniValue : : VOBJ ) ;
ScriptToUniv ( input . redeem_script , r , false ) ;
in . pushKV ( " redeem_script " , r ) ;
}
if ( ! input . witness_script . empty ( ) ) {
UniValue r ( UniValue : : VOBJ ) ;
ScriptToUniv ( input . witness_script , r , false ) ;
in . pushKV ( " witness_script " , r ) ;
}
// keypaths
if ( ! input . hd_keypaths . empty ( ) ) {
UniValue keypaths ( UniValue : : VARR ) ;
for ( auto entry : input . hd_keypaths ) {
UniValue keypath ( UniValue : : VOBJ ) ;
keypath . pushKV ( " pubkey " , HexStr ( entry . first ) ) ;
2018-07-19 23:15:53 -07:00
keypath . pushKV ( " master_fingerprint " , strprintf ( " %08x " , ReadBE32 ( entry . second . fingerprint ) ) ) ;
keypath . pushKV ( " path " , WriteHDKeypath ( entry . second . path ) ) ;
2018-06-28 19:04:40 -07:00
keypaths . push_back ( keypath ) ;
}
in . pushKV ( " bip32_derivs " , keypaths ) ;
}
// Final scriptSig and scriptwitness
if ( ! input . final_script_sig . empty ( ) ) {
UniValue scriptsig ( UniValue : : VOBJ ) ;
scriptsig . pushKV ( " asm " , ScriptToAsmStr ( input . final_script_sig , true ) ) ;
scriptsig . pushKV ( " hex " , HexStr ( input . final_script_sig ) ) ;
in . pushKV ( " final_scriptSig " , scriptsig ) ;
}
if ( ! input . final_script_witness . IsNull ( ) ) {
UniValue txinwitness ( UniValue : : VARR ) ;
for ( const auto & item : input . final_script_witness . stack ) {
2020-06-24 15:48:26 +02:00
txinwitness . push_back ( HexStr ( item ) ) ;
2018-06-28 19:04:40 -07:00
}
in . pushKV ( " final_scriptwitness " , txinwitness ) ;
}
// Unknown data
if ( input . unknown . size ( ) > 0 ) {
UniValue unknowns ( UniValue : : VOBJ ) ;
for ( auto entry : input . unknown ) {
unknowns . pushKV ( HexStr ( entry . first ) , HexStr ( entry . second ) ) ;
}
in . pushKV ( " unknown " , unknowns ) ;
}
inputs . push_back ( in ) ;
}
result . pushKV ( " inputs " , inputs ) ;
// outputs
CAmount output_value = 0 ;
UniValue outputs ( UniValue : : VARR ) ;
for ( unsigned int i = 0 ; i < psbtx . outputs . size ( ) ; + + i ) {
const PSBTOutput & output = psbtx . outputs [ i ] ;
UniValue out ( UniValue : : VOBJ ) ;
// Redeem script and witness script
if ( ! output . redeem_script . empty ( ) ) {
UniValue r ( UniValue : : VOBJ ) ;
ScriptToUniv ( output . redeem_script , r , false ) ;
out . pushKV ( " redeem_script " , r ) ;
}
if ( ! output . witness_script . empty ( ) ) {
UniValue r ( UniValue : : VOBJ ) ;
ScriptToUniv ( output . witness_script , r , false ) ;
out . pushKV ( " witness_script " , r ) ;
}
// keypaths
if ( ! output . hd_keypaths . empty ( ) ) {
UniValue keypaths ( UniValue : : VARR ) ;
for ( auto entry : output . hd_keypaths ) {
UniValue keypath ( UniValue : : VOBJ ) ;
keypath . pushKV ( " pubkey " , HexStr ( entry . first ) ) ;
2018-07-19 23:15:53 -07:00
keypath . pushKV ( " master_fingerprint " , strprintf ( " %08x " , ReadBE32 ( entry . second . fingerprint ) ) ) ;
keypath . pushKV ( " path " , WriteHDKeypath ( entry . second . path ) ) ;
2018-06-28 19:04:40 -07:00
keypaths . push_back ( keypath ) ;
}
out . pushKV ( " bip32_derivs " , keypaths ) ;
}
// Unknown data
if ( output . unknown . size ( ) > 0 ) {
UniValue unknowns ( UniValue : : VOBJ ) ;
for ( auto entry : output . unknown ) {
unknowns . pushKV ( HexStr ( entry . first ) , HexStr ( entry . second ) ) ;
}
out . pushKV ( " unknown " , unknowns ) ;
}
outputs . push_back ( out ) ;
// Fee calculation
2019-10-15 17:15:22 -04:00
if ( MoneyRange ( psbtx . tx - > vout [ i ] . nValue ) & & MoneyRange ( output_value + psbtx . tx - > vout [ i ] . nValue ) ) {
output_value + = psbtx . tx - > vout [ i ] . nValue ;
} else {
// Hack to just not show fee later
have_all_utxos = false ;
}
2018-06-28 19:04:40 -07:00
}
result . pushKV ( " outputs " , outputs ) ;
if ( have_all_utxos ) {
result . pushKV ( " fee " , ValueFromAmount ( total_in - output_value ) ) ;
}
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-06-28 19:04:40 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan combinepsbt ( )
2018-06-28 19:04:40 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " combinepsbt " ,
2018-10-20 08:19:44 -04:00
" \n Combine multiple partially signed Bitcoin transactions into one transaction. \n "
" Implements the Combiner role. \n " ,
2018-10-23 15:22:28 -04:00
{
2020-03-05 10:36:27 +00:00
{ " txs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The base64 strings of partially signed transactions " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " A base64 string of a PSBT " } ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " " , " The base64-encoded partially signed transaction "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2019-10-14 13:09:42 -04:00
HelpExampleCli ( " combinepsbt " , R " ('[ " mybase64_1 " , " mybase64_2 " , " mybase64_3 " ]') " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-28 19:04:40 -07:00
RPCTypeCheck ( request . params , { UniValue : : VARR } , true ) ;
// Unserialize the transactions
std : : vector < PartiallySignedTransaction > psbtxs ;
UniValue txs = request . params [ 0 ] . get_array ( ) ;
2019-02-04 21:26:43 -06:00
if ( txs . empty ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Parameter 'txs' cannot be empty " ) ;
}
2018-06-28 19:04:40 -07:00
for ( unsigned int i = 0 ; i < txs . size ( ) ; + + i ) {
PartiallySignedTransaction psbtx ;
std : : string error ;
2019-01-29 21:32:38 -08:00
if ( ! DecodeBase64PSBT ( psbtx , txs [ i ] . get_str ( ) , error ) ) {
2018-06-28 19:04:40 -07:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
psbtxs . push_back ( psbtx ) ;
}
2019-01-09 03:08:32 -08:00
PartiallySignedTransaction merged_psbt ;
2019-02-14 10:01:06 -05:00
const TransactionError error = CombinePSBTs ( merged_psbt , psbtxs ) ;
if ( error ! = TransactionError : : OK ) {
2019-01-09 03:08:32 -08:00
throw JSONRPCTransactionError ( error ) ;
2018-06-28 19:04:40 -07:00
}
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < merged_psbt ;
2020-08-07 20:25:42 +02:00
return EncodeBase64 ( MakeUCharSpan ( ssTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-06-28 19:04:40 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan finalizepsbt ( )
2018-06-28 19:04:40 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " finalizepsbt " ,
2018-10-20 08:19:44 -04:00
" Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a \n "
" network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be \n "
" created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete. \n "
" Implements the Finalizer and Extractor roles. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " A base64 string of a PSBT " } ,
{ " extract " , RPCArg : : Type : : BOOL , /* default */ " true " , " If true and the transaction is complete, \n "
2018-11-23 11:21:38 -05:00
" extract and return the complete transaction in normal network serialization instead of the PSBT. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : STR , " psbt " , " The base64-encoded partially signed transaction if not extracted " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The hex-encoded network transaction if extracted " } ,
{ RPCResult : : Type : : BOOL , " complete " , " If the transaction has a complete set of signatures " } ,
}
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " finalizepsbt " , " \" psbt \" " )
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-28 19:04:40 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VBOOL } , true ) ;
// Unserialize the transactions
PartiallySignedTransaction psbtx ;
std : : string error ;
2019-01-29 21:32:38 -08:00
if ( ! DecodeBase64PSBT ( psbtx , request . params [ 0 ] . get_str ( ) , error ) ) {
2018-06-28 19:04:40 -07:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
2019-01-09 03:08:32 -08:00
bool extract = request . params [ 1 ] . isNull ( ) | | ( ! request . params [ 1 ] . isNull ( ) & & request . params [ 1 ] . get_bool ( ) ) ;
CMutableTransaction mtx ;
bool complete = FinalizeAndExtractPSBT ( psbtx , mtx ) ;
2018-06-28 19:04:40 -07:00
UniValue result ( UniValue : : VOBJ ) ;
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
2019-01-09 03:08:32 -08:00
std : : string result_str ;
2018-06-28 19:04:40 -07:00
if ( complete & & extract ) {
ssTx < < mtx ;
2020-06-24 17:26:47 +02:00
result_str = HexStr ( ssTx ) ;
2019-01-09 03:08:32 -08:00
result . pushKV ( " hex " , result_str ) ;
2018-06-28 19:04:40 -07:00
} else {
ssTx < < psbtx ;
2019-01-09 03:08:32 -08:00
result_str = EncodeBase64 ( ssTx . str ( ) ) ;
result . pushKV ( " psbt " , result_str ) ;
2018-06-28 19:04:40 -07:00
}
2018-08-09 18:08:45 +02:00
result . pushKV ( " complete " , complete ) ;
2018-06-28 19:04:40 -07:00
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-06-28 19:04:40 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan createpsbt ( )
2018-06-28 19:04:40 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " createpsbt " ,
2018-10-20 08:19:44 -04:00
" \n Creates a transaction in the Partially Signed Transaction format. \n "
" Implements the Creator role. \n " ,
2018-10-23 15:22:28 -04:00
{
2020-03-05 10:36:27 +00:00
{ " inputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The json objects " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " vout " , RPCArg : : Type : : NUM , RPCArg : : Optional : : NO , " The output number " } ,
{ " sequence " , RPCArg : : Type : : NUM , /* default */ " depends on the value of the 'replaceable' and 'locktime' arguments " , " The sequence number " } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
} ,
2018-10-23 15:22:28 -04:00
} ,
2020-03-05 10:36:27 +00:00
{ " outputs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The outputs (key-value pairs), where none of the keys are duplicated. \n "
2018-12-06 16:13:53 -05:00
" That is, each address can only appear once and there can only be one 'data' object. \n "
2018-11-23 11:21:38 -05:00
" For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also \n "
" accepted as second parameter. " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " address " , RPCArg : : Type : : AMOUNT , RPCArg : : Optional : : NO , " A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT } ,
2018-10-23 15:22:28 -04:00
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-12-10 16:56:51 -05:00
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " " ,
2018-10-23 15:22:28 -04:00
{
2018-12-10 16:56:51 -05:00
{ " data " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " A key-value pair. The key must be \" data \" , the value is hex-encoded data " } ,
2018-11-23 11:21:38 -05:00
} ,
2018-10-23 15:22:28 -04:00
} ,
} ,
2018-11-23 11:21:38 -05:00
} ,
2018-12-10 16:56:51 -05:00
{ " locktime " , RPCArg : : Type : : NUM , /* default */ " 0 " , " Raw locktime. Non-0 value also locktime-activates inputs " } ,
{ " replaceable " , RPCArg : : Type : : BOOL , /* default */ " false " , " Marks this transaction as BIP125 replaceable. \n "
2018-11-23 11:21:38 -05:00
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible. " } ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " " , " The resulting raw transaction (base64-encoded string) "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
HelpExampleCli ( " createpsbt " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" \" [{ \\ \" data \\ \" : \\ \" 00010203 \\ \" }] \" " )
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-28 19:04:40 -07:00
RPCTypeCheck ( request . params , {
UniValue : : VARR ,
UniValueType ( ) , // ARR or OBJ, checked later
UniValue : : VNUM ,
UniValue : : VBOOL ,
} , true
) ;
2019-04-27 19:44:38 +02:00
bool rbf = false ;
if ( ! request . params [ 3 ] . isNull ( ) ) {
rbf = request . params [ 3 ] . isTrue ( ) ;
}
CMutableTransaction rawTx = ConstructTransaction ( request . params [ 0 ] , request . params [ 1 ] , request . params [ 2 ] , rbf ) ;
2018-06-28 19:04:40 -07:00
// Make a blank psbt
PartiallySignedTransaction psbtx ;
psbtx . tx = rawTx ;
for ( unsigned int i = 0 ; i < rawTx . vin . size ( ) ; + + i ) {
psbtx . inputs . push_back ( PSBTInput ( ) ) ;
}
for ( unsigned int i = 0 ; i < rawTx . vout . size ( ) ; + + i ) {
psbtx . outputs . push_back ( PSBTOutput ( ) ) ;
}
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
2020-08-07 20:25:42 +02:00
return EncodeBase64 ( MakeUCharSpan ( ssTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-06-28 19:04:40 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan converttopsbt ( )
2018-06-28 19:04:40 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " converttopsbt " ,
2018-10-20 08:19:44 -04:00
" \n Converts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction \n "
" createpsbt and walletcreatefundedpsbt should be used for new applications. \n " ,
{
2018-12-10 16:56:51 -05:00
{ " hexstring " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The hex string of a raw transaction " } ,
2019-04-26 09:04:08 -04:00
{ " permitsigdata " , RPCArg : : Type : : BOOL , /* default */ " false " , " If true, any signatures in the input will be discarded and conversion \n "
2018-11-23 11:21:38 -05:00
" will continue. If false, RPC will fail if any signatures are present. " } ,
2018-12-10 16:56:51 -05:00
{ " iswitness " , RPCArg : : Type : : BOOL , /* default */ " depends on heuristic tests " , " Whether the transaction hex is a serialized witness transaction. \n "
2019-04-26 09:04:08 -04:00
" If iswitness is not present, heuristic tests will be used in decoding. \n "
" If true, only witness deserialization will be tried. \n "
" If false, only non-witness deserialization will be tried. \n "
" This boolean should reflect whether the transaction has inputs \n "
" (e.g. fully valid, or on-chain transactions), if known by the caller. "
} ,
2018-12-21 12:29:36 -05:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " " , " The resulting raw transaction (base64-encoded string) "
2018-12-21 12:29:36 -05:00
} ,
RPCExamples {
2018-06-28 19:04:40 -07:00
" \n Create a transaction \n "
+ HelpExampleCli ( " createrawtransaction " , " \" [{ \\ \" txid \\ \" : \\ \" myid \\ \" , \\ \" vout \\ \" :0}] \" \" [{ \\ \" data \\ \" : \\ \" 00010203 \\ \" }] \" " ) +
" \n Convert the transaction to a PSBT \n "
+ HelpExampleCli ( " converttopsbt " , " \" rawtransaction \" " )
2018-12-21 12:29:36 -05:00
} ,
2020-08-31 18:29:48 +02:00
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-06-28 19:04:40 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VBOOL , UniValue : : VBOOL } , true ) ;
// parse hex string from parameter
CMutableTransaction tx ;
bool permitsigdata = request . params [ 1 ] . isNull ( ) ? false : request . params [ 1 ] . get_bool ( ) ;
bool witness_specified = ! request . params [ 2 ] . isNull ( ) ;
bool iswitness = witness_specified ? request . params [ 2 ] . get_bool ( ) : false ;
2019-04-26 09:04:08 -04:00
const bool try_witness = witness_specified ? iswitness : true ;
const bool try_no_witness = witness_specified ? ! iswitness : true ;
2018-06-28 19:04:40 -07:00
if ( ! DecodeHexTx ( tx , request . params [ 0 ] . get_str ( ) , try_no_witness , try_witness ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
}
// Remove all scriptSigs and scriptWitnesses from inputs
for ( CTxIn & input : tx . vin ) {
2018-09-29 22:09:15 -04:00
if ( ( ! input . scriptSig . empty ( ) | | ! input . scriptWitness . IsNull ( ) ) & & ! permitsigdata ) {
2018-06-28 19:04:40 -07:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Inputs must not have scriptSigs and scriptWitnesses " ) ;
}
input . scriptSig . clear ( ) ;
input . scriptWitness . SetNull ( ) ;
}
// Make a blank psbt
PartiallySignedTransaction psbtx ;
psbtx . tx = tx ;
for ( unsigned int i = 0 ; i < tx . vin . size ( ) ; + + i ) {
psbtx . inputs . push_back ( PSBTInput ( ) ) ;
}
for ( unsigned int i = 0 ; i < tx . vout . size ( ) ; + + i ) {
psbtx . outputs . push_back ( PSBTOutput ( ) ) ;
}
// Serialize the PSBT
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
2020-08-07 20:25:42 +02:00
return EncodeBase64 ( MakeUCharSpan ( ssTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-06-28 19:04:40 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan utxoupdatepsbt ( )
2018-07-20 17:08:25 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " utxoupdatepsbt " ,
2019-02-16 14:59:16 -08:00
" \n Updates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool. \n " ,
2018-07-20 17:08:25 -07:00
{
2019-02-16 14:59:16 -08:00
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " A base64 string of a PSBT " } ,
{ " descriptors " , RPCArg : : Type : : ARR , RPCArg : : Optional : : OMITTED_NAMED_ARG , " An array of either strings or objects " , {
{ " " , RPCArg : : Type : : STR , RPCArg : : Optional : : OMITTED , " An output descriptor " } ,
{ " " , RPCArg : : Type : : OBJ , RPCArg : : Optional : : OMITTED , " An object with an output descriptor and extra information " , {
{ " desc " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " An output descriptor " } ,
{ " range " , RPCArg : : Type : : RANGE , " 1000 " , " Up to what index HD chains should be explored (either end or [begin,end]) " } ,
} } ,
} } ,
2018-07-20 17:08:25 -07:00
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " " , " The base64-encoded partially signed transaction with inputs updated "
2018-07-20 17:08:25 -07:00
} ,
RPCExamples {
HelpExampleCli ( " utxoupdatepsbt " , " \" psbt \" " )
2020-08-31 18:29:48 +02:00
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2019-02-16 14:59:16 -08:00
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VARR } , true ) ;
2018-07-20 17:08:25 -07:00
// Unserialize the transactions
PartiallySignedTransaction psbtx ;
std : : string error ;
if ( ! DecodeBase64PSBT ( psbtx , request . params [ 0 ] . get_str ( ) , error ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
2019-02-16 14:59:16 -08:00
// Parse descriptors, if any.
FlatSigningProvider provider ;
if ( ! request . params [ 1 ] . isNull ( ) ) {
auto descs = request . params [ 1 ] . get_array ( ) ;
for ( size_t i = 0 ; i < descs . size ( ) ; + + i ) {
EvalDescriptorStringOrObject ( descs [ i ] , provider ) ;
}
}
// We don't actually need private keys further on; hide them as a precaution.
HidingSigningProvider public_provider ( & provider , /* nosign */ true , /* nobip32derivs */ false ) ;
2018-07-20 17:08:25 -07:00
// Fetch previous transactions (inputs):
CCoinsView viewDummy ;
CCoinsViewCache view ( & viewDummy ) ;
{
2020-04-17 11:28:45 -04:00
const CTxMemPool & mempool = EnsureMemPool ( request . context ) ;
2018-07-20 17:08:25 -07:00
LOCK2 ( cs_main , mempool . cs ) ;
2019-07-24 11:45:04 -04:00
CCoinsViewCache & viewChain = : : ChainstateActive ( ) . CoinsTip ( ) ;
2018-07-20 17:08:25 -07:00
CCoinsViewMemPool viewMempool ( & viewChain , mempool ) ;
view . SetBackend ( viewMempool ) ; // temporarily switch cache backend to db+mempool view
for ( const CTxIn & txin : psbtx . tx - > vin ) {
view . AccessCoin ( txin . prevout ) ; // Load entries from viewChain into view; can fail.
}
view . SetBackend ( viewDummy ) ; // switch back to avoid locking mempool for too long
}
// Fill the inputs
for ( unsigned int i = 0 ; i < psbtx . tx - > vin . size ( ) ; + + i ) {
PSBTInput & input = psbtx . inputs . at ( i ) ;
if ( input . non_witness_utxo | | ! input . witness_utxo . IsNull ( ) ) {
continue ;
}
const Coin & coin = view . AccessCoin ( psbtx . tx - > vin [ i ] . prevout ) ;
2019-02-16 14:59:16 -08:00
if ( IsSegWitOutput ( provider , coin . out . scriptPubKey ) ) {
2018-07-20 17:08:25 -07:00
input . witness_utxo = coin . out ;
}
2019-02-16 14:59:16 -08:00
// Update script/keypath information using descriptor data.
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
// we don't actually care about those here, in fact.
SignPSBTInput ( public_provider , psbtx , i , /* sighash_type */ 1 ) ;
}
// Update script/keypath information using descriptor data.
for ( unsigned int i = 0 ; i < psbtx . tx - > vout . size ( ) ; + + i ) {
UpdatePSBTOutput ( public_provider , psbtx , i ) ;
2018-07-20 17:08:25 -07:00
}
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbtx ;
2020-08-07 20:25:42 +02:00
return EncodeBase64 ( MakeUCharSpan ( ssTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-07-20 17:08:25 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan joinpsbts ( )
2018-07-20 18:24:16 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " joinpsbts " ,
2018-07-20 18:24:16 -07:00
" \n Joins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs \n "
" No input in any of the PSBTs can be in more than one of the PSBTs. \n " ,
{
2020-03-05 10:36:27 +00:00
{ " txs " , RPCArg : : Type : : ARR , RPCArg : : Optional : : NO , " The base64 strings of partially signed transactions " ,
2018-07-20 18:24:16 -07:00
{
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " A base64 string of a PSBT " }
} }
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : STR , " " , " The base64-encoded partially signed transaction "
2018-07-20 18:24:16 -07:00
} ,
RPCExamples {
HelpExampleCli ( " joinpsbts " , " \" psbt \" " )
2020-08-31 18:29:48 +02:00
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-07-20 18:24:16 -07:00
RPCTypeCheck ( request . params , { UniValue : : VARR } , true ) ;
// Unserialize the transactions
std : : vector < PartiallySignedTransaction > psbtxs ;
UniValue txs = request . params [ 0 ] . get_array ( ) ;
if ( txs . size ( ) < = 1 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " At least two PSBTs are required to join PSBTs. " ) ;
}
2019-09-03 10:53:45 -04:00
uint32_t best_version = 1 ;
2018-07-20 18:24:16 -07:00
uint32_t best_locktime = 0xffffffff ;
for ( unsigned int i = 0 ; i < txs . size ( ) ; + + i ) {
PartiallySignedTransaction psbtx ;
std : : string error ;
if ( ! DecodeBase64PSBT ( psbtx , txs [ i ] . get_str ( ) , error ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
psbtxs . push_back ( psbtx ) ;
// Choose the highest version number
2019-09-03 10:53:45 -04:00
if ( static_cast < uint32_t > ( psbtx . tx - > nVersion ) > best_version ) {
best_version = static_cast < uint32_t > ( psbtx . tx - > nVersion ) ;
2018-07-20 18:24:16 -07:00
}
// Choose the lowest lock time
if ( psbtx . tx - > nLockTime < best_locktime ) {
best_locktime = psbtx . tx - > nLockTime ;
}
}
// Create a blank psbt where everything will be added
PartiallySignedTransaction merged_psbt ;
merged_psbt . tx = CMutableTransaction ( ) ;
2019-09-03 10:53:45 -04:00
merged_psbt . tx - > nVersion = static_cast < int32_t > ( best_version ) ;
2018-07-20 18:24:16 -07:00
merged_psbt . tx - > nLockTime = best_locktime ;
// Merge
for ( auto & psbt : psbtxs ) {
for ( unsigned int i = 0 ; i < psbt . tx - > vin . size ( ) ; + + i ) {
if ( ! merged_psbt . AddInput ( psbt . tx - > vin [ i ] , psbt . inputs [ i ] ) ) {
2019-10-28 13:30:20 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Input %s:%d exists in multiple PSBTs " , psbt . tx - > vin [ i ] . prevout . hash . ToString ( ) , psbt . tx - > vin [ i ] . prevout . n ) ) ;
2018-07-20 18:24:16 -07:00
}
}
for ( unsigned int i = 0 ; i < psbt . tx - > vout . size ( ) ; + + i ) {
merged_psbt . AddOutput ( psbt . tx - > vout [ i ] , psbt . outputs [ i ] ) ;
}
merged_psbt . unknown . insert ( psbt . unknown . begin ( ) , psbt . unknown . end ( ) ) ;
}
2019-07-31 18:02:24 -04:00
// Generate list of shuffled indices for shuffling inputs and outputs of the merged PSBT
std : : vector < int > input_indices ( merged_psbt . inputs . size ( ) ) ;
std : : iota ( input_indices . begin ( ) , input_indices . end ( ) , 0 ) ;
std : : vector < int > output_indices ( merged_psbt . outputs . size ( ) ) ;
std : : iota ( output_indices . begin ( ) , output_indices . end ( ) , 0 ) ;
2019-11-04 04:22:53 -05:00
// Shuffle input and output indices lists
2019-07-31 18:02:24 -04:00
Shuffle ( input_indices . begin ( ) , input_indices . end ( ) , FastRandomContext ( ) ) ;
Shuffle ( output_indices . begin ( ) , output_indices . end ( ) , FastRandomContext ( ) ) ;
PartiallySignedTransaction shuffled_psbt ;
shuffled_psbt . tx = CMutableTransaction ( ) ;
shuffled_psbt . tx - > nVersion = merged_psbt . tx - > nVersion ;
shuffled_psbt . tx - > nLockTime = merged_psbt . tx - > nLockTime ;
for ( int i : input_indices ) {
shuffled_psbt . AddInput ( merged_psbt . tx - > vin [ i ] , merged_psbt . inputs [ i ] ) ;
}
for ( int i : output_indices ) {
shuffled_psbt . AddOutput ( merged_psbt . tx - > vout [ i ] , merged_psbt . outputs [ i ] ) ;
}
shuffled_psbt . unknown . insert ( merged_psbt . unknown . begin ( ) , merged_psbt . unknown . end ( ) ) ;
2018-07-20 18:24:16 -07:00
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
2019-07-31 18:02:24 -04:00
ssTx < < shuffled_psbt ;
2020-08-07 20:25:42 +02:00
return EncodeBase64 ( MakeUCharSpan ( ssTx ) ) ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-07-20 18:24:16 -07:00
}
2020-08-31 18:29:48 +02:00
static RPCHelpMan analyzepsbt ( )
2018-07-31 17:58:01 -07:00
{
2020-08-31 18:29:48 +02:00
return RPCHelpMan { " analyzepsbt " ,
2018-07-31 17:58:01 -07:00
" \n Analyzes and provides information about the current status of a PSBT and its inputs \n " ,
{
{ " psbt " , RPCArg : : Type : : STR , RPCArg : : Optional : : NO , " A base64 string of a PSBT " }
} ,
RPCResult {
2020-01-10 00:00:57 +07:00
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : ARR , " inputs " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : BOOL , " has_utxo " , " Whether a UTXO is provided " } ,
{ RPCResult : : Type : : BOOL , " is_final " , " Whether the input is finalized " } ,
{ RPCResult : : Type : : OBJ , " missing " , /* optional */ true , " Things that are missing that are required to complete this input " ,
{
{ RPCResult : : Type : : ARR , " pubkeys " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR_HEX , " keyid " , " Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing " } ,
} } ,
{ RPCResult : : Type : : ARR , " signatures " , /* optional */ true , " " ,
{
{ RPCResult : : Type : : STR_HEX , " keyid " , " Public key ID, hash160 of the public key, of a public key whose signature is missing " } ,
} } ,
{ RPCResult : : Type : : STR_HEX , " redeemscript " , /* optional */ true , " Hash160 of the redeemScript that is missing " } ,
{ RPCResult : : Type : : STR_HEX , " witnessscript " , /* optional */ true , " SHA256 of the witnessScript that is missing " } ,
} } ,
{ RPCResult : : Type : : STR , " next " , /* optional */ true , " Role of the next person that this input needs to go to " } ,
} } ,
} } ,
{ RPCResult : : Type : : NUM , " estimated_vsize " , /* optional */ true , " Estimated vsize of the final signed transaction " } ,
{ RPCResult : : Type : : STR_AMOUNT , " estimated_feerate " , /* optional */ true , " Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + " /kB. Shown only if all UTXO slots in the PSBT have been filled " } ,
{ RPCResult : : Type : : STR_AMOUNT , " fee " , /* optional */ true , " The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled " } ,
{ RPCResult : : Type : : STR , " next " , " Role of the next person that this psbt needs to go to " } ,
2020-08-02 18:35:18 +02:00
{ RPCResult : : Type : : STR , " error " , /* optional */ true , " Error message (if there is one) " } ,
2020-01-10 00:00:57 +07:00
}
2018-07-31 17:58:01 -07:00
} ,
RPCExamples {
HelpExampleCli ( " analyzepsbt " , " \" psbt \" " )
2020-08-31 18:29:48 +02:00
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
2018-07-31 17:58:01 -07:00
RPCTypeCheck ( request . params , { UniValue : : VSTR } ) ;
// Unserialize the transaction
PartiallySignedTransaction psbtx ;
std : : string error ;
if ( ! DecodeBase64PSBT ( psbtx , request . params [ 0 ] . get_str ( ) , error ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed %s " , error ) ) ;
}
2019-03-01 00:25:10 -08:00
PSBTAnalysis psbta = AnalyzePSBT ( psbtx ) ;
2018-07-31 17:58:01 -07:00
UniValue result ( UniValue : : VOBJ ) ;
UniValue inputs_result ( UniValue : : VARR ) ;
2019-03-01 00:25:10 -08:00
for ( const auto & input : psbta . inputs ) {
2018-07-31 17:58:01 -07:00
UniValue input_univ ( UniValue : : VOBJ ) ;
UniValue missing ( UniValue : : VOBJ ) ;
2019-03-01 00:25:10 -08:00
input_univ . pushKV ( " has_utxo " , input . has_utxo ) ;
input_univ . pushKV ( " is_final " , input . is_final ) ;
input_univ . pushKV ( " next " , PSBTRoleName ( input . next ) ) ;
2018-07-31 17:58:01 -07:00
2019-03-01 00:25:10 -08:00
if ( ! input . missing_pubkeys . empty ( ) ) {
UniValue missing_pubkeys_univ ( UniValue : : VARR ) ;
for ( const CKeyID & pubkey : input . missing_pubkeys ) {
missing_pubkeys_univ . push_back ( HexStr ( pubkey ) ) ;
2018-07-31 17:58:01 -07:00
}
2019-03-01 00:25:10 -08:00
missing . pushKV ( " pubkeys " , missing_pubkeys_univ ) ;
}
if ( ! input . missing_redeem_script . IsNull ( ) ) {
missing . pushKV ( " redeemscript " , HexStr ( input . missing_redeem_script ) ) ;
}
if ( ! input . missing_witness_script . IsNull ( ) ) {
missing . pushKV ( " witnessscript " , HexStr ( input . missing_witness_script ) ) ;
}
if ( ! input . missing_sigs . empty ( ) ) {
UniValue missing_sigs_univ ( UniValue : : VARR ) ;
for ( const CKeyID & pubkey : input . missing_sigs ) {
missing_sigs_univ . push_back ( HexStr ( pubkey ) ) ;
}
missing . pushKV ( " signatures " , missing_sigs_univ ) ;
}
if ( ! missing . getKeys ( ) . empty ( ) ) {
input_univ . pushKV ( " missing " , missing ) ;
2018-07-31 17:58:01 -07:00
}
inputs_result . push_back ( input_univ ) ;
}
2019-11-19 14:35:14 -05:00
if ( ! inputs_result . empty ( ) ) result . pushKV ( " inputs " , inputs_result ) ;
2018-07-31 17:58:01 -07:00
2019-03-01 00:25:10 -08:00
if ( psbta . estimated_vsize ! = nullopt ) {
result . pushKV ( " estimated_vsize " , ( int ) * psbta . estimated_vsize ) ;
2018-07-31 17:58:01 -07:00
}
2019-03-01 00:25:10 -08:00
if ( psbta . estimated_feerate ! = nullopt ) {
result . pushKV ( " estimated_feerate " , ValueFromAmount ( psbta . estimated_feerate - > GetFeePerK ( ) ) ) ;
2018-07-31 17:58:01 -07:00
}
2019-03-01 00:25:10 -08:00
if ( psbta . fee ! = nullopt ) {
result . pushKV ( " fee " , ValueFromAmount ( * psbta . fee ) ) ;
}
result . pushKV ( " next " , PSBTRoleName ( psbta . next ) ) ;
2019-11-19 14:35:14 -05:00
if ( ! psbta . error . empty ( ) ) {
result . pushKV ( " error " , psbta . error ) ;
}
2019-03-01 00:25:10 -08:00
2018-07-31 17:58:01 -07:00
return result ;
2020-08-31 18:29:48 +02:00
} ,
} ;
2018-07-31 17:58:01 -07:00
}
2020-04-06 00:21:33 +08:00
void RegisterRawTransactionRPCCommands ( CRPCTable & t )
{
2018-08-20 14:19:43 +02:00
// clang-format off
2016-03-29 19:43:02 +02:00
static const CRPCCommand commands [ ] =
2017-06-12 12:23:02 -07:00
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ " rawtransactions " , " getrawtransaction " , & getrawtransaction , { " txid " , " verbose " , " blockhash " } } ,
{ " rawtransactions " , " createrawtransaction " , & createrawtransaction , { " inputs " , " outputs " , " locktime " , " replaceable " } } ,
{ " rawtransactions " , " decoderawtransaction " , & decoderawtransaction , { " hexstring " , " iswitness " } } ,
{ " rawtransactions " , " decodescript " , & decodescript , { " hexstring " } } ,
2020-04-01 16:15:35 +02:00
{ " rawtransactions " , " sendrawtransaction " , & sendrawtransaction , { " hexstring " , " maxfeerate " } } ,
2017-06-12 12:23:02 -07:00
{ " rawtransactions " , " combinerawtransaction " , & combinerawtransaction , { " txs " } } ,
{ " rawtransactions " , " signrawtransactionwithkey " , & signrawtransactionwithkey , { " hexstring " , " privkeys " , " prevtxs " , " sighashtype " } } ,
2020-04-01 16:15:35 +02:00
{ " rawtransactions " , " testmempoolaccept " , & testmempoolaccept , { " rawtxs " , " maxfeerate " } } ,
2018-06-28 19:04:40 -07:00
{ " rawtransactions " , " decodepsbt " , & decodepsbt , { " psbt " } } ,
{ " rawtransactions " , " combinepsbt " , & combinepsbt , { " txs " } } ,
{ " rawtransactions " , " finalizepsbt " , & finalizepsbt , { " psbt " , " extract " } } ,
{ " rawtransactions " , " createpsbt " , & createpsbt , { " inputs " , " outputs " , " locktime " , " replaceable " } } ,
{ " rawtransactions " , " converttopsbt " , & converttopsbt , { " hexstring " , " permitsigdata " , " iswitness " } } ,
2019-07-02 18:34:18 +01:00
{ " rawtransactions " , " utxoupdatepsbt " , & utxoupdatepsbt , { " psbt " , " descriptors " } } ,
2018-07-20 18:24:16 -07:00
{ " rawtransactions " , " joinpsbts " , & joinpsbts , { " txs " } } ,
2018-07-31 17:58:01 -07:00
{ " rawtransactions " , " analyzepsbt " , & analyzepsbt , { " psbt " } } ,
2017-06-12 12:23:02 -07:00
{ " blockchain " , " gettxoutproof " , & gettxoutproof , { " txids " , " blockhash " } } ,
{ " blockchain " , " verifytxoutproof " , & verifytxoutproof , { " proof " } } ,
2016-03-29 19:43:02 +02:00
} ;
2018-08-20 14:19:43 +02:00
// clang-format on
2020-07-15 21:29:41 +02:00
for ( const auto & c : commands ) {
t . appendCommand ( c . name , & c ) ;
}
2016-03-29 19:43:02 +02:00
}