Browse Source

Merge #10952: [wallet] Remove vchDefaultKey and have better first run detection

e53615b Remove vchDefaultKey and have better first run detection (Andrew Chow)

Pull request description:

  Removes vchDefaultKey which was only used for first run detection. Improves wallet first run detection by checking to see if any keys were read from the database.

  This also fixes a (rather contrived) case where an encrypted non-HD wallet has corruption such that the default key is no longer valid and is loaded into a Core version that supports HD wallets. This causes a runtime exception since a new hd master key is generated as the software believes the wallet file is newly created but cannot add the generated key to the wallet since it is encrypted. I was only able to replicate this error by creating a non-hd wallet, encrypting it, then editing the wallet using `db_dump` and `db_load` before loading the wallet with hd enabled. This problem has been reported by [two](https://bitcointalk.org/index.php?topic=1993244.0) [users](https://bitcointalk.org/index.php?topic=1746976.msg17511261#msg17511261) so it is something that can happen, although that raises the question of "what corrupted the default key".

  ~P.S. I don't know what's up with the whitespace changes. I think my text editor is doing something stupid but I don't think those are important enough to attempt undoing them.~ Undid those

Tree-SHA512: 63b485f356566e8ffa033ad9b7101f7f6b56372b29ec2a43b947b0eeb1ada4c2cfe24740515d013aedd5f51aa1890dfbe499d2c5c062fc1b5d272324728a7d55
0.16
Wladimir J. van der Laan 7 years ago
parent
commit
262167393d
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
  1. 2
      src/wallet/crypter.h
  2. 26
      src/wallet/wallet.cpp
  3. 4
      src/wallet/wallet.h
  4. 17
      src/wallet/walletdb.cpp
  5. 2
      src/wallet/walletdb.h
  6. 2
      test/functional/keypool-topup.py
  7. 8
      test/functional/wallet-hd.py

2
src/wallet/crypter.h

@ -113,7 +113,6 @@ public: @@ -113,7 +113,6 @@ public:
class CCryptoKeyStore : public CBasicKeyStore
{
private:
CryptedKeyMap mapCryptedKeys;
CKeyingMaterial vMasterKey;
@ -131,6 +130,7 @@ protected: @@ -131,6 +130,7 @@ protected:
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
bool Unlock(const CKeyingMaterial& vMasterKeyIn);
CryptedKeyMap mapCryptedKeys;
public:
CCryptoKeyStore() : fUseCrypto(false), fDecryptionThoroughlyChecked(false)

26
src/wallet/wallet.cpp

@ -3123,9 +3123,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) @@ -3123,9 +3123,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
}
}
// This wallet is in its first run if all of these are empty
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() && mapWatchKeys.empty() && setWatchOnly.empty() && mapScripts.empty();
if (nLoadWalletRet != DB_LOAD_OK)
return nLoadWalletRet;
fFirstRunRet = !vchDefaultKey.IsValid();
uiInterface.LoadWallet(this);
@ -3135,7 +3137,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) @@ -3135,7 +3137,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
AssertLockHeld(cs_wallet); // mapWallet
vchDefaultKey = CPubKey();
DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut)
mapWallet.erase(hash);
@ -3164,7 +3165,6 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 @@ -3164,7 +3165,6 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{
vchDefaultKey = CPubKey();
DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE)
{
@ -3240,14 +3240,6 @@ const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const @@ -3240,14 +3240,6 @@ const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const
return DEFAULT_ACCOUNT_NAME;
}
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{
if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey))
return false;
vchDefaultKey = vchPubKey;
return true;
}
/**
* Mark old keypool keys as used,
* and generate all new keys
@ -4019,13 +4011,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) @@ -4019,13 +4011,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (!walletInstance->SetHDMasterKey(masterPubKey))
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
}
CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey, false)) {
walletInstance->SetDefaultKey(newDefaultKey);
if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) {
InitError(_("Cannot write default address") += "\n");
return nullptr;
}
// Top up the keypool
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate initial keys") += "\n");
return NULL;
}
walletInstance->SetBestChain(chainActive.GetLocator());

4
src/wallet/wallet.h

@ -807,8 +807,6 @@ public: @@ -807,8 +807,6 @@ public:
std::map<CTxDestination, CAddressBookData> mapAddressBook;
CPubKey vchDefaultKey;
std::set<COutPoint> setLockedCoins;
const CWalletTx* GetWalletTx(const uint256& hash) const;
@ -1038,8 +1036,6 @@ public: @@ -1038,8 +1036,6 @@ public:
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
bool SetDefaultKey(const CPubKey &vchPubKey);
//! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower
bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = nullptr, bool fExplicit = false);

17
src/wallet/walletdb.cpp

@ -130,11 +130,6 @@ bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) @@ -130,11 +130,6 @@ bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
return WriteIC(std::string("orderposnext"), nOrderPosNext);
}
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{
return WriteIC(std::string("defaultkey"), vchPubKey);
}
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
{
return batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
@ -452,7 +447,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, @@ -452,7 +447,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
else if (strType == "defaultkey")
{
ssValue >> pwallet->vchDefaultKey;
// We don't want or need the default key, but if there is one set,
// we want to make sure that it is valid so that we can detect corruption
CPubKey vchPubKey;
ssValue >> vchPubKey;
if (!vchPubKey.IsValid()) {
strErr = "Error reading wallet database: Default Key corrupt";
return false;
}
}
else if (strType == "pool")
{
@ -522,7 +524,6 @@ bool CWalletDB::IsKeyType(const std::string& strType) @@ -522,7 +524,6 @@ bool CWalletDB::IsKeyType(const std::string& strType)
DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
{
pwallet->vchDefaultKey = CPubKey();
CWalletScanState wss;
bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK;
@ -565,7 +566,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) @@ -565,7 +566,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
{
// losing keys is considered a catastrophic error, anything else
// we assume the user can live with:
if (IsKeyType(strType))
if (IsKeyType(strType) || strType == "defaultkey")
result = DB_CORRUPT;
else
{

2
src/wallet/walletdb.h

@ -191,8 +191,6 @@ public: @@ -191,8 +191,6 @@ public:
bool WriteOrderPosNext(int64_t nOrderPosNext);
bool WriteDefaultKey(const CPubKey& vchPubKey);
bool ReadPool(int64_t nPool, CKeyPool& keypool);
bool WritePool(int64_t nPool, const CKeyPool& keypool);
bool ErasePool(int64_t nPool);

2
test/functional/keypool-topup.py

@ -69,7 +69,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): @@ -69,7 +69,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
assert_equal(self.nodes[1].listtransactions()[0]['category'], "receive")
# Check that we have marked all keys up to the used keypool key as used
assert_equal(self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/111'")
assert_equal(self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/110'")
if __name__ == '__main__':
KeypoolRestoreTest().main()

8
test/functional/wallet-hd.py

@ -42,7 +42,7 @@ class WalletHDTest(BitcoinTestFramework): @@ -42,7 +42,7 @@ class WalletHDTest(BitcoinTestFramework):
non_hd_add = self.nodes[0].getnewaddress()
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
# This should be enough to keep the master key and the non-HD key
# This should be enough to keep the master key and the non-HD key
self.nodes[1].backupwallet(tmpdir + "/hd.bak")
#self.nodes[1].dumpwallet(tmpdir + "/hd.dump")
@ -54,7 +54,7 @@ class WalletHDTest(BitcoinTestFramework): @@ -54,7 +54,7 @@ class WalletHDTest(BitcoinTestFramework):
for i in range(num_hd_adds):
hd_add = self.nodes[1].getnewaddress()
hd_info = self.nodes[1].validateaddress(hd_add)
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i+1)+"'")
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'")
assert_equal(hd_info["hdmasterkeyid"], masterkeyid)
self.nodes[0].sendtoaddress(hd_add, 1)
self.nodes[0].generate(1)
@ -83,7 +83,7 @@ class WalletHDTest(BitcoinTestFramework): @@ -83,7 +83,7 @@ class WalletHDTest(BitcoinTestFramework):
for _ in range(num_hd_adds):
hd_add_2 = self.nodes[1].getnewaddress()
hd_info_2 = self.nodes[1].validateaddress(hd_add_2)
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_+1)+"'")
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_)+"'")
assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid)
assert_equal(hd_add, hd_add_2)
connect_nodes_bi(self.nodes, 0, 1)
@ -101,7 +101,7 @@ class WalletHDTest(BitcoinTestFramework): @@ -101,7 +101,7 @@ class WalletHDTest(BitcoinTestFramework):
for out in outs:
if out['value'] != 1:
keypath = self.nodes[1].validateaddress(out['scriptPubKey']['addresses'][0])['hdkeypath']
assert_equal(keypath[0:7], "m/0'/1'")
if __name__ == '__main__':

Loading…
Cancel
Save