From fa10c129eca1636654b1d00199c028c26f10d3f2 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 15 Aug 2026 00:50:42 -0400 Subject: [PATCH] Add input script/witness prune on set block. --- .../impl/query/archive/chain_writer.ipp | 24 ++++++------ .../impl/query/archive/wire_writer.ipp | 25 ++++++------ include/bitcoin/database/query.hpp | 26 +++++++------ .../database/tables/archives/input.hpp | 39 ++++++++++++++++++- 4 files changed, 78 insertions(+), 36 deletions(-) diff --git a/include/bitcoin/database/impl/query/archive/chain_writer.ipp b/include/bitcoin/database/impl/query/archive/chain_writer.ipp index c38c0e235..8c1e59398 100644 --- a/include/bitcoin/database/impl/query/archive/chain_writer.ipp +++ b/include/bitcoin/database/impl/query/archive/chain_writer.ipp @@ -64,10 +64,11 @@ bool CLASS::set(const transaction& tx) NOEXCEPT } TEMPLATE -bool CLASS::set(const block& block, bool strong, bool bypass) NOEXCEPT +bool CLASS::set(const block& block, bool strong, bool bypass, + bool prune) NOEXCEPT { // This sets only the txs of a block with header/context already archived. - return !set_code(block, strong, bypass); + return !set_code(block, strong, bypass, prune); } // set transaction @@ -83,12 +84,12 @@ code CLASS::set_code(const transaction& tx) NOEXCEPT if (tx_fk.is_terminal()) return error::tx_tx_allocate; - return set_code(tx_fk, tx, false); + return set_code(tx_fk, tx, false, false); } TEMPLATE code CLASS::set_code(const tx_link& tx_fk, const transaction& tx, - bool bypass) NOEXCEPT + bool bypass, bool prune) NOEXCEPT { // This is the only multitable write query (except initialize/genesis). @@ -109,7 +110,7 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction& tx, // Allocate contiguously and store inputs. input_link in_fk{}; if (!store_.input.put_link(in_fk, - table::input::put_ref{ {}, tx })) + table::input::put_ref{ {}, tx, prune })) return error::tx_input_put; // Allocate contiguously and store outputs. @@ -347,15 +348,16 @@ code CLASS::set_code(header_link& out_fk, const block& block, // releases all memory for parts of itself, due to the custom allocator. TEMPLATE -code CLASS::set_code(const block& block, bool strong, bool bypass) NOEXCEPT +code CLASS::set_code(const block& block, bool strong, bool bypass, + bool prune) NOEXCEPT { header_link unused{}; - return set_code(unused, block, strong, bypass); + return set_code(unused, block, strong, bypass, prune); } TEMPLATE code CLASS::set_code(header_link& out_fk, const block& block, bool strong, - bool bypass) NOEXCEPT + bool bypass, bool prune) NOEXCEPT { out_fk = to_header(block.get_hash()); if (out_fk.is_terminal()) @@ -365,12 +367,12 @@ code CLASS::set_code(header_link& out_fk, const block& block, bool strong, if (!get_height(height, out_fk)) return error::txs_height; - return set_code(block, out_fk, strong, bypass, height); + return set_code(block, out_fk, strong, bypass, height, prune); } TEMPLATE code CLASS::set_code(const block& block, const header_link& key, - bool strong, bool bypass, size_t height) NOEXCEPT + bool strong, bool bypass, size_t height, bool prune) NOEXCEPT { using namespace system; if (key.is_terminal()) @@ -388,7 +390,7 @@ code CLASS::set_code(const block& block, const header_link& key, code ec{}; auto fk = tx_fks; for (const auto& tx: *block.transactions_ptr()) - if ((ec = set_code(fk++, *tx, bypass))) + if ((ec = set_code(fk++, *tx, bypass, prune))) return ec; // Optional hash, only has value on height intervals. diff --git a/include/bitcoin/database/impl/query/archive/wire_writer.ipp b/include/bitcoin/database/impl/query/archive/wire_writer.ipp index 60680468b..f570f4fa1 100644 --- a/include/bitcoin/database/impl/query/archive/wire_writer.ipp +++ b/include/bitcoin/database/impl/query/archive/wire_writer.ipp @@ -32,7 +32,8 @@ namespace database { TEMPLATE code CLASS::set_code(std::vector& twins, const accessors& ptrs, - const allocation& fks, const transaction_view& tx, bool bypass) NOEXCEPT + const allocation& fks, const transaction_view& tx, bool bypass, + bool prune) NOEXCEPT { using namespace system; using ix = linkage; @@ -48,7 +49,7 @@ code CLASS::set_code(std::vector& twins, const accessors& ptrs, // Contiguously store inputs (preallocated). if (!store_.input.put(ptrs.input, fks.in_fk, - table::input::put_view{ {}, tx })) + table::input::put_view{ {}, tx, prune })) return error::tx_input_put; // Contiguously store outputs (preallocated). @@ -185,16 +186,16 @@ code CLASS::set_code(std::vector& twins, const accessors& ptrs, // releases all memory for parts of itself, due to the custom allocator. TEMPLATE -code CLASS::set_code(const block_view& block, bool strong, - bool bypass) NOEXCEPT +code CLASS::set_code(const block_view& block, bool strong, bool bypass, + bool prune) NOEXCEPT { header_link unused{}; - return set_code(unused, block, strong, bypass); + return set_code(unused, block, strong, bypass, prune); } TEMPLATE -code CLASS::set_code(header_link& out_fk, const block_view& block, bool strong, - bool bypass) NOEXCEPT +code CLASS::set_code(header_link& out_fk, const block_view& block, + bool strong, bool bypass, bool prune) NOEXCEPT { out_fk = to_header(block.hash()); if (out_fk.is_terminal()) @@ -204,12 +205,12 @@ code CLASS::set_code(header_link& out_fk, const block_view& block, bool strong, if (!get_height(height, out_fk)) return error::txs_height; - return set_code(block, out_fk, strong, bypass, height); + return set_code(block, out_fk, strong, bypass, height, prune); } TEMPLATE code CLASS::set_code(const block_view& block, const header_link& key, - bool strong, bool bypass, size_t height) NOEXCEPT + bool strong, bool bypass, size_t height, bool prune) NOEXCEPT { using namespace system; using in_t = input_link::integer; @@ -234,7 +235,7 @@ code CLASS::set_code(const block_view& block, const header_link& key, { points += tx.inputs(); outputs += tx.outputs(); - input_bytes += tx.input_table_size(); + input_bytes += tx.input_table_size(prune); output_bytes += tx.outputs() * tx_link::size + tx.output_table_size(); } @@ -314,7 +315,7 @@ code CLASS::set_code(const block_view& block, const header_link& key, std::vector twins{}; for (const auto& tx: block.views()) { - if ((ec = set_code(twins, ptrs, fks, tx, bypass))) + if ((ec = set_code(twins, ptrs, fks, tx, bypass, prune))) return ec; // Output rows are parent fk prefixed (see table::output::put_view). @@ -324,7 +325,7 @@ code CLASS::set_code(const block_view& block, const header_link& key, fks.tx_fk++; fks.ins_fk += tx.inputs(); fks.outs_fk += tx.outputs(); - fks.in_fk += tx.input_table_size(); + fks.in_fk += tx.input_table_size(prune); fks.out_fk += out_bytes; // Unallocated (disabled) address link is terminal (do not increment). diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 6e3848cbb..47c220f6b 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -501,7 +501,8 @@ class query bool set(const block& block, const context& ctx, bool milestone, bool strong) NOEXCEPT; bool set(const transaction& tx) NOEXCEPT; - bool set(const block& block, bool strong, bool bypass) NOEXCEPT; + bool set(const block& block, bool strong, bool bypass, + bool prune=false) NOEXCEPT; /// Set transaction. code set_code(const transaction& tx) NOEXCEPT; @@ -526,19 +527,21 @@ class query code set_code(header_link& out_fk, const block& block, const chain_context& ctx, bool milestone, bool strong) NOEXCEPT; - /// Set block.txs (headers-first). - code set_code(const block& block, bool strong, bool bypass) NOEXCEPT; + /// Set block.txs (headers-first). Prune strips input scripts/witnesses. + code set_code(const block& block, bool strong, bool bypass, + bool prune=false) NOEXCEPT; code set_code(header_link& out_fk, const block& block, bool strong, - bool bypass) NOEXCEPT; + bool bypass, bool prune=false) NOEXCEPT; code set_code(const block& block, const header_link& key, bool strong, - bool bypass, size_t height) NOEXCEPT; + bool bypass, size_t height, bool prune=false) NOEXCEPT; - /// Set block_view (wire). - code set_code(const block_view& block, bool strong, bool bypass) NOEXCEPT; + /// Set block_view (wire). Prune strips input scripts/witnesses. + code set_code(const block_view& block, bool strong, bool bypass, + bool prune=false) NOEXCEPT; code set_code(header_link& out_fk, const block_view& block, bool strong, - bool bypass) NOEXCEPT; + bool bypass, bool prune=false) NOEXCEPT; code set_code(const block_view& block, const header_link& key, bool strong, - bool bypass, size_t height) NOEXCEPT; + bool bypass, size_t height, bool prune=false) NOEXCEPT; /// Context. /// ----------------------------------------------------------------------- @@ -907,7 +910,7 @@ class query /// tx_fk must be allocated. /// ----------------------------------------------------------------------- code set_code(const tx_link& tx_fk, const transaction& tx, - bool bypass) NOEXCEPT; + bool bypass, bool prune) NOEXCEPT; /// Block write batching (all rows preallocated, all accessors held). /// ----------------------------------------------------------------------- @@ -935,7 +938,8 @@ class query }; code set_code(std::vector& twins, const accessors& ptrs, - const allocation& fks,const transaction_view& tx, bool bypass) NOEXCEPT; + const allocation& fks,const transaction_view& tx, bool bypass, + bool prune) NOEXCEPT; /// History. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/tables/archives/input.hpp b/include/bitcoin/database/tables/archives/input.hpp index 1bc07c4ba..214b856db 100644 --- a/include/bitcoin/database/tables/archives/input.hpp +++ b/include/bitcoin/database/tables/archives/input.hpp @@ -149,6 +149,12 @@ struct input chain::point::serialized_size(); const auto& ins = *tx_.inputs_ptr(); + if (prune_) + { + // pruned: (zero ins, zero wits) * inputs. + return possible_narrow_cast(two * ins.size()); + } + const auto other = ins.size() * sequence_ins_size; const auto inputs = std::accumulate(ins.cbegin(), ins.cend(), zero, [](size_t total, const auto& in) NOEXCEPT @@ -163,7 +169,21 @@ struct input inline bool to_data(flipper& sink) const NOEXCEPT { + using namespace system; const auto& ins = *tx_.inputs_ptr(); + if (prune_) + { + std::ranges::for_each(ins, [&](const auto&) NOEXCEPT + { + // empty script + empty witness stack + sink.write_variable(zero); + sink.write_variable(zero); + }); + + BC_ASSERT(!sink || sink.get_write_position() == count()); + return sink; + } + std::ranges::for_each(ins, [&](const auto& in) NOEXCEPT { in->script().to_data(sink, true); @@ -175,6 +195,7 @@ struct input } const system::chain::transaction& tx_{}; + bool prune_{}; }; struct put_view @@ -182,12 +203,25 @@ struct input { inline link count() const NOEXCEPT { - using namespace system; - return possible_narrow_cast(tx_.input_table_size()); + return system::possible_narrow_cast( + tx_.input_table_size(prune_)); } inline bool to_data(flipper& sink) const NOEXCEPT { + if (prune_) + { + for (size_t in{}; in < tx_.inputs(); ++in) + { + // empty script + empty witness stack + sink.write_variable(zero); + sink.write_variable(zero); + } + + BC_ASSERT(!sink || sink.get_write_position() == count()); + return sink; + } + using namespace system; auto istream = tx_.get_inputs_stream(); read::bytes::fast isource{ istream }; @@ -225,6 +259,7 @@ struct input } const system::chain::transaction_view& tx_; + bool prune_{}; }; struct wire_script