Skip to content

Commit 1047757

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 c568fdf commit 1047757

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
@@ -144,3 +144,16 @@ install(TARGETS bitcoinkernel
144144

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

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

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_RECENT_CONSENSUS_CHANGE:
60+
std::cout << "Invalid by a change to consensus rules more recent than SegWit." << std::endl;
61+
break;
62+
case kernel_BlockValidationResult::kernel_BLOCK_CACHED_INVALID:
63+
std::cout << "this block was cached as being invalid and we didn't store the reason why" << std::endl;
64+
break;
65+
case kernel_BlockValidationResult::kernel_BLOCK_INVALID_HEADER:
66+
std::cout << "invalid proof of work or time too old" << std::endl;
67+
break;
68+
case kernel_BlockValidationResult::kernel_BLOCK_MUTATED:
69+
std::cout << "the block's data didn't match the data committed to by the PoW" << std::endl;
70+
break;
71+
case kernel_BlockValidationResult::kernel_BLOCK_MISSING_PREV:
72+
std::cout << "We don't have the previous block the checked one is built on" << std::endl;
73+
break;
74+
case kernel_BlockValidationResult::kernel_BLOCK_INVALID_PREV:
75+
std::cout << "A block this one builds on is invalid" << std::endl;
76+
break;
77+
case kernel_BlockValidationResult::kernel_BLOCK_TIME_FUTURE:
78+
std::cout << "block timestamp was > 2 hours in the future (or our clock is bad)" << std::endl;
79+
break;
80+
case kernel_BlockValidationResult::kernel_BLOCK_CHECKPOINT:
81+
std::cout << "the block failed to meet one of our checkpoints" << std::endl;
82+
break;
83+
}
84+
return;
85+
}
86+
case kernel_ValidationMode::kernel_VALIDATION_STATE_ERROR: {
87+
std::cout << "Internal error" << std::endl;
88+
return;
89+
}
90+
}
91+
}
92+
};
93+
94+
class TestKernelNotifications : public KernelNotifications<TestKernelNotifications>
95+
{
96+
public:
97+
void BlockTipHandler(kernel_SynchronizationState state, kernel_BlockIndex* index) override
98+
{
99+
std::cout << "Block tip changed" << std::endl;
100+
}
101+
102+
void ProgressHandler(const char* title, int progress_percent, bool resume_possible) override
103+
{
104+
std::cout << "Made progress: " << title << " " << progress_percent << "%" << std::endl;
105+
}
106+
107+
void WarningSetHandler(kernel_Warning warning, const char* message) override
108+
{
109+
std::cout << message << std::endl;
110+
}
111+
112+
void WarningUnsetHandler(kernel_Warning warning) override
113+
{
114+
std::cout << "Warning unset: " << warning << std::endl;
115+
}
116+
117+
void FlushErrorHandler(const char* error) override
118+
{
119+
std::cout << error << std::endl;
120+
}
121+
122+
void FatalErrorHandler(const char* error) override
123+
{
124+
std::cout << error << std::endl;
125+
}
126+
};
127+
128+
int main(int argc, char* argv[])
129+
{
130+
// SETUP: Argument parsing and handling
131+
if (argc != 2) {
132+
std::cerr
133+
<< "Usage: " << argv[0] << " DATADIR" << std::endl
134+
<< "Display DATADIR information, and process hex-encoded blocks on standard input." << std::endl
135+
<< std::endl
136+
<< "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING ONLY, AND EXPECTED TO" << std::endl
137+
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR ACTUAL DATADIR." << std::endl;
138+
return 1;
139+
}
140+
std::filesystem::path abs_datadir{std::filesystem::absolute(argv[1])};
141+
std::filesystem::create_directories(abs_datadir);
142+
143+
kernel_LoggingOptions logging_options = {
144+
.log_timestamps = true,
145+
.log_time_micros = false,
146+
.log_threadnames = false,
147+
.log_sourcelocations = false,
148+
.always_print_category_levels = true,
149+
};
150+
151+
Logger logger{std::make_unique<KernelLog>(KernelLog{}), logging_options};
152+
153+
ContextOptions options{};
154+
ChainParams params{kernel_ChainType::kernel_CHAIN_TYPE_REGTEST};
155+
options.SetChainParams(params);
156+
157+
TestKernelNotifications notifications{};
158+
options.SetNotifications(notifications);
159+
160+
Context context{options};
161+
assert(context);
162+
163+
ChainstateManagerOptions chainman_opts{context, abs_datadir};
164+
assert(chainman_opts);
165+
BlockManagerOptions blockman_opts{context, abs_datadir / "blocks"};
166+
assert(blockman_opts);
167+
168+
auto chainman{std::make_unique<ChainMan>(context, chainman_opts, blockman_opts)};
169+
assert(chainman);
170+
171+
ChainstateLoadOptions chainstate_load_opts{};
172+
assert(chainman->LoadChainstate(chainstate_load_opts));
173+
174+
std::cout << "Enter the block you want to validate on the next line:" << std::endl;
175+
176+
for (std::string line; std::getline(std::cin, line);) {
177+
if (line.empty()) {
178+
std::cerr << "Empty line found, try again:" << std::endl;
179+
continue;
180+
}
181+
182+
auto raw_block{hex_string_to_char_vec(line)};
183+
auto block = Block{raw_block};
184+
if (!block) {
185+
std::cout << "Failed to parse entered block, try again:" << std::endl;
186+
continue;
187+
}
188+
189+
kernel_ProcessBlockStatus status{kernel_PROCESS_BLOCK_OK};
190+
bool accepted = chainman->ProcessBlock(block, status);
191+
if (accepted) {
192+
std::cout << "Validated block successfully." << std::endl;
193+
} else {
194+
std::cout << "Block was not accepted" << 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)