Bitcoin Core  27.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 <script/sigcache.h>
27 #include <util/chaintype.h>
28 #include <util/fs.h>
29 #include <util/task_runner.h>
30 #include <validation.h>
31 #include <validationinterface.h>
32 
33 #include <cassert>
34 #include <cstdint>
35 #include <functional>
36 #include <iosfwd>
37 #include <memory>
38 #include <string>
39 
40 int main(int argc, char* argv[])
41 {
42  // SETUP: Argument parsing and handling
43  if (argc != 2) {
44  std::cerr
45  << "Usage: " << argv[0] << " DATADIR" << std::endl
46  << "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
47  << std::endl
48  << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
49  << " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
50  return 1;
51  }
52  fs::path abs_datadir{fs::absolute(argv[1])};
53  fs::create_directories(abs_datadir);
54 
55 
56  // SETUP: Context
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));
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  ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
71 
72  class KernelNotifications : public kernel::Notifications
73  {
74  public:
76  {
77  std::cout << "Block tip changed" << std::endl;
78  return {};
79  }
80  void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override
81  {
82  std::cout << "Header tip changed: " << height << ", " << timestamp << ", " << presync << std::endl;
83  }
84  void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override
85  {
86  std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl;
87  }
88  void warning(const bilingual_str& warning) override
89  {
90  std::cout << "Warning: " << warning.original << std::endl;
91  }
92  void flushError(const bilingual_str& message) override
93  {
94  std::cerr << "Error flushing block data to disk: " << message.original << std::endl;
95  }
96  void fatalError(const bilingual_str& message) override
97  {
98  std::cerr << "Error: " << message.original << std::endl;
99  }
100  };
101  auto notifications = std::make_unique<KernelNotifications>();
102 
103 
104  // SETUP: Chainstate
105  auto chainparams = CChainParams::Main();
106  const ChainstateManager::Options chainman_opts{
107  .chainparams = *chainparams,
108  .datadir = abs_datadir,
109  .notifications = *notifications,
110  .signals = &validation_signals,
111  };
112  const node::BlockManager::Options blockman_opts{
113  .chainparams = chainman_opts.chainparams,
114  .blocks_dir = abs_datadir / "blocks",
115  .notifications = chainman_opts.notifications,
116  };
117  util::SignalInterrupt interrupt;
118  ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
119 
120  node::CacheSizes cache_sizes;
121  cache_sizes.block_tree_db = 2 << 20;
122  cache_sizes.coins_db = 2 << 22;
123  cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
125  auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
126  if (status != node::ChainstateLoadStatus::SUCCESS) {
127  std::cerr << "Failed to load Chain state from your datadir." << std::endl;
128  goto epilogue;
129  } else {
130  std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
131  if (status != node::ChainstateLoadStatus::SUCCESS) {
132  std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
133  goto epilogue;
134  }
135  }
136 
137  for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
138  BlockValidationState state;
139  if (!chainstate->ActivateBestChain(state, nullptr)) {
140  std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
141  goto epilogue;
142  }
143  }
144 
145  // Main program logic starts here
146  std::cout
147  << "Hello! I'm going to print out some information about your datadir." << std::endl
148  << "\t"
149  << "Path: " << abs_datadir << std::endl;
150  {
151  LOCK(chainman.GetMutex());
152  std::cout
153  << "\t" << "Reindexing: " << std::boolalpha << node::fReindex.load() << std::noboolalpha << std::endl
154  << "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
155  << "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
156  << "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl;
157  CBlockIndex* tip = chainman.ActiveTip();
158  if (tip) {
159  std::cout << "\t" << tip->ToString() << std::endl;
160  }
161  }
162 
163  for (std::string line; std::getline(std::cin, line);) {
164  if (line.empty()) {
165  std::cerr << "Empty line found" << std::endl;
166  break;
167  }
168 
169  std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
170  CBlock& block = *blockptr;
171 
172  if (!DecodeHexBlk(block, line)) {
173  std::cerr << "Block decode failed" << std::endl;
174  break;
175  }
176 
177  if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
178  std::cerr << "Block does not start with a coinbase" << std::endl;
179  break;
180  }
181 
182  uint256 hash = block.GetHash();
183  {
184  LOCK(cs_main);
185  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(hash);
186  if (pindex) {
187  if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
188  std::cerr << "duplicate" << std::endl;
189  break;
190  }
191  if (pindex->nStatus & BLOCK_FAILED_MASK) {
192  std::cerr << "duplicate-invalid" << std::endl;
193  break;
194  }
195  }
196  }
197 
198  {
199  LOCK(cs_main);
200  const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
201  if (pindex) {
202  chainman.UpdateUncommittedBlockStructures(block, pindex);
203  }
204  }
205 
206  // Adapted from rpc/mining.cpp
208  {
209  public:
210  uint256 hash;
211  bool found;
213 
214  explicit submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state() {}
215 
216  protected:
217  void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override
218  {
219  if (block.GetHash() != hash)
220  return;
221  found = true;
222  state = stateIn;
223  }
224  };
225 
226  bool new_block;
227  auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
228  validation_signals.RegisterSharedValidationInterface(sc);
229  bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
230  validation_signals.UnregisterSharedValidationInterface(sc);
231  if (!new_block && accepted) {
232  std::cerr << "duplicate" << std::endl;
233  break;
234  }
235  if (!sc->found) {
236  std::cerr << "inconclusive" << std::endl;
237  break;
238  }
239  std::cout << sc->state.ToString() << std::endl;
240  switch (sc->state.GetResult()) {
242  std::cerr << "initial value. Block has not yet been rejected" << std::endl;
243  break;
245  std::cerr << "the block header may be on a too-little-work chain" << std::endl;
246  break;
248  std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
249  break;
251  std::cerr << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
252  break;
254  std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
255  break;
257  std::cerr << "invalid proof of work or time too old" << std::endl;
258  break;
260  std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
261  break;
263  std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
264  break;
266  std::cerr << "A block this one builds on is invalid" << std::endl;
267  break;
269  std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
270  break;
272  std::cerr << "the block failed to meet one of our checkpoints" << std::endl;
273  break;
274  }
275  }
276 
277 epilogue:
278  // Without this precise shutdown sequence, there will be a lot of nullptr
279  // dereferencing and UB.
280  if (chainman.m_thread_load.joinable()) chainman.m_thread_load.join();
281 
282  validation_signals.FlushBackgroundCallbacks();
283  {
284  LOCK(cs_main);
285  for (Chainstate* chainstate : chainman.GetAll()) {
286  if (chainstate->CanFlushToDisk()) {
287  chainstate->ForceFlushStateToDisk();
288  chainstate->ResetCoinsViews();
289  }
290  }
291  }
292 }
int main(int argc, char *argv[])
@ BLOCK_VALID_SCRIPTS
Scripts & signatures ok.
Definition: chain.h:115
@ BLOCK_FAILED_MASK
Definition: chain.h:127
#define Assert(val)
Identity function.
Definition: check.h:77
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:141
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:296
static std::unique_ptr< const CChainParams > Main()
Implement this to subscribe to events generated in validation and mempool.
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:491
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:850
std::string ToString() const
Definition: validation.h:128
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
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 fatalError(const bilingual_str &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 void flushError(const bilingual_str &message)
The flush error notification is sent to notify the user that an error occurred while flushing block d...
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:982
submitblock_StateCatcher(const uint256 &hashIn)
Definition: mining.cpp:979
BlockValidationState state
Definition: mining.cpp:977
256-bit opaque blob.
Definition: uint256.h:106
Helper class that manages an interrupt flag, and allows a thread or signal to interrupt another threa...
@ 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:218
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
static path absolute(const path &p)
Definition: fs.h:82
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:190
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:15
ChainstateLoadResult LoadChainstate(ChainstateManager &chainman, const CacheSizes &cache_sizes, const ChainstateLoadOptions &options)
This sequence can have 4 types of outcomes:
Definition: chainstate.cpp:162
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager &chainman, const ChainstateLoadOptions &options)
Definition: chainstate.cpp:247
std::atomic_bool fReindex
bool InitSignatureCache(size_t max_size_bytes)
Definition: sigcache.cpp:97
Bilingual messages:
Definition: translation.h:18
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
#define LOCK(cs)
Definition: sync.h:257
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:301
This header provides an interface and simple implementation for a task runner.
bool InitScriptExecutionCache(size_t max_size_bytes)
Initializes the script-execution cache.
assert(!tx.IsCoinBase())
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:80