26 #include <boost/algorithm/string.hpp>
35 std::stringstream ret;
36 for (
const unsigned char c : str) {
37 if (c <= 32 || c >= 128 || c ==
'%') {
38 ret << '%' << HexStr(Span<const unsigned char>(&c, 1));
47 std::stringstream ret;
48 for (
unsigned int pos = 0; pos < str.length(); pos++) {
49 unsigned char c = str[pos];
50 if (c ==
'%' && pos+2 < str.length()) {
51 c = (((str[pos+1]>>6)*9+((str[pos+1]-
'0')&15)) << 4) |
52 ((str[pos+2]>>6)*9+((str[pos+2]-
'0')&15));
62 bool fLabelFound =
false;
64 spk_man->GetKey(keyid, key);
66 const auto* address_book_entry =
wallet.FindAddressBookEntry(dest);
67 if (address_book_entry) {
68 if (!strAddr.empty()) {
86 int64_t scanned_time =
wallet.RescanFromTime(time_begin, reserver, update);
87 if (
wallet.IsAbortingRescan()) {
89 }
else if (scanned_time > time_begin) {
97 "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
98 "Hint: use importmulti to import more than one private key.\n"
99 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
100 "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"
101 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
109 "\nDump a private key\n"
111 "\nImport the private key with rescan\n"
113 "\nImport using a label and without rescan\n"
114 +
HelpExampleCli(
"importprivkey",
"\"mykey\" \"testing\" false") +
115 "\nImport using default blank label and without rescan\n"
117 "\nAs a JSON-RPC call\n"
118 +
HelpExampleRpc(
"importprivkey",
"\"mykey\", \"testing\", false")
134 LOCK(pwallet->cs_wallet);
138 std::string strSecret = request.params[0].get_str();
139 std::string strLabel =
"";
140 if (!request.params[1].isNull())
141 strLabel = request.params[1].get_str();
144 if (!request.params[2].isNull())
145 fRescan = request.params[2].get_bool();
147 if (fRescan && pwallet->chain().havePruned()) {
154 if (fRescan && !reserver.
reserve()) {
165 pwallet->MarkDirty();
171 if (!request.params[1].isNull() || !pwallet->FindAddressBookEntry(dest)) {
172 pwallet->SetAddressBook(dest, strLabel,
"receive");
177 if (!pwallet->ImportPrivKeys({{vchAddress, key}}, 1)) {
199 "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
200 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
204 "\nImport a private key\n"
206 "\nAbort the running wallet rescan\n"
208 "\nAs a JSON-RPC call\n"
216 if (!pwallet->IsScanning() || pwallet->IsAbortingRescan())
return false;
217 pwallet->AbortRescan();
226 "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
227 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
228 "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"
229 "If you have the full public key, you should call importpubkey instead of this.\n"
230 "Hint: use importmulti to import more than one address.\n"
231 "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
232 "as change, and not show up in many RPCs.\n"
233 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
242 "\nImport an address with rescan\n"
244 "\nImport using a label without rescan\n"
245 +
HelpExampleCli(
"importaddress",
"\"myaddress\" \"testing\" false") +
246 "\nAs a JSON-RPC call\n"
247 +
HelpExampleRpc(
"importaddress",
"\"myaddress\", \"testing\", false")
256 std::string strLabel;
257 if (!request.params[1].isNull())
258 strLabel = request.params[1].get_str();
262 if (!request.params[2].isNull())
263 fRescan = request.params[2].get_bool();
265 if (fRescan && pwallet->chain().havePruned()) {
273 if (fRescan && !reserver.
reserve()) {
279 if (!request.params[3].isNull())
280 fP2SH = request.params[3].get_bool();
283 LOCK(pwallet->cs_wallet);
294 pwallet->MarkDirty();
297 }
else if (
IsHex(request.params[0].get_str())) {
298 std::vector<unsigned char> data(
ParseHex(request.params[0].get_str()));
299 CScript redeem_script(data.begin(), data.end());
301 std::set<CScript> scripts = {redeem_script};
302 pwallet->ImportScripts(scripts, 0 );
308 pwallet->ImportScriptPubKeys(strLabel, scripts,
false ,
true , 1 );
317 LOCK(pwallet->cs_wallet);
318 pwallet->ReacceptWalletTransactions();
330 "\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
343 if (!
DecodeHexTx(tx, request.params[0].get_str())) {
353 std::vector<uint256> vMatch;
354 std::vector<unsigned int> vIndex;
355 if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
359 LOCK(pwallet->cs_wallet);
361 if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(),
FoundBlock().height(height))) {
365 std::vector<uint256>::const_iterator it;
366 if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
370 unsigned int txnIndex = vIndex[it - vMatch.begin()];
373 if (pwallet->IsMine(*tx_ref)) {
374 pwallet->AddToWallet(std::move(tx_ref),
TxStateConfirmed{merkleBlock.header.GetHash(), height,
static_cast<int>(txnIndex)});
386 "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
392 HelpExampleCli(
"removeprunedfunds",
"\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
393 "\nAs a JSON-RPC call\n"
394 +
HelpExampleRpc(
"removeprunedfunds",
"\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
401 LOCK(pwallet->cs_wallet);
404 std::vector<uint256> vHash;
405 vHash.push_back(hash);
406 std::vector<uint256> vHashOut;
412 if(vHashOut.empty()) {
424 "\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"
425 "Hint: use importmulti to import more than one public key.\n"
426 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
427 "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"
428 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
436 "\nImport a public key with rescan\n"
438 "\nImport using a label without rescan\n"
439 +
HelpExampleCli(
"importpubkey",
"\"mypubkey\" \"testing\" false") +
440 "\nAs a JSON-RPC call\n"
441 +
HelpExampleRpc(
"importpubkey",
"\"mypubkey\", \"testing\", false")
450 std::string strLabel;
451 if (!request.params[1].isNull())
452 strLabel = request.params[1].get_str();
456 if (!request.params[2].isNull())
457 fRescan = request.params[2].get_bool();
459 if (fRescan && pwallet->chain().havePruned()) {
467 if (fRescan && !reserver.
reserve()) {
471 if (!
IsHex(request.params[0].get_str()))
473 std::vector<unsigned char> data(
ParseHex(request.params[0].get_str()));
479 LOCK(pwallet->cs_wallet);
481 std::set<CScript> script_pub_keys;
486 pwallet->MarkDirty();
488 pwallet->ImportScriptPubKeys(strLabel, script_pub_keys,
true ,
true , 1 );
490 pwallet->ImportPubKeys({pubKey.
GetID()}, {{pubKey.
GetID(), pubKey}} , {} ,
false ,
false , 1 );
496 LOCK(pwallet->cs_wallet);
497 pwallet->ReacceptWalletTransactions();
510 "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
511 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
517 "\nDump the wallet\n"
519 "\nImport the wallet\n"
521 "\nImport using the json rpc call\n"
531 if (pwallet->chain().havePruned()) {
543 int64_t nTimeBegin = 0;
546 LOCK(pwallet->cs_wallet);
551 file.open(
fs::u8path(request.params[0].get_str()), std::ios::in | std::ios::ate);
552 if (!file.is_open()) {
557 int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
558 file.seekg(0, file.beg);
562 pwallet->chain().showProgress(
strprintf(
"%s " +
_(
"Importing…").translated, pwallet->GetDisplayName()), 0,
false);
563 std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
564 std::vector<std::pair<CScript, int64_t>> scripts;
565 while (file.good()) {
566 pwallet->chain().showProgress(
"", std::max(1, std::min(50, (
int)(((
double)file.tellg() / (
double)nFilesize) * 100))),
false);
568 std::getline(file, line);
569 if (line.empty() || line[0] ==
'#')
572 std::vector<std::string> vstr;
573 boost::split(vstr, line, boost::is_any_of(
" "));
579 std::string strLabel;
581 for (
unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
582 if (vstr[nStr].front() ==
'#')
584 if (vstr[nStr] ==
"change=1")
586 if (vstr[nStr] ==
"reserve=1")
588 if (vstr[nStr].substr(0,6) ==
"label=") {
593 keys.push_back(std::make_tuple(key, nTime, fLabel, strLabel));
594 }
else if(
IsHex(vstr[0])) {
595 std::vector<unsigned char> vData(
ParseHex(vstr[0]));
598 scripts.push_back(std::pair<CScript, int64_t>(script, birth_time));
604 pwallet->chain().showProgress(
"", 100,
false);
607 double total = (double)(keys.size() + scripts.size());
609 for (
const auto& key_tuple : keys) {
610 pwallet->chain().showProgress(
"", std::max(50, std::min(75, (
int)((progress / total) * 100) + 50)),
false);
611 const CKey& key = std::get<0>(key_tuple);
612 int64_t time = std::get<1>(key_tuple);
613 bool has_label = std::get<2>(key_tuple);
614 std::string label = std::get<3>(key_tuple);
622 if (!pwallet->ImportPrivKeys({{keyid, key}}, time)) {
629 pwallet->SetAddressBook(
PKHash(keyid), label,
"receive");
631 nTimeBegin = std::min(nTimeBegin, time);
634 for (
const auto& script_pair : scripts) {
635 pwallet->chain().showProgress(
"", std::max(50, std::min(75, (
int)((progress / total) * 100) + 50)),
false);
636 const CScript& script = script_pair.first;
637 int64_t time = script_pair.second;
639 if (!pwallet->ImportScripts({script}, time)) {
640 pwallet->WalletLogPrintf(
"Error importing script %s\n",
HexStr(script));
645 nTimeBegin = std::min(nTimeBegin, time);
650 pwallet->chain().showProgress(
"", 100,
false);
652 pwallet->chain().showProgress(
"", 100,
false);
654 pwallet->MarkDirty();
667 "\nReveals the private key corresponding to 'address'.\n"
668 "Then the importprivkey can be used with this output\n",
691 std::string strAddress = request.params[0].get_str();
697 if (keyid.IsNull()) {
701 if (!spk_man.
GetKey(keyid, vchSecret)) {
713 "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
714 "Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
715 "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"
716 "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
740 wallet.BlockUntilSyncedToCurrentChain();
747 filepath = fs::absolute(filepath);
763 std::map<CKeyID, int64_t> mapKeyBirth;
764 wallet.GetKeyBirthTimes(mapKeyBirth);
766 int64_t block_time = 0;
774 std::set<CScriptID> scripts = spk_man.
GetCScripts();
777 std::vector<std::pair<int64_t, CKeyID> > vKeyBirth;
778 for (
const auto& entry : mapKeyBirth) {
779 vKeyBirth.push_back(std::make_pair(entry.second, entry.first));
782 std::sort(vKeyBirth.begin(), vKeyBirth.end());
787 file <<
strprintf(
"# * Best block at time of backup was %i (%s),\n",
wallet.GetLastBlockHeight(),
wallet.GetLastBlockHash().ToString());
796 if (spk_man.
GetKey(seed_id, seed)) {
800 file <<
"# extended private masterkey: " <<
EncodeExtKey(masterKey) <<
"\n\n";
803 for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
804 const CKeyID &keyid = it->second;
807 std::string strLabel;
809 if (spk_man.
GetKey(keyid, key)) {
811 const auto it{spk_man.mapKeyMetadata.find(keyid)};
812 if (it != spk_man.mapKeyMetadata.end()) metadata = it->second;
816 }
else if (keyid == seed_id) {
818 }
else if (mapKeyPool.count(keyid)) {
821 file <<
"inactivehdseed=1";
829 for (
const CScriptID &scriptid : scripts) {
831 std::string create_time =
"0";
834 auto it = spk_man.m_script_metadata.find(scriptid);
835 if (it != spk_man.m_script_metadata.end()) {
840 file <<
strprintf(
" # addr=%s\n", address);
844 file <<
"# End of dump\n";
879 std::vector<std::vector<unsigned char>> solverdata;
882 switch (script_type) {
899 if (!subscript)
return "missing redeemscript";
900 if (
CScriptID(*subscript) !=
id)
return "redeemScript does not match the scriptPubKey";
905 for (
size_t i = 1; i + 1< solverdata.size(); ++i) {
917 if (!subscript)
return "missing witnessscript";
918 if (
CScriptID(*subscript) !=
id)
return "witnessScript does not match the scriptPubKey or redeemScript";
935 return "unspendable script";
939 return "unrecognized script";
944 static UniValue ProcessImportLegacy(
ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys,
bool& have_solving_data,
const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
949 const UniValue& scriptPubKey = data[
"scriptPubKey"];
954 const std::string& output = isScript ? scriptPubKey.
get_str() : scriptPubKey[
"address"].
get_str();
957 const std::string& strRedeemScript = data.
exists(
"redeemscript") ? data[
"redeemscript"].
get_str() :
"";
958 const std::string& witness_script_hex = data.
exists(
"witnessscript") ? data[
"witnessscript"].
get_str() :
"";
961 const bool internal = data.
exists(
"internal") ? data[
"internal"].
get_bool() :
false;
962 const bool watchOnly = data.
exists(
"watchonly") ? data[
"watchonly"].
get_bool() :
false;
964 if (data.
exists(
"range")) {
980 if (!
IsHex(output)) {
983 std::vector<unsigned char> vData(
ParseHex(output));
984 script =
CScript(vData.begin(), vData.end());
990 script_pub_keys.emplace(script);
993 if (strRedeemScript.size()) {
994 if (!
IsHex(strRedeemScript)) {
997 auto parsed_redeemscript =
ParseHex(strRedeemScript);
998 import_data.
redeemscript = std::make_unique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
1000 if (witness_script_hex.size()) {
1001 if (!
IsHex(witness_script_hex)) {
1004 auto parsed_witnessscript =
ParseHex(witness_script_hex);
1005 import_data.
witnessscript = std::make_unique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
1007 for (
size_t i = 0; i < pubKeys.
size(); ++i) {
1008 const auto& str = pubKeys[i].
get_str();
1012 auto parsed_pubkey =
ParseHex(str);
1013 CPubKey pubkey(parsed_pubkey);
1017 pubkey_map.emplace(pubkey.
GetID(), pubkey);
1018 ordered_pubkeys.push_back(pubkey.
GetID());
1020 for (
size_t i = 0; i < keys.
size(); ++i) {
1021 const auto& str = keys[i].
get_str();
1028 if (pubkey_map.count(
id)) {
1029 pubkey_map.erase(
id);
1031 privkey_map.emplace(
id, key);
1037 if (have_solving_data) {
1042 bool spendable = std::all_of(import_data.
used_keys.begin(), import_data.
used_keys.end(), [&](
const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
1043 if (!watchOnly && !spendable) {
1044 warnings.
push_back(
"Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1046 if (watchOnly && spendable) {
1047 warnings.
push_back(
"All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1051 if (
error.empty()) {
1052 for (
const auto& require_key : import_data.
used_keys) {
1053 if (!require_key.second)
continue;
1054 if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
1055 error =
"some required keys are missing";
1060 if (!
error.empty()) {
1061 warnings.
push_back(
"Importing as non-solvable: " +
error +
". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
1064 privkey_map.clear();
1065 have_solving_data =
false;
1068 if (import_data.
redeemscript) warnings.
push_back(
"Ignoring redeemscript as this is not a P2SH script.");
1069 if (import_data.
witnessscript) warnings.
push_back(
"Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
1070 for (
auto it = privkey_map.begin(); it != privkey_map.end(); ) {
1072 if (import_data.
used_keys.count(oldit->first) == 0) {
1073 warnings.
push_back(
"Ignoring irrelevant private key.");
1074 privkey_map.erase(oldit);
1077 for (
auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
1079 auto key_data_it = import_data.
used_keys.find(oldit->first);
1080 if (key_data_it == import_data.
used_keys.end() || !key_data_it->second) {
1081 warnings.
push_back(
"Ignoring public key \"" +
HexStr(oldit->first) +
"\" as it doesn't appear inside P2PKH or P2WPKH.");
1082 pubkey_map.erase(oldit);
1091 static UniValue ProcessImportDescriptor(
ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys,
bool& have_solving_data,
const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
1095 const std::string& descriptor = data[
"desc"].
get_str();
1098 auto parsed_desc =
Parse(descriptor, keys,
error,
true);
1106 have_solving_data = parsed_desc->IsSolvable();
1107 const bool watch_only = data.
exists(
"watchonly") ? data[
"watchonly"].
get_bool() :
false;
1109 int64_t range_start = 0, range_end = 0;
1110 if (!parsed_desc->IsRange() && data.
exists(
"range")) {
1112 }
else if (parsed_desc->IsRange()) {
1113 if (!data.
exists(
"range")) {
1122 for (
int i = range_start; i <= range_end; ++i) {
1124 std::vector<CScript> scripts_temp;
1125 parsed_desc->Expand(i, keys, scripts_temp, out_keys);
1126 std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
1127 for (
const auto& key_pair : out_keys.
pubkeys) {
1128 ordered_pubkeys.push_back(key_pair.first);
1131 for (
const auto& x : out_keys.
scripts) {
1135 parsed_desc->ExpandPrivate(i, keys, out_keys);
1137 std::copy(out_keys.
pubkeys.begin(), out_keys.
pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
1138 std::copy(out_keys.
keys.begin(), out_keys.
keys.end(), std::inserter(privkey_map, privkey_map.end()));
1142 for (
size_t i = 0; i < priv_keys.
size(); ++i) {
1143 const auto& str = priv_keys[i].
get_str();
1152 if (!pubkey_map.count(
id)) {
1153 warnings.
push_back(
"Ignoring irrelevant private key.");
1155 privkey_map.emplace(
id, key);
1163 bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
1164 [&](
const std::pair<CKeyID, CPubKey>& used_key) {
1165 return privkey_map.count(used_key.first) > 0;
1167 [&](
const std::pair<
CKeyID, std::pair<CPubKey, KeyOriginInfo>>& entry) {
1168 return privkey_map.count(entry.first) > 0;
1170 if (!watch_only && !spendable) {
1171 warnings.
push_back(
"Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
1173 if (watch_only && spendable) {
1174 warnings.
push_back(
"All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
1186 const bool internal = data.exists(
"internal") ? data[
"internal"].get_bool() :
false;
1188 if (
internal && data.exists(
"label")) {
1191 const std::string& label = data.exists(
"label") ? data[
"label"].get_str() :
"";
1192 const bool add_keypool = data.exists(
"keypool") ? data[
"keypool"].get_bool() :
false;
1200 std::map<CKeyID, CPubKey> pubkey_map;
1201 std::map<CKeyID, CKey> privkey_map;
1202 std::set<CScript> script_pub_keys;
1203 std::vector<CKeyID> ordered_pubkeys;
1204 bool have_solving_data;
1206 if (data.exists(
"scriptPubKey") && data.exists(
"desc")) {
1208 }
else if (data.exists(
"scriptPubKey")) {
1209 warnings =
ProcessImportLegacy(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
1210 }
else if (data.exists(
"desc")) {
1211 warnings =
ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
1222 for (
const CScript& script : script_pub_keys) {
1233 if (!
wallet.ImportPrivKeys(privkey_map, timestamp)) {
1236 if (!
wallet.ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.
key_origins, add_keypool,
internal, timestamp)) {
1239 if (!
wallet.ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !
internal, timestamp)) {
1246 result.
pushKV(
"error", e);
1252 if (warnings.
size()) result.
pushKV(
"warnings", warnings);
1258 if (data.
exists(
"timestamp")) {
1259 const UniValue& timestamp = data[
"timestamp"];
1260 if (timestamp.
isNum()) {
1262 }
else if (timestamp.
isStr() && timestamp.
get_str() ==
"now") {
1273 "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
1274 "If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
1275 "Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
1276 "\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
1277 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
1278 "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
1286 "", {
"\"<script>\" | { \"address\":\"<address>\" }",
"string / json"}
1289 " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
1290 " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
1291 " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
1292 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
1293 " creation time of all keys being imported by the importmulti call will be scanned.",
1294 "", {
"timestamp | \"now\"",
"integer / string"}
1298 {
"pubkeys",
RPCArg::Type::ARR,
RPCArg::Default{
UniValue::VARR},
"Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
1312 {
"keypool",
RPCArg::Type::BOOL,
RPCArg::Default{
false},
"Stating whether imported public keys should be added to the keypool for when users request new addresses. Only allowed when wallet private keys are disabled"},
1324 RPCResult::Type::ARR,
"",
"Response is an array with the same size as the input that has the execution result",
1341 HelpExampleCli(
"importmulti",
"'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
1342 "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1343 HelpExampleCli(
"importmulti",
"'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
1350 RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
1354 const UniValue& requests = mainRequest.params[0];
1357 bool fRescan =
true;
1359 if (!mainRequest.params[1].isNull()) {
1360 const UniValue& options = mainRequest.params[1];
1362 if (options.
exists(
"rescan")) {
1363 fRescan = options[
"rescan"].
get_bool();
1368 if (fRescan && !reserver.
reserve()) {
1373 bool fRunScan =
false;
1374 int64_t nLowestTimestamp = 0;
1377 LOCK(pwallet->cs_wallet);
1381 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(),
FoundBlock().time(nLowestTimestamp).mtpTime(now)));
1386 const int64_t minimumTimestamp = 1;
1398 if (result[
"success"].get_bool()) {
1403 if (timestamp < nLowestTimestamp) {
1404 nLowestTimestamp = timestamp;
1408 if (fRescan && fRunScan && requests.
size()) {
1409 int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver,
true );
1411 LOCK(pwallet->cs_wallet);
1412 pwallet->ReacceptWalletTransactions();
1415 if (pwallet->IsAbortingRescan()) {
1418 if (scannedTime > nLowestTimestamp) {
1419 std::vector<UniValue> results = response.
getValues();
1428 if (scannedTime <=
GetImportTimestamp(request, now) || results.at(i).exists(
"error")) {
1437 strprintf(
"Rescan failed for key with creation timestamp %d. There was an error reading a "
1438 "block from time %d, which is after or within %d seconds of key creation, and "
1439 "could contain transactions pertaining to the key. As a result, transactions "
1440 "and coins using this key may not appear in the wallet. This error could be "
1441 "caused by pruning or data corruption (see bitcoind log for details) and could "
1442 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1443 "option and rescanblockchain RPC).",
1463 if (!data.exists(
"desc")) {
1467 const std::string& descriptor = data[
"desc"].get_str();
1468 const bool active = data.exists(
"active") ? data[
"active"].get_bool() :
false;
1469 const bool internal = data.exists(
"internal") ? data[
"internal"].get_bool() :
false;
1470 const std::string& label = data.exists(
"label") ? data[
"label"].get_str() :
"";
1475 auto parsed_desc =
Parse(descriptor, keys,
error,
true);
1481 int64_t range_start = 0, range_end = 1, next_index = 0;
1482 if (!parsed_desc->IsRange() && data.exists(
"range")) {
1484 }
else if (parsed_desc->IsRange()) {
1485 if (data.exists(
"range")) {
1487 range_start = range.first;
1488 range_end = range.second + 1;
1490 warnings.
push_back(
"Range not given, using default keypool range");
1494 next_index = range_start;
1496 if (data.exists(
"next_index")) {
1497 next_index = data[
"next_index"].get_int64();
1499 if (next_index < range_start || next_index >= range_end) {
1506 if (active && !parsed_desc->IsRange()) {
1511 if (data.exists(
"range") && data.exists(
"label")) {
1516 if (
internal && data.exists(
"label")) {
1521 if (active && !parsed_desc->IsSingleType()) {
1532 std::vector<CScript> scripts;
1533 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
1534 throw JSONRPCError(
RPC_WALLET_ERROR,
"Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
1536 parsed_desc->ExpandPrivate(0, keys, expand_keys);
1539 bool have_all_privkeys = !expand_keys.
keys.empty();
1540 for (
const auto& entry : expand_keys.
origins) {
1541 const CKeyID& key_id = entry.first;
1543 if (!expand_keys.
GetKey(key_id, key)) {
1544 have_all_privkeys =
false;
1551 if (keys.
keys.empty()) {
1554 if (!have_all_privkeys) {
1555 warnings.
push_back(
"Not all private keys provided. Some wallet functionality may return unexpected errors");
1559 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
1562 auto existing_spk_manager =
wallet.GetDescriptorScriptPubKeyMan(w_desc);
1563 if (existing_spk_manager) {
1564 if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc,
error)) {
1570 auto spk_manager =
wallet.AddWalletDescriptor(w_desc, keys, label,
internal);
1571 if (spk_manager ==
nullptr) {
1578 warnings.
push_back(
"Unknown output type, cannot set descriptor to active.");
1580 wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.
descriptor->GetOutputType(),
internal);
1584 wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.
descriptor->GetOutputType(),
internal);
1591 result.
pushKV(
"error", e);
1593 if (warnings.
size()) result.
pushKV(
"warnings", warnings);
1600 "\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
1601 "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
1602 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
1613 " Use the string \"now\" to substitute the current synced blockchain time.\n"
1614 " \"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
1615 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
1616 " of all descriptors being imported will be scanned.",
1617 "", {
"timestamp | \"now\"",
"integer / string"}
1627 RPCResult::Type::ARR,
"",
"Response is an array with the same size as the input that has the execution result",
1644 HelpExampleCli(
"importdescriptors",
"'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
1645 "{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
1646 HelpExampleCli(
"importdescriptors",
"'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
1658 RPCTypeCheck(main_request.params, {UniValue::VARR, UniValue::VOBJ});
1665 const UniValue& requests = main_request.params[0];
1666 const int64_t minimum_timestamp = 1;
1668 int64_t lowest_timestamp = 0;
1669 bool rescan =
false;
1672 LOCK(pwallet->cs_wallet);
1675 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(),
FoundBlock().time(lowest_timestamp).mtpTime(now)));
1680 const int64_t timestamp = std::max(
GetImportTimestamp(request, now), minimum_timestamp);
1684 if (lowest_timestamp > timestamp ) {
1685 lowest_timestamp = timestamp;
1689 if (!rescan && result[
"success"].get_bool()) {
1693 pwallet->ConnectScriptPubKeyManNotifiers();
1698 int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver,
true );
1700 LOCK(pwallet->cs_wallet);
1701 pwallet->ReacceptWalletTransactions();
1704 if (pwallet->IsAbortingRescan()) {
1708 if (scanned_time > lowest_timestamp) {
1709 std::vector<UniValue> results = response.
getValues();
1714 for (
unsigned int i = 0; i < requests.
size(); ++i) {
1721 if (scanned_time <=
GetImportTimestamp(request, now) || results.at(i).exists(
"error")) {
1730 strprintf(
"Rescan failed for descriptor with timestamp %d. There was an error reading a "
1731 "block from time %d, which is after or within %d seconds of key creation, and "
1732 "could contain transactions pertaining to the desc. As a result, transactions "
1733 "and coins using this desc may not appear in the wallet. This error could be "
1734 "caused by pruning or data corruption (see bitcoind log for details) and could "
1735 "be dealt with by downloading and rescanning the relevant blocks (see -reindex "
1736 "option and rescanblockchain RPC).",
1753 "\nList descriptors imported into a descriptor-enabled wallet.\n",
1765 {
RPCResult::Type::BOOL,
"internal",
true,
"Whether this is an internal or external descriptor; defined only for active descriptors"},
1770 {
RPCResult::Type::NUM,
"next",
true,
"The next index to generate addresses from; defined only for ranged descriptors"},
1787 const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
1795 const auto active_spk_mans =
wallet->GetActiveScriptPubKeyMans();
1796 for (
const auto& spk_man :
wallet->GetAllScriptPubKeyMans()) {
1798 if (!desc_spk_man) {
1802 LOCK(desc_spk_man->cs_desc_man);
1803 const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
1804 std::string descriptor;
1806 if (!desc_spk_man->GetDescriptorString(descriptor, priv)) {
1809 spk.
pushKV(
"desc", descriptor);
1810 spk.
pushKV(
"timestamp", wallet_descriptor.creation_time);
1811 spk.
pushKV(
"active", active_spk_mans.count(desc_spk_man) != 0);
1812 const auto internal =
wallet->IsInternalScriptPubKeyMan(desc_spk_man);
1813 if (
internal.has_value()) {
1814 spk.
pushKV(
"internal", *
internal);
1816 if (wallet_descriptor.descriptor->IsRange()) {
1818 range.
push_back(wallet_descriptor.range_start);
1819 range.
push_back(wallet_descriptor.range_end - 1);
1820 spk.
pushKV(
"range", range);
1821 spk.
pushKV(
"next", wallet_descriptor.next_index);
1828 response.
pushKV(
"descriptors", descriptors);