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