Looking at the coverage report for master on https://corecheck.dev, a number of error branches in descriptor.cpp are never exercised by any test. This PR adds CheckUnparsable vectors for each reachable one:
musig()
- unterminated expression:
tr(musig(00)→ "Invalid musig() expression" - invalid participant key
- invalid derivation path element (the
musig():-prefixed wrapping of the keypath error; the underlyingParseKeyPatherrors were already covered viapkh()/wpkh()) - participants with multipath derivations of mismatched lengths (
/<0;1>vs/<0;1;2>; themulti()and Miniscript variants of this error were covered, themusig()one was not)
Context restrictions
multi()insidetr(),multi_a()at top level, andaddr()/tr()/rawtr()/raw()insidesh()
Taptree structure errors
- exceeding the 128 nesting level limit (129
{s, built withstd::string(129, '{')) - missing
'}'after a right branch, missing','after a left branch, trailing garbage after a script expression and after the internal key
rawtr()
- invalid key.
00is used (rather than the truncated-valid-key pattern used elsewhere in this file) because in Taproot contexts a 32-byte string would parse as a valid x-only key.
Since CheckUnparsable asserts on the exact error message and each targeted branch produces a distinct one, a passing vector proves the corresponding branch executed.
One musig() parse error remains uncovered: "musig(): expected ',', got '%c'" (descriptor.cpp line ~2053). As far as I can tell it is unreachable: reaching it requires the leftover after a participant Expr() to start with '}' or ')', but a ')' inside the expression is rejected earlier by the flat Split() ("Too many ')'"), and a stray '}' always unbalances the enclosing Expr() first, truncating the musig span so that Func() fails with "Invalid musig() expression" instead. Attempting to rebalance with extra parentheses runs into one of the earlier checks in every combination I could construct.