Skip to content
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
@@ -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)
{
{
@@ -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();
@@ -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)
@@ -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.
You can’t perform that action at this time.