Bitcoin ABC 0.33.11
P2P Digital Currency
bitcoin-chainstate.cpp
Go to the documentation of this file.
1// Copyright (c) 2022 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4//
5// The bitcoin-chainstate executable serves to surface the dependencies required
6// by a program wishing to use Bitcoin ABC's consensus engine as it is right
7// now.
8//
9// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
10// it may diverge from Bitcoin ABC's coding style.
11//
12// It is part of the libbitcoinkernel project.
13
14#include <kernel/chainparams.h>
16#include <kernel/checks.h>
17#include <kernel/context.h>
18
19#include <chainparams.h>
20#include <config.h>
22#include <core_io.h>
23#include <kernel/caches.h>
24#include <logging.h>
25#include <node/blockstorage.h>
26#include <node/chainstate.h>
27#include <script/scriptcache.h>
28#include <script/sigcache.h>
29#include <util/chaintype.h>
30#include <util/fs.h>
31#include <util/task_runner.h>
32#include <util/translation.h>
33#include <validation.h>
34#include <validationinterface.h>
35
36#include <cassert>
37#include <cstdint>
38#include <functional>
39#include <iosfwd>
40#include <memory>
41
42int main(int argc, char *argv[]) {
43 // We do not enable logging for this app, so explicitly disable it.
44 // To enable logging instead, replace with:
45 // LogInstance().m_print_to_console = true;
46 // LogInstance().StartLogging();
48
49 // SETUP: Argument parsing and handling
50 if (argc != 2) {
51 std::cerr << "Usage: " << argv[0] << " DATADIR" << std::endl
52 << "Display DATADIR information, and process hex-encoded "
53 "blocks on standard input."
54 << std::endl
55 << std::endl
56 << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING "
57 "ONLY, AND EXPECTED TO"
58 << std::endl
59 << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR "
60 "ACTUAL DATADIR."
61 << std::endl;
62 return 1;
63 }
64 fs::path abs_datadir{fs::absolute(argv[1])};
65 fs::create_directories(abs_datadir);
66
67 // SETUP: Misc Globals
69
71 auto &config = const_cast<Config &>(GetConfig());
72 config.SetChainParams(*chainparams);
73
74 // ECC_Start, etc.
75 kernel::Context kernel_context{};
76 // We can't use a goto here, but we can use an assert since none of the
77 // things instantiated so far requires running the epilogue to be torn down
78 // properly
79 assert(kernel::SanityChecks(kernel_context));
80
81 ValidationSignals validation_signals{
82 std::make_unique<util::ImmediateTaskRunner>()};
83
84 class KernelNotifications : public kernel::Notifications {
85 public:
87 CBlockIndex &) override {
88 std::cout << "Block tip changed" << std::endl;
89 return {};
90 }
91 void headerTip(SynchronizationState, int64_t height, int64_t timestamp,
92 bool presync) override {
93 std::cout << "Header tip changed: " << height << ", " << timestamp
94 << ", " << presync << std::endl;
95 }
96 void progress(const bilingual_str &title, int progress_percent,
97 bool resume_possible) override {
98 std::cout << "Progress: " << title.original << ", "
99 << progress_percent << ", " << resume_possible
100 << std::endl;
101 }
102 void warning(const std::string &warning) override {
103 std::cout << "Warning: " << warning << std::endl;
104 }
105 void flushError(const std::string &debug_message) override {
106 std::cerr << "Error flushing block data to disk: " << debug_message
107 << std::endl;
108 }
109 void fatalError(const std::string &debug_message,
110 const bilingual_str &user_message) override {
111 std::cerr << "Error: " << debug_message << std::endl;
112 std::cerr << (user_message.empty()
113 ? "A fatal internal error occurred."
114 : user_message.original)
115 << std::endl;
116 }
117 };
118 auto notifications = std::make_unique<KernelNotifications>();
119
120 // SETUP: Chainstate
121 const ChainstateManager::Options chainman_opts{
122 .config = config,
123 .datadir = abs_datadir,
124 .adjusted_time_callback = NodeClock::now,
125 .notifications = *notifications,
126 .signals = &validation_signals,
127 };
128 const node::BlockManager::Options blockman_opts{
129 .chainparams = chainman_opts.config.GetChainParams(),
130 .blocks_dir = abs_datadir / "blocks",
131 .notifications = chainman_opts.notifications,
132 };
133 ChainstateManager chainman{kernel_context.interrupt, chainman_opts,
134 blockman_opts};
135
138 options.check_interrupt = [] { return false; };
139 auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
141 std::cerr << "Failed to load Chain state from your datadir."
142 << std::endl;
143 goto epilogue;
144 }
145 std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
147 std::cerr << "Failed to verify loaded Chain state from your datadir."
148 << std::endl;
149 goto epilogue;
150 }
151
152 for (Chainstate *chainstate :
153 WITH_LOCK(::cs_main, return chainman.GetAll())) {
155 if (!chainstate->ActivateBestChain(state, nullptr)) {
156 std::cerr << "Failed to connect best block (" << state.ToString()
157 << ")" << std::endl;
158 goto epilogue;
159 }
160 }
161
162 // Main program logic starts here
163 std::cout
164 << "Hello! I'm going to print out some information about your datadir."
165 << std::endl;
166 {
167 LOCK(chainman.GetMutex());
168 std::cout << "\t"
169 << "Path: " << abs_datadir << std::endl
170 << "\t"
171 << "Reindexing: " << std::boolalpha
172 << chainman.m_blockman.m_reindexing.load() << std::noboolalpha
173 << std::endl
174 << "\t"
175 << "Snapshot Active: " << std::boolalpha
176 << chainman.IsSnapshotActive() << std::noboolalpha
177 << std::endl
178 << "\t"
179 << "Active Height: " << chainman.ActiveHeight() << std::endl
180 << "\t"
181 << "Active IBD: " << std::boolalpha
182 << chainman.IsInitialBlockDownload() << std::noboolalpha
183 << std::endl;
184 CBlockIndex *tip = chainman.ActiveTip();
185 if (tip) {
186 std::cout << "\t" << tip->ToString() << std::endl;
187 }
188 }
189
190 for (std::string line; std::getline(std::cin, line);) {
191 if (line.empty()) {
192 std::cerr << "Empty line found" << std::endl;
193 break;
194 }
195
196 std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
197 CBlock &block = *blockptr;
198
199 if (!DecodeHexBlk(block, line)) {
200 std::cerr << "Block decode failed" << std::endl;
201 break;
202 }
203
204 if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
205 std::cerr << "Block does not start with a coinbase" << std::endl;
206 break;
207 }
208
209 BlockHash hash = block.GetHash();
210 {
211 LOCK(cs_main);
212 const CBlockIndex *pindex =
213 chainman.m_blockman.LookupBlockIndex(hash);
214 if (pindex) {
215 if (pindex->IsValid(BlockValidity::SCRIPTS)) {
216 std::cerr << "Duplicate" << std::endl;
217 break;
218 }
219 if (pindex->nStatus.hasFailed()) {
220 std::cerr << "Duplicate-invalid" << std::endl;
221 break;
222 }
223 }
224 }
225
226 // Adapted from rpc/mining.cpp
228 public:
230 bool found;
232
233 explicit submitblock_StateCatcher(const BlockHash &hashIn)
234 : hash(hashIn), found(false), state() {}
235
236 protected:
237 void BlockChecked(const CBlock &block,
238 const BlockValidationState &stateIn) override {
239 if (block.GetHash() != hash) {
240 return;
241 }
242 found = true;
243 state = stateIn;
244 }
245 };
246
247 bool new_block;
248 auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
249 validation_signals.RegisterSharedValidationInterface(sc);
250 bool accepted = chainman.ProcessNewBlock(
251 blockptr, /*force_processing=*/true, /*min_pow_checked=*/true,
252 /*new_block=*/&new_block);
253 validation_signals.UnregisterSharedValidationInterface(sc);
254 if (!new_block && accepted) {
255 std::cerr << "Duplicate" << std::endl;
256 break;
257 }
258 if (!sc->found) {
259 std::cerr << "Inconclusive" << std::endl;
260 break;
261 }
262 std::cout << sc->state.ToString() << std::endl;
263 switch (sc->state.GetResult()) {
265 std::cerr << "Initial value. Block has not yet been rejected"
266 << std::endl;
267 break;
269 std::cerr
270 << "the block header may be on a too-little-work chain"
271 << std::endl;
272 break;
274 std::cerr << "Invalid by consensus rules (excluding any below "
275 "reasons)"
276 << std::endl;
277 break;
279 std::cerr << "This block was cached as being invalid and we "
280 "didn't store the reason why"
281 << std::endl;
282 break;
284 std::cerr << "Invalid proof of work or time too old"
285 << std::endl;
286 break;
288 std::cerr << "The block's data didn't match the data committed "
289 "to by the PoW"
290 << std::endl;
291 break;
293 std::cerr << "We don't have the previous block the checked one "
294 "is built on"
295 << std::endl;
296 break;
298 std::cerr << "A block this one builds on is invalid"
299 << std::endl;
300 break;
302 std::cerr << "Block timestamp was > 2 hours in the future (or "
303 "our clock is bad)"
304 << std::endl;
305 break;
307 std::cerr << "The block failed to meet one of our checkpoints"
308 << std::endl;
309 break;
310 }
311 }
312
313epilogue:
314 // Without this precise shutdown sequence, there will be a lot of nullptr
315 // dereferencing and UB.
316 if (chainman.m_thread_load.joinable()) {
317 chainman.m_thread_load.join();
318 }
319
320 validation_signals.FlushBackgroundCallbacks();
321 {
322 LOCK(cs_main);
323 for (Chainstate *chainstate : chainman.GetAll()) {
324 if (chainstate->CanFlushToDisk()) {
325 chainstate->ForceFlushStateToDisk();
326 chainstate->ResetCoinsViews();
327 }
328 }
329 }
330}
int main(int argc, char *argv[])
@ SCRIPTS
Scripts & signatures ok.
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given BIP70 chain name.
Definition: chainparams.cpp:50
void DisableLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
This offers a slight speedup and slightly smaller memory usage compared to leaving the logging system...
Definition: logging.cpp:122
BlockHash GetHash() const
Definition: block.cpp:11
Definition: block.h:60
std::vector< CTransactionRef > vtx
Definition: block.h:63
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
bool IsValid(enum BlockValidity nUpTo=BlockValidity::TRANSACTIONS) const EXCLUSIVE_LOCKS_REQUIRED(
Check whether this block index entry is valid up to the passed validity level.
Definition: blockindex.h:191
std::string ToString() const
Definition: blockindex.cpp:30
static std::unique_ptr< const CChainParams > Main(const ChainOptions &options)
Implement this to subscribe to events generated in validation.
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:725
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1174
Definition: config.h:19
virtual void SetChainParams(const CChainParams chainParamsIn)=0
std::string ToString() const
Definition: validation.h:125
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
A base class defining functions for notifying about certain kernel events.
virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync)
virtual void flushError(const std::string &debug_message)
The flush error notification is sent to notify the user that an error occurred while flushing block d...
virtual void fatalError(const std::string &debug_message, const bilingual_str &user_message={})
The fatal error notification is sent to notify the user when an error occurs in kernel code that can'...
virtual void warning(const std::string &warning)
virtual void progress(const bilingual_str &title, int progress_percent, bool resume_possible)
virtual InterruptResult blockTip(SynchronizationState state, CBlockIndex &index)
void BlockChecked(const CBlock &block, const BlockValidationState &stateIn) override
Notifies listeners of a block validation result.
Definition: mining.cpp:1291
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:1288
BlockValidationState state
Definition: mining.cpp:1286
const Config & GetConfig()
Definition: config.cpp:40
@ BLOCK_CHECKPOINT
the block failed to meet one of our checkpoints
@ BLOCK_HEADER_LOW_WORK
the block header may be on a too-little-work chain
@ BLOCK_INVALID_HEADER
invalid proof of work or time too old
@ BLOCK_CACHED_INVALID
this block was cached as being invalid and we didn't store the reason why
@ BLOCK_CONSENSUS
invalid by consensus rules (excluding any below reasons)
@ BLOCK_MISSING_PREV
We don't have the previous block the checked one is built on.
@ BLOCK_INVALID_PREV
A block this one builds on is invalid.
@ BLOCK_MUTATED
the block's data didn't match the data committed to by the PoW
@ BLOCK_TIME_FUTURE
block timestamp was > 2 hours in the future (or our clock is bad)
@ BLOCK_RESULT_UNSET
initial value. Block has not yet been rejected
bool DecodeHexBlk(CBlock &, const std::string &strHexBlk)
Definition: core_read.cpp:231
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
static constexpr int64_t DEFAULT_KERNEL_CACHE
Suggested default amount of cache reserved for the kernel (bytes)
Definition: caches.h:14
BCLog::Logger & LogInstance()
Definition: logging.cpp:28
static path absolute(const path &p)
Definition: fs.h:101
static bool create_directories(const std::filesystem::path &p)
Create directory (and if necessary its parents), unless the leaf directory already exists or is a sym...
Definition: fs.h:185
std::variant< std::monostate, Interrupted > InterruptResult
Simple result type for functions that need to propagate an interrupt status and don't have other retu...
util::Result< void > SanityChecks(const Context &)
Ensure a usable environment with all necessary library support.
Definition: checks.cpp:13
ChainstateLoadResult LoadChainstate(ChainstateManager &chainman, const CacheSizes &cache_sizes, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:172
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager &chainman, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:276
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:29
Bilingual messages:
Definition: translation.h:17
bool empty() const
Definition: translation.h:27
std::string original
Definition: translation.h:18
An options struct for BlockManager, more ergonomically referred to as BlockManager::Options due to th...
const CChainParams & chainparams
An options struct for ChainstateManager, more ergonomically referred to as ChainstateManager::Options...
Context struct holding the kernel library's logically global state, and passed to external libbitcoin...
Definition: context.h:20
std::function< bool()> check_interrupt
Definition: chainstate.h:38
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
This header provides an interface and simple implementation for a task runner.
assert(!tx.IsCoinBase())
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:110