From 0155ccfba5c77ede6da16601cf881e08daf5c420 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 31 May 2024 18:04:10 -0400 Subject: [PATCH] Optimize size computations, use ceilinged_add, style. --- include/bitcoin/system/chain/block.hpp | 6 +- include/bitcoin/system/chain/input.hpp | 15 ++- include/bitcoin/system/chain/operation.hpp | 2 +- include/bitcoin/system/chain/output.hpp | 4 +- include/bitcoin/system/chain/script.hpp | 2 +- include/bitcoin/system/chain/transaction.hpp | 6 +- include/bitcoin/system/intrinsics/haves.hpp | 8 +- src/chain/block.cpp | 65 +++++++---- src/chain/input.cpp | 55 ++++++++-- src/chain/output.cpp | 15 ++- src/chain/script.cpp | 9 +- src/chain/transaction.cpp | 110 ++++++++++++++----- src/chain/witness.cpp | 21 +--- 13 files changed, 220 insertions(+), 98 deletions(-) diff --git a/include/bitcoin/system/chain/block.hpp b/include/bitcoin/system/chain/block.hpp index 629a78cc8b..36ee4ba1fa 100644 --- a/include/bitcoin/system/chain/block.hpp +++ b/include/bitcoin/system/chain/block.hpp @@ -43,7 +43,6 @@ class BC_API block public: DEFAULT_COPY_MOVE_DESTRUCT(block); - typedef std_vector sizes; typedef std::shared_ptr cptr; static bool is_malleable64(const transaction_cptrs& txs) NOEXCEPT; @@ -167,6 +166,7 @@ class BC_API block bool is_unspent_coinbase_collision() const NOEXCEPT; private: + typedef struct { size_t nominal; size_t witnessed; } sizes; using point_cref = std::reference_wrapper; using point_hash = std::hash>; using hash_cref = std::reference_wrapper; @@ -180,6 +180,7 @@ class BC_API block std::unordered_set; static block from_data(reader& source, bool witness) NOEXCEPT; + static sizes serialized_size(const chain::transaction_cptrs& txs) NOEXCEPT; // context free hash_digest generate_merkle_root(bool witness) const NOEXCEPT; @@ -202,8 +203,7 @@ class BC_API block // Cache. bool valid_; - ////size_t nominal_size_; - ////size_t witness_size_; + sizes size_; }; typedef std::vector blocks; diff --git a/include/bitcoin/system/chain/input.hpp b/include/bitcoin/system/chain/input.hpp index 016763ac3a..0562447646 100644 --- a/include/bitcoin/system/chain/input.hpp +++ b/include/bitcoin/system/chain/input.hpp @@ -119,15 +119,22 @@ class BC_API input bool is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT; protected: - // So that witness may be set late in deserialization. - friend class transaction; - input(const chain::point::cptr& point, const chain::script::cptr& script, const chain::witness::cptr& witness, uint32_t sequence, bool valid) NOEXCEPT; private: + // So that witness may be set late in deserialization. + friend class transaction; + size_t nominal_size() const NOEXCEPT; + size_t witnessed_size() const NOEXCEPT; + void set_witness(reader& source) NOEXCEPT; + + typedef struct { size_t nominal; size_t witnessed; } sizes; + static input from_data(reader& source) NOEXCEPT; + static sizes serialized_size(const chain::script& script, + const chain::witness& witness) NOEXCEPT; bool extract_sigop_script(chain::script& out, const chain::script& prevout_script) const NOEXCEPT; @@ -141,7 +148,7 @@ class BC_API input // Cache. bool valid_; - ////size_t size_; + sizes size_; public: /// Public mutable metadata access, copied but not compared for equality. diff --git a/include/bitcoin/system/chain/operation.hpp b/include/bitcoin/system/chain/operation.hpp index 95c1116aa1..a76b2cb6f9 100644 --- a/include/bitcoin/system/chain/operation.hpp +++ b/include/bitcoin/system/chain/operation.hpp @@ -156,6 +156,7 @@ class BC_API operation private: // So script may call count_op. friend class script; + static bool count_op(reader& source) NOEXCEPT; static operation from_data(reader& source) NOEXCEPT; static operation from_push_data(const chunk_cptr& data, @@ -166,7 +167,6 @@ class BC_API operation static chunk_cptr no_data_ptr() NOEXCEPT; static chunk_cptr any_data_ptr() NOEXCEPT; - static bool count_op(reader& source) NOEXCEPT; static uint32_t read_data_size(opcode code, reader& source) NOEXCEPT; static inline opcode opcode_from_data(const data_chunk& push_data, diff --git a/include/bitcoin/system/chain/output.hpp b/include/bitcoin/system/chain/output.hpp index 3b00639cb8..a3cf0fbb9f 100644 --- a/include/bitcoin/system/chain/output.hpp +++ b/include/bitcoin/system/chain/output.hpp @@ -96,6 +96,8 @@ class BC_API output private: static output from_data(reader& source) NOEXCEPT; + static size_t serialized_size(const chain::script& script, + uint64_t value) NOEXCEPT; // Output should be stored as shared (adds 16 bytes). // copy: 3 * 64 + 1 = 25 bytes (vs. 16 when shared). @@ -104,7 +106,7 @@ class BC_API output // Cache. bool valid_; - ////size_t size_; + size_t size_; }; typedef std::vector outputs; diff --git a/include/bitcoin/system/chain/script.hpp b/include/bitcoin/system/chain/script.hpp index 1a857dea14..406e865190 100644 --- a/include/bitcoin/system/chain/script.hpp +++ b/include/bitcoin/system/chain/script.hpp @@ -180,7 +180,7 @@ class BC_API script static size_t serialized_size(const operations& ops) NOEXCEPT; static inline size_t op_size(size_t total, const operation& op) NOEXCEPT { - return total + op.serialized_size(); + return ceilinged_add(total, op.serialized_size()); }; // Script should be stored as shared. diff --git a/include/bitcoin/system/chain/transaction.hpp b/include/bitcoin/system/chain/transaction.hpp index f7c64f8aea..32c4fb41e2 100644 --- a/include/bitcoin/system/chain/transaction.hpp +++ b/include/bitcoin/system/chain/transaction.hpp @@ -224,6 +224,7 @@ class BC_API transaction bool is_confirmed_double_spend(size_t height) const NOEXCEPT; private: + typedef struct { size_t nominal; size_t witnessed; } sizes; typedef struct { hash_digest outputs; @@ -234,6 +235,8 @@ class BC_API transaction static transaction from_data(reader& source, bool witness) NOEXCEPT; static bool segregated(const chain::inputs& inputs) NOEXCEPT; static bool segregated(const chain::input_cptrs& inputs) NOEXCEPT; + static sizes serialized_size(const chain::input_cptrs& inputs, + const chain::output_cptrs& outputs, bool segregated) NOEXCEPT; // signature hash hash_digest output_hash(const input_iterator& input) const NOEXCEPT; @@ -268,8 +271,7 @@ class BC_API transaction // Cache. bool segregated_; bool valid_; - ////size_t nominal_size_; - ////size_t witness_size_; + sizes size_; // TODO: use std::optional to avoid these pointer allocations (0.16%). // Signature and identity hash caching (witness hash if witnessed). diff --git a/include/bitcoin/system/intrinsics/haves.hpp b/include/bitcoin/system/intrinsics/haves.hpp index 621eab431d..134a4b679e 100644 --- a/include/bitcoin/system/intrinsics/haves.hpp +++ b/include/bitcoin/system/intrinsics/haves.hpp @@ -178,7 +178,7 @@ inline bool have_avx512() NOEXCEPT inline bool have_avx2() NOEXCEPT { if constexpr (with_avx2) - return try_avx2(); + return true;//// try_avx2(); else return false; } @@ -186,15 +186,15 @@ inline bool have_avx2() NOEXCEPT inline bool have_sse41() NOEXCEPT { if constexpr (with_sse41) - return try_sse41(); + return true;//// try_sse41(); else return false; } inline bool have_neon() NOEXCEPT { - if constexpr (with_shani) - return try_shani(); + if constexpr (with_neon) + return try_neon(); else return false; } diff --git a/src/chain/block.cpp b/src/chain/block.cpp index 503f944f98..32c98432f2 100644 --- a/src/chain/block.cpp +++ b/src/chain/block.cpp @@ -111,7 +111,10 @@ block::block(reader& source, bool witness) NOEXCEPT // protected block::block(const chain::header::cptr& header, const chain::transactions_cptr& txs, bool valid) NOEXCEPT - : header_(header), txs_(txs), valid_(valid) + : header_(header), + txs_(txs), + valid_(valid), + size_(serialized_size(*txs)) { } @@ -213,7 +216,7 @@ const chain::header::cptr block::header_ptr() const NOEXCEPT const inputs_cptr block::inputs_ptr() const NOEXCEPT { const auto inputs = std::make_shared(); - const auto append_ins = [&inputs](const transaction::cptr& tx) NOEXCEPT + const auto append_ins = [&inputs](const auto& tx) NOEXCEPT { const auto& tx_ins = *tx->inputs_ptr(); inputs->insert(inputs->end(), tx_ins.begin(), tx_ins.end()); @@ -240,7 +243,7 @@ hashes block::transaction_hashes(bool witness) const NOEXCEPT // Vector capacity is never reduced when resizing to smaller size. out.resize(count); - const auto hash = [witness](const transaction::cptr& tx) NOEXCEPT + const auto hash = [witness](const auto& tx) NOEXCEPT { return tx->hash(witness); }; @@ -255,18 +258,35 @@ hash_digest block::hash() const NOEXCEPT return header_->hash(); } -// TODO: this is expensive. -size_t block::serialized_size(bool witness) const NOEXCEPT +// static/private +block::sizes block::serialized_size( + const chain::transaction_cptrs& txs) NOEXCEPT { - // Overflow returns max_size_t. - const auto sum = [=](size_t total, const transaction::cptr& tx) NOEXCEPT + sizes size{}; + std::for_each(txs.begin(), txs.end(), [&](const auto& tx) NOEXCEPT { - return ceilinged_add(total, tx->serialized_size(witness)); - }; + size.nominal = ceilinged_add(size.nominal, tx->serialized_size(false)); + size.witnessed = ceilinged_add(size.witnessed, tx->serialized_size(true)); + }); + + const auto common_size = ceilinged_add( + header::serialized_size(), + variable_size(txs.size())); + + const auto nominal_size = ceilinged_add( + common_size, + size.nominal); - return header::serialized_size() - + variable_size(txs_->size()) - + std::accumulate(txs_->begin(), txs_->end(), zero, sum); + const auto witnessed_size = ceilinged_add( + common_size, + size.witnessed); + + return { nominal_size, witnessed_size }; +} + +size_t block::serialized_size(bool witness) const NOEXCEPT +{ + return witness ? size_.witnessed : size_.nominal; } // Connect. @@ -294,7 +314,7 @@ bool block::is_extra_coinbases() const NOEXCEPT if (txs_->empty()) return false; - const auto value = [](const transaction::cptr& tx) NOEXCEPT + const auto value = [](const auto& tx) NOEXCEPT { return tx->is_coinbase(); }; @@ -318,7 +338,7 @@ bool block::is_forward_reference() const NOEXCEPT return hashes.find(std::ref(input->point().hash())) != hashes.end(); }; - const auto spend = [&spent, &hashes](const transaction::cptr& tx) NOEXCEPT + const auto spend = [&spent, &hashes](const auto& tx) NOEXCEPT { const auto& ins = *tx->inputs_ptr(); const auto forward = std::any_of(ins.begin(), ins.end(), spent); @@ -338,7 +358,7 @@ bool block::is_internal_double_spend() const NOEXCEPT return false; // Overflow returns max_size_t. - const auto sum_ins = [](size_t total, const transaction::cptr& tx) NOEXCEPT + const auto sum_ins = [](size_t total, const auto& tx) NOEXCEPT { return ceilinged_add(total, tx->inputs()); }; @@ -351,7 +371,7 @@ bool block::is_internal_double_spend() const NOEXCEPT return !points.emplace(in->point()).second; }; - const auto double_spent = [&spent](const transaction::cptr& tx) NOEXCEPT + const auto double_spent = [&spent](const auto& tx) NOEXCEPT { const auto& ins = *tx->inputs_ptr(); return std::any_of(ins.begin(), ins.end(), spent); @@ -377,8 +397,9 @@ bool block::is_invalid_merkle_root() const NOEXCEPT size_t block::weight() const NOEXCEPT { // Block weight is 3 * nominal size * + 1 * witness size (bip141). - return base_size_contribution * serialized_size(false) + - total_size_contribution * serialized_size(true); + return ceilinged_add( + ceilinged_multiply(base_size_contribution, serialized_size(false)), + ceilinged_multiply(total_size_contribution, serialized_size(true))); } bool block::is_overweight() const NOEXCEPT @@ -484,7 +505,7 @@ bool block::is_malleable64() const NOEXCEPT // This form of malleability does not imply current block instance is invalid. bool block::is_malleable64(const transaction_cptrs& txs) NOEXCEPT { - const auto two_leaves = [](const transaction::cptr& tx) NOEXCEPT + const auto two_leaves = [](const auto& tx) NOEXCEPT { return tx->serialized_size(false) == two * hash_size; }; @@ -494,7 +515,7 @@ bool block::is_malleable64(const transaction_cptrs& txs) NOEXCEPT bool block::is_segregated() const NOEXCEPT { - const auto segregated = [](const transaction::cptr& tx) NOEXCEPT + const auto segregated = [](const auto& tx) NOEXCEPT { return tx->is_segregated(); }; @@ -547,7 +568,7 @@ static uint64_t block_subsidy(size_t height, uint64_t subsidy_interval, uint64_t block::fees() const NOEXCEPT { // Overflow returns max_uint64. - const auto value = [](uint64_t total, const transaction::cptr& tx) NOEXCEPT + const auto value = [](uint64_t total, const auto& tx) NOEXCEPT { return ceilinged_add(total, tx->fee()); }; @@ -578,7 +599,7 @@ bool block::is_overspent(size_t height, uint64_t subsidy_interval, size_t block::signature_operations(bool bip16, bool bip141) const NOEXCEPT { // Overflow returns max_size_t. - const auto value = [=](size_t total, const transaction::cptr& tx) NOEXCEPT + const auto value = [=](size_t total, const auto& tx) NOEXCEPT { return ceilinged_add(total, tx->signature_operations(bip16, bip141)); }; diff --git a/src/chain/input.cpp b/src/chain/input.cpp index 6a13c96c9b..842b894467 100644 --- a/src/chain/input.cpp +++ b/src/chain/input.cpp @@ -154,7 +154,8 @@ input::input(const chain::point::cptr& point, const chain::script::cptr& script, script_(script), witness_(witness), sequence_(sequence), - valid_(valid) + valid_(valid), + size_(serialized_size(*script, *witness)) { } @@ -216,16 +217,52 @@ void input::to_data(writer& sink) const NOEXCEPT sink.write_4_bytes_little_endian(sequence_); } +// static/private +input::sizes input::serialized_size(const chain::script& script, + const chain::witness& witness) NOEXCEPT +{ + constexpr auto const_size = ceilinged_add( + point::serialized_size(), + sizeof(sequence_)); + + const auto nominal_size = ceilinged_add( + const_size, + script.serialized_size(true)); + + const auto witnessed_size = ceilinged_add( + nominal_size, + witness.serialized_size(true)); + + return { nominal_size, witnessed_size }; +} + +// input.serialized_size(witness) provides sizing for witness, however +// witnesses are serialized by the transaction. This is an ugly hack as a +// consequence of bip144 not serializing witnesses as part of inputs, which +// is logically the proper association. size_t input::serialized_size(bool witness) const NOEXCEPT { - // input.serialized_size(witness) provides sizing for witness, however - // witnesses are serialized by the transaction. This is an ugly hack as a - // consequence of bip144 not serializing witnesses as part of inputs, which - // is logically the proper association. - return point::serialized_size() - + script_->serialized_size(true) - + (witness ? witness_->serialized_size(true) : zero) - + sizeof(sequence_); + return witness ? size_.witnessed : size_.nominal; +} + +// Friend accessors (private). +// ---------------------------------------------------------------------------- + +size_t input::nominal_size() const NOEXCEPT +{ + return size_.nominal; +} + +size_t input::witnessed_size() const NOEXCEPT +{ + return size_.witnessed; +} + +void input::set_witness(reader& source) NOEXCEPT +{ + witness_ = to_shared(source, true); + size_.witnessed = ceilinged_add(size_.nominal, + witness_->serialized_size(true)); } // Properties. diff --git a/src/chain/output.cpp b/src/chain/output.cpp index 6f2c176afd..65c1c19247 100644 --- a/src/chain/output.cpp +++ b/src/chain/output.cpp @@ -98,7 +98,10 @@ output::output(reader& source) NOEXCEPT // protected output::output(uint64_t value, const chain::script::cptr& script, bool valid) NOEXCEPT - : value_(value), script_(script), valid_(valid) + : value_(value), + script_(script), + valid_(valid), + size_(serialized_size(*script, value)) { } @@ -153,10 +156,16 @@ void output::to_data(writer& sink) const NOEXCEPT script_->to_data(sink, true); } -// TODO: this is expensive. +// static/private +size_t output::serialized_size(const chain::script& script, + uint64_t value) NOEXCEPT +{ + return ceilinged_add(sizeof(value), script.serialized_size(true)); +} + size_t output::serialized_size() const NOEXCEPT { - return sizeof(value_) + script_->serialized_size(true); + return size_; } // Properties. diff --git a/src/chain/script.cpp b/src/chain/script.cpp index a9c231e874..5453ec569f 100644 --- a/src/chain/script.cpp +++ b/src/chain/script.cpp @@ -167,7 +167,7 @@ script::script(const operations& ops, bool valid, bool prefail) NOEXCEPT : ops_(ops), valid_(valid), prefail_(prefail), - size_(serialized_size(ops_)), + size_(serialized_size(ops)), offset(ops_.begin()) { } @@ -395,13 +395,10 @@ size_t script::serialized_size(const operations& ops) NOEXCEPT size_t script::serialized_size(bool prefix) const NOEXCEPT { // Recompute it serialization has been affected by offset metadata. - auto size = (offset == ops_.begin()) ? size_ : + const auto size = (offset == ops_.begin()) ? size_ : std::accumulate(offset, ops_.end(), zero, op_size); - if (prefix) - size += variable_size(size); - - return size; + return prefix ? ceilinged_add(size, variable_size(size)) : size; } // Utilities. diff --git a/src/chain/transaction.cpp b/src/chain/transaction.cpp index d4b318afc2..b6a3816ad1 100644 --- a/src/chain/transaction.cpp +++ b/src/chain/transaction.cpp @@ -97,21 +97,29 @@ transaction::transaction(const transaction& other) NOEXCEPT other.segregated_, other.valid_) { + // Optimized for faster optional, not for copy. + if (other.nominal_hash_) nominal_hash_ = to_unique(*other.nominal_hash_); + else + nominal_hash_.reset(); + if (other.witness_hash_) witness_hash_ = to_unique(*other.witness_hash_); + else + witness_hash_.reset(); + if (other.sighash_cache_) sighash_cache_ = to_unique(*other.sighash_cache_); + else + sighash_cache_.reset(); } transaction::transaction(uint32_t version, chain::inputs&& inputs, chain::outputs&& outputs, uint32_t locktime) NOEXCEPT : transaction(version, to_shareds(std::move(inputs)), - to_shareds(std::move(outputs)), locktime, false, true) + to_shareds(std::move(outputs)), locktime) { - // Defer execution for constructor move. - segregated_ = segregated(*inputs_); } transaction::transaction(uint32_t version, const chain::inputs& inputs, @@ -171,7 +179,8 @@ transaction::transaction(uint32_t version, outputs_(outputs ? outputs : to_shared()), locktime_(locktime), segregated_(segregated), - valid_(valid) + valid_(valid), + size_(serialized_size(*inputs, *outputs, segregated)) { } @@ -192,13 +201,24 @@ transaction& transaction::operator=(const transaction& other) NOEXCEPT locktime_ = other.locktime_; segregated_ = other.segregated_; valid_ = other.valid_; + size_ = other.size_; + + // Optimized for faster optional, not for copy. if (other.nominal_hash_) nominal_hash_ = to_unique(*other.nominal_hash_); + else + nominal_hash_.reset(); + if (other.witness_hash_) witness_hash_ = to_unique(*other.witness_hash_); + else + witness_hash_.reset(); + if (other.sighash_cache_) sighash_cache_ = to_unique(*other.sighash_cache_); + else + sighash_cache_.reset(); return *this; } @@ -269,10 +289,8 @@ transaction transaction::from_data(reader& source, bool witness) NOEXCEPT { if (witness) { - // Safe to cast as this method exclusively owns the input and - // input::witness_ a mutable public property of the instance. - const auto setter = const_cast(input.get()); - setter->witness_ = to_shared(source, true); + // Safe to cast as this method exclusively owns the input. + const_cast(input.get())->set_witness(source); } else { @@ -340,29 +358,69 @@ void transaction::to_data(writer& sink, bool witness) const NOEXCEPT sink.write_4_bytes_little_endian(locktime_); } -// TODO: this is expensive. -size_t transaction::serialized_size(bool witness) const NOEXCEPT +// static/private +transaction::sizes transaction::serialized_size( + const chain::input_cptrs& inputs, + const chain::output_cptrs& outputs, bool segregated) NOEXCEPT { - witness &= segregated_; + sizes size{ zero, zero }; - const auto ins = [=](size_t total, const auto& input) NOEXCEPT + // Keep the condition outside of the loop. + if (segregated) { - // Inputs account for witness bytes. - return total + input->serialized_size(witness); - }; + std::for_each(inputs.begin(), inputs.end(), [&](const auto& in) NOEXCEPT + { + size.nominal = ceilinged_add(size.nominal, in->nominal_size()); + size.witnessed = ceilinged_add(size.witnessed, in->witnessed_size()); + }); + } + else + { + // Witness must be zeroed because witnesses have nonzero size when they + // are zero-valued, so they can be archived easily. Also it would be + // wasteful to to count mutiple zero sizes, so exclude them here. + std::for_each(inputs.begin(), inputs.end(), [&](const auto& in) NOEXCEPT + { + size.nominal = ceilinged_add(size.nominal, in->nominal_size()); + }); + } const auto outs = [](size_t total, const auto& output) NOEXCEPT { - return total + output->serialized_size(); + return ceilinged_add(total, output->serialized_size()); }; - return sizeof(version_) - + (witness ? sizeof(witness_marker) + sizeof(witness_enabled) : zero) - + variable_size(inputs_->size()) - + std::accumulate(inputs_->begin(), inputs_->end(), zero, ins) - + variable_size(outputs_->size()) - + std::accumulate(outputs_->begin(), outputs_->end(), zero, outs) - + sizeof(locktime_); + constexpr auto base_const_size = ceilinged_add( + sizeof(version_), + sizeof(locktime_)); + + constexpr auto witness_const_size = ceilinged_add( + sizeof(witness_marker), + sizeof(witness_enabled)); + + const auto base_size = ceilinged_add(ceilinged_add(ceilinged_add( + base_const_size, + variable_size(inputs.size())), + variable_size(outputs.size())), + std::accumulate(outputs.begin(), outputs.end(), zero, outs)); + + const auto nominal_size = ceilinged_add( + base_size, + size.nominal); + + const auto witnessed_size = ceilinged_add(ceilinged_add( + base_size, + witness_const_size), + size.witnessed); + + return { nominal_size, witnessed_size }; +} + +size_t transaction::serialized_size(bool witness) const NOEXCEPT +{ + witness &= segregated_; + + return witness ? size_.witnessed : size_.nominal; } // Properties. @@ -849,7 +907,6 @@ hash_digest transaction::version_0_signature_hash(const input_iterator& input, return unversioned_signature_hash(input, sub, sighash_flags); // Set options. - // C++14: switch in constexpr. const auto anyone = to_bool(sighash_flags & coverage::anyone_can_pay); const auto flag = mask_sighash(sighash_flags); const auto all = (flag == coverage::hash_all); @@ -1014,8 +1071,9 @@ bool transaction::is_segregated() const NOEXCEPT size_t transaction::weight() const NOEXCEPT { // Block weight is 3 * base size * + 1 * total size (bip141). - return base_size_contribution * serialized_size(false) + - total_size_contribution * serialized_size(true); + return ceilinged_add( + ceilinged_multiply(base_size_contribution, serialized_size(false)), + ceilinged_multiply(total_size_contribution, serialized_size(true))); } bool transaction::is_overweight() const NOEXCEPT diff --git a/src/chain/witness.cpp b/src/chain/witness.cpp index d94e373241..255024236d 100644 --- a/src/chain/witness.cpp +++ b/src/chain/witness.cpp @@ -37,6 +37,8 @@ namespace libbitcoin { namespace system { namespace chain { +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + using namespace system::machine; static const auto checksig_script = script{ { opcode::checksig } }; @@ -71,9 +73,7 @@ witness::witness(const chunk_cptrs& stack) NOEXCEPT } witness::witness(const data_slice& data, bool prefix) NOEXCEPT - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) : witness(stream::in::copy(data), prefix) - BC_POP_WARNING() { } @@ -236,11 +236,7 @@ witness witness::from_string(const std::string& mnemonic) NOEXCEPT data_chunk witness::to_data(bool prefix) const NOEXCEPT { data_chunk data(serialized_size(prefix)); - - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) stream::out::copy ostream(data); - BC_POP_WARNING() - to_data(ostream, prefix); return data; } @@ -299,13 +295,10 @@ size_t witness::serialized_size(const chunk_cptrs& stack) NOEXCEPT size_t witness::serialized_size(bool prefix) const NOEXCEPT { - auto size = size_; - // Witness prefix is an element count, not byte length (unlike script). - if (prefix) - size += variable_size(stack_.size()); - - return size; + // An empty stack is not a valid witnessed tx (no inputs) but a consistent + // serialization is used independently by database so zero stack allowed. + return prefix ? ceilinged_add(size_, variable_size(stack_.size())) : size_; } // Utilities. @@ -367,9 +360,6 @@ bool witness::extract_sigop_script(script& out_script, } } -BC_PUSH_WARNING(NO_NEW_OR_DELETE) -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - // Extract script and initial execution stack. bool witness::extract_script(script::cptr& out_script, chunk_cptrs_ptr& out_stack, const script& program_script) const NOEXCEPT @@ -434,7 +424,6 @@ bool witness::extract_script(script::cptr& out_script, } } -BC_POP_WARNING() BC_POP_WARNING() // JSON value convertors.