aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/rpc/backup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/rpc/backup.cpp')
-rw-r--r--src/wallet/rpc/backup.cpp143
1 files changed, 87 insertions, 56 deletions
diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp
index 228564fae4..4c623fa1ba 100644
--- a/src/wallet/rpc/backup.cpp
+++ b/src/wallet/rpc/backup.cpp
@@ -26,8 +26,6 @@
#include <tuple>
#include <string>
-#include <boost/algorithm/string.hpp>
-
#include <univalue.h>
@@ -102,11 +100,13 @@ RPCHelpMan importprivkey()
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"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"
+ "The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
{"label", RPCArg::Type::STR, RPCArg::DefaultHint{"current label if address exists, otherwise \"\""}, "An optional label"},
- {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Scan the chain and mempool for wallet transactions."},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -124,7 +124,7 @@ RPCHelpMan importprivkey()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
@@ -140,7 +140,7 @@ RPCHelpMan importprivkey()
EnsureWalletIsUnlocked(*pwallet);
std::string strSecret = request.params[0].get_str();
- std::string strLabel = "";
+ std::string strLabel;
if (!request.params[1].isNull())
strLabel = request.params[1].get_str();
@@ -192,7 +192,7 @@ RPCHelpMan importprivkey()
RescanWallet(*pwallet, reserver);
}
- return NullUniValue;
+ return UniValue::VNULL;
},
};
}
@@ -200,18 +200,21 @@ RPCHelpMan importprivkey()
RPCHelpMan importaddress()
{
return RPCHelpMan{"importaddress",
- "\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"
+ "\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"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"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"
+ "The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"If you have the full public key, you should call importpubkey instead of this.\n"
"Hint: use importmulti to import more than one address.\n"
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
"as change, and not show up in many RPCs.\n"
- "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n"
+ "Note: This command is only compatible with legacy wallets. Use \"importdescriptors\" with \"addr(X)\" for descriptor wallets.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
- {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Scan the chain and mempool for wallet transactions."},
{"p2sh", RPCArg::Type::BOOL, RPCArg::Default{false}, "Add the P2SH version of the script as well"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -226,7 +229,7 @@ RPCHelpMan importaddress()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -296,7 +299,7 @@ RPCHelpMan importaddress()
}
}
- return NullUniValue;
+ return UniValue::VNULL;
},
};
}
@@ -314,7 +317,7 @@ RPCHelpMan importprunedfunds()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) {
@@ -349,7 +352,7 @@ RPCHelpMan importprunedfunds()
CTransactionRef tx_ref = MakeTransactionRef(tx);
if (pwallet->IsMine(*tx_ref)) {
pwallet->AddToWallet(std::move(tx_ref), TxStateConfirmed{merkleBlock.header.GetHash(), height, static_cast<int>(txnIndex)});
- return NullUniValue;
+ return UniValue::VNULL;
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
@@ -373,7 +376,7 @@ RPCHelpMan removeprunedfunds()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
LOCK(pwallet->cs_wallet);
@@ -390,7 +393,7 @@ RPCHelpMan removeprunedfunds()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction does not exist in wallet.");
}
- return NullUniValue;
+ return UniValue::VNULL;
},
};
}
@@ -402,11 +405,13 @@ RPCHelpMan importpubkey()
"Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"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"
+ "The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "An optional label"},
- {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Rescan the wallet for transactions"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Scan the chain and mempool for wallet transactions."},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -420,7 +425,7 @@ RPCHelpMan importpubkey()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -475,7 +480,7 @@ RPCHelpMan importpubkey()
}
}
- return NullUniValue;
+ return UniValue::VNULL;
},
};
}
@@ -485,7 +490,7 @@ RPCHelpMan importwallet()
{
return RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
- "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
+ "Note: Blockchain and Mempool will be rescanned after a successful import. Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
},
@@ -501,7 +506,7 @@ RPCHelpMan importwallet()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
EnsureLegacyScriptPubKeyMan(*pwallet, true);
@@ -546,8 +551,7 @@ RPCHelpMan importwallet()
if (line.empty() || line[0] == '#')
continue;
- std::vector<std::string> vstr;
- boost::split(vstr, line, boost::is_any_of(" "));
+ std::vector<std::string> vstr = SplitString(line, ' ');
if (vstr.size() < 2)
continue;
CKey key = DecodeSecret(vstr[0]);
@@ -633,7 +637,7 @@ RPCHelpMan importwallet()
if (!fGood)
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
- return NullUniValue;
+ return UniValue::VNULL;
},
};
}
@@ -657,7 +661,7 @@ RPCHelpMan dumpprivkey()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
const LegacyScriptPubKeyMan& spk_man = EnsureConstLegacyScriptPubKeyMan(*pwallet);
@@ -707,7 +711,7 @@ RPCHelpMan dumpwallet()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
const CWallet& wallet = *pwallet;
const LegacyScriptPubKeyMan& spk_man = EnsureConstLegacyScriptPubKeyMan(wallet);
@@ -915,7 +919,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
case TxoutType::WITNESS_V1_TAPROOT:
return "unrecognized script";
} // no default case, so the compiler can warn about missing cases
- CHECK_NONFATAL(false);
+ NONFATAL_UNREACHABLE();
}
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)
@@ -1235,7 +1239,7 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
if (data.exists("timestamp")) {
const UniValue& timestamp = data["timestamp"];
if (timestamp.isNum()) {
- return timestamp.get_int64();
+ return timestamp.getInt<int64_t>();
} else if (timestamp.isStr() && timestamp.get_str() == "now") {
return now;
}
@@ -1252,6 +1256,8 @@ RPCHelpMan importmulti()
"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"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
+ "The rescan parameter can be set to false if the key was never used to create transactions. If it is set to false,\n"
+ "but the key was used to create transactions, rescanblockchain needs to be called with the appropriate block range.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
@@ -1260,7 +1266,7 @@ RPCHelpMan importmulti()
{
{"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
{"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
- /* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
+ /*oneline_description=*/"", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
},
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key expressed in " + UNIX_EPOCH_TIME + ",\n"
" or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
@@ -1268,7 +1274,7 @@ RPCHelpMan importmulti()
" \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
" 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
" creation time of all keys being imported by the importmulti call will be scanned.",
- /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
+ /*oneline_description=*/"", {"timestamp | \"now\"", "integer / string"}
},
{"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
{"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
@@ -1293,7 +1299,7 @@ RPCHelpMan importmulti()
"\"requests\""},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Stating if should rescan the blockchain after all imports"},
+ {"rescan", RPCArg::Type::BOOL, RPCArg::Default{true}, "Scan the chain and mempool for wallet transactions after all imports."},
},
"\"options\""},
},
@@ -1322,7 +1328,7 @@ RPCHelpMan importmulti()
[&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
CWallet& wallet{*pwallet};
// Make sure the results are valid at least up to the most recent block
@@ -1476,7 +1482,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
next_index = range_start;
if (data.exists("next_index")) {
- next_index = data["next_index"].get_int64();
+ next_index = data["next_index"].getInt<int64_t>();
// bound checks
if (next_index < range_start || next_index >= range_end) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "next_index is out of range");
@@ -1595,8 +1601,8 @@ RPCHelpMan importdescriptors()
" Use the string \"now\" to substitute the current synced blockchain time.\n"
" \"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
" 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
- " of all descriptors being imported will be scanned.",
- /* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
+ "of all descriptors being imported will be scanned as well as the mempool.",
+ /*oneline_description=*/"", {"timestamp | \"now\"", "integer / string"}
},
{"internal", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether matching outputs should be treated as not incoming payments (e.g. change)"},
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "Label to assign to the address, only allowed with internal=false. Disabled for ranged descriptors"},
@@ -1624,13 +1630,13 @@ RPCHelpMan importdescriptors()
},
RPCExamples{
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
- "{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
+ "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
CWallet& wallet{*pwallet};
// Make sure the results are valid at least up to the most recent block
@@ -1748,13 +1754,13 @@ RPCHelpMan listdescriptors()
{RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR, "desc", "Descriptor string representation"},
{RPCResult::Type::NUM, "timestamp", "The creation time of the descriptor"},
- {RPCResult::Type::BOOL, "active", "Activeness flag"},
- {RPCResult::Type::BOOL, "internal", true, "Whether this is an internal or external descriptor; defined only for active descriptors"},
- {RPCResult::Type::ARR_FIXED, "range", true, "Defined only for ranged descriptors", {
+ {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
+ {RPCResult::Type::BOOL, "internal", /*optional=*/true, "True if this descriptor is used to generate change addresses. False if this descriptor is used to generate receiving addresses; defined only for active descriptors"},
+ {RPCResult::Type::ARR_FIXED, "range", /*optional=*/true, "Defined only for ranged descriptors", {
{RPCResult::Type::NUM, "", "Range start inclusive"},
{RPCResult::Type::NUM, "", "Range end inclusive"},
}},
- {RPCResult::Type::NUM, "next", true, "The next index to generate addresses from; defined only for ranged descriptors"},
+ {RPCResult::Type::NUM, "next", /*optional=*/true, "The next index to generate addresses from; defined only for ranged descriptors"},
}},
}}
}},
@@ -1765,7 +1771,7 @@ RPCHelpMan listdescriptors()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
- if (!wallet) return NullUniValue;
+ if (!wallet) return UniValue::VNULL;
if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
@@ -1778,34 +1784,59 @@ RPCHelpMan listdescriptors()
LOCK(wallet->cs_wallet);
- UniValue descriptors(UniValue::VARR);
const auto active_spk_mans = wallet->GetActiveScriptPubKeyMans();
+
+ struct WalletDescInfo {
+ std::string descriptor;
+ uint64_t creation_time;
+ bool active;
+ std::optional<bool> internal;
+ std::optional<std::pair<int64_t,int64_t>> range;
+ int64_t next_index;
+ };
+
+ std::vector<WalletDescInfo> wallet_descriptors;
for (const auto& spk_man : wallet->GetAllScriptPubKeyMans()) {
const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
if (!desc_spk_man) {
throw JSONRPCError(RPC_WALLET_ERROR, "Unexpected ScriptPubKey manager type.");
}
- UniValue spk(UniValue::VOBJ);
LOCK(desc_spk_man->cs_desc_man);
const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
std::string descriptor;
-
if (!desc_spk_man->GetDescriptorString(descriptor, priv)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Can't get descriptor string.");
}
- spk.pushKV("desc", descriptor);
- spk.pushKV("timestamp", wallet_descriptor.creation_time);
- spk.pushKV("active", active_spk_mans.count(desc_spk_man) != 0);
- const auto internal = wallet->IsInternalScriptPubKeyMan(desc_spk_man);
- if (internal.has_value()) {
- spk.pushKV("internal", *internal);
+ const bool is_range = wallet_descriptor.descriptor->IsRange();
+ wallet_descriptors.push_back({
+ descriptor,
+ wallet_descriptor.creation_time,
+ active_spk_mans.count(desc_spk_man) != 0,
+ wallet->IsInternalScriptPubKeyMan(desc_spk_man),
+ is_range ? std::optional(std::make_pair(wallet_descriptor.range_start, wallet_descriptor.range_end)) : std::nullopt,
+ wallet_descriptor.next_index
+ });
+ }
+
+ std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](const auto& a, const auto& b) {
+ return a.descriptor < b.descriptor;
+ });
+
+ UniValue descriptors(UniValue::VARR);
+ for (const WalletDescInfo& info : wallet_descriptors) {
+ UniValue spk(UniValue::VOBJ);
+ spk.pushKV("desc", info.descriptor);
+ spk.pushKV("timestamp", info.creation_time);
+ spk.pushKV("active", info.active);
+ if (info.internal.has_value()) {
+ spk.pushKV("internal", info.internal.value());
}
- if (wallet_descriptor.descriptor->IsRange()) {
+ if (info.range.has_value()) {
UniValue range(UniValue::VARR);
- range.push_back(wallet_descriptor.range_start);
- range.push_back(wallet_descriptor.range_end - 1);
+ range.push_back(info.range->first);
+ range.push_back(info.range->second - 1);
spk.pushKV("range", range);
- spk.pushKV("next", wallet_descriptor.next_index);
+ spk.pushKV("next", info.next_index);
}
descriptors.push_back(spk);
}
@@ -1834,7 +1865,7 @@ RPCHelpMan backupwallet()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
- if (!pwallet) return NullUniValue;
+ if (!pwallet) return UniValue::VNULL;
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -1847,7 +1878,7 @@ RPCHelpMan backupwallet()
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
}
- return NullUniValue;
+ return UniValue::VNULL;
},
};
}