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