15 std::vector<std::string> warnings;
20 auto parsed_descs =
Parse(request.descriptor,
keys, error,
true);
21 if (parsed_descs.empty()) {
25 if (request.internal.has_value() && parsed_descs.size() > 1) {
28 "Cannot have multipath descriptor while also specifying 'internal'",
34 bool is_ranged{
false};
35 int64_t range_start = 0, range_end = 1, next_index = 0;
36 if (!parsed_descs.at(0)->IsRange() && request.range.has_value()) {
39 "Range should not be specified for an un-ranged descriptor",
42 }
else if (parsed_descs.at(0)->IsRange()) {
43 if (request.range.has_value()) {
44 int64_t low = request.range->first;
45 int64_t high = request.range->second;
53 warnings.emplace_back(
"Range not given, using default keypool range");
55 range_end =
wallet.m_keypool_size;
57 next_index = request.next_index.value_or(range_start);
60 if (next_index < range_start || next_index >= range_end) {
63 "next_index is out of range",
70 if (request.active && !parsed_descs.at(0)->IsRange()) {
73 "Active descriptors must be ranged",
79 if (parsed_descs.size() > 1 && !request.label.empty()) {
82 "Multipath descriptors should not have a label",
88 if (is_ranged && !request.label.empty()) {
91 "Ranged descriptors should not have a label",
96 bool desc_internal = request.internal.value_or(
false);
98 if (desc_internal && !request.label.empty()) {
101 "Internal addresses should not have a label",
107 if (request.active && !parsed_descs.at(0)->IsSingleType()) {
110 "Combo descriptors cannot be set to active",
119 "Cannot import private keys to a wallet with private keys disabled",
124 for (
size_t j = 0; j < parsed_descs.size(); ++j) {
125 auto parsed_desc = std::move(parsed_descs[j]);
126 if (parsed_descs.size() == 2) {
127 desc_internal = j == 1;
128 }
else if (parsed_descs.size() > 2) {
133 std::vector<CScript> scripts;
134 if (!parsed_desc->Expand(0,
keys, scripts, expand_keys)) {
137 "Cannot expand descriptor. Probably because of hardened derivations without private keys provided",
142 for (
const auto& w : parsed_desc->Warnings()) {
143 warnings.push_back(w);
148 if (
keys.keys.empty()) {
151 "Cannot import descriptor without private keys to a wallet with private keys enabled",
155 if (!parsed_desc->HavePrivateKeys(
keys)) {
156 warnings.emplace_back(
"Not all private keys provided. Some wallet functionality may return unexpected errors");
160 if (!parsed_desc->HasScripts()) {
164 "Cannot import unused() to wallet without private keys enabled",
173 std::set<CPubKey> pubkeys;
174 std::set<CExtPubKey> extpubs;
175 parsed_desc->GetPubKeys(pubkeys, extpubs);
176 std::transform(extpubs.begin(), extpubs.end(), std::inserter(pubkeys, pubkeys.begin()), [](
const CExtPubKey& xpub) { return xpub.pubkey; });
178 if (
wallet.GetKey(pubkeys.begin()->GetID())) {
181 "Cannot import an unused() descriptor when its private key is already in the wallet",
187 Assume(request.timestamp.has_value());
188 WalletDescriptor w_desc(std::move(parsed_desc), request.timestamp.value(), range_start, range_end, next_index);
191 auto spk_manager_res =
wallet.AddWalletDescriptor(w_desc,
keys, request.label, desc_internal);
193 if (!spk_manager_res) {
201 auto& spk_manager = spk_manager_res.value().get();
204 if (request.active) {
206 warnings.emplace_back(
"Unknown output type, cannot set descriptor to active.");
208 wallet.AddActiveScriptPubKeyMan(spk_manager.GetID(), *w_desc.
descriptor->GetOutputType(), desc_internal);
212 wallet.DeactivateScriptPubKeyMan(spk_manager.GetID(), *w_desc.
descriptor->GetOutputType(), desc_internal);
223 std::vector<ImportDescriptorRequest>& requests)
225 std::vector<ImportResult> response;
231 "Wallet is currently rescanning. Abort existing rescan or wait.",
239 wallet.BlockUntilSyncedToCurrentChain();
245 int64_t lowest_timestamp = 0;
252 "Error: Please enter the wallet passphrase with walletpassphrase first.",
261 request.timestamp = request.timestamp.value_or(now);
264 if (lowest_timestamp > request.timestamp.value()) {
265 lowest_timestamp = request.timestamp.value();
271 response.push_back(import_result);
273 wallet.ConnectScriptPubKeyManNotifiers();
278 const int64_t scanned_time =
wallet.Scanner().ScanFromTime(lowest_timestamp, reserver);
281 if (
wallet.Scanner().IsAborting()) {
284 "Rescan aborted by user.",
290 if (scanned_time > lowest_timestamp) {
292 for (
size_t i = 0; i < requests.size(); ++i) {
299 const int64_t timestamp{requests.at(i).timestamp.value()};
300 if (scanned_time > timestamp && !result.
has_error()) {
301 std::string error_msg =
strprintf(
"Rescan failed for descriptor with timestamp %d. There "
302 "was an error reading a block from time %d, which is after or within %d seconds "
303 "of key creation, and could contain transactions pertaining to the desc. As a "
304 "result, transactions and coins using this desc may not appear in the wallet.",
306 if (
wallet.chain().havePruned()) {
307 error_msg +=
strprintf(
" This error could be caused by pruning or data corruption "
308 "(see bitcoind log for details) and could be dealt with by downloading and "
309 "rescanning the relevant blocks (see -reindex option and rescanblockchain RPC).");
310 }
else if (
wallet.chain().hasAssumedValidChain()) {
311 error_msg +=
strprintf(
" This error is likely caused by an in-progress assumeutxo "
312 "background sync. Check logs or getchainstates RPC for assumeutxo background "
313 "sync progress and try again later.");
315 error_msg +=
strprintf(
" This error could potentially caused by data corruption. If "
316 "the issue persists you may want to reindex (see -reindex option).");
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.
#define Assume(val)
Assume is the identity function.
Helper for findBlock to selectively return pieces of block data.
FoundBlock & time(int64_t &time)
FoundBlock & mtpTime(int64_t &mtp_time)
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, ParamFormat format=ParamFormat::JSON)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
@ MEMPOOL_NO_BROADCAST
Add the transaction to the mempool, but don't broadcast to anybody.
bilingual_str ErrorString(const Result< T > &result)
std::vector< ImportResult > ProcessDescriptorsImport(CWallet &wallet, std::vector< ImportDescriptorRequest > &requests)
@ UnlockNeeded
The wallet is locked and the operation requires access to private keys.
@ InvalidDescriptor
TODO Add correct descriptions to each error.
@ GenericError
Generic wallet error.
ImportResult ImportDescriptor(CWallet &wallet, const ImportDescriptorRequest &request) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
util::Expected< void, std::string > CheckDescriptorRangeBounds(int64_t low, int64_t high)
Validate the numeric bounds of a descriptor key-expression range [low, high] (high inclusive).
Information about a descriptor to be imported.
std::optional< ImportError > error
std::vector< std::string > warnings
std::vector< uint16_t > keys
#define EXCLUSIVE_LOCKS_REQUIRED(...)
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.