Skip to content

Add MuSig2 + Silent Payments - #683

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

Add MuSig2 + Silent Payments#683
macgyver13 wants to merge 32 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

@macgyver13
macgyver13 force-pushed the musig_silentpayments branch 2 times, most recently from a77ef4d to 0cf7efd Compare July 13, 2026 18:33
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 127 character limit for silent payments v0
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
Discourage broadcasting silent payments directly from the device. The wallet
coordinator should be responsible for verifying the PSBT and finalizing for 
broadcast.
- Users can restore the default ColdCard behavior by using the Danger Zone -> 
SP Final Export setting.
- USB API was not modified.
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
@macgyver13
macgyver13 force-pushed the musig_silentpayments branch from 0cf7efd to 24c49c7 Compare July 29, 2026 20:30
@doc-hex doc-hex added the new-feature New feature proposal. label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-feature New feature proposal.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants