Bitcoin Core  24.1.0
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 Core'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 Core's coding style.
11 //
12 // It is part of the libbitcoinkernel project.
13 
14 #include <kernel/checks.h>
15 #include <kernel/context.h>
17 
18 #include <chainparams.h>
19 #include <consensus/validation.h>
20 #include <core_io.h>
21 #include <node/blockstorage.h>
22 #include <node/caches.h>
23 #include <node/chainstate.h>
24 #include <scheduler.h>
25 #include <script/sigcache.h>
26 #include <util/system.h>
27 #include <util/thread.h>
28 #include <validation.h>
29 #include <validationinterface.h>
30 
31 #include <cassert>
32 #include <filesystem>
33 #include <functional>
34 #include <iosfwd>
35 
36 int main(int argc, char* argv[])
37 {
38  // SETUP: Argument parsing and handling
39  if (argc != 2) {
40  std::cerr
41  << "Usage: " << argv[0] << " DATADIR" << std::endl
42  << "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
43  << std::endl
44  << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
45  << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
46  return 1;
47  }
48  std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
50  gArgs.ForceSetArg("-datadir", abs_datadir.string());
51 
52 
53  // SETUP: Misc Globals
55  const CChainParams& chainparams = Params();
56 
57  kernel::Context kernel_context{};
58  // We can't use a goto here, but we can use an assert since none of the
59  // things instantiated so far requires running the epilogue to be torn down
60  // properly
61  assert(!kernel::SanityChecks(kernel_context).has_value());
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));
68  Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
69 
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", [&] { scheduler.serviceQueue(); });
75 
76  // Gather some entropy once per minute.
77  scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
78 
80 
81 
82  // SETUP: Chainstate
83  const ChainstateManager::Options chainman_opts{
84  .chainparams = chainparams,
85  .adjusted_time_callback = NodeClock::now,
86  };
87  ChainstateManager chainman{chainman_opts};
88 
89  node::CacheSizes cache_sizes;
90  cache_sizes.block_tree_db = 2 << 20;
91  cache_sizes.coins_db = 2 << 22;
92  cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
94  options.check_interrupt = [] { return false; };
95  auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
97  std::cerr << "Failed to load Chain state from your datadir." << std::endl;
98  goto epilogue;
99  } else {
100  std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
101  if (status != node::ChainstateLoadStatus::SUCCESS) {
102  std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
103  goto epilogue;
104  }
105  }
106 
107  for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
108  BlockValidationState state;
109  if (!chainstate->ActivateBestChain(state, nullptr)) {
110  std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
111  goto epilogue;
112  }
113  }
114 
115  // Main program logic starts here
116  std::cout
117  << "Hello! I'm going to print out some information about your datadir." << std::endl
118  << "\t" << "Path: " << gArgs.GetDataDirNet() << std::endl;
119  {
120  LOCK(chainman.GetMutex());
121  std::cout
122  << "\t" << "Reindexing: " << std::boolalpha << node::fReindex.load() << std::noboolalpha << std::endl
123  << "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
124  << "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
125  << "\t" << "Active IBD: " << std::boolalpha << chainman.ActiveChainstate().IsInitialBlockDownload() << std::noboolalpha << std::endl;
126  CBlockIndex* tip = chainman.ActiveTip();
127  if (tip) {
128  std::cout << "\t" << tip->ToString() << std::endl;
129  }
130  }
131 
132  for (std::string line; std::getline(std::cin, line);) {
133  if (line.empty()) {
134  std::cerr << "Empty line found" << std::endl;
135  break;
136  }
137 
138  std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
139  CBlock& block = *blockptr;
140 
141  if (!DecodeHexBlk(block, line)) {
142  std::cerr << "Block decode failed" << std::endl;
143  break;
144  }
145 
146  if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
147  std::cerr << "Block does not start with a coinbase" << std::endl;
148  break;
149  }
150 
151  uint256 hash = block.GetHash();
152  {
153  LOCK(cs_main);
154  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
155  if (pindex) {
156  if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
157  std::cerr << "duplicate" << std::endl;
158  break;
159  }
160  if (pindex->nStatus & BLOCK_FAILED_MASK) {
161  std::cerr << "duplicate-invalid" << std::endl;
162  break;
163  }
164  }
165  }
166 
167  {
168  LOCK(cs_main);
169  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
170  if (pindex) {
171  chainman.UpdateUncommittedBlockStructures(block, pindex);
172  }
173  }
174 
175  // Adapted from rpc/mining.cpp
177  {
178  public:
179  uint256 hash;
180  bool found;
182 
183  explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
184 
185  protected:
186  void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override
187  {
188  if (block.GetHash() != hash)
189  return;
190  found = true;
191  state = stateIn;
192  }
193  };
194 
195  bool new_block;
196  auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
198  bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
200  if (!new_block && accepted) {
201  std::cerr << "duplicate" << std::endl;
202  break;
203  }
204  if (!sc->found) {
205  std::cerr << "inconclusive" << std::endl;
206  break;
207  }
208  std::cout << sc->state.ToString() << std::endl;
209  switch (sc->state.GetResult()) {
211  std::cerr << "initial value. Block has not yet been rejected" << std::endl;
212  break;
214  std::cerr << "the block header may be on a too-little-work chain" << std::endl;
215  break;
217  std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
218  break;
220  std::cerr << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
221  break;
223  std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
224  break;
226  std::cerr << "invalid proof of work or time too old" << std::endl;
227  break;
229  std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
230  break;
232  std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
233  break;
235  std::cerr << "A block this one builds on is invalid" << std::endl;
236  break;
238  std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
239  break;
241  std::cerr << "the block failed to meet one of our checkpoints" << std::endl;
242  break;
243  }
244  }
245 
246 epilogue:
247  // Without this precise shutdown sequence, there will be a lot of nullptr
248  // dereferencing and UB.
249  scheduler.stop();
250  if (chainman.m_load_block.joinable()) chainman.m_load_block.join();
252 
254  {
255  LOCK(cs_main);
256  for (Chainstate* chainstate : chainman.GetAll()) {
257  if (chainstate->CanFlushToDisk()) {
258  chainstate->ForceFlushStateToDisk();
259  chainstate->ResetCoinsViews();
260  }
261  }
262  }
264 }
std::string ToString() const
Definition: chain.cpp:15
ArgsManager gArgs
Definition: system.cpp:86
bool DecodeHexBlk(CBlock &, const std::string &strHexBlk)
Definition: core_read.cpp:219
assert(!tx.IsCoinBase())
std::atomic_bool fReindex
BlockValidationState state
Definition: mining.cpp:915
int64_t block_tree_db
Definition: caches.h:15
Definition: block.h:68
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager &chainman, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:152
We don&#39;t have the previous block the checked one is built on.
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:799
invalid proof of work or time too old
void BlockChecked(const CBlock &block, const BlockValidationState &stateIn) override
Notifies listeners of a block validation result.
Definition: mining.cpp:920
An options struct for ChainstateManager, more ergonomically referred to as ChainstateManager::Options...
void UnregisterBackgroundSignalScheduler()
Unregister a CScheduler to give callbacks which should run in the background - these callbacks will n...
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system...
Definition: chainparams.h:69
the block header may be on a too-little-work chain
std::function< bool()> check_interrupt
Definition: chainstate.h:30
int main(int argc, char *argv[])
std::optional< bilingual_str > SanityChecks(const Context &)
Ensure a usable environment with all necessary library support.
Definition: checks.cpp:16
void RandAddPeriodic() noexcept
Gather entropy from various expensive sources, and feed them to the PRNG state.
Definition: random.cpp:582
void RegisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Register subscriber.
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:693
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:917
void UnregisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Unregister subscriber.
Implement this to subscribe to events generated in validation.
static const std::string MAIN
Chain name strings.
bool InitSignatureCache(size_t max_size_bytes)
Definition: sigcache.cpp:96
initial value. Block has not yet been rejected
Chainstate stores and provides an API to update our local knowledge of the current best chain...
Definition: validation.h:437
Scripts & signatures ok. Implies all parents are also at least SCRIPTS.
Definition: chain.h:121
this block was cached as being invalid and we didn&#39;t store the reason why
#define LOCK(cs)
Definition: sync.h:261
std::string ToString() const
Definition: validation.h:127
the block failed to meet one of our checkpoints
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given chain name.
CMainSignals & GetMainSignals()
bool InitScriptExecutionCache(size_t max_size_bytes)
Initializes the script-execution cache.
void StopScriptCheckWorkerThreads()
Stop all of the script checking worker threads.
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:188
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:305
int64_t coins
Definition: caches.h:17
bool IsValid(enum BlockStatus nUpTo=BLOCK_VALID_TRANSACTIONS) const EXCLUSIVE_LOCKS_REQUIRED(
Check whether this block index entry is valid up to the passed validity level.
Definition: chain.h:313
uint256 GetHash() const
Definition: block.cpp:11
void RegisterBackgroundSignalScheduler(CScheduler &scheduler)
Register a CScheduler to give callbacks which should run in the background (may only be called once) ...
256-bit opaque blob.
Definition: uint256.h:119
invalid by consensus rules (excluding any below reasons)
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:72
std::thread m_service_thread
Definition: scheduler.h:44
the block&#39;s data didn&#39;t match the data committed to by the PoW
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: chain.h:151
const CChainParams & Params()
Return the currently selected parameters.
void FlushBackgroundCallbacks()
Call any remaining callbacks on the calling thread.
A block this one builds on is invalid.
Context struct holding the kernel library&#39;s logically global state, and passed to external libbitcoin...
Definition: context.h:20
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: validation.cpp:121
Invalid by a change to consensus rules more recent than SegWit.
block timestamp was > 2 hours in the future (or our clock is bad)
ChainstateLoadResult LoadChainstate(ChainstateManager &chainman, const CacheSizes &cache_sizes, const ChainstateLoadOptions &options)
This sequence can have 4 types of outcomes:
Definition: chainstate.cpp:28
Simple class for background tasks that should be run periodically or once "after a while"...
Definition: scheduler.h:38
int64_t coins_db
Definition: caches.h:16
static path absolute(const path &p)
Definition: fs.h:81
bool error(const char *fmt, const Args &... args)
Definition: system.h:48
#define Assert(val)
Identity function.
Definition: check.h:74
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:303
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:16