diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am
index 83fffbc66..f631c9629 100644
--- a/builds/gnu/Makefile.am
+++ b/builds/gnu/Makefile.am
@@ -58,6 +58,7 @@ src_libbitcoin_database_la_SOURCES = \
${srcdir}/../../src/memory/mman.cpp \
${srcdir}/../../src/memory/mstage.cpp \
${srcdir}/../../src/memory/utilities.cpp \
+ ${srcdir}/../../src/types/envelope.cpp \
${srcdir}/../../src/types/history.cpp \
${srcdir}/../../src/types/multisig_view.cpp \
${srcdir}/../../src/types/unspent.cpp
@@ -325,6 +326,7 @@ include_bitcoin_database_types_HEADERS = \
${srcdir}/../../include/bitcoin/database/types/associations.hpp \
${srcdir}/../../include/bitcoin/database/types/block_state.hpp \
${srcdir}/../../include/bitcoin/database/types/difference_set.hpp \
+ ${srcdir}/../../include/bitcoin/database/types/envelope.hpp \
${srcdir}/../../include/bitcoin/database/types/fee_rate.hpp \
${srcdir}/../../include/bitcoin/database/types/header_state.hpp \
${srcdir}/../../include/bitcoin/database/types/history.hpp \
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index 4688b3b64..5e9ae2f36 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -136,6 +136,7 @@
$(IntDir)src_memory_utilities.obj
+
@@ -212,6 +213,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
index dbf332312..69fa481f1 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -132,6 +132,9 @@
src
+
+ src\types
+
src\types
@@ -356,6 +359,9 @@
include\bitcoin\database\types
+
+ include\bitcoin\database\types
+
include\bitcoin\database\types
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
index 5aa1ad631..28cc7e71f 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
@@ -136,6 +136,7 @@
$(IntDir)src_memory_utilities.obj
+
@@ -212,6 +213,7 @@
+
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
index dbf332312..69fa481f1 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -132,6 +132,9 @@
src
+
+ src\types
+
src\types
@@ -356,6 +359,9 @@
include\bitcoin\database\types
+
+ include\bitcoin\database\types
+
include\bitcoin\database\types
diff --git a/include/bitcoin/database.hpp b/include/bitcoin/database.hpp
index ed11b0c6e..2b1ac7a74 100644
--- a/include/bitcoin/database.hpp
+++ b/include/bitcoin/database.hpp
@@ -86,6 +86,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/bitcoin/database/impl/query/archive/chain_writer.ipp b/include/bitcoin/database/impl/query/archive/chain_writer.ipp
index 0bca28396..35b24c6d3 100644
--- a/include/bitcoin/database/impl/query/archive/chain_writer.ipp
+++ b/include/bitcoin/database/impl/query/archive/chain_writer.ipp
@@ -391,9 +391,6 @@ code CLASS::set_code(const block& block, const header_link& key,
// Optional hash, only has value on height intervals.
auto interval = create_interval(key, height);
- // Depth is only set by writer for genesis (is_zero(tx_fks[0])).
- const auto depth = store_.interval_depth();
-
using bytes = linkage::integer;
const auto light = possible_narrow_cast(
block.serialized_size(false));
@@ -418,7 +415,7 @@ code CLASS::set_code(const block& block, const header_link& key,
count,
tx_fks,
std::move(interval),
- depth
+ store_.envelope()
}) ? error::success : error::txs_txs_put;
// ========================================================================
}
diff --git a/include/bitcoin/database/impl/query/archive/wire_writer.ipp b/include/bitcoin/database/impl/query/archive/wire_writer.ipp
index b26f7fb08..a6f4fac1f 100644
--- a/include/bitcoin/database/impl/query/archive/wire_writer.ipp
+++ b/include/bitcoin/database/impl/query/archive/wire_writer.ipp
@@ -238,10 +238,6 @@ code CLASS::set_code(const block_view& block, const header_link& key,
// Optional hash, only has value on height intervals.
auto interval = create_interval(key, height);
- // Depth/forks only set by writer for genesis (is_zero(tx_fks[0])).
- const auto depth = store_.interval_depth();
- const auto forks = store_.fork_flags();
-
using bytes = linkage::integer;
const auto count = possible_narrow_cast>(txs);
const auto light = possible_narrow_cast(block.serialized_size(false));
@@ -343,8 +339,7 @@ code CLASS::set_code(const block_view& block, const header_link& key,
count,
tx_fks,
std::move(interval),
- depth,
- forks
+ store_.envelope()
}) ? error::success : error::txs_txs_put;
// ========================================================================
}
diff --git a/include/bitcoin/database/impl/query/initialize.ipp b/include/bitcoin/database/impl/query/initialize.ipp
index ea5b6f993..7c877b633 100644
--- a/include/bitcoin/database/impl/query/initialize.ipp
+++ b/include/bitcoin/database/impl/query/initialize.ipp
@@ -29,12 +29,10 @@ namespace database {
// Initialization (natural-keyed).
// ----------------------------------------------------------------------------
-// server (warning)
TEMPLATE
-uint32_t CLASS::initialized_forks() const NOEXCEPT
+const database::envelope& CLASS::envelope() const NOEXCEPT
{
- table::txs::get_genesis_forks txs{};
- return store_.txs.at(to_txs(0), txs) ? txs.forks : store_.fork_flags();
+ return store_.envelope();
}
// server/dumps
diff --git a/include/bitcoin/database/impl/query/merkle.ipp b/include/bitcoin/database/impl/query/merkle.ipp
index e8afcc625..54c1ad342 100644
--- a/include/bitcoin/database/impl/query/merkle.ipp
+++ b/include/bitcoin/database/impl/query/merkle.ipp
@@ -295,8 +295,7 @@ size_t CLASS::interval_span() const NOEXCEPT
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();
+ return store_.interval_depth();
}
// protected
diff --git a/include/bitcoin/database/impl/query/unspent.ipp b/include/bitcoin/database/impl/query/unspent.ipp
index 4572db3e7..040eaaf93 100644
--- a/include/bitcoin/database/impl/query/unspent.ipp
+++ b/include/bitcoin/database/impl/query/unspent.ipp
@@ -60,7 +60,7 @@ bool CLASS::is_bip30_exception(bool& out,
return true;
// With the rule unconfigured duplicates are unconstrained (no exception).
- if (!script::is_enabled(initialized_forks(), chain::flags::bip30_rule))
+ if (!store_.envelope().forks.bip30)
return true;
const auto key = get_header_key(link);
diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp
index 4eef24afb..05e694ac0 100644
--- a/include/bitcoin/database/impl/store/store.ipp
+++ b/include/bitcoin/database/impl/store/store.ipp
@@ -27,6 +27,7 @@ namespace database {
TEMPLATE
CLASS::store(const settings& config) NOEXCEPT
: configuration_(config),
+ envelope_(config.envelope),
// Archive.
// ------------------------------------------------------------------------
@@ -132,6 +133,7 @@ CLASS::store(const settings& config) NOEXCEPT
filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk.buckets),
filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx.buckets)
{
+ envelope_.set(config);
}
TEMPLATE
@@ -151,13 +153,13 @@ uint8_t CLASS::interval_depth() const NOEXCEPT
{
// 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);
+ return system::limit(envelope_.interval_depth);
}
TEMPLATE
-uint32_t CLASS::fork_flags() const NOEXCEPT
+const database::envelope& CLASS::envelope() const NOEXCEPT
{
- return configuration_.fork_flags;
+ return envelope_;
}
TEMPLATE
diff --git a/include/bitcoin/database/impl/store/store_open.ipp b/include/bitcoin/database/impl/store/store_open.ipp
index 47edfd70d..e7ed5d8f6 100644
--- a/include/bitcoin/database/impl/store/store_open.ipp
+++ b/include/bitcoin/database/impl/store/store_open.ipp
@@ -84,6 +84,19 @@ code CLASS::open(const event_handler& handler) NOEXCEPT
verify(ec, filter_bk, table_t::filter_bk_table);
verify(ec, filter_tx, table_t::filter_tx_table);
+ if (!ec)
+ {
+ // The stored creation envelope governs, configuration is not read.
+ table::txs::get_envelope genesis{};
+ if (!is_zero(txs.body_size()))
+ {
+ if (txs.at(zero, genesis))
+ envelope_ = genesis.envelope;
+ else
+ ec = error::verify_table;
+ }
+ }
+
if (ec)
{
/* code */ unload_close(handler);
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index 503a0208d..9bee6051d 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -103,8 +103,8 @@ class query
/// Count of puts not resulting in table body search to detect duplication.
size_t negative_search_count() const NOEXCEPT;
- /// Forks configured at store creation.
- uint32_t initialized_forks() const NOEXCEPT;
+ /// Settings envelope stored at store creation.
+ const database::envelope& envelope() const NOEXCEPT;
/// Store extent.
/// -----------------------------------------------------------------------
diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp
index 0d5f1117a..c47381c06 100644
--- a/include/bitcoin/database/settings.hpp
+++ b/include/bitcoin/database/settings.hpp
@@ -20,6 +20,7 @@
#define LIBBITCOIN_DATABASE_SETTINGS_HPP
#include
+#include
#include
#include
@@ -66,8 +67,8 @@ struct BCD_API settings
/// Depth of merkle tree interval caching (used at store create only).
uint16_t interval_depth{ max_uint8 };
- /// Fork flags upon store creation (used at store create only).
- uint32_t fork_flags{};
+ /// Creation settings envelope (used at store create only).
+ database::envelope envelope{};
/// Path to the database directory.
std::filesystem::path path{ "bitcoin" };
diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp
index ca4408a5a..888a2388f 100644
--- a/include/bitcoin/database/store.hpp
+++ b/include/bitcoin/database/store.hpp
@@ -95,8 +95,8 @@ class store
/// Depth of electrum merkle tree interval caching upon store creation.
uint8_t interval_depth() const NOEXCEPT;
- /// Fork flags upon store creation.
- uint32_t fork_flags() const NOEXCEPT;
+ /// Settings envelope stored at store creation.
+ const database::envelope& envelope() const NOEXCEPT;
/// Determine if the store is non-empty/initialized.
bool is_dirty() const NOEXCEPT;
@@ -124,6 +124,9 @@ class store
// This is thread safe.
const settings& configuration_;
+ // This is set at create/open and read-only thereafter.
+ database::envelope envelope_{};
+
/// Archives.
/// -----------------------------------------------------------------------
// These are thread safe.
diff --git a/include/bitcoin/database/tables/archives/header.hpp b/include/bitcoin/database/tables/archives/header.hpp
index cc8ad5ed3..1b750d0e3 100644
--- a/include/bitcoin/database/tables/archives/header.hpp
+++ b/include/bitcoin/database/tables/archives/header.hpp
@@ -110,17 +110,7 @@ struct header
return sink;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return ctx == other.ctx
- && milestone == other.milestone
- && parent_fk == other.parent_fk
- && version == other.version
- && timestamp == other.timestamp
- && bits == other.bits
- && nonce == other.nonce
- && merkle_root == other.merkle_root;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
context ctx{};
bool milestone{};
diff --git a/include/bitcoin/database/tables/archives/input.hpp b/include/bitcoin/database/tables/archives/input.hpp
index 214b856db..38f67353a 100644
--- a/include/bitcoin/database/tables/archives/input.hpp
+++ b/include/bitcoin/database/tables/archives/input.hpp
@@ -66,11 +66,7 @@ struct input
return sink;
}
- inline bool operator==(const slab& other) const NOEXCEPT
- {
- return script == other.script
- && witness == other.witness;
- }
+ inline bool operator==(const slab&) const NOEXCEPT = default;
system::chain::script script{};
system::chain::witness witness{};
diff --git a/include/bitcoin/database/tables/archives/ins.hpp b/include/bitcoin/database/tables/archives/ins.hpp
index e0599d675..dbe9c2816 100644
--- a/include/bitcoin/database/tables/archives/ins.hpp
+++ b/include/bitcoin/database/tables/archives/ins.hpp
@@ -66,11 +66,7 @@ struct ins_point
return index == system::chain::point::null_index;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return hash == other.hash
- && index == other.index;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
hash_digest hash{};
ix::integer index{};
@@ -142,12 +138,7 @@ struct ins_sequence
return sink;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return sequence == other.sequence
- && input_fk == other.input_fk
- && parent_fk == other.parent_fk;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
uint32_t sequence{};
in::integer input_fk{};
diff --git a/include/bitcoin/database/tables/archives/output.hpp b/include/bitcoin/database/tables/archives/output.hpp
index e85e68f99..09431420a 100644
--- a/include/bitcoin/database/tables/archives/output.hpp
+++ b/include/bitcoin/database/tables/archives/output.hpp
@@ -70,12 +70,7 @@ struct output
return sink;
}
- inline bool operator==(const slab& other) const NOEXCEPT
- {
- return parent_fk == other.parent_fk
- && value == other.value
- && script == other.script;
- }
+ inline bool operator==(const slab&) const NOEXCEPT = default;
tx::integer parent_fk{};
uint64_t value{};
diff --git a/include/bitcoin/database/tables/archives/outs.hpp b/include/bitcoin/database/tables/archives/outs.hpp
index 5aab67db9..6b3c70abe 100644
--- a/include/bitcoin/database/tables/archives/outs.hpp
+++ b/include/bitcoin/database/tables/archives/outs.hpp
@@ -93,10 +93,7 @@ struct outs_puts
return sink;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return out_fks == other.out_fks;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
output_links out_fks{};
};
diff --git a/include/bitcoin/database/tables/archives/transaction.hpp b/include/bitcoin/database/tables/archives/transaction.hpp
index b9fd9b003..6373b4a5e 100644
--- a/include/bitcoin/database/tables/archives/transaction.hpp
+++ b/include/bitcoin/database/tables/archives/transaction.hpp
@@ -116,18 +116,7 @@ struct transaction
return sink;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return coinbase == other.coinbase
- && light == other.light
- && heavy == other.heavy
- && locktime == other.locktime
- && version == other.version
- && ins_count == other.ins_count
- && outs_count == other.outs_count
- && point_fk == other.point_fk
- && outs_fk == other.outs_fk;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
bool coinbase{};
bytes::integer light{};
diff --git a/include/bitcoin/database/tables/archives/txs.hpp b/include/bitcoin/database/tables/archives/txs.hpp
index 4fb5695dc..2ac8b3677 100644
--- a/include/bitcoin/database/tables/archives/txs.hpp
+++ b/include/bitcoin/database/tables/archives/txs.hpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -84,7 +85,7 @@ struct txs
return system::possible_narrow_cast(
skip_sizes + ct::size + (tx_fks.size() * tx::size) +
(interval.has_value() ? schema::hash : zero) +
- (is_genesis() ? one + flags::size : zero));
+ (is_genesis() ? envelope.serialized_size() : zero));
}
inline bool from_data(reader& source) NOEXCEPT
@@ -105,12 +106,9 @@ struct txs
interval.reset();
if (is_interval(merged)) interval = source.read_hash();
- // depth/forks (genesis only)
+ // envelope (genesis only)
if (is_genesis())
- {
- depth = source.read_byte();
- forks = source.read_little_endian();
- }
+ envelope.from_data(source);
BC_ASSERT(!source || source.get_read_position() == count());
return source;
@@ -137,33 +135,21 @@ struct txs
// interval (when specified)
if (interval.has_value()) sink.write_bytes(interval.value());
- // depth/forks (genesis only)
+ // envelope (genesis only)
if (is_genesis())
- {
- sink.write_byte(depth);
- sink.write_little_endian(forks);
- }
+ envelope.to_data(sink);
BC_ASSERT(!sink || sink.get_write_position() == count());
return sink;
}
- inline bool operator==(const slab& other) const NOEXCEPT
- {
- return light == other.light
- && heavy == other.heavy
- && tx_fks == other.tx_fks
- && interval == other.interval
- && depth == other.depth
- && forks == other.forks;
- }
+ inline bool operator==(const slab&) const NOEXCEPT = default;
bytes::integer light{};
bytes::integer heavy{};
keys tx_fks{};
hash interval{};
- uint8_t depth{};
- flags::integer forks{};
+ database::envelope envelope{};
};
// put a contiguous set of tx identifiers.
@@ -180,7 +166,7 @@ struct txs
return system::possible_narrow_cast(
skip_sizes + ct::size + (number * tx::size) +
(interval.has_value() ? schema::hash : zero) +
- (is_genesis() ? one + flags::size : zero));
+ (is_genesis() ? envelope.serialized_size() : zero));
}
inline bool to_data(finalizer& sink) const NOEXCEPT
@@ -198,12 +184,9 @@ struct txs
// interval (when specified)
if (interval.has_value()) sink.write_bytes(interval.value());
- // depth/forks (genesis only)
+ // envelope (genesis only)
if (is_genesis())
- {
- sink.write_byte(depth);
- sink.write_little_endian(forks);
- }
+ envelope.to_data(sink);
BC_ASSERT(!sink || sink.get_write_position() == count());
return sink;
@@ -214,8 +197,7 @@ struct txs
ct::integer number{};
tx::integer tx_fk{};
hash interval{};
- uint8_t depth{};
- flags::integer forks{};
+ database::envelope envelope{};
};
struct get_interval
@@ -247,39 +229,7 @@ struct txs
};
// 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
- {
- // tx sizes
- const auto merged = source.read_little_endian();
- source.skip_bytes(bytes::size);
-
- // tx fks
- const auto number = source.read_little_endian();
- source.skip_bytes(number * tx::size);
-
- // interval
- source.skip_bytes(is_interval(merged) ? schema::hash : zero);
-
- // depth
- depth = source.read_byte();
- return source;
- }
-
- uint8_t depth{};
- };
-
- // This reader is only applicable to the genesis block.
- struct get_genesis_forks
+ struct get_envelope
: public schema::txs
{
inline link count() const NOEXCEPT
@@ -302,15 +252,11 @@ struct txs
// interval
source.skip_bytes(is_interval(merged) ? schema::hash : zero);
- // depth
- source.skip_byte();
-
- // forks
- forks = source.read_little_endian();
- return source;
+ // envelope
+ return envelope.from_data(source);
}
- flags::integer forks{};
+ database::envelope envelope{};
};
struct get_position
diff --git a/include/bitcoin/database/tables/caches/prevalid.hpp b/include/bitcoin/database/tables/caches/prevalid.hpp
index 0685c9c66..47129e5ea 100644
--- a/include/bitcoin/database/tables/caches/prevalid.hpp
+++ b/include/bitcoin/database/tables/caches/prevalid.hpp
@@ -57,10 +57,7 @@ struct prevalid
return sink;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return header_fk == other.header_fk;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
header::integer header_fk{};
};
diff --git a/include/bitcoin/database/tables/caches/validated_bk.hpp b/include/bitcoin/database/tables/caches/validated_bk.hpp
index 2b07d5dad..7741b8fd5 100644
--- a/include/bitcoin/database/tables/caches/validated_bk.hpp
+++ b/include/bitcoin/database/tables/caches/validated_bk.hpp
@@ -51,10 +51,7 @@ struct validated_bk
return sink;
}
- inline bool operator==(const record& other) const NOEXCEPT
- {
- return code == other.code;
- }
+ inline bool operator==(const record&) const NOEXCEPT = default;
coding::integer code{};
};
diff --git a/include/bitcoin/database/tables/caches/validated_tx.hpp b/include/bitcoin/database/tables/caches/validated_tx.hpp
index 0c321a595..fbf0fe203 100644
--- a/include/bitcoin/database/tables/caches/validated_tx.hpp
+++ b/include/bitcoin/database/tables/caches/validated_tx.hpp
@@ -71,13 +71,7 @@ struct validated_tx
return sink;
}
- inline bool operator==(const slab& other) const NOEXCEPT
- {
- return ctx == other.ctx
- && code == other.code
- && fee == other.fee
- && sigops == other.sigops;
- }
+ inline bool operator==(const slab&) const NOEXCEPT = default;
context ctx{};
coding::integer code{};
diff --git a/include/bitcoin/database/tables/context.hpp b/include/bitcoin/database/tables/context.hpp
index b1f13d703..39eee2be3 100644
--- a/include/bitcoin/database/tables/context.hpp
+++ b/include/bitcoin/database/tables/context.hpp
@@ -52,12 +52,7 @@ struct context
return system::chain::script::is_enabled(flags, rule);
}
- inline bool operator==(const context& other) const NOEXCEPT
- {
- return flags == other.flags
- && height == other.height
- && mtp == other.mtp;
- }
+ inline bool operator==(const context&) const NOEXCEPT = default;
height_t::integer flags{};
flag_t::integer height{};
diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp
index 0bddef3cb..55a6ac5bf 100644
--- a/include/bitcoin/database/tables/schema.hpp
+++ b/include/bitcoin/database/tables/schema.hpp
@@ -99,6 +99,7 @@ struct header
static_assert(minrow == 97u);
static_assert(link::size == 3u);
static_assert(cell == 4u);
+ bool operator==(const header&) const NOEXCEPT = default;
};
// record hashmap
@@ -126,6 +127,7 @@ struct transaction
static_assert(minrow == 64u);
static_assert(link::size == 4u);
static_assert(cell == 4u);
+ bool operator==(const transaction&) const NOEXCEPT = default;
};
// blob
@@ -143,6 +145,7 @@ struct input
static_assert(minsize == 2u);
static_assert(minrow == 2u);
static_assert(link::size == 5u);
+ bool operator==(const input&) const NOEXCEPT = default;
};
// blob
@@ -161,6 +164,7 @@ struct output
static_assert(minsize == 6u);
static_assert(minrow == 6u);
static_assert(link::size == 5u);
+ bool operator==(const output&) const NOEXCEPT = default;
};
// record multimap aggregate (point spine with sequence column)
@@ -180,6 +184,7 @@ struct ins
static_assert(minrow == 39u);
static_assert(link::size == 4u);
static_assert(cell == 8u);
+ bool operator==(const ins&) const NOEXCEPT = default;
};
// ins sequence column
@@ -198,6 +203,7 @@ struct ins_sequence
static_assert(minsize == 13u);
static_assert(minrow == 13u);
static_assert(link::size == 4u);
+ bool operator==(const ins_sequence&) const NOEXCEPT = default;
};
// record multimap spine (optional head, output script hash key)
@@ -217,6 +223,7 @@ struct address
static_assert(minrow == 4u);
static_assert(link::size == 4u);
static_assert(cell == 4u);
+ bool operator==(const address&) const NOEXCEPT = default;
};
// address column (output fk)
@@ -232,6 +239,7 @@ struct outs
static_assert(minsize == 5u);
static_assert(minrow == 5u);
static_assert(link::size == 4u);
+ bool operator==(const outs&) const NOEXCEPT = default;
};
// slab arraymap
@@ -252,6 +260,7 @@ struct txs
static_assert(minsize == 12u);
static_assert(minrow == 12u);
static_assert(link::size == 5u);
+ bool operator==(const txs&) const NOEXCEPT = default;
};
/// Index tables.
@@ -272,6 +281,7 @@ struct height
static_assert(minrow == 3u);
static_assert(link::size == 3u);
static_assert(cell == 4u);
+ bool operator==(const height&) const NOEXCEPT = default;
};
// record hashmap
@@ -369,6 +379,7 @@ struct validated_bk
static_assert(minsize == 1u);
static_assert(minrow == 1u);
static_assert(link::size == 3u);
+ bool operator==(const validated_bk&) const NOEXCEPT = default;
};
// slab modest (sk:4) multimap, with low multiple rate.
@@ -392,6 +403,7 @@ struct validated_tx
static_assert(minsize == 14u);
static_assert(minrow == 23u);
static_assert(link::size == 5u);
+ bool operator==(const validated_tx&) const NOEXCEPT = default;
};
/// Optional tables.
diff --git a/include/bitcoin/database/types/envelope.hpp b/include/bitcoin/database/types/envelope.hpp
new file mode 100644
index 000000000..d02f7b1ff
--- /dev/null
+++ b/include/bitcoin/database/types/envelope.hpp
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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_TYPES_ENVELOPE_HPP
+#define LIBBITCOIN_DATABASE_TYPES_ENVELOPE_HPP
+
+#include
+#include
+
+namespace libbitcoin {
+namespace database {
+
+struct settings;
+
+/// Store creation settings, written at create and read at load. The stored
+/// values govern the store, configuration is consumed only at creation.
+struct BCD_API envelope
+{
+ static constexpr uint8_t current{ 1 };
+
+ envelope() NOEXCEPT = default;
+ envelope(const system::settings& bitcoin,
+ const settings& database) NOEXCEPT;
+
+ void set(const settings& database) NOEXCEPT;
+
+ bool from_data(reader& source) NOEXCEPT;
+ bool to_data(finalizer& sink) const NOEXCEPT;
+ size_t serialized_size() const NOEXCEPT;
+ bool operator==(const envelope& other) const NOEXCEPT = default;
+
+ /// Database settings.
+ uint16_t interval_depth{};
+ uint32_t header_buckets{};
+ uint32_t ins_buckets{};
+ uint32_t outs_buckets{};
+ uint32_t tx_buckets{};
+ uint32_t strong_tx_buckets{};
+ uint32_t duplicate_buckets{};
+ uint32_t validated_tx_buckets{};
+ bool filter{};
+
+ /// System settings.
+ system::forks forks{};
+ uint64_t initial_subsidy_bitcoin{};
+ uint32_t subsidy_interval_blocks{};
+ uint32_t timestamp_limit_seconds{};
+ uint32_t retargeting_factor{};
+ uint32_t retargeting_interval_seconds{};
+ uint32_t block_spacing_seconds{};
+ uint32_t proof_of_work_limit{};
+ uint32_t first_version{};
+ uint32_t bip34_version{};
+ uint32_t bip66_version{};
+ uint32_t bip65_version{};
+ uint32_t bip9_version_bit0{};
+ uint32_t bip9_version_bit1{};
+ uint32_t bip9_version_bit2{};
+ uint32_t bip9_version_base{};
+ uint32_t bip16_activation_time{};
+ size_t bip34_activation_threshold{};
+ size_t bip34_enforcement_threshold{};
+ size_t bip34_activation_sample{};
+ size_t bip90_bip34_height{};
+ size_t bip90_bip65_height{};
+ size_t bip90_bip66_height{};
+ size_t bip30_reactivate_height{};
+ system::chain::checkpoint bip30_deactivate_checkpoint{};
+ system::chain::checkpoint bip9_bit0_active_checkpoint{};
+ system::chain::checkpoint bip9_bit1_active_checkpoint{};
+ system::chain::checkpoint bip9_bit2_active_checkpoint{};
+};
+
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/types/types.hpp b/include/bitcoin/database/types/types.hpp
index feb2ed375..25c32b45e 100644
--- a/include/bitcoin/database/types/types.hpp
+++ b/include/bitcoin/database/types/types.hpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/src/settings.cpp b/src/settings.cpp
index d936fe468..76d7d090d 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -27,6 +27,7 @@ using namespace bc::system;
settings::settings() NOEXCEPT
{
+ envelope.set(*this);
}
settings::settings(chain::selection context) NOEXCEPT
diff --git a/src/types/envelope.cpp b/src/types/envelope.cpp
new file mode 100644
index 000000000..455eb7b5c
--- /dev/null
+++ b/src/types/envelope.cpp
@@ -0,0 +1,259 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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
+
+#include
+#include
+
+namespace libbitcoin {
+namespace database {
+
+using namespace system;
+
+void envelope::set(const settings& database) NOEXCEPT
+{
+ interval_depth = database.interval_depth;
+ header_buckets = database.header.buckets;
+ ins_buckets = database.ins.buckets;
+ outs_buckets = database.outs.buckets;
+ tx_buckets = database.tx.buckets;
+ strong_tx_buckets = database.strong_tx.buckets;
+ duplicate_buckets = database.duplicate.buckets;
+ validated_tx_buckets = database.validated_tx.buckets;
+ filter = to_bool(database.filter_bk.buckets) &&
+ to_bool(database.filter_tx.buckets);
+}
+
+envelope::envelope(const system::settings& bitcoin,
+ const settings& database) NOEXCEPT
+ : forks(bitcoin.forks),
+ initial_subsidy_bitcoin(bitcoin.initial_subsidy_bitcoin),
+ subsidy_interval_blocks(bitcoin.subsidy_interval_blocks),
+ timestamp_limit_seconds(bitcoin.timestamp_limit_seconds),
+ retargeting_factor(bitcoin.retargeting_factor),
+ retargeting_interval_seconds(bitcoin.retargeting_interval_seconds),
+ block_spacing_seconds(bitcoin.block_spacing_seconds),
+ proof_of_work_limit(bitcoin.proof_of_work_limit),
+ first_version(bitcoin.first_version),
+ bip34_version(bitcoin.bip34_version),
+ bip66_version(bitcoin.bip66_version),
+ bip65_version(bitcoin.bip65_version),
+ bip9_version_bit0(bitcoin.bip9_version_bit0),
+ bip9_version_bit1(bitcoin.bip9_version_bit1),
+ bip9_version_bit2(bitcoin.bip9_version_bit2),
+ bip9_version_base(bitcoin.bip9_version_base),
+ bip16_activation_time(bitcoin.bip16_activation_time),
+ bip34_activation_threshold(bitcoin.bip34_activation_threshold),
+ bip34_enforcement_threshold(bitcoin.bip34_enforcement_threshold),
+ bip34_activation_sample(bitcoin.bip34_activation_sample),
+ bip90_bip34_height(bitcoin.bip90_bip34_height),
+ bip90_bip65_height(bitcoin.bip90_bip65_height),
+ bip90_bip66_height(bitcoin.bip90_bip66_height),
+ bip30_reactivate_height(bitcoin.bip30_reactivate_height),
+ bip30_deactivate_checkpoint(bitcoin.bip30_deactivate_checkpoint),
+ bip9_bit0_active_checkpoint(bitcoin.bip9_bit0_active_checkpoint),
+ bip9_bit1_active_checkpoint(bitcoin.bip9_bit1_active_checkpoint),
+ bip9_bit2_active_checkpoint(bitcoin.bip9_bit2_active_checkpoint)
+{
+ set(database);
+}
+
+bool envelope::from_data(reader& source) NOEXCEPT
+{
+ if (source.read_byte() != current)
+ {
+ source.invalidate();
+ return false;
+ }
+
+ interval_depth = source.read_little_endian();
+ header_buckets = source.read_little_endian();
+ ins_buckets = source.read_little_endian();
+ outs_buckets = source.read_little_endian();
+ tx_buckets = source.read_little_endian();
+ strong_tx_buckets = source.read_little_endian();
+ duplicate_buckets = source.read_little_endian();
+ validated_tx_buckets = source.read_little_endian();
+ filter = to_bool(source.read_byte());
+
+ forks.bip16 = to_bool(source.read_byte());
+ forks.bip90 = to_bool(source.read_byte());
+ forks.bip30 = to_bool(source.read_byte());
+ forks.bip30_deactivate = to_bool(source.read_byte());
+ forks.bip30_reactivate = to_bool(source.read_byte());
+ forks.bip42 = to_bool(source.read_byte());
+ forks.bip34 = to_bool(source.read_byte());
+ forks.bip65 = to_bool(source.read_byte());
+ forks.bip66 = to_bool(source.read_byte());
+ forks.bip68 = to_bool(source.read_byte());
+ forks.bip112 = to_bool(source.read_byte());
+ forks.bip113 = to_bool(source.read_byte());
+ forks.bip141 = to_bool(source.read_byte());
+ forks.bip143 = to_bool(source.read_byte());
+ forks.bip147 = to_bool(source.read_byte());
+ forks.bip341 = to_bool(source.read_byte());
+ forks.bip342 = to_bool(source.read_byte());
+ forks.retarget = to_bool(source.read_byte());
+ forks.difficult = to_bool(source.read_byte());
+ forks.time_warp_patch = to_bool(source.read_byte());
+ forks.block_storm_patch = to_bool(source.read_byte());
+ forks.ltc_time_warp_patch = to_bool(source.read_byte());
+ forks.ltc_retarget_overflow_patch = to_bool(source.read_byte());
+ forks.ltc_scrypt_proof_of_work = to_bool(source.read_byte());
+
+ initial_subsidy_bitcoin = source.read_little_endian();
+ subsidy_interval_blocks = source.read_little_endian();
+ timestamp_limit_seconds = source.read_little_endian();
+ retargeting_factor = source.read_little_endian();
+ retargeting_interval_seconds = source.read_little_endian();
+ block_spacing_seconds = source.read_little_endian();
+ proof_of_work_limit = source.read_little_endian();
+ first_version = source.read_little_endian();
+ bip34_version = source.read_little_endian();
+ bip66_version = source.read_little_endian();
+ bip65_version = source.read_little_endian();
+ bip9_version_bit0 = source.read_little_endian();
+ bip9_version_bit1 = source.read_little_endian();
+ bip9_version_bit2 = source.read_little_endian();
+ bip9_version_base = source.read_little_endian();
+ bip16_activation_time = source.read_little_endian();
+ bip34_activation_threshold = source.read_variable();
+ bip34_enforcement_threshold = source.read_variable();
+ bip34_activation_sample = source.read_variable();
+ bip90_bip34_height = source.read_variable();
+ bip90_bip65_height = source.read_variable();
+ bip90_bip66_height = source.read_variable();
+ bip30_reactivate_height = source.read_variable();
+
+ const auto read_checkpoint = [&]() NOEXCEPT
+ {
+ const auto hash = source.read_hash();
+ return chain::checkpoint{ hash, source.read_variable() };
+ };
+
+ bip30_deactivate_checkpoint = read_checkpoint();
+ bip9_bit0_active_checkpoint = read_checkpoint();
+ bip9_bit1_active_checkpoint = read_checkpoint();
+ bip9_bit2_active_checkpoint = read_checkpoint();
+ return source;
+}
+
+bool envelope::to_data(finalizer& sink) const NOEXCEPT
+{
+ sink.write_byte(current);
+
+ sink.write_little_endian(interval_depth);
+ sink.write_little_endian(header_buckets);
+ sink.write_little_endian(ins_buckets);
+ sink.write_little_endian(outs_buckets);
+ sink.write_little_endian(tx_buckets);
+ sink.write_little_endian(strong_tx_buckets);
+ sink.write_little_endian(duplicate_buckets);
+ sink.write_little_endian(validated_tx_buckets);
+ sink.write_byte(to_int(filter));
+
+ sink.write_byte(to_int(forks.bip16));
+ sink.write_byte(to_int(forks.bip90));
+ sink.write_byte(to_int(forks.bip30));
+ sink.write_byte(to_int(forks.bip30_deactivate));
+ sink.write_byte(to_int(forks.bip30_reactivate));
+ sink.write_byte(to_int(forks.bip42));
+ sink.write_byte(to_int(forks.bip34));
+ sink.write_byte(to_int(forks.bip65));
+ sink.write_byte(to_int(forks.bip66));
+ sink.write_byte(to_int(forks.bip68));
+ sink.write_byte(to_int(forks.bip112));
+ sink.write_byte(to_int(forks.bip113));
+ sink.write_byte(to_int(forks.bip141));
+ sink.write_byte(to_int(forks.bip143));
+ sink.write_byte(to_int(forks.bip147));
+ sink.write_byte(to_int(forks.bip341));
+ sink.write_byte(to_int(forks.bip342));
+ sink.write_byte(to_int(forks.retarget));
+ sink.write_byte(to_int(forks.difficult));
+ sink.write_byte(to_int(forks.time_warp_patch));
+ sink.write_byte(to_int(forks.block_storm_patch));
+ sink.write_byte(to_int(forks.ltc_time_warp_patch));
+ sink.write_byte(to_int(forks.ltc_retarget_overflow_patch));
+ sink.write_byte(to_int(forks.ltc_scrypt_proof_of_work));
+
+ sink.write_little_endian(initial_subsidy_bitcoin);
+ sink.write_little_endian(subsidy_interval_blocks);
+ sink.write_little_endian(timestamp_limit_seconds);
+ sink.write_little_endian(retargeting_factor);
+ sink.write_little_endian(retargeting_interval_seconds);
+ sink.write_little_endian(block_spacing_seconds);
+ sink.write_little_endian(proof_of_work_limit);
+ sink.write_little_endian(first_version);
+ sink.write_little_endian(bip34_version);
+ sink.write_little_endian(bip66_version);
+ sink.write_little_endian(bip65_version);
+ sink.write_little_endian(bip9_version_bit0);
+ sink.write_little_endian(bip9_version_bit1);
+ sink.write_little_endian(bip9_version_bit2);
+ sink.write_little_endian(bip9_version_base);
+ sink.write_little_endian(bip16_activation_time);
+ sink.write_variable(bip34_activation_threshold);
+ sink.write_variable(bip34_enforcement_threshold);
+ sink.write_variable(bip34_activation_sample);
+ sink.write_variable(bip90_bip34_height);
+ sink.write_variable(bip90_bip65_height);
+ sink.write_variable(bip90_bip66_height);
+ sink.write_variable(bip30_reactivate_height);
+
+ const auto write_checkpoint = [&](const chain::checkpoint& in) NOEXCEPT
+ {
+ sink.write_bytes(in.hash());
+ sink.write_variable(in.height());
+ };
+
+ write_checkpoint(bip30_deactivate_checkpoint);
+ write_checkpoint(bip9_bit0_active_checkpoint);
+ write_checkpoint(bip9_bit1_active_checkpoint);
+ write_checkpoint(bip9_bit2_active_checkpoint);
+ return sink;
+}
+
+size_t envelope::serialized_size() const NOEXCEPT
+{
+ constexpr auto forks_size = 24_size;
+ constexpr auto fixed = one + sizeof(uint16_t) + (7 * sizeof(uint32_t)) +
+ one + forks_size + sizeof(uint64_t) + (15 * sizeof(uint32_t));
+
+ const auto checkpoint_size = [](const chain::checkpoint& in) NOEXCEPT
+ {
+ return hash_size + variable_size(in.height());
+ };
+
+ return fixed +
+ variable_size(bip34_activation_threshold) +
+ variable_size(bip34_enforcement_threshold) +
+ variable_size(bip34_activation_sample) +
+ variable_size(bip90_bip34_height) +
+ variable_size(bip90_bip65_height) +
+ variable_size(bip90_bip66_height) +
+ variable_size(bip30_reactivate_height) +
+ checkpoint_size(bip30_deactivate_checkpoint) +
+ checkpoint_size(bip9_bit0_active_checkpoint) +
+ checkpoint_size(bip9_bit1_active_checkpoint) +
+ checkpoint_size(bip9_bit2_active_checkpoint);
+}
+
+} // namespace database
+} // namespace libbitcoin
diff --git a/test/query/archive/chain_writer.cpp b/test/query/archive/chain_writer.cpp
index 0711935b2..bd174a4c3 100644
--- a/test/query/archive/chain_writer.cpp
+++ b/test/query/archive/chain_writer.cpp
@@ -1,1547 +1,1571 @@
-/**
- * Copyright (c) 2011-2026 libbitcoin developers
- *
- * 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"
-
-// ensure context::flags is same size as chain_context::flags.
-static_assert(is_same_type < database::context::flag_t::integer, decltype(system::chain::context{}.flags) > );
-
-BOOST_FIXTURE_TEST_SUITE(query_chain_writer_tests, test::directory_setup_fixture)
-
-// slow test (mmap)
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_header__mmap_get_header__expected)
-{
- constexpr auto milestone = false;
- constexpr auto parent = system::null_hash;
- constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
- constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
- const system::chain::header header
- {
- 0x31323334, // version
- parent, // previous_block_hash
- merkle_root,// merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- };
-
- settings settings{};
- settings.header.buckets = 16;
- settings.path = TEST_DIRECTORY;
- store store{ settings };
- query> query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.set(header, test::context, milestone));
-
- table::header::record element1{};
- BOOST_CHECK(store.header.get(query.to_header(block_hash), element1));
-
- const auto pointer = query.get_header(query.to_header(block_hash));
- BOOST_CHECK(pointer);
- BOOST_CHECK(*pointer == header);
-
- // must open/close mmap
- BOOST_CHECK(!store.close(test::events_handler));
- BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits));
- BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags);
- BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp);
- BOOST_CHECK_EQUAL(element1.milestone, milestone);
- BOOST_CHECK_EQUAL(element1.version, header.version());
- BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal);
- BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root());
- BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp());
- BOOST_CHECK_EQUAL(element1.bits, header.bits());
- BOOST_CHECK_EQUAL(element1.nonce, header.nonce());
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_header__is_header__expected)
-{
- constexpr auto milestone = true;
- constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
- constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
- const system::chain::header header
- {
- 0x31323334, // version
- system::null_hash, // previous_block_hash
- merkle_root,// merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- };
-
- // nosh
- const auto expected_header_head = system::base16_chunk(
- "010000ff" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "000080" // filter[8], pk->
- "de" // filter[0-7]
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
-
- const auto expected_header_body = system::base16_chunk(
- "ffff7f" // next->
- "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash)
- "04030201" // flags
- "141312" // height
- "24232221" // mtp
- "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
- "34333231" // version
- "44434241" // timestamp
- "54535251" // bits
- "64636261" // nonce
- "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); //merkle_root
-
- header_link link{};
- settings settings{};
- settings.header.buckets = 16;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- // store open/close flushes record count to head.
- BOOST_CHECK(!query.is_header(header.hash()));
- BOOST_CHECK(!query.is_associated(0));
- BOOST_CHECK(!query.set_code(link, header, test::context, milestone));
- BOOST_CHECK(!link.is_terminal());
- BOOST_CHECK(query.is_header(header.hash()));
- BOOST_CHECK(!query.is_associated(0));
- table::header::record element1{};
- BOOST_CHECK(store.header.get(query.to_header(block_hash), element1));
- BOOST_CHECK(!store.close(test::events_handler));
- BOOST_CHECK_EQUAL(store.header_head(), expected_header_head);
- BOOST_CHECK_EQUAL(store.header_body(), expected_header_body);
-
- BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits));
- BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags);
- BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp);
- BOOST_CHECK_EQUAL(element1.milestone, milestone);
- BOOST_CHECK_EQUAL(element1.version, header.version());
- BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal);
- BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root());
- BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp());
- BOOST_CHECK_EQUAL(element1.bits, header.bits());
- BOOST_CHECK_EQUAL(element1.nonce, header.nonce());
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__empty__expected)
-{
- const system::chain::transaction tx{};
- const auto expected_head5_array = system::base16_chunk("0000000000");
- const auto expected_outs_head = system::base16_chunk(
- "00000000" // record count
- "ffffffff" // bucket[0]
- "ffffffff");// bucket[1]
- const auto expected_head4_hash = system::base16_chunk(
- "01000000" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto expected_head5_hash = system::base16_chunk(
- "0000000000" // record count
- "ffffffffff" // bucket[0]...
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff");
-
- // data_chunk store.
- settings settings{};
- settings.tx.buckets = 8;
- settings.outs.buckets = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- // store open/close flushes record count to heads.
- BOOST_CHECK(!query.set(tx));
- BOOST_CHECK(!store.close(test::events_handler));
- BOOST_CHECK_EQUAL(store.tx_head(), expected_head4_hash);
- BOOST_CHECK_EQUAL(store.input_head(), expected_head5_array);
- BOOST_CHECK_EQUAL(store.output_head(), expected_head5_array);
- BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head);
- BOOST_CHECK_EQUAL(store.tx_body().size(), schema::transaction::minrow);
- BOOST_CHECK(store.ins_body().empty());
- BOOST_CHECK(store.input_body().empty());
- BOOST_CHECK(store.output_body().empty());
- BOOST_CHECK(store.outs_body().empty());
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_tx__null_input__expected)
-{
- using namespace system::chain;
- const transaction tx
- {
- 0x01020304, // version
- inputs
- {
- input{ point{}, script{}, witness{}, 0 }
- },
- outputs
- {
- output{ 0, script{} }
- },
- 0x11121314 // locktime
- };
- const auto expected_tx_head = system::base16_chunk(
- "01000000" // record count
- "00000000" // bucket[0]... pk->
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto expected_tx_body = system::base16_chunk(
- "ffffff7f" // next->
- "601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62" // sk (tx.hash(false))
- "3c0080" // light (coinbase merged)
- "3c0000" // heavy
- "14131211" // locktime
- "04030201" // version
- "010000" // ins_count
- "010000" // outs_count
- "00000000" // point_fk-> (ins_fk)
- "00000000"); // outs_fk->
- const auto expected_outs_head = system::base16_chunk(
- "01000000" // record count
- "00000000" // bucket[0] (link->0)
- "ffffffff");// bucket[1]
- const auto expected_address_body = system::base16_chunk(
- "ffffffff"); // next->end
- const auto expected_outs_body = system::base16_chunk(
- "0000000000"); // output0_fk->
- const auto expected_output_head = system::base16_chunk("0600000000");
- const auto expected_output_body = system::base16_chunk(
- "00000000" // parent_fk->
- "00" // value
- "00"); // script
- const auto expected_ins_body = system::base16_chunk(
- "ffffffff" // next->
- "0000000000000000000000000000000000000000000000000000000000000000"
- "ffffff"); // index
- ////const auto expected_spend_head = system::base16_chunk(
- //// "01000000" // record count
- //// "00000000" // pk->
- //// "ffffffff"
- //// "ffffffff"
- //// "ffffffff"
- //// "ffffffff");
- ////const auto expected_spend_body = system::base16_chunk(
- //// "ffffffff" // terminal->
- //// "00000000" // fp: point_stub
- //// "ffffff" // fp: point_index (null)
- //// "ffffffff" // point_fk->
- //// "00000000" // parent_fk->
- //// "00000000" // sequence
- //// "0000000000"); // input_fk->
- const auto expected_input_head = system::base16_chunk("0200000000");
- const auto expected_input_body = system::base16_chunk(
- "00" // script
- "00"); // witness
- constexpr auto tx_hash = system::base16_array("601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62");
- BOOST_CHECK_EQUAL(tx_hash, tx.hash(false));
-
- // data_chunk store.
- settings settings{};
- settings.tx.buckets = 8;
- settings.ins.buckets = 8;
- settings.outs.buckets = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(!query.set_code(tx));
- BOOST_CHECK(!store.close(test::events_handler));
-
- BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head);
- BOOST_CHECK_EQUAL(store.input_head(), expected_input_head);
- BOOST_CHECK_EQUAL(store.output_head(), expected_output_head);
- BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head);
- ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head);
-
- BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body);
- BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body);
- BOOST_CHECK_EQUAL(store.input_body(), expected_input_body);
- BOOST_CHECK_EQUAL(store.output_body(), expected_output_body);
- BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body);
- BOOST_CHECK_EQUAL(store.address_body(), expected_address_body);
- ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__get_tx__expected)
-{
- using namespace system::chain;
- const transaction tx
- {
- 0x2a, // version
- inputs
- {
- input
- {
- point{ system::one_hash, 0x18 },
- script{ { { opcode::op_return }, { opcode::pick } } },
- witness{ "[242424]" },
- 0x2a // sequence
- },
- input
- {
- point{ system::one_hash, 0x2a },
- script{ { { opcode::op_return }, { opcode::roll } } },
- witness{ "[424242]" },
- 0x18 // sequence
- }
- },
- outputs
- {
- output
- {
- 0x18, // value
- script{ { { opcode::pick } } }
- },
- output
- {
- 0x2a, // value
- script{ { { opcode::roll } } }
- }
- },
- 0x18 // locktime
- };
- const auto expected_tx_head = system::base16_chunk(
- "01000000" // record count
- "00000000" // bucket[0]... pk->
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto expected_tx_body = system::base16_chunk(
- "ffffff7f" // next->
- "d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a" // sk (tx.hash(false))
- "740000" // light (not coinbase)
- "800000" // heavy
- "18000000" // locktime
- "2a000000" // version
- "020000" // ins_count
- "020000" // outs_count
- "00000000" // point_fk-> (ins_fk)
- "00000000"); // outs_fk->
- const auto expected_outs_head = system::base16_chunk(
- "02000000" // record count
- "ffffffff" // bucket[0]
- "01000000");// bucket[1] (link->1)
- const auto expected_address_body = system::base16_chunk(
- "ffffffff" // next->end
- "00000000"); // next->0
- const auto expected_outs_body = system::base16_chunk(
- "0000000000" // output0_fk->
- "0700000000"); // output1_fk->
- const auto expected_output_head = system::base16_chunk("0e00000000");
- const auto expected_output_body = system::base16_chunk(
- "00000000" // parent_fk->
- "18" // value
- "0179" // script
- "00000000" // parent_fk->
- "2a" // value
- "017a"); // script
- const auto expected_ins_body = system::base16_chunk(
- "ffffffff" // next->
- "0100000000000000000000000000000000000000000000000000000000000000"
- "180000" // index
- "ffffffff" // next->
- "0100000000000000000000000000000000000000000000000000000000000000"
- "2a0000"); // index
- ////const auto expected_spend_head = system::base16_chunk(
- //// "02000000" // record count
- //// "00000000" // spend0_fk->
- //// "ffffffff"
- //// "ffffffff"
- //// "01000000" // spend1_fk->
- //// "ffffffff");
- ////const auto expected_spend_body = system::base16_chunk(
- //// "ffffffff" // terminal->
- //// "01000000" // fp: point_stub
- //// "180000" // fp: point_index
- //// "00000000" // point_fk->
- //// "00000000" // parent_fk->
- //// "2a000000" // sequence
- //// "0000000000" // input_fk->
- ////
- //// "ffffffff" // terminal->
- //// "01000000" // fp: point_stub
- //// "2a0000" // fp: point_index
- //// "01000000" // point_fk->
- //// "00000000" // parent_fk->
- //// "18000000" // sequence
- //// "0800000000"); // input_fk->
- const auto expected_input_head = system::base16_chunk("1000000000");
- const auto expected_input_body = system::base16_chunk(
- "026a79" // script
- "0103242424" // witness
- "026a7a" // script
- "0103424242"); // witness
-
- constexpr auto tx_hash = system::base16_array("d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a");
- BOOST_CHECK_EQUAL(tx_hash, tx.hash(false));
-
- // data_chunk store.
- settings settings{};
- settings.tx.buckets = 8;
- settings.ins.buckets = 8;
- settings.outs.buckets = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(!query.is_tx(tx.hash(false)));
- BOOST_CHECK(query.set(tx));
- BOOST_CHECK(query.is_tx(tx.hash(false)));
-
- const auto pointer = query.get_transaction(query.to_tx(tx_hash), true);
- BOOST_CHECK(pointer);
- BOOST_CHECK(*pointer == tx);
- BOOST_CHECK_EQUAL(pointer->hash(false), tx_hash);
- BOOST_CHECK(!store.close(test::events_handler));
-
- BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head);
- BOOST_CHECK_EQUAL(store.input_head(), expected_input_head);
- BOOST_CHECK_EQUAL(store.output_head(), expected_output_head);
- BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head);
- ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head);
-
- BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body);
- BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body);
- BOOST_CHECK_EQUAL(store.input_body(), expected_input_body);
- BOOST_CHECK_EQUAL(store.output_body(), expected_output_body);
- BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body);
- BOOST_CHECK_EQUAL(store.address_body(), expected_address_body);
- ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_block__get_block__expected)
-{
- constexpr auto milestone = true;
- const auto genesis_header_head = system::base16_chunk(
- "010000ff" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "000080" // filter[8], pk->
- "da"); // filter[0-7]
- const auto genesis_header_body = system::base16_chunk(
- "ffff7f" // next->
- "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash)
- "04030201" // flags
- "141312" // height
- "24232221" // mtp
- "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
- "01000000" // version
- "29ab5f49" // timestamp
- "ffff001d" // bits
- "1dac2b7c" // nonce
- "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root
- const auto genesis_tx_head = system::base16_chunk(
- "01000000" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "00000000" // pk->
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto genesis_tx_body = system::base16_chunk(
- "ffffff7f" // next->
- "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false))
- "cc0080" // light (coinbase merged)
- "cc0000" // heavy
- "00000000" // locktime
- "01000000" // version
- "010000" // ins_count
- "010000" // outs_count
- "00000000" // point_fk-> (ins_fk)
- "00000000"); // outs_fk->
- const auto genesis_outs_head = system::base16_chunk(
- "01000000" // record count
- "ffffffff" // bucket[0]
- "00000000");// bucket[1] (link->0)
- const auto genesis_address_body = system::base16_chunk(
- "ffffffff"); // next->end
- const auto genesis_outs_body = system::base16_chunk(
- "0000000000"); // output0_fk->
- const auto genesis_output_head = system::base16_chunk("5100000000");
- const auto genesis_output_body = system::base16_chunk(
- "00000000" // parent_fk->
- "ff00f2052a01000000" // value
- "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script
- const auto genesis_ins_body = system::base16_chunk(
- "ffffffff" // next->
- "0000000000000000000000000000000000000000000000000000000000000000"
- "ffffff"); // index
- const auto genesis_spend_head = system::base16_chunk(
- "01000000" // record count
- "00000000" // spend0_fk->
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto genesis_spend_body = system::base16_chunk(
- "ffffffff" // terminal->
- "00000000" // fp: point_stub
- "ffffff" // fp: point_index (null)
- "ffffffff" // point_fk->
- "00000000" // parent_fk->
- "ffffffff" // sequence
- "0000000000"); // input_fk-> (coinbase)
- const auto genesis_input_head = system::base16_chunk("4f00000000");
- const auto genesis_input_body = system::base16_chunk(
- "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
- "00"); // witness
- const auto genesis_txs_head = system::base16_chunk(
- "1100000000" // slab size
- "0000000000" // pk->
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff");
- const auto genesis_txs_body = system::base16_chunk(
- "1d0100" // size light (285)
- "1d0100" // size heavy (285)
- "0100" // txs count (1)
- "00000000" // transaction[0]
- "ff" // depth (255) - genesis only
- "00000000"); // forks (0) - genesis only
-
- settings settings{};
- settings.header.buckets = 8;
- settings.tx.buckets = 8;
- settings.ins.buckets = 8;
- settings.txs.buckets = 16;
- settings.outs.buckets = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- // Set block (header/txs).
- BOOST_CHECK(!query.is_block(test::genesis.hash()));
- BOOST_CHECK(query.set(test::genesis, test::context, milestone, false));
- BOOST_CHECK(query.is_block(test::genesis.hash()));
-
- table::header::record element1{};
- BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1));
- BOOST_CHECK(!store.close(test::events_handler));
-
- BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head);
- BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head);
- BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head);
- BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head);
- BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head);
- ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head);
- BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head);
-
- BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body);
- BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body);
- BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body);
- BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body);
- BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body);
- ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body);
- BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body);
-
- const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false);
- BOOST_CHECK(pointer);
- BOOST_CHECK(*pointer == test::genesis);
-
- const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash()));
- BOOST_CHECK_EQUAL(hashes.size(), 1u);
- BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__set_block_txs__get_block__expected)
-{
- constexpr auto milestone = true;
- const auto genesis_header_head = system::base16_chunk(
- "010000ff" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "000080" // filter[8], pk->
- "da"); // filter[0-7]
- const auto genesis_header_body = system::base16_chunk(
- "ffff7f" // next->
- "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash)
- "04030201" // flags
- "141312" // height
- "24232221" // mtp
- "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
- "01000000" // version
- "29ab5f49" // timestamp
- "ffff001d" // bits
- "1dac2b7c" // nonce
- "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root
- const auto genesis_tx_head = system::base16_chunk(
- "01000000" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "00000000" // pk->
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto genesis_tx_body = system::base16_chunk(
- "ffffff7f" // next->
- "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false))
- "cc0080" // light (coinbase merged)
- "cc0000" // heavy
- "00000000" // locktime
- "01000000" // version
- "010000" // ins_count
- "010000" // outs_count
- "00000000" // point_fk-> (ins_fk)
- "00000000"); // outs_fk->
- const auto genesis_outs_head = system::base16_chunk(
- "01000000" // record count
- "ffffffff" // bucket[0]
- "00000000");// bucket[1] (link->0)
- const auto genesis_outs_body = system::base16_chunk(
- "00000000" // spend0_fk->
- "0000000000"); // output0_fk->
- const auto genesis_output_head = system::base16_chunk("5100000000");
- const auto genesis_output_body = system::base16_chunk(
- "00000000" // parent_fk->
- "ff00f2052a01000000" // value
- "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script
- const auto genesis_ins_body = system::base16_chunk(
- "ffffffff" // next->
- "0000000000000000000000000000000000000000000000000000000000000000"
- "ffffff"); // index
- const auto genesis_spend_head = system::base16_chunk(
- "01000000" // record count
- "00000000" // spend0_fk->
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
- const auto genesis_spend_body = system::base16_chunk(
- "ffffffff" // terminal->
- "00000000" // fp: point_hash
- "ffffff" // fp: point_index (null)
- "ffffffff" // point_fk->
- "00000000" // parent_fk->
- "ffffffff" // sequence
- "0000000000"); // input_fk-> (coinbase)
- const auto genesis_input_head = system::base16_chunk("4f00000000");
- const auto genesis_input_body = system::base16_chunk(
- "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
- "00"); // witness
- const auto genesis_txs_head = system::base16_chunk(
- "1100000000" // slab size
- "0000000000" // pk->
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff"
- "ffffffffff");
- const auto genesis_txs_body = system::base16_chunk(
- "1d0100" // size light (285)
- "1d0100" // size heavy (285)
- "0100" // txs count (1)
- "00000000" // transaction[0]
- "ff" // depth (255) - genesis only
- "00000000"); // forks (0) - genesis only
-
- settings settings{};
- settings.header.buckets = 8;
- settings.tx.buckets = 8;
- settings.ins.buckets = 8;
- settings.txs.buckets = 16;
- settings.outs.buckets = 2;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- // Set header and then txs.
- BOOST_CHECK(!query.is_block(test::genesis.hash()));
- BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone));
- BOOST_CHECK(!query.is_associated(0));
- BOOST_CHECK(query.set(test::genesis, false, false));
- BOOST_CHECK(query.is_block(test::genesis.hash()));
- BOOST_CHECK(query.is_associated(0));
-
- table::header::record element1{};
- BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1));
- BOOST_CHECK(!store.close(test::events_handler));
-
- BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head);
- BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head);
- BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head);
- BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head);
- BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head);
- ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head);
- BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head);
-
- BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body);
- BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body);
- BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body);
- BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body);
- BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body);
- ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body);
- BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body);
-
- const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false);
- BOOST_CHECK(pointer);
- BOOST_CHECK(*pointer == test::genesis);
-
- const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash()));
- BOOST_CHECK_EQUAL(hashes.size(), 1u);
- BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false));
-}
-
-// populate_with_metadata
-// ----------------------------------------------------------------------------
-
-// METADATA IS SETTABLE ON CONST TEST OBJECTS.
-// COPY CONSTRUCTION CREATES SHARED POINTER REFERENCES.
-// SO POPULATE ON CONST OBJECTS HAS SIDE EFFECTS IF THE OBJECTS SPAN TESTS.
-const auto& clean_(const auto& block_or_tx) NOEXCEPT
-{
- const auto inputs = block_or_tx.inputs_ptr();
- for (const auto& input: *inputs)
- {
- input->prevout.reset();
- input->metadata = system::chain::prevout{};
- }
-
- return block_or_tx;
-}
-
-// First four blocks have only coinbase txs.
-BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__null_prevouts__true)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, test::context, false, false));
- BOOST_CHECK(query.set(test::block2, test::context, false, false));
- BOOST_CHECK(query.set(test::block3, test::context, false, false));
-
- const auto& copy = clean_(test::genesis);
- const auto& copy1 = clean_(test::block1);
- const auto& copy2 = clean_(test::block2);
- const auto& copy3 = clean_(test::block3);
-
- BOOST_CHECK(query.populate_with_metadata(copy));
- BOOST_CHECK(query.populate_with_metadata(copy1));
- BOOST_CHECK(query.populate_with_metadata(copy2));
- BOOST_CHECK(query.populate_with_metadata(copy3));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__partial_prevouts__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, test::context, false, false));
- BOOST_CHECK(query.set(test::block2a, test::context, false, false));
- BOOST_CHECK(query.set(test::tx4));
-
- const auto& block1a = clean_(test::block1a);
-
- // Block populate treates first tx as null point.
- BOOST_CHECK( query.populate_with_metadata(block1a));
- BOOST_CHECK(!query.populate_with_metadata(*block1a.transactions_ptr()->at(0)));
- BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(0)));
- BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(2)));
-
- // Block populate treates first tx as null point and other has missing prevouts.
- const auto& block2a = clean_(test::block2a);
- BOOST_CHECK(!query.populate_with_metadata(block2a));
- BOOST_CHECK( query.populate_with_metadata(*block2a.transactions_ptr()->at(0)));
- BOOST_CHECK(!query.populate_with_metadata(*block2a.transactions_ptr()->at(1)));
- BOOST_CHECK( query.populate_with_metadata(*block2a.inputs_ptr()->at(0)));
- BOOST_CHECK(!query.populate_with_metadata(*block2a.inputs_ptr()->at(3)));
-
- const auto& tx4 = clean_(test::tx4);
- BOOST_CHECK(query.populate_with_metadata(tx4));
- BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(0)));
- BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(1)));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__metadata__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, test::context, false, false));
- BOOST_CHECK(query.set(test::block2a, test::context, false, false));
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK(!query.is_coinbase(1));
-
- // Genesis only has coinbase, which does not spend.
- const auto& genesis = clean_(test::genesis);
-
- BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout);
- BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase);
- BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32);
-
- BOOST_CHECK(query.populate_with_metadata(genesis));
-
- BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout);
- BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase);
- BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32);
-
- // Transaction population.
- const auto& tx4 = clean_(test::tx4);
-
- BOOST_CHECK(!tx4.inputs_ptr()->at(0)->prevout);
- BOOST_CHECK( tx4.inputs_ptr()->at(0)->metadata.coinbase);
- BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32);
- BOOST_CHECK(!tx4.inputs_ptr()->at(1)->prevout);
- BOOST_CHECK( tx4.inputs_ptr()->at(1)->metadata.coinbase);
- BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, max_uint32);
-
- BOOST_CHECK(query.populate_with_metadata(tx4));
-
- // TODO: test non-coinbase and other parent.
- // spent/mtp are defaults, coinbase/parent are set (to non-default values).
- BOOST_CHECK( tx4.inputs_ptr()->at(0)->prevout);
- BOOST_CHECK(!tx4.inputs_ptr()->at(0)->metadata.coinbase);
- BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, 1u);
- BOOST_CHECK( tx4.inputs_ptr()->at(1)->prevout);
- BOOST_CHECK(!tx4.inputs_ptr()->at(1)->metadata.coinbase);
- BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, 1u);
-}
-
-// populate_without_metadata
-// ----------------------------------------------------------------------------
-
-// First four blocks have only coinbase txs.
-BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__null_prevouts__true)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, test::context, false, false));
- BOOST_CHECK(query.set(test::block2, test::context, false, false));
- BOOST_CHECK(query.set(test::block3, test::context, false, false));
- BOOST_CHECK(query.populate_without_metadata(test::genesis));
- BOOST_CHECK(query.populate_without_metadata(test::block1));
- BOOST_CHECK(query.populate_without_metadata(test::block2));
- BOOST_CHECK(query.populate_without_metadata(test::block3));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__partial_prevouts__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, test::context, false, false));
- BOOST_CHECK(query.set(test::block2a, test::context, false, false));
- BOOST_CHECK(query.set(test::tx4));
-
- // Block populate treates first tx as null point.
- BOOST_CHECK( query.populate_without_metadata(test::block1a));
- BOOST_CHECK(!query.populate_without_metadata(*test::block1a.transactions_ptr()->at(0)));
- BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(0)));
- BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(2)));
-
- // Block populate treates first tx as null point and other has missing prevouts.
- BOOST_CHECK(!query.populate_without_metadata(test::block2a));
- BOOST_CHECK( query.populate_without_metadata(*test::block2a.transactions_ptr()->at(0)));
- BOOST_CHECK(!query.populate_without_metadata(*test::block2a.transactions_ptr()->at(1)));
- BOOST_CHECK( query.populate_without_metadata(*test::block2a.inputs_ptr()->at(0)));
- BOOST_CHECK(!query.populate_without_metadata(*test::block2a.inputs_ptr()->at(3)));
-
- BOOST_CHECK(query.populate_without_metadata(test::tx4));
- BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(0)));
- BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(1)));
-}
-
-// ----------------------------------------------------------------------------
-
-// archive_write (foreign-keyed)
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__coinbase__true)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, context{}, false, false));
- BOOST_CHECK(query.set(test::block2, context{}, false, false));
- BOOST_CHECK(query.set(test::block3, context{}, false, false));
- BOOST_CHECK(query.is_coinbase(0));
- BOOST_CHECK(query.is_coinbase(1));
- BOOST_CHECK(query.is_coinbase(2));
- BOOST_CHECK(query.is_coinbase(3));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__non_coinbase__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, context{}, false, false));
- BOOST_CHECK(query.set(test::block2a, context{}, false, false));
- BOOST_CHECK(!query.is_coinbase(1));
- BOOST_CHECK(!query.is_coinbase(2));
- BOOST_CHECK(!query.is_coinbase(3));
- BOOST_CHECK(!query.is_coinbase(4));
- BOOST_CHECK(!query.is_coinbase(5));
- BOOST_CHECK(!query.is_coinbase(42));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__genesis__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success);
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(!query.is_milestone(0));
- BOOST_CHECK(!query.is_milestone(1));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__set__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success);
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, context{}, true, false));
- BOOST_CHECK(query.set(test::block2, context{}, false, false));;
- BOOST_CHECK(!query.is_milestone(0));
- BOOST_CHECK(query.is_milestone(1));
- BOOST_CHECK(!query.is_milestone(2));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__invalid_parent__expected)
-{
- constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
- constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
- const system::chain::header header
- {
- 0x31323334, // version
- system::null_hash, // previous_block_hash
- root, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- };
- const auto expected_header_head = system::base16_chunk(
- "010000" // record count
- "ffffff" // bucket[0]...
- "000000" // pk->
- "ffffff"
- "ffffff"
- "ffffff"
- "ffffff"
- "ffffff"
- "ffffff"
- "ffffff"
- "ffffff");
- const auto expected_header_body = system::base16_chunk(
- "ffffff" // next->
- "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash)
- "14131211" // flags
- "040302" // height
- "24232221" // mtp
- "424242" // previous_block_hash (header_fk - invalid) (milestone false)
- "34333231" // version
- "44434241" // timestamp
- "54535251" // bits
- "64636261" // nonce
- "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root
-
- settings settings{};
- settings.header.buckets = 16;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- store.header_head() = expected_header_head;
- store.header_body() = expected_header_body;
- BOOST_CHECK(!query.get_header(query.to_header(block_hash)));
- BOOST_CHECK(!query.get_header(header_link::terminal));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__default__expected)
-{
- constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
- constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
- static_assert(0x45d6f6162ab0d085_u64 % 10u == 5u);
- const system::chain::header header
- {
- 0x31323334, // version
- system::null_hash, // previous_block_hash
- root, // merkle_root
- 0x41424344, // timestamp
- 0x51525354, // bits
- 0x61626364 // nonce
- };
-
- // TODO: heads may be undersized due to the change to base2 sizing.
- const auto expected_header_head = system::base16_chunk(
- "000000ff" // record count
- "ffffffff" // bucket[0]...
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "000080" // filter[8], pk->
- "de" // filter[0-7]
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff"
- "ffffffff");
-
- const auto expected_header_body = system::base16_chunk(
- "ffff7f" // next->
- "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash)
- "14131211" // flags
- "040302" // height
- "24232221" // mtp
- "ffffff" // previous_block_hash (header_fk - terminal) (milestone true)
- "34333231" // version
- "44434241" // timestamp
- "54535251" // bits
- "64636261" // nonce
- "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root
-
- settings settings{};
- settings.header.buckets = 16;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- ////store.header_head() = expected_header_head;
- ////store.header_body() = expected_header_body;
- BOOST_CHECK(query.set(header, context{ 0x11121314, 0x01020304, 0x21222324 }, true));
- BOOST_CHECK_EQUAL(store.header_head(), expected_header_head);
- BOOST_CHECK_EQUAL(store.header_body(), expected_header_body);
-
- const auto foo = query.to_header(block_hash);
- const auto pointer1 = query.get_header(foo);
- BOOST_CHECK(pointer1);
- BOOST_CHECK(*pointer1 == header);
-
- // Verify hash caching.
- BOOST_CHECK_EQUAL(pointer1->hash(), block_hash);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_keys__not_found__empty)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.get_tx_keys(query.to_header(system::null_hash)).empty());
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_header_key__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK_EQUAL(query.get_header_key(0), test::genesis.hash());
- BOOST_CHECK_EQUAL(query.get_header_key(1), system::null_hash);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_point_key__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK_EQUAL(query.get_point_hash(0), system::null_hash);
-
- // tx4/5 prevouts are all block1a.tx1.
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK(query.set(test::tx5));
- BOOST_CHECK_EQUAL(query.get_point_hash(1), test::block1a.transactions_ptr()->front()->hash(false));
- BOOST_CHECK_EQUAL(query.get_point_hash(2), test::block1a.transactions_ptr()->front()->hash(false));
-
- // block1a adds three prevouts of two txs.
- BOOST_CHECK(query.set(test::block1a, context{}, false, false));
- BOOST_CHECK_EQUAL(query.get_point_hash(4), system::one_hash);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_key__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK_EQUAL(query.get_tx_key(0), test::genesis.transactions_ptr()->front()->hash(false));
- BOOST_CHECK_EQUAL(query.get_tx_key(1), system::null_hash);
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK(query.set(test::tx5));
- BOOST_CHECK_EQUAL(query.get_tx_key(1), test::tx4.hash(false));
- BOOST_CHECK_EQUAL(query.get_tx_key(2), test::tx5.hash(false));
- BOOST_CHECK_EQUAL(query.get_tx_key(3), system::null_hash);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_height1__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
- BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
-
- size_t out{};
- BOOST_CHECK(query.get_height(out, 0));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_height(out, 1));
- BOOST_CHECK_EQUAL(out, 1u);
- BOOST_CHECK(query.get_height(out, 2));
- BOOST_CHECK_EQUAL(out, 2u);
- BOOST_CHECK(query.get_height(out, 3));
- BOOST_CHECK_EQUAL(out, 3u);
- BOOST_CHECK(query.get_height(out, 4));
- BOOST_CHECK_EQUAL(out, 1u);
- BOOST_CHECK(query.get_height(out, 5));
- BOOST_CHECK_EQUAL(out, 2u);
- BOOST_CHECK(!query.get_height(out, 6));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_height2__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false));
- BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
- BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
-
- size_t out{};
- BOOST_CHECK(query.get_height(out, test::genesis.hash()));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_height(out, test::block1.hash()));
- BOOST_CHECK_EQUAL(out, 1u);
- BOOST_CHECK(query.get_height(out, test::block2.hash()));
- BOOST_CHECK_EQUAL(out, 2u);
- BOOST_CHECK(query.get_height(out, test::block3.hash()));
- BOOST_CHECK_EQUAL(out, 3u);
- BOOST_CHECK(query.get_height(out, test::block1a.hash()));
- BOOST_CHECK_EQUAL(out, 1u);
- BOOST_CHECK(query.get_height(out, test::block2a.hash()));
- BOOST_CHECK_EQUAL(out, 2u);
- BOOST_CHECK(!query.get_height(out, system::one_hash));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_height__not_strong__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::tx4));
-
- size_t out{};
- BOOST_CHECK(!query.get_tx_height(out, 1));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__confirmed__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
- BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
- BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false));
-
- size_t out{};
- const auto foo = query.get_tx_position(out, 0);
- BOOST_CHECK(foo);
- BOOST_CHECK_EQUAL(out, 0u);
-
- BOOST_CHECK(!query.get_tx_position(out, 1));
- BOOST_CHECK(!query.get_tx_position(out, 2));
- BOOST_CHECK(!query.get_tx_position(out, 3));
- BOOST_CHECK(!query.get_tx_position(out, 4));
- BOOST_CHECK(query.set_strong(1));
- BOOST_CHECK(query.set_strong(2));
- BOOST_CHECK(query.set_strong(3));
-
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 0));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 1));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 2));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 3));
- BOOST_CHECK_EQUAL(out, 1u);
- BOOST_CHECK(query.get_tx_position(out, 4));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(!query.get_tx_position(out, 5));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
- BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
- BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false));
- BOOST_CHECK(query.set(test::tx4));
-
- size_t out{};
- BOOST_CHECK(query.get_tx_position(out, 0));
- BOOST_CHECK_EQUAL(out, 0u);
-
- BOOST_CHECK(!query.get_tx_position(out, 1));
- BOOST_CHECK(!query.get_tx_position(out, 2));
- BOOST_CHECK(!query.get_tx_position(out, 3));
- BOOST_CHECK(!query.get_tx_position(out, 4));
- BOOST_CHECK(query.set_strong(1));
- BOOST_CHECK(query.set_strong(2));
- BOOST_CHECK(query.set_strong(3));
-
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 0));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 1));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 2));
- BOOST_CHECK_EQUAL(out, 0u);
- BOOST_CHECK(query.get_tx_position(out, 3));
- BOOST_CHECK_EQUAL(out, 1u);
- BOOST_CHECK(query.get_tx_position(out, 4));
- BOOST_CHECK_EQUAL(out, 0u);
-
- // tx4 is unconfirmed.
- BOOST_CHECK(!query.get_tx_position(out, 5));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_sizes__coinbase__204)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
-
- size_t light{};
- size_t heavy{};
- BOOST_CHECK(query.get_tx_sizes(light, heavy, 0));
- BOOST_CHECK_EQUAL(light, 204u);
- BOOST_CHECK_EQUAL(heavy, 204u);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_count__coinbase__1)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK_EQUAL(query.get_tx_count(0), 1u);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__not_found__nullptr)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), 0u, false));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__genesis__expected)
-{
- settings settings{};
- settings.header.buckets = 8;
- settings.tx.buckets = 8;
- settings.txs.buckets = 16;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.set(test::genesis, test::context, false, false));
-
- const auto tx = test::genesis.transactions_ptr()->front();
- const auto input = tx->inputs_ptr()->front();
- ////BOOST_CHECK(*input == *query.get_input(query.to_tx(tx->hash(false)), 0u));
- ////BOOST_CHECK(*input == *query.get_input(0));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__tx_not_found__nullptr)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(!query.get_inputs(1, false));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__found__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK_EQUAL(query.get_inputs(1, false)->size(), 2u);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__not_found__nullptr)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(!query.get_output(query.to_tx(system::null_hash), 0u));
- BOOST_CHECK(!query.get_output(query.to_output(system::chain::point{ system::null_hash, 0u })));
- BOOST_CHECK(!query.get_output(0));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__genesis__expected)
-{
- settings settings{};
- settings.header.buckets = 8;
- settings.tx.buckets = 8;
- settings.txs.buckets = 16;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.set(test::genesis, test::context, false, false));
-
- const auto tx = test::genesis.transactions_ptr()->front();
- const auto output1 = tx->outputs_ptr()->front();
- BOOST_CHECK(*output1 == *query.get_output(query.to_tx(tx->hash(false)), 0u));
- BOOST_CHECK(*output1 == *query.get_output(query.to_output(tx->hash(false), 0u)));
- BOOST_CHECK(*output1 == *query.get_output(0));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__tx_not_found__nullptr)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(!query.get_outputs(1));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__found__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK_EQUAL(query.get_outputs(1)->size(), 1u);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__tx_not_found__nullptr)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK(!query.get_transactions(3, false));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__found__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1a, test::context, false, false));
- BOOST_CHECK(query.set(test::block2a, test::context, false, false));
- BOOST_CHECK(query.set(test::tx4));
- BOOST_CHECK_EQUAL(query.get_transactions(0, false)->size(), 1u);
- BOOST_CHECK_EQUAL(query.get_transactions(1, false)->size(), 1u);
- BOOST_CHECK_EQUAL(query.get_transactions(2, false)->size(), 2u);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_spenders__unspent_or_not_found__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
- BOOST_CHECK(query.set(test::block1, test::context, false, false));
- BOOST_CHECK(query.set(test::block2, test::context, false, false));
- BOOST_CHECK(query.set(test::block3, test::context, false, false));
-
- // Caller should always test for nullptr.
- BOOST_CHECK(query.get_spenders(output_link::terminal, true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(0, 0), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(0, 1), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(1, 0), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(1, 1), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(2, 0), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(2, 1), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(3, 0), true)->empty());
- BOOST_CHECK(query.get_spenders(query.to_output(3, 1), true)->empty());
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_writer__get_value__genesis__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
- BOOST_CHECK(query.initialize(test::genesis));
-
- uint64_t value{};
- BOOST_CHECK(query.get_value(value, query.to_output(0, 0)));
- BOOST_CHECK_EQUAL(value, 5000000000u);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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"
+
+// ensure context::flags is same size as chain_context::flags.
+static_assert(is_same_type < database::context::flag_t::integer, decltype(system::chain::context{}.flags) > );
+
+BOOST_FIXTURE_TEST_SUITE(query_chain_writer_tests, test::directory_setup_fixture)
+
+// slow test (mmap)
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_header__mmap_get_header__expected)
+{
+ constexpr auto milestone = false;
+ constexpr auto parent = system::null_hash;
+ constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
+ constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
+ const system::chain::header header
+ {
+ 0x31323334, // version
+ parent, // previous_block_hash
+ merkle_root,// merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ };
+
+ settings settings{};
+ settings.header.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ store store{ settings };
+ query> query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.set(header, test::context, milestone));
+
+ table::header::record element1{};
+ BOOST_CHECK(store.header.get(query.to_header(block_hash), element1));
+
+ const auto pointer = query.get_header(query.to_header(block_hash));
+ BOOST_CHECK(pointer);
+ BOOST_CHECK(*pointer == header);
+
+ // must open/close mmap
+ BOOST_CHECK(!store.close(test::events_handler));
+ BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits));
+ BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags);
+ BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp);
+ BOOST_CHECK_EQUAL(element1.milestone, milestone);
+ BOOST_CHECK_EQUAL(element1.version, header.version());
+ BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal);
+ BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root());
+ BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp());
+ BOOST_CHECK_EQUAL(element1.bits, header.bits());
+ BOOST_CHECK_EQUAL(element1.nonce, header.nonce());
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_header__is_header__expected)
+{
+ constexpr auto milestone = true;
+ constexpr auto merkle_root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
+ constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
+ const system::chain::header header
+ {
+ 0x31323334, // version
+ system::null_hash, // previous_block_hash
+ merkle_root,// merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ };
+
+ // nosh
+ const auto expected_header_head = system::base16_chunk(
+ "010000ff" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "000080" // filter[8], pk->
+ "de" // filter[0-7]
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+
+ const auto expected_header_body = system::base16_chunk(
+ "ffff7f" // next->
+ "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash)
+ "04030201" // flags
+ "141312" // height
+ "24232221" // mtp
+ "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
+ "34333231" // version
+ "44434241" // timestamp
+ "54535251" // bits
+ "64636261" // nonce
+ "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); //merkle_root
+
+ header_link link{};
+ settings settings{};
+ settings.header.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ // store open/close flushes record count to head.
+ BOOST_CHECK(!query.is_header(header.hash()));
+ BOOST_CHECK(!query.is_associated(0));
+ BOOST_CHECK(!query.set_code(link, header, test::context, milestone));
+ BOOST_CHECK(!link.is_terminal());
+ BOOST_CHECK(query.is_header(header.hash()));
+ BOOST_CHECK(!query.is_associated(0));
+ table::header::record element1{};
+ BOOST_CHECK(store.header.get(query.to_header(block_hash), element1));
+ BOOST_CHECK(!store.close(test::events_handler));
+ BOOST_CHECK_EQUAL(store.header_head(), expected_header_head);
+ BOOST_CHECK_EQUAL(store.header_body(), expected_header_body);
+
+ BOOST_CHECK_EQUAL(element1.ctx.height, system::mask_left(test::context.height, byte_bits));
+ BOOST_CHECK_EQUAL(element1.ctx.flags, test::context.flags);
+ BOOST_CHECK_EQUAL(element1.ctx.mtp, test::context.mtp);
+ BOOST_CHECK_EQUAL(element1.milestone, milestone);
+ BOOST_CHECK_EQUAL(element1.version, header.version());
+ BOOST_CHECK_EQUAL(element1.parent_fk, schema::header::link::terminal);
+ BOOST_CHECK_EQUAL(element1.merkle_root, header.merkle_root());
+ BOOST_CHECK_EQUAL(element1.timestamp, header.timestamp());
+ BOOST_CHECK_EQUAL(element1.bits, header.bits());
+ BOOST_CHECK_EQUAL(element1.nonce, header.nonce());
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__empty__expected)
+{
+ const system::chain::transaction tx{};
+ const auto expected_head5_array = system::base16_chunk("0000000000");
+ const auto expected_outs_head = system::base16_chunk(
+ "00000000" // record count
+ "ffffffff" // bucket[0]
+ "ffffffff");// bucket[1]
+ const auto expected_head4_hash = system::base16_chunk(
+ "01000000" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto expected_head5_hash = system::base16_chunk(
+ "0000000000" // record count
+ "ffffffffff" // bucket[0]...
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff");
+
+ // data_chunk store.
+ settings settings{};
+ settings.tx.buckets = 8;
+ settings.outs.buckets = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ // store open/close flushes record count to heads.
+ BOOST_CHECK(!query.set(tx));
+ BOOST_CHECK(!store.close(test::events_handler));
+ BOOST_CHECK_EQUAL(store.tx_head(), expected_head4_hash);
+ BOOST_CHECK_EQUAL(store.input_head(), expected_head5_array);
+ BOOST_CHECK_EQUAL(store.output_head(), expected_head5_array);
+ BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head);
+ BOOST_CHECK_EQUAL(store.tx_body().size(), schema::transaction::minrow);
+ BOOST_CHECK(store.ins_body().empty());
+ BOOST_CHECK(store.input_body().empty());
+ BOOST_CHECK(store.output_body().empty());
+ BOOST_CHECK(store.outs_body().empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_link_tx__null_input__expected)
+{
+ using namespace system::chain;
+ const transaction tx
+ {
+ 0x01020304, // version
+ inputs
+ {
+ input{ point{}, script{}, witness{}, 0 }
+ },
+ outputs
+ {
+ output{ 0, script{} }
+ },
+ 0x11121314 // locktime
+ };
+ const auto expected_tx_head = system::base16_chunk(
+ "01000000" // record count
+ "00000000" // bucket[0]... pk->
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto expected_tx_body = system::base16_chunk(
+ "ffffff7f" // next->
+ "601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62" // sk (tx.hash(false))
+ "3c0080" // light (coinbase merged)
+ "3c0000" // heavy
+ "14131211" // locktime
+ "04030201" // version
+ "010000" // ins_count
+ "010000" // outs_count
+ "00000000" // point_fk-> (ins_fk)
+ "00000000"); // outs_fk->
+ const auto expected_outs_head = system::base16_chunk(
+ "01000000" // record count
+ "00000000" // bucket[0] (link->0)
+ "ffffffff");// bucket[1]
+ const auto expected_address_body = system::base16_chunk(
+ "ffffffff"); // next->end
+ const auto expected_outs_body = system::base16_chunk(
+ "0000000000"); // output0_fk->
+ const auto expected_output_head = system::base16_chunk("0600000000");
+ const auto expected_output_body = system::base16_chunk(
+ "00000000" // parent_fk->
+ "00" // value
+ "00"); // script
+ const auto expected_ins_body = system::base16_chunk(
+ "ffffffff" // next->
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "ffffff"); // index
+ ////const auto expected_spend_head = system::base16_chunk(
+ //// "01000000" // record count
+ //// "00000000" // pk->
+ //// "ffffffff"
+ //// "ffffffff"
+ //// "ffffffff"
+ //// "ffffffff");
+ ////const auto expected_spend_body = system::base16_chunk(
+ //// "ffffffff" // terminal->
+ //// "00000000" // fp: point_stub
+ //// "ffffff" // fp: point_index (null)
+ //// "ffffffff" // point_fk->
+ //// "00000000" // parent_fk->
+ //// "00000000" // sequence
+ //// "0000000000"); // input_fk->
+ const auto expected_input_head = system::base16_chunk("0200000000");
+ const auto expected_input_body = system::base16_chunk(
+ "00" // script
+ "00"); // witness
+ constexpr auto tx_hash = system::base16_array("601f0fa54d6de8362c17dc883cc047e1f3ae0523d732598a05e3010fac591f62");
+ BOOST_CHECK_EQUAL(tx_hash, tx.hash(false));
+
+ // data_chunk store.
+ settings settings{};
+ settings.tx.buckets = 8;
+ settings.ins.buckets = 8;
+ settings.outs.buckets = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(!query.set_code(tx));
+ BOOST_CHECK(!store.close(test::events_handler));
+
+ BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head);
+ BOOST_CHECK_EQUAL(store.input_head(), expected_input_head);
+ BOOST_CHECK_EQUAL(store.output_head(), expected_output_head);
+ BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head);
+ ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head);
+
+ BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body);
+ BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body);
+ BOOST_CHECK_EQUAL(store.input_body(), expected_input_body);
+ BOOST_CHECK_EQUAL(store.output_body(), expected_output_body);
+ BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body);
+ BOOST_CHECK_EQUAL(store.address_body(), expected_address_body);
+ ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_tx__get_tx__expected)
+{
+ using namespace system::chain;
+ const transaction tx
+ {
+ 0x2a, // version
+ inputs
+ {
+ input
+ {
+ point{ system::one_hash, 0x18 },
+ script{ { { opcode::op_return }, { opcode::pick } } },
+ witness{ "[242424]" },
+ 0x2a // sequence
+ },
+ input
+ {
+ point{ system::one_hash, 0x2a },
+ script{ { { opcode::op_return }, { opcode::roll } } },
+ witness{ "[424242]" },
+ 0x18 // sequence
+ }
+ },
+ outputs
+ {
+ output
+ {
+ 0x18, // value
+ script{ { { opcode::pick } } }
+ },
+ output
+ {
+ 0x2a, // value
+ script{ { { opcode::roll } } }
+ }
+ },
+ 0x18 // locktime
+ };
+ const auto expected_tx_head = system::base16_chunk(
+ "01000000" // record count
+ "00000000" // bucket[0]... pk->
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto expected_tx_body = system::base16_chunk(
+ "ffffff7f" // next->
+ "d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a" // sk (tx.hash(false))
+ "740000" // light (not coinbase)
+ "800000" // heavy
+ "18000000" // locktime
+ "2a000000" // version
+ "020000" // ins_count
+ "020000" // outs_count
+ "00000000" // point_fk-> (ins_fk)
+ "00000000"); // outs_fk->
+ const auto expected_outs_head = system::base16_chunk(
+ "02000000" // record count
+ "ffffffff" // bucket[0]
+ "01000000");// bucket[1] (link->1)
+ const auto expected_address_body = system::base16_chunk(
+ "ffffffff" // next->end
+ "00000000"); // next->0
+ const auto expected_outs_body = system::base16_chunk(
+ "0000000000" // output0_fk->
+ "0700000000"); // output1_fk->
+ const auto expected_output_head = system::base16_chunk("0e00000000");
+ const auto expected_output_body = system::base16_chunk(
+ "00000000" // parent_fk->
+ "18" // value
+ "0179" // script
+ "00000000" // parent_fk->
+ "2a" // value
+ "017a"); // script
+ const auto expected_ins_body = system::base16_chunk(
+ "ffffffff" // next->
+ "0100000000000000000000000000000000000000000000000000000000000000"
+ "180000" // index
+ "ffffffff" // next->
+ "0100000000000000000000000000000000000000000000000000000000000000"
+ "2a0000"); // index
+ ////const auto expected_spend_head = system::base16_chunk(
+ //// "02000000" // record count
+ //// "00000000" // spend0_fk->
+ //// "ffffffff"
+ //// "ffffffff"
+ //// "01000000" // spend1_fk->
+ //// "ffffffff");
+ ////const auto expected_spend_body = system::base16_chunk(
+ //// "ffffffff" // terminal->
+ //// "01000000" // fp: point_stub
+ //// "180000" // fp: point_index
+ //// "00000000" // point_fk->
+ //// "00000000" // parent_fk->
+ //// "2a000000" // sequence
+ //// "0000000000" // input_fk->
+ ////
+ //// "ffffffff" // terminal->
+ //// "01000000" // fp: point_stub
+ //// "2a0000" // fp: point_index
+ //// "01000000" // point_fk->
+ //// "00000000" // parent_fk->
+ //// "18000000" // sequence
+ //// "0800000000"); // input_fk->
+ const auto expected_input_head = system::base16_chunk("1000000000");
+ const auto expected_input_body = system::base16_chunk(
+ "026a79" // script
+ "0103242424" // witness
+ "026a7a" // script
+ "0103424242"); // witness
+
+ constexpr auto tx_hash = system::base16_array("d80f19b9c0f649081c0b279d9183b0fae35b41b72a34eb181001f82afe22043a");
+ BOOST_CHECK_EQUAL(tx_hash, tx.hash(false));
+
+ // data_chunk store.
+ settings settings{};
+ settings.tx.buckets = 8;
+ settings.ins.buckets = 8;
+ settings.outs.buckets = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(!query.is_tx(tx.hash(false)));
+ BOOST_CHECK(query.set(tx));
+ BOOST_CHECK(query.is_tx(tx.hash(false)));
+
+ const auto pointer = query.get_transaction(query.to_tx(tx_hash), true);
+ BOOST_CHECK(pointer);
+ BOOST_CHECK(*pointer == tx);
+ BOOST_CHECK_EQUAL(pointer->hash(false), tx_hash);
+ BOOST_CHECK(!store.close(test::events_handler));
+
+ BOOST_CHECK_EQUAL(store.tx_head(), expected_tx_head);
+ BOOST_CHECK_EQUAL(store.input_head(), expected_input_head);
+ BOOST_CHECK_EQUAL(store.output_head(), expected_output_head);
+ BOOST_CHECK_EQUAL(store.outs_head(), expected_outs_head);
+ ////BOOST_CHECK_EQUAL(store.spend_head(), expected_spend_head);
+
+ BOOST_CHECK_EQUAL(store.tx_body(), expected_tx_body);
+ BOOST_CHECK_EQUAL(store.ins_body(), expected_ins_body);
+ BOOST_CHECK_EQUAL(store.input_body(), expected_input_body);
+ BOOST_CHECK_EQUAL(store.output_body(), expected_output_body);
+ BOOST_CHECK_EQUAL(store.outs_body(), expected_outs_body);
+ BOOST_CHECK_EQUAL(store.address_body(), expected_address_body);
+ ////BOOST_CHECK_EQUAL(store.spend_body(), expected_spend_body);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_block__get_block__expected)
+{
+ constexpr auto milestone = true;
+ const auto genesis_header_head = system::base16_chunk(
+ "010000ff" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "000080" // filter[8], pk->
+ "da"); // filter[0-7]
+ const auto genesis_header_body = system::base16_chunk(
+ "ffff7f" // next->
+ "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash)
+ "04030201" // flags
+ "141312" // height
+ "24232221" // mtp
+ "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
+ "01000000" // version
+ "29ab5f49" // timestamp
+ "ffff001d" // bits
+ "1dac2b7c" // nonce
+ "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root
+ const auto genesis_tx_head = system::base16_chunk(
+ "01000000" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "00000000" // pk->
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto genesis_tx_body = system::base16_chunk(
+ "ffffff7f" // next->
+ "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false))
+ "cc0080" // light (coinbase merged)
+ "cc0000" // heavy
+ "00000000" // locktime
+ "01000000" // version
+ "010000" // ins_count
+ "010000" // outs_count
+ "00000000" // point_fk-> (ins_fk)
+ "00000000"); // outs_fk->
+ const auto genesis_outs_head = system::base16_chunk(
+ "01000000" // record count
+ "ffffffff" // bucket[0]
+ "00000000");// bucket[1] (link->0)
+ const auto genesis_address_body = system::base16_chunk(
+ "ffffffff"); // next->end
+ const auto genesis_outs_body = system::base16_chunk(
+ "0000000000"); // output0_fk->
+ const auto genesis_output_head = system::base16_chunk("5100000000");
+ const auto genesis_output_body = system::base16_chunk(
+ "00000000" // parent_fk->
+ "ff00f2052a01000000" // value
+ "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script
+ const auto genesis_ins_body = system::base16_chunk(
+ "ffffffff" // next->
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "ffffff"); // index
+ const auto genesis_spend_head = system::base16_chunk(
+ "01000000" // record count
+ "00000000" // spend0_fk->
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto genesis_spend_body = system::base16_chunk(
+ "ffffffff" // terminal->
+ "00000000" // fp: point_stub
+ "ffffff" // fp: point_index (null)
+ "ffffffff" // point_fk->
+ "00000000" // parent_fk->
+ "ffffffff" // sequence
+ "0000000000"); // input_fk-> (coinbase)
+ const auto genesis_input_head = system::base16_chunk("4f00000000");
+ const auto genesis_input_body = system::base16_chunk(
+ "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
+ "00"); // witness
+ const auto genesis_txs_head = system::base16_chunk(
+ "1301000000" // slab size
+ "0000000000" // pk->
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff");
+ const auto genesis_txs_body = system::build_chunk(
+ {
+ system::base16_chunk(
+ "1d0100" // size light (285)
+ "1d0100" // size heavy (285)
+ "0100" // txs count (1)
+ "00000000" // transaction[0]
+ "01" // envelope version (1) - genesis only
+ "ff00" // interval depth (255)
+ "08000000" // header buckets (8)
+ "08000000" // ins buckets (8)
+ "02000000" // outs buckets (2)
+ "08000000" // tx buckets (8)
+ "80000000" // strong_tx buckets (128)
+ "80000000" // duplicate buckets (128)
+ "80000000" // validated_tx buckets (128)
+ "01"), // filter (true)
+ system::data_chunk(231, 0x00)
+ });
+
+ settings settings{};
+ settings.header.buckets = 8;
+ settings.tx.buckets = 8;
+ settings.ins.buckets = 8;
+ settings.txs.buckets = 16;
+ settings.outs.buckets = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ // Set block (header/txs).
+ BOOST_CHECK(!query.is_block(test::genesis.hash()));
+ BOOST_CHECK(query.set(test::genesis, test::context, milestone, false));
+ BOOST_CHECK(query.is_block(test::genesis.hash()));
+
+ table::header::record element1{};
+ BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1));
+ BOOST_CHECK(!store.close(test::events_handler));
+
+ BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head);
+ BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head);
+ BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head);
+ BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head);
+ BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head);
+ ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head);
+ BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head);
+
+ BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body);
+ BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body);
+ BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body);
+ BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body);
+ BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body);
+ ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body);
+ BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body);
+
+ const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false);
+ BOOST_CHECK(pointer);
+ BOOST_CHECK(*pointer == test::genesis);
+
+ const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash()));
+ BOOST_CHECK_EQUAL(hashes.size(), 1u);
+ BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__set_block_txs__get_block__expected)
+{
+ constexpr auto milestone = true;
+ const auto genesis_header_head = system::base16_chunk(
+ "010000ff" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "000080" // filter[8], pk->
+ "da"); // filter[0-7]
+ const auto genesis_header_body = system::base16_chunk(
+ "ffff7f" // next->
+ "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash)
+ "04030201" // flags
+ "141312" // height
+ "24232221" // mtp
+ "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
+ "01000000" // version
+ "29ab5f49" // timestamp
+ "ffff001d" // bits
+ "1dac2b7c" // nonce
+ "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root
+ const auto genesis_tx_head = system::base16_chunk(
+ "01000000" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "00000000" // pk->
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto genesis_tx_body = system::base16_chunk(
+ "ffffff7f" // next->
+ "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false))
+ "cc0080" // light (coinbase merged)
+ "cc0000" // heavy
+ "00000000" // locktime
+ "01000000" // version
+ "010000" // ins_count
+ "010000" // outs_count
+ "00000000" // point_fk-> (ins_fk)
+ "00000000"); // outs_fk->
+ const auto genesis_outs_head = system::base16_chunk(
+ "01000000" // record count
+ "ffffffff" // bucket[0]
+ "00000000");// bucket[1] (link->0)
+ const auto genesis_outs_body = system::base16_chunk(
+ "00000000" // spend0_fk->
+ "0000000000"); // output0_fk->
+ const auto genesis_output_head = system::base16_chunk("5100000000");
+ const auto genesis_output_body = system::base16_chunk(
+ "00000000" // parent_fk->
+ "ff00f2052a01000000" // value
+ "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script
+ const auto genesis_ins_body = system::base16_chunk(
+ "ffffffff" // next->
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "ffffff"); // index
+ const auto genesis_spend_head = system::base16_chunk(
+ "01000000" // record count
+ "00000000" // spend0_fk->
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+ const auto genesis_spend_body = system::base16_chunk(
+ "ffffffff" // terminal->
+ "00000000" // fp: point_hash
+ "ffffff" // fp: point_index (null)
+ "ffffffff" // point_fk->
+ "00000000" // parent_fk->
+ "ffffffff" // sequence
+ "0000000000"); // input_fk-> (coinbase)
+ const auto genesis_input_head = system::base16_chunk("4f00000000");
+ const auto genesis_input_body = system::base16_chunk(
+ "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
+ "00"); // witness
+ const auto genesis_txs_head = system::base16_chunk(
+ "1301000000" // slab size
+ "0000000000" // pk->
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff"
+ "ffffffffff");
+ const auto genesis_txs_body = system::build_chunk(
+ {
+ system::base16_chunk(
+ "1d0100" // size light (285)
+ "1d0100" // size heavy (285)
+ "0100" // txs count (1)
+ "00000000" // transaction[0]
+ "01" // envelope version (1) - genesis only
+ "ff00" // interval depth (255)
+ "08000000" // header buckets (8)
+ "08000000" // ins buckets (8)
+ "02000000" // outs buckets (2)
+ "08000000" // tx buckets (8)
+ "80000000" // strong_tx buckets (128)
+ "80000000" // duplicate buckets (128)
+ "80000000" // validated_tx buckets (128)
+ "01"), // filter (true)
+ system::data_chunk(231, 0x00)
+ });
+
+ settings settings{};
+ settings.header.buckets = 8;
+ settings.tx.buckets = 8;
+ settings.ins.buckets = 8;
+ settings.txs.buckets = 16;
+ settings.outs.buckets = 2;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ // Set header and then txs.
+ BOOST_CHECK(!query.is_block(test::genesis.hash()));
+ BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone));
+ BOOST_CHECK(!query.is_associated(0));
+ BOOST_CHECK(query.set(test::genesis, false, false));
+ BOOST_CHECK(query.is_block(test::genesis.hash()));
+ BOOST_CHECK(query.is_associated(0));
+
+ table::header::record element1{};
+ BOOST_CHECK(store.header.get(query.to_header(test::genesis.hash()), element1));
+ BOOST_CHECK(!store.close(test::events_handler));
+
+ BOOST_CHECK_EQUAL(store.header_head(), genesis_header_head);
+ BOOST_CHECK_EQUAL(store.tx_head(), genesis_tx_head);
+ BOOST_CHECK_EQUAL(store.input_head(), genesis_input_head);
+ BOOST_CHECK_EQUAL(store.output_head(), genesis_output_head);
+ BOOST_CHECK_EQUAL(store.outs_head(), genesis_outs_head);
+ ////BOOST_CHECK_EQUAL(store.spend_head(), genesis_spend_head);
+ BOOST_CHECK_EQUAL(store.txs_head(), genesis_txs_head);
+
+ BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body);
+ BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body);
+ BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body);
+ BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body);
+ BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body);
+ ////BOOST_CHECK_EQUAL(store.spend_body(), genesis_spend_body);
+ BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body);
+
+ const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false);
+ BOOST_CHECK(pointer);
+ BOOST_CHECK(*pointer == test::genesis);
+
+ const auto hashes = query.get_tx_keys(query.to_header(test::genesis.hash()));
+ BOOST_CHECK_EQUAL(hashes.size(), 1u);
+ BOOST_CHECK_EQUAL(hashes, test::genesis.transaction_hashes(false));
+}
+
+// populate_with_metadata
+// ----------------------------------------------------------------------------
+
+// METADATA IS SETTABLE ON CONST TEST OBJECTS.
+// COPY CONSTRUCTION CREATES SHARED POINTER REFERENCES.
+// SO POPULATE ON CONST OBJECTS HAS SIDE EFFECTS IF THE OBJECTS SPAN TESTS.
+const auto& clean_(const auto& block_or_tx) NOEXCEPT
+{
+ const auto inputs = block_or_tx.inputs_ptr();
+ for (const auto& input: *inputs)
+ {
+ input->prevout.reset();
+ input->metadata = system::chain::prevout{};
+ }
+
+ return block_or_tx;
+}
+
+// First four blocks have only coinbase txs.
+BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__null_prevouts__true)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+ BOOST_CHECK(query.set(test::block3, test::context, false, false));
+
+ const auto& copy = clean_(test::genesis);
+ const auto& copy1 = clean_(test::block1);
+ const auto& copy2 = clean_(test::block2);
+ const auto& copy3 = clean_(test::block3);
+
+ BOOST_CHECK(query.populate_with_metadata(copy));
+ BOOST_CHECK(query.populate_with_metadata(copy1));
+ BOOST_CHECK(query.populate_with_metadata(copy2));
+ BOOST_CHECK(query.populate_with_metadata(copy3));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__partial_prevouts__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2a, test::context, false, false));
+ BOOST_CHECK(query.set(test::tx4));
+
+ const auto& block1a = clean_(test::block1a);
+
+ // Block populate treates first tx as null point.
+ BOOST_CHECK( query.populate_with_metadata(block1a));
+ BOOST_CHECK(!query.populate_with_metadata(*block1a.transactions_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_with_metadata(*block1a.inputs_ptr()->at(2)));
+
+ // Block populate treates first tx as null point and other has missing prevouts.
+ const auto& block2a = clean_(test::block2a);
+ BOOST_CHECK(!query.populate_with_metadata(block2a));
+ BOOST_CHECK( query.populate_with_metadata(*block2a.transactions_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_with_metadata(*block2a.transactions_ptr()->at(1)));
+ BOOST_CHECK( query.populate_with_metadata(*block2a.inputs_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_with_metadata(*block2a.inputs_ptr()->at(3)));
+
+ const auto& tx4 = clean_(test::tx4);
+ BOOST_CHECK(query.populate_with_metadata(tx4));
+ BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(0)));
+ BOOST_CHECK(query.populate_with_metadata(*tx4.inputs_ptr()->at(1)));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__populate_with_metadata__metadata__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2a, test::context, false, false));
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK(!query.is_coinbase(1));
+
+ // Genesis only has coinbase, which does not spend.
+ const auto& genesis = clean_(test::genesis);
+
+ BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout);
+ BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase);
+ BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32);
+
+ BOOST_CHECK(query.populate_with_metadata(genesis));
+
+ BOOST_CHECK(!genesis.inputs_ptr()->at(0)->prevout);
+ BOOST_CHECK( genesis.inputs_ptr()->at(0)->metadata.coinbase);
+ BOOST_CHECK_EQUAL(genesis.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32);
+
+ // Transaction population.
+ const auto& tx4 = clean_(test::tx4);
+
+ BOOST_CHECK(!tx4.inputs_ptr()->at(0)->prevout);
+ BOOST_CHECK( tx4.inputs_ptr()->at(0)->metadata.coinbase);
+ BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, max_uint32);
+ BOOST_CHECK(!tx4.inputs_ptr()->at(1)->prevout);
+ BOOST_CHECK( tx4.inputs_ptr()->at(1)->metadata.coinbase);
+ BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, max_uint32);
+
+ BOOST_CHECK(query.populate_with_metadata(tx4));
+
+ // TODO: test non-coinbase and other parent.
+ // spent/mtp are defaults, coinbase/parent are set (to non-default values).
+ BOOST_CHECK( tx4.inputs_ptr()->at(0)->prevout);
+ BOOST_CHECK(!tx4.inputs_ptr()->at(0)->metadata.coinbase);
+ BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(0)->metadata.parent_tx, 1u);
+ BOOST_CHECK( tx4.inputs_ptr()->at(1)->prevout);
+ BOOST_CHECK(!tx4.inputs_ptr()->at(1)->metadata.coinbase);
+ BOOST_CHECK_EQUAL(tx4.inputs_ptr()->at(1)->metadata.parent_tx, 1u);
+}
+
+// populate_without_metadata
+// ----------------------------------------------------------------------------
+
+// First four blocks have only coinbase txs.
+BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__null_prevouts__true)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+ BOOST_CHECK(query.set(test::block3, test::context, false, false));
+ BOOST_CHECK(query.populate_without_metadata(test::genesis));
+ BOOST_CHECK(query.populate_without_metadata(test::block1));
+ BOOST_CHECK(query.populate_without_metadata(test::block2));
+ BOOST_CHECK(query.populate_without_metadata(test::block3));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__populate_without_metadata__partial_prevouts__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2a, test::context, false, false));
+ BOOST_CHECK(query.set(test::tx4));
+
+ // Block populate treates first tx as null point.
+ BOOST_CHECK( query.populate_without_metadata(test::block1a));
+ BOOST_CHECK(!query.populate_without_metadata(*test::block1a.transactions_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_without_metadata(*test::block1a.inputs_ptr()->at(2)));
+
+ // Block populate treates first tx as null point and other has missing prevouts.
+ BOOST_CHECK(!query.populate_without_metadata(test::block2a));
+ BOOST_CHECK( query.populate_without_metadata(*test::block2a.transactions_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_without_metadata(*test::block2a.transactions_ptr()->at(1)));
+ BOOST_CHECK( query.populate_without_metadata(*test::block2a.inputs_ptr()->at(0)));
+ BOOST_CHECK(!query.populate_without_metadata(*test::block2a.inputs_ptr()->at(3)));
+
+ BOOST_CHECK(query.populate_without_metadata(test::tx4));
+ BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(0)));
+ BOOST_CHECK(query.populate_without_metadata(*test::tx4.inputs_ptr()->at(1)));
+}
+
+// ----------------------------------------------------------------------------
+
+// archive_write (foreign-keyed)
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__coinbase__true)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, context{}, false, false));
+ BOOST_CHECK(query.set(test::block2, context{}, false, false));
+ BOOST_CHECK(query.set(test::block3, context{}, false, false));
+ BOOST_CHECK(query.is_coinbase(0));
+ BOOST_CHECK(query.is_coinbase(1));
+ BOOST_CHECK(query.is_coinbase(2));
+ BOOST_CHECK(query.is_coinbase(3));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__is_coinbase__non_coinbase__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, context{}, false, false));
+ BOOST_CHECK(query.set(test::block2a, context{}, false, false));
+ BOOST_CHECK(!query.is_coinbase(1));
+ BOOST_CHECK(!query.is_coinbase(2));
+ BOOST_CHECK(!query.is_coinbase(3));
+ BOOST_CHECK(!query.is_coinbase(4));
+ BOOST_CHECK(!query.is_coinbase(5));
+ BOOST_CHECK(!query.is_coinbase(42));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__genesis__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success);
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(!query.is_milestone(0));
+ BOOST_CHECK(!query.is_milestone(1));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__is_milestone__set__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK_EQUAL(store.create(test::events_handler), error::success);
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, context{}, true, false));
+ BOOST_CHECK(query.set(test::block2, context{}, false, false));;
+ BOOST_CHECK(!query.is_milestone(0));
+ BOOST_CHECK(query.is_milestone(1));
+ BOOST_CHECK(!query.is_milestone(2));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__invalid_parent__expected)
+{
+ constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
+ constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
+ const system::chain::header header
+ {
+ 0x31323334, // version
+ system::null_hash, // previous_block_hash
+ root, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ };
+ const auto expected_header_head = system::base16_chunk(
+ "010000" // record count
+ "ffffff" // bucket[0]...
+ "000000" // pk->
+ "ffffff"
+ "ffffff"
+ "ffffff"
+ "ffffff"
+ "ffffff"
+ "ffffff"
+ "ffffff"
+ "ffffff");
+ const auto expected_header_body = system::base16_chunk(
+ "ffffff" // next->
+ "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash)
+ "14131211" // flags
+ "040302" // height
+ "24232221" // mtp
+ "424242" // previous_block_hash (header_fk - invalid) (milestone false)
+ "34333231" // version
+ "44434241" // timestamp
+ "54535251" // bits
+ "64636261" // nonce
+ "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root
+
+ settings settings{};
+ settings.header.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ store.header_head() = expected_header_head;
+ store.header_body() = expected_header_body;
+ BOOST_CHECK(!query.get_header(query.to_header(block_hash)));
+ BOOST_CHECK(!query.get_header(header_link::terminal));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_header__default__expected)
+{
+ constexpr auto root = system::base16_array("119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f");
+ constexpr auto block_hash = system::base16_array("85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2");
+ static_assert(0x45d6f6162ab0d085_u64 % 10u == 5u);
+ const system::chain::header header
+ {
+ 0x31323334, // version
+ system::null_hash, // previous_block_hash
+ root, // merkle_root
+ 0x41424344, // timestamp
+ 0x51525354, // bits
+ 0x61626364 // nonce
+ };
+
+ // TODO: heads may be undersized due to the change to base2 sizing.
+ const auto expected_header_head = system::base16_chunk(
+ "000000ff" // record count
+ "ffffffff" // bucket[0]...
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "000080" // filter[8], pk->
+ "de" // filter[0-7]
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff"
+ "ffffffff");
+
+ const auto expected_header_body = system::base16_chunk(
+ "ffff7f" // next->
+ "85d0b02a16f6d645aa865fad4a8666f5e7bb2b0c4392a5d675496d6c3defa1f2" // sk (block.hash)
+ "14131211" // flags
+ "040302" // height
+ "24232221" // mtp
+ "ffffff" // previous_block_hash (header_fk - terminal) (milestone true)
+ "34333231" // version
+ "44434241" // timestamp
+ "54535251" // bits
+ "64636261" // nonce
+ "119192939495969798999a9b9c9d9e9f229192939495969798999a9b9c9d9e9f"); // merkle_root
+
+ settings settings{};
+ settings.header.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ ////store.header_head() = expected_header_head;
+ ////store.header_body() = expected_header_body;
+ BOOST_CHECK(query.set(header, context{ 0x11121314, 0x01020304, 0x21222324 }, true));
+ BOOST_CHECK_EQUAL(store.header_head(), expected_header_head);
+ BOOST_CHECK_EQUAL(store.header_body(), expected_header_body);
+
+ const auto foo = query.to_header(block_hash);
+ const auto pointer1 = query.get_header(foo);
+ BOOST_CHECK(pointer1);
+ BOOST_CHECK(*pointer1 == header);
+
+ // Verify hash caching.
+ BOOST_CHECK_EQUAL(pointer1->hash(), block_hash);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_keys__not_found__empty)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.get_tx_keys(query.to_header(system::null_hash)).empty());
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_header_key__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_header_key(0), test::genesis.hash());
+ BOOST_CHECK_EQUAL(query.get_header_key(1), system::null_hash);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_point_key__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_point_hash(0), system::null_hash);
+
+ // tx4/5 prevouts are all block1a.tx1.
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK(query.set(test::tx5));
+ BOOST_CHECK_EQUAL(query.get_point_hash(1), test::block1a.transactions_ptr()->front()->hash(false));
+ BOOST_CHECK_EQUAL(query.get_point_hash(2), test::block1a.transactions_ptr()->front()->hash(false));
+
+ // block1a adds three prevouts of two txs.
+ BOOST_CHECK(query.set(test::block1a, context{}, false, false));
+ BOOST_CHECK_EQUAL(query.get_point_hash(4), system::one_hash);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_key__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_tx_key(0), test::genesis.transactions_ptr()->front()->hash(false));
+ BOOST_CHECK_EQUAL(query.get_tx_key(1), system::null_hash);
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK(query.set(test::tx5));
+ BOOST_CHECK_EQUAL(query.get_tx_key(1), test::tx4.hash(false));
+ BOOST_CHECK_EQUAL(query.get_tx_key(2), test::tx5.hash(false));
+ BOOST_CHECK_EQUAL(query.get_tx_key(3), system::null_hash);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_height1__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
+
+ size_t out{};
+ BOOST_CHECK(query.get_height(out, 0));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_height(out, 1));
+ BOOST_CHECK_EQUAL(out, 1u);
+ BOOST_CHECK(query.get_height(out, 2));
+ BOOST_CHECK_EQUAL(out, 2u);
+ BOOST_CHECK(query.get_height(out, 3));
+ BOOST_CHECK_EQUAL(out, 3u);
+ BOOST_CHECK(query.get_height(out, 4));
+ BOOST_CHECK_EQUAL(out, 1u);
+ BOOST_CHECK(query.get_height(out, 5));
+ BOOST_CHECK_EQUAL(out, 2u);
+ BOOST_CHECK(!query.get_height(out, 6));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_height2__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block3, context{ 0, 3, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
+
+ size_t out{};
+ BOOST_CHECK(query.get_height(out, test::genesis.hash()));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_height(out, test::block1.hash()));
+ BOOST_CHECK_EQUAL(out, 1u);
+ BOOST_CHECK(query.get_height(out, test::block2.hash()));
+ BOOST_CHECK_EQUAL(out, 2u);
+ BOOST_CHECK(query.get_height(out, test::block3.hash()));
+ BOOST_CHECK_EQUAL(out, 3u);
+ BOOST_CHECK(query.get_height(out, test::block1a.hash()));
+ BOOST_CHECK_EQUAL(out, 1u);
+ BOOST_CHECK(query.get_height(out, test::block2a.hash()));
+ BOOST_CHECK_EQUAL(out, 2u);
+ BOOST_CHECK(!query.get_height(out, system::one_hash));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_height__not_strong__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::tx4));
+
+ size_t out{};
+ BOOST_CHECK(!query.get_tx_height(out, 1));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__confirmed__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false));
+
+ size_t out{};
+ const auto foo = query.get_tx_position(out, 0);
+ BOOST_CHECK(foo);
+ BOOST_CHECK_EQUAL(out, 0u);
+
+ BOOST_CHECK(!query.get_tx_position(out, 1));
+ BOOST_CHECK(!query.get_tx_position(out, 2));
+ BOOST_CHECK(!query.get_tx_position(out, 3));
+ BOOST_CHECK(!query.get_tx_position(out, 4));
+ BOOST_CHECK(query.set_strong(1));
+ BOOST_CHECK(query.set_strong(2));
+ BOOST_CHECK(query.set_strong(3));
+
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 0));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 1));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 2));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 3));
+ BOOST_CHECK_EQUAL(out, 1u);
+ BOOST_CHECK(query.get_tx_position(out, 4));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(!query.get_tx_position(out, 5));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_position__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
+ BOOST_CHECK(query.set(test::block3a, context{ 0, 3, 0 }, false, false));
+ BOOST_CHECK(query.set(test::tx4));
+
+ size_t out{};
+ BOOST_CHECK(query.get_tx_position(out, 0));
+ BOOST_CHECK_EQUAL(out, 0u);
+
+ BOOST_CHECK(!query.get_tx_position(out, 1));
+ BOOST_CHECK(!query.get_tx_position(out, 2));
+ BOOST_CHECK(!query.get_tx_position(out, 3));
+ BOOST_CHECK(!query.get_tx_position(out, 4));
+ BOOST_CHECK(query.set_strong(1));
+ BOOST_CHECK(query.set_strong(2));
+ BOOST_CHECK(query.set_strong(3));
+
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 0));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 1));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 2));
+ BOOST_CHECK_EQUAL(out, 0u);
+ BOOST_CHECK(query.get_tx_position(out, 3));
+ BOOST_CHECK_EQUAL(out, 1u);
+ BOOST_CHECK(query.get_tx_position(out, 4));
+ BOOST_CHECK_EQUAL(out, 0u);
+
+ // tx4 is unconfirmed.
+ BOOST_CHECK(!query.get_tx_position(out, 5));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_sizes__coinbase__204)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ size_t light{};
+ size_t heavy{};
+ BOOST_CHECK(query.get_tx_sizes(light, heavy, 0));
+ BOOST_CHECK_EQUAL(light, 204u);
+ BOOST_CHECK_EQUAL(heavy, 204u);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_tx_count__coinbase__1)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK_EQUAL(query.get_tx_count(0), 1u);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__not_found__nullptr)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), 0u, false));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_input__genesis__expected)
+{
+ settings settings{};
+ settings.header.buckets = 8;
+ settings.tx.buckets = 8;
+ settings.txs.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.set(test::genesis, test::context, false, false));
+
+ const auto tx = test::genesis.transactions_ptr()->front();
+ const auto input = tx->inputs_ptr()->front();
+ ////BOOST_CHECK(*input == *query.get_input(query.to_tx(tx->hash(false)), 0u));
+ ////BOOST_CHECK(*input == *query.get_input(0));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__tx_not_found__nullptr)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(!query.get_inputs(1, false));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_inputs__found__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK_EQUAL(query.get_inputs(1, false)->size(), 2u);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__not_found__nullptr)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(!query.get_output(query.to_tx(system::null_hash), 0u));
+ BOOST_CHECK(!query.get_output(query.to_output(system::chain::point{ system::null_hash, 0u })));
+ BOOST_CHECK(!query.get_output(0));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_output__genesis__expected)
+{
+ settings settings{};
+ settings.header.buckets = 8;
+ settings.tx.buckets = 8;
+ settings.txs.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.set(test::genesis, test::context, false, false));
+
+ const auto tx = test::genesis.transactions_ptr()->front();
+ const auto output1 = tx->outputs_ptr()->front();
+ BOOST_CHECK(*output1 == *query.get_output(query.to_tx(tx->hash(false)), 0u));
+ BOOST_CHECK(*output1 == *query.get_output(query.to_output(tx->hash(false), 0u)));
+ BOOST_CHECK(*output1 == *query.get_output(0));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__tx_not_found__nullptr)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(!query.get_outputs(1));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_outputs__found__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK_EQUAL(query.get_outputs(1)->size(), 1u);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__tx_not_found__nullptr)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK(!query.get_transactions(3, false));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_transactions__found__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1a, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2a, test::context, false, false));
+ BOOST_CHECK(query.set(test::tx4));
+ BOOST_CHECK_EQUAL(query.get_transactions(0, false)->size(), 1u);
+ BOOST_CHECK_EQUAL(query.get_transactions(1, false)->size(), 1u);
+ BOOST_CHECK_EQUAL(query.get_transactions(2, false)->size(), 2u);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_spenders__unspent_or_not_found__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+ BOOST_CHECK(query.set(test::block1, test::context, false, false));
+ BOOST_CHECK(query.set(test::block2, test::context, false, false));
+ BOOST_CHECK(query.set(test::block3, test::context, false, false));
+
+ // Caller should always test for nullptr.
+ BOOST_CHECK(query.get_spenders(output_link::terminal, true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(0, 0), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(0, 1), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(1, 0), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(1, 1), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(2, 0), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(2, 1), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(3, 0), true)->empty());
+ BOOST_CHECK(query.get_spenders(query.to_output(3, 1), true)->empty());
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_writer__get_value__genesis__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+ BOOST_CHECK(query.initialize(test::genesis));
+
+ uint64_t value{};
+ BOOST_CHECK(query.get_value(value, query.to_output(0, 0)));
+ BOOST_CHECK_EQUAL(value, 5000000000u);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/query/archive/wire_writer.cpp b/test/query/archive/wire_writer.cpp
index ee80619b1..1e286f9a7 100644
--- a/test/query/archive/wire_writer.cpp
+++ b/test/query/archive/wire_writer.cpp
@@ -1,110 +1,122 @@
-/**
- * Copyright (c) 2011-2026 libbitcoin developers
- *
- * 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_wire_writer_writer_tests, test::directory_setup_fixture)
-
-// The view (wire) block writer must produce a store byte-identical to the
-// chain block writer (see analogous chain_writer expectations).
-BOOST_AUTO_TEST_CASE(query_wire_writer__set_block_view__genesis__expected)
-{
- constexpr auto milestone = true;
- const auto genesis_header_body = system::base16_chunk(
- "ffff7f" // next->
- "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash)
- "04030201" // flags
- "141312" // height
- "24232221" // mtp
- "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
- "01000000" // version
- "29ab5f49" // timestamp
- "ffff001d" // bits
- "1dac2b7c" // nonce
- "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root
- const auto genesis_tx_body = system::base16_chunk(
- "ffffff7f" // next->
- "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false))
- "cc0080" // light (coinbase merged)
- "cc0000" // heavy
- "00000000" // locktime
- "01000000" // version
- "010000" // ins_count
- "010000" // outs_count
- "00000000" // point_fk-> (ins_fk)
- "00000000"); // outs_fk->
- const auto genesis_outs_body = system::base16_chunk(
- "0000000000"); // output0_fk->
- const auto genesis_output_body = system::base16_chunk(
- "00000000" // parent_fk->
- "ff00f2052a01000000" // value
- "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script
- const auto genesis_ins_body = system::base16_chunk(
- "ffffffff" // next->
- "0000000000000000000000000000000000000000000000000000000000000000"
- "ffffff"); // index
- const auto genesis_sequence_body = system::base16_chunk(
- "ffffffff" // sequence
- "0000000000" // input_fk-> (coinbase)
- "00000000"); // parent_fk->
- const auto genesis_input_body = system::base16_chunk(
- "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
- "00"); // witness
- const auto genesis_txs_body = system::base16_chunk(
- "1d0100" // size light (285)
- "1d0100" // size heavy (285)
- "0100" // txs count (1)
- "00000000" // transaction[0]
- "ff" // depth (255) - genesis only
- "00000000"); // forks (0) - genesis only
-
- settings settings{};
- settings.header.buckets = 8;
- settings.tx.buckets = 8;
- settings.ins.buckets = 8;
- settings.txs.buckets = 16;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_CHECK(!store.create(test::events_handler));
-
- // Set header, then txs from the parsed view of the serialized block.
- BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone));
- system::chain::block_view view{ test::genesis.to_data(true), true };
- BOOST_CHECK(view.is_valid());
- BOOST_CHECK_EQUAL(query.set_code(view, false, true), error::success);
- BOOST_CHECK(query.is_block(test::genesis.hash()));
- BOOST_CHECK(!store.close(test::events_handler));
-
- BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body);
- BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body);
- BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body);
- BOOST_CHECK_EQUAL(store.ins_sequence_body(), genesis_sequence_body);
- BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body);
- BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body);
- BOOST_CHECK_EQUAL(store.outs_body(), genesis_outs_body);
- BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body);
-
- const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false);
- BOOST_CHECK(pointer);
- BOOST_CHECK(*pointer == test::genesis);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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_wire_writer_writer_tests, test::directory_setup_fixture)
+
+// The view (wire) block writer must produce a store byte-identical to the
+// chain block writer (see analogous chain_writer expectations).
+BOOST_AUTO_TEST_CASE(query_wire_writer__set_block_view__genesis__expected)
+{
+ constexpr auto milestone = true;
+ const auto genesis_header_body = system::base16_chunk(
+ "ffff7f" // next->
+ "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" // sk (block.hash)
+ "04030201" // flags
+ "141312" // height
+ "24232221" // mtp
+ "ffffff" // previous_block_hash (header_fk - not found) (milestone true)
+ "01000000" // version
+ "29ab5f49" // timestamp
+ "ffff001d" // bits
+ "1dac2b7c" // nonce
+ "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"); // merkle_root
+ const auto genesis_tx_body = system::base16_chunk(
+ "ffffff7f" // next->
+ "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" // sk (tx.hash(false))
+ "cc0080" // light (coinbase merged)
+ "cc0000" // heavy
+ "00000000" // locktime
+ "01000000" // version
+ "010000" // ins_count
+ "010000" // outs_count
+ "00000000" // point_fk-> (ins_fk)
+ "00000000"); // outs_fk->
+ const auto genesis_outs_body = system::base16_chunk(
+ "0000000000"); // output0_fk->
+ const auto genesis_output_body = system::base16_chunk(
+ "00000000" // parent_fk->
+ "ff00f2052a01000000" // value
+ "434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"); // script
+ const auto genesis_ins_body = system::base16_chunk(
+ "ffffffff" // next->
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "ffffff"); // index
+ const auto genesis_sequence_body = system::base16_chunk(
+ "ffffffff" // sequence
+ "0000000000" // input_fk-> (coinbase)
+ "00000000"); // parent_fk->
+ const auto genesis_input_body = system::base16_chunk(
+ "4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73" // script
+ "00"); // witness
+ const auto genesis_txs_body = system::build_chunk(
+ {
+ system::base16_chunk(
+ "1d0100" // size light (285)
+ "1d0100" // size heavy (285)
+ "0100" // txs count (1)
+ "00000000" // transaction[0]
+ "01" // envelope version (1) - genesis only
+ "ff00" // interval depth (255)
+ "08000000" // header buckets (8)
+ "08000000" // ins buckets (8)
+ "80000000" // outs buckets (128)
+ "08000000" // tx buckets (8)
+ "80000000" // strong_tx buckets (128)
+ "80000000" // duplicate buckets (128)
+ "80000000" // validated_tx buckets (128)
+ "01"), // filter (true)
+ system::data_chunk(231, 0x00)
+ });
+
+ settings settings{};
+ settings.header.buckets = 8;
+ settings.tx.buckets = 8;
+ settings.ins.buckets = 8;
+ settings.txs.buckets = 16;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_CHECK(!store.create(test::events_handler));
+
+ // Set header, then txs from the parsed view of the serialized block.
+ BOOST_CHECK(query.set(test::genesis.header(), test::context, milestone));
+ system::chain::block_view view{ test::genesis.to_data(true), true };
+ BOOST_CHECK(view.is_valid());
+ BOOST_CHECK_EQUAL(query.set_code(view, false, true), error::success);
+ BOOST_CHECK(query.is_block(test::genesis.hash()));
+ BOOST_CHECK(!store.close(test::events_handler));
+
+ BOOST_CHECK_EQUAL(store.header_body(), genesis_header_body);
+ BOOST_CHECK_EQUAL(store.tx_body(), genesis_tx_body);
+ BOOST_CHECK_EQUAL(store.ins_body(), genesis_ins_body);
+ BOOST_CHECK_EQUAL(store.ins_sequence_body(), genesis_sequence_body);
+ BOOST_CHECK_EQUAL(store.input_body(), genesis_input_body);
+ BOOST_CHECK_EQUAL(store.output_body(), genesis_output_body);
+ BOOST_CHECK_EQUAL(store.outs_body(), genesis_outs_body);
+ BOOST_CHECK_EQUAL(store.txs_body(), genesis_txs_body);
+
+ const auto pointer = query.get_block(query.to_header(test::genesis.hash()), false);
+ BOOST_CHECK(pointer);
+ BOOST_CHECK(*pointer == test::genesis);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/query/extent.cpp b/test/query/extent.cpp
index 5ffb9c5fe..c13efc5b7 100644
--- a/test/query/extent.cpp
+++ b/test/query/extent.cpp
@@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected)
BOOST_REQUIRE_EQUAL(query.input_body_size(), 79u);
BOOST_REQUIRE_EQUAL(query.ins_body_size(), schema::ins::minrow + schema::ins_sequence::minrow);
BOOST_REQUIRE_EQUAL(query.outs_body_size(), schema::address::minrow + schema::outs::minrow);
- BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow + one + schema::flags);
+ BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow + settings.envelope.serialized_size());
BOOST_REQUIRE_EQUAL(query.tx_body_size(), schema::transaction::minrow);
BOOST_REQUIRE_EQUAL(query.candidate_body_size(), zero);
diff --git a/test/settings.cpp b/test/settings.cpp
index fd94ff8a3..01abe828e 100644
--- a/test/settings.cpp
+++ b/test/settings.cpp
@@ -26,7 +26,15 @@ BOOST_AUTO_TEST_CASE(settings__construct__default__expected)
BOOST_REQUIRE_EQUAL(configuration.turbo, false);
BOOST_REQUIRE_EQUAL(configuration.mark_unconfirmable, true);
BOOST_REQUIRE_EQUAL(configuration.interval_depth, 255u);
- BOOST_REQUIRE_EQUAL(configuration.fork_flags, 0u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.interval_depth, 255u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.header_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.ins_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.outs_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.tx_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.strong_tx_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.duplicate_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.validated_tx_buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.envelope.filter, true);
BOOST_REQUIRE_EQUAL(configuration.path, "bitcoin");
// Archives.
diff --git a/test/tables/archives/txs.cpp b/test/tables/archives/txs.cpp
index 6547690a0..c145e04e6 100644
--- a/test/tables/archives/txs.cpp
+++ b/test/tables/archives/txs.cpp
@@ -30,12 +30,11 @@ const table::txs::slab slab0
0x000000,
0x000000,
{
- // tx fk 0 uniquely identifies genesis, resulting in depth storage.
+ // tx fk 0 uniquely identifies genesis, resulting in envelope storage.
0x00000000_u32
},
{}, // interval (unused due to default span)
- 0x42, // depth (genesis only)
- 0x12345678 // flags (genesis only)
+ {} // envelope (genesis only)
};
const table::txs::slab slab1
{
@@ -67,26 +66,29 @@ const table::txs::slab slab3
0x56341233_u32
}
};
-const data_chunk expected0
+const data_chunk expected0 = build_chunk(
{
- // slab0 (light) [0x00]
- 0x00, 0x00, 0x00,
+ data_chunk
+ {
+ // slab0 (light) [0x00]
+ 0x00, 0x00, 0x00,
- // slab0 (heavy) [0x00]
- 0x00, 0x00, 0x00,
+ // slab0 (heavy) [0x00]
+ 0x00, 0x00, 0x00,
- // slab0 (count) [1]
- 0x01, 0x00,
+ // slab0 (count) [1]
+ 0x01, 0x00,
- // slab0 (txs)
- 0x00, 0x00, 0x00, 0x00,
+ // slab0 (txs)
+ 0x00, 0x00, 0x00, 0x00,
- // depth (genesis)
- 0x42,
+ // envelope version (genesis)
+ 0x01
+ },
- // flags (genesis)
- 0x78, 0x56, 0x34, 0x12
-};
+ // default envelope body (genesis)
+ data_chunk(262, 0x00)
+});
const data_chunk expected1
{
// slab1 (light) [0x0000ab]
@@ -151,8 +153,7 @@ BOOST_AUTO_TEST_CASE(txs__put__get__expected)
BOOST_CHECK(slab == slab0);
BOOST_CHECK_EQUAL(body_store.buffer(), build_chunk({ expected0 }));
- slab.depth = {};
- slab.forks = {};
+ slab.envelope = {};
BOOST_CHECK(instance.put(key, slab1));
BOOST_CHECK(instance.exists(key));
BOOST_CHECK(instance.at(key, slab));