diff --git a/Makefile.am b/Makefile.am
index 5dcaedea9..095699911 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -101,6 +101,7 @@ test_libbitcoin_database_test_SOURCES = \
test/query/properties_block.cpp \
test/query/properties_tx.cpp \
test/query/sequences.cpp \
+ test/query/signatures.cpp \
test/query/sizes.cpp \
test/query/address/address_balance.cpp \
test/query/address/address_history.cpp \
@@ -134,7 +135,9 @@ test_libbitcoin_database_test_SOURCES = \
test/tables/archives/transaction.cpp \
test/tables/archives/txs.cpp \
test/tables/caches/duplicate.cpp \
+ test/tables/caches/ecdsa.cpp \
test/tables/caches/prevout.cpp \
+ test/tables/caches/schnorr.cpp \
test/tables/caches/validated_bk.cpp \
test/tables/caches/validated_tx.cpp \
test/tables/indexes/height.cpp \
@@ -217,6 +220,7 @@ include_bitcoin_database_impl_query_HEADERS = \
include/bitcoin/database/impl/query/properties_tx.ipp \
include/bitcoin/database/impl/query/query.ipp \
include/bitcoin/database/impl/query/sequences.ipp \
+ include/bitcoin/database/impl/query/signatures.ipp \
include/bitcoin/database/impl/query/sizes.ipp
include_bitcoin_database_impl_query_addressdir = ${includedir}/bitcoin/database/impl/query/address
@@ -316,7 +320,9 @@ include_bitcoin_database_tables_archives_HEADERS = \
include_bitcoin_database_tables_cachesdir = ${includedir}/bitcoin/database/tables/caches
include_bitcoin_database_tables_caches_HEADERS = \
include/bitcoin/database/tables/caches/duplicate.hpp \
+ include/bitcoin/database/tables/caches/ecdsa.hpp \
include/bitcoin/database/tables/caches/prevout.hpp \
+ include/bitcoin/database/tables/caches/schnorr.hpp \
include/bitcoin/database/tables/caches/validated_bk.hpp \
include/bitcoin/database/tables/caches/validated_tx.hpp
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
index ed7e9387b..e580d4c1b 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj
@@ -184,6 +184,7 @@
+
@@ -196,7 +197,9 @@
+
+
diff --git a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
index 4aac69976..7a3eb8dd2 100644
--- a/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
@@ -228,6 +228,9 @@
src\query
+
+ src\query
+
src\query
@@ -264,9 +267,15 @@
src\tables\caches
+
+ src\tables\caches
+
src\tables\caches
+
+ src\tables\caches
+
src\tables\caches
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index 975ce9478..8f7f605e1 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -186,7 +186,9 @@
+
+
@@ -260,6 +262,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
index f867d21d4..9ce1ddeb1 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -269,9 +269,15 @@
include\bitcoin\database\tables\caches
+
+ include\bitcoin\database\tables\caches
+
include\bitcoin\database\tables\caches
+
+ include\bitcoin\database\tables\caches
+
include\bitcoin\database\tables\caches
@@ -487,6 +493,9 @@
include\bitcoin\database\impl\query
+
+ include\bitcoin\database\impl\query
+
include\bitcoin\database\impl\query
diff --git a/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj b/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj
index fb97dc000..ae03f69dd 100644
--- a/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj
@@ -184,6 +184,7 @@
+
@@ -196,7 +197,9 @@
+
+
diff --git a/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
index 4aac69976..7a3eb8dd2 100644
--- a/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-database-test/libbitcoin-database-test.vcxproj.filters
@@ -228,6 +228,9 @@
src\query
+
+ src\query
+
src\query
@@ -264,9 +267,15 @@
src\tables\caches
+
+ src\tables\caches
+
src\tables\caches
+
+ src\tables\caches
+
src\tables\caches
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
index 0642cc7e9..cd33d4049 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
@@ -186,7 +186,9 @@
+
+
@@ -260,6 +262,7 @@
+
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
index f867d21d4..9ce1ddeb1 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -269,9 +269,15 @@
include\bitcoin\database\tables\caches
+
+ include\bitcoin\database\tables\caches
+
include\bitcoin\database\tables\caches
+
+ include\bitcoin\database\tables\caches
+
include\bitcoin\database\tables\caches
@@ -487,6 +493,9 @@
include\bitcoin\database\impl\query
+
+ include\bitcoin\database\impl\query
+
include\bitcoin\database\impl\query
diff --git a/include/bitcoin/database.hpp b/include/bitcoin/database.hpp
index c7fbe314c..2959ad983 100644
--- a/include/bitcoin/database.hpp
+++ b/include/bitcoin/database.hpp
@@ -67,7 +67,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
diff --git a/include/bitcoin/database/impl/query/extent.ipp b/include/bitcoin/database/impl/query/extent.ipp
index b330dab21..d30e24579 100644
--- a/include/bitcoin/database/impl/query/extent.ipp
+++ b/include/bitcoin/database/impl/query/extent.ipp
@@ -91,6 +91,8 @@ size_t CLASS::store_body_size() const NOEXCEPT
+ candidate_body_size()
+ confirmed_body_size()
+ strong_tx_body_size()
+ + ecdsa_body_size()
+ + schnorr_body_size()
+ duplicate_body_size()
+ prevout_body_size()
+ validated_bk_body_size()
@@ -120,6 +122,8 @@ size_t CLASS::store_head_size() const NOEXCEPT
+ candidate_head_size()
+ confirmed_head_size()
+ strong_tx_head_size()
+ + ecdsa_head_size()
+ + schnorr_head_size()
+ duplicate_head_size()
+ prevout_head_size()
+ validated_bk_head_size()
@@ -144,6 +148,8 @@ DEFINE_SIZES(tx)
DEFINE_SIZES(candidate)
DEFINE_SIZES(confirmed)
DEFINE_SIZES(strong_tx)
+DEFINE_SIZES(ecdsa)
+DEFINE_SIZES(schnorr)
DEFINE_SIZES(duplicate)
DEFINE_SIZES(prevout)
DEFINE_SIZES(validated_bk)
@@ -181,6 +187,8 @@ DEFINE_RECORDS(tx)
DEFINE_RECORDS(candidate)
DEFINE_RECORDS(confirmed)
DEFINE_RECORDS(strong_tx)
+DEFINE_RECORDS(ecdsa)
+DEFINE_RECORDS(schnorr)
DEFINE_RECORDS(duplicate)
DEFINE_RECORDS(filter_bk)
DEFINE_RECORDS(address)
diff --git a/include/bitcoin/database/impl/query/signatures.ipp b/include/bitcoin/database/impl/query/signatures.ipp
new file mode 100644
index 000000000..9d352430c
--- /dev/null
+++ b/include/bitcoin/database/impl/query/signatures.ipp
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_DATABASE_QUERY_SIGNATURES_IPP
+#define LIBBITCOIN_DATABASE_QUERY_SIGNATURES_IPP
+
+#include
+
+namespace libbitcoin {
+namespace database {
+
+TEMPLATE
+bool CLASS::set_signature(const hash_digest& digest, const ec_compressed& point,
+ const ec_signature& signature, const header_link& link) NOEXCEPT
+{
+ // ========================================================================
+ const auto scope = store_.get_transactor();
+
+ // Clean single allocation failure (e.g. disk full).
+ return store_.ecdsa.put(table::ecdsa::record
+ {
+ {},
+ digest,
+ point,
+ signature,
+ link
+ });
+ // ========================================================================
+}
+
+TEMPLATE
+bool CLASS::set_signature(const hash_digest& digest, const ec_xonly& point,
+ const ec_signature& signature, const header_link& link) NOEXCEPT
+{
+ // ========================================================================
+ const auto scope = store_.get_transactor();
+
+ // Clean single allocation failure (e.g. disk full).
+ return store_.schnorr.put(table::schnorr::record
+ {
+ {},
+ digest,
+ point,
+ signature,
+ link
+ });
+ // ========================================================================
+}
+
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/impl/store.ipp b/include/bitcoin/database/impl/store.ipp
index 575859e2d..29199dced 100644
--- a/include/bitcoin/database/impl/store.ipp
+++ b/include/bitcoin/database/impl/store.ipp
@@ -103,6 +103,12 @@ const std::unordered_map CLASS::tables
{ table_t::strong_tx_body, "strong_tx_body" },
// Caches.
+ { table_t::ecdsa_table, "ecdsa_table" },
+ { table_t::ecdsa_head, "ecdsa_head" },
+ { table_t::ecdsa_body, "ecdsa_body" },
+ { table_t::schnorr_table, "schnorr_table" },
+ { table_t::schnorr_head, "schnorr_head" },
+ { table_t::schnorr_body, "schnorr_body" },
{ table_t::duplicate_table, "duplicate_table" },
{ table_t::duplicate_head, "duplicate_head" },
{ table_t::duplicate_body, "duplicate_body" },
@@ -137,90 +143,105 @@ CLASS::store(const settings& config) NOEXCEPT
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),
- header(header_head_, header_body_, config.header_buckets),
input_head_(head(config.path / schema::dir::heads, schema::archive::input), 1, 0, random),
input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate, sequential),
- input(input_head_, input_body_),
output_head_(head(config.path / schema::dir::heads, schema::archive::output), 1, 0, random),
output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate, sequential),
- output(output_head_, output_body_),
point_head_(head(config.path / schema::dir::heads, schema::archive::point), 1, 0, random),
point_body_(body(config.path, schema::archive::point), config.point_size, config.point_rate, sequential),
- point(point_head_, point_body_, config.point_buckets),
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),
- ins(ins_head_, ins_body_),
outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), 1, 0, random),
outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate, sequential),
- outs(outs_head_, outs_body_),
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),
- tx(tx_head_, tx_body_, config.tx_buckets),
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),
- txs(txs_head_, txs_body_, config.txs_buckets),
// Indexes.
// ------------------------------------------------------------------------
candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), 1, 0, random),
candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate, sequential),
- candidate(candidate_head_, candidate_body_),
confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), 1, 0, random),
confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate, sequential),
- confirmed(confirmed_head_, confirmed_body_),
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),
- strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx_buckets),
// Caches.
// ------------------------------------------------------------------------
+ ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), 1, 0, random),
+ ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa_size, config.ecdsa_rate, sequential),
+
+ schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), 1, 0, random),
+ schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr_size, config.schnorr_rate, sequential),
+
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),
- duplicate(duplicate_head_, duplicate_body_, config.duplicate_buckets),
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),
- prevout(prevout_head_, prevout_body_, config.prevout_buckets),
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),
- validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk_buckets),
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),
- validated_tx(validated_tx_head_, validated_tx_body_, config.validated_tx_buckets),
// 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),
- address(address_head_, address_body_, config.address_buckets),
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),
- filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk_buckets),
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),
- filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx_buckets),
// Locks.
// ------------------------------------------------------------------------
flush_lock_(lock(config.path, schema::locks::flush)),
- process_lock_(lock(config.path, schema::locks::process))
+ process_lock_(lock(config.path, schema::locks::process)),
+
+ // Tables.
+ // ------------------------------------------------------------------------
+
+ header(header_head_, header_body_, config.header_buckets),
+ input(input_head_, input_body_),
+ output(output_head_, output_body_),
+ point(point_head_, point_body_, config.point_buckets),
+ ins(ins_head_, ins_body_),
+ outs(outs_head_, outs_body_),
+ 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),
+
+ ecdsa(ecdsa_head_, ecdsa_body_),
+ schnorr(schnorr_head_, schnorr_body_),
+ duplicate(duplicate_head_, duplicate_body_, config.duplicate_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)
{
}
@@ -294,6 +315,10 @@ code CLASS::create(const event_handler& handler) NOEXCEPT
create(ec, strong_tx_head_, table_t::strong_tx_head);
create(ec, strong_tx_body_, table_t::strong_tx_body);
+ create(ec, ecdsa_head_, table_t::ecdsa_head);
+ create(ec, ecdsa_body_, table_t::ecdsa_body);
+ create(ec, schnorr_head_, table_t::schnorr_head);
+ create(ec, schnorr_body_, table_t::schnorr_body);
create(ec, duplicate_head_, table_t::duplicate_head);
create(ec, duplicate_body_, table_t::duplicate_body);
create(ec, prevout_head_, table_t::prevout_head);
@@ -337,6 +362,8 @@ code CLASS::create(const event_handler& handler) NOEXCEPT
populate(ec, confirmed, table_t::confirmed_table);
populate(ec, strong_tx, table_t::strong_tx_table);
+ populate(ec, ecdsa, table_t::ecdsa_table);
+ populate(ec, schnorr, table_t::schnorr_table);
populate(ec, duplicate, table_t::duplicate_table);
populate(ec, prevout, table_t::prevout_table);
populate(ec, validated_bk, table_t::validated_bk_table);
@@ -410,6 +437,8 @@ code CLASS::open(const event_handler& handler) NOEXCEPT
verify(ec, confirmed, table_t::confirmed_table);
verify(ec, strong_tx, table_t::strong_tx_table);
+ verify(ec, ecdsa, table_t::ecdsa_table);
+ verify(ec, schnorr, table_t::schnorr_table);
verify(ec, duplicate, table_t::duplicate_table);
verify(ec, prevout, table_t::prevout_table);
verify(ec, validated_bk, table_t::validated_bk_table);
@@ -526,6 +555,8 @@ code CLASS::snapshot(const event_handler& handler, bool prune) NOEXCEPT
flush(ec, confirmed_body_, table_t::confirmed_body);
flush(ec, strong_tx_body_, table_t::strong_tx_body);
+ flush(ec, ecdsa_body_, table_t::ecdsa_body);
+ flush(ec, schnorr_body_, table_t::schnorr_body);
flush(ec, duplicate_body_, table_t::duplicate_body);
if (!prune) flush(ec, prevout_body_, table_t::prevout_body);
flush(ec, validated_bk_body_, table_t::validated_bk_body);
@@ -588,6 +619,10 @@ code CLASS::reload(const event_handler& handler) NOEXCEPT
reload(ec, strong_tx_head_, table_t::strong_tx_head);
reload(ec, strong_tx_body_, table_t::strong_tx_body);
+ reload(ec, ecdsa_head_, table_t::ecdsa_head);
+ reload(ec, ecdsa_body_, table_t::ecdsa_body);
+ reload(ec, schnorr_head_, table_t::schnorr_head);
+ reload(ec, schnorr_body_, table_t::schnorr_body);
reload(ec, duplicate_head_, table_t::duplicate_head);
reload(ec, duplicate_body_, table_t::duplicate_body);
reload(ec, prevout_head_, table_t::prevout_head);
@@ -641,6 +676,8 @@ code CLASS::close(const event_handler& handler) NOEXCEPT
close(ec, confirmed, table_t::confirmed_table);
close(ec, strong_tx, table_t::strong_tx_table);
+ close(ec, ecdsa, table_t::ecdsa_table);
+ close(ec, schnorr, table_t::schnorr_table);
close(ec, duplicate, table_t::duplicate_table);
close(ec, prevout, table_t::prevout_table);
close(ec, validated_bk, table_t::validated_bk_table);
@@ -706,6 +743,10 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT
open(ec, strong_tx_head_, table_t::strong_tx_head);
open(ec, strong_tx_body_, table_t::strong_tx_body);
+ open(ec, ecdsa_head_, table_t::ecdsa_head);
+ open(ec, ecdsa_body_, table_t::ecdsa_body);
+ open(ec, schnorr_head_, table_t::schnorr_head);
+ open(ec, schnorr_body_, table_t::schnorr_body);
open(ec, duplicate_head_, table_t::duplicate_head);
open(ec, duplicate_body_, table_t::duplicate_body);
open(ec, prevout_head_, table_t::prevout_head);
@@ -755,6 +796,10 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT
load(ec, strong_tx_head_, table_t::strong_tx_head);
load(ec, strong_tx_body_, table_t::strong_tx_body);
+ load(ec, ecdsa_head_, table_t::ecdsa_head);
+ load(ec, ecdsa_body_, table_t::ecdsa_body);
+ load(ec, schnorr_head_, table_t::schnorr_head);
+ load(ec, schnorr_body_, table_t::schnorr_body);
load(ec, duplicate_head_, table_t::duplicate_head);
load(ec, duplicate_body_, table_t::duplicate_body);
load(ec, prevout_head_, table_t::prevout_head);
@@ -814,6 +859,10 @@ code CLASS::unload_close(const event_handler& handler) NOEXCEPT
unload(ec, strong_tx_head_, table_t::strong_tx_head);
unload(ec, strong_tx_body_, table_t::strong_tx_body);
+ unload(ec, ecdsa_head_, table_t::ecdsa_head);
+ unload(ec, ecdsa_body_, table_t::ecdsa_body);
+ unload(ec, schnorr_head_, table_t::schnorr_head);
+ unload(ec, schnorr_body_, table_t::schnorr_body);
unload(ec, duplicate_head_, table_t::duplicate_head);
unload(ec, duplicate_body_, table_t::duplicate_body);
unload(ec, prevout_head_, table_t::prevout_head);
@@ -863,6 +912,10 @@ code CLASS::unload_close(const event_handler& handler) NOEXCEPT
close(ec, strong_tx_head_, table_t::strong_tx_head);
close(ec, strong_tx_body_, table_t::strong_tx_body);
+ close(ec, ecdsa_head_, table_t::ecdsa_head);
+ close(ec, ecdsa_body_, table_t::ecdsa_body);
+ close(ec, schnorr_head_, table_t::schnorr_head);
+ close(ec, schnorr_body_, table_t::schnorr_body);
close(ec, duplicate_head_, table_t::duplicate_head);
close(ec, duplicate_body_, table_t::duplicate_body);
close(ec, prevout_head_, table_t::prevout_head);
@@ -910,6 +963,8 @@ code CLASS::backup(const event_handler& handler, bool prune) NOEXCEPT
backup(ec, confirmed, table_t::confirmed_table);
backup(ec, strong_tx, table_t::strong_tx_table);
+ backup(ec, ecdsa, table_t::ecdsa_table);
+ backup(ec, schnorr, table_t::schnorr_table);
backup(ec, duplicate, table_t::duplicate_table);
backup(ec, prevout, table_t::prevout_table, prune);
backup(ec, validated_bk, table_t::validated_bk_table);
@@ -975,6 +1030,8 @@ code CLASS::dump(const path& folder,
auto confirmed_buffer = confirmed_head_.get();
auto strong_tx_buffer = strong_tx_head_.get();
+ auto ecdsa_buffer = ecdsa_head_.get();
+ auto schnorr_buffer = schnorr_head_.get();
auto duplicate_buffer = duplicate_head_.get();
auto prevout_buffer = prevout_head_.get();
auto validated_bk_buffer = validated_bk_head_.get();
@@ -997,6 +1054,8 @@ code CLASS::dump(const path& folder,
if (!confirmed_buffer) return error::unloaded_file;
if (!strong_tx_buffer) return error::unloaded_file;
+ if (!ecdsa_buffer) return error::unloaded_file;
+ if (!schnorr_buffer) return error::unloaded_file;
if (!duplicate_buffer) return error::unloaded_file;
if (!prevout_buffer) return error::unloaded_file;
if (!validated_bk_buffer) return error::unloaded_file;
@@ -1031,6 +1090,8 @@ code CLASS::dump(const path& folder,
dump(ec, confirmed_buffer, schema::indexes::confirmed, table_t::confirmed_head);
dump(ec, strong_tx_buffer, schema::indexes::strong_tx, table_t::strong_tx_head);
+ dump(ec, ecdsa_buffer, schema::caches::ecdsa, table_t::ecdsa_head);
+ dump(ec, schnorr_buffer, schema::caches::schnorr, table_t::schnorr_head);
dump(ec, duplicate_buffer, schema::caches::duplicate, table_t::duplicate_head);
dump(ec, prevout_buffer, schema::caches::prevout, table_t::prevout_head);
dump(ec, validated_bk_buffer, schema::caches::validated_bk, table_t::validated_bk_head);
@@ -1124,6 +1185,8 @@ code CLASS::restore(const event_handler& handler) NOEXCEPT
restore(ec, confirmed, table_t::confirmed_table);
restore(ec, strong_tx, table_t::strong_tx_table);
+ restore(ec, ecdsa, table_t::ecdsa_table);
+ restore(ec, schnorr, table_t::schnorr_table);
restore(ec, duplicate, table_t::duplicate_table);
restore(ec, prevout, table_t::prevout_table);
restore(ec, validated_bk, table_t::validated_bk_table);
@@ -1187,6 +1250,8 @@ code CLASS::get_fault() const NOEXCEPT
if ((ec = candidate_body_.get_fault())) return ec;
if ((ec = confirmed_body_.get_fault())) return ec;
if ((ec = strong_tx_body_.get_fault())) return ec;
+ if ((ec = ecdsa_body_.get_fault())) return ec;
+ if ((ec = schnorr_body_.get_fault())) return ec;
if ((ec = duplicate_body_.get_fault())) return ec;
if ((ec = prevout_body_.get_fault())) return ec;
if ((ec = validated_bk_body_.get_fault())) return ec;
@@ -1217,6 +1282,8 @@ size_t CLASS::get_space() const NOEXCEPT
space(candidate_body_);
space(confirmed_body_);
space(strong_tx_body_);
+ space(ecdsa_body_);
+ space(schnorr_body_);
space(duplicate_body_);
space(prevout_body_);
space(validated_bk_body_);
@@ -1251,6 +1318,8 @@ void CLASS::report(const error_handler& handler) const NOEXCEPT
report(candidate_body_, table_t::candidate_body);
report(confirmed_body_, table_t::confirmed_body);
report(strong_tx_body_, table_t::strong_tx_body);
+ report(ecdsa_body_, table_t::ecdsa_body);
+ report(schnorr_body_, table_t::schnorr_body);
report(duplicate_body_, table_t::duplicate_body);
report(prevout_body_, table_t::prevout_body);
report(validated_bk_body_, table_t::validated_bk_body);
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index a936ce35a..1bddcf0d8 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -53,6 +53,9 @@ class query
using chain_state = system::chain::chain_state;
using chain_state_cptr = system::chain::chain_state::cptr;
using chain_context = system::chain::context;
+ using ec_compressed = system::ec_compressed;
+ using ec_signature = system::ec_signature;
+ using ec_xonly = system::ec_xonly;
query(Store& store) NOEXCEPT;
@@ -118,6 +121,8 @@ class query
size_t candidate_head_size() const NOEXCEPT;
size_t confirmed_head_size() const NOEXCEPT;
size_t strong_tx_head_size() const NOEXCEPT;
+ size_t ecdsa_head_size() const NOEXCEPT;
+ size_t schnorr_head_size() const NOEXCEPT;
size_t duplicate_head_size() const NOEXCEPT;
size_t prevout_head_size() const NOEXCEPT;
size_t validated_bk_head_size() const NOEXCEPT;
@@ -139,6 +144,8 @@ class query
size_t candidate_body_size() const NOEXCEPT;
size_t confirmed_body_size() const NOEXCEPT;
size_t strong_tx_body_size() const NOEXCEPT;
+ size_t ecdsa_body_size() const NOEXCEPT;
+ size_t schnorr_body_size() const NOEXCEPT;
size_t duplicate_body_size() const NOEXCEPT;
size_t prevout_body_size() const NOEXCEPT;
size_t validated_bk_body_size() const NOEXCEPT;
@@ -160,6 +167,8 @@ class query
size_t candidate_size() const NOEXCEPT;
size_t confirmed_size() const NOEXCEPT;
size_t strong_tx_size() const NOEXCEPT;
+ size_t ecdsa_size() const NOEXCEPT;
+ size_t schnorr_size() const NOEXCEPT;
size_t duplicate_size() const NOEXCEPT;
size_t prevout_size() const NOEXCEPT;
size_t validated_bk_size() const NOEXCEPT;
@@ -193,6 +202,8 @@ class query
size_t candidate_records() const NOEXCEPT;
size_t confirmed_records() const NOEXCEPT;
size_t strong_tx_records() const NOEXCEPT;
+ size_t ecdsa_records() const NOEXCEPT;
+ size_t schnorr_records() const NOEXCEPT;
size_t duplicate_records() const NOEXCEPT;
size_t filter_bk_records() const NOEXCEPT;
size_t address_records() const NOEXCEPT;
@@ -559,6 +570,12 @@ class query
bool set_tx_connected(const tx_link& link, const context& ctx,
uint64_t fee, size_t sigops) NOEXCEPT;
+ /// Set signature (ecdsa/schnorr) table entry.
+ bool set_signature(const hash_digest& digest, const ec_compressed& point,
+ const ec_signature& signature, const header_link& link) NOEXCEPT;
+ bool set_signature(const hash_digest& digest, const ec_xonly& point,
+ const ec_signature& signature, const header_link& link) NOEXCEPT;
+
/// Confirmation.
/// -----------------------------------------------------------------------
/// These are not used in consensus confirmation.
@@ -941,6 +958,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
#include
#include
#include
+#include
#include
BC_POP_WARNING()
diff --git a/include/bitcoin/database/settings.hpp b/include/bitcoin/database/settings.hpp
index 749c90368..5a7371db1 100644
--- a/include/bitcoin/database/settings.hpp
+++ b/include/bitcoin/database/settings.hpp
@@ -95,6 +95,12 @@ struct BCD_API settings
/// Caches.
/// -----------------------------------------------------------------------
+ uint64_t ecdsa_size;
+ uint16_t ecdsa_rate;
+
+ uint64_t schnorr_size;
+ uint16_t schnorr_rate;
+
// This one is 16 bit (could use table link::integer) for these.
uint16_t duplicate_buckets;
uint64_t duplicate_size;
diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp
index 762ec95b9..c0c2ec2ff 100644
--- a/include/bitcoin/database/store.hpp
+++ b/include/bitcoin/database/store.hpp
@@ -103,35 +103,6 @@ class store
/// Dump all error/full conditions to handler.
void report(const error_handler& handler) const NOEXCEPT;
- /// Tables.
- /// -----------------------------------------------------------------------
-
- /// Archives.
- table::header header;
- table::input input;
- table::output output;
- table::point point;
- table::ins ins;
- table::outs outs;
- table::transaction tx;
- table::txs txs;
-
- /// Indexes.
- table::height candidate;
- table::height confirmed;
- table::strong_tx strong_tx;
-
- /// Caches.
- table::duplicate duplicate;
- table::prevout prevout;
- table::validated_bk validated_bk;
- table::validated_tx validated_tx;
-
- /// Optionals.
- table::address address;
- table::filter_bk filter_bk;
- table::filter_tx filter_tx;
-
protected:
using path = std::filesystem::path;
@@ -196,11 +167,19 @@ class store
/// Caches.
/// -----------------------------------------------------------------------
- // bloc arraymap
+ // array
+ Storage ecdsa_head_;
+ Storage ecdsa_body_;
+
+ // array
+ Storage schnorr_head_;
+ Storage schnorr_body_;
+
+ // blob arraymap
Storage duplicate_head_;
Storage duplicate_body_;
- // bloc arraymap
+ // blob arraymap
Storage prevout_head_;
Storage prevout_body_;
@@ -253,6 +232,38 @@ class store
{
return folder / (name + schema::ext::lock);
}
+
+public:
+ /// Tables.
+ /// -----------------------------------------------------------------------
+
+ /// Archives.
+ table::header header;
+ table::input input;
+ table::output output;
+ table::point point;
+ table::ins ins;
+ table::outs outs;
+ table::transaction tx;
+ table::txs txs;
+
+ /// Indexes.
+ table::height candidate;
+ table::height confirmed;
+ table::strong_tx strong_tx;
+
+ /// Caches.
+ table::ecdsa ecdsa;
+ table::schnorr schnorr;
+ table::duplicate duplicate;
+ table::prevout prevout;
+ table::validated_bk validated_bk;
+ table::validated_tx validated_tx;
+
+ /// Optionals.
+ table::address address;
+ table::filter_bk filter_bk;
+ table::filter_tx filter_tx;
};
} // namespace database
diff --git a/include/bitcoin/database/tables/caches/ecdsa.hpp b/include/bitcoin/database/tables/caches/ecdsa.hpp
new file mode 100644
index 000000000..734a55849
--- /dev/null
+++ b/include/bitcoin/database/tables/caches/ecdsa.hpp
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_DATABASE_TABLES_CACHES_ECDSA_HPP
+#define LIBBITCOIN_DATABASE_TABLES_CACHES_ECDSA_HPP
+
+#include
+#include
+#include
+
+namespace libbitcoin {
+namespace database {
+namespace table {
+
+/// ecdsa is an array of ecdsa signature validation records.
+struct ecdsa
+ : public no_map
+{
+ using header = schema::header::link;
+ using no_map::nomap;
+
+ struct record
+ : public schema::ecdsa
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ digest = source.read_hash();
+ point = source.read_forward();
+ signature = source.read_forward();
+ header_fk = source.read_little_endian();
+ BC_ASSERT(!source || source.get_read_position() == minrow);
+ return source;
+ }
+
+ inline bool to_data(flipper& sink) const NOEXCEPT
+ {
+ sink.write_bytes(digest);
+ sink.write_bytes(point);
+ sink.write_bytes(signature);
+ sink.write_little_endian(header_fk);
+ BC_ASSERT(!sink || sink.get_write_position() == minrow);
+ return sink;
+ }
+
+ inline bool operator==(const record& other) const NOEXCEPT
+ {
+ return digest == other.digest
+ && point == other.point
+ && signature == other.signature
+ && header_fk == other.header_fk;
+ }
+
+ system::hash_digest digest{};
+ system::ec_compressed point{};
+ system::ec_signature signature{};
+ header::integer header_fk{};
+ };
+};
+
+static_assert(offsetof(system::ecdsa::triple, digest) == 0);
+static_assert(offsetof(system::ecdsa::triple, point) == 32);
+static_assert(offsetof(system::ecdsa::triple, signature) == 65);
+static_assert(offsetof(system::ecdsa::triple, identifier) == 129);
+static_assert(sizeof(system::ecdsa::triple) == 32 + 33 + 64 + 3);
+
+} // namespace table
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/tables/caches/schnorr.hpp b/include/bitcoin/database/tables/caches/schnorr.hpp
new file mode 100644
index 000000000..02d8d0ebd
--- /dev/null
+++ b/include/bitcoin/database/tables/caches/schnorr.hpp
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_DATABASE_TABLES_CACHES_SCHNORR_HPP
+#define LIBBITCOIN_DATABASE_TABLES_CACHES_SCHNORR_HPP
+
+#include
+#include
+#include
+
+namespace libbitcoin {
+namespace database {
+namespace table {
+
+/// schnorr is an array of schnorr signature validation records.
+struct schnorr
+ : public no_map
+{
+ using header = schema::header::link;
+ using no_map::nomap;
+
+ struct record
+ : public schema::schnorr
+ {
+ inline bool from_data(reader& source) NOEXCEPT
+ {
+ digest = source.read_hash();
+ point = source.read_forward();
+ signature = source.read_forward();
+ header_fk = source.read_little_endian();
+ BC_ASSERT(!source || source.get_read_position() == minrow);
+ return source;
+ }
+
+ inline bool to_data(flipper& sink) const NOEXCEPT
+ {
+ sink.write_bytes(digest);
+ sink.write_bytes(point);
+ sink.write_bytes(signature);
+ sink.write_little_endian(header_fk);
+ BC_ASSERT(!sink || sink.get_write_position() == minrow);
+ return sink;
+ }
+
+ inline bool operator==(const record& other) const NOEXCEPT
+ {
+ return digest == other.digest
+ && point == other.point
+ && signature == other.signature
+ && header_fk == other.header_fk;
+ }
+
+ system::hash_digest digest{};
+ system::ec_xonly point{};
+ system::ec_signature signature{};
+ header::integer header_fk{};
+ };
+};
+
+static_assert(offsetof(system::schnorr::triple, digest) == 0);
+static_assert(offsetof(system::schnorr::triple, point) == 32);
+static_assert(offsetof(system::schnorr::triple, signature) == 64);
+static_assert(offsetof(system::schnorr::triple, identifier) == 128);
+static_assert(sizeof(system::schnorr::triple) == 32 + 32 + 64 + 3);
+
+} // namespace table
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/tables/names.hpp b/include/bitcoin/database/tables/names.hpp
index b5a1c018b..6d22e8f57 100644
--- a/include/bitcoin/database/tables/names.hpp
+++ b/include/bitcoin/database/tables/names.hpp
@@ -57,6 +57,8 @@ namespace indexes
namespace caches
{
+ constexpr auto ecdsa = "ecdsa";
+ constexpr auto schnorr = "schnorr";
constexpr auto duplicate = "duplicate";
constexpr auto prevout = "prevout";
constexpr auto validated_bk = "validated_bk";
diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp
index 9775871c9..707eae2b4 100644
--- a/include/bitcoin/database/tables/schema.hpp
+++ b/include/bitcoin/database/tables/schema.hpp
@@ -221,7 +221,7 @@ struct txs
/// Index tables.
/// ---------------------------------------------------------------------------
-// candidate and confirmed arrays
+// array (candidate and confirmed)
struct height
{
static constexpr size_t pk = schema::header::pk;
@@ -259,6 +259,44 @@ struct strong_tx
/// Cache tables.
/// ---------------------------------------------------------------------------
+// array
+struct ecdsa
+{
+ static constexpr size_t pk = schema::header::pk;
+ using link = schema::header::link;
+ static constexpr size_t minsize =
+ system::hash_size +
+ system::ec_compressed_size +
+ system::ec_signature_size +
+ schema::header::pk;
+ static constexpr size_t minrow = minsize;
+ static constexpr size_t size = minsize;
+ static constexpr link count() NOEXCEPT { return 1; }
+ static_assert(minsize == 132u);
+ static_assert(minrow == 132u);
+ static_assert(link::size == 3u);
+ static_assert(minrow - link::size == 129u);
+};
+
+// array
+struct schnorr
+{
+ static constexpr size_t pk = schema::header::pk;
+ using link = schema::header::link;
+ static constexpr size_t minsize =
+ system::hash_size +
+ system::ec_xonly_size +
+ system::ec_signature_size +
+ schema::header::pk;
+ static constexpr size_t minrow = minsize;
+ static constexpr size_t size = minsize;
+ static constexpr link count() NOEXCEPT { return 1; }
+ static_assert(minsize == 131u);
+ static_assert(minrow == 131u);
+ static_assert(link::size == 3u);
+ static_assert(minrow - link::size == 128u);
+};
+
// record hashmap
struct duplicate
{
diff --git a/include/bitcoin/database/tables/table.hpp b/include/bitcoin/database/tables/table.hpp
index b35b37806..eed537ac2 100644
--- a/include/bitcoin/database/tables/table.hpp
+++ b/include/bitcoin/database/tables/table.hpp
@@ -69,6 +69,12 @@ enum class table_t
strong_tx_body,
/// Caches.
+ ecdsa_table,
+ ecdsa_head,
+ ecdsa_body,
+ schnorr_table,
+ schnorr_head,
+ schnorr_body,
duplicate_table,
duplicate_head,
duplicate_body,
diff --git a/include/bitcoin/database/tables/tables.hpp b/include/bitcoin/database/tables/tables.hpp
index ab005a75f..2c7bd04ae 100644
--- a/include/bitcoin/database/tables/tables.hpp
+++ b/include/bitcoin/database/tables/tables.hpp
@@ -28,8 +28,10 @@
#include
#include
+#include
#include
#include
+#include
#include
#include
diff --git a/src/settings.cpp b/src/settings.cpp
index b87279e1e..0d00ca64f 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -71,6 +71,12 @@ settings::settings() NOEXCEPT
// Caches.
+ ecdsa_size{ 1 },
+ ecdsa_rate{ 50 },
+
+ schnorr_size{ 1 },
+ schnorr_rate{ 50 },
+
duplicate_buckets{ 128 },
duplicate_size{ 1 },
duplicate_rate{ 50 },
diff --git a/test/mocks/chunk_store.hpp b/test/mocks/chunk_store.hpp
index 750e6b7b6..c68b97ec9 100644
--- a/test/mocks/chunk_store.hpp
+++ b/test/mocks/chunk_store.hpp
@@ -148,6 +148,26 @@ class chunk_store
// Caches.
+ system::data_chunk& ecdsa_head() NOEXCEPT
+ {
+ return ecdsa_head_.buffer();
+ }
+
+ system::data_chunk& ecdsa_body() NOEXCEPT
+ {
+ return ecdsa_body_.buffer();
+ }
+
+ system::data_chunk& schnorr_head() NOEXCEPT
+ {
+ return schnorr_head_.buffer();
+ }
+
+ system::data_chunk& schnorr_body() NOEXCEPT
+ {
+ return schnorr_body_.buffer();
+ }
+
system::data_chunk& duplicate_head() NOEXCEPT
{
return duplicate_head_.buffer();
@@ -225,4 +245,4 @@ using query_accessor = query>;
} // namespace test
-#endif
+#endif
\ No newline at end of file
diff --git a/test/mocks/map_store.hpp b/test/mocks/map_store.hpp
index 7701b50d3..099cb6cd9 100644
--- a/test/mocks/map_store.hpp
+++ b/test/mocks/map_store.hpp
@@ -169,6 +169,26 @@ class map_store
// Caches.
+ inline const path& ecdsa_head_file() const NOEXCEPT
+ {
+ return ecdsa_head_.file();
+ }
+
+ inline const path& ecdsa_body_file() const NOEXCEPT
+ {
+ return ecdsa_body_.file();
+ }
+
+ inline const path& schnorr_head_file() const NOEXCEPT
+ {
+ return schnorr_head_.file();
+ }
+
+ inline const path& schnorr_body_file() const NOEXCEPT
+ {
+ return schnorr_body_.file();
+ }
+
inline const path& duplicate_head_file() const NOEXCEPT
{
return duplicate_head_.file();
@@ -261,4 +281,4 @@ class map_store
} // namespace test
-#endif
+#endif
\ No newline at end of file
diff --git a/test/query/extent.cpp b/test/query/extent.cpp
index b2f81f1ea..5be68517c 100644
--- a/test/query/extent.cpp
+++ b/test/query/extent.cpp
@@ -51,6 +51,8 @@ BOOST_AUTO_TEST_CASE(query_extent__body_sizes__genesis__expected)
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.duplicate_body_size(), zero);
BOOST_REQUIRE_EQUAL(query.prevout_body_size(), zero);
BOOST_REQUIRE_EQUAL(query.validated_bk_body_size(), zero);
@@ -101,6 +103,8 @@ BOOST_AUTO_TEST_CASE(query_extent__records__genesis__expected)
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.duplicate_records(), zero);
BOOST_REQUIRE_EQUAL(query.filter_bk_records(), one);
BOOST_REQUIRE_EQUAL(query.address_records(), one);
diff --git a/test/query/signatures.cpp b/test/query/signatures.cpp
new file mode 100644
index 000000000..d4097cb66
--- /dev/null
+++ b/test/query/signatures.cpp
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include "../test.hpp"
+#include "../mocks/blocks.hpp"
+#include "../mocks/chunk_store.hpp"
+
+BOOST_FIXTURE_TEST_SUITE(query_signatures_tests, test::directory_setup_fixture)
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/settings.cpp b/test/settings.cpp
index 1d35a7dde..aa0cffc79 100644
--- a/test/settings.cpp
+++ b/test/settings.cpp
@@ -59,6 +59,10 @@ BOOST_AUTO_TEST_CASE(settings__construct__default__expected)
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.duplicate_buckets, 128u);
BOOST_REQUIRE_EQUAL(configuration.duplicate_size, 1u);
BOOST_REQUIRE_EQUAL(configuration.duplicate_rate, 50u);
diff --git a/test/tables/caches/ecdsa.cpp b/test/tables/caches/ecdsa.cpp
new file mode 100644
index 000000000..6bfb4d417
--- /dev/null
+++ b/test/tables/caches/ecdsa.cpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include "../../test.hpp"
+#include "../../mocks/chunk_storage.hpp"
+
+BOOST_AUTO_TEST_SUITE(ecdsa_tests)
+
+using namespace system;
+
+const table::ecdsa::record record1
+{
+ {},
+ base16_hash("1111111111111111111111111111111111111111111111111111111111111111"),
+ base16_array("222222222222222222222222222222222222222222222222222222222222222222"),
+ base16_array("33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"),
+ 0x00345678_u32
+};
+
+const table::ecdsa::record record2
+{
+ {},
+ base16_hash("4444444444444444444444444444444444444444444444444444444444444444"),
+ base16_array("555555555555555555555555555555555555555555555555555555555555555555"),
+ base16_array("66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666"),
+ 0x00cdef12_u32
+};
+
+const auto expected_head = base16_chunk("000000");
+const auto closed_head = base16_chunk("020000");
+const auto expected_body = base16_chunk
+(
+ // record 1
+ "1111111111111111111111111111111111111111111111111111111111111111"
+ "222222222222222222222222222222222222222222222222222222222222222222"
+ "33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"
+ "785634"
+
+ // record 2
+ "4444444444444444444444444444444444444444444444444444444444444444"
+ "555555555555555555555555555555555555555555555555555555555555555555"
+ "66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666"
+ "12efcd"
+);
+
+BOOST_AUTO_TEST_CASE(ecdsa__put__two__expected)
+{
+ test::chunk_storage head_store{};
+ test::chunk_storage body_store{};
+ table::ecdsa instance{ head_store, body_store };
+ BOOST_REQUIRE(instance.create());
+
+ table::ecdsa::link link1{};
+ BOOST_REQUIRE(instance.put_link(link1, record1));
+ BOOST_REQUIRE_EQUAL(link1, 0u);
+
+ table::ecdsa::link link2{};
+ BOOST_REQUIRE(instance.put_link(link2, record2));
+ BOOST_REQUIRE_EQUAL(link2, 1u);
+
+ BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head);
+ BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body);
+ BOOST_REQUIRE(instance.close());
+ BOOST_REQUIRE_EQUAL(head_store.buffer(), closed_head);
+}
+
+BOOST_AUTO_TEST_CASE(ecdsa__get__two__expected)
+{
+ auto head = expected_head;
+ auto body = expected_body;
+ test::chunk_storage head_store{ head };
+ test::chunk_storage body_store{ body };
+ table::ecdsa instance{ head_store, body_store };
+ BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head);
+ BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body);
+
+ table::ecdsa::record out{};
+ BOOST_REQUIRE(instance.get(0u, out));
+ BOOST_REQUIRE(out == record1);
+ BOOST_REQUIRE(instance.get(1u, out));
+ BOOST_REQUIRE(out == record2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/tables/caches/schnorr.cpp b/test/tables/caches/schnorr.cpp
new file mode 100644
index 000000000..76b309b8e
--- /dev/null
+++ b/test/tables/caches/schnorr.cpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#include "../../test.hpp"
+#include "../../mocks/chunk_storage.hpp"
+
+BOOST_AUTO_TEST_SUITE(schnorr_tests)
+
+using namespace system;
+
+const table::schnorr::record record1
+{
+ {},
+ base16_hash("1111111111111111111111111111111111111111111111111111111111111111"),
+ base16_array("2222222222222222222222222222222222222222222222222222222222222222"),
+ base16_array("33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"),
+ 0x00345678_u32
+};
+
+const table::schnorr::record record2
+{
+ {},
+ base16_hash("4444444444444444444444444444444444444444444444444444444444444444"),
+ base16_array("5555555555555555555555555555555555555555555555555555555555555555"),
+ base16_array("66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666"),
+ 0x00cdef12_u32
+};
+
+const auto expected_head = base16_chunk("000000");
+const auto closed_head = base16_chunk("020000");
+const auto expected_body = base16_chunk
+(
+ // record 1
+ "1111111111111111111111111111111111111111111111111111111111111111"
+ "2222222222222222222222222222222222222222222222222222222222222222"
+ "33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"
+ "785634"
+
+ // record 2
+ "4444444444444444444444444444444444444444444444444444444444444444"
+ "5555555555555555555555555555555555555555555555555555555555555555"
+ "66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666"
+ "12efcd"
+);
+
+BOOST_AUTO_TEST_CASE(schnorr__put__two__expected)
+{
+ test::chunk_storage head_store{};
+ test::chunk_storage body_store{};
+ table::schnorr instance{ head_store, body_store };
+ BOOST_REQUIRE(instance.create());
+
+ table::schnorr::link link1{};
+ BOOST_REQUIRE(instance.put_link(link1, record1));
+ BOOST_REQUIRE_EQUAL(link1, 0u);
+
+ table::schnorr::link link2{};
+ BOOST_REQUIRE(instance.put_link(link2, record2));
+ BOOST_REQUIRE_EQUAL(link2, 1u);
+
+ BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head);
+ BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body);
+ BOOST_REQUIRE(instance.close());
+ BOOST_REQUIRE_EQUAL(head_store.buffer(), closed_head);
+}
+
+BOOST_AUTO_TEST_CASE(schnorr__get__two__expected)
+{
+ auto head = expected_head;
+ auto body = expected_body;
+ test::chunk_storage head_store{ head };
+ test::chunk_storage body_store{ body };
+ table::schnorr instance{ head_store, body_store };
+ BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head);
+ BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body);
+
+ table::schnorr::record out{};
+ BOOST_REQUIRE(instance.get(0u, out));
+ BOOST_REQUIRE(out == record1);
+ BOOST_REQUIRE(instance.get(1u, out));
+ BOOST_REQUIRE(out == record2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/tables/caches/validated_bk.cpp b/test/tables/caches/validated_bk.cpp
index 64e753815..c3fb1302e 100644
--- a/test/tables/caches/validated_bk.cpp
+++ b/test/tables/caches/validated_bk.cpp
@@ -24,7 +24,7 @@ BOOST_AUTO_TEST_SUITE(validated_bk_tests)
using namespace system;
const table::validated_bk::record record1{ {}, 0x42 };
const table::validated_bk::record record2{ {}, 0xab };
-const data_chunk expected_head = base16_chunk
+const auto expected_head = base16_chunk
(
"000000"
"000000"
@@ -36,7 +36,7 @@ const data_chunk expected_head = base16_chunk
"ffffff"
"ffffff"
);
-const data_chunk closed_head = base16_chunk
+const auto closed_head = base16_chunk
(
"020000"
"000000"
@@ -48,7 +48,7 @@ const data_chunk closed_head = base16_chunk
"ffffff"
"ffffff"
);
-const data_chunk expected_body = base16_chunk
+const auto expected_body = base16_chunk
(
"42" // code1
"ab" // code2
diff --git a/test/tables/caches/validated_tx.cpp b/test/tables/caches/validated_tx.cpp
index df3abf7fc..184417f90 100644
--- a/test/tables/caches/validated_tx.cpp
+++ b/test/tables/caches/validated_tx.cpp
@@ -72,7 +72,7 @@ const table::validated_tx::slab out2
0x0000000000000042, // fee
0x00000055 // sigops
};
-const data_chunk expected_head = base16_chunk
+const auto expected_head = base16_chunk
(
"0000000000"
"ffffffffff"
@@ -84,7 +84,7 @@ const data_chunk expected_head = base16_chunk
"ffffffffff"
"ffffffffff"
);
-const data_chunk closed_head = base16_chunk
+const auto closed_head = base16_chunk
(
"3a00000000"
"ffffffffff"
@@ -96,7 +96,7 @@ const data_chunk closed_head = base16_chunk
"ffffffffff"
"ffffffffff"
);
-const data_chunk expected_body = base16_chunk
+const auto expected_body = base16_chunk
(
"ffffffffff" // next->end
"01020304" // key1
diff --git a/test/tables/indexes/height.cpp b/test/tables/indexes/height.cpp
index ff83d69d4..7fdb5b588 100644
--- a/test/tables/indexes/height.cpp
+++ b/test/tables/indexes/height.cpp
@@ -26,15 +26,15 @@ const table::height::record in1{ {}, 0x12345678 };
const table::height::record in2{ {}, 0xabcdef12 };
const table::height::record out1{ {}, 0x00345678 };
const table::height::record out2{ {}, 0x00cdef12 };
-const data_chunk expected_head = base16_chunk
+const auto expected_head = base16_chunk
(
"000000"
);
-const data_chunk closed_head = base16_chunk
+const auto closed_head = base16_chunk
(
"020000"
);
-const data_chunk expected_body = base16_chunk
+const auto expected_body = base16_chunk
(
"785634" // header_fk1
"12efcd" // header_fk2
diff --git a/test/tables/indexes/strong_tx.cpp b/test/tables/indexes/strong_tx.cpp
index 29ed8ce14..d13fa6950 100644
--- a/test/tables/indexes/strong_tx.cpp
+++ b/test/tables/indexes/strong_tx.cpp
@@ -27,7 +27,7 @@ const table::strong_tx::key key2{ 0xa1, 0xa2, 0xa3, 0xa4 };
const table::strong_tx::record strong1{ {}, table::strong_tx::merge(true, 0x0078f87f) };
const table::strong_tx::record strong2{ {}, table::strong_tx::merge(false, 0x0078f87f) };
-const data_chunk expected_head = base16_chunk
+const auto expected_head = base16_chunk
(
"00000000"
"ffffffff"
@@ -39,7 +39,7 @@ const data_chunk expected_head = base16_chunk
"ffffffff"
"ffffffff"
);
-const data_chunk closed_head = base16_chunk
+const auto closed_head = base16_chunk
(
"02000000"
"ffffffff"
@@ -51,7 +51,7 @@ const data_chunk closed_head = base16_chunk
"ffffffff"
"ffffffff"
);
-const data_chunk expected_body = base16_chunk
+const auto expected_body = base16_chunk
(
"ffffffff" // next->end
"01020304" // key1
diff --git a/test/tables/optional/address.cpp b/test/tables/optional/address.cpp
index a838b9608..5d331b3a5 100644
--- a/test/tables/optional/address.cpp
+++ b/test/tables/optional/address.cpp
@@ -28,7 +28,7 @@ const table::address::record in1{ {}, 0x1234567890abcdef };
const table::address::record in2{ {}, 0xabcdef1234567890 };
const table::address::record out1{ {}, 0x0000007890abcdef };
const table::address::record out2{ {}, 0x0000001234567890 };
-const data_chunk expected_head = base16_chunk
+const auto expected_head = base16_chunk
(
"00000000"
"01000000"
@@ -40,7 +40,7 @@ const data_chunk expected_head = base16_chunk
"ffffffff"
"ffffffff"
);
-const data_chunk closed_head = base16_chunk
+const auto closed_head = base16_chunk
(
"02000000"
"01000000"
@@ -52,7 +52,7 @@ const data_chunk closed_head = base16_chunk
"ffffffff"
"ffffffff"
);
-const data_chunk expected_body = base16_chunk
+const auto expected_body = base16_chunk
(
"ffffffff" // next->end
"100000000000000000000000000000000000000000000000000000000000000a" // key1
diff --git a/test/tables/optional/filter_bk.cpp b/test/tables/optional/filter_bk.cpp
index 7492c5887..01b2a4649 100644
--- a/test/tables/optional/filter_bk.cpp
+++ b/test/tables/optional/filter_bk.cpp
@@ -25,7 +25,7 @@ using namespace system;
BOOST_AUTO_TEST_CASE(filter_bk__put__ordered__expected)
{
- const data_chunk expected_head = base16_chunk
+ const auto expected_head = base16_chunk
(
"000000"
"000000"
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(filter_bk__put__ordered__expected)
"ffffff"
"ffffff"
);
- const data_chunk closed_head = base16_chunk
+ const auto closed_head = base16_chunk
(
"050000"
"000000"
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(filter_bk__put__ordered__expected)
"ffffff"
"ffffff"
);
- const data_chunk expected_body = base16_chunk
+ const auto expected_body = base16_chunk
(
// hash, head
"0000000000000000000000000000000000000000000000000000000000000000""0000000000000000000000000000000000000000000000000000000000000000"
diff --git a/test/tables/optional/filter_tx.cpp b/test/tables/optional/filter_tx.cpp
index 06a31dc1c..71d033d1a 100644
--- a/test/tables/optional/filter_tx.cpp
+++ b/test/tables/optional/filter_tx.cpp
@@ -25,7 +25,7 @@ using namespace system;
BOOST_AUTO_TEST_CASE(filter_tx__put__ordered__expected)
{
- const data_chunk expected_head = base16_chunk
+ const auto expected_head = base16_chunk
(
"0000000000"
"0000000000"
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(filter_tx__put__ordered__expected)
"ffffffffff"
"ffffffffff"
);
- const data_chunk closed_head = base16_chunk
+ const auto closed_head = base16_chunk
(
"a500000000"
"0000000000"
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(filter_tx__put__ordered__expected)
"ffffffffff"
"ffffffffff"
);
- const data_chunk expected_body = base16_chunk
+ const auto expected_body = base16_chunk
(
"20""0000000000000000000000000000000000000000000000000000000000000000"
"20""0100000000000000000000000000000000000000000000000000000000000000"