Bitcoin ABC  0.26.3
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 
15 
16 #include <chainparams.h>
17 #include <config.h>
18 #include <consensus/validation.h>
19 #include <core_io.h>
20 #include <init/common.h>
21 #include <node/blockstorage.h>
22 #include <node/caches.h>
23 #include <node/chainstate.h>
24 #include <scheduler.h>
25 #include <script/scriptcache.h>
26 #include <script/sigcache.h>
27 #include <util/system.h>
28 #include <util/thread.h>
29 #include <validation.h>
30 #include <validationinterface.h>
31 
32 #include <filesystem>
33 #include <functional>
34 #include <iosfwd>
35 
36 int main(int argc, char *argv[]) {
37  // SETUP: Argument parsing and handling
38  if (argc != 2) {
39  std::cerr << "Usage: " << argv[0] << " DATADIR" << std::endl
40  << "Display DATADIR information, and process hex-encoded "
41  "blocks on standard input."
42  << std::endl
43  << std::endl
44  << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING "
45  "ONLY, AND EXPECTED TO"
46  << std::endl
47  << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR "
48  "ACTUAL DATADIR."
49  << std::endl;
50  return 1;
51  }
52  std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
54  gArgs.ForceSetArg("-datadir", abs_datadir.string());
55 
56  // SETUP: Misc Globals
58  const Config &config = GetConfig();
59 
60  // ECC_Start, etc.
62 
63  // Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
64  // which will try the script cache first and fall back to actually
65  // performing the check with the signature cache.
66  kernel::ValidationCacheSizes validation_cache_sizes{};
67  Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
69  validation_cache_sizes.script_execution_cache_bytes));
70 
71  // SETUP: Scheduling and Background Signals
72  CScheduler scheduler{};
73  // Start the lightweight task scheduler thread
74  scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler",
75  [&] { scheduler.serviceQueue(); });
76 
77  // Gather some entropy once per minute.
78  scheduler.scheduleEvery(
79  [] {
81  return true;
82  },
83  std::chrono::minutes{1});
84 
86 
87  // SETUP: Chainstate
88  const ChainstateManager::Options chainman_opts{
89  .config = config,
90  .datadir = gArgs.GetDataDirNet(),
91  .adjusted_time_callback = NodeClock::now,
92  };
93  const node::BlockManager::Options blockman_opts{
94  .chainparams = chainman_opts.config.GetChainParams(),
95  };
96  ChainstateManager chainman{chainman_opts, blockman_opts};
97 
98  node::CacheSizes cache_sizes;
99  cache_sizes.block_tree_db = 2 << 20;
100  cache_sizes.coins_db = 2 << 22;
101  cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
103  options.check_interrupt = [] { return false; };
104  auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
105  if (status != node::ChainstateLoadStatus::SUCCESS) {
106  std::cerr << "Failed to load Chain state from your datadir."
107  << std::endl;
108  goto epilogue;
109  }
110  std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
111  if (status != node::ChainstateLoadStatus::SUCCESS) {
112  std::cerr << "Failed to verify loaded Chain state from your datadir."
113  << std::endl;
114  goto epilogue;
115  }
116 
117  for (Chainstate *chainstate :
118  WITH_LOCK(::cs_main, return chainman.GetAll())) {
119  BlockValidationState state;
120  if (!chainstate->ActivateBestChain(state, nullptr)) {
121  std::cerr << "Failed to connect best block (" << state.ToString()
122  << ")" << std::endl;
123  goto epilogue;
124  }
125  }
126 
127  // Main program logic starts here
128  std::cout
129  << "Hello! I'm going to print out some information about your datadir."
130  << std::endl;
131  {
132  LOCK(chainman.GetMutex());
133  std::cout << "\t"
134  << "Path: " << gArgs.GetDataDirNet() << std::endl
135  << "\t"
136  << "Reindexing: " << std::boolalpha << node::fReindex.load()
137  << std::noboolalpha << std::endl
138  << "\t"
139  << "Snapshot Active: " << std::boolalpha
140  << chainman.IsSnapshotActive() << std::noboolalpha
141  << std::endl
142  << "\t"
143  << "Active Height: " << chainman.ActiveHeight() << std::endl
144  << "\t"
145  << "Active IBD: " << std::boolalpha
146  << chainman.ActiveChainstate().IsInitialBlockDownload()
147  << std::noboolalpha << std::endl;
148  CBlockIndex *tip = chainman.ActiveTip();
149  if (tip) {
150  std::cout << "\t" << tip->ToString() << std::endl;
151  }
152  }
153 
154  for (std::string line; std::getline(std::cin, line);) {
155  if (line.empty()) {
156  std::cerr << "Empty line found" << std::endl;
157  break;
158  }
159 
160  std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
161  CBlock &block = *blockptr;
162 
163  if (!DecodeHexBlk(block, line)) {
164  std::cerr << "Block decode failed" << std::endl;
165  break;
166  }
167 
168  if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
169  std::cerr << "Block does not start with a coinbase" << std::endl;
170  break;
171  }
172 
173  BlockHash hash = block.GetHash();
174  {
175  LOCK(cs_main);
176  const CBlockIndex *pindex =
177  chainman.m_blockman.LookupBlockIndex(hash);
178  if (pindex) {
179  if (pindex->IsValid(BlockValidity::SCRIPTS)) {
180  std::cerr << "Duplicate" << std::endl;
181  break;
182  }
183  if (pindex->nStatus.hasFailed()) {
184  std::cerr << "Duplicate-invalid" << std::endl;
185  break;
186  }
187  }
188  }
189 
190  // Adapted from rpc/mining.cpp
191  class submitblock_StateCatcher final : public CValidationInterface {
192  public:
193  BlockHash hash;
194  bool found;
196 
197  explicit submitblock_StateCatcher(const BlockHash &hashIn)
198  : hash(hashIn), found(false), state() {}
199 
200  protected:
201  void BlockChecked(const CBlock &block,
202  const BlockValidationState &stateIn) override {
203  if (block.GetHash() != hash) {
204  return;
205  }
206  found = true;
207  state = stateIn;
208  }
209  };
210 
211  bool new_block;
212  auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
214  bool accepted = chainman.ProcessNewBlock(blockptr,
215  /*force_processing=*/true,
216  /*min_pow_checked=*/true,
217  /*new_block=*/&new_block);
219  if (!new_block && accepted) {
220  std::cerr << "Duplicate" << std::endl;
221  break;
222  }
223  if (!sc->found) {
224  std::cerr << "Inconclusive" << std::endl;
225  break;
226  }
227  std::cout << sc->state.ToString() << std::endl;
228  switch (sc->state.GetResult()) {
230  std::cerr << "Initial value. Block has not yet been rejected"
231  << std::endl;
232  break;
234  std::cerr
235  << "the block header may be on a too-little-work chain"
236  << std::endl;
237  break;
239  std::cerr << "Invalid by consensus rules (excluding any below "
240  "reasons)"
241  << std::endl;
242  break;
244  std::cerr << "This block was cached as being invalid and we "
245  "didn't store the reason why"
246  << std::endl;
247  break;
249  std::cerr << "Invalid proof of work or time too old"
250  << std::endl;
251  break;
253  std::cerr << "The block's data didn't match the data committed "
254  "to by the PoW"
255  << std::endl;
256  break;
258  std::cerr << "We don't have the previous block the checked one "
259  "is built on"
260  << std::endl;
261  break;
263  std::cerr << "A block this one builds on is invalid"
264  << std::endl;
265  break;
267  std::cerr << "Block timestamp was > 2 hours in the future (or "
268  "our clock is bad)"
269  << std::endl;
270  break;
272  std::cerr << "The block failed to meet one of our checkpoints"
273  << std::endl;
274  break;
275  }
276  }
277 
278 epilogue:
279  // Without this precise shutdown sequence, there will be a lot of nullptr
280  // dereferencing and UB.
281  scheduler.stop();
282  if (chainman.m_load_block.joinable()) {
283  chainman.m_load_block.join();
284  }
286 
288  {
289  LOCK(cs_main);
290  for (Chainstate *chainstate : chainman.GetAll()) {
291  if (chainstate->CanFlushToDisk()) {
292  chainstate->ForceFlushStateToDisk();
293  chainstate->ResetCoinsViews();
294  }
295  }
296  }
298 
300 }
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.
#define Assert(val)
Identity function.
Definition: check.h:84
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:706
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:266
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
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:628
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1143
Definition: config.h:17
std::string ToString() const
Definition: validation.h:118
void BlockChecked(const CBlock &block, const BlockValidationState &stateIn) override
Notifies listeners of a block validation result.
Definition: mining.cpp:1128
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:1124
BlockValidationState state
Definition: mining.cpp:1122
const Config & GetConfig()
Definition: config.cpp:34
@ 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.
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:29
void UnsetGlobals()
Definition: common.cpp:37
ChainstateLoadResult LoadChainstate(ChainstateManager &chainman, const CacheSizes &cache_sizes, const ChainstateLoadOptions &options)
This sequence can have 4 types of outcomes:
Definition: chainstate.cpp:166
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager &chainman, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:261
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:84
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
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
ArgsManager gArgs
Definition: system.cpp:80
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
void StopScriptCheckWorkerThreads()
Stop all of the script checking worker threads.
CMainSignals & GetMainSignals()
void UnregisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Unregister subscriber.
void RegisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Register subscriber.