Skip to content

Commit 27aefac

Browse files
committed
validation: detect witness stripping without re-running Script checks
Since it was introduced in 4eb5155 (bitcoin#18044), the detection of a stripped witness relies on running the Script checks 3 times. In the worst case, this consists in running Script validation 3 times for every single input. Detection of a stripped witness is necessary because in this case wtxid==txid, and the transaction's wtxid must not be added to the reject filter or it could allow a malicious peer to interfere with txid-based orphan resolution as used in 1p1c package relay. However it is not necessary to run Script validation to detect a stripped witness (much less so doing it 3 times in a row). There are 3 types of witness program: defined program types (Taproot, P2WPKH, P2WSH), undefined types, and the Pay-to-anchor carve-out. For defined program types, Script validation with an empty witness will always fail (by consensus). For undefined program types, Script validation is always going to fail regardless of the witness (by standardness). For P2A, an empty witness is never going to lead to a failure. Therefore it holds that we can always detect a stripped witness without re-running Script validation. However this might lead to more "false positives" (cases where we return witness stripping for an otherwise invalid transaction) than the existing implementation. For instance a transaction with one P2PKH input with an invalid signature and one P2WPKH input with its witness stripped. The existing implementation would treat it as consensus invalid while the implementation in this commit would always consider it witness stripped.
1 parent 2907b58 commit 27aefac

File tree

1 file changed

+2
-7
lines changed

1 file changed

+2
-7
lines changed

src/validation.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,13 +1254,8 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
12541254
// Check input scripts and signatures.
12551255
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
12561256
if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
1257-
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
1258-
// need to turn both off, and compare against just turning off CLEANSTACK
1259-
// to see if the failure is specifically due to witness validation.
1260-
TxValidationState state_dummy; // Want reported failures to be from first CheckInputScripts
1261-
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata, GetValidationCache()) &&
1262-
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
1263-
// Only the witness is missing, so the transaction itself may be fine.
1257+
// Detect a failure due to a missing witness so that p2p code can handle rejection caching appropriately.
1258+
if (!tx.HasWitness() && SpendsNonAnchorWitnessProg(tx, m_view)) {
12641259
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
12651260
state.GetRejectReason(), state.GetDebugMessage());
12661261
}

0 commit comments

Comments
 (0)