From adc442eab7473068910bccb33735c0dcfb20560e Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 11 Oct 2026 12:36:29 -0400 Subject: [PATCH 1/2] Restore batch columns and split silent and scan tables by cardinality. --- .../database/impl/query/batch/ecdsa.ipp | 27 ++- .../database/impl/query/batch/schnorr.ipp | 27 ++- .../database/impl/query/batch/silent.ipp | 220 ++++++++++-------- .../bitcoin/database/impl/query/extent.ipp | 12 + include/bitcoin/database/impl/store/store.ipp | 18 ++ .../database/impl/store/store_backup.ipp | 3 + .../database/impl/store/store_close.ipp | 3 + .../database/impl/store/store_create.ipp | 9 + .../database/impl/store/store_dump.ipp | 3 + .../database/impl/store/store_open.ipp | 3 + .../database/impl/store/store_open_load.ipp | 12 + .../database/impl/store/store_prune.ipp | 7 + .../database/impl/store/store_reload.ipp | 6 + .../database/impl/store/store_report.ipp | 15 ++ .../database/impl/store/store_restore.ipp | 3 + .../database/impl/store/store_snapshot.ipp | 3 + .../database/impl/store/store_tables.ipp | 9 + .../impl/store/store_unload_close.ipp | 12 + include/bitcoin/database/query.hpp | 16 +- include/bitcoin/database/store.hpp | 10 + .../bitcoin/database/tables/caches/ecdsa.hpp | 213 ++++++++++++++--- .../database/tables/caches/schnorr.hpp | 125 ++++++++-- .../bitcoin/database/tables/caches/silent.hpp | 155 +++++++++--- .../bitcoin/database/tables/indexes/scan.hpp | 149 ++++++++---- include/bitcoin/database/tables/names.hpp | 22 +- include/bitcoin/database/tables/schema.hpp | 36 ++- include/bitcoin/database/tables/table.hpp | 9 + include/bitcoin/database/types/type.hpp | 3 +- test/query/extent.cpp | 6 + test/tables/caches/ecdsa.cpp | 146 ++++++++++-- test/tables/caches/schnorr.cpp | 30 ++- test/tables/envelope.cpp | 2 +- test/tables/indexes/scan.cpp | 98 ++++++-- 33 files changed, 1105 insertions(+), 307 deletions(-) diff --git a/include/bitcoin/database/impl/query/batch/ecdsa.ipp b/include/bitcoin/database/impl/query/batch/ecdsa.ipp index 93a288eda..df484d51c 100644 --- a/include/bitcoin/database/impl/query/batch/ecdsa.ipp +++ b/include/bitcoin/database/impl/query/batch/ecdsa.ipp @@ -38,20 +38,29 @@ bool CLASS::verify_ecdsa_signatures(const stopper& cancel, { auto& bank_table = store_.ecdsa_bank(bank); const auto correlate_ptr = bank_table.correlate.get_memory(); - const auto row_ptr = bank_table.row.get_memory(); + const auto digest_ptr = bank_table.digest.get_memory(); + const auto compressed_ptr = bank_table.compressed.get_memory(); + const auto signature_ptr = bank_table.signature.get_memory(); using correlate_t = const system::ecdsa::batch::correlate_t; - using row_t = const system::ecdsa::batch::row_t; + using digest_t = const table::ecdsa_digest::span; + using compressed_t = const table::ecdsa_compressed::span; + using signature_t = const table::ecdsa_signature::span; using namespace system; const auto correlate = pointer_cast(correlate_ptr.data()); - const auto row = pointer_cast(row_ptr.data()); + const auto digest = pointer_cast(digest_ptr.data()); + const auto compressed = pointer_cast(compressed_ptr.data()); + const auto signature = pointer_cast(signature_ptr.data()); + // Shortest column. const auto count = bank_table.count(); const ecdsa::batch batch { .correlates = { correlate, count }, - .rows = { row, count } + .digests = { digest, count }, + .points = { compressed, count }, + .signatures = { signature, count } }; // False return only implies canceled. @@ -76,7 +85,9 @@ bool CLASS::set_signatures(const system::chain::ecdsa_signatures& sigs, const header_link& link, bool bank) NOEXCEPT { using correlate_t = table::ecdsa_correlate::put_signatures; - using row_t = table::ecdsa_row::put_signatures; + using digest_t = table::ecdsa_digest::put_signatures; + using compressed_t = table::ecdsa_compressed::put_signatures; + using signature_t = table::ecdsa_signature::put_signatures; if (sigs.empty()) return true; @@ -97,10 +108,12 @@ bool CLASS::set_signatures(const system::chain::ecdsa_signatures& sigs, // Guard against remap (required for nomaps::put(fk)). const auto guard = bank_table.guard(); - // Write the accumulator to the columns, expanding group bands. + // Deinterleave the accumulator into the columns, expanding group bands. return bank_table.correlate.put(fk, correlate_t{ {}, link, sigs }) && - bank_table.row.put(fk, row_t{ {}, sigs }); + bank_table.digest.put(fk, digest_t{ {}, sigs }) && + bank_table.compressed.put(fk, compressed_t{ {}, sigs }) && + bank_table.signature.put(fk, signature_t{ {}, sigs }); // ======================================================================== } diff --git a/include/bitcoin/database/impl/query/batch/schnorr.ipp b/include/bitcoin/database/impl/query/batch/schnorr.ipp index 168ac8369..46477c147 100644 --- a/include/bitcoin/database/impl/query/batch/schnorr.ipp +++ b/include/bitcoin/database/impl/query/batch/schnorr.ipp @@ -37,20 +37,29 @@ bool CLASS::verify_schnorr_signatures(const stopper& cancel, { auto& bank_table = store_.schnorr_bank(bank); const auto correlate_ptr = bank_table.correlate.get_memory(); - const auto row_ptr = bank_table.row.get_memory(); + const auto digest_ptr = bank_table.digest.get_memory(); + const auto xonly_ptr = bank_table.xonly.get_memory(); + const auto signature_ptr = bank_table.signature.get_memory(); using correlate_t = const system::schnorr::batch::correlate_t; - using row_t = const system::schnorr::batch::row_t; + using digest_t = const table::schnorr_digest::span; + using xonly_t = const table::schnorr_xonly::span; + using signature_t = const table::schnorr_signature::span; using namespace system; const auto correlate = pointer_cast(correlate_ptr.data()); - const auto row = pointer_cast(row_ptr.data()); + const auto digest = pointer_cast(digest_ptr.data()); + const auto xonly = pointer_cast(xonly_ptr.data()); + const auto signature = pointer_cast(signature_ptr.data()); + // Shortest column. const auto count = bank_table.count(); const schnorr::batch batch { .correlates = { correlate, count }, - .rows = { row, count } + .digests = { digest, count }, + .points = { xonly, count }, + .signatures = { signature, count } }; // False return only implies canceled. @@ -75,7 +84,9 @@ bool CLASS::set_signatures(const system::chain::schnorr_signatures& sigs, const header_link& link, bool bank) NOEXCEPT { using correlate_t = table::schnorr_correlate::put_signatures; - using row_t = table::schnorr_row::put_signatures; + using digest_t = table::schnorr_digest::put_signatures; + using xonly_t = table::schnorr_xonly::put_signatures; + using signature_t = table::schnorr_signature::put_signatures; if (sigs.empty()) return true; @@ -97,10 +108,12 @@ bool CLASS::set_signatures(const system::chain::schnorr_signatures& sigs, // Guard against remap (required for nomaps::put(fk)). const auto guard = bank_table.guard(); - // Write the accumulator to the columns. + // Deinterleave the accumulator into the columns. return bank_table.correlate.put(fk, correlate_t{ {}, link, sigs }) && - bank_table.row.put(fk, row_t{ {}, sigs }); + bank_table.digest.put(fk, digest_t{ {}, sigs }) && + bank_table.xonly.put(fk, xonly_t{ {}, sigs }) && + bank_table.signature.put(fk, signature_t{ {}, sigs }); // ======================================================================== } diff --git a/include/bitcoin/database/impl/query/batch/silent.ipp b/include/bitcoin/database/impl/query/batch/silent.ipp index a8410f191..7d7fbc83e 100644 --- a/include/bitcoin/database/impl/query/batch/silent.ipp +++ b/include/bitcoin/database/impl/query/batch/silent.ipp @@ -34,28 +34,39 @@ bool CLASS::scan_silent(const stopper& cancel, return scan_silent(cancel, keys, zero, store_.scan.count(), callback); } -// Rows [first, last) must begin and end on transaction boundaries. +// Transactions [first, last) are scanned against all output prefixes. TEMPLATE bool CLASS::scan_silent(const stopper& cancel, const system::scan::batch::receiver& keys, size_t first, size_t last, const silent_handler& callback) NOEXCEPT { const auto correlate_ptr = store_.scan.correlate.get_memory(); - const auto row_ptr = store_.scan.row.get_memory(); + const auto point_ptr = store_.scan.point.get_memory(); + const auto outputs_ptr = store_.scan.outputs.get_memory(); + const auto prefix_ptr = store_.scan_prefix.get_memory(); using correlate_t = const table::scan_correlate::span; - using row_t = const system::scan::batch::row_t; + using point_t = const table::scan_point::span; + using outputs_t = const system::scan::batch::outputs_t; + using prefix_t = const system::scan::batch::prefix; using namespace system; const auto correlate = pointer_cast(correlate_ptr.data()); - const auto row = pointer_cast(row_ptr.data()); + const auto point = pointer_cast(point_ptr.data()); + const auto outputs = pointer_cast(outputs_ptr.data()); + const auto prefix = pointer_cast(prefix_ptr.data()); BC_ASSERT(first <= last && last <= store_.scan.count()); const auto count = last - first; + const auto prefixes = possible_narrow_cast( + store_.scan_prefix.count().value); + const system::scan::batch batch { .correlates = { std::next(correlate, first), count }, - .rows = { std::next(row, first), count } + .points = { std::next(point, first), count }, + .outputs = { std::next(outputs, first), count }, + .prefixes = { prefix, prefixes } }; // False return only implies canceled. @@ -176,7 +187,8 @@ bool CLASS::purge_silents(bool bank) NOEXCEPT { // ======================================================================== const auto scope = get_transactor(); - return store_.silent_bank(bank).truncate(0); + return store_.silent_bank(bank).truncate(0) && + store_.silent_prefix_bank(bank).truncate(0); // ======================================================================== } @@ -245,95 +257,93 @@ bool CLASS::get_silent_prevouts(data_chunk& prevouts, return !!sink; } -// The bank is read and computed under its guard alone, then released before -// records are set, as no two tables are guarded at once. +// The bank is quiescent (drain claimed, no writers), so it is read across the +// scan write, which is sourced directly from its columns. Scan rows are +// allocated for every bank tx and prefix, and an invalid tx records no +// outputs, so the scan allocation never waits on the compute. TEMPLATE code CLASS::compute_silents(const stopper& cancel, bool& device, bool bank) NOEXCEPT { - struct computed - { - tx_link link{}; - bool valid{}; - ec_compressed point{}; - std::vector prefixes{}; - }; - using namespace system; - std::vector txs{}; - { - auto& bank_table = store_.silent_bank(bank); - const auto count = possible_narrow_cast( - bank_table.count().value); - if (is_zero(count)) - return error::success; - - const auto correlate_ptr = bank_table.correlate.get_memory(); - const auto row_ptr = bank_table.row.get_memory(); - - using correlate_t = const table::silent_correlate::span; - using row_t = const silent::batch::row_t; - const auto correlates = pointer_cast( - correlate_ptr.data()); - const auto rows = pointer_cast(row_ptr.data()); - - std_vector points{}; - data_chunk valid{}; - const silent::batch batch{ { rows, count } }; - if (!silent::batch::compute(points, valid, device, cancel, batch)) - return error::query_canceled; - - for (size_t row{}; row < count; ++row) - { - const auto& correlate = *std::next(correlates, row); - if (is_zero(row) || correlate != *std::next(correlates, sub1(row))) - { - txs.push_back({ tx_link{ correlate }, is_nonzero(valid[row]), - points[row] }); - } - - const auto& prefix = std::next(rows, row)->prefix; - txs.back().prefixes.push_back( - unsafe_from_little_endian(prefix.data())); - } - } - - size_t rows{}; - for (const auto& tx: txs) - if (tx.valid) - rows += tx.prefixes.size(); - - if (is_zero(rows)) + auto& bank_table = store_.silent_bank(bank); + auto& bank_prefix = store_.silent_prefix_bank(bank); + const auto txs = possible_narrow_cast(bank_table.count().value); + if (is_zero(txs)) return error::success; using word_t = table::scan_correlate::tx::integer; - const auto total = possible_narrow_cast(rows); + using correlate_t = const table::silent_correlate::span; + using sum_t = const table::silent_sum::span; + using hash_t = const table::silent_hash::span; + using outputs_t = const scan::batch::outputs_t; + using prefix_t = const scan::batch::prefix; + using span_t = table::scan_prefix::put_span; // ======================================================================== const auto scope = get_transactor(); - // The valid rows of the bank are allocated and written together. - const auto fk = store_.scan.allocate(total); - if (fk.is_terminal()) + const auto fk = store_.scan.allocate( + possible_narrow_cast(txs)); + const auto pk = store_.scan_prefix.allocate(bank_prefix.count()); + if (fk.is_terminal() || pk.is_terminal()) return error::integrity; // Guard against remap (required for nomaps::put(fk)). const auto guard = store_.scan.guard(); + const auto point_ptr = store_.scan.point.get_memory(); + const auto correlate_ptr = bank_table.correlate.get_memory(); + const auto sum_ptr = bank_table.sum.get_memory(); + const auto hash_ptr = bank_table.hash.get_memory(); + const auto outputs_ptr = bank_table.outputs.get_memory(); + const auto prefix_ptr = bank_prefix.get_memory(); + const auto words = pointer_cast(guard.data()); + const auto correlates = pointer_cast(correlate_ptr.data()); + const auto sums = pointer_cast(sum_ptr.data()); + const auto hashes = pointer_cast(hash_ptr.data()); + const auto outputs = pointer_cast(outputs_ptr.data()); + const auto prefixes = pointer_cast(prefix_ptr.data()); + + // Points are computed directly into the allocated scan rows. + const std::span points + { + std::next(pointer_cast(point_ptr.data()), fk.value), txs + }; - auto row = fk; - for (const auto& tx: txs) + data_chunk valid(txs); + const silent::batch batch{ { sums, txs }, { hashes, txs } }; + if (!silent::batch::compute(points, valid, device, cancel, batch)) + return error::query_canceled; + + auto row = pk; + for (size_t tx{}; tx < txs; ++tx) { - if (!tx.valid) + const scan_link link{ possible_narrow_cast( + fk.value + tx) }; + const tx_link correlate{ *std::next(correlates, tx) }; + const auto& output = *std::next(outputs, tx); + const auto first = from_little_endian(output.first); + const auto count = from_little_endian(output.count); + + if (is_zero(valid[tx])) + { + points[tx] = {}; + if (!put_silent_(link, correlate, row, zero, words)) + return error::integrity; + continue; + } - if (!put_silent_(row, tx.link, tx.point, tx.prefixes, words)) + if (!store_.scan_prefix.put(row, span_t{ {}, + { std::next(prefixes, first), count } }) || + !put_silent_(link, correlate, row, count, words)) return error::integrity; - row += tx.prefixes.size(); + row += count; } - store_.scan.complete(fk, total); + store_.scan.complete(fk, possible_narrow_cast(txs)); return error::success; // ======================================================================== } @@ -393,34 +403,44 @@ bool CLASS::set_silents_(size_t& rows, const header_link& link, if (is_zero(rows)) return true; - using row_t = table::silent_row::put_ref; - using correlate_t = table::silent_correlate::records; + using correlate_t = table::silent_correlate::record; + using sum_t = table::silent_sum::put_ref; + using hash_t = table::silent_hash::put_ref; + using outputs_t = table::silent_outputs::record; + using prefix_t = table::silent_prefix::put_refs; // ======================================================================== const auto scope = get_transactor(); auto& bank_table = store_.silent_bank(bank); + auto& bank_prefix = store_.silent_prefix_bank(bank); - // Allocate all of the block's rows across all columns. + // Allocate all of the block's txs across all columns, and their outputs. using namespace system; const auto fk = bank_table.allocate( - possible_narrow_cast(rows)); - if (fk.is_terminal()) + possible_narrow_cast(captures.size())); + const auto pk = bank_prefix.allocate( + possible_narrow_cast(rows)); + if (fk.is_terminal() || pk.is_terminal()) return false; // Guard against remap (required for nomaps::put(fk)). const auto guard = bank_table.guard(); - auto row = fk; + auto tx = fk; + auto out = pk; for (const auto& item: captures) { - const auto size = item.prefixes.size(); - if (!bank_table.row.put(row, - row_t{ {}, item.prefixes, item.sum, item.hash }) || - !bank_table.correlate.put(row, - correlate_t{ {}, size, item.link.value })) + const auto outputs = item.prefixes.size(); + if (!bank_table.correlate.put(tx, correlate_t{ {}, item.link.value }) || + !bank_table.sum.put(tx, sum_t{ {}, item.sum }) || + !bank_table.hash.put(tx, hash_t{ {}, item.hash }) || + !bank_table.outputs.put(tx, outputs_t{ {}, out.value, + possible_narrow_cast(outputs) }) || + !bank_prefix.put(out, prefix_t{ {}, item.prefixes })) return false; - row += size; + tx += one; + out += outputs; } return true; @@ -465,49 +485,49 @@ bool CLASS::set_silent_(const tx_link& link, const ec_compressed& point, using namespace system; using word_t = table::scan_correlate::tx::integer; - const auto rows = possible_narrow_cast(prefixes.size()); + using point_t = table::scan_point::put_ref; + using prefix_t = table::scan_prefix::put_refs; + const auto outputs = prefixes.size(); // ======================================================================== const auto scope = get_transactor(); - // Allocate rows across all columns. - const auto fk = store_.scan.allocate(rows); - if (fk.is_terminal()) + // Allocate the tx across all columns, and its outputs. + const auto fk = store_.scan.allocate(one); + const auto pk = store_.scan_prefix.allocate( + possible_narrow_cast(outputs)); + if (fk.is_terminal() || pk.is_terminal()) return false; // Guard against remap (required for nomaps::put(fk)). const auto guard = store_.scan.guard(); const auto words = pointer_cast(guard.data()); - if (!put_silent_(fk, link, point, prefixes, words)) + if (!store_.scan_prefix.put(pk, prefix_t{ {}, prefixes }) || + !store_.scan.point.put(fk, point_t{ {}, point }) || + !put_silent_(fk, link, pk, outputs, words)) return false; - store_.scan.complete(fk, rows); + store_.scan.complete(fk, one); return true; // ======================================================================== } TEMPLATE bool CLASS::put_silent_(const scan_link& fk, const tx_link& link, - const ec_compressed& point, const std::vector& prefixes, + const prefix_link& first, size_t outputs, table::scan_correlate::tx::integer* words) NOEXCEPT { using namespace system; - using row_t = table::scan_row::put_ref; - if (!store_.scan.row.put(fk, row_t{ {}, prefixes, point })) + using outputs_t = table::scan_outputs::record; + if (!store_.scan.outputs.put(fk, outputs_t{ {}, first.value, + possible_narrow_cast(outputs) })) return false; // The guard is the correlate column, published last (get_silent_frontier). using word_t = table::scan_correlate::tx::integer; static_assert(schema::scan_correlate::minrow == sizeof(word_t)); - const auto value = native_to_little_end(link.value); - const auto end = fk.value + prefixes.size(); - - for (auto row = fk.value; row < end; ++row) - { - std::atomic_ref word{ *std::next(words, row) }; - word.store(value, std::memory_order_release); - } - + std::atomic_ref word{ *std::next(words, fk.value) }; + word.store(native_to_little_end(link.value), std::memory_order_release); return true; } diff --git a/include/bitcoin/database/impl/query/extent.ipp b/include/bitcoin/database/impl/query/extent.ipp index dd5fdef0d..52a9c9f1f 100644 --- a/include/bitcoin/database/impl/query/extent.ipp +++ b/include/bitcoin/database/impl/query/extent.ipp @@ -91,12 +91,15 @@ size_t CLASS::store_body_size() const NOEXCEPT + confirmed_body_size() + strong_tx_body_size() + scan_body_size() + + scan_prefix_body_size() + ecdsa0_body_size() + ecdsa1_body_size() + schnorr0_body_size() + schnorr1_body_size() + silent0_body_size() + silent1_body_size() + + silent0_prefix_body_size() + + silent1_prefix_body_size() + duplicate_body_size() + prevalid0_body_size() + prevalid1_body_size() @@ -129,12 +132,15 @@ size_t CLASS::store_head_size() const NOEXCEPT + confirmed_head_size() + strong_tx_head_size() + scan_head_size() + + scan_prefix_head_size() + ecdsa0_head_size() + ecdsa1_head_size() + schnorr0_head_size() + schnorr1_head_size() + silent0_head_size() + silent1_head_size() + + silent0_prefix_head_size() + + silent1_prefix_head_size() + duplicate_head_size() + prevalid0_head_size() + prevalid1_head_size() @@ -162,12 +168,15 @@ DEFINE_SIZES(candidate) DEFINE_SIZES(confirmed) DEFINE_SIZES(strong_tx) DEFINE_SIZES(scan) +DEFINE_SIZES(scan_prefix) DEFINE_SIZES(ecdsa0) DEFINE_SIZES(ecdsa1) DEFINE_SIZES(schnorr0) DEFINE_SIZES(schnorr1) DEFINE_SIZES(silent0) DEFINE_SIZES(silent1) +DEFINE_SIZES(silent0_prefix) +DEFINE_SIZES(silent1_prefix) DEFINE_SIZES(duplicate) DEFINE_SIZES(prevalid0) DEFINE_SIZES(prevalid1) @@ -209,12 +218,15 @@ DEFINE_RECORDS(candidate) DEFINE_RECORDS(confirmed) DEFINE_RECORDS(strong_tx) DEFINE_RECORDS(scan) +DEFINE_RECORDS(scan_prefix) DEFINE_RECORDS(ecdsa0) DEFINE_RECORDS(ecdsa1) DEFINE_RECORDS(schnorr0) DEFINE_RECORDS(schnorr1) DEFINE_RECORDS(silent0) DEFINE_RECORDS(silent1) +DEFINE_RECORDS(silent0_prefix) +DEFINE_RECORDS(silent1_prefix) DEFINE_RECORDS(duplicate) DEFINE_RECORDS(prevalid0) DEFINE_RECORDS(prevalid1) diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp index b177a0944..ad9f75d56 100644 --- a/include/bitcoin/database/impl/store/store.ipp +++ b/include/bitcoin/database/impl/store/store.ipp @@ -77,6 +77,8 @@ CLASS::store(const settings& config) NOEXCEPT strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx, sequential, staged), scan_head_(head(config.path / schema::dir::heads, schema::indexes::scan), head_settings(config.scan), sequential), scan_body_(body(config.path, schema::indexes::scan), config.scan, sequential, staged), + scan_prefix_head_(head(config.path / schema::dir::heads, schema::indexes::scan_prefix), head_settings(config.scan), sequential), + scan_prefix_body_(body(config.path, schema::indexes::scan_prefix), config.scan, sequential, staged), // Caches. // ------------------------------------------------------------------------ @@ -97,6 +99,10 @@ CLASS::store(const settings& config) NOEXCEPT silent0_body_(body(config.path, schema::caches::silent0), config.silent, sequential, staged), silent1_head_(head(config.path / schema::dir::heads, schema::caches::silent1), head_settings(config.silent), sequential), silent1_body_(body(config.path, schema::caches::silent1), config.silent, sequential, staged), + silent0_prefix_head_(head(config.path / schema::dir::heads, schema::caches::silent0_prefix), head_settings(config.silent), sequential), + silent0_prefix_body_(body(config.path, schema::caches::silent0_prefix), config.silent, sequential, staged), + silent1_prefix_head_(head(config.path / schema::dir::heads, schema::caches::silent1_prefix), head_settings(config.silent), sequential), + silent1_prefix_body_(body(config.path, schema::caches::silent1_prefix), config.silent, sequential, staged), envelope_head_(head(config.path / schema::dir::heads, schema::caches::envelope), head_settings(config.duplicate), sequential), duplicate_head_(head(config.path / schema::dir::heads, schema::indexes::duplicate), head_settings(config.duplicate), random), @@ -150,6 +156,7 @@ CLASS::store(const settings& config) NOEXCEPT confirmed(confirmed_head_), strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx.buckets, config.strong_tx.expected), scan(scan_head_, scan_body_), + scan_prefix(scan_prefix_head_, scan_prefix_body_), ecdsa0(ecdsa0_head_, ecdsa0_body_), ecdsa1(ecdsa1_head_, ecdsa1_body_), @@ -157,6 +164,8 @@ CLASS::store(const settings& config) NOEXCEPT schnorr1(schnorr1_head_, schnorr1_body_), silent0(silent0_head_, silent0_body_), silent1(silent1_head_, silent1_body_), + silent0_prefix(silent0_prefix_head_, silent0_prefix_body_), + silent1_prefix(silent1_prefix_head_, silent1_prefix_body_), envelope(envelope_head_), duplicate(duplicate_head_, duplicate_body_, config.duplicate.buckets, config.duplicate.expected), prevalid0(prevalid0_head_, prevalid0_body_), @@ -260,6 +269,12 @@ table::silent& CLASS::silent_bank(bool bank) NOEXCEPT return bank ? silent1 : silent0; } +TEMPLATE +table::silent_prefix& CLASS::silent_prefix_bank(bool bank) NOEXCEPT +{ + return bank ? silent1_prefix : silent0_prefix; +} + TEMPLATE table::schnorr& CLASS::schnorr_bank(bool bank) NOEXCEPT { @@ -308,12 +323,15 @@ void CLASS::set_current(bool current) NOEXCEPT confirmed_head_.current(current); strong_tx_head_.current(current); scan_head_.current(current); + scan_prefix_head_.current(current); ecdsa0_head_.current(current); ecdsa1_head_.current(current); schnorr0_head_.current(current); schnorr1_head_.current(current); silent0_head_.current(current); silent1_head_.current(current); + silent0_prefix_head_.current(current); + silent1_prefix_head_.current(current); envelope_head_.current(current); duplicate_head_.current(current); prevalid0_head_.current(current); diff --git a/include/bitcoin/database/impl/store/store_backup.ipp b/include/bitcoin/database/impl/store/store_backup.ipp index d625a8e35..71522e9a2 100644 --- a/include/bitcoin/database/impl/store/store_backup.ipp +++ b/include/bitcoin/database/impl/store/store_backup.ipp @@ -54,6 +54,7 @@ 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, scan, table_t::scan_table); + backup(ec, scan_prefix, table_t::scan_prefix_table); backup(ec, ecdsa0, table_t::ecdsa0_table); backup(ec, ecdsa1, table_t::ecdsa1_table); @@ -61,6 +62,8 @@ code CLASS::backup(const event_handler& handler, bool prune) NOEXCEPT backup(ec, schnorr1, table_t::schnorr1_table); backup(ec, silent0, table_t::silent0_table); backup(ec, silent1, table_t::silent1_table); + backup(ec, silent0_prefix, table_t::silent0_prefix_table); + backup(ec, silent1_prefix, table_t::silent1_prefix_table); backup(ec, envelope, table_t::envelope_table); backup(ec, duplicate, table_t::duplicate_table); backup(ec, prevalid0, table_t::prevalid0_table, prune); diff --git a/include/bitcoin/database/impl/store/store_close.ipp b/include/bitcoin/database/impl/store/store_close.ipp index addfa6f99..33b8613f5 100644 --- a/include/bitcoin/database/impl/store/store_close.ipp +++ b/include/bitcoin/database/impl/store/store_close.ipp @@ -58,6 +58,7 @@ 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, scan, table_t::scan_table); + close(ec, scan_prefix, table_t::scan_prefix_table); close(ec, ecdsa0, table_t::ecdsa0_table); close(ec, ecdsa1, table_t::ecdsa1_table); @@ -65,6 +66,8 @@ code CLASS::close(const event_handler& handler) NOEXCEPT close(ec, schnorr1, table_t::schnorr1_table); close(ec, silent0, table_t::silent0_table); close(ec, silent1, table_t::silent1_table); + close(ec, silent0_prefix, table_t::silent0_prefix_table); + close(ec, silent1_prefix, table_t::silent1_prefix_table); close(ec, envelope, table_t::envelope_table); close(ec, duplicate, table_t::duplicate_table); close(ec, prevalid0, table_t::prevalid0_table); diff --git a/include/bitcoin/database/impl/store/store_create.ipp b/include/bitcoin/database/impl/store/store_create.ipp index b08412c6d..44072b303 100644 --- a/include/bitcoin/database/impl/store/store_create.ipp +++ b/include/bitcoin/database/impl/store/store_create.ipp @@ -100,6 +100,8 @@ code CLASS::create_load(const event_handler& handler) NOEXCEPT create(ec, strong_tx_body_, table_t::strong_tx_body); create(ec, scan_head_, table_t::scan_head); create(ec, scan_body_, table_t::scan_body); + create(ec, scan_prefix_head_, table_t::scan_prefix_head); + create(ec, scan_prefix_body_, table_t::scan_prefix_body); create(ec, ecdsa0_head_, table_t::ecdsa0_head); create(ec, ecdsa0_body_, table_t::ecdsa0_body); @@ -113,6 +115,10 @@ code CLASS::create_load(const event_handler& handler) NOEXCEPT create(ec, silent0_body_, table_t::silent0_body); create(ec, silent1_head_, table_t::silent1_head); create(ec, silent1_body_, table_t::silent1_body); + create(ec, silent0_prefix_head_, table_t::silent0_prefix_head); + create(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + create(ec, silent1_prefix_head_, table_t::silent1_prefix_head); + create(ec, silent1_prefix_body_, table_t::silent1_prefix_body); create(ec, envelope_head_, table_t::envelope_head); create(ec, duplicate_head_, table_t::duplicate_head); create(ec, duplicate_body_, table_t::duplicate_body); @@ -162,6 +168,7 @@ code CLASS::create_load(const event_handler& handler) NOEXCEPT populate(ec, confirmed, table_t::confirmed_table); populate(ec, strong_tx, table_t::strong_tx_table); populate(ec, scan, table_t::scan_table); + populate(ec, scan_prefix, table_t::scan_prefix_table); populate(ec, ecdsa0, table_t::ecdsa0_table); populate(ec, ecdsa1, table_t::ecdsa1_table); @@ -169,6 +176,8 @@ code CLASS::create_load(const event_handler& handler) NOEXCEPT populate(ec, schnorr1, table_t::schnorr1_table); populate(ec, silent0, table_t::silent0_table); populate(ec, silent1, table_t::silent1_table); + populate(ec, silent0_prefix, table_t::silent0_prefix_table); + populate(ec, silent1_prefix, table_t::silent1_prefix_table); populate(ec, envelope, table_t::envelope_table); populate(ec, duplicate, table_t::duplicate_table); populate(ec, prevalid0, table_t::prevalid0_table); diff --git a/include/bitcoin/database/impl/store/store_dump.ipp b/include/bitcoin/database/impl/store/store_dump.ipp index 9738dd8db..1a462e2d0 100644 --- a/include/bitcoin/database/impl/store/store_dump.ipp +++ b/include/bitcoin/database/impl/store/store_dump.ipp @@ -54,6 +54,7 @@ code CLASS::dump(const path& folder, dump(ec, confirmed_head_, schema::indexes::confirmed, table_t::confirmed_head); dump(ec, strong_tx_head_, schema::indexes::strong_tx, table_t::strong_tx_head); dump(ec, scan_head_, schema::indexes::scan, table_t::scan_head); + dump(ec, scan_prefix_head_, schema::indexes::scan_prefix, table_t::scan_prefix_head); dump(ec, ecdsa0_head_, schema::caches::ecdsa0, table_t::ecdsa0_head); dump(ec, ecdsa1_head_, schema::caches::ecdsa1, table_t::ecdsa1_head); @@ -61,6 +62,8 @@ code CLASS::dump(const path& folder, dump(ec, schnorr1_head_, schema::caches::schnorr1, table_t::schnorr1_head); dump(ec, silent0_head_, schema::caches::silent0, table_t::silent0_head); dump(ec, silent1_head_, schema::caches::silent1, table_t::silent1_head); + dump(ec, silent0_prefix_head_, schema::caches::silent0_prefix, table_t::silent0_prefix_head); + dump(ec, silent1_prefix_head_, schema::caches::silent1_prefix, table_t::silent1_prefix_head); dump(ec, envelope_head_, schema::caches::envelope, table_t::envelope_head); dump(ec, duplicate_head_, schema::indexes::duplicate, table_t::duplicate_head); dump(ec, prevalid0_head_, schema::caches::prevalid0, table_t::prevalid0_head); diff --git a/include/bitcoin/database/impl/store/store_open.ipp b/include/bitcoin/database/impl/store/store_open.ipp index e65c04c7a..9c12e0f52 100644 --- a/include/bitcoin/database/impl/store/store_open.ipp +++ b/include/bitcoin/database/impl/store/store_open.ipp @@ -76,6 +76,7 @@ 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, scan, table_t::scan_table); + verify(ec, scan_prefix, table_t::scan_prefix_table); verify(ec, ecdsa0, table_t::ecdsa0_table); verify(ec, ecdsa1, table_t::ecdsa1_table); @@ -83,6 +84,8 @@ code CLASS::open(const event_handler& handler) NOEXCEPT verify(ec, schnorr1, table_t::schnorr1_table); verify(ec, silent0, table_t::silent0_table); verify(ec, silent1, table_t::silent1_table); + verify(ec, silent0_prefix, table_t::silent0_prefix_table); + verify(ec, silent1_prefix, table_t::silent1_prefix_table); verify(ec, envelope, table_t::envelope_table); verify(ec, duplicate, table_t::duplicate_table); verify(ec, prevalid0, table_t::prevalid0_table); diff --git a/include/bitcoin/database/impl/store/store_open_load.ipp b/include/bitcoin/database/impl/store/store_open_load.ipp index 1240d13dc..f66dc1080 100644 --- a/include/bitcoin/database/impl/store/store_open_load.ipp +++ b/include/bitcoin/database/impl/store/store_open_load.ipp @@ -84,6 +84,8 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT open(ec, strong_tx_body_, table_t::strong_tx_body); open(ec, scan_head_, table_t::scan_head); open(ec, scan_body_, table_t::scan_body); + open(ec, scan_prefix_head_, table_t::scan_prefix_head); + open(ec, scan_prefix_body_, table_t::scan_prefix_body); open(ec, ecdsa0_head_, table_t::ecdsa0_head); open(ec, ecdsa0_body_, table_t::ecdsa0_body); @@ -97,6 +99,10 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT open(ec, silent0_body_, table_t::silent0_body); open(ec, silent1_head_, table_t::silent1_head); open(ec, silent1_body_, table_t::silent1_body); + open(ec, silent0_prefix_head_, table_t::silent0_prefix_head); + open(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + open(ec, silent1_prefix_head_, table_t::silent1_prefix_head); + open(ec, silent1_prefix_body_, table_t::silent1_prefix_body); open(ec, duplicate_head_, table_t::duplicate_head); open(ec, duplicate_body_, table_t::duplicate_body); open(ec, prevalid0_head_, table_t::prevalid0_head); @@ -140,6 +146,8 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT load(ec, strong_tx_body_, table_t::strong_tx_body); load(ec, scan_head_, table_t::scan_head); load(ec, scan_body_, table_t::scan_body); + load(ec, scan_prefix_head_, table_t::scan_prefix_head); + load(ec, scan_prefix_body_, table_t::scan_prefix_body); load(ec, ecdsa0_head_, table_t::ecdsa0_head); load(ec, ecdsa0_body_, table_t::ecdsa0_body); @@ -153,6 +161,10 @@ code CLASS::open_load(const event_handler& handler) NOEXCEPT load(ec, silent0_body_, table_t::silent0_body); load(ec, silent1_head_, table_t::silent1_head); load(ec, silent1_body_, table_t::silent1_body); + load(ec, silent0_prefix_head_, table_t::silent0_prefix_head); + load(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + load(ec, silent1_prefix_head_, table_t::silent1_prefix_head); + load(ec, silent1_prefix_body_, table_t::silent1_prefix_body); load(ec, duplicate_head_, table_t::duplicate_head); load(ec, duplicate_body_, table_t::duplicate_body); load(ec, prevalid0_head_, table_t::prevalid0_head); diff --git a/include/bitcoin/database/impl/store/store_prune.ipp b/include/bitcoin/database/impl/store/store_prune.ipp index 48f90f407..54d6be917 100644 --- a/include/bitcoin/database/impl/store/store_prune.ipp +++ b/include/bitcoin/database/impl/store/store_prune.ipp @@ -108,6 +108,13 @@ code CLASS::prune(const event_handler& handler) NOEXCEPT handler(event_t::unload_file, table_t::silent1_body); if (!ec) ec = silent1_body_.shrink(); handler(event_t::load_file, table_t::silent1_body); + + handler(event_t::unload_file, table_t::silent0_prefix_body); + if (!ec) ec = silent0_prefix_body_.shrink(); + handler(event_t::load_file, table_t::silent0_prefix_body); + handler(event_t::unload_file, table_t::silent1_prefix_body); + if (!ec) ec = silent1_prefix_body_.shrink(); + handler(event_t::load_file, table_t::silent1_prefix_body); } } diff --git a/include/bitcoin/database/impl/store/store_reload.ipp b/include/bitcoin/database/impl/store/store_reload.ipp index 5fbaf34fc..e2daa3b69 100644 --- a/include/bitcoin/database/impl/store/store_reload.ipp +++ b/include/bitcoin/database/impl/store/store_reload.ipp @@ -74,6 +74,8 @@ code CLASS::reload(const event_handler& handler) NOEXCEPT reload(ec, strong_tx_body_, table_t::strong_tx_body); reload(ec, scan_head_, table_t::scan_head); reload(ec, scan_body_, table_t::scan_body); + reload(ec, scan_prefix_head_, table_t::scan_prefix_head); + reload(ec, scan_prefix_body_, table_t::scan_prefix_body); reload(ec, ecdsa0_head_, table_t::ecdsa0_head); reload(ec, ecdsa0_body_, table_t::ecdsa0_body); @@ -87,6 +89,10 @@ code CLASS::reload(const event_handler& handler) NOEXCEPT reload(ec, silent0_body_, table_t::silent0_body); reload(ec, silent1_head_, table_t::silent1_head); reload(ec, silent1_body_, table_t::silent1_body); + reload(ec, silent0_prefix_head_, table_t::silent0_prefix_head); + reload(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + reload(ec, silent1_prefix_head_, table_t::silent1_prefix_head); + reload(ec, silent1_prefix_body_, table_t::silent1_prefix_body); reload(ec, envelope_head_, table_t::envelope_head); reload(ec, duplicate_head_, table_t::duplicate_head); reload(ec, duplicate_body_, table_t::duplicate_body); diff --git a/include/bitcoin/database/impl/store/store_report.ipp b/include/bitcoin/database/impl/store/store_report.ipp index 4a9133783..601b2eebf 100644 --- a/include/bitcoin/database/impl/store/store_report.ipp +++ b/include/bitcoin/database/impl/store/store_report.ipp @@ -50,12 +50,15 @@ void CLASS::report(const error_handler& handler) const NOEXCEPT report(txs_body_, table_t::txs_body); report(strong_tx_body_, table_t::strong_tx_body); report(scan_body_, table_t::scan_body); + report(scan_prefix_body_, table_t::scan_prefix_body); report(ecdsa0_body_, table_t::ecdsa0_body); report(ecdsa1_body_, table_t::ecdsa1_body); report(schnorr0_body_, table_t::schnorr0_body); report(schnorr1_body_, table_t::schnorr1_body); report(silent0_body_, table_t::silent0_body); report(silent1_body_, table_t::silent1_body); + report(silent0_prefix_body_, table_t::silent0_prefix_body); + report(silent1_prefix_body_, table_t::silent1_prefix_body); report(duplicate_body_, table_t::duplicate_body); report(prevalid0_body_, table_t::prevalid0_body); report(prevalid1_body_, table_t::prevalid1_body); @@ -93,6 +96,8 @@ code CLASS::get_fault() const NOEXCEPT if ((ec = strong_tx_body_.get_fault())) return ec; if ((ec = scan_head_.get_fault())) return ec; if ((ec = scan_body_.get_fault())) return ec; + if ((ec = scan_prefix_head_.get_fault())) return ec; + if ((ec = scan_prefix_body_.get_fault())) return ec; if ((ec = ecdsa0_head_.get_fault())) return ec; if ((ec = ecdsa0_body_.get_fault())) return ec; if ((ec = ecdsa1_head_.get_fault())) return ec; @@ -105,6 +110,10 @@ code CLASS::get_fault() const NOEXCEPT if ((ec = silent0_body_.get_fault())) return ec; if ((ec = silent1_head_.get_fault())) return ec; if ((ec = silent1_body_.get_fault())) return ec; + if ((ec = silent0_prefix_head_.get_fault())) return ec; + if ((ec = silent0_prefix_body_.get_fault())) return ec; + if ((ec = silent1_prefix_head_.get_fault())) return ec; + if ((ec = silent1_prefix_body_.get_fault())) return ec; if ((ec = envelope_head_.get_fault())) return ec; if ((ec = duplicate_head_.get_fault())) return ec; if ((ec = duplicate_body_.get_fault())) return ec; @@ -159,6 +168,8 @@ size_t CLASS::get_space() const NOEXCEPT space(strong_tx_body_); space(scan_head_); space(scan_body_); + space(scan_prefix_head_); + space(scan_prefix_body_); space(ecdsa0_head_); space(ecdsa0_body_); space(ecdsa1_head_); @@ -171,6 +182,10 @@ size_t CLASS::get_space() const NOEXCEPT space(silent0_body_); space(silent1_head_); space(silent1_body_); + space(silent0_prefix_head_); + space(silent0_prefix_body_); + space(silent1_prefix_head_); + space(silent1_prefix_body_); space(envelope_head_); space(duplicate_head_); space(duplicate_body_); diff --git a/include/bitcoin/database/impl/store/store_restore.ipp b/include/bitcoin/database/impl/store/store_restore.ipp index 33007a1e8..648f216ec 100644 --- a/include/bitcoin/database/impl/store/store_restore.ipp +++ b/include/bitcoin/database/impl/store/store_restore.ipp @@ -139,6 +139,7 @@ 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, scan, table_t::scan_table); + restore(ec, scan_prefix, table_t::scan_prefix_table); // ecdsa, schnorr, and prevalid are dropped. //--------------------------------------------------------------------- @@ -155,6 +156,8 @@ code CLASS::restore(const event_handler& handler) NOEXCEPT dropped(ec, schnorr1, table_t::schnorr1_table); restore(ec, silent0, table_t::silent0_table); restore(ec, silent1, table_t::silent1_table); + restore(ec, silent0_prefix, table_t::silent0_prefix_table); + restore(ec, silent1_prefix, table_t::silent1_prefix_table); restore(ec, envelope, table_t::envelope_table); restore(ec, duplicate, table_t::duplicate_table); dropped(ec, prevalid0, table_t::prevalid0_table); diff --git a/include/bitcoin/database/impl/store/store_snapshot.ipp b/include/bitcoin/database/impl/store/store_snapshot.ipp index 60e92fb13..428f7d8e6 100644 --- a/include/bitcoin/database/impl/store/store_snapshot.ipp +++ b/include/bitcoin/database/impl/store/store_snapshot.ipp @@ -55,6 +55,7 @@ code CLASS::snapshot(const event_handler& handler, bool prune) NOEXCEPT flush(ec, strong_tx_body_, table_t::strong_tx_body); flush(ec, scan_body_, table_t::scan_body); + flush(ec, scan_prefix_body_, table_t::scan_prefix_body); flush(ec, ecdsa0_body_, table_t::ecdsa0_body); flush(ec, ecdsa1_body_, table_t::ecdsa1_body); @@ -62,6 +63,8 @@ code CLASS::snapshot(const event_handler& handler, bool prune) NOEXCEPT flush(ec, schnorr1_body_, table_t::schnorr1_body); flush(ec, silent0_body_, table_t::silent0_body); flush(ec, silent1_body_, table_t::silent1_body); + flush(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + flush(ec, silent1_prefix_body_, table_t::silent1_prefix_body); flush(ec, duplicate_body_, table_t::duplicate_body); flush(ec, prevalid0_body_, table_t::prevalid0_body); flush(ec, prevalid1_body_, table_t::prevalid1_body); diff --git a/include/bitcoin/database/impl/store/store_tables.ipp b/include/bitcoin/database/impl/store/store_tables.ipp index 55d80227c..3fbb54440 100644 --- a/include/bitcoin/database/impl/store/store_tables.ipp +++ b/include/bitcoin/database/impl/store/store_tables.ipp @@ -63,6 +63,9 @@ const CLASS::table_map CLASS::tables { table_t::scan_table, "scan_table" }, { table_t::scan_head, "scan_head" }, { table_t::scan_body, "scan_body" }, + { table_t::scan_prefix_table, "scan_prefix_table" }, + { table_t::scan_prefix_head, "scan_prefix_head" }, + { table_t::scan_prefix_body, "scan_prefix_body" }, // Caches. { table_t::ecdsa0_table, "ecdsa0_table" }, @@ -83,6 +86,12 @@ const CLASS::table_map CLASS::tables { table_t::silent1_table, "silent1_table" }, { table_t::silent1_head, "silent1_head" }, { table_t::silent1_body, "silent1_body" }, + { table_t::silent0_prefix_table, "silent0_prefix_table" }, + { table_t::silent0_prefix_head, "silent0_prefix_head" }, + { table_t::silent0_prefix_body, "silent0_prefix_body" }, + { table_t::silent1_prefix_table, "silent1_prefix_table" }, + { table_t::silent1_prefix_head, "silent1_prefix_head" }, + { table_t::silent1_prefix_body, "silent1_prefix_body" }, { table_t::envelope_table, "envelope_table" }, { table_t::envelope_head, "envelope_head" }, { table_t::duplicate_table, "duplicate_table" }, diff --git a/include/bitcoin/database/impl/store/store_unload_close.ipp b/include/bitcoin/database/impl/store/store_unload_close.ipp index 1ee3e5b43..193bd179c 100644 --- a/include/bitcoin/database/impl/store/store_unload_close.ipp +++ b/include/bitcoin/database/impl/store/store_unload_close.ipp @@ -61,6 +61,8 @@ code CLASS::unload_close(const event_handler& handler) NOEXCEPT unload(ec, strong_tx_body_, table_t::strong_tx_body); unload(ec, scan_head_, table_t::scan_head); unload(ec, scan_body_, table_t::scan_body); + unload(ec, scan_prefix_head_, table_t::scan_prefix_head); + unload(ec, scan_prefix_body_, table_t::scan_prefix_body); unload(ec, ecdsa0_head_, table_t::ecdsa0_head); unload(ec, ecdsa0_body_, table_t::ecdsa0_body); @@ -74,6 +76,10 @@ code CLASS::unload_close(const event_handler& handler) NOEXCEPT unload(ec, silent0_body_, table_t::silent0_body); unload(ec, silent1_head_, table_t::silent1_head); unload(ec, silent1_body_, table_t::silent1_body); + unload(ec, silent0_prefix_head_, table_t::silent0_prefix_head); + unload(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + unload(ec, silent1_prefix_head_, table_t::silent1_prefix_head); + unload(ec, silent1_prefix_body_, table_t::silent1_prefix_body); unload(ec, envelope_head_, table_t::envelope_head); unload(ec, duplicate_head_, table_t::duplicate_head); unload(ec, duplicate_body_, table_t::duplicate_body); @@ -127,6 +133,8 @@ code CLASS::unload_close(const event_handler& handler) NOEXCEPT close(ec, strong_tx_body_, table_t::strong_tx_body); close(ec, scan_head_, table_t::scan_head); close(ec, scan_body_, table_t::scan_body); + close(ec, scan_prefix_head_, table_t::scan_prefix_head); + close(ec, scan_prefix_body_, table_t::scan_prefix_body); close(ec, ecdsa0_head_, table_t::ecdsa0_head); close(ec, ecdsa0_body_, table_t::ecdsa0_body); @@ -140,6 +148,10 @@ code CLASS::unload_close(const event_handler& handler) NOEXCEPT close(ec, silent0_body_, table_t::silent0_body); close(ec, silent1_head_, table_t::silent1_head); close(ec, silent1_body_, table_t::silent1_body); + close(ec, silent0_prefix_head_, table_t::silent0_prefix_head); + close(ec, silent0_prefix_body_, table_t::silent0_prefix_body); + close(ec, silent1_prefix_head_, table_t::silent1_prefix_head); + close(ec, silent1_prefix_body_, table_t::silent1_prefix_body); close(ec, envelope_head_, table_t::envelope_head); close(ec, duplicate_head_, table_t::duplicate_head); close(ec, duplicate_body_, table_t::duplicate_body); diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index f46cfc9ad..186daee54 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -142,12 +142,15 @@ class query size_t confirmed_head_size() const NOEXCEPT; size_t strong_tx_head_size() const NOEXCEPT; size_t scan_head_size() const NOEXCEPT; + size_t scan_prefix_head_size() const NOEXCEPT; size_t ecdsa0_head_size() const NOEXCEPT; size_t ecdsa1_head_size() const NOEXCEPT; size_t schnorr0_head_size() const NOEXCEPT; size_t schnorr1_head_size() const NOEXCEPT; size_t silent0_head_size() const NOEXCEPT; size_t silent1_head_size() const NOEXCEPT; + size_t silent0_prefix_head_size() const NOEXCEPT; + size_t silent1_prefix_head_size() const NOEXCEPT; size_t duplicate_head_size() const NOEXCEPT; size_t prevalid0_head_size() const NOEXCEPT; size_t prevalid1_head_size() const NOEXCEPT; @@ -172,12 +175,15 @@ class query size_t confirmed_body_size() const NOEXCEPT; size_t strong_tx_body_size() const NOEXCEPT; size_t scan_body_size() const NOEXCEPT; + size_t scan_prefix_body_size() const NOEXCEPT; size_t ecdsa0_body_size() const NOEXCEPT; size_t ecdsa1_body_size() const NOEXCEPT; size_t schnorr0_body_size() const NOEXCEPT; size_t schnorr1_body_size() const NOEXCEPT; size_t silent0_body_size() const NOEXCEPT; size_t silent1_body_size() const NOEXCEPT; + size_t silent0_prefix_body_size() const NOEXCEPT; + size_t silent1_prefix_body_size() const NOEXCEPT; size_t duplicate_body_size() const NOEXCEPT; size_t prevalid0_body_size() const NOEXCEPT; size_t prevalid1_body_size() const NOEXCEPT; @@ -202,12 +208,15 @@ class query size_t confirmed_size() const NOEXCEPT; size_t strong_tx_size() const NOEXCEPT; size_t scan_size() const NOEXCEPT; + size_t scan_prefix_size() const NOEXCEPT; size_t ecdsa0_size() const NOEXCEPT; size_t ecdsa1_size() const NOEXCEPT; size_t schnorr0_size() const NOEXCEPT; size_t schnorr1_size() const NOEXCEPT; size_t silent0_size() const NOEXCEPT; size_t silent1_size() const NOEXCEPT; + size_t silent0_prefix_size() const NOEXCEPT; + size_t silent1_prefix_size() const NOEXCEPT; size_t duplicate_size() const NOEXCEPT; size_t prevalid0_size() const NOEXCEPT; size_t prevalid1_size() const NOEXCEPT; @@ -245,12 +254,15 @@ class query size_t confirmed_records() const NOEXCEPT; size_t strong_tx_records() const NOEXCEPT; size_t scan_records() const NOEXCEPT; + size_t scan_prefix_records() const NOEXCEPT; size_t ecdsa0_records() const NOEXCEPT; size_t ecdsa1_records() const NOEXCEPT; size_t schnorr0_records() const NOEXCEPT; size_t schnorr1_records() const NOEXCEPT; size_t silent0_records() const NOEXCEPT; size_t silent1_records() const NOEXCEPT; + size_t silent0_prefix_records() const NOEXCEPT; + size_t silent1_prefix_records() const NOEXCEPT; size_t duplicate_records() const NOEXCEPT; size_t prevalid0_records() const NOEXCEPT; size_t prevalid1_records() const NOEXCEPT; @@ -1105,9 +1117,9 @@ class query bool set_silent_(const tx_link& link, const ec_compressed& point, const std::vector& prefixes) NOEXCEPT; - /// Write silent payment records at fk, caller must hold transactor. + /// Write a scan tx's outputs and publish it, caller must hold transactor. bool put_silent_(const scan_link& fk, const tx_link& link, - const ec_compressed& point, const std::vector& prefixes, + const prefix_link& first, size_t outputs, table::scan_correlate::tx::integer* words) NOEXCEPT; /// A block's tx is indexed unless linked below its coinbase and pooled. diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp index 13ba9c09e..41f7f1ac6 100644 --- a/include/bitcoin/database/store.hpp +++ b/include/bitcoin/database/store.hpp @@ -121,6 +121,7 @@ class store table::ecdsa& ecdsa_bank(bool bank) NOEXCEPT; table::schnorr& schnorr_bank(bool bank) NOEXCEPT; table::silent& silent_bank(bool bank) NOEXCEPT; + table::silent_prefix& silent_prefix_bank(bool bank) NOEXCEPT; table::prevalid& prevalid_bank(bool bank) NOEXCEPT; /// Reevaluate currency (called by chain writes). @@ -215,6 +216,8 @@ class store Storage strong_tx_body_; Storage scan_head_; table::scan_storage scan_body_; + Storage scan_prefix_head_; + Storage scan_prefix_body_; /// Caches. /// ----------------------------------------------------------------------- @@ -236,6 +239,10 @@ class store table::silent_storage silent0_body_; Storage silent1_head_; table::silent_storage silent1_body_; + Storage silent0_prefix_head_; + Storage silent0_prefix_body_; + Storage silent1_prefix_head_; + Storage silent1_prefix_body_; // headmap slab Storage envelope_head_; @@ -352,6 +359,7 @@ class store table::height confirmed; table::strong_tx strong_tx; table::scan scan; + table::scan_prefix scan_prefix; /// Caches. table::ecdsa ecdsa0; @@ -360,6 +368,8 @@ class store table::schnorr schnorr1; table::silent silent0; table::silent silent1; + table::silent_prefix silent0_prefix; + table::silent_prefix silent1_prefix; table::envelope envelope; table::duplicate duplicate; table::prevalid prevalid0; diff --git a/include/bitcoin/database/tables/caches/ecdsa.hpp b/include/bitcoin/database/tables/caches/ecdsa.hpp index 665fb302e..02748e810 100644 --- a/include/bitcoin/database/tables/caches/ecdsa.hpp +++ b/include/bitcoin/database/tables/caches/ecdsa.hpp @@ -28,14 +28,14 @@ namespace libbitcoin { namespace database { namespace table { -/// ecdsa_row is an array of ecdsa verification digest|key|signature rows. -struct ecdsa_row - : public no_map +/// ecdsa_digest is an array of ecdsa verification record signature hashes. +struct ecdsa_digest + : public no_map { - using no_map::nomap; + using no_map::nomap; struct put_ref - : public schema::ecdsa_row + : public schema::ecdsa_digest { inline link count() const NOEXCEPT { @@ -45,19 +45,92 @@ struct ecdsa_row inline bool to_data(flipper& sink) const NOEXCEPT { sink.write_bytes(digest); - sink.write_bytes(key); - sink.write_bytes(signature); BC_ASSERT(!sink || sink.get_write_position() == minrow); return sink; } const hash_digest& digest; + }; + + struct put_refs + : public schema::ecdsa_digest + { + inline link count() const NOEXCEPT + { + return system::possible_narrow_cast(rows); + } + + inline bool to_data(flipper& sink) const NOEXCEPT + { + // ecdsa multisig capture is limited to common signature hash. + for (size_t row{}; row < rows; ++row) + sink.write_bytes(digest); + + BC_ASSERT(!sink || sink.get_write_position() == count() * minrow); + return sink; + } + + const size_t rows{}; + const hash_digest& digest; + }; + + struct put_signatures + : public schema::ecdsa_digest + { + inline link count() const NOEXCEPT + { + return system::possible_narrow_cast(sigs.rows()); + } + + inline bool to_data(flipper& sink) const NOEXCEPT + { + using namespace system; + sigs.for_each([&](const hash_digest& digest, + std::span keys, + std::span sigs) NOEXCEPT + { + // Group capture is limited to common signature hash. + const auto rows = chain::multisig::rows(sigs.size(), + keys.size()); + + for (size_t row{}; row < rows; ++row) + sink.write_bytes(digest); + }); + + BC_ASSERT(!sink || sink.get_write_position() == count() * minrow); + return sink; + } + + const system::chain::ecdsa_signatures& sigs; + }; +}; + +/// ecdsa_compressed is an array of ecdsa verification compressed public keys. +struct ecdsa_compressed + : public no_map +{ + using no_map::nomap; + + struct put_ref + : public schema::ecdsa_compressed + { + inline link count() const NOEXCEPT + { + return 1; + } + + inline bool to_data(flipper& sink) const NOEXCEPT + { + sink.write_bytes(key); + BC_ASSERT(!sink || sink.get_write_position() == minrow); + return sink; + } + const system::ec_compressed& key; - const system::ec_signature& signature; }; struct put_refs - : public schema::ecdsa_row + : public schema::ecdsa_compressed { inline link count() const NOEXCEPT { @@ -67,34 +140,26 @@ struct ecdsa_row inline bool to_data(flipper& sink) const NOEXCEPT { using namespace system::chain; - const auto m = sigs.size(); + const auto m = sigs; const auto n = keys.size(); BC_ASSERT(multisig::rows(m, n) == rows); - // Group capture is limited to common signature hash. const auto gap = (n - m); for (size_t sig{}; sig < m; ++sig) - { for (auto key = sig; key <= gap + sig; ++key) - { - sink.write_bytes(digest); sink.write_bytes(keys[key]); - sink.write_bytes(sigs[sig]); - } - } BC_ASSERT(!sink || sink.get_write_position() == count() * minrow); return sink; } const size_t rows{}; - const hash_digest& digest; const std::span keys; - const std::span sigs; + const size_t sigs{}; }; struct put_signatures - : public schema::ecdsa_row + : public schema::ecdsa_compressed { inline link count() const NOEXCEPT { @@ -104,23 +169,100 @@ struct ecdsa_row inline bool to_data(flipper& sink) const NOEXCEPT { using namespace system; - sigs.for_each([&](const hash_digest& digest, + sigs.for_each([&](const hash_digest&, std::span keys, std::span sigs) NOEXCEPT { - // Group capture is limited to common signature hash. const auto m = sigs.size(); const auto gap = keys.size() - m; for (size_t sig{}; sig < m; ++sig) - { for (auto key = sig; key <= gap + sig; ++key) - { - sink.write_bytes(digest); sink.write_bytes(keys[key]); + }); + + BC_ASSERT(!sink || sink.get_write_position() == count() * minrow); + return sink; + } + + const system::chain::ecdsa_signatures& sigs; + }; +}; + +/// ecdsa_signature is an array of ecdsa verification signatures. +struct ecdsa_signature + : public no_map +{ + using no_map::nomap; + + struct put_ref + : public schema::ecdsa_signature + { + inline link count() const NOEXCEPT + { + return 1; + } + + inline bool to_data(flipper& sink) const NOEXCEPT + { + sink.write_bytes(signature); + BC_ASSERT(!sink || sink.get_write_position() == minrow); + return sink; + } + + const system::ec_signature& signature; + }; + + struct put_refs + : public schema::ecdsa_signature + { + inline link count() const NOEXCEPT + { + return system::possible_narrow_cast(rows); + } + + inline bool to_data(flipper& sink) const NOEXCEPT + { + using namespace system::chain; + const auto m = sigs.size(); + const auto n = keys; + BC_ASSERT(multisig::rows(m, n) == rows); + + const auto gap = (n - m); + for (size_t sig{}; sig < m; ++sig) + for (auto key = sig; key <= gap + sig; ++key) + sink.write_bytes(sigs[sig]); + + BC_ASSERT(!sink || sink.get_write_position() == count() * minrow); + return sink; + } + + const size_t rows{}; + const size_t keys{}; + const std::span sigs; + }; + + struct put_signatures + : public schema::ecdsa_signature + { + inline link count() const NOEXCEPT + { + return system::possible_narrow_cast(sigs.rows()); + } + + inline bool to_data(flipper& sink) const NOEXCEPT + { + using namespace system; + sigs.for_each([&](const hash_digest&, + std::span keys, + std::span sigs) NOEXCEPT + { + const auto m = sigs.size(); + const auto gap = keys.size() - m; + + for (size_t sig{}; sig < m; ++sig) + for (auto key = sig; key <= gap + sig; ++key) sink.write_bytes(sigs[sig]); - } - } }); BC_ASSERT(!sink || sink.get_write_position() == count() * minrow); @@ -271,7 +413,9 @@ using ecdsa_files = mmaps < Storage, ecdsa_correlate, - ecdsa_row + ecdsa_digest, + ecdsa_compressed, + ecdsa_signature >; template