diff --git a/modules/abstract-utxo/package.json b/modules/abstract-utxo/package.json index 0c42fd5807..a106888fac 100644 --- a/modules/abstract-utxo/package.json +++ b/modules/abstract-utxo/package.json @@ -63,7 +63,6 @@ "@bitgo/blockapis": "^1.13.0", "@bitgo/sdk-api": "^1.74.1", "@bitgo/sdk-core": "^36.31.1", - "@bitgo/secp256k1": "^1.10.0", "@bitgo/utxo-core": "^1.33.0", "@bitgo/utxo-lib": "^11.21.0", "@bitgo/utxo-ord": "^1.26.0", @@ -71,7 +70,6 @@ "@types/lodash": "^4.14.121", "@types/superagent": "4.1.15", "bignumber.js": "^9.0.2", - "bitcoinjs-message": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.3", "debug": "^3.1.0", "io-ts": "npm:@bitgo-forks/io-ts@2.1.4", "lodash": "^4.17.14", diff --git a/modules/abstract-utxo/src/abstractUtxoCoin.ts b/modules/abstract-utxo/src/abstractUtxoCoin.ts index 4d8312abdf..2c650acbe5 100644 --- a/modules/abstract-utxo/src/abstractUtxoCoin.ts +++ b/modules/abstract-utxo/src/abstractUtxoCoin.ts @@ -3,7 +3,7 @@ import { randomBytes } from 'crypto'; import _ from 'lodash'; import * as utxolib from '@bitgo/utxo-lib'; -import { bip32 } from '@bitgo/secp256k1'; +import { BIP32, fixedScriptWallet } from '@bitgo/wasm-utxo'; import { bitgo, getMainnet } from '@bitgo/utxo-lib'; import { AddressCoinSpecific, @@ -42,7 +42,6 @@ import { isValidPrv, isValidXprv, } from '@bitgo/sdk-core'; -import { fixedScriptWallet } from '@bitgo/wasm-utxo'; import { backupKeyRecovery, @@ -496,7 +495,7 @@ export abstract class AbstractUtxoCoin */ isValidPub(pub: string): boolean { try { - return bip32.fromBase58(pub).isNeutered(); + return BIP32.fromBase58(pub).isNeutered(); } catch (e) { return false; } @@ -978,7 +977,7 @@ export abstract class AbstractUtxoCoin // maximum entropy and gives us maximum security against cracking. seed = randomBytes(512 / 8); } - const extendedKey = bip32.fromSeed(seed); + const extendedKey = BIP32.fromSeed(seed); return { pub: extendedKey.neutered().toBase58(), prv: extendedKey.toBase58(), @@ -1084,7 +1083,7 @@ export abstract class AbstractUtxoCoin throw new Error('invalid private key'); } if (publicKey) { - const genPubKey = bip32.fromBase58(prv).neutered().toBase58(); + const genPubKey = BIP32.fromBase58(prv).neutered().toBase58(); if (genPubKey !== publicKey) { throw new Error('public key does not match private key'); } diff --git a/modules/abstract-utxo/src/descriptor/createWallet/createDescriptorWallet.ts b/modules/abstract-utxo/src/descriptor/createWallet/createDescriptorWallet.ts index a98320fee2..211f374b1d 100644 --- a/modules/abstract-utxo/src/descriptor/createWallet/createDescriptorWallet.ts +++ b/modules/abstract-utxo/src/descriptor/createWallet/createDescriptorWallet.ts @@ -1,5 +1,5 @@ import { BitGoAPI } from '@bitgo/sdk-api'; -import { bip32 } from '@bitgo/secp256k1'; +import { BIP32 } from '@bitgo/wasm-utxo'; import { Wallet } from '@bitgo/sdk-core'; import { AbstractUtxoCoin } from '../../abstractUtxoCoin'; @@ -56,12 +56,12 @@ export async function createDescriptorWalletWithWalletPassphrase( if (!userKeychain.prv) { throw new Error('Missing private key'); } - const userKey = bip32.fromBase58(userKeychain.prv); + const userKey = BIP32.fromBase58(userKeychain.prv); const cosigners = [backupKeychain, bitgoKeychain].map((keychain) => { if (!keychain.pub) { throw new Error('Missing public key'); } - return bip32.fromBase58(keychain.pub); + return BIP32.fromBase58(keychain.pub); }); return createDescriptorWallet(bitgo, coin, { ...params, diff --git a/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts b/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts index 68516fa421..cfb4332773 100644 --- a/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts +++ b/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts @@ -12,7 +12,6 @@ import { Triple, xprvToRawPrv, } from '@bitgo/sdk-core'; -import { bip32 } from '@bitgo/secp256k1'; import { createPsbtForSingleInscriptionPassingTransaction, DefaultInscriptionConstraints, @@ -27,10 +26,11 @@ import { WalletUnspent, type TapLeafScript, } from '@bitgo/utxo-ord'; -import { fixedScriptWallet } from '@bitgo/wasm-utxo'; +import { BIP32, fixedScriptWallet } from '@bitgo/wasm-utxo'; import { AbstractUtxoCoin } from '../../abstractUtxoCoin'; import { fetchKeychains } from '../../keychains'; +import { toUtxolibBIP32 } from '../../wasmUtil'; /** Key identifier for signing */ type SignerKey = 'user' | 'backup' | 'bitgo'; @@ -58,7 +58,7 @@ export class InscriptionBuilder implements IInscriptionBuilder { const user = await this.wallet.baseCoin.keychains().get({ id: this.wallet.keyIds()[KeyIndices.USER] }); assert(user.pub); - const userKey = bip32.fromBase58(user.pub); + const userKey = toUtxolibBIP32(BIP32.fromBase58(user.pub)); const { key: derivedKey } = BaseCoin.deriveKeyWithSeedBip32(userKey, inscriptionData.toString()); const result = inscriptions.createInscriptionRevealData( diff --git a/modules/abstract-utxo/src/keychains.ts b/modules/abstract-utxo/src/keychains.ts index b4b2cdccd0..073b40a848 100644 --- a/modules/abstract-utxo/src/keychains.ts +++ b/modules/abstract-utxo/src/keychains.ts @@ -2,9 +2,8 @@ import assert from 'assert'; import * as t from 'io-ts'; import { bitgo } from '@bitgo/utxo-lib'; -import { BIP32Interface, bip32 } from '@bitgo/secp256k1'; import { IRequestTracer, IWallet, KeyIndices, promiseProps, Triple } from '@bitgo/sdk-core'; -import { fixedScriptWallet } from '@bitgo/wasm-utxo'; +import { BIP32, bip32, fixedScriptWallet } from '@bitgo/wasm-utxo'; import { AbstractUtxoCoin } from './abstractUtxoCoin'; import { UtxoWallet } from './wallet'; @@ -50,9 +49,9 @@ export function toKeychainTriple(keychains: UtxoNamedKeychains): Triple | string[] -): Triple { +): Triple { if (keychains instanceof bitgo.RootWalletKeys) { - return keychains.triple; + return keychains.triple.map((k) => BIP32.fromBase58(k.toBase58())) as Triple; } if (Array.isArray(keychains)) { if (keychains.length !== 3) { @@ -60,14 +59,14 @@ export function toBip32Triple( } return keychains.map((keychain: { pub: string } | string) => { const v = typeof keychain === 'string' ? keychain : keychain.pub; - return bip32.fromBase58(v); - }) as Triple; + return BIP32.fromBase58(v); + }) as Triple; } return toBip32Triple(toKeychainTriple(keychains)); } -function toXpub(keychain: { pub: string } | string | BIP32Interface): string { +function toXpub(keychain: { pub: string } | string | bip32.BIP32Interface): string { if (typeof keychain === 'string') { if (keychain.startsWith('xpub')) { return keychain; @@ -84,7 +83,7 @@ function toXpub(keychain: { pub: string } | string | BIP32Interface): string { } export function toXpubTriple( - keychains: UtxoNamedKeychains | Triple<{ pub: string }> | Triple | Triple + keychains: UtxoNamedKeychains | Triple<{ pub: string }> | Triple | Triple ): Triple { if (Array.isArray(keychains)) { if (keychains.length !== 3) { diff --git a/modules/abstract-utxo/src/offlineVault/OfflineVaultHalfSigned.ts b/modules/abstract-utxo/src/offlineVault/OfflineVaultHalfSigned.ts index 59f6401172..83ffccbcef 100644 --- a/modules/abstract-utxo/src/offlineVault/OfflineVaultHalfSigned.ts +++ b/modules/abstract-utxo/src/offlineVault/OfflineVaultHalfSigned.ts @@ -1,8 +1,8 @@ -import { BIP32Interface, bip32 } from '@bitgo/secp256k1'; -import { Psbt } from '@bitgo/wasm-utxo'; +import { BIP32, bip32, Psbt } from '@bitgo/wasm-utxo'; import { BaseCoin } from '@bitgo/sdk-core'; import { UtxoCoinName } from '../names'; +import { toUtxolibBIP32 } from '../wasmUtil'; import { OfflineVaultSignable } from './OfflineVaultSignable'; import { DescriptorTransaction, getHalfSignedPsbt } from './descriptor'; @@ -17,19 +17,17 @@ function createHalfSignedFromPsbt(psbt: Psbt): OfflineVaultHalfSigned { export function createHalfSigned( coinName: UtxoCoinName, - prv: string | BIP32Interface, + prv: string | bip32.BIP32Interface, derivationId: string, tx: unknown ): OfflineVaultHalfSigned { - if (typeof prv === 'string') { - prv = bip32.fromBase58(prv); - } - prv = BaseCoin.deriveKeyWithSeedBip32(prv, derivationId).key; + const key = typeof prv === 'string' ? BIP32.fromBase58(prv) : prv; + const derivedKey = BaseCoin.deriveKeyWithSeedBip32(toUtxolibBIP32(key), derivationId).key; if (!OfflineVaultSignable.is(tx)) { throw new Error('unsupported transaction type'); } if (DescriptorTransaction.is(tx)) { - return createHalfSignedFromPsbt(getHalfSignedPsbt(tx, prv, coinName)); + return createHalfSignedFromPsbt(getHalfSignedPsbt(tx, derivedKey, coinName)); } throw new Error('unsupported transaction type'); } diff --git a/modules/abstract-utxo/src/offlineVault/OfflineVaultSignable.ts b/modules/abstract-utxo/src/offlineVault/OfflineVaultSignable.ts index da7108997c..80d2eaee9a 100644 --- a/modules/abstract-utxo/src/offlineVault/OfflineVaultSignable.ts +++ b/modules/abstract-utxo/src/offlineVault/OfflineVaultSignable.ts @@ -1,4 +1,4 @@ -import { BIP32Interface, bip32 } from '@bitgo/secp256k1'; +import { BIP32 } from '@bitgo/wasm-utxo'; import { Triple } from '@bitgo/sdk-core'; import * as t from 'io-ts'; @@ -28,8 +28,6 @@ export type OfflineVaultUnsigned = t.TypeOf; type WithXpub = { xpub: string }; type NamedKeys = { user: WithXpub; backup: WithXpub; bitgo: WithXpub }; -export function toKeyTriple(xpubs: NamedKeys): Triple { - return [xpubs.user.xpub, xpubs.backup.xpub, xpubs.bitgo.xpub].map((xpub) => - bip32.fromBase58(xpub) - ) as Triple; +export function toKeyTriple(xpubs: NamedKeys): Triple { + return [BIP32.fromBase58(xpubs.user.xpub), BIP32.fromBase58(xpubs.backup.xpub), BIP32.fromBase58(xpubs.bitgo.xpub)]; } diff --git a/modules/abstract-utxo/src/offlineVault/descriptor/transaction.ts b/modules/abstract-utxo/src/offlineVault/descriptor/transaction.ts index 85575de304..ec54140c23 100644 --- a/modules/abstract-utxo/src/offlineVault/descriptor/transaction.ts +++ b/modules/abstract-utxo/src/offlineVault/descriptor/transaction.ts @@ -1,6 +1,5 @@ import * as t from 'io-ts'; -import { Psbt } from '@bitgo/wasm-utxo'; -import type { BIP32Interface } from '@bitgo/utxo-lib'; +import { bip32, Psbt } from '@bitgo/wasm-utxo'; import { DescriptorMap, NamedDescriptor } from '../../descriptor'; import { OfflineVaultSignable, toKeyTriple } from '../OfflineVaultSignable'; @@ -34,7 +33,7 @@ export function getDescriptorsFromDescriptorTransaction(tx: DescriptorTransactio return toDescriptorMapValidate(descriptors, pubkeys, policy); } -export function getHalfSignedPsbt(tx: DescriptorTransaction, prv: BIP32Interface, coinName: UtxoCoinName): Psbt { +export function getHalfSignedPsbt(tx: DescriptorTransaction, prv: bip32.BIP32Interface, coinName: UtxoCoinName): Psbt { const psbt = toWasmPsbt(Buffer.from(tx.coinSpecific.txHex, 'hex')); const descriptorMap = getDescriptorsFromDescriptorTransaction(tx); signPsbt(psbt, descriptorMap, prv, { onUnknownInput: 'throw' }); diff --git a/modules/abstract-utxo/src/recovery/crossChainRecovery.ts b/modules/abstract-utxo/src/recovery/crossChainRecovery.ts index cf36e7ce9d..eb27a2423d 100644 --- a/modules/abstract-utxo/src/recovery/crossChainRecovery.ts +++ b/modules/abstract-utxo/src/recovery/crossChainRecovery.ts @@ -1,5 +1,4 @@ -import { BIP32Interface, bip32 } from '@bitgo/secp256k1'; -import { CoinName, fixedScriptWallet, address as wasmAddress } from '@bitgo/wasm-utxo'; +import { BIP32, CoinName, fixedScriptWallet, address as wasmAddress } from '@bitgo/wasm-utxo'; import { BitGoBase, IWallet, Keychain, Triple, Wallet } from '@bitgo/sdk-core'; import { decrypt } from '@bitgo/sdk-api'; @@ -294,9 +293,9 @@ async function getFeeRateSatVB(coin: AbstractUtxoCoin): Promise { * @param wallet * @return signing key */ -async function getPrv(xprv?: string, passphrase?: string, wallet?: IWallet | WalletV1): Promise { +async function getPrv(xprv?: string, passphrase?: string, wallet?: IWallet | WalletV1): Promise { if (xprv) { - const key = bip32.fromBase58(xprv); + const key = BIP32.fromBase58(xprv); if (key.isNeutered()) { throw new Error(`not a private key`); } diff --git a/modules/abstract-utxo/src/transaction/bip322.ts b/modules/abstract-utxo/src/transaction/bip322.ts index fa2ef6eb74..07b16203ed 100644 --- a/modules/abstract-utxo/src/transaction/bip322.ts +++ b/modules/abstract-utxo/src/transaction/bip322.ts @@ -1,6 +1,5 @@ import { decodeOrElse } from '@bitgo/sdk-core'; -import { bitgo } from '@bitgo/utxo-lib'; -import { bip322, fixedScriptWallet, Transaction, type CoinName, type Triple } from '@bitgo/wasm-utxo'; +import { bip322, fixedScriptWallet, hasPsbtMagic, Transaction, type CoinName, type Triple } from '@bitgo/wasm-utxo'; import * as t from 'io-ts'; const BIP322MessageInfo = t.type({ @@ -52,7 +51,7 @@ export function verifyTransactionFromBroadcastableMessage( } const network = coinName as CoinName; - if (bitgo.isPsbt(message.txHex)) { + if (hasPsbtMagic(Buffer.from(message.txHex, 'hex'))) { const psbt = fixedScriptWallet.BitGoPsbt.fromBytes(Buffer.from(message.txHex, 'hex'), network); try { message.messageInfo.forEach((info, inputIndex) => { diff --git a/modules/abstract-utxo/src/transaction/fixedScript/SigningError.ts b/modules/abstract-utxo/src/transaction/fixedScript/SigningError.ts index 97762e99dd..a3e728c52e 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/SigningError.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/SigningError.ts @@ -1,11 +1,20 @@ import type { Unspent } from '../../unspent'; -import type { PsbtParsedScriptType } from './signPsbtUtxolib'; +export type PsbtParsedScriptType = + | 'p2sh' + | 'p2wsh' + | 'p2shP2wsh' + | 'p2shP2pk' + | 'taprootKeyPathSpend' + | 'taprootScriptPathSpend' + | 'p2trLegacy' + | 'p2trMusig2ScriptPath' + | 'p2trMusig2KeyPath'; export class InputSigningError extends Error { static expectedWalletUnspent( inputIndex: number, - inputType: PsbtParsedScriptType | null, // null for legacy transaction format + inputType: PsbtParsedScriptType | null, unspent: Unspent | { id: string } ): InputSigningError { return new InputSigningError( @@ -18,7 +27,7 @@ export class InputSigningError extends constructor( public inputIndex: number, - public inputType: PsbtParsedScriptType | null, // null for legacy transaction format + public inputType: PsbtParsedScriptType | null, public unspent: Unspent | { id: string }, public reason: Error | string ) { diff --git a/modules/abstract-utxo/src/transaction/fixedScript/explainTransaction.ts b/modules/abstract-utxo/src/transaction/fixedScript/explainTransaction.ts index 42f284be1d..2dcbca9f0c 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/explainTransaction.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/explainTransaction.ts @@ -1,8 +1,8 @@ import * as utxolib from '@bitgo/utxo-lib'; import { bip322 } from '@bitgo/utxo-core'; -import { BIP32Interface, bip32 } from '@bitgo/secp256k1'; import { bitgo } from '@bitgo/utxo-lib'; import { ITransactionExplanation as BaseTransactionExplanation, Triple } from '@bitgo/sdk-core'; +import { BIP32 } from '@bitgo/wasm-utxo'; import * as utxocore from '@bitgo/utxo-core'; import type { Bip322Message } from '../../abstractUtxoCoin'; @@ -11,6 +11,7 @@ import type { Unspent } from '../../unspent'; import { toExtendedAddressFormat } from '../recipient'; import { getPayGoVerificationPubkey } from '../getPayGoVerificationPubkey'; import { toBip32Triple } from '../../keychains'; +import { toUtxolibBIP32 } from '../../wasmUtil'; import { getNetworkFromCoinName, UtxoCoinName } from '../../names'; // ===== Transaction Explanation Type Definitions ===== @@ -174,8 +175,8 @@ function getRootWalletKeys(params: { pubs?: bitgo.RootWalletKeys | string[] }): if (params.pubs instanceof bitgo.RootWalletKeys) { return params.pubs; } - const keys = params.pubs?.map((xpub) => bip32.fromBase58(xpub)); - return keys && keys.length === 3 ? new bitgo.RootWalletKeys(keys as Triple) : undefined; + const keys = params.pubs?.map((xpub) => toUtxolibBIP32(BIP32.fromBase58(xpub))); + return keys && keys.length === 3 ? new bitgo.RootWalletKeys(keys as Triple) : undefined; } function getPsbtInputSignaturesCount( @@ -247,9 +248,15 @@ function getChainAndIndexFromBip32Derivations(output: bitgo.PsbtOutput) { return utxolib.bitgo.getChainAndIndexFromPath(paths[0]); } -function getChangeInfo(psbt: bitgo.UtxoPsbt, walletKeys?: Triple): ChangeAddressInfo[] | undefined { +function getChangeInfo( + psbt: bitgo.UtxoPsbt, + walletKeys?: Triple | Triple +): ChangeAddressInfo[] | undefined { + let utxolibKeys: Triple; try { - walletKeys = walletKeys ?? utxolib.bitgo.getSortedRootNodes(psbt); + utxolibKeys = walletKeys + ? (walletKeys.map((k) => toUtxolibBIP32(k)) as Triple) + : utxolib.bitgo.getSortedRootNodes(psbt); } catch (e) { if (e instanceof utxolib.bitgo.ErrorNoMultiSigInputFound) { return undefined; @@ -257,7 +264,7 @@ function getChangeInfo(psbt: bitgo.UtxoPsbt, walletKeys?: Triple throw e; } - return utxolib.bitgo.findWalletOutputIndices(psbt, walletKeys).map((i) => { + return utxolib.bitgo.findWalletOutputIndices(psbt, utxolibKeys).map((i) => { const derivationInformation = getChainAndIndexFromBip32Derivations(psbt.data.outputs[i]); if (!derivationInformation) { throw new Error('could not find derivation information on bip32Derivation or tapBip32Derivation'); @@ -392,7 +399,7 @@ export function explainPsbt( utxocore.paygo.verifyPayGoAddressProof( psbt, payGoVerificationInfo.outputIndex, - bip32.fromBase58(payGoVerificationInfo.verificationPubkey, utxolib.networks.bitcoin).publicKey + Buffer.from(BIP32.fromBase58(payGoVerificationInfo.verificationPubkey).publicKey) ); } catch (e) { if (strict) { diff --git a/modules/abstract-utxo/src/transaction/fixedScript/parseTransaction.ts b/modules/abstract-utxo/src/transaction/fixedScript/parseTransaction.ts index 6a606cd10b..7d6e57bccc 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/parseTransaction.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/parseTransaction.ts @@ -2,7 +2,6 @@ import assert from 'assert'; import _ from 'lodash'; import { ITransactionRecipient, Triple, VerificationOptions, Wallet } from '@bitgo/sdk-core'; -import * as utxolib from '@bitgo/utxo-lib'; import type { AbstractUtxoCoin, ParseTransactionOptions } from '../../abstractUtxoCoin'; import type { FixedScriptWalletOutput, Output, ParsedTransaction } from '../types'; @@ -15,6 +14,7 @@ import { toOutputScript, } from '../recipient'; import { ComparableOutput, ExpectedOutput, outputDifference } from '../outputDifference'; +import { toTNumber } from '../../tnumber'; import type { TransactionExplanation } from './explainTransaction'; import { CustomChangeOptions, parseOutput } from './parseOutput'; @@ -231,10 +231,10 @@ export async function parseTransaction( // these are all the non-wallet outputs that had been originally explicitly specified in recipients const explicitExternalOutputs = explicitOutputs.filter((output) => output.external); // this is the sum of all the originally explicitly specified non-wallet output values - const explicitExternalSpendAmount = utxolib.bitgo.toTNumber( - explicitExternalOutputs.reduce((sum: bigint, o) => sum + BigInt(o.value), BigInt(0)) as bigint, + const explicitExternalSpendAmount = toTNumber( + explicitExternalOutputs.reduce((sum: bigint, o) => sum + BigInt(o.value), BigInt(0)), coin.amountType - ); + ) as TNumber; /** * The calculation of the implicit external spend amount pertains to verifying the pay-as-you-go-fee BitGo @@ -250,10 +250,10 @@ export async function parseTransaction( // make sure that all the extra addresses are change addresses // get all the additional external outputs the server added and calculate their values const implicitExternalOutputs = implicitOutputs.filter((output) => output.external); - const implicitExternalSpendAmount = utxolib.bitgo.toTNumber( - implicitExternalOutputs.reduce((sum: bigint, o) => sum + BigInt(o.value), BigInt(0)) as bigint, + const implicitExternalSpendAmount = toTNumber( + implicitExternalOutputs.reduce((sum: bigint, o) => sum + BigInt(o.value), BigInt(0)), coin.amountType - ); + ) as TNumber; function toOutputs(outputs: ExpectedOutput[] | ComparableOutputWithExternal[]): Output[] { return outputs.map((output) => ({ diff --git a/modules/abstract-utxo/src/transaction/fixedScript/replayProtection.ts b/modules/abstract-utxo/src/transaction/fixedScript/replayProtection.ts index 2f11151b9b..81fecf0cac 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/replayProtection.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/replayProtection.ts @@ -1,6 +1,6 @@ -import { utxolibCompat } from '@bitgo/wasm-utxo'; +import { address, type AddressFormat } from '@bitgo/wasm-utxo'; -import { getNetworkFromCoinName, UtxoCoinName } from '../../names'; +import { UtxoCoinName } from '../../names'; export const pubkeyProd = Buffer.from('0255b9f71ac2c78fffd83e3e37b9e17ae70d5437b7f56d0ed2e93b7de08015aa59', 'hex'); @@ -27,18 +27,14 @@ const replayProtectionScriptsProd = [Buffer.from('a914174315cfde84f4c45395ac6f15 // bchtest:pqtjmnzwqffkrk2349g3cecfwwjwxusvnq87n07cal const replayProtectionScriptsTestnet = [Buffer.from('a914172dcc4e025361d951a9511c670973a4e3720c9887', 'hex')]; -export function getReplayProtectionAddresses( - coinName: UtxoCoinName, - format: 'default' | 'cashaddr' = 'default' -): string[] { - const network = getNetworkFromCoinName(coinName); +export function getReplayProtectionAddresses(coinName: UtxoCoinName, format: AddressFormat = 'default'): string[] { switch (coinName) { case 'bch': case 'bsv': - return replayProtectionScriptsProd.map((script) => utxolibCompat.fromOutputScript(script, network, format)); + return replayProtectionScriptsProd.map((script) => address.fromOutputScriptWithCoin(script, coinName, format)); case 'tbsv': case 'tbch': - return replayProtectionScriptsTestnet.map((script) => utxolibCompat.fromOutputScript(script, network, format)); + return replayProtectionScriptsTestnet.map((script) => address.fromOutputScriptWithCoin(script, coinName, format)); default: return []; } diff --git a/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts b/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts index 9208ab97bc..95fcfae2a5 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/signLegacyTransaction.ts @@ -1,13 +1,14 @@ import assert from 'assert'; import * as utxolib from '@bitgo/utxo-lib'; -import { BIP32Interface, bip32 } from '@bitgo/secp256k1'; import { bitgo } from '@bitgo/utxo-lib'; import { isTriple, Triple } from '@bitgo/sdk-core'; +import { BIP32, bip32 } from '@bitgo/wasm-utxo'; import debugLib from 'debug'; import { UtxoCoinName } from '../../names'; import type { Unspent, WalletUnspent } from '../../unspent'; +import { toUtxolibBIP32 } from '../../wasmUtil'; import { getReplayProtectionAddresses } from './replayProtection'; import { InputSigningError, TransactionSigningError } from './SigningError'; @@ -120,7 +121,7 @@ export function signAndVerifyWalletTransaction( export function signLegacyTransaction( tx: utxolib.bitgo.UtxoTransaction, - signerKeychain: BIP32Interface | undefined, + signerKeychain: bip32.BIP32Interface | undefined, coinName: UtxoCoinName, params: { isLastSignature: boolean; @@ -148,12 +149,16 @@ export function signLegacyTransaction( throw new Error(`must provide xpub array`); } - const keychains = params.pubs.map((pub) => bip32.fromBase58(pub)) as Triple; + const keychains = params.pubs.map((pub) => toUtxolibBIP32(BIP32.fromBase58(pub))) as Triple; const cosignerPub = params.cosignerPub ?? params.pubs[2]; - const cosignerKeychain = bip32.fromBase58(cosignerPub); + const cosignerKeychain = toUtxolibBIP32(BIP32.fromBase58(cosignerPub)); assert(signerKeychain); - const walletSigner = new bitgo.WalletUnspentSigner(keychains, signerKeychain, cosignerKeychain); + const walletSigner = new bitgo.WalletUnspentSigner( + keychains, + toUtxolibBIP32(signerKeychain), + cosignerKeychain + ); return signAndVerifyWalletTransaction(tx, params.txInfo.unspents, walletSigner, coinName, { isLastSignature: params.isLastSignature, }) as utxolib.bitgo.UtxoTransaction; diff --git a/modules/abstract-utxo/src/transaction/fixedScript/signPsbtUtxolib.ts b/modules/abstract-utxo/src/transaction/fixedScript/signPsbtUtxolib.ts index 34a51b07a0..12bf626dd1 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/signPsbtUtxolib.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/signPsbtUtxolib.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import * as utxolib from '@bitgo/utxo-lib'; -import { BIP32Interface } from '@bitgo/secp256k1'; import { bitgo } from '@bitgo/utxo-lib'; import debugLib from 'debug'; @@ -35,7 +34,7 @@ export type PsbtParsedScriptType = */ export function signAndVerifyPsbt( psbt: utxolib.bitgo.UtxoPsbt, - signerKeychain: BIP32Interface + signerKeychain: utxolib.BIP32Interface ): utxolib.bitgo.UtxoPsbt { const txInputs = psbt.txInputs; const outputIds: string[] = []; @@ -107,7 +106,7 @@ const PSBT_CACHE = new Map(); export async function signPsbtWithMusig2ParticipantUtxolib( coin: Musig2Participant, tx: utxolib.bitgo.UtxoPsbt, - signerKeychain: BIP32Interface | undefined, + signerKeychain: utxolib.BIP32Interface | undefined, params: { signingStep: 'signerNonce' | 'cosignerNonce' | 'signerSignature' | undefined; walletId: string | undefined; diff --git a/modules/abstract-utxo/src/transaction/fixedScript/signPsbtWasm.ts b/modules/abstract-utxo/src/transaction/fixedScript/signPsbtWasm.ts index 52305f3be6..776d9e6fa2 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/signPsbtWasm.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/signPsbtWasm.ts @@ -1,7 +1,6 @@ import assert from 'assert'; -import { BIP32Interface } from '@bitgo/utxo-lib'; -import { BIP32, ECPair, fixedScriptWallet } from '@bitgo/wasm-utxo'; +import { BIP32, bip32, ECPair, fixedScriptWallet } from '@bitgo/wasm-utxo'; import { toWasmBIP32 } from '../../wasmUtil'; @@ -36,7 +35,7 @@ function hasKeyPathSpendInput( */ export function signAndVerifyPsbtWasm( tx: fixedScriptWallet.BitGoPsbt, - signerKeychain: BIP32Interface | BIP32, + signerKeychain: bip32.BIP32Interface | BIP32, rootWalletKeys: fixedScriptWallet.RootWalletKeys, replayProtection: ReplayProtectionKeys ): fixedScriptWallet.BitGoPsbt { @@ -81,7 +80,7 @@ export function signAndVerifyPsbtWasm( export async function signPsbtWithMusig2ParticipantWasm( coin: Musig2Participant, tx: fixedScriptWallet.BitGoPsbt, - signerKeychain: BIP32Interface | undefined, + signerKeychain: bip32.BIP32Interface | undefined, rootWalletKeys: fixedScriptWallet.RootWalletKeys, params: { replayProtection: ReplayProtectionKeys; diff --git a/modules/abstract-utxo/src/transaction/fixedScript/signTransaction.ts b/modules/abstract-utxo/src/transaction/fixedScript/signTransaction.ts index 80339a3556..4844733d88 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/signTransaction.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/signTransaction.ts @@ -2,13 +2,13 @@ import assert from 'assert'; import { isTriple } from '@bitgo/sdk-core'; import _ from 'lodash'; -import { BIP32Interface } from '@bitgo/secp256k1'; import { bitgo } from '@bitgo/utxo-lib'; import * as utxolib from '@bitgo/utxo-lib'; -import { BIP32, fixedScriptWallet } from '@bitgo/wasm-utxo'; +import { BIP32, bip32, fixedScriptWallet } from '@bitgo/wasm-utxo'; import { UtxoCoinName } from '../../names'; import type { Unspent } from '../../unspent'; +import { toUtxolibBIP32 } from '../../wasmUtil'; import { Musig2Participant } from './musig2'; import { signLegacyTransaction } from './signLegacyTransaction'; @@ -21,33 +21,30 @@ import { getReplayProtectionPubkeys } from './replayProtection'; */ export function signAndVerifyPsbt( psbt: utxolib.bitgo.UtxoPsbt, - signerKeychain: BIP32Interface | BIP32, + signerKeychain: bip32.BIP32Interface | BIP32, rootWalletKeys: fixedScriptWallet.RootWalletKeys | undefined, replayProtection: ReplayProtectionKeys | undefined ): utxolib.bitgo.UtxoPsbt; export function signAndVerifyPsbt( psbt: fixedScriptWallet.BitGoPsbt, - signerKeychain: BIP32Interface | BIP32, + signerKeychain: bip32.BIP32Interface | BIP32, rootWalletKeys: fixedScriptWallet.RootWalletKeys, replayProtection: ReplayProtectionKeys ): fixedScriptWallet.BitGoPsbt; export function signAndVerifyPsbt( psbt: utxolib.bitgo.UtxoPsbt | fixedScriptWallet.BitGoPsbt, - signerKeychain: BIP32Interface | BIP32, + signerKeychain: bip32.BIP32Interface | BIP32, rootWalletKeys: fixedScriptWallet.RootWalletKeys, replayProtection: ReplayProtectionKeys ): utxolib.bitgo.UtxoPsbt | fixedScriptWallet.BitGoPsbt; export function signAndVerifyPsbt( psbt: utxolib.bitgo.UtxoPsbt | fixedScriptWallet.BitGoPsbt, - signerKeychain: BIP32Interface | BIP32, + signerKeychain: bip32.BIP32Interface | BIP32, rootWalletKeys: fixedScriptWallet.RootWalletKeys | undefined, replayProtection: ReplayProtectionKeys | undefined ): utxolib.bitgo.UtxoPsbt | fixedScriptWallet.BitGoPsbt { if (psbt instanceof bitgo.UtxoPsbt) { - if (signerKeychain instanceof BIP32) { - signerKeychain = utxolib.bip32.fromBase58(signerKeychain.toBase58()); - } - return signAndVerifyPsbtUtxolib(psbt, signerKeychain); + return signAndVerifyPsbtUtxolib(psbt, toUtxolibBIP32(signerKeychain)); } assert(rootWalletKeys, 'rootWalletKeys required for wasm-utxo signing'); assert(replayProtection, 'replayProtection required for wasm-utxo signing'); @@ -59,7 +56,7 @@ export async function signTransaction< >( coin: Musig2Participant | Musig2Participant, tx: T, - signerKeychain: BIP32Interface | undefined, + signerKeychain: bip32.BIP32Interface | undefined, coinName: UtxoCoinName, params: { walletId: string | undefined; @@ -84,7 +81,7 @@ export async function signTransaction< const signedPsbt = await signPsbtWithMusig2ParticipantUtxolib( coin as Musig2Participant, tx, - signerKeychain, + signerKeychain ? toUtxolibBIP32(signerKeychain) : undefined, { signingStep: params.signingStep, walletId: params.walletId, diff --git a/modules/abstract-utxo/src/transaction/recipient.ts b/modules/abstract-utxo/src/transaction/recipient.ts index 3998d8c573..8dde2fbd43 100644 --- a/modules/abstract-utxo/src/transaction/recipient.ts +++ b/modules/abstract-utxo/src/transaction/recipient.ts @@ -1,6 +1,6 @@ -import * as utxolib from '@bitgo/utxo-lib'; +import { address } from '@bitgo/wasm-utxo'; -import { getNetworkFromCoinName, UtxoCoinName } from '../names'; +import { UtxoCoinName } from '../names'; const ScriptRecipientPrefix = 'scriptPubKey:'; @@ -29,8 +29,7 @@ export function fromExtendedAddressFormatToScript(extendedAddress: string, coinN if ('script' in result) { return Buffer.from(result.script, 'hex'); } - const network = getNetworkFromCoinName(coinName); - return utxolib.addressFormat.toOutputScriptTryFormats(result.address, network); + return Buffer.from(address.toOutputScriptWithCoin(result.address, coinName)); } export function toOutputScript(v: string | { address: string } | { script: string }, coinName: UtxoCoinName): Buffer { @@ -46,6 +45,8 @@ export function toOutputScript(v: string | { address: string } | { script: strin throw new Error('invalid input'); } +const OP_RETURN = 0x6a; + /** * Convert a script or address to the extended address format. * @param script @@ -53,10 +54,9 @@ export function toOutputScript(v: string | { address: string } | { script: strin * @returns if the script is an OP_RETURN script, then it will be prefixed with `scriptPubKey:`, otherwise it will be converted to an address. */ export function toExtendedAddressFormat(script: Buffer, coinName: UtxoCoinName): string { - const network = getNetworkFromCoinName(coinName); - return script[0] === utxolib.opcodes.OP_RETURN + return script[0] === OP_RETURN ? `${ScriptRecipientPrefix}${script.toString('hex')}` - : utxolib.address.fromOutputScript(script, network); + : address.fromOutputScriptWithCoin(script, coinName); } export function assertValidTransactionRecipient(output: { amount: bigint | number | string; address?: string }): void { diff --git a/modules/abstract-utxo/src/transaction/signTransaction.ts b/modules/abstract-utxo/src/transaction/signTransaction.ts index 44329d8d75..d9a47d9f1c 100644 --- a/modules/abstract-utxo/src/transaction/signTransaction.ts +++ b/modules/abstract-utxo/src/transaction/signTransaction.ts @@ -1,7 +1,6 @@ import _ from 'lodash'; import { BitGoBase } from '@bitgo/sdk-core'; -import * as utxolib from '@bitgo/utxo-lib'; -import { bip32 } from '@bitgo/secp256k1'; +import { BIP32 } from '@bitgo/wasm-utxo'; import buildDebug from 'debug'; import { AbstractUtxoCoin, SignTransactionOptions } from '../abstractUtxoCoin'; @@ -15,14 +14,14 @@ import { encodeTransaction } from './decode'; const debug = buildDebug('bitgo:abstract-utxo:transaction:signTransaction'); -function getSignerKeychain(userPrv: unknown): utxolib.BIP32Interface | undefined { +function getSignerKeychain(userPrv: unknown): BIP32 | undefined { if (userPrv === undefined) { return undefined; } if (typeof userPrv !== 'string') { throw new Error('expected user private key to be a string'); } - const signerKeychain = bip32.fromBase58(userPrv, utxolib.networks.bitcoin); + const signerKeychain = BIP32.fromBase58(userPrv); if (signerKeychain.isNeutered()) { throw new Error('expected user private key but received public key'); } diff --git a/modules/abstract-utxo/src/unspent.ts b/modules/abstract-utxo/src/unspent.ts index 9ab828a7ee..1773b336d0 100644 --- a/modules/abstract-utxo/src/unspent.ts +++ b/modules/abstract-utxo/src/unspent.ts @@ -1,4 +1,4 @@ -import * as utxolib from '@bitgo/utxo-lib'; +import { fixedScriptWallet } from '@bitgo/wasm-utxo'; /** * Unspent transaction output (UTXO) type definition @@ -35,7 +35,7 @@ export interface Unspent { * - index: number (index for wallet derivation) */ export interface WalletUnspent extends Unspent { - chain: utxolib.bitgo.ChainCode; + chain: fixedScriptWallet.ChainCode; index: number; } diff --git a/modules/abstract-utxo/src/verifyKey.ts b/modules/abstract-utxo/src/verifyKey.ts index 204bf4bf56..15dbbc1391 100644 --- a/modules/abstract-utxo/src/verifyKey.ts +++ b/modules/abstract-utxo/src/verifyKey.ts @@ -6,9 +6,7 @@ These are actually not utxo-specific and belong in a more general module. import assert from 'assert'; import buildDebug from 'debug'; -import * as utxolib from '@bitgo/utxo-lib'; -import { bip32 } from '@bitgo/secp256k1'; -import * as bitcoinMessage from 'bitcoinjs-message'; +import { BIP32, message } from '@bitgo/wasm-utxo'; import { BitGoBase, decryptKeychainPrivateKey, KeyIndices } from '@bitgo/sdk-core'; import { VerifyKeySignaturesOptions, VerifyUserPublicKeyOptions } from './abstractUtxoCoin'; @@ -25,7 +23,6 @@ const debug = buildDebug('bitgo:abstract-utxo:verifyKey'); * @return {{backup: boolean, bitgo: boolean}} */ export function verifyKeySignature(params: VerifyKeySignaturesOptions): boolean { - // first, let's verify the integrity of the user key, whose public key is used for subsequent verifications const { userKeychain, keychainToVerify, keySignature } = params; if (!userKeychain) { throw new Error('user keychain is required'); @@ -39,38 +36,20 @@ export function verifyKeySignature(params: VerifyKeySignaturesOptions): boolean throw new Error('key signature is required'); } - // verify the signature against the user public key assert(userKeychain.pub); - const publicKey = bip32.fromBase58(userKeychain.pub).publicKey; - // Due to interface of `bitcoinMessage`, we need to convert the public key to an address. - // Note that this address has no relationship to on-chain transactions. We are - // only interested in the address as a representation of the public key. - const signingAddress = utxolib.address.toBase58Check( - utxolib.crypto.hash160(publicKey), - utxolib.networks.bitcoin.pubKeyHash, - // we do not pass `this.network` here because it would fail for zcash - // the bitcoinMessage library decodes the address and throws away the first byte - // because zcash has a two-byte prefix, verify() decodes zcash addresses to an invalid pubkey hash - utxolib.networks.bitcoin - ); - - // BG-5703: use BTC mainnet prefix for all key signature operations - // (this means do not pass a prefix parameter, and let it use the default prefix instead) + const publicKey = BIP32.fromBase58(userKeychain.pub).publicKey; + assert(keychainToVerify.pub); try { - return bitcoinMessage.verify(keychainToVerify.pub, signingAddress, Buffer.from(keySignature, 'hex')); + return message.verifyMessage(keychainToVerify.pub, publicKey, Buffer.from(keySignature, 'hex')); } catch (e) { - debug('error thrown from bitcoinmessage while verifying key signature', e); + debug('error thrown from wasm-utxo while verifying key signature', e); return false; } } /** * Verify signatures against the user private key over the change wallet extended keys - * @param {ParsedTransaction} tx - * @param {Keychain} userKeychain - * @return {boolean} - * @protected */ export function verifyCustomChangeKeySignatures( tx: ParsedTransaction, @@ -104,10 +83,6 @@ export function verifyCustomChangeKeySignatures /** * Decrypt the wallet's user private key and verify that the claimed public key matches - * @param {BitGoBase} bitgo - * @param {VerifyUserPublicKeyOptions} params - * @return {boolean} - * @protected */ export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): boolean { const { userKeychain, txParams, disableNetworking } = params; @@ -117,7 +92,6 @@ export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKe const userPub = userKeychain.pub; - // decrypt the user private key, so we can verify that the claimed public key is a match let userPrv = userKeychain.prv; if (!userPrv && txParams.walletPassphrase) { userPrv = decryptKeychainPrivateKey(bitgo, userKeychain, txParams.walletPassphrase); @@ -132,7 +106,7 @@ export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKe throw new Error(errorMessage); } } else { - const userPrivateKey = bip32.fromBase58(userPrv); + const userPrivateKey = BIP32.fromBase58(userPrv); if (userPrivateKey.toBase58() === userPrivateKey.neutered().toBase58()) { throw new Error('user private key is only public'); } diff --git a/modules/abstract-utxo/src/wasmUtil.ts b/modules/abstract-utxo/src/wasmUtil.ts index 1f67833d48..f22e4fcf50 100644 --- a/modules/abstract-utxo/src/wasmUtil.ts +++ b/modules/abstract-utxo/src/wasmUtil.ts @@ -1,7 +1,7 @@ -import { BIP32, ECPair, Psbt, descriptorWallet } from '@bitgo/wasm-utxo'; +import { BIP32, bip32, ECPair, Psbt, descriptorWallet } from '@bitgo/wasm-utxo'; import * as utxolib from '@bitgo/utxo-lib'; -export type BIP32Key = BIP32 | utxolib.BIP32Interface; +export type BIP32Key = BIP32 | bip32.BIP32Interface | utxolib.BIP32Interface; export type ECPairKey = ECPair | utxolib.ECPairInterface | Uint8Array; export type UtxoLibPsbt = utxolib.Psbt | utxolib.bitgo.UtxoPsbt; @@ -28,6 +28,14 @@ export function toWasmBIP32(key: BIP32Key): BIP32 { return BIP32.fromBase58(key.toBase58()); } +/** + * Convert a wasm-utxo BIP32 to a utxo-lib BIP32Interface. + * Used at boundaries where utxo-lib APIs require their own BIP32Interface type. + */ +export function toUtxolibBIP32(key: BIP32Key): utxolib.BIP32Interface { + return utxolib.bip32.fromBase58(key.toBase58()); +} + export function toWasmECPair(key: ECPairKey): ECPair { if (key instanceof ECPair) { return key; diff --git a/modules/abstract-utxo/test/unit/coins.ts b/modules/abstract-utxo/test/unit/coins.ts index 0040eee4d8..64fcc080cd 100644 --- a/modules/abstract-utxo/test/unit/coins.ts +++ b/modules/abstract-utxo/test/unit/coins.ts @@ -2,9 +2,9 @@ import * as assert from 'assert'; import * as utxolib from '@bitgo/utxo-lib'; -import { getMainnetCoinName, getNetworkFromCoinName, utxoCoinsMainnet, utxoCoinsTestnet } from '../../src/names'; +import { getMainnetCoinName, utxoCoinsMainnet, utxoCoinsTestnet } from '../../src/names'; -import { getUtxoCoinForNetwork, utxoCoins } from './util'; +import { getNetworkForCoinName, getUtxoCoinForNetwork, utxoCoins } from './util'; describe('utxoCoins', function () { it('has expected chain/network values for items', function () { @@ -13,7 +13,7 @@ describe('utxoCoins', function () { c.getChain(), c.getFamily(), c.getFullName(), - utxolib.getNetworkName(getNetworkFromCoinName(c.name)), + utxolib.getNetworkName(getNetworkForCoinName(c.name)), ]), [ ['btc', 'btc', 'Bitcoin', 'bitcoin'], diff --git a/modules/abstract-utxo/test/unit/util/transaction.ts b/modules/abstract-utxo/test/unit/util/transaction.ts index 6a358a5442..bc62c0b943 100644 --- a/modules/abstract-utxo/test/unit/util/transaction.ts +++ b/modules/abstract-utxo/test/unit/util/transaction.ts @@ -3,8 +3,10 @@ import assert from 'assert'; import * as utxolib from '@bitgo/utxo-lib'; import { ECPair, fixedScriptWallet, hasPsbtMagic, address as wasmAddress } from '@bitgo/wasm-utxo'; -import { getCoinName, UtxoCoinName } from '../../../src/names'; +import type { UtxoCoinName } from '../../../src/names'; import type { Unspent, WalletUnspent } from '../../../src/unspent'; + +import { getCoinNameForNetwork } from './utxoCoins'; const { isWalletUnspent, signInputWithUnspent } = utxolib.bitgo; type UtxolibRootWalletKeys = utxolib.bitgo.RootWalletKeys; type WasmRootWalletKeys = fixedScriptWallet.RootWalletKeys; @@ -28,7 +30,7 @@ function toTxOutput( network: utxolib.Network ): utxolib.TxOutput { return { - script: Buffer.from(wasmAddress.toOutputScriptWithCoin(u.address, getCoinName(network))), + script: Buffer.from(wasmAddress.toOutputScriptWithCoin(u.address, getCoinNameForNetwork(network))), value: u.value, }; } diff --git a/modules/abstract-utxo/test/unit/util/unspents.ts b/modules/abstract-utxo/test/unit/util/unspents.ts index c303a90139..ab8c85873e 100644 --- a/modules/abstract-utxo/test/unit/util/unspents.ts +++ b/modules/abstract-utxo/test/unit/util/unspents.ts @@ -3,9 +3,10 @@ import { getSeed } from '@bitgo/sdk-test'; import * as wasmUtxo from '@bitgo/wasm-utxo'; import { getReplayProtectionAddresses } from '../../../src'; -import { getCoinName, isUtxoCoinName, type UtxoCoinName } from '../../../src/names'; +import { isUtxoCoinName, type UtxoCoinName } from '../../../src/names'; import type { Unspent, UnspentWithPrevTx, WalletUnspent } from '../../../src/unspent'; +import { getCoinNameForNetwork } from './utxoCoins'; import { getWalletAddress } from './address'; const { chainCodesP2sh, getExternalChainCode, getInternalChainCode } = utxolib.bitgo; @@ -25,7 +26,7 @@ function toCoinName(network: NetworkArg): UtxoCoinName { } return network; } - return getCoinName(network); + return getCoinNameForNetwork(network); } export type InputScriptType = utxolib.bitgo.outputScripts.ScriptType2Of3 | 'replayProtection'; diff --git a/modules/abstract-utxo/test/unit/util/utxoCoins.ts b/modules/abstract-utxo/test/unit/util/utxoCoins.ts index 019211622d..67f771d929 100644 --- a/modules/abstract-utxo/test/unit/util/utxoCoins.ts +++ b/modules/abstract-utxo/test/unit/util/utxoCoins.ts @@ -26,9 +26,57 @@ import { Tzec, getReplayProtectionAddresses, } from '../../../src'; +import type { UtxoCoinName } from '../../../src/names'; export const defaultBitGo = TestBitGo.decorate(BitGoAPI, { env: 'mock' }); +/** Map coin names to utxolib network objects for test infrastructure */ +const coinNameToNetwork: Record = { + btc: utxolib.networks.bitcoin, + tbtc: utxolib.networks.testnet, + tbtcsig: utxolib.networks.bitcoinPublicSignet, + tbtc4: utxolib.networks.bitcoinTestnet4, + tbtcbgsig: utxolib.networks.bitcoinBitGoSignet, + bch: utxolib.networks.bitcoincash, + tbch: utxolib.networks.bitcoincashTestnet, + bcha: utxolib.networks.ecash, + tbcha: utxolib.networks.ecashTest, + bsv: utxolib.networks.bitcoinsv, + tbsv: utxolib.networks.bitcoinsvTestnet, + btg: utxolib.networks.bitcoingold, + dash: utxolib.networks.dash, + tdash: utxolib.networks.dashTest, + doge: utxolib.networks.dogecoin, + tdoge: utxolib.networks.dogecoinTest, + ltc: utxolib.networks.litecoin, + tltc: utxolib.networks.litecoinTest, + zec: utxolib.networks.zcash, + tzec: utxolib.networks.zcashTest, +}; + +/** + * Get utxolib Network for a coin name. For test infrastructure only. + */ +export function getNetworkForCoinName(coinName: string): utxolib.Network { + const network = coinNameToNetwork[coinName]; + if (!network) { + throw new Error(`Unknown coin name: ${coinName}`); + } + return network; +} + +/** + * Get coin name for a utxolib Network. For test infrastructure only. + */ +export function getCoinNameForNetwork(network: utxolib.Network): UtxoCoinName { + for (const [name, n] of Object.entries(coinNameToNetwork)) { + if (n === network) { + return name as UtxoCoinName; + } + } + throw new Error(`Unknown network: ${utxolib.getNetworkName(network)}`); +} + const utxoCoinClasses = [ Btc, Tbtc, @@ -61,7 +109,10 @@ function getUtxoCoins(bitgo: BitGoAPI = defaultBitGo): AbstractUtxoCoin[] { throw new Error(`error creating ${cls.name}: ${e}`); } }) - .sort((a, b) => utxolib.getNetworkList().indexOf(a.network) - utxolib.getNetworkList().indexOf(b.network)); + .sort((a, b) => { + const networkList = utxolib.getNetworkList(); + return networkList.indexOf(getNetworkForCoinName(a.name)) - networkList.indexOf(getNetworkForCoinName(b.name)); + }); } export const utxoCoins = getUtxoCoins(); @@ -92,7 +143,7 @@ export function getUtxoCoin(name: string): AbstractUtxoCoin { export function getUtxoCoinForNetwork(n: utxolib.Network): AbstractUtxoCoin { for (const c of utxoCoins) { - if (c.network === n) { + if (getNetworkForCoinName(c.name) === n) { return c; } }