fix(bip137): wallet script type bip137 header #866
Conversation
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (85.26%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## develop #866 +/- ##
===========================================
- Coverage 97.30% 97.06% -0.24%
===========================================
Files 83 85 +2
Lines 10798 10980 +182
===========================================
+ Hits 10507 10658 +151
- Misses 291 322 +31 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
e35223d to
4c95cf7
Compare
odudex
left a comment
There was a problem hiding this comment.
The header-byte fix is correct: single-sig P2SH-P2WPKH and P2WPKH wallets were
emitting legacy-message signatures with the P2PKH-compressed header byte (31-34)
instead of the BIP-137 script-type headers (35-38 / 39-42). Signing now routes
through bip137.sign, which preserves the recovery id and rebases the header on
the script type. Verified against the regenerated vectors:
- bc1qgl5... (P2WPKH): 32 -> 40 (39 + recid) OK
- 38CahkV... (P2SH-P2WPKH): 31 -> 35 (35 + recid) OK
- tb1qynp... (P2WPKH tnet): 32 -> 40 (39 + recid) OK
sign_at emits 31 + recid, so recovery_id = raw_header - 31 is correct and
the re-encoding only swaps the script-type base. Tests pass (5 passed).
Blocking: taproot message signing shouldn't stop working
This PR makes single-sig taproot (m/86') message signing fail. From a user's
perspective this is a regression / introduced bug, not a tightening: taproot
signing worked before and Sparrow verified the result as valid.
Mechanism: get_script_type_from_path("m/86'/...") returns "p2tr", which is
passed to bip137.sign -> build_header. build_header has no p2tr branch, so
it raises ValueError, which the menu catches and shows as an error screen.
Why it really was working (so this is a real loss of function, not removal of a
broken path):
- Krux signs the standard BIP-137 commitment with an ECDSA-recoverable
signature using the taproot internal key. - Lenient verifiers (Sparrow / Electrum-style) recover the pubkey and
reconstruct the address of the claimed type from it - for taproot they apply
the taproot tweak (same as script.p2tr) and the recovered+tweaked key matches
the bc1p... address. They effectively ignore the header's script-type bits. - So the signature genuinely proves control of the taproot key and verifies in
practice. It's just not a formally specified scheme (BIP-137 has no taproot;
BIP-322 is the eventual standard).
Required outcome: taproot message signing must keep working. Acceptable options:
- Keep the previous behavior - give build_header a p2tr branch that returns a
valid recoverable header so Krux keeps emitting the Sparrow-verifiable
signature (this is the no-regression path). The segwit header fix (#865) is
independent and does not require dropping taproot. - Implement proper BIP-322 signing for taproot.
What is NOT acceptable: shipping a version where taproot signing errors out.
Other required changes
-
Misleading error message in build_header.
"%s legacy sign not supported"implies the script type itself is
unsupported. p2tr is a supported wallet type; the limitation is that BIP-137
legacy message signing has no taproot scheme. If any reject path remains,
reword to make that clear and point at BIP-322. Also drop the trailing space
(currently shown verbatim asValueError('p2tr legacy sign not supported ')). -
Add the MIT license header to src/krux/bip137.py.
Every other src/krux/*.py file opens with the MIT block; this module starts
straight atP2PKH_HEADER = 31. -
Dead module-level constants. P2PKH_HEADER / P2SH_P2WPKH_HEADER / P2WPKH_HEADER
are defined but never used; build_header hardcodes the literals 31/35/39.
Use the constants or drop them so they cannot drift. -
Unreachable assertion in test_sign_message_p2tr_bip137.
Theassert ctx.input.wait_for_button.call_count == len(btn_seq)sits after
the raising call inside thewith pytest.raises(...)block, so it never runs.
Move it after the block or delete it. (This test should change anyway once
taproot signing is restored.)
Suggested (non-blocking)
- Commit says "add test vectors for bip137 module" but there are no direct unit
tests for bip137.py; coverage is only via the UI. The API-boundary guards in
build_header / sign are never exercised. Add tests/test_bip137.py.
This commit fix a P2PKH-compressed header byte (`31..34`) for legacy signed message for `singlesig` wallets with P2SH-P2WPKH. Also added support to bip322 (simple) message sign. Tested with sparrow. Fix selfcustody#865
What is this PR for?
Minor bug fix.
src/krux/pages/home_pages/sign_message_ui.pybip137.pyproduces a P2PKH-compressed header31for P2SH-P2WPKH and P2WPKH addresses. While signing correctly, this is not fully compliant with BIP-137. This PR add asrc/krux/bip137.py` module moving this logic with those fixes.Changes made to:
Did you build the code and tested on device?
What is the purpose of this pull request?