Add a malformed-proof test case to rpc_txoutproof.py that removes the traversal bits from an otherwise valid CMerkleBlock proof and checks that verifytxoutproof rejects it.
The existing test already covers one tweaked invalid proof, but it still preserves traversal metadata in the partial merkle tree.
This case is intended to cover a different invalid-proof class: malformed proofs where the traversal metadata itself is missing or unusable.
In CPartialMerkleTree, the tree is encoded via a depth-first traversal that stores one flag bit per visited node. Those flag bits are part of the serialized format, and decoding consumes them together with the hashes to reconstruct the matched path (src/merkleblock.h). That means vBits is not incidental metadata, but part of the proof structure required for decoding. If those traversal bits are missing, the partial merkle tree cannot be decoded correctly.
This is also an explicitly validated failure mode in the implementation. ExtractMatches() requires at least one bit per node in the partial tree, and at least one node per hash, rejecting cases where vBits.size() < vHash.size() (src/merkleblock.cpp). So this is not just an artificial mutation, but a concrete malformed-proof class that the decoder is expected to reject.
This test removes vBits entirely rather than truncating them partially on purpose. A partially truncated vBits vector can be ambiguous as a test input, because decoding consumes bits as needed by the traversal in TraverseAndExtract(), and the final validation only checks that all bits were consumed except for the byte padding introduced by serialization in ExtractMatches() (src/merkleblock.cpp). Depending on the proof shape, removing only a suffix of bits may still leave a proof decodable. By contrast, emptying vBits in this otherwise valid proof makes the malformed input unambiguous and triggers the rejection path deterministically.
The goal of this test is to cover that malformed-proof rejection path through verifytxoutproof at the functional/RPC level. This adds coverage that the RPC-facing path handles this invalid proof class correctly.
Test
python3 -m ruff check test/functional/rpc_txoutproof.pypython3 -m py_compile test/functional/rpc_txoutproof.pypython3 build/test/functional/test_runner.py rpc_txoutproof.py