aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Garzik <jgarzik@bitpay.com>2013-06-10 09:36:29 -0400
committerJeff Garzik <jgarzik@bitpay.com>2013-06-10 09:36:29 -0400
commit3869fb89b60091281b43a35921057ba3f43c18f0 (patch)
tree3510aec7ccc74db41f25393f929db896f087cfea
parentf59530ce6eb5a889e6eb750024ddb20e7b0df9d7 (diff)
Wallet: store key creation time. Calculate whole-wallet birthday.
This also encapsulate wallet-read state information into CWalletScanState.
-rw-r--r--src/wallet.cpp27
-rw-r--r--src/wallet.h6
-rw-r--r--src/walletdb.cpp89
-rw-r--r--src/walletdb.h51
4 files changed, 135 insertions, 38 deletions
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 7041d49dab..aa13711110 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<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))
return false;
if (!fFileBacked)
@@ -66,9 +79,13 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char
{
LOCK(cs_wallet);
if (pwalletdbEncryption)
- return pwalletdbEncryption->WriteCryptedKey(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 674bae66dd..a0688b8a02 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -123,6 +123,8 @@ public:
std::set<COutPoint> 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<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)
bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
bool AddCScript(const CScript& redeemScript);
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 8910cac4bc..96fd4a5fc1 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<uint256> vWalletUpgrade;
+
+ CWalletScanState() {
+ nKeys = nCKeys = nKeyMeta = 0;
+ fIsEncrypted = false;
+ fAnyUnordered = false;
+ nFileVersion = 0;
+ }
+};
bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
- int& nFileVersion, vector<uint256>& 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<unsigned char> 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<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")
{
@@ -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<uint256> 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<uint256> 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 9732eb29e4..287361b33d 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<unsigned char>& vchCryptedSecret, bool fEraseUnencryptedKey = true)
+ bool WriteCryptedKey(const CPubKey& vchPubKey,
+ const std::vector<unsigned char>& 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)