Bitcoin ABC 0.30.13
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>
19
20#include <chainparams.h>
21#include <common/args.h>
22#include <config.h>
24#include <core_io.h>
25#include <node/blockstorage.h>
26#include <node/caches.h>
27#include <node/chainstate.h>
28#include <scheduler.h>
29#include <script/scriptcache.h>
30#include <script/sigcache.h>
31#include <util/thread.h>
32#include <util/translation.h>
33#include <validation.h>
34#include <validationinterface.h>
35
36#include <cassert>
37#include <cstdint>
38#include <filesystem>
39#include <functional>
40#include <iosfwd>
41#include <memory>
42
43int main(int argc, char *argv[]) {
44 // SETUP: Argument parsing and handling
45 if (argc != 2) {
46 std::cerr << "Usage: " << argv[0] << " DATADIR" << std::endl
47 << "Display DATADIR information, and process hex-encoded "
48 "blocks on standard input."
49 << std::endl
50 << std::endl
51 << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING "
52 "ONLY, AND EXPECTED TO"
53 << std::endl
54 << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR "
55 "ACTUAL DATADIR."
56 << std::endl;
57 return 1;
58 }
59 std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
61 gArgs.ForceSetArg("-datadir", abs_datadir.string());
62
63 // SETUP: Misc Globals
65
67 auto &config = const_cast<Config &>(GetConfig());
68 config.SetChainParams(*chainparams);
69
70 // ECC_Start, etc.
71 kernel::Context kernel_context{};
72 // We can't use a goto here, but we can use an assert since none of the
73 // things instantiated so far requires running the epilogue to be torn down
74 // properly
75 assert(kernel::SanityChecks(kernel_context));
76
77 // Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
78 // which will try the script cache first and fall back to actually
79 // performing the check with the signature cache.
80 kernel::ValidationCacheSizes validation_cache_sizes{};
81 Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
83 validation_cache_sizes.script_execution_cache_bytes));
84
85 // SETUP: Scheduling and Background Signals
86 CScheduler scheduler{};
87 // Start the lightweight task scheduler thread
88 scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler",
89 [&] { scheduler.serviceQueue(); });
90
91 // Gather some entropy once per minute.
92 scheduler.scheduleEvery(
93 [] {
95 return true;
96 },
97 std::chrono::minutes{1});
98
100
101 class KernelNotifications : public kernel::Notifications {
102 public:
103 void blockTip(SynchronizationState, CBlockIndex &) override {
104 std::cout << "Block tip changed" << std::endl;
105 }
106 void headerTip(SynchronizationState, int64_t height, int64_t timestamp,
107 bool presync) override {
108 std::cout << "Header tip changed: " << height << ", " << timestamp
109 << ", " << presync << std::endl;
110 }
111 void progress(const bilingual_str &title, int progress_percent,
112 bool resume_possible) override {
113 std::cout << "Progress: " << title.original << ", "
114 << progress_percent << ", " << resume_possible
115 << std::endl;
116 }
117 void warning(const std::string &warning) override {
118 std::cout << "Warning: " << warning << std::endl;
119 }
120 };
121 auto notifications = std::make_unique<KernelNotifications>();
122
123 // SETUP: Chainstate
124 const ChainstateManager::Options chainman_opts{
125 .config = config,
126 .datadir = gArgs.GetDataDirNet(),
127 .adjusted_time_callback = NodeClock::now,
128 .notifications = *notifications,
129 };
130 const node::BlockManager::Options blockman_opts{
131 .chainparams = chainman_opts.config.GetChainParams(),
132 .blocks_dir = gArgs.GetBlocksDirPath(),
133 };
134 ChainstateManager chainman{chainman_opts, blockman_opts};
135
136 node::CacheSizes cache_sizes;
137 cache_sizes.block_tree_db = 2 << 20;
138 cache_sizes.coins_db = 2 << 22;
139 cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
141 options.check_interrupt = [] { return false; };
142 auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
144 std::cerr << "Failed to load Chain state from your datadir."
145 << std::endl;
146 goto epilogue;
147 }
148 std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
150 std::cerr << "Failed to verify loaded Chain state from your datadir."
151 << std::endl;
152 goto epilogue;
153 }
154
155 for (Chainstate *chainstate :
156 WITH_LOCK(::cs_main, return chainman.GetAll())) {
158 if (!chainstate->ActivateBestChain(state, nullptr)) {
159 std::cerr << "Failed to connect best block (" << state.ToString()
160 << ")" << std::endl;
161 goto epilogue;
162 }
163 }
164
165 // Main program logic starts here
166 std::cout
167 << "Hello! I'm going to print out some information about your datadir."
168 << std::endl;
169 {
170 LOCK(chainman.GetMutex());
171 std::cout << "\t"
172 << "Path: " << gArgs.GetDataDirNet() << std::endl
173 << "\t"
174 << "Reindexing: " << std::boolalpha << node::fReindex.load()
175 << std::noboolalpha << std::endl
176 << "\t"
177 << "Snapshot Active: " << std::boolalpha
178 << chainman.IsSnapshotActive() << std::noboolalpha
179 << std::endl
180 << "\t"
181 << "Active Height: " << chainman.ActiveHeight() << std::endl
182 << "\t"
183 << "Active IBD: " << std::boolalpha
184 << chainman.IsInitialBlockDownload() << std::noboolalpha
185 << std::endl;
186 CBlockIndex *tip = chainman.ActiveTip();
187 if (tip) {
188 std::cout << "\t" << tip->ToString() << std::endl;
189 }
190 }
191
192 for (std::string line; std::getline(std::cin, line);) {
193 if (line.empty()) {
194 std::cerr << "Empty line found" << std::endl;
195 break;
196 }
197
198 std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
199 CBlock &block = *blockptr;
200
201 if (!DecodeHexBlk(block, line)) {
202 std::cerr << "Block decode failed" << std::endl;
203 break;
204 }
205
206 if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
207 std::cerr << "Block does not start with a coinbase" << std::endl;
208 break;
209 }
210
211 BlockHash hash = block.GetHash();
212 {
213 LOCK(cs_main);
214 const CBlockIndex *pindex =
215 chainman.m_blockman.LookupBlockIndex(hash);
216 if (pindex) {
217 if (pindex->IsValid(BlockValidity::SCRIPTS)) {
218 std::cerr << "Duplicate" << std::endl;
219 break;
220 }
221 if (pindex->nStatus.hasFailed()) {
222 std::cerr << "Duplicate-invalid" << std::endl;
223 break;
224 }
225 }
226 }
227
228 // Adapted from rpc/mining.cpp
230 public:
232 bool found;
234
235 explicit submitblock_StateCatcher(const BlockHash &hashIn)
236 : hash(hashIn), found(false), state() {}
237
238 protected:
239 void BlockChecked(const CBlock &block,
240 const BlockValidationState &stateIn) override {
241 if (block.GetHash() != hash) {
242 return;
243 }
244 found = true;
245 state = stateIn;
246 }
247 };
248
249 bool new_block;
250 auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
252 bool accepted = chainman.ProcessNewBlock(blockptr,
253 /*force_processing=*/true,
254 /*min_pow_checked=*/true,
255 /*new_block=*/&new_block);
257 if (!new_block && accepted) {
258 std::cerr << "Duplicate" << std::endl;
259 break;
260 }
261 if (!sc->found) {
262 std::cerr << "Inconclusive" << std::endl;
263 break;
264 }
265 std::cout << sc->state.ToString() << std::endl;
266 switch (sc->state.GetResult()) {
268 std::cerr << "Initial value. Block has not yet been rejected"
269 << std::endl;
270 break;
272 std::cerr
273 << "the block header may be on a too-little-work chain"
274 << std::endl;
275 break;
277 std::cerr << "Invalid by consensus rules (excluding any below "
278 "reasons)"
279 << std::endl;
280 break;
282 std::cerr << "This block was cached as being invalid and we "
283 "didn't store the reason why"
284 << std::endl;
285 break;
287 std::cerr << "Invalid proof of work or time too old"
288 << std::endl;
289 break;
291 std::cerr << "The block's data didn't match the data committed "
292 "to by the PoW"
293 << std::endl;
294 break;
296 std::cerr << "We don't have the previous block the checked one "
297 "is built on"
298 << std::endl;
299 break;
301 std::cerr << "A block this one builds on is invalid"
302 << std::endl;
303 break;
305 std::cerr << "Block timestamp was > 2 hours in the future (or "
306 "our clock is bad)"
307 << std::endl;
308 break;
310 std::cerr << "The block failed to meet one of our checkpoints"
311 << std::endl;
312 break;
313 }
314 }
315
316epilogue:
317 // Without this precise shutdown sequence, there will be a lot of nullptr
318 // dereferencing and UB.
319 scheduler.stop();
320 if (chainman.m_thread_load.joinable()) {
321 chainman.m_thread_load.join();
322 }
324
326 {
327 LOCK(cs_main);
328 for (Chainstate *chainstate : chainman.GetAll()) {
329 if (chainstate->CanFlushToDisk()) {
330 chainstate->ForceFlushStateToDisk();
331 chainstate->ResetCoinsViews();
332 }
333 }
334 }
336}
ArgsManager gArgs
Definition: args.cpp:38
int main(int argc, char *argv[])
@ SCRIPTS
Scripts & signatures ok.
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
Definition: chainparams.cpp:51
#define Assert(val)
Identity function.
Definition: check.h:84
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: args.cpp:597
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:215
fs::path GetBlocksDirPath() const
Get blocks directory path.
Definition: args.cpp:289
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
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:211
std::string ToString() const
Definition: blockindex.cpp:28
static std::unique_ptr< const CChainParams > Main(const ChainOptions &options)
void UnregisterBackgroundSignalScheduler()
Unregister a CScheduler to give callbacks which should run in the background - these callbacks will n...
void RegisterBackgroundSignalScheduler(CScheduler &scheduler)
Register a CScheduler to give callbacks which should run in the background (may only be called once)
void FlushBackgroundCallbacks()
Call any remaining callbacks on the calling thread.
Simple class for background tasks that should be run periodically or once "after a while".
Definition: scheduler.h:41
std::thread m_service_thread
Definition: scheduler.h:46
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:699
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1140
Definition: config.h:19
virtual void SetChainParams(const CChainParams chainParamsIn)=0
std::string ToString() const
Definition: validation.h:125
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 warning(const std::string &warning)
virtual void progress(const bilingual_str &title, int progress_percent, bool resume_possible)
virtual void blockTip(SynchronizationState state, CBlockIndex &index)
void BlockChecked(const CBlock &block, const BlockValidationState &stateIn) override
Notifies listeners of a block validation result.
Definition: mining.cpp:1290
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:1286
BlockValidationState state
Definition: mining.cpp:1284
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:232
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
bool error(const char *fmt, const Args &...args)
Definition: logging.h:263
static path absolute(const path &p)
Definition: fs.h:96
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:179
util::Result< void > SanityChecks(const Context &)
Ensure a usable environment with all necessary library support.
Definition: checks.cpp:14
ChainstateLoadResult LoadChainstate(ChainstateManager &chainman, const CacheSizes &cache_sizes, const ChainstateLoadOptions &options)
This sequence can have 4 types of outcomes:
Definition: chainstate.cpp:167
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager &chainman, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:271
std::atomic_bool fReindex
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:14
void RandAddPeriodic() noexcept
Gather entropy from various expensive sources, and feed them to the PRNG state.
Definition: random.cpp:645
bool InitScriptExecutionCache(size_t max_size_bytes)
Initializes the script-execution cache.
Definition: scriptcache.cpp:76
bool InitSignatureCache(size_t max_size_bytes)
Definition: sigcache.cpp:85
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:71
Bilingual messages:
Definition: translation.h:17
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
int64_t coins
Definition: caches.h:17
int64_t block_tree_db
Definition: caches.h:15
int64_t coins_db
Definition: caches.h:16
std::function< bool()> check_interrupt
Definition: chainstate.h:36
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
void StopScriptCheckWorkerThreads()
Stop all of the script checking worker threads.
assert(!tx.IsCoinBase())
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:114
CMainSignals & GetMainSignals()
void UnregisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Unregister subscriber.
void RegisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Register subscriber.