Merge bitcoin#17304: refactor: Move many functions into LegacyScriptPubKeyMan and further separate it from CWallet

* MOVEONLY: Reorder LegacyScriptPubKeyMan methods

Can verify move-only with:

    git log -p -n1 --color-moved

This commit is move-only and doesn't change code or affect behavior.

* Refactor: Declare LegacyScriptPubKeyMan methods as virtual

This commit does not change behavior.

* Refactor: Add new ScriptPubKeyMan virtual methods

This commit does not change behavior.

* Refactor: Move SetAddressBook call out of LegacyScriptPubKeyMan::GetNewDestination

This commit does not change behavior.

* Refactor: Move SetWalletFlag out of LegacyScriptPubKeyMan::UpgradeKeyMetadata

This commit does not change behavior.

* Remove SetWalletFlag from WalletStorage

SetWalletFlag is unused.

Does not change any behavior

* Refactor: Remove UnsetWalletFlag call from LegacyScriptPubKeyMan::SetHDSeed

This commit does not change behavior.

* refactor: Replace UnsetWalletFlagWithDB with UnsetBlankWalletFlag in ScriptPubKeyMan

ScriptPubKeyMan is only using UnsetWalletFlagWithDB to unset the blank
wallet flag. Just make that it's own function and not expose the flag
writing directly.

This does not change behavior.

* Refactor: Move SetAddressBookWithDB call out of LegacyScriptPubKeyMan::ImportScriptPubKeys

This commit does not change behavior.

* Refactor: Move LoadKey LegacyScriptPubKeyMan method definition

This commit does not change behavior.

* Refactor: Move GetMetadata code out of getaddressinfo

Easier to review ignoring whitespace:

    git log -p -n1 -w

This commit does not change behavior.

* Refactor: Move MarkUnusedAddresses code out of CWallet::AddToWalletIfInvolvingMe

This commit does not change behavior.

* Refactor: Move HavePrivateKeys code out of CWallet::CreateWalletFromFile

This commit does not change behavior.

* Refactor: Move RewriteDB code out of CWallet

This commit does not change behavior.

* Refactor: Move GetKeypoolSize code out of CWallet

This commit does not change behavior.

* Refactor: Move nTimeFirstKey accesses out of CWallet

This commit does not change behavior.

* Re-order methods of scriptpubkeyman for easier backporting changes in future

* Fixup for missing cs_wallet lock:

```
wallet/wallet.cpp:4536:41: error: calling function 'GetTimeFirstKey' requires holding mutex 'spk_man->cs_wallet' exclusively [-Werror,-Wthread-safety-analysis]
                int64_t time = spk_man->GetTimeFirstKey();
                                        ^
wallet/wallet.cpp:4570:106: error: calling function 'GetTimeFirstKey' requires holding mutex 'walletInstance->m_spk_man->cs_wallet' exclusively [-Werror,-Wthread-safety-analysis]
        walletInstance->WalletLogPrintf("nTimeFirstKey = %u\n",               walletInstance->m_spk_man->GetTimeFirstKey());
```

* Fix 2 locks

* more of "refactor: Replace UnsetWalletFlagWithDB with UnsetBlankWalletFlag in ScriptPubKeyMan"

* Refactoring GetOldestKeyInPool -> GetOldestKeyTimeInPool, partial bitcoin#10235

Co-authored-by: Andrew Chow <achow101-github@achow101.com>
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
Konstantin Akimov 2022-09-20 14:31:09 +07:00 committed by GitHub
parent 7228d918f6
commit 9845f8c992
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 419 additions and 265 deletions

View File

@ -3617,34 +3617,31 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
ret.pushKV("label", pwallet->mapAddressBook[dest].name); ret.pushKV("label", pwallet->mapAddressBook[dest].name);
} }
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
const CKeyMetadata* meta = nullptr; ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
const CKeyID *key_id = boost::get<CKeyID>(&dest); if (spk_man) {
if (key_id != nullptr && !key_id->IsNull()) { const CKeyID *key_id = boost::get<CKeyID>(&dest);
auto it = pwallet->mapKeyMetadata.find(*key_id); const CKeyMetadata* meta = nullptr;
if (it != pwallet->mapKeyMetadata.end()) { if (key_id != nullptr && !key_id->IsNull()) {
meta = &it->second; meta = spk_man->GetMetadata(*key_id);
} }
} if (!meta) {
if (!meta) { meta = spk_man->GetMetadata(CScriptID(scriptPubKey));
auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
if (it != pwallet->m_script_metadata.end()) {
meta = &it->second;
} }
} if (meta) {
if (meta) { ret.pushKV("timestamp", meta->nCreateTime);
ret.pushKV("timestamp", meta->nCreateTime); CHDChain hdChainCurrent;
CHDChain hdChainCurrent; LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan(); if (spk_man != nullptr) {
if (spk_man != nullptr) { LOCK(pwallet->cs_KeyStore);
LOCK(pwallet->cs_KeyStore); AssertLockHeld(spk_man->cs_KeyStore);
AssertLockHeld(spk_man->cs_KeyStore); if (key_id && pwallet->mapHdPubKeys.count(*key_id) && spk_man->GetHDChain(hdChainCurrent)) {
if (key_id && pwallet->mapHdPubKeys.count(*key_id) && spk_man->GetHDChain(hdChainCurrent)) { ret.pushKV("hdchainid", hdChainCurrent.GetID().GetHex());
ret.pushKV("hdchainid", hdChainCurrent.GetID().GetHex()); }
}
if (meta->has_key_origin) {
ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
} }
}
if (meta->has_key_origin) {
ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
ret.pushKV("hdmasterfingerprint", HexStr(meta->key_origin.fingerprint));
} }
} }

View File

@ -12,9 +12,8 @@
#include <wallet/scriptpubkeyman.h> #include <wallet/scriptpubkeyman.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
bool LegacyScriptPubKeyMan::GetNewDestination(const std::string label, CTxDestination& dest, std::string& error) bool LegacyScriptPubKeyMan::GetNewDestination(CTxDestination& dest, std::string& error)
{ {
LOCK(cs_wallet);
error.clear(); error.clear();
TopUpKeyPool(); TopUpKeyPool();
@ -26,8 +25,6 @@ bool LegacyScriptPubKeyMan::GetNewDestination(const std::string label, CTxDestin
} }
//LearnRelatedScripts(new_key); //LearnRelatedScripts(new_key);
dest = new_key.GetID(); dest = new_key.GetID();
m_wallet.SetAddressBook(dest, label, "receive");
return true; return true;
} }
@ -266,6 +263,62 @@ bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
return true; return true;
} }
bool LegacyScriptPubKeyMan::GetReservedDestination(bool internal, int64_t& index, CKeyPool& keypool)
{
{
if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
return false;
}
}
return true;
}
void LegacyScriptPubKeyMan::KeepDestination(int64_t index)
{
KeepKey(index);
}
void LegacyScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey)
{
ReturnKey(index, internal, pubkey);
}
bool LegacyScriptPubKeyMan::TopUp(unsigned int size)
{
return TopUpKeyPool(size);
}
void LegacyScriptPubKeyMan::MarkUnusedAddresses(WalletBatch &batch, const CScript& script, const uint256& hashBlock)
{
AssertLockHeld(cs_wallet);
// extract addresses and check if they match with an unused keypool key
for (const auto& keyid : GetAffectedKeys(script, *this)) {
std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) {
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
MarkReserveKeysAsUsed(mi->second);
if (!TopUpKeyPool()) {
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
}
}
if (!hashBlock.IsNull()) {
int64_t block_time;
bool found_block = m_wallet.chain().findBlock(hashBlock, nullptr /* block */, &block_time);
assert(found_block);
if (mapKeyMetadata[keyid].nCreateTime > block_time) {
WalletLogPrintf("%s: Found a key which appears to be used earlier than we expected, updating metadata\n", __func__);
CPubKey vchPubKey;
bool res = GetPubKey(keyid, vchPubKey);
assert(res); // this should never fail
mapKeyMetadata[keyid].nCreateTime = block_time;
batch.WriteKeyMetadata(mapKeyMetadata[keyid], vchPubKey, true);
UpdateTimeFirstKey(block_time);
}
}
}
}
void LegacyScriptPubKeyMan::UpgradeKeyMetadata() void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
{ {
AssertLockHeld(cs_wallet); // mapKeyMetadata AssertLockHeld(cs_wallet); // mapKeyMetadata
@ -314,8 +367,6 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
} }
} }
} }
batch.reset(); //write before setting the flag
m_storage.SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
} }
void LegacyScriptPubKeyMan::GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase) void LegacyScriptPubKeyMan::GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase)
@ -421,7 +472,7 @@ bool LegacyScriptPubKeyMan::SetHDChain(WalletBatch &batch, const CHDChain& chain
throw std::runtime_error(std::string(__func__) + ": WriteHDChain failed"); throw std::runtime_error(std::string(__func__) + ": WriteHDChain failed");
} }
m_wallet.UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET); m_storage.UnsetBlankWalletFlag(batch);
} }
return true; return true;
@ -442,7 +493,7 @@ bool LegacyScriptPubKeyMan::SetCryptedHDChain(WalletBatch &batch, const CHDChain
if (!batch.WriteCryptedHDChain(chain)) if (!batch.WriteCryptedHDChain(chain))
throw std::runtime_error(std::string(__func__) + ": WriteCryptedHDChain failed"); throw std::runtime_error(std::string(__func__) + ": WriteCryptedHDChain failed");
} }
m_wallet.UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET); m_storage.UnsetBlankWalletFlag(batch);
} }
return true; return true;
@ -615,7 +666,30 @@ bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal)
return keypool_has_keys; return keypool_has_keys;
} }
static int64_t GetOldestKeyInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
bool LegacyScriptPubKeyMan::HavePrivateKeys() const
{
LOCK(cs_KeyStore);
return !mapKeys.empty() || !mapCryptedKeys.empty();
}
void LegacyScriptPubKeyMan::RewriteDB()
{
AssertLockHeld(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) {
if (setKeyPool.empty()) {
// if the keypool is empty, return <NOW>
return GetTime();
}
CKeyPool keypool; CKeyPool keypool;
int64_t nIndex = *(setKeyPool.begin()); int64_t nIndex = *(setKeyPool.begin());
if (!batch.ReadPool(nIndex, keypool)) { if (!batch.ReadPool(nIndex, keypool)) {
@ -629,19 +703,11 @@ int64_t LegacyScriptPubKeyMan::GetOldestKeyPoolTime()
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// if the keypool is empty, return <NOW>
if (setExternalKeyPool.empty() && setInternalKeyPool.empty())
return GetTime();
WalletBatch batch(m_storage.GetDatabase()); WalletBatch batch(m_storage.GetDatabase());
int64_t oldestKey = -1; int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch);
// load oldest key from keypool, get time and return if (IsHDEnabled()) {
if (!setInternalKeyPool.empty()) { oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey);
oldestKey = std::max(GetOldestKeyInPool(setInternalKeyPool, batch), oldestKey);
}
if (!setExternalKeyPool.empty()) {
oldestKey = std::max(GetOldestKeyInPool(setExternalKeyPool, batch), oldestKey);
} }
return oldestKey; return oldestKey;
} }
@ -658,6 +724,33 @@ size_t LegacyScriptPubKeyMan::KeypoolCountInternalKeys()
return setInternalKeyPool.size(); return setInternalKeyPool.size();
} }
unsigned int LegacyScriptPubKeyMan::GetKeyPoolSize() const
{
AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const
{
AssertLockHeld(cs_wallet);
return nTimeFirstKey;
}
const CKeyMetadata* LegacyScriptPubKeyMan::GetMetadata(uint160 id) const
{
AssertLockHeld(cs_wallet);
auto it = mapKeyMetadata.find(CKeyID(id));
if (it != mapKeyMetadata.end()) {
return &it->second;
} else {
auto it2 = m_script_metadata.find(CScriptID(id));
if (it2 != m_script_metadata.end()) {
return &it2->second;
}
}
return nullptr;
}
/** /**
* Update wallet first key creation time. This should be called whenever keys * Update wallet first key creation time. This should be called whenever keys
* are added to the wallet, with the oldest key creation time. * are added to the wallet, with the oldest key creation time.
@ -674,10 +767,9 @@ void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime)
} }
} }
int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const bool LegacyScriptPubKeyMan::LoadKey(const CKey& key, const CPubKey &pubkey)
{ {
AssertLockHeld(cs_wallet); return AddKeyPubKeyInner(key, pubkey);
return nTimeFirstKey;
} }
bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
@ -722,7 +814,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& s
secret.GetPrivKey(), secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]); mapKeyMetadata[pubkey.GetID()]);
} }
m_wallet.UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET); m_storage.UnsetBlankWalletFlag(batch);
return true; return true;
} }
@ -932,7 +1024,7 @@ bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript
UpdateTimeFirstKey(meta.nCreateTime); UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true); NotifyWatchonlyChanged(true);
if (batch.WriteWatchOnly(dest, meta)) { if (batch.WriteWatchOnly(dest, meta)) {
m_wallet.UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET); m_storage.UnsetBlankWalletFlag(batch);
return true; return true;
} }
return false; return false;
@ -1015,7 +1107,7 @@ bool LegacyScriptPubKeyMan::AddHDPubKey(WalletBatch &batch, const CExtPubKey &ex
if (!batch.WriteHDPubKey(hdPubKey, mapKeyMetadata[extPubKey.pubkey.GetID()])) { if (!batch.WriteHDPubKey(hdPubKey, mapKeyMetadata[extPubKey.pubkey.GetID()])) {
return false; return false;
} }
m_wallet.UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET); m_storage.UnsetBlankWalletFlag(batch);
return true; return true;
} }
@ -1331,12 +1423,14 @@ bool LegacyScriptPubKeyMan::TopUpKeyPool(unsigned int kpSize)
return true; return true;
} }
/*
void LegacyScriptPubKeyMan::AddKeypoolPubkey(const CPubKey& pubkey, const bool internal) void LegacyScriptPubKeyMan::AddKeypoolPubkey(const CPubKey& pubkey, const bool internal)
{ {
WalletBatch batch(m_storage.GetDatabase()); WalletBatch batch(m_storage.GetDatabase());
AddKeypoolPubkeyWithDB(pubkey, internal, batch); AddKeypoolPubkeyWithDB(pubkey, internal, batch);
NotifyCanGetAddressesChanged(); NotifyCanGetAddressesChanged();
} }
*/
void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch) void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch)
{ {
@ -1496,7 +1590,7 @@ bool LegacyScriptPubKeyMan::AddCScriptWithDB(WalletBatch& batch, const CScript&
if (!FillableSigningProvider::AddCScript(redeemScript)) if (!FillableSigningProvider::AddCScript(redeemScript))
return false; return false;
if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
m_wallet.UnsetWalletFlag(batch, WALLET_FLAG_BLANK_WALLET); m_storage.UnsetBlankWalletFlag(batch);
return true; return true;
} }
return false; return false;
@ -1580,7 +1674,7 @@ bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector<CKeyID>& ordered_pub
return true; return true;
} }
bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set<CScript>& script_pub_keys, const bool have_solving_data, const int64_t timestamp)
{ {
WalletBatch batch(m_storage.GetDatabase()); WalletBatch batch(m_storage.GetDatabase());
for (const CScript& script : script_pub_keys) { for (const CScript& script : script_pub_keys) {
@ -1589,11 +1683,6 @@ bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::string& label, const
return false; return false;
} }
} }
CTxDestination dest;
ExtractDestination(script, dest);
if (apply_label && IsValidDestination(dest)) {
m_wallet.SetAddressBookWithDB(batch, dest, label, "receive");
}
} }
return true; return true;
} }

View File

@ -26,7 +26,7 @@ public:
virtual const std::string GetDisplayName() const = 0; virtual const std::string GetDisplayName() const = 0;
virtual WalletDatabase& GetDatabase() = 0; virtual WalletDatabase& GetDatabase() = 0;
virtual bool IsWalletFlagSet(uint64_t) const = 0; virtual bool IsWalletFlagSet(uint64_t) const = 0;
virtual void SetWalletFlag(uint64_t) = 0; virtual void UnsetBlankWalletFlag(WalletBatch&) = 0;
virtual bool CanSupportFeature(enum WalletFeature) const = 0; virtual bool CanSupportFeature(enum WalletFeature) const = 0;
virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr, bool = false) = 0; virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr, bool = false) = 0;
virtual bool IsLocked(bool fForMixing = false) const = 0; virtual bool IsLocked(bool fForMixing = false) const = 0;
@ -35,6 +35,8 @@ public:
//! Default for -keypool //! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider);
/** A key from a CWallet's keypool /** A key from a CWallet's keypool
* *
* The wallet holds several keypools. These are sets of keys that have not * The wallet holds several keypools. These are sets of keys that have not
@ -132,31 +134,86 @@ protected:
public: public:
ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {} ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {}
virtual ~ScriptPubKeyMan() {};
virtual bool GetNewDestination(CTxDestination& dest, std::string& error) { return false; }
virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; }
virtual isminetype IsMine(const CTxDestination& dest) const { return ISMINE_NO; }
virtual bool GetReservedDestination(bool internal, int64_t& index, CKeyPool& keypool) { return false; }
virtual void KeepDestination(int64_t index) {}
virtual void ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) {}
virtual bool TopUp(unsigned int size = 0) { return false; }
//! Mark unused addresses as being used
virtual void MarkUnusedAddresses(WalletBatch &batch, const CScript& script, const uint256& hashBlock) {}
/* Returns true if HD is enabled */
virtual bool IsHDEnabled() const { return false; }
/* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
virtual bool CanGetAddresses(bool internal = false) { return false; }
virtual bool HavePrivateKeys() const { return false; }
//! The action to do when the DB needs rewrite
virtual void RewriteDB() {}
virtual int64_t GetOldestKeyPoolTime() { return GetTime(); }
virtual size_t KeypoolCountExternalKeys() { return 0; }
virtual size_t KeypoolCountInternalKeys() { return 0; }
virtual unsigned int GetKeyPoolSize() const { return 0; }
virtual int64_t GetTimeFirstKey() const { return 0; }
virtual const CKeyMetadata* GetMetadata(uint160 id) const { return nullptr; }
}; };
class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
{ {
private: private:
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
using WatchOnlySet = std::set<CScript>; using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>; using WatchKeyMap = std::map<CKeyID, CPubKey>;
using HDPubKeyMap = std::map<CKeyID, CHDPubKey>; using HDPubKeyMap = std::map<CKeyID, CHDPubKey>;
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr;
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore); WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore); WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
HDPubKeyMap mapHdPubKeys GUARDED_BY(cs_KeyStore); //<! memory map of HD extended pubkeys HDPubKeyMap mapHdPubKeys GUARDED_BY(cs_KeyStore); //<! memory map of HD extended pubkeys
int64_t nTimeFirstKey GUARDED_BY(cs_wallet) = 0;
bool HaveKeyInner(const CKeyID &address) const; bool HaveKeyInner(const CKeyID &address) const;
bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey); bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool GetKeyInner(const CKeyID &address, CKey& keyOut) const; bool GetKeyInner(const CKeyID &address, CKey& keyOut) const;
bool GetPubKeyInner(const CKeyID &address, CPubKey& vchPubKeyOut) const; bool GetPubKeyInner(const CKeyID &address, CPubKey& vchPubKeyOut) const;
WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr; /**
* Private version of AddWatchOnly method which does not accept a
* timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if
* the watch key did not previously have a timestamp associated with it.
* Because this is an inherited virtual method, it is accessible despite
* being marked private, but it is marked private anyway to encourage use
* of the other AddWatchOnly which accepts a timestamp and sets
* nTimeFirstKey more intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyInMem(const CScript &dest);
void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
/* the HD chain data model (external chain counters) */ /* the HD chain data model (external chain counters) */
CHDChain hdChain GUARDED_BY(cs_KeyStore); CHDChain hdChain GUARDED_BY(cs_KeyStore);
@ -170,105 +227,8 @@ private:
int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index; std::map<CKeyID, int64_t> m_pool_key_to_index;
int64_t nTimeFirstKey GUARDED_BY(cs_wallet) = 0; //! Fetches a key from the keypool
bool GetKeyFromPool(CPubKey &key, bool fInternal /*= false*/);
/**
* Private version of AddWatchOnly method which does not accept a
* timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if
* the watch key did not previously have a timestamp associated with it.
* Because this is an inherited virtual method, it is accessible despite
* being marked private, but it is marked private anyway to encourage use
* of the other AddWatchOnly which accepts a timestamp and sets
* nTimeFirstKey more intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyInMem(const CScript &dest);
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
public:
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a script to the store and saves it to disk
bool AddCScriptWithDB(WalletBatch& batch, const CScript& script);
public:
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_wallet);
// Map from Script ID to key metadata (for watch-only keys).
std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet);
bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, bool overwrite);
/**
* keystore implementation
* Generate a new key
*/
CPubKey GenerateNewKey(WalletBatch& batch, uint32_t nAccountIndex, bool fInternal /*= false*/) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return AddKeyPubKeyInner(key, pubkey); }
//! Load metadata (used by LoadWallet)
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
int64_t GetTimeFirstKey() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! GetKey implementation that can derive a HD private key on the fly
bool GetKey(const CKeyID &address, CKey& keyOut) const override;
//! GetPubKey implementation that also checks the mapHdPubKeys
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
//! HaveKey implementation that also checks the mapHdPubKeys
bool HaveKey(const CKeyID &address) const override;
//! Adds a HDPubKey into the wallet(database)
bool AddHDPubKey(WalletBatch &batch, const CExtPubKey &extPubKey, bool fInternal);
//! loads a HDPubKey into the wallets memory
bool LoadHDPubKey(const CHDPubKey &hdPubKey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::set<CKeyID> GetKeys() const override;
bool AddCScript(const CScript& redeemScript) override;
bool LoadCScript(const CScript& redeemScript);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool RemoveWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
//! Returns whether the watch-only script is in the wallet
bool HaveWatchOnly(const CScript &dest) const;
//! Returns whether there are any watch-only things in the wallet
bool HaveWatchOnly() const;
//! Fetches a pubkey from mapWatchKeys if it exists there
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
size_t KeypoolCountInternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
void AddKeypoolPubkey(const CPubKey& pubkey, const bool internal);
/** /**
* Reserves a key from the keypool and sets nIndex to its index * Reserves a key from the keypool and sets nIndex to its index
* *
@ -284,19 +244,136 @@ public:
* or external keypool * or external keypool
*/ */
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
void KeepKey(int64_t nIndex); void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey);
//! Fetches a key from the keypool
bool GetKeyFromPool(CPubKey &key, bool internal /* = false */);
int64_t GetOldestKeyPoolTime();
public:
bool GetNewDestination(CTxDestination& dest, std::string& error) override;
isminetype IsMine(const CScript& script) const override;
isminetype IsMine(const CTxDestination& dest) const override;
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool GetReservedDestination(bool internal, int64_t& index, CKeyPool& keypool) override;
void KeepDestination(int64_t index) override;
void ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) override;
bool TopUp(unsigned int size = 0) override;
void MarkUnusedAddresses(WalletBatch &batch, const CScript& script, const uint256& hashBlock) override;
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Returns true if HD is enabled */
bool IsHDEnabled() const override;
bool HavePrivateKeys() const override;
void RewriteDB() override;
int64_t GetOldestKeyPoolTime() override;
size_t KeypoolCountExternalKeys() override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
size_t KeypoolCountInternalKeys() override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int GetKeyPoolSize() const override;
int64_t GetTimeFirstKey() const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const CKeyMetadata* GetMetadata(uint160 id) const override;
bool CanGetAddresses(bool internal = false) override;
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_wallet);
// Map from Script ID to key metadata (for watch-only keys).
std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet);
//! Adds a script to the store and saves it to disk
bool AddCScriptWithDB(WalletBatch& batch, const CScript& script);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a 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);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a CScript to the store
bool LoadCScript(const CScript& redeemScript);
//! Load metadata (used by LoadWallet)
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, bool overwrite);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Generate a new key
CPubKey GenerateNewKey(WalletBatch& batch, uint32_t nAccountIndex, bool fInternal /*= false*/) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Set the HD chain model (chain child index counters) */
bool SetHDChain(WalletBatch &batch, const CHDChain& chain, bool memonly);
bool SetCryptedHDChain(WalletBatch &batch, const CHDChain& chain, bool memonly);
/** /**
* Marks all keys in the keypool up to and including reserve_key as used. * Set the HD chain model (chain child index counters) using temporary wallet db object
* which causes db flush every time these methods are used
*/ */
void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SetHDChainSingle(const CHDChain& chain, bool memonly);
bool SetCryptedHDChainSingle(const CHDChain& chain, bool memonly);
isminetype IsMine(const CScript& script) const; //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
isminetype IsMine(const CTxDestination& dest) const; 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;
//! Remove a watch only script from the keystore
bool RemoveWatchOnly(const CScript &dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Fetches a pubkey from mapWatchKeys if it exists there
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
/* SigningProvider overrides */
//! HaveKey implementation that also checks the mapHdPubKeys
bool HaveKey(const CKeyID &address) const override;
//! GetKey implementation that can derive a HD private key on the fly
bool GetKey(const CKeyID &address, CKey& keyOut) const override;
//! GetPubKey implementation that also checks the mapHdPubKeys
bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
bool AddCScript(const CScript& redeemScript) override;
/** Implement lookup of key origin information through wallet key metadata. */
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
bool NewKeyPool();
// Seems as not used now anywhere in code
// void AddKeypoolPubkey(const CPubKey& pubkey, const bool internal);
bool ImportScripts(const std::set<CScript> scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportScriptPubKeys(const std::set<CScript>& script_pub_keys, const bool have_solving_data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Returns true if the wallet can generate new keys */
bool CanGenerateKeys();
//! Adds a HDPubKey into the wallet(database)
bool AddHDPubKey(WalletBatch &batch, const CExtPubKey &extPubKey, bool fInternal);
//! loads a HDPubKey into the wallets memory
bool LoadHDPubKey(const CHDPubKey &hdPubKey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* HD Wallet Functions * HD Wallet Functions
@ -309,29 +386,10 @@ public:
bool SetCryptedHDChain(const CHDChain& chain); bool SetCryptedHDChain(const CHDChain& chain);
bool GetDecryptedHDChain(CHDChain& hdChainRet); bool GetDecryptedHDChain(CHDChain& hdChainRet);
/* Returns true if HD is enabled */
bool IsHDEnabled() const;
/* Returns true if the wallet can generate new keys */
bool CanGenerateKeys();
/* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
bool CanGetAddresses(bool internal = false);
/* Generates a new HD chain */ /* Generates a new HD chain */
void GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase); void GenerateNewHDChain(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase);
bool GenerateNewHDChainEncrypted(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, const SecureString& secureWalletPassphrase); bool GenerateNewHDChainEncrypted(const SecureString& secureMnemonic, const SecureString& secureMnemonicPassphrase, const SecureString& secureWalletPassphrase);
/* Set the HD chain model (chain child index counters) */
bool SetHDChain(WalletBatch &batch, const CHDChain& chain, bool memonly);
bool SetCryptedHDChain(WalletBatch &batch, const CHDChain& chain, bool memonly);
/**
* Set the HD chain model (chain child index counters) using temporary wallet db object
* which causes db flush every time these methods are used
*/
bool SetHDChainSingle(const CHDChain& chain, bool memonly);
bool SetCryptedHDChainSingle(const CHDChain& chain, bool memonly);
/** /**
* Explicitly make the wallet learn the related scripts for outputs to the * 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 * given key. This is purely to make the wallet file compatible with older
@ -346,13 +404,13 @@ public:
*/ */
// void LearnAllRelatedScripts(const CPubKey& key); // void LearnAllRelatedScripts(const CPubKey& key);
/** Implement lookup of key origin information through wallet key metadata. */ /**
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; * Marks all keys in the keypool up to and including reserve_key as used.
*/
void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
bool GetNewDestination(const std::string label, CTxDestination& dest, std::string& error);
/** Add a KeyOriginInfo to the wallet */ std::set<CKeyID> GetKeys() const override;
bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
// Temporary CWallet accessors and aliases. // Temporary CWallet accessors and aliases.
friend class CWallet; friend class CWallet;

View File

@ -337,8 +337,6 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); 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);
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
@ -350,10 +348,15 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
void CWallet::UpgradeKeyMetadata() void CWallet::UpgradeKeyMetadata()
{ {
if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
return;
}
if (m_spk_man) { if (m_spk_man) {
AssertLockHeld(m_spk_man->cs_wallet); AssertLockHeld(m_spk_man->cs_wallet);
m_spk_man->UpgradeKeyMetadata(); m_spk_man->UpgradeKeyMetadata();
} }
SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
} }
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
@ -975,33 +978,8 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
WalletBatch batch(*database); WalletBatch batch(*database);
// loop though all outputs // loop though all outputs
for (const CTxOut& txout: tx.vout) { for (const CTxOut& txout: tx.vout) {
// extract addresses, check if they match with an unused keypool key, update metadata if needed if (auto spk_man = m_spk_man.get()) {
if (m_spk_man == nullptr) continue; spk_man->MarkUnusedAddresses(batch, txout.scriptPubKey, confirm.hashBlock);
AssertLockHeld(m_spk_man->cs_wallet);
for (const auto& keyid : GetAffectedKeys(txout.scriptPubKey, *m_spk_man)) {
std::map<CKeyID, int64_t>::const_iterator mi = m_spk_man->m_pool_key_to_index.find(keyid);
if (mi != m_spk_man->m_pool_key_to_index.end()) {
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
MarkReserveKeysAsUsed(mi->second);
if (!m_spk_man->TopUpKeyPool()) {
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
}
}
if (!confirm.hashBlock.IsNull()) {
int64_t block_time;
bool found_block = chain().findBlock(confirm.hashBlock, nullptr /* block */, &block_time);
assert(found_block);
if (mapKeyMetadata[keyid].nCreateTime > block_time) {
WalletLogPrintf("%s: Found a key which appears to be used earlier than we expected, updating metadata\n", __func__);
CPubKey vchPubKey;
bool res = m_spk_man->GetPubKey(keyid, vchPubKey);
assert(res); // this should never fail
mapKeyMetadata[keyid].nCreateTime = block_time;
batch.WriteKeyMetadata(mapKeyMetadata[keyid], vchPubKey, true);
m_spk_man->UpdateTimeFirstKey(block_time);
}
}
} }
} }
@ -1581,10 +1559,10 @@ void CWallet::UnsetWalletFlag(uint64_t flag)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
WalletBatch batch(*database); WalletBatch batch(*database);
UnsetWalletFlag(batch, flag); UnsetWalletFlagWithDB(batch, flag);
} }
void CWallet::UnsetWalletFlag(WalletBatch& batch, uint64_t flag) void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
m_wallet_flags &= ~flag; m_wallet_flags &= ~flag;
@ -1592,6 +1570,11 @@ void CWallet::UnsetWalletFlag(WalletBatch& batch, uint64_t flag)
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed"); throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
} }
void CWallet::UnsetBlankWalletFlag(WalletBatch& batch)
{
UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
}
bool CWallet::IsWalletFlagSet(uint64_t flag) const bool CWallet::IsWalletFlagSet(uint64_t flag) const
{ {
return (m_wallet_flags & flag); return (m_wallet_flags & flag);
@ -1688,9 +1671,19 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
return false; return false;
} }
AssertLockHeld(spk_man->cs_wallet); AssertLockHeld(spk_man->cs_wallet);
if (!spk_man->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, apply_label, timestamp)) { if (!spk_man->ImportScriptPubKeys(script_pub_keys, have_solving_data, timestamp)) {
return false; return false;
} }
if (apply_label) {
WalletBatch batch(*database);
for (const CScript& script : script_pub_keys) {
CTxDestination dest;
ExtractDestination(script, dest);
if (IsValidDestination(dest)) {
SetAddressBookWithDB(batch, dest, label, "receive");
}
}
}
return true; return true;
} }
@ -3574,13 +3567,10 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{ {
if (database->Rewrite("\x04pool")) if (database->Rewrite("\x04pool"))
{ {
setInternalKeyPool.clear(); if (auto spk_man = m_spk_man.get()) {
setExternalKeyPool.clear(); spk_man->RewriteDB();
}
nKeysLeftSinceAutoBackup = 0; nKeysLeftSinceAutoBackup = 0;
m_spk_man->m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
} }
} }
@ -3645,12 +3635,9 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{ {
if (database->Rewrite("\x04pool")) if (database->Rewrite("\x04pool"))
{ {
setInternalKeyPool.clear(); if (auto spk_man = m_spk_man.get()) {
setExternalKeyPool.clear(); spk_man->RewriteDB();
m_spk_man->m_pool_key_to_index.clear(); }
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
} }
} }
@ -3732,30 +3719,47 @@ size_t CWallet::KeypoolCountInternalKeys()
return count; return count;
} }
unsigned int CWallet::GetKeyPoolSize() const
{
AssertLockHeld(cs_wallet);
unsigned int count = 0;
if (auto spk_man = m_spk_man.get()) {
count += spk_man->GetKeyPoolSize();
}
return count;
}
bool CWallet::TopUpKeyPool(unsigned int kpSize) bool CWallet::TopUpKeyPool(unsigned int kpSize)
{ {
bool res = true; bool res = true;
if (auto spk_man = m_spk_man.get()) { if (auto spk_man = m_spk_man.get()) {
res &= spk_man->TopUpKeyPool(kpSize); res &= spk_man->TopUp(kpSize);
} }
return res; return res;
} }
bool CWallet::GetNewDestination(const std::string label, CTxDestination& dest, std::string& error) bool CWallet::GetNewDestination(const std::string label, CTxDestination& dest, std::string& error)
{ {
LOCK(cs_wallet);
error.clear(); error.clear();
bool result = false; bool result = false;
auto spk_man = m_spk_man.get(); auto spk_man = m_spk_man.get();
if (spk_man) { if (spk_man) {
result = spk_man->GetNewDestination(label, dest, error); result = spk_man->GetNewDestination(dest, error);
} }
if (result) {
SetAddressBook(dest, label, "receive");
}
return result; return result;
} }
bool CWallet::GetNewChangeDestination(CTxDestination& dest, std::string& error) bool CWallet::GetNewChangeDestination(CTxDestination& dest, std::string& error)
{ {
error.clear(); error.clear();
m_spk_man->TopUpKeyPool();
m_spk_man->TopUp();
ReserveDestination reservedest(this); ReserveDestination reservedest(this);
if (!reservedest.GetReservedDestination(dest, true)) { if (!reservedest.GetReservedDestination(dest, true)) {
@ -3928,7 +3932,7 @@ bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool fInte
if (nIndex == -1) if (nIndex == -1)
{ {
CKeyPool keypool; CKeyPool keypool;
if (!m_spk_man->ReserveKeyFromKeyPool(nIndex, keypool, fInternalIn)) { if (!m_spk_man->GetReservedDestination(fInternalIn, nIndex, keypool)) {
return false; return false;
} }
vchPubKey = keypool.vchPubKey; vchPubKey = keypool.vchPubKey;
@ -3943,7 +3947,7 @@ bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool fInte
void ReserveDestination::KeepDestination() void ReserveDestination::KeepDestination()
{ {
if (nIndex != -1) { if (nIndex != -1) {
m_spk_man->KeepKey(nIndex); m_spk_man->KeepDestination(nIndex);
} }
nIndex = -1; nIndex = -1;
vchPubKey = CPubKey(); vchPubKey = CPubKey();
@ -3953,7 +3957,7 @@ void ReserveDestination::KeepDestination()
void ReserveDestination::ReturnDestination() void ReserveDestination::ReturnDestination()
{ {
if (nIndex != -1) { if (nIndex != -1) {
m_spk_man->ReturnKey(nIndex, fInternal, vchPubKey); m_spk_man->ReturnDestination(nIndex, fInternal, vchPubKey);
} }
nIndex = -1; nIndex = -1;
vchPubKey = CPubKey(); vchPubKey = CPubKey();
@ -4341,7 +4345,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
} // Otherwise, do not create a new HD chain } // Otherwise, do not create a new HD chain
// Top up the keypool // Top up the keypool
if (walletInstance->m_spk_man->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) { if (walletInstance->m_spk_man->CanGenerateKeys() && !walletInstance->m_spk_man->TopUp()) {
return unload_wallet(_("Unable to generate initial keys")); return unload_wallet(_("Unable to generate initial keys"));
} }
@ -4359,9 +4363,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
error = strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile); error = strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile);
return NULL; return NULL;
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { } else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
LOCK(walletInstance->cs_KeyStore); if (walletInstance->m_spk_man) {
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) { if (walletInstance->m_spk_man->HavePrivateKeys()) {
warnings.push_back(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile)); warnings.push_back(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
}
} }
} }
else if (gArgs.IsArgSet("-usehd")) { else if (gArgs.IsArgSet("-usehd")) {
@ -4526,8 +4531,14 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// our wallet birthday (as adjusted for block time variability) // our wallet birthday (as adjusted for block time variability)
// unless a full rescan was requested // unless a full rescan was requested
if (gArgs.GetArg("-rescan", 0) != 2) { if (gArgs.GetArg("-rescan", 0) != 2) {
if (walletInstance->nTimeFirstKey) { Optional<int64_t> time_first_key;
if (Optional<int> first_block = chain.findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) { if (auto spk_man = walletInstance->m_spk_man.get()) {
LOCK(spk_man->cs_wallet);
int64_t time = spk_man->GetTimeFirstKey();
if (!time_first_key || time < *time_first_key) time_first_key = time;
}
if (time_first_key) {
if (Optional<int> first_block = chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
rescan_height = *first_block; rescan_height = *first_block;
} }
} }
@ -4557,7 +4568,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->WalletLogPrintf("setInternalKeyPool.size() = %u\n", walletInstance->KeypoolCountInternalKeys()); walletInstance->WalletLogPrintf("setInternalKeyPool.size() = %u\n", walletInstance->KeypoolCountInternalKeys());
walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size()); walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size()); walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
walletInstance->WalletLogPrintf("nTimeFirstKey = %u\n", walletInstance->nTimeFirstKey); if (auto spk_man = walletInstance->m_spk_man.get()) {
LOCK(spk_man->cs_wallet);
walletInstance->WalletLogPrintf("nTimeFirstKey = %u\n", spk_man->GetTimeFirstKey());
}
} }
return walletInstance; return walletInstance;

View File

@ -730,6 +730,14 @@ private:
std::atomic<uint64_t> m_wallet_flags{0}; std::atomic<uint64_t> m_wallet_flags{0};
bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
//! Unsets a wallet flag and saves it to disk
void UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag);
//! Unset the blank wallet flag and saves it to disk
void UnsetBlankWalletFlag(WalletBatch& batch) override;
/** Interface for accessing chain state. */ /** Interface for accessing chain state. */
interfaces::Chain* m_chain; interfaces::Chain* m_chain;
@ -1096,16 +1104,11 @@ public:
void AutoLockMasternodeCollaterals(); void AutoLockMasternodeCollaterals();
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
bool DelAddressBook(const CTxDestination& address); bool DelAddressBook(const CTxDestination& address);
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
{
AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
//! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
void SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false) override; void SetMinVersion(enum WalletFeature, WalletBatch* batch_in = nullptr, bool fExplicit = false) override;
@ -1229,11 +1232,10 @@ public:
void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_main, cs_wallet); void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_main, cs_wallet);
/** set a single wallet flag */ /** set a single wallet flag */
void SetWalletFlag(uint64_t flags) override; void SetWalletFlag(uint64_t flags);
/** Unsets a single wallet flag */ /** Unsets a single wallet flag */
void UnsetWalletFlag(uint64_t flag); void UnsetWalletFlag(uint64_t flag);
void UnsetWalletFlag(WalletBatch& batch, uint64_t flag);
/** check if a certain wallet flag is set */ /** check if a certain wallet flag is set */
bool IsWalletFlagSet(uint64_t flag) const override; bool IsWalletFlagSet(uint64_t flag) const override;
@ -1284,12 +1286,6 @@ public:
LegacyScriptPubKeyMan::WatchKeyMap& mapWatchKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapWatchKeys; LegacyScriptPubKeyMan::WatchKeyMap& mapWatchKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapWatchKeys;
LegacyScriptPubKeyMan::HDPubKeyMap& mapHdPubKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapHdPubKeys; LegacyScriptPubKeyMan::HDPubKeyMap& mapHdPubKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapHdPubKeys;
WalletBatch*& encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch; WalletBatch*& encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch;
std::set<int64_t>& setInternalKeyPool GUARDED_BY(cs_wallet) = m_spk_man->setInternalKeyPool;
std::set<int64_t>& setExternalKeyPool GUARDED_BY(cs_wallet) = m_spk_man->setExternalKeyPool;
int64_t& nTimeFirstKey GUARDED_BY(cs_wallet) = m_spk_man->nTimeFirstKey;
std::map<CKeyID, CKeyMetadata>& mapKeyMetadata GUARDED_BY(cs_wallet) = m_spk_man->mapKeyMetadata;
std::map<CScriptID, CKeyMetadata>& m_script_metadata GUARDED_BY(cs_wallet) = m_spk_man->m_script_metadata;
void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(m_spk_man->cs_wallet); m_spk_man->MarkReserveKeysAsUsed(keypool_id); }
using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap; using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap;
}; };