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