Merge #9951: Wallet database handling abstractions/simplifications

911a480 wallet: Add comment describing the various classes in walletdb.h (Wladimir J. van der Laan)
69d2e9b wallet: Make IsDummy private in CWalletDBWrapper (Wladimir J. van der Laan)
3323281 wallet: CWalletDB CDB composition not inheritance (Wladimir J. van der Laan)
be9e1a9 wallet: Reduce references to global bitdb environment (Wladimir J. van der Laan)
071c955 wallet: Get rid of fFileBacked (Wladimir J. van der Laan)
71afe3c wallet: Introduce database handle wrapper (Wladimir J. van der Laan)

Tree-SHA512: e4e72953c61a2f6995d609a32f8ed8e18cab9a92bc9e193d46a1d1f06d9daa5c6da6fce2867d4e3ba4fc0439141901a3d35f246486f0fa8f59587786379dfcbd
This commit is contained in:
Wladimir J. van der Laan 2017-04-24 14:43:38 +02:00
commit fa1ac2881f
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
9 changed files with 356 additions and 243 deletions

View File

@ -86,7 +86,8 @@ void WalletTests::walletTests()
TestChain100Setup test; TestChain100Setup test;
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
bitdb.MakeMock(); bitdb.MakeMock();
CWallet wallet("wallet_test.dat"); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
CWallet wallet(std::move(dbw));
bool firstRun; bool firstRun;
wallet.LoadWallet(firstRun); wallet.LoadWallet(firstRun);
{ {

View File

@ -359,13 +359,16 @@ void CDBEnv::CheckpointLSN(const std::string& strFile)
} }
CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL) CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL)
{ {
int ret; int ret;
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
fFlushOnClose = fFlushOnCloseIn; fFlushOnClose = fFlushOnCloseIn;
if (strFilename.empty()) env = dbw.env;
if (dbw.IsDummy()) {
return; return;
}
const std::string &strFilename = dbw.strFile;
bool fCreate = strchr(pszMode, 'c') != NULL; bool fCreate = strchr(pszMode, 'c') != NULL;
unsigned int nFlags = DB_THREAD; unsigned int nFlags = DB_THREAD;
@ -373,17 +376,17 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
nFlags |= DB_CREATE; nFlags |= DB_CREATE;
{ {
LOCK(bitdb.cs_db); LOCK(env->cs_db);
if (!bitdb.Open(GetDataDir())) if (!env->Open(GetDataDir()))
throw std::runtime_error("CDB: Failed to open database environment."); throw std::runtime_error("CDB: Failed to open database environment.");
strFile = strFilename; strFile = strFilename;
++bitdb.mapFileUseCount[strFile]; ++env->mapFileUseCount[strFile];
pdb = bitdb.mapDb[strFile]; pdb = env->mapDb[strFile];
if (pdb == NULL) { if (pdb == NULL) {
pdb = new Db(bitdb.dbenv, 0); pdb = new Db(env->dbenv, 0);
bool fMockDb = bitdb.IsMock(); bool fMockDb = env->IsMock();
if (fMockDb) { if (fMockDb) {
DbMpoolFile* mpf = pdb->get_mpf(); DbMpoolFile* mpf = pdb->get_mpf();
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
@ -401,7 +404,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
if (ret != 0) { if (ret != 0) {
delete pdb; delete pdb;
pdb = NULL; pdb = NULL;
--bitdb.mapFileUseCount[strFile]; --env->mapFileUseCount[strFile];
strFile = ""; strFile = "";
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
} }
@ -413,7 +416,7 @@ CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnClose
fReadOnly = fTmp; fReadOnly = fTmp;
} }
bitdb.mapDb[strFile] = pdb; env->mapDb[strFile] = pdb;
} }
} }
} }
@ -428,7 +431,7 @@ void CDB::Flush()
if (fReadOnly) if (fReadOnly)
nMinutes = 1; nMinutes = 1;
bitdb.dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); env->dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
} }
void CDB::Close() void CDB::Close()
@ -444,8 +447,8 @@ void CDB::Close()
Flush(); Flush();
{ {
LOCK(bitdb.cs_db); LOCK(env->cs_db);
--bitdb.mapFileUseCount[strFile]; --env->mapFileUseCount[strFile];
} }
} }
@ -472,23 +475,28 @@ bool CDBEnv::RemoveDb(const std::string& strFile)
return (rc == 0); return (rc == 0);
} }
bool CDB::Rewrite(const std::string& strFile, const char* pszSkip) bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
{ {
if (dbw.IsDummy()) {
return true;
}
CDBEnv *env = dbw.env;
const std::string& strFile = dbw.strFile;
while (true) { while (true) {
{ {
LOCK(bitdb.cs_db); LOCK(env->cs_db);
if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) { if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
// Flush log data to the dat file // Flush log data to the dat file
bitdb.CloseDb(strFile); env->CloseDb(strFile);
bitdb.CheckpointLSN(strFile); env->CheckpointLSN(strFile);
bitdb.mapFileUseCount.erase(strFile); env->mapFileUseCount.erase(strFile);
bool fSuccess = true; bool fSuccess = true;
LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile); LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile);
std::string strFileRes = strFile + ".rewrite"; std::string strFileRes = strFile + ".rewrite";
{ // surround usage of db with extra {} { // surround usage of db with extra {}
CDB db(strFile.c_str(), "r"); CDB db(dbw, "r");
Db* pdbCopy = new Db(bitdb.dbenv, 0); Db* pdbCopy = new Db(env->dbenv, 0);
int ret = pdbCopy->open(NULL, // Txn pointer int ret = pdbCopy->open(NULL, // Txn pointer
strFileRes.c_str(), // Filename strFileRes.c_str(), // Filename
@ -531,17 +539,17 @@ bool CDB::Rewrite(const std::string& strFile, const char* pszSkip)
} }
if (fSuccess) { if (fSuccess) {
db.Close(); db.Close();
bitdb.CloseDb(strFile); env->CloseDb(strFile);
if (pdbCopy->close(0)) if (pdbCopy->close(0))
fSuccess = false; fSuccess = false;
delete pdbCopy; delete pdbCopy;
} }
} }
if (fSuccess) { if (fSuccess) {
Db dbA(bitdb.dbenv, 0); Db dbA(env->dbenv, 0);
if (dbA.remove(strFile.c_str(), NULL, 0)) if (dbA.remove(strFile.c_str(), NULL, 0))
fSuccess = false; fSuccess = false;
Db dbB(bitdb.dbenv, 0); Db dbB(env->dbenv, 0);
if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
fSuccess = false; fSuccess = false;
} }
@ -596,16 +604,21 @@ void CDBEnv::Flush(bool fShutdown)
} }
} }
bool CDB::PeriodicFlush(std::string strFile) bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
{ {
if (dbw.IsDummy()) {
return true;
}
bool ret = false; bool ret = false;
CDBEnv *env = dbw.env;
const std::string& strFile = dbw.strFile;
TRY_LOCK(bitdb.cs_db,lockDb); TRY_LOCK(bitdb.cs_db,lockDb);
if (lockDb) if (lockDb)
{ {
// Don't do this if any databases are in use // Don't do this if any databases are in use
int nRefCount = 0; int nRefCount = 0;
std::map<std::string, int>::iterator mit = bitdb.mapFileUseCount.begin(); std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
while (mit != bitdb.mapFileUseCount.end()) while (mit != env->mapFileUseCount.end())
{ {
nRefCount += (*mit).second; nRefCount += (*mit).second;
mit++; mit++;
@ -614,17 +627,17 @@ bool CDB::PeriodicFlush(std::string strFile)
if (nRefCount == 0) if (nRefCount == 0)
{ {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
std::map<std::string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile); std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
if (mi != bitdb.mapFileUseCount.end()) if (mi != env->mapFileUseCount.end())
{ {
LogPrint(BCLog::DB, "Flushing %s\n", strFile); LogPrint(BCLog::DB, "Flushing %s\n", strFile);
int64_t nStart = GetTimeMillis(); int64_t nStart = GetTimeMillis();
// Flush wallet file so it's self contained // Flush wallet file so it's self contained
bitdb.CloseDb(strFile); env->CloseDb(strFile);
bitdb.CheckpointLSN(strFile); env->CheckpointLSN(strFile);
bitdb.mapFileUseCount.erase(mi++); env->mapFileUseCount.erase(mi++);
LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart); LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
ret = true; ret = true;
} }
@ -633,3 +646,52 @@ bool CDB::PeriodicFlush(std::string strFile)
return ret; return ret;
} }
bool CWalletDBWrapper::Rewrite(const char* pszSkip)
{
return CDB::Rewrite(*this, pszSkip);
}
bool CWalletDBWrapper::Backup(const std::string& strDest)
{
if (IsDummy()) {
return false;
}
while (true)
{
{
LOCK(env->cs_db);
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
{
// Flush log data to the dat file
env->CloseDb(strFile);
env->CheckpointLSN(strFile);
env->mapFileUseCount.erase(strFile);
// Copy wallet file
fs::path pathSrc = GetDataDir() / strFile;
fs::path pathDest(strDest);
if (fs::is_directory(pathDest))
pathDest /= strFile;
try {
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;
} catch (const fs::filesystem_error& e) {
LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
return false;
}
}
}
MilliSleep(100);
}
return false;
}
void CWalletDBWrapper::Flush(bool shutdown)
{
if (!IsDummy()) {
env->Flush(shutdown);
}
}

View File

@ -86,6 +86,52 @@ public:
extern CDBEnv bitdb; extern CDBEnv bitdb;
/** An instance of this class represents one database.
* For BerkeleyDB this is just a (env, strFile) tuple.
**/
class CWalletDBWrapper
{
friend class CDB;
public:
/** Create dummy DB handle */
CWalletDBWrapper(): env(nullptr)
{
}
/** Create DB handle to real database */
CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in):
env(env_in), strFile(strFile_in)
{
}
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
*/
bool Rewrite(const char* pszSkip=nullptr);
/** Back up the entire database to a file.
*/
bool Backup(const std::string& strDest);
/** Get a name for this database, for debugging etc.
*/
std::string GetName() const { return strFile; }
/** Make sure all changes are flushed to disk.
*/
void Flush(bool shutdown);
private:
/** BerkeleyDB specific */
CDBEnv *env;
std::string strFile;
/** Return whether this database handle is a dummy for testing.
* Only to be used at a low level, application should ideally not care
* about this.
*/
bool IsDummy() { return env == nullptr; }
};
/** RAII class that provides access to a Berkeley database */ /** RAII class that provides access to a Berkeley database */
class CDB class CDB
@ -96,18 +142,19 @@ protected:
DbTxn* activeTxn; DbTxn* activeTxn;
bool fReadOnly; bool fReadOnly;
bool fFlushOnClose; bool fFlushOnClose;
CDBEnv *env;
explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB() { Close(); }
public: public:
explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB() { Close(); }
void Flush(); void Flush();
void Close(); void Close();
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)); static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
/* flush the wallet passively (TRY_LOCK) /* flush the wallet passively (TRY_LOCK)
ideal to be called periodically */ ideal to be called periodically */
static bool PeriodicFlush(std::string strFile); static bool PeriodicFlush(CWalletDBWrapper& dbw);
/* verifies the database environment */ /* verifies the database environment */
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr); static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
/* verifies the database file */ /* verifies the database file */
@ -117,7 +164,7 @@ private:
CDB(const CDB&); CDB(const CDB&);
void operator=(const CDB&); void operator=(const CDB&);
protected: public:
template <typename K, typename T> template <typename K, typename T>
bool Read(const K& key, T& value) bool Read(const K& key, T& value)
{ {
@ -156,7 +203,7 @@ protected:
bool Write(const K& key, const T& value, bool fOverwrite = true) bool Write(const K& key, const T& value, bool fOverwrite = true)
{ {
if (!pdb) if (!pdb)
return false; return true;
if (fReadOnly) if (fReadOnly)
assert(!"Write called on database in read-only mode"); assert(!"Write called on database in read-only mode");
@ -310,7 +357,7 @@ public:
return Write(std::string("version"), nVersion); return Write(std::string("version"), nVersion);
} }
bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = NULL);
}; };
#endif // BITCOIN_WALLET_DB_H #endif // BITCOIN_WALLET_DB_H

View File

@ -2078,7 +2078,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
int64_t nSleepTime = request.params[1].get_int64(); int64_t nSleepTime = request.params[1].get_int64();
pwallet->nRelockTime = GetTime() + nSleepTime; pwallet->nRelockTime = GetTime() + nSleepTime;
RPCRunLater(strprintf("lockwallet(%s)", pwallet->strWalletFile), boost::bind(LockWallet, pwallet), nSleepTime); RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), boost::bind(LockWallet, pwallet), nSleepTime);
return NullUniValue; return NullUniValue;
} }

View File

@ -14,7 +14,8 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
bitdb.MakeMock(); bitdb.MakeMock();
bool fFirstRun; bool fFirstRun;
pwalletMain = new CWallet("wallet_test.dat"); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
pwalletMain = new CWallet(std::move(dbw));
pwalletMain->LoadWallet(fFirstRun); pwalletMain->LoadWallet(fFirstRun);
RegisterValidationInterface(pwalletMain); RegisterValidationInterface(pwalletMain);

View File

@ -162,7 +162,7 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter
secret = childKey.key; secret = childKey.key;
metadata.hdMasterKeyID = hdChain.masterKeyID; metadata.hdMasterKeyID = hdChain.masterKeyID;
// update the chain model in the database // update the chain model in the database
if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) if (!CWalletDB(*dbw).WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
} }
@ -181,10 +181,8 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
if (HaveWatchOnly(script)) if (HaveWatchOnly(script))
RemoveWatchOnly(script); RemoveWatchOnly(script);
if (!fFileBacked)
return true;
if (!IsCrypted()) { if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteKey(pubkey, return CWalletDB(*dbw).WriteKey(pubkey,
secret.GetPrivKey(), secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]); mapKeyMetadata[pubkey.GetID()]);
} }
@ -196,8 +194,6 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
{ {
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false; return false;
if (!fFileBacked)
return true;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (pwalletdbEncryption) if (pwalletdbEncryption)
@ -205,7 +201,7 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
vchCryptedSecret, vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]); mapKeyMetadata[vchPubKey.GetID()]);
else else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, return CWalletDB(*dbw).WriteCryptedKey(vchPubKey,
vchCryptedSecret, vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]); mapKeyMetadata[vchPubKey.GetID()]);
} }
@ -241,9 +237,7 @@ bool CWallet::AddCScript(const CScript& redeemScript)
{ {
if (!CCryptoKeyStore::AddCScript(redeemScript)) if (!CCryptoKeyStore::AddCScript(redeemScript))
return false; return false;
if (!fFileBacked) return CWalletDB(*dbw).WriteCScript(Hash160(redeemScript), redeemScript);
return true;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
} }
bool CWallet::LoadCScript(const CScript& redeemScript) bool CWallet::LoadCScript(const CScript& redeemScript)
@ -269,9 +263,7 @@ bool CWallet::AddWatchOnly(const CScript& dest)
const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)]; const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime); UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true); NotifyWatchonlyChanged(true);
if (!fFileBacked) return CWalletDB(*dbw).WriteWatchOnly(dest, meta);
return true;
return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta);
} }
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
@ -287,8 +279,7 @@ bool CWallet::RemoveWatchOnly(const CScript &dest)
return false; return false;
if (!HaveWatchOnly()) if (!HaveWatchOnly())
NotifyWatchonlyChanged(false); NotifyWatchonlyChanged(false);
if (fFileBacked) if (!CWalletDB(*dbw).EraseWatchOnly(dest))
if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))
return false; return false;
return true; return true;
@ -354,7 +345,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false; return false;
if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey)) if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey))
return false; return false;
CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); CWalletDB(*dbw).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked) if (fWasLocked)
Lock(); Lock();
return true; return true;
@ -367,7 +358,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
void CWallet::SetBestChain(const CBlockLocator& loc) void CWallet::SetBestChain(const CBlockLocator& loc)
{ {
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
walletdb.WriteBestBlock(loc); walletdb.WriteBestBlock(loc);
} }
@ -386,9 +377,8 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn,
if (nVersion > nWalletMaxVersion) if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion; nWalletMaxVersion = nVersion;
if (fFileBacked)
{ {
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(*dbw);
if (nWalletVersion > 40000) if (nWalletVersion > 40000)
pwalletdb->WriteMinVersion(nWalletVersion); pwalletdb->WriteMinVersion(nWalletVersion);
if (!pwalletdbIn) if (!pwalletdbIn)
@ -442,7 +432,7 @@ bool CWallet::HasWalletSpend(const uint256& txid) const
void CWallet::Flush(bool shutdown) void CWallet::Flush(bool shutdown)
{ {
bitdb.Flush(shutdown); dbw->Flush(shutdown);
} }
bool CWallet::Verify() bool CWallet::Verify()
@ -595,24 +585,19 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
if (fFileBacked)
{
assert(!pwalletdbEncryption); assert(!pwalletdbEncryption);
pwalletdbEncryption = new CWalletDB(strWalletFile); pwalletdbEncryption = new CWalletDB(*dbw);
if (!pwalletdbEncryption->TxnBegin()) { if (!pwalletdbEncryption->TxnBegin()) {
delete pwalletdbEncryption; delete pwalletdbEncryption;
pwalletdbEncryption = NULL; pwalletdbEncryption = NULL;
return false; return false;
} }
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
}
if (!EncryptKeys(_vMasterKey)) if (!EncryptKeys(_vMasterKey))
{ {
if (fFileBacked) {
pwalletdbEncryption->TxnAbort(); pwalletdbEncryption->TxnAbort();
delete pwalletdbEncryption; delete pwalletdbEncryption;
}
// We now probably have half of our keys encrypted in memory, and half not... // We now probably have half of our keys encrypted in memory, and half not...
// die and let the user reload the unencrypted wallet. // die and let the user reload the unencrypted wallet.
assert(false); assert(false);
@ -621,8 +606,6 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Encryption was introduced in version 0.4.0 // Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
if (fFileBacked)
{
if (!pwalletdbEncryption->TxnCommit()) { if (!pwalletdbEncryption->TxnCommit()) {
delete pwalletdbEncryption; delete pwalletdbEncryption;
// We now have keys encrypted in memory, but not on disk... // We now have keys encrypted in memory, but not on disk...
@ -632,7 +615,6 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
delete pwalletdbEncryption; delete pwalletdbEncryption;
pwalletdbEncryption = NULL; pwalletdbEncryption = NULL;
}
Lock(); Lock();
Unlock(strWalletPassphrase); Unlock(strWalletPassphrase);
@ -652,7 +634,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// Need to completely rewrite the wallet file; if we don't, bdb might keep // Need to completely rewrite the wallet file; if we don't, bdb might keep
// bits of the unencrypted private key in slack space in the database file. // bits of the unencrypted private key in slack space in the database file.
CDB::Rewrite(strWalletFile); dbw->Rewrite();
} }
NotifyStatusChanged(this); NotifyStatusChanged(this);
@ -663,7 +645,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
DBErrors CWallet::ReorderTransactions() DBErrors CWallet::ReorderTransactions()
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
// Old wallets didn't have any defined order for transactions // Old wallets didn't have any defined order for transactions
// Probably a bad idea to change the output of this // Probably a bad idea to change the output of this
@ -744,14 +726,14 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)
if (pwalletdb) { if (pwalletdb) {
pwalletdb->WriteOrderPosNext(nOrderPosNext); pwalletdb->WriteOrderPosNext(nOrderPosNext);
} else { } else {
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); CWalletDB(*dbw).WriteOrderPosNext(nOrderPosNext);
} }
return nRet; return nRet;
} }
bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment)
{ {
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
if (!walletdb.TxnBegin()) if (!walletdb.TxnBegin())
return false; return false;
@ -785,7 +767,7 @@ bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmoun
bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew) bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew)
{ {
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
CAccount account; CAccount account;
walletdb.ReadAccount(strAccount, account); walletdb.ReadAccount(strAccount, account);
@ -846,7 +828,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString(); wtx.mapValue["replaced_by_txid"] = newHash.ToString();
CWalletDB walletdb(strWalletFile, "r+"); CWalletDB walletdb(*dbw, "r+");
bool success = true; bool success = true;
if (!walletdb.WriteTx(wtx)) { if (!walletdb.WriteTx(wtx)) {
@ -863,7 +845,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); CWalletDB walletdb(*dbw, "r+", fFlushOnClose);
uint256 hash = wtxIn.GetHash(); uint256 hash = wtxIn.GetHash();
@ -1007,7 +989,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{ {
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
CWalletDB walletdb(strWalletFile, "r+"); CWalletDB walletdb(*dbw, "r+");
std::set<uint256> todo; std::set<uint256> todo;
std::set<uint256> done; std::set<uint256> done;
@ -1079,7 +1061,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
return; return;
// Do not flush the wallet here for performance reasons // Do not flush the wallet here for performance reasons
CWalletDB walletdb(strWalletFile, "r+", false); CWalletDB walletdb(*dbw, "r+", false);
std::set<uint256> todo; std::set<uint256> todo;
std::set<uint256> done; std::set<uint256> done;
@ -1362,7 +1344,7 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey, CHDChain *possibleOldChain)
bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain)) if (!memonly && !CWalletDB(*dbw).WriteHDChain(chain))
throw std::runtime_error(std::string(__func__) + ": writing chain failed"); throw std::runtime_error(std::string(__func__) + ": writing chain failed");
hdChain = chain; hdChain = chain;
@ -2759,13 +2741,13 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
} }
void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) { void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) {
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
return walletdb.ListAccountCreditDebit(strAccount, entries); return walletdb.ListAccountCreditDebit(strAccount, entries);
} }
bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry) bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry)
{ {
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
return AddAccountingEntry(acentry, &walletdb); return AddAccountingEntry(acentry, &walletdb);
} }
@ -2817,13 +2799,11 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
DBErrors CWallet::LoadWallet(bool& fFirstRunRet) DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{ {
if (!fFileBacked)
return DB_LOAD_OK;
fFirstRunRet = false; fFirstRunRet = false;
DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this);
if (nLoadWalletRet == DB_NEED_REWRITE) if (nLoadWalletRet == DB_NEED_REWRITE)
{ {
if (CDB::Rewrite(strWalletFile, "\x04pool")) if (dbw->Rewrite("\x04pool"))
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
setKeyPool.clear(); setKeyPool.clear();
@ -2844,17 +2824,15 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{ {
if (!fFileBacked)
return DB_LOAD_OK;
AssertLockHeld(cs_wallet); // mapWallet AssertLockHeld(cs_wallet); // mapWallet
vchDefaultKey = CPubKey(); vchDefaultKey = CPubKey();
DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(vHashIn, vHashOut); DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut) for (uint256 hash : vHashOut)
mapWallet.erase(hash); mapWallet.erase(hash);
if (nZapSelectTxRet == DB_NEED_REWRITE) if (nZapSelectTxRet == DB_NEED_REWRITE)
{ {
if (CDB::Rewrite(strWalletFile, "\x04pool")) if (dbw->Rewrite("\x04pool"))
{ {
setKeyPool.clear(); setKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked. // Note: can't top-up keypool here, because wallet is locked.
@ -2874,13 +2852,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
{ {
if (!fFileBacked)
return DB_LOAD_OK;
vchDefaultKey = CPubKey(); vchDefaultKey = CPubKey();
DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(vWtx); DBErrors nZapWalletTxRet = CWalletDB(*dbw,"cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE) if (nZapWalletTxRet == DB_NEED_REWRITE)
{ {
if (CDB::Rewrite(strWalletFile, "\x04pool")) if (dbw->Rewrite("\x04pool"))
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
setKeyPool.clear(); setKeyPool.clear();
@ -2910,11 +2886,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
} }
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!fFileBacked) if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
return false; return false;
if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName);
return false;
return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);
} }
bool CWallet::DelAddressBook(const CTxDestination& address) bool CWallet::DelAddressBook(const CTxDestination& address)
@ -2922,33 +2896,25 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
{ {
LOCK(cs_wallet); // mapAddressBook LOCK(cs_wallet); // mapAddressBook
if(fFileBacked)
{
// Delete destdata tuples associated with address // Delete destdata tuples associated with address
std::string strAddress = CBitcoinAddress(address).ToString(); std::string strAddress = CBitcoinAddress(address).ToString();
BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata) BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata)
{ {
CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); CWalletDB(*dbw).EraseDestData(strAddress, item.first);
}
} }
mapAddressBook.erase(address); mapAddressBook.erase(address);
} }
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);
if (!fFileBacked) CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString());
return false; return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString());
CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString());
return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());
} }
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
{ {
if (fFileBacked) if (!CWalletDB(*dbw).WriteDefaultKey(vchPubKey))
{
if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey))
return false; return false;
}
vchDefaultKey = vchPubKey; vchDefaultKey = vchPubKey;
return true; return true;
} }
@ -2961,7 +2927,7 @@ bool CWallet::NewKeyPool()
{ {
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
BOOST_FOREACH(int64_t nIndex, setKeyPool) BOOST_FOREACH(int64_t nIndex, setKeyPool)
walletdb.ErasePool(nIndex); walletdb.ErasePool(nIndex);
setKeyPool.clear(); setKeyPool.clear();
@ -2982,7 +2948,7 @@ size_t CWallet::KeypoolCountExternalKeys()
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
return setKeyPool.size(); return setKeyPool.size();
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
// count amount of external keys // count amount of external keys
size_t amountE = 0; size_t amountE = 0;
@ -3025,7 +2991,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
missingInternal = 0; missingInternal = 0;
} }
bool internal = false; bool internal = false;
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
for (int64_t i = missingInternal + missingExternal; i--;) for (int64_t i = missingInternal + missingExternal; i--;)
{ {
int64_t nEnd = 1; int64_t nEnd = 1;
@ -3056,7 +3022,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
if(setKeyPool.empty()) if(setKeyPool.empty())
return; return;
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
// try to find a key that matches the internal/external filter // try to find a key that matches the internal/external filter
for(const int64_t& id : setKeyPool) for(const int64_t& id : setKeyPool)
@ -3082,11 +3048,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
void CWallet::KeepKey(int64_t nIndex) void CWallet::KeepKey(int64_t nIndex)
{ {
// Remove from key pool // Remove from key pool
if (fFileBacked) CWalletDB walletdb(*dbw);
{
CWalletDB walletdb(strWalletFile);
walletdb.ErasePool(nIndex); walletdb.ErasePool(nIndex);
}
LogPrintf("keypool keep %d\n", nIndex); LogPrintf("keypool keep %d\n", nIndex);
} }
@ -3128,7 +3091,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
return GetTime(); return GetTime();
CKeyPool keypool; CKeyPool keypool;
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT))
{ {
@ -3296,7 +3259,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter) CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter)
{ {
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
} }
@ -3376,7 +3339,7 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
{ {
setAddress.clear(); setAddress.clear();
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(*dbw);
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(const int64_t& id, setKeyPool) BOOST_FOREACH(const int64_t& id, setKeyPool)
@ -3598,18 +3561,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co
return false; return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
if (!fFileBacked) return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
return true;
return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
} }
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
{ {
if (!mapAddressBook[dest].destdata.erase(key)) if (!mapAddressBook[dest].destdata.erase(key))
return false; return false;
if (!fFileBacked) return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key);
return true;
return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key);
} }
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
@ -3679,7 +3638,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
if (GetBoolArg("-zapwallettxes", false)) { if (GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet...")); uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
CWallet *tempWallet = new CWallet(walletFile); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
CWallet *tempWallet = new CWallet(std::move(dbw));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) { if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@ -3694,7 +3654,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
int64_t nStart = GetTimeMillis(); int64_t nStart = GetTimeMillis();
bool fFirstRun = true; bool fFirstRun = true;
CWallet *walletInstance = new CWallet(walletFile); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
CWallet *walletInstance = new CWallet(std::move(dbw));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK) if (nLoadWalletRet != DB_LOAD_OK)
{ {
@ -3785,7 +3746,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
CBlockIndex *pindexRescan = chainActive.Genesis(); CBlockIndex *pindexRescan = chainActive.Genesis();
if (!GetBoolArg("-rescan", false)) if (!GetBoolArg("-rescan", false))
{ {
CWalletDB walletdb(walletFile); CWalletDB walletdb(*walletInstance->dbw);
CBlockLocator locator; CBlockLocator locator;
if (walletdb.ReadBestBlock(locator)) if (walletdb.ReadBestBlock(locator))
pindexRescan = FindForkInGlobalIndex(chainActive, locator); pindexRescan = FindForkInGlobalIndex(chainActive, locator);
@ -3818,7 +3779,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
// Restore wallet transaction metadata after -zapwallettxes=1 // Restore wallet transaction metadata after -zapwallettxes=1
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
{ {
CWalletDB walletdb(walletFile); CWalletDB walletdb(*walletInstance->dbw);
BOOST_FOREACH(const CWalletTx& wtxOld, vWtx) BOOST_FOREACH(const CWalletTx& wtxOld, vWtx)
{ {
@ -3978,38 +3939,7 @@ bool CWallet::ParameterInteraction()
bool CWallet::BackupWallet(const std::string& strDest) bool CWallet::BackupWallet(const std::string& strDest)
{ {
if (!fFileBacked) return dbw->Backup(strDest);
return false;
while (true)
{
{
LOCK(bitdb.cs_db);
if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0)
{
// Flush log data to the dat file
bitdb.CloseDb(strWalletFile);
bitdb.CheckpointLSN(strWalletFile);
bitdb.mapFileUseCount.erase(strWalletFile);
// Copy wallet file
fs::path pathSrc = GetDataDir() / strWalletFile;
fs::path pathDest(strDest);
if (fs::is_directory(pathDest))
pathDest /= strWalletFile;
try {
fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string());
return true;
} catch (const fs::filesystem_error& e) {
LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what());
return false;
}
}
}
MilliSleep(100);
}
return false;
} }
CKeyPool::CKeyPool() CKeyPool::CKeyPool()

View File

@ -699,8 +699,6 @@ private:
/* HD derive new child key (on internal or external chain) */ /* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false); void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false);
bool fFileBacked;
std::set<int64_t> setKeyPool; std::set<int64_t> setKeyPool;
int64_t nTimeFirstKey; int64_t nTimeFirstKey;
@ -716,17 +714,33 @@ private:
*/ */
bool AddWatchOnly(const CScript& dest) override; bool AddWatchOnly(const CScript& dest) override;
std::unique_ptr<CWalletDBWrapper> dbw;
public: public:
/* /*
* Main wallet lock. * Main wallet lock.
* This lock protects all the fields added by CWallet * This lock protects all the fields added by CWallet.
* except for:
* fFileBacked (immutable after instantiation)
* strWalletFile (immutable after instantiation)
*/ */
mutable CCriticalSection cs_wallet; mutable CCriticalSection cs_wallet;
const std::string strWalletFile; /** Get database handle used by this wallet. Ideally this function would
* not be necessary.
*/
CWalletDBWrapper& GetDBHandle()
{
return *dbw;
}
/** Get a name for this wallet for logging/debugging purposes.
*/
std::string GetName() const
{
if (dbw) {
return dbw->GetName();
} else {
return "dummy";
}
}
void LoadKeyPool(int nIndex, const CKeyPool &keypool) void LoadKeyPool(int nIndex, const CKeyPool &keypool)
{ {
@ -748,15 +762,16 @@ public:
MasterKeyMap mapMasterKeys; MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID; unsigned int nMasterKeyMaxID;
CWallet() // Create wallet with dummy database handle
CWallet(): dbw(new CWalletDBWrapper())
{ {
SetNull(); SetNull();
} }
CWallet(const std::string& strWalletFileIn) : strWalletFile(strWalletFileIn) // Create wallet with passed-in database handle
CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in))
{ {
SetNull(); SetNull();
fFileBacked = true;
} }
~CWallet() ~CWallet()
@ -769,7 +784,6 @@ public:
{ {
nWalletVersion = FEATURE_BASE; nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE; nWalletMaxVersion = FEATURE_BASE;
fFileBacked = false;
nMasterKeyMaxID = 0; nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL; pwalletdbEncryption = NULL;
nOrderPosNext = 0; nOrderPosNext = 0;

View File

@ -33,7 +33,7 @@ static std::atomic<unsigned int> nWalletDBUpdateCounter;
bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName) bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(make_pair(std::string("name"), strAddress), strName); return batch.Write(std::make_pair(std::string("name"), strAddress), strName);
} }
bool CWalletDB::EraseName(const std::string& strAddress) bool CWalletDB::EraseName(const std::string& strAddress)
@ -41,38 +41,38 @@ bool CWalletDB::EraseName(const std::string& strAddress)
// This should only be used for sending addresses, never for receiving addresses, // This should only be used for sending addresses, never for receiving addresses,
// receiving addresses must always have an address book entry if they're not change return. // receiving addresses must always have an address book entry if they're not change return.
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Erase(make_pair(std::string("name"), strAddress)); return batch.Erase(std::make_pair(std::string("name"), strAddress));
} }
bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose) bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(make_pair(std::string("purpose"), strAddress), strPurpose); return batch.Write(std::make_pair(std::string("purpose"), strAddress), strPurpose);
} }
bool CWalletDB::ErasePurpose(const std::string& strPurpose) bool CWalletDB::ErasePurpose(const std::string& strPurpose)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Erase(make_pair(std::string("purpose"), strPurpose)); return batch.Erase(std::make_pair(std::string("purpose"), strPurpose));
} }
bool CWalletDB::WriteTx(const CWalletTx& wtx) bool CWalletDB::WriteTx(const CWalletTx& wtx)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); return batch.Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
} }
bool CWalletDB::EraseTx(uint256 hash) bool CWalletDB::EraseTx(uint256 hash)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("tx"), hash)); return batch.Erase(std::make_pair(std::string("tx"), hash));
} }
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta, false)) keyMeta, false))
return false; return false;
@ -82,7 +82,7 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); return batch.Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
} }
bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
@ -92,16 +92,16 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
const bool fEraseUnencryptedKey = true; const bool fEraseUnencryptedKey = true;
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta)) keyMeta))
return false; return false;
if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) if (!batch.Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
return false; return false;
if (fEraseUnencryptedKey) if (fEraseUnencryptedKey)
{ {
Erase(std::make_pair(std::string("key"), vchPubKey)); batch.Erase(std::make_pair(std::string("key"), vchPubKey));
Erase(std::make_pair(std::string("wkey"), vchPubKey)); batch.Erase(std::make_pair(std::string("wkey"), vchPubKey));
} }
return true; return true;
} }
@ -109,92 +109,92 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); return batch.Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
} }
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); return batch.Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
} }
bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta)) if (!batch.Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
return false; return false;
return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); return batch.Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
} }
bool CWalletDB::EraseWatchOnly(const CScript &dest) bool CWalletDB::EraseWatchOnly(const CScript &dest)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) if (!batch.Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
return false; return false;
return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); return batch.Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
} }
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan batch.Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
return Write(std::string("bestblock_nomerkle"), locator); return batch.Write(std::string("bestblock_nomerkle"), locator);
} }
bool CWalletDB::ReadBestBlock(CBlockLocator& locator) bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
{ {
if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; if (batch.Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true;
return Read(std::string("bestblock_nomerkle"), locator); return batch.Read(std::string("bestblock_nomerkle"), locator);
} }
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::string("orderposnext"), nOrderPosNext); return batch.Write(std::string("orderposnext"), nOrderPosNext);
} }
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::string("defaultkey"), vchPubKey); return batch.Write(std::string("defaultkey"), vchPubKey);
} }
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
{ {
return Read(std::make_pair(std::string("pool"), nPool), keypool); return batch.Read(std::make_pair(std::string("pool"), nPool), keypool);
} }
bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("pool"), nPool), keypool); return batch.Write(std::make_pair(std::string("pool"), nPool), keypool);
} }
bool CWalletDB::ErasePool(int64_t nPool) bool CWalletDB::ErasePool(int64_t nPool)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("pool"), nPool)); return batch.Erase(std::make_pair(std::string("pool"), nPool));
} }
bool CWalletDB::WriteMinVersion(int nVersion) bool CWalletDB::WriteMinVersion(int nVersion)
{ {
return Write(std::string("minversion"), nVersion); return batch.Write(std::string("minversion"), nVersion);
} }
bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account) bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account)
{ {
account.SetNull(); account.SetNull();
return Read(make_pair(std::string("acc"), strAccount), account); return batch.Read(std::make_pair(std::string("acc"), strAccount), account);
} }
bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account) bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account)
{ {
return Write(make_pair(std::string("acc"), strAccount), account); return batch.Write(std::make_pair(std::string("acc"), strAccount), account);
} }
bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
{ {
return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); return batch.Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
} }
bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry) bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
@ -218,7 +218,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<
{ {
bool fAllAccounts = (strAccount == "*"); bool fAllAccounts = (strAccount == "*");
Dbc* pcursor = GetCursor(); Dbc* pcursor = batch.GetCursor();
if (!pcursor) if (!pcursor)
throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor"); throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor");
bool setRange = true; bool setRange = true;
@ -229,7 +229,7 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list<
if (setRange) if (setRange)
ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0))); ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0)));
CDataStream ssValue(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange); int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange);
setRange = false; setRange = false;
if (ret == DB_NOTFOUND) if (ret == DB_NOTFOUND)
break; break;
@ -560,7 +560,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
try { try {
int nMinVersion = 0; int nMinVersion = 0;
if (Read((std::string)"minversion", nMinVersion)) if (batch.Read((std::string)"minversion", nMinVersion))
{ {
if (nMinVersion > CLIENT_VERSION) if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW; return DB_TOO_NEW;
@ -568,7 +568,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
} }
// Get cursor // Get cursor
Dbc* pcursor = GetCursor(); Dbc* pcursor = batch.GetCursor();
if (!pcursor) if (!pcursor)
{ {
LogPrintf("Error getting wallet database cursor\n"); LogPrintf("Error getting wallet database cursor\n");
@ -580,7 +580,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
// Read next record // Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue); int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND) if (ret == DB_NOTFOUND)
break; break;
else if (ret != 0) else if (ret != 0)
@ -664,14 +664,14 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
try { try {
int nMinVersion = 0; int nMinVersion = 0;
if (Read((std::string)"minversion", nMinVersion)) if (batch.Read((std::string)"minversion", nMinVersion))
{ {
if (nMinVersion > CLIENT_VERSION) if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW; return DB_TOO_NEW;
} }
// Get cursor // Get cursor
Dbc* pcursor = GetCursor(); Dbc* pcursor = batch.GetCursor();
if (!pcursor) if (!pcursor)
{ {
LogPrintf("Error getting wallet database cursor\n"); LogPrintf("Error getting wallet database cursor\n");
@ -683,7 +683,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal
// Read next record // Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue); int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue);
if (ret == DB_NOTFOUND) if (ret == DB_NOTFOUND)
break; break;
else if (ret != 0) else if (ret != 0)
@ -797,10 +797,10 @@ void MaybeCompactWalletDB()
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
{ {
const std::string& strFile = pwalletMain->strWalletFile; if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) {
if (CDB::PeriodicFlush(strFile))
nLastFlushed = CWalletDB::GetUpdateCounter(); nLastFlushed = CWalletDB::GetUpdateCounter();
} }
}
fOneThread = false; fOneThread = false;
} }
@ -855,20 +855,20 @@ bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); return batch.Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
} }
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); return batch.Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
} }
bool CWalletDB::WriteHDChain(const CHDChain& chain) bool CWalletDB::WriteHDChain(const CHDChain& chain)
{ {
nWalletDBUpdateCounter++; nWalletDBUpdateCounter++;
return Write(std::string("hdchain"), chain); return batch.Write(std::string("hdchain"), chain);
} }
void CWalletDB::IncrementUpdateCounter() void CWalletDB::IncrementUpdateCounter()
@ -880,3 +880,28 @@ unsigned int CWalletDB::GetUpdateCounter()
{ {
return nWalletDBUpdateCounter; return nWalletDBUpdateCounter;
} }
bool CWalletDB::TxnBegin()
{
return batch.TxnBegin();
}
bool CWalletDB::TxnCommit()
{
return batch.TxnCommit();
}
bool CWalletDB::TxnAbort()
{
return batch.TxnAbort();
}
bool CWalletDB::ReadVersion(int& nVersion)
{
return batch.ReadVersion(nVersion);
}
bool CWalletDB::WriteVersion(int nVersion)
{
return batch.WriteVersion(nVersion);
}

View File

@ -17,6 +17,21 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
/**
* Overview of wallet database classes:
*
* - CDBEnv is an environment in which the database exists (has no analog in dbwrapper.h)
* - CWalletDBWrapper represents a wallet database (similar to CDBWrapper in dbwrapper.h)
* - CDB is a low-level database transaction (similar to CDBBatch in dbwrapper.h)
* - CWalletDB is a modifier object for the wallet, and encapsulates a database
* transaction as well as methods to act on the database (no analog in
* dbwrapper.h)
*
* The latter two are named confusingly, in contrast to what the names CDB
* and CWalletDB suggest they are transient transaction objects and don't
* represent the database itself.
*/
static const bool DEFAULT_FLUSHWALLET = true; static const bool DEFAULT_FLUSHWALLET = true;
class CAccount; class CAccount;
@ -118,11 +133,16 @@ public:
} }
}; };
/** Access to the wallet database */ /** Access to the wallet database.
class CWalletDB : public CDB * This should really be named CWalletDBBatch, as it represents a single transaction at the
* database. It will be committed when the object goes out of scope.
* Optionally (on by default) it will flush to disk as well.
*/
class CWalletDB
{ {
public: public:
CWalletDB(const std::string& strFilename, const char* pszMode = "r+", bool _fFlushOnClose = true) : CDB(strFilename, pszMode, _fFlushOnClose) CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) :
batch(dbw, pszMode, _fFlushOnClose)
{ {
} }
@ -194,7 +214,20 @@ public:
static void IncrementUpdateCounter(); static void IncrementUpdateCounter();
static unsigned int GetUpdateCounter(); static unsigned int GetUpdateCounter();
//! Begin a new transaction
bool TxnBegin();
//! Commit current transaction
bool TxnCommit();
//! Abort current transaction
bool TxnAbort();
//! Read wallet version
bool ReadVersion(int& nVersion);
//! Write wallet version
bool WriteVersion(int nVersion);
private: private:
CDB batch;
CWalletDB(const CWalletDB&); CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&); void operator=(const CWalletDB&);
}; };