diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/crypter.cpp | 192 | ||||
-rw-r--r-- | src/wallet/crypter.h | 54 | ||||
-rw-r--r-- | src/wallet/ismine.cpp | 1 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 8 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 275 | ||||
-rw-r--r-- | src/wallet/wallet.h | 65 |
7 files changed, 337 insertions, 260 deletions
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index dd56ea10ab..0b76c1a0eb 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -107,8 +107,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM return true; } - -static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); @@ -118,7 +117,7 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); } -static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); @@ -128,7 +127,7 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<u return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); } -static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) +bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) { CKeyingMaterial vchSecret; if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) @@ -140,188 +139,3 @@ static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsi key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); return key.VerifyPubKey(vchPubKey); } - -bool CCryptoKeyStore::SetCrypted() -{ - LOCK(cs_KeyStore); - if (fUseCrypto) - return true; - if (!mapKeys.empty()) - return false; - fUseCrypto = true; - return true; -} - -bool CCryptoKeyStore::IsLocked() const -{ - if (!IsCrypted()) { - return false; - } - LOCK(cs_KeyStore); - return vMasterKey.empty(); -} - -bool CCryptoKeyStore::Lock() -{ - if (!SetCrypted()) - return false; - - { - LOCK(cs_KeyStore); - vMasterKey.clear(); - } - - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::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 CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) -{ - LOCK(cs_KeyStore); - if (!IsCrypted()) { - return CBasicKeyStore::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 CCryptoKeyStore::AddCryptedKey(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 CCryptoKeyStore::HaveKey(const CKeyID &address) const -{ - LOCK(cs_KeyStore); - if (!IsCrypted()) { - return CBasicKeyStore::HaveKey(address); - } - return mapCryptedKeys.count(address) > 0; -} - -bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const -{ - LOCK(cs_KeyStore); - if (!IsCrypted()) { - return CBasicKeyStore::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 CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const -{ - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - vchPubKeyOut = (*mi).second.first; - return true; - } - // Check for watch-only pubkeys - return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); -} - -std::set<CKeyID> CCryptoKeyStore::GetKeys() const -{ - LOCK(cs_KeyStore); - if (!IsCrypted()) { - return CBasicKeyStore::GetKeys(); - } - std::set<CKeyID> set_address; - for (const auto& mi : mapCryptedKeys) { - set_address.insert(mi.first); - } - return set_address; -} - -bool CCryptoKeyStore::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; -} diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 8e195ca8fa..17a4e9820c 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -5,9 +5,9 @@ #ifndef BITCOIN_WALLET_CRYPTER_H #define BITCOIN_WALLET_CRYPTER_H -#include <keystore.h> #include <serialize.h> #include <support/allocators/secure.h> +#include <script/signingprovider.h> #include <atomic> @@ -109,54 +109,8 @@ public: } }; -/** Keystore which keeps the private keys encrypted. - * It derives from the basic key store, which is used if no encryption is active. - */ -class CCryptoKeyStore : public CBasicKeyStore -{ -private: - - CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore); - - //! if fUseCrypto is true, mapKeys must be empty - //! if fUseCrypto is false, vMasterKey must be empty - std::atomic<bool> fUseCrypto; - - //! keeps track of whether Unlock has run a thorough check before - bool fDecryptionThoroughlyChecked; - -protected: - using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>; - - bool SetCrypted(); - - //! will encrypt previously unencrypted keys - bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); - - bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false); - CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); - -public: - CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false) - { - } - - bool IsCrypted() const { return fUseCrypto; } - bool IsLocked() const; - bool Lock(); - - virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; - bool HaveKey(const CKeyID &address) const override; - bool GetKey(const CKeyID &address, CKey& keyOut) const override; - bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; - std::set<CKeyID> GetKeys() const override; - - /** - * Wallet status (encrypted, locked) changed. - * Note: Called without locks held. - */ - boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; -}; +bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); +bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); +bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key); #endif // BITCOIN_WALLET_CRYPTER_H diff --git a/src/wallet/ismine.cpp b/src/wallet/ismine.cpp index 6138d4ae44..b7ef2d4490 100644 --- a/src/wallet/ismine.cpp +++ b/src/wallet/ismine.cpp @@ -8,6 +8,7 @@ #include <key.h> #include <script/script.h> #include <script/sign.h> +#include <script/signingprovider.h> #include <wallet/wallet.h> typedef std::vector<unsigned char> valtype; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 87ef58ee96..19becf93c7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -52,6 +52,14 @@ static inline bool GetAvoidReuseFlag(CWallet * const pwallet, const UniValue& pa return avoid_reuse; } +/** Checks if a CKey is in the given CWallet compressed or otherwise*/ +bool HaveKey(const CWallet& wallet, const CKey& key) +{ + CKey key2; + key2.Set(key.begin(), key.end(), !key.IsCompressed()); + return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID()); +} + bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name) { if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index d69e555e1d..609ae9a5e4 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -489,7 +489,7 @@ static size_t CalculateNestedKeyhashInputSize(bool use_max_sig) CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL; // Add inner-script to key store and key to watchonly - CBasicKeyStore keystore; + FillableSigningProvider keystore; keystore.AddCScript(inner_script); keystore.AddKeyPubKey(key, pubkey); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e53b433ca8..07fc8c35d5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -13,13 +13,13 @@ #include <interfaces/wallet.h> #include <key.h> #include <key_io.h> -#include <keystore.h> #include <policy/fees.h> #include <policy/policy.h> #include <primitives/block.h> #include <primitives/transaction.h> #include <script/descriptor.h> #include <script/script.h> +#include <script/signingprovider.h> #include <util/bip32.h> #include <util/error.h> #include <util/fees.h> @@ -358,14 +358,14 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C // Make sure we aren't adding private keys to private key disabled wallets assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); - // CCryptoKeyStore has no concept of wallet databases, but calls AddCryptedKey + // 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 (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { + if (!AddKeyPubKeyInner(secret, pubkey)) { if (needsDB) encrypted_batch = nullptr; return false; } @@ -400,7 +400,7 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { - if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) + if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) return false; { LOCK(cs_wallet); @@ -468,7 +468,7 @@ void CWallet::UpgradeKeyMetadata() bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { - return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); + return AddCryptedKeyInner(vchPubKey, vchCryptedSecret); } /** @@ -495,7 +495,7 @@ bool CWallet::AddCScript(const CScript& redeemScript) bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript) { - if (!CCryptoKeyStore::AddCScript(redeemScript)) + if (!FillableSigningProvider::AddCScript(redeemScript)) return false; if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); @@ -516,12 +516,40 @@ bool CWallet::LoadCScript(const CScript& redeemScript) return true; } - return CCryptoKeyStore::AddCScript(redeemScript); + return FillableSigningProvider::AddCScript(redeemScript); +} + +static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) +{ + //TODO: Use Solver to extract this? + CScript::const_iterator pc = dest.begin(); + opcodetype opcode; + std::vector<unsigned char> vch; + if (!dest.GetOp(pc, opcode, vch) || !CPubKey::ValidSize(vch)) + return false; + pubKeyOut = CPubKey(vch); + if (!pubKeyOut.IsFullyValid()) + return false; + if (!dest.GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG || dest.GetOp(pc, opcode, vch)) + return false; + return true; +} + +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 (!CCryptoKeyStore::AddWatchOnly(dest)) + if (!AddWatchOnlyInMem(dest)) return false; const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)]; UpdateTimeFirstKey(meta.nCreateTime); @@ -554,8 +582,17 @@ bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) bool CWallet::RemoveWatchOnly(const CScript &dest) { AssertLockHeld(cs_wallet); - if (!CCryptoKeyStore::RemoveWatchOnly(dest)) - return false; + { + 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)) @@ -566,7 +603,19 @@ bool CWallet::RemoveWatchOnly(const CScript &dest) bool CWallet::LoadWatchOnly(const CScript &dest) { - return CCryptoKeyStore::AddWatchOnly(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) @@ -582,7 +631,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_key return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) continue; // try another master key - if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys)) { + if (Unlock(_vMasterKey, accept_no_keys)) { // Now that we've unlocked, upgrade the key metadata UpgradeKeyMetadata(); return true; @@ -608,7 +657,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) return false; - if (CCryptoKeyStore::Unlock(_vMasterKey)) + if (Unlock(_vMasterKey)) { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); @@ -4681,3 +4730,203 @@ bool CWallet::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, cons mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path); return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); } + +bool CWallet::SetCrypted() +{ + LOCK(cs_KeyStore); + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + return true; +} + +bool CWallet::IsLocked() const +{ + if (!IsCrypted()) { + return false; + } + LOCK(cs_KeyStore); + return vMasterKey.empty(); +} + +bool CWallet::Lock() +{ + if (!SetCrypted()) + return false; + + { + LOCK(cs_KeyStore); + vMasterKey.clear(); + } + + 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 7e2230554d..5f226cf5c0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -35,6 +35,8 @@ #include <utility> #include <vector> +#include <boost/signals2/signal.hpp> + //! Explicitly unload and delete the wallet. //! Blocks the current thread after signaling the unload intent so that all //! wallet clients release the wallet. @@ -719,9 +721,35 @@ class WalletRescanReserver; //forward declarations for ScanForWalletTransactions * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ -class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notifications +class CWallet final : public FillableSigningProvider, private interfaces::Chain::Notifications { private: + CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore); + + //! if fUseCrypto is true, mapKeys must be empty + //! if fUseCrypto is false, vMasterKey must be empty + std::atomic<bool> fUseCrypto; + + //! keeps track of whether Unlock has run a thorough check before + bool fDecryptionThoroughlyChecked; + + using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>; + using WatchOnlySet = std::set<CScript>; + using WatchKeyMap = std::map<CKeyID, CPubKey>; + + bool SetCrypted(); + + //! will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + + bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false); + CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); + WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore); + WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore); + + bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); + bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey); + std::atomic<bool> fAbortRescan{false}; std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver std::atomic<int64_t> m_scanning_start{0}; @@ -804,8 +832,9 @@ private: * of the other AddWatchOnly which accepts a timestamp and sets * nTimeFirstKey more intelligently for more efficient rescans. */ - bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddWatchOnlyInMem(const CScript &dest); /** Add a KeyOriginInfo to the wallet */ bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info); @@ -892,7 +921,9 @@ public: /** Construct wallet with specified name and database implementation. */ CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) - : m_chain(chain), + : fUseCrypto(false), + fDecryptionThoroughlyChecked(false), + m_chain(chain), m_location(location), database(std::move(database)) { @@ -906,6 +937,10 @@ public: encrypted_batch = nullptr; } + bool IsCrypted() const { return fUseCrypto; } + bool IsLocked() const; + bool Lock(); + std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet); typedef std::multimap<int64_t, CWalletTx*> TxItems; @@ -988,7 +1023,7 @@ public: //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a key to the store, without saving it to disk (used by LoadWallet) - bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } + bool LoadKey(const CKey& key, const CPubKey &pubkey) { return AddKeyPubKeyInner(key, pubkey); } //! Load metadata (used by LoadWallet) void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -999,9 +1034,13 @@ public: void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds an encrypted key to the store, and saves it to disk. - bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override; + bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); + bool GetKey(const CKeyID &address, CKey& keyOut) const override; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; + bool HaveKey(const CKeyID &address) const override; + std::set<CKeyID> GetKeys() const override; bool AddCScript(const CScript& redeemScript) override; bool LoadCScript(const CScript& redeemScript); @@ -1018,9 +1057,15 @@ public: //! Adds a watch-only address to the store, and saves it to disk. bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool RemoveWatchOnly(const CScript &dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool RemoveWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) bool LoadWatchOnly(const CScript &dest); + //! Returns whether the watch-only script is in the wallet + bool HaveWatchOnly(const CScript &dest) const; + //! Returns whether there are any watch-only things in the wallet + bool HaveWatchOnly() const; + //! Fetches a pubkey from mapWatchKeys if it exists there + bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const; //! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock(). int64_t nRelockTime = 0; @@ -1247,6 +1292,12 @@ public: /** Keypool has new keys */ boost::signals2::signal<void ()> NotifyCanGetAddressesChanged; + /** + * Wallet status (encrypted, locked) changed. + * Note: Called without locks held. + */ + boost::signals2::signal<void (CWallet* wallet)> NotifyStatusChanged; + /** Inquire whether this wallet broadcasts transactions. */ bool GetBroadcastTransactions() const { return fBroadcastTransactions; } /** Set whether this wallet broadcasts transactions. */ @@ -1311,7 +1362,7 @@ public: /** * Explicitly make the wallet learn the related scripts for outputs to the * given key. This is purely to make the wallet file compatible with older - * software, as CBasicKeyStore automatically does this implicitly for all + * software, as FillableSigningProvider automatically does this implicitly for all * keys now. */ void LearnRelatedScripts(const CPubKey& key, OutputType); |