2021-04-02 14:17:00 -03:00
|
|
|
// Copyright (c) 2011-2021 The Bitcoin Core developers
|
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2021-04-02 15:42:05 -03:00
|
|
|
#include <node/blockstorage.h>
|
2021-04-02 14:17:00 -03:00
|
|
|
|
2021-04-02 15:42:05 -03:00
|
|
|
#include <chain.h>
|
2021-04-02 14:17:00 -03:00
|
|
|
#include <chainparams.h>
|
2021-04-02 15:42:05 -03:00
|
|
|
#include <flatfile.h>
|
2021-04-02 14:17:00 -03:00
|
|
|
#include <fs.h>
|
2021-04-02 15:42:05 -03:00
|
|
|
#include <pow.h>
|
2021-04-02 14:17:00 -03:00
|
|
|
#include <shutdown.h>
|
2021-04-02 15:42:05 -03:00
|
|
|
#include <signet.h>
|
|
|
|
#include <streams.h>
|
2021-04-02 14:17:00 -03:00
|
|
|
#include <util/system.h>
|
|
|
|
#include <validation.h>
|
|
|
|
|
2021-04-18 03:46:01 -04:00
|
|
|
std::atomic_bool fImporting(false);
|
|
|
|
std::atomic_bool fReindex(false);
|
|
|
|
bool fHavePruned = false;
|
|
|
|
bool fPruneMode = false;
|
|
|
|
uint64_t nPruneTarget = 0;
|
|
|
|
|
2021-04-18 09:41:08 -04:00
|
|
|
// TODO make namespace {
|
|
|
|
RecursiveMutex cs_LastBlockFile;
|
|
|
|
std::vector<CBlockFileInfo> vinfoBlockFile;
|
|
|
|
int nLastBlockFile = 0;
|
|
|
|
/** Global flag to indicate we should check to see if there are
|
|
|
|
* block/undo files that should be deleted. Set on startup
|
|
|
|
* or if we allocate more file space when we're in prune mode
|
|
|
|
*/
|
|
|
|
bool fCheckForPruning = false;
|
|
|
|
|
|
|
|
/** Dirty block index entries. */
|
|
|
|
std::set<CBlockIndex*> setDirtyBlockIndex;
|
|
|
|
|
|
|
|
/** Dirty block file entries. */
|
|
|
|
std::set<int> setDirtyFileInfo;
|
|
|
|
// } // namespace
|
|
|
|
|
2021-04-18 03:46:01 -04:00
|
|
|
bool IsBlockPruned(const CBlockIndex* pblockindex)
|
|
|
|
{
|
|
|
|
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're using -prune with -reindex, then delete block files that will be ignored by the
|
|
|
|
// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
|
|
|
|
// is missing, do the same here to delete any later block files after a gap. Also delete all
|
|
|
|
// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile
|
|
|
|
// is in sync with what's actually on disk by the time we start downloading, so that pruning
|
|
|
|
// works correctly.
|
|
|
|
void CleanupBlockRevFiles()
|
|
|
|
{
|
|
|
|
std::map<std::string, fs::path> mapBlockFiles;
|
|
|
|
|
|
|
|
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
|
|
|
|
// Remove the rev files immediately and insert the blk file paths into an
|
|
|
|
// ordered map keyed by block file index.
|
|
|
|
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
|
|
|
|
fs::path blocksdir = gArgs.GetBlocksDirPath();
|
|
|
|
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
|
|
|
|
if (fs::is_regular_file(*it) &&
|
|
|
|
it->path().filename().string().length() == 12 &&
|
|
|
|
it->path().filename().string().substr(8,4) == ".dat")
|
|
|
|
{
|
|
|
|
if (it->path().filename().string().substr(0,3) == "blk")
|
|
|
|
mapBlockFiles[it->path().filename().string().substr(3,5)] = it->path();
|
|
|
|
else if (it->path().filename().string().substr(0,3) == "rev")
|
|
|
|
remove(it->path());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all block files that aren't part of a contiguous set starting at
|
|
|
|
// zero by walking the ordered map (keys are block file indices) by
|
|
|
|
// keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
|
|
|
|
// start removing block files.
|
|
|
|
int nContigCounter = 0;
|
|
|
|
for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
|
|
|
|
if (atoi(item.first) == nContigCounter) {
|
|
|
|
nContigCounter++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
remove(item.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 15:42:05 -03:00
|
|
|
// From validation. TODO move here
|
|
|
|
bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false);
|
|
|
|
|
|
|
|
static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
|
|
|
|
{
|
|
|
|
// Open history file to append
|
|
|
|
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
|
2021-04-02 14:27:59 -03:00
|
|
|
if (fileout.IsNull()) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return error("WriteBlockToDisk: OpenBlockFile failed");
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 15:42:05 -03:00
|
|
|
|
|
|
|
// Write index header
|
|
|
|
unsigned int nSize = GetSerializeSize(block, fileout.GetVersion());
|
|
|
|
fileout << messageStart << nSize;
|
|
|
|
|
|
|
|
// Write block
|
|
|
|
long fileOutPos = ftell(fileout.Get());
|
2021-04-02 14:27:59 -03:00
|
|
|
if (fileOutPos < 0) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return error("WriteBlockToDisk: ftell failed");
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 15:42:05 -03:00
|
|
|
pos.nPos = (unsigned int)fileOutPos;
|
|
|
|
fileout << block;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
|
|
|
|
{
|
|
|
|
block.SetNull();
|
|
|
|
|
|
|
|
// Open history file to read
|
|
|
|
CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
|
2021-04-02 14:27:59 -03:00
|
|
|
if (filein.IsNull()) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString());
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 15:42:05 -03:00
|
|
|
|
|
|
|
// Read block
|
|
|
|
try {
|
|
|
|
filein >> block;
|
2021-04-02 14:27:59 -03:00
|
|
|
} catch (const std::exception& e) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the header
|
2021-04-02 14:27:59 -03:00
|
|
|
if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 15:42:05 -03:00
|
|
|
|
|
|
|
// Signet only: check block solution
|
|
|
|
if (consensusParams.signet_blocks && !CheckSignetBlockSolution(block, consensusParams)) {
|
|
|
|
return error("ReadBlockFromDisk: Errors in block solution at %s", pos.ToString());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
|
|
|
{
|
|
|
|
FlatFilePos blockPos;
|
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
|
|
|
blockPos = pindex->GetBlockPos();
|
|
|
|
}
|
|
|
|
|
2021-04-02 14:27:59 -03:00
|
|
|
if (!ReadBlockFromDisk(block, blockPos, consensusParams)) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return false;
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
|
|
|
if (block.GetHash() != pindex->GetBlockHash()) {
|
2021-04-02 15:42:05 -03:00
|
|
|
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
|
|
|
|
pindex->ToString(), pindex->GetBlockPos().ToString());
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 15:42:05 -03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start)
|
|
|
|
{
|
|
|
|
FlatFilePos hpos = pos;
|
|
|
|
hpos.nPos -= 8; // Seek back 8 bytes for meta header
|
|
|
|
CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
|
|
|
|
if (filein.IsNull()) {
|
|
|
|
return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
CMessageHeader::MessageStartChars blk_start;
|
|
|
|
unsigned int blk_size;
|
|
|
|
|
|
|
|
filein >> blk_start >> blk_size;
|
|
|
|
|
|
|
|
if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
|
|
|
|
return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
|
|
|
|
HexStr(blk_start),
|
|
|
|
HexStr(message_start));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blk_size > MAX_SIZE) {
|
|
|
|
return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(),
|
|
|
|
blk_size, MAX_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
block.resize(blk_size); // Zeroing of memory is intentional here
|
|
|
|
filein.read((char*)block.data(), blk_size);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
|
|
|
|
{
|
|
|
|
FlatFilePos block_pos;
|
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
|
|
|
block_pos = pindex->GetBlockPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ReadRawBlockFromDisk(block, block_pos, message_start);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
|
|
|
|
FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp)
|
|
|
|
{
|
|
|
|
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
|
|
|
|
FlatFilePos blockPos;
|
2021-04-02 14:27:59 -03:00
|
|
|
if (dbp != nullptr) {
|
2021-04-02 15:42:05 -03:00
|
|
|
blockPos = *dbp;
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 15:42:05 -03:00
|
|
|
if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) {
|
|
|
|
error("%s: FindBlockPos failed", __func__);
|
|
|
|
return FlatFilePos();
|
|
|
|
}
|
|
|
|
if (dbp == nullptr) {
|
|
|
|
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
|
|
|
|
AbortNode("Failed to write block");
|
|
|
|
return FlatFilePos();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return blockPos;
|
|
|
|
}
|
|
|
|
|
2021-04-02 14:17:00 -03:00
|
|
|
struct CImportingNow {
|
|
|
|
CImportingNow()
|
|
|
|
{
|
|
|
|
assert(fImporting == false);
|
|
|
|
fImporting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
~CImportingNow()
|
|
|
|
{
|
|
|
|
assert(fImporting == true);
|
|
|
|
fImporting = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
|
|
|
|
{
|
|
|
|
const CChainParams& chainparams = Params();
|
|
|
|
ScheduleBatchPriority();
|
|
|
|
|
|
|
|
{
|
|
|
|
CImportingNow imp;
|
|
|
|
|
|
|
|
// -reindex
|
|
|
|
if (fReindex) {
|
|
|
|
int nFile = 0;
|
|
|
|
while (true) {
|
|
|
|
FlatFilePos pos(nFile, 0);
|
2021-04-02 14:27:59 -03:00
|
|
|
if (!fs::exists(GetBlockPosFilename(pos))) {
|
2021-04-02 14:17:00 -03:00
|
|
|
break; // No block files left to reindex
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 14:17:00 -03:00
|
|
|
FILE* file = OpenBlockFile(pos, true);
|
2021-04-02 14:27:59 -03:00
|
|
|
if (!file) {
|
2021-04-02 14:17:00 -03:00
|
|
|
break; // This error is logged in OpenBlockFile
|
2021-04-02 14:27:59 -03:00
|
|
|
}
|
2021-04-02 14:17:00 -03:00
|
|
|
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
|
2021-04-02 14:27:59 -03:00
|
|
|
chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file, &pos);
|
2021-04-02 14:17:00 -03:00
|
|
|
if (ShutdownRequested()) {
|
|
|
|
LogPrintf("Shutdown requested. Exit %s\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nFile++;
|
|
|
|
}
|
|
|
|
pblocktree->WriteReindexing(false);
|
|
|
|
fReindex = false;
|
|
|
|
LogPrintf("Reindexing finished\n");
|
|
|
|
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
|
2021-04-02 14:27:59 -03:00
|
|
|
chainman.ActiveChainstate().LoadGenesisBlock(chainparams);
|
2021-04-02 14:17:00 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// -loadblock=
|
|
|
|
for (const fs::path& path : vImportFiles) {
|
|
|
|
FILE* file = fsbridge::fopen(path, "rb");
|
|
|
|
if (file) {
|
|
|
|
LogPrintf("Importing blocks file %s...\n", path.string());
|
2021-04-02 14:27:59 -03:00
|
|
|
chainman.ActiveChainstate().LoadExternalBlockFile(chainparams, file);
|
2021-04-02 14:17:00 -03:00
|
|
|
if (ShutdownRequested()) {
|
|
|
|
LogPrintf("Shutdown requested. Exit %s\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LogPrintf("Warning: Could not open blocks file %s\n", path.string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// scan for better chains in the block chain database, that are not yet connected in the active best chain
|
|
|
|
|
|
|
|
// We can't hold cs_main during ActivateBestChain even though we're accessing
|
|
|
|
// the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
|
|
|
|
// the relevant pointers before the ABC call.
|
|
|
|
for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
|
|
|
|
BlockValidationState state;
|
|
|
|
if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
|
|
|
|
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
|
|
|
|
StartShutdown();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
|
|
|
|
LogPrintf("Stopping after block import\n");
|
|
|
|
StartShutdown();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} // End scope of CImportingNow
|
|
|
|
chainman.ActiveChainstate().LoadMempool(args);
|
|
|
|
}
|