diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am
index c7f10e158..b759cf5b8 100644
--- a/builds/gnu/Makefile.am
+++ b/builds/gnu/Makefile.am
@@ -227,6 +227,7 @@ include_bitcoin_database_memory_HEADERS = \
${srcdir}/../../include/bitcoin/database/memory/mmaps.hpp \
${srcdir}/../../include/bitcoin/database/memory/mstage.hpp \
${srcdir}/../../include/bitcoin/database/memory/reader.hpp \
+ ${srcdir}/../../include/bitcoin/database/memory/settings.hpp \
${srcdir}/../../include/bitcoin/database/memory/streamers.hpp \
${srcdir}/../../include/bitcoin/database/memory/utilities.hpp
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
index 49769e11e..77b1138e2 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
@@ -161,6 +161,7 @@
+
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
index 85293bd77..134ca4e1f 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -197,6 +197,9 @@
include\bitcoin\database\memory
+
+ include\bitcoin\database\memory
+
include\bitcoin\database\memory
diff --git a/include/bitcoin/database/impl/memory/mmap.ipp b/include/bitcoin/database/impl/memory/mmap.ipp
index b2cbc8ade..1c77a4d80 100644
--- a/include/bitcoin/database/impl/memory/mmap.ipp
+++ b/include/bitcoin/database/impl/memory/mmap.ipp
@@ -32,12 +32,12 @@ namespace database {
// ----------------------------------------------------------------------------
TEMPLATE
-CLASS::mmap(const path& filename, size_t minimum, size_t expansion,
+CLASS::mmap(const path& filename, const storage_settings& settings,
bool random, bool staged) NOEXCEPT
requires (is_one(columns))
: filenames_{ filename },
- minimum_(to_rows(minimum)),
- expansion_(expansion),
+ minimum_(to_rows(settings.size)),
+ expansion_(settings.rate),
random_(random),
staged_(staged),
opened_{ file::invalid }
@@ -45,12 +45,12 @@ CLASS::mmap(const path& filename, size_t minimum, size_t expansion,
}
TEMPLATE
-CLASS::mmap(const paths& filenames, size_t minimum, size_t expansion,
+CLASS::mmap(const paths& filenames, const storage_settings& settings,
bool random, bool staged) NOEXCEPT
requires (columns > one)
: filenames_(filenames),
- minimum_(to_rows(minimum)),
- expansion_(expansion),
+ minimum_(to_rows(settings.size)),
+ expansion_(settings.rate),
random_(random),
staged_(staged),
opened_{}
diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp
index 7f9b35a28..a9ee300d9 100644
--- a/include/bitcoin/database/impl/store/store.ipp
+++ b/include/bitcoin/database/impl/store/store.ipp
@@ -31,79 +31,79 @@ CLASS::store(const settings& config) NOEXCEPT
// Archive.
// ------------------------------------------------------------------------
- header_head_(head(config.path / schema::dir::heads, schema::archive::header), 1, 0, random),
- header_body_(body(config.path, schema::archive::header), config.header_size, config.header_rate, sequential, staged),
+ header_head_(head(config.path / schema::dir::heads, schema::archive::header), head_settings, random),
+ header_body_(body(config.path, schema::archive::header), config.header, sequential, staged),
- input_head_(head(config.path / schema::dir::heads, schema::archive::input), 1, 0, sequential),
- input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate, sequential, staged),
+ input_head_(head(config.path / schema::dir::heads, schema::archive::input), head_settings, sequential),
+ input_body_(body(config.path, schema::archive::input), config.input, sequential, staged),
- output_head_(head(config.path / schema::dir::heads, schema::archive::output), 1, 0, sequential),
- output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate, sequential, staged),
+ output_head_(head(config.path / schema::dir::heads, schema::archive::output), head_settings, sequential),
+ output_body_(body(config.path, schema::archive::output), config.output, sequential, staged),
- ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), 1, 0, random),
- ins_body_(body(config.path, schema::archive::ins), config.ins_size, config.ins_rate, sequential, staged),
+ ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), head_settings, random),
+ ins_body_(body(config.path, schema::archive::ins), config.ins, sequential, staged),
- outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), 1, 0, sequential),
- outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate, sequential, staged),
+ outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), head_settings, sequential),
+ outs_body_(body(config.path, schema::archive::outs), config.outs, sequential, staged),
- tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), 1, 0, random),
- tx_body_(body(config.path, schema::archive::tx), config.tx_size, config.tx_rate, sequential, staged),
+ tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), head_settings, random),
+ tx_body_(body(config.path, schema::archive::tx), config.tx, sequential, staged),
- txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), 1, 0, random),
- txs_body_(body(config.path, schema::archive::txs), config.txs_size, config.txs_rate, sequential, staged),
+ txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), head_settings, random),
+ txs_body_(body(config.path, schema::archive::txs), config.txs, sequential, staged),
// Indexes.
// ------------------------------------------------------------------------
- candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), 1, 0, sequential),
- candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate, sequential, staged),
+ candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), head_settings, sequential),
+ candidate_body_(body(config.path, schema::indexes::candidate), config.candidate, sequential, staged),
- confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), 1, 0, sequential),
- confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate, sequential, staged),
+ confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), head_settings, sequential),
+ confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed, sequential, staged),
- strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), 1, 0, random),
- strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx_size, config.strong_tx_rate, sequential, staged),
+ strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), head_settings, random),
+ strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx, sequential, staged),
// Caches.
// ------------------------------------------------------------------------
// TODO: body not random, but keep in memory.
- ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), 1, 0, sequential),
- ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa_size, config.ecdsa_rate, sequential, staged),
+ ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), head_settings, sequential),
+ ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa, sequential, staged),
// TODO: body not random, but keep in memory.
- schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), 1, 0, sequential),
- schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr_size, config.schnorr_rate, sequential, staged),
+ schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), head_settings, sequential),
+ schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr, sequential, staged),
- silent_head_(head(config.path / schema::dir::heads, schema::caches::silent), 1, 0, sequential),
- silent_body_(body(config.path, schema::caches::silent), config.silent_size, config.silent_rate, sequential, staged),
+ silent_head_(head(config.path / schema::dir::heads, schema::caches::silent), head_settings, sequential),
+ silent_body_(body(config.path, schema::caches::silent), config.silent, sequential, staged),
- duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), 1, 0, random),
- duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate_size, config.duplicate_rate, sequential, staged),
+ duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), head_settings, random),
+ duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate, sequential, staged),
- prevalid_head_(head(config.path / schema::dir::heads, schema::caches::prevalid), 1, 0, sequential),
- prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid_size, config.prevalid_rate, sequential, staged),
+ prevalid_head_(head(config.path / schema::dir::heads, schema::caches::prevalid), head_settings, sequential),
+ prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid, sequential, staged),
- prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), 1, 0, random),
- prevout_body_(body(config.path, schema::caches::prevout), config.prevout_size, config.prevout_rate, sequential, staged),
+ prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), head_settings, random),
+ prevout_body_(body(config.path, schema::caches::prevout), config.prevout, sequential, staged),
- validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), 1, 0, random),
- validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk_size, config.validated_bk_rate, sequential, staged),
+ validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), head_settings, random),
+ validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk, sequential, staged),
- validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), 1, 0, random),
- validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx_size, config.validated_tx_rate, sequential, staged),
+ validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), head_settings, random),
+ validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx, sequential, staged),
// Optionals.
// ------------------------------------------------------------------------
- address_head_(head(config.path / schema::dir::heads, schema::optionals::address), 1, 0, random),
- address_body_(body(config.path, schema::optionals::address), config.address_size, config.address_rate, sequential, staged),
+ address_head_(head(config.path / schema::dir::heads, schema::optionals::address), head_settings, random),
+ address_body_(body(config.path, schema::optionals::address), config.address, sequential, staged),
- filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), 1, 0, random),
- filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk_size, config.filter_bk_rate, sequential, staged),
+ filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), head_settings, random),
+ filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk, sequential, staged),
- filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), 1, 0, random),
- filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx_size, config.filter_tx_rate, sequential, staged),
+ filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), head_settings, random),
+ filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx, sequential, staged),
// Locks.
// ------------------------------------------------------------------------
@@ -114,30 +114,30 @@ CLASS::store(const settings& config) NOEXCEPT
// Tables.
// ------------------------------------------------------------------------
- header(header_head_, header_body_, config.header_buckets),
+ header(header_head_, header_body_, config.header.buckets),
input(input_head_, input_body_),
output(output_head_, output_body_),
- ins(ins_head_, ins_body_, config.ins_buckets),
+ ins(ins_head_, ins_body_, config.ins.buckets),
outs(outs_head_, outs_body_),
- tx(tx_head_, tx_body_, config.tx_buckets),
- txs(txs_head_, txs_body_, config.txs_buckets),
+ tx(tx_head_, tx_body_, config.tx.buckets),
+ txs(txs_head_, txs_body_, config.txs.buckets),
candidate(candidate_head_, candidate_body_),
confirmed(confirmed_head_, confirmed_body_),
- strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx_buckets),
+ strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx.buckets),
ecdsa(ecdsa_head_, ecdsa_body_),
schnorr(schnorr_head_, schnorr_body_),
silent(silent_head_, silent_body_),
- duplicate(duplicate_head_, duplicate_body_, config.duplicate_buckets),
+ duplicate(duplicate_head_, duplicate_body_, config.duplicate.buckets),
prevalid(prevalid_head_, prevalid_body_),
- prevout(prevout_head_, prevout_body_, config.prevout_buckets),
- validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk_buckets),
- validated_tx(validated_tx_head_, validated_tx_body_, config.validated_tx_buckets),
+ prevout(prevout_head_, prevout_body_, config.prevout.buckets),
+ validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk.buckets),
+ validated_tx(validated_tx_head_, validated_tx_body_, config.validated_tx.buckets),
- address(address_head_, address_body_, config.address_buckets),
- filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk_buckets),
- filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx_buckets)
+ address(address_head_, address_body_, config.address.buckets),
+ filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk.buckets),
+ filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx.buckets)
{
}
diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp
index 548c6d1b2..b8d286dfd 100644
--- a/include/bitcoin/database/memory/mmap.hpp
+++ b/include/bitcoin/database/memory/mmap.hpp
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
namespace libbitcoin {
namespace database {
@@ -68,13 +69,16 @@ class mmap
/// file only by explicit transfer, so no dirty file-backed page ever
/// exists (no effect where the staging backend is not built).
- /// Scalar construction (columns == 1): unchanged signature and codegen.
- mmap(const path& filename, size_t minimum=1, size_t expansion=0,
+ /// Storage tuning is passed by settings (future tunables land there
+ /// without signature change); random and staged are structural.
+
+ /// Scalar construction (columns == 1).
+ mmap(const path& filename, const storage_settings& settings={},
bool random=true, bool staged=false) NOEXCEPT
requires (is_one(columns));
/// Aggregate construction (columns > 1): one file per column, shared guards.
- mmap(const paths& filenames, size_t minimum=1, size_t expansion=0,
+ mmap(const paths& filenames, const storage_settings& settings={},
bool random=true, bool staged=false) NOEXCEPT
requires (columns > one);
diff --git a/include/bitcoin/database/memory/mmaps.hpp b/include/bitcoin/database/memory/mmaps.hpp
index 8f42eceb3..daa7d1d70 100644
--- a/include/bitcoin/database/memory/mmaps.hpp
+++ b/include/bitcoin/database/memory/mmaps.hpp
@@ -51,17 +51,18 @@ class mmaps
}...
};
- mmaps(const path& base_path, size_t size, size_t rate,
+ mmaps(const path& base_path, const storage_settings& settings,
bool random_access, bool staged=false) NOEXCEPT
- : mmaps(base_path, size, rate, random_access, staged, sequence)
+ : mmaps(base_path, settings, random_access, staged, sequence)
{
}
protected:
template
- mmaps(const path& base_path, size_t size, size_t rate, bool random_access,
- bool staged, std::index_sequence) NOEXCEPT
- : base(paths{ to_subpath(base_path, suffixes[Index])... }, size, rate,
+ mmaps(const path& base_path, const storage_settings& settings,
+ bool random_access, bool staged,
+ std::index_sequence) NOEXCEPT
+ : base(paths{ to_subpath(base_path, suffixes[Index])... }, settings,
random_access, staged),
base_path_{ base_path }
{
diff --git a/include/bitcoin/database/memory/settings.hpp b/include/bitcoin/database/memory/settings.hpp
new file mode 100644
index 000000000..64bf2b94e
--- /dev/null
+++ b/include/bitcoin/database/memory/settings.hpp
@@ -0,0 +1,42 @@
+/**
+ * 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_MEMORY_SETTINGS_HPP
+#define LIBBITCOIN_DATABASE_MEMORY_SETTINGS_HPP
+
+#include
+
+namespace libbitcoin {
+namespace database {
+
+/// Storage tuning consumed by memory map construction: the base of table
+/// configuration, as network settings bases are derived by server services.
+/// Future staging tunables land here without constructor signature changes.
+struct storage_settings
+{
+ /// Minimum body allocation (bytes).
+ uint64_t size{ 1 };
+
+ /// Body expansion rate (percentage).
+ uint16_t rate{ 5 };
+};
+
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp
index a41f63abe..545879273 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
@@ -36,6 +37,23 @@ struct BCD_API settings
settings() NOEXCEPT;
settings(system::chain::selection context) NOEXCEPT;
+ /// Table settings.
+ /// -----------------------------------------------------------------------
+
+ /// Body storage sizing (all tables), the storage construction base.
+ struct simple_table
+ : public storage_settings
+ {
+ };
+
+ /// Adds the head bucket count (hashmap hash space or arraymap length).
+ struct bucket_table
+ : public simple_table
+ {
+ /// Head bucket count.
+ uint32_t buckets{ 128 };
+ };
+
/// Properties.
/// -----------------------------------------------------------------------
@@ -57,90 +75,39 @@ struct BCD_API settings
/// Archives.
/// -----------------------------------------------------------------------
- uint32_t header_buckets;
- uint64_t header_size;
- uint16_t header_rate;
-
- uint64_t input_size;
- uint16_t input_rate;
-
- uint64_t output_size;
- uint16_t output_rate;
-
- uint32_t ins_buckets;
- uint64_t ins_size;
- uint16_t ins_rate;
-
- uint64_t outs_size;
- uint16_t outs_rate;
-
- uint32_t tx_buckets;
- uint64_t tx_size;
- uint16_t tx_rate;
-
- uint32_t txs_buckets;
- uint64_t txs_size;
- uint16_t txs_rate;
+ bucket_table header{};
+ simple_table input{};
+ simple_table output{};
+ bucket_table ins{};
+ simple_table outs{};
+ bucket_table tx{};
+ bucket_table txs{};
/// Indexes.
/// -----------------------------------------------------------------------
- uint64_t candidate_size;
- uint16_t candidate_rate;
-
- uint64_t confirmed_size;
- uint16_t confirmed_rate;
-
- uint32_t strong_tx_buckets;
- uint64_t strong_tx_size;
- uint16_t strong_tx_rate;
+ simple_table candidate{};
+ simple_table confirmed{};
+ bucket_table strong_tx{};
/// Caches.
/// -----------------------------------------------------------------------
- uint64_t ecdsa_size;
- uint16_t ecdsa_rate;
-
- uint64_t schnorr_size;
- uint16_t schnorr_rate;
-
- uint64_t silent_size;
- uint16_t silent_rate;
-
- // This one is 16 bit (could use table link::integer) for these.
- uint16_t duplicate_buckets;
- uint64_t duplicate_size;
- uint16_t duplicate_rate;
-
- uint64_t prevalid_size;
- uint16_t prevalid_rate;
-
- uint32_t prevout_buckets;
- uint64_t prevout_size;
- uint16_t prevout_rate;
-
- uint32_t validated_bk_buckets;
- uint64_t validated_bk_size;
- uint16_t validated_bk_rate;
-
- uint32_t validated_tx_buckets;
- uint64_t validated_tx_size;
- uint16_t validated_tx_rate;
+ simple_table ecdsa{};
+ simple_table schnorr{};
+ simple_table silent{};
+ bucket_table duplicate{};
+ simple_table prevalid{};
+ bucket_table prevout{};
+ bucket_table validated_bk{};
+ bucket_table validated_tx{};
/// Optionals.
/// -----------------------------------------------------------------------
- uint32_t address_buckets;
- uint64_t address_size;
- uint16_t address_rate;
-
- uint32_t filter_bk_buckets;
- uint64_t filter_bk_size;
- uint16_t filter_bk_rate;
-
- uint32_t filter_tx_buckets;
- uint64_t filter_tx_size;
- uint16_t filter_tx_rate;
+ bucket_table address{};
+ bucket_table filter_bk{};
+ bucket_table filter_tx{};
};
} // namespace database
diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp
index 08c5ddfef..4ef68cebf 100644
--- a/include/bitcoin/database/store.hpp
+++ b/include/bitcoin/database/store.hpp
@@ -234,6 +234,10 @@ class store
static constexpr bool random = true;
static constexpr bool sequential = false;
+ // Heads are minimally allocated with no expansion (heads size to their
+ // configured buckets at table creation).
+ static constexpr storage_settings head_settings{ 1, 0 };
+
// Bodies are append-only, so stage writes in anonymous memory where the
// staging backend is built (heads update in place and remain resident).
static constexpr bool staged = true;
diff --git a/include/bitcoin/database/tables/caches/ecdsa.hpp b/include/bitcoin/database/tables/caches/ecdsa.hpp
index 85d864090..02748e810 100644
--- a/include/bitcoin/database/tables/caches/ecdsa.hpp
+++ b/include/bitcoin/database/tables/caches/ecdsa.hpp
@@ -423,9 +423,10 @@ class ecdsa_storage
: public ecdsa_files
{
public:
- ecdsa_storage(const std::filesystem::path& path, size_t size,
- size_t rate, bool random_access, bool staged=false) NOEXCEPT
- : ecdsa_files(path, size, rate, random_access, staged)
+ ecdsa_storage(const std::filesystem::path& path,
+ const storage_settings& settings, bool random_access,
+ bool staged=false) NOEXCEPT
+ : ecdsa_files(path, settings, random_access, staged)
{
}
};
diff --git a/include/bitcoin/database/tables/caches/schnorr.hpp b/include/bitcoin/database/tables/caches/schnorr.hpp
index 9e0256ee6..9f7f4659a 100644
--- a/include/bitcoin/database/tables/caches/schnorr.hpp
+++ b/include/bitcoin/database/tables/caches/schnorr.hpp
@@ -250,9 +250,10 @@ class schnorr_storage
: public schnorr_files
{
public:
- schnorr_storage(const std::filesystem::path& path, size_t size,
- size_t rate, bool random_access, bool staged=false) NOEXCEPT
- : schnorr_files(path, size, rate, random_access, staged)
+ schnorr_storage(const std::filesystem::path& path,
+ const storage_settings& settings, bool random_access,
+ bool staged=false) NOEXCEPT
+ : schnorr_files(path, settings, random_access, staged)
{
}
};
diff --git a/include/bitcoin/database/tables/caches/silent.hpp b/include/bitcoin/database/tables/caches/silent.hpp
index 40005d97d..e27e3ae30 100644
--- a/include/bitcoin/database/tables/caches/silent.hpp
+++ b/include/bitcoin/database/tables/caches/silent.hpp
@@ -153,9 +153,10 @@ class silent_storage
: public silent_files
{
public:
- silent_storage(const std::filesystem::path& path, size_t size,
- size_t rate, bool random_access, bool staged=false) NOEXCEPT
- : silent_files(path, size, rate, random_access, staged)
+ silent_storage(const std::filesystem::path& path,
+ const storage_settings& settings, bool random_access,
+ bool staged=false) NOEXCEPT
+ : silent_files(path, settings, random_access, staged)
{
}
};
diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp
index 025b3bdd3..c71defd0a 100644
--- a/include/bitcoin/database/tables/schema.hpp
+++ b/include/bitcoin/database/tables/schema.hpp
@@ -52,7 +52,7 @@ constexpr size_t hash = system::hash_size;
/// Primary keys.
/// -----------------------------------------------------------------------
-constexpr size_t dup = 2; // ->duplicate [TODO: increase to 3]
+constexpr size_t dup = 3; // ->duplicate
constexpr size_t put = 5; // ->input/output slab.
constexpr size_t ins_ = 4; // ->point|ins record.
constexpr size_t outs_ = 4; // ->outs (puts) record.
@@ -312,8 +312,8 @@ struct duplicate
static constexpr size_t cell = sizeof(uint32_t);
static constexpr link count() NOEXCEPT { return 1; }
static_assert(minsize == 0u);
- static_assert(minrow == 37u);
- static_assert(link::size == 2u);
+ static_assert(minrow == 38u);
+ static_assert(link::size == 3u);
static_assert(cell == 4u);
};
diff --git a/src/define.cpp b/src/define.cpp
index 52bd3c530..d1b6efd41 100644
--- a/src/define.cpp
+++ b/src/define.cpp
@@ -1,33 +1,33 @@
-/**
- * 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
-
-// version :
-// boost : version
-// error : boost
-// define : error
-// settings : define
-// /locks : define
-// /file : define
-// /memory : /file
-// /primitives : /memory
-// /tables : /primitives
-// /types : /tables
-// store : /types settings /locks
-// query : /types settings
+/**
+ * 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
+
+// version :
+// boost : version
+// error : boost
+// define : error
+// settings : define
+// /locks : define
+// /file : define
+// /memory : /file settings
+// /primitives : /memory
+// /tables : /primitives
+// /types : /tables
+// store : /types settings /locks
+// query : /types settings
diff --git a/src/settings.cpp b/src/settings.cpp
index d956ded4c..d936fe468 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -26,89 +26,6 @@ namespace database {
using namespace bc::system;
settings::settings() NOEXCEPT
- :
- // Archives.
-
- header_buckets{ 128 },
- header_size{ 1 },
- header_rate{ 50 },
-
- input_size{ 1 },
- input_rate{ 50 },
-
- output_size{ 1 },
- output_rate{ 50 },
-
- ins_buckets{ 128 },
- ins_size{ 1 },
- ins_rate{ 50 },
-
- outs_size{ 1 },
- outs_rate{ 50 },
-
- tx_buckets{ 128 },
- tx_size{ 1 },
- tx_rate{ 50 },
-
- txs_buckets{ 128 },
- txs_size{ 1 },
- txs_rate{ 50 },
-
- // Indexes.
-
- candidate_size{ 1 },
- candidate_rate{ 50 },
-
- confirmed_size{ 1 },
- confirmed_rate{ 50 },
-
- strong_tx_buckets{ 128 },
- strong_tx_size{ 1 },
- strong_tx_rate{ 50 },
-
- // Caches.
-
- ecdsa_size{ 1 },
- ecdsa_rate{ 50 },
-
- schnorr_size{ 1 },
- schnorr_rate{ 50 },
-
- silent_size{ 1 },
- silent_rate{ 50 },
-
- duplicate_buckets{ 128 },
- duplicate_size{ 1 },
- duplicate_rate{ 50 },
-
- prevalid_size{ 1 },
- prevalid_rate{ 50 },
-
- prevout_buckets{ 128 },
- prevout_size{ 1 },
- prevout_rate{ 50 },
-
- validated_bk_buckets{ 128 },
- validated_bk_size{ 1 },
- validated_bk_rate{ 50 },
-
- validated_tx_buckets{ 128 },
- validated_tx_size{ 1 },
- validated_tx_rate{ 50 },
-
- // Optionals.
-
- address_buckets{ 128 },
- address_size{ 1 },
- address_rate{ 50 },
-
- filter_bk_buckets{ 128 },
- filter_bk_size{ 1 },
- filter_bk_rate{ 50 },
-
- filter_tx_buckets{ 128 },
- filter_tx_size{ 1 },
- filter_tx_rate{ 50 }
{
}
diff --git a/test/memory/mmap.cpp b/test/memory/mmap.cpp
index c03692f2e..46a2e4bc2 100644
--- a/test/memory/mmap.cpp
+++ b/test/memory/mmap.cpp
@@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__loaded__expected_capacity)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, half_rate);
+ map instance(file, { minimum, half_rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.capacity(), minimum);
@@ -325,7 +325,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__minimum_no_expansion__expected_capacity)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, 0);
+ map instance(file, { minimum, 0 });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.allocate(size), zero);
@@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__no_minimum_expansion__expected_capacity)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, rate);
+ map instance(file, { minimum, rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.allocate(size), zero);
@@ -392,7 +392,7 @@ BOOST_AUTO_TEST_CASE(mmap__truncate__increase_capacity__failure)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, half_rate);
+ map instance(file, { minimum, half_rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.capacity(), minimum);
@@ -410,7 +410,7 @@ BOOST_AUTO_TEST_CASE(mmap__truncate__unchanged__success_capacity_unchanged)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, half_rate);
+ map instance(file, { minimum, half_rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.size(), zero);
@@ -431,7 +431,7 @@ BOOST_AUTO_TEST_CASE(mmap__truncate__decrease__success_logical_decreased_capacit
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, half_rate);
+ map instance(file, { minimum, half_rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.allocate(minimum), zero);
@@ -466,7 +466,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__loaded__expected_capacity)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, half_rate);
+ map instance(file, { minimum, half_rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE_EQUAL(instance.capacity(), minimum);
@@ -512,7 +512,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__minimum_no_expansion__expected_capacity)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, rate);
+ map instance(file, { minimum, rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE(instance.get_filled(offset, size, fill));
@@ -535,7 +535,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__no_minimum_expansion__expected_capacity)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, rate);
+ map instance(file, { minimum, rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
BOOST_REQUIRE(instance.get_filled(offset, size, fill));
@@ -560,7 +560,7 @@ BOOST_AUTO_TEST_CASE(mmap__get_filled__loaded__expected_fill)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, minimum, half_rate);
+ map instance(file, { minimum, half_rate });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -701,7 +701,8 @@ BOOST_AUTO_TEST_CASE(mmap__get_capacity__size__expected)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file);
+ // No expansion, so capacity equals the allocation.
+ map instance(file, { 1, 0 });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -826,7 +827,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__write_flush_write_reload__expected)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, 1, 50, true, true);
+ map instance(file, { 1, 50 }, true, true);
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -862,7 +863,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__write_flush_write_reload__expected)
BOOST_REQUIRE(!instance.get_fault());
// All content persists across close and reopen.
- map reopened(file, 1, 50, true, true);
+ map reopened(file, { 1, 50 }, true, true);
BOOST_REQUIRE(!reopened.open());
BOOST_REQUIRE(!reopened.load());
BOOST_REQUIRE_EQUAL(reopened.size(), first + second);
@@ -887,7 +888,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__truncate_below_flush_rewrite__expected)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, 1, 50, true, true);
+ map instance(file, { 1, 50 }, true, true);
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -920,7 +921,7 @@ BOOST_AUTO_TEST_CASE(mmap__staged__truncate_below_flush_rewrite__expected)
BOOST_REQUIRE(!instance.get_fault());
// Retained content precedes replacement content across reopen.
- map reopened(file, 1, 50, true, true);
+ map reopened(file, { 1, 50 }, true, true);
BOOST_REQUIRE(!reopened.open());
BOOST_REQUIRE(!reopened.load());
BOOST_REQUIRE_EQUAL(reopened.size(), retained + appended);
@@ -944,7 +945,7 @@ BOOST_AUTO_TEST_CASE(mmap__frontier__staged_out_of_order_completion__expected)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, 1, 50, true, true);
+ map instance(file, { 1, 50 }, true, true);
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -971,7 +972,7 @@ BOOST_AUTO_TEST_CASE(mmap__frontier__staged_truncate__discards_extents)
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));
- map instance(file, 1, 50, true, true);
+ map instance(file, { 1, 50 }, true, true);
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -999,7 +1000,7 @@ BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected)
BOOST_REQUIRE(test::create(file));
// Unstaged (default) retains in-place rewrite of flushed content (heads).
- map instance(file, 1, 50, true);
+ map instance(file, { 1, 50 }, true);
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -1031,7 +1032,7 @@ BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected)
BOOST_REQUIRE(!instance.close());
BOOST_REQUIRE(!instance.get_fault());
- map reopened(file, 1, 50, true);
+ map reopened(file, { 1, 50 }, true);
BOOST_REQUIRE(!reopened.open());
BOOST_REQUIRE(!reopened.load());
BOOST_REQUIRE_EQUAL(reopened.size(), size);
@@ -1078,7 +1079,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__aggregate_remap__expected_geometry)
const storage_t::paths files{ file + "_0", file + "_1" };
// minimum 1 byte -> 1 row; zero expansion makes capacity == rows exactly.
- storage_t instance(files, 1, 0);
+ storage_t instance(files, { 1, 0 });
BOOST_REQUIRE(!instance.create());
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
@@ -1163,7 +1164,7 @@ BOOST_AUTO_TEST_CASE(mmap__allocate__concurrent__unique_dense_claims)
BOOST_REQUIRE(test::create(file));
// Tiny minimum and modest expansion force frequent slow-path remaps.
- map instance(file, 1, 25);
+ map instance(file, { 1, 25 });
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());
diff --git a/test/mocks/chunk_storage.hpp b/test/mocks/chunk_storage.hpp
index c3a6d88e6..db87d72c0 100644
--- a/test/mocks/chunk_storage.hpp
+++ b/test/mocks/chunk_storage.hpp
@@ -34,9 +34,10 @@ class default_storage
public:
using path = std::filesystem::path;
- default_storage(const path& filename="test", size_t minimum=1,
- size_t expansion=0, bool random=true) NOEXCEPT
- : Storage(filename, minimum, expansion, random)
+ default_storage(const path& filename="test",
+ const database::storage_settings& settings={},
+ bool random=true) NOEXCEPT
+ : Storage(filename, settings, random)
{
}
};
@@ -69,14 +70,16 @@ class chunk_storages
{
}
- chunk_storages(const path& filename, size_t=1, size_t=0, bool=true,
+ chunk_storages(const path& filename,
+ const database::storage_settings& ={}, bool=true,
bool=false) NOEXCEPT requires (is_one(columns))
: alias_{}, paths_{ filename }, logical_{}
{
}
/// Aggregate construction (columns > 1): one backing per column.
- chunk_storages(const paths& filenames, size_t=1, size_t=0, bool=true,
+ chunk_storages(const paths& filenames,
+ const database::storage_settings& ={}, bool=true,
bool=false) NOEXCEPT requires (columns > one)
: alias_{}, paths_{ filenames }, logical_{}
{
diff --git a/test/query/archive/chain_reader.cpp b/test/query/archive/chain_reader.cpp
index 57ab00c53..1cfd0e764 100644
--- a/test/query/archive/chain_reader.cpp
+++ b/test/query/archive/chain_reader.cpp
@@ -1,906 +1,906 @@
-/**
- * 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_reader_tests, test::directory_setup_fixture)
-
-// get_header
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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);
-}
-
-// is_coinbase
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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));
-}
-
-// is_tx_segregated
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__is_tx_segregated__always__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::block1a, context{}, false, false));
- BOOST_CHECK(!query.is_tx_segregated(0));
- BOOST_CHECK( query.is_tx_segregated(1));
- BOOST_CHECK(!query.is_tx_segregated(2));
-}
-
-// is_block_segregated
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__is_block_segregated__always__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::block1a, context{}, false, false));
- BOOST_CHECK(!query.is_block_segregated(0));
- BOOST_CHECK( query.is_block_segregated(1));
- BOOST_CHECK(!query.is_block_segregated(2));
-}
-
-// is_milestone
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__get_tx_position__strong__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_CHECK_EQUAL(query.get_position_tx(0, 0), 0u);
- BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u);
- BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u);
- BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u);
- BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u);
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__get_position_tx__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_EQUAL(query.get_position_tx(0, 0), 0u);
- BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u);
- BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u);
- BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u);
- BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u);
- BOOST_CHECK(query.get_position_tx(3, 1).is_terminal());
- BOOST_CHECK(query.get_position_tx({}, 0).is_terminal());
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__get_witness__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_witness(query.to_point({}, {})));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__get_input_script__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_script(query.to_point(tx_link{}, {})));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_point(tx_link{}, {}), false));
- BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), {}, false));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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();
- const auto point = query.to_point(query.to_tx(tx->hash(false)), 0u);
- BOOST_CHECK(*input == *query.get_input(point, false));
- BOOST_CHECK(input->witness() == *query.get_witness(point));
- BOOST_CHECK(input->script() == *query.get_input_script(point));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__get_output_script__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_script(query.to_output(system::null_hash, 0u)));
- BOOST_CHECK(!query.get_output(0));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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::null_hash, 0u)));
- BOOST_CHECK(!query.get_output(0));
- BOOST_CHECK(!store.close(test::events_handler));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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 output = tx->outputs_ptr()->front();
- BOOST_CHECK(*output == *query.get_output(0));
- BOOST_CHECK(*output == *query.get_output(query.to_tx(tx->hash(false)), 0u));
- BOOST_CHECK(*output == *query.get_output(query.to_output(tx->hash(false), 0u)));
- BOOST_CHECK(output->script() == *query.get_output_script(query.to_output(tx->hash(false), 0u)));
-}
-
-BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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);
-}
-
-// populate_with_metadata
-// ----------------------------------------------------------------------------
-
-// METADATA IS SETTABLE ON CONST TEST OBJECTS.
-// COPY CONSTRUCTION CREATES SAHRED 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_reader__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_reader__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_reader__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_reader__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_reader__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)));
-}
-
-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_reader_tests, test::directory_setup_fixture)
+
+// get_header
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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);
+}
+
+// is_coinbase
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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));
+}
+
+// is_tx_segregated
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__is_tx_segregated__always__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::block1a, context{}, false, false));
+ BOOST_CHECK(!query.is_tx_segregated(0));
+ BOOST_CHECK( query.is_tx_segregated(1));
+ BOOST_CHECK(!query.is_tx_segregated(2));
+}
+
+// is_block_segregated
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__is_block_segregated__always__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::block1a, context{}, false, false));
+ BOOST_CHECK(!query.is_block_segregated(0));
+ BOOST_CHECK( query.is_block_segregated(1));
+ BOOST_CHECK(!query.is_block_segregated(2));
+}
+
+// is_milestone
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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_reader__get_tx_position__strong__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_CHECK_EQUAL(query.get_position_tx(0, 0), 0u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u);
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__get_position_tx__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_EQUAL(query.get_position_tx(0, 0), 0u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(1, 0), 1u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(2, 0), 2u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(2, 1), 3u);
+ BOOST_CHECK_EQUAL(query.get_position_tx(3, 0), 4u);
+ BOOST_CHECK(query.get_position_tx(3, 1).is_terminal());
+ BOOST_CHECK(query.get_position_tx({}, 0).is_terminal());
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__get_witness__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_witness(query.to_point({}, {})));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__get_input_script__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_script(query.to_point(tx_link{}, {})));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_point(tx_link{}, {}), false));
+ BOOST_CHECK(!query.get_input(query.to_tx(system::null_hash), {}, false));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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();
+ const auto point = query.to_point(query.to_tx(tx->hash(false)), 0u);
+ BOOST_CHECK(*input == *query.get_input(point, false));
+ BOOST_CHECK(input->witness() == *query.get_witness(point));
+ BOOST_CHECK(input->script() == *query.get_input_script(point));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__get_output_script__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_script(query.to_output(system::null_hash, 0u)));
+ BOOST_CHECK(!query.get_output(0));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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::null_hash, 0u)));
+ BOOST_CHECK(!query.get_output(0));
+ BOOST_CHECK(!store.close(test::events_handler));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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 output = tx->outputs_ptr()->front();
+ BOOST_CHECK(*output == *query.get_output(0));
+ BOOST_CHECK(*output == *query.get_output(query.to_tx(tx->hash(false)), 0u));
+ BOOST_CHECK(*output == *query.get_output(query.to_output(tx->hash(false), 0u)));
+ BOOST_CHECK(output->script() == *query.get_output_script(query.to_output(tx->hash(false), 0u)));
+}
+
+BOOST_AUTO_TEST_CASE(query_chain_reader__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_reader__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_reader__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_reader__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_reader__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_reader__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);
+}
+
+// populate_with_metadata
+// ----------------------------------------------------------------------------
+
+// METADATA IS SETTABLE ON CONST TEST OBJECTS.
+// COPY CONSTRUCTION CREATES SAHRED 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_reader__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_reader__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_reader__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_reader__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_reader__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)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/query/archive/chain_writer.cpp b/test/query/archive/chain_writer.cpp
index 6bcf6d4a9..725b39383 100644
--- a/test/query/archive/chain_writer.cpp
+++ b/test/query/archive/chain_writer.cpp
@@ -1,1518 +1,1518 @@
-/**
- * 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_head4_array = system::base16_chunk("00000000");
- 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.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_head4_array);
- 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");
- 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.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.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");
- 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.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.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");
- 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.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");
- 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.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_head4_array = system::base16_chunk("00000000");
+ 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.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_head4_array);
+ 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");
+ 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.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.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");
+ 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.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.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");
+ 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.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");
+ 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.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 1cd2a4aaf..ee80619b1 100644
--- a/test/query/archive/wire_writer.cpp
+++ b/test/query/archive/wire_writer.cpp
@@ -1,110 +1,110 @@
-/**
- * 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::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()
diff --git a/test/query/extent.cpp b/test/query/extent.cpp
index 230749fa5..628f0d20a 100644
--- a/test/query/extent.cpp
+++ b/test/query/extent.cpp
@@ -1,173 +1,173 @@
-/**
- * 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_extent_tests, test::directory_setup_fixture)
-
-BOOST_AUTO_TEST_CASE(query__is_full__chunk_store__false)
-{
- const settings configuration{};
- test::chunk_store store{ configuration };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!query.is_full());
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- BOOST_REQUIRE_EQUAL(query.header_body_size(), schema::header::minrow);
- BOOST_REQUIRE_EQUAL(query.output_body_size(), 81u);
- 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::outs::minrow);
- BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow + one + schema::flags);
- BOOST_REQUIRE_EQUAL(query.tx_body_size(), schema::transaction::minrow);
-
- BOOST_REQUIRE_EQUAL(query.candidate_body_size(), schema::height::minrow);
- BOOST_REQUIRE_EQUAL(query.confirmed_body_size(), schema::height::minrow);
- BOOST_REQUIRE_EQUAL(query.strong_tx_body_size(), schema::strong_tx::minrow);
- BOOST_REQUIRE_EQUAL(query.ecdsa_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.schnorr_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.silent_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.duplicate_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.prevalid_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.prevout_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.validated_bk_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.validated_tx_body_size(), zero);
- BOOST_REQUIRE_EQUAL(query.filter_bk_body_size(), schema::filter_bk::minrow);
- BOOST_REQUIRE_EQUAL(query.filter_tx_body_size(), 5u);
- BOOST_REQUIRE_EQUAL(query.address_body_size(), schema::address::minrow);
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__buckets__genesis__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- BOOST_REQUIRE_EQUAL(query.header_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.ins_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.txs_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.tx_buckets(), 128u);
-
- BOOST_REQUIRE_EQUAL(query.strong_tx_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.duplicate_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.prevout_buckets(), 128);
- BOOST_REQUIRE_EQUAL(query.validated_tx_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.validated_bk_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.filter_tx_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.filter_bk_buckets(), 128u);
- BOOST_REQUIRE_EQUAL(query.address_buckets(), 128u);
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__records__genesis__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- BOOST_REQUIRE_EQUAL(query.header_records(), one);
- BOOST_REQUIRE_EQUAL(query.ins_records(), one);
- BOOST_REQUIRE_EQUAL(query.tx_records(), one);
-
- BOOST_REQUIRE_EQUAL(query.candidate_records(), one);
- BOOST_REQUIRE_EQUAL(query.confirmed_records(), one);
- BOOST_REQUIRE_EQUAL(query.strong_tx_records(), one);
- BOOST_REQUIRE_EQUAL(query.ecdsa_records(), zero);
- BOOST_REQUIRE_EQUAL(query.schnorr_records(), zero);
- BOOST_REQUIRE_EQUAL(query.silent_records(), zero);
- BOOST_REQUIRE_EQUAL(query.duplicate_records(), zero);
- BOOST_REQUIRE_EQUAL(query.prevalid_records(), zero);
- BOOST_REQUIRE_EQUAL(query.filter_bk_records(), one);
- BOOST_REQUIRE_EQUAL(query.address_records(), one);
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__input_output_count__genesis__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- BOOST_REQUIRE_EQUAL(query.input_count(0), one);
- BOOST_REQUIRE_EQUAL(query.output_count(0), one);
- BOOST_REQUIRE_EQUAL(query.put_counts(0).first, one);
- BOOST_REQUIRE_EQUAL(query.put_counts(0).second, one);
-
- BOOST_REQUIRE_EQUAL(query.input_count(1), zero);
- BOOST_REQUIRE_EQUAL(query.output_count(1), zero);
- BOOST_REQUIRE_EQUAL(query.put_counts(1).first, zero);
- BOOST_REQUIRE_EQUAL(query.put_counts(1).second, zero);
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__optionals_enabled__default__true)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.address_enabled());
- BOOST_REQUIRE(query.filter_enabled());
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__address_enabled__disabled__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- settings.address_buckets = 0;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(!query.address_enabled());
- BOOST_REQUIRE(query.filter_enabled());
-}
-
-BOOST_AUTO_TEST_CASE(query_extent__filter_enabled__disabled__false)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- settings.filter_tx_buckets = 0;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.address_enabled());
- BOOST_REQUIRE(!query.filter_enabled());
-}
-
-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_extent_tests, test::directory_setup_fixture)
+
+BOOST_AUTO_TEST_CASE(query__is_full__chunk_store__false)
+{
+ const settings configuration{};
+ test::chunk_store store{ configuration };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!query.is_full());
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ BOOST_REQUIRE_EQUAL(query.header_body_size(), schema::header::minrow);
+ BOOST_REQUIRE_EQUAL(query.output_body_size(), 81u);
+ 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::outs::minrow);
+ BOOST_REQUIRE_EQUAL(query.txs_body_size(), schema::txs::minrow + one + schema::flags);
+ BOOST_REQUIRE_EQUAL(query.tx_body_size(), schema::transaction::minrow);
+
+ BOOST_REQUIRE_EQUAL(query.candidate_body_size(), schema::height::minrow);
+ BOOST_REQUIRE_EQUAL(query.confirmed_body_size(), schema::height::minrow);
+ BOOST_REQUIRE_EQUAL(query.strong_tx_body_size(), schema::strong_tx::minrow);
+ BOOST_REQUIRE_EQUAL(query.ecdsa_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.schnorr_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.silent_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.duplicate_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.prevalid_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.prevout_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.validated_bk_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.validated_tx_body_size(), zero);
+ BOOST_REQUIRE_EQUAL(query.filter_bk_body_size(), schema::filter_bk::minrow);
+ BOOST_REQUIRE_EQUAL(query.filter_tx_body_size(), 5u);
+ BOOST_REQUIRE_EQUAL(query.address_body_size(), schema::address::minrow);
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__buckets__genesis__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ BOOST_REQUIRE_EQUAL(query.header_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.ins_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.txs_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.tx_buckets(), 128u);
+
+ BOOST_REQUIRE_EQUAL(query.strong_tx_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.duplicate_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.prevout_buckets(), 128);
+ BOOST_REQUIRE_EQUAL(query.validated_tx_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.validated_bk_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.filter_tx_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.filter_bk_buckets(), 128u);
+ BOOST_REQUIRE_EQUAL(query.address_buckets(), 128u);
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__records__genesis__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ BOOST_REQUIRE_EQUAL(query.header_records(), one);
+ BOOST_REQUIRE_EQUAL(query.ins_records(), one);
+ BOOST_REQUIRE_EQUAL(query.tx_records(), one);
+
+ BOOST_REQUIRE_EQUAL(query.candidate_records(), one);
+ BOOST_REQUIRE_EQUAL(query.confirmed_records(), one);
+ BOOST_REQUIRE_EQUAL(query.strong_tx_records(), one);
+ BOOST_REQUIRE_EQUAL(query.ecdsa_records(), zero);
+ BOOST_REQUIRE_EQUAL(query.schnorr_records(), zero);
+ BOOST_REQUIRE_EQUAL(query.silent_records(), zero);
+ BOOST_REQUIRE_EQUAL(query.duplicate_records(), zero);
+ BOOST_REQUIRE_EQUAL(query.prevalid_records(), zero);
+ BOOST_REQUIRE_EQUAL(query.filter_bk_records(), one);
+ BOOST_REQUIRE_EQUAL(query.address_records(), one);
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__input_output_count__genesis__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ BOOST_REQUIRE_EQUAL(query.input_count(0), one);
+ BOOST_REQUIRE_EQUAL(query.output_count(0), one);
+ BOOST_REQUIRE_EQUAL(query.put_counts(0).first, one);
+ BOOST_REQUIRE_EQUAL(query.put_counts(0).second, one);
+
+ BOOST_REQUIRE_EQUAL(query.input_count(1), zero);
+ BOOST_REQUIRE_EQUAL(query.output_count(1), zero);
+ BOOST_REQUIRE_EQUAL(query.put_counts(1).first, zero);
+ BOOST_REQUIRE_EQUAL(query.put_counts(1).second, zero);
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__optionals_enabled__default__true)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.address_enabled());
+ BOOST_REQUIRE(query.filter_enabled());
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__address_enabled__disabled__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ settings.address.buckets = 0;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(!query.address_enabled());
+ BOOST_REQUIRE(query.filter_enabled());
+}
+
+BOOST_AUTO_TEST_CASE(query_extent__filter_enabled__disabled__false)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ settings.filter_tx.buckets = 0;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.address_enabled());
+ BOOST_REQUIRE(!query.filter_enabled());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/query/navigate/navigate_reverse.cpp b/test/query/navigate/navigate_reverse.cpp
index c02f9fdef..f29cb8760 100644
--- a/test/query/navigate/navigate_reverse.cpp
+++ b/test/query/navigate/navigate_reverse.cpp
@@ -1,448 +1,448 @@
-/**
- * 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_navigate_tests, test::directory_setup_fixture)
-
-const auto& address0 = test::block1a_address0;
-const auto& address1 = test::block1a_address1;
-
-// to_parent
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_parent__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE_EQUAL(query.to_parent(0), header_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_parent(1), 0u);
- BOOST_REQUIRE_EQUAL(query.to_parent(2), 1u);
- BOOST_REQUIRE_EQUAL(query.to_parent(3), 0u);
- BOOST_REQUIRE_EQUAL(query.to_parent(4), 3u);
- BOOST_REQUIRE_EQUAL(query.to_parent(5), header_link::terminal);
-}
-
-// to_touched_txs1
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs1__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links links{};
- BOOST_REQUIRE(!query.to_address_outputs(links, address1));
-
- tx_links out{};
- BOOST_REQUIRE(!query.to_touched_txs(out, links));
- BOOST_REQUIRE_EQUAL(out.size(), 4u);
-
- // owners
- BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1)
-
- // spenders of (0)
- BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3)
- BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4
- BOOST_REQUIRE_EQUAL(out.at(3), 6u); // block3a (tx6)
-}
-
-// to_touched_txs2
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs2__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links links{};
- const std::atomic_bool cancel{};
- BOOST_REQUIRE(!query.to_address_outputs(cancel, links, address0));
-
- tx_links out{};
- BOOST_REQUIRE(!query.to_touched_txs(cancel, out, links));
- BOOST_REQUIRE_EQUAL(out.size(), 10u);
-
- // owners
- BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1)
-
- // spenders of (0)
- BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3)
- BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4
- BOOST_REQUIRE_EQUAL(out.at(3), 5u); // tx5
- BOOST_REQUIRE_EQUAL(out.at(4), 6u); // block3a (tx6)
-
- // owners (unspent)
- BOOST_REQUIRE_EQUAL(out.at(5), 2u); // block2a (tx2) [duplicate]
- BOOST_REQUIRE_EQUAL(out.at(6), 3u); // block2a (tx3)
- BOOST_REQUIRE_EQUAL(out.at(7), 4u); // tx4 [duplicate]
- BOOST_REQUIRE_EQUAL(out.at(8), 5u); // tx5 [duplicate]
- BOOST_REQUIRE_EQUAL(out.at(9), 6u); // block3a (tx6) [duplicate]
-}
-
-// to_address_outputs1
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs1__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links out{};
- BOOST_REQUIRE(!query.to_address_outputs(out, address1));
-
- // There is 1 instance of the `script{ { { opcode::roll } } }` output.
- BOOST_REQUIRE_EQUAL(out.size(), 1u);
- BOOST_REQUIRE_EQUAL(out.front(), 88u);
-}
-
-// to_address_outputs2
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs2__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links out{};
- const std::atomic_bool cancel{};
- BOOST_REQUIRE(!query.to_address_outputs(cancel, out, address0));
-
- // There are 6 instances of the `script{ { { opcode::pick } } }` output.
- BOOST_REQUIRE_EQUAL(out.size(), 6u);
- BOOST_REQUIRE_EQUAL(out.at(0), 123u);
- BOOST_REQUIRE_EQUAL(out.at(1), 116u);
- BOOST_REQUIRE_EQUAL(out.at(2), 109u);
- BOOST_REQUIRE_EQUAL(out.at(3), 102u);
- BOOST_REQUIRE_EQUAL(out.at(4), 95u);
- BOOST_REQUIRE_EQUAL(out.at(5), 81u);
-}
-
-// to_address_outputs3
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__terminal__not_reduced)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links out{};
- address_link end{};
- const std::atomic_bool cancel{};
- BOOST_REQUIRE(!query.to_address_outputs(cancel, end, out, address0, max_size_t));
-
- // There are 6 instances of the `script{ { { opcode::pick } } }` output.
- BOOST_REQUIRE_EQUAL(out.size(), 6u);
- BOOST_REQUIRE_EQUAL(out.at(0), 123u);
- BOOST_REQUIRE_EQUAL(out.at(1), 116u);
- BOOST_REQUIRE_EQUAL(out.at(2), 109u);
- BOOST_REQUIRE_EQUAL(out.at(3), 102u);
- BOOST_REQUIRE_EQUAL(out.at(4), 95u);
- BOOST_REQUIRE_EQUAL(out.at(5), 81u);
-}
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__limit__depth_limited)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links out{};
- address_link end{};
- const std::atomic_bool cancel{};
- BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, end, out, address0, 4), error::depth_limited);
-
- // The limit is applied before deduplication.
- // There are 6 instances of the `script{ { { opcode::pick } } }` output, limited to 4.
- BOOST_REQUIRE_EQUAL(out.size(), 4u);
- BOOST_REQUIRE_EQUAL(out.at(0), 123u);
- BOOST_REQUIRE_EQUAL(out.at(1), 116u);
- BOOST_REQUIRE_EQUAL(out.at(2), 109u);
- BOOST_REQUIRE_EQUAL(out.at(3), 102u);
- ////BOOST_REQUIRE_EQUAL(out.at(4), 95u);
- ////BOOST_REQUIRE_EQUAL(out.at(5), 81u);
-}
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_mismatch__populated_invalid_cursor)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links out{};
- address_link cursor{ 4242 };
- const std::atomic_bool cancel{};
- const auto ec = query.to_address_outputs(cancel, cursor, out, address0, max_size_t);
- BOOST_REQUIRE_EQUAL(ec, error::invalid_cursor);
-
- // The end was not found but the full list is returned.
- BOOST_REQUIRE_EQUAL(out.size(), 6u);
- BOOST_REQUIRE_EQUAL(out.at(0), 123u);
- BOOST_REQUIRE_EQUAL(out.at(1), 116u);
- BOOST_REQUIRE_EQUAL(out.at(2), 109u);
- BOOST_REQUIRE_EQUAL(out.at(3), 102u);
- BOOST_REQUIRE_EQUAL(out.at(4), 95u);
- BOOST_REQUIRE_EQUAL(out.at(5), 81u);
-}
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_match__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
-
- output_links out{};
- address_link cursor{ 3 };
- const std::atomic_bool cancel{};
- BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
- BOOST_REQUIRE_EQUAL(cursor.value, 7u);
-
- // The stop was found so partial list is returned.
- BOOST_REQUIRE_EQUAL(out.size(), 4u);
- BOOST_REQUIRE_EQUAL(out.at(0), 123u);
- BOOST_REQUIRE_EQUAL(out.at(1), 116u);
- BOOST_REQUIRE_EQUAL(out.at(2), 109u);
- BOOST_REQUIRE_EQUAL(out.at(3), 102u);
-}
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
-
- output_links out{};
- address_link cursor{};
- const std::atomic_bool cancel{};
-
- // Add three unconfirmed blocks and two txs, with 7 outputs, 6 matching address.
- BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
- BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
- BOOST_REQUIRE_EQUAL(cursor.value, 7u);
- BOOST_REQUIRE_EQUAL(out.size(), 6u);
- BOOST_REQUIRE_EQUAL(out.at(0), 123u);
- BOOST_REQUIRE_EQUAL(out.at(1), 116u);
- BOOST_REQUIRE_EQUAL(out.at(2), 109u);
- BOOST_REQUIRE_EQUAL(out.at(3), 102u);
- BOOST_REQUIRE_EQUAL(out.at(4), 95u);
- BOOST_REQUIRE_EQUAL(out.at(5), 81u);
-
- // Add two unconfirmed blocks with 3 outputs, all matching address.
- BOOST_REQUIRE(query.set(test::block1b, database::context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2b, database::context{ 0, 2, 0 }, false, false));
- BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
- BOOST_REQUIRE_EQUAL(cursor.value, 10u);
- BOOST_REQUIRE_EQUAL(out.size(), 3u);
- BOOST_REQUIRE_EQUAL(out.at(0), 144u);
- BOOST_REQUIRE_EQUAL(out.at(1), 137u);
- BOOST_REQUIRE_EQUAL(out.at(2), 130u);
-
- // Add one tx with one output, matching address.
- BOOST_REQUIRE(query.set(test::tx2b));
- BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
- BOOST_REQUIRE_EQUAL(cursor.value, 11u);
- BOOST_REQUIRE_EQUAL(out.size(), 1u);
- BOOST_REQUIRE_EQUAL(out.at(0), 151u);
-
- // No changes to this address since cursor.
- BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
- BOOST_REQUIRE_EQUAL(cursor.value, 11u);
- BOOST_REQUIRE(out.empty());
-}
-
-// to_input_tx
-// to_output_tx
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_output_tx__to_output__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
-
- // All 5 blocks have one transaction with 1 output.
- BOOST_REQUIRE_EQUAL(query.to_output_tx(0 * 0x51), 0u);
- BOOST_REQUIRE_EQUAL(query.to_output_tx(1 * 0x51), 1u);
- BOOST_REQUIRE_EQUAL(query.to_output_tx(2 * 0x51), 2u);
- BOOST_REQUIRE_EQUAL(query.to_output_tx(3 * 0x51), 3u);
- BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51), 4u);
- BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 7u), 4u);
-
- BOOST_REQUIRE_EQUAL(query.to_output(0, 0), 0u * 0x51u);
- BOOST_REQUIRE_EQUAL(query.to_output(1, 0), 1u * 0x51u);
- BOOST_REQUIRE_EQUAL(query.to_output(2, 0), 2u * 0x51u);
- BOOST_REQUIRE_EQUAL(query.to_output(3, 0), 3u * 0x51u);
- BOOST_REQUIRE_EQUAL(query.to_output(4, 0), 4u * 0x51u);
- BOOST_REQUIRE_EQUAL(query.to_output(4, 1), 4u * 0x51u + 7u);
-
- const output_links expected_outputs4{ 4 * 0x51, 4 * 0x51 + 7 };
- BOOST_REQUIRE_EQUAL(query.to_outputs(0), output_links{ 0 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_outputs(1), output_links{ 1 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_outputs(2), output_links{ 2 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_outputs(3), output_links{ 3 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_outputs(4), expected_outputs4);
-
- // All blocks have one transaction.
- BOOST_REQUIRE_EQUAL(query.to_block_outputs(0), output_links{ 0 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_block_outputs(1), output_links{ 1 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_block_outputs(2), output_links{ 2 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_block_outputs(3), output_links{ 3 * 0x51 });
- BOOST_REQUIRE_EQUAL(query.to_block_outputs(4), expected_outputs4);
-
- // No prevouts that exist.
- const output_links expected_prevouts4{ output_link::terminal, output_link::terminal, output_link::terminal };
- BOOST_REQUIRE_EQUAL(query.to_prevouts(0), output_links{ output_link::terminal });
- BOOST_REQUIRE_EQUAL(query.to_prevouts(1), output_links{ output_link::terminal });
- BOOST_REQUIRE_EQUAL(query.to_prevouts(2), output_links{ output_link::terminal });
- BOOST_REQUIRE_EQUAL(query.to_prevouts(3), output_links{ output_link::terminal });
- BOOST_REQUIRE_EQUAL(query.to_prevouts(4), expected_prevouts4);
-
- // All blocks have one transaction.
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{});
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{});
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), output_links{});
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(3), output_links{});
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(4), output_links{});
-
- // Past end.
- BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 14), tx_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_output(5, 0), output_link::terminal);
- BOOST_REQUIRE(query.to_outputs(5).empty());
- BOOST_REQUIRE(query.to_block_outputs(5).empty());
-}
-
-// to_prevout_tx
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_prevout_tx__to_prevout__expected)
-{
- settings settings{};
- settings.tx_buckets = 3;
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2a, context{ 0, 1, 0 }, false, false));
-
- // inputs in link order.
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(0), tx_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(1), tx_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(2), tx_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(3), tx_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(4), 1u); // block1a:0 (second serialized tx)
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(5), 1u); // block1a:0 (second serialized tx)
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(6), tx_link::terminal);
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(7), tx_link::terminal);
-
- const output_links expected_prevouts1{ output_link::terminal };
- const output_links expected_prevouts2{ output_link::terminal, output_link::terminal };
- const output_links expected_prevouts3{ output_link::terminal, output_link::terminal, output_link::terminal };
- const output_links expected_prevouts{ 0x51u, 0x51u + 7u };
- BOOST_REQUIRE_EQUAL(query.to_prevouts(0), expected_prevouts1);
- BOOST_REQUIRE_EQUAL(query.to_prevouts(1), expected_prevouts3);
- BOOST_REQUIRE_EQUAL(query.to_prevouts(2), expected_prevouts);
- BOOST_REQUIRE_EQUAL(query.to_prevouts(3), expected_prevouts2);
-
- // First tx is coinbase, or tx has undefined prevouts.
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{});
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{});
- BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), expected_prevouts2);
-
- // Past end.
- BOOST_REQUIRE_EQUAL(query.to_prevout_tx(8), tx_link::terminal);
-}
-
-// to_spenders1
-// to_spenders2
-// to_duplicates
-
-// to_block
-
-BOOST_AUTO_TEST_CASE(query_navigate__to_block__always__expected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
-
- class accessor
- : public test::query_accessor
- {
- public:
- using test::query_accessor::query_accessor;
- using test::query_accessor::to_block;
- };
-
- accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false));
-
- // Either not strong or not found, except genesis.
- BOOST_REQUIRE(!query.to_block(0).is_terminal());
- BOOST_REQUIRE(query.to_block(1).is_terminal());
- BOOST_REQUIRE(query.to_block(2).is_terminal());
- BOOST_REQUIRE(query.to_block(3).is_terminal());
-}
-
-// to_input_index
-// to_output_index
-
-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_navigate_tests, test::directory_setup_fixture)
+
+const auto& address0 = test::block1a_address0;
+const auto& address1 = test::block1a_address1;
+
+// to_parent
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_parent__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2a, context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE_EQUAL(query.to_parent(0), header_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_parent(1), 0u);
+ BOOST_REQUIRE_EQUAL(query.to_parent(2), 1u);
+ BOOST_REQUIRE_EQUAL(query.to_parent(3), 0u);
+ BOOST_REQUIRE_EQUAL(query.to_parent(4), 3u);
+ BOOST_REQUIRE_EQUAL(query.to_parent(5), header_link::terminal);
+}
+
+// to_touched_txs1
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs1__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links links{};
+ BOOST_REQUIRE(!query.to_address_outputs(links, address1));
+
+ tx_links out{};
+ BOOST_REQUIRE(!query.to_touched_txs(out, links));
+ BOOST_REQUIRE_EQUAL(out.size(), 4u);
+
+ // owners
+ BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1)
+
+ // spenders of (0)
+ BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3)
+ BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4
+ BOOST_REQUIRE_EQUAL(out.at(3), 6u); // block3a (tx6)
+}
+
+// to_touched_txs2
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_touched_txs2__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links links{};
+ const std::atomic_bool cancel{};
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, links, address0));
+
+ tx_links out{};
+ BOOST_REQUIRE(!query.to_touched_txs(cancel, out, links));
+ BOOST_REQUIRE_EQUAL(out.size(), 10u);
+
+ // owners
+ BOOST_REQUIRE_EQUAL(out.at(0), 1u); // block1a (tx1)
+
+ // spenders of (0)
+ BOOST_REQUIRE_EQUAL(out.at(1), 2u); // block2a (tx2/3)
+ BOOST_REQUIRE_EQUAL(out.at(2), 4u); // tx4
+ BOOST_REQUIRE_EQUAL(out.at(3), 5u); // tx5
+ BOOST_REQUIRE_EQUAL(out.at(4), 6u); // block3a (tx6)
+
+ // owners (unspent)
+ BOOST_REQUIRE_EQUAL(out.at(5), 2u); // block2a (tx2) [duplicate]
+ BOOST_REQUIRE_EQUAL(out.at(6), 3u); // block2a (tx3)
+ BOOST_REQUIRE_EQUAL(out.at(7), 4u); // tx4 [duplicate]
+ BOOST_REQUIRE_EQUAL(out.at(8), 5u); // tx5 [duplicate]
+ BOOST_REQUIRE_EQUAL(out.at(9), 6u); // block3a (tx6) [duplicate]
+}
+
+// to_address_outputs1
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs1__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links out{};
+ BOOST_REQUIRE(!query.to_address_outputs(out, address1));
+
+ // There is 1 instance of the `script{ { { opcode::roll } } }` output.
+ BOOST_REQUIRE_EQUAL(out.size(), 1u);
+ BOOST_REQUIRE_EQUAL(out.front(), 88u);
+}
+
+// to_address_outputs2
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs2__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links out{};
+ const std::atomic_bool cancel{};
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, out, address0));
+
+ // There are 6 instances of the `script{ { { opcode::pick } } }` output.
+ BOOST_REQUIRE_EQUAL(out.size(), 6u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 123u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 116u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 109u);
+ BOOST_REQUIRE_EQUAL(out.at(3), 102u);
+ BOOST_REQUIRE_EQUAL(out.at(4), 95u);
+ BOOST_REQUIRE_EQUAL(out.at(5), 81u);
+}
+
+// to_address_outputs3
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__terminal__not_reduced)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links out{};
+ address_link end{};
+ const std::atomic_bool cancel{};
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, end, out, address0, max_size_t));
+
+ // There are 6 instances of the `script{ { { opcode::pick } } }` output.
+ BOOST_REQUIRE_EQUAL(out.size(), 6u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 123u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 116u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 109u);
+ BOOST_REQUIRE_EQUAL(out.at(3), 102u);
+ BOOST_REQUIRE_EQUAL(out.at(4), 95u);
+ BOOST_REQUIRE_EQUAL(out.at(5), 81u);
+}
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__limit__depth_limited)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links out{};
+ address_link end{};
+ const std::atomic_bool cancel{};
+ BOOST_REQUIRE_EQUAL(query.to_address_outputs(cancel, end, out, address0, 4), error::depth_limited);
+
+ // The limit is applied before deduplication.
+ // There are 6 instances of the `script{ { { opcode::pick } } }` output, limited to 4.
+ BOOST_REQUIRE_EQUAL(out.size(), 4u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 123u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 116u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 109u);
+ BOOST_REQUIRE_EQUAL(out.at(3), 102u);
+ ////BOOST_REQUIRE_EQUAL(out.at(4), 95u);
+ ////BOOST_REQUIRE_EQUAL(out.at(5), 81u);
+}
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_mismatch__populated_invalid_cursor)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links out{};
+ address_link cursor{ 4242 };
+ const std::atomic_bool cancel{};
+ const auto ec = query.to_address_outputs(cancel, cursor, out, address0, max_size_t);
+ BOOST_REQUIRE_EQUAL(ec, error::invalid_cursor);
+
+ // The end was not found but the full list is returned.
+ BOOST_REQUIRE_EQUAL(out.size(), 6u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 123u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 116u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 109u);
+ BOOST_REQUIRE_EQUAL(out.at(3), 102u);
+ BOOST_REQUIRE_EQUAL(out.at(4), 95u);
+ BOOST_REQUIRE_EQUAL(out.at(5), 81u);
+}
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__stop_match__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+
+ output_links out{};
+ address_link cursor{ 3 };
+ const std::atomic_bool cancel{};
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
+ BOOST_REQUIRE_EQUAL(cursor.value, 7u);
+
+ // The stop was found so partial list is returned.
+ BOOST_REQUIRE_EQUAL(out.size(), 4u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 123u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 116u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 109u);
+ BOOST_REQUIRE_EQUAL(out.at(3), 102u);
+}
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_address_outputs3__progression__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+
+ output_links out{};
+ address_link cursor{};
+ const std::atomic_bool cancel{};
+
+ // Add three unconfirmed blocks and two txs, with 7 outputs, 6 matching address.
+ BOOST_REQUIRE(setup_three_block_unconfirmed_address_store(query));
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
+ BOOST_REQUIRE_EQUAL(cursor.value, 7u);
+ BOOST_REQUIRE_EQUAL(out.size(), 6u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 123u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 116u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 109u);
+ BOOST_REQUIRE_EQUAL(out.at(3), 102u);
+ BOOST_REQUIRE_EQUAL(out.at(4), 95u);
+ BOOST_REQUIRE_EQUAL(out.at(5), 81u);
+
+ // Add two unconfirmed blocks with 3 outputs, all matching address.
+ BOOST_REQUIRE(query.set(test::block1b, database::context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2b, database::context{ 0, 2, 0 }, false, false));
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
+ BOOST_REQUIRE_EQUAL(cursor.value, 10u);
+ BOOST_REQUIRE_EQUAL(out.size(), 3u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 144u);
+ BOOST_REQUIRE_EQUAL(out.at(1), 137u);
+ BOOST_REQUIRE_EQUAL(out.at(2), 130u);
+
+ // Add one tx with one output, matching address.
+ BOOST_REQUIRE(query.set(test::tx2b));
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
+ BOOST_REQUIRE_EQUAL(cursor.value, 11u);
+ BOOST_REQUIRE_EQUAL(out.size(), 1u);
+ BOOST_REQUIRE_EQUAL(out.at(0), 151u);
+
+ // No changes to this address since cursor.
+ BOOST_REQUIRE(!query.to_address_outputs(cancel, cursor, out, address0, max_size_t));
+ BOOST_REQUIRE_EQUAL(cursor.value, 11u);
+ BOOST_REQUIRE(out.empty());
+}
+
+// to_input_tx
+// to_output_tx
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_output_tx__to_output__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+
+ // All 5 blocks have one transaction with 1 output.
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(0 * 0x51), 0u);
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(1 * 0x51), 1u);
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(2 * 0x51), 2u);
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(3 * 0x51), 3u);
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51), 4u);
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 7u), 4u);
+
+ BOOST_REQUIRE_EQUAL(query.to_output(0, 0), 0u * 0x51u);
+ BOOST_REQUIRE_EQUAL(query.to_output(1, 0), 1u * 0x51u);
+ BOOST_REQUIRE_EQUAL(query.to_output(2, 0), 2u * 0x51u);
+ BOOST_REQUIRE_EQUAL(query.to_output(3, 0), 3u * 0x51u);
+ BOOST_REQUIRE_EQUAL(query.to_output(4, 0), 4u * 0x51u);
+ BOOST_REQUIRE_EQUAL(query.to_output(4, 1), 4u * 0x51u + 7u);
+
+ const output_links expected_outputs4{ 4 * 0x51, 4 * 0x51 + 7 };
+ BOOST_REQUIRE_EQUAL(query.to_outputs(0), output_links{ 0 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_outputs(1), output_links{ 1 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_outputs(2), output_links{ 2 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_outputs(3), output_links{ 3 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_outputs(4), expected_outputs4);
+
+ // All blocks have one transaction.
+ BOOST_REQUIRE_EQUAL(query.to_block_outputs(0), output_links{ 0 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_block_outputs(1), output_links{ 1 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_block_outputs(2), output_links{ 2 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_block_outputs(3), output_links{ 3 * 0x51 });
+ BOOST_REQUIRE_EQUAL(query.to_block_outputs(4), expected_outputs4);
+
+ // No prevouts that exist.
+ const output_links expected_prevouts4{ output_link::terminal, output_link::terminal, output_link::terminal };
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(0), output_links{ output_link::terminal });
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(1), output_links{ output_link::terminal });
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(2), output_links{ output_link::terminal });
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(3), output_links{ output_link::terminal });
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(4), expected_prevouts4);
+
+ // All blocks have one transaction.
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{});
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{});
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), output_links{});
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(3), output_links{});
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(4), output_links{});
+
+ // Past end.
+ BOOST_REQUIRE_EQUAL(query.to_output_tx(4 * 0x51 + 14), tx_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_output(5, 0), output_link::terminal);
+ BOOST_REQUIRE(query.to_outputs(5).empty());
+ BOOST_REQUIRE(query.to_block_outputs(5).empty());
+}
+
+// to_prevout_tx
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_prevout_tx__to_prevout__expected)
+{
+ settings settings{};
+ settings.tx.buckets = 3;
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1a, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2a, context{ 0, 1, 0 }, false, false));
+
+ // inputs in link order.
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(0), tx_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(1), tx_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(2), tx_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(3), tx_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(4), 1u); // block1a:0 (second serialized tx)
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(5), 1u); // block1a:0 (second serialized tx)
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(6), tx_link::terminal);
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(7), tx_link::terminal);
+
+ const output_links expected_prevouts1{ output_link::terminal };
+ const output_links expected_prevouts2{ output_link::terminal, output_link::terminal };
+ const output_links expected_prevouts3{ output_link::terminal, output_link::terminal, output_link::terminal };
+ const output_links expected_prevouts{ 0x51u, 0x51u + 7u };
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(0), expected_prevouts1);
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(1), expected_prevouts3);
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(2), expected_prevouts);
+ BOOST_REQUIRE_EQUAL(query.to_prevouts(3), expected_prevouts2);
+
+ // First tx is coinbase, or tx has undefined prevouts.
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(0), output_links{});
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(1), output_links{});
+ BOOST_REQUIRE_EQUAL(query.to_block_prevouts(2), expected_prevouts2);
+
+ // Past end.
+ BOOST_REQUIRE_EQUAL(query.to_prevout_tx(8), tx_link::terminal);
+}
+
+// to_spenders1
+// to_spenders2
+// to_duplicates
+
+// to_block
+
+BOOST_AUTO_TEST_CASE(query_navigate__to_block__always__expected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+
+ class accessor
+ : public test::query_accessor
+ {
+ public:
+ using test::query_accessor::query_accessor;
+ using test::query_accessor::to_block;
+ };
+
+ accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{ 0, 1, 0 }, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{ 0, 1, 0 }, false, false));
+
+ // Either not strong or not found, except genesis.
+ BOOST_REQUIRE(!query.to_block(0).is_terminal());
+ BOOST_REQUIRE(query.to_block(1).is_terminal());
+ BOOST_REQUIRE(query.to_block(2).is_terminal());
+ BOOST_REQUIRE(query.to_block(3).is_terminal());
+}
+
+// to_input_index
+// to_output_index
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/query/properties_tx.cpp b/test/query/properties_tx.cpp
index 8ec597eac..daf4b5b98 100644
--- a/test/query/properties_tx.cpp
+++ b/test/query/properties_tx.cpp
@@ -1,138 +1,138 @@
-/**
- * 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_properties_tx_tests, test::directory_setup_fixture)
-
-BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__invalid_link__unvalidated)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
-
- uint64_t fee{};
- size_t sigops{};
- BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated);
- BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated);
- BOOST_REQUIRE_EQUAL(fee, 0u);
-}
-
-BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__unvalidated__unvalidated)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
-
- uint64_t fee{};
- size_t sigops{};
- BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated);
- BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated);
- BOOST_REQUIRE_EQUAL(fee, 0u);
-}
-
-BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_out_of_context__unvalidated)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- settings.validated_tx_buckets = 1;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{}, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{}, false, false));
-
- uint64_t fee{};
- size_t sigops{};
- constexpr context ctx{ 7, 8, 9 };
-
- // Set a context which does not match ctx.
- BOOST_REQUIRE(query.set_tx_connected(0, { 1, 5, 9 }, 0, 0));
- BOOST_REQUIRE(query.set_tx_connected(1, { 2, 6, 0 }, 0, 0));
- BOOST_REQUIRE(query.set_tx_connected(2, { 3, 7, 1 }, 0, 0));
- BOOST_REQUIRE(query.set_tx_connected(3, { 4, 8, 2 }, 0, 0));
- BOOST_REQUIRE_EQUAL(query.get_tx_state(1, ctx), error::unvalidated);
- BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, ctx), error::unvalidated);
- BOOST_REQUIRE_EQUAL(fee, 0u);
- BOOST_REQUIRE_EQUAL(sigops, 0u);
-}
-
-BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_connected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- settings.validated_tx_buckets = 1;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{}, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{}, false, false));
-
- uint64_t fee{};
- size_t sigops{};
- constexpr uint64_t expected_fee = 42;
- constexpr size_t expected_sigops = 24;
- constexpr context ctx{ 7, 8, 9 };
- BOOST_REQUIRE(query.set_tx_connected(0, ctx, 11, 12));
- BOOST_REQUIRE(query.set_tx_connected(1, ctx, 13, 14));
- BOOST_REQUIRE(query.set_tx_connected(2, ctx, expected_fee, expected_sigops));
- BOOST_REQUIRE(query.set_tx_connected(2, { 1, 5, 9 }, 15, 16));
- BOOST_REQUIRE(query.set_tx_connected(2, { 2, 6, 0 }, 17, 18));
- BOOST_REQUIRE(query.set_tx_connected(3, ctx, 19, 20));
- BOOST_REQUIRE_EQUAL(query.get_tx_state(2, ctx), error::tx_connected);
- BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 2, ctx), error::tx_connected);
- BOOST_REQUIRE_EQUAL(fee, expected_fee);
- BOOST_REQUIRE_EQUAL(sigops, expected_sigops);
-}
-
-BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_disconnected)
-{
- settings settings{};
- settings.path = TEST_DIRECTORY;
- test::chunk_store store{ settings };
- test::query_accessor query{ store };
- BOOST_REQUIRE(!store.create(test::events_handler));
- BOOST_REQUIRE(query.initialize(test::genesis));
- BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
- BOOST_REQUIRE(query.set(test::block2, context{}, false, false));
- BOOST_REQUIRE(query.set(test::block3, context{}, false, false));
-
- uint64_t fee{};
- size_t sigops{};
- constexpr context ctx{ 7, 8, 9 };
- BOOST_REQUIRE(query.set_tx_disconnected(4, ctx));
- BOOST_REQUIRE_EQUAL(query.get_tx_state(4, ctx), error::tx_disconnected);
- BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 4, ctx), error::tx_disconnected);
- BOOST_REQUIRE_EQUAL(fee, 0u);
- BOOST_REQUIRE_EQUAL(sigops, 0u);
-}
-
-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_properties_tx_tests, test::directory_setup_fixture)
+
+BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__invalid_link__unvalidated)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+
+ uint64_t fee{};
+ size_t sigops{};
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated);
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated);
+ BOOST_REQUIRE_EQUAL(fee, 0u);
+}
+
+BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__unvalidated__unvalidated)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
+
+ uint64_t fee{};
+ size_t sigops{};
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(1, {}), error::unvalidated);
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, {}), error::unvalidated);
+ BOOST_REQUIRE_EQUAL(fee, 0u);
+}
+
+BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_out_of_context__unvalidated)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ settings.validated_tx.buckets = 1;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{}, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{}, false, false));
+
+ uint64_t fee{};
+ size_t sigops{};
+ constexpr context ctx{ 7, 8, 9 };
+
+ // Set a context which does not match ctx.
+ BOOST_REQUIRE(query.set_tx_connected(0, { 1, 5, 9 }, 0, 0));
+ BOOST_REQUIRE(query.set_tx_connected(1, { 2, 6, 0 }, 0, 0));
+ BOOST_REQUIRE(query.set_tx_connected(2, { 3, 7, 1 }, 0, 0));
+ BOOST_REQUIRE(query.set_tx_connected(3, { 4, 8, 2 }, 0, 0));
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(1, ctx), error::unvalidated);
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 1, ctx), error::unvalidated);
+ BOOST_REQUIRE_EQUAL(fee, 0u);
+ BOOST_REQUIRE_EQUAL(sigops, 0u);
+}
+
+BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_connected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ settings.validated_tx.buckets = 1;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{}, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{}, false, false));
+
+ uint64_t fee{};
+ size_t sigops{};
+ constexpr uint64_t expected_fee = 42;
+ constexpr size_t expected_sigops = 24;
+ constexpr context ctx{ 7, 8, 9 };
+ BOOST_REQUIRE(query.set_tx_connected(0, ctx, 11, 12));
+ BOOST_REQUIRE(query.set_tx_connected(1, ctx, 13, 14));
+ BOOST_REQUIRE(query.set_tx_connected(2, ctx, expected_fee, expected_sigops));
+ BOOST_REQUIRE(query.set_tx_connected(2, { 1, 5, 9 }, 15, 16));
+ BOOST_REQUIRE(query.set_tx_connected(2, { 2, 6, 0 }, 17, 18));
+ BOOST_REQUIRE(query.set_tx_connected(3, ctx, 19, 20));
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(2, ctx), error::tx_connected);
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 2, ctx), error::tx_connected);
+ BOOST_REQUIRE_EQUAL(fee, expected_fee);
+ BOOST_REQUIRE_EQUAL(sigops, expected_sigops);
+}
+
+BOOST_AUTO_TEST_CASE(query_properties_tx__get_tx_state__connected_in_context__tx_disconnected)
+{
+ settings settings{};
+ settings.path = TEST_DIRECTORY;
+ test::chunk_store store{ settings };
+ test::query_accessor query{ store };
+ BOOST_REQUIRE(!store.create(test::events_handler));
+ BOOST_REQUIRE(query.initialize(test::genesis));
+ BOOST_REQUIRE(query.set(test::block1, context{}, false, false));
+ BOOST_REQUIRE(query.set(test::block2, context{}, false, false));
+ BOOST_REQUIRE(query.set(test::block3, context{}, false, false));
+
+ uint64_t fee{};
+ size_t sigops{};
+ constexpr context ctx{ 7, 8, 9 };
+ BOOST_REQUIRE(query.set_tx_disconnected(4, ctx));
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(4, ctx), error::tx_disconnected);
+ BOOST_REQUIRE_EQUAL(query.get_tx_state(fee, sigops, 4, ctx), error::tx_disconnected);
+ BOOST_REQUIRE_EQUAL(fee, 0u);
+ BOOST_REQUIRE_EQUAL(sigops, 0u);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/settings.cpp b/test/settings.cpp
index 898ad3b9a..bb1c21f7c 100644
--- a/test/settings.cpp
+++ b/test/settings.cpp
@@ -1,95 +1,95 @@
-/**
- * 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"
-
-BOOST_AUTO_TEST_SUITE(settings_tests)
-
-BOOST_AUTO_TEST_CASE(settings__construct__default__expected)
-{
- database::settings configuration;
- 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.path, "bitcoin");
-
- // Archives.
- BOOST_REQUIRE_EQUAL(configuration.header_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.header_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.header_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.ins_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.ins_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.ins_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.input_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.input_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.output_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.output_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.outs_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.outs_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.tx_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.tx_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.tx_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.txs_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.txs_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.txs_rate, 50u);
-
- // Indexes.
- BOOST_REQUIRE_EQUAL(configuration.candidate_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.candidate_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.confirmed_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.confirmed_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.strong_tx_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.strong_tx_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.strong_tx_rate, 50u);
-
- // Caches.
- BOOST_REQUIRE_EQUAL(configuration.ecdsa_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.ecdsa_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.schnorr_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.schnorr_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.silent_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.silent_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.duplicate_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.duplicate_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.duplicate_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.prevalid_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.prevalid_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.prevout_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.prevout_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.prevout_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.validated_bk_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.validated_bk_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.validated_bk_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.validated_tx_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.validated_tx_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.validated_tx_rate, 50u);
-
- // Optionals.
- BOOST_REQUIRE_EQUAL(configuration.address_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.address_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.address_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.filter_bk_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.filter_bk_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.filter_bk_rate, 50u);
- BOOST_REQUIRE_EQUAL(configuration.filter_tx_buckets, 128u);
- BOOST_REQUIRE_EQUAL(configuration.filter_tx_size, 1u);
- BOOST_REQUIRE_EQUAL(configuration.filter_tx_rate, 50u);
-}
-
-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"
+
+BOOST_AUTO_TEST_SUITE(settings_tests)
+
+BOOST_AUTO_TEST_CASE(settings__construct__default__expected)
+{
+ database::settings configuration;
+ 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.path, "bitcoin");
+
+ // Archives.
+ BOOST_REQUIRE_EQUAL(configuration.header.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.header.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.header.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.ins.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.ins.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.ins.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.input.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.input.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.output.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.output.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.outs.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.outs.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.tx.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.tx.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.tx.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.txs.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.txs.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.txs.rate, 5u);
+
+ // Indexes.
+ BOOST_REQUIRE_EQUAL(configuration.candidate.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.candidate.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.confirmed.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.confirmed.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.strong_tx.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.strong_tx.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.strong_tx.rate, 5u);
+
+ // Caches.
+ BOOST_REQUIRE_EQUAL(configuration.ecdsa.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.ecdsa.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.schnorr.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.schnorr.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.silent.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.silent.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.duplicate.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.duplicate.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.duplicate.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.prevalid.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.prevalid.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.prevout.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.prevout.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.prevout.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.validated_bk.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.validated_bk.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.validated_bk.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.validated_tx.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.validated_tx.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.validated_tx.rate, 5u);
+
+ // Optionals.
+ BOOST_REQUIRE_EQUAL(configuration.address.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.address.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.address.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.filter_bk.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.filter_bk.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.filter_bk.rate, 5u);
+ BOOST_REQUIRE_EQUAL(configuration.filter_tx.buckets, 128u);
+ BOOST_REQUIRE_EQUAL(configuration.filter_tx.size, 1u);
+ BOOST_REQUIRE_EQUAL(configuration.filter_tx.rate, 5u);
+}
+
+BOOST_AUTO_TEST_SUITE_END()