Skip to content

Commit 4815830

Browse files
committed
kernel: Add pure kernel bitcoin-chainstate
This showcases a re-implementation of bitcoin-chainstate only using the kernel C++ API header.
1 parent bf80d2f commit 4815830

File tree

4 files changed

+212
-0
lines changed

4 files changed

+212
-0
lines changed

src/kernel/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,16 @@ install(TARGETS bitcoinkernel
145145

146146
install(FILES bitcoinkernel.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT Kernel)
147147

148+
add_executable(kernel-bitcoin-chainstate
149+
bitcoin-chainstate.cpp
150+
)
151+
152+
target_link_libraries(kernel-bitcoin-chainstate
153+
PRIVATE
154+
core_interface
155+
bitcoinkernel
156+
)
157+
158+
set_target_properties(kernel-bitcoin-chainstate PROPERTIES
159+
SKIP_BUILD_RPATH OFF
160+
)

src/kernel/bitcoin-chainstate.cpp

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#include <kernel/bitcoinkernel_wrapper.h>
2+
3+
#include <cassert>
4+
#include <filesystem>
5+
#include <iostream>
6+
#include <optional>
7+
#include <sstream>
8+
9+
std::vector<unsigned char> hex_string_to_char_vec(const std::string& hex)
10+
{
11+
std::vector<unsigned char> bytes;
12+
13+
for (size_t i{0}; i < hex.length(); i += 2) {
14+
std::string byteString{hex.substr(i, 2)};
15+
unsigned char byte = (char)std::strtol(byteString.c_str(), nullptr, 16);
16+
bytes.push_back(byte);
17+
}
18+
19+
return bytes;
20+
}
21+
22+
class KernelLog
23+
{
24+
public:
25+
void LogMessage(const char* message)
26+
{
27+
std::cout << "kernel: " << message;
28+
}
29+
};
30+
31+
class TestValidationInterface : public ValidationInterface<TestValidationInterface>
32+
{
33+
public:
34+
TestValidationInterface() : ValidationInterface() {}
35+
36+
std::optional<std::string> m_expected_valid_block = std::nullopt;
37+
38+
void BlockChecked(const UnownedBlock block, const BlockValidationState state) override
39+
{
40+
auto mode{state.ValidationMode()};
41+
switch (mode) {
42+
case kernel_ValidationMode::kernel_VALIDATION_STATE_VALID: {
43+
std::cout << "Valid block" << std::endl;
44+
return;
45+
}
46+
case kernel_ValidationMode::kernel_VALIDATION_STATE_INVALID: {
47+
std::cout << "Invalid block: ";
48+
auto result{state.BlockValidationResult()};
49+
switch (result) {
50+
case kernel_BlockValidationResult::kernel_BLOCK_RESULT_UNSET:
51+
std::cout << "initial value. Block has not yet been rejected" << std::endl;
52+
break;
53+
case kernel_BlockValidationResult::kernel_BLOCK_HEADER_LOW_WORK:
54+
std::cout << "the block header may be on a too-little-work chain" << std::endl;
55+
break;
56+
case kernel_BlockValidationResult::kernel_BLOCK_CONSENSUS:
57+
std::cout << "invalid by consensus rules (excluding any below reasons)" << std::endl;
58+
break;
59+
case kernel_BlockValidationResult::kernel_BLOCK_CACHED_INVALID:
60+
std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
61+
break;
62+
case kernel_BlockValidationResult::kernel_BLOCK_INVALID_HEADER:
63+
std::cout << "invalid proof of work or time too old" << std::endl;
64+
break;
65+
case kernel_BlockValidationResult::kernel_BLOCK_MUTATED:
66+
std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
67+
break;
68+
case kernel_BlockValidationResult::kernel_BLOCK_MISSING_PREV:
69+
std::cout << "We don't have the previous block the checked one is built on" << std::endl;
70+
break;
71+
case kernel_BlockValidationResult::kernel_BLOCK_INVALID_PREV:
72+
std::cout << "A block this one builds on is invalid" << std::endl;
73+
break;
74+
case kernel_BlockValidationResult::kernel_BLOCK_TIME_FUTURE:
75+
std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
76+
break;
77+
case kernel_BlockValidationResult::kernel_BLOCK_CHECKPOINT:
78+
std::cout << "the block failed to meet one of our checkpoints" << std::endl;
79+
break;
80+
}
81+
return;
82+
}
83+
case kernel_ValidationMode::kernel_VALIDATION_STATE_ERROR: {
84+
std::cout << "Internal error" << std::endl;
85+
return;
86+
}
87+
}
88+
}
89+
};
90+
91+
class TestKernelNotifications : public KernelNotifications<TestKernelNotifications>
92+
{
93+
public:
94+
void BlockTipHandler(kernel_SynchronizationState state, kernel_BlockIndex* index) override
95+
{
96+
std::cout << "Block tip changed" << std::endl;
97+
}
98+
99+
void ProgressHandler(const char* title, int progress_percent, bool resume_possible) override
100+
{
101+
std::cout << "Made progress: " << title << " " << progress_percent << "%" << std::endl;
102+
}
103+
104+
void WarningSetHandler(kernel_Warning warning, const char* message) override
105+
{
106+
std::cout << message << std::endl;
107+
}
108+
109+
void WarningUnsetHandler(kernel_Warning warning) override
110+
{
111+
std::cout << "Warning unset: " << warning << std::endl;
112+
}
113+
114+
void FlushErrorHandler(const char* error) override
115+
{
116+
std::cout << error << std::endl;
117+
}
118+
119+
void FatalErrorHandler(const char* error) override
120+
{
121+
std::cout << error << std::endl;
122+
}
123+
};
124+
125+
int main(int argc, char* argv[])
126+
{
127+
// SETUP: Argument parsing and handling
128+
if (argc != 2) {
129+
std::cerr
130+
<< "Usage: " << argv[0] << " DATADIR" << std::endl
131+
<< "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
132+
<< std::endl
133+
<< "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
134+
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
135+
return 1;
136+
}
137+
std::filesystem::path abs_datadir{std::filesystem::absolute(argv[1])};
138+
std::filesystem::create_directories(abs_datadir);
139+
140+
kernel_LoggingOptions logging_options = {
141+
.log_timestamps = true,
142+
.log_time_micros = false,
143+
.log_threadnames = false,
144+
.log_sourcelocations = false,
145+
.always_print_category_levels = true,
146+
};
147+
148+
Logger logger{std::make_unique<KernelLog>(KernelLog{}), logging_options};
149+
150+
ContextOptions options{};
151+
ChainParams params{kernel_ChainType::kernel_CHAIN_TYPE_REGTEST};
152+
options.SetChainParams(params);
153+
154+
TestKernelNotifications notifications{};
155+
options.SetNotifications(notifications);
156+
157+
Context context{options};
158+
assert(context);
159+
160+
ChainstateManagerOptions chainman_opts{context, abs_datadir};
161+
assert(chainman_opts);
162+
BlockManagerOptions blockman_opts{context, abs_datadir / "blocks"};
163+
assert(blockman_opts);
164+
165+
auto chainman{std::make_unique<ChainMan>(context, chainman_opts, blockman_opts)};
166+
assert(chainman);
167+
168+
ChainstateLoadOptions chainstate_load_opts{};
169+
assert(chainman->LoadChainstate(chainstate_load_opts));
170+
171+
std::cout << "Enter the block you want to validate on the next line:" << std::endl;
172+
173+
for (std::string line; std::getline(std::cin, line);) {
174+
if (line.empty()) {
175+
std::cerr << "Empty line found, try again:" << std::endl;
176+
continue;
177+
}
178+
179+
auto raw_block{hex_string_to_char_vec(line)};
180+
auto block = Block{raw_block};
181+
if (!block) {
182+
std::cout << "Failed to parse entered block, try again:" << std::endl;
183+
continue;
184+
}
185+
186+
bool new_block = false;
187+
bool accepted = chainman->ProcessBlock(block, &new_block);
188+
if (accepted) {
189+
std::cout << "Validated block successfully." << std::endl;
190+
} else {
191+
std::cout << "Block was not accepted" << std::endl;
192+
}
193+
if (!new_block) {
194+
std::cout << "Block is a duplicate" << std::endl;
195+
}
196+
}
197+
}

test/lint/lint-locale-dependence.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"src/test/fuzz/locale.cpp:.*setlocale",
4747
"src/test/util_tests.cpp:.*strtoll",
4848
"src/test/kernel/test_kernel.cpp:.*strtol",
49+
"src/kernel/bitcoin-chainstate.cpp:.*strtol",
4950
"src/wallet/bdb.cpp:.*DbEnv::strerror", # False positive
5051
"src/util/syserror.cpp:.*strerror", # Outside this function use `SysErrorString`
5152
]

test/lint/test_runner/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ fn lint_std_filesystem() -> LintResult {
276276
"./src/",
277277
":(exclude)src/util/fs.h",
278278
":(exclude)src/test/kernel/test_kernel.cpp",
279+
":(exclude)src/kernel/bitcoin-chainstate.cpp",
279280
])
280281
.status()
281282
.expect("command error")

0 commit comments

Comments
 (0)