diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/interfaces/wallet.cpp | 25 | ||||
-rw-r--r-- | src/qt/test/wallettests.cpp | 4 | ||||
-rw-r--r-- | src/test/util.cpp | 6 | ||||
-rw-r--r-- | src/wallet/init.cpp | 1 | ||||
-rw-r--r-- | src/wallet/ismine.h | 3 | ||||
-rw-r--r-- | src/wallet/psbtwallet.cpp | 4 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 66 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 96 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.cpp | 240 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.h | 240 | ||||
-rw-r--r-- | src/wallet/test/ismine_tests.cpp | 168 | ||||
-rw-r--r-- | src/wallet/test/psbt_wallet_tests.cpp | 13 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 54 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 260 | ||||
-rw-r--r-- | src/wallet/wallet.h | 209 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 39 | ||||
-rw-r--r-- | src/wallet/wallettool.cpp | 7 |
17 files changed, 896 insertions, 539 deletions
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 9b0a8b64c9..6bccde1b8f 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -46,7 +46,7 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co result.txout_is_mine.emplace_back(wallet.IsMine(txout)); result.txout_address.emplace_back(); 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); @@ -117,10 +117,17 @@ public: std::string error; return m_wallet->GetNewDestination(type, label, dest, error); } - bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetPubKey(address, pub_key); } - bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetKey(address, key); } - bool isSpendable(const CTxDestination& dest) override { return IsMine(*m_wallet, dest) & ISMINE_SPENDABLE; } - bool haveWatchOnly() override { return m_wallet->HaveWatchOnly(); }; + bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetPubKey(address, pub_key); } + bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetKey(address, key); } + bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; } + 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); @@ -143,7 +150,7 @@ public: *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; @@ -155,11 +162,11 @@ public: LOCK(m_wallet->cs_wallet); std::vector<WalletAddress> result; for (const auto& item : m_wallet->mapAddressBook) { - result.emplace_back(item.first, IsMine(*m_wallet, item.first), item.second.name, item.second.purpose); + 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); } + void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet->GetLegacyScriptPubKeyMan()->LearnRelatedScripts(key, type); } bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override { LOCK(m_wallet->cs_wallet); @@ -342,7 +349,7 @@ public: 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 = bal.m_watchonly_untrusted_pending; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index eea874c0d4..379bd8af88 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -138,9 +138,11 @@ void TestGUI() 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 index b7bb6deeaa..ed031270f2 100644 --- a/src/test/util.cpp +++ b/src/test/util.cpp @@ -32,13 +32,15 @@ std::string getnewaddress(CWallet& w) 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); assert(IsValidDestination(dest)); const auto script = GetScriptForDestination(dest); wallet.MarkDirty(); - assert(!wallet.HaveWatchOnly(script)); - if (!wallet.AddWatchOnly(script, 0 /* nCreateTime */)) assert(false); + assert(!spk_man->HaveWatchOnly(script)); + if (!spk_man->AddWatchOnly(script, 0 /* nCreateTime */)) assert(false); wallet.SetAddressBook(dest, /* label */ "", "receive"); } #endif // ENABLE_WALLET diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index c622318894..3657a157b6 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -10,7 +10,6 @@ #include <util/moneystr.h> #include <util/system.h> #include <util/translation.h> -#include <wallet/scriptpubkeyman.h> #include <wallet/wallet.h> #include <walletinitinterface.h> diff --git a/src/wallet/ismine.h b/src/wallet/ismine.h index 41555fcb93..0bc6c90354 100644 --- a/src/wallet/ismine.h +++ b/src/wallet/ismine.h @@ -28,9 +28,6 @@ enum isminetype : unsigned int /** 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 index 721a244afb..aa13cacca4 100644 --- a/src/wallet/psbtwallet.cpp +++ b/src/wallet/psbtwallet.cpp @@ -39,12 +39,12 @@ TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& ps return TransactionError::SIGHASH_MISMATCH; } - complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type); + complete &= 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 (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { - UpdatePSBTOutput(HidingSigningProvider(pwallet, true, !bip32derivs), psbtx, i); + UpdatePSBTOutput(HidingSigningProvider(pwallet->GetSigningProvider(), true, !bip32derivs), psbtx, i); } return TransactionError::OK; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 1cd4cb93b4..0176dc7cda 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -67,11 +67,11 @@ static std::string DecodeDumpString(const std::string &str) { return ret.str(); } -static bool GetWalletAddressesForKey(CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +static bool GetWalletAddressesForKey(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()) { @@ -138,6 +138,11 @@ UniValue importprivkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with 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; { @@ -264,6 +269,10 @@ UniValue importaddress(const JSONRPCRequest& request) }, }.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()) @@ -466,6 +475,10 @@ UniValue importpubkey(const JSONRPCRequest& request) }, }.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()) @@ -549,6 +562,11 @@ UniValue importwallet(const JSONRPCRequest& request) }, }.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), @@ -706,6 +724,11 @@ UniValue dumpprivkey(const JSONRPCRequest& request) }, }.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); @@ -716,12 +739,12 @@ UniValue dumpprivkey(const JSONRPCRequest& request) if (!IsValidDestination(dest)) { 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"); } return EncodeSecret(vchSecret); @@ -755,8 +778,14 @@ UniValue dumpwallet(const JSONRPCRequest& request) }, }.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); @@ -778,10 +807,10 @@ UniValue dumpwallet(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); std::map<CKeyID, int64_t> mapKeyBirth; - const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys(); + const std::map<CKeyID, int64_t>& mapKeyPool = spk_man->GetAllReserveKeys(); pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth); - std::set<CScriptID> scripts = pwallet->GetCScripts(); + std::set<CScriptID> scripts = spk_man->GetCScripts(); // sort time/key pairs std::vector<std::pair<int64_t, CKeyID> > vKeyBirth; @@ -800,11 +829,11 @@ UniValue dumpwallet(const JSONRPCRequest& request) 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()); @@ -817,20 +846,20 @@ UniValue dumpwallet(const JSONRPCRequest& request) 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(pwallet, keyid, strAddr, strLabel)) { + if (GetWalletAddressesForKey(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 ? " hdkeypath="+WriteHDKeypath(pwallet->mapKeyMetadata[keyid].key_origin.path) : "")); + file << strprintf(" # addr=%s%s\n", strAddr, (spk_man->mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(spk_man->mapKeyMetadata[keyid].key_origin.path) : "")); } } file << "\n"; @@ -839,11 +868,11 @@ UniValue dumpwallet(const JSONRPCRequest& request) std::string create_time = "0"; std::string address = EncodeDestination(ScriptHash(scriptid)); // 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); file << strprintf(" # addr=%s\n", address); } @@ -1219,7 +1248,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con // 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 (\"" + HexStr(script.begin(), script.end()) + "\")"); } } @@ -1339,6 +1368,11 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) 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 index e571501221..7701767bc2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -68,7 +68,7 @@ static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWall /** 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()); @@ -303,7 +303,7 @@ static UniValue setlabel(const JSONRPCRequest& request) 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"); @@ -550,9 +550,11 @@ static UniValue signmessage(const JSONRPCRequest& request) 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"); } @@ -610,7 +612,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "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"); } @@ -694,7 +696,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) for (const CTxOut& txout : wtx.tx->vout) { CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) { + if (ExtractDestination(txout.scriptPubKey, address) && pwallet->IsMine(address) && setAddress.count(address)) { if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) nAmount += txout.nValue; } @@ -964,6 +966,11 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) }, }.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); @@ -980,7 +987,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str())); } else { - pubkeys.push_back(AddrToPubKey(pwallet, keys_or_addrs[i].get_str())); + pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str())); } } @@ -993,7 +1000,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) // Construct using pay-to-script-hash: CScript inner; - CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, *pwallet, inner); + CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, *spk_man, inner); pwallet->SetAddressBook(dest, label, "send"); UniValue result(UniValue::VOBJ); @@ -1064,7 +1071,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co continue; } - isminefilter mine = IsMine(*pwallet, address); + isminefilter mine = pwallet->IsMine(address); if(!(mine & filter)) continue; @@ -1288,7 +1295,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con for (const COutputEntry& s : listSent) { UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { + if (involvesWatchonly || (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } MaybePushAddress(entry, s.destination); @@ -1319,7 +1326,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con continue; } UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { + if (involvesWatchonly || (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } MaybePushAddress(entry, r.destination); @@ -2379,7 +2386,8 @@ static UniValue getbalances(const JSONRPCRequest& request) } 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)); balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending)); @@ -2449,7 +2457,15 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) 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)); } @@ -2457,9 +2473,6 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) 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)); obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)); if (pwallet->IsScanning()) { @@ -2920,10 +2933,11 @@ static UniValue listunspent(const JSONRPCRequest& request) entry.pushKV("label", i->second.name); } + const SigningProvider* provider = pwallet->GetSigningProvider(); if (scriptPubKey.IsPayToScriptHash()) { const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address)); CScript redeemScript; - if (pwallet->GetCScript(hash, redeemScript)) { + if (provider->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); // Now check if the redeemScript is actually a P2WSH script CTxDestination witness_destination; @@ -2935,7 +2949,7 @@ static UniValue listunspent(const JSONRPCRequest& request) CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; - if (pwallet->GetCScript(id, witnessScript)) { + if (provider->GetCScript(id, witnessScript)) { entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); } } @@ -2945,7 +2959,7 @@ static UniValue listunspent(const JSONRPCRequest& request) CScriptID id; CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); CScript witnessScript; - if (pwallet->GetCScript(id, witnessScript)) { + if (provider->GetCScript(id, witnessScript)) { entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); } } @@ -2957,7 +2971,7 @@ static UniValue listunspent(const JSONRPCRequest& request) 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) entry.pushKV("reused", reused); @@ -3267,7 +3281,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) // 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]); } static UniValue bumpfee(const JSONRPCRequest& request) @@ -3539,7 +3553,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request) class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> { public: - CWallet * const pwallet; + const SigningProvider * const provider; void ProcessSubScript(const CScript& subscript, UniValue& obj) const { @@ -3575,7 +3589,7 @@ public: } } - explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {} + explicit DescribeWalletAddressVisitor(const SigningProvider* _provider) : provider(_provider) {} UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); } @@ -3584,7 +3598,7 @@ public: 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()); } @@ -3596,7 +3610,7 @@ public: 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; @@ -3606,7 +3620,7 @@ public: { UniValue obj(UniValue::VOBJ); CPubKey pubkey; - if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + if (provider && provider->GetPubKey(CKeyID(id), pubkey)) { obj.pushKV("pubkey", HexStr(pubkey)); } return obj; @@ -3619,7 +3633,7 @@ public: CRIPEMD160 hasher; uint160 hash; hasher.Write(id.begin(), 32).Finalize(hash.begin()); - if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + if (provider && provider->GetCScript(CScriptID(hash), subscript)) { ProcessSubScript(subscript, obj); } return obj; @@ -3632,8 +3646,12 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de { 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)); + ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider), dest)); return ret; } @@ -3722,13 +3740,14 @@ UniValue getaddressinfo(const JSONRPCRequest& request) 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); @@ -3738,7 +3757,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request) } 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()) { @@ -3916,6 +3935,11 @@ UniValue sethdseed(const JSONRPCRequest& request) }, }.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, "Cannot set a new HD seed while still in Initial Block Download"); } @@ -3941,22 +3965,22 @@ UniValue sethdseed(const JSONRPCRequest& request) 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()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "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); - if (flush_key_pool) pwallet->NewKeyPool(); + spk_man->SetHDSeed(master_pub_key); + if (flush_key_pool) spk_man->NewKeyPool(); return NullUniValue; } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 77ee8f97cb..c13fddfaf3 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -11,11 +11,10 @@ #include <wallet/scriptpubkeyman.h> #include <wallet/wallet.h> -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(); // Generate a new key that is added to wallet @@ -27,7 +26,7 @@ bool CWallet::GetNewDestination(const OutputType type, const std::string label, LearnRelatedScripts(new_key, type); dest = GetDestinationForKey(new_key, type); - SetAddressBook(dest, label, "receive"); + m_wallet.SetAddressBook(dest, label, "receive"); return true; } @@ -66,7 +65,7 @@ bool PermitsUncompressed(IsMineSigVersion sigversion) return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH; } -bool HaveKeys(const std::vector<valtype>& pubkeys, const CWallet& keystore) +bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan& keystore) { for (const valtype& pubkey : pubkeys) { CKeyID keyID = CPubKey(pubkey).GetID(); @@ -75,7 +74,7 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const CWallet& keystore) return true; } -IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion) +IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion) { IsMineResult ret = IsMineResult::NO; @@ -192,9 +191,9 @@ IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, I } // namespace -isminetype IsMine(const CWallet& keystore, const CScript& scriptPubKey) +isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const { - switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) { + switch (IsMineInner(*this, script, IsMineSigVersion::TOP)) { case IsMineResult::INVALID: case IsMineResult::NO: return ISMINE_NO; @@ -244,7 +243,7 @@ bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys) return true; } -bool CWallet::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial& vMasterKeyIn) { LOCK(cs_KeyStore); if (!mapCryptedKeys.empty() || IsCrypted()) @@ -266,14 +265,14 @@ bool CWallet::EncryptKeys(CKeyingMaterial& vMasterKeyIn) 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; } - std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(*database); + std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(m_storage.GetDatabase()); for (auto& meta_pair : mapKeyMetadata) { CKeyMetadata& meta = meta_pair.second; if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin @@ -300,20 +299,20 @@ void CWallet::UpgradeKeyMetadata() } } batch.reset(); //write before setting the flag - 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; @@ -339,15 +338,15 @@ static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, Walle 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()) { oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey); @@ -357,7 +356,7 @@ int64_t CWallet::GetOldestKeyPoolTime() return oldestKey; } -size_t CWallet::KeypoolCountExternalKeys() +size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() { AssertLockHeld(cs_wallet); return setExternalKeyPool.size() + set_pre_split_keypool.size(); @@ -367,7 +366,7 @@ size_t CWallet::KeypoolCountExternalKeys() * 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) { @@ -379,18 +378,16 @@ void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) } } -bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { - WalletBatch batch(*database); - return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey); + WalletBatch batch(m_storage.GetDatabase()); + return LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(batch, secret, pubkey); } -bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey) +bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey) { - AssertLockHeld(cs_wallet); - // 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 handle is @@ -421,11 +418,11 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C secret.GetPrivKey(), 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 @@ -440,28 +437,28 @@ bool CWallet::LoadCScript(const CScript& redeemScript) 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; } @@ -477,12 +474,12 @@ bool CWallet::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) return true; } -bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) +bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return AddCryptedKeyInner(vchPubKey, vchCryptedSecret); } -bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) +bool LegacyScriptPubKeyMan::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { LOCK(cs_KeyStore); if (!SetCrypted()) { @@ -494,7 +491,7 @@ bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<uns return true; } -bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, +bool LegacyScriptPubKeyMan::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) @@ -506,19 +503,19 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); else - return WalletBatch(*database).WriteCryptedKey(vchPubKey, + 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()); @@ -531,7 +528,7 @@ static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) (pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); } -bool CWallet::RemoveWatchOnly(const CScript &dest) +bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest) { AssertLockHeld(cs_wallet); { @@ -547,18 +544,18 @@ bool CWallet::RemoveWatchOnly(const CScript &dest) if (!HaveWatchOnly()) NotifyWatchonlyChanged(false); - if (!WalletBatch(*database).EraseWatchOnly(dest)) + if (!WalletBatch(m_storage.GetDatabase()).EraseWatchOnly(dest)) return false; return true; } -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); @@ -570,7 +567,7 @@ bool CWallet::AddWatchOnlyInMem(const CScript &dest) return true; } -bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) +bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) { if (!AddWatchOnlyInMem(dest)) return false; @@ -578,40 +575,40 @@ bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) 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) +bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest) { - WalletBatch batch(*database); + 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"); hdChain = chain; } -bool CWallet::HaveKey(const CKeyID &address) const +bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const { LOCK(cs_KeyStore); if (!IsCrypted()) { @@ -620,7 +617,7 @@ bool CWallet::HaveKey(const CKeyID &address) const 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()) { @@ -637,7 +634,7 @@ bool CWallet::GetKey(const CKeyID &address, CKey& keyOut) const return false; } -bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const +bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const { CKeyMetadata meta; { @@ -656,7 +653,7 @@ bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const 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); @@ -667,7 +664,7 @@ bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const 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()) { @@ -687,12 +684,12 @@ bool CWallet::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const return GetWatchPubKey(address, vchPubKeyOut); } -CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) +CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, bool internal) { - assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); - assert(!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); + assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); + assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); AssertLockHeld(cs_wallet); - bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets CKey secret; @@ -702,14 +699,14 @@ CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) // use HD key derivation if HD was enabled during wallet creation and a seed is present if (IsHDEnabled()) { - DeriveNewChildKey(batch, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); + DeriveNewChildKey(batch, metadata, secret, (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(); @@ -726,7 +723,7 @@ CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) 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 CKey seed; //seed (256bit) @@ -746,7 +743,7 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey 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)); // derive child key at next index, skip keys already known to the wallet @@ -781,7 +778,7 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } -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) { @@ -802,22 +799,22 @@ void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); } -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() +CPubKey LegacyScriptPubKeyMan::GenerateNewSeed() { - assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); + 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); @@ -845,32 +842,32 @@ CPubKey CWallet::DeriveNewSeed(const CKey& key) 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) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; + 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() +bool LegacyScriptPubKeyMan::NewKeyPool() { - if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + 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); @@ -892,12 +889,12 @@ bool CWallet::NewKeyPool() if (!TopUpKeyPool()) { 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; @@ -905,7 +902,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) { LOCK(cs_wallet); - if (IsLocked()) return false; + if (m_storage.IsLocked()) return false; // Top up key pool unsigned int nTargetSize; @@ -919,13 +916,13 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0); int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 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) { @@ -943,7 +940,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) 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); assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys? @@ -959,15 +956,15 @@ void CWallet::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, 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 { @@ -985,7 +982,7 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) 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; @@ -995,9 +992,9 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) { LOCK(cs_wallet); int64_t nIndex; - if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - if (IsLocked()) return false; - WalletBatch batch(*database); + if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { + if (m_storage.IsLocked()) return false; + WalletBatch batch(m_storage.GetDatabase()); result = GenerateNewKey(batch, internal); return true; } @@ -1007,7 +1004,7 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) 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(); @@ -1017,7 +1014,7 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe TopUpKeyPool(); bool fReturningInternal = fRequestedInternal; - fReturningInternal &= (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); + fReturningInternal &= (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<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; @@ -1026,7 +1023,7 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe return false; } - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); auto it = setKeyPool.begin(); nIndex = *it; @@ -1053,7 +1050,7 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe return true; } -void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type) +void LegacyScriptPubKeyMan::LearnRelatedScripts(const CPubKey& key, OutputType type) { if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) { CTxDestination witdest = WitnessV0KeyHash(key.GetID()); @@ -1064,13 +1061,13 @@ void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type) } } -void CWallet::LearnAllRelatedScripts(const CPubKey& key) +void LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey& key) { // OutputType::P2SH_SEGWIT always adds all necessary scripts for all types. LearnRelatedScripts(key, OutputType::P2SH_SEGWIT); } -void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) +void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) { AssertLockHeld(cs_wallet); bool internal = setInternalKeyPool.count(keypool_id); @@ -1078,7 +1075,7 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &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) break; // set*KeyPool is ordered @@ -1106,9 +1103,9 @@ std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& p return ret; } -void CWallet::MarkPreSplitKeys() +void LegacyScriptPubKeyMan::MarkPreSplitKeys() { - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { int64_t index = *it; CKeyPool keypool; @@ -1124,24 +1121,24 @@ void CWallet::MarkPreSplitKeys() } } -bool CWallet::AddCScript(const CScript& redeemScript) +bool LegacyScriptPubKeyMan::AddCScript(const CScript& redeemScript) { - WalletBatch batch(*database); + 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); @@ -1151,9 +1148,9 @@ bool CWallet::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, cons return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); } -bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp) +bool LegacyScriptPubKeyMan::ImportScripts(const std::set<CScript> scripts, int64_t timestamp) { - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (const auto& entry : scripts) { CScriptID id(entry); if (HaveCScript(id)) { @@ -1175,9 +1172,9 @@ bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp) return true; } -bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) +bool LegacyScriptPubKeyMan::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) { - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (const auto& entry : privkey_map) { const CKey& key = entry.second; CPubKey pubkey = key.GetPubKey(); @@ -1198,9 +1195,9 @@ bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const in return true; } -bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) +bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& 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); } @@ -1230,11 +1227,11 @@ bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const st return true; } -bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) +bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) { - WalletBatch batch(*database); + WalletBatch batch(m_storage.GetDatabase()); for (const CScript& script : script_pub_keys) { - if (!have_solving_data || !::IsMine(*this, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated + 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)) { return false; } @@ -1242,13 +1239,13 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri 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<CKeyID> CWallet::GetKeys() const +std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const { LOCK(cs_KeyStore); if (!IsCrypted()) { @@ -1260,3 +1257,18 @@ std::set<CKeyID> CWallet::GetKeys() const } 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<typename... Params> void LegacyScriptPubKeyMan::WalletLogPrintf(const std::string& fmt, const Params&... parameters) const { return m_wallet.WalletLogPrintf(fmt, parameters...); } diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index f151cec444..55184098b7 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -16,6 +16,25 @@ 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; @@ -112,4 +131,225 @@ public: } }; +/* + * 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<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>; + using WatchOnlySet = std::set<CScript>; + using WatchKeyMap = std::map<CKeyID, CPubKey>; + + //! 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<unsigned char> &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<int64_t> setInternalKeyPool GUARDED_BY(cs_wallet); + std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_wallet); + std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_wallet); + int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; + std::map<CKeyID, int64_t> 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<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_wallet); + + // Map from Script ID to key metadata (for watch-only keys). + std::map<CScriptID, CKeyMetadata> 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<unsigned char> &vchCryptedSecret); + //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) + bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &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<CKeyID> 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<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& 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<CScript>& 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<CKeyID, int64_t>& 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<typename... Params> void WalletLogPrintf(const std::string& fmt, const Params&... parameters) const; + CWallet& m_wallet; + CCriticalSection& cs_wallet; + CKeyingMaterial& vMasterKey GUARDED_BY(cs_KeyStore); + std::atomic<bool>& fUseCrypto; + bool& fDecryptionThoroughlyChecked; +}; + #endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp index 062fef7748..12355e300a 100644 --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -38,12 +38,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) 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 - BOOST_CHECK(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); } @@ -54,12 +54,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) 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 - BOOST_CHECK(keystore.AddKey(uncompressedKey)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -70,12 +70,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) 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 - BOOST_CHECK(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); } @@ -86,12 +86,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) 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 - BOOST_CHECK(keystore.AddKey(uncompressedKey)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -104,17 +104,17 @@ BOOST_AUTO_TEST_CASE(ismine_standard) 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 - 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_NO); // Keystore has redeemScript and key - BOOST_CHECK(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); } @@ -127,11 +127,11 @@ BOOST_AUTO_TEST_CASE(ismine_standard) CScript redeemscript = 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); } @@ -144,11 +144,11 @@ BOOST_AUTO_TEST_CASE(ismine_standard) CScript witnessscript = GetScriptForDestination(ScriptHash(redeemscript)); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); - BOOST_CHECK(keystore.AddCScript(witnessscript)); - BOOST_CHECK(keystore.AddCScript(redeemscript)); - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.AddKey(keys[0])); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -160,10 +160,10 @@ BOOST_AUTO_TEST_CASE(ismine_standard) CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0]))); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); - BOOST_CHECK(keystore.AddCScript(witnessscript)); - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.AddKey(keys[0])); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -176,11 +176,11 @@ BOOST_AUTO_TEST_CASE(ismine_standard) CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner)); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); - BOOST_CHECK(keystore.AddCScript(witnessscript_inner)); - BOOST_CHECK(keystore.AddCScript(witnessscript)); - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.AddKey(keys[0])); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript_inner)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -188,13 +188,13 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); LOCK(keystore.cs_wallet); - BOOST_CHECK(keystore.AddKey(keys[0])); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0]))); // Keystore implicitly has key and P2SH redeemScript - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -202,17 +202,17 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); LOCK(keystore.cs_wallet); - BOOST_CHECK(keystore.AddKey(uncompressedKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(uncompressedPubkey))); // Keystore has key, but no P2SH redeemScript - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has key and P2SH redeemScript - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -224,25 +224,25 @@ BOOST_AUTO_TEST_CASE(ismine_standard) scriptPubKey = 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 - BOOST_CHECK(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 - BOOST_CHECK(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 - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -250,19 +250,19 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(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); } @@ -270,24 +270,24 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); LOCK(keystore.cs_wallet); - BOOST_CHECK(keystore.AddKey(keys[0])); - BOOST_CHECK(keystore.AddKey(keys[1])); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]}); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); // Keystore has keys, but no witnessScript or P2SH redeemScript - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has keys and witnessScript, but no P2SH redeemScript - BOOST_CHECK(keystore.AddCScript(witnessScript)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has keys, witnessScript, P2SH redeemScript - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -295,24 +295,24 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(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 witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); // Keystore has keys, but no witnessScript or P2SH redeemScript - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has keys and witnessScript, but no P2SH redeemScript - BOOST_CHECK(keystore.AddCScript(witnessScript)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has keys, witnessScript, P2SH redeemScript - BOOST_CHECK(keystore.AddCScript(scriptPubKey)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -326,19 +326,19 @@ BOOST_AUTO_TEST_CASE(ismine_standard) scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); // Keystore has no witnessScript, P2SH redeemScript, or keys - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has witnessScript and P2SH redeemScript, but no keys - BOOST_CHECK(keystore.AddCScript(redeemScript)); - BOOST_CHECK(keystore.AddCScript(witnessScript)); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript)); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript)); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); // Keystore has keys, witnessScript, P2SH redeemScript - BOOST_CHECK(keystore.AddKey(keys[0])); - BOOST_CHECK(keystore.AddKey(keys[1])); - result = IsMine(keystore, scriptPubKey); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); + BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } @@ -346,12 +346,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(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); } @@ -359,12 +359,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(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_0 << ToByteVector(ParseHex("aabb")); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -372,12 +372,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(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_16 << ToByteVector(ParseHex("aabb")); - result = IsMine(keystore, scriptPubKey); + result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_NO); } @@ -385,12 +385,12 @@ BOOST_AUTO_TEST_CASE(ismine_standard) { CWallet keystore(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 index 0400f1207c..27a64ff12f 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -16,6 +16,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 @@ -35,23 +36,23 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test) CScript rs1; CDataStream s_rs1(ParseHex("475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae"), SER_NETWORK, PROTOCOL_VERSION); s_rs1 >> rs1; - m_wallet.AddCScript(rs1); + spk_man->AddCScript(rs1); CScript rs2; CDataStream s_rs2(ParseHex("2200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"), SER_NETWORK, PROTOCOL_VERSION); s_rs2 >> rs2; - m_wallet.AddCScript(rs2); + spk_man->AddCScript(rs2); CScript ws1; CDataStream s_ws1(ParseHex("47522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"), SER_NETWORK, PROTOCOL_VERSION); s_ws1 >> ws1; - m_wallet.AddCScript(ws1); + spk_man->AddCScript(ws1); // Add hd seed CKey key = DecodeSecret("5KSSJQ7UNfFGwVgpCZDSHm5rVNhMFcFtvWM3zQ8mW4qNDEN7LFd"); // Mainnet and uncompressed form of cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T - 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 index a2b2a7b227..ca4b932915 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -27,8 +27,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) @@ -194,9 +196,11 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // Import key into wallet and call dumpwallet to create backup file. { std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy()); + auto spk_man = wallet->GetLegacyScriptPubKeyMan(); LOCK(wallet->cs_wallet); - wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; - wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AssertLockHeld(spk_man->cs_wallet); + spk_man->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; + spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); JSONRPCRequest request; request.params.setArray(); @@ -242,11 +246,13 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) auto chain = interfaces::MakeChain(); CWallet wallet(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); @@ -254,10 +260,10 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) // cache the current immature credit amount, which is 0. BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 0); - // Invalidate the cached value, add the key, and make sure a new immature + // Invalidate the cached vanue, 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); } @@ -337,37 +343,38 @@ BOOST_AUTO_TEST_CASE(LoadReceiveRequests) BOOST_CHECK_EQUAL(values[1], "val_rr1"); } -// Test some watch-only wallet methods by the procedure of loading (LoadWatchOnly), +// 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)); BOOST_CHECK(found_pubkey == CPubKey()); // passed key is unchanged } - 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)); BOOST_CHECK(found_pubkey == add_pubkey); // passed key is unchanged } } @@ -382,37 +389,38 @@ static void PollutePubKey(CPubKey& pubkey) 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.cpp b/src/wallet/wallet.cpp index a625c4ddec..4b1adfb38f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -29,7 +29,6 @@ #include <util/validation.h> #include <wallet/coincontrol.h> #include <wallet/fees.h> -#include <wallet/scriptpubkeyman.h> #include <algorithm> #include <assert.h> @@ -211,9 +210,9 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& } // 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(); @@ -248,6 +247,14 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const 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; @@ -526,14 +533,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) } 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 @@ -554,11 +562,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) Unlock(strWalletPassphrase); // if we are using HD, replace the HD seed with a new one - if (IsHDEnabled()) { - SetHDSeed(GenerateNewSeed()); + 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 keep @@ -690,7 +698,7 @@ void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool 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)) { AddDestData(dst, "used", "p"); // p for "present", opposite of absent (null) @@ -704,7 +712,7 @@ void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool 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 uint256& hash, unsigned int n) const @@ -864,13 +872,13 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::St // loop though all outputs for (const CTxOut& txout: tx.vout) { // extract addresses and check if they match with an unused keypool key - for (const auto& keyid : GetAffectedKeys(txout.scriptPubKey, *this)) { - std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid); - if (mi != m_pool_key_to_index.end()) { + for (const auto& keyid : GetAffectedKeys(txout.scriptPubKey, *m_spk_man)) { + std::map<CKeyID, int64_t>::const_iterator mi = 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__); } } @@ -1126,13 +1134,21 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const isminetype CWallet::IsMine(const CTxOut& txout) const { - return ::IsMine(*this, txout.scriptPubKey); + return IsMine(txout.scriptPubKey); +} + +isminetype CWallet::IsMine(const CTxDestination& dest) const +{ + return IsMine(GetScriptForDestination(dest)); } -isminetype IsMine(const CWallet& keystore, const CTxDestination& dest) +isminetype CWallet::IsMine(const CScript& script) const { - CScript script = GetScriptForDestination(dest); - return IsMine(keystore, script); + isminetype result = ISMINE_NO; + if (auto spk_man = m_spk_man.get()) { + result = spk_man->IsMine(script); + } + return result; } CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const @@ -1156,7 +1172,7 @@ bool CWallet::IsChange(const CScript& script) const // a better way of identifying which 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)) @@ -1246,6 +1262,26 @@ CAmount CWallet::GetChange(const CTransaction& tx) const 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); @@ -1302,7 +1338,9 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig const CScript& scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(*this, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { + const SigningProvider* provider = GetSigningProvider(); + + if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { return false; } UpdateInput(tx_in, sigdata); @@ -1325,6 +1363,49 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> return true; } +bool CWallet::ImportScripts(const std::set<CScript> 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<CKeyID, CKey>& 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<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& 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<CScript>& 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<CTxOut> txouts; @@ -2001,7 +2082,9 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector< 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) && (coinControl && coinControl->fAllowWatchOnly && solvable)); vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly))); @@ -2235,7 +2318,13 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; SignatureData sigdata; - if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + + const SigningProvider* provider = GetSigningProvider(); + if (!provider) { + return false; + } + + if (!ProduceSignature(*provider, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { return false; } UpdateInput(input, sigdata); @@ -2693,7 +2782,12 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std const CScript& scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + const SigningProvider* provider = GetSigningProvider(); + if (!provider) { + return false; + } + + if (!ProduceSignature(*provider, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) { strFailReason = _("Signing transaction failed").translated; return false; @@ -2801,7 +2895,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { 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. @@ -2838,7 +2932,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 { 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. @@ -2863,7 +2957,7 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) 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. @@ -2887,7 +2981,7 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add if (!strPurpose.empty()) /* update purpose only if requested */ mapAddressBook[address].purpose = strPurpose; } - NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, + NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose)) return false; @@ -2914,17 +3008,50 @@ bool CWallet::DelAddressBook(const CTxDestination& address) 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(EncodeDestination(address)); return WalletBatch(*database).EraseName(EncodeDestination(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)) { @@ -2936,6 +3063,15 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des return true; } +int64_t CWallet::GetOldestKeyPoolTime() +{ + int64_t oldestKey = std::numeric_limits<int64_t>::max(); + if (auto spk_man = m_spk_man.get()) { + oldestKey = spk_man->GetOldestKeyPoolTime(); + } + return oldestKey; +} + std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain) { std::map<CTxDestination, CAmount> balances; @@ -3085,6 +3221,11 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co 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; } @@ -3092,14 +3233,14 @@ bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestin if (nIndex == -1) { CKeyPool keypool; - if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { + if (!m_spk_man->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { return false; } vchPubKey = keypool.vchPubKey; fInternal = keypool.fInternal; } assert(vchPubKey.IsValid()); - pwallet->LearnRelatedScripts(vchPubKey, type); + m_spk_man->LearnRelatedScripts(vchPubKey, type); address = GetDestinationForKey(vchPubKey, type); dest = address; return true; @@ -3108,7 +3249,7 @@ bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestin void ReserveDestination::KeepDestination() { if (nIndex != -1) - pwallet->KeepKey(nIndex); + m_spk_man->KeepKey(nIndex); nIndex = -1; vchPubKey = CPubKey(); address = CNoDestination(); @@ -3117,7 +3258,7 @@ void ReserveDestination::KeepDestination() void ReserveDestination::ReturnDestination() { if (nIndex != -1) { - pwallet->ReturnKey(nIndex, fInternal, vchPubKey); + m_spk_man->ReturnKey(nIndex, fInternal, vchPubKey); } nIndex = -1; vchPubKey = CPubKey(); @@ -3166,8 +3307,12 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C 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; } @@ -3177,7 +3322,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C const Optional<int> tip_height = locked_chain.getHeight(); const int max_height = tip_height && *tip_height > 144 ? *tip_height - 144 : 0; // the tip can be reorganized; use a 144-block safety margin std::map<CKeyID, int> mapKeyFirstBlock; - for (const CKeyID &keyid : GetKeys()) { + for (const CKeyID &keyid : spk_man->GetKeys()) { if (mapKeyBirth.count(keyid) == 0) mapKeyFirstBlock[keyid] = max_height; } @@ -3194,7 +3339,7 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C // ... which are already in a block for (const CTxOut &txout : wtx.tx->vout) { // iterate over all their outputs - for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *this)) { + for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) { // ... and all their affected keys std::map<CKeyID, int>::iterator rit = mapKeyFirstBlock.find(keyid); if (rit != mapKeyFirstBlock.end() && *height < rit->second) @@ -3461,13 +3606,13 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, bool hd_upgrade = false; bool split_upgrade = false; - if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) { + if (walletInstance->CanSupportFeature(FEATURE_HD) && !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 @@ -3482,7 +3627,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, } // 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; } @@ -3497,12 +3642,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, walletInstance->SetWalletFlags(wallet_creation_flags, false); 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; } @@ -3858,3 +4003,18 @@ bool CWallet::Lock() 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/wallet.h b/src/wallet/wallet.h index 7a0cdec5ef..f3b791441c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -18,7 +18,7 @@ #include <validationinterface.h> #include <wallet/coinselection.h> #include <wallet/crypter.h> -#include <wallet/ismine.h> +#include <wallet/scriptpubkeyman.h> #include <wallet/walletdb.h> #include <wallet/walletutil.h> @@ -141,6 +141,7 @@ class ReserveDestination 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 @@ -581,10 +582,9 @@ struct CoinSelectionParams class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime /** - * 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, private interfaces::Chain::Notifications +class CWallet final : public WalletStorage, private interfaces::Chain::Notifications { private: CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore); @@ -596,22 +596,8 @@ private: //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; - using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>; - using WatchOnlySet = std::set<CScript>; - using WatchKeyMap = std::map<CKeyID, CPubKey>; - 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<unsigned char> &vchCryptedSecret); - bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey); std::atomic<bool> fAbortRescan{false}; std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver @@ -620,8 +606,6 @@ private: 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}; @@ -671,52 +655,12 @@ private: * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock = 0, 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<int64_t> setInternalKeyPool GUARDED_BY(cs_wallet); - std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_wallet); - std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_wallet); - int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; - std::map<CKeyID, int64_t> m_pool_key_to_index; std::atomic<uint64_t> 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; @@ -737,9 +681,6 @@ private: */ uint256 m_last_block_processed GUARDED_BY(cs_wallet); - //! Fetches a key from the keypool - bool GetKeyFromPool(CPubKey &key, bool internal = false); - public: /* * Main wallet lock. @@ -754,6 +695,7 @@ public: { return *database; } + WalletDatabase& GetDatabase() override { return *database; } /** * Select a set of coins such that nValueRet >= nTargetValue and at least @@ -769,15 +711,6 @@ public: */ 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<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_wallet); - - // Map from Script ID to key metadata (for watch-only keys). - std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet); - typedef std::map<unsigned int, CMasterKey> MasterKeyMap; MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID = 0; @@ -801,7 +734,7 @@ public: } bool IsCrypted() const { return fUseCrypto; } - bool IsLocked() const; + bool IsLocked() const override; bool Lock(); /** Interface to assert chain access and if successful lock it */ @@ -831,7 +764,7 @@ public: const CWalletTx* GetWalletTx(const uint256& hash) const; //! check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } + bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } /** * populate vCoins with vector of available COutputs. @@ -881,34 +814,10 @@ public: int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; } double ScanningProgress() const { 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); bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; 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<unsigned char> &vchCryptedSecret); - //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) - bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &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<CKeyID> 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, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -921,18 +830,6 @@ public: //! Get all destination values matching a prefix. std::vector<std::string> 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(). int64_t nRelockTime = 0; @@ -1048,33 +945,10 @@ public: /** Absolute maximum transaction fee (in satoshis) used by default for the wallet */ CAmount 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<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::map<CTxDestination, CAmount> GetAddressBalances(interfaces::Chain::Lock& locked_chain); @@ -1084,6 +958,8 @@ public: bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error); 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 @@ -1120,7 +996,7 @@ public: } //! 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); + void SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false) override; //! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) bool SetMaxVersion(int nVersion); @@ -1199,31 +1075,12 @@ public: 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 @@ -1232,35 +1089,21 @@ public: */ 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 */ void UnsetWalletFlag(uint64_t flag); /** 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 returns false if unknown, non-tolerable flags are present */ bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly); /** 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); }; @@ -1271,8 +1114,28 @@ public: LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), 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<LegacyScriptPubKeyMan> m_spk_man = MakeUnique<LegacyScriptPubKeyMan>(*this); + CCriticalSection& 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<int64_t>& setInternalKeyPool GUARDED_BY(cs_wallet) = m_spk_man->setInternalKeyPool; + std::set<int64_t>& setExternalKeyPool GUARDED_BY(cs_wallet) = m_spk_man->setExternalKeyPool; + int64_t& nTimeFirstKey GUARDED_BY(cs_wallet) = m_spk_man->nTimeFirstKey; + std::map<CKeyID, CKeyMetadata>& mapKeyMetadata GUARDED_BY(cs_wallet) = m_spk_man->mapKeyMetadata; + std::map<CScriptID, CKeyMetadata>& 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/walletdb.cpp b/src/wallet/walletdb.cpp index edd8bab9ae..2ba7cdac36 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -12,7 +12,6 @@ #include <sync.h> #include <util/system.h> #include <util/time.h> -#include <wallet/scriptpubkeyman.h> #include <wallet/wallet.h> #include <atomic> @@ -197,7 +196,7 @@ public: static bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, - CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) + CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet, pwallet->GetLegacyScriptPubKeyMan()->cs_wallet) { try { // Unserialize @@ -251,8 +250,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> script; char fYes; ssValue >> fYes; - if (fYes == '1') - pwallet->LoadWatchOnly(script); + if (fYes == '1') { + pwallet->GetLegacyScriptPubKeyMan()->LoadWatchOnly(script); + } } else if (strType == DBKeys::KEY) { CPubKey vchPubKey; ssKey >> vchPubKey; @@ -303,12 +303,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } - if (!pwallet->LoadKey(key, vchPubKey)) + if (!pwallet->GetLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey)) { - strErr = "Error reading wallet database: LoadKey failed"; + 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; @@ -333,9 +334,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> vchPrivKey; wss.nCKeys++; - if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) + if (!pwallet->GetLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey)) { - strErr = "Error reading wallet database: LoadCryptedKey failed"; + strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed"; return false; } wss.fIsEncrypted = true; @@ -345,14 +346,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, 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 corruption @@ -368,15 +369,15 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, 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)) + if (!pwallet->GetLegacyScriptPubKeyMan()->LoadCScript(script)) { - strErr = "Error reading wallet database: LoadCScript failed"; + strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed"; return false; } } else if (strType == DBKeys::ORDERPOSNEXT) { @@ -390,7 +391,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } 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; @@ -433,6 +434,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) 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)) { @@ -513,8 +515,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records); // nTimeFirstKey is only reliable if all keys have metadata - if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) - pwallet->UpdateTimeFirstKey(1); + if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) { + auto spk_man = pwallet->GetLegacyScriptPubKeyMan(); + if (spk_man) { + spk_man->UpdateTimeFirstKey(1); + } + } for (const uint256& hash : wss.vWalletUpgrade) WriteTx(pwallet->mapWallet.at(hash)); @@ -707,6 +713,7 @@ bool WalletBatch::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, C { // 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 index 0843194511..a6f49536b7 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -37,8 +37,9 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs:: 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(); @@ -94,7 +95,7 @@ static void WalletShowInfo(CWallet* wallet_instance) tfm::format(std::cout, "Wallet info\n===========\n"); 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"); + tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no"); tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize()); tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size()); tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size()); |