bitcoin-chainstate.cpp raw

   1  // Copyright (c) 2022-present 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/bitcoinkernel_wrapper.h>
  15  
  16  #include <cassert>
  17  #include <charconv>
  18  #include <filesystem>
  19  #include <iostream>
  20  #include <optional>
  21  #include <string>
  22  #include <string_view>
  23  #include <vector>
  24  
  25  using namespace btck;
  26  
  27  std::vector<std::byte> hex_string_to_byte_vec(std::string_view hex)
  28  {
  29      std::vector<std::byte> bytes;
  30      bytes.reserve(hex.length() / 2);
  31  
  32      for (size_t i{0}; i < hex.length(); i += 2) {
  33          uint8_t byte_value;
  34          auto [ptr, ec] = std::from_chars(hex.data() + i, hex.data() + i + 2, byte_value, 16);
  35  
  36          if (ec != std::errc{} || ptr != hex.data() + i + 2) {
  37              throw std::invalid_argument("Invalid hex character");
  38          }
  39          bytes.push_back(static_cast<std::byte>(byte_value));
  40      }
  41      return bytes;
  42  }
  43  
  44  class KernelLog
  45  {
  46  public:
  47      void LogMessage(std::string_view message)
  48      {
  49          std::cout << "kernel: " << message;
  50      }
  51  };
  52  
  53  class TestValidationInterface : public ValidationInterface
  54  {
  55  public:
  56      TestValidationInterface() = default;
  57  
  58      std::optional<std::string> m_expected_valid_block = std::nullopt;
  59  
  60      void BlockChecked(Block block, BlockValidationStateView state) override
  61      {
  62          auto mode{state.GetValidationMode()};
  63          switch (mode) {
  64          case ValidationMode::VALID: {
  65              std::cout << "Valid block" << std::endl;
  66              return;
  67          }
  68          case ValidationMode::INVALID: {
  69              std::cout << "Invalid block: ";
  70              auto result{state.GetBlockValidationResult()};
  71              switch (result) {
  72              case BlockValidationResult::UNSET:
  73                  std::cout << "initial value. Block has not yet been rejected" << std::endl;
  74                  break;
  75              case BlockValidationResult::HEADER_LOW_WORK:
  76                  std::cout << "the block header may be on a too-little-work chain" << std::endl;
  77                  break;
  78              case BlockValidationResult::CONSENSUS:
  79                  std::cout << "invalid by consensus rules" << std::endl;
  80                  break;
  81              case BlockValidationResult::CACHED_INVALID:
  82                  std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
  83                  break;
  84              case BlockValidationResult::INVALID_HEADER:
  85                  std::cout << "invalid proof of work or time too old" << std::endl;
  86                  break;
  87              case BlockValidationResult::MUTATED:
  88                  std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
  89                  break;
  90              case BlockValidationResult::MISSING_PREV:
  91                  std::cout << "We don't have the previous block the checked one is built on" << std::endl;
  92                  break;
  93              case BlockValidationResult::INVALID_PREV:
  94                  std::cout << "A block this one builds on is invalid" << std::endl;
  95                  break;
  96              case BlockValidationResult::TIME_FUTURE:
  97                  std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
  98                  break;
  99              }
 100              return;
 101          }
 102          case ValidationMode::INTERNAL_ERROR: {
 103              std::cout << "Internal error" << std::endl;
 104              return;
 105          }
 106          }
 107      }
 108  };
 109  
 110  class TestKernelNotifications : public KernelNotifications
 111  {
 112  public:
 113      void BlockTipHandler(SynchronizationState, const BlockTreeEntry, double) override
 114      {
 115          std::cout << "Block tip changed" << std::endl;
 116      }
 117  
 118      void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) override
 119      {
 120          std::cout << "Made progress: " << title << " " << progress_percent << "%" << std::endl;
 121      }
 122  
 123      void WarningSetHandler(Warning warning, std::string_view message) override
 124      {
 125          std::cout << message << std::endl;
 126      }
 127  
 128      void WarningUnsetHandler(Warning warning) override
 129      {
 130          std::cout << "Warning unset: " << static_cast<std::underlying_type_t<Warning>>(warning) << std::endl;
 131      }
 132  
 133      void FlushErrorHandler(std::string_view error) override
 134      {
 135          std::cout << error << std::endl;
 136      }
 137  
 138      void FatalErrorHandler(std::string_view error) override
 139      {
 140          std::cout << error << std::endl;
 141      }
 142  };
 143  
 144  int main(int argc, char* argv[])
 145  {
 146      // SETUP: Argument parsing and handling
 147      const bool has_regtest_flag{argc == 3 && std::string(argv[1]) == "-regtest"};
 148      if (argc < 2 || argc > 3 || (argc == 3 && !has_regtest_flag)) {
 149          std::cerr
 150              << "Usage: " << argv[0] << " [-regtest] DATADIR" << std::endl
 151              << "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
 152              << "Uses mainnet parameters by default, regtest with -regtest flag" << std::endl
 153              << std::endl
 154              << "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
 155              << "           BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
 156          return 1;
 157      }
 158      std::filesystem::path abs_datadir{std::filesystem::absolute(argv[argc-1])};
 159      std::filesystem::create_directories(abs_datadir);
 160  
 161      btck_LoggingOptions logging_options = {
 162          .log_timestamps = true,
 163          .log_time_micros = false,
 164          .log_threadnames = false,
 165          .log_sourcelocations = false,
 166          .always_print_category_levels = true,
 167      };
 168  
 169      logging_set_options(logging_options);
 170  
 171      Logger logger{std::make_unique<KernelLog>()};
 172  
 173      ContextOptions options{};
 174      ChainParams params{has_regtest_flag ? ChainType::REGTEST : ChainType::MAINNET};
 175      options.SetChainParams(params);
 176  
 177      options.SetNotifications(std::make_shared<TestKernelNotifications>());
 178      options.SetValidationInterface(std::make_shared<TestValidationInterface>());
 179  
 180      Context context{options};
 181  
 182      ChainstateManagerOptions chainman_opts{context, abs_datadir.string(), (abs_datadir / "blocks").string()};
 183      chainman_opts.SetWorkerThreads(4);
 184  
 185      std::unique_ptr<ChainMan> chainman;
 186      try {
 187          chainman = std::make_unique<ChainMan>(context, chainman_opts);
 188      } catch (std::exception&) {
 189          std::cerr << "Failed to instantiate ChainMan, exiting" << std::endl;
 190          return 1;
 191      }
 192  
 193      std::cout << "Enter the block you want to validate on the next line:" << std::endl;
 194  
 195      for (std::string line; std::getline(std::cin, line);) {
 196          if (line.empty()) {
 197              std::cerr << "Empty line found, try again:" << std::endl;
 198              continue;
 199          }
 200  
 201          auto raw_block{hex_string_to_byte_vec(line)};
 202          std::unique_ptr<Block> block;
 203          try {
 204              block = std::make_unique<Block>(raw_block);
 205          } catch (std::exception&) {
 206              std::cerr << "Block decode failed, try again:" << std::endl;
 207              continue;
 208          }
 209  
 210          bool new_block = false;
 211          bool accepted = chainman->ProcessBlock(*block, &new_block);
 212          if (accepted) {
 213              std::cerr << "Block has not yet been rejected" << std::endl;
 214          } else {
 215              std::cerr << "Block was not accepted" << std::endl;
 216          }
 217          if (!new_block) {
 218              std::cerr << "Block is a duplicate" << std::endl;
 219          }
 220      }
 221  }
 222