Bitcoin Core  31.0.0
P2P Digital Currency
bitcoin-chainstate.cpp
Go to the documentation of this file.
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 
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 
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  }
69  std::cout << "Invalid block: ";
70  auto result{state.GetBlockValidationResult()};
71  switch (result) {
73  std::cout << "initial value. Block has not yet been rejected" << std::endl;
74  break;
76  std::cout << "the block header may be on a too-little-work chain" << std::endl;
77  break;
79  std::cout << "invalid by consensus rules" << std::endl;
80  break;
82  std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
83  break;
85  std::cout << "invalid proof of work or time too old" << std::endl;
86  break;
88  std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
89  break;
91  std::cout << "We don't have the previous block the checked one is built on" << std::endl;
92  break;
94  std::cout << "A block this one builds on is invalid" << std::endl;
95  break;
97  std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
98  break;
99  }
100  return;
101  }
103  std::cout << "Internal error" << std::endl;
104  return;
105  }
106  }
107  }
108 };
109 
111 {
112 public:
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 }
void SetWorkerThreads(int worker_threads)
void BlockChecked(Block block, BlockValidationStateView state) override
ValidationMode GetValidationMode() const
void WarningUnsetHandler(Warning warning) override
int main(int argc, char *argv[])
void WarningSetHandler(Warning warning, std::string_view message) override
void LogMessage(std::string_view message)
int log_timestamps
Prepend a timestamp to log messages.
std::vector< std::byte > hex_string_to_byte_vec(std::string_view hex)
void logging_set_options(const btck_LoggingOptions &logging_options)
void BlockTipHandler(SynchronizationState, const BlockTreeEntry, double) override
auto result
Definition: common-types.h:74
void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) override
BlockValidationResult GetBlockValidationResult() const
void FatalErrorHandler(std::string_view error) override
void FlushErrorHandler(std::string_view error) override
Options controlling the format of log messages.