aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am3
-rw-r--r--src/script/signingprovider.h4
-rw-r--r--src/util/translation.h2
-rw-r--r--src/wallet/init.cpp1
-rw-r--r--src/wallet/ismine.cpp192
-rw-r--r--src/wallet/scriptpubkeyman.cpp1262
-rw-r--r--src/wallet/scriptpubkeyman.h115
-rw-r--r--src/wallet/wallet.cpp1083
-rw-r--r--src/wallet/wallet.h95
-rw-r--r--src/wallet/walletdb.cpp1
10 files changed, 1392 insertions, 1366 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d50524a8ae..342f517938 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -235,6 +235,7 @@ BITCOIN_CORE_H = \
wallet/load.h \
wallet/psbtwallet.h \
wallet/rpcwallet.h \
+ wallet/scriptpubkeyman.h \
wallet/wallet.h \
wallet/walletdb.h \
wallet/wallettool.h \
@@ -338,11 +339,11 @@ libbitcoin_wallet_a_SOURCES = \
wallet/db.cpp \
wallet/feebumper.cpp \
wallet/fees.cpp \
- wallet/ismine.cpp \
wallet/load.cpp \
wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
+ wallet/scriptpubkeyman.cpp \
wallet/wallet.cpp \
wallet/walletdb.cpp \
wallet/walletutil.cpp \
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index 4eec2311d4..c40fecac5c 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -63,8 +63,6 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
class FillableSigningProvider : public SigningProvider
{
protected:
- mutable CCriticalSection cs_KeyStore;
-
using KeyMap = std::map<CKeyID, CKey>;
using ScriptMap = std::map<CScriptID, CScript>;
@@ -74,6 +72,8 @@ protected:
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
public:
+ mutable CCriticalSection cs_KeyStore;
+
virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
diff --git a/src/util/translation.h b/src/util/translation.h
index 0e6eb5a094..fc45da440a 100644
--- a/src/util/translation.h
+++ b/src/util/translation.h
@@ -6,7 +6,7 @@
#define BITCOIN_UTIL_TRANSLATION_H
#include <tinyformat.h>
-
+#include <functional>
/**
* Bilingual messages:
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 3657a157b6..c622318894 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -10,6 +10,7 @@
#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.cpp b/src/wallet/ismine.cpp
deleted file mode 100644
index 029b922785..0000000000
--- a/src/wallet/ismine.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <wallet/ismine.h>
-
-#include <key.h>
-#include <script/script.h>
-#include <script/signingprovider.h>
-#include <wallet/wallet.h>
-
-typedef std::vector<unsigned char> valtype;
-
-namespace {
-
-/**
- * This is an enum that tracks the execution context of a script, similar to
- * SigVersion in script/interpreter. It is separate however because we want to
- * distinguish between top-level scriptPubKey execution and P2SH redeemScript
- * execution (a distinction that has no impact on consensus rules).
- */
-enum class IsMineSigVersion
-{
- TOP = 0, //!< scriptPubKey execution
- P2SH = 1, //!< P2SH redeemScript
- WITNESS_V0 = 2, //!< P2WSH witness script execution
-};
-
-/**
- * This is an internal representation of isminetype + invalidity.
- * Its order is significant, as we return the max of all explored
- * possibilities.
- */
-enum class IsMineResult
-{
- NO = 0, //!< Not ours
- WATCH_ONLY = 1, //!< Included in watch-only balance
- SPENDABLE = 2, //!< Included in all balances
- INVALID = 3, //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
-};
-
-bool PermitsUncompressed(IsMineSigVersion sigversion)
-{
- return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
-}
-
-bool HaveKeys(const std::vector<valtype>& pubkeys, const CWallet& keystore)
-{
- for (const valtype& pubkey : pubkeys) {
- CKeyID keyID = CPubKey(pubkey).GetID();
- if (!keystore.HaveKey(keyID)) return false;
- }
- return true;
-}
-
-IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
-{
- IsMineResult ret = IsMineResult::NO;
-
- std::vector<valtype> vSolutions;
- txnouttype whichType = Solver(scriptPubKey, vSolutions);
-
- CKeyID keyID;
- switch (whichType)
- {
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
- break;
- case TX_PUBKEY:
- keyID = CPubKey(vSolutions[0]).GetID();
- if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
- return IsMineResult::INVALID;
- }
- if (keystore.HaveKey(keyID)) {
- ret = std::max(ret, IsMineResult::SPENDABLE);
- }
- break;
- case TX_WITNESS_V0_KEYHASH:
- {
- if (sigversion == IsMineSigVersion::WITNESS_V0) {
- // P2WPKH inside P2WSH is invalid.
- return IsMineResult::INVALID;
- }
- if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
- // We do not support bare witness outputs unless the P2SH version of it would be
- // acceptable as well. This protects against matching before segwit activates.
- // This also applies to the P2WSH case.
- break;
- }
- ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
- break;
- }
- case TX_PUBKEYHASH:
- keyID = CKeyID(uint160(vSolutions[0]));
- if (!PermitsUncompressed(sigversion)) {
- CPubKey pubkey;
- if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
- return IsMineResult::INVALID;
- }
- }
- if (keystore.HaveKey(keyID)) {
- ret = std::max(ret, IsMineResult::SPENDABLE);
- }
- break;
- case TX_SCRIPTHASH:
- {
- if (sigversion != IsMineSigVersion::TOP) {
- // P2SH inside P2WSH or P2SH is invalid.
- return IsMineResult::INVALID;
- }
- CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
- CScript subscript;
- if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
- }
- break;
- }
- case TX_WITNESS_V0_SCRIPTHASH:
- {
- if (sigversion == IsMineSigVersion::WITNESS_V0) {
- // P2WSH inside P2WSH is invalid.
- return IsMineResult::INVALID;
- }
- if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
- break;
- }
- uint160 hash;
- CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
- CScriptID scriptID = CScriptID(hash);
- CScript subscript;
- if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
- }
- break;
- }
-
- case TX_MULTISIG:
- {
- // Never treat bare multisig outputs as ours (they can still be made watchonly-though)
- if (sigversion == IsMineSigVersion::TOP) {
- break;
- }
-
- // Only consider transactions "mine" if we own ALL the
- // keys involved. Multi-signature transactions that are
- // partially owned (somebody else has a key that can spend
- // them) enable spend-out-from-under-you attacks, especially
- // in shared-wallet situations.
- std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
- if (!PermitsUncompressed(sigversion)) {
- for (size_t i = 0; i < keys.size(); i++) {
- if (keys[i].size() != 33) {
- return IsMineResult::INVALID;
- }
- }
- }
- if (HaveKeys(keys, keystore)) {
- ret = std::max(ret, IsMineResult::SPENDABLE);
- }
- break;
- }
- }
-
- if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
- ret = std::max(ret, IsMineResult::WATCH_ONLY);
- }
- return ret;
-}
-
-} // namespace
-
-isminetype IsMine(const CWallet& keystore, const CScript& scriptPubKey)
-{
- switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
- case IsMineResult::INVALID:
- case IsMineResult::NO:
- return ISMINE_NO;
- case IsMineResult::WATCH_ONLY:
- return ISMINE_WATCH_ONLY;
- case IsMineResult::SPENDABLE:
- return ISMINE_SPENDABLE;
- }
- assert(false);
-}
-
-isminetype IsMine(const CWallet& keystore, const CTxDestination& dest)
-{
- CScript script = GetScriptForDestination(dest);
- return IsMine(keystore, script);
-}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
new file mode 100644
index 0000000000..77ee8f97cb
--- /dev/null
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -0,0 +1,1262 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key_io.h>
+#include <outputtype.h>
+#include <script/descriptor.h>
+#include <util/bip32.h>
+#include <util/strencodings.h>
+#include <util/translation.h>
+#include <wallet/scriptpubkeyman.h>
+#include <wallet/wallet.h>
+
+bool CWallet::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
+ CPubKey new_key;
+ if (!GetKeyFromPool(new_key)) {
+ error = "Error: Keypool ran out, please call keypoolrefill first";
+ return false;
+ }
+ LearnRelatedScripts(new_key, type);
+ dest = GetDestinationForKey(new_key, type);
+
+ SetAddressBook(dest, label, "receive");
+ return true;
+}
+
+typedef std::vector<unsigned char> valtype;
+
+namespace {
+
+/**
+ * This is an enum that tracks the execution context of a script, similar to
+ * SigVersion in script/interpreter. It is separate however because we want to
+ * distinguish between top-level scriptPubKey execution and P2SH redeemScript
+ * execution (a distinction that has no impact on consensus rules).
+ */
+enum class IsMineSigVersion
+{
+ TOP = 0, //!< scriptPubKey execution
+ P2SH = 1, //!< P2SH redeemScript
+ WITNESS_V0 = 2, //!< P2WSH witness script execution
+};
+
+/**
+ * This is an internal representation of isminetype + invalidity.
+ * Its order is significant, as we return the max of all explored
+ * possibilities.
+ */
+enum class IsMineResult
+{
+ NO = 0, //!< Not ours
+ WATCH_ONLY = 1, //!< Included in watch-only balance
+ SPENDABLE = 2, //!< Included in all balances
+ INVALID = 3, //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
+};
+
+bool PermitsUncompressed(IsMineSigVersion sigversion)
+{
+ return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
+}
+
+bool HaveKeys(const std::vector<valtype>& pubkeys, const CWallet& keystore)
+{
+ for (const valtype& pubkey : pubkeys) {
+ CKeyID keyID = CPubKey(pubkey).GetID();
+ if (!keystore.HaveKey(keyID)) return false;
+ }
+ return true;
+}
+
+IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
+{
+ IsMineResult ret = IsMineResult::NO;
+
+ std::vector<valtype> vSolutions;
+ txnouttype whichType = Solver(scriptPubKey, vSolutions);
+
+ CKeyID keyID;
+ switch (whichType)
+ {
+ case TX_NONSTANDARD:
+ case TX_NULL_DATA:
+ case TX_WITNESS_UNKNOWN:
+ break;
+ case TX_PUBKEY:
+ keyID = CPubKey(vSolutions[0]).GetID();
+ if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
+ return IsMineResult::INVALID;
+ }
+ if (keystore.HaveKey(keyID)) {
+ ret = std::max(ret, IsMineResult::SPENDABLE);
+ }
+ break;
+ case TX_WITNESS_V0_KEYHASH:
+ {
+ if (sigversion == IsMineSigVersion::WITNESS_V0) {
+ // P2WPKH inside P2WSH is invalid.
+ return IsMineResult::INVALID;
+ }
+ if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
+ // We do not support bare witness outputs unless the P2SH version of it would be
+ // acceptable as well. This protects against matching before segwit activates.
+ // This also applies to the P2WSH case.
+ break;
+ }
+ ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
+ break;
+ }
+ case TX_PUBKEYHASH:
+ keyID = CKeyID(uint160(vSolutions[0]));
+ if (!PermitsUncompressed(sigversion)) {
+ CPubKey pubkey;
+ if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
+ return IsMineResult::INVALID;
+ }
+ }
+ if (keystore.HaveKey(keyID)) {
+ ret = std::max(ret, IsMineResult::SPENDABLE);
+ }
+ break;
+ case TX_SCRIPTHASH:
+ {
+ if (sigversion != IsMineSigVersion::TOP) {
+ // P2SH inside P2WSH or P2SH is invalid.
+ return IsMineResult::INVALID;
+ }
+ CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
+ CScript subscript;
+ if (keystore.GetCScript(scriptID, subscript)) {
+ ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
+ }
+ break;
+ }
+ case TX_WITNESS_V0_SCRIPTHASH:
+ {
+ if (sigversion == IsMineSigVersion::WITNESS_V0) {
+ // P2WSH inside P2WSH is invalid.
+ return IsMineResult::INVALID;
+ }
+ if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
+ break;
+ }
+ uint160 hash;
+ CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
+ CScriptID scriptID = CScriptID(hash);
+ CScript subscript;
+ if (keystore.GetCScript(scriptID, subscript)) {
+ ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
+ }
+ break;
+ }
+
+ case TX_MULTISIG:
+ {
+ // Never treat bare multisig outputs as ours (they can still be made watchonly-though)
+ if (sigversion == IsMineSigVersion::TOP) {
+ break;
+ }
+
+ // Only consider transactions "mine" if we own ALL the
+ // keys involved. Multi-signature transactions that are
+ // partially owned (somebody else has a key that can spend
+ // them) enable spend-out-from-under-you attacks, especially
+ // in shared-wallet situations.
+ std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
+ if (!PermitsUncompressed(sigversion)) {
+ for (size_t i = 0; i < keys.size(); i++) {
+ if (keys[i].size() != 33) {
+ return IsMineResult::INVALID;
+ }
+ }
+ }
+ if (HaveKeys(keys, keystore)) {
+ ret = std::max(ret, IsMineResult::SPENDABLE);
+ }
+ break;
+ }
+ }
+
+ if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
+ ret = std::max(ret, IsMineResult::WATCH_ONLY);
+ }
+ return ret;
+}
+
+} // namespace
+
+isminetype IsMine(const CWallet& keystore, const CScript& scriptPubKey)
+{
+ switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
+ case IsMineResult::INVALID:
+ case IsMineResult::NO:
+ return ISMINE_NO;
+ case IsMineResult::WATCH_ONLY:
+ return ISMINE_WATCH_ONLY;
+ case IsMineResult::SPENDABLE:
+ return ISMINE_SPENDABLE;
+ }
+ assert(false);
+}
+
+bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
+{
+ {
+ LOCK(cs_KeyStore);
+ if (!SetCrypted())
+ return false;
+
+ bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
+ bool keyFail = false;
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
+ for (; mi != mapCryptedKeys.end(); ++mi)
+ {
+ const CPubKey &vchPubKey = (*mi).second.first;
+ const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+ CKey key;
+ if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
+ {
+ keyFail = true;
+ break;
+ }
+ keyPass = true;
+ if (fDecryptionThoroughlyChecked)
+ break;
+ }
+ if (keyPass && keyFail)
+ {
+ LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
+ throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
+ }
+ if (keyFail || (!keyPass && !accept_no_keys))
+ return false;
+ vMasterKey = vMasterKeyIn;
+ fDecryptionThoroughlyChecked = true;
+ }
+ NotifyStatusChanged(this);
+ return true;
+}
+
+bool CWallet::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
+{
+ LOCK(cs_KeyStore);
+ if (!mapCryptedKeys.empty() || IsCrypted())
+ return false;
+
+ fUseCrypto = true;
+ for (const KeyMap::value_type& mKey : mapKeys)
+ {
+ const CKey &key = mKey.second;
+ CPubKey vchPubKey = key.GetPubKey();
+ CKeyingMaterial vchSecret(key.begin(), key.end());
+ std::vector<unsigned char> vchCryptedSecret;
+ if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
+ return false;
+ if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
+ return false;
+ }
+ mapKeys.clear();
+ return true;
+}
+
+void CWallet::UpgradeKeyMetadata()
+{
+ AssertLockHeld(cs_wallet);
+ if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
+ return;
+ }
+
+ std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(*database);
+ 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
+ CKey key;
+ GetKey(meta.hd_seed_id, key);
+ CExtKey masterKey;
+ masterKey.SetSeed(key.begin(), key.size());
+ // Add to map
+ CKeyID master_id = masterKey.key.GetPubKey().GetID();
+ std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint);
+ if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) {
+ throw std::runtime_error("Invalid stored hdKeypath");
+ }
+ meta.has_key_origin = true;
+ if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) {
+ meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN;
+ }
+
+ // Write meta to wallet
+ CPubKey pubkey;
+ if (GetPubKey(meta_pair.first, pubkey)) {
+ batch->WriteKeyMetadata(meta, pubkey, true);
+ }
+ }
+ }
+ batch.reset(); //write before setting the flag
+ SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
+}
+
+bool CWallet::IsHDEnabled() const
+{
+ return !hdChain.seed_id.IsNull();
+}
+
+bool CWallet::CanGetAddresses(bool internal)
+{
+ LOCK(cs_wallet);
+ // Check if the keypool has keys
+ bool keypool_has_keys;
+ if (internal && CanSupportFeature(FEATURE_HD_SPLIT)) {
+ keypool_has_keys = setInternalKeyPool.size() > 0;
+ } else {
+ keypool_has_keys = KeypoolCountExternalKeys() > 0;
+ }
+ // If the keypool doesn't have keys, check if we can generate them
+ if (!keypool_has_keys) {
+ return CanGenerateKeys();
+ }
+ return keypool_has_keys;
+}
+
+static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
+ if (setKeyPool.empty()) {
+ return GetTime();
+ }
+
+ CKeyPool keypool;
+ int64_t nIndex = *(setKeyPool.begin());
+ if (!batch.ReadPool(nIndex, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
+ }
+ assert(keypool.vchPubKey.IsValid());
+ return keypool.nTime;
+}
+
+int64_t CWallet::GetOldestKeyPoolTime()
+{
+ LOCK(cs_wallet);
+
+ WalletBatch batch(*database);
+
+ // load oldest key from keypool, get time and return
+ int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
+ if (IsHDEnabled() && 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);
+ }
+ }
+
+ return oldestKey;
+}
+
+size_t CWallet::KeypoolCountExternalKeys()
+{
+ AssertLockHeld(cs_wallet);
+ return setExternalKeyPool.size() + set_pre_split_keypool.size();
+}
+
+/**
+ * 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)
+{
+ AssertLockHeld(cs_wallet);
+ if (nCreateTime <= 1) {
+ // Cannot determine birthday information, so set the wallet birthday to
+ // the beginning of time.
+ nTimeFirstKey = 1;
+ } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {
+ nTimeFirstKey = nCreateTime;
+ }
+}
+
+bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
+{
+ WalletBatch batch(*database);
+ return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey);
+}
+
+bool CWallet::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));
+
+ // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey
+ // which is overridden below. To avoid flushes, the database handle is
+ // tunneled through to it.
+ bool needsDB = !encrypted_batch;
+ if (needsDB) {
+ encrypted_batch = &batch;
+ }
+ if (!AddKeyPubKeyInner(secret, pubkey)) {
+ if (needsDB) encrypted_batch = nullptr;
+ return false;
+ }
+ if (needsDB) encrypted_batch = nullptr;
+
+ // check if we need to remove from watch-only
+ CScript script;
+ script = GetScriptForDestination(PKHash(pubkey));
+ if (HaveWatchOnly(script)) {
+ RemoveWatchOnly(script);
+ }
+ script = GetScriptForRawPubKey(pubkey);
+ if (HaveWatchOnly(script)) {
+ RemoveWatchOnly(script);
+ }
+
+ if (!IsCrypted()) {
+ return batch.WriteKey(pubkey,
+ secret.GetPrivKey(),
+ mapKeyMetadata[pubkey.GetID()]);
+ }
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
+ return true;
+}
+
+bool CWallet::LoadCScript(const CScript& redeemScript)
+{
+ /* A sanity check was added in pull #3843 to avoid adding redeemScripts
+ * that never can be redeemed. However, old wallets may still contain
+ * these. Do not add them to the wallet and warn. */
+ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
+ {
+ std::string strAddr = EncodeDestination(ScriptHash(redeemScript));
+ WalletLogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
+ return true;
+ }
+
+ return FillableSigningProvider::AddCScript(redeemScript);
+}
+
+void CWallet::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)
+{
+ AssertLockHeld(cs_wallet);
+ UpdateTimeFirstKey(meta.nCreateTime);
+ m_script_metadata[script_id] = meta;
+}
+
+bool CWallet::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::AddKeyPubKey(key, pubkey);
+ }
+
+ if (IsLocked()) {
+ return false;
+ }
+
+ std::vector<unsigned char> vchCryptedSecret;
+ CKeyingMaterial vchSecret(key.begin(), key.end());
+ if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
+ return false;
+ }
+
+ if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
+ return false;
+ }
+ return true;
+}
+
+bool CWallet::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)
+{
+ LOCK(cs_KeyStore);
+ if (!SetCrypted()) {
+ return false;
+ }
+
+ mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
+ ImplicitlyLearnRelatedKeyScripts(vchPubKey);
+ return true;
+}
+
+bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
+ const std::vector<unsigned char> &vchCryptedSecret)
+{
+ if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret))
+ return false;
+ {
+ LOCK(cs_wallet);
+ if (encrypted_batch)
+ return encrypted_batch->WriteCryptedKey(vchPubKey,
+ vchCryptedSecret,
+ mapKeyMetadata[vchPubKey.GetID()]);
+ else
+ return WalletBatch(*database).WriteCryptedKey(vchPubKey,
+ vchCryptedSecret,
+ mapKeyMetadata[vchPubKey.GetID()]);
+ }
+}
+
+bool CWallet::HaveWatchOnly(const CScript &dest) const
+{
+ LOCK(cs_KeyStore);
+ return setWatchOnly.count(dest) > 0;
+}
+
+bool CWallet::HaveWatchOnly() const
+{
+ LOCK(cs_KeyStore);
+ return (!setWatchOnly.empty());
+}
+
+static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
+{
+ std::vector<std::vector<unsigned char>> solutions;
+ return Solver(dest, solutions) == TX_PUBKEY &&
+ (pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
+}
+
+bool CWallet::RemoveWatchOnly(const CScript &dest)
+{
+ AssertLockHeld(cs_wallet);
+ {
+ LOCK(cs_KeyStore);
+ setWatchOnly.erase(dest);
+ CPubKey pubKey;
+ if (ExtractPubKey(dest, pubKey)) {
+ mapWatchKeys.erase(pubKey.GetID());
+ }
+ // Related CScripts are not removed; having superfluous scripts around is
+ // harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
+ }
+
+ if (!HaveWatchOnly())
+ NotifyWatchonlyChanged(false);
+ if (!WalletBatch(*database).EraseWatchOnly(dest))
+ return false;
+
+ return true;
+}
+
+bool CWallet::LoadWatchOnly(const CScript &dest)
+{
+ return AddWatchOnlyInMem(dest);
+}
+
+bool CWallet::AddWatchOnlyInMem(const CScript &dest)
+{
+ LOCK(cs_KeyStore);
+ setWatchOnly.insert(dest);
+ CPubKey pubKey;
+ if (ExtractPubKey(dest, pubKey)) {
+ mapWatchKeys[pubKey.GetID()] = pubKey;
+ ImplicitlyLearnRelatedKeyScripts(pubKey);
+ }
+ return true;
+}
+
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
+{
+ if (!AddWatchOnlyInMem(dest))
+ return false;
+ const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
+ UpdateTimeFirstKey(meta.nCreateTime);
+ NotifyWatchonlyChanged(true);
+ if (batch.WriteWatchOnly(dest, meta)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
+ return true;
+ }
+ return false;
+}
+
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time)
+{
+ m_script_metadata[CScriptID(dest)].nCreateTime = create_time;
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
+bool CWallet::AddWatchOnly(const CScript& dest)
+{
+ WalletBatch batch(*database);
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
+bool CWallet::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)
+{
+ LOCK(cs_wallet);
+ if (!memonly && !WalletBatch(*database).WriteHDChain(chain))
+ throw std::runtime_error(std::string(__func__) + ": writing chain failed");
+
+ hdChain = chain;
+}
+
+bool CWallet::HaveKey(const CKeyID &address) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::HaveKey(address);
+ }
+ return mapCryptedKeys.count(address) > 0;
+}
+
+bool CWallet::GetKey(const CKeyID &address, CKey& keyOut) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::GetKey(address, keyOut);
+ }
+
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+ if (mi != mapCryptedKeys.end())
+ {
+ const CPubKey &vchPubKey = (*mi).second.first;
+ const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
+ return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
+ }
+ return false;
+}
+
+bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
+{
+ CKeyMetadata meta;
+ {
+ LOCK(cs_wallet);
+ auto it = mapKeyMetadata.find(keyID);
+ if (it != mapKeyMetadata.end()) {
+ meta = it->second;
+ }
+ }
+ if (meta.has_key_origin) {
+ std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, info.fingerprint);
+ info.path = meta.key_origin.path;
+ } else { // Single pubkeys get the master fingerprint of themselves
+ std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint);
+ }
+ return true;
+}
+
+bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
+{
+ LOCK(cs_KeyStore);
+ WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
+ if (it != mapWatchKeys.end()) {
+ pubkey_out = it->second;
+ return true;
+ }
+ return false;
+}
+
+bool CWallet::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) {
+ return GetWatchPubKey(address, vchPubKeyOut);
+ }
+ return true;
+ }
+
+ CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
+ if (mi != mapCryptedKeys.end())
+ {
+ vchPubKeyOut = (*mi).second.first;
+ return true;
+ }
+ // Check for watch-only pubkeys
+ return GetWatchPubKey(address, vchPubKeyOut);
+}
+
+CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
+{
+ assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ assert(!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
+
+ CKey secret;
+
+ // Create new metadata
+ int64_t nCreationTime = GetTime();
+ CKeyMetadata metadata(nCreationTime);
+
+ // 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));
+ } else {
+ secret.MakeNewKey(fCompressed);
+ }
+
+ // Compressed public keys were introduced in version 0.6.0
+ if (fCompressed) {
+ SetMinVersion(FEATURE_COMPRPUBKEY);
+ }
+
+ CPubKey pubkey = secret.GetPubKey();
+ assert(secret.VerifyPubKey(pubkey));
+
+ mapKeyMetadata[pubkey.GetID()] = metadata;
+ UpdateTimeFirstKey(nCreationTime);
+
+ if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) {
+ throw std::runtime_error(std::string(__func__) + ": AddKey failed");
+ }
+ return pubkey;
+}
+
+const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
+
+void CWallet::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)
+ CExtKey masterKey; //hd master key
+ CExtKey accountKey; //key at m/0'
+ CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
+ CExtKey childKey; //key at m/0'/0'/<n>'
+
+ // try to get the seed
+ if (!GetKey(hdChain.seed_id, seed))
+ throw std::runtime_error(std::string(__func__) + ": seed not found");
+
+ masterKey.SetSeed(seed.begin(), seed.size());
+
+ // derive m/0'
+ // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
+ 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);
+ accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0));
+
+ // derive child key at next index, skip keys already known to the wallet
+ do {
+ // always derive hardened keys
+ // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
+ // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
+ if (internal) {
+ chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/1'/" + std::to_string(hdChain.nInternalChainCounter) + "'";
+ metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ hdChain.nInternalChainCounter++;
+ }
+ else {
+ chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
+ metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
+ metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
+ hdChain.nExternalChainCounter++;
+ }
+ } while (HaveKey(childKey.key.GetPubKey().GetID()));
+ secret = childKey.key;
+ metadata.hd_seed_id = hdChain.seed_id;
+ CKeyID master_id = masterKey.key.GetPubKey().GetID();
+ std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint);
+ metadata.has_key_origin = true;
+ // update the chain model in the database
+ if (!batch.WriteHDChain(hdChain))
+ throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
+}
+
+void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
+{
+ AssertLockHeld(cs_wallet);
+ if (keypool.m_pre_split) {
+ set_pre_split_keypool.insert(nIndex);
+ } else if (keypool.fInternal) {
+ setInternalKeyPool.insert(nIndex);
+ } else {
+ setExternalKeyPool.insert(nIndex);
+ }
+ m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
+ m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
+
+ // If no metadata exists yet, create a default with the pool key's
+ // creation time. Note that this may be overwritten by actually
+ // stored metadata for that key later, which is fine.
+ CKeyID keyid = keypool.vchPubKey.GetID();
+ if (mapKeyMetadata.count(keyid) == 0)
+ mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
+}
+
+bool CWallet::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);
+}
+
+CPubKey CWallet::GenerateNewSeed()
+{
+ assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ CKey key;
+ key.MakeNewKey(true);
+ return DeriveNewSeed(key);
+}
+
+CPubKey CWallet::DeriveNewSeed(const CKey& key)
+{
+ int64_t nCreationTime = GetTime();
+ CKeyMetadata metadata(nCreationTime);
+
+ // calculate the seed
+ CPubKey seed = key.GetPubKey();
+ assert(key.VerifyPubKey(seed));
+
+ // set the hd keypath to "s" -> Seed, refers the seed to itself
+ metadata.hdKeypath = "s";
+ metadata.has_key_origin = false;
+ metadata.hd_seed_id = seed.GetID();
+
+ {
+ LOCK(cs_wallet);
+
+ // mem store the metadata
+ mapKeyMetadata[seed.GetID()] = metadata;
+
+ // write the key&metadata to the database
+ if (!AddKeyPubKey(key, seed))
+ throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
+ }
+
+ return seed;
+}
+
+void CWallet::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.seed_id = seed.GetID();
+ SetHDChain(newHdChain, false);
+ NotifyCanGetAddressesChanged();
+ UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+}
+
+/**
+ * Mark old keypool keys as used,
+ * and generate all new keys
+ */
+bool CWallet::NewKeyPool()
+{
+ if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ return false;
+ }
+ {
+ LOCK(cs_wallet);
+ WalletBatch batch(*database);
+
+ for (const int64_t nIndex : setInternalKeyPool) {
+ batch.ErasePool(nIndex);
+ }
+ setInternalKeyPool.clear();
+
+ for (const int64_t nIndex : setExternalKeyPool) {
+ batch.ErasePool(nIndex);
+ }
+ setExternalKeyPool.clear();
+
+ for (const int64_t nIndex : set_pre_split_keypool) {
+ batch.ErasePool(nIndex);
+ }
+ set_pre_split_keypool.clear();
+
+ m_pool_key_to_index.clear();
+
+ if (!TopUpKeyPool()) {
+ return false;
+ }
+ WalletLogPrintf("CWallet::NewKeyPool rewrote keypool\n");
+ }
+ return true;
+}
+
+bool CWallet::TopUpKeyPool(unsigned int kpSize)
+{
+ if (!CanGenerateKeys()) {
+ return false;
+ }
+ {
+ LOCK(cs_wallet);
+
+ if (IsLocked()) return false;
+
+ // Top up key pool
+ unsigned int nTargetSize;
+ if (kpSize > 0)
+ nTargetSize = kpSize;
+ else
+ nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
+
+ // count amount of available keys (internal, external)
+ // make sure the keypool of external and internal keys fits the user selected target (-keypool)
+ 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))
+ {
+ // don't create extra internal keys
+ missingInternal = 0;
+ }
+ bool internal = false;
+ WalletBatch batch(*database);
+ for (int64_t i = missingInternal + missingExternal; i--;)
+ {
+ if (i < missingInternal) {
+ internal = true;
+ }
+
+ CPubKey pubkey(GenerateNewKey(batch, internal));
+ AddKeypoolPubkeyWithDB(pubkey, internal, batch);
+ }
+ if (missingInternal + missingExternal > 0) {
+ WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size());
+ }
+ }
+ NotifyCanGetAddressesChanged();
+ return true;
+}
+
+void CWallet::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?
+ int64_t index = ++m_max_keypool_index;
+ if (!batch.WritePool(index, CKeyPool(pubkey, internal))) {
+ throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed");
+ }
+ if (internal) {
+ setInternalKeyPool.insert(index);
+ } else {
+ setExternalKeyPool.insert(index);
+ }
+ m_pool_key_to_index[pubkey.GetID()] = index;
+}
+
+void CWallet::KeepKey(int64_t nIndex)
+{
+ // Remove from key pool
+ WalletBatch batch(*database);
+ batch.ErasePool(nIndex);
+ WalletLogPrintf("keypool keep %d\n", nIndex);
+}
+
+void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
+{
+ // Return to key pool
+ {
+ LOCK(cs_wallet);
+ if (fInternal) {
+ setInternalKeyPool.insert(nIndex);
+ } else if (!set_pre_split_keypool.empty()) {
+ set_pre_split_keypool.insert(nIndex);
+ } else {
+ setExternalKeyPool.insert(nIndex);
+ }
+ m_pool_key_to_index[pubkey.GetID()] = nIndex;
+ NotifyCanGetAddressesChanged();
+ }
+ WalletLogPrintf("keypool return %d\n", nIndex);
+}
+
+bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
+{
+ if (!CanGetAddresses(internal)) {
+ return false;
+ }
+
+ CKeyPool keypool;
+ {
+ LOCK(cs_wallet);
+ int64_t nIndex;
+ if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ if (IsLocked()) return false;
+ WalletBatch batch(*database);
+ result = GenerateNewKey(batch, internal);
+ return true;
+ }
+ KeepKey(nIndex);
+ result = keypool.vchPubKey;
+ }
+ return true;
+}
+
+bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
+{
+ nIndex = -1;
+ keypool.vchPubKey = CPubKey();
+ {
+ LOCK(cs_wallet);
+
+ TopUpKeyPool();
+
+ bool fReturningInternal = fRequestedInternal;
+ fReturningInternal &= (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || 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;
+
+ // Get the oldest key
+ if (setKeyPool.empty()) {
+ return false;
+ }
+
+ WalletBatch batch(*database);
+
+ auto it = setKeyPool.begin();
+ nIndex = *it;
+ setKeyPool.erase(it);
+ if (!batch.ReadPool(nIndex, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": read failed");
+ }
+ CPubKey pk;
+ if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) {
+ throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
+ }
+ // If the key was pre-split keypool, we don't care about what type it is
+ if (use_split_keypool && keypool.fInternal != fReturningInternal) {
+ throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
+ }
+ if (!keypool.vchPubKey.IsValid()) {
+ throw std::runtime_error(std::string(__func__) + ": keypool entry invalid");
+ }
+
+ m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
+ WalletLogPrintf("keypool reserve %d\n", nIndex);
+ }
+ NotifyCanGetAddressesChanged();
+ return true;
+}
+
+void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
+{
+ if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
+ CTxDestination witdest = WitnessV0KeyHash(key.GetID());
+ CScript witprog = GetScriptForDestination(witdest);
+ // Make sure the resulting program is solvable.
+ assert(IsSolvable(*this, witprog));
+ AddCScript(witprog);
+ }
+}
+
+void CWallet::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)
+{
+ AssertLockHeld(cs_wallet);
+ bool internal = setInternalKeyPool.count(keypool_id);
+ if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(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);
+ while (it != std::end(*setKeyPool)) {
+ const int64_t& index = *(it);
+ if (index > keypool_id) break; // set*KeyPool is ordered
+
+ CKeyPool keypool;
+ if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary
+ m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
+ }
+ LearnAllRelatedScripts(keypool.vchPubKey);
+ batch.ErasePool(index);
+ WalletLogPrintf("keypool index %d removed\n", index);
+ it = setKeyPool->erase(it);
+ }
+}
+
+std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider)
+{
+ std::vector<CScript> dummy;
+ FlatSigningProvider out;
+ InferDescriptor(spk, provider)->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out);
+ std::vector<CKeyID> ret;
+ for (const auto& entry : out.pubkeys) {
+ ret.push_back(entry.first);
+ }
+ return ret;
+}
+
+void CWallet::MarkPreSplitKeys()
+{
+ WalletBatch batch(*database);
+ for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) {
+ int64_t index = *it;
+ CKeyPool keypool;
+ if (!batch.ReadPool(index, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": read keypool entry failed");
+ }
+ keypool.m_pre_split = true;
+ if (!batch.WritePool(index, keypool)) {
+ throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed");
+ }
+ set_pre_split_keypool.insert(index);
+ it = setExternalKeyPool.erase(it);
+ }
+}
+
+bool CWallet::AddCScript(const CScript& redeemScript)
+{
+ WalletBatch batch(*database);
+ return AddCScriptWithDB(batch, redeemScript);
+}
+
+bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
+{
+ if (!FillableSigningProvider::AddCScript(redeemScript))
+ return false;
+ if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
+ return true;
+ }
+ return false;
+}
+
+bool CWallet::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);
+ mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path;
+ mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
+ mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path);
+ return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
+}
+
+bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : scripts) {
+ CScriptID id(entry);
+ if (HaveCScript(id)) {
+ WalletLogPrintf("Already have script %s, skipping\n", HexStr(entry));
+ continue;
+ }
+ if (!AddCScriptWithDB(batch, entry)) {
+ return false;
+ }
+
+ if (timestamp > 0) {
+ m_script_metadata[CScriptID(entry)].nCreateTime = timestamp;
+ }
+ }
+ if (timestamp > 0) {
+ UpdateTimeFirstKey(timestamp);
+ }
+
+ return true;
+}
+
+bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : privkey_map) {
+ const CKey& key = entry.second;
+ CPubKey pubkey = key.GetPubKey();
+ const CKeyID& id = entry.first;
+ assert(key.VerifyPubKey(pubkey));
+ // Skip if we already have the key
+ if (HaveKey(id)) {
+ WalletLogPrintf("Already have key with pubkey %s, skipping\n", HexStr(pubkey));
+ continue;
+ }
+ mapKeyMetadata[id].nCreateTime = timestamp;
+ // If the private key is not present in the wallet, insert it.
+ if (!AddKeyPubKeyWithDB(batch, key, pubkey)) {
+ return false;
+ }
+ UpdateTimeFirstKey(timestamp);
+ }
+ 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)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : key_origins) {
+ AddKeyOriginWithDB(batch, entry.second.first, entry.second.second);
+ }
+ for (const CKeyID& id : ordered_pubkeys) {
+ auto entry = pubkey_map.find(id);
+ if (entry == pubkey_map.end()) {
+ continue;
+ }
+ const CPubKey& pubkey = entry->second;
+ CPubKey temp;
+ if (GetPubKey(id, temp)) {
+ // Already have pubkey, skipping
+ WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp));
+ continue;
+ }
+ if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
+ return false;
+ }
+ mapKeyMetadata[id].nCreateTime = timestamp;
+
+ // Add to keypool only works with pubkeys
+ if (add_keypool) {
+ AddKeypoolPubkeyWithDB(pubkey, internal, batch);
+ NotifyCanGetAddressesChanged();
+ }
+ }
+ 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)
+{
+ WalletBatch batch(*database);
+ 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 (!AddWatchOnlyWithDB(batch, script, timestamp)) {
+ return false;
+ }
+ }
+ CTxDestination dest;
+ ExtractDestination(script, dest);
+ if (apply_label && IsValidDestination(dest)) {
+ SetAddressBookWithDB(batch, dest, label, "receive");
+ }
+ }
+ return true;
+}
+
+std::set<CKeyID> CWallet::GetKeys() const
+{
+ LOCK(cs_KeyStore);
+ if (!IsCrypted()) {
+ return FillableSigningProvider::GetKeys();
+ }
+ std::set<CKeyID> set_address;
+ for (const auto& mi : mapCryptedKeys) {
+ set_address.insert(mi.first);
+ }
+ return set_address;
+}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
new file mode 100644
index 0000000000..f151cec444
--- /dev/null
+++ b/src/wallet/scriptpubkeyman.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
+#define BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
+
+#include <script/signingprovider.h>
+#include <script/standard.h>
+#include <wallet/crypter.h>
+#include <wallet/ismine.h>
+#include <wallet/walletdb.h>
+#include <wallet/walletutil.h>
+
+#include <boost/signals2/signal.hpp>
+
+enum class OutputType;
+
+//! Default for -keypool
+static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
+
+/** A key from a CWallet's keypool
+ *
+ * The wallet holds one (for pre HD-split wallets) or several keypools. These
+ * are sets of keys that have not yet been used to provide addresses or receive
+ * change.
+ *
+ * The Bitcoin Core wallet was originally a collection of unrelated private
+ * keys with their associated addresses. If a non-HD wallet generated a
+ * key/address, gave that address out and then restored a backup from before
+ * that key's generation, then any funds sent to that address would be
+ * lost definitively.
+ *
+ * The keypool was implemented to avoid this scenario (commit: 10384941). The
+ * wallet would generate a set of keys (100 by default). When a new public key
+ * was required, either to give out as an address or to use in a change output,
+ * it would be drawn from the keypool. The keypool would then be topped up to
+ * maintain 100 keys. This ensured that as long as the wallet hadn't used more
+ * than 100 keys since the previous backup, all funds would be safe, since a
+ * restored wallet would be able to scan for all owned addresses.
+ *
+ * A keypool also allowed encrypted wallets to give out addresses without
+ * having to be decrypted to generate a new private key.
+ *
+ * With the introduction of HD wallets (commit: f1902510), the keypool
+ * essentially became an address look-ahead pool. Restoring old backups can no
+ * longer definitively lose funds as long as the addresses used were from the
+ * wallet's HD seed (since all private keys can be rederived from the seed).
+ * However, if many addresses were used since the backup, then the wallet may
+ * not know how far ahead in the HD chain to look for its addresses. The
+ * keypool is used to implement a 'gap limit'. The keypool maintains a set of
+ * keys (by default 1000) ahead of the last used key and scans for the
+ * addresses of those keys. This avoids the risk of not seeing transactions
+ * involving the wallet's addresses, or of re-using the same address.
+ *
+ * The HD-split wallet feature added a second keypool (commit: 02592f4c). There
+ * is an external keypool (for addresses to hand out) and an internal keypool
+ * (for change addresses).
+ *
+ * Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
+ * stored as sets of indexes in the wallet (setInternalKeyPool,
+ * setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
+ * index (m_pool_key_to_index). The CKeyPool object is used to
+ * serialize/deserialize the pool data to/from the database.
+ */
+class CKeyPool
+{
+public:
+ //! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
+ int64_t nTime;
+ //! The public key
+ CPubKey vchPubKey;
+ //! Whether this keypool entry is in the internal keypool (for change outputs)
+ bool fInternal;
+ //! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
+ bool m_pre_split;
+
+ CKeyPool();
+ CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH))
+ READWRITE(nVersion);
+ READWRITE(nTime);
+ READWRITE(vchPubKey);
+ if (ser_action.ForRead()) {
+ try {
+ READWRITE(fInternal);
+ }
+ catch (std::ios_base::failure&) {
+ /* flag as external address if we can't read the internal boolean
+ (this will be the case for any wallet before the HD chain split version) */
+ fInternal = false;
+ }
+ try {
+ READWRITE(m_pre_split);
+ }
+ catch (std::ios_base::failure&) {
+ /* flag as postsplit address if we can't read the m_pre_split boolean
+ (this will be the case for any wallet that upgrades to HD chain split)*/
+ m_pre_split = false;
+ }
+ }
+ else {
+ READWRITE(fInternal);
+ READWRITE(m_pre_split);
+ }
+ }
+};
+
+#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 159d4f78c6..a625c4ddec 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -29,6 +29,7 @@
#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <wallet/scriptpubkeyman.h>
#include <algorithm>
#include <assert.h>
@@ -224,8 +225,6 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
return WalletCreationStatus::SUCCESS;
}
-const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
-
const uint256 CWalletTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
@@ -238,17 +237,7 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
-std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider)
-{
- std::vector<CScript> dummy;
- FlatSigningProvider out;
- InferDescriptor(spk, provider)->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out);
- std::vector<CKeyID> ret;
- for (const auto& entry : out.pubkeys) {
- ret.push_back(entry.first);
- }
- return ret;
-}
+std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider);
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
@@ -259,356 +248,6 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
return &(it->second);
}
-CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
-{
- assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
- assert(!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
-
- CKey secret;
-
- // Create new metadata
- int64_t nCreationTime = GetTime();
- CKeyMetadata metadata(nCreationTime);
-
- // 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));
- } else {
- secret.MakeNewKey(fCompressed);
- }
-
- // Compressed public keys were introduced in version 0.6.0
- if (fCompressed) {
- SetMinVersion(FEATURE_COMPRPUBKEY);
- }
-
- CPubKey pubkey = secret.GetPubKey();
- assert(secret.VerifyPubKey(pubkey));
-
- mapKeyMetadata[pubkey.GetID()] = metadata;
- UpdateTimeFirstKey(nCreationTime);
-
- if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) {
- throw std::runtime_error(std::string(__func__) + ": AddKey failed");
- }
- return pubkey;
-}
-
-void CWallet::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)
- CExtKey masterKey; //hd master key
- CExtKey accountKey; //key at m/0'
- CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
- CExtKey childKey; //key at m/0'/0'/<n>'
-
- // try to get the seed
- if (!GetKey(hdChain.seed_id, seed))
- throw std::runtime_error(std::string(__func__) + ": seed not found");
-
- masterKey.SetSeed(seed.begin(), seed.size());
-
- // derive m/0'
- // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
- 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);
- accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0));
-
- // derive child key at next index, skip keys already known to the wallet
- do {
- // always derive hardened keys
- // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
- // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
- if (internal) {
- chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- metadata.hdKeypath = "m/0'/1'/" + std::to_string(hdChain.nInternalChainCounter) + "'";
- metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
- metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT);
- metadata.key_origin.path.push_back(hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- hdChain.nInternalChainCounter++;
- }
- else {
- chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'";
- metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
- metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);
- metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
- hdChain.nExternalChainCounter++;
- }
- } while (HaveKey(childKey.key.GetPubKey().GetID()));
- secret = childKey.key;
- metadata.hd_seed_id = hdChain.seed_id;
- CKeyID master_id = masterKey.key.GetPubKey().GetID();
- std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint);
- metadata.has_key_origin = true;
- // update the chain model in the database
- if (!batch.WriteHDChain(hdChain))
- throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
-}
-
-bool CWallet::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));
-
- // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey
- // which is overridden below. To avoid flushes, the database handle is
- // tunneled through to it.
- bool needsDB = !encrypted_batch;
- if (needsDB) {
- encrypted_batch = &batch;
- }
- if (!AddKeyPubKeyInner(secret, pubkey)) {
- if (needsDB) encrypted_batch = nullptr;
- return false;
- }
- if (needsDB) encrypted_batch = nullptr;
-
- // check if we need to remove from watch-only
- CScript script;
- script = GetScriptForDestination(PKHash(pubkey));
- if (HaveWatchOnly(script)) {
- RemoveWatchOnly(script);
- }
- script = GetScriptForRawPubKey(pubkey);
- if (HaveWatchOnly(script)) {
- RemoveWatchOnly(script);
- }
-
- if (!IsCrypted()) {
- return batch.WriteKey(pubkey,
- secret.GetPrivKey(),
- mapKeyMetadata[pubkey.GetID()]);
- }
- UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
- return true;
-}
-
-bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
-{
- WalletBatch batch(*database);
- return CWallet::AddKeyPubKeyWithDB(batch, secret, pubkey);
-}
-
-bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
- const std::vector<unsigned char> &vchCryptedSecret)
-{
- if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret))
- return false;
- {
- LOCK(cs_wallet);
- if (encrypted_batch)
- return encrypted_batch->WriteCryptedKey(vchPubKey,
- vchCryptedSecret,
- mapKeyMetadata[vchPubKey.GetID()]);
- else
- return WalletBatch(*database).WriteCryptedKey(vchPubKey,
- vchCryptedSecret,
- mapKeyMetadata[vchPubKey.GetID()]);
- }
-}
-
-void CWallet::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)
-{
- AssertLockHeld(cs_wallet);
- UpdateTimeFirstKey(meta.nCreateTime);
- m_script_metadata[script_id] = meta;
-}
-
-void CWallet::UpgradeKeyMetadata()
-{
- AssertLockHeld(cs_wallet);
- if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
- return;
- }
-
- std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(*database);
- 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
- CKey key;
- GetKey(meta.hd_seed_id, key);
- CExtKey masterKey;
- masterKey.SetSeed(key.begin(), key.size());
- // Add to map
- CKeyID master_id = masterKey.key.GetPubKey().GetID();
- std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint);
- if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) {
- throw std::runtime_error("Invalid stored hdKeypath");
- }
- meta.has_key_origin = true;
- if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) {
- meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN;
- }
-
- // Write meta to wallet
- CPubKey pubkey;
- if (GetPubKey(meta_pair.first, pubkey)) {
- batch->WriteKeyMetadata(meta, pubkey, true);
- }
- }
- }
- batch.reset(); //write before setting the flag
- SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
-}
-
-bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
-{
- return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
-}
-
-/**
- * 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)
-{
- AssertLockHeld(cs_wallet);
- if (nCreateTime <= 1) {
- // Cannot determine birthday information, so set the wallet birthday to
- // the beginning of time.
- nTimeFirstKey = 1;
- } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {
- nTimeFirstKey = nCreateTime;
- }
-}
-
-bool CWallet::AddCScript(const CScript& redeemScript)
-{
- WalletBatch batch(*database);
- return AddCScriptWithDB(batch, redeemScript);
-}
-
-bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
-{
- if (!FillableSigningProvider::AddCScript(redeemScript))
- return false;
- if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
- UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
- return true;
- }
- return false;
-}
-
-bool CWallet::LoadCScript(const CScript& redeemScript)
-{
- /* A sanity check was added in pull #3843 to avoid adding redeemScripts
- * that never can be redeemed. However, old wallets may still contain
- * these. Do not add them to the wallet and warn. */
- if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
- {
- std::string strAddr = EncodeDestination(ScriptHash(redeemScript));
- WalletLogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
- return true;
- }
-
- return FillableSigningProvider::AddCScript(redeemScript);
-}
-
-static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
-{
- std::vector<std::vector<unsigned char>> solutions;
- return Solver(dest, solutions) == TX_PUBKEY &&
- (pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
-}
-
-bool CWallet::AddWatchOnlyInMem(const CScript &dest)
-{
- LOCK(cs_KeyStore);
- setWatchOnly.insert(dest);
- CPubKey pubKey;
- if (ExtractPubKey(dest, pubKey)) {
- mapWatchKeys[pubKey.GetID()] = pubKey;
- ImplicitlyLearnRelatedKeyScripts(pubKey);
- }
- return true;
-}
-
-bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
-{
- if (!AddWatchOnlyInMem(dest))
- return false;
- const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
- UpdateTimeFirstKey(meta.nCreateTime);
- NotifyWatchonlyChanged(true);
- if (batch.WriteWatchOnly(dest, meta)) {
- UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
- return true;
- }
- return false;
-}
-
-bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time)
-{
- m_script_metadata[CScriptID(dest)].nCreateTime = create_time;
- return AddWatchOnlyWithDB(batch, dest);
-}
-
-bool CWallet::AddWatchOnly(const CScript& dest)
-{
- WalletBatch batch(*database);
- return AddWatchOnlyWithDB(batch, dest);
-}
-
-bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
-{
- m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime;
- return AddWatchOnly(dest);
-}
-
-bool CWallet::RemoveWatchOnly(const CScript &dest)
-{
- AssertLockHeld(cs_wallet);
- {
- LOCK(cs_KeyStore);
- setWatchOnly.erase(dest);
- CPubKey pubKey;
- if (ExtractPubKey(dest, pubKey)) {
- mapWatchKeys.erase(pubKey.GetID());
- }
- // Related CScripts are not removed; having superfluous scripts around is
- // harmless (see comment in ImplicitlyLearnRelatedKeyScripts).
- }
-
- if (!HaveWatchOnly())
- NotifyWatchonlyChanged(false);
- if (!WalletBatch(*database).EraseWatchOnly(dest))
- return false;
-
- return true;
-}
-
-bool CWallet::LoadWatchOnly(const CScript &dest)
-{
- return AddWatchOnlyInMem(dest);
-}
-
-bool CWallet::HaveWatchOnly(const CScript &dest) const
-{
- LOCK(cs_KeyStore);
- return setWatchOnly.count(dest) > 0;
-}
-
-bool CWallet::HaveWatchOnly() const
-{
- LOCK(cs_KeyStore);
- return (!setWatchOnly.empty());
-}
-
bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys)
{
CCrypter crypter;
@@ -1490,6 +1129,12 @@ isminetype CWallet::IsMine(const CTxOut& txout) const
return ::IsMine(*this, txout.scriptPubKey);
}
+isminetype IsMine(const CWallet& keystore, const CTxDestination& dest)
+{
+ CScript script = GetScriptForDestination(dest);
+ return IsMine(keystore, script);
+}
+
CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const
{
if (!MoneyRange(txout.nValue))
@@ -1601,94 +1246,6 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
return nChange;
}
-CPubKey CWallet::GenerateNewSeed()
-{
- assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
- CKey key;
- key.MakeNewKey(true);
- return DeriveNewSeed(key);
-}
-
-CPubKey CWallet::DeriveNewSeed(const CKey& key)
-{
- int64_t nCreationTime = GetTime();
- CKeyMetadata metadata(nCreationTime);
-
- // calculate the seed
- CPubKey seed = key.GetPubKey();
- assert(key.VerifyPubKey(seed));
-
- // set the hd keypath to "s" -> Seed, refers the seed to itself
- metadata.hdKeypath = "s";
- metadata.has_key_origin = false;
- metadata.hd_seed_id = seed.GetID();
-
- {
- LOCK(cs_wallet);
-
- // mem store the metadata
- mapKeyMetadata[seed.GetID()] = metadata;
-
- // write the key&metadata to the database
- if (!AddKeyPubKey(key, seed))
- throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
- }
-
- return seed;
-}
-
-void CWallet::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.seed_id = seed.GetID();
- SetHDChain(newHdChain, false);
- NotifyCanGetAddressesChanged();
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
-}
-
-void CWallet::SetHDChain(const CHDChain& chain, bool memonly)
-{
- LOCK(cs_wallet);
- if (!memonly && !WalletBatch(*database).WriteHDChain(chain))
- throw std::runtime_error(std::string(__func__) + ": writing chain failed");
-
- hdChain = chain;
-}
-
-bool CWallet::IsHDEnabled() const
-{
- return !hdChain.seed_id.IsNull();
-}
-
-bool CWallet::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);
-}
-
-bool CWallet::CanGetAddresses(bool internal)
-{
- LOCK(cs_wallet);
- // Check if the keypool has keys
- bool keypool_has_keys;
- if (internal && CanSupportFeature(FEATURE_HD_SPLIT)) {
- keypool_has_keys = setInternalKeyPool.size() > 0;
- } else {
- keypool_has_keys = KeypoolCountExternalKeys() > 0;
- }
- // If the keypool doesn't have keys, check if we can generate them
- if (!keypool_has_keys) {
- return CanGenerateKeys();
- }
- return keypool_has_keys;
-}
-
void CWallet::SetWalletFlag(uint64_t flags)
{
LOCK(cs_wallet);
@@ -1768,103 +1325,6 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
return true;
}
-bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
-{
- WalletBatch batch(*database);
- for (const auto& entry : scripts) {
- CScriptID id(entry);
- if (HaveCScript(id)) {
- WalletLogPrintf("Already have script %s, skipping\n", HexStr(entry));
- continue;
- }
- if (!AddCScriptWithDB(batch, entry)) {
- return false;
- }
-
- if (timestamp > 0) {
- m_script_metadata[CScriptID(entry)].nCreateTime = timestamp;
- }
- }
- if (timestamp > 0) {
- UpdateTimeFirstKey(timestamp);
- }
-
- return true;
-}
-
-bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
-{
- WalletBatch batch(*database);
- for (const auto& entry : privkey_map) {
- const CKey& key = entry.second;
- CPubKey pubkey = key.GetPubKey();
- const CKeyID& id = entry.first;
- assert(key.VerifyPubKey(pubkey));
- // Skip if we already have the key
- if (HaveKey(id)) {
- WalletLogPrintf("Already have key with pubkey %s, skipping\n", HexStr(pubkey));
- continue;
- }
- mapKeyMetadata[id].nCreateTime = timestamp;
- // If the private key is not present in the wallet, insert it.
- if (!AddKeyPubKeyWithDB(batch, key, pubkey)) {
- return false;
- }
- UpdateTimeFirstKey(timestamp);
- }
- 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)
-{
- WalletBatch batch(*database);
- for (const auto& entry : key_origins) {
- AddKeyOriginWithDB(batch, entry.second.first, entry.second.second);
- }
- for (const CKeyID& id : ordered_pubkeys) {
- auto entry = pubkey_map.find(id);
- if (entry == pubkey_map.end()) {
- continue;
- }
- const CPubKey& pubkey = entry->second;
- CPubKey temp;
- if (GetPubKey(id, temp)) {
- // Already have pubkey, skipping
- WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp));
- continue;
- }
- if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
- return false;
- }
- mapKeyMetadata[id].nCreateTime = timestamp;
-
- // Add to keypool only works with pubkeys
- if (add_keypool) {
- AddKeypoolPubkeyWithDB(pubkey, internal, batch);
- NotifyCanGetAddressesChanged();
- }
- }
- 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)
-{
- WalletBatch batch(*database);
- 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 (!AddWatchOnlyWithDB(batch, script, timestamp)) {
- return false;
- }
- }
- CTxDestination dest;
- ExtractDestination(script, dest);
- if (apply_label && IsValidDestination(dest)) {
- SetAddressBookWithDB(batch, dest, label, "receive");
- }
- }
- return true;
-}
-
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
@@ -3460,247 +2920,6 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
return WalletBatch(*database).EraseName(EncodeDestination(address));
}
-/**
- * Mark old keypool keys as used,
- * and generate all new keys
- */
-bool CWallet::NewKeyPool()
-{
- if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- return false;
- }
- {
- LOCK(cs_wallet);
- WalletBatch batch(*database);
-
- for (const int64_t nIndex : setInternalKeyPool) {
- batch.ErasePool(nIndex);
- }
- setInternalKeyPool.clear();
-
- for (const int64_t nIndex : setExternalKeyPool) {
- batch.ErasePool(nIndex);
- }
- setExternalKeyPool.clear();
-
- for (const int64_t nIndex : set_pre_split_keypool) {
- batch.ErasePool(nIndex);
- }
- set_pre_split_keypool.clear();
-
- m_pool_key_to_index.clear();
-
- if (!TopUpKeyPool()) {
- return false;
- }
- WalletLogPrintf("CWallet::NewKeyPool rewrote keypool\n");
- }
- return true;
-}
-
-size_t CWallet::KeypoolCountExternalKeys()
-{
- AssertLockHeld(cs_wallet);
- return setExternalKeyPool.size() + set_pre_split_keypool.size();
-}
-
-void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
-{
- AssertLockHeld(cs_wallet);
- if (keypool.m_pre_split) {
- set_pre_split_keypool.insert(nIndex);
- } else if (keypool.fInternal) {
- setInternalKeyPool.insert(nIndex);
- } else {
- setExternalKeyPool.insert(nIndex);
- }
- m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
- m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
-
- // If no metadata exists yet, create a default with the pool key's
- // creation time. Note that this may be overwritten by actually
- // stored metadata for that key later, which is fine.
- CKeyID keyid = keypool.vchPubKey.GetID();
- if (mapKeyMetadata.count(keyid) == 0)
- mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
-}
-
-bool CWallet::TopUpKeyPool(unsigned int kpSize)
-{
- if (!CanGenerateKeys()) {
- return false;
- }
- {
- LOCK(cs_wallet);
-
- if (IsLocked()) return false;
-
- // Top up key pool
- unsigned int nTargetSize;
- if (kpSize > 0)
- nTargetSize = kpSize;
- else
- nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
-
- // count amount of available keys (internal, external)
- // make sure the keypool of external and internal keys fits the user selected target (-keypool)
- 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))
- {
- // don't create extra internal keys
- missingInternal = 0;
- }
- bool internal = false;
- WalletBatch batch(*database);
- for (int64_t i = missingInternal + missingExternal; i--;)
- {
- if (i < missingInternal) {
- internal = true;
- }
-
- CPubKey pubkey(GenerateNewKey(batch, internal));
- AddKeypoolPubkeyWithDB(pubkey, internal, batch);
- }
- if (missingInternal + missingExternal > 0) {
- WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size());
- }
- }
- NotifyCanGetAddressesChanged();
- return true;
-}
-
-void CWallet::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?
- int64_t index = ++m_max_keypool_index;
- if (!batch.WritePool(index, CKeyPool(pubkey, internal))) {
- throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed");
- }
- if (internal) {
- setInternalKeyPool.insert(index);
- } else {
- setExternalKeyPool.insert(index);
- }
- m_pool_key_to_index[pubkey.GetID()] = index;
-}
-
-bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
-{
- nIndex = -1;
- keypool.vchPubKey = CPubKey();
- {
- LOCK(cs_wallet);
-
- TopUpKeyPool();
-
- bool fReturningInternal = fRequestedInternal;
- fReturningInternal &= (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) || 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;
-
- // Get the oldest key
- if (setKeyPool.empty()) {
- return false;
- }
-
- WalletBatch batch(*database);
-
- auto it = setKeyPool.begin();
- nIndex = *it;
- setKeyPool.erase(it);
- if (!batch.ReadPool(nIndex, keypool)) {
- throw std::runtime_error(std::string(__func__) + ": read failed");
- }
- CPubKey pk;
- if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) {
- throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
- }
- // If the key was pre-split keypool, we don't care about what type it is
- if (use_split_keypool && keypool.fInternal != fReturningInternal) {
- throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
- }
- if (!keypool.vchPubKey.IsValid()) {
- throw std::runtime_error(std::string(__func__) + ": keypool entry invalid");
- }
-
- m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
- WalletLogPrintf("keypool reserve %d\n", nIndex);
- }
- NotifyCanGetAddressesChanged();
- return true;
-}
-
-void CWallet::KeepKey(int64_t nIndex)
-{
- // Remove from key pool
- WalletBatch batch(*database);
- batch.ErasePool(nIndex);
- WalletLogPrintf("keypool keep %d\n", nIndex);
-}
-
-void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
-{
- // Return to key pool
- {
- LOCK(cs_wallet);
- if (fInternal) {
- setInternalKeyPool.insert(nIndex);
- } else if (!set_pre_split_keypool.empty()) {
- set_pre_split_keypool.insert(nIndex);
- } else {
- setExternalKeyPool.insert(nIndex);
- }
- m_pool_key_to_index[pubkey.GetID()] = nIndex;
- NotifyCanGetAddressesChanged();
- }
- WalletLogPrintf("keypool return %d\n", nIndex);
-}
-
-bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
-{
- if (!CanGetAddresses(internal)) {
- return false;
- }
-
- CKeyPool keypool;
- {
- LOCK(cs_wallet);
- int64_t nIndex;
- if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- if (IsLocked()) return false;
- WalletBatch batch(*database);
- result = GenerateNewKey(batch, internal);
- return true;
- }
- KeepKey(nIndex);
- result = keypool.vchPubKey;
- }
- return true;
-}
-
-bool CWallet::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
- CPubKey new_key;
- if (!GetKeyFromPool(new_key)) {
- error = "Error: Keypool ran out, please call keypoolrefill first";
- return false;
- }
- LearnRelatedScripts(new_key, type);
- dest = GetDestinationForKey(new_key, type);
-
- SetAddressBook(dest, label, "receive");
- return true;
-}
-
bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
error.clear();
@@ -3717,38 +2936,6 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des
return true;
}
-static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
- if (setKeyPool.empty()) {
- return GetTime();
- }
-
- CKeyPool keypool;
- int64_t nIndex = *(setKeyPool.begin());
- if (!batch.ReadPool(nIndex, keypool)) {
- throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
- }
- assert(keypool.vchPubKey.IsValid());
- return keypool.nTime;
-}
-
-int64_t CWallet::GetOldestKeyPoolTime()
-{
- LOCK(cs_wallet);
-
- WalletBatch batch(*database);
-
- // load oldest key from keypool, get time and return
- int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
- if (IsHDEnabled() && 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);
- }
- }
-
- return oldestKey;
-}
-
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain)
{
std::map<CTxDestination, CAmount> balances;
@@ -3937,30 +3124,6 @@ void ReserveDestination::ReturnDestination()
address = CNoDestination();
}
-void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
-{
- AssertLockHeld(cs_wallet);
- bool internal = setInternalKeyPool.count(keypool_id);
- if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(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);
- while (it != std::end(*setKeyPool)) {
- const int64_t& index = *(it);
- if (index > keypool_id) break; // set*KeyPool is ordered
-
- CKeyPool keypool;
- if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary
- m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
- }
- LearnAllRelatedScripts(keypool.vchPubKey);
- batch.ErasePool(index);
- WalletLogPrintf("keypool index %d removed\n", index);
- it = setKeyPool->erase(it);
- }
-}
-
void CWallet::LockCoin(const COutPoint& output)
{
AssertLockHeld(cs_wallet);
@@ -4156,24 +3319,6 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-void CWallet::MarkPreSplitKeys()
-{
- WalletBatch batch(*database);
- for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) {
- int64_t index = *it;
- CKeyPool keypool;
- if (!batch.ReadPool(index, keypool)) {
- throw std::runtime_error(std::string(__func__) + ": read keypool entry failed");
- }
- keypool.m_pre_split = true;
- if (!batch.WritePool(index, keypool)) {
- throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed");
- }
- set_pre_split_keypool.insert(index);
- it = setExternalKeyPool.erase(it);
- }
-}
-
bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::vector<std::string>& warnings)
{
// Do some checking on wallet path. It should be either a:
@@ -4650,23 +3795,6 @@ bool CWalletTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
return GetBlocksToMaturity(locked_chain) > 0;
}
-void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
-{
- if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
- CTxDestination witdest = WitnessV0KeyHash(key.GetID());
- CScript witprog = GetScriptForDestination(witdest);
- // Make sure the resulting program is solvable.
- assert(IsSolvable(*this, witprog));
- AddCScript(witprog);
- }
-}
-
-void CWallet::LearnAllRelatedScripts(const CPubKey& key)
-{
- // OutputType::P2SH_SEGWIT always adds all necessary scripts for all types.
- LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
-}
-
std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const {
std::vector<OutputGroup> groups;
std::map<CTxDestination, OutputGroup> gmap;
@@ -4697,35 +3825,6 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
return groups;
}
-bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
-{
- CKeyMetadata meta;
- {
- LOCK(cs_wallet);
- auto it = mapKeyMetadata.find(keyID);
- if (it != mapKeyMetadata.end()) {
- meta = it->second;
- }
- }
- if (meta.has_key_origin) {
- std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, info.fingerprint);
- info.path = meta.key_origin.path;
- } else { // Single pubkeys get the master fingerprint of themselves
- std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint);
- }
- return true;
-}
-
-bool CWallet::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);
- mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path;
- mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
- mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path);
- return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
-}
-
bool CWallet::SetCrypted()
{
LOCK(cs_KeyStore);
@@ -4759,169 +3858,3 @@ bool CWallet::Lock()
NotifyStatusChanged(this);
return true;
}
-
-bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
-{
- {
- LOCK(cs_KeyStore);
- if (!SetCrypted())
- return false;
-
- bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
- bool keyFail = false;
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
- for (; mi != mapCryptedKeys.end(); ++mi)
- {
- const CPubKey &vchPubKey = (*mi).second.first;
- const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
- CKey key;
- if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
- {
- keyFail = true;
- break;
- }
- keyPass = true;
- if (fDecryptionThoroughlyChecked)
- break;
- }
- if (keyPass && keyFail)
- {
- LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
- throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
- }
- if (keyFail || (!keyPass && !accept_no_keys))
- return false;
- vMasterKey = vMasterKeyIn;
- fDecryptionThoroughlyChecked = true;
- }
- NotifyStatusChanged(this);
- return true;
-}
-
-bool CWallet::HaveKey(const CKeyID &address) const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return FillableSigningProvider::HaveKey(address);
- }
- return mapCryptedKeys.count(address) > 0;
-}
-
-bool CWallet::GetKey(const CKeyID &address, CKey& keyOut) const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return FillableSigningProvider::GetKey(address, keyOut);
- }
-
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
- if (mi != mapCryptedKeys.end())
- {
- const CPubKey &vchPubKey = (*mi).second.first;
- const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
- return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
- }
- return false;
-}
-
-bool CWallet::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
-{
- LOCK(cs_KeyStore);
- WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
- if (it != mapWatchKeys.end()) {
- pubkey_out = it->second;
- return true;
- }
- return false;
-}
-
-bool CWallet::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) {
- return GetWatchPubKey(address, vchPubKeyOut);
- }
- return true;
- }
-
- CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
- if (mi != mapCryptedKeys.end())
- {
- vchPubKeyOut = (*mi).second.first;
- return true;
- }
- // Check for watch-only pubkeys
- return GetWatchPubKey(address, vchPubKeyOut);
-}
-
-std::set<CKeyID> CWallet::GetKeys() const
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return FillableSigningProvider::GetKeys();
- }
- std::set<CKeyID> set_address;
- for (const auto& mi : mapCryptedKeys) {
- set_address.insert(mi.first);
- }
- return set_address;
-}
-
-bool CWallet::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
-{
- LOCK(cs_KeyStore);
- if (!mapCryptedKeys.empty() || IsCrypted())
- return false;
-
- fUseCrypto = true;
- for (const KeyMap::value_type& mKey : mapKeys)
- {
- const CKey &key = mKey.second;
- CPubKey vchPubKey = key.GetPubKey();
- CKeyingMaterial vchSecret(key.begin(), key.end());
- std::vector<unsigned char> vchCryptedSecret;
- if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
- return false;
- if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
- return false;
- }
- mapKeys.clear();
- return true;
-}
-
-bool CWallet::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
-{
- LOCK(cs_KeyStore);
- if (!IsCrypted()) {
- return FillableSigningProvider::AddKeyPubKey(key, pubkey);
- }
-
- if (IsLocked()) {
- return false;
- }
-
- std::vector<unsigned char> vchCryptedSecret;
- CKeyingMaterial vchSecret(key.begin(), key.end());
- if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
- return false;
- }
-
- if (!AddCryptedKey(pubkey, vchCryptedSecret)) {
- return false;
- }
- return true;
-}
-
-
-bool CWallet::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
-{
- LOCK(cs_KeyStore);
- if (!SetCrypted()) {
- return false;
- }
-
- mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
- ImplicitlyLearnRelatedKeyScripts(vchPubKey);
- return true;
-}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 9ab9e283fa..7a0cdec5ef 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -57,8 +57,6 @@ enum class WalletCreationStatus {
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::shared_ptr<CWallet>& result);
-//! Default for -keypool
-static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
//! -fallbackfee default
@@ -123,99 +121,6 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
-/** A key from a CWallet's keypool
- *
- * The wallet holds one (for pre HD-split wallets) or several keypools. These
- * are sets of keys that have not yet been used to provide addresses or receive
- * change.
- *
- * The Bitcoin Core wallet was originally a collection of unrelated private
- * keys with their associated addresses. If a non-HD wallet generated a
- * key/address, gave that address out and then restored a backup from before
- * that key's generation, then any funds sent to that address would be
- * lost definitively.
- *
- * The keypool was implemented to avoid this scenario (commit: 10384941). The
- * wallet would generate a set of keys (100 by default). When a new public key
- * was required, either to give out as an address or to use in a change output,
- * it would be drawn from the keypool. The keypool would then be topped up to
- * maintain 100 keys. This ensured that as long as the wallet hadn't used more
- * than 100 keys since the previous backup, all funds would be safe, since a
- * restored wallet would be able to scan for all owned addresses.
- *
- * A keypool also allowed encrypted wallets to give out addresses without
- * having to be decrypted to generate a new private key.
- *
- * With the introduction of HD wallets (commit: f1902510), the keypool
- * essentially became an address look-ahead pool. Restoring old backups can no
- * longer definitively lose funds as long as the addresses used were from the
- * wallet's HD seed (since all private keys can be rederived from the seed).
- * However, if many addresses were used since the backup, then the wallet may
- * not know how far ahead in the HD chain to look for its addresses. The
- * keypool is used to implement a 'gap limit'. The keypool maintains a set of
- * keys (by default 1000) ahead of the last used key and scans for the
- * addresses of those keys. This avoids the risk of not seeing transactions
- * involving the wallet's addresses, or of re-using the same address.
- *
- * The HD-split wallet feature added a second keypool (commit: 02592f4c). There
- * is an external keypool (for addresses to hand out) and an internal keypool
- * (for change addresses).
- *
- * Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
- * stored as sets of indexes in the wallet (setInternalKeyPool,
- * setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
- * index (m_pool_key_to_index). The CKeyPool object is used to
- * serialize/deserialize the pool data to/from the database.
- */
-class CKeyPool
-{
-public:
- //! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
- int64_t nTime;
- //! The public key
- CPubKey vchPubKey;
- //! Whether this keypool entry is in the internal keypool (for change outputs)
- bool fInternal;
- //! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
- bool m_pre_split;
-
- CKeyPool();
- CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- int nVersion = s.GetVersion();
- if (!(s.GetType() & SER_GETHASH))
- READWRITE(nVersion);
- READWRITE(nTime);
- READWRITE(vchPubKey);
- if (ser_action.ForRead()) {
- try {
- READWRITE(fInternal);
- }
- catch (std::ios_base::failure&) {
- /* flag as external address if we can't read the internal boolean
- (this will be the case for any wallet before the HD chain split version) */
- fInternal = false;
- }
- try {
- READWRITE(m_pre_split);
- }
- catch (std::ios_base::failure&) {
- /* flag as postsplit address if we can't read the m_pre_split boolean
- (this will be the case for any wallet that upgrades to HD chain split)*/
- m_pre_split = false;
- }
- }
- else {
- READWRITE(fInternal);
- READWRITE(m_pre_split);
- }
- }
-};
-
/** A wrapper to reserve an address from a wallet
*
* ReserveDestination is used to reserve an address.
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index a9e6763c6d..edd8bab9ae 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -12,6 +12,7 @@
#include <sync.h>
#include <util/system.h>
#include <util/time.h>
+#include <wallet/scriptpubkeyman.h>
#include <wallet/wallet.h>
#include <atomic>