2018-07-27 00:36:45 +02:00
// Copyright (c) 2009-2018 The Bitcoin Core developers
2014-11-20 03:19:29 +01:00
// Distributed under the MIT software license, see the accompanying
2012-05-18 16:02:28 +02:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2011-07-13 11:56:38 +02:00
2017-11-10 01:57:53 +01:00
# include <chain.h>
2018-11-06 16:40:50 +01:00
# include <core_io.h>
2017-07-26 16:23:01 +02:00
# include <interfaces/chain.h>
2017-09-20 03:12:25 +02:00
# include <key_io.h>
2018-11-06 16:40:50 +01:00
# include <merkleblock.h>
2017-11-10 01:57:53 +01:00
# include <rpc/server.h>
2018-11-06 16:40:50 +01:00
# include <rpc/util.h>
2017-11-10 01:57:53 +01:00
# include <script/script.h>
# include <script/standard.h>
# include <sync.h>
2018-10-23 00:51:11 +02:00
# include <util/system.h>
# include <util/time.h>
2018-11-06 16:40:50 +01:00
# include <validation.h>
2017-11-10 01:57:53 +01:00
# include <wallet/wallet.h>
2011-07-13 11:56:38 +02:00
2017-11-10 01:57:53 +01:00
# include <wallet/rpcwallet.h>
2017-01-06 18:53:06 +01:00
2013-04-13 07:13:08 +02:00
# include <stdint.h>
# include <boost/algorithm/string.hpp>
2013-04-29 19:50:56 +02:00
# include <boost/date_time/posix_time/posix_time.hpp>
2014-09-14 12:43:56 +02:00
2015-09-04 16:11:34 +02:00
# include <univalue.h>
2012-04-05 03:19:27 +02:00
2015-07-05 14:17:46 +02:00
2013-04-13 07:13:08 +02:00
int64_t static DecodeDumpTime ( const std : : string & str ) {
2013-04-29 19:50:56 +02:00
static const boost : : posix_time : : ptime epoch = boost : : posix_time : : from_time_t ( 0 ) ;
2014-02-17 11:32:43 +01:00
static const std : : locale loc ( std : : locale : : classic ( ) ,
new boost : : posix_time : : time_input_facet ( " %Y-%m-%dT%H:%M:%SZ " ) ) ;
2013-04-29 19:50:56 +02:00
std : : istringstream iss ( str ) ;
iss . imbue ( loc ) ;
boost : : posix_time : : ptime ptime ( boost : : date_time : : not_a_date_time ) ;
iss > > ptime ;
if ( ptime . is_not_a_date_time ( ) )
return 0 ;
return ( ptime - epoch ) . total_seconds ( ) ;
}
std : : string static EncodeDumpString ( const std : : string & str ) {
std : : stringstream ret ;
2018-06-18 07:58:28 +02:00
for ( const unsigned char c : str ) {
2013-04-29 19:50:56 +02:00
if ( c < = 32 | | c > = 128 | | c = = ' % ' ) {
ret < < ' % ' < < HexStr ( & c , & c + 1 ) ;
} else {
ret < < c ;
}
}
return ret . str ( ) ;
}
2018-05-02 17:14:48 +02:00
static std : : string DecodeDumpString ( const std : : string & str ) {
2013-04-29 19:50:56 +02:00
std : : stringstream ret ;
for ( unsigned int pos = 0 ; pos < str . length ( ) ; pos + + ) {
unsigned char c = str [ pos ] ;
if ( c = = ' % ' & & pos + 2 < str . length ( ) ) {
2018-05-22 17:18:07 +02:00
c = ( ( ( str [ pos + 1 ] > > 6 ) * 9 + ( ( str [ pos + 1 ] - ' 0 ' ) & 15 ) ) < < 4 ) |
2013-04-29 19:50:56 +02:00
( ( str [ pos + 2 ] > > 6 ) * 9 + ( ( str [ pos + 2 ] - ' 0 ' ) & 15 ) ) ;
pos + = 2 ;
}
ret < < c ;
2011-07-13 11:56:38 +02:00
}
2013-04-29 19:50:56 +02:00
return ret . str ( ) ;
}
2011-07-13 11:56:38 +02:00
2018-05-02 17:14:48 +02:00
static bool GetWalletAddressesForKey ( CWallet * const pwallet , const CKeyID & keyid , std : : string & strAddr , std : : string & strLabel )
2018-01-31 16:48:20 +01:00
{
bool fLabelFound = false ;
CKey key ;
pwallet - > GetKey ( keyid , key ) ;
for ( const auto & dest : GetAllDestinationsForKey ( key . GetPubKey ( ) ) ) {
if ( pwallet - > mapAddressBook . count ( dest ) ) {
if ( ! strAddr . empty ( ) ) {
strAddr + = " , " ;
}
strAddr + = EncodeDestination ( dest ) ;
strLabel = EncodeDumpString ( pwallet - > mapAddressBook [ dest ] . name ) ;
fLabelFound = true ;
}
}
if ( ! fLabelFound ) {
2018-02-11 03:06:35 +01:00
strAddr = EncodeDestination ( GetDestinationForKey ( key . GetPubKey ( ) , pwallet - > m_default_address_type ) ) ;
2018-01-31 16:48:20 +01:00
}
return fLabelFound ;
}
2018-07-13 17:49:24 +02:00
static const int64_t TIMESTAMP_MIN = 0 ;
static void RescanWallet ( CWallet & wallet , const WalletRescanReserver & reserver , int64_t time_begin = TIMESTAMP_MIN , bool update = true )
{
int64_t scanned_time = wallet . RescanFromTime ( time_begin , reserver , update ) ;
if ( wallet . IsAbortingRescan ( ) ) {
throw JSONRPCError ( RPC_MISC_ERROR , " Rescan aborted by user. " ) ;
} else if ( scanned_time > time_begin ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Rescan was unable to fully rescan the blockchain. Some transactions may be missing. " ) ;
}
}
2018-01-31 16:48:20 +01:00
2016-09-22 09:46:41 +02:00
UniValue importprivkey ( const JSONRPCRequest & request )
2011-07-13 11:56:38 +02:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2015-05-10 13:35:44 +02:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2017-01-07 20:15:22 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) < 1 | | request . params . size ( ) > 3 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2017-10-09 05:18:48 +02:00
" importprivkey \" privkey \" ( \" label \" ) ( rescan ) \n "
2017-09-08 14:19:25 +02:00
" \n Adds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup. \n "
2018-03-16 01:58:24 +01:00
" Hint: use importmulti to import more than one private key. \n "
2013-10-29 12:29:44 +01:00
" \n Arguments: \n "
2017-10-09 05:18:48 +02:00
" 1. \" privkey \" (string, required) The private key (see dumpprivkey) \n "
2018-10-05 14:33:21 +02:00
" 2. \" label \" (string, optional, default=current label if address exists, otherwise \" \" ) An optional label \n "
2013-10-29 12:29:44 +01:00
" 3. rescan (boolean, optional, default=true) Rescan the wallet for transactions \n "
2018-08-08 07:39:34 +02:00
" \n Note: This call can take over an hour to complete if rescan is true, during that time, other rpc calls \n "
2018-01-05 21:43:31 +01:00
" may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes. \n "
2013-10-29 12:29:44 +01:00
" \n Examples: \n "
" \n Dump a private key \n "
+ HelpExampleCli ( " dumpprivkey " , " \" myaddress \" " ) +
2014-07-22 08:58:49 +02:00
" \n Import the private key with rescan \n "
2013-10-29 12:29:44 +01:00
+ HelpExampleCli ( " importprivkey " , " \" mykey \" " ) +
2014-07-22 08:58:49 +02:00
" \n Import using a label and without rescan \n "
2013-10-29 12:29:44 +01:00
+ HelpExampleCli ( " importprivkey " , " \" mykey \" \" testing \" false " ) +
2017-04-14 05:11:42 +02:00
" \n Import using default blank label and without rescan \n "
+ HelpExampleCli ( " importprivkey " , " \" mykey \" \" \" false " ) +
2014-07-22 08:58:49 +02:00
" \n As a JSON-RPC call \n "
2013-10-29 12:29:44 +01:00
+ HelpExampleRpc ( " importprivkey " , " \" mykey \" , \" testing \" , false " )
) ;
2011-07-13 11:56:38 +02:00
2015-04-24 21:42:51 +02:00
2017-12-13 00:13:58 +01:00
WalletRescanReserver reserver ( pwallet ) ;
2012-12-06 20:05:11 +01:00
bool fRescan = true ;
2017-09-08 01:29:59 +02:00
{
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2012-12-06 20:05:11 +01:00
2017-09-08 01:29:59 +02:00
EnsureWalletIsUnlocked ( pwallet ) ;
2015-09-07 03:28:32 +02:00
2017-09-08 01:29:59 +02:00
std : : string strSecret = request . params [ 0 ] . get_str ( ) ;
std : : string strLabel = " " ;
if ( ! request . params [ 1 ] . isNull ( ) )
strLabel = request . params [ 1 ] . get_str ( ) ;
2011-07-13 11:56:38 +02:00
2017-09-08 01:29:59 +02:00
// Whether to perform rescan after import
if ( ! request . params [ 2 ] . isNull ( ) )
fRescan = request . params [ 2 ] . get_bool ( ) ;
2011-07-13 11:56:38 +02:00
2017-09-08 01:29:59 +02:00
if ( fRescan & & fPruneMode )
throw JSONRPCError ( RPC_WALLET_ERROR , " Rescan is disabled in pruned mode " ) ;
2014-01-28 09:38:10 +01:00
2017-12-13 00:13:58 +01:00
if ( fRescan & & ! reserver . reserve ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Wallet is currently rescanning. Abort existing rescan or wait. " ) ;
}
2017-09-20 01:49:52 +02:00
CKey key = DecodeSecret ( strSecret ) ;
if ( ! key . IsValid ( ) ) throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid private key encoding " ) ;
2013-04-29 19:50:56 +02:00
2017-09-08 01:29:59 +02:00
CPubKey pubkey = key . GetPubKey ( ) ;
assert ( key . VerifyPubKey ( pubkey ) ) ;
CKeyID vchAddress = pubkey . GetID ( ) ;
{
pwallet - > MarkDirty ( ) ;
2018-10-05 14:33:21 +02:00
// We don't know which corresponding address will be used;
// label all new addresses, and label existing addresses if a
// label was passed.
2017-09-08 01:29:59 +02:00
for ( const auto & dest : GetAllDestinationsForKey ( pubkey ) ) {
2018-10-05 14:33:21 +02:00
if ( ! request . params [ 1 ] . isNull ( ) | | pwallet - > mapAddressBook . count ( dest ) = = 0 ) {
pwallet - > SetAddressBook ( dest , strLabel , " receive " ) ;
}
2017-09-08 01:29:59 +02:00
}
2014-01-09 20:23:20 +01:00
2017-09-08 01:29:59 +02:00
// Don't throw error in case a key is already there
if ( pwallet - > HaveKey ( vchAddress ) ) {
return NullUniValue ;
}
2013-05-01 06:52:05 +02:00
2017-09-08 01:29:59 +02:00
// whenever a key is imported, we need to scan the whole chain
pwallet - > UpdateTimeFirstKey ( 1 ) ;
pwallet - > mapKeyMetadata [ vchAddress ] . nCreateTime = 1 ;
2014-01-09 20:23:20 +01:00
2017-09-08 01:29:59 +02:00
if ( ! pwallet - > AddKeyPubKey ( key , pubkey ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding key to wallet " ) ;
}
pwallet - > LearnAllRelatedScripts ( pubkey ) ;
2012-12-06 20:05:11 +01:00
}
2011-07-13 11:56:38 +02:00
}
2017-09-08 01:29:59 +02:00
if ( fRescan ) {
2018-07-13 17:49:24 +02:00
RescanWallet ( * pwallet , reserver ) ;
2017-09-08 01:29:59 +02:00
}
2011-07-13 11:56:38 +02:00
2014-08-20 21:15:16 +02:00
return NullUniValue ;
2011-07-13 11:56:38 +02:00
}
2017-04-17 16:32:29 +02:00
UniValue abortrescan ( const JSONRPCRequest & request )
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2017-04-17 16:32:29 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
return NullUniValue ;
}
if ( request . fHelp | | request . params . size ( ) > 0 )
throw std : : runtime_error (
" abortrescan \n "
2017-11-22 20:06:53 +01:00
" \n Stops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call. \n "
2017-04-17 16:32:29 +02:00
" \n Examples: \n "
" \n Import a private key \n "
+ HelpExampleCli ( " importprivkey " , " \" mykey \" " ) +
" \n Abort the running wallet rescan \n "
+ HelpExampleCli ( " abortrescan " , " " ) +
" \n As a JSON-RPC call \n "
+ HelpExampleRpc ( " abortrescan " , " " )
) ;
if ( ! pwallet - > IsScanning ( ) | | pwallet - > IsAbortingRescan ( ) ) return false ;
pwallet - > AbortRescan ( ) ;
return true ;
}
2018-05-02 17:14:48 +02:00
static void ImportAddress ( CWallet * , const CTxDestination & dest , const std : : string & strLabel ) ;
2018-04-25 23:16:16 +02:00
static void ImportScript ( CWallet * const pwallet , const CScript & script , const std : : string & strLabel , bool isRedeemScript ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
2015-06-11 09:57:26 +02:00
{
2016-10-25 10:04:23 +02:00
if ( ! isRedeemScript & & : : IsMine ( * pwallet , script ) = = ISMINE_SPENDABLE ) {
2015-06-11 09:57:26 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " The wallet already contains the private key for this address or script " ) ;
2016-10-25 10:04:23 +02:00
}
2015-06-11 09:57:26 +02:00
2016-09-09 07:32:12 +02:00
pwallet - > MarkDirty ( ) ;
2015-06-11 09:57:26 +02:00
2016-10-25 10:04:23 +02:00
if ( ! pwallet - > HaveWatchOnly ( script ) & & ! pwallet - > AddWatchOnly ( script , 0 /* nCreateTime */ ) ) {
2015-06-11 09:57:26 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding address to wallet " ) ;
2016-10-25 10:04:23 +02:00
}
2015-06-11 09:57:50 +02:00
if ( isRedeemScript ) {
2018-04-18 23:41:32 +02:00
const CScriptID id ( script ) ;
if ( ! pwallet - > HaveCScript ( id ) & & ! pwallet - > AddCScript ( script ) ) {
2015-06-11 09:57:50 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding p2sh redeemScript to wallet " ) ;
2016-10-25 10:04:23 +02:00
}
2018-04-18 23:41:32 +02:00
ImportAddress ( pwallet , id , strLabel ) ;
2016-03-14 18:55:19 +01:00
} else {
CTxDestination destination ;
if ( ExtractDestination ( script , destination ) ) {
2016-09-09 07:32:12 +02:00
pwallet - > SetAddressBook ( destination , strLabel , " receive " ) ;
2016-03-14 18:55:19 +01:00
}
2015-06-11 09:57:50 +02:00
}
2015-06-11 09:57:26 +02:00
}
2018-04-25 23:16:16 +02:00
static void ImportAddress ( CWallet * const pwallet , const CTxDestination & dest , const std : : string & strLabel ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
2015-06-11 09:57:26 +02:00
{
2017-08-23 03:02:33 +02:00
CScript script = GetScriptForDestination ( dest ) ;
2016-09-09 07:32:12 +02:00
ImportScript ( pwallet , script , strLabel , false ) ;
2015-06-11 09:57:26 +02:00
// add to address book or update label
2017-08-23 03:02:33 +02:00
if ( IsValidDestination ( dest ) )
pwallet - > SetAddressBook ( dest , strLabel , " receive " ) ;
2015-06-11 09:57:26 +02:00
}
2016-09-22 09:46:41 +02:00
UniValue importaddress ( const JSONRPCRequest & request )
2013-07-26 01:06:01 +02:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2015-05-10 13:35:44 +02:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2017-01-07 20:15:22 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) < 1 | | request . params . size ( ) > 4 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2015-06-11 09:57:50 +02:00
" importaddress \" address \" ( \" label \" rescan p2sh ) \n "
2018-04-25 20:08:35 +02:00
" \n Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup. \n "
2014-07-22 08:58:49 +02:00
" \n Arguments: \n "
2018-04-25 20:08:35 +02:00
" 1. \" address \" (string, required) The Bitcoin address (or hex-encoded script) \n "
2014-07-22 08:58:49 +02:00
" 2. \" label \" (string, optional, default= \" \" ) An optional label \n "
" 3. rescan (boolean, optional, default=true) Rescan the wallet for transactions \n "
2015-06-11 09:57:50 +02:00
" 4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well \n "
2018-08-08 07:39:34 +02:00
" \n Note: This call can take over an hour to complete if rescan is true, during that time, other rpc calls \n "
2018-01-05 21:43:31 +01:00
" may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes. \n "
2016-03-21 04:16:19 +01:00
" If you have the full public key, you should call importpubkey instead of this. \n "
2016-03-14 18:55:19 +01:00
" \n Note: If you import a non-standard raw script in hex form, outputs sending to it will be treated \n "
" as change, and not show up in many RPCs. \n "
2014-07-22 08:58:49 +02:00
" \n Examples: \n "
2018-04-25 20:08:35 +02:00
" \n Import an address with rescan \n "
+ HelpExampleCli ( " importaddress " , " \" myaddress \" " ) +
2014-07-22 08:58:49 +02:00
" \n Import using a label without rescan \n "
2018-04-25 20:08:35 +02:00
+ HelpExampleCli ( " importaddress " , " \" myaddress \" \" testing \" false " ) +
2014-07-22 08:58:49 +02:00
" \n As a JSON-RPC call \n "
2018-04-25 20:08:35 +02:00
+ HelpExampleRpc ( " importaddress " , " \" myaddress \" , \" testing \" , false " )
2014-07-22 08:58:49 +02:00
) ;
2014-06-09 21:11:59 +02:00
2015-04-24 21:42:51 +02:00
2018-01-14 18:15:31 +01:00
std : : string strLabel ;
2017-07-10 17:44:39 +02:00
if ( ! request . params [ 1 ] . isNull ( ) )
2016-09-22 09:46:41 +02:00
strLabel = request . params [ 1 ] . get_str ( ) ;
2014-10-19 10:46:17 +02:00
2014-08-30 02:35:05 +02:00
// Whether to perform rescan after import
2013-07-26 01:06:01 +02:00
bool fRescan = true ;
2017-07-10 17:44:39 +02:00
if ( ! request . params [ 2 ] . isNull ( ) )
2016-09-22 09:46:41 +02:00
fRescan = request . params [ 2 ] . get_bool ( ) ;
2013-07-26 01:06:01 +02:00
2015-09-07 03:28:32 +02:00
if ( fRescan & & fPruneMode )
throw JSONRPCError ( RPC_WALLET_ERROR , " Rescan is disabled in pruned mode " ) ;
2017-12-08 22:07:37 +01:00
WalletRescanReserver reserver ( pwallet ) ;
if ( fRescan & & ! reserver . reserve ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Wallet is currently rescanning. Abort existing rescan or wait. " ) ;
}
2015-06-11 09:57:50 +02:00
// Whether to import a p2sh version, too
bool fP2SH = false ;
2017-07-10 17:44:39 +02:00
if ( ! request . params [ 3 ] . isNull ( ) )
2016-09-22 09:46:41 +02:00
fP2SH = request . params [ 3 ] . get_bool ( ) ;
2013-07-26 01:06:01 +02:00
2017-09-08 01:29:59 +02:00
{
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2013-07-26 01:06:01 +02:00
2017-09-08 01:29:59 +02:00
CTxDestination dest = DecodeDestination ( request . params [ 0 ] . get_str ( ) ) ;
if ( IsValidDestination ( dest ) ) {
if ( fP2SH ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Cannot use the p2sh flag with an address - use a script instead " ) ;
}
ImportAddress ( pwallet , dest , strLabel ) ;
} else if ( IsHex ( request . params [ 0 ] . get_str ( ) ) ) {
std : : vector < unsigned char > data ( ParseHex ( request . params [ 0 ] . get_str ( ) ) ) ;
ImportScript ( pwallet , CScript ( data . begin ( ) , data . end ( ) ) , strLabel , fP2SH ) ;
} else {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid Bitcoin address or script " ) ;
2017-08-23 03:02:33 +02:00
}
2014-06-09 21:11:59 +02:00
}
2015-06-11 09:57:26 +02:00
if ( fRescan )
{
2018-07-13 17:49:24 +02:00
RescanWallet ( * pwallet , reserver ) ;
2016-10-25 09:45:57 +02:00
pwallet - > ReacceptWalletTransactions ( ) ;
2013-07-26 01:06:01 +02:00
}
2014-08-20 21:15:16 +02:00
return NullUniValue ;
2013-07-26 01:06:01 +02:00
}
2016-09-22 09:46:41 +02:00
UniValue importprunedfunds ( const JSONRPCRequest & request )
2016-02-19 01:31:12 +01:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2016-02-19 01:31:12 +01:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2016-02-19 01:31:12 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) ! = 2 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2018-11-13 19:42:36 +01:00
" importprunedfunds \" rawtransaction \" \" txoutproof \" \n "
2016-02-19 01:31:12 +01:00
" \n Imports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included. \n "
" \n Arguments: \n "
" 1. \" rawtransaction \" (string, required) A raw transaction in hex funding an already-existing address in wallet \n "
" 2. \" txoutproof \" (string, required) The hex output from gettxoutproof that contains the transaction \n "
) ;
2016-11-30 23:50:20 +01:00
CMutableTransaction tx ;
2016-09-22 09:46:41 +02:00
if ( ! DecodeHexTx ( tx , request . params [ 0 ] . get_str ( ) ) )
2016-02-19 01:31:12 +01:00
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
uint256 hashTx = tx . GetHash ( ) ;
2016-10-25 09:45:57 +02:00
CWalletTx wtx ( pwallet , MakeTransactionRef ( std : : move ( tx ) ) ) ;
2016-02-19 01:31:12 +01:00
2016-09-22 09:46:41 +02:00
CDataStream ssMB ( ParseHexV ( request . params [ 1 ] , " proof " ) , SER_NETWORK , PROTOCOL_VERSION ) ;
2016-02-19 01:31:12 +01:00
CMerkleBlock merkleBlock ;
ssMB > > merkleBlock ;
//Search partial merkle tree in proof for our transaction and index in valid block
2017-01-27 02:33:45 +01:00
std : : vector < uint256 > vMatch ;
std : : vector < unsigned int > vIndex ;
2016-02-19 01:31:12 +01:00
unsigned int txnIndex = 0 ;
if ( merkleBlock . txn . ExtractMatches ( vMatch , vIndex ) = = merkleBlock . header . hashMerkleRoot ) {
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
2018-01-12 01:23:09 +01:00
const CBlockIndex * pindex = LookupBlockIndex ( merkleBlock . header . GetHash ( ) ) ;
if ( ! pindex | | ! chainActive . Contains ( pindex ) ) {
2016-02-19 01:31:12 +01:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Block not found in chain " ) ;
2018-01-12 01:23:09 +01:00
}
2016-02-19 01:31:12 +01:00
2017-01-27 02:33:45 +01:00
std : : vector < uint256 > : : const_iterator it ;
2016-02-19 01:31:12 +01:00
if ( ( it = std : : find ( vMatch . begin ( ) , vMatch . end ( ) , hashTx ) ) = = vMatch . end ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Transaction given doesn't exist in proof " ) ;
}
txnIndex = vIndex [ it - vMatch . begin ( ) ] ;
}
else {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Something wrong with merkleblock " ) ;
}
wtx . nIndex = txnIndex ;
wtx . hashBlock = merkleBlock . header . GetHash ( ) ;
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2016-02-19 01:31:12 +01:00
2017-05-09 08:46:26 +02:00
if ( pwallet - > IsMine ( * wtx . tx ) ) {
2016-10-25 09:45:57 +02:00
pwallet - > AddToWallet ( wtx , false ) ;
2016-02-19 01:31:12 +01:00
return NullUniValue ;
}
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " No addresses in wallet correspond to included transaction " ) ;
}
2016-09-22 09:46:41 +02:00
UniValue removeprunedfunds ( const JSONRPCRequest & request )
2016-03-07 14:51:06 +01:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2016-03-07 14:51:06 +01:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2016-03-07 14:51:06 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) ! = 1 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2016-03-07 14:51:06 +01:00
" removeprunedfunds \" txid \" \n "
2017-05-27 17:44:10 +02:00
" \n Deletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances. \n "
2016-03-07 14:51:06 +01:00
" \n Arguments: \n "
" 1. \" txid \" (string, required) The hex-encoded id of the transaction you are deleting \n "
" \n Examples: \n "
+ HelpExampleCli ( " removeprunedfunds " , " \" a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5 \" " ) +
" \n As a JSON-RPC call \n "
2017-07-03 09:29:13 +02:00
+ HelpExampleRpc ( " removeprunedfunds " , " \" a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5 \" " )
2016-03-07 14:51:06 +01:00
) ;
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2016-03-07 14:51:06 +01:00
2018-06-08 20:16:07 +02:00
uint256 hash ( ParseHashV ( request . params [ 0 ] , " txid " ) ) ;
2017-01-27 02:33:45 +01:00
std : : vector < uint256 > vHash ;
2016-03-07 14:51:06 +01:00
vHash . push_back ( hash ) ;
2017-01-27 02:33:45 +01:00
std : : vector < uint256 > vHashOut ;
2016-03-07 14:51:06 +01:00
2018-03-09 15:03:40 +01:00
if ( pwallet - > ZapSelectTx ( vHash , vHashOut ) ! = DBErrors : : LOAD_OK ) {
2017-02-09 22:27:28 +01:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Could not properly delete the transaction. " ) ;
2016-03-07 14:51:06 +01:00
}
if ( vHashOut . empty ( ) ) {
2017-02-09 22:27:28 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Transaction does not exist in wallet. " ) ;
2016-03-07 14:51:06 +01:00
}
return NullUniValue ;
}
2016-09-22 09:46:41 +02:00
UniValue importpubkey ( const JSONRPCRequest & request )
2015-07-10 07:47:36 +02:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2015-07-10 07:47:36 +02:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2015-07-10 07:47:36 +02:00
2018-06-19 23:34:38 +02:00
if ( request . fHelp | | request . params . size ( ) < 1 | | request . params . size ( ) > 3 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2015-07-10 07:47:36 +02:00
" importpubkey \" pubkey \" ( \" label \" rescan ) \n "
2017-09-08 14:19:25 +02:00
" \n Adds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup. \n "
2015-07-10 07:47:36 +02:00
" \n Arguments: \n "
" 1. \" pubkey \" (string, required) The hex-encoded public key \n "
" 2. \" label \" (string, optional, default= \" \" ) An optional label \n "
" 3. rescan (boolean, optional, default=true) Rescan the wallet for transactions \n "
2018-08-08 07:39:34 +02:00
" \n Note: This call can take over an hour to complete if rescan is true, during that time, other rpc calls \n "
2018-01-05 21:43:31 +01:00
" may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes. \n "
2015-07-10 07:47:36 +02:00
" \n Examples: \n "
" \n Import a public key with rescan \n "
+ HelpExampleCli ( " importpubkey " , " \" mypubkey \" " ) +
" \n Import using a label without rescan \n "
+ HelpExampleCli ( " importpubkey " , " \" mypubkey \" \" testing \" false " ) +
" \n As a JSON-RPC call \n "
+ HelpExampleRpc ( " importpubkey " , " \" mypubkey \" , \" testing \" , false " )
) ;
2018-01-14 18:15:31 +01:00
std : : string strLabel ;
2017-07-10 17:44:39 +02:00
if ( ! request . params [ 1 ] . isNull ( ) )
2016-09-22 09:46:41 +02:00
strLabel = request . params [ 1 ] . get_str ( ) ;
2013-07-26 01:06:01 +02:00
2014-08-30 02:35:05 +02:00
// Whether to perform rescan after import
2013-07-26 01:06:01 +02:00
bool fRescan = true ;
2017-07-10 17:44:39 +02:00
if ( ! request . params [ 2 ] . isNull ( ) )
2016-09-22 09:46:41 +02:00
fRescan = request . params [ 2 ] . get_bool ( ) ;
2013-07-26 01:06:01 +02:00
2015-09-07 03:28:32 +02:00
if ( fRescan & & fPruneMode )
throw JSONRPCError ( RPC_WALLET_ERROR , " Rescan is disabled in pruned mode " ) ;
2017-12-08 22:07:37 +01:00
WalletRescanReserver reserver ( pwallet ) ;
if ( fRescan & & ! reserver . reserve ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Wallet is currently rescanning. Abort existing rescan or wait. " ) ;
}
2016-09-22 09:46:41 +02:00
if ( ! IsHex ( request . params [ 0 ] . get_str ( ) ) )
2015-07-10 07:47:36 +02:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Pubkey must be a hex string " ) ;
2016-09-22 09:46:41 +02:00
std : : vector < unsigned char > data ( ParseHex ( request . params [ 0 ] . get_str ( ) ) ) ;
2015-07-10 07:47:36 +02:00
CPubKey pubKey ( data . begin ( ) , data . end ( ) ) ;
if ( ! pubKey . IsFullyValid ( ) )
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Pubkey is not a valid public key " ) ;
2013-07-26 01:06:01 +02:00
2017-09-08 01:29:59 +02:00
{
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2013-07-26 01:06:01 +02:00
2017-09-08 01:29:59 +02:00
for ( const auto & dest : GetAllDestinationsForKey ( pubKey ) ) {
ImportAddress ( pwallet , dest , strLabel ) ;
}
ImportScript ( pwallet , GetScriptForRawPubKey ( pubKey ) , strLabel , false ) ;
pwallet - > LearnAllRelatedScripts ( pubKey ) ;
2017-12-01 01:49:11 +01:00
}
2015-07-10 07:47:36 +02:00
if ( fRescan )
{
2018-07-13 17:49:24 +02:00
RescanWallet ( * pwallet , reserver ) ;
2016-10-25 09:45:57 +02:00
pwallet - > ReacceptWalletTransactions ( ) ;
2013-07-26 01:06:01 +02:00
}
2014-08-20 21:15:16 +02:00
return NullUniValue ;
2013-07-26 01:06:01 +02:00
}
2015-07-10 07:47:36 +02:00
2016-09-22 09:46:41 +02:00
UniValue importwallet ( const JSONRPCRequest & request )
2013-04-29 19:50:56 +02:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2015-05-10 13:35:44 +02:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2017-01-07 20:15:22 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) ! = 1 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2013-10-29 12:29:44 +01:00
" importwallet \" filename \" \n "
2017-09-08 14:19:25 +02:00
" \n Imports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys. \n "
2013-10-29 12:29:44 +01:00
" \n Arguments: \n "
" 1. \" filename \" (string, required) The wallet file \n "
" \n Examples: \n "
" \n Dump the wallet \n "
+ HelpExampleCli ( " dumpwallet " , " \" test \" " ) +
" \n Import the wallet \n "
+ HelpExampleCli ( " importwallet " , " \" test \" " ) +
" \n Import using the json rpc call \n "
+ HelpExampleRpc ( " importwallet " , " \" test \" " )
) ;
2013-04-29 19:50:56 +02:00
2015-04-24 21:42:51 +02:00
if ( fPruneMode )
throw JSONRPCError ( RPC_WALLET_ERROR , " Importing wallets is disabled in pruned mode " ) ;
2017-12-08 22:07:37 +01:00
WalletRescanReserver reserver ( pwallet ) ;
if ( ! reserver . reserve ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Wallet is currently rescanning. Abort existing rescan or wait. " ) ;
}
2017-09-08 01:29:59 +02:00
int64_t nTimeBegin = 0 ;
bool fGood = true ;
{
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 10:46:17 +02:00
2017-09-08 01:29:59 +02:00
EnsureWalletIsUnlocked ( pwallet ) ;
2013-04-29 19:50:56 +02:00
2018-08-04 15:32:13 +02:00
fsbridge : : ifstream file ;
file . open ( request . params [ 0 ] . get_str ( ) , std : : ios : : in | std : : ios : : ate ) ;
2017-09-08 01:29:59 +02:00
if ( ! file . is_open ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot open wallet dump file " ) ;
}
nTimeBegin = chainActive . Tip ( ) - > GetBlockTime ( ) ;
2013-04-29 19:50:56 +02:00
2017-09-08 01:29:59 +02:00
int64_t nFilesize = std : : max ( ( int64_t ) 1 , ( int64_t ) file . tellg ( ) ) ;
file . seekg ( 0 , file . beg ) ;
2013-04-13 07:13:08 +02:00
2018-03-31 22:02:58 +02:00
// Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
2018-04-24 21:12:02 +02:00
// we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
2018-06-16 01:02:52 +02:00
uiInterface . ShowProgress ( strprintf ( " %s " + _ ( " Importing... " ) , pwallet - > GetDisplayName ( ) ) , 0 , false ) ; // show progress dialog in GUI
2017-09-08 01:29:59 +02:00
while ( file . good ( ) ) {
2018-03-31 22:02:58 +02:00
uiInterface . ShowProgress ( " " , std : : max ( 1 , std : : min ( 99 , ( int ) ( ( ( double ) file . tellg ( ) / ( double ) nFilesize ) * 100 ) ) ) , false ) ;
2017-09-08 01:29:59 +02:00
std : : string line ;
std : : getline ( file , line ) ;
if ( line . empty ( ) | | line [ 0 ] = = ' # ' )
continue ;
2013-04-29 19:50:56 +02:00
2017-09-08 01:29:59 +02:00
std : : vector < std : : string > vstr ;
boost : : split ( vstr , line , boost : : is_any_of ( " " ) ) ;
if ( vstr . size ( ) < 2 )
2017-11-12 04:28:46 +01:00
continue ;
2017-09-20 01:49:52 +02:00
CKey key = DecodeSecret ( vstr [ 0 ] ) ;
if ( key . IsValid ( ) ) {
2017-09-08 01:29:59 +02:00
CPubKey pubkey = key . GetPubKey ( ) ;
assert ( key . VerifyPubKey ( pubkey ) ) ;
CKeyID keyid = pubkey . GetID ( ) ;
if ( pwallet - > HaveKey ( keyid ) ) {
2018-06-16 01:02:52 +02:00
pwallet - > WalletLogPrintf ( " Skipping import of %s (key already present) \n " , EncodeDestination ( keyid ) ) ;
2017-09-08 01:29:59 +02:00
continue ;
2017-11-12 04:28:46 +01:00
}
2017-09-08 01:29:59 +02:00
int64_t nTime = DecodeDumpTime ( vstr [ 1 ] ) ;
std : : string strLabel ;
bool fLabel = true ;
for ( unsigned int nStr = 2 ; nStr < vstr . size ( ) ; nStr + + ) {
2018-07-22 21:34:45 +02:00
if ( vstr [ nStr ] . front ( ) = = ' # ' )
2017-09-08 01:29:59 +02:00
break ;
if ( vstr [ nStr ] = = " change=1 " )
fLabel = false ;
if ( vstr [ nStr ] = = " reserve=1 " )
fLabel = false ;
2018-07-22 21:34:45 +02:00
if ( vstr [ nStr ] . substr ( 0 , 6 ) = = " label= " ) {
2017-09-08 01:29:59 +02:00
strLabel = DecodeDumpString ( vstr [ nStr ] . substr ( 6 ) ) ;
fLabel = true ;
}
}
2018-06-16 01:02:52 +02:00
pwallet - > WalletLogPrintf ( " Importing %s... \n " , EncodeDestination ( keyid ) ) ;
2017-09-08 01:29:59 +02:00
if ( ! pwallet - > AddKeyPubKey ( key , pubkey ) ) {
fGood = false ;
continue ;
}
pwallet - > mapKeyMetadata [ keyid ] . nCreateTime = nTime ;
if ( fLabel )
pwallet - > SetAddressBook ( keyid , strLabel , " receive " ) ;
nTimeBegin = std : : min ( nTimeBegin , nTime ) ;
} else if ( IsHex ( vstr [ 0 ] ) ) {
std : : vector < unsigned char > vData ( ParseHex ( vstr [ 0 ] ) ) ;
CScript script = CScript ( vData . begin ( ) , vData . end ( ) ) ;
2018-04-18 23:41:32 +02:00
CScriptID id ( script ) ;
if ( pwallet - > HaveCScript ( id ) ) {
2018-06-16 01:02:52 +02:00
pwallet - > WalletLogPrintf ( " Skipping import of %s (script already present) \n " , vstr [ 0 ] ) ;
2017-09-08 01:29:59 +02:00
continue ;
}
if ( ! pwallet - > AddCScript ( script ) ) {
2018-06-16 01:02:52 +02:00
pwallet - > WalletLogPrintf ( " Error importing script %s \n " , vstr [ 0 ] ) ;
2017-09-08 01:29:59 +02:00
fGood = false ;
continue ;
}
int64_t birth_time = DecodeDumpTime ( vstr [ 1 ] ) ;
if ( birth_time > 0 ) {
2018-04-18 23:41:32 +02:00
pwallet - > m_script_metadata [ id ] . nCreateTime = birth_time ;
2017-09-08 01:29:59 +02:00
nTimeBegin = std : : min ( nTimeBegin , birth_time ) ;
}
2017-11-12 04:28:46 +01:00
}
2013-04-29 19:50:56 +02:00
}
2017-09-08 01:29:59 +02:00
file . close ( ) ;
2018-03-31 22:02:58 +02:00
uiInterface . ShowProgress ( " " , 100 , false ) ; // hide progress dialog in GUI
2017-09-08 01:29:59 +02:00
pwallet - > UpdateTimeFirstKey ( nTimeBegin ) ;
2013-04-29 19:50:56 +02:00
}
2017-08-31 02:34:03 +02:00
uiInterface . ShowProgress ( " " , 100 , false ) ; // hide progress dialog in GUI
2018-07-13 17:49:24 +02:00
RescanWallet ( * pwallet , reserver , nTimeBegin , false /* update */ ) ;
2016-10-25 09:45:57 +02:00
pwallet - > MarkDirty ( ) ;
2013-04-29 19:50:56 +02:00
if ( ! fGood )
2017-11-12 04:28:46 +01:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding some keys/scripts to wallet " ) ;
2013-04-29 19:50:56 +02:00
2014-08-20 21:15:16 +02:00
return NullUniValue ;
2013-04-29 19:50:56 +02:00
}
2016-09-22 09:46:41 +02:00
UniValue dumpprivkey ( const JSONRPCRequest & request )
2011-07-13 11:56:38 +02:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2015-05-10 13:35:44 +02:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2017-01-07 20:15:22 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) ! = 1 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2016-11-21 14:39:58 +01:00
" dumpprivkey \" address \" \n "
" \n Reveals the private key corresponding to 'address'. \n "
2013-10-29 12:29:44 +01:00
" Then the importprivkey can be used with this output \n "
" \n Arguments: \n "
2016-11-21 14:39:58 +01:00
" 1. \" address \" (string, required) The bitcoin address for the private key \n "
2013-10-29 12:29:44 +01:00
" \n Result: \n "
" \" key \" (string) The private key \n "
" \n Examples: \n "
+ HelpExampleCli ( " dumpprivkey " , " \" myaddress \" " )
+ HelpExampleCli ( " importprivkey " , " \" mykey \" " )
+ HelpExampleRpc ( " dumpprivkey " , " \" myaddress \" " )
) ;
2011-07-13 11:56:38 +02:00
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 10:46:17 +02:00
2016-10-25 09:45:57 +02:00
EnsureWalletIsUnlocked ( pwallet ) ;
2013-04-29 19:50:56 +02:00
2017-01-27 02:33:45 +01:00
std : : string strAddress = request . params [ 0 ] . get_str ( ) ;
2017-08-23 03:02:33 +02:00
CTxDestination dest = DecodeDestination ( strAddress ) ;
if ( ! IsValidDestination ( dest ) ) {
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid Bitcoin address " ) ;
2017-08-23 03:02:33 +02:00
}
2017-12-01 01:48:55 +01:00
auto keyid = GetKeyForDestination ( * pwallet , dest ) ;
if ( keyid . IsNull ( ) ) {
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_TYPE_ERROR , " Address does not refer to a key " ) ;
2017-08-23 03:02:33 +02:00
}
2013-05-01 06:52:05 +02:00
CKey vchSecret ;
2017-12-01 01:48:55 +01:00
if ( ! pwallet - > GetKey ( keyid , vchSecret ) ) {
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Private key for address " + strAddress + " is not known " ) ;
2016-10-25 10:04:23 +02:00
}
2017-09-20 01:49:52 +02:00
return EncodeSecret ( vchSecret ) ;
2011-07-13 11:56:38 +02:00
}
2013-04-29 19:50:56 +02:00
2016-09-22 09:46:41 +02:00
UniValue dumpwallet ( const JSONRPCRequest & request )
2013-04-29 19:50:56 +02:00
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( request ) ;
CWallet * const pwallet = wallet . get ( ) ;
2016-10-25 10:04:23 +02:00
if ( ! EnsureWalletIsAvailable ( pwallet , request . fHelp ) ) {
2015-05-10 13:35:44 +02:00
return NullUniValue ;
2016-10-25 10:04:23 +02:00
}
2017-01-07 20:15:22 +01:00
2016-09-22 09:46:41 +02:00
if ( request . fHelp | | request . params . size ( ) ! = 1 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2013-10-29 12:29:44 +01:00
" dumpwallet \" filename \" \n "
2017-03-07 09:50:41 +01:00
" \n Dumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files. \n "
2017-11-12 05:11:26 +01:00
" Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet. \n "
2017-10-19 11:01:30 +02:00
" Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by \n "
" only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile). \n "
2013-10-29 12:29:44 +01:00
" \n Arguments: \n "
2017-02-10 13:47:53 +01:00
" 1. \" filename \" (string, required) The filename with path (either absolute or relative to bitcoind) \n "
" \n Result: \n "
" { (json object) \n "
" \" filename \" : { (string) The filename with full absolute path \n "
" } \n "
2013-10-29 12:29:44 +01:00
" \n Examples: \n "
+ HelpExampleCli ( " dumpwallet " , " \" test \" " )
+ HelpExampleRpc ( " dumpwallet " , " \" test \" " )
) ;
2013-04-29 19:50:56 +02:00
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2014-10-19 10:46:17 +02:00
2016-10-25 09:45:57 +02:00
EnsureWalletIsUnlocked ( pwallet ) ;
2013-04-29 19:50:56 +02:00
2018-08-15 15:05:21 +02:00
fs : : path filepath = request . params [ 0 ] . get_str ( ) ;
filepath = fs : : absolute ( filepath ) ;
2017-03-07 09:50:41 +01:00
/* Prevent arbitrary files from being overwritten. There have been reports
* that users have overwritten wallet files this way :
* https : //github.com/bitcoin/bitcoin/issues/9934
* It may also avoid other security issues .
*/
2018-08-15 15:05:21 +02:00
if ( fs : : exists ( filepath ) ) {
2017-03-07 09:50:41 +01:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , filepath . string ( ) + " already exists. If you are sure this is what you want, move it out of the way first " ) ;
}
2018-08-04 15:32:13 +02:00
fsbridge : : ofstream file ;
file . open ( filepath ) ;
2013-04-29 19:50:56 +02:00
if ( ! file . is_open ( ) )
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot open wallet dump file " ) ;
2016-11-08 22:55:02 +01:00
std : : map < CTxDestination , int64_t > mapKeyBirth ;
2017-07-21 19:54:13 +02:00
const std : : map < CKeyID , int64_t > & mapKeyPool = pwallet - > GetAllReserveKeys ( ) ;
2017-07-31 21:30:21 +02:00
pwallet - > GetKeyBirthTimes ( * locked_chain , mapKeyBirth ) ;
2013-04-29 19:50:56 +02:00
2017-11-11 09:21:12 +01:00
std : : set < CScriptID > scripts = pwallet - > GetCScripts ( ) ;
2017-12-20 07:01:05 +01:00
// TODO: include scripts in GetKeyBirthTimes() output instead of separate
2017-11-11 09:21:12 +01:00
2013-04-29 19:50:56 +02:00
// sort time/key pairs
2013-04-13 07:13:08 +02:00
std : : vector < std : : pair < int64_t , CKeyID > > vKeyBirth ;
2016-11-08 22:55:02 +01:00
for ( const auto & entry : mapKeyBirth ) {
if ( const CKeyID * keyID = boost : : get < CKeyID > ( & entry . first ) ) { // set and test
vKeyBirth . push_back ( std : : make_pair ( entry . second , * keyID ) ) ;
}
2013-04-29 19:50:56 +02:00
}
mapKeyBirth . clear ( ) ;
std : : sort ( vKeyBirth . begin ( ) , vKeyBirth . end ( ) ) ;
// produce output
2016-06-09 11:09:21 +02:00
file < < strprintf ( " # Wallet dump created by Bitcoin %s \n " , CLIENT_BUILD ) ;
2018-02-28 16:46:31 +01:00
file < < strprintf ( " # * Created on %s \n " , FormatISO8601DateTime ( GetTime ( ) ) ) ;
2014-01-16 16:15:27 +01:00
file < < strprintf ( " # * Best block at time of backup was %i (%s), \n " , chainActive . Height ( ) , chainActive . Tip ( ) - > GetBlockHash ( ) . ToString ( ) ) ;
2018-02-28 16:46:31 +01:00
file < < strprintf ( " # mined on %s \n " , FormatISO8601DateTime ( chainActive . Tip ( ) - > GetBlockTime ( ) ) ) ;
2013-04-29 19:50:56 +02:00
file < < " \n " ;
2016-06-15 10:49:29 +02:00
2017-09-08 01:29:59 +02:00
// add the base58check encoded extended master if the wallet uses HD
2018-04-04 17:43:45 +02:00
CKeyID seed_id = pwallet - > GetHDChain ( ) . seed_id ;
if ( ! seed_id . IsNull ( ) )
2016-06-15 10:49:29 +02:00
{
2018-04-04 18:47:55 +02:00
CKey seed ;
if ( pwallet - > GetKey ( seed_id , seed ) ) {
2016-06-15 10:49:29 +02:00
CExtKey masterKey ;
2018-04-04 18:47:55 +02:00
masterKey . SetSeed ( seed . begin ( ) , seed . size ( ) ) ;
2016-06-15 10:49:29 +02:00
2017-09-20 02:13:46 +02:00
file < < " # extended private masterkey: " < < EncodeExtKey ( masterKey ) < < " \n \n " ;
2016-06-15 10:49:29 +02:00
}
}
2013-04-13 07:13:08 +02:00
for ( std : : vector < std : : pair < int64_t , CKeyID > > : : const_iterator it = vKeyBirth . begin ( ) ; it ! = vKeyBirth . end ( ) ; it + + ) {
2013-04-29 19:50:56 +02:00
const CKeyID & keyid = it - > second ;
2018-02-28 16:46:31 +01:00
std : : string strTime = FormatISO8601DateTime ( it - > first ) ;
2018-01-31 16:48:20 +01:00
std : : string strAddr ;
std : : string strLabel ;
2013-04-29 19:50:56 +02:00
CKey key ;
2016-10-25 09:45:57 +02:00
if ( pwallet - > GetKey ( keyid , key ) ) {
2017-09-20 01:49:52 +02:00
file < < strprintf ( " %s %s " , EncodeSecret ( key ) , strTime ) ;
2018-01-31 16:48:20 +01:00
if ( GetWalletAddressesForKey ( pwallet , keyid , strAddr , strLabel ) ) {
file < < strprintf ( " label=%s " , strLabel ) ;
2018-04-04 17:43:45 +02:00
} else if ( keyid = = seed_id ) {
file < < " hdseed=1 " ;
2017-07-21 19:54:13 +02:00
} else if ( mapKeyPool . count ( keyid ) ) {
2016-06-15 10:49:29 +02:00
file < < " reserve=1 " ;
2018-04-04 18:47:55 +02:00
} else if ( pwallet - > mapKeyMetadata [ keyid ] . hdKeypath = = " s " ) {
file < < " inactivehdseed=1 " ;
2013-04-29 19:50:56 +02:00
} else {
2016-06-15 10:49:29 +02:00
file < < " change=1 " ;
2013-04-29 19:50:56 +02:00
}
2016-10-25 09:45:57 +02:00
file < < strprintf ( " # addr=%s%s \n " , strAddr , ( pwallet - > mapKeyMetadata [ keyid ] . hdKeypath . size ( ) > 0 ? " hdkeypath= " + pwallet - > mapKeyMetadata [ keyid ] . hdKeypath : " " ) ) ;
2013-04-29 19:50:56 +02:00
}
}
file < < " \n " ;
2017-11-11 09:21:12 +01:00
for ( const CScriptID & scriptid : scripts ) {
CScript script ;
2017-12-20 07:01:05 +01:00
std : : string create_time = " 0 " ;
2017-11-11 09:21:12 +01:00
std : : string address = EncodeDestination ( scriptid ) ;
2017-12-20 07:01:05 +01:00
// get birth times for scripts with metadata
auto it = pwallet - > m_script_metadata . find ( scriptid ) ;
if ( it ! = pwallet - > m_script_metadata . end ( ) ) {
2018-02-28 16:46:31 +01:00
create_time = FormatISO8601DateTime ( it - > second . nCreateTime ) ;
2017-12-20 07:01:05 +01:00
}
2017-11-11 09:21:12 +01:00
if ( pwallet - > GetCScript ( scriptid , script ) ) {
2017-12-20 07:01:05 +01:00
file < < strprintf ( " %s %s script=1 " , HexStr ( script . begin ( ) , script . end ( ) ) , create_time ) ;
2017-11-11 09:21:12 +01:00
file < < strprintf ( " # addr=%s \n " , address ) ;
}
}
file < < " \n " ;
2013-04-29 19:50:56 +02:00
file < < " # End of dump \n " ;
file . close ( ) ;
2017-02-10 13:47:53 +01:00
UniValue reply ( UniValue : : VOBJ ) ;
2017-09-22 20:04:07 +02:00
reply . pushKV ( " filename " , filepath . string ( ) ) ;
2017-02-10 13:47:53 +01:00
return reply ;
2013-04-29 19:50:56 +02:00
}
2016-06-16 16:57:48 +02:00
2018-04-25 23:16:16 +02:00
static UniValue ProcessImport ( CWallet * const pwallet , const UniValue & data , const int64_t timestamp ) EXCLUSIVE_LOCKS_REQUIRED ( pwallet - > cs_wallet )
2017-02-03 22:23:13 +01:00
{
2016-06-16 16:57:48 +02:00
try {
2018-10-09 06:29:27 +02:00
// First ensure scriptPubKey has either a script or JSON with "address" string
2016-06-16 16:57:48 +02:00
const UniValue & scriptPubKey = data [ " scriptPubKey " ] ;
2018-10-09 06:29:27 +02:00
bool isScript = scriptPubKey . getType ( ) = = UniValue : : VSTR ;
if ( ! isScript & & ! ( scriptPubKey . getType ( ) = = UniValue : : VOBJ & & scriptPubKey . exists ( " address " ) ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " scriptPubKey must be string with script or JSON with address string " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
const std : : string & output = isScript ? scriptPubKey . get_str ( ) : scriptPubKey [ " address " ] . get_str ( ) ;
2016-06-16 16:57:48 +02:00
// Optional fields.
2017-01-27 02:33:45 +01:00
const std : : string & strRedeemScript = data . exists ( " redeemscript " ) ? data [ " redeemscript " ] . get_str ( ) : " " ;
2018-10-09 06:29:27 +02:00
const std : : string & witness_script_hex = data . exists ( " witnessscript " ) ? data [ " witnessscript " ] . get_str ( ) : " " ;
2016-06-16 16:57:48 +02:00
const UniValue & pubKeys = data . exists ( " pubkeys " ) ? data [ " pubkeys " ] . get_array ( ) : UniValue ( ) ;
const UniValue & keys = data . exists ( " keys " ) ? data [ " keys " ] . get_array ( ) : UniValue ( ) ;
2017-07-16 02:07:52 +02:00
const bool internal = data . exists ( " internal " ) ? data [ " internal " ] . get_bool ( ) : false ;
const bool watchOnly = data . exists ( " watchonly " ) ? data [ " watchonly " ] . get_bool ( ) : false ;
2018-10-09 06:29:27 +02:00
const std : : string & label = data . exists ( " label " ) ? data [ " label " ] . get_str ( ) : " " ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Generate the script and destination for the scriptPubKey provided
2016-06-16 16:57:48 +02:00
CScript script ;
2017-08-23 03:02:33 +02:00
CTxDestination dest ;
2016-06-16 16:57:48 +02:00
if ( ! isScript ) {
2017-08-23 03:02:33 +02:00
dest = DecodeDestination ( output ) ;
if ( ! IsValidDestination ( dest ) ) {
2017-02-13 23:54:51 +01:00
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid address " ) ;
}
2017-08-23 03:02:33 +02:00
script = GetScriptForDestination ( dest ) ;
2016-06-16 16:57:48 +02:00
} else {
if ( ! IsHex ( output ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid scriptPubKey " ) ;
}
std : : vector < unsigned char > vData ( ParseHex ( output ) ) ;
script = CScript ( vData . begin ( ) , vData . end ( ) ) ;
2016-11-28 23:19:27 +01:00
if ( ! ExtractDestination ( script , dest ) & & ! internal ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Internal must be set to true for nonstandard scriptPubKey imports. " ) ;
}
2016-06-16 16:57:48 +02:00
}
// Watchonly and private keys
if ( watchOnly & & keys . size ( ) ) {
2018-10-09 06:29:27 +02:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Watch-only addresses should not include private keys " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
// Internal addresses should not have a label
2016-06-16 16:57:48 +02:00
if ( internal & & data . exists ( " label " ) ) {
2018-10-09 06:29:27 +02:00
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Internal addresses should not have a label " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
// Force users to provide the witness script in its field rather than redeemscript
if ( ! strRedeemScript . empty ( ) & & script . IsPayToWitnessScriptHash ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " P2WSH addresses have an empty redeemscript. Please provide the witnessscript instead. " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
CScript scriptpubkey_script = script ;
CTxDestination scriptpubkey_dest = dest ;
bool allow_p2wpkh = true ;
2016-06-16 16:57:48 +02:00
// P2SH
2018-10-09 06:29:27 +02:00
if ( ! strRedeemScript . empty ( ) & & script . IsPayToScriptHash ( ) ) {
// Check the redeemScript is valid
if ( ! IsHex ( strRedeemScript ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid redeem script: must be hex string " ) ;
}
2016-06-16 16:57:48 +02:00
// Import redeem script.
std : : vector < unsigned char > vData ( ParseHex ( strRedeemScript ) ) ;
CScript redeemScript = CScript ( vData . begin ( ) , vData . end ( ) ) ;
2018-10-09 06:29:27 +02:00
CScriptID redeem_id ( redeemScript ) ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Check that the redeemScript and scriptPubKey match
if ( GetScriptForDestination ( redeem_id ) ! = script ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " The redeemScript does not match the scriptPubKey " ) ;
2016-06-16 16:57:48 +02:00
}
2016-09-09 07:32:12 +02:00
pwallet - > MarkDirty ( ) ;
2016-06-16 16:57:48 +02:00
2017-02-21 16:53:07 +01:00
if ( ! pwallet - > AddWatchOnly ( redeemScript , timestamp ) ) {
2016-06-16 16:57:48 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding address to wallet " ) ;
}
2018-04-18 23:41:32 +02:00
if ( ! pwallet - > HaveCScript ( redeem_id ) & & ! pwallet - > AddCScript ( redeemScript ) ) {
2016-06-16 16:57:48 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding p2sh redeemScript to wallet " ) ;
}
2018-10-09 06:29:27 +02:00
// Now set script to the redeemScript so we parse the inner script as P2WSH or P2WPKH below
script = redeemScript ;
ExtractDestination ( script , dest ) ;
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// (P2SH-)P2WSH
if ( ! witness_script_hex . empty ( ) & & script . IsPayToWitnessScriptHash ( ) ) {
if ( ! IsHex ( witness_script_hex ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid witness script: must be hex string " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
// Generate the scripts
std : : vector < unsigned char > witness_script_parsed ( ParseHex ( witness_script_hex ) ) ;
CScript witness_script = CScript ( witness_script_parsed . begin ( ) , witness_script_parsed . end ( ) ) ;
CScriptID witness_id ( witness_script ) ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Check that the witnessScript and scriptPubKey match
if ( GetScriptForDestination ( WitnessV0ScriptHash ( witness_script ) ) ! = script ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " The witnessScript does not match the scriptPubKey or redeemScript " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
// Add the witness script as watch only only if it is not for P2SH-P2WSH
if ( ! scriptpubkey_script . IsPayToScriptHash ( ) & & ! pwallet - > AddWatchOnly ( witness_script , timestamp ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding address to wallet " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
if ( ! pwallet - > HaveCScript ( witness_id ) & & ! pwallet - > AddCScript ( witness_script ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding p2wsh witnessScript to wallet " ) ;
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Now set script to the witnessScript so we parse the inner script as P2PK or P2PKH below
script = witness_script ;
ExtractDestination ( script , dest ) ;
allow_p2wpkh = false ; // P2WPKH cannot be embedded in P2WSH
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// (P2SH-)P2PK/P2PKH/P2WPKH
if ( dest . type ( ) = = typeid ( CKeyID ) | | dest . type ( ) = = typeid ( WitnessV0KeyHash ) ) {
if ( ! allow_p2wpkh & & dest . type ( ) = = typeid ( WitnessV0KeyHash ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " P2WPKH cannot be embedded in P2WSH " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
if ( keys . size ( ) > 1 | | pubKeys . size ( ) > 1 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " More than one key given for one single-key address " ) ;
}
CPubKey pubkey ;
if ( keys . size ( ) ) {
pubkey = DecodeSecret ( keys [ 0 ] . get_str ( ) ) . GetPubKey ( ) ;
}
if ( pubKeys . size ( ) ) {
2017-01-27 02:33:45 +01:00
const std : : string & strPubKey = pubKeys [ 0 ] . get_str ( ) ;
2016-06-16 16:57:48 +02:00
if ( ! IsHex ( strPubKey ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Pubkey must be a hex string " ) ;
}
2018-10-09 06:29:27 +02:00
std : : vector < unsigned char > vData ( ParseHex ( pubKeys [ 0 ] . get_str ( ) ) ) ;
CPubKey pubkey_temp ( vData . begin ( ) , vData . end ( ) ) ;
if ( pubkey . size ( ) & & pubkey_temp ! = pubkey ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Private key does not match public key for address " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
pubkey = pubkey_temp ;
}
if ( pubkey . size ( ) > 0 ) {
if ( ! pubkey . IsFullyValid ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Pubkey is not a valid public key " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
// Check the key corresponds to the destination given
std : : vector < CTxDestination > destinations = GetAllDestinationsForKey ( pubkey ) ;
if ( std : : find ( destinations . begin ( ) , destinations . end ( ) , dest ) = = destinations . end ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Key does not match address destination " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
// This is necessary to force the wallet to import the pubKey
CScript scriptRawPubKey = GetScriptForRawPubKey ( pubkey ) ;
2016-06-16 16:57:48 +02:00
2016-09-09 07:32:12 +02:00
if ( : : IsMine ( * pwallet , scriptRawPubKey ) = = ISMINE_SPENDABLE ) {
2016-06-16 16:57:48 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " The wallet already contains the private key for this address or script " ) ;
}
2016-09-09 07:32:12 +02:00
pwallet - > MarkDirty ( ) ;
2016-06-16 16:57:48 +02:00
2017-02-21 16:53:07 +01:00
if ( ! pwallet - > AddWatchOnly ( scriptRawPubKey , timestamp ) ) {
2016-06-16 16:57:48 +02:00
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding address to wallet " ) ;
}
}
2018-10-09 06:29:27 +02:00
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Import the address
if ( : : IsMine ( * pwallet , scriptpubkey_script ) = = ISMINE_SPENDABLE ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " The wallet already contains the private key for this address or script " ) ;
}
2016-10-19 16:17:42 +02:00
2018-10-09 06:29:27 +02:00
pwallet - > MarkDirty ( ) ;
2016-10-19 16:17:42 +02:00
2018-10-09 06:29:27 +02:00
if ( ! pwallet - > AddWatchOnly ( scriptpubkey_script , timestamp ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding address to wallet " ) ;
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
if ( ! watchOnly & & ! pwallet - > HaveCScript ( CScriptID ( scriptpubkey_script ) ) & & ! pwallet - > AddCScript ( scriptpubkey_script ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding scriptPubKey script to wallet " ) ;
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// add to address book or update label
if ( IsValidDestination ( scriptpubkey_dest ) ) {
pwallet - > SetAddressBook ( scriptpubkey_dest , label , " receive " ) ;
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Import private keys.
for ( size_t i = 0 ; i < keys . size ( ) ; i + + ) {
const std : : string & strPrivkey = keys [ i ] . get_str ( ) ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
// Checks.
CKey key = DecodeSecret ( strPrivkey ) ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
if ( ! key . IsValid ( ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid private key encoding " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
CPubKey pubKey = key . GetPubKey ( ) ;
assert ( key . VerifyPubKey ( pubKey ) ) ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
CKeyID vchAddress = pubKey . GetID ( ) ;
pwallet - > MarkDirty ( ) ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
if ( pwallet - > HaveKey ( vchAddress ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Already have this key " ) ;
}
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
pwallet - > mapKeyMetadata [ vchAddress ] . nCreateTime = timestamp ;
2016-06-16 16:57:48 +02:00
2018-10-09 06:29:27 +02:00
if ( ! pwallet - > AddKeyPubKey ( key , pubKey ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error adding key to wallet " ) ;
2016-06-16 16:57:48 +02:00
}
2018-10-09 06:29:27 +02:00
pwallet - > UpdateTimeFirstKey ( timestamp ) ;
2016-06-16 16:57:48 +02:00
}
UniValue result = UniValue ( UniValue : : VOBJ ) ;
2018-10-09 06:29:27 +02:00
result . pushKV ( " success " , UniValue ( true ) ) ;
2016-06-16 16:57:48 +02:00
return result ;
} catch ( const UniValue & e ) {
UniValue result = UniValue ( UniValue : : VOBJ ) ;
result . pushKV ( " success " , UniValue ( false ) ) ;
result . pushKV ( " error " , e ) ;
return result ;
} catch ( . . . ) {
UniValue result = UniValue ( UniValue : : VOBJ ) ;
result . pushKV ( " success " , UniValue ( false ) ) ;
result . pushKV ( " error " , JSONRPCError ( RPC_MISC_ERROR , " Missing required fields " ) ) ;
return result ;
}
}
2018-05-02 17:14:48 +02:00
static int64_t GetImportTimestamp ( const UniValue & data , int64_t now )
2017-02-03 22:23:13 +01:00
{
if ( data . exists ( " timestamp " ) ) {
const UniValue & timestamp = data [ " timestamp " ] ;
if ( timestamp . isNum ( ) ) {
return timestamp . get_int64 ( ) ;
} else if ( timestamp . isStr ( ) & & timestamp . get_str ( ) = = " now " ) {
return now ;
}
throw JSONRPCError ( RPC_TYPE_ERROR , strprintf ( " Expected number or \" now \" timestamp value for key. got type %s " , uvTypeName ( timestamp . type ( ) ) ) ) ;
}
throw JSONRPCError ( RPC_TYPE_ERROR , " Missing required timestamp field for key " ) ;
}
2016-06-16 16:57:48 +02:00
UniValue importmulti ( const JSONRPCRequest & mainRequest )
{
2018-05-22 17:18:07 +02:00
std : : shared_ptr < CWallet > const wallet = GetWalletForJSONRPCRequest ( mainRequest ) ;
CWallet * const pwallet = wallet . get ( ) ;
2017-01-07 20:15:22 +01:00
if ( ! EnsureWalletIsAvailable ( pwallet , mainRequest . fHelp ) ) {
return NullUniValue ;
}
2016-10-25 09:45:57 +02:00
2016-06-16 16:57:48 +02:00
// clang-format off
if ( mainRequest . fHelp | | mainRequest . params . size ( ) < 1 | | mainRequest . params . size ( ) > 2 )
2017-01-27 02:33:45 +01:00
throw std : : runtime_error (
2018-11-13 19:42:36 +01:00
" importmulti \" requests \" ( \" options \" ) \n "
" \n Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup. \n \n "
2016-06-16 16:57:48 +02:00
" Arguments: \n "
2016-11-21 14:32:36 +01:00
" 1. requests (array, required) Data to be imported \n "
2016-06-16 16:57:48 +02:00
" [ (array of json objects) \n "
" { \n "
" \" scriptPubKey \" : \" <script> \" | { \" address \" : \" <address> \" }, (string / json, required) Type of scriptPubKey (string for script, json for address) \n "
2017-02-03 22:23:13 +01:00
" \" timestamp \" : timestamp | \" now \" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT), \n "
" or the string \" now \" to substitute the current synced blockchain time. The timestamp of the oldest \n "
" key will determine how far back blockchain rescans need to begin for missing wallet transactions. \n "
" \" now \" can be specified to bypass scanning, for keys which are known to never have been used, and \n "
2017-02-14 23:39:26 +01:00
" 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key \n "
" creation time of all keys being imported by the importmulti call will be scanned. \n "
2018-10-09 06:29:27 +02:00
" \" redeemscript \" : \" <script> \" , (string, optional) Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey \n "
" \" witnessscript \" : \" <script> \" , (string, optional) Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey \n "
2016-06-16 16:57:48 +02:00
" \" pubkeys \" : [ \" <pubKey> \" , ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript \n "
" \" keys \" : [ \" <key> \" , ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript \n "
2017-06-20 00:57:31 +02:00
" \" internal \" : <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments \n "
2016-06-16 16:57:48 +02:00
" \" watchonly \" : <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty \n "
2018-07-27 23:05:24 +02:00
" \" label \" : <label> , (string, optional, default: '') Label to assign to the address, only allowed with internal=false \n "
2016-06-16 16:57:48 +02:00
" } \n "
" ,... \n "
" ] \n "
2016-11-21 14:32:36 +01:00
" 2. options (json, optional) \n "
2016-06-16 16:57:48 +02:00
" { \n "
" \" rescan \" : <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports \n "
" } \n "
2018-08-08 07:39:34 +02:00
" \n Note: This call can take over an hour to complete if rescan is true, during that time, other rpc calls \n "
2018-01-05 21:43:31 +01:00
" may report that the imported keys, addresses or scripts exists but related transactions are still missing. \n "
2016-06-16 16:57:48 +02:00
" \n Examples: \n " +
HelpExampleCli ( " importmulti " , " '[{ \" scriptPubKey \" : { \" address \" : \" <my address> \" }, \" timestamp \" :1455191478 }, "
" { \" scriptPubKey \" : { \" address \" : \" <my 2nd address> \" }, \" label \" : \" example 2 \" , \" timestamp \" : 1455191480 }]' " ) +
HelpExampleCli ( " importmulti " , " '[{ \" scriptPubKey \" : { \" address \" : \" <my address> \" }, \" timestamp \" :1455191478 }]' '{ \" rescan \" : false}' " ) +
" \n Response is an array with the same size as the input that has the execution result : \n "
" [{ \" success \" : true } , { \" success \" : false, \" error \" : { \" code \" : -1, \" message \" : \" Internal Server Error \" } }, ... ] \n " ) ;
// clang-format on
2017-06-06 21:15:28 +02:00
RPCTypeCheck ( mainRequest . params , { UniValue : : VARR , UniValue : : VOBJ } ) ;
2016-06-16 16:57:48 +02:00
const UniValue & requests = mainRequest . params [ 0 ] ;
//Default options
bool fRescan = true ;
2017-07-10 17:44:39 +02:00
if ( ! mainRequest . params [ 1 ] . isNull ( ) ) {
2016-06-16 16:57:48 +02:00
const UniValue & options = mainRequest . params [ 1 ] ;
if ( options . exists ( " rescan " ) ) {
fRescan = options [ " rescan " ] . get_bool ( ) ;
}
}
2017-12-08 22:07:37 +01:00
WalletRescanReserver reserver ( pwallet ) ;
if ( fRescan & & ! reserver . reserve ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Wallet is currently rescanning. Abort existing rescan or wait. " ) ;
}
2017-09-08 01:29:59 +02:00
int64_t now = 0 ;
2016-06-16 16:57:48 +02:00
bool fRunScan = false ;
2016-11-10 08:11:51 +01:00
int64_t nLowestTimestamp = 0 ;
2016-06-16 16:57:48 +02:00
UniValue response ( UniValue : : VARR ) ;
2017-09-08 01:29:59 +02:00
{
2017-07-26 16:23:01 +02:00
auto locked_chain = pwallet - > chain ( ) . lock ( ) ;
LOCK ( pwallet - > cs_wallet ) ;
2017-09-08 01:29:59 +02:00
EnsureWalletIsUnlocked ( pwallet ) ;
2016-06-16 16:57:48 +02:00
2017-09-08 01:29:59 +02:00
// Verify all timestamps are present before importing any keys.
now = chainActive . Tip ( ) ? chainActive . Tip ( ) - > GetMedianTimePast ( ) : 0 ;
for ( const UniValue & data : requests . getValues ( ) ) {
GetImportTimestamp ( data , now ) ;
2016-06-16 16:57:48 +02:00
}
2017-09-08 01:29:59 +02:00
const int64_t minimumTimestamp = 1 ;
if ( fRescan & & chainActive . Tip ( ) ) {
nLowestTimestamp = chainActive . Tip ( ) - > GetBlockTime ( ) ;
} else {
fRescan = false ;
2016-06-16 16:57:48 +02:00
}
2017-09-08 01:29:59 +02:00
for ( const UniValue & data : requests . getValues ( ) ) {
const int64_t timestamp = std : : max ( GetImportTimestamp ( data , now ) , minimumTimestamp ) ;
const UniValue result = ProcessImport ( pwallet , data , timestamp ) ;
response . push_back ( result ) ;
if ( ! fRescan ) {
continue ;
}
// If at least one request was successful then allow rescan.
if ( result [ " success " ] . get_bool ( ) ) {
fRunScan = true ;
}
// Get the lowest timestamp.
if ( timestamp < nLowestTimestamp ) {
nLowestTimestamp = timestamp ;
}
2016-06-16 16:57:48 +02:00
}
}
2017-02-14 15:38:29 +01:00
if ( fRescan & & fRunScan & & requests . size ( ) ) {
2017-12-13 00:13:58 +01:00
int64_t scannedTime = pwallet - > RescanFromTime ( nLowestTimestamp , reserver , true /* update */ ) ;
2017-03-02 21:24:50 +01:00
pwallet - > ReacceptWalletTransactions ( ) ;
2017-02-16 16:49:03 +01:00
2017-08-31 02:34:03 +02:00
if ( pwallet - > IsAbortingRescan ( ) ) {
throw JSONRPCError ( RPC_MISC_ERROR , " Rescan aborted by user. " ) ;
}
2017-06-22 23:16:24 +02:00
if ( scannedTime > nLowestTimestamp ) {
2017-02-16 16:49:03 +01:00
std : : vector < UniValue > results = response . getValues ( ) ;
response . clear ( ) ;
response . setArray ( ) ;
size_t i = 0 ;
for ( const UniValue & request : requests . getValues ( ) ) {
// If key creation date is within the successfully scanned
// range, or if the import result already has an error set, let
// the result stand unmodified. Otherwise replace the result
// with an error message.
2017-06-22 23:16:24 +02:00
if ( scannedTime < = GetImportTimestamp ( request , now ) | | results . at ( i ) . exists ( " error " ) ) {
2017-02-16 16:49:03 +01:00
response . push_back ( results . at ( i ) ) ;
} else {
UniValue result = UniValue ( UniValue : : VOBJ ) ;
result . pushKV ( " success " , UniValue ( false ) ) ;
2017-05-15 15:11:03 +02:00
result . pushKV (
" error " ,
JSONRPCError (
RPC_MISC_ERROR ,
strprintf ( " Rescan failed for key with creation timestamp %d. There was an error reading a "
" block from time %d, which is after or within %d seconds of key creation, and "
" could contain transactions pertaining to the key. As a result, transactions "
" and coins using this key may not appear in the wallet. This error could be "
" caused by pruning or data corruption (see bitcoind log for details) and could "
" be dealt with by downloading and rescanning the relevant blocks (see -reindex "
" and -rescan options). " ,
2017-06-22 23:16:24 +02:00
GetImportTimestamp ( request , now ) , scannedTime - TIMESTAMP_WINDOW - 1 , TIMESTAMP_WINDOW ) ) ) ;
2017-02-16 16:49:03 +01:00
response . push_back ( std : : move ( result ) ) ;
}
2017-02-22 20:11:44 +01:00
+ + i ;
2017-02-16 16:49:03 +01:00
}
}
2016-06-16 16:57:48 +02:00
}
return response ;
}