Skip to content

Commit 07f5e2f

Browse files
committed
Refactor is_invalid_witness_commitment, use cref to avoid copies.
1 parent 199702c commit 07f5e2f

6 files changed

Lines changed: 48 additions & 30 deletions

File tree

include/bitcoin/system/chain/block.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ class BC_API block
200200

201201
// context free
202202
hash_digest generate_merkle_root(bool witness) const NOEXCEPT;
203+
bool get_witness_commitment(hash_cref& commitment) const NOEXCEPT;
204+
bool get_witness_reservation(hash_cref& reservation) const NOEXCEPT;
203205

204206
// contextual
205207
uint64_t reward(size_t height, uint64_t subsidy_interval,

include/bitcoin/system/chain/input.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class BC_API input
110110

111111
bool is_final() const NOEXCEPT;
112112
bool is_roller() const NOEXCEPT;
113-
bool reserved_hash(hash_digest& out) const NOEXCEPT;
113+
bool reserved_hash(hash_cref& out) const NOEXCEPT;
114114

115115
/// Assumes coinbase if prevout not populated (returns only legacy sigops).
116116
size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT;

include/bitcoin/system/chain/output.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class BC_API output
8585
/// Methods.
8686
/// -----------------------------------------------------------------------
8787

88-
bool committed_hash(hash_digest& out) const NOEXCEPT;
88+
bool committed_hash(hash_cref& out) const NOEXCEPT;
8989
size_t signature_operations(bool bip141) const NOEXCEPT;
9090
bool is_dust(uint64_t minimum_output_value) const NOEXCEPT;
9191

src/chain/block.cpp

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,30 @@ size_t block::segregated() const NOEXCEPT
592592
return std::count_if(txs_->begin(), txs_->end(), count_segregated);
593593
}
594594

595+
// Last output of commitment pattern holds the committed value (bip141).
596+
bool block::get_witness_commitment(hash_cref& commitment) const NOEXCEPT
597+
{
598+
if (txs_->empty())
599+
return false;
600+
601+
const auto& outputs = *txs_->front()->outputs_ptr();
602+
for (const auto& output: std::views::reverse(outputs))
603+
if (output->committed_hash(commitment))
604+
return true;
605+
606+
return false;
607+
}
608+
609+
// Coinbase input witness must be 32 byte witness reserved value (bip141).
610+
bool block::get_witness_reservation(hash_cref& reservation) const NOEXCEPT
611+
{
612+
if (txs_->empty())
613+
return false;
614+
615+
const auto& inputs = *txs_->front()->inputs_ptr();
616+
return !inputs.empty() && inputs.front()->reserved_hash(reservation);
617+
}
618+
595619
// The witness merkle root is obtained from wtxids, subject to malleation just
596620
// as the txs commitment. However, since tx duplicates are precluded by the
597621
// malleable32 (or complete) block check, there is no opportunity for this.
@@ -601,26 +625,19 @@ bool block::is_invalid_witness_commitment() const NOEXCEPT
601625
if (txs_->empty())
602626
return false;
603627

604-
const auto& coinbase = txs_->front();
605-
if (coinbase->inputs_ptr()->empty())
606-
return false;
607-
628+
// Witness data (segregated) disallowed if no commitment (bip141).
608629
// If no block tx has witness data the commitment is optional (bip141).
609-
if (!is_segregated())
610-
return false;
630+
hash_cref commit{ null_hash };
631+
if (!get_witness_commitment(commit))
632+
return is_segregated();
611633

612-
// If there is a valid commitment, return false (valid).
613-
// Coinbase input witness must be 32 byte witness reserved value (bip141).
614-
// Last output of commitment pattern holds the committed value (bip141).
615-
hash_digest reserved{}, committed{};
616-
if (coinbase->inputs_ptr()->front()->reserved_hash(reserved))
617-
for (const auto& output: std::views::reverse(*coinbase->outputs_ptr()))
618-
if (output->committed_hash(committed))
619-
if (committed == sha256::double_hash(
620-
generate_merkle_root(true), reserved))
621-
return false;
634+
// If there is a witness reservation there must be a commitment (bip141).
635+
hash_cref reserve{ null_hash };
636+
if (!get_witness_reservation(reserve))
637+
return true;
622638

623-
return true;
639+
// If there is a valid commitment, return false (valid).
640+
return commit != sha256::double_hash(generate_merkle_root(true), reserve);
624641
}
625642

626643
//*****************************************************************************

src/chain/input.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,14 @@ bool input::is_relative_locked(size_t height,
430430
metadata.height, metadata.median_time_past);
431431
}
432432

433-
bool input::reserved_hash(hash_digest& out) const NOEXCEPT
433+
bool input::reserved_hash(hash_cref& out) const NOEXCEPT
434434
{
435-
if (!witness::is_reserved_pattern(get_witness().stack()))
435+
const auto& stack = get_witness().stack();
436+
if (!witness::is_reserved_pattern(stack))
436437
return false;
437438

438-
std::copy_n(get_witness().stack().front()->begin(), hash_size, out.begin());
439+
// Guarded by is_reserved_pattern.
440+
out = unsafe_array_cast<uint8_t, hash_size>(stack.front()->data());
439441
return true;
440442
}
441443

src/chain/output.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,17 @@ const chain::script::cptr& output::script_ptr() const NOEXCEPT
182182
// Methods.
183183
// ----------------------------------------------------------------------------
184184

185-
bool output::committed_hash(hash_digest& out) const NOEXCEPT
185+
bool output::committed_hash(hash_cref& out) const NOEXCEPT
186186
{
187187
const auto& ops = script_->ops();
188188
if (!script::is_commitment_pattern(ops))
189189
return false;
190190

191-
// The four byte offset for the witness commitment hash (bip141).
191+
// Offset four bytes for witness commitment head (bip141).
192+
const auto start = std::next(ops[1].data().data(), sizeof(witness_head));
192193

193-
// More efficient [] dereference is guarded above.
194-
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
195-
const auto start = std::next(ops[1].data().begin(), sizeof(witness_head));
196-
BC_POP_WARNING()
197-
198-
std::copy_n(start, hash_size, out.begin());
194+
// Guarded by is_commitment_pattern.
195+
out = unsafe_array_cast<uint8_t, hash_size>(start);
199196
return true;
200197
}
201198

0 commit comments

Comments
 (0)