2019-02-13 02:16:29 +01:00
// Copyright (c) 2017-2019 The Bitcoin Core developers
2017-09-29 06:21:28 +02:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-09-20 03:12:25 +02:00
# include <key_io.h>
2017-09-29 06:21:28 +02:00
# include <keystore.h>
# include <rpc/util.h>
# include <tinyformat.h>
2018-10-23 00:51:11 +02:00
# include <util/strencodings.h>
2017-09-29 06:21:28 +02:00
2017-05-30 21:55:17 +02:00
InitInterfaces * g_rpc_interfaces = nullptr ;
2017-09-29 06:21:28 +02:00
// Converts a hex string to a public key if possible
CPubKey HexToPubKey ( const std : : string & hex_in )
{
if ( ! IsHex ( hex_in ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid public key: " + hex_in ) ;
}
CPubKey vchPubKey ( ParseHex ( hex_in ) ) ;
if ( ! vchPubKey . IsFullyValid ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid public key: " + hex_in ) ;
}
return vchPubKey ;
}
// Retrieves a public key for an address from the given CKeyStore
CPubKey AddrToPubKey ( CKeyStore * const keystore , const std : : string & addr_in )
{
CTxDestination dest = DecodeDestination ( addr_in ) ;
if ( ! IsValidDestination ( dest ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid address: " + addr_in ) ;
}
CKeyID key = GetKeyForDestination ( * keystore , dest ) ;
if ( key . IsNull ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " %s does not refer to a key " , addr_in ) ) ;
}
CPubKey vchPubKey ;
if ( ! keystore - > GetPubKey ( key , vchPubKey ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , strprintf ( " no full public key for address %s " , addr_in ) ) ;
}
if ( ! vchPubKey . IsFullyValid ( ) ) {
throw JSONRPCError ( RPC_INTERNAL_ERROR , " Wallet contains an invalid public key " ) ;
}
return vchPubKey ;
}
// Creates a multisig redeemscript from a given list of public keys and number required.
CScript CreateMultisigRedeemscript ( const int required , const std : : vector < CPubKey > & pubkeys )
{
// Gather public keys
if ( required < 1 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " a multisignature address must require at least one key to redeem " ) ;
}
if ( ( int ) pubkeys . size ( ) < required ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " not enough keys supplied (got %u keys, but need at least %d to redeem) " , pubkeys.size(), required)) ;
}
if ( pubkeys . size ( ) > 16 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Number of keys involved in the multisignature address creation > 16 \n Reduce the number " ) ;
}
CScript result = GetScriptForMultisig ( required , pubkeys ) ;
if ( result . size ( ) > MAX_SCRIPT_ELEMENT_SIZE ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , ( strprintf ( " redeemScript exceeds size limit: %d > %d " , result . size ( ) , MAX_SCRIPT_ELEMENT_SIZE ) ) ) ;
}
return result ;
}
2017-12-04 18:49:20 +01:00
class DescribeAddressVisitor : public boost : : static_visitor < UniValue >
{
public :
explicit DescribeAddressVisitor ( ) { }
UniValue operator ( ) ( const CNoDestination & dest ) const
{
return UniValue ( UniValue : : VOBJ ) ;
}
UniValue operator ( ) ( const CKeyID & keyID ) const
{
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " isscript " , false ) ;
obj . pushKV ( " iswitness " , false ) ;
return obj ;
}
UniValue operator ( ) ( const CScriptID & scriptID ) const
{
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " isscript " , true ) ;
obj . pushKV ( " iswitness " , false ) ;
return obj ;
}
UniValue operator ( ) ( const WitnessV0KeyHash & id ) const
{
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " isscript " , false ) ;
obj . pushKV ( " iswitness " , true ) ;
obj . pushKV ( " witness_version " , 0 ) ;
obj . pushKV ( " witness_program " , HexStr ( id . begin ( ) , id . end ( ) ) ) ;
return obj ;
}
UniValue operator ( ) ( const WitnessV0ScriptHash & id ) const
{
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " isscript " , true ) ;
obj . pushKV ( " iswitness " , true ) ;
obj . pushKV ( " witness_version " , 0 ) ;
obj . pushKV ( " witness_program " , HexStr ( id . begin ( ) , id . end ( ) ) ) ;
return obj ;
}
UniValue operator ( ) ( const WitnessUnknown & id ) const
{
UniValue obj ( UniValue : : VOBJ ) ;
obj . pushKV ( " iswitness " , true ) ;
obj . pushKV ( " witness_version " , ( int ) id . version ) ;
obj . pushKV ( " witness_program " , HexStr ( id . program , id . program + id . length ) ) ;
return obj ;
}
} ;
UniValue DescribeAddress ( const CTxDestination & dest )
{
return boost : : apply_visitor ( DescribeAddressVisitor ( ) , dest ) ;
}
2018-10-23 21:22:28 +02:00
2017-07-29 03:40:29 +02:00
unsigned int ParseConfirmTarget ( const UniValue & value , unsigned int max_target )
2019-02-08 21:29:45 +01:00
{
int target = value . get_int ( ) ;
if ( target < 1 | | ( unsigned int ) target > max_target ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Invalid conf_target, must be between %u - %u " , 1 , max_target ) ) ;
}
return ( unsigned int ) target ;
}
2019-02-10 05:51:33 +01:00
RPCErrorCode RPCErrorFromTransactionError ( TransactionError terr )
{
switch ( terr ) {
case TransactionError : : MEMPOOL_REJECTED :
return RPC_TRANSACTION_REJECTED ;
case TransactionError : : ALREADY_IN_CHAIN :
return RPC_TRANSACTION_ALREADY_IN_CHAIN ;
case TransactionError : : P2P_DISABLED :
return RPC_CLIENT_P2P_DISABLED ;
case TransactionError : : INVALID_PSBT :
2019-01-09 12:08:32 +01:00
case TransactionError : : PSBT_MISMATCH :
return RPC_INVALID_PARAMETER ;
2019-02-10 05:51:33 +01:00
case TransactionError : : SIGHASH_MISMATCH :
return RPC_DESERIALIZATION_ERROR ;
default : break ;
}
return RPC_TRANSACTION_ERROR ;
}
UniValue JSONRPCTransactionError ( TransactionError terr , const std : : string & err_string )
{
if ( err_string . length ( ) > 0 ) {
return JSONRPCError ( RPCErrorFromTransactionError ( terr ) , err_string ) ;
} else {
return JSONRPCError ( RPCErrorFromTransactionError ( terr ) , TransactionErrorString ( terr ) ) ;
}
}
2018-11-23 17:21:38 +01:00
struct Section {
Section ( const std : : string & left , const std : : string & right )
: m_left { left } , m_right { right } { }
const std : : string m_left ;
const std : : string m_right ;
} ;
struct Sections {
std : : vector < Section > m_sections ;
size_t m_max_pad { 0 } ;
void PushSection ( const Section & s )
{
m_max_pad = std : : max ( m_max_pad , s . m_left . size ( ) ) ;
m_sections . push_back ( s ) ;
}
enum class OuterType {
ARR ,
OBJ ,
NAMED_ARG , // Only set on first recursion
} ;
void Push ( const RPCArg & arg , const size_t current_indent = 5 , const OuterType outer_type = OuterType : : NAMED_ARG )
{
const auto indent = std : : string ( current_indent , ' ' ) ;
const auto indent_next = std : : string ( current_indent + 2 , ' ' ) ;
switch ( arg . m_type ) {
case RPCArg : : Type : : STR_HEX :
case RPCArg : : Type : : STR :
case RPCArg : : Type : : NUM :
case RPCArg : : Type : : AMOUNT :
2019-02-27 22:45:47 +01:00
case RPCArg : : Type : : RANGE :
2018-11-23 17:21:38 +01:00
case RPCArg : : Type : : BOOL : {
if ( outer_type = = OuterType : : NAMED_ARG ) return ; // Nothing more to do for non-recursive types on first recursion
auto left = indent ;
if ( arg . m_type_str . size ( ) ! = 0 & & outer_type = = OuterType : : OBJ ) {
2018-12-04 19:30:06 +01:00
left + = " \" " + arg . m_name + " \" : " + arg . m_type_str . at ( 0 ) ;
2018-11-23 17:21:38 +01:00
} else {
2018-12-04 19:30:06 +01:00
left + = outer_type = = OuterType : : OBJ ? arg . ToStringObj ( /* oneline */ false ) : arg . ToString ( /* oneline */ false ) ;
2018-11-23 17:21:38 +01:00
}
left + = " , " ;
2018-12-10 22:56:51 +01:00
PushSection ( { left , arg . ToDescriptionString ( ) } ) ;
2018-11-23 17:21:38 +01:00
break ;
}
case RPCArg : : Type : : OBJ :
case RPCArg : : Type : : OBJ_USER_KEYS : {
2018-12-10 22:56:51 +01:00
const auto right = outer_type = = OuterType : : NAMED_ARG ? " " : arg . ToDescriptionString ( ) ;
2018-11-23 17:21:38 +01:00
PushSection ( { indent + " { " , right } ) ;
for ( const auto & arg_inner : arg . m_inner ) {
Push ( arg_inner , current_indent + 2 , OuterType : : OBJ ) ;
}
if ( arg . m_type ! = RPCArg : : Type : : OBJ ) {
PushSection ( { indent_next + " ... " , " " } ) ;
}
PushSection ( { indent + " } " + ( outer_type ! = OuterType : : NAMED_ARG ? " , " : " " ) , " " } ) ;
break ;
}
case RPCArg : : Type : : ARR : {
auto left = indent ;
2018-12-04 19:30:06 +01:00
left + = outer_type = = OuterType : : OBJ ? " \" " + arg . m_name + " \" : " : " " ;
2018-11-23 17:21:38 +01:00
left + = " [ " ;
2018-12-10 22:56:51 +01:00
const auto right = outer_type = = OuterType : : NAMED_ARG ? " " : arg . ToDescriptionString ( ) ;
2018-11-23 17:21:38 +01:00
PushSection ( { left , right } ) ;
for ( const auto & arg_inner : arg . m_inner ) {
Push ( arg_inner , current_indent + 2 , OuterType : : ARR ) ;
}
PushSection ( { indent_next + " ... " , " " } ) ;
PushSection ( { indent + " ] " + ( outer_type ! = OuterType : : NAMED_ARG ? " , " : " " ) , " " } ) ;
break ;
}
// no default case, so the compiler can warn about missing cases
}
}
std : : string ToString ( ) const
{
std : : string ret ;
const size_t pad = m_max_pad + 4 ;
for ( const auto & s : m_sections ) {
if ( s . m_right . empty ( ) ) {
ret + = s . m_left ;
ret + = " \n " ;
continue ;
}
std : : string left = s . m_left ;
left . resize ( pad , ' ' ) ;
ret + = left ;
// Properly pad after newlines
std : : string right ;
size_t begin = 0 ;
size_t new_line_pos = s . m_right . find_first_of ( ' \n ' ) ;
while ( true ) {
right + = s . m_right . substr ( begin , new_line_pos - begin ) ;
if ( new_line_pos = = std : : string : : npos ) {
break ; //No new line
}
right + = " \n " + std : : string ( pad , ' ' ) ;
begin = s . m_right . find_first_not_of ( ' ' , new_line_pos + 1 ) ;
if ( begin = = std : : string : : npos ) {
break ; // Empty line
}
new_line_pos = s . m_right . find_first_of ( ' \n ' , begin + 1 ) ;
}
ret + = right ;
ret + = " \n " ;
}
return ret ;
}
} ;
2018-12-21 18:29:36 +01:00
RPCHelpMan : : RPCHelpMan ( std : : string name , std : : string description , std : : vector < RPCArg > args , RPCResults results , RPCExamples examples )
: m_name { std : : move ( name ) } ,
m_description { std : : move ( description ) } ,
m_args { std : : move ( args ) } ,
m_results { std : : move ( results ) } ,
m_examples { std : : move ( examples ) }
2018-12-06 16:37:39 +01:00
{
std : : set < std : : string > named_args ;
for ( const auto & arg : m_args ) {
// Should have unique named arguments
assert ( named_args . insert ( arg . m_name ) . second ) ;
}
}
2018-12-21 18:29:36 +01:00
std : : string RPCResults : : ToDescriptionString ( ) const
{
std : : string result ;
for ( const auto & r : m_results ) {
if ( r . m_cond . empty ( ) ) {
result + = " \n Result: \n " ;
} else {
result + = " \n Result ( " + r . m_cond + " ): \n " ;
}
result + = r . m_result ;
}
return result ;
}
std : : string RPCExamples : : ToDescriptionString ( ) const
{
return m_examples . empty ( ) ? m_examples : " \n Examples: \n " + m_examples ;
}
2019-02-13 02:16:29 +01:00
bool RPCHelpMan : : IsValidNumArgs ( size_t num_args ) const
{
size_t num_required_args = 0 ;
for ( size_t n = m_args . size ( ) ; n > 0 ; - - n ) {
if ( ! m_args . at ( n - 1 ) . IsOptional ( ) ) {
num_required_args = n ;
break ;
}
}
return num_required_args < = num_args & & num_args < = m_args . size ( ) ;
}
2018-10-23 21:22:28 +02:00
std : : string RPCHelpMan : : ToString ( ) const
{
std : : string ret ;
2018-11-23 17:21:38 +01:00
// Oneline summary
2018-10-23 21:22:28 +02:00
ret + = m_name ;
2018-12-05 17:43:55 +01:00
bool was_optional { false } ;
2018-10-23 21:22:28 +02:00
for ( const auto & arg : m_args ) {
2019-02-13 02:16:29 +01:00
const bool optional = arg . IsOptional ( ) ;
2018-10-23 21:22:28 +02:00
ret + = " " ;
2018-12-10 22:56:51 +01:00
if ( optional ) {
2018-12-05 17:43:55 +01:00
if ( ! was_optional ) ret + = " ( " ;
was_optional = true ;
2018-10-23 21:22:28 +02:00
} else {
2018-12-05 17:43:55 +01:00
if ( was_optional ) ret + = " ) " ;
was_optional = false ;
2018-10-23 21:22:28 +02:00
}
2018-11-23 17:21:38 +01:00
ret + = arg . ToString ( /* oneline */ true ) ;
2018-10-23 21:22:28 +02:00
}
2018-12-05 17:43:55 +01:00
if ( was_optional ) ret + = " ) " ;
2018-10-23 21:22:28 +02:00
ret + = " \n " ;
2018-11-23 17:21:38 +01:00
// Description
2018-10-20 14:19:44 +02:00
ret + = m_description ;
2018-11-23 17:21:38 +01:00
// Arguments
Sections sections ;
for ( size_t i { 0 } ; i < m_args . size ( ) ; + + i ) {
const auto & arg = m_args . at ( i ) ;
if ( i = = 0 ) ret + = " \n Arguments: \n " ;
// Push named argument name and description
2018-12-05 17:43:55 +01:00
sections . m_sections . emplace_back ( std : : to_string ( i + 1 ) + " . " + arg . m_name , arg . ToDescriptionString ( ) ) ;
2018-11-23 17:21:38 +01:00
sections . m_max_pad = std : : max ( sections . m_max_pad , sections . m_sections . back ( ) . m_left . size ( ) ) ;
// Recursively push nested args
sections . Push ( arg ) ;
}
ret + = sections . ToString ( ) ;
2018-12-21 18:29:36 +01:00
// Result
ret + = m_results . ToDescriptionString ( ) ;
// Examples
ret + = m_examples . ToDescriptionString ( ) ;
2018-11-23 17:21:38 +01:00
return ret ;
}
2019-02-13 02:16:29 +01:00
bool RPCArg : : IsOptional ( ) const
{
if ( m_fallback . which ( ) = = 1 ) {
return true ;
} else {
return RPCArg : : Optional : : NO ! = boost : : get < RPCArg : : Optional > ( m_fallback ) ;
}
}
2018-12-10 22:56:51 +01:00
std : : string RPCArg : : ToDescriptionString ( ) const
2018-11-23 17:21:38 +01:00
{
std : : string ret ;
ret + = " ( " ;
if ( m_type_str . size ( ) ! = 0 ) {
ret + = m_type_str . at ( 1 ) ;
} else {
switch ( m_type ) {
case Type : : STR_HEX :
case Type : : STR : {
ret + = " string " ;
break ;
}
case Type : : NUM : {
ret + = " numeric " ;
break ;
}
case Type : : AMOUNT : {
ret + = " numeric or string " ;
break ;
}
2019-02-27 22:45:47 +01:00
case Type : : RANGE : {
ret + = " numeric or array " ;
break ;
}
2018-11-23 17:21:38 +01:00
case Type : : BOOL : {
ret + = " boolean " ;
break ;
}
case Type : : OBJ :
case Type : : OBJ_USER_KEYS : {
ret + = " json object " ;
break ;
}
case Type : : ARR : {
ret + = " json array " ;
break ;
}
// no default case, so the compiler can warn about missing cases
}
}
2018-12-10 22:56:51 +01:00
if ( m_fallback . which ( ) = = 1 ) {
ret + = " , optional, default= " + boost : : get < std : : string > ( m_fallback ) ;
} else {
switch ( boost : : get < RPCArg : : Optional > ( m_fallback ) ) {
case RPCArg : : Optional : : OMITTED : {
// nothing to do. Element is treated as if not present and has no default value
break ;
}
case RPCArg : : Optional : : OMITTED_NAMED_ARG : {
ret + = " , optional " ; // Default value is "null"
break ;
}
case RPCArg : : Optional : : NO : {
ret + = " , required " ;
break ;
}
// no default case, so the compiler can warn about missing cases
2018-11-23 17:21:38 +01:00
}
}
ret + = " ) " ;
ret + = m_description . empty ( ) ? " " : " " + m_description ;
2018-10-23 21:22:28 +02:00
return ret ;
}
2018-12-04 19:30:06 +01:00
std : : string RPCArg : : ToStringObj ( const bool oneline ) const
2018-10-23 21:22:28 +02:00
{
2018-12-04 19:30:06 +01:00
std : : string res ;
res + = " \" " ;
res + = m_name ;
if ( oneline ) {
res + = " \" : " ;
} else {
res + = " \" : " ;
}
2018-10-23 21:22:28 +02:00
switch ( m_type ) {
case Type : : STR :
return res + " \" str \" " ;
case Type : : STR_HEX :
return res + " \" hex \" " ;
case Type : : NUM :
return res + " n " ;
2019-02-27 22:45:47 +01:00
case Type : : RANGE :
return res + " n or [n,n] " ;
2018-10-23 21:22:28 +02:00
case Type : : AMOUNT :
return res + " amount " ;
case Type : : BOOL :
return res + " bool " ;
case Type : : ARR :
res + = " [ " ;
for ( const auto & i : m_inner ) {
2018-12-04 19:30:06 +01:00
res + = i . ToString ( oneline ) + " , " ;
2018-10-23 21:22:28 +02:00
}
return res + " ...] " ;
case Type : : OBJ :
case Type : : OBJ_USER_KEYS :
// Currently unused, so avoid writing dead code
assert ( false ) ;
// no default case, so the compiler can warn about missing cases
}
assert ( false ) ;
}
2018-11-23 17:21:38 +01:00
std : : string RPCArg : : ToString ( const bool oneline ) const
2018-10-23 21:22:28 +02:00
{
2018-11-23 17:21:38 +01:00
if ( oneline & & ! m_oneline_description . empty ( ) ) return m_oneline_description ;
2018-10-20 14:19:44 +02:00
2018-10-23 21:22:28 +02:00
switch ( m_type ) {
case Type : : STR_HEX :
case Type : : STR : {
return " \" " + m_name + " \" " ;
}
case Type : : NUM :
2019-02-27 22:45:47 +01:00
case Type : : RANGE :
2018-10-23 21:22:28 +02:00
case Type : : AMOUNT :
case Type : : BOOL : {
return m_name ;
}
case Type : : OBJ :
case Type : : OBJ_USER_KEYS : {
std : : string res ;
for ( size_t i = 0 ; i < m_inner . size ( ) ; ) {
2018-12-04 19:30:06 +01:00
res + = m_inner [ i ] . ToStringObj ( oneline ) ;
2018-10-23 21:22:28 +02:00
if ( + + i < m_inner . size ( ) ) res + = " , " ;
}
if ( m_type = = Type : : OBJ ) {
return " { " + res + " } " ;
} else {
return " { " + res + " ,...} " ;
}
}
case Type : : ARR : {
std : : string res ;
for ( const auto & i : m_inner ) {
2018-11-23 17:21:38 +01:00
res + = i . ToString ( oneline ) + " , " ;
2018-10-23 21:22:28 +02:00
}
return " [ " + res + " ...] " ;
}
// no default case, so the compiler can warn about missing cases
}
assert ( false ) ;
}
2019-02-27 22:41:41 +01:00
std : : pair < int64_t , int64_t > ParseRange ( const UniValue & value )
{
if ( value . isNum ( ) ) {
return { 0 , value . get_int64 ( ) } ;
}
if ( value . isArray ( ) & & value . size ( ) = = 2 & & value [ 0 ] . isNum ( ) & & value [ 1 ] . isNum ( ) ) {
int64_t low = value [ 0 ] . get_int64 ( ) ;
int64_t high = value [ 1 ] . get_int64 ( ) ;
if ( low > high ) throw JSONRPCError ( RPC_INVALID_PARAMETER , " Range specified as [begin,end] must not have begin after end " ) ;
return { low , high } ;
}
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Range must be specified as end or as [begin,end] " ) ;
}