Skip to content

Commit

Permalink
Merge pull request #1588 from evoskuil/master
Browse files Browse the repository at this point in the history
Simplify is_internal_double_spend and is_forward_reference.
  • Loading branch information
evoskuil authored Jan 21, 2025
2 parents 9734372 + 2028001 commit d9ea76b
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 37 deletions.
1 change: 1 addition & 0 deletions include/bitcoin/system/chain/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class BC_API block
hashes transaction_hashes(bool witness) const NOEXCEPT;

/// Computed properties.
size_t outputs() const NOEXCEPT;
size_t spends() const NOEXCEPT;
size_t weight() const NOEXCEPT;
uint64_t fees() const NOEXCEPT;
Expand Down
71 changes: 34 additions & 37 deletions src/chain/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ hashes block::transaction_hashes(bool witness) const NOEXCEPT
{
const auto count = txs_->size();
const auto size = is_odd(count) && count > one ? add1(count) : count;
hashes out(size);
hashes out{ size };

// Extra allocation for odd count optimizes for merkle root.
// Vector capacity is never reduced when resizing to smaller size.
Expand All @@ -250,6 +250,21 @@ hashes block::transaction_hashes(bool witness) const NOEXCEPT
return out;
}

// computed
size_t block::outputs() const NOEXCEPT
{
if (txs_->empty())
return zero;

// Overflow returns max_size_t.
const auto outs = [](size_t total, const auto& tx) NOEXCEPT
{
return ceilinged_add(total, tx->outputs());
};

return std::accumulate(std::next(txs_->begin()), txs_->end(), zero, outs);
}

// computed
size_t block::spends() const NOEXCEPT
{
Expand All @@ -262,6 +277,7 @@ size_t block::spends() const NOEXCEPT
return ceilinged_add(total, tx->inputs());
};

// inputs() is add1(spends()) if the block is valid (one coinbase input).
return std::accumulate(std::next(txs_->begin()), txs_->end(), zero, ins);
}

Expand Down Expand Up @@ -382,22 +398,17 @@ bool block::is_forward_reference() const NOEXCEPT
if (txs_->empty())
return false;

const auto sum_txs = sub1(txs_->size());
unordered_set_of_hash_cref hashes{ sum_txs };
const auto spent = [&hashes](const input::cptr& input) NOEXCEPT
unordered_set_of_hash_cref hashes{ sub1(txs_->size()) };
for (auto tx = txs_->rbegin(); tx != std::prev(txs_->rend()); ++tx)
{
return hashes.find(std::ref(input->point().hash())) != hashes.end();
};
for (const auto& in: *(*tx)->inputs_ptr())
if (hashes.contains(in->point().hash()))
return true;

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);
hashes.emplace(tx->get_hash(false));
return forward;
};
hashes.emplace((*tx)->get_hash(false));
}

return std::any_of(txs_->rbegin(), std::prev(txs_->rend()), spend);
return false;
}

// This also precludes the block merkle calculation DoS exploit by preventing
Expand All @@ -408,27 +419,13 @@ bool block::is_internal_double_spend() const NOEXCEPT
if (txs_->empty())
return false;

// Overflow returns max_size_t.
const auto sum_ins = [](size_t total, const auto& tx) NOEXCEPT
{
return ceilinged_add(total, tx->inputs());
};

const auto tx1 = std::next(txs_->begin());
const auto spends_count = std::accumulate(tx1, txs_->end(), zero, sum_ins);
unordered_set_of_point_cref points{ spends_count };
const auto spent = [&points](const input::cptr& in) NOEXCEPT
{
return !points.emplace(in->point()).second;
};

const auto double_spent = [&spent](const auto& tx) NOEXCEPT
{
const auto& ins = tx->inputs_ptr();
return std::any_of(ins->begin(), ins->end(), spent);
};
unordered_set_of_point_cref points{ spends() };
for (auto tx = std::next(txs_->begin()); tx != txs_->end(); ++tx)
for (const auto& in: *(*tx)->inputs_ptr())
if (!points.emplace(in->point()).second)
return true;

return std::any_of(tx1, txs_->end(), double_spent);
return false;
}

// private
Expand Down Expand Up @@ -477,7 +474,7 @@ bool block::is_hash_limit_exceeded() const NOEXCEPT
return false;

// A set is used to collapse duplicates.
unordered_set_of_hash_cref hashes{};
unordered_set_of_hash_cref hashes{ txs_->size() };

// Just the coinbase tx hash, skip its null input hashes.
hashes.emplace(txs_->front()->get_hash(false));
Expand Down Expand Up @@ -645,7 +642,7 @@ uint64_t block::fees() const NOEXCEPT
return ceilinged_add(total, tx->fee());
};

return std::accumulate(txs_->begin(), txs_->end(), uint64_t{0}, value);
return std::accumulate(txs_->begin(), txs_->end(), uint64_t{}, value);
}

uint64_t block::claim() const NOEXCEPT
Expand Down Expand Up @@ -709,7 +706,7 @@ bool block::populate(const chain::context& ctx) const NOEXCEPT

const auto start = std::next(txs_->begin());
const auto bip68 = ctx.is_enabled(chain::flags::bip68_rule);
unordered_map_of_cref_point_to_output_cptr_cref points{};
unordered_map_of_cref_point_to_output_cptr_cref points{ outputs() };
uint32_t index{};

// Populate outputs hash table.
Expand Down

0 comments on commit d9ea76b

Please sign in to comment.