Skip to content

Add MuSig2 + Silent Payments#683

Draft
macgyver13 wants to merge 30 commits into
Coldcard:new_edgefrom
macgyver13:musig_silentpayments
Draft

Add MuSig2 + Silent Payments#683
macgyver13 wants to merge 30 commits into
Coldcard:new_edgefrom
macgyver13:musig_silentpayments

Conversation

@macgyver13

@macgyver13 macgyver13 commented Jul 1, 2026

Copy link
Copy Markdown

Builds on #587 and introduces MuSig2 + Silent Payments signing.

Welcome review and feedback on the concept.

Signing Flow

A MuSig2 taproot key-spend wallet (e.g. 3-of-3 tr(musig(A,B,C)/0/*)) can now pay
Silent Payments recipients. Because the aggregate secret key behind the taproot
output key Q is split across all signers, no single party can compute the BIP-352
ECDH shared secret alone. Each signer contributes a partial ECDH share
(share_i = a_i * B_scan) plus a DLEQ proof, carried in two new provisional PSBT
fields - PSBT_IN_MUSIG2_PARTIAL_ECDH_SHARE and PSBT_IN_MUSIG2_PARTIAL_DLEQ.

To keep PSBT rounds to a minimum, MuSig2 nonce generation and ECDH share collection
are combined into the first round:

  • Round 1 - Contribute - Partial ECDH share, DLEQ proof, and fresh MuSig2 pubnonce from every signer (last signer computes Silent Payment output scripts from the aggregated ECDH share).
  • Round 2 - Sign - Each signer independently re-verifies Silent Payment output scripts then contributes partial MuSig2 signature completing the MuSig2 aggregation and PSBT finalization as normal.

Find more details on the signing flow here.
MuSig2 + Silent Payments documentation updated.

Testing

Regression

pytest test_bip352_vectors.py PASS
pytest test_bip375_vectors.py PASS
pytest test_silentpayments.py PASS

python run_sim_tests.py --multiproc --num-proc 6 -m test_musig2.py -m test_multisig.py
 FAILED test_multisig.py::test_ms_addr_explorer[14-M_N0-1000-multi-True]
  err_Miniscript wallet not found
 FAILED test_musig2.py::test_musig[True-False-0-1] 
  Coldcard Error: buffer too small
 FAILED test_musig2.py::test_musig[True-False-1-1]
  Coldcard Error: buffer too small

New

pytest test_musig2_silentpayments.py
test_musig2_silentpayments.py::test_musig2_sp_round1 PASSED
test_musig2_silentpayments.py::test_musig2_sp_full_flow PASSED
test_musig2_silentpayments.py::test_musig2_sp_round2_tampered_recipient PASSED
pytest test_musig2_sp_signers.py
test_musig2_sp_signers.py::test_musig2_sp_three_hardware_signers[final-txn] PASSED
test_musig2_sp_signers.py::test_musig2_sp_three_hardware_signers[signed-psbt] PASSED

external/ckcc-protocol
- Add bip375 and bip376 constants
external/libngu
- Expose ec_pubkey_combine
- Expose ec_pubkey_tweak_mul
- Add bip352_encode
Add bip352 crypto primitives / helpers
Add bip352 tagged hashes
Add bip374 tagged hashes
Add dleq bip374 reference generate_dleq_proof, verify_dleq_proof functions
Extend psbt handling with silent payment specific functions
Incorporate validation functions from bip375 test vectors
- psbt_structure, input_eligibility, ecdh_coverage
Add private key derivation helper functions
Only store key_data for short_values - remove key_type
Only serialize PSBT_OUT_SCRIPT if self.script has a value
Adjust v2 script assert to not required if sp_v0_info is present
Provides silent payments testing framework with access to MockPSBT
Use devtest/verify_sp_outputs.py for simulator testing 
Add testing/test_bip375_vectors.py and testing/bip375_test_vectors.json
- Correct input eligibility ecdh share assumptions
Add devtest/unit_silentpayments.py to perform unit testing via simulator
Mirror psbt fields in testing/psbt.py
Add testing/test_bip352_vectors.py and testing/bip352_test_vectors.json 
- Verify SP sending and receiving test vectors
Integrate preview silent payment address as output in auth:interact
Integrate silent payments in signing workflow
Add sp_hrp property to chains.py
Add silent payments fields to psbt.py
Add sp_spend support to _derive_input_privkey
Validate bip376 spend key derivation
Create end-to-end tests for SP in testing/test_silentpayments.py
- Expose foreign_mk for partial ownership coverage test
Integrate SP spend into psbt functions: consider_inputs, determine_my_signing_key, sign_it
- Synthesize parsed_subpaths from PSBT_IN_SPEND_BIP32_DERIVATION
- Set input.sp_idxs to placeholder to synthesize input is_owned
- Implement SP input signing
Refactor _compute_and_store_ecdh_shares and _derive_input_privkey to leverage input.sp_idxs
Add test to validate 'Contribute Shares?' UX
Simplify SP preview and signing code paths
Add silent payments label change tests
- Detect presence or absence of 'Change back:' in UI story
Add multi-signer incomplete coverage scenario tests
Verify SP spend path was derived from m/352h
Add signature verification for taproot inputs and SP outputs
Include key-value pairs: spscan, deriv, name, xfp, key_exp in bip352 dictionary
Add bip352 test block to test_export_coldcard
Support bech32m 1023 character limit for silent payments
Mixed inputs SP spend and P2TR
Reject non-SIGHASH_ALL
Reject psbt v0 send to SP output
Remove MockPSBT from verify_sp_outputs
Test test_sp_spend_to_labeled_change_address should sign transaction
Replace _serialize_psbt with read_psbt
Replace single use compute_foreign_share with generate_dleq_proof
Remove unnecessary int-byte conversions in test_bip352_vectors.py
Convert validate_bip376_spend to validate_silent_payment_inputs
Use accounts to validate sp spend material and mark output.is_change
Introduce reusable sp_derive_path function
Fix change detection issue raised by previous test hygiene commit
- Signing was restored for test_sp_spend_labeled_change_detected
Fix psbt exception in consider_outputs with sp change
- Add apply_label_to_spend_key primitive
- Match spend public key to labeled spend public key for change

New tests:
sp spend to non sp output
sp spend rejection tests - unknown account, incorrect sp spend derivation path
sp change output with foreign keys is allowed but displays warning
Changed test:
sp spend to label change - fix label spend key derivation, use sp input
…r multiple outputs

Per BIP-375: sort by spend key then output index in ascending order for each scan key group to determine ordering of the k value
- Fix compute_silent_payment_output_scripts to match spec 
- Update bip375_test_vectors.json to version 1.1.1
Adds provisional MUSIG2_PARTIAL_* fields.
Generate nonce message for SP outputs - required since scriptPubKey is 
not defined on musig round 1.

taproot sighash computation remains unaffected for musig round 2.
Gracefully handle the 3-element value parse_taproot_subpaths caches
when the same input is parsed more than once (MuSig2 + SP).
auth:
- Signing UX: prompt the user to contribute shares, run round 1 nonce generation.

psbt:
- Add musig_keyagg_context: assemble the keyagg cache and derive the BIP-327
  combine factors (MusigEcdhFactors).
- Allow musig sp inputs round 1 sign_it despite incomplete SP coverage.

serializations:
- determine_my_change - get_address returns (None, None) when scriptPubKey is None,  
  no longer errors on musig sp inputs before output scripts are computed.

silentpayments:
- Contribute partial shares and DLEQ proof bound to the account-level participant 
 key (_contribute_musig_ecdh_shares).
- Aggregate shares via KeyAgg coefficients and the input's combine factors (_musig_aggregate_shares, _musig_keyagg_coefficient, _musig_sp_ecdh_factors, _musig_input_ecdh_share).

Add bip327 tagged hashes

Add musig2 silent payments round 1 test and fixtures.
Add cosigner contrib test fixture for simulating round 2 partial signature 
- 1 simulator/signer in test_musig2_sp_full_flow, the other 2 participants 
in 3 of 3 provide contributions from coordinator.
test_musig2_sp_three_hardware_signers coordinates three separate simulator processes to compute silent payments outputs and sign a psbt paying multiple recipients through two rounds.


multi signer refactor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant