Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,34 +574,43 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
return true;
}

void BlockManager::FlushUndoFile(int block_file, bool finalize)
bool BlockManager::FlushUndoFile(int block_file, bool finalize)
{
FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize);
if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
return false;
}
return true;
}

void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
bool BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
{
bool success = true;
LOCK(cs_LastBlockFile);

if (m_blockfile_info.size() < 1) {
// Return if we haven't loaded any blockfiles yet. This happens during
// chainstate init, when we call ChainstateManager::MaybeRebalanceCaches() (which
// then calls FlushStateToDisk()), resulting in a call to this function before we
// have populated `m_blockfile_info` via LoadBlockIndexDB().
return;
return true;
}
assert(static_cast<int>(m_blockfile_info.size()) > m_last_blockfile);

FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize);
if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
success = false;
}
// we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
// e.g. during IBD or a sync after a node going offline
if (!fFinalize || finalize_undo) FlushUndoFile(m_last_blockfile, finalize_undo);
if (!fFinalize || finalize_undo) {
if (!FlushUndoFile(m_last_blockfile, finalize_undo)) {
success = false;
}
}
return success;
}

uint64_t BlockManager::CalculateCurrentUsage()
Expand Down Expand Up @@ -696,7 +705,19 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne
if (!fKnown) {
LogPrint(BCLog::BLOCKSTORAGE, "Leaving block file %i: %s\n", m_last_blockfile, m_blockfile_info[m_last_blockfile].ToString());
}
FlushBlockFile(!fKnown, finalize_undo);

// Do not propagate the return code. The flush concerns a previous block
// and undo file that has already been written to. If a flush fails
// here, and we crash, there is no expected additional block data
// inconsistency arising from the flush failure here. However, the undo
// data may be inconsistent after a crash if the flush is called during
// a reindex. A flush error might also leave some of the data files
// untrimmed.
if (!FlushBlockFile(!fKnown, finalize_undo)) {
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning, /* Continued */
"Failed to flush previous block file %05i (finalize=%i, finalize_undo=%i) before opening new block file %05i\n",
m_last_blockfile, !fKnown, finalize_undo, nFile);
}
m_last_blockfile = nFile;
m_undo_height_in_last_blockfile = 0; // No undo data yet in the new file, so reset our undo-height tracking.
}
Expand Down Expand Up @@ -789,7 +810,14 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid
// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
// the FindBlockPos function
if (_pos.nFile < m_last_blockfile && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) {
FlushUndoFile(_pos.nFile, true);
// Do not propagate the return code, a failed flush here should not
// be an indication for a failed write. If it were propagated here,
// the caller would assume the undo data not to be written, when in
// fact it is. Note though, that a failed flush might leave the data
// file untrimmed.
if (!FlushUndoFile(_pos.nFile, true)) {
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning, "Failed to flush undo file %05i\n", _pos.nFile);
}
} else if (_pos.nFile == m_last_blockfile && static_cast<uint32_t>(block.nHeight) > m_undo_height_in_last_blockfile) {
m_undo_height_in_last_blockfile = block.nHeight;
}
Expand Down
11 changes: 8 additions & 3 deletions src/node/blockstorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,14 @@ class BlockManager
*/
bool LoadBlockIndex()
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
void FlushUndoFile(int block_file, bool finalize = false);
bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown);

/** Return false if block file or undo file flushing fails. */
[[nodiscard]] bool FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);

/** Return false if undo file flushing fails. */
[[nodiscard]] bool FlushUndoFile(int block_file, bool finalize = false);

[[nodiscard]] bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown);
bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);

/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
Expand Down
6 changes: 5 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3020,7 +3020,11 @@ bool Chainstate::FlushStateToDisk(
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCHMARK);

// First make sure all block and undo data is flushed to disk.
m_blockman.FlushBlockFile();
// TODO: Handle return error, or add detailed comment why it is
// safe to not return an error upon failure.
if (!m_blockman.FlushBlockFile()) {
LogPrintLevel(BCLog::VALIDATION, BCLog::Level::Warning, "%s: Failed to flush block file.\n", __func__);
}
}

// Then update all block file information (which may refer to block and undo files).
Expand Down
62 changes: 51 additions & 11 deletions src/wallet/bdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <stdint.h>

#include <db_cxx.h>
#include <sys/stat.h>

// Windows may not define S_IRUSR or S_IWUSR. We define both
Expand All @@ -25,12 +26,10 @@
#endif
#endif

static_assert(BDB_DB_FILE_ID_LEN == DB_FILE_ID_LEN, "DB_FILE_ID_LEN should be 20.");

namespace wallet {
namespace {
Span<const std::byte> SpanFromDbt(const BerkeleyBatch::SafeDbt& dbt)
{
return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()};
}

//! Make sure database has a unique fileid within the environment. If it
//! doesn't, throw an error. BDB caches do not work properly when more than one
Expand Down Expand Up @@ -232,17 +231,37 @@ BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
fMockDb = true;
}

BerkeleyBatch::SafeDbt::SafeDbt()
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
{
Dbt m_dbt;

public:
// construct Dbt with internally-managed data
SafeDbt();
// construct Dbt with provided data
SafeDbt(void* data, size_t size);
~SafeDbt();

// delegate to Dbt
const void* get_data() const;
uint32_t get_size() const;

// conversion operator to access the underlying Dbt
operator Dbt*();
};

SafeDbt::SafeDbt()
{
m_dbt.set_flags(DB_DBT_MALLOC);
}

BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
SafeDbt::SafeDbt(void* data, size_t size)
: m_dbt(data, size)
{
}

BerkeleyBatch::SafeDbt::~SafeDbt()
SafeDbt::~SafeDbt()
{
if (m_dbt.get_data() != nullptr) {
// Clear memory, e.g. in case it was a private key
Expand All @@ -256,21 +275,33 @@ BerkeleyBatch::SafeDbt::~SafeDbt()
}
}

const void* BerkeleyBatch::SafeDbt::get_data() const
const void* SafeDbt::get_data() const
{
return m_dbt.get_data();
}

uint32_t BerkeleyBatch::SafeDbt::get_size() const
uint32_t SafeDbt::get_size() const
{
return m_dbt.get_size();
}

BerkeleyBatch::SafeDbt::operator Dbt*()
SafeDbt::operator Dbt*()
{
return &m_dbt;
}

static Span<const std::byte> SpanFromDbt(const SafeDbt& dbt)
{
return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()};
}

BerkeleyDatabase::BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
WalletDatabase(), env(std::move(env)), m_filename(std::move(filename)), m_max_log_mb(options.max_log_mb)
{
auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
assert(inserted.second);
}

bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
{
fs::path walletDir = env->Directory();
Expand Down Expand Up @@ -459,6 +490,15 @@ void BerkeleyEnvironment::ReloadDbEnv()
Open(open_err);
}

DbTxn* BerkeleyEnvironment::TxnBegin(int flags)
{
DbTxn* ptxn = nullptr;
int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
if (!ptxn || ret != 0)
return nullptr;
return ptxn;
}

bool BerkeleyDatabase::Rewrite(const char* pszSkip)
{
while (true) {
Expand Down Expand Up @@ -715,7 +755,7 @@ bool BerkeleyBatch::TxnBegin()
{
if (!pdb || activeTxn)
return false;
DbTxn* ptxn = env->TxnBegin();
DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
if (!ptxn)
return false;
activeTxn = ptxn;
Expand Down
48 changes: 11 additions & 37 deletions src/wallet/bdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@

struct bilingual_str;

#include <db_cxx.h>
class DbEnv;
class DbTxn;
class Db;
class Dbc;

// This constant was introduced in BDB 4.0.14 and has never changed, but there
// is a belt-and-suspenders check in the cpp file just in case.
#define BDB_DB_FILE_ID_LEN 20 /* Unique file ID length. */

namespace wallet {
struct WalletDatabaseFileId {
uint8_t value[DB_FILE_ID_LEN];
uint8_t value[BDB_DB_FILE_ID_LEN];
bool operator==(const WalletDatabaseFileId& rhs) const;
};

Expand Down Expand Up @@ -64,14 +71,7 @@ class BerkeleyEnvironment
void CloseDb(const fs::path& filename);
void ReloadDbEnv();

DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
{
DbTxn* ptxn = nullptr;
int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
if (!ptxn || ret != 0)
return nullptr;
return ptxn;
}
DbTxn* TxnBegin(int flags);
};

/** Get BerkeleyEnvironment given a directory path. */
Expand All @@ -88,12 +88,7 @@ class BerkeleyDatabase : public WalletDatabase
BerkeleyDatabase() = delete;

/** Create DB handle to real database */
BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
WalletDatabase(), env(std::move(env)), m_filename(std::move(filename)), m_max_log_mb(options.max_log_mb)
{
auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
assert(inserted.second);
}
BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options);

~BerkeleyDatabase() override;

Expand Down Expand Up @@ -161,27 +156,6 @@ class BerkeleyDatabase : public WalletDatabase
/** RAII class that provides access to a Berkeley database */
class BerkeleyBatch : public DatabaseBatch
{
public:
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
{
Dbt m_dbt;

public:
// construct Dbt with internally-managed data
SafeDbt();
// construct Dbt with provided data
SafeDbt(void* data, size_t size);
~SafeDbt();

// delegate to Dbt
const void* get_data() const;
uint32_t get_size() const;

// conversion operator to access the underlying Dbt
operator Dbt*();
};

private:
bool ReadKey(CDataStream&& key, CDataStream& value) override;
bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
Expand Down
4 changes: 3 additions & 1 deletion src/wallet/salvage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <wallet/wallet.h>
#include <wallet/walletdb.h>

#include <db_cxx.h>

namespace wallet {
/* End of headers, beginning of key/value data */
static const char *HEADER_END = "HEADER=END";
Expand Down Expand Up @@ -134,7 +136,7 @@ bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bil
return false;
}

DbTxn* ptxn = env->TxnBegin();
DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
CWallet dummyWallet(/*chain=*/nullptr, /*coinjoin_loader=*/nullptr, "", gArgs, CreateDummyWalletDatabase());
dummyWallet.SetupLegacyScriptPubKeyMan();
for (KeyValPair& row : salvagedData)
Expand Down
10 changes: 4 additions & 6 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4803,7 +4803,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
return false;
}
} else {
// Labels for everything else (send) should be cloned to all
// Labels for everything else ("send") should be cloned to all
if (data.watchonly_wallet) {
LOCK(data.watchonly_wallet->cs_wallet);
// Add to the watchonly. Preserve the labels, purpose, and change-ness
Expand All @@ -4815,7 +4815,6 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
if (!addr_pair.second.IsChange()) {
data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
}
continue;
}
if (data.solvable_wallet) {
LOCK(data.solvable_wallet->cs_wallet);
Expand All @@ -4828,7 +4827,6 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
if (!addr_pair.second.IsChange()) {
data.solvable_wallet->m_address_book[addr_pair.first].SetLabel(label);
}
continue;
}
}
}
Expand All @@ -4840,10 +4838,10 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
for (const auto& [destination, addr_book_data] : wallet.m_address_book) {
auto address{EncodeDestination(destination)};
auto purpose{addr_book_data.purpose};
auto label{addr_book_data.GetLabel()};
// don't bother writing default values (unknown purpose, empty label)
std::optional<std::string> label = addr_book_data.IsChange() ? std::nullopt : std::make_optional(addr_book_data.GetLabel());
// don't bother writing default values (unknown purpose)
if (purpose != "unknown") batch.WritePurpose(address, purpose);
if (!label.empty()) batch.WriteName(address, label);
if (label) batch.WriteName(address, *label);
}
};
if (data.watchonly_wallet) persist_address_book(*data.watchonly_wallet);
Expand Down
Loading
Loading