Permalink
Browse files

Use CWalletScanState, CKeyMetadata, CAffectedKeysVisitor

  • Loading branch information...
iamunick committed Jun 6, 2014
1 parent ca9673a commit 6b6782800471168920da6f8a468df3e978b7b83c
Showing with 147 additions and 37 deletions.
  1. +76 −6 src/wallet.cpp
  2. +9 −3 src/wallet.h
  3. +49 −26 src/walletdb.cpp
  4. +13 −2 src/walletdb.h
View
@@ -47,33 +47,52 @@ CPubKey CWallet::GenerateNewKey()
return key.GetPubKey();
}
bool CWallet::AddKey(const CKey& key)
bool CWallet::AddKey(const CKey& key, int64 nCreateTime)
{
if(!nCreateTime)
nCreateTime = GetTime();
if (!nTimeFirstKey || nCreateTime < nTimeFirstKey)
nTimeFirstKey = nCreateTime;
if (!CCryptoKeyStore::AddKey(key))
return false;
if (!fFileBacked)
return true;
if (!IsCrypted())
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.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)
return true;
{
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;
}
bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
{
if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey))
nTimeFirstKey = meta.nCreateTime;
mapKeyMetadata[pubkey.GetID()] = meta;
return true;
}
bool CWallet::AddCScript(const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
@@ -2179,7 +2198,7 @@ void CReserveKey::ReturnKey()
vchPubKey = CPubKey();
}
void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress)
void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const
{
setAddress.clear();
@@ -2199,6 +2218,57 @@ void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress)
}
}
void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const {
mapKeyBirth.clear();
// get birth times for keys with metadata
for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++)
if (it->second.nCreateTime)
mapKeyBirth[it->first] = it->second.nCreateTime;
// map in which we'll infer heights of other keys
CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin
std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock;
std::set<CKeyID> setKeys;
GetKeys(setKeys);
BOOST_FOREACH(const CKeyID &keyid, setKeys) {
if (mapKeyBirth.count(keyid) == 0)
mapKeyFirstBlock[keyid] = pindexMax;
}
setKeys.clear();
// if there are no such keys, we're done
if (mapKeyFirstBlock.empty())
return;
// find first block that affects those keys, if there are any left
std::vector<CKeyID> vAffected;
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) {
// iterate over all wallet transactions...
const CWalletTx &wtx = (*it).second;
std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) {
// ... which are already in a block
int nHeight = blit->second->nHeight;
BOOST_FOREACH(const CTxOut &txout, wtx.vout) {
// iterate over all their outputs
::ExtractAffectedKeys(*this, txout.scriptPubKey, vAffected);
BOOST_FOREACH(const CKeyID &keyid, vAffected) {
// ... and all their affected keys
std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
rit->second = blit->second;
}
vAffected.clear();
}
}
}
// Extract block timestamps for those keys
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
}
void CWallet::UpdatedTransaction(const uint256 &hashTx)
{
{
View
@@ -87,6 +87,7 @@ class CWallet : public CCryptoKeyStore
std::string strWalletFile;
std::set<int64> setKeyPool;
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
@@ -120,6 +121,7 @@ class CWallet : public CCryptoKeyStore
std::map<CTxDestination, std::string> mapAddressBook;
CPubKey vchDefaultKey;
int64 nTimeFirstKey;
// check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
@@ -130,14 +132,16 @@ class CWallet : public CCryptoKeyStore
// Generate a new key
CPubKey GenerateNewKey();
// Adds a key to the store, and saves it to disk.
bool AddKey(const CKey& key);
bool AddKey(const CKey& key, int64 nCreateTime = 0);
// Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key) { return CCryptoKeyStore::AddKey(key); }
// Load metadata (used by LoadWallet)
bool LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &metadata);
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) { SetMinVersion(FEATURE_WALLETCRYPT); return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
bool AddCScript(const CScript& redeemScript);
@@ -147,6 +151,8 @@ class CWallet : public CCryptoKeyStore
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
void GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const;
/** Increment the next transaction order id
@return next transaction order id
*/
@@ -191,7 +197,7 @@ class CWallet : public CCryptoKeyStore
void ReturnKey(int64 nIndex);
bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true);
int64 GetOldestKeyPoolTime();
void GetAllReserveKeys(std::set<CKeyID>& setAddress);
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, int64> GetAddressBalances();
View
@@ -201,8 +201,7 @@ class CWalletScanState {
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
@@ -246,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());
@@ -269,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")
@@ -284,6 +283,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CKey key;
if (strType == "key")
{
wss.nKeys++;
CPrivKey pkey;
ssValue >> pkey;
key.SetPubKey(vchPubKey);
@@ -347,6 +347,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
else if (strType == "ckey")
{
wss.nCKeys++;
vector<unsigned char> vchPubKey;
ssKey >> vchPubKey;
vector<unsigned char> vchPrivKey;
@@ -356,7 +357,22 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strErr = "Error reading wallet database: LoadCryptedKey failed";
return false;
}
fIsEncrypted = true;
wss.fIsEncrypted = true;
}
else if (strType == "keymeta")
{
CPubKey vchPubKey;
ssKey >> vchPubKey;
CKeyMetadata keyMeta;
ssValue >> keyMeta;
wss.nKeyMeta++;
pwallet->LoadKeyMetadata(vchPubKey, keyMeta);
// find earliest key creation time, as wallet birthday
if (!pwallet->nTimeFirstKey ||
(keyMeta.nCreateTime < pwallet->nTimeFirstKey))
pwallet->nTimeFirstKey = keyMeta.nCreateTime;
}
else if (strType == "defaultkey")
{
@@ -366,11 +382,20 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
{
int64 nIndex;
ssKey >> nIndex;
CKeyPool keypool;
ssValue >> keypool;
pwallet->setKeyPool.insert(nIndex);
// If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually
// stored metadata for that key later, which is fine.
CKeyID keyid = keypool.vchPubKey.GetID();
if (pwallet->mapKeyMetadata.count(keyid) == 0)
pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
else if (strType == "version")
{
ssValue >> nFileVersion;
ssValue >> wss.nFileVersion;
if (nFileVersion == 10300)
nFileVersion = 300;
}
@@ -406,10 +431,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;
@@ -447,8 +469,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:
@@ -481,19 +502,26 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (result != DB_LOAD_OK)
return result;
printf("nFileVersion = %d\n", nFileVersion);
printf("nFileVersion = %d\n", wss.nFileVersion);
printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n",
wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys);
BOOST_FOREACH(uint256 hash, vWalletUpgrade)
// nTimeFirstKey is only reliable if all keys have metadata
if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta)
pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value'
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;
@@ -649,10 +677,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)
@@ -663,9 +688,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)
View
@@ -81,15 +81,26 @@ class CWalletDB : public CDB
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.Raw()), 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)
{
nWalletDBUpdated++;
bool fEraseUnencryptedKey = true;
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.Raw()), vchCryptedSecret, false))
return false;
if (fEraseUnencryptedKey)

0 comments on commit 6b67828

Please sign in to comment.