qa: test a compact block with an invalid transaction

The current test to exercise a block with an invalid transaction actually
creates a block with an invalid coinbase witness, which is checked early and
for which MaybePunishNodeForBlock() is not called.

Add a test case with an invalid regular transaction, which will lead
CheckInputScripts to return a CONSENSUS error and MaybePunishNodeForBlock() to
be called, appropriately not disconnecting upon an invalid compact block. This
was until now untested as can be checked with the following diff:
```diff
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 0c4a89c44cb..d243fb88d4b 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1805,10 +1805,10 @@ void PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
     // The node is providing invalid data:
     case BlockValidationResult::BLOCK_CONSENSUS:
     case BlockValidationResult::BLOCK_MUTATED:
-        if (!via_compact_block) {
+        //if (!via_compact_block) {
             if (peer) Misbehaving(*peer, message);
             return;
-        }
+        //}
         break;
     case BlockValidationResult::BLOCK_CACHED_INVALID:
         {
```

Finally, note this failure is cached (unlike the malleated witness failure),
which will be used in the following commits.
This commit is contained in:
Antoine Poinsot
2025-07-28 17:28:02 -04:00
parent d6c37b28a7
commit f12d8b104e

View File

@@ -54,6 +54,7 @@ from test_framework.script import (
CScript,
OP_DROP,
OP_TRUE,
OP_RETURN,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -726,6 +727,23 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_not_equal(node.getbestblockhash(), block.hash_hex)
test_node.sync_with_ping()
# Re-establish a proper witness commitment with the coinbase witness, but
# invalidate the last tx in the block.
block.vtx[4].vin[0].scriptSig = CScript([OP_RETURN])
block.hashMerkleRoot = block.calc_merkle_root()
add_witness_commitment(block)
block.solve()
# This will lead to a consensus failure for which we also won't be disconnected but which
# will be cached.
comp_block.initialize_from_block(block, prefill_list=list(range(len(block.vtx))), use_witness=True)
msg = msg_cmpctblock(comp_block.to_p2p())
test_node.send_and_ping(msg)
# The tip still didn't advance.
assert_not_equal(node.getbestblockhash(), block.hash_hex)
test_node.sync_with_ping()
# Helper for enabling cb announcements
# Send the sendcmpct request and sync headers
def request_cb_announcements(self, peer):