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 }