Skip to content

Commit e52a0f1

Browse files
authored
Merge pull request #1641 from evoskuil/master
Refactor is_invalid_witness_commitment, use cref to avoid copies.
2 parents 559063d + d2d1408 commit e52a0f1

7 files changed

Lines changed: 58 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

include/bitcoin/system/hash/functions.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ inline bool operator!=(const bc::system::hash_cref& left,
184184
{
185185
return !(left == right);
186186
}
187+
188+
inline bool operator!=(const bc::system::hash_cref& left,
189+
const bc::system::hash_digest& right) NOEXCEPT
190+
{
191+
return !(left.get() == right);
192+
}
187193
} // namespace std
188194

189195
/// Extend std and boost namespaces with djb2_hash.

src/chain/block.cpp

Lines changed: 36 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
//*****************************************************************************
@@ -851,6 +868,7 @@ code block::identify() const NOEXCEPT
851868
return error::block_success;
852869
}
853870

871+
// bip141 should be disabled when the node is not accepting witness data.
854872
code block::identify(const context& ctx) const NOEXCEPT
855873
{
856874
const auto bip141 = ctx.is_enabled(bip141_rule);
@@ -894,6 +912,7 @@ code block::check() const NOEXCEPT
894912
// median_time_past
895913

896914
// TODO: use of get_hash() in is_hash_limit_exceeded makes this thread unsafe.
915+
// bip141 should be disabled when the node is not accepting witness data.
897916
code block::check(const context& ctx) const NOEXCEPT
898917
{
899918
const auto bip141 = ctx.is_enabled(bip141_rule);

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: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace system {
3131
namespace chain {
3232

3333
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
34+
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
3435

3536
// This is a consensus critical value that must be set on reset.
3637
const uint64_t output::not_found = sighash_null_value;
@@ -182,20 +183,17 @@ const chain::script::cptr& output::script_ptr() const NOEXCEPT
182183
// Methods.
183184
// ----------------------------------------------------------------------------
184185

185-
bool output::committed_hash(hash_digest& out) const NOEXCEPT
186+
bool output::committed_hash(hash_cref& out) const NOEXCEPT
186187
{
187188
const auto& ops = script_->ops();
188189
if (!script::is_commitment_pattern(ops))
189190
return false;
190191

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

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());
195+
// Guarded by is_commitment_pattern.
196+
out = unsafe_array_cast<uint8_t, hash_size>(start);
199197
return true;
200198
}
201199

@@ -221,6 +219,7 @@ bool output::is_dust(uint64_t minimum_value) const NOEXCEPT
221219
return value_ < minimum_value && !script_->is_unspendable();
222220
}
223221

222+
BC_POP_WARNING()
224223
BC_POP_WARNING()
225224

226225
// JSON value convertors.

0 commit comments

Comments
 (0)