Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wallet: Refactor BerkeleyDatabase to introduce DatabaseBatch abstract class #19325

Merged
merged 2 commits into from Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/wallet/bdb.cpp
Expand Up @@ -841,3 +841,8 @@ bool BerkeleyBatch::HasKey(CDataStream&& key)
int ret = pdb->exists(activeTxn, datKey, 0);
return ret == 0;
}

std::unique_ptr<BerkeleyBatch> BerkeleyDatabase::MakeBatch(const char* mode, bool flush_on_close)
{
return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
}
84 changes: 19 additions & 65 deletions src/wallet/bdb.h
Expand Up @@ -93,6 +93,8 @@ std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, s
/** Return wheter a BDB wallet database is currently loaded. */
bool IsBDBWalletLoaded(const fs::path& wallet_path);

class BerkeleyBatch;

/** An instance of this class represents one database.
* For BerkeleyDB this is just a (env, strFile) tuple.
**/
Expand Down Expand Up @@ -161,6 +163,9 @@ class BerkeleyDatabase
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
std::unique_ptr<Db> m_db;

/** Make a BerkeleyBatch connected to this database */
std::unique_ptr<BerkeleyBatch> MakeBatch(const char* mode, bool flush_on_close);
Copy link
Member

@promag promag Jun 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

de11059

I was expecting std::unique_ptr<DatabaseBatch> here - I suppose a future refactor will make BerkeleyDatabase extends WalletDatabase and this method will be virtual?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Member

@Sjors Sjors Jul 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably wouldn't hurt to do that in this PR. (neh, doing it while adding WalletDatabase in #19334 makes sense)


private:
std::string strFile;

Expand All @@ -172,7 +177,7 @@ class BerkeleyDatabase
};

/** RAII class that provides access to a Berkeley database */
class BerkeleyBatch
class BerkeleyBatch : public DatabaseBatch
{
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
Expand All @@ -195,10 +200,10 @@ class BerkeleyBatch
};

private:
bool ReadKey(CDataStream&& key, CDataStream& value);
bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true);
bool EraseKey(CDataStream&& key);
bool HasKey(CDataStream&& key);
bool ReadKey(CDataStream&& key, CDataStream& value) override;
bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
bool EraseKey(CDataStream&& key) override;
bool HasKey(CDataStream&& key) override;

protected:
Db* pdb;
Expand All @@ -211,71 +216,20 @@ class BerkeleyBatch

public:
explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~BerkeleyBatch() { Close(); }
~BerkeleyBatch() override { Close(); }

BerkeleyBatch(const BerkeleyBatch&) = delete;
BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;

void Flush();
void Close();

template <typename K, typename T>
bool Read(const K& key, T& value)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

CDataStream ssValue(SER_DISK, CLIENT_VERSION);
if (!ReadKey(std::move(ssKey), ssValue)) return false;
try {
ssValue >> value;
return true;
} catch (const std::exception&) {
return false;
}
}

template <typename K, typename T>
bool Write(const K& key, const T& value, bool fOverwrite = true)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(10000);
ssValue << value;

return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
}

template <typename K>
bool Erase(const K& key)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

return EraseKey(std::move(ssKey));
}

template <typename K>
bool Exists(const K& key)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

return HasKey(std::move(ssKey));
}
void Flush() override;
void Close() override;

bool StartCursor();
bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete);
void CloseCursor();
bool TxnBegin();
bool TxnCommit();
bool TxnAbort();
bool StartCursor() override;
bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
void CloseCursor() override;
bool TxnBegin() override;
bool TxnCommit() override;
bool TxnAbort() override;
};

std::string BerkeleyDatabaseVersion();
Expand Down
80 changes: 80 additions & 0 deletions src/wallet/db.h
Expand Up @@ -6,12 +6,92 @@
#ifndef BITCOIN_WALLET_DB_H
#define BITCOIN_WALLET_DB_H

#include <clientversion.h>
#include <fs.h>
#include <streams.h>

#include <string>

/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
fs::path WalletDataFilePath(const fs::path& wallet_path);
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);

/** RAII class that provides access to a WalletDatabase */
class DatabaseBatch
{
private:
virtual bool ReadKey(CDataStream&& key, CDataStream& value) = 0;
virtual bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) = 0;
virtual bool EraseKey(CDataStream&& key) = 0;
virtual bool HasKey(CDataStream&& key) = 0;

public:
explicit DatabaseBatch() {}
virtual ~DatabaseBatch() {}

DatabaseBatch(const DatabaseBatch&) = delete;
DatabaseBatch& operator=(const DatabaseBatch&) = delete;

virtual void Flush() = 0;
virtual void Close() = 0;

template <typename K, typename T>
bool Read(const K& key, T& value)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

CDataStream ssValue(SER_DISK, CLIENT_VERSION);
if (!ReadKey(std::move(ssKey), ssValue)) return false;
try {
ssValue >> value;
return true;
} catch (const std::exception&) {
return false;
}
}

template <typename K, typename T>
bool Write(const K& key, const T& value, bool fOverwrite = true)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(10000);
ssValue << value;

return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
}

template <typename K>
bool Erase(const K& key)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

return EraseKey(std::move(ssKey));
}

template <typename K>
bool Exists(const K& key)
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;

return HasKey(std::move(ssKey));
}

virtual bool StartCursor() = 0;
virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0;
virtual void CloseCursor() = 0;
virtual bool TxnBegin() = 0;
virtual bool TxnCommit() = 0;
virtual bool TxnAbort() = 0;
};

#endif // BITCOIN_WALLET_DB_H
38 changes: 19 additions & 19 deletions src/wallet/walletdb.cpp
Expand Up @@ -121,7 +121,7 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
// It may already exist, so try writing just the checksum
std::vector<unsigned char> val;
if (!m_batch.Read(key, val)) {
if (!m_batch->Read(key, val)) {
return false;
}
if (!WriteIC(key, std::make_pair(val, checksum), true)) {
Expand Down Expand Up @@ -166,8 +166,8 @@ bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)

bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
{
if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
}

bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
Expand All @@ -177,7 +177,7 @@ bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)

bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
{
return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool);
return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
}

bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
Expand Down Expand Up @@ -693,14 +693,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}

// Get cursor
if (!m_batch.StartCursor())
if (!m_batch->StartCursor())
{
pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
Expand All @@ -712,13 +712,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
bool ret = m_batch.ReadAtCursor(ssKey, ssValue, complete);
bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
break;
}
else if (!ret)
{
m_batch.CloseCursor();
m_batch->CloseCursor();
pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
Expand Down Expand Up @@ -748,7 +748,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
} catch (...) {
result = DBErrors::CORRUPT;
}
m_batch.CloseCursor();
m_batch->CloseCursor();

// Set the active ScriptPubKeyMans
for (auto spk_man_pair : wss.m_active_external_spks) {
Expand Down Expand Up @@ -785,7 +785,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)

// Last client version to open this wallet, was previously the file version number
int last_client = CLIENT_VERSION;
m_batch.Read(DBKeys::VERSION, last_client);
m_batch->Read(DBKeys::VERSION, last_client);

int wallet_version = pwallet->GetVersion();
pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client);
Expand All @@ -810,7 +810,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
return DBErrors::NEED_REWRITE;

if (last_client < CLIENT_VERSION) // Update
m_batch.Write(DBKeys::VERSION, CLIENT_VERSION);
m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);

if (wss.fAnyUnordered)
result = pwallet->ReorderTransactions();
Expand Down Expand Up @@ -846,13 +846,13 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal

try {
int nMinVersion = 0;
if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
}

// Get cursor
if (!m_batch.StartCursor())
if (!m_batch->StartCursor())
{
LogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
Expand All @@ -864,11 +864,11 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
bool ret = m_batch.ReadAtCursor(ssKey, ssValue, complete);
bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
break;
} else if (!ret) {
m_batch.CloseCursor();
m_batch->CloseCursor();
LogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
Expand All @@ -886,7 +886,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
} catch (...) {
result = DBErrors::CORRUPT;
}
m_batch.CloseCursor();
m_batch->CloseCursor();

return result;
}
Expand Down Expand Up @@ -999,17 +999,17 @@ bool WalletBatch::WriteWalletFlags(const uint64_t flags)

bool WalletBatch::TxnBegin()
{
return m_batch.TxnBegin();
return m_batch->TxnBegin();
}

bool WalletBatch::TxnCommit()
{
return m_batch.TxnCommit();
return m_batch->TxnCommit();
}

bool WalletBatch::TxnAbort()
{
return m_batch.TxnAbort();
return m_batch->TxnAbort();
}

bool IsWalletLoaded(const fs::path& wallet_path)
Expand Down