Wallet: store key creation time. Calculate whole-wallet birthday.

This also encapsulate wallet-read state information into CWalletScanState.
This commit is contained in:
Jeff Garzik 2013-06-10 09:36:29 -04:00
parent f59530ce6e
commit 3869fb89b6
4 changed files with 135 additions and 38 deletions

View file

@ -45,20 +45,33 @@ CPubKey CWallet::GenerateNewKey()
return pubkey; 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)) if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey))
return false; return false;
if (!fFileBacked) if (!fFileBacked)
return true; return true;
if (!IsCrypted()) { if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey()); return CWalletDB(strWalletFile).WriteKey(pubkey,
secret.GetPrivKey(),
nCreateTime);
} }
return true; return true;
} }
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char> &vchCryptedSecret) bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const vector<unsigned char> &vchCryptedSecret,
int64 nCreateTime)
{ {
if (!nCreateTime)
nCreateTime = GetTime();
if (!nTimeFirstKey || (nCreateTime < nTimeFirstKey))
nTimeFirstKey = nCreateTime;
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false; return false;
if (!fFileBacked) if (!fFileBacked)
@ -66,9 +79,13 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (pwalletdbEncryption) if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); return pwalletdbEncryption->WriteCryptedKey(vchPubKey,
vchCryptedSecret,
nCreateTime);
else else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey,
vchCryptedSecret,
nCreateTime);
} }
return false; return false;
} }

View file

@ -123,6 +123,8 @@ public:
std::set<COutPoint> setLockedCoins; std::set<COutPoint> setLockedCoins;
int64 nTimeFirstKey;
// check whether we are allowed to upgrade (or already support) to the named feature // check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
@ -138,14 +140,14 @@ public:
// Generate a new key // Generate a new key
CPubKey GenerateNewKey(); CPubKey GenerateNewKey();
// Adds a key to the store, and saves it to disk. // 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) // 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 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; } 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. // Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, int64 nCreateTime = 0);
// Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) // 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); bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddCScript(const CScript& redeemScript); bool AddCScript(const CScript& redeemScript);

View file

@ -180,11 +180,27 @@ CWalletDB::ReorderTransactions(CWallet* pwallet)
return DB_LOAD_OK; return DB_LOAD_OK;
} }
class CWalletScanState {
public:
unsigned int nKeys;
unsigned int nCKeys;
unsigned int nKeyMeta;
bool fIsEncrypted;
bool fAnyUnordered;
int nFileVersion;
vector<uint256> vWalletUpgrade;
CWalletScanState() {
nKeys = nCKeys = nKeyMeta = 0;
fIsEncrypted = false;
fAnyUnordered = false;
nFileVersion = 0;
}
};
bool bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
int& nFileVersion, vector<uint256>& vWalletUpgrade, CWalletScanState &wss, string& strType, string& strErr)
bool& fIsEncrypted, bool& fAnyUnordered, string& strType, string& strErr)
{ {
try { try {
// Unserialize // 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()); strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
wtx.fTimeReceivedIsTxTime = 0; wtx.fTimeReceivedIsTxTime = 0;
} }
vWalletUpgrade.push_back(hash); wss.vWalletUpgrade.push_back(hash);
} }
if (wtx.nOrderPos == -1) if (wtx.nOrderPos == -1)
fAnyUnordered = true; wss.fAnyUnordered = true;
//// debug print //// debug print
//printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
@ -252,12 +268,12 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
if (nNumber > nAccountingEntryNumber) if (nNumber > nAccountingEntryNumber)
nAccountingEntryNumber = nNumber; nAccountingEntryNumber = nNumber;
if (!fAnyUnordered) if (!wss.fAnyUnordered)
{ {
CAccountingEntry acentry; CAccountingEntry acentry;
ssValue >> acentry; ssValue >> acentry;
if (acentry.nOrderPos == -1) if (acentry.nOrderPos == -1)
fAnyUnordered = true; wss.fAnyUnordered = true;
} }
} }
else if (strType == "key" || strType == "wkey") else if (strType == "key" || strType == "wkey")
@ -272,8 +288,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CKey key; CKey key;
CPrivKey pkey; CPrivKey pkey;
if (strType == "key") if (strType == "key")
{
wss.nKeys++;
ssValue >> pkey; ssValue >> pkey;
else { } else {
CWalletKey wkey; CWalletKey wkey;
ssValue >> wkey; ssValue >> wkey;
pkey = wkey.vchPrivKey; pkey = wkey.vchPrivKey;
@ -315,12 +333,27 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> vchPubKey; ssKey >> vchPubKey;
vector<unsigned char> vchPrivKey; vector<unsigned char> vchPrivKey;
ssValue >> vchPrivKey; ssValue >> vchPrivKey;
wss.nCKeys++;
if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
{ {
strErr = "Error reading wallet database: LoadCryptedKey failed"; strErr = "Error reading wallet database: LoadCryptedKey failed";
return false; return false;
} }
fIsEncrypted = true; wss.fIsEncrypted = true;
}
else if (strType == "keymeta")
{
vector<unsigned char> 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") else if (strType == "defaultkey")
{ {
@ -334,9 +367,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
} }
else if (strType == "version") else if (strType == "version")
{ {
ssValue >> nFileVersion; ssValue >> wss.nFileVersion;
if (nFileVersion == 10300) if (wss.nFileVersion == 10300)
nFileVersion = 300; wss.nFileVersion = 300;
} }
else if (strType == "cscript") else if (strType == "cscript")
{ {
@ -370,10 +403,7 @@ static bool IsKeyType(string strType)
DBErrors CWalletDB::LoadWallet(CWallet* pwallet) DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
{ {
pwallet->vchDefaultKey = CPubKey(); pwallet->vchDefaultKey = CPubKey();
int nFileVersion = 0; CWalletScanState wss;
vector<uint256> vWalletUpgrade;
bool fIsEncrypted = false;
bool fAnyUnordered = false;
bool fNoncriticalErrors = false; bool fNoncriticalErrors = false;
DBErrors result = DB_LOAD_OK; DBErrors result = DB_LOAD_OK;
@ -411,8 +441,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
// Try to be tolerant of single corrupt records: // Try to be tolerant of single corrupt records:
string strType, strErr; string strType, strErr;
if (!ReadKeyValue(pwallet, ssKey, ssValue, nFileVersion, if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
vWalletUpgrade, fIsEncrypted, fAnyUnordered, strType, strErr))
{ {
// losing keys is considered a catastrophic error, anything else // losing keys is considered a catastrophic error, anything else
// we assume the user can live with: // we assume the user can live with:
@ -447,19 +476,26 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (result != DB_LOAD_OK) if (result != DB_LOAD_OK)
return result; 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]); WriteTx(hash, pwallet->mapWallet[hash]);
// Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: // 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; return DB_NEED_REWRITE;
if (nFileVersion < CLIENT_VERSION) // Update if (wss.nFileVersion < CLIENT_VERSION) // Update
WriteVersion(CLIENT_VERSION); WriteVersion(CLIENT_VERSION);
if (fAnyUnordered) if (wss.fAnyUnordered)
result = ReorderTransactions(pwallet); result = ReorderTransactions(pwallet);
return result; return result;
@ -615,10 +651,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
return false; return false;
} }
CWallet dummyWallet; CWallet dummyWallet;
int nFileVersion = 0; CWalletScanState wss;
vector<uint256> vWalletUpgrade;
bool fIsEncrypted = false;
bool fAnyUnordered = false;
DbTxn* ptxn = dbenv.TxnBegin(); DbTxn* ptxn = dbenv.TxnBegin();
BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) 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); CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
string strType, strErr; string strType, strErr;
bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue,
nFileVersion, vWalletUpgrade, wss, strType, strErr);
fIsEncrypted, fAnyUnordered,
strType, strErr);
if (!IsKeyType(strType)) if (!IsKeyType(strType))
continue; continue;
if (!fReadOK) if (!fReadOK)

View file

@ -25,6 +25,37 @@ enum DBErrors
DB_NEED_REWRITE 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) */ /** Access to the wallet database (wallet.dat) */
class CWalletDB : public CDB class CWalletDB : public CDB
{ {
@ -52,15 +83,31 @@ public:
return Erase(std::make_pair(std::string("tx"), hash)); 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++; 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); return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false);
} }
bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, bool fEraseUnencryptedKey = true) bool WriteCryptedKey(const CPubKey& vchPubKey,
const std::vector<unsigned char>& vchCryptedSecret,
int64 nCreateTime)
{ {
const bool fEraseUnencryptedKey = true;
nWalletDBUpdated++; 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)) if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
return false; return false;
if (fEraseUnencryptedKey) if (fEraseUnencryptedKey)