diff --git a/include/bitcoin/system/chain/block.hpp b/include/bitcoin/system/chain/block.hpp index 834a1ac91b..c80c406a32 100644 --- a/include/bitcoin/system/chain/block.hpp +++ b/include/bitcoin/system/chain/block.hpp @@ -98,6 +98,7 @@ class BC_API block bool is_segregated() const NOEXCEPT; size_t serialized_size(bool witness) const NOEXCEPT; size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT; + size_t segregated() const NOEXCEPT; /// Computed malleation properties. bool is_malleable() const NOEXCEPT; diff --git a/include/bitcoin/system/error/macros.hpp b/include/bitcoin/system/error/macros.hpp index bebea1cf94..9bb8b326f4 100644 --- a/include/bitcoin/system/error/macros.hpp +++ b/include/bitcoin/system/error/macros.hpp @@ -57,6 +57,7 @@ class BC_API cat##_category \ static const message_map messages; \ public: \ static const cat##_category singleton; \ + static bool contains(const std::error_code& ec) noexcept; \ virtual const char* name() const noexcept; \ virtual std::string message(int condition) const noexcept; \ virtual std::error_condition default_error_condition(int value) const noexcept; \ @@ -74,6 +75,7 @@ class BC_API cat##_category \ static const message_map messages; \ public: \ static const cat##_category singleton; \ + static bool contains(const std::error_code& ec) noexcept; \ virtual const char* name() const noexcept; \ virtual std::string message(int condition) const noexcept; \ virtual std::error_condition default_error_condition(int value) const noexcept; \ @@ -84,6 +86,10 @@ std::error_condition make_error_condition(cat##_t value) noexcept #define DEFINE_ERROR_T_CATEGORY(cat, category_name, unmapped) \ const cat##_category cat##_category::singleton; \ +bool cat##_category::contains(const std::error_code& ec) noexcept \ +{ \ + return is_zero(std::strcmp(ec.category().name(), category_name)); \ +} \ const char* cat##_category::name() const noexcept \ { \ return category_name; \ diff --git a/src/chain/block.cpp b/src/chain/block.cpp index 6b1b5dc40b..33dff1d784 100644 --- a/src/chain/block.cpp +++ b/src/chain/block.cpp @@ -587,6 +587,16 @@ bool block::is_segregated() const NOEXCEPT return std::any_of(txs_->begin(), txs_->end(), segregated); } +size_t block::segregated() const NOEXCEPT +{ + const auto count_segregated = [](const auto& tx) NOEXCEPT + { + return tx->is_segregated(); + }; + + return std::count_if(txs_->begin(), txs_->end(), count_segregated); +} + // The witness merkle root is obtained from wtxids, subject to malleation just // as the txs commitment. However, since tx duplicates are precluded by the // malleable32 (or complete) block check, there is no opportunity for this. @@ -600,6 +610,10 @@ bool block::is_invalid_witness_commitment() const NOEXCEPT if (coinbase->inputs_ptr()->empty()) return false; + // If no block tx has witness data the commitment is optional (bip141). + if (!is_segregated()) + return false; + // If there is a valid commitment, return false (valid). // Coinbase input witness must be 32 byte witness reserved value (bip141). // Last output of commitment pattern holds the committed value (bip141). @@ -610,10 +624,8 @@ bool block::is_invalid_witness_commitment() const NOEXCEPT if (committed == sha256::double_hash( generate_merkle_root(true), reserved)) return false; - - // If no valid commitment, return true (invalid) if segregated. - // If no block tx has witness data the commitment is optional (bip141). - return is_segregated(); + + return true; } //***************************************************************************** diff --git a/test/chain/block.cpp b/test/chain/block.cpp index f177ca3004..1ec88dfa67 100644 --- a/test/chain/block.cpp +++ b/test/chain/block.cpp @@ -460,6 +460,7 @@ BOOST_AUTO_TEST_CASE(block__hash__default__matches_header_hash) // is_malleable // is_segregated // serialized_size +// segregated // validation (public) // ---------------------------------------------------------------------------- diff --git a/test/error/error.cpp b/test/error/error.cpp index 207a8ea702..cf494cf49e 100644 --- a/test/error/error.cpp +++ b/test/error/error.cpp @@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(error_t__code__message_overflow__default_message) // Overflows can be cast in, and value retained, and yield default message. BOOST_AUTO_TEST_CASE(error_t__code__value_overflow__default_message) { - const auto overflow = static_cast(error::error_last); + constexpr auto overflow = static_cast(error::error_last); const auto ec = code(static_cast(overflow)); BOOST_REQUIRE(ec); BOOST_REQUIRE_EQUAL(ec.message(), default_message); @@ -108,6 +108,14 @@ BOOST_AUTO_TEST_CASE(error_t__code__category_name__expected) { BOOST_REQUIRE_EQUAL(code().category().name(), system_category_name); BOOST_REQUIRE_EQUAL(code(error::success).category().name(), bitcoin_category_name); + BOOST_REQUIRE_NE(code(error::script_success).category().name(), code(error::success).category().name()); +} + +BOOST_AUTO_TEST_CASE(error_t__code__category_contains__expected) +{ + BOOST_REQUIRE(system::error::error_category::contains(system::error::success)); + BOOST_REQUIRE(!system::error::script_error_category::contains(system::error::success)); + BOOST_REQUIRE(system::error::script_error_category::contains(system::error::script_success)); } BOOST_AUTO_TEST_CASE(error_t__code__default_error_condition_category_name__expected)