From 3a53f19718a2207f0d74c32a611ae91703d9da8d Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Fri, 14 Jul 2017 22:36:58 +0000 Subject: [PATCH 1/4] Pushdown walletdb object through GenerateNewKey/DeriveNewChildKey. This is needed but not sufficient for batching the wallet flushing when topping up the keypool. --- src/wallet/wallet.cpp | 14 ++++++++------ src/wallet/wallet.h | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5689cc7b0c..b3b3c1c10a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -87,7 +87,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } -CPubKey CWallet::GenerateNewKey(bool internal) +CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) { AssertLockHeld(cs_wallet); // mapKeyMetadata bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets @@ -100,7 +100,7 @@ CPubKey CWallet::GenerateNewKey(bool internal) // use HD key derivation if HD was enabled during wallet creation if (IsHDEnabled()) { - DeriveNewChildKey(metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); + DeriveNewChildKey(walletdb, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); } else { secret.MakeNewKey(fCompressed); } @@ -120,7 +120,7 @@ CPubKey CWallet::GenerateNewKey(bool internal) return pubkey; } -void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal) +void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k CKey key; //master key seed (256bit) @@ -162,7 +162,7 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter secret = childKey.key; metadata.hdMasterKeyID = hdChain.masterKeyID; // update the chain model in the database - if (!CWalletDB(*dbw).WriteHDChain(hdChain)) + if (!walletdb.WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } @@ -3183,8 +3183,9 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) nEnd = std::max(nEnd, *(setExternalKeyPool.rbegin()) + 1); } - if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal))) + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(walletdb, internal), internal))) { throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); + } if (internal) { setInternalKeyPool.insert(nEnd); @@ -3266,7 +3267,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) if (nIndex == -1) { if (IsLocked()) return false; - result = GenerateNewKey(internal); + CWalletDB walletdb(*dbw); + result = GenerateNewKey(walletdb, internal); return true; } KeepKey(nIndex); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 8848448682..e91a6effd3 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -697,7 +697,7 @@ private: CHDChain hdChain; /* HD derive new child key (on internal or external chain) */ - void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false); + void DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal = false); std::set setInternalKeyPool; std::set setExternalKeyPool; @@ -866,7 +866,7 @@ public: * keystore implementation * Generate a new key */ - CPubKey GenerateNewKey(bool internal = false); + CPubKey GenerateNewKey(CWalletDB& walletdb, bool internal = false); //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; //! Adds a key to the store, without saving it to disk (used by LoadWallet) From 30d8f3a18e7d927459e409a38ee1c0d1ddf054ad Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sat, 15 Jul 2017 01:00:30 +0000 Subject: [PATCH 2/4] Pushdown walletdb though CWallet::AddKeyPubKey to avoid flushes. This prevents the wallet from being flushed between each and every key during top-up. This results in a >10x speed-up for the top-up. --- src/wallet/wallet.cpp | 35 ++++++++++++++++++++++++++++------- src/wallet/wallet.h | 1 + 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b3b3c1c10a..d8c1dd94fc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -106,8 +106,9 @@ CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) } // Compressed public keys were introduced in version 0.6.0 - if (fCompressed) + if (fCompressed) { SetMinVersion(FEATURE_COMPRPUBKEY); + } CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); @@ -115,8 +116,9 @@ CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) mapKeyMetadata[pubkey.GetID()] = metadata; UpdateTimeFirstKey(nCreationTime); - if (!AddKeyPubKey(secret, pubkey)) + if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) { throw std::runtime_error(std::string(__func__) + ": AddKey failed"); + } return pubkey; } @@ -166,29 +168,48 @@ void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKe throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } -bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const CPubKey &pubkey) { AssertLockHeld(cs_wallet); // mapKeyMetadata - if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) + + // CCryptoKeyStore 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 = !pwalletdbEncryption; + if (needsDB) { + pwalletdbEncryption = &walletdb; + } + if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { + if (needsDB) pwalletdbEncryption = NULL; return false; + } + if (needsDB) pwalletdbEncryption = NULL; // check if we need to remove from watch-only CScript script; script = GetScriptForDestination(pubkey.GetID()); - if (HaveWatchOnly(script)) + if (HaveWatchOnly(script)) { RemoveWatchOnly(script); + } script = GetScriptForRawPubKey(pubkey); - if (HaveWatchOnly(script)) + if (HaveWatchOnly(script)) { RemoveWatchOnly(script); + } if (!IsCrypted()) { - return CWalletDB(*dbw).WriteKey(pubkey, + return walletdb.WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } return true; } +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +{ + CWalletDB walletdb(*dbw); + return CWallet::AddKeyPubKeyWithDB(walletdb, secret, pubkey); +} + bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e91a6effd3..0bf6ccf722 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -869,6 +869,7 @@ public: CPubKey GenerateNewKey(CWalletDB& walletdb, bool internal = false); //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; + bool AddKeyPubKeyWithDB(CWalletDB &walletdb,const CKey& key, const CPubKey &pubkey); //! 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); } //! Load metadata (used by LoadWallet) From 41dc1635878766e4a810e6a7c57637d079fced64 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sat, 15 Jul 2017 01:09:10 +0000 Subject: [PATCH 3/4] Increase wallet default keypool size to 1000. --- src/wallet/wallet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 0bf6ccf722..9e687c4bfd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -40,7 +40,7 @@ extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; extern bool fWalletRbf; -static const unsigned int DEFAULT_KEYPOOL_SIZE = 100; +static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; //! -paytxfee default static const CAmount DEFAULT_TRANSACTION_FEE = 0; //! -fallbackfee default From b0e8e2de8408cbaed9d70914c67b4c9f11397cb7 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Mon, 17 Jul 2017 08:16:36 +0000 Subject: [PATCH 4/4] Print one log message per keypool top-up, not one per key. --- src/wallet/wallet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d8c1dd94fc..5db13ef02f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3213,7 +3213,9 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) } else { setExternalKeyPool.insert(nEnd); } - LogPrintf("keypool added key %d, size=%u (%u internal), new key is %s\n", nEnd, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size(), internal ? "internal" : "external"); + } + if (missingInternal + missingExternal > 0) { + LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size()); } } return true;