forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbitcoin-chainstate.cpp
More file actions
221 lines (192 loc) · 7.6 KB
/
Copy pathbitcoin-chainstate.cpp
File metadata and controls
221 lines (192 loc) · 7.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Copyright (c) 2022-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// The bitcoin-chainstate executable serves to surface the dependencies required
// by a program wishing to use Bitcoin Core's consensus engine as it is right
// now.
//
// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
// it may diverge from Bitcoin Core's coding style.
//
// It is part of the libbitcoinkernel project.
#include <kernel/bitcoinkernel_wrapper.h>
#include <cassert>
#include <charconv>
#include <filesystem>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
using namespace btck;
std::vector<std::byte> hex_string_to_byte_vec(std::string_view hex)
{
std::vector<std::byte> bytes;
bytes.reserve(hex.length() / 2);
for (size_t i{0}; i < hex.length(); i += 2) {
uint8_t byte_value;
auto [ptr, ec] = std::from_chars(hex.data() + i, hex.data() + i + 2, byte_value, 16);
if (ec != std::errc{} || ptr != hex.data() + i + 2) {
throw std::invalid_argument("Invalid hex character");
}
bytes.push_back(static_cast<std::byte>(byte_value));
}
return bytes;
}
class KernelLog
{
public:
void LogMessage(std::string_view message)
{
std::cout << "kernel: " << message;
}
};
class TestValidationInterface : public ValidationInterface
{
public:
TestValidationInterface() = default;
std::optional<std::string> m_expected_valid_block = std::nullopt;
void BlockChecked(Block block, BlockValidationStateView state) override
{
auto mode{state.GetValidationMode()};
switch (mode) {
case ValidationMode::VALID: {
std::cout << "Valid block" << std::endl;
return;
}
case ValidationMode::INVALID: {
std::cout << "Invalid block: ";
auto result{state.GetBlockValidationResult()};
switch (result) {
case BlockValidationResult::UNSET:
std::cout << "initial value. Block has not yet been rejected" << std::endl;
break;
case BlockValidationResult::HEADER_LOW_WORK:
std::cout << "the block header may be on a too-little-work chain" << std::endl;
break;
case BlockValidationResult::CONSENSUS:
std::cout << "invalid by consensus rules" << std::endl;
break;
case BlockValidationResult::CACHED_INVALID:
std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
break;
case BlockValidationResult::INVALID_HEADER:
std::cout << "invalid proof of work or time too old" << std::endl;
break;
case BlockValidationResult::MUTATED:
std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
break;
case BlockValidationResult::MISSING_PREV:
std::cout << "We don't have the previous block the checked one is built on" << std::endl;
break;
case BlockValidationResult::INVALID_PREV:
std::cout << "A block this one builds on is invalid" << std::endl;
break;
case BlockValidationResult::TIME_FUTURE:
std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
break;
}
return;
}
case ValidationMode::INTERNAL_ERROR: {
std::cout << "Internal error" << std::endl;
return;
}
}
}
};
class TestKernelNotifications : public KernelNotifications
{
public:
void BlockTipHandler(SynchronizationState, const Chain, double) override
{
std::cout << "Block tip changed" << std::endl;
}
void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) override
{
std::cout << "Made progress: " << title << " " << progress_percent << "%" << std::endl;
}
void WarningSetHandler(Warning warning, std::string_view message) override
{
std::cout << message << std::endl;
}
void WarningUnsetHandler(Warning warning) override
{
std::cout << "Warning unset: " << static_cast<std::underlying_type_t<Warning>>(warning) << std::endl;
}
void FlushErrorHandler(std::string_view error) override
{
std::cout << error << std::endl;
}
void FatalErrorHandler(std::string_view error) override
{
std::cout << error << std::endl;
}
};
int main(int argc, char* argv[])
{
// SETUP: Argument parsing and handling
const bool has_regtest_flag{argc == 3 && std::string(argv[1]) == "-regtest"};
if (argc < 2 || argc > 3 || (argc == 3 && !has_regtest_flag)) {
std::cerr
<< "Usage: " << argv[0] << " [-regtest] DATADIR" << std::endl
<< "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
<< "Uses mainnet parameters by default, regtest with -regtest flag" << std::endl
<< std::endl
<< "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
return 1;
}
std::filesystem::path abs_datadir{std::filesystem::absolute(argv[argc-1])};
std::filesystem::create_directories(abs_datadir);
btck_LoggingOptions logging_options = {
.log_timestamps = true,
.log_time_micros = false,
.log_threadnames = false,
.log_sourcelocations = false,
.always_print_category_levels = true,
};
logging_set_options(logging_options);
Logger logger{std::make_unique<KernelLog>()};
ContextOptions options{};
ChainParams params{has_regtest_flag ? ChainType::REGTEST : ChainType::MAINNET};
options.SetChainParams(params);
options.SetNotifications(std::make_shared<TestKernelNotifications>());
options.SetValidationInterface(std::make_shared<TestValidationInterface>());
Context context{options};
ChainstateManagerOptions chainman_opts{context, abs_datadir.string(), (abs_datadir / "blocks").string()};
chainman_opts.SetWorkerThreads(4);
std::unique_ptr<ChainMan> chainman;
try {
chainman = std::make_unique<ChainMan>(context, chainman_opts);
} catch (std::exception&) {
std::cerr << "Failed to instantiate ChainMan, exiting" << std::endl;
return 1;
}
std::cout << "Enter the block you want to validate on the next line:" << std::endl;
for (std::string line; std::getline(std::cin, line);) {
if (line.empty()) {
std::cerr << "Empty line found, try again:" << std::endl;
continue;
}
auto raw_block{hex_string_to_byte_vec(line)};
std::unique_ptr<Block> block;
try {
block = std::make_unique<Block>(raw_block);
} catch (std::exception&) {
std::cerr << "Block decode failed, try again:" << std::endl;
continue;
}
bool new_block = false;
bool accepted = chainman->ProcessBlock(*block, &new_block);
if (accepted) {
std::cerr << "Block has not yet been rejected" << std::endl;
} else {
std::cerr << "Block was not accepted" << std::endl;
}
if (!new_block) {
std::cerr << "Block is a duplicate" << std::endl;
}
}
}