diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -44,7 +44,7 @@ result.txout_address_is_mine.emplace_back( ExtractDestination(txout.scriptPubKey, result.txout_address.back()) - ? IsMine(wallet, result.txout_address.back()) + ? wallet.IsMine(result.txout_address.back()) : ISMINE_NO); } result.credit = wtx.GetCredit(locked_chain, ISMINE_ALL); @@ -132,15 +132,22 @@ return m_wallet->chainParams; } bool getPubKey(const CKeyID &address, CPubKey &pub_key) override { - return m_wallet->GetPubKey(address, pub_key); + return m_wallet->GetLegacyScriptPubKeyMan()->GetPubKey(address, + pub_key); } bool getPrivKey(const CKeyID &address, CKey &key) override { - return m_wallet->GetKey(address, key); + return m_wallet->GetLegacyScriptPubKeyMan()->GetKey(address, key); } bool isSpendable(const CTxDestination &dest) override { - return IsMine(*m_wallet, dest) & ISMINE_SPENDABLE; + return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; } - bool haveWatchOnly() override { return m_wallet->HaveWatchOnly(); }; + bool haveWatchOnly() override { + auto spk_man = m_wallet->GetLegacyScriptPubKeyMan(); + if (spk_man) { + return spk_man->HaveWatchOnly(); + } + return false; + }; bool setAddressBook(const CTxDestination &dest, const std::string &name, const std::string &purpose) override { return m_wallet->SetAddressBook(dest, name, purpose); @@ -159,7 +166,7 @@ *name = it->second.name; } if (is_mine) { - *is_mine = IsMine(*m_wallet, dest); + *is_mine = m_wallet->IsMine(dest); } if (purpose) { *purpose = it->second.purpose; @@ -170,13 +177,14 @@ LOCK(m_wallet->cs_wallet); std::vector result; for (const auto &item : m_wallet->mapAddressBook) { - result.emplace_back(item.first, IsMine(*m_wallet, item.first), + result.emplace_back(item.first, m_wallet->IsMine(item.first), item.second.name, item.second.purpose); } return result; } void learnRelatedScripts(const CPubKey &key, OutputType type) override { - m_wallet->LearnRelatedScripts(key, type); + m_wallet->GetLegacyScriptPubKeyMan()->LearnRelatedScripts(key, + type); } bool addDestData(const CTxDestination &dest, const std::string &key, const std::string &value) override { @@ -319,7 +327,7 @@ result.balance = bal.m_mine_trusted; result.unconfirmed_balance = bal.m_mine_untrusted_pending; result.immature_balance = bal.m_mine_immature; - result.have_watch_only = m_wallet->HaveWatchOnly(); + result.have_watch_only = haveWatchOnly(); if (result.have_watch_only) { result.watch_only_balance = bal.m_watchonly_trusted; result.unconfirmed_watch_only_balance = diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -120,12 +120,14 @@ bool firstRun; wallet->LoadWallet(firstRun); { + auto spk_man = wallet->GetLegacyScriptPubKeyMan(); LOCK(wallet->cs_wallet); + AssertLockHeld(spk_man->cs_wallet); wallet->SetAddressBook( GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive"); - wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); + spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); } { auto locked_chain = wallet->chain().lock(); diff --git a/src/test/util.cpp b/src/test/util.cpp --- a/src/test/util.cpp +++ b/src/test/util.cpp @@ -32,13 +32,15 @@ } void importaddress(CWallet &wallet, const std::string &address) { + auto spk_man = wallet.GetLegacyScriptPubKeyMan(); LOCK(wallet.cs_wallet); + AssertLockHeld(spk_man->cs_wallet); const auto dest = DecodeDestination(address, wallet.chainParams); assert(IsValidDestination(dest)); const auto script = GetScriptForDestination(dest); wallet.MarkDirty(); - assert(!wallet.HaveWatchOnly(script)); - if (!wallet.AddWatchOnly(script, 0 /* nCreateTime */)) { + assert(!spk_man->HaveWatchOnly(script)); + if (!spk_man->AddWatchOnly(script, 0 /* nCreateTime */)) { assert(false); } wallet.SetAddressBook(dest, /* label */ "", "receive"); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/src/wallet/ismine.h b/src/wallet/ismine.h --- a/src/wallet/ismine.h +++ b/src/wallet/ismine.h @@ -28,9 +28,6 @@ /** used for bitflags of isminetype */ typedef uint8_t isminefilter; -isminetype IsMine(const CWallet &wallet, const CScript &scriptPubKey); -isminetype IsMine(const CWallet &wallet, const CTxDestination &dest); - /** * Cachable amount subdivided into watchonly and spendable parts. */ diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp --- a/src/wallet/psbtwallet.cpp +++ b/src/wallet/psbtwallet.cpp @@ -44,14 +44,16 @@ } complete &= - SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), + SignPSBTInput(HidingSigningProvider(pwallet->GetSigningProvider(), + !sign, !bip32derivs), psbtx, i, sighash_type); } // Fill in the bip32 keypaths and redeemscripts for the outputs so that // hardware wallets can identify change for (size_t i = 0; i < psbtx.tx->vout.size(); ++i) { - UpdatePSBTOutput(HidingSigningProvider(pwallet, true, !bip32derivs), + UpdatePSBTOutput(HidingSigningProvider(pwallet->GetSigningProvider(), + true, !bip32derivs), psbtx, i); } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -52,14 +52,14 @@ return ret.str(); } -static bool GetWalletAddressesForKey(const Config &config, - CWallet *const pwallet, - const CKeyID &keyid, std::string &strAddr, - std::string &strLabel) +static bool +GetWalletAddressesForKey(const Config &config, LegacyScriptPubKeyMan *spk_man, + CWallet *const pwallet, const CKeyID &keyid, + std::string &strAddr, std::string &strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { bool fLabelFound = false; CKey key; - pwallet->GetKey(keyid, key); + spk_man->GetKey(keyid, key); for (const auto &dest : GetAllDestinationsForKey(key.GetPubKey())) { if (pwallet->mapAddressBook.count(dest)) { if (!strAddr.empty()) { @@ -141,6 +141,12 @@ "private keys disabled"); } + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + WalletRescanReserver reserver(pwallet); bool fRescan = true; { @@ -283,6 +289,12 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + std::string strLabel; if (!request.params[1].isNull()) { strLabel = request.params[1].get_str(); @@ -533,6 +545,12 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + std::string strLabel; if (!request.params[1].isNull()) { strLabel = request.params[1].get_str(); @@ -626,6 +644,12 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + if (pwallet->chain().havePruned()) { // Exit early and print an error. // If a block is pruned after this check, we will import the key(s), @@ -824,6 +848,12 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -836,12 +866,12 @@ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } - auto keyid = GetKeyForDestination(*pwallet, dest); + auto keyid = GetKeyForDestination(*spk_man, dest); if (keyid.IsNull()) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); } CKey vchSecret; - if (!pwallet->GetKey(keyid, vchSecret)) { + if (!spk_man->GetKey(keyid, vchSecret)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); } @@ -879,8 +909,15 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); + AssertLockHeld(spk_man->cs_wallet); EnsureWalletIsUnlocked(pwallet); @@ -908,10 +945,10 @@ } std::map mapKeyBirth; - const std::map &mapKeyPool = pwallet->GetAllReserveKeys(); + const std::map &mapKeyPool = spk_man->GetAllReserveKeys(); pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth); - std::set scripts = pwallet->GetCScripts(); + std::set scripts = spk_man->GetCScripts(); // sort time/key pairs std::vector> vKeyBirth; @@ -937,10 +974,10 @@ file << "\n"; // add the base58check encoded extended master if the wallet uses HD - CKeyID seed_id = pwallet->GetHDChain().seed_id; + CKeyID seed_id = spk_man->GetHDChain().seed_id; if (!seed_id.IsNull()) { CKey seed; - if (pwallet->GetKey(seed_id, seed)) { + if (spk_man->GetKey(seed_id, seed)) { CExtKey masterKey; masterKey.SetSeed(seed.begin(), seed.size()); @@ -956,26 +993,26 @@ std::string strAddr; std::string strLabel; CKey key; - if (pwallet->GetKey(keyid, key)) { + if (spk_man->GetKey(keyid, key)) { file << strprintf("%s %s ", EncodeSecret(key), strTime); - if (GetWalletAddressesForKey(config, pwallet, keyid, strAddr, - strLabel)) { + if (GetWalletAddressesForKey(config, spk_man, pwallet, keyid, + strAddr, strLabel)) { file << strprintf("label=%s", strLabel); } else if (keyid == seed_id) { file << "hdseed=1"; } else if (mapKeyPool.count(keyid)) { file << "reserve=1"; - } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "s") { + } else if (spk_man->mapKeyMetadata[keyid].hdKeypath == "s") { file << "inactivehdseed=1"; } else { file << "change=1"; } file << strprintf( " # addr=%s%s\n", strAddr, - (pwallet->mapKeyMetadata[keyid].has_key_origin + (spk_man->mapKeyMetadata[keyid].has_key_origin ? " hdkeypath=" + WriteHDKeypath( - pwallet->mapKeyMetadata[keyid].key_origin.path) + spk_man->mapKeyMetadata[keyid].key_origin.path) : "")); } } @@ -985,11 +1022,11 @@ std::string create_time = "0"; std::string address = EncodeDestination(ScriptHash(scriptid), config); // get birth times for scripts with metadata - auto it = pwallet->m_script_metadata.find(scriptid); - if (it != pwallet->m_script_metadata.end()) { + auto it = spk_man->m_script_metadata.find(scriptid); + if (it != spk_man->m_script_metadata.end()) { create_time = FormatISO8601DateTime(it->second.nCreateTime); } - if (pwallet->GetCScript(scriptid, script)) { + if (spk_man->GetCScript(scriptid, script)) { file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time); @@ -1440,7 +1477,7 @@ // Check whether we have any work to do for (const CScript &script : script_pub_keys) { - if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) { + if (pwallet->IsMine(script) & ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private " "key for this address or script (\"" + @@ -1649,6 +1686,12 @@ RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ}); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + const UniValue &requests = mainRequest.params[0]; // Default options diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -56,7 +56,7 @@ /** * Checks if a CKey is in the given CWallet compressed or otherwise */ -bool HaveKey(const CWallet &wallet, const CKey &key) { +bool HaveKey(const SigningProvider &wallet, const CKey &key) { CKey key2; key2.Set(key.begin(), key.end(), !key.IsCompressed()); return wallet.HaveKey(key.GetPubKey().GetID()) || @@ -318,7 +318,7 @@ std::string old_label = pwallet->mapAddressBook[dest].name; std::string label = LabelFromValue(request.params[1]); - if (IsMine(*pwallet, dest)) { + if (pwallet->IsMine(dest)) { pwallet->SetAddressBook(dest, label, "receive"); } else { pwallet->SetAddressBook(dest, label, "send"); @@ -603,9 +603,11 @@ throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } + const SigningProvider *provider = pwallet->GetSigningProvider(); + CKey key; CKeyID keyID(*pkhash); - if (!pwallet->GetKey(keyID, key)) { + if (!provider->GetKey(keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } @@ -674,7 +676,7 @@ "Invalid Bitcoin address"); } CScript scriptPubKey = GetScriptForDestination(dest); - if (!IsMine(*pwallet, scriptPubKey)) { + if (!pwallet->IsMine(scriptPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } @@ -773,7 +775,7 @@ for (const CTxOut &txout : wtx.tx->vout) { CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && - IsMine(*pwallet, address) && setAddress.count(address)) { + pwallet->IsMine(address) && setAddress.count(address)) { if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) { nAmount += txout.nValue; } @@ -1112,6 +1114,12 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -1131,7 +1139,7 @@ keys_or_addrs[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str())); } else { - pubkeys.push_back(AddrToPubKey(config.GetChainParams(), pwallet, + pubkeys.push_back(AddrToPubKey(config.GetChainParams(), spk_man, keys_or_addrs[i].get_str())); } } @@ -1141,7 +1149,7 @@ // Construct using pay-to-script-hash: CScript inner; CTxDestination dest = AddAndGetMultisigDestination( - required, pubkeys, output_type, *pwallet, inner); + required, pubkeys, output_type, *spk_man, inner); pwallet->SetAddressBook(dest, label, "send"); UniValue result(UniValue::VOBJ); @@ -1219,7 +1227,7 @@ continue; } - isminefilter mine = IsMine(*pwallet, address); + isminefilter mine = pwallet->IsMine(address); if (!(mine & filter)) { continue; } @@ -1469,7 +1477,7 @@ for (const COutputEntry &s : listSent) { UniValue entry(UniValue::VOBJ); if (involvesWatchonly || - (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { + (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } MaybePushAddress(entry, s.destination); @@ -1502,7 +1510,7 @@ } UniValue entry(UniValue::VOBJ); if (involvesWatchonly || - (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { + (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } MaybePushAddress(entry, r.destination); @@ -2791,7 +2799,8 @@ } balances.pushKV("mine", balances_mine); } - if (wallet.HaveWatchOnly()) { + auto spk_man = wallet.GetLegacyScriptPubKeyMan(); + if (spk_man && spk_man->HaveWatchOnly()) { UniValue balances_watchonly{UniValue::VOBJ}; balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted)); @@ -2888,7 +2897,15 @@ obj.pushKV("txcount", (int)pwallet->mapWallet.size()); obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime()); obj.pushKV("keypoolsize", (int64_t)kpExternalSize); - CKeyID seed_id = pwallet->GetHDChain().seed_id; + + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (spk_man) { + CKeyID seed_id = spk_man->GetHDChain().seed_id; + if (!seed_id.IsNull()) { + obj.pushKV("hdseedid", seed_id.GetHex()); + } + } + if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) { obj.pushKV("keypoolsize_hd_internal", int64_t(pwallet->GetKeyPoolSize() - kpExternalSize)); @@ -2897,9 +2914,6 @@ obj.pushKV("unlocked_until", pwallet->nRelockTime); } obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK())); - if (!seed_id.IsNull()) { - obj.pushKV("hdseedid", seed_id.GetHex()); - } obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); if (pwallet->IsScanning()) { @@ -3453,11 +3467,12 @@ entry.pushKV("label", i->second.name); } + const SigningProvider *provider = pwallet->GetSigningProvider(); if (scriptPubKey.IsPayToScriptHash()) { const CScriptID &hash = CScriptID(boost::get(address)); CScript redeemScript; - if (pwallet->GetCScript(hash, redeemScript)) { + if (provider->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); } @@ -3471,7 +3486,8 @@ entry.pushKV("spendable", out.fSpendable); entry.pushKV("solvable", out.fSolvable); if (out.fSolvable) { - auto descriptor = InferDescriptor(scriptPubKey, *pwallet); + auto descriptor = InferDescriptor( + scriptPubKey, *pwallet->GetLegacyScriptPubKeyMan()); entry.pushKV("desc", descriptor->ToString()); } if (avoid_reuse) { @@ -3819,7 +3835,8 @@ // Parse the prevtxs array ParsePrevouts(request.params[1], nullptr, coins); - return SignTransaction(mtx, pwallet, coins, request.params[2]); + return SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, + request.params[2]); } UniValue rescanblockchain(const Config &config, const JSONRPCRequest &request) { @@ -3932,7 +3949,7 @@ class DescribeWalletAddressVisitor : public boost::static_visitor { public: - CWallet *const pwallet; + const SigningProvider *const provider; void ProcessSubScript(const CScript &subscript, UniValue &obj) const { // Always present: script type and redeemscript @@ -3973,8 +3990,8 @@ } } - explicit DescribeWalletAddressVisitor(CWallet *_pwallet) - : pwallet(_pwallet) {} + explicit DescribeWalletAddressVisitor(const SigningProvider *_provider) + : provider(_provider) {} UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); @@ -3984,7 +4001,7 @@ CKeyID keyID(pkhash); UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; - if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { + if (provider && provider->GetPubKey(keyID, vchPubKey)) { obj.pushKV("pubkey", HexStr(vchPubKey)); obj.pushKV("iscompressed", vchPubKey.IsCompressed()); } @@ -3995,7 +4012,7 @@ CScriptID scriptID(scripthash); UniValue obj(UniValue::VOBJ); CScript subscript; - if (pwallet && pwallet->GetCScript(scriptID, subscript)) { + if (provider && provider->GetCScript(scriptID, subscript)) { ProcessSubScript(subscript, obj); } return obj; @@ -4006,9 +4023,13 @@ const CTxDestination &dest) { UniValue ret(UniValue::VOBJ); UniValue detail = DescribeAddress(dest); + const SigningProvider *provider = nullptr; + if (pwallet) { + provider = pwallet->GetSigningProvider(); + } ret.pushKVs(detail); ret.pushKVs( - boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest)); + boost::apply_visitor(DescribeWalletAddressVisitor(provider), dest)); return ret; } @@ -4132,13 +4153,15 @@ CScript scriptPubKey = GetScriptForDestination(dest); ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + const SigningProvider *provider = pwallet->GetSigningProvider(); - isminetype mine = IsMine(*pwallet, dest); + isminetype mine = pwallet->IsMine(dest); ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); - bool solvable = IsSolvable(*pwallet, scriptPubKey); + bool solvable = IsSolvable(*provider, scriptPubKey); ret.pushKV("solvable", solvable); if (solvable) { - ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString()); + ret.pushKV("desc", + InferDescriptor(scriptPubKey, *provider)->ToString()); } ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); UniValue detail = DescribeWalletAddress(pwallet, dest); @@ -4148,7 +4171,7 @@ } ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); const CKeyMetadata *meta = nullptr; - CKeyID key_id = GetKeyForDestination(*pwallet, dest); + CKeyID key_id = GetKeyForDestination(*provider, dest); if (!key_id.IsNull()) { auto it = pwallet->mapKeyMetadata.find(key_id); if (it != pwallet->mapKeyMetadata.end()) { @@ -4342,6 +4365,12 @@ } .Check(request); + LegacyScriptPubKeyMan *spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This type of wallet does not support this command"); + } + if (pwallet->chain().isInitialBlockDownload()) { throw JSONRPCError( RPC_CLIENT_IN_INITIAL_DOWNLOAD, @@ -4374,7 +4403,7 @@ CPubKey master_pub_key; if (request.params[1].isNull()) { - master_pub_key = pwallet->GenerateNewSeed(); + master_pub_key = spk_man->GenerateNewSeed(); } else { CKey key = DecodeSecret(request.params[1].get_str()); if (!key.IsValid()) { @@ -4382,18 +4411,18 @@ "Invalid private key"); } - if (HaveKey(*pwallet, key)) { + if (HaveKey(*spk_man, key)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or " "as a loose private key)"); } - master_pub_key = pwallet->DeriveNewSeed(key); + master_pub_key = spk_man->DeriveNewSeed(key); } - pwallet->SetHDSeed(master_pub_key); + spk_man->SetHDSeed(master_pub_key); if (flush_key_pool) { - pwallet->NewKeyPool(); + spk_man->NewKeyPool(); } return NullUniValue; diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -16,6 +16,27 @@ enum class OutputType; +// Wallet storage things that ScriptPubKeyMans need in order to be able to store +// things to the wallet database. It provides access to things that are part of +// the entire wallet and not specific to a ScriptPubKeyMan such as wallet flags, +// wallet version, encryption keys, encryption status, and the database itself. +// This allows a ScriptPubKeyMan to have callbacks into CWallet without causing +// a circular dependency. WalletStorage should be the same for all +// ScriptPubKeyMans. +class WalletStorage { +public: + virtual ~WalletStorage() = default; + virtual const std::string GetDisplayName() const = 0; + virtual WalletDatabase &GetDatabase() = 0; + virtual bool IsWalletFlagSet(uint64_t) const = 0; + virtual void SetWalletFlag(uint64_t) = 0; + virtual void UnsetWalletFlagWithDB(WalletBatch &, uint64_t) = 0; + virtual bool CanSupportFeature(enum WalletFeature) const = 0; + virtual void SetMinVersion(enum WalletFeature, WalletBatch * = nullptr, + bool = false) = 0; + virtual bool IsLocked() const = 0; +}; + //! Default for -keypool static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; @@ -119,4 +140,273 @@ } }; +/* + * A class implementing ScriptPubKeyMan manages some (or all) scriptPubKeys used + * in a wallet. It contains the scripts and keys related to the scriptPubKeys it + * manages. A ScriptPubKeyMan will be able to give out scriptPubKeys to be used, + * as well as marking when a scriptPubKey has been used. It also handles when + * and how to store a scriptPubKey and its related scripts and keys, including + * encryption. + */ +class ScriptPubKeyMan { +protected: + WalletStorage &m_storage; + +public: + ScriptPubKeyMan(WalletStorage &storage) : m_storage(storage) {} +}; + +class LegacyScriptPubKeyMan : public ScriptPubKeyMan, + public FillableSigningProvider { +private: + using CryptedKeyMap = + std::map>>; + using WatchOnlySet = std::set; + using WatchKeyMap = std::map; + + //! will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial &vMasterKeyIn); + + CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); + WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore); + WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore); + + bool AddCryptedKeyInner(const CPubKey &vchPubKey, + const std::vector &vchCryptedSecret); + bool AddKeyPubKeyInner(const CKey &key, const CPubKey &pubkey); + + WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr; + + /* the HD chain data model (external chain counters) */ + CHDChain hdChain; + + /* HD derive new child key (on internal or external chain) */ + void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata, + CKey &secret, bool internal = false) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + std::set setInternalKeyPool GUARDED_BY(cs_wallet); + std::set setExternalKeyPool GUARDED_BY(cs_wallet); + std::set set_pre_split_keypool GUARDED_BY(cs_wallet); + int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; + std::map m_pool_key_to_index; + + int64_t nTimeFirstKey GUARDED_BY(cs_wallet) = 0; + + /** + * Private version of AddWatchOnly method which does not accept a + * timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if + * the watch key did not previously have a timestamp associated with it. + * Because this is an inherited virtual method, it is accessible despite + * being marked private, but it is marked private anyway to encourage use + * of the other AddWatchOnly which accepts a timestamp and sets + * nTimeFirstKey more intelligently for more efficient rescans. + */ + bool AddWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddWatchOnlyInMem(const CScript &dest); + + /** Add a KeyOriginInfo to the wallet */ + bool AddKeyOriginWithDB(WalletBatch &batch, const CPubKey &pubkey, + const KeyOriginInfo &info); + + //! Adds a key to the store, and saves it to disk. + bool AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &key, + const CPubKey &pubkey) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + //! Adds a watch-only address to the store, and saves it to disk. + bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest, + int64_t create_time) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + void AddKeypoolPubkeyWithDB(const CPubKey &pubkey, const bool internal, + WalletBatch &batch); + + //! Adds a script to the store and saves it to disk + bool AddCScriptWithDB(WalletBatch &batch, const CScript &script); + +public: + //! Fetches a key from the keypool + bool GetKeyFromPool(CPubKey &key, bool internal = false); + void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + // Map from Key ID to key metadata. + std::map mapKeyMetadata GUARDED_BY(cs_wallet); + + // Map from Script ID to key metadata (for watch-only keys). + std::map m_script_metadata GUARDED_BY(cs_wallet); + + /** + * keystore implementation + * Generate a new key + */ + CPubKey GenerateNewKey(WalletBatch &batch, bool internal = false) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Adds a key to the store, and saves it to disk. + bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) override + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Adds a key to the store, without saving it to disk (used by LoadWallet) + bool LoadKey(const CKey &key, const CPubKey &pubkey) { + return AddKeyPubKeyInner(key, pubkey); + } + //! Load metadata (used by LoadWallet) + void LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &metadata) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void LoadScriptMetadata(const CScriptID &script_id, + const CKeyMetadata &metadata) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Upgrade stored CKeyMetadata objects to store key origin info as + //! KeyOriginInfo + void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void UpdateTimeFirstKey(int64_t nCreateTime) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + //! Adds an encrypted key to the store, and saves it to disk. + bool AddCryptedKey(const CPubKey &vchPubKey, + const std::vector &vchCryptedSecret); + //! Adds an encrypted key to the store, without saving it to disk (used by + //! LoadWallet) + bool LoadCryptedKey(const CPubKey &vchPubKey, + const std::vector &vchCryptedSecret); + bool GetKey(const CKeyID &address, CKey &keyOut) const override; + bool GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const override; + bool HaveKey(const CKeyID &address) const override; + std::set GetKeys() const override; + bool AddCScript(const CScript &redeemScript) override; + bool LoadCScript(const CScript &redeemScript); + + //! Adds a watch-only address to the store, and saves it to disk. + bool AddWatchOnly(const CScript &dest, int64_t nCreateTime) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool RemoveWatchOnly(const CScript &dest) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Adds a watch-only address to the store, without saving it to disk (used + //! by LoadWallet) + bool LoadWatchOnly(const CScript &dest); + //! Returns whether the watch-only script is in the wallet + bool HaveWatchOnly(const CScript &dest) const; + //! Returns whether there are any watch-only things in the wallet + bool HaveWatchOnly() const; + //! Fetches a pubkey from mapWatchKeys if it exists there + bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const; + + bool ImportScripts(const std::set scripts, int64_t timestamp) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool ImportPrivKeys(const std::map &privkey_map, + const int64_t timestamp) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool ImportPubKeys( + const std::vector &ordered_pubkeys, + const std::map &pubkey_map, + const std::map> &key_origins, + const bool add_keypool, const bool internal, const int64_t timestamp) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool ImportScriptPubKeys(const std::string &label, + const std::set &script_pub_keys, + const bool have_solving_data, + const bool apply_label, const int64_t timestamp) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + bool NewKeyPool(); + size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool TopUpKeyPool(unsigned int kpSize = 0); + + /** + * Reserves a key from the keypool and sets nIndex to its index + * + * @param[out] nIndex the index of the key in keypool + * @param[out] keypool the keypool the key was drawn from, which could be + * the the pre-split pool if present, or the internal or external pool + * @param fRequestedInternal true if the caller would like the key drawn + * from the internal keypool, false if external is preferred + * + * @return true if succeeded, false if failed due to empty keypool + * @throws std::runtime_error if keypool read failed, key was invalid, + * was not found in the wallet, or was misclassified in the internal + * or external keypool + */ + bool ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, + bool fRequestedInternal); + void KeepKey(int64_t nIndex); + void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey); + int64_t GetOldestKeyPoolTime(); + /** + * Marks all keys in the keypool up to and including reserve_key as used. + */ + void MarkReserveKeysAsUsed(int64_t keypool_id) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + const std::map &GetAllReserveKeys() const { + return m_pool_key_to_index; + } + bool GetNewDestination(const OutputType type, const std::string label, + CTxDestination &dest, std::string &error); + + isminetype IsMine(const CScript &script) const; + + /* Set the HD chain model (chain child index counters) */ + void SetHDChain(const CHDChain &chain, bool memonly); + const CHDChain &GetHDChain() const { return hdChain; } + + /* Returns true if HD is enabled */ + bool IsHDEnabled() const; + + /* Returns true if the wallet can generate new keys */ + bool CanGenerateKeys(); + + /* Returns true if the wallet can give out new addresses. This means it has + * keys in the keypool or can generate new keys */ + bool CanGetAddresses(bool internal = false); + + /* Generates a new HD seed (will not be activated) */ + CPubKey GenerateNewSeed(); + + /* Derives a new HD seed (will not be activated) */ + CPubKey DeriveNewSeed(const CKey &key); + + /* Set the current HD seed (will reset the chain child index counters) + Sets the seed's version based on the current wallet version (so the + caller must ensure the current wallet version is correct before calling + this function). */ + void SetHDSeed(const CPubKey &key); + + /** + * Explicitly make the wallet learn the related scripts for outputs to the + * given key. This is purely to make the wallet file compatible with older + * software, as FillableSigningProvider automatically does this implicitly + * for all keys now. + */ + void LearnRelatedScripts(const CPubKey &key, OutputType); + + /** + * Same as LearnRelatedScripts, but when the OutputType is not known (and + * could be anything). + */ + void LearnAllRelatedScripts(const CPubKey &key); + + /** Implement lookup of key origin information through wallet key metadata. + */ + bool GetKeyOrigin(const CKeyID &keyid, KeyOriginInfo &info) const override; + + // Temporary CWallet accessors and aliases. + friend class CWallet; + friend class ReserveDestination; + LegacyScriptPubKeyMan(CWallet &wallet); + bool SetCrypted(); + bool IsCrypted() const; + void NotifyWatchonlyChanged(bool fHaveWatchOnly) const; + void NotifyCanGetAddressesChanged() const; + template + void WalletLogPrintf(const std::string &fmt, + const Params &... parameters) const; + CWallet &m_wallet; + RecursiveMutex &cs_wallet; + CKeyingMaterial &vMasterKey GUARDED_BY(cs_KeyStore); + std::atomic &fUseCrypto; + bool &fDecryptionThoroughlyChecked; +}; + #endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -12,8 +12,10 @@ #include #include -bool CWallet::GetNewDestination(const OutputType type, const std::string label, - CTxDestination &dest, std::string &error) { +bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, + const std::string label, + CTxDestination &dest, + std::string &error) { LOCK(cs_wallet); error.clear(); TopUpKeyPool(); @@ -27,7 +29,7 @@ LearnRelatedScripts(new_key, type); dest = GetDestinationForKey(new_key, type); - SetAddressBook(dest, label, "receive"); + m_wallet.SetAddressBook(dest, label, "receive"); return true; } @@ -58,7 +60,8 @@ INVALID = 3, //! Not spendable by anyone (P2SH inside P2SH) }; -bool HaveKeys(const std::vector &pubkeys, const CWallet &keystore) { +bool HaveKeys(const std::vector &pubkeys, + const LegacyScriptPubKeyMan &keystore) { for (const valtype &pubkey : pubkeys) { CKeyID keyID = CPubKey(pubkey).GetID(); if (!keystore.HaveKey(keyID)) { @@ -68,7 +71,8 @@ return true; } -IsMineResult IsMineInner(const CWallet &keystore, const CScript &scriptPubKey, +IsMineResult IsMineInner(const LegacyScriptPubKeyMan &keystore, + const CScript &scriptPubKey, IsMineSigVersion sigversion) { IsMineResult ret = IsMineResult::NO; @@ -135,8 +139,8 @@ } // namespace -isminetype IsMine(const CWallet &keystore, const CScript &scriptPubKey) { - switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) { +isminetype LegacyScriptPubKeyMan::IsMine(const CScript &script) const { + switch (IsMineInner(*this, script, IsMineSigVersion::TOP)) { case IsMineResult::INVALID: case IsMineResult::NO: return ISMINE_NO; @@ -187,7 +191,7 @@ return true; } -bool CWallet::EncryptKeys(CKeyingMaterial &vMasterKeyIn) { +bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial &vMasterKeyIn) { LOCK(cs_KeyStore); if (!mapCryptedKeys.empty() || IsCrypted()) { return false; @@ -211,13 +215,14 @@ return true; } -void CWallet::UpgradeKeyMetadata() { +void LegacyScriptPubKeyMan::UpgradeKeyMetadata() { AssertLockHeld(cs_wallet); - if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { + if (m_storage.IsLocked() || + m_storage.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { return; } - auto batch = std::make_unique(*database); + auto batch = std::make_unique(m_storage.GetDatabase()); for (auto &meta_pair : mapKeyMetadata) { CKeyMetadata &meta = meta_pair.second; // If the hdKeypath is "s", that's the seed and it doesn't have a key @@ -250,18 +255,18 @@ // write before setting the flag batch.reset(); - SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA); + m_storage.SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA); } -bool CWallet::IsHDEnabled() const { +bool LegacyScriptPubKeyMan::IsHDEnabled() const { return !hdChain.seed_id.IsNull(); } -bool CWallet::CanGetAddresses(bool internal) { +bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) { LOCK(cs_wallet); // Check if the keypool has keys bool keypool_has_keys; - if (internal && CanSupportFeature(FEATURE_HD_SPLIT)) { + if (internal && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { keypool_has_keys = setInternalKeyPool.size() > 0; } else { keypool_has_keys = KeypoolCountExternalKeys() > 0; @@ -290,14 +295,14 @@ return keypool.nTime; } -int64_t CWallet::GetOldestKeyPoolTime() { +int64_t LegacyScriptPubKeyMan::GetOldestKeyPoolTime() { LOCK(cs_wallet); - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); // load oldest key from keypool, get time and return int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); - if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { + if (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); if (!set_pre_split_keypool.empty()) { @@ -310,7 +315,7 @@ return oldestKey; } -size_t CWallet::KeypoolCountExternalKeys() { +size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() { AssertLockHeld(cs_wallet); return setExternalKeyPool.size() + set_pre_split_keypool.size(); } @@ -319,7 +324,7 @@ * Update wallet first key creation time. This should be called whenever keys * are added to the wallet, with the oldest key creation time. */ -void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) { +void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime) { AssertLockHeld(cs_wallet); if (nCreateTime <= 1) { // Cannot determine birthday information, so set the wallet birthday to @@ -330,17 +335,17 @@ } } -bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) { - WalletBatch batch(*database); - return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey); +bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey &secret, + const CPubKey &pubkey) { + WalletBatch batch(m_storage.GetDatabase()); + return LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(batch, secret, pubkey); } -bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &secret, - const CPubKey &pubkey) { - AssertLockHeld(cs_wallet); - +bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch &batch, + const CKey &secret, + const CPubKey &pubkey) { // Make sure we aren't adding private keys to private key disabled wallets - assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); + assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); // FillableSigningProvider has no concept of wallet databases, but calls // AddCryptedKey which is overridden below. To avoid flushes, the database @@ -377,11 +382,11 @@ mapKeyMetadata[pubkey.GetID()]); } - UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); + m_storage.UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); return true; } -bool CWallet::LoadCScript(const CScript &redeemScript) { +bool LegacyScriptPubKeyMan::LoadCScript(const CScript &redeemScript) { /** * A sanity check was added in pull #3843 to avoid adding redeemScripts that * never can be redeemed. However, old wallets may still contain these. Do @@ -401,26 +406,28 @@ return FillableSigningProvider::AddCScript(redeemScript); } -void CWallet::LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &meta) { +void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID &keyID, + const CKeyMetadata &meta) { AssertLockHeld(cs_wallet); UpdateTimeFirstKey(meta.nCreateTime); mapKeyMetadata[keyID] = meta; } -void CWallet::LoadScriptMetadata(const CScriptID &script_id, - const CKeyMetadata &meta) { +void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID &script_id, + const CKeyMetadata &meta) { AssertLockHeld(cs_wallet); UpdateTimeFirstKey(meta.nCreateTime); m_script_metadata[script_id] = meta; } -bool CWallet::AddKeyPubKeyInner(const CKey &key, const CPubKey &pubkey) { +bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey &key, + const CPubKey &pubkey) { LOCK(cs_KeyStore); if (!IsCrypted()) { return FillableSigningProvider::AddKeyPubKey(key, pubkey); } - if (IsLocked()) { + if (m_storage.IsLocked()) { return false; } @@ -437,13 +444,13 @@ return true; } -bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret) { +bool LegacyScriptPubKeyMan::LoadCryptedKey( + const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { return AddCryptedKeyInner(vchPubKey, vchCryptedSecret); } -bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret) { +bool LegacyScriptPubKeyMan::AddCryptedKeyInner( + const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { LOCK(cs_KeyStore); if (!SetCrypted()) { return false; @@ -453,8 +460,8 @@ return true; } -bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret) { +bool LegacyScriptPubKeyMan::AddCryptedKey( + const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) { return false; } @@ -465,16 +472,17 @@ vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } - return WalletBatch(*database).WriteCryptedKey( - vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); + return WalletBatch(m_storage.GetDatabase()) + .WriteCryptedKey(vchPubKey, vchCryptedSecret, + mapKeyMetadata[vchPubKey.GetID()]); } -bool CWallet::HaveWatchOnly(const CScript &dest) const { +bool LegacyScriptPubKeyMan::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); return setWatchOnly.count(dest) > 0; } -bool CWallet::HaveWatchOnly() const { +bool LegacyScriptPubKeyMan::HaveWatchOnly() const { LOCK(cs_KeyStore); return (!setWatchOnly.empty()); } @@ -485,7 +493,7 @@ (pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); } -bool CWallet::RemoveWatchOnly(const CScript &dest) { +bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest) { AssertLockHeld(cs_wallet); { LOCK(cs_KeyStore); @@ -500,14 +508,14 @@ NotifyWatchonlyChanged(false); } - return WalletBatch(*database).EraseWatchOnly(dest); + return WalletBatch(m_storage.GetDatabase()).EraseWatchOnly(dest); } -bool CWallet::LoadWatchOnly(const CScript &dest) { +bool LegacyScriptPubKeyMan::LoadWatchOnly(const CScript &dest) { return AddWatchOnlyInMem(dest); } -bool CWallet::AddWatchOnlyInMem(const CScript &dest) { +bool LegacyScriptPubKeyMan::AddWatchOnlyInMem(const CScript &dest) { LOCK(cs_KeyStore); setWatchOnly.insert(dest); CPubKey pubKey; @@ -517,7 +525,8 @@ return true; } -bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest) { +bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, + const CScript &dest) { if (!AddWatchOnlyInMem(dest)) { return false; } @@ -526,31 +535,33 @@ UpdateTimeFirstKey(meta.nCreateTime); NotifyWatchonlyChanged(true); if (batch.WriteWatchOnly(dest, meta)) { - UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); + m_storage.UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); return true; } return false; } -bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest, - int64_t create_time) { +bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, + const CScript &dest, + int64_t create_time) { m_script_metadata[CScriptID(dest)].nCreateTime = create_time; return AddWatchOnlyWithDB(batch, dest); } -bool CWallet::AddWatchOnly(const CScript &dest) { - WalletBatch batch(*database); +bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript &dest) { + WalletBatch batch(m_storage.GetDatabase()); return AddWatchOnlyWithDB(batch, dest); } -bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) { +bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript &dest, + int64_t nCreateTime) { m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; return AddWatchOnly(dest); } -void CWallet::SetHDChain(const CHDChain &chain, bool memonly) { +void LegacyScriptPubKeyMan::SetHDChain(const CHDChain &chain, bool memonly) { LOCK(cs_wallet); - if (!memonly && !WalletBatch(*database).WriteHDChain(chain)) { + if (!memonly && !WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) { throw std::runtime_error(std::string(__func__) + ": writing chain failed"); } @@ -558,7 +569,7 @@ hdChain = chain; } -bool CWallet::HaveKey(const CKeyID &address) const { +bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const { LOCK(cs_KeyStore); if (!IsCrypted()) { return FillableSigningProvider::HaveKey(address); @@ -566,7 +577,7 @@ return mapCryptedKeys.count(address) > 0; } -bool CWallet::GetKey(const CKeyID &address, CKey &keyOut) const { +bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey &keyOut) const { LOCK(cs_KeyStore); if (!IsCrypted()) { return FillableSigningProvider::GetKey(address, keyOut); @@ -581,7 +592,8 @@ return false; } -bool CWallet::GetKeyOrigin(const CKeyID &keyID, KeyOriginInfo &info) const { +bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID &keyID, + KeyOriginInfo &info) const { CKeyMetadata meta; { LOCK(cs_wallet); @@ -601,7 +613,8 @@ return true; } -bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const { +bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, + CPubKey &pubkey_out) const { LOCK(cs_KeyStore); WatchKeyMap::const_iterator it = mapWatchKeys.find(address); if (it != mapWatchKeys.end()) { @@ -611,7 +624,8 @@ return false; } -bool CWallet::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const { +bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, + CPubKey &vchPubKeyOut) const { LOCK(cs_KeyStore); if (!IsCrypted()) { if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) { @@ -630,12 +644,13 @@ return GetWatchPubKey(address, vchPubKeyOut); } -CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) { - assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); - assert(!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); +CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, + bool internal) { + assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); + assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); AssertLockHeld(cs_wallet); // default to compressed public keys if we want 0.6.0 wallets - bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); + bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); CKey secret; @@ -648,14 +663,14 @@ if (IsHDEnabled()) { DeriveNewChildKey( batch, metadata, secret, - (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); + (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); } else { secret.MakeNewKey(fCompressed); } // Compressed public keys were introduced in version 0.6.0 if (fCompressed) { - SetMinVersion(FEATURE_COMPRPUBKEY); + m_storage.SetMinVersion(FEATURE_COMPRPUBKEY); } CPubKey pubkey = secret.GetPubKey(); @@ -673,8 +688,9 @@ const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; -void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata, - CKey &secret, bool internal) { +void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, + CKeyMetadata &metadata, + CKey &secret, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k // seed (256bit) CKey seed; @@ -700,7 +716,7 @@ masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) - assert(internal ? CanSupportFeature(FEATURE_HD_SPLIT) : true); + assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true); accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT + (internal ? 1 : 0)); @@ -747,7 +763,8 @@ } } -void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { +void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, + const CKeyPool &keypool) { AssertLockHeld(cs_wallet); if (keypool.m_pre_split) { set_pre_split_keypool.insert(nIndex); @@ -768,21 +785,21 @@ } } -bool CWallet::CanGenerateKeys() { +bool LegacyScriptPubKeyMan::CanGenerateKeys() { // A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a // non-HD wallet (pre FEATURE_HD) LOCK(cs_wallet); - return IsHDEnabled() || !CanSupportFeature(FEATURE_HD); + return IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD); } -CPubKey CWallet::GenerateNewSeed() { - assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); +CPubKey LegacyScriptPubKeyMan::GenerateNewSeed() { + assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); CKey key; key.MakeNewKey(true); return DeriveNewSeed(key); } -CPubKey CWallet::DeriveNewSeed(const CKey &key) { +CPubKey LegacyScriptPubKeyMan::DeriveNewSeed(const CKey &key) { int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); @@ -809,30 +826,30 @@ return seed; } -void CWallet::SetHDSeed(const CPubKey &seed) { +void LegacyScriptPubKeyMan::SetHDSeed(const CPubKey &seed) { LOCK(cs_wallet); // Store the keyid (hash160) together with the child index counter in the // database as a hdchain object. CHDChain newHdChain; - newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) + newHdChain.nVersion = m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; newHdChain.seed_id = seed.GetID(); SetHDChain(newHdChain, false); NotifyCanGetAddressesChanged(); - UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET); + m_wallet.UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET); } /** * Mark old keypool keys as used, and generate all new keys. */ -bool CWallet::NewKeyPool() { - if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { +bool LegacyScriptPubKeyMan::NewKeyPool() { + if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { return false; } LOCK(cs_wallet); - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (const int64_t nIndex : setInternalKeyPool) { batch.ErasePool(nIndex); @@ -855,18 +872,18 @@ return false; } - WalletLogPrintf("CWallet::NewKeyPool rewrote keypool\n"); + WalletLogPrintf("LegacyScriptPubKeyMan::NewKeyPool rewrote keypool\n"); return true; } -bool CWallet::TopUpKeyPool(unsigned int kpSize) { +bool LegacyScriptPubKeyMan::TopUpKeyPool(unsigned int kpSize) { if (!CanGenerateKeys()) { return false; } { LOCK(cs_wallet); - if (IsLocked()) { + if (m_storage.IsLocked()) { return false; } @@ -887,12 +904,12 @@ int64_t missingInternal = std::max( std::max(nTargetSize, 1) - setInternalKeyPool.size(), 0); - if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { + if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { // don't create extra internal keys missingInternal = 0; } bool internal = false; - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (int64_t i = missingInternal + missingExternal; i--;) { if (i < missingInternal) { internal = true; @@ -914,8 +931,9 @@ return true; } -void CWallet::AddKeypoolPubkeyWithDB(const CPubKey &pubkey, const bool internal, - WalletBatch &batch) { +void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey &pubkey, + const bool internal, + WalletBatch &batch) { LOCK(cs_wallet); // How in the hell did you use so many keys? assert(m_max_keypool_index < std::numeric_limits::max()); @@ -932,14 +950,15 @@ m_pool_key_to_index[pubkey.GetID()] = index; } -void CWallet::KeepKey(int64_t nIndex) { +void LegacyScriptPubKeyMan::KeepKey(int64_t nIndex) { // Remove from key pool. - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); batch.ErasePool(nIndex); WalletLogPrintf("keypool keep %d\n", nIndex); } -void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { +void LegacyScriptPubKeyMan::ReturnKey(int64_t nIndex, bool fInternal, + const CPubKey &pubkey) { // Return to key pool { LOCK(cs_wallet); @@ -957,7 +976,7 @@ WalletLogPrintf("keypool return %d\n", nIndex); } -bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { +bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey &result, bool internal) { if (!CanGetAddresses(internal)) { return false; } @@ -966,11 +985,11 @@ LOCK(cs_wallet); int64_t nIndex; if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && - !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - if (IsLocked()) { + !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + if (m_storage.IsLocked()) { return false; } - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); result = GenerateNewKey(batch, internal); return true; } @@ -981,8 +1000,9 @@ return true; } -bool CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, - bool fRequestedInternal) { +bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t &nIndex, + CKeyPool &keypool, + bool fRequestedInternal) { nIndex = -1; keypool.vchPubKey = CPubKey(); { @@ -992,8 +1012,8 @@ bool fReturningInternal = fRequestedInternal; fReturningInternal &= - (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || - IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); + (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || + m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); bool use_split_keypool = set_pre_split_keypool.empty(); std::set &setKeyPool = use_split_keypool @@ -1005,7 +1025,7 @@ return false; } - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); auto it = setKeyPool.begin(); nIndex = *it; @@ -1035,15 +1055,16 @@ return true; } -void CWallet::LearnRelatedScripts(const CPubKey &key, OutputType type) { +void LegacyScriptPubKeyMan::LearnRelatedScripts(const CPubKey &key, + OutputType type) { // Nothing to do... } -void CWallet::LearnAllRelatedScripts(const CPubKey &key) { +void LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey &key) { // Nothing to do... } -void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { +void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) { AssertLockHeld(cs_wallet); bool internal = setInternalKeyPool.count(keypool_id); if (!internal) { @@ -1057,7 +1078,7 @@ : &set_pre_split_keypool); auto it = setKeyPool->begin(); - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); while (it != std::end(*setKeyPool)) { const int64_t &index = *(it); if (index > keypool_id) { @@ -1090,8 +1111,8 @@ return ret; } -void CWallet::MarkPreSplitKeys() { - WalletBatch batch(*database); +void LegacyScriptPubKeyMan::MarkPreSplitKeys() { + WalletBatch batch(m_storage.GetDatabase()); for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { int64_t index = *it; @@ -1110,25 +1131,26 @@ } } -bool CWallet::AddCScript(const CScript &redeemScript) { - WalletBatch batch(*database); +bool LegacyScriptPubKeyMan::AddCScript(const CScript &redeemScript) { + WalletBatch batch(m_storage.GetDatabase()); return AddCScriptWithDB(batch, redeemScript); } -bool CWallet::AddCScriptWithDB(WalletBatch &batch, - const CScript &redeemScript) { +bool LegacyScriptPubKeyMan::AddCScriptWithDB(WalletBatch &batch, + const CScript &redeemScript) { if (!FillableSigningProvider::AddCScript(redeemScript)) { return false; } if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { - UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); + m_storage.UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); return true; } return false; } -bool CWallet::AddKeyOriginWithDB(WalletBatch &batch, const CPubKey &pubkey, - const KeyOriginInfo &info) { +bool LegacyScriptPubKeyMan::AddKeyOriginWithDB(WalletBatch &batch, + const CPubKey &pubkey, + const KeyOriginInfo &info) { LOCK(cs_wallet); std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint); @@ -1138,9 +1160,9 @@ return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); } -bool CWallet::ImportScripts(const std::set scripts, - int64_t timestamp) { - WalletBatch batch(*database); +bool LegacyScriptPubKeyMan::ImportScripts(const std::set scripts, + int64_t timestamp) { + WalletBatch batch(m_storage.GetDatabase()); for (const auto &entry : scripts) { CScriptID id(entry); if (HaveCScript(id)) { @@ -1163,9 +1185,9 @@ return true; } -bool CWallet::ImportPrivKeys(const std::map &privkey_map, - const int64_t timestamp) { - WalletBatch batch(*database); +bool LegacyScriptPubKeyMan::ImportPrivKeys( + const std::map &privkey_map, const int64_t timestamp) { + WalletBatch batch(m_storage.GetDatabase()); for (const auto &entry : privkey_map) { const CKey &key = entry.second; CPubKey pubkey = key.GetPubKey(); @@ -1187,12 +1209,12 @@ return true; } -bool CWallet::ImportPubKeys( +bool LegacyScriptPubKeyMan::ImportPubKeys( const std::vector &ordered_pubkeys, const std::map &pubkey_map, const std::map> &key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) { - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (const auto &entry : key_origins) { AddKeyOriginWithDB(batch, entry.second.first, entry.second.second); } @@ -1223,14 +1245,13 @@ return true; } -bool CWallet::ImportScriptPubKeys(const std::string &label, - const std::set &script_pub_keys, - const bool have_solving_data, - const bool apply_label, - const int64_t timestamp) { - WalletBatch batch(*database); +bool LegacyScriptPubKeyMan::ImportScriptPubKeys( + const std::string &label, const std::set &script_pub_keys, + const bool have_solving_data, const bool apply_label, + const int64_t timestamp) { + WalletBatch batch(m_storage.GetDatabase()); for (const CScript &script : script_pub_keys) { - if (!have_solving_data || !::IsMine(*this, script)) { + if (!have_solving_data || !IsMine(script)) { // Always call AddWatchOnly for non-solvable watch-only, so that // watch timestamp gets updated if (!AddWatchOnlyWithDB(batch, script, timestamp)) { @@ -1240,13 +1261,13 @@ CTxDestination dest; ExtractDestination(script, dest); if (apply_label && IsValidDestination(dest)) { - SetAddressBookWithDB(batch, dest, label, "receive"); + m_wallet.SetAddressBookWithDB(batch, dest, label, "receive"); } } return true; } -std::set CWallet::GetKeys() const { +std::set LegacyScriptPubKeyMan::GetKeys() const { LOCK(cs_KeyStore); if (!IsCrypted()) { return FillableSigningProvider::GetKeys(); @@ -1257,3 +1278,27 @@ } return set_address; } + +// Temporary CWallet accessors and aliases. +LegacyScriptPubKeyMan::LegacyScriptPubKeyMan(CWallet &wallet) + : ScriptPubKeyMan(wallet), m_wallet(wallet), cs_wallet(wallet.cs_wallet), + vMasterKey(wallet.vMasterKey), fUseCrypto(wallet.fUseCrypto), + fDecryptionThoroughlyChecked(wallet.fDecryptionThoroughlyChecked) {} + +bool LegacyScriptPubKeyMan::SetCrypted() { + return m_wallet.SetCrypted(); +} +bool LegacyScriptPubKeyMan::IsCrypted() const { + return m_wallet.IsCrypted(); +} +void LegacyScriptPubKeyMan::NotifyWatchonlyChanged(bool fHaveWatchOnly) const { + return m_wallet.NotifyWatchonlyChanged(fHaveWatchOnly); +} +void LegacyScriptPubKeyMan::NotifyCanGetAddressesChanged() const { + return m_wallet.NotifyCanGetAddressesChanged(); +} +template +void LegacyScriptPubKeyMan::WalletLogPrintf( + const std::string &fmt, const Params &... parameters) const { + return m_wallet.WalletLogPrintf(fmt, parameters...); +} diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -43,12 +43,12 @@ scriptPubKey = GetScriptForRawPubKey(pubkeys[0]); // Keystore does not have key - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has key - keystore.AddKey(keys[0]); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -60,12 +60,13 @@ scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey); // Keystore does not have key - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has key - keystore.AddKey(uncompressedKey); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -77,12 +78,12 @@ scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0])); // Keystore does not have key - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has key - keystore.AddKey(keys[0]); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -94,12 +95,13 @@ scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey)); // Keystore does not have key - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has key - keystore.AddKey(uncompressedKey); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -113,17 +115,18 @@ scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); // Keystore does not have redeemScript or key - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has redeemScript but no key - keystore.AddCScript(redeemScript); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has redeemScript and key - keystore.AddKey(keys[0]); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -139,12 +142,14 @@ GetScriptForDestination(ScriptHash(redeemscript_inner)); scriptPubKey = GetScriptForDestination(ScriptHash(redeemscript)); - BOOST_CHECK(keystore.AddCScript(redeemscript)); - BOOST_CHECK(keystore.AddCScript(redeemscript_inner)); - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.AddKey(keys[0])); - - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript( + redeemscript_inner)); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -158,25 +163,27 @@ GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); // Keystore does not have any keys - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has 1/2 keys - keystore.AddKey(uncompressedKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has 2/2 keys - keystore.AddKey(keys[1]); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has 2/2 keys and the script - keystore.AddCScript(scriptPubKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -185,20 +192,22 @@ CWallet keystore(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); LOCK(keystore.cs_wallet); - BOOST_CHECK(keystore.AddKey(uncompressedKey)); - BOOST_CHECK(keystore.AddKey(keys[1])); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); // Keystore has no redeemScript - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has redeemScript - BOOST_CHECK(keystore.AddCScript(redeemScript)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK( + keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -207,12 +216,12 @@ CWallet keystore(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); LOCK(keystore.cs_wallet); - BOOST_CHECK(keystore.AddKey(keys[0])); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); scriptPubKey.clear(); scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -221,12 +230,12 @@ CWallet keystore(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); LOCK(keystore.cs_wallet); - BOOST_CHECK(keystore.AddKey(keys[0])); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); scriptPubKey.clear(); scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } } diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -17,6 +17,7 @@ BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup) BOOST_AUTO_TEST_CASE(psbt_updater_test) { + auto spk_man = m_wallet.GetLegacyScriptPubKeyMan(); LOCK(m_wallet.cs_wallet); // Create prevtxs and add to wallet @@ -55,7 +56,7 @@ "dae4dba2fbfef536d752ae"), SER_NETWORK, PROTOCOL_VERSION); s_rs1 >> rs1; - m_wallet.AddCScript(rs1); + spk_man->AddCScript(rs1); CScript rs2; CDataStream s_rs2( @@ -64,16 +65,16 @@ "6151926860221f0e7352ae"), SER_NETWORK, PROTOCOL_VERSION); s_rs2 >> rs2; - m_wallet.AddCScript(rs2); + spk_man->AddCScript(rs2); // Add hd seed // Mainnet and uncompressed form of // cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T CKey key = DecodeSecret("5KSSJQ7UNfFGwVgpCZDSHm5rVNhMFcFtvWM3zQ8mW4qNDEN7LFd"); - CPubKey master_pub_key = m_wallet.DeriveNewSeed(key); - m_wallet.SetHDSeed(master_pub_key); - m_wallet.NewKeyPool(); + CPubKey master_pub_key = spk_man->DeriveNewSeed(key); + spk_man->SetHDSeed(master_pub_key); + spk_man->NewKeyPool(); // Call FillPSBT PartiallySignedTransaction psbtx; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -28,8 +28,10 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) static void AddKey(CWallet &wallet, const CKey &key) { + auto spk_man = wallet.GetLegacyScriptPubKeyMan(); LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(key, key.GetPubKey()); + AssertLockHeld(spk_man->cs_wallet); + spk_man->AddKeyPubKey(key, key.GetPubKey()); } BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) { @@ -224,10 +226,12 @@ std::shared_ptr wallet = std::make_shared(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + auto spk_man = wallet->GetLegacyScriptPubKeyMan(); LOCK(wallet->cs_wallet); - wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = + AssertLockHeld(spk_man->cs_wallet); + spk_man->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; - wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); JSONRPCRequest request; request.params.setArray(); @@ -273,11 +277,13 @@ auto chain = interfaces::MakeChain(node, Params()); CWallet wallet(Params(), chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + auto spk_man = wallet.GetLegacyScriptPubKeyMan(); CWalletTx wtx(&wallet, m_coinbase_txns.back()); auto locked_chain = chain->lock(); LockAssertion lock(::cs_main); LOCK(wallet.cs_wallet); + AssertLockHeld(spk_man->cs_wallet); wtx.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 0); @@ -289,7 +295,7 @@ // Invalidate the cached value, add the key, and make sure a new immature // credit amount is calculated. wtx.MarkDirty(); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey())); BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 50 * COIN); } @@ -368,40 +374,42 @@ BOOST_CHECK_EQUAL(values[1], "val_rr1"); } -// Test some watch-only wallet methods by the procedure of loading -// (LoadWatchOnly), checking (HaveWatchOnly), getting (GetWatchPubKey) and -// removing (RemoveWatchOnly) a given PubKey, resp. its corresponding P2PK +// Test some watch-only LegacyScriptPubKeyMan methods by the procedure of +// loading (LoadWatchOnly), checking (HaveWatchOnly), getting (GetWatchPubKey) +// and removing (RemoveWatchOnly) a given PubKey, resp. its corresponding P2PK // Script. Results of the the impact on the address -> PubKey map is dependent // on whether the PubKey is a point on the curve -static void TestWatchOnlyPubKey(CWallet &wallet, const CPubKey &add_pubkey) { +static void TestWatchOnlyPubKey(LegacyScriptPubKeyMan *spk_man, + const CPubKey &add_pubkey) { CScript p2pk = GetScriptForRawPubKey(add_pubkey); CKeyID add_address = add_pubkey.GetID(); CPubKey found_pubkey; - LOCK(wallet.cs_wallet); + LOCK(spk_man->cs_wallet); // all Scripts (i.e. also all PubKeys) are added to the general watch-only // set - BOOST_CHECK(!wallet.HaveWatchOnly(p2pk)); - wallet.LoadWatchOnly(p2pk); - BOOST_CHECK(wallet.HaveWatchOnly(p2pk)); + BOOST_CHECK(!spk_man->HaveWatchOnly(p2pk)); + spk_man->LoadWatchOnly(p2pk); + BOOST_CHECK(spk_man->HaveWatchOnly(p2pk)); // only PubKeys on the curve shall be added to the watch-only address -> // PubKey map bool is_pubkey_fully_valid = add_pubkey.IsFullyValid(); if (is_pubkey_fully_valid) { - BOOST_CHECK(wallet.GetWatchPubKey(add_address, found_pubkey)); + BOOST_CHECK(spk_man->GetWatchPubKey(add_address, found_pubkey)); BOOST_CHECK(found_pubkey == add_pubkey); } else { - BOOST_CHECK(!wallet.GetWatchPubKey(add_address, found_pubkey)); + BOOST_CHECK(!spk_man->GetWatchPubKey(add_address, found_pubkey)); // passed key is unchanged BOOST_CHECK(found_pubkey == CPubKey()); } - wallet.RemoveWatchOnly(p2pk); - BOOST_CHECK(!wallet.HaveWatchOnly(p2pk)); + AssertLockHeld(spk_man->cs_wallet); + spk_man->RemoveWatchOnly(p2pk); + BOOST_CHECK(!spk_man->HaveWatchOnly(p2pk)); if (is_pubkey_fully_valid) { - BOOST_CHECK(!wallet.GetWatchPubKey(add_address, found_pubkey)); + BOOST_CHECK(!spk_man->GetWatchPubKey(add_address, found_pubkey)); // passed key is unchanged BOOST_CHECK(found_pubkey == add_pubkey); } @@ -416,36 +424,37 @@ assert(pubkey.IsValid()); } -// Test watch-only wallet logic for PubKeys +// Test watch-only logic for PubKeys BOOST_AUTO_TEST_CASE(WatchOnlyPubKeys) { CKey key; CPubKey pubkey; + LegacyScriptPubKeyMan *spk_man = m_wallet.GetLegacyScriptPubKeyMan(); - BOOST_CHECK(!m_wallet.HaveWatchOnly()); + BOOST_CHECK(!spk_man->HaveWatchOnly()); // uncompressed valid PubKey key.MakeNewKey(false); pubkey = key.GetPubKey(); assert(!pubkey.IsCompressed()); - TestWatchOnlyPubKey(m_wallet, pubkey); + TestWatchOnlyPubKey(spk_man, pubkey); // uncompressed cryptographically invalid PubKey PollutePubKey(pubkey); - TestWatchOnlyPubKey(m_wallet, pubkey); + TestWatchOnlyPubKey(spk_man, pubkey); // compressed valid PubKey key.MakeNewKey(true); pubkey = key.GetPubKey(); assert(pubkey.IsCompressed()); - TestWatchOnlyPubKey(m_wallet, pubkey); + TestWatchOnlyPubKey(spk_man, pubkey); // compressed cryptographically invalid PubKey PollutePubKey(pubkey); - TestWatchOnlyPubKey(m_wallet, pubkey); + TestWatchOnlyPubKey(spk_man, pubkey); // invalid empty PubKey pubkey = CPubKey(); - TestWatchOnlyPubKey(m_wallet, pubkey); + TestWatchOnlyPubKey(spk_man, pubkey); } class ListCoinsTestingSetup : public TestChain100Setup { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include #include @@ -138,6 +138,7 @@ protected: //! The wallet to reserve from CWallet *pwallet; + LegacyScriptPubKeyMan *m_spk_man{nullptr}; //! The index of the address's key in the keypool int64_t nIndex{-1}; //! The public key for the address @@ -622,11 +623,10 @@ class WalletRescanReserver; /** - * A CWallet is an extension of a keystore, which also maintains a set of - * transactions and balances, and provides the ability to create new - * transactions. + * A CWallet maintains a set of transactions and balances, and provides the + * ability to create new transactions. */ -class CWallet final : public FillableSigningProvider, +class CWallet final : public WalletStorage, public interfaces::Chain::Notifications { private: CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore); @@ -638,25 +638,9 @@ //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; - using CryptedKeyMap = - std::map>>; - using WatchOnlySet = std::set; - using WatchKeyMap = std::map; - bool SetCrypted(); - - //! will encrypt previously unencrypted keys - bool EncryptKeys(CKeyingMaterial &vMasterKeyIn); - bool Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys = false); - CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); - WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore); - WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore); - - bool AddCryptedKeyInner(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret); - bool AddKeyPubKeyInner(const CKey &key, const CPubKey &pubkey); std::atomic fAbortRescan{false}; // controlled by WalletRescanReserver @@ -666,8 +650,6 @@ std::mutex mutexScanning; friend class WalletRescanReserver; - WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr; - //! the current wallet version: clients below this version are not able to //! load the wallet int nWalletVersion GUARDED_BY(cs_wallet) = FEATURE_BASE; @@ -741,63 +723,14 @@ bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - /* the HD chain data model (external chain counters) */ - CHDChain hdChain; - - /* HD derive new child key (on internal or external chain) */ - void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata &metadata, - CKey &secret, bool internal = false) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - - std::set setInternalKeyPool GUARDED_BY(cs_wallet); - std::set setExternalKeyPool GUARDED_BY(cs_wallet); - std::set set_pre_split_keypool GUARDED_BY(cs_wallet); - int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; - std::map m_pool_key_to_index; std::atomic m_wallet_flags{0}; - int64_t nTimeFirstKey GUARDED_BY(cs_wallet) = 0; - - /** - * Private version of AddWatchOnly method which does not accept a timestamp, - * and which will reset the wallet's nTimeFirstKey value to 1 if the watch - * key did not previously have a timestamp associated with it. Because this - * is an inherited virtual method, it is accessible despite being marked - * private, but it is marked private anyway to encourage use of the other - * AddWatchOnly which accepts a timestamp and sets nTimeFirstKey more - * intelligently for more efficient rescans. - */ - bool AddWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool AddWatchOnlyInMem(const CScript &dest); - - /** Add a KeyOriginInfo to the wallet */ - bool AddKeyOriginWithDB(WalletBatch &batch, const CPubKey &pubkey, - const KeyOriginInfo &info); - - //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKeyWithDB(WalletBatch &batch, const CKey &key, - const CPubKey &pubkey) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - - //! Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript &dest, - int64_t create_time) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - - void AddKeypoolPubkeyWithDB(const CPubKey &pubkey, const bool internal, - WalletBatch &batch); - bool SetAddressBookWithDB(WalletBatch &batch, const CTxDestination &address, const std::string &strName, const std::string &strPurpose); - //! Adds a script to the store and saves it to disk - bool AddCScriptWithDB(WalletBatch &batch, const CScript &script); - //! Unsets a wallet flag and saves it to disk - void UnsetWalletFlagWithDB(WalletBatch &batch, uint64_t flag); + void UnsetWalletFlagWithDB(WalletBatch &batch, uint64_t flag) override; /** Interface for accessing chain state. */ interfaces::Chain *m_chain; @@ -820,9 +753,6 @@ */ BlockHash m_last_block_processed GUARDED_BY(cs_wallet); - //! Fetches a key from the keypool - bool GetKeyFromPool(CPubKey &key, bool internal = false); - public: const CChainParams &chainParams; /* @@ -836,6 +766,7 @@ * be necessary. */ WalletDatabase &GetDBHandle() { return *database; } + WalletDatabase &GetDatabase() override { return *database; } /** * Select a set of coins such that nValueRet >= nTargetValue and at least @@ -856,16 +787,6 @@ */ const std::string &GetName() const { return m_location.GetName(); } - void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - - // Map from Key ID to key metadata. - std::map mapKeyMetadata GUARDED_BY(cs_wallet); - - // Map from Script ID to key metadata (for watch-only keys). - std::map m_script_metadata GUARDED_BY(cs_wallet); - typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID = 0; @@ -886,7 +807,7 @@ } bool IsCrypted() const { return fUseCrypto; } - bool IsLocked() const; + bool IsLocked() const override; bool Lock(); /** Interface to assert chain access and if successful lock it */ @@ -920,7 +841,7 @@ //! check whether we are allowed to upgrade (or already support) to the //! named feature - bool CanSupportFeature(enum WalletFeature wf) const + bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; @@ -1001,25 +922,6 @@ return fScanningWallet ? double(m_scanning_progress) : 0; } - /** - * keystore implementation - * Generate a new key - */ - CPubKey GenerateNewKey(WalletBatch &batch, bool internal = false) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) override - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! Adds a key to the store, without saving it to disk (used by LoadWallet) - bool LoadKey(const CKey &key, const CPubKey &pubkey) { - return AddKeyPubKeyInner(key, pubkey); - } - //! Load metadata (used by LoadWallet) - void LoadKeyMetadata(const CKeyID &keyID, const CKeyMetadata &metadata) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - void LoadScriptMetadata(const CScriptID &script_id, - const CKeyMetadata &metadata) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Upgrade stored CKeyMetadata objects to store key origin info as //! KeyOriginInfo void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -1030,22 +932,6 @@ nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } - void UpdateTimeFirstKey(int64_t nCreateTime) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - - //! Adds an encrypted key to the store, and saves it to disk. - bool AddCryptedKey(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret); - //! Adds an encrypted key to the store, without saving it to disk (used by - //! LoadWallet) - bool LoadCryptedKey(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret); - bool GetKey(const CKeyID &address, CKey &keyOut) const override; - bool GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const override; - bool HaveKey(const CKeyID &address) const override; - std::set GetKeys() const override; - bool AddCScript(const CScript &redeemScript) override; - bool LoadCScript(const CScript &redeemScript); //! Adds a destination data tuple to the store, and saves it to disk bool AddDestData(const CTxDestination &dest, const std::string &key, @@ -1067,21 +953,6 @@ std::vector GetDestValues(const std::string &prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnly(const CScript &dest, int64_t nCreateTime) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool RemoveWatchOnly(const CScript &dest) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! Adds a watch-only address to the store, without saving it to disk (used - //! by LoadWallet) - bool LoadWatchOnly(const CScript &dest); - //! Returns whether the watch-only script is in the wallet - bool HaveWatchOnly(const CScript &dest) const; - //! Returns whether there are any watch-only things in the wallet - bool HaveWatchOnly() const; - //! Fetches a pubkey from mapWatchKeys if it exists there - bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const; - //! Holds a timestamp at which point the wallet is scheduled (externally) to //! be relocked. Caller must arrange for actual relocking to occur via //! Lock(). @@ -1244,37 +1115,10 @@ */ Amount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE}; - bool NewKeyPool(); size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool TopUpKeyPool(unsigned int kpSize = 0); - /** - * Reserves a key from the keypool and sets nIndex to its index - * - * @param[out] nIndex the index of the key in keypool - * @param[out] keypool the keypool the key was drawn from, which could be - * the the pre-split pool if present, or the internal or external pool - * @param fRequestedInternal true if the caller would like the key drawn - * from the internal keypool, false if external is preferred - * - * @return true if succeeded, false if failed due to empty keypool - * @throws std::runtime_error if keypool read failed, key was invalid, - * was not found in the wallet, or was misclassified in the internal - * or external keypool - */ - bool ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool, - bool fRequestedInternal); - void KeepKey(int64_t nIndex); - void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey); int64_t GetOldestKeyPoolTime(); - /** - * Marks all keys in the keypool up to and including reserve_key as used. - */ - void MarkReserveKeysAsUsed(int64_t keypool_id) - EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - const std::map &GetAllReserveKeys() const { - return m_pool_key_to_index; - } std::set> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -1288,6 +1132,8 @@ bool GetNewChangeDestination(const OutputType type, CTxDestination &dest, std::string &error); + isminetype IsMine(const CTxDestination &dest) const; + isminetype IsMine(const CScript &script) const; isminetype IsMine(const CTxIn &txin) const; /** * Returns amount of debit if the input matches the filter, otherwise @@ -1328,7 +1174,7 @@ //! signify that a particular wallet feature is now used. this may change //! nWalletVersion and nWalletMaxVersion if those are lower void SetMinVersion(enum WalletFeature, WalletBatch *batch_in = nullptr, - bool fExplicit = false); + bool fExplicit = false) override; //! change which version we're allowed to upgrade to (note that this does //! not immediately imply upgrading to that format) @@ -1431,38 +1277,15 @@ bool BackupWallet(const std::string &strDest); - /* Set the HD chain model (chain child index counters) */ - void SetHDChain(const CHDChain &chain, bool memonly); - const CHDChain &GetHDChain() const { return hdChain; } - /* Returns true if HD is enabled */ bool IsHDEnabled() const; - /* Returns true if the wallet can generate new keys */ - bool CanGenerateKeys(); - /** * Returns true if the wallet can give out new addresses. This means it has * keys in the keypool or can generate new keys. */ bool CanGetAddresses(bool internal = false); - /* Generates a new HD seed (will not be activated) */ - CPubKey GenerateNewSeed(); - - /** - * Derives a new HD seed (will not be activated) - */ - CPubKey DeriveNewSeed(const CKey &key); - - /** - * Set the current HD seed (will reset the chain child index counters) - * Sets the seed's version based on the current wallet version (so the - * caller must ensure the current wallet version is correct before calling - * this function). - */ - void SetHDSeed(const CPubKey &key); - /** * Blocks until the wallet state is up-to-date to /at least/ the current * chain at the time this function is entered. @@ -1471,24 +1294,10 @@ */ void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_main, cs_wallet); - /** - * Explicitly make the wallet learn the related scripts for outputs to the - * given key. This is purely to make the wallet file compatible with older - * software, as FillableSigningProvider automatically does this implicitly - * for all keys now. - */ - void LearnRelatedScripts(const CPubKey &key, OutputType); - - /** - * Same as LearnRelatedScripts, but when the OutputType is not known (and - * could be anything). - */ - void LearnAllRelatedScripts(const CPubKey &key); - /** * Set a single wallet flag. */ - void SetWalletFlag(uint64_t flags); + void SetWalletFlag(uint64_t flags) override; /** * Unsets a single wallet flag. @@ -1498,7 +1307,7 @@ /** * Check if a certain wallet flag is set. */ - bool IsWalletFlagSet(uint64_t flag) const; + bool IsWalletFlagSet(uint64_t flag) const override; /** * Overwrite all flags by the given uint64_t. @@ -1510,7 +1319,7 @@ * Returns a bracketed wallet name for displaying in logs, will return * [default wallet] if the wallet has no name. */ - const std::string GetDisplayName() const { + const std::string GetDisplayName() const override { std::string wallet_name = GetName().length() == 0 ? "default wallet" : GetName(); return strprintf("[%s]", wallet_name); @@ -1532,10 +1341,46 @@ parameters...); }; - /** - * Implement lookup of key origin information through wallet key metadata. - */ - bool GetKeyOrigin(const CKeyID &keyid, KeyOriginInfo &info) const override; + ScriptPubKeyMan *GetScriptPubKeyMan() const; + const SigningProvider *GetSigningProvider() const; + LegacyScriptPubKeyMan *GetLegacyScriptPubKeyMan() const; + + // Temporary LegacyScriptPubKeyMan accessors and aliases. + friend class LegacyScriptPubKeyMan; + std::unique_ptr m_spk_man = + std::make_unique(*this); + RecursiveMutex &cs_KeyStore = m_spk_man->cs_KeyStore; + LegacyScriptPubKeyMan::KeyMap & + mapKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapKeys; + LegacyScriptPubKeyMan::ScriptMap & + mapScripts GUARDED_BY(cs_KeyStore) = m_spk_man->mapScripts; + LegacyScriptPubKeyMan::CryptedKeyMap & + mapCryptedKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapCryptedKeys; + LegacyScriptPubKeyMan::WatchOnlySet & + setWatchOnly GUARDED_BY(cs_KeyStore) = m_spk_man->setWatchOnly; + LegacyScriptPubKeyMan::WatchKeyMap & + mapWatchKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapWatchKeys; + WalletBatch *& + encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch; + std::set &setInternalKeyPool GUARDED_BY(cs_wallet) = + m_spk_man->setInternalKeyPool; + std::set &setExternalKeyPool GUARDED_BY(cs_wallet) = + m_spk_man->setExternalKeyPool; + int64_t &nTimeFirstKey GUARDED_BY(cs_wallet) = m_spk_man->nTimeFirstKey; + std::map & + mapKeyMetadata GUARDED_BY(cs_wallet) = m_spk_man->mapKeyMetadata; + std::map & + m_script_metadata GUARDED_BY(cs_wallet) = m_spk_man->m_script_metadata; + void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { + AssertLockHeld(m_spk_man->cs_wallet); + m_spk_man->MarkPreSplitKeys(); + } + void MarkReserveKeysAsUsed(int64_t keypool_id) + EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { + AssertLockHeld(m_spk_man->cs_wallet); + m_spk_man->MarkReserveKeysAsUsed(keypool_id); + } + using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap; }; /** diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include @@ -231,9 +230,9 @@ } // Set a seed for the wallet - CPubKey master_pub_key = wallet->GenerateNewSeed(); - wallet->SetHDSeed(master_pub_key); - wallet->NewKeyPool(); + CPubKey master_pub_key = wallet->m_spk_man->GenerateNewSeed(); + wallet->m_spk_man->SetHDSeed(master_pub_key); + wallet->m_spk_man->NewKeyPool(); // Relock the wallet wallet->Lock(); @@ -271,6 +270,13 @@ return &(it->second); } +void CWallet::UpgradeKeyMetadata() { + AssertLockHeld(m_spk_man->cs_wallet); + if (m_spk_man) { + m_spk_man->UpgradeKeyMetadata(); + } +} + bool CWallet::Unlock(const SecureString &strWalletPassphrase, bool accept_no_keys) { CCrypter crypter; @@ -617,13 +623,16 @@ } encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey); - if (!EncryptKeys(_vMasterKey)) { - encrypted_batch->TxnAbort(); - delete encrypted_batch; - encrypted_batch = nullptr; - // We now probably have half of our keys encrypted in memory, and - // half not... die and let the user reload the unencrypted wallet. - assert(false); + if (auto spk_man = m_spk_man.get()) { + if (!spk_man->EncryptKeys(_vMasterKey)) { + encrypted_batch->TxnAbort(); + delete encrypted_batch; + encrypted_batch = nullptr; + // We now probably have half of our keys encrypted in memory, + // and half not... die and let the user reload the unencrypted + // wallet. + assert(false); + } } // Encryption was introduced in version 0.4.0 @@ -644,12 +653,12 @@ Lock(); Unlock(strWalletPassphrase); - // If we are using HD, replace the HD seed with a new one - if (IsHDEnabled()) { - SetHDSeed(GenerateNewSeed()); + // if we are using HD, replace the HD seed with a new one + if (m_spk_man->IsHDEnabled()) { + m_spk_man->SetHDSeed(m_spk_man->GenerateNewSeed()); } - NewKeyPool(); + m_spk_man->NewKeyPool(); Lock(); // Need to completely rewrite the wallet file; if we don't, bdb might @@ -751,7 +760,7 @@ CTxDestination dst; if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) { - if (::IsMine(*this, dst)) { + if (IsMine(dst)) { LOCK(cs_wallet); if (used && !GetDestData(dst, "used", nullptr)) { // p for "present", opposite of absent (null) @@ -765,7 +774,7 @@ bool CWallet::IsUsedDestination(const CTxDestination &dst) const { LOCK(cs_wallet); - return ::IsMine(*this, dst) && GetDestData(dst, "used", nullptr); + return IsMine(dst) && GetDestData(dst, "used", nullptr); } bool CWallet::IsUsedDestination(const TxId &txid, unsigned int n) const { @@ -932,16 +941,16 @@ // extract addresses and check if they match with an unused keypool // key for (const auto &keyid : - GetAffectedKeys(txout.scriptPubKey, *this)) { + GetAffectedKeys(txout.scriptPubKey, *m_spk_man)) { std::map::const_iterator mi = - m_pool_key_to_index.find(keyid); - if (mi != m_pool_key_to_index.end()) { + m_spk_man->m_pool_key_to_index.find(keyid); + if (mi != m_spk_man->m_pool_key_to_index.end()) { WalletLogPrintf("%s: Detected a used keypool key, mark all " "keypool key up to this key as used\n", __func__); MarkReserveKeysAsUsed(mi->second); - if (!TopUpKeyPool()) { + if (!m_spk_man->TopUpKeyPool()) { WalletLogPrintf( "%s: Topping up keypool failed (locked wallet)\n", __func__); @@ -1215,12 +1224,19 @@ } isminetype CWallet::IsMine(const CTxOut &txout) const { - return ::IsMine(*this, txout.scriptPubKey); + return IsMine(txout.scriptPubKey); } -isminetype IsMine(const CWallet &keystore, const CTxDestination &dest) { - CScript script = GetScriptForDestination(dest); - return IsMine(keystore, script); +isminetype CWallet::IsMine(const CTxDestination &dest) const { + return IsMine(GetScriptForDestination(dest)); +} + +isminetype CWallet::IsMine(const CScript &script) const { + isminetype result = ISMINE_NO; + if (auto spk_man = m_spk_man.get()) { + result = spk_man->IsMine(script); + } + return result; } Amount CWallet::GetCredit(const CTxOut &txout, @@ -1246,7 +1262,7 @@ // outputs are 'the send' and which are 'the change' will need to be // implemented (maybe extend CWalletTx to remember which output, if any, was // change). - if (::IsMine(*this, script)) { + if (IsMine(script)) { CTxDestination address; if (!ExtractDestination(script, address)) { return true; @@ -1351,6 +1367,24 @@ return nChange; } +bool CWallet::IsHDEnabled() const { + bool result = true; + if (auto spk_man = m_spk_man.get()) { + result &= spk_man->IsHDEnabled(); + } + return result; +} + +bool CWallet::CanGetAddresses(bool internal) { + { + auto spk_man = m_spk_man.get(); + if (spk_man && spk_man->CanGetAddresses(internal)) { + return true; + } + } + return false; +} + void CWallet::SetWalletFlag(uint64_t flags) { LOCK(cs_wallet); m_wallet_flags |= flags; @@ -1407,7 +1441,9 @@ const CScript &scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(*this, + const SigningProvider *provider = GetSigningProvider(); + + if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { @@ -1435,6 +1471,57 @@ return true; } +bool CWallet::ImportScripts(const std::set scripts, + int64_t timestamp) { + auto spk_man = GetLegacyScriptPubKeyMan(); + if (!spk_man) { + return false; + } + AssertLockHeld(spk_man->cs_wallet); + return spk_man->ImportScripts(scripts, timestamp); +} + +bool CWallet::ImportPrivKeys(const std::map &privkey_map, + const int64_t timestamp) { + auto spk_man = GetLegacyScriptPubKeyMan(); + if (!spk_man) { + return false; + } + AssertLockHeld(spk_man->cs_wallet); + return spk_man->ImportPrivKeys(privkey_map, timestamp); +} + +bool CWallet::ImportPubKeys( + const std::vector &ordered_pubkeys, + const std::map &pubkey_map, + const std::map> &key_origins, + const bool add_keypool, const bool internal, const int64_t timestamp) { + auto spk_man = GetLegacyScriptPubKeyMan(); + if (!spk_man) { + return false; + } + AssertLockHeld(spk_man->cs_wallet); + return spk_man->ImportPubKeys(ordered_pubkeys, pubkey_map, key_origins, + add_keypool, internal, timestamp); +} + +bool CWallet::ImportScriptPubKeys(const std::string &label, + const std::set &script_pub_keys, + const bool have_solving_data, + const bool apply_label, + const int64_t timestamp) { + auto spk_man = GetLegacyScriptPubKeyMan(); + if (!spk_man) { + return false; + } + AssertLockHeld(spk_man->cs_wallet); + if (!spk_man->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, + apply_label, timestamp)) { + return false; + } + return true; +} + int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) { std::vector txouts; @@ -2234,7 +2321,11 @@ continue; } - bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey); + const SigningProvider *provider = GetSigningProvider(); + + bool solvable = + provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) + : false; bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && @@ -2552,7 +2643,13 @@ const Amount amount = mi->second.tx->vout[input.prevout.GetN()].nValue; SignatureData sigdata; SigHashType sigHashType = SigHashType().withForkId(); - if (!ProduceSignature(*this, + + const SigningProvider *provider = GetSigningProvider(); + if (!provider) { + return false; + } + + if (!ProduceSignature(*provider, MutableTransactionSignatureCreator( &tx, nIn, amount, sigHashType), scriptPubKey, sigdata)) { @@ -3044,8 +3141,13 @@ const CScript &scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; + const SigningProvider *provider = GetSigningProvider(); + if (!provider) { + return false; + } + if (!ProduceSignature( - *this, + *provider, MutableTransactionSignatureCreator( &txNew, nIn, coin.txout.nValue, sigHashType), scriptPubKey, sigdata)) { @@ -3150,7 +3252,7 @@ if (database->Rewrite("\x04pool")) { setInternalKeyPool.clear(); setExternalKeyPool.clear(); - m_pool_key_to_index.clear(); + m_spk_man->m_pool_key_to_index.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3190,7 +3292,7 @@ if (database->Rewrite("\x04pool")) { setInternalKeyPool.clear(); setExternalKeyPool.clear(); - m_pool_key_to_index.clear(); + m_spk_man->m_pool_key_to_index.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3213,7 +3315,7 @@ LOCK(cs_wallet); setInternalKeyPool.clear(); setExternalKeyPool.clear(); - m_pool_key_to_index.clear(); + m_spk_man->m_pool_key_to_index.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3245,7 +3347,7 @@ } NotifyAddressBookChanged(this, address, strName, - ::IsMine(*this, address) != ISMINE_NO, strPurpose, + IsMine(address) != ISMINE_NO, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW)); if (!strPurpose.empty() && !batch.WritePurpose(address, strPurpose)) { return false; @@ -3273,20 +3375,50 @@ mapAddressBook.erase(address); } - NotifyAddressBookChanged(this, address, "", - ::IsMine(*this, address) != ISMINE_NO, "", - CT_DELETED); + NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, + "", CT_DELETED); WalletBatch(*database).ErasePurpose(address); return WalletBatch(*database).EraseName(address); } +size_t CWallet::KeypoolCountExternalKeys() { + AssertLockHeld(cs_wallet); + + unsigned int count = 0; + if (auto spk_man = m_spk_man.get()) { + AssertLockHeld(spk_man->cs_wallet); + count += spk_man->KeypoolCountExternalKeys(); + } + + return count; +} + +bool CWallet::TopUpKeyPool(unsigned int kpSize) { + bool res = true; + if (auto spk_man = m_spk_man.get()) { + res &= spk_man->TopUpKeyPool(kpSize); + } + return res; +} + +bool CWallet::GetNewDestination(const OutputType type, const std::string label, + CTxDestination &dest, std::string &error) { + error.clear(); + bool result = false; + auto spk_man = m_spk_man.get(); + if (spk_man) { + result = spk_man->GetNewDestination(type, label, dest, error); + } + return result; +} + bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination &dest, std::string &error) { error.clear(); - TopUpKeyPool(); + m_spk_man->TopUpKeyPool(); ReserveDestination reservedest(this); if (!reservedest.GetReservedDestination(type, dest, true)) { @@ -3298,6 +3430,14 @@ return true; } +int64_t CWallet::GetOldestKeyPoolTime() { + int64_t oldestKey = std::numeric_limits::max(); + if (auto spk_man = m_spk_man.get()) { + oldestKey = spk_man->GetOldestKeyPoolTime(); + } + return oldestKey; +} + std::map CWallet::GetAddressBalances(interfaces::Chain::Lock &locked_chain) { std::map balances; @@ -3466,13 +3606,18 @@ bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestination &dest, bool internal) { + m_spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (!m_spk_man) { + return false; + } + if (!pwallet->CanGetAddresses(internal)) { return false; } if (nIndex == -1) { CKeyPool keypool; - if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { + if (!m_spk_man->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { return false; } @@ -3481,7 +3626,7 @@ } assert(vchPubKey.IsValid()); - pwallet->LearnRelatedScripts(vchPubKey, type); + m_spk_man->LearnRelatedScripts(vchPubKey, type); address = GetDestinationForKey(vchPubKey, type); dest = address; return true; @@ -3489,7 +3634,7 @@ void ReserveDestination::KeepDestination() { if (nIndex != -1) { - pwallet->KeepKey(nIndex); + m_spk_man->KeepKey(nIndex); } nIndex = -1; @@ -3499,7 +3644,7 @@ void ReserveDestination::ReturnDestination() { if (nIndex != -1) { - pwallet->ReturnKey(nIndex, fInternal, vchPubKey); + m_spk_man->ReturnKey(nIndex, fInternal, vchPubKey); } nIndex = -1; vchPubKey = CPubKey(); @@ -3541,8 +3686,12 @@ AssertLockHeld(cs_wallet); mapKeyBirth.clear(); + LegacyScriptPubKeyMan *spk_man = GetLegacyScriptPubKeyMan(); + assert(spk_man != nullptr); + AssertLockHeld(spk_man->cs_wallet); + // Get birth times for keys with metadata. - for (const auto &entry : mapKeyMetadata) { + for (const auto &entry : spk_man->mapKeyMetadata) { if (entry.second.nCreateTime) { mapKeyBirth[entry.first] = entry.second.nCreateTime; } @@ -3554,7 +3703,7 @@ const int max_height = tip_height && *tip_height > 144 ? *tip_height - 144 : 0; std::map mapKeyFirstBlock; - for (const CKeyID &keyid : GetKeys()) { + for (const CKeyID &keyid : spk_man->GetKeys()) { if (mapKeyBirth.count(keyid) == 0) { mapKeyFirstBlock[keyid] = max_height; } @@ -3575,7 +3724,7 @@ for (const CTxOut &txout : wtx.tx->vout) { // Iterate over all their outputs... for (const auto &keyid : - GetAffectedKeys(txout.scriptPubKey, *this)) { + GetAffectedKeys(txout.scriptPubKey, *spk_man)) { // ... and all their affected keys. std::map::iterator rit = mapKeyFirstBlock.find(keyid); @@ -3897,13 +4046,13 @@ bool hd_upgrade = false; bool split_upgrade = false; if (walletInstance->CanSupportFeature(FEATURE_HD) && - !walletInstance->IsHDEnabled()) { + !walletInstance->m_spk_man->IsHDEnabled()) { walletInstance->WalletLogPrintf("Upgrading wallet to HD\n"); walletInstance->SetMinVersion(FEATURE_HD); // generate a new master key - CPubKey masterPubKey = walletInstance->GenerateNewSeed(); - walletInstance->SetHDSeed(masterPubKey); + CPubKey masterPubKey = walletInstance->m_spk_man->GenerateNewSeed(); + walletInstance->m_spk_man->SetHDSeed(masterPubKey); hd_upgrade = true; } // Upgrade to HD chain split if necessary @@ -3919,7 +4068,7 @@ } // Regenerate the keypool if upgraded to HD if (hd_upgrade) { - if (!walletInstance->TopUpKeyPool()) { + if (!walletInstance->m_spk_man->TopUpKeyPool()) { error = _("Unable to generate keys").translated; return nullptr; } @@ -3935,13 +4084,13 @@ if (!(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { // generate a new seed - CPubKey seed = walletInstance->GenerateNewSeed(); - walletInstance->SetHDSeed(seed); + CPubKey seed = walletInstance->m_spk_man->GenerateNewSeed(); + walletInstance->m_spk_man->SetHDSeed(seed); } // Top up the keypool - if (walletInstance->CanGenerateKeys() && - !walletInstance->TopUpKeyPool()) { + if (walletInstance->m_spk_man->CanGenerateKeys() && + !walletInstance->m_spk_man->TopUpKeyPool()) { error = _("Unable to generate initial keys").translated; return nullptr; } @@ -4339,3 +4488,15 @@ NotifyStatusChanged(this); return true; } + +ScriptPubKeyMan *CWallet::GetScriptPubKeyMan() const { + return m_spk_man.get(); +} + +const SigningProvider *CWallet::GetSigningProvider() const { + return m_spk_man.get(); +} + +LegacyScriptPubKeyMan *CWallet::GetLegacyScriptPubKeyMan() const { + return m_spk_man.get(); +} diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -212,7 +211,8 @@ static bool ReadKeyValue(CWallet *pwallet, CDataStream &ssKey, CDataStream &ssValue, CWalletScanState &wss, std::string &strType, std::string &strErr) - EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet, + pwallet->GetLegacyScriptPubKeyMan()->cs_wallet) { try { // Unserialize // Taking advantage of the fact that pair serialization is just the two @@ -278,7 +278,7 @@ char fYes; ssValue >> fYes; if (fYes == '1') { - pwallet->LoadWatchOnly(script); + pwallet->GetLegacyScriptPubKeyMan()->LoadWatchOnly(script); } } else if (strType == DBKeys::KEY) { CPubKey vchPubKey; @@ -327,11 +327,14 @@ strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } - if (!pwallet->LoadKey(key, vchPubKey)) { - strErr = "Error reading wallet database: LoadKey failed"; + if (!pwallet->GetLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey)) { + strErr = "Error reading wallet database: " + "LegacyScriptPubKeyMan::LoadKey failed"; return false; } } else if (strType == DBKeys::MASTER_KEY) { + // Master encryption key is loaded into only the wallet and not any + // of the ScriptPubKeyMans. unsigned int nID; ssKey >> nID; CMasterKey kMasterKey; @@ -357,8 +360,10 @@ ssValue >> vchPrivKey; wss.nCKeys++; - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { - strErr = "Error reading wallet database: LoadCryptedKey failed"; + if (!pwallet->GetLegacyScriptPubKeyMan()->LoadCryptedKey( + vchPubKey, vchPrivKey)) { + strErr = "Error reading wallet database: " + "LegacyScriptPubKeyMan::LoadCryptedKey failed"; return false; } wss.fIsEncrypted = true; @@ -368,15 +373,16 @@ CKeyMetadata keyMeta; ssValue >> keyMeta; wss.nKeyMeta++; - - pwallet->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); + pwallet->GetLegacyScriptPubKeyMan()->LoadKeyMetadata( + vchPubKey.GetID(), keyMeta); } else if (strType == DBKeys::WATCHMETA) { CScript script; ssKey >> script; CKeyMetadata keyMeta; ssValue >> keyMeta; wss.nKeyMeta++; - pwallet->LoadScriptMetadata(CScriptID(script), keyMeta); + pwallet->GetLegacyScriptPubKeyMan()->LoadScriptMetadata( + CScriptID(script), keyMeta); } else if (strType == DBKeys::DEFAULTKEY) { // We don't want or need the default key, but if there is one set, // we want to make sure that it is valid so that we can detect @@ -393,14 +399,15 @@ CKeyPool keypool; ssValue >> keypool; - pwallet->LoadKeyPool(nIndex, keypool); + pwallet->GetLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool); } else if (strType == DBKeys::CSCRIPT) { uint160 hash; ssKey >> hash; CScript script; ssValue >> script; - if (!pwallet->LoadCScript(script)) { - strErr = "Error reading wallet database: LoadCScript failed"; + if (!pwallet->GetLegacyScriptPubKeyMan()->LoadCScript(script)) { + strErr = "Error reading wallet database: " + "LegacyScriptPubKeyMan::LoadCScript failed"; return false; } } else if (strType == DBKeys::ORDERPOSNEXT) { @@ -416,7 +423,7 @@ } else if (strType == DBKeys::HDCHAIN) { CHDChain chain; ssValue >> chain; - pwallet->SetHDChain(chain, true); + pwallet->GetLegacyScriptPubKeyMan()->SetHDChain(chain, true); } else if (strType == DBKeys::FLAGS) { uint64_t flags; ssValue >> flags; @@ -461,6 +468,7 @@ DBErrors result = DBErrors::LOAD_OK; LOCK(pwallet->cs_wallet); + AssertLockHeld(pwallet->GetLegacyScriptPubKeyMan()->cs_wallet); try { int nMinVersion = 0; if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) { @@ -551,7 +559,10 @@ // nTimeFirstKey is only reliable if all keys have metadata if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) { - pwallet->UpdateTimeFirstKey(1); + auto spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (spk_man) { + spk_man->UpdateTimeFirstKey(1); + } } for (const TxId &txid : wss.vWalletUpgrade) { @@ -759,6 +770,7 @@ { // Required in LoadKeyMetadata(): LOCK(dummyWallet->cs_wallet); + AssertLockHeld(dummyWallet->GetLegacyScriptPubKeyMan()->cs_wallet); fReadOK = ReadKeyValue(dummyWallet, ssKey, ssValue, dummyWss, strType, strErr); } diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -42,8 +42,9 @@ wallet_instance->SetMinVersion(FEATURE_HD_SPLIT); // generate a new HD seed - CPubKey seed = wallet_instance->GenerateNewSeed(); - wallet_instance->SetHDSeed(seed); + auto spk_man = wallet_instance->GetLegacyScriptPubKeyMan(); + CPubKey seed = spk_man->GenerateNewSeed(); + spk_man->SetHDSeed(seed); tfm::format(std::cout, "Topping up keypool...\n"); wallet_instance->TopUpKeyPool(); @@ -111,7 +112,7 @@ tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no"); tfm::format(std::cout, "HD (hd seed available): %s\n", - wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes"); + wallet_instance->IsHDEnabled() ? "yes" : "no"); tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize()); tfm::format(std::cout, "Transactions: %zu\n", diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -25,7 +25,6 @@ "wallet/fees -> wallet/wallet -> wallet/fees" "wallet/rpcwallet -> wallet/wallet -> wallet/rpcwallet" "wallet/wallet -> wallet/walletdb -> wallet/wallet" - "wallet/ismine -> wallet/wallet -> wallet/ismine" "avalanche/processor -> validation -> avalanche/processor" "chainparams -> protocol -> chainparams" "chainparamsbase -> util/system -> chainparamsbase" @@ -33,6 +32,7 @@ "script/scriptcache -> validation -> script/scriptcache" "seeder/bitcoin -> seeder/db -> seeder/bitcoin" "chainparams -> protocol -> config -> chainparams" + "wallet/scriptpubkeyman -> wallet/wallet -> wallet/scriptpubkeyman" ) EXIT_CODE=0 diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py --- a/test/lint/lint-format-strings.py +++ b/test/lint/lint-format-strings.py @@ -26,6 +26,11 @@ ("src/wallet/wallet.h", "LogPrintf((\"%s \" + fmt).c_str(), GetDisplayName(), parameters...)"), ("src/logging.h", "LogPrintf(const char *fmt, const Args &... args)"), + ("src/wallet/scriptpubkeyman.h", + "WalletLogPrintf(const std::string& fmt, const Params&... parameters)"), + ("src/wallet/scriptpubkeyman.cpp", "WalletLogPrintf(fmt, parameters...)"), + ("src/wallet/scriptpubkeyman.cpp", + "WalletLogPrintf(const std::string& fmt, const Params&... parameters)"), ] FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS = [