From e154c8222d656e88bf6a8bfd31c2dc090c0141d8 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 25 May 2026 20:21:03 -0400 Subject: [PATCH 1/4] Fixes and updates for chain block_view writers. --- .../impl/query/archive/wire_writer.ipp | 14 +++- .../database/tables/archives/input.hpp | 74 ++++++++++--------- .../bitcoin/database/tables/archives/ins.hpp | 49 +++++++++--- .../database/tables/archives/output.hpp | 26 +++---- .../bitcoin/database/tables/archives/outs.hpp | 14 ++-- 5 files changed, 106 insertions(+), 71 deletions(-) diff --git a/include/bitcoin/database/impl/query/archive/wire_writer.ipp b/include/bitcoin/database/impl/query/archive/wire_writer.ipp index b73a146d..8f024a20 100644 --- a/include/bitcoin/database/impl/query/archive/wire_writer.ipp +++ b/include/bitcoin/database/impl/query/archive/wire_writer.ipp @@ -95,6 +95,7 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, auto source = tx.get_inputs_stream(); read::bytes::fast ins{ source }; + for (size_t in{}; in < inputs; ++in) { // Should always be a null point - but could be invalid. @@ -102,8 +103,9 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, table::point::record{})) return error::tx_null_point_put; - // Skip script. + // Skip script and sequence. ins.skip_bytes(ins.read_size()); + ins.skip_bytes(sizeof(uint32_t)); } } else @@ -120,6 +122,7 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, auto ptr = store_.point.get_memory(); auto source = tx.get_inputs_stream(); read::bytes::fast ins{ source }; + for (size_t in{}; in < inputs; ++in) { bool duplicate{}; @@ -133,8 +136,9 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, twins.push_back(chain::point(ins)); } - // Skip script. + // Skip script and sequence. ins.skip_bytes(ins.read_size()); + ins.skip_bytes(sizeof(uint32_t)); } ptr.reset(); @@ -151,14 +155,16 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, auto ptr = store_.point.get_memory(); auto source = tx.get_inputs_stream(); read::bytes::fast ins{ source }; + for (size_t in{}; in < inputs; ++in) { if (!store_.point.put(ptr, ins_fk++, chain::point(ins), table::point::record{})) return error::tx_point_put; - // Skip script. + // Skip script and sequence. ins.skip_bytes(ins.read_size()); + ins.skip_bytes(sizeof(uint32_t)); } ptr.reset(); @@ -176,10 +182,12 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, const auto ptr = store_.address.get_memory(); auto source = tx.get_outputs_stream(); read::bytes::fast ous{ source }; + for (size_t out{}; out < outputs; ++out) { const auto start = ous.get_read_position(); const auto value = ous.read_variable(); + if (!store_.address.put(ptr, ad_fk++, sha256_hash(ous.read_bytes(ous.read_size())), table::address::record{ {}, out_fk })) diff --git a/include/bitcoin/database/tables/archives/input.hpp b/include/bitcoin/database/tables/archives/input.hpp index 8684252d..ef58c146 100644 --- a/include/bitcoin/database/tables/archives/input.hpp +++ b/include/bitcoin/database/tables/archives/input.hpp @@ -182,43 +182,32 @@ struct input inline link count() const NOEXCEPT { using namespace system; - constexpr auto sequence_size = sizeof(uint32_t); - constexpr auto sequence_point_size = sequence_size + - chain::point::serialized_size(); - size_t total{}; - auto istream = tx_.get_inputs_stream(); read::bytes::fast isource{ istream }; + + // inputs for (size_t in{}; in < tx_.inputs(); ++in) { - const auto start = isource.get_read_position(); isource.skip_bytes(chain::point::serialized_size()); - isource.skip_bytes(isource.read_size() + sequence_size); - const auto input_size = isource.get_read_position() - start; - total += (input_size - sequence_point_size); + const auto bytes = isource.read_size(); + isource.skip_bytes(bytes + sizeof(uint32_t)); + total += variable_size(bytes) + bytes; } - if (!tx_.is_segregated()) - { - // Optimize out stream and loop for non-segregated. - total += (tx_.inputs() * variable_size(zero)); - } - else + // witnesses + if (tx_.is_segregated()) { auto wstream = tx_.get_witnesses_stream(); read::bytes::fast wsource{ wstream }; + for (size_t in{}; in < tx_.inputs(); ++in) - { - const auto stack = wsource.read_size(); - total += variable_size(stack); - for (size_t element{}; element < stack; ++element) - { - const auto element_size = wsource.read_size(); - wsource.skip_bytes(element_size); - total += variable_size(element_size) + element_size; - } - } + total += tx_.read_witness_size(wsource); + } + else + { + // Optimize out stream and loop for non-segregated. + total += (tx_.inputs() * variable_size(zero)); } return possible_narrow_cast(total); @@ -228,19 +217,38 @@ struct input { using namespace system; auto istream = tx_.get_inputs_stream(); - auto wstream = tx_.get_witnesses_stream(); read::bytes::fast isource{ istream }; - read::bytes::fast wsource{ wstream }; - for (size_t in{}; in < tx_.inputs(); ++in) + + if (tx_.is_segregated()) + { + auto wstream = tx_.get_witnesses_stream(); + read::bytes::fast wsource{ wstream }; + + for (size_t in{}; in < tx_.inputs(); ++in) + { + // input script + witness stack + tx_.write_input_script(sink, isource); + isource.skip_bytes(sizeof(uint32_t)); + tx_.write_witness(sink, wsource); + } + + if (!wsource) + isource.invalidate(); + } + else { - // input (without point) + witness stack (or zero). - tx_.write_input_script(sink, isource); - tx_.write_witness(sink, wsource); + for (size_t in{}; in < tx_.inputs(); ++in) + { + // input script + empty witness stack + tx_.write_input_script(sink, isource); + isource.skip_bytes(sizeof(uint32_t)); + sink.write_variable(zero); + } } - BC_ASSERT(isource && wsource); + BC_ASSERT(isource); BC_ASSERT(!sink || sink.get_write_position() == count()); - return sink && isource && wsource; + return sink && isource; } const system::chain::transaction_view& tx_; diff --git a/include/bitcoin/database/tables/archives/ins.hpp b/include/bitcoin/database/tables/archives/ins.hpp index 60dfa9d3..d00c1784 100644 --- a/include/bitcoin/database/tables/archives/ins.hpp +++ b/include/bitcoin/database/tables/archives/ins.hpp @@ -144,22 +144,47 @@ struct ins inline bool to_data(flipper& sink) const NOEXCEPT { using namespace system; - constexpr auto sequence_point_size = sizeof(uint32_t) + - chain::point::serialized_size(); - auto in_fk = input_fk; auto stream = tx_.get_inputs_stream(); read::bytes::fast source{ stream }; - for (size_t in{}; in < tx_.inputs(); ++in) + + if (tx_.is_segregated()) { - const auto start = source.get_read_position(); - source.skip_bytes(chain::point::serialized_size()); - source.skip_bytes(source.read_size()); - sink.write_little_endian(source.read_little_endian()); - sink.write_little_endian(in_fk); - sink.write_little_endian(parent_fk); - const auto input_size = source.get_read_position() - start; - in_fk += (input_size - sequence_point_size); + auto wstream = tx_.get_witnesses_stream(); + read::bytes::fast wsource{ wstream }; + + for (size_t in{}; in < tx_.inputs(); ++in) + { + source.skip_bytes(chain::point::serialized_size()); + const auto bytes = source.read_size(); + source.skip_bytes(bytes); + const auto sequence = source.read_little_endian(); + + sink.write_little_endian(sequence); + sink.write_little_endian(in_fk); + sink.write_little_endian(parent_fk); + + // Advance by size stored in input table for this input. + in_fk += variable_size(bytes) + bytes + + tx_.read_witness_size(wsource); + } + } + else + { + for (size_t in{}; in < tx_.inputs(); ++in) + { + source.skip_bytes(chain::point::serialized_size()); + const auto bytes = source.read_size(); + source.skip_bytes(bytes); + const auto sequence = source.read_little_endian(); + + sink.write_little_endian(sequence); + sink.write_little_endian(in_fk); + sink.write_little_endian(parent_fk); + + // Advance by size stored in input table for this input. + in_fk += variable_size(bytes) + bytes + variable_size(zero); + } } BC_ASSERT(source); diff --git a/include/bitcoin/database/tables/archives/output.hpp b/include/bitcoin/database/tables/archives/output.hpp index c29da69f..be25c727 100644 --- a/include/bitcoin/database/tables/archives/output.hpp +++ b/include/bitcoin/database/tables/archives/output.hpp @@ -236,27 +236,21 @@ struct output inline link count() const NOEXCEPT { using namespace system; - static_assert(tx::size <= sizeof(uint64_t)); - constexpr auto value_size = sizeof(uint64_t); - constexpr auto value_parent = value_size - tx::size; - - size_t outputs{}; - const auto other = tx_.outputs() * value_parent; - auto stream = tx_.get_outputs_stream(); read::bytes::fast source{ stream }; + size_t total{}; + for (size_t out{}; out < tx_.outputs(); ++out) { - const auto start = source.get_read_position(); const auto value = source.read_8_bytes_little_endian(); - source.skip_bytes(source.read_size()); - const auto output_size = source.get_read_position() - start; - outputs += variable_size(value) + output_size; + const auto bytes = source.read_size(); + source.skip_bytes(bytes); + total += tx::size + variable_size(value) + + variable_size(bytes) + bytes; } // Converts value from fixed size wire encoding to variable. - // (variable_size(value) + (value + script)) - (value - parent) - return possible_narrow_cast(outputs - other); + return possible_narrow_cast(total); } inline bool to_data(flipper& sink) const NOEXCEPT @@ -264,14 +258,18 @@ struct output using namespace system; auto stream = tx_.get_outputs_stream(); read::bytes::fast source{ stream }; + for (size_t out{}; out < tx_.outputs(); ++out) { // tx view output writer not used due to variable value. sink.write_little_endian(parent_fk); sink.write_variable(source.read_8_bytes_little_endian()); - sink.write_bytes(source.read_bytes(source.read_size())); + const auto bytes = source.read_size(); + sink.write_variable(bytes); + sink.write_bytes(source.read_bytes(bytes)); } + BC_ASSERT(source); BC_ASSERT(!sink || sink.get_write_position() == count()); return sink; } diff --git a/include/bitcoin/database/tables/archives/outs.hpp b/include/bitcoin/database/tables/archives/outs.hpp index 1f8ab024..380e876c 100644 --- a/include/bitcoin/database/tables/archives/outs.hpp +++ b/include/bitcoin/database/tables/archives/outs.hpp @@ -141,21 +141,17 @@ struct outs inline bool to_data(flipper& sink) const NOEXCEPT { using namespace system; - static_assert(tx::size <= sizeof(uint64_t)); - constexpr auto value_parent = sizeof(uint64_t) - tx::size; - auto out_fk = output_fk; auto stream = tx_.get_outputs_stream(); read::bytes::fast source{ stream }; for (size_t out{}; out < tx_.outputs(); ++out) { sink.write_little_endian(out_fk); - - const auto start = source.get_read_position(); - const auto value = source.read_variable(); - source.skip_bytes(source.read_size()); - const auto output_size = source.get_read_position() - start; - out_fk += (variable_size(value) + output_size - value_parent); + const auto value = source.read_8_bytes_little_endian(); + const auto bytes = source.read_size(); + source.skip_bytes(bytes); + out_fk += tx::size + variable_size(value) + + variable_size(bytes) + bytes; } BC_ASSERT(source); From 817f357e8078785ebe3441429de8a7b8c9f2ff2f Mon Sep 17 00:00:00 2001 From: evoskuil Date: Mon, 25 May 2026 22:16:36 -0400 Subject: [PATCH 2/4] Integrate chain tx view in/out table size helpers. --- .../database/tables/archives/input.hpp | 30 +------------------ .../database/tables/archives/output.hpp | 18 ++--------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/include/bitcoin/database/tables/archives/input.hpp b/include/bitcoin/database/tables/archives/input.hpp index ef58c146..180fd347 100644 --- a/include/bitcoin/database/tables/archives/input.hpp +++ b/include/bitcoin/database/tables/archives/input.hpp @@ -182,35 +182,7 @@ struct input inline link count() const NOEXCEPT { using namespace system; - size_t total{}; - auto istream = tx_.get_inputs_stream(); - read::bytes::fast isource{ istream }; - - // inputs - for (size_t in{}; in < tx_.inputs(); ++in) - { - isource.skip_bytes(chain::point::serialized_size()); - const auto bytes = isource.read_size(); - isource.skip_bytes(bytes + sizeof(uint32_t)); - total += variable_size(bytes) + bytes; - } - - // witnesses - if (tx_.is_segregated()) - { - auto wstream = tx_.get_witnesses_stream(); - read::bytes::fast wsource{ wstream }; - - for (size_t in{}; in < tx_.inputs(); ++in) - total += tx_.read_witness_size(wsource); - } - else - { - // Optimize out stream and loop for non-segregated. - total += (tx_.inputs() * variable_size(zero)); - } - - return possible_narrow_cast(total); + return possible_narrow_cast(tx_.input_table_size()); } inline bool to_data(flipper& sink) const NOEXCEPT diff --git a/include/bitcoin/database/tables/archives/output.hpp b/include/bitcoin/database/tables/archives/output.hpp index be25c727..dfbf05ac 100644 --- a/include/bitcoin/database/tables/archives/output.hpp +++ b/include/bitcoin/database/tables/archives/output.hpp @@ -236,21 +236,9 @@ struct output inline link count() const NOEXCEPT { using namespace system; - auto stream = tx_.get_outputs_stream(); - read::bytes::fast source{ stream }; - size_t total{}; - - for (size_t out{}; out < tx_.outputs(); ++out) - { - const auto value = source.read_8_bytes_little_endian(); - const auto bytes = source.read_size(); - source.skip_bytes(bytes); - total += tx::size + variable_size(value) + - variable_size(bytes) + bytes; - } - - // Converts value from fixed size wire encoding to variable. - return possible_narrow_cast(total); + const auto parents_size = tx_.outputs() * tx::size; + return possible_narrow_cast(parents_size + + tx_.output_table_size()); } inline bool to_data(flipper& sink) const NOEXCEPT From 19352a3151e443758f8019a05d0869b731b15260 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 26 May 2026 11:02:15 -0400 Subject: [PATCH 3/4] Comment. --- include/bitcoin/database/tables/archives/transaction.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/bitcoin/database/tables/archives/transaction.hpp b/include/bitcoin/database/tables/archives/transaction.hpp index e1da4e4d..be3d5e7f 100644 --- a/include/bitcoin/database/tables/archives/transaction.hpp +++ b/include/bitcoin/database/tables/archives/transaction.hpp @@ -193,7 +193,6 @@ struct transaction { using namespace system; - // is_coinbase() is computed over the point. const auto coinbase = tx.is_coinbase(); const auto light_ = tx.serialized_size(false); const auto heavy_ = tx.serialized_size(true); From ff43ba02d695fa8bf7d4df42573d5448b7c6fedd Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 26 May 2026 11:02:47 -0400 Subject: [PATCH 4/4] Refactor set_code(transaction_view). --- .../impl/query/archive/wire_writer.ipp | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/include/bitcoin/database/impl/query/archive/wire_writer.ipp b/include/bitcoin/database/impl/query/archive/wire_writer.ipp index 8f024a20..9a440a48 100644 --- a/include/bitcoin/database/impl/query/archive/wire_writer.ipp +++ b/include/bitcoin/database/impl/query/archive/wire_writer.ipp @@ -86,6 +86,9 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, return error::tx_tx_set; } + auto ins = tx.get_inputs_stream(); + read::bytes::fast isource{ ins }; + // Commit points (hashmap). if (coinbase) { @@ -93,19 +96,16 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, if (!store_.point.expand(ins_fk + inputs)) return error::tx_point_allocate; - auto source = tx.get_inputs_stream(); - read::bytes::fast ins{ source }; - for (size_t in{}; in < inputs; ++in) { // Should always be a null point - but could be invalid. - if (!store_.point.put(ins_fk++, chain::point(ins), + if (!store_.point.put(ins_fk++, chain::point(isource), table::point::record{})) return error::tx_null_point_put; // Skip script and sequence. - ins.skip_bytes(ins.read_size()); - ins.skip_bytes(sizeof(uint32_t)); + isource.skip_bytes(isource.read_size()); + isource.skip_bytes(sizeof(uint32_t)); } } else @@ -120,25 +120,23 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, // Collect duplicates to store in duplicate table. std::vector twins{}; auto ptr = store_.point.get_memory(); - auto source = tx.get_inputs_stream(); - read::bytes::fast ins{ source }; for (size_t in{}; in < inputs; ++in) { bool duplicate{}; if (!store_.point.put(duplicate, ptr, ins_fk++, - chain::point(ins), table::point::record{})) + chain::point(isource), table::point::record{})) return error::tx_point_put; if (duplicate) { - ins.rewind_bytes(chain::point::serialized_size()); - twins.push_back(chain::point(ins)); + isource.rewind_bytes(chain::point::serialized_size()); + twins.push_back(chain::point(isource)); } // Skip script and sequence. - ins.skip_bytes(ins.read_size()); - ins.skip_bytes(sizeof(uint32_t)); + isource.skip_bytes(isource.read_size()); + isource.skip_bytes(sizeof(uint32_t)); } ptr.reset(); @@ -153,24 +151,26 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, else { auto ptr = store_.point.get_memory(); - auto source = tx.get_inputs_stream(); - read::bytes::fast ins{ source }; for (size_t in{}; in < inputs; ++in) { - if (!store_.point.put(ptr, ins_fk++, chain::point(ins), + if (!store_.point.put(ptr, ins_fk++, chain::point(isource), table::point::record{})) return error::tx_point_put; // Skip script and sequence. - ins.skip_bytes(ins.read_size()); - ins.skip_bytes(sizeof(uint32_t)); + isource.skip_bytes(isource.read_size()); + isource.skip_bytes(sizeof(uint32_t)); } ptr.reset(); } } + BC_ASSERT(isource); + if (!isource) + return error::tx_null_point_put; + // Commit address index records (hashmap). if (address_enabled()) { @@ -178,27 +178,28 @@ code CLASS::set_code(const tx_link& tx_fk, const transaction_view& tx, if (ad_fk.is_terminal()) return error::tx_address_allocate; - constexpr auto value_parent = sizeof(uint64_t) - tx_link::size; const auto ptr = store_.address.get_memory(); - auto source = tx.get_outputs_stream(); - read::bytes::fast ous{ source }; + auto outs = tx.get_outputs_stream(); + read::bytes::fast osource{ outs }; for (size_t out{}; out < outputs; ++out) { - const auto start = ous.get_read_position(); - const auto value = ous.read_variable(); + const auto value = osource.read_8_bytes_little_endian(); + const auto bytes = osource.read_size(); if (!store_.address.put(ptr, ad_fk++, - sha256_hash(ous.read_bytes(ous.read_size())), + sha256_hash(osource.read_bytes(bytes)), table::address::record{ {}, out_fk })) return error::tx_address_put; - // See outs::put_ref. - // Calculate next corresponding output fk from serialized size. - // (variable_size(value) + (value + script)) - (value - parent) - const auto output_size = ous.get_read_position() - start; - out_fk.value += (variable_size(value) + output_size - value_parent); + out_fk.value += possible_narrow_cast( + tx_link::size + variable_size(value) + variable_size(bytes) + + bytes); } + + BC_ASSERT(osource); + if (!osource) + return error::tx_address_put; } // Commit tx to search (hashmap).