Bitcoin Core  25.99.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/chainparams.h>
16 #include <kernel/checks.h>
17 #include <kernel/context.h>
19 
20 #include <consensus/validation.h>
21 #include <core_io.h>
22 #include <node/blockstorage.h>
23 #include <node/caches.h>
24 #include <node/chainstate.h>
25 #include <random.h>
26 #include <scheduler.h>
27 #include <script/sigcache.h>
28 #include <util/chaintype.h>
29 #include <util/thread.h>
30 #include <validation.h>
31 #include <validationinterface.h>
32 
33 #include <cassert>
34 #include <cstdint>
35 #include <filesystem>
36 #include <functional>
37 #include <iosfwd>
38 #include <memory>
39 #include <string>
40 
41 int main(int argc, char* argv[])
42 {
43  // SETUP: Argument parsing and handling
44  if (argc != 2) {
45  std::cerr
46  << "Usage: " << argv[0] << " DATADIR" << std::endl
47  << "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
48  << std::endl
49  << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
50  << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
51  return 1;
52  }
53  std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
55 
56 
57  // SETUP: Context
58  kernel::Context kernel_context{};
59  // We can't use a goto here, but we can use an assert since none of the
60  // things instantiated so far requires running the epilogue to be torn down
61  // properly
62  assert(kernel::SanityChecks(kernel_context));
63 
64  // Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
65  // which will try the script cache first and fall back to actually
66  // performing the check with the signature cache.
67  kernel::ValidationCacheSizes validation_cache_sizes{};
68  Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
69  Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
70 
71 
72  // SETUP: Scheduling and Background Signals
73  CScheduler scheduler{};
74  // Start the lightweight task scheduler thread
75  scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { scheduler.serviceQueue(); });
76 
77  // Gather some entropy once per minute.
78  scheduler.scheduleEvery(RandAddPeriodic, std::chrono::minutes{1});
79 
81 
82  class KernelNotifications : public kernel::Notifications
83  {
84  public:
86  {
87  std::cout << "Block tip changed" << std::endl;
88  return {};
89  }
90  void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override
91  {
92  std::cout << "Header tip changed: " << height << ", " << timestamp << ", " << presync << std::endl;
93  }
94  void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override
95  {
96  std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl;
97  }
98  void warning(const bilingual_str& warning) override
99  {
100  std::cout << "Warning: " << warning.original << std::endl;
101  }
102  void flushError(const std::string& debug_message) override
103  {
104  std::cerr << "Error flushing block data to disk: " << debug_message << std::endl;
105  }
106  void fatalError(const std::string& debug_message, const bilingual_str& user_message) override
107  {
108  std::cerr << "Error: " << debug_message << std::endl;
109  std::cerr << (user_message.empty() ? "A fatal internal error occurred." : user_message.original) << std::endl;
110  }
111  };
112  auto notifications = std::make_unique<KernelNotifications>();
113 
114 
115  // SETUP: Chainstate
116  auto chainparams = CChainParams::Main();
117  const ChainstateManager::Options chainman_opts{
118  .chainparams = *chainparams,
119  .datadir = abs_datadir,
120  .adjusted_time_callback = NodeClock::now,
121  .notifications = *notifications,
122  };
123  const node::BlockManager::Options blockman_opts{
124  .chainparams = chainman_opts.chainparams,
125  .blocks_dir = abs_datadir / "blocks",
126  .notifications = chainman_opts.notifications,
127  };
128  ChainstateManager chainman{kernel_context.interrupt, 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);
137  if (status != node::ChainstateLoadStatus::SUCCESS) {
138  std::cerr << "Failed to load Chain state from your datadir." << std::endl;
139  goto epilogue;
140  } else {
141  std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
142  if (status != node::ChainstateLoadStatus::SUCCESS) {
143  std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
144  goto epilogue;
145  }
146  }
147 
148  for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
149  BlockValidationState state;
150  if (!chainstate->ActivateBestChain(state, nullptr)) {
151  std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
152  goto epilogue;
153  }
154  }
155 
156  // Main program logic starts here
157  std::cout
158  << "Hello! I'm going to print out some information about your datadir." << std::endl
159  << "\t"
160  << "Path: " << abs_datadir << std::endl;
161  {
162  LOCK(chainman.GetMutex());
163  std::cout
164  << "\t" << "Reindexing: " << std::boolalpha << node::fReindex.load() << std::noboolalpha << std::endl
165  << "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
166  << "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
167  << "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl;
168  CBlockIndex* tip = chainman.ActiveTip();
169  if (tip) {
170  std::cout << "\t" << tip->ToString() << std::endl;
171  }
172  }
173 
174  for (std::string line; std::getline(std::cin, line);) {
175  if (line.empty()) {
176  std::cerr << "Empty line found" << std::endl;
177  break;
178  }
179 
180  std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
181  CBlock& block = *blockptr;
182 
183  if (!DecodeHexBlk(block, line)) {
184  std::cerr << "Block decode failed" << std::endl;
185  break;
186  }
187 
188  if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
189  std::cerr << "Block does not start with a coinbase" << std::endl;
190  break;
191  }
192 
193  uint256 hash = block.GetHash();
194  {
195  LOCK(cs_main);
196  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
197  if (pindex) {
198  if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
199  std::cerr << "duplicate" << std::endl;
200  break;
201  }
202  if (pindex->nStatus & BLOCK_FAILED_MASK) {
203  std::cerr << "duplicate-invalid" << std::endl;
204  break;
205  }
206  }
207  }
208 
209  {
210  LOCK(cs_main);
211  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
212  if (pindex) {
213  chainman.UpdateUncommittedBlockStructures(block, pindex);
214  }
215  }
216 
217  // Adapted from rpc/mining.cpp
219  {
220  public:
221  uint256 hash;
222  bool found;
224 
225  explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
226 
227  protected:
228  void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override
229  {
230  if (block.GetHash() != hash)
231  return;
232  found = true;
233  state = stateIn;
234  }
235  };
236 
237  bool new_block;
238  auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
240  bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
242  if (!new_block && accepted) {
243  std::cerr << "duplicate" << std::endl;
244  break;
245  }
246  if (!sc->found) {
247  std::cerr << "inconclusive" << std::endl;
248  break;
249  }
250  std::cout << sc->state.ToString() << std::endl;
251  switch (sc->state.GetResult()) {
253  std::cerr << "initial value. Block has not yet been rejected" << std::endl;
254  break;
256  std::cerr << "the block header may be on a too-little-work chain" << std::endl;
257  break;
259  std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
260  break;
262  std::cerr << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
263  break;
265  std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
266  break;
268  std::cerr << "invalid proof of work or time too old" << std::endl;
269  break;
271  std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
272  break;
274  std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
275  break;
277  std::cerr << "A block this one builds on is invalid" << std::endl;
278  break;
280  std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
281  break;
283  std::cerr << "the block failed to meet one of our checkpoints" << std::endl;
284  break;
285  }
286  }
287 
288 epilogue:
289  // Without this precise shutdown sequence, there will be a lot of nullptr
290  // dereferencing and UB.
291  scheduler.stop();
292  if (chainman.m_thread_load.joinable()) chainman.m_thread_load.join();
294 
296  {
297  LOCK(cs_main);
298  for (Chainstate* chainstate : chainman.GetAll()) {
299  if (chainstate->CanFlushToDisk()) {
300  chainstate->ForceFlushStateToDisk();
301  chainstate->ResetCoinsViews();
302  }
303  }
304  }
306 }
int main(int argc, char *argv[])
@ BLOCK_VALID_SCRIPTS
Scripts & signatures ok. Implies all parents are either at least VALID_SCRIPTS, or are ASSUMED_VALID.
Definition: chain.h:120
@ BLOCK_FAILED_MASK
Definition: chain.h:132
#define Assert(val)
Identity function.
Definition: check.h:73
uint256 hashPrevBlock
Definition: block.h:26
uint256 GetHash() const
Definition: block.cpp:11
Definition: block.h:69
std::vector< CTransactionRef > vtx
Definition: block.h:72
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:159
std::string ToString() const
Definition: chain.cpp:15
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:316
static std::unique_ptr< const CChainParams > Main()
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:39
std::thread m_service_thread
Definition: scheduler.h:44
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:466
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:818
std::string ToString() const
Definition: validation.h:127
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 bilingual_str &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:966
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:963
BlockValidationState state
Definition: mining.cpp:961
256-bit opaque blob.
Definition: uint256.h:106
@ BLOCK_CHECKPOINT
the block failed to meet one of our checkpoints
@ BLOCK_RECENT_CONSENSUS_CHANGE
Invalid by a change to consensus rules more recent than SegWit.
@ 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:219
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
bool error(const char *fmt, const Args &... args)
Definition: logging.h:262
static path absolute(const path &p)
Definition: fs.h:81
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
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:16
ChainstateLoadResult LoadChainstate(ChainstateManager &chainman, const CacheSizes &cache_sizes, const ChainstateLoadOptions &options)
This sequence can have 4 types of outcomes:
Definition: chainstate.cpp:161
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager &chainman, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:239
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:16
void RandAddPeriodic() noexcept
Gather entropy from various expensive sources, and feed them to the PRNG state.
Definition: random.cpp:563
bool InitSignatureCache(size_t max_size_bytes)
Definition: sigcache.cpp:97
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:70
Bilingual messages:
Definition: translation.h:18
bool empty() const
Definition: translation.h:29
std::string original
Definition: translation.h:19
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:35
#define LOCK(cs)
Definition: sync.h:258
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:302
bool InitScriptExecutionCache(size_t max_size_bytes)
Initializes the script-execution cache.
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:86
CMainSignals & GetMainSignals()
void UnregisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Unregister subscriber.
void RegisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Register subscriber.