mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-08-25 20:51:33 +02:00
Merge bitcoin/bitcoin#33105: validation: detect witness stripping without re-running Script checks
27aefac425
validation: detect witness stripping without re-running Script checks (Antoine Poinsot)2907b58834
policy: introduce a helper to detect whether a transaction spends Segwit outputs (Antoine Poinsot)eb073209db
qa: test witness stripping in p2p_segwit (Antoine Poinsot) Pull request description: Since it was introduced in4eb515574e
(#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 for every single input 3 times. 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 and 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 PR would always consider it witness stripped. h/t AJ: this essentially implements a variant of https://github.com/bitcoin/bitcoin/pull/33066#issuecomment-3135258539. ACKs for top commit: sipa: re-ACK27aefac425
Crypt-iQ: re-ACK27aefac425
glozow: reACK27aefac425
Tree-SHA512: 70cf76b655b52bc8fa2759133315a3f11140844b6b80d9de3c95f592050978cc01a87bd2446e3a9c25cc872efea7659d6da3337b1a709511771fece206e9f149
This commit is contained in:
@@ -693,6 +693,12 @@ class SegWitTest(BitcoinTestFramework):
|
||||
expected_msgs=[spend_tx.txid_hex, 'was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)']):
|
||||
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
|
||||
|
||||
# The transaction was detected as witness stripped above and not added to the reject
|
||||
# filter. Trying again will check it again and result in the same error.
|
||||
with self.nodes[0].assert_debug_log(
|
||||
expected_msgs=[spend_tx.txid_hex, 'was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)']):
|
||||
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False)
|
||||
|
||||
# Try to put the witness script in the scriptSig, should also fail.
|
||||
spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a'])
|
||||
with self.nodes[0].assert_debug_log(
|
||||
@@ -1245,6 +1251,13 @@ class SegWitTest(BitcoinTestFramework):
|
||||
test_transaction_acceptance(self.nodes[0], self.test_node, tx2, with_witness=True, accepted=True)
|
||||
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=False)
|
||||
|
||||
# Now do the opposite: strip the witness entirely. This will be detected as witness stripping and
|
||||
# the (w)txid won't be added to the reject filter: we can try again and get the same error.
|
||||
tx3.wit.vtxinwit[0].scriptWitness.stack = []
|
||||
reason = "was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)"
|
||||
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=False, accepted=False, reason=reason)
|
||||
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=False, accepted=False, reason=reason)
|
||||
|
||||
# Get rid of the extra witness, and verify acceptance.
|
||||
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
|
||||
# Also check that old_node gets a tx announcement, even though this is
|
||||
|
Reference in New Issue
Block a user