From 201ae545f34e1886beb261d94c1d49d484dbfc37 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 17 Dec 2025 15:23:09 +0100 Subject: [PATCH 1/4] build(abstract-utxo): update wasm-utxo to v1.14.0 Update @bitgo/wasm-utxo dependency from 1.11.0 to 1.14.0 across abstract-utxo, utxo-bin, utxo-core and utxo-staking modules. Issue: BTC-2656 Co-authored-by: llm-git --- modules/abstract-utxo/package.json | 2 +- modules/utxo-bin/package.json | 2 +- modules/utxo-core/package.json | 2 +- modules/utxo-staking/package.json | 2 +- yarn.lock | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/abstract-utxo/package.json b/modules/abstract-utxo/package.json index 618e24a468..4155b58dad 100644 --- a/modules/abstract-utxo/package.json +++ b/modules/abstract-utxo/package.json @@ -68,7 +68,7 @@ "@bitgo/utxo-core": "^1.27.0", "@bitgo/utxo-lib": "^11.18.0", "@bitgo/utxo-ord": "^1.22.19", - "@bitgo/wasm-utxo": "1.11.0", + "@bitgo/wasm-utxo": "1.14.0", "@types/lodash": "^4.14.121", "@types/superagent": "4.1.15", "bignumber.js": "^9.0.2", diff --git a/modules/utxo-bin/package.json b/modules/utxo-bin/package.json index 1b2037ffee..207441c519 100644 --- a/modules/utxo-bin/package.json +++ b/modules/utxo-bin/package.json @@ -31,7 +31,7 @@ "@bitgo/unspents": "^0.50.12", "@bitgo/utxo-core": "^1.27.0", "@bitgo/utxo-lib": "^11.18.0", - "@bitgo/wasm-utxo": "1.11.0", + "@bitgo/wasm-utxo": "1.14.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 dcf507503d..d65399c99f 100644 --- a/modules/utxo-core/package.json +++ b/modules/utxo-core/package.json @@ -81,7 +81,7 @@ "@bitgo/secp256k1": "^1.7.0", "@bitgo/unspents": "^0.50.12", "@bitgo/utxo-lib": "^11.18.0", - "@bitgo/wasm-utxo": "1.11.0", + "@bitgo/wasm-utxo": "1.14.0", "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4", "bitcoinjs-message": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.3", "fast-sha256": "^1.3.0" diff --git a/modules/utxo-staking/package.json b/modules/utxo-staking/package.json index 902f53851d..077fcd9dbd 100644 --- a/modules/utxo-staking/package.json +++ b/modules/utxo-staking/package.json @@ -63,7 +63,7 @@ "@bitgo/babylonlabs-io-btc-staking-ts": "^3.2.1", "@bitgo/utxo-core": "^1.27.0", "@bitgo/utxo-lib": "^11.18.0", - "@bitgo/wasm-utxo": "1.11.0", + "@bitgo/wasm-utxo": "1.14.0", "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4", "bip322-js": "^2.0.0", "bitcoinjs-lib": "^6.1.7", diff --git a/yarn.lock b/yarn.lock index 83d7a16145..d214078c52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -968,10 +968,10 @@ monocle-ts "^2.3.13" newtype-ts "^0.3.5" -"@bitgo/wasm-utxo@1.11.0": - version "1.11.0" - resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-1.11.0.tgz#e6b90de37c8afac17b51d20403ba1fa102198e39" - integrity sha512-yLORJZR8iD96WowF9lvBIFq4su6LUlIooOuG7awX6dNaTgHStPiRUEJbbKW1cSXAzTOSscl6wNHzgTaKkxYM5A== +"@bitgo/wasm-utxo@1.14.0": + version "1.14.0" + resolved "https://registry.npmjs.org/@bitgo/wasm-utxo/-/wasm-utxo-1.14.0.tgz#d4447ddf47a38b0e471ccffbf13540c1eaf30a44" + integrity sha512-8h0iImoUmK2Z0tdDLyfX/RfXKxb83qcRZSUhH+YFATo6SDlKpA1TpV+OjEY4Xrvte7abvVuezxU5Wq820xJcNQ== "@brandonblack/musig@^0.0.1-alpha.0": version "0.0.1-alpha.1" From 91c095aa809143967dd932a5ece2c4a7133c0718 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 17 Dec 2025 15:57:39 +0100 Subject: [PATCH 2/4] feat(utxo-lib): allow buffer as p2shP2pk key Accept raw public key buffer in addition to BIP32 key instances when creating p2shP2pk scripts, which allows for reuse by BCH, BTG and other forks. Issue: BTC-2656 Co-authored-by: llm-git --- modules/utxo-lib/src/testutil/mock.ts | 7 +++++-- modules/utxo-lib/src/testutil/psbt.ts | 15 +++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/utxo-lib/src/testutil/mock.ts b/modules/utxo-lib/src/testutil/mock.ts index 9f27700cde..72b58a33af 100644 --- a/modules/utxo-lib/src/testutil/mock.ts +++ b/modules/utxo-lib/src/testutil/mock.ts @@ -76,9 +76,12 @@ export function isReplayProtectionUnspent( export function mockReplayProtectionUnspent( network: Network, value: TNumber, - { key = replayProtectionKeyPair, vout = 0 }: { key?: BIP32Interface; vout?: number } = {} + { key = replayProtectionKeyPair, vout = 0 }: { key?: BIP32Interface | Buffer; vout?: number } = {} ): UnspentWithPrevTx { - const outputScript = createOutputScriptP2shP2pk(key.publicKey).scriptPubKey; + if (!Buffer.isBuffer(key)) { + key = key.publicKey; + } + const outputScript = createOutputScriptP2shP2pk(key).scriptPubKey; const prevTransaction = mockPrevTx(vout, outputScript, BigInt(value), network); return { ...fromOutputWithPrevTx(prevTransaction, vout), value }; } diff --git a/modules/utxo-lib/src/testutil/psbt.ts b/modules/utxo-lib/src/testutil/psbt.ts index 0a6285257b..504bfefc6d 100644 --- a/modules/utxo-lib/src/testutil/psbt.ts +++ b/modules/utxo-lib/src/testutil/psbt.ts @@ -86,10 +86,14 @@ export function toUnspent( input: Input, index: number, network: Network, - rootWalletKeys: RootWalletKeys + rootWalletKeys: RootWalletKeys, + { p2shP2pkKey }: { p2shP2pkKey?: Buffer } = {} ): Unspent { if (input.scriptType === 'p2shP2pk') { - return mockReplayProtectionUnspent(network, input.value, { key: rootWalletKeys['user'], vout: index }); + return mockReplayProtectionUnspent(network, input.value, { + key: p2shP2pkKey ?? rootWalletKeys['user'], + vout: index, + }); } else { const chain = getInternalChainCode(input.scriptType === 'taprootKeyPathSpend' ? 'p2trMusig2' : input.scriptType); return mockWalletUnspent(network, input.value, { @@ -177,6 +181,7 @@ export function constructPsbt( rootWalletKeys: RootWalletKeys, signStage: SignStage, params?: { + p2shP2pkKey?: Buffer; signers?: { signerName: KeyName; cosignerName?: KeyName }; deterministic?: boolean; skipNonWitnessUtxo?: boolean; @@ -194,14 +199,16 @@ export function constructPsbt( addXpubsToPsbt(psbt, rootWalletKeys); } - const unspents = inputs.map((input, i) => toUnspent(input, i, network, rootWalletKeys)); + const unspents = inputs.map((input, i) => + toUnspent(input, i, network, rootWalletKeys, { p2shP2pkKey: params?.p2shP2pkKey }) + ); unspents.forEach((u, i) => { const { signerName, cosignerName } = signers ? signers : getSigners(inputs[i].scriptType); if (isWalletUnspent(u) && cosignerName) { addWalletUnspentToPsbt(psbt, u, rootWalletKeys, signerName, cosignerName, { skipNonWitnessUtxo }); } else { - const { redeemScript } = createOutputScriptP2shP2pk(rootWalletKeys.user.publicKey); + const { redeemScript } = createOutputScriptP2shP2pk(params?.p2shP2pkKey ?? rootWalletKeys.user.publicKey); assert(redeemScript); addReplayProtectionUnspentToPsbt(psbt, u, redeemScript, { skipNonWitnessUtxo }); } From 90d369ebff5324a8d9559406349db02e4d8d5e11 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 17 Dec 2025 17:13:40 +0100 Subject: [PATCH 3/4] feat(abstract-utxo): enable wasm support for BCH, BTG, BSV, XEC Enable wasm-utxo support for all networks except ZCash. Issue: BTC-2656 Co-authored-by: llm-git --- .../test/unit/transaction/fixedScript/util.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/modules/abstract-utxo/test/unit/transaction/fixedScript/util.ts b/modules/abstract-utxo/test/unit/transaction/fixedScript/util.ts index 813fe24d9e..9591c7438f 100644 --- a/modules/abstract-utxo/test/unit/transaction/fixedScript/util.ts +++ b/modules/abstract-utxo/test/unit/transaction/fixedScript/util.ts @@ -1,11 +1,5 @@ import * as utxolib from '@bitgo/utxo-lib'; export function hasWasmUtxoSupport(network: utxolib.Network): boolean { - return ![ - utxolib.networks.bitcoincash, - utxolib.networks.bitcoingold, - utxolib.networks.bitcoinsv, - utxolib.networks.ecash, - utxolib.networks.zcash, - ].includes(utxolib.getMainnet(network)); + return utxolib.getMainnet(network) !== utxolib.networks.zcash; } From 8c2bb89b74ea75a99c99d67359c38d24ae999427 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 17 Dec 2025 17:13:50 +0100 Subject: [PATCH 4/4] feat(abstract-utxo): add getReplayProtectionPubkeys function Add function to generate replay protection pubkeys and use it in tests Issue: BTC-2656 Co-authored-by: llm-git --- modules/abstract-utxo/test/unit/transaction.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/abstract-utxo/test/unit/transaction.ts b/modules/abstract-utxo/test/unit/transaction.ts index 6d99bd60b9..eb38355494 100644 --- a/modules/abstract-utxo/test/unit/transaction.ts +++ b/modules/abstract-utxo/test/unit/transaction.ts @@ -13,7 +13,7 @@ import { WalletSignTransactionOptions, } from '@bitgo/sdk-core'; -import { AbstractUtxoCoin, getReplayProtectionAddresses, generateAddress } from '../../src'; +import { AbstractUtxoCoin, getReplayProtectionAddresses, generateAddress, getReplayProtectionPubkeys } from '../../src'; import { SdkBackend } from '../../src/transaction/types'; import { hasWasmUtxoSupport } from './transaction/fixedScript/util'; @@ -176,7 +176,9 @@ describe(`UTXO coin signTransaction`, async function () { })); const unspentSum = inputs.reduce((prev: bigint, curr) => prev + curr.value, BigInt(0)); const outputs: testutil.Output[] = [{ scriptType: 'p2sh', value: unspentSum - BigInt(1000) }]; - const psbt = testutil.constructPsbt(inputs, outputs, coin.network, rootWalletKeys, 'unsigned'); + const psbt = testutil.constructPsbt(inputs, outputs, coin.network, rootWalletKeys, 'unsigned', { + p2shP2pkKey: getReplayProtectionPubkeys(coin.network)[0], + }); for (const v of [false, true]) { await signTransaction(psbt, v); @@ -402,7 +404,9 @@ function run( const outputs: testutil.Output[] = [ { address: getOutputAddress(getWalletKeys('test')), value: unspentSum - BigInt(1000) }, ]; - const psbt = testutil.constructPsbt(inputs, outputs, coin.network, walletKeys, 'unsigned'); + const psbt = testutil.constructPsbt(inputs, outputs, coin.network, walletKeys, 'unsigned', { + p2shP2pkKey: getReplayProtectionPubkeys(coin.network)[0], + }); utxolib.bitgo.addXpubsToPsbt(psbt, walletKeys); return psbt; }