Skip to content

Commit

Permalink
Merge pull request libbitcoin#1432 from evoskuil/master
Browse files Browse the repository at this point in the history
Factor and fix malleability checks.
  • Loading branch information
evoskuil authored Apr 5, 2024
2 parents 1e0f600 + d080c43 commit a4d3985
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 29 deletions.
2 changes: 2 additions & 0 deletions include/bitcoin/system/chain/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class BC_API block
uint64_t claim() const NOEXCEPT;
hash_digest hash() const NOEXCEPT;
bool is_malleable() const NOEXCEPT;
bool is_malleable_duplicate() const NOEXCEPT;
bool is_malleable_coincident() const NOEXCEPT;
bool is_segregated() const NOEXCEPT;
size_t serialized_size(bool witness) const NOEXCEPT;
size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT;
Expand Down
53 changes: 24 additions & 29 deletions src/chain/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
#include <bitcoin/system/chain/block.hpp>

#include <algorithm>
#include <cfenv>
#include <iterator>
#include <memory>
#include <numeric>
#include <set>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
Expand Down Expand Up @@ -274,23 +274,6 @@ size_t block::serialized_size(bool witness) const NOEXCEPT
// Connect.
// ----------------------------------------------------------------------------

// Subset of is_internal_double_spend if sha256 collisions cannot happen. This
// is because each tx must have an input and for there to be no double spend in
// the block the inputs must be unique (and only one coinbase). As the
// is_internal_double_spend check invalidates any block with duplicated txs,
// there can be no tx hash duplication within the merkle tree. And a block that
// fails block.check is not archived, and its header remains potentially valid.
////bool block::is_distinct_transaction_set() const
////{
//// // A set is used to collapse duplicates.
//// std::set<hash_digest> hashes;
////
//// for (const auto& tx: *txs_)
//// hashes.insert(tx->hash(false));
////
//// return hashes.size() == txs_->size();
////}

bool block::is_empty() const NOEXCEPT
{
return txs_->empty();
Expand Down Expand Up @@ -456,26 +439,38 @@ bool block::is_hash_limit_exceeded() const NOEXCEPT
}

// This is not part of validation. Should be called after *invalidation* to
// determine if the invalidity is universal (otherwise do not cache invalid).
// determine if the invalidity is universal (otherwise do not cache invalid),
// and as an abbreviated validation when under checkpoint/milestone.
// lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html
bool block::is_malleable() const NOEXCEPT
{
// A two tx block cannot be malleable as coinbase is singular, otherwise
// if the last two non-witness tx hashes match then the id is malleable.
const auto count = txs_->size();
if (count > two && is_even(count) &&
txs_->at(sub1(count))->hash(false) == txs_->at(count)->hash(false))
{
return true;
}
return is_malleable_coincident() || is_malleable_duplicate();
}

// Hash of two same concatenated leaves is same as doubling one odd leaf.
// Repeated tx hashes is a subset of is_internal_double_spend.
// This form of malleability also implies current block instance is invalid.
bool block::is_malleable_duplicate() const NOEXCEPT
{
// A set is used to collapse duplicates.
std::set<hash_digest> hashes;

BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
for (const auto& tx: *txs_)
hashes.insert(tx->hash(false));
BC_POP_WARNING()

return hashes.size() == txs_->size();
}

// If all non-witness tx serializations are 64 bytes the id is malleable.
// This form of malleability does not imply current block instance is invalid.
bool block::is_malleable_coincident() const NOEXCEPT
{
const auto two_leaf_size = [](const transaction::cptr& tx) NOEXCEPT
{
return tx->serialized_size(false) == two * hash_size;
};

// If all non-witness tx serializations are 64 bytes the id is malleable.
return std::all_of(txs_->begin(), txs_->end(), two_leaf_size);
}

Expand Down

0 comments on commit a4d3985

Please sign in to comment.