Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/db.h
Show First 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | private: | ||||
* Only to be used at a low level, application should ideally not care | * Only to be used at a low level, application should ideally not care | ||||
* about this. | * about this. | ||||
*/ | */ | ||||
bool IsDummy() { return env == nullptr; } | bool IsDummy() { return env == nullptr; } | ||||
}; | }; | ||||
/** RAII class that provides access to a Berkeley database */ | /** RAII class that provides access to a Berkeley database */ | ||||
class BerkeleyBatch { | class BerkeleyBatch { | ||||
/** RAII class that automatically cleanses its data on destruction */ | |||||
class SafeDbt final { | |||||
Dbt m_dbt; | |||||
public: | |||||
// construct Dbt with data or flags | |||||
SafeDbt(u_int32_t flags = 0); | |||||
SafeDbt(void *data, size_t size); | |||||
~SafeDbt(); | |||||
// delegate to Dbt | |||||
const void *get_data() const; | |||||
u_int32_t get_size() const; | |||||
// conversion operator to access the underlying Dbt | |||||
operator Dbt *(); | |||||
}; | |||||
protected: | protected: | ||||
Db *pdb; | Db *pdb; | ||||
std::string strFile; | std::string strFile; | ||||
DbTxn *activeTxn; | DbTxn *activeTxn; | ||||
bool fReadOnly; | bool fReadOnly; | ||||
bool fFlushOnClose; | bool fFlushOnClose; | ||||
BerkeleyEnvironment *env; | BerkeleyEnvironment *env; | ||||
Show All 22 Lines | static bool VerifyEnvironment(const fs::path &file_path, | ||||
std::string &errorStr); | std::string &errorStr); | ||||
/* verifies the database file */ | /* verifies the database file */ | ||||
static bool | static bool | ||||
VerifyDatabaseFile(const fs::path &file_path, | VerifyDatabaseFile(const fs::path &file_path, | ||||
std::vector<std::string> &warnings, | std::vector<std::string> &warnings, | ||||
std::string &errorStr, | std::string &errorStr, | ||||
BerkeleyEnvironment::recoverFunc_type recoverFunc); | BerkeleyEnvironment::recoverFunc_type recoverFunc); | ||||
public: | |||||
template <typename K, typename T> bool Read(const K &key, T &value) { | template <typename K, typename T> bool Read(const K &key, T &value) { | ||||
if (!pdb) { | if (!pdb) { | ||||
return false; | return false; | ||||
} | } | ||||
// Key | // Key | ||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | ||||
ssKey.reserve(1000); | ssKey.reserve(1000); | ||||
ssKey << key; | ssKey << key; | ||||
Dbt datKey(ssKey.data(), ssKey.size()); | SafeDbt datKey(ssKey.data(), ssKey.size()); | ||||
// Read | // Read | ||||
Dbt datValue; | SafeDbt datValue(DB_DBT_MALLOC); | ||||
datValue.set_flags(DB_DBT_MALLOC); | int ret = pdb->get(activeTxn, datKey, datValue, 0); | ||||
int ret = pdb->get(activeTxn, &datKey, &datValue, 0); | |||||
memory_cleanse(datKey.get_data(), datKey.get_size()); | |||||
bool success = false; | bool success = false; | ||||
if (datValue.get_data() != nullptr) { | if (datValue.get_data() != nullptr) { | ||||
// Unserialize value | // Unserialize value | ||||
try { | try { | ||||
CDataStream ssValue((char *)datValue.get_data(), | CDataStream ssValue((char *)datValue.get_data(), | ||||
(char *)datValue.get_data() + | (char *)datValue.get_data() + | ||||
datValue.get_size(), | datValue.get_size(), | ||||
SER_DISK, CLIENT_VERSION); | SER_DISK, CLIENT_VERSION); | ||||
ssValue >> value; | ssValue >> value; | ||||
success = true; | success = true; | ||||
} catch (const std::exception &) { | } catch (const std::exception &) { | ||||
// In this case success remains 'false' | // In this case success remains 'false' | ||||
} | } | ||||
// Clear and free memory | |||||
memory_cleanse(datValue.get_data(), datValue.get_size()); | |||||
free(datValue.get_data()); | |||||
} | } | ||||
return ret == 0 && success; | return ret == 0 && success; | ||||
} | } | ||||
template <typename K, typename T> | template <typename K, typename T> | ||||
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 true; | return true; | ||||
} | } | ||||
if (fReadOnly) { | if (fReadOnly) { | ||||
assert(!"Write called on database in read-only mode"); | assert(!"Write called on database in read-only mode"); | ||||
} | } | ||||
// Key | // Key | ||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | ||||
ssKey.reserve(1000); | ssKey.reserve(1000); | ||||
ssKey << key; | ssKey << key; | ||||
Dbt datKey(ssKey.data(), ssKey.size()); | SafeDbt datKey(ssKey.data(), ssKey.size()); | ||||
// Value | // Value | ||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION); | CDataStream ssValue(SER_DISK, CLIENT_VERSION); | ||||
ssValue.reserve(10000); | ssValue.reserve(10000); | ||||
ssValue << value; | ssValue << value; | ||||
Dbt datValue(ssValue.data(), ssValue.size()); | SafeDbt datValue(ssValue.data(), ssValue.size()); | ||||
// Write | // Write | ||||
int ret = pdb->put(activeTxn, &datKey, &datValue, | int ret = pdb->put(activeTxn, datKey, datValue, | ||||
(fOverwrite ? 0 : DB_NOOVERWRITE)); | (fOverwrite ? 0 : DB_NOOVERWRITE)); | ||||
// Clear memory in case it was a private key | |||||
memory_cleanse(datKey.get_data(), datKey.get_size()); | |||||
memory_cleanse(datValue.get_data(), datValue.get_size()); | |||||
return (ret == 0); | return (ret == 0); | ||||
} | } | ||||
template <typename K> bool Erase(const K &key) { | template <typename K> bool Erase(const K &key) { | ||||
if (!pdb) { | if (!pdb) { | ||||
return false; | return false; | ||||
} | } | ||||
if (fReadOnly) { | if (fReadOnly) { | ||||
assert(!"Erase called on database in read-only mode"); | assert(!"Erase called on database in read-only mode"); | ||||
} | } | ||||
// Key | // Key | ||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | ||||
ssKey.reserve(1000); | ssKey.reserve(1000); | ||||
ssKey << key; | ssKey << key; | ||||
Dbt datKey(ssKey.data(), ssKey.size()); | SafeDbt datKey(ssKey.data(), ssKey.size()); | ||||
// Erase | // Erase | ||||
int ret = pdb->del(activeTxn, &datKey, 0); | int ret = pdb->del(activeTxn, datKey, 0); | ||||
// Clear memory | |||||
memory_cleanse(datKey.get_data(), datKey.get_size()); | |||||
return (ret == 0 || ret == DB_NOTFOUND); | return (ret == 0 || ret == DB_NOTFOUND); | ||||
} | } | ||||
template <typename K> bool Exists(const K &key) { | template <typename K> bool Exists(const K &key) { | ||||
if (!pdb) { | if (!pdb) { | ||||
return false; | return false; | ||||
} | } | ||||
// Key | // Key | ||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | ||||
ssKey.reserve(1000); | ssKey.reserve(1000); | ||||
ssKey << key; | ssKey << key; | ||||
Dbt datKey(ssKey.data(), ssKey.size()); | SafeDbt datKey(ssKey.data(), ssKey.size()); | ||||
// Exists | // Exists | ||||
int ret = pdb->exists(activeTxn, &datKey, 0); | int ret = pdb->exists(activeTxn, datKey, 0); | ||||
// Clear memory | |||||
memory_cleanse(datKey.get_data(), datKey.get_size()); | |||||
return (ret == 0); | return (ret == 0); | ||||
} | } | ||||
Dbc *GetCursor() { | Dbc *GetCursor() { | ||||
if (!pdb) { | if (!pdb) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
Dbc *pcursor = nullptr; | Dbc *pcursor = nullptr; | ||||
int ret = pdb->cursor(nullptr, &pcursor, 0); | int ret = pdb->cursor(nullptr, &pcursor, 0); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return pcursor; | return pcursor; | ||||
} | } | ||||
int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue) { | int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue) { | ||||
// Read at cursor | // Read at cursor | ||||
Dbt datKey; | SafeDbt datKey(DB_DBT_MALLOC); | ||||
Dbt datValue; | SafeDbt datValue(DB_DBT_MALLOC); | ||||
datKey.set_flags(DB_DBT_MALLOC); | int ret = pcursor->get(datKey, datValue, DB_NEXT); | ||||
datValue.set_flags(DB_DBT_MALLOC); | |||||
int ret = pcursor->get(&datKey, &datValue, DB_NEXT); | |||||
if (ret != 0) { | if (ret != 0) { | ||||
return ret; | return ret; | ||||
} else if (datKey.get_data() == nullptr || | } else if (datKey.get_data() == nullptr || | ||||
datValue.get_data() == nullptr) { | datValue.get_data() == nullptr) { | ||||
return 99999; | return 99999; | ||||
} | } | ||||
// Convert to streams | // Convert to streams | ||||
ssKey.SetType(SER_DISK); | ssKey.SetType(SER_DISK); | ||||
ssKey.clear(); | ssKey.clear(); | ||||
ssKey.write((char *)datKey.get_data(), datKey.get_size()); | ssKey.write((char *)datKey.get_data(), datKey.get_size()); | ||||
ssValue.SetType(SER_DISK); | ssValue.SetType(SER_DISK); | ||||
ssValue.clear(); | ssValue.clear(); | ||||
ssValue.write((char *)datValue.get_data(), datValue.get_size()); | ssValue.write((char *)datValue.get_data(), datValue.get_size()); | ||||
// Clear and free memory | |||||
memory_cleanse(datKey.get_data(), datKey.get_size()); | |||||
memory_cleanse(datValue.get_data(), datValue.get_size()); | |||||
free(datKey.get_data()); | |||||
free(datValue.get_data()); | |||||
return 0; | return 0; | ||||
} | } | ||||
public: | |||||
bool TxnBegin() { | bool TxnBegin() { | ||||
if (!pdb || activeTxn) { | if (!pdb || activeTxn) { | ||||
return false; | return false; | ||||
} | } | ||||
DbTxn *ptxn = env->TxnBegin(); | DbTxn *ptxn = env->TxnBegin(); | ||||
if (!ptxn) { | if (!ptxn) { | ||||
return false; | return false; | ||||
} | } | ||||
Show All 27 Lines |