aboutsummaryrefslogtreecommitdiff
path: root/src/walletdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/walletdb.cpp')
-rw-r--r--src/walletdb.cpp197
1 files changed, 143 insertions, 54 deletions
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 4a73413d26..831ef8b00d 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -1,10 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "walletdb.h"
#include "wallet.h"
+#include <boost/version.hpp>
#include <boost/filesystem.hpp>
using namespace std;
@@ -31,6 +32,18 @@ bool CWalletDB::EraseName(const string& strAddress)
return Erase(make_pair(string("name"), strAddress));
}
+bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose)
+{
+ nWalletDBUpdated++;
+ return Write(make_pair(string("purpose"), strAddress), strPurpose);
+}
+
+bool CWalletDB::ErasePurpose(const string& strPurpose)
+{
+ nWalletDBUpdated++;
+ return Erase(make_pair(string("purpose"), strPurpose));
+}
+
bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
{
account.SetNull();
@@ -72,7 +85,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin
if (!pcursor)
throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
unsigned int fFlags = DB_SET_RANGE;
- loop
+ while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
@@ -180,11 +193,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
@@ -195,7 +224,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
{
string strAddress;
ssKey >> strAddress;
- ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()];
+ ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name;
+ }
+ else if (strType == "purpose")
+ {
+ string strAddress;
+ ssKey >> strAddress;
+ ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose;
}
else if (strType == "tx")
{
@@ -204,7 +239,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletTx& wtx = pwallet->mapWallet[hash];
ssValue >> wtx;
CValidationState state;
- if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid())
+ if (CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())
wtx.BindWallet(pwallet);
else
{
@@ -229,15 +264,15 @@ 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());
- //printf(" %12"PRI64d" %s %s %s\n",
+ //LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
+ //LogPrintf(" %12"PRI64d" %s %s %s\n",
// wtx.vout[0].nValue,
// DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()).c_str(),
// wtx.hashBlock.ToString().c_str(),
@@ -252,12 +287,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")
@@ -271,21 +306,51 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
CKey key;
CPrivKey pkey;
+ uint256 hash = 0;
+
if (strType == "key")
+ {
+ wss.nKeys++;
ssValue >> pkey;
- else {
+ } else {
CWalletKey wkey;
ssValue >> wkey;
pkey = wkey.vchPrivKey;
}
- if (!key.SetPrivKey(pkey, vchPubKey.IsCompressed()))
+
+ // Old wallets store keys as "key" [pubkey] => [privkey]
+ // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
+ // using EC operations as a checksum.
+ // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
+ // remaining backwards-compatible.
+ try
{
- strErr = "Error reading wallet database: CPrivKey corrupt";
- return false;
+ ssValue >> hash;
}
- if (key.GetPubKey() != vchPubKey)
+ catch(...){}
+
+ bool fSkipCheck = false;
+
+ if (hash != 0)
{
- strErr = "Error reading wallet database: CPrivKey pubkey inconsistency";
+ // hash pubkey/privkey to accelerate wallet load
+ std::vector<unsigned char> vchKey;
+ vchKey.reserve(vchPubKey.size() + pkey.size());
+ vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
+ vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
+
+ if (Hash(vchKey.begin(), vchKey.end()) != hash)
+ {
+ strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
+ return false;
+ }
+
+ fSkipCheck = true;
+ }
+
+ if (!key.Load(pkey, vchPubKey, fSkipCheck))
+ {
+ strErr = "Error reading wallet database: CPrivKey corrupt";
return false;
}
if (!pwallet->LoadKey(key, vchPubKey))
@@ -315,12 +380,29 @@ 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")
+ {
+ 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")
{
@@ -330,13 +412,22 @@ 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;
- if (nFileVersion == 10300)
- nFileVersion = 300;
+ ssValue >> wss.nFileVersion;
+ if (wss.nFileVersion == 10300)
+ wss.nFileVersion = 300;
}
else if (strType == "cscript")
{
@@ -370,10 +461,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;
@@ -391,11 +479,11 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
Dbc* pcursor = GetCursor();
if (!pcursor)
{
- printf("Error getting wallet database cursor\n");
+ LogPrintf("Error getting wallet database cursor\n");
return DB_CORRUPT;
}
- loop
+ while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
@@ -405,14 +493,13 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
break;
else if (ret != 0)
{
- printf("Error reading next record from wallet database\n");
+ LogPrintf("Error reading next record from wallet database\n");
return DB_CORRUPT;
}
// 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:
@@ -428,7 +515,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
}
}
if (!strErr.empty())
- printf("%s\n", strErr.c_str());
+ LogPrintf("%s\n", strErr.c_str());
}
pcursor->close();
}
@@ -447,19 +534,26 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (result != DB_LOAD_OK)
return result;
- printf("nFileVersion = %d\n", nFileVersion);
+ LogPrintf("nFileVersion = %d\n", wss.nFileVersion);
+
+ LogPrintf("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;
@@ -510,7 +604,7 @@ void ThreadFlushWalletDB(const string& strFile)
map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
if (mi != bitdb.mapFileUseCount.end())
{
- printf("Flushing wallet.dat\n");
+ LogPrint("db", "Flushing wallet.dat\n");
nLastFlushed = nWalletDBUpdated;
int64 nStart = GetTimeMillis();
@@ -519,7 +613,7 @@ void ThreadFlushWalletDB(const string& strFile)
bitdb.CheckpointLSN(strFile);
bitdb.mapFileUseCount.erase(mi++);
- printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
+ LogPrint("db", "Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
}
}
}
@@ -554,10 +648,10 @@ bool BackupWallet(const CWallet& wallet, const string& strDest)
#else
filesystem::copy_file(pathSrc, pathDest);
#endif
- printf("copied wallet.dat to %s\n", pathDest.string().c_str());
+ LogPrintf("copied wallet.dat to %s\n", pathDest.string().c_str());
return true;
} catch(const filesystem::filesystem_error &e) {
- printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
+ LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
return false;
}
}
@@ -585,10 +679,10 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL,
newFilename.c_str(), DB_AUTO_COMMIT);
if (result == 0)
- printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
+ LogPrintf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str());
else
{
- printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
+ LogPrintf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str());
return false;
}
@@ -596,10 +690,10 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
bool allOK = dbenv.Salvage(newFilename, true, salvagedData);
if (salvagedData.empty())
{
- printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
+ LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str());
return false;
}
- printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size());
+ LogPrintf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size());
bool fSuccess = allOK;
Db* pdbCopy = new Db(&dbenv.dbenv, 0);
@@ -611,14 +705,11 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys)
0);
if (ret > 0)
{
- printf("Cannot create database file %s\n", filename.c_str());
+ LogPrintf("Cannot create database file %s\n", filename.c_str());
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,14 +720,12 @@ 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)
{
- printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
+ LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str());
continue;
}
}