mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-06 14:19:59 -05:00

b4dd7ab43e
logging: use std::string_view (Anthony Towns)558df5c733
logging: Apply formatting to early log messages (Anthony Towns)6cf9b34440
logging: Limit early logging buffer (Anthony Towns)0b1960f1b2
logging: Add DisableLogging() (Anthony Towns)6bbc2dd6c5
logging: Add thread safety annotations (Anthony Towns) Pull request description: In order to cope gracefully with `Log*()` calls that are invoked prior to logging being fully configured (indicated by calling `StartLogging()` we buffer early log messages in `m_msgs_before_open`. This has a couple of minor issues: * if there are many such log messages the buffer can become arbitrarily large; this can be a problem for users of libkernel that might not wish to worry about logging at all, and as a result never invoke `StartLogging()` * early log messages are formatted before the formatting options are configured, leading to inconsistent output Fix those issues by buffering the log info prior to formatting it, and setting a limit on the size of the buffer (dropping the oldest lines, and reporting the number of lines skipped). Also adds some thread safety annotations, and the ability to invoke `LogInstance().DisableLogging()` if you want to disable logging entirely, for a minor efficiency improvement. ACKs for top commit: maflcko: re-ACKb4dd7ab43e
🕴 ryanofsky: Code review ACKb4dd7ab43e
TheCharlatan: Nice, ACKb4dd7ab43e
Tree-SHA512: 966660181276939225a9f776de6ee0665e44577d2ee9cc76b06c8937297217482e6e426bdc5772d1ce533a0ba093a8556b6a50857d4c876ad8923e432a200440
298 lines
12 KiB
C++
298 lines
12 KiB
C++
// Copyright (c) 2022 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
//
|
|
// The bitcoin-chainstate executable serves to surface the dependencies required
|
|
// by a program wishing to use Bitcoin Core's consensus engine as it is right
|
|
// now.
|
|
//
|
|
// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
|
|
// it may diverge from Bitcoin Core's coding style.
|
|
//
|
|
// It is part of the libbitcoinkernel project.
|
|
|
|
#include <kernel/chainparams.h>
|
|
#include <kernel/chainstatemanager_opts.h>
|
|
#include <kernel/checks.h>
|
|
#include <kernel/context.h>
|
|
#include <kernel/warning.h>
|
|
|
|
#include <consensus/validation.h>
|
|
#include <core_io.h>
|
|
#include <logging.h>
|
|
#include <node/blockstorage.h>
|
|
#include <node/caches.h>
|
|
#include <node/chainstate.h>
|
|
#include <random.h>
|
|
#include <script/sigcache.h>
|
|
#include <util/chaintype.h>
|
|
#include <util/fs.h>
|
|
#include <util/signalinterrupt.h>
|
|
#include <util/task_runner.h>
|
|
#include <util/translation.h>
|
|
#include <validation.h>
|
|
#include <validationinterface.h>
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <iosfwd>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
// We do not enable logging for this app, so explicitly disable it.
|
|
// To enable logging instead, replace with:
|
|
// LogInstance().m_print_to_console = true;
|
|
// LogInstance().StartLogging();
|
|
LogInstance().DisableLogging();
|
|
|
|
// SETUP: Argument parsing and handling
|
|
if (argc != 2) {
|
|
std::cerr
|
|
<< "Usage: " << argv[0] << " DATADIR" << std::endl
|
|
<< "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
|
|
<< std::endl
|
|
<< "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
|
|
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
|
|
return 1;
|
|
}
|
|
fs::path abs_datadir{fs::absolute(argv[1])};
|
|
fs::create_directories(abs_datadir);
|
|
|
|
|
|
// SETUP: Context
|
|
kernel::Context kernel_context{};
|
|
// We can't use a goto here, but we can use an assert since none of the
|
|
// things instantiated so far requires running the epilogue to be torn down
|
|
// properly
|
|
assert(kernel::SanityChecks(kernel_context));
|
|
|
|
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
|
|
|
|
class KernelNotifications : public kernel::Notifications
|
|
{
|
|
public:
|
|
kernel::InterruptResult blockTip(SynchronizationState, CBlockIndex&) override
|
|
{
|
|
std::cout << "Block tip changed" << std::endl;
|
|
return {};
|
|
}
|
|
void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override
|
|
{
|
|
std::cout << "Header tip changed: " << height << ", " << timestamp << ", " << presync << std::endl;
|
|
}
|
|
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override
|
|
{
|
|
std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl;
|
|
}
|
|
void warningSet(kernel::Warning id, const bilingual_str& message) override
|
|
{
|
|
std::cout << "Warning " << static_cast<int>(id) << " set: " << message.original << std::endl;
|
|
}
|
|
void warningUnset(kernel::Warning id) override
|
|
{
|
|
std::cout << "Warning " << static_cast<int>(id) << " unset" << std::endl;
|
|
}
|
|
void flushError(const bilingual_str& message) override
|
|
{
|
|
std::cerr << "Error flushing block data to disk: " << message.original << std::endl;
|
|
}
|
|
void fatalError(const bilingual_str& message) override
|
|
{
|
|
std::cerr << "Error: " << message.original << std::endl;
|
|
}
|
|
};
|
|
auto notifications = std::make_unique<KernelNotifications>();
|
|
|
|
|
|
// SETUP: Chainstate
|
|
auto chainparams = CChainParams::Main();
|
|
const ChainstateManager::Options chainman_opts{
|
|
.chainparams = *chainparams,
|
|
.datadir = abs_datadir,
|
|
.notifications = *notifications,
|
|
.signals = &validation_signals,
|
|
};
|
|
const node::BlockManager::Options blockman_opts{
|
|
.chainparams = chainman_opts.chainparams,
|
|
.blocks_dir = abs_datadir / "blocks",
|
|
.notifications = chainman_opts.notifications,
|
|
};
|
|
util::SignalInterrupt interrupt;
|
|
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
|
|
|
|
node::CacheSizes cache_sizes;
|
|
cache_sizes.block_tree_db = 2 << 20;
|
|
cache_sizes.coins_db = 2 << 22;
|
|
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
|
|
node::ChainstateLoadOptions options;
|
|
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
|
|
if (status != node::ChainstateLoadStatus::SUCCESS) {
|
|
std::cerr << "Failed to load Chain state from your datadir." << std::endl;
|
|
goto epilogue;
|
|
} else {
|
|
std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
|
|
if (status != node::ChainstateLoadStatus::SUCCESS) {
|
|
std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
|
|
goto epilogue;
|
|
}
|
|
}
|
|
|
|
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
|
|
BlockValidationState state;
|
|
if (!chainstate->ActivateBestChain(state, nullptr)) {
|
|
std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
|
|
goto epilogue;
|
|
}
|
|
}
|
|
|
|
// Main program logic starts here
|
|
std::cout
|
|
<< "Hello! I'm going to print out some information about your datadir." << std::endl
|
|
<< "\t"
|
|
<< "Path: " << abs_datadir << std::endl;
|
|
{
|
|
LOCK(chainman.GetMutex());
|
|
std::cout
|
|
<< "\t" << "Blockfiles Indexed: " << std::boolalpha << chainman.m_blockman.m_blockfiles_indexed.load() << std::noboolalpha << std::endl
|
|
<< "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
|
|
<< "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
|
|
<< "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl;
|
|
CBlockIndex* tip = chainman.ActiveTip();
|
|
if (tip) {
|
|
std::cout << "\t" << tip->ToString() << std::endl;
|
|
}
|
|
}
|
|
|
|
for (std::string line; std::getline(std::cin, line);) {
|
|
if (line.empty()) {
|
|
std::cerr << "Empty line found" << std::endl;
|
|
break;
|
|
}
|
|
|
|
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
|
|
CBlock& block = *blockptr;
|
|
|
|
if (!DecodeHexBlk(block, line)) {
|
|
std::cerr << "Block decode failed" << std::endl;
|
|
break;
|
|
}
|
|
|
|
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
|
|
std::cerr << "Block does not start with a coinbase" << std::endl;
|
|
break;
|
|
}
|
|
|
|
uint256 hash = block.GetHash();
|
|
{
|
|
LOCK(cs_main);
|
|
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
|
|
if (pindex) {
|
|
if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
|
|
std::cerr << "duplicate" << std::endl;
|
|
break;
|
|
}
|
|
if (pindex->nStatus & BLOCK_FAILED_MASK) {
|
|
std::cerr << "duplicate-invalid" << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
LOCK(cs_main);
|
|
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
|
|
if (pindex) {
|
|
chainman.UpdateUncommittedBlockStructures(block, pindex);
|
|
}
|
|
}
|
|
|
|
// Adapted from rpc/mining.cpp
|
|
class submitblock_StateCatcher final : public CValidationInterface
|
|
{
|
|
public:
|
|
uint256 hash;
|
|
bool found;
|
|
BlockValidationState state;
|
|
|
|
explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
|
|
|
|
protected:
|
|
void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override
|
|
{
|
|
if (block.GetHash() != hash)
|
|
return;
|
|
found = true;
|
|
state = stateIn;
|
|
}
|
|
};
|
|
|
|
bool new_block;
|
|
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
|
|
validation_signals.RegisterSharedValidationInterface(sc);
|
|
bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
|
|
validation_signals.UnregisterSharedValidationInterface(sc);
|
|
if (!new_block && accepted) {
|
|
std::cerr << "duplicate" << std::endl;
|
|
break;
|
|
}
|
|
if (!sc->found) {
|
|
std::cerr << "inconclusive" << std::endl;
|
|
break;
|
|
}
|
|
std::cout << sc->state.ToString() << std::endl;
|
|
switch (sc->state.GetResult()) {
|
|
case BlockValidationResult::BLOCK_RESULT_UNSET:
|
|
std::cerr << "initial value. Block has not yet been rejected" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
|
|
std::cerr << "the block header may be on a too-little-work chain" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_CONSENSUS:
|
|
std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
|
|
std::cerr << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_CACHED_INVALID:
|
|
std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_INVALID_HEADER:
|
|
std::cerr << "invalid proof of work or time too old" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_MUTATED:
|
|
std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_MISSING_PREV:
|
|
std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_INVALID_PREV:
|
|
std::cerr << "A block this one builds on is invalid" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_TIME_FUTURE:
|
|
std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
|
|
break;
|
|
case BlockValidationResult::BLOCK_CHECKPOINT:
|
|
std::cerr << "the block failed to meet one of our checkpoints" << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
epilogue:
|
|
// Without this precise shutdown sequence, there will be a lot of nullptr
|
|
// dereferencing and UB.
|
|
if (chainman.m_thread_load.joinable()) chainman.m_thread_load.join();
|
|
|
|
validation_signals.FlushBackgroundCallbacks();
|
|
{
|
|
LOCK(cs_main);
|
|
for (Chainstate* chainstate : chainman.GetAll()) {
|
|
if (chainstate->CanFlushToDisk()) {
|
|
chainstate->ForceFlushStateToDisk();
|
|
chainstate->ResetCoinsViews();
|
|
}
|
|
}
|
|
}
|
|
}
|