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