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 }