diff --git a/modules/abstract-utxo/package.json b/modules/abstract-utxo/package.json index 530d50b9d9..d2f32b9685 100644 --- a/modules/abstract-utxo/package.json +++ b/modules/abstract-utxo/package.json @@ -66,7 +66,7 @@ "@bitgo/utxo-core": "^1.36.0", "@bitgo/utxo-lib": "^11.22.0", "@bitgo/utxo-ord": "^1.29.0", - "@bitgo/wasm-utxo": "^4.1.0", + "@bitgo/wasm-utxo": "^4.7.0", "@types/lodash": "^4.14.121", "@types/superagent": "4.1.15", "bignumber.js": "^9.0.2", diff --git a/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts b/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts index 95fcfae2a5..304e012740 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts @@ -7,7 +7,7 @@ import { BIP32, bip32 } from '@bitgo/wasm-utxo'; import debugLib from 'debug'; import { UtxoCoinName } from '../../names'; -import type { Unspent, WalletUnspent } from '../../unspent'; +import { isWalletUnspent, type Unspent } from '../../unspent'; import { toUtxolibBIP32 } from '../../wasmUtil'; import { getReplayProtectionAddresses } from './replayProtection'; @@ -15,10 +15,12 @@ import { InputSigningError, TransactionSigningError } from './SigningError'; const debug = debugLib('bitgo:v2:utxo'); -const { isWalletUnspent, signInputWithUnspent, toOutput } = utxolib.bitgo; +const { signInputWithUnspent, toOutput } = utxolib.bitgo; type RootWalletKeys = utxolib.bitgo.RootWalletKeys; +const UTXOLIB_VALID_CHAIN_CODES = new Set([0, 1, 10, 11, 20, 21, 30, 31, 40, 41] as const); + /** * Sign all inputs of a wallet transaction and verify signatures after signing. * Collects and logs signing errors and verification errors, throws error in the end if any of them @@ -71,8 +73,21 @@ export function signAndVerifyWalletTransaction( if (!isWalletUnspent(unspent)) { return InputSigningError.expectedWalletUnspent(inputIndex, null, unspent); } + if (!UTXOLIB_VALID_CHAIN_CODES.has(unspent.chain as utxolib.bitgo.ChainCode)) { + return new InputSigningError( + inputIndex, + null, + unspent, + new Error(`Chain code ${unspent.chain} is not supported for legacy signing`) + ); + } try { - signInputWithUnspent(txBuilder, inputIndex, unspent as WalletUnspent, walletSigner); + signInputWithUnspent( + txBuilder, + inputIndex, + unspent as unknown as utxolib.bitgo.WalletUnspent, + walletSigner + ); debug('Successfully signed input %d of %d', inputIndex + 1, unspents.length); } catch (e) { return new InputSigningError(inputIndex, null, unspent, e); @@ -96,10 +111,20 @@ export function signAndVerifyWalletTransaction( if (!isWalletUnspent(unspent)) { return InputSigningError.expectedWalletUnspent(inputIndex, null, unspent); } - const walletUnspent = unspent as WalletUnspent; + if (!UTXOLIB_VALID_CHAIN_CODES.has(unspent.chain as utxolib.bitgo.ChainCode)) { + return new InputSigningError( + inputIndex, + null, + unspent, + new Error(`Chain code ${unspent.chain} is not supported for legacy verification`) + ); + } + const walletUnspent = unspent; try { - const publicKey = walletSigner.deriveForChainAndIndex(walletUnspent.chain, walletUnspent.index).signer - .publicKey; + const publicKey = walletSigner.deriveForChainAndIndex( + walletUnspent.chain as utxolib.bitgo.ChainCode, + walletUnspent.index + ).signer.publicKey; if ( !utxolib.bitgo.verifySignatureWithPublicKey(signedTransaction, inputIndex, prevOutputs, publicKey) ) { diff --git a/modules/abstract-utxo/src/unspent.ts b/modules/abstract-utxo/src/unspent.ts index 1773b336d0..4422833492 100644 --- a/modules/abstract-utxo/src/unspent.ts +++ b/modules/abstract-utxo/src/unspent.ts @@ -1,5 +1,12 @@ import { fixedScriptWallet } from '@bitgo/wasm-utxo'; +/** + * Type guard to check if an Unspent is a WalletUnspent + */ +export function isWalletUnspent(u: Unspent): u is WalletUnspent { + return 'chain' in u && 'index' in u && fixedScriptWallet.ChainCode.is((u as WalletUnspent).chain); +} + /** * Unspent transaction output (UTXO) type definition * diff --git a/modules/abstract-utxo/test/unit/util/transaction.ts b/modules/abstract-utxo/test/unit/util/transaction.ts index 95688fc688..1c8de5564d 100644 --- a/modules/abstract-utxo/test/unit/util/transaction.ts +++ b/modules/abstract-utxo/test/unit/util/transaction.ts @@ -4,7 +4,7 @@ import * as utxolib from '@bitgo/utxo-lib'; import { ECPair, fixedScriptWallet, hasPsbtMagic, address as wasmAddress } from '@bitgo/wasm-utxo'; import type { UtxoCoinName } from '../../../src/names'; -import type { Unspent, WalletUnspent } from '../../../src/unspent'; +import type { Unspent } from '../../../src/unspent'; import { getCoinNameForNetwork } from './utxoCoins'; const { isWalletUnspent, signInputWithUnspent } = utxolib.bitgo; @@ -123,7 +123,7 @@ function createTransactionBuilderWithSignedInputs { if (isWalletUnspent(u)) { - signInputWithUnspent(txBuilder, inputIndex, u as WalletUnspent, signer); + signInputWithUnspent(txBuilder, inputIndex, u, signer); } }); return txBuilder; diff --git a/modules/utxo-bin/package.json b/modules/utxo-bin/package.json index 1828c51c91..d8ef5963c3 100644 --- a/modules/utxo-bin/package.json +++ b/modules/utxo-bin/package.json @@ -31,7 +31,7 @@ "@bitgo/unspents": "^0.51.3", "@bitgo/utxo-core": "^1.36.0", "@bitgo/utxo-lib": "^11.22.0", - "@bitgo/wasm-utxo": "^4.1.0", + "@bitgo/wasm-utxo": "^4.7.0", "@noble/curves": "1.8.1", "archy": "^1.0.0", "bech32": "^2.0.0", diff --git a/modules/utxo-core/package.json b/modules/utxo-core/package.json index c766d91698..bac833a3cc 100644 --- a/modules/utxo-core/package.json +++ b/modules/utxo-core/package.json @@ -81,7 +81,7 @@ "@bitgo/secp256k1": "^1.11.0", "@bitgo/unspents": "^0.51.3", "@bitgo/utxo-lib": "^11.22.0", - "@bitgo/wasm-utxo": "^4.1.0", + "@bitgo/wasm-utxo": "^4.7.0", "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4", "fast-sha256": "^1.3.0" }, diff --git a/modules/utxo-ord/package.json b/modules/utxo-ord/package.json index 25f492976d..57f0e65d16 100644 --- a/modules/utxo-ord/package.json +++ b/modules/utxo-ord/package.json @@ -45,7 +45,7 @@ "directory": "modules/utxo-ord" }, "dependencies": { - "@bitgo/wasm-utxo": "^4.1.0" + "@bitgo/wasm-utxo": "^4.7.0" }, "devDependencies": { "@bitgo/utxo-lib": "^11.22.0" diff --git a/modules/utxo-staking/package.json b/modules/utxo-staking/package.json index bca5650f95..c5bc71a5f9 100644 --- a/modules/utxo-staking/package.json +++ b/modules/utxo-staking/package.json @@ -63,7 +63,7 @@ "@bitgo/babylonlabs-io-btc-staking-ts": "^3.5.0", "@bitgo/utxo-core": "^1.36.0", "@bitgo/utxo-lib": "^11.22.0", - "@bitgo/wasm-utxo": "^4.1.0", + "@bitgo/wasm-utxo": "^4.7.0", "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4", "bip322-js": "^2.0.0", "bitcoinjs-lib": "^6.1.7", diff --git a/modules/utxo-staking/test/unit/babylon/bug71.ts b/modules/utxo-staking/test/unit/babylon/bug71.ts index 25532396cb..9a69e509b9 100644 --- a/modules/utxo-staking/test/unit/babylon/bug71.ts +++ b/modules/utxo-staking/test/unit/babylon/bug71.ts @@ -34,7 +34,7 @@ describe('btc-staking-ts bug #71', function () { const psbt = wasmMiniscript.Psbt.deserialize(buf); assert.throws(() => { psbt.finalize(); - }, /CouldNotSatisfyTr/); + }, /Could not satisfy Tr descriptor/); }); it('cannot finalize with bitcoind', async function (this: Mocha.Context) { diff --git a/yarn.lock b/yarn.lock index a98a9c49dc..b7906a70c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1030,10 +1030,10 @@ resolved "https://registry.npmjs.org/@bitgo/wasm-ton/-/wasm-ton-1.1.1.tgz" integrity sha512-Y4x2V2ZcYWlmx42v7dlrKDtT2DuUt8smk8E98mh7RhpiifJhLk2v5RmXDwBl0A3v9TzUOU6qMOnSS/iZ8Pq52w== -"@bitgo/wasm-utxo@^4.1.0": - version "4.1.0" - resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-4.1.0.tgz" - integrity sha512-J6tKdfhJggt8LHKSh+KScz6/Q6VcX59D/1ycbUw/w+zWVOSKt+Z2+FFbYTJIVatxc4gA2bGqvHftC9dSbZBAwA== +"@bitgo/wasm-utxo@^4.7.0": + version "4.7.0" + resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-4.7.0.tgz#2ec1103c840b3be1a2ed29fae4ebc03fd57160a6" + integrity sha512-7T1vZNxM1dGPi2EqbWAFzHN0A8uWlR05c9Q7UAmZv1dQt6SBTsGc5rPyoEmwvkyPJSdbvcPS3NCoTyWIcbqUUA== "@brandonblack/musig@^0.0.1-alpha.0": version "0.0.1-alpha.1"