/ src / bitcoin-chainstate.cpp
bitcoin-chainstate.cpp
  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>
 15  #include <kernel/chainstatemanager_opts.h>
 16  #include <kernel/checks.h>
 17  #include <kernel/context.h>
 18  #include <kernel/validation_cache_sizes.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:
 75          kernel::InterruptResult blockTip(SynchronizationState, CBlockIndex&) override
 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);
124      node::ChainstateLoadOptions options;
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
207          class submitblock_StateCatcher final : public CValidationInterface
208          {
209          public:
210              uint256 hash;
211              bool found;
212              BlockValidationState state;
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()) {
241          case BlockValidationResult::BLOCK_RESULT_UNSET:
242              std::cerr << "initial value. Block has not yet been rejected" << std::endl;
243              break;
244          case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
245              std::cerr << "the block header may be on a too-little-work chain" << std::endl;
246              break;
247          case BlockValidationResult::BLOCK_CONSENSUS:
248              std::cerr << "invalid by consensus rules (excluding any below reasons)" << std::endl;
249              break;
250          case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
251              std::cerr << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
252              break;
253          case BlockValidationResult::BLOCK_CACHED_INVALID:
254              std::cerr << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
255              break;
256          case BlockValidationResult::BLOCK_INVALID_HEADER:
257              std::cerr << "invalid proof of work or time too old" << std::endl;
258              break;
259          case BlockValidationResult::BLOCK_MUTATED:
260              std::cerr << "the block's data didn't match the data committed to by the PoW" << std::endl;
261              break;
262          case BlockValidationResult::BLOCK_MISSING_PREV:
263              std::cerr << "We don't have the previous block the checked one is built on" << std::endl;
264              break;
265          case BlockValidationResult::BLOCK_INVALID_PREV:
266              std::cerr << "A block this one builds on is invalid" << std::endl;
267              break;
268          case BlockValidationResult::BLOCK_TIME_FUTURE:
269              std::cerr << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
270              break;
271          case BlockValidationResult::BLOCK_CHECKPOINT:
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  }