From 3869fb89b60091281b43a35921057ba3f43c18f0 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Mon, 10 Jun 2013 09:36:29 -0400 Subject: [PATCH] Wallet: store key creation time. Calculate whole-wallet birthday. This also encapsulate wallet-read state information into CWalletScanState. --- src/wallet.cpp | 27 ++++++++++++--- src/wallet.h | 6 ++-- src/walletdb.cpp | 89 ++++++++++++++++++++++++++++++++---------------- src/walletdb.h | 51 +++++++++++++++++++++++++-- 4 files changed, 135 insertions(+), 38 deletions(-) diff --git a/src/wallet.cpp b/src/wallet.cpp index 7041d49da..aa1371111 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -45,20 +45,33 @@ CPubKey CWallet::GenerateNewKey() return pubkey; } -bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey, + int64 nCreateTime) { + if (!nCreateTime) + nCreateTime = GetTime(); + if (!nTimeFirstKey || (nCreateTime < nTimeFirstKey)) + nTimeFirstKey = nCreateTime; if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) return false; if (!fFileBacked) return true; if (!IsCrypted()) { - return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey()); + return CWalletDB(strWalletFile).WriteKey(pubkey, + secret.GetPrivKey(), + nCreateTime); } return true; } -bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) +bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, + const vector &vchCryptedSecret, + int64 nCreateTime) { + if (!nCreateTime) + nCreateTime = GetTime(); + if (!nTimeFirstKey || (nCreateTime < nTimeFirstKey)) + nTimeFirstKey = nCreateTime; if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; if (!fFileBacked) @@ -66,9 +79,13 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vectorWriteCryptedKey(vchPubKey, vchCryptedSecret); + return pwalletdbEncryption->WriteCryptedKey(vchPubKey, + vchCryptedSecret, + nCreateTime); else - return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, + vchCryptedSecret, + nCreateTime); } return false; } diff --git a/src/wallet.h b/src/wallet.h index 674bae66d..a0688b8a0 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -123,6 +123,8 @@ public: std::set setLockedCoins; + int64 nTimeFirstKey; + // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } @@ -138,14 +140,14 @@ public: // Generate a new key CPubKey GenerateNewKey(); // Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey, int64 nCreateTime = 0); // 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 LoadMinVersion(int nVersion) { nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } // Adds an encrypted key to the store, and saves it to disk. - bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); + bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret, int64 nCreateTime = 0); // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); bool AddCScript(const CScript& redeemScript); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 8910cac4b..96fd4a5fc 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -180,11 +180,27 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) return DB_LOAD_OK; } +class CWalletScanState { +public: + unsigned int nKeys; + unsigned int nCKeys; + unsigned int nKeyMeta; + bool fIsEncrypted; + bool fAnyUnordered; + int nFileVersion; + vector vWalletUpgrade; + + CWalletScanState() { + nKeys = nCKeys = nKeyMeta = 0; + fIsEncrypted = false; + fAnyUnordered = false; + nFileVersion = 0; + } +}; bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, - int& nFileVersion, vector& vWalletUpgrade, - bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr) + CWalletScanState &wss, string& strType, string& strErr) { try { // Unserialize @@ -229,11 +245,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); wtx.fTimeReceivedIsTxTime = 0; } - vWalletUpgrade.push_back(hash); + wss.vWalletUpgrade.push_back(hash); } if (wtx.nOrderPos == -1) - fAnyUnordered = true; + wss.fAnyUnordered = true; //// debug print //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); @@ -252,12 +268,12 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, if (nNumber > nAccountingEntryNumber) nAccountingEntryNumber = nNumber; - if (!fAnyUnordered) + if (!wss.fAnyUnordered) { CAccountingEntry acentry; ssValue >> acentry; if (acentry.nOrderPos == -1) - fAnyUnordered = true; + wss.fAnyUnordered = true; } } else if (strType == "key" || strType == "wkey") @@ -272,8 +288,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CKey key; CPrivKey pkey; if (strType == "key") + { + wss.nKeys++; ssValue >> pkey; - else { + } else { CWalletKey wkey; ssValue >> wkey; pkey = wkey.vchPrivKey; @@ -315,12 +333,27 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> vchPubKey; vector vchPrivKey; ssValue >> vchPrivKey; + wss.nCKeys++; + if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { strErr = "Error reading wallet database: LoadCryptedKey failed"; return false; } - fIsEncrypted = true; + wss.fIsEncrypted = true; + } + else if (strType == "keymeta") + { + vector vchPubKey; + ssKey >> vchPubKey; + CKeyMetadata keyMeta; + ssValue >> keyMeta; + wss.nKeyMeta++; + + // find earliest key creation time, as wallet birthday + if (!pwallet->nTimeFirstKey || + (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) + pwallet->nTimeFirstKey = keyMeta.nCreateTime; } else if (strType == "defaultkey") { @@ -334,9 +367,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "version") { - ssValue >> nFileVersion; - if (nFileVersion == 10300) - nFileVersion = 300; + ssValue >> wss.nFileVersion; + if (wss.nFileVersion == 10300) + wss.nFileVersion = 300; } else if (strType == "cscript") { @@ -370,10 +403,7 @@ static bool IsKeyType(string strType) DBErrors CWalletDB::LoadWallet(CWallet* pwallet) { pwallet->vchDefaultKey = CPubKey(); - int nFileVersion = 0; - vector vWalletUpgrade; - bool fIsEncrypted = false; - bool fAnyUnordered = false; + CWalletScanState wss; bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; @@ -411,8 +441,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) // Try to be tolerant of single corrupt records: string strType, strErr; - if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion, - vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr)) + if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: @@ -447,19 +476,26 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) if (result != DB_LOAD_OK) return result; - printf("nFileVersion = %d\n", nFileVersion); + printf("nFileVersion = %d\n", wss.nFileVersion); - BOOST_FOREACH(uint256 hash, vWalletUpgrade) + printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", + wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); + + // nTimeFirstKey is only reliable if all keys have metadata + if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) + pwallet->nTimeFirstKey = 0; + + BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade) WriteTx(hash, pwallet->mapWallet[hash]); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) + if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) return DB_NEED_REWRITE; - if (nFileVersion < CLIENT_VERSION) // Update + if (wss.nFileVersion < CLIENT_VERSION) // Update WriteVersion(CLIENT_VERSION); - if (fAnyUnordered) + if (wss.fAnyUnordered) result = ReorderTransactions(pwallet); return result; @@ -615,10 +651,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) return false; } CWallet dummyWallet; - int nFileVersion = 0; - vector vWalletUpgrade; - bool fIsEncrypted = false; - bool fAnyUnordered = false; + CWalletScanState wss; DbTxn* ptxn = dbenv.TxnBegin(); BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) @@ -629,9 +662,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); string strType, strErr; bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, - nFileVersion, vWalletUpgrade, - fIsEncrypted, fAnyUnordered, - strType, strErr); + wss, strType, strErr); if (!IsKeyType(strType)) continue; if (!fReadOK) diff --git a/src/walletdb.h b/src/walletdb.h index 9732eb29e..287361b33 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -25,6 +25,37 @@ enum DBErrors DB_NEED_REWRITE }; +class CKeyMetadata +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64 nCreateTime; + + CKeyMetadata() + { + SetNull(); + } + CKeyMetadata(int64 nCreateTime_) + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = nCreateTime_; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nCreateTime); + ) + + void SetNull() + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = GetTime(); + } +}; + /** Access to the wallet database (wallet.dat) */ class CWalletDB : public CDB { @@ -52,15 +83,31 @@ public: return Erase(std::make_pair(std::string("tx"), hash)); } - bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey) + bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, + int64 nCreateTime) { nWalletDBUpdated++; + + CKeyMetadata keyMeta(nCreateTime); + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), + keyMeta, false)) + return false; + return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); } - bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, bool fEraseUnencryptedKey = true) + bool WriteCryptedKey(const CPubKey& vchPubKey, + const std::vector& vchCryptedSecret, + int64 nCreateTime) { + const bool fEraseUnencryptedKey = true; nWalletDBUpdated++; + + CKeyMetadata keyMeta(nCreateTime); + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), + keyMeta, false)) + return false; + if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) return false; if (fEraseUnencryptedKey)