Merge #10831: Batch flushing operations to the walletdb during top up and increase keypool size.

b0e8e2d Print one log message per keypool top-up, not one per key. (Gregory Maxwell)
41dc163 Increase wallet default keypool size to 1000. (Gregory Maxwell)
30d8f3a Pushdown walletdb though CWallet::AddKeyPubKey to avoid flushes. (Gregory Maxwell)
3a53f19 Pushdown walletdb object through GenerateNewKey/DeriveNewChildKey. (Gregory Maxwell)

Pull request description:

  This carries the walletdb object from top-up through GenerateNewKey/DeriveNewChildKey/CWallet::AddKeyPubKey, which allows us to avoid the flush on destruction until the top up finishes instead of flushing the wallet for every key.

  This speeds up adding keys by well over 10x on my laptop (actually something like 17x), I wouldn't be surprised if it were an even bigger speedup on spinning rust.

  Then it increases the keypool size to 1000. I would have preferred to use 10,000 but in the case where the user creates a new wallet and then turns on encryption it seems kind of dumb to have >400KB of marked-used born unencrypted keys just laying around.

  (Thanks to Matt for cluesticking me on how to bypass the crypter spaghetti)

Tree-SHA512: 868303de38fce4c3f67d7fe133f765f15435c94b39d252d7450b5fee5c607a3cc2f5e531861a69d8c8877bf130e0ff4c539f97500a6bc0ff6d67e4a42c9385c7
This commit is contained in:
Wladimir J. van der Laan 2017-07-17 17:16:00 +02:00
commit 0b019357ff
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
2 changed files with 43 additions and 17 deletions

View file

@ -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,14 +100,15 @@ 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);
}
// 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,12 +116,13 @@ CPubKey CWallet::GenerateNewKey(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;
}
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,33 +164,52 @@ 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");
}
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<unsigned char> &vchCryptedSecret)
{
@ -3197,15 +3218,18 @@ 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);
} 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;
@ -3280,7 +3304,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);

View file

@ -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
@ -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<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
@ -866,9 +866,10 @@ 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;
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)