interfaces.cpp raw
1 // Copyright (c) 2018-2022 The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <interfaces/wallet.h>
6
7 #include <common/args.h>
8 #include <consensus/amount.h>
9 #include <interfaces/chain.h>
10 #include <interfaces/handler.h>
11 #include <key_io.h>
12 #include <node/types.h>
13 #include <policy/fees.h>
14 #include <primitives/transaction.h>
15 #include <rpc/server.h>
16 #include <scheduler.h>
17 #include <support/allocators/secure.h>
18 #include <sync.h>
19 #include <uint256.h>
20 #include <util/check.h>
21 #include <util/translation.h>
22 #include <util/ui_change_type.h>
23 #include <wallet/coincontrol.h>
24 #include <wallet/context.h>
25 #include <wallet/dump.h>
26 #include <wallet/feebumper.h>
27 #include <wallet/fees.h>
28 #include <wallet/types.h>
29 #include <wallet/load.h>
30 #include <wallet/receive.h>
31 #include <wallet/rpc/wallet.h>
32 #include <wallet/spend.h>
33 #include <wallet/wallet.h>
34
35 #include <memory>
36 #include <set>
37 #include <string>
38 #include <utility>
39 #include <vector>
40
41 using common::PSBTError;
42 using interfaces::Chain;
43 using interfaces::FoundBlock;
44 using interfaces::Handler;
45 using interfaces::MakeSignalHandler;
46 using interfaces::Wallet;
47 using interfaces::WalletAddress;
48 using interfaces::WalletBalances;
49 using interfaces::WalletLoader;
50 using interfaces::WalletMigrationResult;
51 using interfaces::WalletOrderForm;
52 using interfaces::WalletTx;
53 using interfaces::WalletTxOut;
54 using interfaces::WalletTxStatus;
55 using interfaces::WalletValueMap;
56
57 std::set<CScript> AddressesToKeys(std::vector<std::string> addresses)
58 {
59 std::set<CScript> keys;
60 for (const auto& address : addresses) {
61 CScript scriptPubKey = GetScriptForDestination(DecodeDestination(address));
62 keys.insert(scriptPubKey);
63 }
64 return keys;
65 }
66
67 namespace wallet {
68 // All members of the classes in this namespace are intentionally public, as the
69 // classes themselves are private.
70 namespace {
71 //! Construct wallet tx struct.
72 WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
73 {
74 LOCK(wallet.cs_wallet);
75 WalletTx result;
76 result.tx = wtx.tx;
77 result.txin_is_mine.reserve(wtx.tx->vin.size());
78 for (const auto& txin : wtx.tx->vin) {
79 result.txin_is_mine.emplace_back(InputIsMine(wallet, txin));
80 }
81 result.txout_is_mine.reserve(wtx.tx->vout.size());
82 result.txout_address.reserve(wtx.tx->vout.size());
83 result.txout_address_is_mine.reserve(wtx.tx->vout.size());
84 for (const auto& txout : wtx.tx->vout) {
85 result.txout_is_mine.emplace_back(wallet.IsMine(txout));
86 result.txout_is_change.push_back(OutputIsChange(wallet, txout));
87 result.txout_address.emplace_back();
88 result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
89 wallet.IsMine(result.txout_address.back()) :
90 ISMINE_NO);
91 }
92 result.credit = CachedTxGetCredit(wallet, wtx, ISMINE_ALL);
93 result.debit = CachedTxGetDebit(wallet, wtx, ISMINE_ALL);
94 result.change = CachedTxGetChange(wallet, wtx);
95 result.time = wtx.GetTxTime();
96 result.value_map = wtx.mapValue;
97 result.is_coinbase = wtx.IsCoinBase();
98 return result;
99 }
100
101 //! Construct wallet tx status struct.
102 WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
103 EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
104 {
105 AssertLockHeld(wallet.cs_wallet);
106
107 WalletTxStatus result;
108 result.block_height =
109 wtx.state<TxStateConfirmed>() ? wtx.state<TxStateConfirmed>()->confirmed_block_height :
110 wtx.state<TxStateBlockConflicted>() ? wtx.state<TxStateBlockConflicted>()->conflicting_block_height :
111 std::numeric_limits<int>::max();
112 result.blocks_to_maturity = wallet.GetTxBlocksToMaturity(wtx);
113 result.depth_in_main_chain = wallet.GetTxDepthInMainChain(wtx);
114 result.time_received = wtx.nTimeReceived;
115 result.lock_time = wtx.tx->nLockTime;
116 result.is_trusted = CachedTxIsTrusted(wallet, wtx);
117 result.is_abandoned = wtx.isAbandoned();
118 result.is_coinbase = wtx.IsCoinBase();
119 result.is_in_main_chain = wtx.isConfirmed();
120 result.is_assumed = wallet.IsTxAssumed(wtx);
121 return result;
122 }
123
124 //! Construct wallet TxOut struct.
125 WalletTxOut MakeWalletTxOut(const CWallet& wallet,
126 const CWalletTx& wtx,
127 int n,
128 int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
129 {
130 WalletTxOut result;
131 result.txout = wtx.tx->vout[n];
132 result.time = wtx.GetTxTime();
133 result.depth_in_main_chain = depth;
134 result.is_spent = wallet.IsSpent(COutPoint(wtx.GetHash(), n));
135 return result;
136 }
137
138 WalletTxOut MakeWalletTxOut(const CWallet& wallet,
139 const COutput& output) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
140 {
141 WalletTxOut result;
142 result.txout = output.txout;
143 result.time = output.time;
144 result.depth_in_main_chain = output.depth;
145 result.is_spent = wallet.IsSpent(output.outpoint);
146 return result;
147 }
148
149 class WalletImpl : public Wallet
150 {
151 public:
152 explicit WalletImpl(WalletContext& context, const std::shared_ptr<CWallet>& wallet) : m_context(context), m_wallet(wallet) {}
153
154 bool encryptWallet(const SecureString& wallet_passphrase) override
155 {
156 return m_wallet->EncryptWallet(wallet_passphrase);
157 }
158 bool isCrypted() override { return m_wallet->IsCrypted(); }
159 bool lock() override { return m_wallet->Lock(); }
160 bool unlock(const SecureString& wallet_passphrase) override { return m_wallet->Unlock(wallet_passphrase); }
161 bool isLocked() override { return m_wallet->IsLocked(); }
162 bool changeWalletPassphrase(const SecureString& old_wallet_passphrase,
163 const SecureString& new_wallet_passphrase) override
164 {
165 return m_wallet->ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase);
166 }
167 void abortRescan() override { m_wallet->AbortRescan(); }
168 bool canBackupToDbDump() override {
169 return (m_wallet->GetDatabase().Format() != "bdb");
170 }
171 bool backupWallet(const std::string& filename, const WalletBackupFormat format, bilingual_str& error) override {
172 switch (format) {
173 case WalletBackupFormat::DbDump:
174 return DumpWallet(m_wallet->GetDatabase(), error, filename);
175 case WalletBackupFormat::Raw:
176 return m_wallet->BackupWallet(filename);
177 }
178 return false;
179 }
180 std::string getWalletName() override { return m_wallet->GetName(); }
181 util::Result<CTxDestination> getNewDestination(const OutputType type, const std::string& label) override
182 {
183 LOCK(m_wallet->cs_wallet);
184 return m_wallet->GetNewDestination(type, label);
185 }
186 bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
187 {
188 std::unique_ptr<SigningProvider> provider = m_wallet->GetSolvingProvider(script);
189 if (provider) {
190 return provider->GetPubKey(address, pub_key);
191 }
192 return false;
193 }
194 SigningResult signMessage(const MessageSignatureFormat format, const std::string& message, const CTxDestination& address, std::string& str_sig) override
195 {
196 return m_wallet->SignMessage(format, message, address, str_sig);
197 }
198 bool isSpendable(const CTxDestination& dest) override
199 {
200 LOCK(m_wallet->cs_wallet);
201 return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
202 }
203 bool haveWatchOnly() override
204 {
205 auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();
206 if (spk_man) {
207 return spk_man->HaveWatchOnly();
208 }
209 return false;
210 };
211 bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::optional<AddressPurpose>& purpose) override
212 {
213 return m_wallet->SetAddressBook(dest, name, purpose);
214 }
215 bool delAddressBook(const CTxDestination& dest) override
216 {
217 return m_wallet->DelAddressBook(dest);
218 }
219 bool getAddress(const CTxDestination& dest,
220 std::string* name,
221 isminetype* is_mine,
222 AddressPurpose* purpose) override
223 {
224 LOCK(m_wallet->cs_wallet);
225 const auto& entry = m_wallet->FindAddressBookEntry(dest, /*allow_change=*/false);
226 if (!entry) return false; // addr not found
227 if (name) {
228 *name = entry->GetLabel();
229 }
230 std::optional<isminetype> dest_is_mine;
231 if (is_mine || purpose) {
232 dest_is_mine = m_wallet->IsMine(dest);
233 }
234 if (is_mine) {
235 *is_mine = *dest_is_mine;
236 }
237 if (purpose) {
238 // In very old wallets, address purpose may not be recorded so we derive it from IsMine
239 *purpose = entry->purpose.value_or(*dest_is_mine ? AddressPurpose::RECEIVE : AddressPurpose::SEND);
240 }
241 return true;
242 }
243 std::vector<WalletAddress> getAddresses() override
244 {
245 LOCK(m_wallet->cs_wallet);
246 std::vector<WalletAddress> result;
247 m_wallet->ForEachAddrBookEntry([&](const CTxDestination& dest, const std::string& label, bool is_change, const std::optional<AddressPurpose>& purpose) EXCLUSIVE_LOCKS_REQUIRED(m_wallet->cs_wallet) {
248 if (is_change) return;
249 isminetype is_mine = m_wallet->IsMine(dest);
250 // In very old wallets, address purpose may not be recorded so we derive it from IsMine
251 result.emplace_back(dest, is_mine, purpose.value_or(is_mine ? AddressPurpose::RECEIVE : AddressPurpose::SEND), label);
252 });
253 return result;
254 }
255 std::vector<std::string> getAddressReceiveRequests() override {
256 LOCK(m_wallet->cs_wallet);
257 return m_wallet->GetAddressReceiveRequests();
258 }
259 bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) override {
260 // Note: The setAddressReceiveRequest interface used by the GUI to store
261 // receive requests is a little awkward and could be improved in the
262 // future:
263 //
264 // - The same method is used to save requests and erase them, but
265 // having separate methods could be clearer and prevent bugs.
266 //
267 // - Request ids are passed as strings even though they are generated as
268 // integers.
269 //
270 // - Multiple requests can be stored for the same address, but it might
271 // be better to only allow one request or only keep the current one.
272 LOCK(m_wallet->cs_wallet);
273 WalletBatch batch{m_wallet->GetDatabase()};
274 return value.empty() ? m_wallet->EraseAddressReceiveRequest(batch, dest, id)
275 : m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
276 }
277 util::Result<void> displayAddress(const CTxDestination& dest) override
278 {
279 LOCK(m_wallet->cs_wallet);
280 return m_wallet->DisplayAddress(dest);
281 }
282 bool checkAddressForUsage(const std::vector<std::string>& addresses) const override
283 {
284 LOCK(m_wallet->cs_wallet);
285 return m_wallet->FindScriptPubKeyUsed(AddressesToKeys(addresses));
286 }
287 bool findAddressUsage(const std::vector<std::string>& addresses, std::function<void(const std::string&, const WalletTx&, uint32_t)> callback) const override
288 {
289 LOCK(m_wallet->cs_wallet);
290 return m_wallet->FindScriptPubKeyUsed(AddressesToKeys(addresses), [&callback, this](const CWalletTx& wtx, uint32_t output_index){
291 CTxDestination dest;
292 bool success = ExtractDestination(wtx.tx->vout[output_index].scriptPubKey, dest);
293 assert(success); // It shouldn't be possible to end up here with anything unrecognised
294 std::string address = EncodeDestination(dest);
295 WalletTx interface_wtx = MakeWalletTx(*m_wallet, wtx);
296 callback(address, interface_wtx, output_index);
297 });
298 }
299 bool lockCoin(const COutPoint& output, const bool write_to_db) override
300 {
301 LOCK(m_wallet->cs_wallet);
302 std::unique_ptr<WalletBatch> batch = write_to_db ? std::make_unique<WalletBatch>(m_wallet->GetDatabase()) : nullptr;
303 return m_wallet->LockCoin(output, batch.get());
304 }
305 bool unlockCoin(const COutPoint& output) override
306 {
307 LOCK(m_wallet->cs_wallet);
308 std::unique_ptr<WalletBatch> batch = std::make_unique<WalletBatch>(m_wallet->GetDatabase());
309 return m_wallet->UnlockCoin(output, batch.get());
310 }
311 bool isLockedCoin(const COutPoint& output) override
312 {
313 LOCK(m_wallet->cs_wallet);
314 return m_wallet->IsLockedCoin(output);
315 }
316 void listLockedCoins(std::vector<COutPoint>& outputs) override
317 {
318 LOCK(m_wallet->cs_wallet);
319 return m_wallet->ListLockedCoins(outputs);
320 }
321 util::Result<CTransactionRef> createTransaction(const std::vector<CRecipient>& recipients,
322 const CCoinControl& coin_control,
323 bool sign,
324 int& change_pos,
325 CAmount& fee) override
326 {
327 LOCK(m_wallet->cs_wallet);
328 auto res = CreateTransaction(*m_wallet, recipients, change_pos == -1 ? std::nullopt : std::make_optional(change_pos),
329 coin_control, sign);
330 if (!res) return util::Error{util::ErrorString(res)};
331 const auto& txr = *res;
332 fee = txr.fee;
333 change_pos = txr.change_pos ? int(*txr.change_pos) : -1;
334
335 return txr.tx;
336 }
337 void commitTransaction(CTransactionRef tx,
338 WalletValueMap value_map,
339 WalletOrderForm order_form) override
340 {
341 LOCK(m_wallet->cs_wallet);
342 m_wallet->CommitTransaction(std::move(tx), std::move(value_map), std::move(order_form));
343 }
344 bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet->TransactionCanBeAbandoned(txid); }
345 bool abandonTransaction(const uint256& txid) override
346 {
347 LOCK(m_wallet->cs_wallet);
348 return m_wallet->AbandonTransaction(txid);
349 }
350 bool transactionCanBeBumped(const uint256& txid) override
351 {
352 return feebumper::TransactionCanBeBumped(*m_wallet.get(), txid);
353 }
354 bool createBumpTransaction(const uint256& txid,
355 const CCoinControl& coin_control,
356 std::vector<bilingual_str>& errors,
357 CAmount& old_fee,
358 CAmount& new_fee,
359 CMutableTransaction& mtx) override
360 {
361 std::vector<CTxOut> outputs; // just an empty list of new recipients for now
362 return feebumper::CreateRateBumpTransaction(*m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx, /* require_mine= */ true, outputs) == feebumper::Result::OK;
363 }
364 bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(*m_wallet.get(), mtx); }
365 bool commitBumpTransaction(const uint256& txid,
366 CMutableTransaction&& mtx,
367 std::vector<bilingual_str>& errors,
368 uint256& bumped_txid) override
369 {
370 return feebumper::CommitTransaction(*m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) ==
371 feebumper::Result::OK;
372 }
373 CTransactionRef getTx(const uint256& txid) override
374 {
375 LOCK(m_wallet->cs_wallet);
376 auto mi = m_wallet->mapWallet.find(txid);
377 if (mi != m_wallet->mapWallet.end()) {
378 return mi->second.tx;
379 }
380 return {};
381 }
382 WalletTx getWalletTx(const uint256& txid) override
383 {
384 LOCK(m_wallet->cs_wallet);
385 auto mi = m_wallet->mapWallet.find(txid);
386 if (mi != m_wallet->mapWallet.end()) {
387 return MakeWalletTx(*m_wallet, mi->second);
388 }
389 return {};
390 }
391 std::set<WalletTx> getWalletTxs() override
392 {
393 LOCK(m_wallet->cs_wallet);
394 std::set<WalletTx> result;
395 for (const auto& entry : m_wallet->mapWallet) {
396 result.emplace(MakeWalletTx(*m_wallet, entry.second));
397 }
398 return result;
399 }
400 bool tryGetTxStatus(const uint256& txid,
401 interfaces::WalletTxStatus& tx_status,
402 int& num_blocks,
403 int64_t& block_time) override
404 {
405 TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
406 if (!locked_wallet) {
407 return false;
408 }
409 auto mi = m_wallet->mapWallet.find(txid);
410 if (mi == m_wallet->mapWallet.end()) {
411 return false;
412 }
413 num_blocks = m_wallet->GetLastBlockHeight();
414 block_time = -1;
415 CHECK_NONFATAL(m_wallet->chain().findBlock(m_wallet->GetLastBlockHash(), FoundBlock().time(block_time)));
416 tx_status = MakeWalletTxStatus(*m_wallet, mi->second);
417 return true;
418 }
419 WalletTx getWalletTxDetails(const uint256& txid,
420 WalletTxStatus& tx_status,
421 WalletOrderForm& order_form,
422 bool& in_mempool,
423 int& num_blocks) override
424 {
425 LOCK(m_wallet->cs_wallet);
426 auto mi = m_wallet->mapWallet.find(txid);
427 if (mi != m_wallet->mapWallet.end()) {
428 num_blocks = m_wallet->GetLastBlockHeight();
429 in_mempool = mi->second.InMempool();
430 order_form = mi->second.vOrderForm;
431 tx_status = MakeWalletTxStatus(*m_wallet, mi->second);
432 return MakeWalletTx(*m_wallet, mi->second);
433 }
434 return {};
435 }
436 std::optional<PSBTError> fillPSBT(int sighash_type,
437 bool sign,
438 bool bip32derivs,
439 size_t* n_signed,
440 PartiallySignedTransaction& psbtx,
441 bool& complete) override
442 {
443 return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
444 }
445 WalletBalances getBalances() override
446 {
447 const auto bal = GetBalance(*m_wallet);
448 WalletBalances result;
449 result.balance = bal.m_mine_trusted;
450 result.unconfirmed_balance = bal.m_mine_untrusted_pending;
451 result.immature_balance = bal.m_mine_immature;
452 result.have_watch_only = haveWatchOnly();
453 if (result.have_watch_only) {
454 result.watch_only_balance = bal.m_watchonly_trusted;
455 result.unconfirmed_watch_only_balance = bal.m_watchonly_untrusted_pending;
456 result.immature_watch_only_balance = bal.m_watchonly_immature;
457 }
458 return result;
459 }
460 bool tryGetBalances(WalletBalances& balances, uint256& block_hash) override
461 {
462 TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
463 if (!locked_wallet) {
464 return false;
465 }
466 block_hash = m_wallet->GetLastBlockHash();
467 balances = getBalances();
468 return true;
469 }
470 CAmount getBalance() override { return GetBalance(*m_wallet).m_mine_trusted; }
471 std::string getPreciseBalance() override { return wallet::AttosatsToString(m_wallet->GetPreciseBalanceAttosats()); }
472 util::Result<uint256> sendStealthPayment(const CTxDestination& dest, CAmount amount_attosats, CAmount fee_attosats, int change_outputs) override
473 {
474 return m_wallet->SendStealthPayment(dest, amount_attosats, fee_attosats, change_outputs);
475 }
476 util::Result<uint256> mintConfidential(CAmount amount_attosats, CAmount fee_attosats, int output_count) override
477 {
478 return m_wallet->MintConfidential(amount_attosats, fee_attosats, output_count);
479 }
480 CAmount getConfidentialBalance() override
481 {
482 CAmount total{0};
483 std::vector<std::pair<COutPoint, wallet::CTReceipt>> all;
484 if (wallet::ListUnspentCTOutputs(*m_wallet, {}, all)) {
485 for (const auto& [op, rec] : all) total += rec.Amount();
486 }
487 return total;
488 }
489 CAmount getAvailableBalance(const CCoinControl& coin_control) override
490 {
491 LOCK(m_wallet->cs_wallet);
492 CAmount total_amount = 0;
493 // Fetch selected coins total amount
494 if (coin_control.HasSelected()) {
495 FastRandomContext rng{};
496 CoinSelectionParams params(rng);
497 // Note: for now, swallow any error.
498 if (auto res = FetchSelectedInputs(*m_wallet, coin_control, params)) {
499 total_amount += res->total_amount;
500 }
501 }
502
503 // And fetch the wallet available coins
504 if (coin_control.m_allow_other_inputs) {
505 total_amount += AvailableCoins(*m_wallet, &coin_control).GetTotalAmount();
506 }
507
508 return total_amount;
509 }
510 isminetype txinIsMine(const CTxIn& txin) override
511 {
512 LOCK(m_wallet->cs_wallet);
513 return InputIsMine(*m_wallet, txin);
514 }
515 isminetype txoutIsMine(const CTxOut& txout) override
516 {
517 LOCK(m_wallet->cs_wallet);
518 return m_wallet->IsMine(txout);
519 }
520 CAmount getDebit(const CTxIn& txin, isminefilter filter) override
521 {
522 LOCK(m_wallet->cs_wallet);
523 return m_wallet->GetDebit(txin, filter);
524 }
525 CAmount getCredit(const CTxOut& txout, isminefilter filter) override
526 {
527 LOCK(m_wallet->cs_wallet);
528 return OutputGetCredit(*m_wallet, txout, filter);
529 }
530 CoinsList listCoins() override
531 {
532 LOCK(m_wallet->cs_wallet);
533 CoinsList result;
534 for (const auto& entry : ListCoins(*m_wallet)) {
535 auto& group = result[entry.first];
536 for (const auto& coin : entry.second) {
537 group.emplace_back(coin.outpoint,
538 MakeWalletTxOut(*m_wallet, coin));
539 }
540 }
541 return result;
542 }
543 std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
544 {
545 LOCK(m_wallet->cs_wallet);
546 std::vector<WalletTxOut> result;
547 result.reserve(outputs.size());
548 for (const auto& output : outputs) {
549 result.emplace_back();
550 auto it = m_wallet->mapWallet.find(output.hash);
551 if (it != m_wallet->mapWallet.end()) {
552 int depth = m_wallet->GetTxDepthInMainChain(it->second);
553 if (depth >= 0) {
554 result.back() = MakeWalletTxOut(*m_wallet, it->second, output.n, depth);
555 }
556 }
557 }
558 return result;
559 }
560 CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(*m_wallet, tx_bytes); }
561 CAmount getMinimumFee(unsigned int tx_bytes,
562 const CCoinControl& coin_control,
563 int* returned_target,
564 FeeReason* reason) override
565 {
566 FeeCalculation fee_calc;
567 CAmount result;
568 result = GetMinimumFee(*m_wallet, tx_bytes, coin_control, &fee_calc);
569 if (returned_target) *returned_target = fee_calc.returnedTarget;
570 if (reason) *reason = fee_calc.reason;
571 return result;
572 }
573 unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
574 bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
575 bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
576 bool hasExternalSigner() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER); }
577 bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); }
578 bool taprootEnabled() override {
579 if (m_wallet->IsLegacy()) return false;
580 auto spk_man = m_wallet->GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/false);
581 return spk_man != nullptr;
582 }
583 OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
584 CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
585 void remove() override
586 {
587 RemoveWallet(m_context, m_wallet, /*load_on_start=*/false);
588 }
589 bool isLegacy() override { return m_wallet->IsLegacy(); }
590 std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
591 {
592 return MakeSignalHandler(m_wallet->NotifyUnload.connect(fn));
593 }
594 std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
595 {
596 return MakeSignalHandler(m_wallet->ShowProgress.connect(fn));
597 }
598 std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override
599 {
600 return MakeSignalHandler(m_wallet->NotifyStatusChanged.connect([fn](CWallet*) { fn(); }));
601 }
602 std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
603 {
604 return MakeSignalHandler(m_wallet->NotifyAddressBookChanged.connect(
605 [fn](const CTxDestination& address, const std::string& label, bool is_mine,
606 AddressPurpose purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
607 }
608 std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override
609 {
610 return MakeSignalHandler(m_wallet->NotifyTransactionChanged.connect(
611 [fn](const uint256& txid, ChangeType status) { fn(txid, status); }));
612 }
613 std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override
614 {
615 return MakeSignalHandler(m_wallet->NotifyWatchonlyChanged.connect(fn));
616 }
617 std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) override
618 {
619 return MakeSignalHandler(m_wallet->NotifyCanGetAddressesChanged.connect(fn));
620 }
621 CWallet* wallet() override { return m_wallet.get(); }
622
623 WalletContext& m_context;
624 std::shared_ptr<CWallet> m_wallet;
625 };
626
627 class WalletLoaderImpl : public WalletLoader
628 {
629 public:
630 WalletLoaderImpl(Chain& chain, ArgsManager& args)
631 {
632 m_context.chain = &chain;
633 m_context.args = &args;
634 }
635 ~WalletLoaderImpl() override { stop(); }
636
637 //! HACK to workaround libc++ bugs (assigning from other locations such as sweepprivkeys breaks std::any_cast type checking); see also https://github.com/llvm/llvm-project/issues/55684
638 void assignContextHACK(std::any& a) override
639 {
640 a = &m_context;
641 }
642 //! ChainClient methods
643 void registerRpcs() override
644 {
645 for (const CRPCCommand& command : GetWalletRPCCommands()) {
646 m_rpc_commands.emplace_back(command.category, command.name, [this, &command](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
647 JSONRPCRequest wallet_request = request;
648 assignContextHACK(wallet_request.context);
649 return command.actor(wallet_request, result, last_handler);
650 }, command.argNames, command.unique_id);
651 m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
652 }
653 }
654 bool verify() override { return VerifyWallets(m_context); }
655 bool load() override { return LoadWallets(m_context); }
656 void start(CScheduler& scheduler) override
657 {
658 m_context.scheduler = &scheduler;
659 return StartWallets(m_context);
660 }
661 void flush() override { return FlushWallets(m_context); }
662 void stop() override { return UnloadWallets(m_context); }
663 void setMockTime(int64_t time) override { return SetMockTime(time); }
664 void schedulerMockForward(std::chrono::seconds delta) override { Assert(m_context.scheduler)->MockForward(delta); }
665
666 //! WalletLoader methods
667 util::Result<std::unique_ptr<Wallet>> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, std::vector<bilingual_str>& warnings) override
668 {
669 DatabaseOptions options;
670 DatabaseStatus status;
671 ReadDatabaseArgs(*m_context.args, options);
672 options.require_create = true;
673 options.create_flags = wallet_creation_flags;
674 options.create_passphrase = passphrase;
675 bilingual_str error;
676 std::unique_ptr<Wallet> wallet{MakeWallet(m_context, CreateWallet(m_context, name, /*load_on_start=*/true, options, status, error, warnings))};
677 if (wallet) {
678 return wallet;
679 } else {
680 return util::Error{error};
681 }
682 }
683 util::Result<std::unique_ptr<Wallet>> loadWallet(const std::string& name, std::vector<bilingual_str>& warnings) override
684 {
685 DatabaseOptions options;
686 DatabaseStatus status;
687 ReadDatabaseArgs(*m_context.args, options);
688 options.require_existing = true;
689 bilingual_str error;
690 std::unique_ptr<Wallet> wallet{MakeWallet(m_context, LoadWallet(m_context, name, /*load_on_start=*/true, options, status, error, warnings))};
691 if (wallet) {
692 return wallet;
693 } else {
694 return util::Error{error};
695 }
696 }
697 util::Result<std::unique_ptr<Wallet>> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, std::vector<bilingual_str>& warnings) override
698 {
699 DatabaseStatus status;
700 bilingual_str error;
701 std::unique_ptr<Wallet> wallet{MakeWallet(m_context, RestoreWallet(m_context, backup_file, wallet_name, /*load_on_start=*/true, status, error, warnings))};
702 if (wallet) {
703 return wallet;
704 } else {
705 return util::Error{error};
706 }
707 }
708 util::Result<WalletMigrationResult> migrateWallet(const std::string& name, const SecureString& passphrase) override
709 {
710 auto res = wallet::MigrateLegacyToDescriptor(name, passphrase, m_context);
711 if (!res) return util::Error{util::ErrorString(res)};
712 WalletMigrationResult out{
713 .wallet = MakeWallet(m_context, res->wallet),
714 .watchonly_wallet_name = res->watchonly_wallet ? std::make_optional(res->watchonly_wallet->GetName()) : std::nullopt,
715 .solvables_wallet_name = res->solvables_wallet ? std::make_optional(res->solvables_wallet->GetName()) : std::nullopt,
716 .backup_path = res->backup_path,
717 };
718 return out;
719 }
720 bool isEncrypted(const std::string& wallet_name) override
721 {
722 auto wallets{GetWallets(m_context)};
723 auto it = std::find_if(wallets.begin(), wallets.end(), [&](std::shared_ptr<CWallet> w){ return w->GetName() == wallet_name; });
724 if (it != wallets.end()) return (*it)->IsCrypted();
725
726 // Unloaded wallet, read db
727 DatabaseOptions options;
728 options.require_existing = true;
729 DatabaseStatus status;
730 bilingual_str error;
731 auto db = MakeWalletDatabase(wallet_name, options, status, error);
732 if (!db) return false;
733 return WalletBatch(*db).IsEncrypted();
734 }
735 std::string getWalletDir() override
736 {
737 return fs::PathToString(GetWalletDir());
738 }
739 std::vector<std::pair<std::string, std::string>> listWalletDir() override
740 {
741 std::vector<std::pair<std::string, std::string>> paths;
742 for (auto& [path, format] : ListDatabases(GetWalletDir())) {
743 paths.emplace_back(fs::PathToString(path), format);
744 }
745 return paths;
746 }
747 std::vector<std::unique_ptr<Wallet>> getWallets() override
748 {
749 std::vector<std::unique_ptr<Wallet>> wallets;
750 for (const auto& wallet : GetWallets(m_context)) {
751 wallets.emplace_back(MakeWallet(m_context, wallet));
752 }
753 return wallets;
754 }
755 std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
756 {
757 return HandleLoadWallet(m_context, std::move(fn));
758 }
759 WalletContext* context() override { return &m_context; }
760
761 WalletContext m_context;
762 const std::vector<std::string> m_wallet_filenames;
763 std::vector<std::unique_ptr<Handler>> m_rpc_handlers;
764 std::list<CRPCCommand> m_rpc_commands;
765 };
766 } // namespace
767 } // namespace wallet
768
769 namespace interfaces {
770 std::unique_ptr<Wallet> MakeWallet(wallet::WalletContext& context, const std::shared_ptr<wallet::CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(context, wallet) : nullptr; }
771
772 std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args)
773 {
774 return std::make_unique<wallet::WalletLoaderImpl>(chain, args);
775 }
776 } // namespace interfaces
777