41 "Imports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
54 if (!
DecodeHexTx(tx, request.params[0].get_str())) {
63 std::vector<uint256> vMatch;
64 std::vector<unsigned int> vIndex;
69 LOCK(pwallet->cs_wallet);
71 if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.
header.
GetHash(),
FoundBlock().height(height))) {
75 std::vector<uint256>::const_iterator it;
76 if ((it = std::find(vMatch.begin(), vMatch.end(), tx.
GetHash())) == vMatch.end()) {
80 unsigned int txnIndex = vIndex[it - vMatch.begin()];
83 if (pwallet->IsMine(*tx_ref)) {
97 "Deletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
103 HelpExampleCli(
"removeprunedfunds",
"\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
104 "\nAs a JSON-RPC call\n"
105 +
HelpExampleRpc(
"removeprunedfunds",
"\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
112 LOCK(pwallet->cs_wallet);
115 std::vector<Txid> vHash;
116 vHash.push_back(hash);
117 if (
auto res = pwallet->RemoveTxs(vHash); !res) {
128 if (
data.exists(
"timestamp")) {
130 if (timestamp.
isNum()) {
131 return timestamp.
getInt<int64_t>();
132 }
else if (timestamp.
isStr() && timestamp.
get_str() ==
"now") {
146 if (!
data.exists(
"desc")) {
150 const std::string& descriptor =
data[
"desc"].get_str();
151 const bool active =
data.exists(
"active") ?
data[
"active"].get_bool() :
false;
157 auto parsed_descs =
Parse(descriptor, keys, error,
true);
158 if (parsed_descs.empty()) {
161 std::optional<bool> internal;
162 if (
data.exists(
"internal")) {
163 if (parsed_descs.size() > 1) {
166 internal =
data[
"internal"].get_bool();
170 int64_t range_start = 0, range_end = 1, next_index = 0;
171 if (!parsed_descs.at(0)->IsRange() &&
data.exists(
"range")) {
173 }
else if (parsed_descs.at(0)->IsRange()) {
174 if (
data.exists(
"range")) {
176 range_start = range.first;
177 range_end = range.second + 1;
179 warnings.
push_back(
"Range not given, using default keypool range");
181 range_end =
wallet.m_keypool_size;
183 next_index = range_start;
185 if (
data.exists(
"next_index")) {
186 next_index =
data[
"next_index"].getInt<int64_t>();
188 if (next_index < range_start || next_index >= range_end) {
195 if (active && !parsed_descs.at(0)->IsRange()) {
200 if (parsed_descs.size() > 1 &&
data.exists(
"label")) {
205 if (
data.exists(
"range") &&
data.exists(
"label")) {
210 if (internal &&
data.exists(
"label")) {
215 if (active && !parsed_descs.at(0)->IsSingleType()) {
224 for (
size_t j = 0; j < parsed_descs.size(); ++j) {
225 auto parsed_desc = std::move(parsed_descs[j]);
226 bool desc_internal = internal.has_value() && internal.value();
227 if (parsed_descs.size() == 2) {
228 desc_internal = j == 1;
229 }
else if (parsed_descs.size() > 2) {
234 std::vector<CScript> scripts;
235 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
236 throw JSONRPCError(
RPC_WALLET_ERROR,
"Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
238 parsed_desc->ExpandPrivate(0, keys, expand_keys);
241 bool have_all_privkeys = !expand_keys.
keys.empty();
242 for (
const auto& entry : expand_keys.
origins) {
243 const CKeyID& key_id = entry.first;
245 if (!expand_keys.
GetKey(key_id, key)) {
246 have_all_privkeys =
false;
253 if (keys.
keys.empty()) {
256 if (!have_all_privkeys) {
257 warnings.
push_back(
"Not all private keys provided. Some wallet functionality may return unexpected errors");
261 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
264 auto spk_manager_res =
wallet.AddWalletDescriptor(w_desc, keys, label, desc_internal);
266 if (!spk_manager_res) {
270 auto spk_manager = spk_manager_res.value();
272 if (spk_manager ==
nullptr) {
279 warnings.
push_back(
"Unknown output type, cannot set descriptor to active.");
281 wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.
descriptor->GetOutputType(), desc_internal);
285 wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), *w_desc.
descriptor->GetOutputType(), desc_internal);
293 result.
pushKV(
"error", e);
303 "Import 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"
304 "When importing descriptors with multipath key expressions, if the multipath specifier contains exactly two elements, the descriptor produced from the second element will be imported as an internal descriptor.\n"
305 "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
306 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
307 "The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",
318 "Use the string \"now\" to substitute the current synced blockchain time.\n"
319 "\"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n"
320 "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n"
321 "of all descriptors being imported will be scanned as well as the mempool.",
332 RPCResult::Type::ARR,
"",
"Response is an array with the same size as the input that has the execution result",
349 HelpExampleCli(
"importdescriptors",
"'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, "
350 "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
351 HelpExampleCli(
"importdescriptors",
"'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
361 wallet.BlockUntilSyncedToCurrentChain();
375 LOCK(pwallet->m_relock_mutex);
377 const UniValue& requests = main_request.params[0];
378 const int64_t minimum_timestamp = 1;
380 int64_t lowest_timestamp = 0;
384 LOCK(pwallet->cs_wallet);
387 CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(),
FoundBlock().time(lowest_timestamp).mtpTime(now)));
392 const int64_t timestamp = std::max(
GetImportTimestamp(request, now), minimum_timestamp);
396 if (lowest_timestamp > timestamp ) {
397 lowest_timestamp = timestamp;
401 if (!rescan && result[
"success"].get_bool()) {
405 pwallet->ConnectScriptPubKeyManNotifiers();
410 int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver,
true);
411 pwallet->ResubmitWalletTransactions(
false,
true);
413 if (pwallet->IsAbortingRescan()) {
417 if (scanned_time > lowest_timestamp) {
418 std::vector<UniValue> results = response.
getValues();
423 for (
unsigned int i = 0; i < requests.
size(); ++i) {
430 if (scanned_time <=
GetImportTimestamp(request, now) || results.at(i).exists(
"error")) {
433 std::string error_msg{
strprintf(
"Rescan failed for descriptor with timestamp %d. There "
434 "was an error reading a block from time %d, which is after or within %d seconds "
435 "of key creation, and could contain transactions pertaining to the desc. As a "
436 "result, transactions and coins using this desc may not appear in the wallet.",
438 if (pwallet->chain().havePruned()) {
439 error_msg +=
strprintf(
" This error could be caused by pruning or data corruption "
440 "(see bitcoind log for details) and could be dealt with by downloading and "
441 "rescanning the relevant blocks (see -reindex option and rescanblockchain RPC).");
442 }
else if (pwallet->chain().hasAssumedValidChain()) {
443 error_msg +=
strprintf(
" This error is likely caused by an in-progress assumeutxo "
444 "background sync. Check logs or getchainstates RPC for assumeutxo background "
445 "sync progress and try again later.");
447 error_msg +=
strprintf(
" This error could potentially caused by data corruption. If "
448 "the issue persists you may want to reindex (see -reindex option).");
469 "List descriptors imported into a descriptor-enabled wallet.\n",
475 {
RPCResult::Type::ARR,
"descriptors",
"Array of descriptor objects (sorted by descriptor string representation)",
480 {
RPCResult::Type::BOOL,
"active",
"Whether this descriptor is currently used to generate new addresses"},
481 {
RPCResult::Type::BOOL,
"internal",
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"},
486 {
RPCResult::Type::NUM,
"next",
true,
"Same as next_index field. Kept for compatibility reason."},
487 {
RPCResult::Type::NUM,
"next_index",
true,
"The next index to generate addresses from; defined only for ranged descriptors"},
504 const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
511 const auto active_spk_mans =
wallet->GetActiveScriptPubKeyMans();
513 struct WalletDescInfo {
514 std::string descriptor;
515 uint64_t creation_time;
517 std::optional<bool> internal;
518 std::optional<std::pair<int64_t,int64_t>> range;
522 std::vector<WalletDescInfo> wallet_descriptors;
523 for (
const auto& spk_man :
wallet->GetAllScriptPubKeyMans()) {
528 LOCK(desc_spk_man->cs_desc_man);
529 const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
530 std::string descriptor;
531 if (!desc_spk_man->GetDescriptorString(descriptor, priv)) {
534 const bool is_range = wallet_descriptor.descriptor->IsRange();
535 wallet_descriptors.push_back({
537 wallet_descriptor.creation_time,
538 active_spk_mans.count(desc_spk_man) != 0,
539 wallet->IsInternalScriptPubKeyMan(desc_spk_man),
540 is_range ? std::optional(std::make_pair(wallet_descriptor.range_start, wallet_descriptor.range_end)) : std::nullopt,
541 wallet_descriptor.next_index
545 std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](
const auto& a,
const auto& b) {
546 return a.descriptor < b.descriptor;
550 for (
const WalletDescInfo& info : wallet_descriptors) {
552 spk.
pushKV(
"desc", info.descriptor);
553 spk.
pushKV(
"timestamp", info.creation_time);
554 spk.
pushKV(
"active", info.active);
555 if (info.internal.has_value()) {
556 spk.
pushKV(
"internal", info.internal.value());
558 if (info.range.has_value()) {
562 spk.
pushKV(
"range", std::move(range));
563 spk.
pushKV(
"next", info.next_index);
564 spk.
pushKV(
"next_index", info.next_index);
571 response.
pushKV(
"descriptors", std::move(descriptors));
582 "Safely copies the current wallet file to the specified destination, which can either be a directory or a path with a filename.\n",
598 pwallet->BlockUntilSyncedToCurrentChain();
600 LOCK(pwallet->cs_wallet);
602 std::string strDest = request.params[0].get_str();
603 if (!pwallet->BackupWallet(strDest)) {
617 "Restores and loads a wallet from backup.\n"
618 "\nThe rescan is significantly faster if a descriptor wallet is restored"
619 "\nand block filters are available (using startup option \"-blockfilterindex=1\").\n",
629 {
RPCResult::Type::ARR,
"warnings",
true,
"Warning messages, if any, related to restoring and loading the wallet.",
636 HelpExampleCli(
"restorewallet",
"\"testwallet\" \"home\\backups\\backup-file.bak\"")
637 +
HelpExampleRpc(
"restorewallet",
"\"testwallet\" \"home\\backups\\backup-file.bak\"")
638 +
HelpExampleCliNamed(
"restorewallet", {{
"wallet_name",
"testwallet"}, {
"backup_file",
"home\\backups\\backup-file.bak\""}, {
"load_on_startup",
true}})
639 +
HelpExampleRpcNamed(
"restorewallet", {{
"wallet_name",
"testwallet"}, {
"backup_file",
"home\\backups\\backup-file.bak\""}, {
"load_on_startup",
true}})
646 auto backup_file =
fs::u8path(request.params[1].get_str());
648 std::string wallet_name = request.params[0].get_str();
650 std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool());
654 std::vector<bilingual_str> warnings;
656 const std::shared_ptr<CWallet>
wallet =
RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings);
static constexpr int64_t TIMESTAMP_WINDOW
Timestamp window used as a grace period by code that compares external timestamps (such as timestamps...
#define CHECK_NONFATAL(condition)
Identity function.
An encapsulated private key.
A reference to a CKey: the Hash160 of its serialized public key.
Used to relay blocks as header + vector<merkle branch> to filtered nodes.
CBlockHeader header
Public only for unit testing.
uint256 ExtractMatches(std::vector< uint256 > &vMatch, std::vector< unsigned int > &vnIndex)
extract the matching txid's represented by this partial merkle tree and their respective indices with...
Double ended buffer combining vector and stream-like interfaces.
void push_back(UniValue val)
const std::string & get_str() const
const std::vector< UniValue > & getValues() const
void pushKV(std::string key, UniValue val)
Helper for findBlock to selectively return pieces of block data.
static transaction_identifier FromUint256(const uint256 &id)
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Descriptor with some wallet metadata.
std::shared_ptr< Descriptor > descriptor
RAII object to check and reserve a wallet rescan.
bool reserve(bool with_passphrase=false)
static UniValue Parse(std::string_view raw)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
bool DecodeHexTx(CMutableTransaction &tx, const std::string &hex_tx, bool try_no_witness=false, bool try_witness=true)
static path u8path(const std::string &utf8_str)
bilingual_str ErrorString(const Result< T > &result)
static UniValue ProcessDescriptorImport(CWallet &wallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
RPCHelpMan removeprunedfunds()
void HandleWalletError(const std::shared_ptr< CWallet > wallet, DatabaseStatus &status, bilingual_str &error)
void EnsureWalletIsUnlocked(const CWallet &wallet)
RPCHelpMan backupwallet()
RPCHelpMan importprunedfunds()
std::shared_ptr< CWallet > RestoreWallet(WalletContext &context, const fs::path &backup_file, const std::string &wallet_name, std::optional< bool > load_on_start, DatabaseStatus &status, bilingual_str &error, std::vector< bilingual_str > &warnings, bool load_after_restore)
WalletContext & EnsureWalletContext(const std::any &context)
RPCHelpMan listdescriptors()
std::string LabelFromValue(const UniValue &value)
RPCHelpMan restorewallet()
RPCHelpMan importdescriptors()
static int64_t GetImportTimestamp(const UniValue &data, int64_t now)
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
UniValue JSONRPCError(int code, const std::string &message)
@ RPC_MISC_ERROR
General application defined errors.
@ RPC_TYPE_ERROR
Unexpected type was passed as parameter.
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
@ RPC_WALLET_ERROR
Wallet errors.
@ RPC_DESERIALIZATION_ERROR
Error parsing or validating structure in raw format.
@ RPC_INVALID_ADDRESS_OR_KEY
Invalid address or key.
std::pair< int64_t, int64_t > ParseDescriptorRange(const UniValue &value)
Parse a JSON range specified as int64, or [int64, int64].
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
std::string HelpExampleRpcNamed(const std::string &methodname, const RPCArgList &args)
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string_view name)
void PushWarnings(const UniValue &warnings, UniValue &obj)
Push warning messages to an RPC "warnings" field as a JSON array of strings.
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
const std::string UNIX_EPOCH_TIME
String used to describe UNIX epoch time in documentation, factored out to a constant for consistency.
std::string HelpExampleCliNamed(const std::string &methodname, const RPCArgList &args)
uint256 ParseHashV(const UniValue &v, std::string_view name)
Utilities: convert hex-encoded Values (throws error if not hex).
A mutable version of CTransaction.
Txid GetHash() const
Compute the hash of this CMutableTransaction.
std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > origins
bool GetKey(const CKeyID &keyid, CKey &key) const override
std::map< CKeyID, CKey > keys
@ RANGE
Special type that is a NUM or [NUM,NUM].
@ STR_HEX
Special type that is a STR with only hex chars.
@ OMITTED
Optional argument for which the default value is omitted from help text for one of two reasons:
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings....
std::string oneline_description
Should be empty unless it is supposed to override the auto-generated summary line.
@ ELISION
Special type to denote elision (...)
@ ARR_FIXED
Special array that has a fixed number of entries.
State of transaction confirmed in a block.
WalletContext struct containing references to state shared between CWallet instances,...
#define EXCLUSIVE_LOCKS_REQUIRED(...)
const char * uvTypeName(UniValue::VType t)