2018-01-15 15:26:12 +00:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2021-06-20 22:44:44 +02:00
|
|
|
* Copyright (C) 2017-2018 Tom Zander <tom@flowee.org>
|
2018-01-15 15:26:12 +00:00
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "VerifyDB.h"
|
2021-11-02 10:23:33 +01:00
|
|
|
#include <primitives/Block.h>
|
2018-07-23 18:05:43 +02:00
|
|
|
#include <utxo/UnspentOutputDatabase.h>
|
2018-01-15 15:26:12 +00:00
|
|
|
#include <Application.h>
|
2018-02-10 22:48:07 +01:00
|
|
|
#include <UiInterface.h>
|
2018-01-15 15:26:12 +00:00
|
|
|
#include <main.h>
|
|
|
|
|
#include <util.h>
|
|
|
|
|
#include <chain.h>
|
|
|
|
|
#include <init.h>
|
|
|
|
|
#include <txmempool.h>
|
2018-07-23 18:05:43 +02:00
|
|
|
#include <server/BlocksDB.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UnspentOutputDatabaseRam
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-15 15:26:12 +00:00
|
|
|
|
|
|
|
|
VerifyDB::VerifyDB()
|
|
|
|
|
: validator(Validation::SkipAutoBlockProcessing)
|
|
|
|
|
{
|
|
|
|
|
uiInterface.ShowProgress(_("Verifying blocks..."), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyDB::~VerifyDB()
|
|
|
|
|
{
|
|
|
|
|
uiInterface.ShowProgress("", 100);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-23 18:05:43 +02:00
|
|
|
bool VerifyDB::verifyDB(int nCheckLevel, int nCheckDepth)
|
2018-01-15 15:26:12 +00:00
|
|
|
{
|
|
|
|
|
auto global = Application::instance()->validation();
|
|
|
|
|
const CChain *globalChain = global->blockchain();
|
|
|
|
|
assert(globalChain);
|
|
|
|
|
if (!globalChain->Tip() || globalChain->Height() < 5)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
CChain blockchain(*globalChain);
|
|
|
|
|
validator.setBlockchain(&blockchain);
|
2018-07-23 18:05:43 +02:00
|
|
|
std::unique_ptr<UnspentOutputDatabase> utxo(UnspentOutputDatabase::createMemOnlyDB(GetDataDir() / "unspent"));
|
2018-01-15 15:26:12 +00:00
|
|
|
CTxMemPool pool;
|
2018-07-23 18:05:43 +02:00
|
|
|
pool.setUtxo(utxo.get());
|
2018-01-15 15:26:12 +00:00
|
|
|
validator.setMempool(&pool);
|
|
|
|
|
|
|
|
|
|
if (nCheckDepth <= 0)
|
|
|
|
|
nCheckDepth = 1000000000; // suffices until the year 19000
|
|
|
|
|
if (nCheckDepth > blockchain.Height())
|
|
|
|
|
nCheckDepth = blockchain.Height();
|
|
|
|
|
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
|
|
|
|
|
logCritical(Log::Bitcoin) << "Verifying last" << nCheckDepth << "blocks at level" << nCheckLevel;
|
|
|
|
|
|
|
|
|
|
CBlockIndex* pindexState = blockchain.Tip();
|
|
|
|
|
CBlockIndex* pindexFailure = nullptr;
|
|
|
|
|
int nGoodTransactions = 0;
|
|
|
|
|
for (CBlockIndex* pindex = blockchain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
|
|
|
|
|
boost::this_thread::interruption_point();
|
|
|
|
|
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99,
|
|
|
|
|
(int)(((double)(globalChain->Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))));
|
|
|
|
|
|
|
|
|
|
if (pindex->nHeight < globalChain->Height()-nCheckDepth)
|
|
|
|
|
break;
|
|
|
|
|
auto future = validator.addBlock(pindex->GetBlockPos(), 0);
|
|
|
|
|
future.setOnlyCheckValidity(true);
|
|
|
|
|
future.setCheckTransactionValidity(nCheckLevel >= 1);
|
|
|
|
|
future.start();
|
|
|
|
|
future.waitUntilFinished();
|
|
|
|
|
if (!future.error().empty()) {
|
|
|
|
|
logFatal(Log::Bitcoin) << "VerifyDB failed validate block at height" << pindex->nHeight << "Error is" << future.error();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
|
|
|
|
|
if (nCheckLevel >= 3 && pindex == pindexState/* && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage*/) {
|
|
|
|
|
bool fClean = true;
|
|
|
|
|
auto fastBlock = Blocks::DB::instance()->loadBlock(pindex->GetBlockPos());
|
|
|
|
|
try {
|
|
|
|
|
fastBlock.findTransactions();
|
2020-11-18 22:08:00 +01:00
|
|
|
if (!validator.disconnectTip(pindex, &fClean))
|
2018-01-15 15:26:12 +00:00
|
|
|
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
|
|
|
|
} catch (const std::runtime_error &e) {
|
|
|
|
|
logDebug() << e;
|
|
|
|
|
fClean = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pindexState = pindex->pprev;
|
|
|
|
|
blockchain.SetTip(pindexState);
|
|
|
|
|
if (!fClean) {
|
|
|
|
|
nGoodTransactions = 0;
|
|
|
|
|
pindexFailure = pindex;
|
|
|
|
|
} else {
|
|
|
|
|
nGoodTransactions += fastBlock.transactions().size();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ShutdownRequested())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (pindexFailure)
|
|
|
|
|
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n",
|
|
|
|
|
blockchain.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check level 4: try reconnecting blocks
|
|
|
|
|
if (nCheckLevel >= 4) {
|
|
|
|
|
CBlockIndex *pindex = pindexState;
|
|
|
|
|
while (pindex != globalChain->Tip()) {
|
|
|
|
|
boost::this_thread::interruption_point();
|
|
|
|
|
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))));
|
|
|
|
|
pindex = globalChain->Next(pindex);
|
|
|
|
|
|
|
|
|
|
auto future = validator.addBlock(pindex->GetBlockPos(), 0).start();
|
|
|
|
|
future.waitUntilFinished();
|
|
|
|
|
if (!future.error().empty()) {
|
|
|
|
|
logFatal(Log::Bitcoin) << "VerifyDB failed re-attach block at height" << pindex->nHeight << "Error is" << future.error();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logCritical(Log::Bitcoin).nospace() << "No coin database inconsistencies in last " << chainActive.Height() - pindexState->nHeight
|
|
|
|
|
<< " blocks (" << nGoodTransactions << " transactions)";
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|