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