/ src / bitcoin-chainstate.cpp
bitcoin-chainstate.cpp
  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  }