Skip to content

Commit 0466b8d

Browse files
committed
script: require empty witness for P2A spends
1 parent 2f5c422 commit 0466b8d

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

src/script/interpreter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1999,7 +1999,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
19991999
}
20002000
return set_success(serror);
20012001
}
2002-
} else if (!is_p2sh && CScript::IsPayToAnchor(witversion, program)) {
2002+
} else if (stack.empty() && !is_p2sh && CScript::IsPayToAnchor(witversion, program)) {
20032003
return true;
20042004
} else {
20052005
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {

test/functional/feature_uasf_reduced_data.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
add_witness_commitment,
8080
)
8181
from test_framework.script_util import (
82+
PAY_TO_ANCHOR,
8283
script_to_p2wsh_script,
8384
script_to_p2sh_script,
8485
)
@@ -856,6 +857,65 @@ def create_block_with_generation_output(script_pubkey):
856857
assert_equal(result, 'bad-txns-vout-script-toolarge')
857858
self.log.info(" ✓ Generation tx with 84-byte OP_RETURN output rejected")
858859

860+
def test_p2a_witness_rejected(self):
861+
"""Test that P2A (PayToAnchor) spends with non-empty witness are rejected."""
862+
self.log.info("Testing P2A non-empty witness rejection...")
863+
node = self.nodes[0]
864+
865+
# Create a P2A output (4 bytes, within the 34-byte limit)
866+
p2a_funding = self.create_test_transaction(PAY_TO_ANCHOR)
867+
p2a_funding.rehash()
868+
p2a_value = p2a_funding.vout[0].nValue
869+
870+
block_height = node.getblockcount() + 1
871+
block = create_block(int(node.getbestblockhash(), 16), create_coinbase(block_height), int(node.getblockheader(node.getbestblockhash())['time']) + 1)
872+
block.vtx.append(p2a_funding)
873+
add_witness_commitment(block)
874+
block.solve()
875+
assert_equal(node.submitblock(block.serialize().hex()), None)
876+
self.log.info(" P2A output created")
877+
878+
# Test 1: Spend with 100 KB of arbitrary witness data (must be rejected)
879+
self.log.info(" Test: P2A spend with large arbitrary witness (should be rejected)")
880+
arbitrary_data = b'\xab' * 100_000
881+
882+
p2a_spend = CTransaction()
883+
p2a_spend.vin = [CTxIn(COutPoint(int(p2a_funding.rehash(), 16), 0))]
884+
p2a_spend.vout = [CTxOut(p2a_value - 1000, CScript([OP_0, hash160(b'\x01' * 33)]))]
885+
p2a_spend.wit.vtxinwit = [CTxInWitness()]
886+
p2a_spend.wit.vtxinwit[0].scriptWitness.stack = [arbitrary_data]
887+
p2a_spend.rehash()
888+
889+
block_height = node.getblockcount() + 1
890+
block_bad = create_block(int(node.getbestblockhash(), 16), create_coinbase(block_height), int(node.getblockheader(node.getbestblockhash())['time']) + 1)
891+
block_bad.vtx.append(p2a_spend)
892+
add_witness_commitment(block_bad)
893+
block_bad.solve()
894+
895+
result = node.submitblock(block_bad.serialize().hex())
896+
assert result is not None
897+
assert_equal(node.getblockcount(), block_height - 1)
898+
self.log.info(f" ✓ P2A spend with 100 KB witness rejected ({result})")
899+
900+
# Test 2: Spend with empty witness (must still be accepted)
901+
self.log.info(" Test: P2A spend with empty witness (should be accepted)")
902+
p2a_spend_empty = CTransaction()
903+
p2a_spend_empty.vin = [CTxIn(COutPoint(int(p2a_funding.rehash(), 16), 0))]
904+
p2a_spend_empty.vout = [CTxOut(p2a_value - 1000, CScript([OP_0, hash160(b'\x01' * 33)]))]
905+
p2a_spend_empty.wit.vtxinwit = [CTxInWitness()]
906+
p2a_spend_empty.wit.vtxinwit[0].scriptWitness.stack = []
907+
p2a_spend_empty.rehash()
908+
909+
block_height = node.getblockcount() + 1
910+
block_good = create_block(int(node.getbestblockhash(), 16), create_coinbase(block_height), int(node.getblockheader(node.getbestblockhash())['time']) + 1)
911+
block_good.vtx.append(p2a_spend_empty)
912+
add_witness_commitment(block_good)
913+
block_good.solve()
914+
915+
assert_equal(node.submitblock(block_good.serialize().hex()), None)
916+
assert_equal(node.getblockcount(), block_height)
917+
self.log.info(" ✓ P2A spend with empty witness accepted")
918+
859919
def run_test(self):
860920
self.init_test()
861921

@@ -869,6 +929,7 @@ def run_test(self):
869929
self.test_op_success_rejection()
870930
self.test_op_if_notif_rejection()
871931
self.test_mandatory_flags_cannot_be_bypassed()
932+
self.test_p2a_witness_rejected()
872933

873934
self.log.info("All UASF-ReducedData tests completed")
874935

0 commit comments

Comments
 (0)