Bitcoin ABC 0.30.5
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>
17
18#include <chainparams.h>
19#include <common/args.h>
20#include <config.h>
22#include <core_io.h>
23#include <init/common.h>
24#include <node/blockstorage.h>
25#include <node/caches.h>
26#include <node/chainstate.h>
27#include <scheduler.h>
28#include <script/scriptcache.h>
29#include <script/sigcache.h>
30#include <util/thread.h>
31#include <util/translation.h>
32#include <validation.h>
33#include <validationinterface.h>
34
35#include <cstdint>
36#include <filesystem>
37#include <functional>
38#include <iosfwd>
39#include <memory>
40
41int main(int argc, char *argv[]) {
42 // SETUP: Argument parsing and handling
43 if (argc != 2) {
44 std::cerr << "Usage: " << argv[0] << " DATADIR" << std::endl
45 << "Display DATADIR information, and process hex-encoded "
46 "blocks on standard input."
47 << std::endl
48 << std::endl
49 << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING "
50 "ONLY, AND EXPECTED TO"
51 << std::endl
52 << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR "
53 "ACTUAL DATADIR."
54 << std::endl;
55 return 1;
56 }
57 std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
59 gArgs.ForceSetArg("-datadir", abs_datadir.string());
60
61 // SETUP: Misc Globals
63
65 auto &config = const_cast<Config &>(GetConfig());
66 config.SetChainParams(*chainparams);
67
68 // ECC_Start, etc.
70
71 // Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
72 // which will try the script cache first and fall back to actually
73 // performing the check with the signature cache.
74 kernel::ValidationCacheSizes validation_cache_sizes{};
75 Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
77 validation_cache_sizes.script_execution_cache_bytes));
78
79 // SETUP: Scheduling and Background Signals
80 CScheduler scheduler{};
81 // Start the lightweight task scheduler thread
82 scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler",
83 [&] { scheduler.serviceQueue(); });
84
85 // Gather some entropy once per minute.
86 scheduler.scheduleEvery(
87 [] {
89 return true;
90 },
91 std::chrono::minutes{1});
92
94
95 class KernelNotifications : public kernel::Notifications {
96 public:
97 void blockTip(SynchronizationState, CBlockIndex &) override {
98 std::cout << "Block tip changed" << std::endl;
99 }
100 void headerTip(SynchronizationState, int64_t height, int64_t timestamp,
101 bool presync) override {
102 std::cout << "Header tip changed: " << height << ", " << timestamp
103 << ", " << presync << std::endl;
104 }
105 void progress(const bilingual_str &title, int progress_percent,
106 bool resume_possible) override {
107 std::cout << "Progress: " << title.original << ", "
108 << progress_percent << ", " << resume_possible
109 << std::endl;
110 }
111 void warning(const std::string &warning) override {
112 std::cout << "Warning: " << warning << std::endl;
113 }
114 };
115 auto notifications = std::make_unique<KernelNotifications>();
116
117 // SETUP: Chainstate
118 const ChainstateManager::Options chainman_opts{
119 .config = config,
120 .datadir = gArgs.GetDataDirNet(),
121 .adjusted_time_callback = NodeClock::now,
122 .notifications = *notifications,
123 };
124 const node::BlockManager::Options blockman_opts{
125 .chainparams = chainman_opts.config.GetChainParams(),
126 .blocks_dir = gArgs.GetBlocksDirPath(),
127 };
128 ChainstateManager chainman{chainman_opts, blockman_opts};
129
130 node::CacheSizes cache_sizes;
131 cache_sizes.block_tree_db = 2 << 20;
132 cache_sizes.coins_db = 2 << 22;
133 cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
135 options.check_interrupt = [] { return false; };
136 auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
138 std::cerr << "Failed to load Chain state from your datadir."
139 << std::endl;
140 goto epilogue;
141 }
142 std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
144 std::cerr << "Failed to verify loaded Chain state from your datadir."
145 << std::endl;
146 goto epilogue;
147 }
148
149 for (Chainstate *chainstate :
150 WITH_LOCK(::cs_main, return chainman.GetAll())) {
152 if (!chainstate->ActivateBestChain(state, nullptr)) {
153 std::cerr << "Failed to connect best block (" << state.ToString()
154 << ")" << std::endl;
155 goto epilogue;
156 }
157 }
158
159 // Main program logic starts here
160 std::cout
161 << "Hello! I'm going to print out some information about your datadir."
162 << std::endl;
163 {
164 LOCK(chainman.GetMutex());
165 std::cout << "\t"
166 << "Path: " << gArgs.GetDataDirNet() << std::endl
167 << "\t"
168 << "Reindexing: " << std::boolalpha << node::fReindex.load()
169 << std::noboolalpha << std::endl
170 << "\t"
171 << "Snapshot Active: " << std::boolalpha
172 << chainman.IsSnapshotActive() << std::noboolalpha
173 << std::endl
174 << "\t"
175 << "Active Height: " << chainman.ActiveHeight() << std::endl
176 << "\t"
177 << "Active IBD: " << std::boolalpha
178 << chainman.ActiveChainstate().IsInitialBlockDownload()
179 << std::noboolalpha << std::endl;
180 CBlockIndex *tip = chainman.ActiveTip();
181 if (tip) {
182 std::cout << "\t" << tip->ToString() << std::endl;
183 }
184 }
185
186 for (std::string line; std::getline(std::cin, line);) {
187 if (line.empty()) {
188 std::cerr << "Empty line found" << std::endl;
189 break;
190 }
191
192 std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
193 CBlock &block = *blockptr;
194
195 if (!DecodeHexBlk(block, line)) {
196 std::cerr << "Block decode failed" << std::endl;
197 break;
198 }
199
200 if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
201 std::cerr << "Block does not start with a coinbase" << std::endl;
202 break;
203 }
204
205 BlockHash hash = block.GetHash();
206 {
207 LOCK(cs_main);
208 const CBlockIndex *pindex =
209 chainman.m_blockman.LookupBlockIndex(hash);
210 if (pindex) {
211 if (pindex->IsValid(BlockValidity::SCRIPTS)) {
212 std::cerr << "Duplicate" << std::endl;
213 break;
214 }
215 if (pindex->nStatus.hasFailed()) {
216 std::cerr << "Duplicate-invalid" << std::endl;
217 break;
218 }
219 }
220 }
221
222 // Adapted from rpc/mining.cpp
224 public:
226 bool found;
228
229 explicit submitblock_StateCatcher(const BlockHash &hashIn)
230 : hash(hashIn), found(false), state() {}
231
232 protected:
233 void BlockChecked(const CBlock &block,
234 const BlockValidationState &stateIn) override {
235 if (block.GetHash() != hash) {
236 return;
237 }
238 found = true;
239 state = stateIn;
240 }
241 };
242
243 bool new_block;
244 auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
246 bool accepted = chainman.ProcessNewBlock(blockptr,
247 /*force_processing=*/true,
248 /*min_pow_checked=*/true,
249 /*new_block=*/&new_block);
251 if (!new_block && accepted) {
252 std::cerr << "Duplicate" << std::endl;
253 break;
254 }
255 if (!sc->found) {
256 std::cerr << "Inconclusive" << std::endl;
257 break;
258 }
259 std::cout << sc->state.ToString() << std::endl;
260 switch (sc->state.GetResult()) {
262 std::cerr << "Initial value. Block has not yet been rejected"
263 << std::endl;
264 break;
266 std::cerr
267 << "the block header may be on a too-little-work chain"
268 << std::endl;
269 break;
271 std::cerr << "Invalid by consensus rules (excluding any below "
272 "reasons)"
273 << std::endl;
274 break;
276 std::cerr << "This block was cached as being invalid and we "
277 "didn't store the reason why"
278 << std::endl;
279 break;
281 std::cerr << "Invalid proof of work or time too old"
282 << std::endl;
283 break;
285 std::cerr << "The block's data didn't match the data committed "
286 "to by the PoW"
287 << std::endl;
288 break;
290 std::cerr << "We don't have the previous block the checked one "
291 "is built on"
292 << std::endl;
293 break;
295 std::cerr << "A block this one builds on is invalid"
296 << std::endl;
297 break;
299 std::cerr << "Block timestamp was > 2 hours in the future (or "
300 "our clock is bad)"
301 << std::endl;
302 break;
304 std::cerr << "The block failed to meet one of our checkpoints"
305 << std::endl;
306 break;
307 }
308 }
309
310epilogue:
311 // Without this precise shutdown sequence, there will be a lot of nullptr
312 // dereferencing and UB.
313 scheduler.stop();
314 if (chainman.m_load_block.joinable()) {
315 chainman.m_load_block.join();
316 }
318
320 {
321 LOCK(cs_main);
322 for (Chainstate *chainstate : chainman.GetAll()) {
323 if (chainstate->CanFlushToDisk()) {
324 chainstate->ForceFlushStateToDisk();
325 chainstate->ResetCoinsViews();
326 }
327 }
328 }
330
332}
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:1219
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:1229
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:1225
BlockValidationState state
Definition: mining.cpp:1223
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
Common init functions shared by bitcoin-node, bitcoin-wallet, etc.
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
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
void SetGlobals()
Definition: common.cpp:30
void UnsetGlobals()
Definition: common.cpp:38
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:262
std::atomic_bool fReindex
void TraceThread(const char *thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:13
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...
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.
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.