LCOV - code coverage report
Current view: top level - src/wallet - rpcdump.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 585 810 72.2 %
Date: 2025-02-23 09:33:43 Functions: 21 22 95.5 %

          Line data    Source code
       1             : // Copyright (c) 2009-2014 The Bitcoin developers
       2             : // Copyright (c) 2014-2015 The Dash developers
       3             : // Copyright (c) 2015-2021 The PIVX Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include "bip38.h"
       8             : #include "key_io.h"
       9             : #include "destination_io.h"
      10             : #include "rpc/server.h"
      11             : #include "sapling/key_io_sapling.h"
      12             : #include "script/script.h"
      13             : #include "script/standard.h"
      14             : #include "sync.h"
      15             : #include "util/system.h"
      16             : #include "utilstrencodings.h"
      17             : #include "utiltime.h"
      18             : #include "wallet/rpcwallet.h"
      19             : #include "wallet.h"
      20             : #include "validation.h"
      21             : 
      22             : #include <secp256k1.h>
      23             : #include <stdint.h>
      24             : 
      25             : #include <boost/algorithm/string.hpp>
      26             : #include <boost/date_time/posix_time/posix_time.hpp>
      27             : 
      28             : #include <univalue.h>
      29             : 
      30             : 
      31        1867 : int64_t static DecodeDumpTime(const std::string& str)
      32             : {
      33        1867 :     static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0);
      34        1867 :     static const std::locale loc(std::locale::classic(),
      35        1871 :         new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
      36        3734 :     std::istringstream iss(str);
      37        1867 :     iss.imbue(loc);
      38        1867 :     boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
      39        1867 :     iss >> ptime;
      40        1867 :     if (ptime.is_not_a_date_time())
      41             :         return 0;
      42        1867 :     return (ptime - epoch).total_seconds();
      43             : }
      44             : 
      45          77 : std::string static EncodeDumpString(const std::string& str)
      46             : {
      47          77 :     std::stringstream ret;
      48          84 :     for (unsigned char c : str) {
      49           7 :         if (c <= 32 || c >= 128 || c == '%') {
      50           0 :             ret << '%' << HexStr(Span<const unsigned char>(&c, 1));
      51             :         } else {
      52          14 :             ret << c;
      53             :         }
      54             :     }
      55          77 :     return ret.str();
      56             : }
      57             : 
      58          15 : std::string DecodeDumpString(const std::string& str)
      59             : {
      60          15 :     std::stringstream ret;
      61          15 :     for (unsigned int pos = 0; pos < str.length(); pos++) {
      62           0 :         unsigned char c = str[pos];
      63           0 :         if (c == '%' && pos + 2 < str.length()) {
      64           0 :             c = (((str[pos + 1] >> 6) * 9 + ((str[pos + 1] - '0') & 15)) << 4) |
      65           0 :                 ((str[pos + 2] >> 6) * 9 + ((str[pos + 2] - '0') & 15));
      66           0 :             pos += 2;
      67             :         }
      68           0 :         ret << c;
      69             :     }
      70          15 :     return ret.str();
      71             : }
      72             : 
      73        1892 : bool IsStakingDerPath(KeyOriginInfo keyOrigin)
      74             : {
      75        1892 :     return keyOrigin.path.size() > 3 && keyOrigin.path[3] == (2 | BIP32_HARDENED_KEY_LIMIT);
      76             : }
      77             : 
      78           9 : UniValue importprivkey(const JSONRPCRequest& request)
      79             : {
      80           9 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
      81             : 
      82           9 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
      83           0 :         return NullUniValue;
      84             : 
      85           9 :     if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
      86           0 :         throw std::runtime_error(
      87             :             "importprivkey \"privkey\" ( \"label\" rescan is_staking_address )\n"
      88           0 :             "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n" +
      89           0 :             HelpRequiringPassphrase(pwallet) + "\n"
      90             :             "\nArguments:\n"
      91             :             "1. \"privkey\"          (string, required) The private key (see dumpprivkey)\n"
      92             :             "2. \"label\"            (string, optional, default=\"\") An optional label\n"
      93             :             "3. rescan               (boolean, optional, default=true) Rescan the wallet for transactions\n"
      94             :             "4. is_staking_address   (boolean, optional, default=false) Whether this key refers to a (cold) staking address\n"
      95             :             "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n"
      96             :             "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"
      97             :             "\nExamples:\n"
      98           0 :             "\nDump a private key\n" +
      99           0 :             HelpExampleCli("dumpprivkey", "\"myaddress\"") +
     100           0 :             "\nImport the private key with rescan\n" +
     101           0 :             HelpExampleCli("importprivkey", "\"mykey\"") +
     102           0 :             "\nImport using a label and without rescan\n" +
     103           0 :             HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
     104           0 :             "\nAs a JSON-RPC call\n" +
     105           0 :             HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false"));
     106             : 
     107          18 :     const std::string strSecret = request.params[0].get_str();
     108          25 :     const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : "");
     109           9 :     const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true);
     110             : 
     111          18 :     WalletRescanReserver reserver(pwallet);
     112           9 :     if (fRescan && !reserver.reserve()) {
     113           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     114             :     }
     115             : 
     116           9 :     const bool fStakingAddress = (request.params.size() > 3 ? request.params[3].get_bool() : false);
     117             : 
     118          18 :     CKey key = KeyIO::DecodeSecret(strSecret);
     119           9 :     if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
     120             : 
     121           9 :     CPubKey pubkey = key.GetPubKey();
     122           9 :     assert(key.VerifyPubKey(pubkey));
     123           9 :     CKeyID vchAddress = pubkey.GetID();
     124           9 :     {
     125          18 :         LOCK2(cs_main, pwallet->cs_wallet);
     126           9 :         EnsureWalletIsUnlocked(pwallet);
     127             : 
     128           9 :         pwallet->MarkDirty();
     129          18 :         pwallet->SetAddressBook(vchAddress, strLabel, (
     130             :                 fStakingAddress ?
     131             :                         AddressBook::AddressBookPurpose::COLD_STAKING :
     132             :                         AddressBook::AddressBookPurpose::RECEIVE));
     133             : 
     134             :         // Don't throw error in case a key is already there
     135           9 :         if (pwallet->HaveKey(vchAddress))
     136           0 :             return NullUniValue;
     137             : 
     138             :         // whenever a key is imported, we need to scan the whole chain
     139           9 :         pwallet->UpdateTimeFirstKey(1);
     140           9 :         pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
     141             : 
     142           9 :         if (!pwallet->AddKeyPubKey(key, pubkey))
     143           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
     144             :     }
     145           9 :     if (fRescan) {
     146           7 :         pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
     147             :     }
     148             : 
     149           9 :     return NullUniValue;
     150             : }
     151             : 
     152           0 : UniValue abortrescan(const JSONRPCRequest& request)
     153             : {
     154           0 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     155             : 
     156           0 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     157           0 :         return NullUniValue;
     158             : 
     159           0 :     if (request.fHelp || request.params.size() > 0)
     160           0 :         throw std::runtime_error(
     161             :                 "abortrescan\n"
     162             :                 "\nStops current wallet rescan triggered e.g. by an importprivkey call.\n"
     163             :                 "\nExamples:\n"
     164             :                 "\nImport a private key\n"
     165           0 :                 + HelpExampleCli("importprivkey", "\"mykey\"") +
     166             :                 "\nAbort the running wallet rescan\n"
     167           0 :                 + HelpExampleCli("abortrescan", "") +
     168             :                 "\nAs a JSON-RPC call\n"
     169           0 :                 + HelpExampleRpc("abortrescan", "")
     170           0 :         );
     171             : 
     172           0 :     if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
     173           0 :     pwallet->AbortRescan();
     174           0 :     return true;
     175             : }
     176             : 
     177             : static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel, const std::string& strPurpose);
     178             : 
     179          19 : static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
     180             : {
     181          19 :     if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE)
     182           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
     183             : 
     184          19 :     pwallet->MarkDirty();
     185             : 
     186          19 :     if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script))
     187           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
     188             : 
     189          19 :     if (isRedeemScript) {
     190           1 :         if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script))
     191           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
     192           2 :         ImportAddress(pwallet, CScriptID(script), strLabel,  "receive");
     193             :     }
     194          19 : }
     195             : 
     196          14 : static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel, const std::string& strPurpose)
     197             : {
     198          14 :     CScript script = GetScriptForDestination(dest);
     199          14 :     ImportScript(pwallet, script, strLabel, false);
     200             :     // add to address book or update label
     201          14 :     if (IsValidDestination(dest)) {
     202          28 :         pwallet->SetAddressBook(dest, strLabel, strPurpose);
     203             :     }
     204          14 : }
     205             : 
     206          10 : UniValue importaddress(const JSONRPCRequest& request)
     207             : {
     208          10 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     209             : 
     210          10 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     211           0 :         return NullUniValue;
     212             : 
     213          10 :     if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
     214           0 :         throw std::runtime_error(
     215             :             "importaddress \"script\" ( \"label\" rescan )\n"
     216             :             "\nAdds a script (in hex), or address, that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
     217             :             "\nArguments:\n"
     218             :             "1. \"script\"           (string, required) hex-encoded script (or address)\n"
     219             :             "2. \"label\"            (string, optional, default=\"\") An optional label\n"
     220             :             "3. rescan               (boolean, optional, default=true) Rescan the wallet for transactions\n"
     221             :             "4. p2sh                 (boolean, optional, default=false) Add the P2SH version of the script as well\n"
     222             :             "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n"
     223             :             "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"
     224             :             "\nExamples:\n"
     225           0 :             "\nImport a script with rescan\n" +
     226           0 :             HelpExampleCli("importaddress", "\"myscript\"") +
     227           0 :             "\nImport using a label without rescan\n" +
     228           0 :             HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
     229           0 :             "\nAs a JSON-RPC call\n" +
     230           0 :             HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false"));
     231             : 
     232          28 :     const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : "");
     233             :     // Whether to perform rescan after import
     234          10 :     const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true);
     235             : 
     236          20 :     WalletRescanReserver reserver(pwallet);
     237          10 :     if (fRescan && !reserver.reserve()) {
     238           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     239             :     }
     240             : 
     241             :     // Whether to import a p2sh version, too
     242          10 :     const bool fP2SH = (request.params.size() > 3 ? request.params[3].get_bool() : false);
     243             : 
     244          10 :     {
     245          20 :         LOCK2(cs_main, pwallet->cs_wallet);
     246             : 
     247          10 :         bool isStaking = false;
     248          10 :         bool isExchange = false;
     249          20 :         CTxDestination dest = DecodeDestination(request.params[0].get_str(), isStaking, isExchange);
     250             : 
     251          10 :         if (IsValidDestination(dest)) {
     252           9 :             if (fP2SH)
     253           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
     254          14 :             ImportAddress(pwallet, dest, strLabel, isStaking ?
     255             :                                             AddressBook::AddressBookPurpose::COLD_STAKING :
     256             :                                             AddressBook::AddressBookPurpose::RECEIVE);
     257             : 
     258           1 :         } else if (IsHex(request.params[0].get_str())) {
     259           2 :             std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
     260           2 :             ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH);
     261             :         } else {
     262           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address or script");
     263             :         }
     264             :     }
     265          10 :     if (fRescan) {
     266           7 :         pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
     267           7 :         pwallet->ReacceptWalletTransactions();
     268             :     }
     269             : 
     270          10 :     return NullUniValue;
     271             : }
     272             : 
     273           4 : UniValue importpubkey(const JSONRPCRequest& request)
     274             : {
     275           4 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     276             : 
     277           4 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     278           0 :         return NullUniValue;
     279             : 
     280           4 :     if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
     281           0 :         throw std::runtime_error(
     282             :             "importpubkey \"pubkey\" ( \"label\" rescan )\n"
     283             :             "\nAdds 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"
     284             :             "\nArguments:\n"
     285             :             "1. \"pubkey\"           (string, required) The hex-encoded public key\n"
     286             :             "2. \"label\"            (string, optional, default=\"\") An optional label\n"
     287             :             "3. rescan               (boolean, optional, default=true) Rescan the wallet for transactions\n"
     288             :             "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n"
     289             :             "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"
     290             :             "\nExamples:\n"
     291             :             "\nImport a public key with rescan\n"
     292           0 :             + HelpExampleCli("importpubkey", "\"mypubkey\"") +
     293             :             "\nImport using a label without rescan\n"
     294           0 :             + HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
     295             :             "\nAs a JSON-RPC call\n"
     296           0 :             + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
     297           0 :         );
     298             : 
     299          12 :     const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : "");
     300             :     // Whether to perform rescan after import
     301           4 :     const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true);
     302             : 
     303           8 :     WalletRescanReserver reserver(pwallet);
     304           4 :     if (fRescan && !reserver.reserve()) {
     305           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     306             :     }
     307             : 
     308           4 :     if (!IsHex(request.params[0].get_str()))
     309           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
     310           8 :     std::vector<unsigned char> data(ParseHex(request.params[0].get_str()));
     311           4 :     CPubKey pubKey(data.begin(), data.end());
     312           4 :     if (!pubKey.IsFullyValid())
     313           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
     314             : 
     315           4 :     {
     316           8 :         LOCK2(cs_main, pwallet->cs_wallet);
     317             : 
     318           8 :         ImportAddress(pwallet, pubKey.GetID(), strLabel, "receive");
     319           8 :         ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
     320             :     }
     321           4 :     if (fRescan) {
     322           2 :         pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
     323           2 :         pwallet->ReacceptWalletTransactions();
     324             :     }
     325             : 
     326           4 :     return NullUniValue;
     327             : }
     328             : 
     329             : // TODO: Needs further review over the HD flow, staking addresses and multisig import.
     330           4 : UniValue importwallet(const JSONRPCRequest& request)
     331             : {
     332           4 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     333             : 
     334           4 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     335           0 :         return NullUniValue;
     336             : 
     337           4 :     if (request.fHelp || request.params.size() != 1)
     338           0 :         throw std::runtime_error(
     339             :             "importwallet \"filename\"\n"
     340           0 :             "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup.\n" +
     341           0 :             HelpRequiringPassphrase(pwallet) + "\n"
     342             :             "\nArguments:\n"
     343             :             "1. \"filename\"    (string, required) The wallet file\n"
     344             : 
     345             :             "\nExamples:\n"
     346           0 :             "\nDump the wallet\n" +
     347           0 :             HelpExampleCli("dumpwallet", "\"test\"") +
     348           0 :             "\nImport the wallet\n" +
     349           0 :             HelpExampleCli("importwallet", "\"test\"") +
     350           0 :             "\nImport using the json rpc call\n" +
     351           0 :             HelpExampleRpc("importwallet", "\"test\""));
     352             : 
     353           8 :     fsbridge::ifstream file;
     354           8 :     file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
     355           4 :     if (!file.is_open()) {
     356           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
     357             :     }
     358             : 
     359           8 :     WalletRescanReserver reserver(pwallet);
     360           4 :     if (!reserver.reserve()) {
     361           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     362             :     }
     363             : 
     364           4 :     int64_t nTimeBegin = 0;
     365           4 :     bool fGood = true;
     366           4 :     {
     367           8 :         LOCK2(cs_main, pwallet->cs_wallet);
     368           4 :         EnsureWalletIsUnlocked(pwallet);
     369             : 
     370           8 :         nTimeBegin = chainActive.Tip()->GetBlockTime();
     371           4 :         int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
     372           4 :         file.seekg(0, file.beg);
     373             : 
     374           8 :         pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
     375         988 :         while (file.good()) {
     376        2893 :             pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))));
     377        1918 :             std::string line;
     378         984 :             std::getline(file, line);
     379         984 :             if (line.empty() || line[0] == '#')
     380         100 :                 continue;
     381             : 
     382        1868 :             std::vector<std::string> vstr;
     383         934 :             boost::split(vstr, line, boost::is_any_of(" "));
     384         934 :             if (vstr.size() < 2)
     385           0 :                 continue;
     386             : 
     387             :             // Sapling keys
     388             :             // Let's see if the address is a valid PIVX spending key
     389         934 :             if (pwallet->HasSaplingSPKM()) {
     390        1866 :                 libzcash::SpendingKey spendingkey = KeyIO::DecodeSpendingKey(vstr[0]);
     391         933 :                 int64_t nTime = DecodeDumpTime(vstr[1]);
     392         933 :                 if (IsValidSpendingKey(spendingkey)) {
     393           0 :                     libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey);
     394           0 :                     auto addResult = pwallet->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet(
     395           0 :                             Params().GetConsensus(), saplingSpendingKey, nTime);
     396           0 :                     if (addResult == KeyAlreadyExists) {
     397           0 :                         LogPrint(BCLog::SAPLING, "Skipping import of shielded addr (key already present)\n");
     398           0 :                     } else if (addResult == KeyNotAdded) {
     399             :                         // Something went wrong
     400           0 :                         fGood = false;
     401             :                     }
     402           0 :                     continue;
     403             :                 }
     404             :             }
     405             : 
     406        1868 :             CKey key = KeyIO::DecodeSecret(vstr[0]);
     407         934 :             if (!key.IsValid())
     408           0 :                 continue;
     409         934 :             CPubKey pubkey = key.GetPubKey();
     410         934 :             assert(key.VerifyPubKey(pubkey));
     411         934 :             CKeyID keyid = pubkey.GetID();
     412         934 :             if (pwallet->HaveKey(keyid)) {
     413           0 :                 LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid));
     414           0 :                 continue;
     415             :             }
     416         934 :             int64_t nTime = DecodeDumpTime(vstr[1]);
     417        1868 :             std::string strLabel;
     418         934 :             bool fLabel = true;
     419        1868 :             for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
     420        1868 :                 const std::string& type = vstr[nStr];
     421        1868 :                 if (boost::algorithm::starts_with(type, "#"))
     422             :                     break;
     423         934 :                 if (type == "change=1")
     424             :                     fLabel = false;
     425         915 :                 else if (type == "reserve=1")
     426             :                     fLabel = false;
     427          18 :                 else if (type == "hdseed")
     428           0 :                     fLabel = false;
     429         934 :                 if (boost::algorithm::starts_with(type, "label=")) {
     430          15 :                     strLabel = DecodeDumpString(vstr[nStr].substr(6));
     431          15 :                     fLabel = true;
     432             :                 }
     433             :             }
     434        1868 :             LogPrintf("Importing %s...\n", EncodeDestination(keyid));
     435         934 :             if (!pwallet->AddKeyPubKey(key, pubkey)) {
     436           0 :                 fGood = false;
     437           0 :                 continue;
     438             :             }
     439         934 :             pwallet->mapKeyMetadata[keyid].nCreateTime = nTime;
     440         934 :             if (fLabel) // TODO: This is not entirely true.. needs to be reviewed properly.
     441          36 :                 pwallet->SetAddressBook(keyid, strLabel, AddressBook::AddressBookPurpose::RECEIVE);
     442         937 :             nTimeBegin = std::min(nTimeBegin, nTime);
     443             :         }
     444           4 :         file.close();
     445           4 :         pwallet->ShowProgress("", 100); // hide progress dialog in GUI
     446           4 :         pwallet->UpdateTimeFirstKey(nTimeBegin);
     447             :     }
     448           4 :     pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */);
     449           4 :     pwallet->MarkDirty();
     450             : 
     451           4 :     if (!fGood)
     452           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet");
     453             : 
     454           4 :     return NullUniValue;
     455             : }
     456             : 
     457          53 : UniValue dumpprivkey(const JSONRPCRequest& request)
     458             : {
     459          53 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     460             : 
     461          53 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     462           0 :         return NullUniValue;
     463             : 
     464          53 :     if (request.fHelp || request.params.size() != 1)
     465           0 :         throw std::runtime_error(
     466             :             "dumpprivkey \"address\"\n"
     467             :             "\nReveals the private key corresponding to 'address'.\n"
     468           0 :             "Then the importprivkey can be used with this output\n" +
     469           0 :             HelpRequiringPassphrase(pwallet) + "\n"
     470             : 
     471             :             "\nArguments:\n"
     472             :             "1. \"address\"   (string, required) The pivx address for the private key\n"
     473             : 
     474             :             "\nResult:\n"
     475             :             "\"key\"                (string) The private key\n"
     476             : 
     477           0 :             "\nExamples:\n" +
     478           0 :             HelpExampleCli("dumpprivkey", "\"myaddress\"") + HelpExampleCli("importprivkey", "\"mykey\"") + HelpExampleRpc("dumpprivkey", "\"myaddress\""));
     479             : 
     480         156 :     LOCK2(cs_main, pwallet->cs_wallet);
     481             : 
     482          53 :     EnsureWalletIsUnlocked(pwallet);
     483             : 
     484         100 :     std::string strAddress = request.params[0].get_str();
     485         100 :     CTxDestination dest = DecodeDestination(strAddress);
     486          50 :     if (!IsValidDestination(dest))
     487           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
     488          50 :     const CKeyID* keyID = boost::get<CKeyID>(&dest);
     489          50 :     if (!keyID)
     490           0 :         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
     491         100 :     CKey vchSecret;
     492          50 :     if (!pwallet->GetKey(*keyID, vchSecret))
     493           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
     494         100 :     return KeyIO::EncodeSecret(vchSecret);
     495             : }
     496             : 
     497          11 : UniValue dumpwallet(const JSONRPCRequest& request)
     498             : {
     499          11 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     500             : 
     501          11 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     502           0 :         return NullUniValue;
     503             : 
     504          11 :     if (request.fHelp || request.params.size() != 1)
     505           0 :         throw std::runtime_error(
     506             :             "dumpwallet \"filename\"\n"
     507             :             "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
     508             :             "Imported scripts are not currently included in wallet dumps, these must be backed up separately.\n"
     509             :             "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"
     510           0 :             "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n" +
     511           0 :             HelpRequiringPassphrase(pwallet) + "\n"
     512             :             "\nArguments:\n"
     513             :             "1. \"filename\"    (string, required) The filename\n"
     514             : 
     515             :             "\nResult:\n"
     516             :             "{\n"
     517             :             "  \"filename\": \"xxxx\",     (string) The full path to the wallet dump file.\n"
     518             :             "  \"warning\": \"xxxx\"       (string) A warning message about the exact contents of this file.\n"
     519             :             "}\n"
     520             : 
     521           0 :             "\nExamples:\n" +
     522           0 :             HelpExampleCli("dumpwallet", "\"test\"") + HelpExampleRpc("dumpwallet", "\"test\""));
     523             : 
     524          11 :     if (request.params[0].get_str().find("bug") != std::string::npos ||
     525          10 :         request.params[0].get_str().find("log") != std::string::npos) {
     526           4 :             throw JSONRPCError(RPC_MISC_ERROR, "Scam attempt detected!");
     527             :     }
     528             : 
     529             :     // Make sure the results are valid at least up to the most recent block
     530             :     // the user could have gotten from another RPC command prior to now
     531           9 :     pwallet->BlockUntilSyncedToCurrentChain();
     532             : 
     533          26 :     LOCK2(cs_main, pwallet->cs_wallet);
     534             : 
     535           9 :     EnsureWalletIsUnlocked(pwallet);
     536             : 
     537           9 :     ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
     538             : 
     539          18 :     fs::path filepath = request.params[0].get_str().c_str();
     540          28 :     filepath = fs::absolute(filepath);
     541             : 
     542             :     /* Prevent arbitrary files from being overwritten. There have been reports
     543             :      * that users have overwritten wallet files this way:
     544             :      * https://github.com/bitcoin/bitcoin/issues/9934
     545             :      * It may also avoid other security issues.
     546             :      */
     547          17 :     if (fs::exists(filepath)) {
     548           2 :         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");
     549             :     }
     550             : 
     551          16 :     fsbridge::ofstream file;
     552           8 :     file.open(filepath);
     553           8 :     if (!file.is_open())
     554           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
     555             : 
     556          16 :     std::map<CKeyID, int64_t> mapKeyBirth;
     557           8 :     pwallet->GetKeyBirthTimes(mapKeyBirth);
     558           8 :     const std::map<CKeyID, int64_t>& mapKeyPool = spk_man->GetAllReserveKeys();
     559             : 
     560             : 
     561             :     // sort time/key pairs
     562          16 :     std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
     563        1981 :     for (const auto& entry : mapKeyBirth) {
     564        1973 :         vKeyBirth.emplace_back(entry.second, entry.first);
     565             :     }
     566           8 :     mapKeyBirth.clear();
     567           8 :     std::sort(vKeyBirth.begin(), vKeyBirth.end());
     568             : 
     569           8 :     CBlockIndex* tip = chainActive.Tip();
     570             :     // produce output
     571          16 :     file << strprintf("# Wallet dump created by PIVX %s\n", CLIENT_BUILD);
     572          24 :     file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
     573           8 :     if (tip) {
     574          16 :         file << strprintf("# * Best block at time of backup was %i (%s),\n", tip->nHeight,
     575          16 :                           tip->GetBlockHash().ToString());
     576          32 :         file << strprintf("#   mined on %s\n", FormatISO8601DateTime(tip->GetBlockTime()));
     577             :     } else {
     578           0 :         file << "# Missing tip information\n";
     579             :     }
     580           8 :     file << "\n";
     581             : 
     582             :     // Add the base58check encoded extended master if the wallet uses HD
     583           8 :     CKeyID seed_id = spk_man->GetHDChain().GetID();
     584          54 :     if (!seed_id.IsNull())
     585             :     {
     586          12 :         CKey seed;
     587           6 :         if (pwallet->GetKey(seed_id, seed)) {
     588          12 :             CExtKey masterKey;
     589          12 :             masterKey.SetSeed(seed.begin(), seed.size());
     590             : 
     591          18 :             file << "# extended private masterkey: " << KeyIO::EncodeExtKey(masterKey) << "\n\n";
     592             :         }
     593             :     }
     594             : 
     595        1981 :     for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
     596        1973 :         const CKeyID& keyid = it->second;
     597        3946 :         std::string strTime = FormatISO8601DateTime(it->first);
     598        3946 :         CKey key;
     599        1973 :         if (pwallet->GetKey(keyid, key)) {
     600        1973 :             const CKeyMetadata& metadata = pwallet->mapKeyMetadata[keyid];
     601        1892 :             std::string strAddr = EncodeDestination(keyid, (metadata.HasKeyOrigin() && IsStakingDerPath(metadata.key_origin) ?
     602             :                                                           CChainParams::STAKING_ADDRESS :
     603        7811 :                                                           CChainParams::PUBKEY_ADDRESS));
     604             : 
     605        5919 :             file << strprintf("%s %s ", KeyIO::EncodeSecret(key), strTime);
     606        1973 :             if (pwallet->HasAddressBook(keyid)) {
     607         154 :                 file << strprintf("label=%s", EncodeDumpString(pwallet->GetNameForAddressBookEntry(keyid)));
     608        1896 :             } else if (keyid == seed_id) {
     609           6 :                 file << "hdseed=1";
     610        1890 :             } else if (mapKeyPool.count(keyid)) {
     611        1499 :                 file << "reserve=1";
     612             :             } else {
     613         391 :                 file << "change=1";
     614             :             }
     615        9694 :             file << strprintf(" # addr=%s%s\n", strAddr, (metadata.HasKeyOrigin() ? " hdkeypath="+metadata.key_origin.pathToString() : ""));
     616             :         }
     617             :     }
     618             : 
     619             :     // Sapling
     620           8 :     file << "# Sapling keys\n";
     621           8 :     file << "\n";
     622          16 :     std::set<libzcash::SaplingPaymentAddress> saplingAddresses;
     623           8 :     pwallet->GetSaplingPaymentAddresses(saplingAddresses);
     624           8 :     file << "\n";
     625           8 :     for (const auto& addr : saplingAddresses) {
     626           0 :         libzcash::SaplingExtendedSpendingKey extsk;
     627           0 :         if (pwallet->GetSaplingExtendedSpendingKey(addr, extsk)) {
     628           0 :             auto ivk = extsk.expsk.full_viewing_key().in_viewing_key();
     629           0 :             CKeyMetadata keyMeta = pwallet->GetSaplingScriptPubKeyMan()->mapSaplingZKeyMetadata[ivk];
     630           0 :             std::string strTime = FormatISO8601DateTime(keyMeta.nCreateTime);
     631             :             // Keys imported with importsaplingkey do not have key origin metadata
     632           0 :             file << strprintf("%s %s # shielded_addr=%s%s\n",
     633           0 :                     KeyIO::EncodeSpendingKey(extsk),
     634             :                     strTime,
     635           0 :                     KeyIO::EncodePaymentAddress(addr),
     636           0 :                     (keyMeta.HasKeyOrigin() ? " hdkeypath=" + keyMeta.key_origin.pathToString() : "")
     637           0 :                     );
     638             :         }
     639             :     }
     640             : 
     641           8 :     file << "\n";
     642           8 :     file << "# End of dump\n";
     643           8 :     file.close();
     644             : 
     645          16 :     UniValue reply(UniValue::VOBJ);
     646           8 :     reply.pushKV("filename", filepath.string());
     647          16 :     reply.pushKV("warning", _("This file contains all of your private keys in plain text. DO NOT send this file to anyone!"));
     648             : 
     649           8 :     return reply;
     650             : }
     651             : 
     652          32 : static UniValue processImport(CWallet* const pwallet, const UniValue& data, const int64_t timestamp)
     653             : {
     654          32 :     try {
     655          32 :         bool success = false;
     656             : 
     657             :         // Required fields.
     658          32 :         const UniValue& scriptPubKey = data["scriptPubKey"];
     659             : 
     660             :         // Should have script or JSON with "address".
     661          75 :         if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
     662           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
     663             :         }
     664             : 
     665             :         // Optional fields.
     666         102 :         const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
     667         110 :         const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
     668          82 :         const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
     669          71 :         const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
     670          75 :         const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
     671          82 :         const std::string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
     672             : 
     673          32 :         bool isScript = scriptPubKey.getType() == UniValue::VSTR;
     674          32 :         bool isP2SH = strRedeemScript.length() > 0;
     675          65 :         const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
     676             : 
     677             :         // Parse the output.
     678          64 :         CScript script;
     679          64 :         CTxDestination dest;
     680             : 
     681          32 :         if (!isScript) {
     682          44 :             dest = DecodeDestination(output);
     683          22 :             if (!IsValidDestination(dest)) {
     684           2 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     685             :             }
     686          21 :             script = GetScriptForDestination(dest);
     687             :         } else {
     688          10 :             if (!IsHex(output)) {
     689           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
     690             :             }
     691          20 :             std::vector<unsigned char> vData(ParseHex(output));
     692          10 :             script = CScript(vData.begin(), vData.end());
     693             :         }
     694             : 
     695             :         // Watchonly and private keys
     696          31 :         if (watchOnly && keys.size()) {
     697           4 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
     698             :         }
     699             : 
     700             :         // Internal + Label
     701          54 :         if (internal && data.exists("label")) {
     702           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
     703             :         }
     704             : 
     705             :         // Not having Internal + Script
     706          29 :         if (!internal && isScript) {
     707           6 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey");
     708             :         }
     709             : 
     710             :         // Keys / PubKeys size check.
     711          26 :         if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
     712           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
     713             :         }
     714             : 
     715             :         // Invalid P2SH redeemScript
     716          26 :         if (isP2SH && !IsHex(strRedeemScript)) {
     717           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
     718             :         }
     719             : 
     720             :         // Process. //
     721             : 
     722             :         // P2SH
     723          26 :         if (isP2SH) {
     724             :             // Import redeem script.
     725           4 :             std::vector<unsigned char> vData(ParseHex(strRedeemScript));
     726           4 :             CScript redeemScript = CScript(vData.begin(), vData.end());
     727             : 
     728             :             // Invalid P2SH address
     729           2 :             if (!script.IsPayToScriptHash()) {
     730           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
     731             :             }
     732             : 
     733           2 :             pwallet->MarkDirty();
     734             : 
     735           2 :             if (!pwallet->HaveWatchOnly(redeemScript) && !pwallet->AddWatchOnly(redeemScript)) {
     736           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
     737             :             }
     738             : 
     739           2 :             if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) {
     740           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
     741             :             }
     742             : 
     743           4 :             CTxDestination redeem_dest = CScriptID(redeemScript);
     744           4 :             CScript redeemDestination = GetScriptForDestination(redeem_dest);
     745             : 
     746           2 :             if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
     747           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
     748             :             }
     749             : 
     750           2 :             pwallet->MarkDirty();
     751             : 
     752           2 :             if (!pwallet->HaveWatchOnly(redeemDestination) && !pwallet->AddWatchOnly(redeemDestination)) {
     753           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
     754             :             }
     755             : 
     756             :             // add to address book or update label
     757           2 :             if (IsValidDestination(dest)) {
     758           6 :                 pwallet->SetAddressBook(dest, label, "receive");
     759             :             }
     760             : 
     761             :             // Import private keys.
     762           2 :             if (keys.size()) {
     763           3 :                 for (size_t i = 0; i < keys.size(); i++) {
     764           2 :                     const std::string& privkey = keys[i].get_str();
     765           4 :                     CKey key = KeyIO::DecodeSecret(privkey);
     766             : 
     767           2 :                     if (!key.IsValid()) {
     768           0 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
     769             :                     }
     770             : 
     771           2 :                     CPubKey pubkey = key.GetPubKey();
     772           2 :                     assert(key.VerifyPubKey(pubkey));
     773             : 
     774           2 :                     CKeyID vchAddress = pubkey.GetID();
     775           2 :                     pwallet->MarkDirty();
     776           6 :                     pwallet->SetAddressBook(vchAddress, label, "receive");
     777             : 
     778           2 :                     if (pwallet->HaveKey(vchAddress)) {
     779           0 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
     780             :                     }
     781             : 
     782           2 :                     pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
     783             : 
     784           2 :                     if (!pwallet->AddKeyPubKey(key, pubkey)) {
     785           0 :                         throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
     786             :                     }
     787             : 
     788           2 :                     if (timestamp < pwallet->nTimeFirstKey) {
     789           0 :                         pwallet->nTimeFirstKey = timestamp;
     790             :                     }
     791             :                 }
     792             :             }
     793             : 
     794           2 :             success = true;
     795             :         } else {
     796             :             // Import public keys.
     797          24 :             if (pubKeys.size() && keys.size() == 0) {
     798           7 :                 const std::string& strPubKey = pubKeys[0].get_str();
     799             : 
     800           7 :                 if (!IsHex(strPubKey)) {
     801           0 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
     802             :                 }
     803             : 
     804          14 :                 std::vector<unsigned char> vData(ParseHex(strPubKey));
     805           7 :                 CPubKey pubKey(vData.begin(), vData.end());
     806             : 
     807           7 :                 if (!pubKey.IsFullyValid()) {
     808           0 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
     809             :                 }
     810             : 
     811          14 :                 CTxDestination pubkey_dest = pubKey.GetID();
     812             : 
     813             :                 // Consistency check.
     814           7 :                 if (!isScript && !(pubkey_dest == dest)) {
     815           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
     816             :                 }
     817             : 
     818             :                 // Consistency check.
     819           6 :                 if (isScript) {
     820           4 :                     CTxDestination destination;
     821           2 :                     if (ExtractDestination(script, destination) && !(destination == pubkey_dest)) {
     822           2 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
     823             :                     }
     824             :                 }
     825             : 
     826          10 :                 CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
     827             : 
     828           5 :                 if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
     829           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
     830             :                 }
     831             : 
     832           5 :                 pwallet->MarkDirty();
     833             : 
     834           5 :                 if (!pwallet->HaveWatchOnly(pubKeyScript) && !pwallet->AddWatchOnly(pubKeyScript)) {
     835           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
     836             :                 }
     837             : 
     838             :                 // add to address book or update label
     839           5 :                 if (IsValidDestination(pubkey_dest)) {
     840          15 :                     pwallet->SetAddressBook(pubkey_dest, label, "receive");
     841             :                 }
     842             : 
     843             :                 // TODO Is this necessary?
     844          10 :                 CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
     845             : 
     846           5 :                 if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
     847           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
     848             :                 }
     849             : 
     850           5 :                 pwallet->MarkDirty();
     851             : 
     852           5 :                 if (!pwallet->HaveWatchOnly(scriptRawPubKey) && !pwallet->AddWatchOnly(scriptRawPubKey)) {
     853           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
     854             :                 }
     855             : 
     856           5 :                 success = true;
     857             :             }
     858             : 
     859             :             // Import private keys.
     860          22 :             if (keys.size()) {
     861           8 :                 const std::string& strPrivkey = keys[0].get_str();
     862          16 :                 CKey key = KeyIO::DecodeSecret(strPrivkey);
     863             : 
     864           8 :                 if (!key.IsValid()) {
     865           0 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
     866             :                 }
     867             : 
     868           8 :                 CPubKey pubKey = key.GetPubKey();
     869           8 :                 assert(key.VerifyPubKey(pubKey));
     870             : 
     871          16 :                 CTxDestination pubkey_dest = pubKey.GetID();
     872             : 
     873             :                 // Consistency check.
     874           8 :                 if (!isScript && !(pubkey_dest == dest)) {
     875           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
     876             :                 }
     877             : 
     878             :                 // Consistency check.
     879           7 :                 if (isScript) {
     880           4 :                     CTxDestination destination;
     881           2 :                     if (ExtractDestination(script, destination) && !(destination == pubkey_dest)) {
     882           2 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
     883             :                     }
     884             :                 }
     885             : 
     886           6 :                 CKeyID vchAddress = pubKey.GetID();
     887           6 :                 pwallet->MarkDirty();
     888          18 :                 pwallet->SetAddressBook(vchAddress, label, "receive");
     889             : 
     890           6 :                 if (pwallet->HaveKey(vchAddress)) {
     891           2 :                     throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
     892             :                 }
     893             : 
     894           5 :                 pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
     895             : 
     896           5 :                 if (!pwallet->AddKeyPubKey(key, pubKey)) {
     897           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
     898             :                 }
     899             : 
     900           5 :                 if (timestamp < pwallet->nTimeFirstKey) {
     901           0 :                     pwallet->nTimeFirstKey = timestamp;
     902             :                 }
     903             : 
     904           5 :                 success = true;
     905             :             }
     906             : 
     907             :             // Import scriptPubKey only.
     908          19 :             if (pubKeys.size() == 0 && keys.size() == 0) {
     909           9 :                 if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
     910           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
     911             :                 }
     912             : 
     913           9 :                 pwallet->MarkDirty();
     914             : 
     915           9 :                 if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script)) {
     916           0 :                     throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
     917             :                 }
     918             : 
     919           9 :                 if (scriptPubKey.getType() == UniValue::VOBJ) {
     920             :                     // add to address book or update label
     921           6 :                     if (IsValidDestination(dest)) {
     922          18 :                         pwallet->SetAddressBook(dest, label, "receive");
     923             :                     }
     924             :                 }
     925             : 
     926             :                 success = true;
     927             :             }
     928             :         }
     929             : 
     930          42 :         UniValue result = UniValue(UniValue::VOBJ);
     931          42 :         result.pushKV("success", UniValue(success));
     932          21 :         return result;
     933          22 :     } catch (const UniValue& e) {
     934          22 :         UniValue result = UniValue(UniValue::VOBJ);
     935          22 :         result.pushKV("success", UniValue(false));
     936          11 :         result.pushKV("error", e);
     937          11 :         return result;
     938           0 :     } catch (...) {
     939           0 :         UniValue result = UniValue(UniValue::VOBJ);
     940           0 :         result.pushKV("success", UniValue(false));
     941           0 :         result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
     942           0 :         return result;
     943             :     }
     944             : }
     945             : 
     946          66 : static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
     947             : {
     948         132 :     if (data.exists("timestamp")) {
     949          65 :         const UniValue& timestamp = data["timestamp"];
     950          65 :         if (timestamp.isNum()) {
     951          22 :             return timestamp.get_int64();
     952          43 :         } else if (timestamp.isStr() && timestamp.get_str() == "now") {
     953             :             return now;
     954             :         }
     955           2 :         throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected number or \"now\" timestamp value for key. got type %s", uvTypeName(timestamp.type())));
     956             :     }
     957           2 :     throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
     958             : }
     959             : 
     960          33 : UniValue importmulti(const JSONRPCRequest& mainRequest)
     961             : {
     962          33 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(mainRequest);
     963             : 
     964          33 :     if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp))
     965           0 :         return NullUniValue;
     966             : 
     967          33 :     if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
     968           0 :         throw std::runtime_error(
     969             :             "importmulti \"requests\" ( \"options\" )\n"
     970           0 :             "\nImport 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" +
     971           0 :             HelpRequiringPassphrase(pwallet) + "\n"
     972             : 
     973             :             "\nArguments:\n"
     974             :             "1. requests     (array, required) Data to be imported\n"
     975             :             "  [     (array of json objects)\n"
     976             :             "    {\n"
     977             :             "      \"scriptPubKey\": \"script\" | { \"address\":\"address\" }, (string / JSON, required) Type of scriptPubKey (string for script, json for address)\n"
     978             :             "      \"timestamp\": timestamp | \"now\"                      (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
     979             :             "                                                                 or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
     980             :             "                                                                 key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
     981             :             "                                                                 \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
     982             :             "                                                                 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
     983             :             "                                                                 creation time of all keys being imported by the importmulti call will be scanned.\n"
     984             :             "      \"redeemscript\": \"script\",                           (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
     985             :             "      \"pubkeys\": [\"pubKey\", ... ],                        (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
     986             :             "      \"keys\": [\"key\", ... ],                              (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
     987             :             "      \"internal\": true|false,                               (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n"
     988             :             "      \"watchonly\": true|false,                              (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"
     989             :             "      \"label\": label,                                       (string, optional, default: '') Label to assign to the address, only allowed with internal=false\n"
     990             :             "    }\n"
     991             :             "  ,...\n"
     992             :             "  ]\n"
     993             :             "2. options                 (JSON, optional)\n"
     994             :             "  {\n"
     995             :             "     \"rescan\": true|false,         (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
     996             :             "  }\n"
     997             : 
     998             :             "\nResult:\n"
     999             :             "[                               (Array) An array with the same size as the input that has the execution result\n"
    1000             :             "  {\n"
    1001             :             "    \"success\": true|false,    (boolean) True if import succeeded, otherwise false\n"
    1002             :             "    \"error\": {                (JSON Object) Object containing error information. Only present when import fails\n"
    1003             :             "      \"code\": n,              (numeric) The error code\n"
    1004             :             "      \"message\": xxxx         (string) The error message\n"
    1005             :             "    }\n"
    1006             :             "  }\n"
    1007             :             "  ,...\n"
    1008             :             "]\n"
    1009             :             "\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n"
    1010             :             "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n"
    1011           0 :             "\nExamples:\n" +
    1012           0 :             HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
    1013           0 :                                           "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
    1014           0 :             HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'"));
    1015             : 
    1016          33 :     RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
    1017          33 :     const UniValue& requests = mainRequest.params[0];
    1018             : 
    1019             :     //Default options
    1020          33 :     bool fRescan = true;
    1021             : 
    1022          33 :     if (mainRequest.params.size() > 1) {
    1023           9 :         const UniValue& options = mainRequest.params[1];
    1024             : 
    1025          18 :         if (options.exists("rescan")) {
    1026           9 :             fRescan = options["rescan"].get_bool();
    1027             :         }
    1028             :     }
    1029             : 
    1030          66 :     WalletRescanReserver reserver(pwallet);
    1031          33 :     if (fRescan && !reserver.reserve()) {
    1032           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
    1033             :     }
    1034             : 
    1035          33 :     int64_t now = 0;
    1036          33 :     bool fRunScan = false;
    1037          33 :     int64_t nLowestTimestamp = 0;
    1038             : 
    1039          66 :     UniValue response(UniValue::VARR);
    1040          33 :     {
    1041          68 :         LOCK2(cs_main, pwallet->cs_wallet);
    1042          33 :         EnsureWalletIsUnlocked(pwallet);
    1043             : 
    1044             :         // Verify all timestamps are present before importing any keys.
    1045          33 :         int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0;
    1046          65 :         for (const UniValue& data : requests.getValues()) {
    1047          34 :             GetImportTimestamp(data, now);
    1048             :         }
    1049             : 
    1050          31 :         const int64_t minimumTimestamp = 1;
    1051             : 
    1052          59 :         if (fRescan && chainActive.Tip()) {
    1053          28 :             nLowestTimestamp = chainActive.Tip()->GetBlockTime();
    1054             :         } else {
    1055             :             fRescan = false;
    1056             :         }
    1057             : 
    1058          63 :         for (const UniValue& data: requests.getValues()) {
    1059          32 :             const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
    1060          61 :             const UniValue result = processImport(pwallet, data, timestamp);
    1061          32 :             response.push_back(result);
    1062             : 
    1063          32 :             if (!fRescan) {
    1064           3 :                 continue;
    1065             :             }
    1066             : 
    1067             :             // If at least one request was successful then allow rescan.
    1068          29 :             if (result["success"].get_bool()) {
    1069          18 :                 fRunScan = true;
    1070             :             }
    1071             : 
    1072             :             // Get the lowest timestamp.
    1073          29 :             if (timestamp < nLowestTimestamp) {
    1074           7 :                 nLowestTimestamp = timestamp;
    1075             :             }
    1076             :         }
    1077             :     }
    1078          31 :     if (fRescan && fRunScan && requests.size()) {
    1079          17 :         int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
    1080          17 :         pwallet->ReacceptWalletTransactions();
    1081             : 
    1082          17 :         if (scannedTime > nLowestTimestamp) {
    1083           0 :             std::vector<UniValue> results = response.getValues();
    1084           0 :             response.clear();
    1085           0 :             response.setArray();
    1086           0 :             size_t i = 0;
    1087           0 :             for (const UniValue& request : requests.getValues()) {
    1088             :                 // If key creation date is within the successfully scanned
    1089             :                 // range, or if the import result already has an error set, let
    1090             :                 // the result stand unmodified. Otherwise replace the result
    1091             :                 // with an error message.
    1092           0 :                 if (scannedTime <= GetImportTimestamp(request, now) || results.at(i).exists("error")) {
    1093           0 :                     response.push_back(results.at(i));
    1094             :                 } else {
    1095           0 :                     UniValue result = UniValue(UniValue::VOBJ);
    1096           0 :                     result.pushKV("success", UniValue(false));
    1097           0 :                     result.pushKV("error", JSONRPCError(RPC_MISC_ERROR,
    1098           0 :                                                 strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
    1099             :                                                           "block from time %d, which is after or within %d seconds of key creation, and "
    1100             :                                                           "could contain transactions pertaining to the key. As a result, transactions "
    1101             :                                                           "and coins using this key may not appear in the wallet. This error could be "
    1102             :                                                           "caused by pruning or data corruption (see pivxd log for details) and could "
    1103             :                                                           "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
    1104             :                                                           "and -rescan options).",
    1105           0 :                                                           GetImportTimestamp(request, now), scannedTime - TIMESTAMP_WINDOW - 1, TIMESTAMP_WINDOW)));
    1106           0 :                     response.push_back(std::move(result));
    1107             :                 }
    1108           0 :                 ++i;
    1109             :             }
    1110             :         }
    1111             :     }
    1112             : 
    1113          31 :     return response;
    1114             : }
    1115             : 
    1116           1 : UniValue bip38encrypt(const JSONRPCRequest& request)
    1117             : {
    1118           1 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    1119             : 
    1120           1 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
    1121           0 :         return NullUniValue;
    1122             : 
    1123           1 :     if (request.fHelp || request.params.size() != 2)
    1124           0 :         throw std::runtime_error(
    1125             :             "bip38encrypt \"address\" \"passphrase\"\n"
    1126           0 :             "\nEncrypts a private key corresponding to 'address'.\n" +
    1127           0 :             HelpRequiringPassphrase(pwallet) + "\n"
    1128             : 
    1129             :             "\nArguments:\n"
    1130             :             "1. \"address\"      (string, required) The pivx address for the private key (you must hold the key already)\n"
    1131             :             "2. \"passphrase\"   (string, required) The passphrase you want the private key to be encrypted with - Valid special chars: !#$%&'()*+,-./:;<=>?`{|}~ \n"
    1132             : 
    1133             :             "\nResult:\n"
    1134             :             "\"key\"                (string) The encrypted private key\n"
    1135             : 
    1136           0 :             "\nExamples:\n" +
    1137           0 :             HelpExampleCli("bip38encrypt", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"mypasphrase\"") +
    1138           0 :             HelpExampleRpc("bip38encrypt", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"mypasphrase\""));
    1139             : 
    1140           3 :     LOCK2(cs_main, pwallet->cs_wallet);
    1141             : 
    1142           1 :     EnsureWalletIsUnlocked(pwallet);
    1143             : 
    1144           2 :     std::string strAddress = request.params[0].get_str();
    1145           2 :     std::string strPassphrase = request.params[1].get_str();
    1146             : 
    1147           2 :     CTxDestination address = DecodeDestination(strAddress);
    1148           1 :     if (!IsValidDestination(address))
    1149           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
    1150           1 :     const CKeyID* keyID = boost::get<CKeyID>(&address);
    1151           1 :     if (!keyID)
    1152           0 :         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
    1153           2 :     CKey vchSecret;
    1154           1 :     if (!pwallet->GetKey(*keyID, vchSecret))
    1155           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
    1156             : 
    1157           1 :     uint256 privKey = vchSecret.GetPrivKey_256();
    1158           5 :     std::string encryptedOut = BIP38_Encrypt(strAddress, strPassphrase, privKey, vchSecret.IsCompressed());
    1159             : 
    1160           2 :     UniValue result(UniValue::VOBJ);
    1161           1 :     result.pushKV("Address", strAddress);
    1162           1 :     result.pushKV("Encrypted Key", encryptedOut);
    1163             : 
    1164           1 :     return result;
    1165             : }
    1166             : 
    1167           1 : UniValue bip38decrypt(const JSONRPCRequest& request)
    1168             : {
    1169           1 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    1170             : 
    1171           1 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
    1172           0 :         return NullUniValue;
    1173             : 
    1174           1 :     if (request.fHelp || request.params.size() != 2)
    1175           0 :         throw std::runtime_error(
    1176             :             "bip38decrypt \"encrypted_key\" \"passphrase\"\n"
    1177           0 :             "\nDecrypts and then imports password protected private key.\n" +
    1178           0 :             HelpRequiringPassphrase(pwallet) + "\n"
    1179             : 
    1180             :             "\nArguments:\n"
    1181             :             "1. \"encrypted_key\"   (string, required) The encrypted private key\n"
    1182             :             "2. \"passphrase\"      (string, required) The passphrase you want the private key to be encrypted with\n"
    1183             : 
    1184             :             "\nResult:\n"
    1185             :             "\"key\"                (string) The decrypted private key\n"
    1186             : 
    1187           0 :             "\nExamples:\n" +
    1188           0 :             HelpExampleCli("bip38decrypt", "\"encryptedkey\" \"mypassphrase\"") +
    1189           0 :             HelpExampleRpc("bip38decrypt", "\"encryptedkey\" \"mypassphrase\""));
    1190             : 
    1191             : 
    1192             :     /** Collect private key and passphrase **/
    1193           2 :     std::string strKey = request.params[0].get_str();
    1194           2 :     std::string strPassphrase = request.params[1].get_str();
    1195             : 
    1196           1 :     uint256 privKey;
    1197           1 :     bool fCompressed;
    1198           4 :     if (!BIP38_Decrypt(strPassphrase, strKey, privKey, fCompressed))
    1199           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Failed To Decrypt");
    1200             : 
    1201           2 :     UniValue result(UniValue::VOBJ);
    1202           2 :     result.pushKV("privatekey", HexStr(privKey));
    1203             : 
    1204           2 :     CKey key;
    1205           1 :     key.Set(privKey.begin(), privKey.end(), fCompressed);
    1206             : 
    1207           1 :     if (!key.IsValid())
    1208           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Private Key Not Valid");
    1209             : 
    1210           2 :     WalletRescanReserver reserver(pwallet);
    1211           1 :     if (!reserver.reserve()) {
    1212           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
    1213             :     }
    1214             : 
    1215           1 :     CPubKey pubkey = key.GetPubKey();
    1216           1 :     assert(key.VerifyPubKey(pubkey));
    1217           3 :     result.pushKV("Address", EncodeDestination(pubkey.GetID()));
    1218           1 :     CKeyID vchAddress = pubkey.GetID();
    1219           1 :     {
    1220           2 :         LOCK2(cs_main, pwallet->cs_wallet);
    1221           1 :         EnsureWalletIsUnlocked(pwallet);
    1222           1 :         pwallet->MarkDirty();
    1223           3 :         pwallet->SetAddressBook(vchAddress, "", AddressBook::AddressBookPurpose::RECEIVE);
    1224             : 
    1225             :         // Don't throw error in case a key is already there
    1226           1 :         if (pwallet->HaveKey(vchAddress))
    1227           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Key already held by wallet");
    1228             : 
    1229           1 :         pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
    1230             : 
    1231           1 :         if (!pwallet->AddKeyPubKey(key, pubkey))
    1232           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
    1233             :     }
    1234             : 
    1235             :     // whenever a key is imported, we need to scan the whole chain
    1236           2 :     pwallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
    1237             : 
    1238           1 :     return result;
    1239             : }
    1240             : 
    1241             : // Sapling
    1242             : 
    1243        1008 : UniValue importsaplingkey(const JSONRPCRequest& request)
    1244             : {
    1245        1008 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    1246             : 
    1247        1008 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
    1248           0 :         return NullUniValue;
    1249             : 
    1250        1008 :     if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
    1251           1 :         throw std::runtime_error(
    1252             :                 "importsaplingkey \"key\" ( rescan height )\n"
    1253             :                 "\nAdds a key (as returned by exportsaplingkey) to your wallet.\n"
    1254           2 :                 + HelpRequiringPassphrase(pwallet) + "\n"
    1255             : 
    1256             :                 "\nArguments:\n"
    1257             :                 "1. \"key\"             (string, required) The zkey (see exportsaplingkey)\n"
    1258             :                 "2. rescan             (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n"
    1259             :                 "3. height              (numeric, optional, default=0) Block height to start rescan from\n"
    1260             :                 "\nNote: This call can take minutes to complete if rescan is true.\n"
    1261             : 
    1262             :                 "\nResult:\n"
    1263             :                 "{\n"
    1264             :                 "  \"address\" : \"address|DefaultAddress\",    (string) The address corresponding to the spending key (the default address).\n"
    1265             :                 "}\n"
    1266             : 
    1267             :                 "\nExamples:\n"
    1268             :                 "\nExport a zkey\n"
    1269           5 :                 + HelpExampleCli("exportsaplingkey", "\"myaddress\"") +
    1270             :                 "\nImport the key with rescan\n"
    1271           5 :                 + HelpExampleCli("importsaplingkey", "\"mykey\"") +
    1272             :                 "\nImport the key with partial rescan\n"
    1273           6 :                 + HelpExampleCli("importsaplingkey", "\"mykey\" whenkeyisnew 30000") +
    1274             :                 "\nRe-import the key with longer partial rescan\n"
    1275           6 :                 + HelpExampleCli("importsaplingkey", "\"mykey\" yes 20000") +
    1276             :                 "\nAs a JSON-RPC call\n"
    1277           5 :                 + HelpExampleRpc("importsaplingkey", "\"mykey\", \"no\"")
    1278           3 :         );
    1279             : 
    1280             :     // Whether to perform rescan after import
    1281        1007 :     bool fRescan = true;
    1282        1007 :     bool fIgnoreExistingKey = true;
    1283        1007 :     if (request.params.size() > 1) {
    1284           4 :         auto rescan = request.params[1].get_str();
    1285           2 :         if (rescan.compare("whenkeyisnew") != 0) {
    1286           2 :             fIgnoreExistingKey = false;
    1287           2 :             if (rescan.compare("yes") == 0) {
    1288             :                 fRescan = true;
    1289           0 :             } else if (rescan.compare("no") == 0) {
    1290             :                 fRescan = false;
    1291             :             } else {
    1292           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "rescan must be \"yes\", \"no\" or \"whenkeyisnew\"");
    1293             :             }
    1294             :         }
    1295             :     }
    1296             : 
    1297        2014 :     WalletRescanReserver reserver(pwallet);
    1298        1007 :     if (fRescan && !reserver.reserve()) {
    1299           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
    1300             :     }
    1301             : 
    1302        2014 :     UniValue result(UniValue::VOBJ);
    1303        1007 :     CBlockIndex* pindexRescan{nullptr};
    1304        1007 :     {
    1305        2013 :         LOCK2(cs_main, pwallet->cs_wallet);
    1306        1007 :         EnsureWalletIsUnlocked(pwallet);
    1307             : 
    1308             :         // Height to rescan from
    1309        1007 :         int nRescanHeight = 0;
    1310        1007 :         if (request.params.size() > 2)
    1311           0 :             nRescanHeight = request.params[2].get_int();
    1312        1007 :         if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) {
    1313           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
    1314             :         }
    1315             : 
    1316        2013 :         std::string strSecret = request.params[0].get_str();
    1317        2013 :         auto spendingkey = KeyIO::DecodeSpendingKey(strSecret);
    1318        1007 :         if (!IsValidSpendingKey(spendingkey)) {
    1319           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
    1320             :         }
    1321             : 
    1322        1007 :         libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey);
    1323        3021 :         result.pushKV("address", KeyIO::EncodePaymentAddress( saplingSpendingKey.DefaultAddress()));
    1324             : 
    1325             :         // Sapling support
    1326        1007 :         auto addResult = pwallet->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet(Params().GetConsensus(), saplingSpendingKey, -1);
    1327        1007 :         if (addResult == KeyAlreadyExists && fIgnoreExistingKey) {
    1328           1 :             return result;
    1329             :         }
    1330        1006 :         pwallet->MarkDirty();
    1331        1006 :         if (addResult == KeyNotAdded) {
    1332           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
    1333             :         }
    1334        2012 :         pindexRescan = chainActive[nRescanHeight];
    1335             :     }
    1336             : 
    1337             :     // We want to scan for transactions and notes
    1338        1006 :     if (fRescan) {
    1339        1006 :         pwallet->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
    1340             :     }
    1341             : 
    1342        1006 :     return result;
    1343             : }
    1344             : 
    1345           3 : UniValue importsaplingviewingkey(const JSONRPCRequest& request)
    1346             : {
    1347           3 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    1348             : 
    1349           3 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
    1350           0 :         return NullUniValue;
    1351             : 
    1352           3 :     if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
    1353           0 :         throw std::runtime_error(
    1354             :                 "importsaplingviewingkey \"vkey\" ( rescan height )\n"
    1355             :                 "\nAdds a viewing key (as returned by exportsaplingviewingkey) to your wallet.\n"
    1356           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
    1357             : 
    1358             :                 "\nArguments:\n"
    1359             :                 "1. \"vkey\"        (string, required) The viewing key (see exportsaplingviewingkey)\n"
    1360             :                 "2. rescan          (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n"
    1361             :                 "3. height          (numeric, optional, default=0) Block height to start rescan from\n"
    1362             :                 "\nNote: This call can take minutes to complete if rescan is true.\n"
    1363             : 
    1364             :                 "\nResult:\n"
    1365             :                 "{\n"
    1366             :                 "  \"address\" : \"address|DefaultAddress\",    (string) The address corresponding to the viewing key (for Sapling, this is the default address).\n"
    1367             :                 "}\n"
    1368             : 
    1369             :                 "\nExamples:\n"
    1370             :                 "\nImport a viewing key\n"
    1371           0 :                 + HelpExampleCli("importsaplingviewingkey", "\"vkey\"") +
    1372             :                 "\nImport the viewing key without rescan\n"
    1373           0 :                 + HelpExampleCli("importsaplingviewingkey", "\"vkey\", no") +
    1374             :                 "\nImport the viewing key with partial rescan\n"
    1375           0 :                 + HelpExampleCli("importsaplingviewingkey", "\"vkey\" whenkeyisnew 30000") +
    1376             :                 "\nRe-import the viewing key with longer partial rescan\n"
    1377           0 :                 + HelpExampleCli("importsaplingviewingkey", "\"vkey\" yes 20000") +
    1378             :                 "\nAs a JSON-RPC call\n"
    1379           0 :                 + HelpExampleRpc("importsaplingviewingkey", "\"vkey\", \"no\"")
    1380           0 :         );
    1381             : 
    1382             :     // Whether to perform rescan after import
    1383           3 :     bool fRescan = true;
    1384           3 :     bool fIgnoreExistingKey = true;
    1385           3 :     if (request.params.size() > 1) {
    1386           6 :         auto rescan = request.params[1].get_str();
    1387           3 :         if (rescan.compare("whenkeyisnew") != 0) {
    1388           2 :             fIgnoreExistingKey = false;
    1389           2 :             if (rescan.compare("no") == 0) {
    1390             :                 fRescan = false;
    1391           2 :             } else if (rescan.compare("yes") != 0) {
    1392           0 :                 throw JSONRPCError(
    1393             :                         RPC_INVALID_PARAMETER,
    1394           0 :                         "rescan must be \"yes\", \"no\" or \"whenkeyisnew\"");
    1395             :             }
    1396             :         }
    1397             :     }
    1398             : 
    1399           6 :     WalletRescanReserver reserver(pwallet);
    1400           3 :     if (fRescan && !reserver.reserve()) {
    1401           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
    1402             :     }
    1403             : 
    1404           6 :     UniValue result(UniValue::VOBJ);
    1405           3 :     CBlockIndex* pindexRescan{nullptr};
    1406           3 :     {
    1407           6 :         LOCK2(cs_main, pwallet->cs_wallet);
    1408           3 :         EnsureWalletIsUnlocked(pwallet);
    1409             : 
    1410             :         // Height to rescan from
    1411           3 :         int nRescanHeight = 0;
    1412           3 :         if (request.params.size() > 2) {
    1413           1 :             nRescanHeight = request.params[2].get_int();
    1414             :         }
    1415           3 :         if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) {
    1416           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
    1417             :         }
    1418             : 
    1419           6 :         std::string strVKey = request.params[0].get_str();
    1420           6 :         libzcash::ViewingKey viewingkey = KeyIO::DecodeViewingKey(strVKey);
    1421           3 :         if (!IsValidViewingKey(viewingkey)) {
    1422           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
    1423             :         }
    1424           3 :         libzcash::SaplingExtendedFullViewingKey efvk = *boost::get<libzcash::SaplingExtendedFullViewingKey>(&viewingkey);
    1425           9 :         result.pushKV("address", KeyIO::EncodePaymentAddress(efvk.DefaultAddress()));
    1426             : 
    1427           3 :         auto addResult = pwallet->GetSaplingScriptPubKeyMan()->AddViewingKeyToWallet(efvk);
    1428           3 :         if (addResult == SpendingKeyExists) {
    1429           0 :             throw JSONRPCError(
    1430             :                     RPC_WALLET_ERROR,
    1431           0 :                     "The wallet already contains the private key for this viewing key");
    1432           3 :         } else if (addResult == KeyAlreadyExists && fIgnoreExistingKey) {
    1433           0 :             return result;
    1434             :         }
    1435           3 :         pwallet->MarkDirty();
    1436           3 :         if (addResult == KeyNotAdded) {
    1437           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet");
    1438             :         }
    1439             : 
    1440           6 :         pindexRescan = chainActive[nRescanHeight];
    1441             :     }
    1442             : 
    1443             :     // We want to scan for transactions and notes
    1444           3 :     if (fRescan) {
    1445           3 :         pwallet->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
    1446             :     }
    1447             : 
    1448           3 :     return result;
    1449             : }
    1450             : 
    1451           3 : UniValue exportsaplingviewingkey(const JSONRPCRequest& request)
    1452             : {
    1453           3 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    1454             : 
    1455           3 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
    1456           0 :         return NullUniValue;
    1457             : 
    1458           3 :     if (request.fHelp || request.params.size() != 1)
    1459           0 :         throw std::runtime_error(
    1460             :                 "exportsaplingviewingkey \"shield_addr\"\n"
    1461             :                 "\nReveals the viewing key corresponding to 'shield_addr'.\n"
    1462             :                 "Then the importsaplingviewingkey can be used with this output\n"
    1463           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
    1464             : 
    1465             :                 "\nArguments:\n"
    1466             :                 "1. \"shield_addr\"   (string, required) The shield addr for the viewing key\n"
    1467             : 
    1468             :                 "\nResult:\n"
    1469             :                 "\"vkey\"                  (string) The viewing key\n"
    1470             : 
    1471             :                 "\nExamples:\n"
    1472           0 :                 + HelpExampleCli("exportsaplingviewingkey", "\"myaddress\"")
    1473           0 :                 + HelpExampleRpc("exportsaplingviewingkey", "\"myaddress\"")
    1474           0 :         );
    1475             : 
    1476           9 :     LOCK2(cs_main, pwallet->cs_wallet);
    1477             : 
    1478           3 :     EnsureWalletIsUnlocked(pwallet);
    1479             : 
    1480           6 :     std::string strAddress = request.params[0].get_str();
    1481           6 :     auto address = KeyIO::DecodePaymentAddress(strAddress);
    1482           3 :     if (!IsValidPaymentAddress(address)) {
    1483           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid shield addr");
    1484             :     }
    1485           3 :     const libzcash::SaplingPaymentAddress &sapAddr = *boost::get<libzcash::SaplingPaymentAddress>(&address);
    1486           6 :     auto vk = pwallet->GetSaplingScriptPubKeyMan()->GetViewingKeyForPaymentAddress(sapAddr);
    1487           3 :     if (vk) {
    1488           6 :         return KeyIO::EncodeViewingKey(vk.get());
    1489             :     } else {
    1490           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this shield addr");
    1491             :     }
    1492             : }
    1493             : 
    1494        1006 : UniValue exportsaplingkey(const JSONRPCRequest& request)
    1495             : {
    1496        1006 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
    1497             : 
    1498        1006 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
    1499           0 :         return NullUniValue;
    1500             : 
    1501        1006 :     if (request.fHelp || request.params.size() != 1)
    1502           2 :         throw std::runtime_error(
    1503             :                 "exportsaplingkey \"shield_addr\"\n"
    1504             :                 "\nReveals the key corresponding to the 'shield_addr'.\n"
    1505             :                 "Then the importsaplingkey can be used with this output\n"
    1506           4 :                 + HelpRequiringPassphrase(pwallet) + "\n"
    1507             : 
    1508             :                 "\nArguments:\n"
    1509             :                 "1. \"addr\"   (string, required) The shield addr for the private key\n"
    1510             : 
    1511             :                 "\nResult:\n"
    1512             :                 "\"key\"                  (string) The private key\n"
    1513             : 
    1514             :                 "\nExamples:\n"
    1515          10 :                 + HelpExampleCli("exportsaplingkey", "\"myaddress\"")
    1516          10 :                 + HelpExampleRpc("exportsaplingkey", "\"myaddress\"")
    1517           6 :         );
    1518             : 
    1519        3012 :     LOCK2(cs_main, pwallet->cs_wallet);
    1520             : 
    1521        1004 :     EnsureWalletIsUnlocked(pwallet);
    1522             : 
    1523        2008 :     std::string strAddress = request.params[0].get_str();
    1524             : 
    1525        2008 :     auto address = KeyIO::DecodePaymentAddress(strAddress);
    1526        1004 :     if (!IsValidPaymentAddress(address)) {
    1527           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid shield addr");
    1528             :     }
    1529        1004 :     libzcash::SaplingPaymentAddress addr = *boost::get<libzcash::SaplingPaymentAddress>(&address);
    1530             : 
    1531             :     // Sapling support
    1532        2008 :     Optional<libzcash::SaplingExtendedSpendingKey> sk = pwallet->GetSaplingScriptPubKeyMan()->GetSpendingKeyForPaymentAddress(addr);
    1533        1004 :     if (!sk) {
    1534           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key for this shield addr");
    1535             :     }
    1536        2008 :     return KeyIO::EncodeSpendingKey(libzcash::SpendingKey(sk.get()));
    1537             : }

Generated by: LCOV version 1.14