Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <coins.h>

#include <consensus/consensus.h>
#include <logging.h>
#include <random.h>
#include <version.h>

Expand Down Expand Up @@ -258,3 +259,19 @@ const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
}
return coinEmpty;
}

bool CCoinsViewErrorCatcher::GetCoin(const COutPoint &outpoint, Coin &coin) const {
try {
return CCoinsViewBacked::GetCoin(outpoint, coin);
} catch(const std::runtime_error& e) {
for (auto f : m_err_callbacks) {
f();
}
LogPrintf("Error reading from database: %s\n", e.what());
// Starting the shutdown sequence and returning false to the caller would be
// interpreted as 'entry not found' (as opposed to unable to read data), and
// could lead to invalid interpretation. Just exit immediately, as we can't
// continue anyway, and all writes should be atomic.
std::abort();
}
}
25 changes: 25 additions & 0 deletions src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <assert.h>
#include <stdint.h>

#include <functional>
#include <unordered_map>

/**
Expand Down Expand Up @@ -315,4 +316,28 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool
//! lookups to database, so it should be used with care.
const Coin& AccessByTxid(const CCoinsViewCache& cache, const uint256& txid);

/**
* This is a minimally invasive approach to shutdown on LevelDB read errors from the
* chainstate, while keeping user interface out of the common library, which is shared
* between bitcoind, and bitcoin-qt and non-server tools.
*
* Writes do not need similar protection, as failure to write is handled by the caller.
*/
class CCoinsViewErrorCatcher final : public CCoinsViewBacked
{
public:
explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}

void AddReadErrCallback(std::function<void()> f) {
m_err_callbacks.emplace_back(std::move(f));
}

bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;

private:
/** A list of callbacks to execute upon leveldb read error. */
std::vector<std::function<void()>> m_err_callbacks;

};

#endif // BITCOIN_COINS_H
31 changes: 6 additions & 25 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
Expand Down Expand Up @@ -146,31 +147,6 @@ NODISCARD static bool CreatePidFile()
// shutdown thing.
//

/**
* This is a minimally invasive approach to shutdown on LevelDB read errors from the
* chainstate, while keeping user interface out of the common library, which is shared
* between bitcoind, and bitcoin-qt and non-server tools.
*/
class CCoinsViewErrorCatcher final : public CCoinsViewBacked
{
public:
explicit CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override {
try {
return CCoinsViewBacked::GetCoin(outpoint, coin);
} catch(const std::runtime_error& e) {
uiInterface.ThreadSafeMessageBox(_("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR);
LogPrintf("Error reading from database: %s\n", e.what());
// Starting the shutdown sequence and returning false to the caller would be
// interpreted as 'entry not found' (as opposed to unable to read data), and
// could lead to invalid interpretation. Just exit immediately, as we can't
// continue anyway, and all writes should be atomic.
abort();
}
}
// Writes do not need similar protection, as failure to write is handled by the caller.
};

static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher;
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;

Expand Down Expand Up @@ -1542,6 +1518,11 @@ bool AppInitMain(InitInterfaces& interfaces)

pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState));
pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get()));
pcoinscatcher->AddReadErrCallback([]() {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down."),
"", CClientUIInterface::MSG_ERROR);
});

// If necessary, upgrade from older database format.
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
Expand Down