diff --git a/include/bitcoin/system/chain/block.hpp b/include/bitcoin/system/chain/block.hpp index c63d689c85..96491f210a 100644 --- a/include/bitcoin/system/chain/block.hpp +++ b/include/bitcoin/system/chain/block.hpp @@ -104,9 +104,11 @@ class BC_API block /// Computed malleation properties. bool is_malleable() const NOEXCEPT; - bool is_malleable64() const NOEXCEPT; + bool is_malleated() const NOEXCEPT; bool is_malleable32() const NOEXCEPT; bool is_malleated32() const NOEXCEPT; + bool is_malleable64() const NOEXCEPT; + bool is_malleated64() const NOEXCEPT; /// Validation. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/system/error/block_error_t.hpp b/include/bitcoin/system/error/block_error_t.hpp index b9ea622996..ae271faf4a 100644 --- a/include/bitcoin/system/error/block_error_t.hpp +++ b/include/bitcoin/system/error/block_error_t.hpp @@ -56,7 +56,7 @@ enum block_error_t : uint8_t forward_reference, invalid_transaction_commitment, block_legacy_sigop_limit, - type32_malleated, + block_malleated, // accept block block_non_final, diff --git a/src/chain/block.cpp b/src/chain/block.cpp index c6d9bceeea..b945141206 100644 --- a/src/chain/block.cpp +++ b/src/chain/block.cpp @@ -452,6 +452,11 @@ bool block::is_malleable() const NOEXCEPT return is_malleable64() || is_malleable32(); } +bool block::is_malleated() const NOEXCEPT +{ + return is_malleated32() || is_malleated64(); +} + bool block::is_malleable32() const NOEXCEPT { const auto unmalleated = txs_->size(); @@ -514,6 +519,14 @@ bool block::is_malleable64(const transaction_cptrs& txs) NOEXCEPT return !txs.empty() && std::all_of(txs.begin(), txs.end(), two_leaves); } +// A mallaeable64 block is considered malleated if the first tx is not a valid +// coinbase. It is possible but computationally infeasible to grind a valid +// coinbase and therefore treated similarly to sha256 hash collision. +bool block::is_malleated64() const NOEXCEPT +{ + return is_malleable64(*txs_) && !txs_->front()->is_coinbase(); +} + bool block::is_segregated() const NOEXCEPT { const auto segregated = [](const auto& tx) NOEXCEPT @@ -736,9 +749,10 @@ code block::check(bool bypass) const NOEXCEPT if (is_invalid_merkle_root()) return error::invalid_transaction_commitment; - // type32_malleated is subset of is_internal_double_spend - if (bypass && is_malleated32()) - return error::type32_malleated; + // type32 malleated is a subset of is_internal_double_spend + // type64 malleated is a subset of first_not_coinbase + if (bypass && is_malleated()) + return error::block_malleated; if (bypass) return error::block_success; diff --git a/src/error/block_error_t.cpp b/src/error/block_error_t.cpp index ca63b5d4ff..6475a85ee0 100644 --- a/src/error/block_error_t.cpp +++ b/src/error/block_error_t.cpp @@ -52,7 +52,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(block_error) { forward_reference, "transactions out of order" }, { invalid_transaction_commitment, "invalid transaction commitment" }, { block_legacy_sigop_limit, "too many block legacy signature operations" }, - { type32_malleated, "block is type32 malleated" }, + { block_malleated, "block is malleated" }, // accept block { block_non_final, "block contains a non-final transaction" }, diff --git a/test/error/block_error_t.cpp b/test/error/block_error_t.cpp index 6f4f7a72b4..47399379d0 100644 --- a/test/error/block_error_t.cpp +++ b/test/error/block_error_t.cpp @@ -183,13 +183,13 @@ BOOST_AUTO_TEST_CASE(block_error_t__code__block_legacy_sigop_limit__true_exected BOOST_REQUIRE_EQUAL(ec.message(), "too many block legacy signature operations"); } -BOOST_AUTO_TEST_CASE(block_error_t__code__type32_malleated__true_exected_message) +BOOST_AUTO_TEST_CASE(block_error_t__code__block_malleated__true_exected_message) { - constexpr auto value = error::type32_malleated; + constexpr auto value = error::block_malleated; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "block is type32 malleated"); + BOOST_REQUIRE_EQUAL(ec.message(), "block is malleated"); } // accept block