diff --git a/Makefile.am b/Makefile.am
index 305844e41..74c63bc39 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -97,6 +97,7 @@ test_libbitcoin_database_test_SOURCES = \
test/query/network.cpp \
test/query/objects.cpp \
test/query/optional.cpp \
+ test/query/services.cpp \
test/query/translate.cpp \
test/query/validate.cpp \
test/tables/archives/header.cpp \
@@ -187,6 +188,7 @@ include_bitcoin_database_impl_query_HEADERS = \
include/bitcoin/database/impl/query/objects.ipp \
include/bitcoin/database/impl/query/optional.ipp \
include/bitcoin/database/impl/query/query.ipp \
+ include/bitcoin/database/impl/query/services.ipp \
include/bitcoin/database/impl/query/translate.ipp \
include/bitcoin/database/impl/query/validate.ipp
diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt
index 0bbf66263..84c6c5886 100644
--- a/builds/cmake/CMakeLists.txt
+++ b/builds/cmake/CMakeLists.txt
@@ -307,6 +307,7 @@ if (with-tests)
"../../test/query/network.cpp"
"../../test/query/objects.cpp"
"../../test/query/optional.cpp"
+ "../../test/query/services.cpp"
"../../test/query/translate.cpp"
"../../test/query/validate.cpp"
"../../test/tables/archives/header.cpp"
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
index 68a94d2c2..2f3d71807 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
@@ -160,6 +160,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
index 6c5cc7060..444136b76 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
@@ -141,6 +141,9 @@
src\query
+
+ src\query
+
src\query
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index 101acf5a9..9f0399b7c 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -226,6 +226,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
index 04231aeed..f4c97428c 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -367,6 +367,9 @@
include\bitcoin\database\impl\query
+
+ include\bitcoin\database\impl\query
+
include\bitcoin\database\impl\query
diff --git a/include/bitcoin/database/impl/query/archive_write.ipp b/include/bitcoin/database/impl/query/archive_write.ipp
index 3e1f1cce4..7886372c2 100644
--- a/include/bitcoin/database/impl/query/archive_write.ipp
+++ b/include/bitcoin/database/impl/query/archive_write.ipp
@@ -398,6 +398,9 @@ code CLASS::set_code(const block& block, const header_link& key,
const auto size = block.serialized_size(true);
const auto wire = possible_narrow_cast(size);
+ // Depth is only used for genesis (is_zero(tx_fks[0])).
+ const auto depth = store_.interval_depth();
+
// ========================================================================
const auto scope = store_.get_transactor();
constexpr auto positive = true;
@@ -414,7 +417,8 @@ code CLASS::set_code(const block& block, const header_link& key,
wire,
count,
tx_fks,
- std::move(interval)
+ std::move(interval),
+ depth
}) ? error::success : error::txs_txs_put;
// ========================================================================
}
diff --git a/include/bitcoin/database/impl/query/optional.ipp b/include/bitcoin/database/impl/query/optional.ipp
index 799c69d2f..1092baef1 100644
--- a/include/bitcoin/database/impl/query/optional.ipp
+++ b/include/bitcoin/database/impl/query/optional.ipp
@@ -26,7 +26,7 @@
#include
#include
- // TODO: address table could use point keys to compress the multimap.
+// TODO: address table could use point keys to compress the multimap.
namespace libbitcoin {
namespace database {
@@ -259,151 +259,6 @@ code CLASS::get_confirmed_balance(std::atomic_bool& cancel, uint64_t& balance,
return error::success;
}
-// merkle
-// ----------------------------------------------------------------------------
-
-// protected
-TEMPLATE
-size_t CLASS::interval_span() const NOEXCEPT
-{
- // span of zero (overflow) is disallowed (division by zero).
- // span of one (2^0) caches every block (no optimization, wasted storage).
- // span greater than top height eliminates caching (no optimization).
- const auto span = system::power2(store_.interval_depth());
- return is_zero(span) ? max_size_t : span;
-}
-
-// protected
-TEMPLATE
-CLASS::hash_option CLASS::create_interval(header_link link,
- size_t height) const NOEXCEPT
-{
- // Interval ends at nth block where n is a multiple of span.
- // One is a functional but undesirable case (no optimization).
- const auto span = interval_span();
- BC_ASSERT(!is_zero(span));
-
- // If valid link is provided then empty return implies non-interval height.
- if (link.is_terminal() || !system::is_multiple(add1(height), span))
- return {};
-
- // Generate the leaf nodes for the span.
- hashes leaves(span);
- for (auto& leaf: std::views::reverse(leaves))
- {
- leaf = get_header_key(link);
- link = to_parent(link);
- }
-
- // Generate the merkle root of the interval ending on link header.
- return system::merkle_root(std::move(leaves));
-}
-
-// protected
-TEMPLATE
-CLASS::hash_option CLASS::get_confirmed_interval(size_t height) const NOEXCEPT
-{
- const auto span = interval_span();
- BC_ASSERT(!is_zero(span));
-
- if (!system::is_multiple(add1(height), span))
- return {};
-
- table::txs::get_interval txs{};
- if (!store_.txs.at(to_confirmed(height), txs))
- return {};
-
- return txs.interval;
-}
-
-// static/protected
-TEMPLATE
-void CLASS::merge_merkle(hashes& to, hashes&& from, size_t first) NOEXCEPT
-{
- using namespace system;
- for (const auto& row: block::merkle_branch(first, from.size()))
- {
- BC_ASSERT(row.sibling * add1(row.width) <= from.size());
- const auto it = std::next(from.begin(), row.sibling * row.width);
- const auto mover = std::make_move_iterator(it);
- to.push_back(merkle_root({ mover, std::next(mover, row.width) }));
- }
-}
-
-// protected
-TEMPLATE
-code CLASS::get_merkle_proof(hashes& proof, hashes roots, size_t target,
- size_t waypoint) const NOEXCEPT
-{
- const auto span = interval_span();
- BC_ASSERT(!is_zero(span));
-
- const auto first = (target / span) * span;
- const auto last = std::min(sub1(first + span), waypoint);
- auto other = get_confirmed_hashes(first, add1(last - first));
- if (other.empty())
- return error::merkle_proof;
-
- using namespace system;
- proof.reserve(ceilinged_log2(other.size()) + ceilinged_log2(roots.size()));
- merge_merkle(proof, std::move(other), target % span);
- merge_merkle(proof, std::move(roots), target / span);
- return error::success;
-}
-
-// protected
-TEMPLATE
-code CLASS::get_merkle_tree(hashes& tree, size_t waypoint) const NOEXCEPT
-{
- const auto span = interval_span();
- BC_ASSERT(!is_zero(span));
-
- const auto range = add1(waypoint);
- tree.reserve(system::ceilinged_divide(range, span));
- for (size_t first{}; first < range; first += span)
- {
- const auto last = std::min(sub1(first + span), waypoint);
- const auto size = add1(last - first);
-
- if (size == span)
- {
- auto interval = get_confirmed_interval(last);
- if (!interval.has_value()) return error::merkle_interval;
- tree.push_back(std::move(interval.value()));
- }
- else
- {
- auto confirmed = get_confirmed_hashes(first, size);
- if (confirmed.empty()) return error::merkle_hashes;
- tree.push_back(system::merkle_root(std::move(confirmed)));
- }
- }
-
- return error::success;
-}
-
-TEMPLATE
-code CLASS::get_merkle_root_and_proof(hash_digest& root, hashes& proof,
- size_t target, size_t waypoint) const NOEXCEPT
-{
- if (target > waypoint)
- return error::merkle_arguments;
-
- if (waypoint > get_top_confirmed())
- return error::merkle_not_found;
-
- hashes tree{};
- if (const auto ec = get_merkle_tree(tree, waypoint))
- return ec;
-
- proof.clear();
- if (const auto ec = get_merkle_proof(proof, tree, target, waypoint))
- return ec;
-
- root = system::merkle_root(std::move(tree));
- return {};
-}
-
////TEMPLATE
////bool CLASS::set_address_output(const output& output,
//// const output_link& link) NOEXCEPT
diff --git a/include/bitcoin/database/impl/query/query.ipp b/include/bitcoin/database/impl/query/query.ipp
index 21860d39b..87dae87dd 100644
--- a/include/bitcoin/database/impl/query/query.ipp
+++ b/include/bitcoin/database/impl/query/query.ipp
@@ -74,6 +74,37 @@ CLASS::query(Store& store) NOEXCEPT
{
}
+TEMPLATE
+size_t CLASS::interval_span() const NOEXCEPT
+{
+ if (const auto span = span_.load(std::memory_order_relaxed);
+ is_nonzero(span))
+ return span;
+
+ // initialize_span() never returns zero.
+ span_.store(initialize_span(), std::memory_order_relaxed);
+ return span_;
+}
+
+// protected
+TEMPLATE
+size_t CLASS::interval_depth() const NOEXCEPT
+{
+ table::txs::get_genesis_depth txs{};
+ return store_.txs.at(to_txs(0), txs) ? txs.depth : store_.interval_depth();
+}
+
+// protected
+TEMPLATE
+size_t CLASS::initialize_span() const NOEXCEPT
+{
+ // span of zero (overflow) is disallowed (division by zero).
+ // span of one (2^0) caches every block (no optimization, wasted storage).
+ // span greater than top height eliminates caching (no optimization).
+ const auto span = system::power2(interval_depth());
+ return is_zero(span) ? max_size_t : span;
+}
+
TEMPLATE
code CLASS::get_code() const NOEXCEPT
{
diff --git a/include/bitcoin/database/impl/query/services.ipp b/include/bitcoin/database/impl/query/services.ipp
new file mode 100644
index 000000000..657c3ccf8
--- /dev/null
+++ b/include/bitcoin/database/impl/query/services.ipp
@@ -0,0 +1,168 @@
+/**
+ * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_DATABASE_QUERY_SERVICES_IPP
+#define LIBBITCOIN_DATABASE_QUERY_SERVICES_IPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace libbitcoin {
+namespace database {
+
+// merkle
+// ----------------------------------------------------------------------------
+
+// protected
+TEMPLATE
+CLASS::hash_option CLASS::create_interval(header_link link,
+ size_t height) const NOEXCEPT
+{
+ // Interval ends at nth block where n is a multiple of span.
+ // One is a functional but undesirable case (no optimization).
+ const auto span = interval_span();
+ BC_ASSERT(!is_zero(span));
+
+ // If valid link is provided then empty return implies non-interval height.
+ if (link.is_terminal() || !system::is_multiple(add1(height), span))
+ return {};
+
+ // Generate the leaf nodes for the span.
+ hashes leaves(span);
+ for (auto& leaf: std::views::reverse(leaves))
+ {
+ leaf = get_header_key(link);
+ link = to_parent(link);
+ }
+
+ // Generate the merkle root of the interval ending on link header.
+ return system::merkle_root(std::move(leaves));
+}
+
+// protected
+TEMPLATE
+CLASS::hash_option CLASS::get_confirmed_interval(size_t height) const NOEXCEPT
+{
+ const auto span = interval_span();
+ BC_ASSERT(!is_zero(span));
+
+ if (!system::is_multiple(add1(height), span))
+ return {};
+
+ table::txs::get_interval txs{};
+ if (!store_.txs.at(to_confirmed(height), txs))
+ return {};
+
+ return txs.interval;
+}
+
+// static/protected
+TEMPLATE
+void CLASS::merge_merkle(hashes& to, hashes&& from, size_t first) NOEXCEPT
+{
+ using namespace system;
+ for (const auto& row: block::merkle_branch(first, from.size()))
+ {
+ BC_ASSERT(row.sibling * add1(row.width) <= from.size());
+ const auto it = std::next(from.begin(), row.sibling * row.width);
+ const auto mover = std::make_move_iterator(it);
+ to.push_back(merkle_root({ mover, std::next(mover, row.width) }));
+ }
+}
+
+// protected
+TEMPLATE
+code CLASS::get_merkle_proof(hashes& proof, hashes roots, size_t target,
+ size_t waypoint) const NOEXCEPT
+{
+ const auto span = interval_span();
+ BC_ASSERT(!is_zero(span));
+
+ const auto first = (target / span) * span;
+ const auto last = std::min(sub1(first + span), waypoint);
+ auto other = get_confirmed_hashes(first, add1(last - first));
+ if (other.empty())
+ return error::merkle_proof;
+
+ using namespace system;
+ proof.reserve(ceilinged_log2(other.size()) + ceilinged_log2(roots.size()));
+ merge_merkle(proof, std::move(other), target % span);
+ merge_merkle(proof, std::move(roots), target / span);
+ return error::success;
+}
+
+// protected
+TEMPLATE
+code CLASS::get_merkle_tree(hashes& tree, size_t waypoint) const NOEXCEPT
+{
+ const auto span = interval_span();
+ BC_ASSERT(!is_zero(span));
+
+ const auto range = add1(waypoint);
+ tree.reserve(system::ceilinged_divide(range, span));
+ for (size_t first{}; first < range; first += span)
+ {
+ const auto last = std::min(sub1(first + span), waypoint);
+ const auto size = add1(last - first);
+
+ if (size == span)
+ {
+ auto interval = get_confirmed_interval(last);
+ if (!interval.has_value()) return error::merkle_interval;
+ tree.push_back(std::move(interval.value()));
+ }
+ else
+ {
+ auto confirmed = get_confirmed_hashes(first, size);
+ if (confirmed.empty()) return error::merkle_hashes;
+ tree.push_back(system::merkle_root(std::move(confirmed)));
+ }
+ }
+
+ return error::success;
+}
+
+TEMPLATE
+code CLASS::get_merkle_root_and_proof(hash_digest& root, hashes& proof,
+ size_t target, size_t waypoint) const NOEXCEPT
+{
+ if (target > waypoint)
+ return error::merkle_arguments;
+
+ if (waypoint > get_top_confirmed())
+ return error::merkle_not_found;
+
+ hashes tree{};
+ if (const auto ec = get_merkle_tree(tree, waypoint))
+ return ec;
+
+ proof.clear();
+ if (const auto ec = get_merkle_proof(proof, tree, target, waypoint))
+ return ec;
+
+ root = system::merkle_root(std::move(tree));
+ return {};
+}
+
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/impl/store.ipp b/include/bitcoin/database/impl/store.ipp
index c87a9b7af..90cb01cd9 100644
--- a/include/bitcoin/database/impl/store.ipp
+++ b/include/bitcoin/database/impl/store.ipp
@@ -235,7 +235,9 @@ bool CLASS::turbo() const NOEXCEPT
TEMPLATE
uint8_t CLASS::interval_depth() const NOEXCEPT
{
- return configuration_.interval_depth;
+ // Configuration uses uint16_t because of boost parser bug for single byte.
+ // But 2^255 is sufficient given that interval is limited by chain length.
+ return system::limit(configuration_.interval_depth);
}
TEMPLATE
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index 29839670e..59d1a4bfe 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -210,9 +210,10 @@ class query
size_t output_count(const tx_links& txs) const NOEXCEPT;
two_counts put_counts(const tx_links& txs) const NOEXCEPT;
- /// Optional table state.
+ /// Optional/configured table state.
bool address_enabled() const NOEXCEPT;
bool filter_enabled() const NOEXCEPT;
+ size_t interval_span() const NOEXCEPT;
/// Initialization (natural-keyed).
/// -----------------------------------------------------------------------
@@ -398,6 +399,12 @@ class query
bool populate_without_metadata(const block& block) const NOEXCEPT;
bool populate_without_metadata(const transaction& tx) const NOEXCEPT;
+ /// Services.
+ /// -----------------------------------------------------------------------
+
+ code get_merkle_root_and_proof(hash_digest& root, hashes& proof,
+ size_t target, size_t checkpoint) const NOEXCEPT;
+
/// Archive writes.
/// -----------------------------------------------------------------------
@@ -574,8 +581,6 @@ class query
code get_confirmed_balance(std::atomic_bool& cancel,
uint64_t& balance, const hash_digest& key,
bool turbo=false) const NOEXCEPT;
- code get_merkle_root_and_proof(hash_digest& root, hashes& proof,
- size_t target, size_t checkpoint) const NOEXCEPT;
bool is_filtered_body(const header_link& link) const NOEXCEPT;
bool get_filter_body(filter& out, const header_link& link) const NOEXCEPT;
@@ -718,12 +723,16 @@ class query
outpoints& out, const hash_digest& key,
uint64_t minimum) const NOEXCEPT;
- /// merkle
+ /// services:merkle
/// -----------------------------------------------------------------------
+
static void merge_merkle(hashes& branch, hashes&& hashes,
size_t first) NOEXCEPT;
- size_t interval_span() const NOEXCEPT;
+ // merkle related configuration
+ size_t interval_depth() const NOEXCEPT;
+ size_t initialize_span() const NOEXCEPT;
+
hash_option get_confirmed_interval(size_t height) const NOEXCEPT;
hash_option create_interval(header_link link, size_t height) const NOEXCEPT;
code get_merkle_tree(hashes& roots, size_t waypoint) const NOEXCEPT;
@@ -750,6 +759,7 @@ class query
// These are thread safe.
mutable std::shared_mutex candidate_reorganization_mutex_{};
mutable std::shared_mutex confirmed_reorganization_mutex_{};
+ mutable std::atomic span_{};
Store& store_;
};
@@ -773,6 +783,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
#include
#include
#include
+#include
#include
#include
diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp
index f54615b11..c4c5d523c 100644
--- a/include/bitcoin/database/settings.hpp
+++ b/include/bitcoin/database/settings.hpp
@@ -43,7 +43,7 @@ struct BCD_API settings
bool turbo{ false };
/// Depth of electrum merkle tree interval caching.
- uint8_t interval_depth{ max_uint8 };
+ uint16_t interval_depth{ max_uint8 };
/// Path to the database directory.
std::filesystem::path path{ "bitcoin" };
diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp
index c3dda20d6..b93943df7 100644
--- a/include/bitcoin/database/tables/archives/txs.hpp
+++ b/include/bitcoin/database/tables/archives/txs.hpp
@@ -57,11 +57,17 @@ struct txs
struct slab
: public schema::txs
{
+ inline bool is_genesis() const NOEXCEPT
+ {
+ return !tx_fks.empty() && is_zero(tx_fks.front());
+ }
+
inline link count() const NOEXCEPT
{
return system::possible_narrow_cast(ct::size +
bytes::size + tx::size * tx_fks.size() +
- (interval.has_value() ? schema::hash : zero));
+ (interval.has_value() ? schema::hash : zero) +
+ to_int(is_genesis()));
}
inline bool from_data(reader& source) NOEXCEPT
@@ -76,6 +82,7 @@ struct txs
});
if (get_right(flagged, offset)) interval = source.read_hash();
+ if (is_genesis()) depth = source.read_byte();
BC_ASSERT(!source || source.get_read_position() == count());
return source;
}
@@ -98,6 +105,7 @@ struct txs
});
if (flag) sink.write_bytes(interval.value());
+ if (is_genesis()) sink.write_byte(depth);
BC_ASSERT(!sink || sink.get_write_position() == count());
return sink;
}
@@ -112,17 +120,24 @@ struct txs
bytes::integer wire{};
keys tx_fks{};
hash interval{};
+ uint8_t depth{};
};
// put a contiguous set of tx identifiers.
struct put_group
: public schema::txs
{
+ inline bool is_genesis() const NOEXCEPT
+ {
+ return is_zero(tx_fk);
+ }
+
inline link count() const NOEXCEPT
{
return system::possible_narrow_cast(ct::size +
bytes::size + tx::size * number +
- (interval.has_value() ? schema::hash : zero));
+ (interval.has_value() ? schema::hash : zero) +
+ to_int(is_zero(tx_fk)));
}
inline bool to_data(finalizer& sink) const NOEXCEPT
@@ -140,6 +155,7 @@ struct txs
sink.write_little_endian(fk);
if (flag) sink.write_bytes(interval.value());
+ if (is_genesis()) sink.write_byte(depth);
BC_ASSERT(!sink || sink.get_write_position() == count());
return sink;
}
@@ -148,6 +164,7 @@ struct txs
ct::integer number{};
tx::integer tx_fk{};
hash interval{};
+ uint8_t depth{};
};
struct get_interval
@@ -172,6 +189,31 @@ struct txs
hash interval{};
};
+ // This reader is only applicable to the genesis block.
+ struct get_genesis_depth
+ : public schema::txs
+ {
+ inline link count() const NOEXCEPT
+ {
+ BC_ASSERT(false);
+ return {};
+ }
+
+ // Stored at end since only read once (at startup).
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ const auto number = source.read_little_endian();
+ const auto flagged = source.read_little_endian();
+ const auto skip = tx::size * number +
+ (system::get_right(flagged, offset) ? schema::hash : zero);
+ source.skip_bytes(skip);
+ depth = source.read_byte();
+ return source;
+ }
+
+ uint8_t depth{};
+ };
+
struct get_position
: public schema::txs
{
diff --git a/test/query/archive_write.cpp b/test/query/archive_write.cpp
index 5fa4a9cbb..f3c7955ab 100644
--- a/test/query/archive_write.cpp
+++ b/test/query/archive_write.cpp
@@ -518,7 +518,7 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block__get_block__expected)
"4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
"00"); // witness
const auto genesis_txs_head = system::base16_chunk(
- "0a00000000" // slabs size
+ "0b00000000" // slab size
"0000000000" // pk->
"ffffffffff"
"ffffffffff"
@@ -538,7 +538,8 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block__get_block__expected)
const auto genesis_txs_body = system::base16_chunk(
"010000" // txs count (1)
"1d0100" // txs wire (285)
- "00000000"); // transaction[0]
+ "00000000" // transaction[0]
+ "ff"); // depth (255)
settings settings{};
settings.header_buckets = 8;
@@ -669,7 +670,7 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block_txs__get_block__expected)
"4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
"00"); // witness
const auto genesis_txs_head = system::base16_chunk(
- "0a00000000" // slabs size
+ "0b00000000" // slab size
"0000000000" // pk->
"ffffffffff"
"ffffffffff"
@@ -689,7 +690,8 @@ BOOST_AUTO_TEST_CASE(query_archive_write__set_block_txs__get_block__expected)
const auto genesis_txs_body = system::base16_chunk(
"010000" // txs count (1)
"1d0100" // txs wire (285)
- "00000000"); // transaction[0]
+ "00000000" // transaction[0]
+ "ff"); // depth (255)
settings settings{};
settings.header_buckets = 8;
diff --git a/test/query/extent.cpp b/test/query/extent.cpp
index 36418b655..414da5a19 100644
--- a/test/query/extent.cpp
+++ b/test/query/extent.cpp
@@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected)
BOOST_REQUIRE_EQUAL(query.point_body_size(), schema::point::minrow);
BOOST_REQUIRE_EQUAL(query.ins_body_size(), schema::ins::minrow);
BOOST_REQUIRE_EQUAL(query.outs_body_size(), schema::outs::minrow);
- BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow);
+ BOOST_REQUIRE_EQUAL(query.txs_body_size(), add1(schema::txs::minrow));
BOOST_REQUIRE_EQUAL(query.tx_body_size(), schema::transaction::minrow);
BOOST_REQUIRE_EQUAL(query.candidate_body_size(), schema::height::minrow);
diff --git a/test/query/optional.cpp b/test/query/optional.cpp
index 71ddb04c5..abdb68b59 100644
--- a/test/query/optional.cpp
+++ b/test/query/optional.cpp
@@ -227,458 +227,6 @@ BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_balance__genesis__expected)
BOOST_REQUIRE_EQUAL(out, 5000000000u);
}
-class merkle_accessor
- : public test::query_accessor
-{
-public:
- using base = test::query_accessor;
- using base::base;
- using base::interval_span;
- using base::create_interval;
- using base::get_confirmed_interval;
- using base::merge_merkle;
- using base::get_merkle_proof;
- using base::get_merkle_tree;
- using base::get_merkle_root_and_proof;
-};
-
-// interval_span
-
-BOOST_AUTO_TEST_CASE(query_optional__interval_span__default__max_size_t)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(settings.interval_depth, max_uint8);
- BOOST_REQUIRE_EQUAL(query.interval_span(), max_size_t);
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__interval_span__11__2048)
-{
- settings settings{};
- settings.interval_depth = 11;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(query.interval_span(), 2048u);
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__interval_span__0__1)
-{
- settings settings{};
- settings.interval_depth = 0;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(query.interval_span(), 1u);
-}
-
-// Merkle root of test blocks [0..1]
-constexpr auto root01 = system::base16_hash("abdc2227d02d114b77be15085c1257709252a7a103f9ac0ab3c85d67e12bc0b8");
-
-// Merkle root of test blocks [2..4]
-constexpr auto root02 = system::base16_hash("f2a2a2907abb326726a2d6500fe494f63772a941b414236c302e920bc1aa9caf");
-
-// Merkle root of test blocks [0..4]
-constexpr auto root04 = system::sha256::double_hash(root01, root02);
-
-// create_interval
-
-BOOST_AUTO_TEST_CASE(query_optional__create_interval__depth_0__block_hash)
-{
- settings settings{};
- settings.interval_depth = 0;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
-
- const auto header0 = query.to_header(test::genesis.hash());
- const auto header1 = query.to_header(test::block1.hash());
- const auto header2 = query.to_header(test::block2.hash());
- const auto header3 = query.to_header(test::block3.hash());
- BOOST_REQUIRE(!header0.is_terminal());
- BOOST_REQUIRE(!header1.is_terminal());
- BOOST_REQUIRE(!header2.is_terminal());
- BOOST_REQUIRE(!header3.is_terminal());
- BOOST_REQUIRE(query.create_interval(header0, 0).has_value());
- BOOST_REQUIRE(query.create_interval(header1, 1).has_value());
- BOOST_REQUIRE(query.create_interval(header2, 2).has_value());
- BOOST_REQUIRE(query.create_interval(header3, 3).has_value());
- BOOST_REQUIRE_EQUAL(query.create_interval(header0, 0).value(), test::genesis.hash());
- BOOST_REQUIRE_EQUAL(query.create_interval(header1, 1).value(), test::block1.hash());
- BOOST_REQUIRE_EQUAL(query.create_interval(header2, 2).value(), test::block2.hash());
- BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), test::block3.hash());
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__create_interval__depth_1__expected)
-{
- settings settings{};
- settings.interval_depth = 1;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
-
- const auto header0 = query.to_header(test::genesis.hash());
- const auto header1 = query.to_header(test::block1.hash());
- const auto header2 = query.to_header(test::block2.hash());
- const auto header3 = query.to_header(test::block3.hash());
- BOOST_REQUIRE(!header0.is_terminal());
- BOOST_REQUIRE(!header1.is_terminal());
- BOOST_REQUIRE(!header2.is_terminal());
- BOOST_REQUIRE(!header3.is_terminal());
- BOOST_REQUIRE(!query.create_interval(header0, 0).has_value());
- BOOST_REQUIRE( query.create_interval(header1, 1).has_value());
- BOOST_REQUIRE(!query.create_interval(header2, 2).has_value());
- BOOST_REQUIRE( query.create_interval(header3, 3).has_value());
- BOOST_REQUIRE_EQUAL(query.create_interval(header1, 1).value(), root01);
- BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), root02);
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__create_interval__depth_2__expected)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
-
- const auto header3 = query.to_header(test::block3.hash());
- BOOST_REQUIRE(!header3.is_terminal());
- BOOST_REQUIRE(query.create_interval(header3, 3).has_value());
- BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), root04);
-}
-
-// get_confirmed_interval
-
-BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_interval__not_multiple__no_value)
-{
- settings settings{};
- settings.interval_depth = 3;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(query.interval_span(), system::power2(settings.interval_depth));
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(!query.get_confirmed_interval(0).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(1).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(6).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(7).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(14).has_value());
-}
-
-// Interval is set by create_interval(), integral to set(block).
-BOOST_AUTO_TEST_CASE(query_optional__get_confirmed_interval__multiple__expected_value)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(query.interval_span(), system::power2(settings.interval_depth));
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
- BOOST_REQUIRE(!query.get_confirmed_interval(0).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(1).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(2).has_value());
- BOOST_REQUIRE( query.get_confirmed_interval(3).has_value());
- BOOST_REQUIRE(!query.get_confirmed_interval(4).has_value());
-}
-
-// merge_merkle
-
-BOOST_AUTO_TEST_CASE(query_optional__merge_merkle__empty_from__empty_to)
-{
- hashes to{};
- merkle_accessor::merge_merkle(to, {}, 0);
- BOOST_REQUIRE(to.empty());
-
- merkle_accessor::merge_merkle(to, { system::null_hash }, 0);
- BOOST_REQUIRE(to.empty());
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__push_merkle__two_leaves_target_zero__merges_one_sibling)
-{
- hashes to{};
- hashes from
- {
- test::genesis.hash(),
- test::block1.hash()
- };
-
- merkle_accessor::merge_merkle(to, std::move(from), 0);
- BOOST_REQUIRE_EQUAL(to.size(), 1u);
- BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::block1.hash() }));
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__merge_merkle__three_leaves_target_two__handles_odd_length)
-{
- hashes to{};
- hashes from
- {
- test::genesis.hash(),
- test::block1.hash(),
- test::block2.hash()
- };
-
- merkle_accessor::merge_merkle(to, std::move(from), 2);
- BOOST_REQUIRE_EQUAL(to.size(), one);
- BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__merge_merkle__four_leaves_target_three__merges_two_siblings)
-{
- hashes to{};
- hashes from
- {
- test::genesis.hash(),
- test::block1.hash(),
- test::block2.hash(),
- test::block3.hash()
- };
-
- merkle_accessor::merge_merkle(to, std::move(from), 3);
- BOOST_REQUIRE_EQUAL(to.size(), 2u);
- BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::block2.hash() }));
- BOOST_REQUIRE_EQUAL(to[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
-}
-
-// get_merkle_proof
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_proof__no_confirmed_blocks__error_merkle_proof)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- hashes proof{};
- BOOST_REQUIRE_EQUAL(query.get_merkle_proof(proof, {}, 5u, 10u), error::merkle_proof);
- BOOST_REQUIRE(proof.empty());
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_proof__target_in_first_interval__expected)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
-
- hashes proof{};
- BOOST_REQUIRE_EQUAL(query.get_merkle_proof(proof, {}, 3u, 3u), error::success);
- BOOST_REQUIRE_EQUAL(proof.size(), 2u);
- BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() }));
- BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_proof__multiple_intervals__expected)
-{
- settings settings{};
- settings.interval_depth = 1;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
-
- hashes proof{};
- const hashes roots
- {
- system::merkle_root({ test::genesis.hash(), test::block1.hash() }),
- system::merkle_root({ test::block2.hash(), test::block3.hash() })
- };
- BOOST_REQUIRE_EQUAL(query.get_merkle_proof(proof, roots, 3u, 3u), error::success);
- BOOST_REQUIRE_EQUAL(proof.size(), 2u);
- BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() }));
- BOOST_REQUIRE_EQUAL(proof[1], roots[0]);
-}
-
-// get_merkle_tree
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_tree__waypoint_zero__genesis)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- hashes tree{};
- const auto expected = test::genesis.hash();
- BOOST_REQUIRE_EQUAL(query.get_merkle_tree(tree, 0), error::success);
- BOOST_REQUIRE_EQUAL(tree.size(), 1u);
- BOOST_REQUIRE_EQUAL(tree[0], expected);
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_tree__one_full_interval__expected_root)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
-
- hashes tree{};
- const auto expected = system::merkle_root(
- {
- test::genesis.hash(),
- test::block1.hash(),
- test::block2.hash(),
- test::block3.hash()
- });
- BOOST_REQUIRE_EQUAL(query.get_merkle_tree(tree, 3), error::success);
- BOOST_REQUIRE_EQUAL(tree.size(), 1u);
- BOOST_REQUIRE_EQUAL(tree[0], expected);
-}
-
-// get_merkle_root_and_proof
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_root_and_proof__target_equals_waypoint__success)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
-
- // Final root is the merkle root of the entire tree.
- hashes proof{};
- hash_digest root{};
- const auto expected = system::merkle_root(
- {
- test::genesis.hash(),
- test::block1.hash(),
- test::block2.hash(),
- test::block3.hash()
- });
- BOOST_REQUIRE(!query.get_merkle_root_and_proof(root, proof, 3u, 3u));
- BOOST_REQUIRE_EQUAL(proof.size(), 2u);
- BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() }));
- BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
- BOOST_REQUIRE_EQUAL(root, expected);
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_root_and_proof__target_less_than_waypoint__success)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
- BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
-
- // Deeper path because target is not the rightmost.
- hashes proof{};
- hash_digest root{};
- const auto expected = system::merkle_root(
- {
- test::genesis.hash(),
- test::block1.hash(),
- test::block2.hash(),
- test::block3.hash()
- });
- BOOST_REQUIRE(!query.get_merkle_root_and_proof(root, proof, 1u, 3u));
- BOOST_REQUIRE_EQUAL(root, expected);
- BOOST_REQUIRE_EQUAL(proof.size(), 2u);
- BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::genesis.hash() }));
- BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::block2.hash(), test::block3.hash() }));
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_root_and_proof__target_greater_than_waypoint__error_merkle_arguments)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- hashes proof{};
- hash_digest root{};
- BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 5u, 3u), error::merkle_arguments);
-}
-
-BOOST_AUTO_TEST_CASE(query_optional__get_merkle_root_and_proof__waypoint_beyond_top__error_merkle_not_found)
-{
- settings settings{};
- settings.interval_depth = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- merkle_accessor query{ store };
- BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- hashes proof{};
- hash_digest root{};
- BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 0u, 100u), error::merkle_not_found);
-}
-
////BOOST_AUTO_TEST_CASE(query_optional__set_filter__get_filter_and_head__expected)
////{
//// const auto& filter_head0 = system::null_hash;
diff --git a/test/query/services.cpp b/test/query/services.cpp
new file mode 100644
index 000000000..0f85e974b
--- /dev/null
+++ b/test/query/services.cpp
@@ -0,0 +1,484 @@
+/**
+ * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include "../test.hpp"
+#include "../mocks/blocks.hpp"
+#include "../mocks/chunk_store.hpp"
+
+BOOST_FIXTURE_TEST_SUITE(query_services_tests, test::directory_setup_fixture)
+
+// nop event handler.
+const auto events_handler = [](auto, auto) {};
+
+// Merkle root of test blocks [0..1]
+constexpr auto root01 = system::base16_hash("abdc2227d02d114b77be15085c1257709252a7a103f9ac0ab3c85d67e12bc0b8");
+
+// Merkle root of test blocks [2..4]
+constexpr auto root02 = system::base16_hash("f2a2a2907abb326726a2d6500fe494f63772a941b414236c302e920bc1aa9caf");
+
+// Merkle root of test blocks [0..4]
+constexpr auto root04 = system::sha256::double_hash(root01, root02);
+
+class merkle_accessor
+ : public test::query_accessor
+{
+public:
+ using base = test::query_accessor;
+ using base::base;
+ using base::interval_span;
+ using base::create_interval;
+ using base::get_confirmed_interval;
+ using base::merge_merkle;
+ using base::get_merkle_proof;
+ using base::get_merkle_tree;
+ using base::get_merkle_root_and_proof;
+};
+
+// interval_span
+
+BOOST_AUTO_TEST_CASE(query_services__interval_span__uninitialized__max_size_t)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(settings.interval_depth, max_uint8);
+ BOOST_REQUIRE_EQUAL(query.interval_span(), max_size_t);
+}
+
+BOOST_AUTO_TEST_CASE(query_services__interval_span__11__2048)
+{
+ settings settings{};
+ settings.interval_depth = 11;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE_EQUAL(query.interval_span(), 2048u);
+}
+
+BOOST_AUTO_TEST_CASE(query_services__interval_span__0__1)
+{
+ settings settings{};
+ settings.interval_depth = 0;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE_EQUAL(query.interval_span(), 1u);
+}
+
+// create_interval
+
+BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_0__block_hash)
+{
+ settings settings{};
+ settings.interval_depth = 0;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+
+ const auto header0 = query.to_header(test::genesis.hash());
+ const auto header1 = query.to_header(test::block1.hash());
+ const auto header2 = query.to_header(test::block2.hash());
+ const auto header3 = query.to_header(test::block3.hash());
+ BOOST_REQUIRE(!header0.is_terminal());
+ BOOST_REQUIRE(!header1.is_terminal());
+ BOOST_REQUIRE(!header2.is_terminal());
+ BOOST_REQUIRE(!header3.is_terminal());
+ BOOST_REQUIRE(query.create_interval(header0, 0).has_value());
+ BOOST_REQUIRE(query.create_interval(header1, 1).has_value());
+ BOOST_REQUIRE(query.create_interval(header2, 2).has_value());
+ BOOST_REQUIRE(query.create_interval(header3, 3).has_value());
+ BOOST_REQUIRE_EQUAL(query.create_interval(header0, 0).value(), test::genesis.hash());
+ BOOST_REQUIRE_EQUAL(query.create_interval(header1, 1).value(), test::block1.hash());
+ BOOST_REQUIRE_EQUAL(query.create_interval(header2, 2).value(), test::block2.hash());
+ BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), test::block3.hash());
+}
+
+BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_1__expected)
+{
+ settings settings{};
+ settings.interval_depth = 1;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+
+ const auto header0 = query.to_header(test::genesis.hash());
+ const auto header1 = query.to_header(test::block1.hash());
+ const auto header2 = query.to_header(test::block2.hash());
+ const auto header3 = query.to_header(test::block3.hash());
+ BOOST_REQUIRE(!header0.is_terminal());
+ BOOST_REQUIRE(!header1.is_terminal());
+ BOOST_REQUIRE(!header2.is_terminal());
+ BOOST_REQUIRE(!header3.is_terminal());
+ BOOST_REQUIRE(!query.create_interval(header0, 0).has_value());
+ BOOST_REQUIRE( query.create_interval(header1, 1).has_value());
+ BOOST_REQUIRE(!query.create_interval(header2, 2).has_value());
+ BOOST_REQUIRE( query.create_interval(header3, 3).has_value());
+ BOOST_REQUIRE_EQUAL(query.create_interval(header1, 1).value(), root01);
+ BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), root02);
+}
+
+BOOST_AUTO_TEST_CASE(query_services__create_interval__depth_2__expected)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+
+ const auto header3 = query.to_header(test::block3.hash());
+ BOOST_REQUIRE(!header3.is_terminal());
+ BOOST_REQUIRE(query.create_interval(header3, 3).has_value());
+ BOOST_REQUIRE_EQUAL(query.create_interval(header3, 3).value(), root04);
+}
+
+// get_confirmed_interval
+
+BOOST_AUTO_TEST_CASE(query_services__get_confirmed_interval__not_multiple__no_value)
+{
+ settings settings{};
+ settings.interval_depth = 3;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(query.interval_span(), system::power2(settings.interval_depth));
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(!query.get_confirmed_interval(0).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(1).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(6).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(7).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(14).has_value());
+}
+
+// Interval is set by create_interval(), integral to set(block).
+BOOST_AUTO_TEST_CASE(query_services__get_confirmed_interval__multiple__expected_value)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(query.interval_span(), system::power2(settings.interval_depth));
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
+ BOOST_REQUIRE(!query.get_confirmed_interval(0).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(1).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(2).has_value());
+ BOOST_REQUIRE( query.get_confirmed_interval(3).has_value());
+ BOOST_REQUIRE(!query.get_confirmed_interval(4).has_value());
+}
+
+// merge_merkle
+
+BOOST_AUTO_TEST_CASE(query_services__merge_merkle__empty_from__empty_to)
+{
+ hashes to{};
+ merkle_accessor::merge_merkle(to, {}, 0);
+ BOOST_REQUIRE(to.empty());
+
+ merkle_accessor::merge_merkle(to, { system::null_hash }, 0);
+ BOOST_REQUIRE(to.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_services__push_merkle__two_leaves_target_zero__merges_one_sibling)
+{
+ hashes to{};
+ hashes from
+ {
+ test::genesis.hash(),
+ test::block1.hash()
+ };
+
+ merkle_accessor::merge_merkle(to, std::move(from), 0);
+ BOOST_REQUIRE_EQUAL(to.size(), 1u);
+ BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::block1.hash() }));
+}
+
+BOOST_AUTO_TEST_CASE(query_services__merge_merkle__three_leaves_target_two__handles_odd_length)
+{
+ hashes to{};
+ hashes from
+ {
+ test::genesis.hash(),
+ test::block1.hash(),
+ test::block2.hash()
+ };
+
+ merkle_accessor::merge_merkle(to, std::move(from), 2);
+ BOOST_REQUIRE_EQUAL(to.size(), one);
+ BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
+}
+
+BOOST_AUTO_TEST_CASE(query_services__merge_merkle__four_leaves_target_three__merges_two_siblings)
+{
+ hashes to{};
+ hashes from
+ {
+ test::genesis.hash(),
+ test::block1.hash(),
+ test::block2.hash(),
+ test::block3.hash()
+ };
+
+ merkle_accessor::merge_merkle(to, std::move(from), 3);
+ BOOST_REQUIRE_EQUAL(to.size(), 2u);
+ BOOST_REQUIRE_EQUAL(to[0], system::merkle_root({ test::block2.hash() }));
+ BOOST_REQUIRE_EQUAL(to[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
+}
+
+// get_merkle_proof
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__no_confirmed_blocks__error_merkle_proof)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ hashes proof{};
+ BOOST_REQUIRE_EQUAL(query.get_merkle_proof(proof, {}, 5u, 10u), error::merkle_proof);
+ BOOST_REQUIRE(proof.empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__target_in_first_interval__expected)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
+
+ hashes proof{};
+ BOOST_REQUIRE_EQUAL(query.get_merkle_proof(proof, {}, 3u, 3u), error::success);
+ BOOST_REQUIRE_EQUAL(proof.size(), 2u);
+ BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() }));
+ BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
+}
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_proof__multiple_intervals__expected)
+{
+ settings settings{};
+ settings.interval_depth = 1;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
+
+ hashes proof{};
+ const hashes roots
+ {
+ system::merkle_root({ test::genesis.hash(), test::block1.hash() }),
+ system::merkle_root({ test::block2.hash(), test::block3.hash() })
+ };
+ BOOST_REQUIRE_EQUAL(query.get_merkle_proof(proof, roots, 3u, 3u), error::success);
+ BOOST_REQUIRE_EQUAL(proof.size(), 2u);
+ BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() }));
+ BOOST_REQUIRE_EQUAL(proof[1], roots[0]);
+}
+
+// get_merkle_tree
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__waypoint_zero__genesis)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ hashes tree{};
+ const auto expected = test::genesis.hash();
+ BOOST_REQUIRE_EQUAL(query.get_merkle_tree(tree, 0), error::success);
+ BOOST_REQUIRE_EQUAL(tree.size(), 1u);
+ BOOST_REQUIRE_EQUAL(tree[0], expected);
+}
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_tree__one_full_interval__expected_root)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
+
+ hashes tree{};
+ const auto expected = system::merkle_root(
+ {
+ test::genesis.hash(),
+ test::block1.hash(),
+ test::block2.hash(),
+ test::block3.hash()
+ });
+ BOOST_REQUIRE_EQUAL(query.get_merkle_tree(tree, 3), error::success);
+ BOOST_REQUIRE_EQUAL(tree.size(), 1u);
+ BOOST_REQUIRE_EQUAL(tree[0], expected);
+}
+
+// get_merkle_root_and_proof
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_equals_waypoint__success)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
+
+ // Final root is the merkle root of the entire tree.
+ hashes proof{};
+ hash_digest root{};
+ const auto expected = system::merkle_root(
+ {
+ test::genesis.hash(),
+ test::block1.hash(),
+ test::block2.hash(),
+ test::block3.hash()
+ });
+ BOOST_REQUIRE(!query.get_merkle_root_and_proof(root, proof, 3u, 3u));
+ BOOST_REQUIRE_EQUAL(proof.size(), 2u);
+ BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::block2.hash() }));
+ BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::genesis.hash(), test::block1.hash() }));
+ BOOST_REQUIRE_EQUAL(root, expected);
+}
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_less_than_waypoint__success)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block1.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block2.hash()), false));
+ BOOST_REQUIRE(query.push_confirmed(query.to_header(test::block3.hash()), false));
+
+ // Deeper path because target is not the rightmost.
+ hashes proof{};
+ hash_digest root{};
+ const auto expected = system::merkle_root(
+ {
+ test::genesis.hash(),
+ test::block1.hash(),
+ test::block2.hash(),
+ test::block3.hash()
+ });
+ BOOST_REQUIRE(!query.get_merkle_root_and_proof(root, proof, 1u, 3u));
+ BOOST_REQUIRE_EQUAL(root, expected);
+ BOOST_REQUIRE_EQUAL(proof.size(), 2u);
+ BOOST_REQUIRE_EQUAL(proof[0], system::merkle_root({ test::genesis.hash() }));
+ BOOST_REQUIRE_EQUAL(proof[1], system::merkle_root({ test::block2.hash(), test::block3.hash() }));
+}
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__target_greater_than_waypoint__error_merkle_arguments)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ hashes proof{};
+ hash_digest root{};
+ BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 5u, 3u), error::merkle_arguments);
+}
+
+BOOST_AUTO_TEST_CASE(query_services__get_merkle_root_and_proof__waypoint_beyond_top__error_merkle_not_found)
+{
+ settings settings{};
+ settings.interval_depth = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ merkle_accessor query{ store };
+ BOOST_REQUIRE_EQUAL(store.create(events_handler), error::success);
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ hashes proof{};
+ hash_digest root{};
+ BOOST_REQUIRE_EQUAL(query.get_merkle_root_and_proof(root, proof, 0u, 100u), error::merkle_not_found);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/tables/archives/txs.cpp b/test/tables/archives/txs.cpp
index 377134644..60cea7e63 100644
--- a/test/tables/archives/txs.cpp
+++ b/test/tables/archives/txs.cpp
@@ -21,20 +21,24 @@
BOOST_AUTO_TEST_SUITE(txs_tests)
+// Default configuration doesn't result in intervals set here.
+
using namespace system;
const table::txs::slab slab0
{
{}, // schema::txs [all const static members]
0x000000,
- std::vector
{
- }
+ // tx fk 0 uniquely identifies genesis, resulting in depth storage.
+ 0x00000000_u32
+ },
+ {}, // interval (unused due to default span)
+ 0x42 // depth (genesis only)
};
const table::txs::slab slab1
{
{}, // schema::txs [all const static members]
0x0000ab,
- std::vector
{
0x56341211_u32
}
@@ -43,7 +47,6 @@ const table::txs::slab slab2
{
{}, // schema::txs [all const static members]
0x00a00b,
- std::vector
{
0x56341221_u32,
0x56341222_u32
@@ -53,7 +56,6 @@ const table::txs::slab slab3
{
{}, // schema::txs [all const static members]
0x09000b,
- std::vector
{
0x56341231_u32,
0x56341232_u32,
@@ -62,13 +64,17 @@ const table::txs::slab slab3
};
const data_chunk expected0
{
- // slab0 (count) [0]
- 0x00, 0x00, 0x00,
+ // slab0 (count) [1]
+ 0x01, 0x00, 0x00,
// slab0 (wire) [0x00]
0x00, 0x00, 0x00,
// slab0 (txs)
+ 0x00, 0x00, 0x00, 0x00,
+
+ // depth (genesis)
+ 0x42
};
const data_chunk expected1
{
@@ -79,7 +85,7 @@ const data_chunk expected1
0xab, 0x00, 0x00,
// slab1 (txs)
- 0x11, 0x12, 0x34, 0x56,
+ 0x11, 0x12, 0x34, 0x56
};
const data_chunk expected2
{
@@ -91,7 +97,7 @@ const data_chunk expected2
// slab2
0x21, 0x12, 0x34, 0x56,
- 0x22, 0x12, 0x34, 0x56,
+ 0x22, 0x12, 0x34, 0x56
};
const data_chunk expected3
{
@@ -114,40 +120,40 @@ BOOST_AUTO_TEST_CASE(txs__put__get__expected)
test::chunk_storage head_store{};
test::chunk_storage body_store{};
table::txs instance{ head_store, body_store, 20 };
- BOOST_REQUIRE(instance.create());
- BOOST_REQUIRE(!instance.exists(key));
-
- BOOST_REQUIRE(instance.put(key, slab0));
- BOOST_REQUIRE(instance.exists(key));
- BOOST_REQUIRE(instance.at(key, slab));
- BOOST_REQUIRE(slab == slab0);
- BOOST_REQUIRE(instance.get(instance.at(key), slab));
- BOOST_REQUIRE(slab == slab0);
- BOOST_REQUIRE_EQUAL(body_store.buffer(), build_chunk({ expected0 }));
-
- BOOST_REQUIRE(instance.put(key, slab1));
- BOOST_REQUIRE(instance.exists(key));
- BOOST_REQUIRE(instance.at(key, slab));
- BOOST_REQUIRE(slab == slab1);
- BOOST_REQUIRE(instance.get(instance.at(key), slab));
- BOOST_REQUIRE(slab == slab1);
- BOOST_REQUIRE_EQUAL(body_store.buffer(), build_chunk({ expected0, expected1 }));
-
- BOOST_REQUIRE(instance.put(key, slab2));
- BOOST_REQUIRE(instance.exists(key));
- BOOST_REQUIRE(instance.at(key, slab));
- BOOST_REQUIRE(slab == slab2);
- BOOST_REQUIRE(instance.get(instance.at(key), slab));
- BOOST_REQUIRE(slab == slab2);
- BOOST_REQUIRE_EQUAL(body_store.buffer(), build_chunk({ expected0, expected1, expected2 }));
-
- BOOST_REQUIRE(instance.put(key, slab3));
- BOOST_REQUIRE(instance.exists(key));
- BOOST_REQUIRE(instance.at(key, slab));
- BOOST_REQUIRE(slab == slab3);
- BOOST_REQUIRE(instance.get(instance.at(key), slab));
- BOOST_REQUIRE(slab == slab3);
- BOOST_REQUIRE_EQUAL(body_store.buffer(), build_chunk({ expected0, expected1, expected2, expected3 }));
+ BOOST_CHECK(instance.create());
+ BOOST_CHECK(!instance.exists(key));
+
+ BOOST_CHECK(instance.put(key, slab0));
+ BOOST_CHECK(instance.exists(key));
+ BOOST_CHECK(instance.at(key, slab));
+ BOOST_CHECK(slab == slab0);
+ BOOST_CHECK(instance.get(instance.at(key), slab));
+ BOOST_CHECK(slab == slab0);
+ BOOST_CHECK_EQUAL(body_store.buffer(), build_chunk({ expected0 }));
+
+ BOOST_CHECK(instance.put(key, slab1));
+ BOOST_CHECK(instance.exists(key));
+ BOOST_CHECK(instance.at(key, slab));
+ BOOST_CHECK(slab == slab1);
+ BOOST_CHECK(instance.get(instance.at(key), slab));
+ BOOST_CHECK(slab == slab1);
+ BOOST_CHECK_EQUAL(body_store.buffer(), build_chunk({ expected0, expected1 }));
+
+ BOOST_CHECK(instance.put(key, slab2));
+ BOOST_CHECK(instance.exists(key));
+ BOOST_CHECK(instance.at(key, slab));
+ BOOST_CHECK(slab == slab2);
+ BOOST_CHECK(instance.get(instance.at(key), slab));
+ BOOST_CHECK(slab == slab2);
+ BOOST_CHECK_EQUAL(body_store.buffer(), build_chunk({ expected0, expected1, expected2 }));
+
+ BOOST_CHECK(instance.put(key, slab3));
+ BOOST_CHECK(instance.exists(key));
+ BOOST_CHECK(instance.at(key, slab));
+ BOOST_CHECK(slab == slab3);
+ BOOST_CHECK(instance.get(instance.at(key), slab));
+ BOOST_CHECK(slab == slab3);
+ BOOST_CHECK_EQUAL(body_store.buffer(), build_chunk({ expected0, expected1, expected2, expected3 }));
}
BOOST_AUTO_TEST_SUITE_END()