Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ CMakeUserPresets.json
build-asan/
test_runner/
doc/release-notes/*.backup
debug.log
error.log
*.mdb
bchn/
benchmark_results.txt
4 changes: 2 additions & 2 deletions conan.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "0.5",
"requires": [
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1765381907.696",
"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1765284699.337",
"tiny-aes-c/1.0.0#0f0a930c53a0d33d5a91a4c930f836ab%1750950589.092",
"spdlog/1.16.0#942c2c39562ae25ba575d9c8e2bdf3b6%1760375663.769",
"simdutf/7.1.0#31a29f1641af1eec7fc18d86620477ad%1762802019.944",
Expand All @@ -20,7 +20,7 @@
"build_requires": [
"secp256k1-precompute/1.0.0#53841449a185e25ee2337aa5e4f3583f%1751564430.278",
"m4/1.4.19#b38ced39a01e31fef5435bc634461fd2%1700758725.451",
"cmake/4.2.1#951586eff155256a7b5298631d6b1416%1765387599.255",
"cmake/4.2.0#ae0a44f44a1ef9ab68fd4b3e9a1f8671%1764093466.37",
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1763665505.054",
"b2/5.3.3#107c15377719889654eb9a162a673975%1750340310.079"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ struct KB_API block_chain : safe_chain, fast_chain, noncopyable {

/// Insert a block to the blockchain, height is checked for existence.
/// Reads and reorgs are undefined when chain is gapped.
bool insert(block_const_ptr block, size_t height) override;
// bool insert(block_const_ptr block, size_t height, int) override;
bool insert(block_const_ptr block, size_t height, uint32_t median_time_past) override;

/// Push an unconfirmed transaction to the tx table and index outputs.
void push(transaction_const_ptr tx, dispatcher& dispatch, result_handler handler) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ struct KB_API fast_chain {

#if ! defined(KTH_DB_READONLY)
/// Insert a block to the blockchain, height is checked for existence.
// virtual bool insert(block_const_ptr block, size_t height, int) = 0;
virtual bool insert(block_const_ptr block, size_t height) = 0;
virtual bool insert(block_const_ptr block, size_t height, uint32_t median_time_past) = 0;

/// Push an unconfirmed transaction to the tx table and index outputs.
virtual void push(transaction_const_ptr tx, dispatcher& dispatch,
Expand Down
13 changes: 6 additions & 7 deletions src/blockchain/src/interface/block_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ bool block_chain::get_block_exists_safe(hash_digest const& block_hash) const {
bool block_chain::get_block_hash(hash_digest& out_hash, size_t height) const {
auto const result = database_.internal_db().get_header(height);
if ( ! result.is_valid()) return false;
out_hash = result.hash();
out_hash = domain::chain::hash(result);
return true;
}

Expand Down Expand Up @@ -259,9 +259,8 @@ std::pair<bool, database::internal_database::utxo_pool_t> block_chain::get_utxo_
// ----------------------------------------------------------------------------
#if ! defined(KTH_DB_READONLY)

// bool block_chain::insert(block_const_ptr block, size_t height, int) {
bool block_chain::insert(block_const_ptr block, size_t height) {
return database_.insert(*block, height) == error::success;
bool block_chain::insert(block_const_ptr block, size_t height, uint32_t median_time_past) {
return database_.insert(*block, height, median_time_past) == error::success;
}

void block_chain::push(transaction_const_ptr tx, dispatcher&, result_handler handler) {
Expand Down Expand Up @@ -640,7 +639,7 @@ void block_chain::fetch_locator_block_hashes(get_blocks_const_ptr locator,
}

static auto const id = inventory::type_id::block;
hashes->inventories().emplace_back(id, result.header().hash());
hashes->inventories().emplace_back(id, hash(result.header()));
}

handler(error::success, std::move(hashes));
Expand Down Expand Up @@ -1241,7 +1240,7 @@ void block_chain::fetch_block_hash_timestamp(size_t height, block_hash_time_fetc
return;
}

handler(error::success, result.hash(), result.timestamp(), height);
handler(error::success, hash(result), result.timestamp(), height);

}

Expand Down Expand Up @@ -1406,7 +1405,7 @@ void block_chain::fetch_block_locator(block::indexes const& heights, block_locat
handler(error::not_found, nullptr);
break;
}
hashes.push_back(result.hash());
hashes.push_back(hash(result));
}

handler(error::success, message);
Expand Down
21 changes: 20 additions & 1 deletion src/blockchain/src/pools/branch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <kth/blockchain/pools/branch.hpp>

#include <algorithm>
#include <array>
#include <cstddef>
#include <memory>
#include <numeric>
Expand Down Expand Up @@ -122,9 +123,27 @@ size_t branch::height_at(size_t index) const {
}

// private
// TODO: measure the cost of computing MTP on-the-fly vs storing it in the block.
// Previously: return (*blocks_)[index]->header().validation.median_time_past;
uint32_t branch::median_time_past_at(size_t index) const {
KTH_ASSERT(index < size());
return (*blocks_)[index]->header().validation.median_time_past;
// return (*blocks_)[index]->header().validation.median_time_past;

// Collect timestamps from blocks before index (not including index itself)
std::array<uint32_t, domain::chain::median_time_past_blocks> timestamps{};
size_t count = 0;

// Go backwards from index-1, collecting up to 11 timestamps
for (size_t i = 0; i < domain::chain::median_time_past_blocks && i < index; ++i) {
timestamps[count++] = (*blocks_)[index - 1 - i]->header().timestamp();
}

if (count == 0) {
return 0;
}

std::sort(timestamps.begin(), timestamps.begin() + count);
return timestamps[count / 2];
}

// TODO(legacy): absorb into the main chain for speed and code consolidation.
Expand Down
12 changes: 6 additions & 6 deletions src/database/include/kth/database/data_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ struct KD_API data_base : store, noncopyable {

/// Store a block in the database.
/// Returns store_block_duplicate if a block already exists at height.
code insert(domain::chain::block const& block, size_t height);
code insert(domain::chain::block const& block, size_t height, uint32_t median_time_past);

/// Add an unconfirmed tx to the store (without indexing).
/// Returns unspent_duplicate if existing unspent hash duplicate exists.
code push(domain::chain::transaction const& tx, uint32_t forks);

/// Returns store_block_missing_parent if not linked.
/// Returns store_block_invalid_height if height is not the current top + 1.
code push(domain::chain::block const& block, size_t height);
code push(domain::chain::block const& block, size_t height, uint32_t median_time_past);

code prune_reorg();

Expand All @@ -83,7 +83,7 @@ struct KD_API data_base : store, noncopyable {
// ------------------------------------------------------------------------

/// Invoke pop_all and then push_all under a common lock.
void reorganize(infrastructure::config::checkpoint const& fork_point, block_const_ptr_list_const_ptr incoming_blocks, block_const_ptr_list_ptr outgoing_blocks, dispatcher& dispatch, result_handler handler);
void reorganize(infrastructure::config::checkpoint const& fork_point, block_const_ptr_list_const_ptr incoming_blocks, std::vector<uint32_t> const& median_time_pasts, block_const_ptr_list_ptr outgoing_blocks, dispatcher& dispatch, result_handler handler);
#endif // ! defined(KTH_DB_READONLY)

protected:
Expand All @@ -92,7 +92,7 @@ struct KD_API data_base : store, noncopyable {
#if ! defined(KTH_DB_READONLY)

// Sets error if first_height is not the current top + 1 or not linked.
void push_all(block_const_ptr_list_const_ptr in_blocks, size_t first_height, dispatcher& dispatch, result_handler handler);
void push_all(block_const_ptr_list_const_ptr in_blocks, std::vector<uint32_t> const& median_time_pasts, size_t first_height, dispatcher& dispatch, result_handler handler);

// Pop the set of blocks above the given hash.
// Sets error if the database is corrupt or the hash doesn't exist.
Expand Down Expand Up @@ -124,11 +124,11 @@ struct KD_API data_base : store, noncopyable {
// Asynchronous writers.
// ------------------------------------------------------------------------
#if ! defined(KTH_DB_READONLY)
void push_next(code const& ec, block_const_ptr_list_const_ptr blocks, size_t index, size_t height, dispatcher& dispatch, result_handler handler);
void push_next(code const& ec, block_const_ptr_list_const_ptr blocks, std::vector<uint32_t> const& median_time_pasts, size_t index, size_t height, dispatcher& dispatch, result_handler handler);
void do_push(block_const_ptr block, size_t height, uint32_t median_time_past, dispatcher& dispatch, result_handler handler);


void handle_pop(code const& ec, block_const_ptr_list_const_ptr incoming_blocks, size_t first_height, dispatcher& dispatch, result_handler handler);
void handle_pop(code const& ec, block_const_ptr_list_const_ptr incoming_blocks, std::vector<uint32_t> const& median_time_pasts, size_t first_height, dispatcher& dispatch, result_handler handler);
void handle_push(code const& ec, result_handler handler) const;
#endif // ! defined(KTH_DB_READONLY)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ result_code internal_database_basis<Clock>::push_genesis(domain::chain::block co
auto const& txs = block.transactions();
auto const& coinbase = txs.front();
auto const& hash = coinbase.hash();
auto const median_time_past = block.header().validation.median_time_past;
constexpr uint32_t median_time_past = 0u; // Genesis block has no previous blocks, so MTP is 0.

res = insert_transaction(tx_count, coinbase, 0, median_time_past, 0, db_txn);
if (res != result_code::success && res != result_code::duplicated_key) {
Expand Down
29 changes: 12 additions & 17 deletions src/database/src/data_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ uint32_t get_next_height(internal_database const& db) {

static inline
hash_digest get_previous_hash(internal_database const& db, size_t height) {
return height == 0 ? null_hash : db.get_header(height - 1).hash();
return height == 0 ? null_hash : domain::chain::hash(db.get_header(height - 1));
}

//TODO(fernando): const?
Expand Down Expand Up @@ -175,10 +175,7 @@ code data_base::verify_push(block const& block, size_t height) const {

// Add block to the database at the given height (gaps allowed/created).
// This is designed for write concurrency but only with itself.
code data_base::insert(domain::chain::block const& block, size_t height) {

auto const median_time_past = block.header().validation.median_time_past;

code data_base::insert(domain::chain::block const& block, size_t height, uint32_t median_time_past) {
auto const ec = verify_insert(block, height);

if (ec) return ec;
Expand Down Expand Up @@ -210,8 +207,7 @@ code data_base::push(domain::chain::transaction const& tx, uint32_t forks) {
#if ! defined(KTH_DB_READONLY)
// Add a block in order (creates no gaps, must be at top).
// This is designed for write exclusivity and read concurrency.
code data_base::push(block const& block, size_t height) {
auto const median_time_past = block.header().validation.median_time_past;
code data_base::push(block const& block, size_t height, uint32_t median_time_past) {
auto res = internal_db_->push_block(block, height, median_time_past);
if ( ! succeed(res)) {
return error::database_push_failed; //TODO(fernando): create a new operation_failed
Expand Down Expand Up @@ -291,28 +287,27 @@ bool data_base::pop_outputs(const output::list& outputs, size_t height) {
// Add a list of blocks in order.
// If the dispatch threadpool is shut down when this is running the handler
// will never be invoked, resulting in a threadpool.join indefinite hang.
void data_base::push_all(block_const_ptr_list_const_ptr in_blocks, size_t first_height, dispatcher& dispatch, result_handler handler) {
void data_base::push_all(block_const_ptr_list_const_ptr in_blocks, std::vector<uint32_t> const& median_time_pasts, size_t first_height, dispatcher& dispatch, result_handler handler) {
DEBUG_ONLY(*safe_add(in_blocks->size(), first_height));

// This is the beginning of the push_all sequence.
push_next(error::success, in_blocks, 0, first_height, dispatch, handler);
push_next(error::success, in_blocks, median_time_pasts, 0, first_height, dispatch, handler);
}

// TODO(legacy): resolve inconsistency with height and median_time_past passing.
void data_base::push_next(code const& ec, block_const_ptr_list_const_ptr blocks, size_t index, size_t height, dispatcher& dispatch, result_handler handler) {
void data_base::push_next(code const& ec, block_const_ptr_list_const_ptr blocks, std::vector<uint32_t> const& median_time_pasts, size_t index, size_t height, dispatcher& dispatch, result_handler handler) {
if (ec || index >= blocks->size()) {
// This ends the loop.
handler(ec);
return;
}

auto const block = (*blocks)[index];
auto const median_time_past = block->header().validation.median_time_past;
auto const median_time_past = median_time_pasts[index];

// Set push start time for the block.
block->validation.start_push = asio::steady_clock::now();

result_handler const next = std::bind(&data_base::push_next, this, _1, blocks, index + 1, height + 1, std::ref(dispatch), handler);
result_handler const next = std::bind(&data_base::push_next, this, _1, blocks, std::cref(median_time_pasts), index + 1, height + 1, std::ref(dispatch), handler);

// This is the beginning of the block sub-sequence.
dispatch.concurrent(&data_base::do_push, this, block, height, median_time_past, std::ref(dispatch), next);
Expand Down Expand Up @@ -389,23 +384,23 @@ code data_base::prune_reorg() {

#if ! defined(KTH_DB_READONLY)
// This is designed for write exclusivity and read concurrency.
void data_base::reorganize(infrastructure::config::checkpoint const& fork_point, block_const_ptr_list_const_ptr incoming_blocks, block_const_ptr_list_ptr outgoing_blocks, dispatcher& dispatch, result_handler handler) {
void data_base::reorganize(infrastructure::config::checkpoint const& fork_point, block_const_ptr_list_const_ptr incoming_blocks, std::vector<uint32_t> const& median_time_pasts, block_const_ptr_list_ptr outgoing_blocks, dispatcher& dispatch, result_handler handler) {
auto const next_height = *safe_add(fork_point.height(), size_t(1));
// TODO: remove std::bind, use lambda instead.
// TOOD: Even better use C++20 coroutines.
result_handler const pop_handler = std::bind(&data_base::handle_pop, this, _1, incoming_blocks, next_height, std::ref(dispatch), handler);
result_handler const pop_handler = std::bind(&data_base::handle_pop, this, _1, incoming_blocks, std::cref(median_time_pasts), next_height, std::ref(dispatch), handler);
pop_above(outgoing_blocks, fork_point.hash(), dispatch, pop_handler);
}

void data_base::handle_pop(code const& ec, block_const_ptr_list_const_ptr incoming_blocks, size_t first_height, dispatcher& dispatch, result_handler handler) {
void data_base::handle_pop(code const& ec, block_const_ptr_list_const_ptr incoming_blocks, std::vector<uint32_t> const& median_time_pasts, size_t first_height, dispatcher& dispatch, result_handler handler) {
result_handler const push_handler = std::bind(&data_base::handle_push, this, _1, handler);

if (ec) {
push_handler(ec);
return;
}

push_all(incoming_blocks, first_height, std::ref(dispatch), push_handler);
push_all(incoming_blocks, median_time_pasts, first_height, std::ref(dispatch), push_handler);
}

// We never invoke the caller's handler under the mutex, we never fail to clear
Expand Down
6 changes: 3 additions & 3 deletions src/database/test/internal_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,13 +652,13 @@ TEST_CASE("internal database insert genesis", "[None]") {
REQUIRE(db.push_block(genesis, 0, 1) == result_code::success);

REQUIRE(db.get_header(genesis.hash()).first.is_valid());
REQUIRE(db.get_header(genesis.hash()).first.hash() == genesis.hash());
REQUIRE(chain::hash(db.get_header(genesis.hash()).first) == genesis.hash());
REQUIRE(db.get_header(genesis.hash()).second == 0);
REQUIRE(db.get_header(0).is_valid());
REQUIRE(db.get_header(0).hash() == genesis.hash());
REQUIRE(chain::hash(db.get_header(0)) == genesis.hash());


REQUIRE(db.get_block(0).header().hash() == genesis.hash());
REQUIRE(chain::hash(db.get_block(0).header()) == genesis.hash());

hash_digest txid;
std::string txid_enc = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b";
Expand Down
33 changes: 29 additions & 4 deletions src/domain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ set(kth_sources_just_legacy
src/chain/block.cpp
src/chain/chain_state.cpp
src/chain/compact.cpp
src/chain/header_basis.cpp
src/chain/header.cpp
src/chain/header_raw.cpp
src/chain/header_members.cpp
src/chain/input_basis.cpp
src/chain/input.cpp
src/chain/output_basis.cpp
Expand Down Expand Up @@ -339,7 +339,7 @@ set(kth_headers
include/kth/domain/concepts.hpp
include/kth/domain/chain/points_value.hpp
include/kth/domain/chain/chain_state.hpp
include/kth/domain/chain/header_basis.hpp
include/kth/domain/chain/header_raw.hpp
include/kth/domain/chain/block_basis.hpp
include/kth/domain/chain/input_point.hpp
include/kth/domain/chain/input_basis.hpp
Expand All @@ -349,12 +349,13 @@ set(kth_headers
include/kth/domain/chain/token_data.hpp
include/kth/domain/chain/token_data_serialization.hpp
include/kth/domain/chain/output_point.hpp
include/kth/domain/chain/hash_memoizer.hpp
# include/kth/domain/chain/hash_memoizer.hpp # No longer used
include/kth/domain/chain/script_basis.hpp
include/kth/domain/chain/transaction_basis.hpp
include/kth/domain/chain/stealth.hpp
include/kth/domain/chain/point_iterator.hpp
include/kth/domain/chain/header.hpp
include/kth/domain/chain/header_members.hpp
include/kth/domain/chain/history.hpp
include/kth/domain/chain/compact.hpp
include/kth/domain/chain/input.hpp
Expand Down Expand Up @@ -618,6 +619,30 @@ if (ENABLE_TEST AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
endif()
endif()

# Benchmarks
# ------------------------------------------------------------------------------
if (ENABLE_TEST AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
find_package(nanobench REQUIRED)

# Benchmark with header_raw implementation (default, array-based)
add_executable(kth_domain_benchmarks_header_raw
test/chain/header_benchmarks.cpp
)
target_include_directories(kth_domain_benchmarks_header_raw PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/test>)
target_link_libraries(kth_domain_benchmarks_header_raw PRIVATE ${PROJECT_NAME})
target_link_libraries(kth_domain_benchmarks_header_raw PRIVATE nanobench::nanobench)
# No define needed - header_raw is the default

# Benchmark with header_members implementation (member-based)
add_executable(kth_domain_benchmarks_header_members
test/chain/header_benchmarks.cpp
)
target_include_directories(kth_domain_benchmarks_header_members PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/test>)
target_link_libraries(kth_domain_benchmarks_header_members PRIVATE ${PROJECT_NAME})
target_link_libraries(kth_domain_benchmarks_header_members PRIVATE nanobench::nanobench)
target_compile_definitions(kth_domain_benchmarks_header_members PRIVATE KTH_USE_HEADER_MEMBERS)
endif()

# Examples
# ------------------------------------------------------------------------------
if (WITH_EXAMPLES)
Expand Down
Loading
Loading