22 #include <util/time.h> 43 "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",
56 if (!
DecodeHexTx(tx, request.params[0].get_str())) {
64 std::vector<Txid> vMatch;
65 std::vector<unsigned int> vIndex;
70 LOCK(pwallet->cs_wallet);
76 std::vector<Txid>::const_iterator it;
77 if ((it = std::find(vMatch.begin(), vMatch.end(), tx.
GetHash())) == vMatch.end()) {
81 unsigned int txnIndex = vIndex[it - vMatch.begin()];
84 if (pwallet->IsMine(*tx_ref)) {
98 "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",
104 HelpExampleCli(
"removeprunedfunds",
"\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"") +
105 "\nAs a JSON-RPC call\n" 106 +
HelpExampleRpc(
"removeprunedfunds",
"\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
113 LOCK(pwallet->cs_wallet);
116 std::vector<Txid> vHash;
117 vHash.push_back(hash);
118 if (
auto res = pwallet->RemoveTxs(vHash); !res) {
129 if (
data.exists(
"timestamp")) {
131 if (timestamp.
isNum()) {
132 return timestamp.
getInt<int64_t>();
133 }
else if (timestamp.
isStr() && timestamp.
get_str() ==
"now") {
147 if (!
data.exists(
"desc")) {
151 const std::string& descriptor =
data[
"desc"].get_str();
152 const bool active =
data.exists(
"active") ?
data[
"active"].get_bool() :
false;
158 auto parsed_descs =
Parse(descriptor, keys, error,
true);
159 if (parsed_descs.empty()) {
162 std::optional<bool>
internal;
163 if (
data.exists(
"internal")) {
164 if (parsed_descs.size() > 1) {
167 internal =
data[
"internal"].get_bool();
171 std::optional<bool> is_ranged;
172 int64_t range_start = 0, range_end = 1, next_index = 0;
173 if (!parsed_descs.at(0)->IsRange() &&
data.exists(
"range")) {
175 }
else if (parsed_descs.at(0)->IsRange()) {
176 if (
data.exists(
"range")) {
178 range_start = range.first;
179 range_end = range.second + 1;
181 warnings.
push_back(
"Range not given, using default keypool range");
183 range_end =
wallet.m_keypool_size;
185 next_index = range_start;
188 if (
data.exists(
"next_index")) {
189 next_index =
data[
"next_index"].getInt<int64_t>();
191 if (next_index < range_start || next_index >= range_end) {
198 if (active && !parsed_descs.at(0)->IsRange()) {
203 if (parsed_descs.size() > 1 &&
data.exists(
"label")) {
208 if (is_ranged.has_value() && is_ranged.value() &&
data.exists(
"label")) {
212 bool desc_internal =
internal.has_value() &&
internal.value();
214 if (desc_internal &&
data.exists(
"label")) {
219 if (active && !parsed_descs.at(0)->IsSingleType()) {
228 for (
size_t j = 0; j < parsed_descs.size(); ++j) {
229 auto parsed_desc = std::move(parsed_descs[j]);
230 if (parsed_descs.size() == 2) {
231 desc_internal = j == 1;
232 }
else if (parsed_descs.size() > 2) {
237 std::vector<CScript> scripts;
238 if (!parsed_desc->Expand(0, keys, scripts, expand_keys)) {
239 throw JSONRPCError(
RPC_WALLET_ERROR,
"Cannot expand descriptor. Probably because of hardened derivations without private keys provided");
241 parsed_desc->ExpandPrivate(0, keys, expand_keys);
243 for (
const auto& w : parsed_desc->Warnings()) {
248 bool have_all_privkeys = !expand_keys.
keys.empty();
249 for (
const auto& entry : expand_keys.
origins) {
250 const CKeyID& key_id = entry.first;
252 if (!expand_keys.
GetKey(key_id, key)) {
253 have_all_privkeys =
false;
260 if (keys.
keys.empty()) {
263 if (!have_all_privkeys) {
264 warnings.
push_back(
"Not all private keys provided. Some wallet functionality may return unexpected errors");
268 WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
271 auto spk_manager_res =
wallet.AddWalletDescriptor(w_desc, keys, label, desc_internal);
273 if (!spk_manager_res) {
277 auto& spk_manager = spk_manager_res.value().get();
282 warnings.
push_back(
"Unknown output type, cannot set descriptor to active.");
284 wallet.AddActiveScriptPubKeyMan(spk_manager.GetID(), *w_desc.
descriptor->GetOutputType(), desc_internal);
288 wallet.DeactivateScriptPubKeyMan(spk_manager.GetID(), *w_desc.
descriptor->GetOutputType(), desc_internal);
296 result.pushKV(
"error", e);
306 "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" 307 "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" 308 "\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n" 309 "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n" 310 "The rescan is significantly faster if block filters are available (using startup option \"-blockfilterindex=1\").\n",
321 "Use the string \"now\" to substitute the current synced blockchain time.\n" 322 "\"now\" can be specified to bypass scanning, for outputs which are known to never have been used, and\n" 323 "0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest timestamp\n" 324 "of all descriptors being imported will be scanned as well as the mempool.",
335 RPCResult::Type::ARR,
"",
"Response is an array with the same size as the input that has the execution result",
352 HelpExampleCli(
"importdescriptors",
"'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"internal\": true }, " 353 "{ \"desc\": \"<my descriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
354 HelpExampleCli(
"importdescriptors",
"'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
364 wallet.BlockUntilSyncedToCurrentChain();
373 LOCK(pwallet->m_relock_mutex);
375 const UniValue& requests = main_request.params[0];
376 const int64_t minimum_timestamp = 1;
378 int64_t lowest_timestamp = 0;
382 LOCK(pwallet->cs_wallet);
390 const int64_t timestamp = std::max(
GetImportTimestamp(request, now), minimum_timestamp);
394 if (lowest_timestamp > timestamp ) {
395 lowest_timestamp = timestamp;
399 if (!rescan &&
result[
"success"].get_bool()) {
403 pwallet->ConnectScriptPubKeyManNotifiers();
404 pwallet->RefreshAllTXOs();
409 int64_t scanned_time = pwallet->RescanFromTime(lowest_timestamp, reserver,
true);
412 if (pwallet->IsAbortingRescan()) {
416 if (scanned_time > lowest_timestamp) {
417 std::vector<UniValue> results = response.
getValues();
422 for (
unsigned int i = 0; i < requests.
size(); ++i) {
429 if (scanned_time <=
GetImportTimestamp(request, now) || results.at(i).exists(
"error")) {
432 std::string error_msg{
strprintf(
"Rescan failed for descriptor with timestamp %d. There " 433 "was an error reading a block from time %d, which is after or within %d seconds " 434 "of key creation, and could contain transactions pertaining to the desc. As a " 435 "result, transactions and coins using this desc may not appear in the wallet.",
437 if (pwallet->chain().havePruned()) {
438 error_msg +=
strprintf(
" This error could be caused by pruning or data corruption " 439 "(see bitcoind log for details) and could be dealt with by downloading and " 440 "rescanning the relevant blocks (see -reindex option and rescanblockchain RPC).");
441 }
else if (pwallet->chain().hasAssumedValidChain()) {
442 error_msg +=
strprintf(
" This error is likely caused by an in-progress assumeutxo " 443 "background sync. Check logs or getchainstates RPC for assumeutxo background " 444 "sync progress and try again later.");
446 error_msg +=
strprintf(
" This error could potentially caused by data corruption. If " 447 "the issue persists you may want to reindex (see -reindex option).");
468 "List all descriptors present in a wallet.\n",
474 {
RPCResult::Type::ARR,
"descriptors",
"Array of descriptor objects (sorted by descriptor string representation)",
479 {
RPCResult::Type::BOOL,
"active",
"Whether this descriptor is currently used to generate new addresses"},
480 {
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"},
485 {
RPCResult::Type::NUM,
"next",
true,
"Same as next_index field. Kept for compatibility reason."},
486 {
RPCResult::Type::NUM,
"next_index",
true,
"The next index to generate addresses from; defined only for ranged descriptors"},
499 const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
509 const auto active_spk_mans =
wallet->GetActiveScriptPubKeyMans();
511 struct WalletDescInfo {
512 std::string descriptor;
513 uint64_t creation_time;
515 std::optional<bool>
internal;
516 std::optional<std::pair<int64_t,int64_t>> range;
520 std::vector<WalletDescInfo> wallet_descriptors;
521 for (
const auto& spk_man :
wallet->GetAllScriptPubKeyMans()) {
526 LOCK(desc_spk_man->cs_desc_man);
527 const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
528 std::string descriptor;
529 CHECK_NONFATAL(desc_spk_man->GetDescriptorString(descriptor, priv));
530 const bool is_range = wallet_descriptor.descriptor->IsRange();
531 wallet_descriptors.push_back({
533 wallet_descriptor.creation_time,
534 active_spk_mans.contains(desc_spk_man),
535 wallet->IsInternalScriptPubKeyMan(desc_spk_man),
536 is_range ? std::optional(std::make_pair(wallet_descriptor.range_start, wallet_descriptor.range_end)) : std::nullopt,
537 wallet_descriptor.next_index
541 std::sort(wallet_descriptors.begin(), wallet_descriptors.end(), [](
const auto& a,
const auto& b) {
542 return a.descriptor < b.descriptor;
546 for (
const WalletDescInfo& info : wallet_descriptors) {
548 spk.
pushKV(
"desc", info.descriptor);
549 spk.
pushKV(
"timestamp", info.creation_time);
550 spk.
pushKV(
"active", info.active);
551 if (info.internal.has_value()) {
552 spk.
pushKV(
"internal", info.internal.value());
554 if (info.range.has_value()) {
558 spk.
pushKV(
"range", std::move(range));
559 spk.
pushKV(
"next", info.next_index);
560 spk.
pushKV(
"next_index", info.next_index);
562 descriptors.push_back(std::move(spk));
567 response.
pushKV(
"descriptors", std::move(descriptors));
578 "Safely copies the current wallet file to the specified destination, which can either be a directory or a path with a filename.\n",
594 pwallet->BlockUntilSyncedToCurrentChain();
596 LOCK(pwallet->cs_wallet);
598 std::string strDest = request.params[0].get_str();
599 if (!pwallet->BackupWallet(strDest)) {
613 "Restores and loads a wallet from backup.\n" 614 "\nThe rescan is significantly faster if block filters are available" 615 "\n(using startup option \"-blockfilterindex=1\").\n",
625 {
RPCResult::Type::ARR,
"warnings",
true,
"Warning messages, if any, related to restoring and loading the wallet.",
632 HelpExampleCli(
"restorewallet",
"\"testwallet\" \"home\\backups\\backup-file.bak\"")
633 +
HelpExampleRpc(
"restorewallet",
"\"testwallet\" \"home\\backups\\backup-file.bak\"")
634 +
HelpExampleCliNamed(
"restorewallet", {{
"wallet_name",
"testwallet"}, {
"backup_file",
"home\\backups\\backup-file.bak\""}, {
"load_on_startup",
true}})
635 +
HelpExampleRpcNamed(
"restorewallet", {{
"wallet_name",
"testwallet"}, {
"backup_file",
"home\\backups\\backup-file.bak\""}, {
"load_on_startup",
true}})
642 auto backup_file =
fs::u8path(request.params[1].get_str());
644 std::string wallet_name = request.params[0].get_str();
646 std::optional<bool> load_on_start = request.params[2].isNull() ? std::nullopt : std::optional<bool>(request.params[2].get_bool());
650 std::vector<bilingual_str> warnings;
652 const std::shared_ptr<CWallet>
wallet =
RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings);
std::shared_ptr< const CTransaction > CTransactionRef
static UniValue Parse(std::string_view raw, ParamFormat format=ParamFormat::JSON)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Helper for findBlock to selectively return pieces of block data.
void push_back(UniValue val)
CBlockHeader header
Public only for unit testing.
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_opts.type_str.at(0) will override the type of the value in a key-value pair, m_opts.type_str.at(1) will override the type in the argument description.
const std::vector< UniValue > & getValues() const
std::string HelpExampleRpcNamed(const std::string &methodname, const RPCArgList &args)
RPCHelpMan restorewallet()
std::map< CKeyID, CKey > keys
#define CHECK_NONFATAL(condition)
Identity function.
std::map< CKeyID, std::pair< CPubKey, KeyOriginInfo > > origins
const std::string & get_str() const
RAII object to check and reserve a wallet rescan.
bool reserve(bool with_passphrase=false)
static int64_t GetImportTimestamp(const UniValue &data, int64_t now)
State of transaction confirmed in a block.
static constexpr int64_t TIMESTAMP_WINDOW
Timestamp window used as a grace period by code that compares external timestamps (such as timestamps...
Invalid, missing or duplicate parameter.
static UniValue ProcessDescriptorImport(CWallet &wallet, const UniValue &data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
Minimal stream for reading from an existing byte array by std::span.
std::pair< int64_t, int64_t > ParseDescriptorRange(const UniValue &value)
Parse a JSON range specified as int64, or [int64, int64].
std::string oneline_description
Should be empty unless it is supposed to override the auto-generated summary line.
Special type that is a STR with only hex chars.
void HandleWalletError(const std::shared_ptr< CWallet > &wallet, DatabaseStatus &status, bilingual_str &error)
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
const char * uvTypeName(UniValue::VType t)
Used to relay blocks as header + vector<merkle branch> to filtered nodes.
UniValue JSONRPCError(int code, const std::string &message)
std::string LabelFromValue(const UniValue &value)
Special array that has a fixed number of entries.
std::shared_ptr< Descriptor > descriptor
WalletContext & EnsureWalletContext(const std::any &context)
Unexpected type was passed as parameter.
RPCHelpMan listdescriptors()
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
General application defined errors.
std::string HelpExampleCliNamed(const std::string &methodname, const RPCArgList &args)
void PushWarnings(const UniValue &warnings, UniValue &obj)
Push warning messages to an RPC "warnings" field as a JSON array of strings.
Txid GetHash() const
Compute the hash of this CMutableTransaction.
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
RPCHelpMan importdescriptors()
Descriptor with some wallet metadata.
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Special type that is a NUM or [NUM,NUM].
Optional argument for which the default value is omitted from help text for one of two reasons: ...
#define EXCLUSIVE_LOCKS_REQUIRED(...)
bool GetKey(const CKeyID &keyid, CKey &key) const override
RPCHelpMan importprunedfunds()
void pushKV(std::string key, UniValue val)
static transaction_identifier FromUint256(const uint256 &id)
uint256 ParseHashV(const UniValue &v, std::string_view name)
Utilities: convert hex-encoded Values (throws error if not hex).
uint256 ExtractMatches(std::vector< Txid > &vMatch, std::vector< unsigned int > &vnIndex)
extract the matching txid's represented by this partial merkle tree and their respective indices with...
bool DecodeHexTx(CMutableTransaction &tx, const std::string &hex_tx, bool try_no_witness, bool try_witness)
A reference to a CKey: the Hash160 of its serialized public key.
Add the transaction to the mempool, but don't broadcast to anybody.
WalletContext struct containing references to state shared between CWallet instances, like the reference to the chain interface, and the list of opened wallets.
void EnsureWalletIsUnlocked(const CWallet &wallet)
bilingual_str ErrorString(const Result< T > &result)
A mutable version of CTransaction.
static path u8path(std::string_view utf8_str)
FoundBlock & height(int &height)
RPCHelpMan removeprunedfunds()
An encapsulated private key.
FoundBlock & mtpTime(int64_t &mtp_time)
RPCHelpMan backupwallet()
is a home for public enum and struct type definitions that are used internally by node code...
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, bool allow_unnamed)
FoundBlock & time(int64_t &time)
std::shared_ptr< CWallet > GetWalletForJSONRPCRequest(const JSONRPCRequest &request)
Figures out what wallet, if any, to use for a JSONRPCRequest.
Error parsing or validating structure in raw format.
const std::string UNIX_EPOCH_TIME
String used to describe UNIX epoch time in documentation, factored out to a constant for consistency...
Special type to denote elision (...)
std::vector< unsigned char > ParseHexV(const UniValue &v, std::string_view name)