Skip to content

Commit 9515d04

Browse files
OttoAllmendingerllm-git
andcommitted
feat(utxo-lib): add cross-validation tests for MuSig2 implementation
Add Python test script that validates the TypeScript MuSig2 implementation against the BIP-327 reference implementation. The tests use JSON fixtures generated from the TypeScript test suite to ensure both implementations produce identical results at every step of the MuSig2 protocol. Validation includes key aggregation, taproot tweaking, deterministic nonce generation, partial signing, signature verification, and aggregation. Tests use fixed session IDs to ensure bit-for-bit identical outputs between both implementations. Issue: BTC-2652 Co-authored-by: llm-git <[email protected]>
1 parent a9a5880 commit 9515d04

14 files changed

+1091
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__/

modules/utxo-lib/bip-0327/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,37 @@ The test suite runs:
171171
4. BitGo legacy p2tr tests
172172
5. BitGo standard p2trMusig2 tests
173173

174+
### TypeScript Cross-Validation Tests
175+
176+
The `test_typescript_fixtures.py` script validates that the Python reference implementation produces identical results to the TypeScript implementation:
177+
178+
```bash
179+
cd modules/utxo-lib/bip-0327
180+
python3 test_typescript_fixtures.py
181+
```
182+
183+
These tests read JSON fixtures from the `musig2/` directory, which are generated by the TypeScript test suite in `test/bitgo/psbt/Musig2Methods.ts`. The fixtures contain:
184+
185+
- **createTapInternalKey.json** - Key aggregation results
186+
- **createTapOutputKey.json** - Tweaked aggregate key results
187+
- **createTapTweak.json** - Taproot tweak computation
188+
- **createAggregateNonce.json** - Nonce aggregation results
189+
- **createMusig2SigningSession.json** - Session context creation
190+
- **musig2PartialSignAndVerify.json** - Partial signature verification
191+
- **musig2AggregateSigs.json** - Signature aggregation and final verification
192+
- **fullSigningFlow.json** - Complete end-to-end signing workflow
193+
194+
The cross-validation tests ensure that:
195+
1. Key aggregation produces identical aggregate public keys
196+
2. Taproot tweaking produces identical output keys
197+
3. Deterministic nonce generation produces identical nonces (using session IDs `0x0101...` and `0x0202...`)
198+
4. Partial signature generation produces identical signatures
199+
5. Partial signature verification works identically
200+
6. Signature aggregation produces identical results
201+
7. Final Schnorr signature verification succeeds in both implementations
202+
203+
The tests use fully deterministic nonce generation with fixed session IDs to ensure bit-for-bit identical outputs between Python and TypeScript. This provides strong confidence that the TypeScript and Python implementations are compatible and produce identical cryptographic outputs at every step of the MuSig2 protocol.
204+
174205
## References
175206

176207
- [BIP327 Specification](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)

modules/utxo-lib/bip-0327/test_typescript_fixtures.py

Lines changed: 412 additions & 0 deletions
Large diffs are not rendered by default.

modules/utxo-lib/bip-0327/tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ set -e
55
cd "$(dirname "$0")"
66
mypy --no-error-summary reference.py
77
python3 reference.py
8+
python3 test_typescript_fixtures.py
89
python3 gen_vectors_helper.py > /dev/null
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../test/bitgo/fixtures/musig2
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"inputs": {
3+
"pubNonces": [
4+
"027389d7aa456cbe418467f42928601462d77000c3b260e5127f1111b57588b35e036f13a501e1708aa7288a313d71975324e0de1d129acff61e40e1c96ae5976c29",
5+
"02949960a953f58a5bad40d8c6161bac10a52a971a37cfe858ba4a1452524b1691026dca4ba06a76389621d0d5a6965976120eb9dac6b861cf32d674c65e080a7c13"
6+
]
7+
},
8+
"output": {
9+
"aggregateNonce": "0233086bdf4150163179ef61beed827f55b073563e8bde58755ed8bcddc00635a902418bed8a65d3ee02007694fb18b5a1ec202d8b69ece07e2d54dd193ef7373b88"
10+
}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"inputs": {
3+
"pubNonces": [
4+
"027389d7aa456cbe418467f42928601462d77000c3b260e5127f1111b57588b35e036f13a501e1708aa7288a313d71975324e0de1d129acff61e40e1c96ae5976c29",
5+
"02949960a953f58a5bad40d8c6161bac10a52a971a37cfe858ba4a1452524b1691026dca4ba06a76389621d0d5a6965976120eb9dac6b861cf32d674c65e080a7c13"
6+
],
7+
"txHash": "d11d3b06a83bb14c95fad947da2192ad9f6faee3c0fb36ad197a05f943aa7acd",
8+
"pubKeys": [
9+
"03e9fc8f605f59d6a4f5ceb0d27852151013f2bf1965f9adf44ce9ba9821c7f106",
10+
"030710ec162d2199bb828e5d8760f16d65412bbb2c85ec13fe027f77d83a349c62"
11+
],
12+
"internalPubKey": "6d767a69e03d4611485407d777711ee4d1321a84cf82f591db0cd3e179c49821",
13+
"tapTreeRoot": "63a3684763dca2d4bb5d412d01f5ce73214af3d7816b52d7ad549562cc4cd890"
14+
},
15+
"output": {
16+
"sessionKey": {
17+
"aggNonce": "0233086bdf4150163179ef61beed827f55b073563e8bde58755ed8bcddc00635a902418bed8a65d3ee02007694fb18b5a1ec202d8b69ece07e2d54dd193ef7373b88",
18+
"msg": "d11d3b06a83bb14c95fad947da2192ad9f6faee3c0fb36ad197a05f943aa7acd",
19+
"publicKey": "04e5406bdcc45b02e86b30c446fd57375e8bfc8144238d61033d353c8a3641a43d5bc152f290f28255baf6bf18c5de0d67f81921fc4e1c05c3c5625eb7784eab01"
20+
}
21+
}
22+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"inputs": {
3+
"pubKeys": [
4+
"03e9fc8f605f59d6a4f5ceb0d27852151013f2bf1965f9adf44ce9ba9821c7f106",
5+
"030710ec162d2199bb828e5d8760f16d65412bbb2c85ec13fe027f77d83a349c62"
6+
]
7+
},
8+
"output": {
9+
"tapInternalKey": "6d767a69e03d4611485407d777711ee4d1321a84cf82f591db0cd3e179c49821"
10+
}
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"inputs": {
3+
"internalPubKey": "6d767a69e03d4611485407d777711ee4d1321a84cf82f591db0cd3e179c49821",
4+
"tapTreeRoot": "63a3684763dca2d4bb5d412d01f5ce73214af3d7816b52d7ad549562cc4cd890"
5+
},
6+
"output": {
7+
"tapOutputKey": "e5406bdcc45b02e86b30c446fd57375e8bfc8144238d61033d353c8a3641a43d"
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"inputs": {
3+
"tapInternalKey": "6d767a69e03d4611485407d777711ee4d1321a84cf82f591db0cd3e179c49821",
4+
"tapMerkleRoot": "63a3684763dca2d4bb5d412d01f5ce73214af3d7816b52d7ad549562cc4cd890"
5+
},
6+
"output": {
7+
"tapTweak": "c990a77a31b07eea9d1a616bfe80bd33ba2a7e2a22db0e900a168d87a5e01a2c"
8+
}
9+
}

0 commit comments

Comments
 (0)