|
1 | 1 | import assert from 'assert'; |
2 | 2 |
|
3 | 3 | import * as utxolib from '@bitgo/utxo-lib'; |
| 4 | +import { bip322 as coreBip322 } from '@bitgo/utxo-core'; |
4 | 5 | import { bip322 as wasmBip322, fixedScriptWallet, BIP32, type Triple } from '@bitgo/wasm-utxo'; |
5 | 6 |
|
6 | 7 | import { |
@@ -401,4 +402,67 @@ describe('BIP322', function () { |
401 | 402 | ); |
402 | 403 | }); |
403 | 404 | }); |
| 405 | + |
| 406 | + describe('utxolib verification stack - wasm-utxo respects input.sighashType', function () { |
| 407 | + // This test verifies that wasm-utxo correctly respects the input.sighashType field |
| 408 | + // when creating musig2 partial signatures. |
| 409 | + // |
| 410 | + // Previously (before fix), wasm-utxo would always create signatures with SIGHASH_DEFAULT (0) |
| 411 | + // regardless of the input.sighashType field, causing validation to fail. |
| 412 | + // |
| 413 | + // Now (after fix), wasm-utxo reads input.sighashType and creates signatures with the |
| 414 | + // correct sighash type, allowing validation to succeed. |
| 415 | + |
| 416 | + it('should validate signatures when wasm-utxo respects input.sighashType', function () { |
| 417 | + const seed = 'p2trMusig2_sighash_test'; |
| 418 | + const { xprivs } = createTestWalletKeys(seed); |
| 419 | + |
| 420 | + // Create utxolib RootWalletKeys for utxo-core PSBT construction |
| 421 | + const utxolibRootWalletKeys = new utxolib.bitgo.RootWalletKeys(utxolib.testutil.getKeyTriple(seed)); |
| 422 | + |
| 423 | + // p2trMusig2 external chain code |
| 424 | + const chain = utxolib.bitgo.getExternalChainCode('p2trMusig2'); |
| 425 | + const index = 0; |
| 426 | + const messageText = 'BIP322 sighash test'; |
| 427 | + |
| 428 | + // Create BIP322 PSBT using utxo-core |
| 429 | + const psbt = coreBip322.createBaseToSignPsbt(utxolibRootWalletKeys, utxolib.networks.bitcoin); |
| 430 | + coreBip322.addBip322InputWithChainAndIndex(psbt, messageText, utxolibRootWalletKeys, { chain, index }); |
| 431 | + |
| 432 | + // Note: utxo-core sets sighashType: Transaction.SIGHASH_ALL (1) for BIP322 inputs |
| 433 | + const SIGHASH_ALL = 1; |
| 434 | + assert.strictEqual(psbt.data.inputs[0].sighashType, SIGHASH_ALL); |
| 435 | + |
| 436 | + // Convert to wasm-utxo PSBT for cosigning |
| 437 | + const wasmPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(psbt.toBuffer(), 'btc'); |
| 438 | + |
| 439 | + // Generate musig2 nonces and sign with wasm-utxo |
| 440 | + // wasm-utxo now respects input.sighashType and creates signatures with SIGHASH_ALL |
| 441 | + const userKey = BIP32.fromBase58(xprivs[0]); |
| 442 | + const bitgoKey = BIP32.fromBase58(xprivs[2]); |
| 443 | + |
| 444 | + wasmPsbt.generateMusig2Nonces(userKey); |
| 445 | + wasmPsbt.generateMusig2Nonces(bitgoKey); |
| 446 | + wasmPsbt.sign(0, userKey); |
| 447 | + wasmPsbt.sign(0, bitgoKey); |
| 448 | + |
| 449 | + // Convert back to utxolib PSBT for validation |
| 450 | + const signedPsbt = utxolib.bitgo.createPsbtFromBuffer( |
| 451 | + Buffer.from(wasmPsbt.serialize()), |
| 452 | + utxolib.networks.bitcoin |
| 453 | + ); |
| 454 | + |
| 455 | + // Validation should succeed because wasm-utxo now creates signatures |
| 456 | + // with the correct sighash type (SIGHASH_ALL) matching input.sighashType |
| 457 | + const validationResult = utxolib.bitgo.getSignatureValidationArrayPsbt(signedPsbt, utxolibRootWalletKeys); |
| 458 | + |
| 459 | + // Verify that both user (index 0) and bitgo (index 2) signatures are valid |
| 460 | + assert.strictEqual(validationResult.length, 1); |
| 461 | + const [inputIndex, sigValidation] = validationResult[0]; |
| 462 | + assert.strictEqual(inputIndex, 0); |
| 463 | + assert.strictEqual(sigValidation[0], true, 'user signature should be valid'); |
| 464 | + assert.strictEqual(sigValidation[1], false, 'backup signature should not be present'); |
| 465 | + assert.strictEqual(sigValidation[2], true, 'bitgo signature should be valid'); |
| 466 | + }); |
| 467 | + }); |
404 | 468 | }); |
0 commit comments