From ab29ff90390f409786a5815f8946a67cbcf66c53 Mon Sep 17 00:00:00 2001 From: Zahin Mohammad Date: Tue, 3 Sep 2024 17:52:08 -0400 Subject: [PATCH 1/4] feat(abstract-cosmos): remove GG18 recovery tx signing --- modules/abstract-cosmos/src/cosmosCoin.ts | 81 +++++++++++++---------- modules/sdk-coin-atom/test/unit/atom.ts | 12 ---- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/modules/abstract-cosmos/src/cosmosCoin.ts b/modules/abstract-cosmos/src/cosmosCoin.ts index e48e6489a5..96d8d26cbb 100644 --- a/modules/abstract-cosmos/src/cosmosCoin.ts +++ b/modules/abstract-cosmos/src/cosmosCoin.ts @@ -22,7 +22,7 @@ import { VerifyAddressOptions, VerifyTransactionOptions, } from '@bitgo/sdk-core'; -import { EcdsaPaillierProof, EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; +import { EcdsaPaillierProof, EcdsaRangeProof, EcdsaTypes, DklsTypes, DklsUtils } from '@bitgo/sdk-lib-mpc'; import { BaseCoin as StaticsBaseCoin, CoinFamily } from '@bitgo/statics'; import { bip32 } from '@bitgo/utxo-lib'; import { Coin } from '@cosmjs/stargate'; @@ -136,7 +136,7 @@ export class CosmosCoin extends BaseCoin { * @returns {CosmosLikeCoinRecoveryOutput} the serialized transaction hex string and index * of the address being swept */ - async recover(params: RecoveryOptions, openSSLBytes: Uint8Array): Promise { + async recover(params: RecoveryOptions): Promise { // Step 1: Check if params contains the required parameters if (!params.bitgoKey) { throw new Error('missing bitgoKey'); @@ -157,9 +157,6 @@ export class CosmosCoin extends BaseCoin { if (!params.walletPassphrase) { throw new Error('missing wallet passphrase'); } - if (!openSSLBytes) { - throw new Error('missing openSSLBytes'); - } // Step 2: Fetch the bitgo key from params const userKey = params.userKey.replace(/\s/g, ''); @@ -215,47 +212,61 @@ export class CosmosCoin extends BaseCoin { const signableHex = unsignedTransaction.signablePayload.toString('hex'); const isGG18SigningMaterial = ECDSAUtils.isGG18SigningMaterial(userKey, params.walletPassphrase); - let signature: ECDSA.Signature; - if (isGG18SigningMaterial) { - // GG18 - const [userKeyCombined, backupKeyCombined] = ((): [ - ECDSAMethodTypes.KeyCombined | undefined, - ECDSAMethodTypes.KeyCombined | undefined - ] => { - const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares( - userKey, - backupKey, - params.walletPassphrase - ); - return [userKeyCombined, backupKeyCombined]; - })(); - - if (!userKeyCombined || !backupKeyCombined) { - throw new Error('Missing combined key shares for user or backup'); - } + /** + * MPCv2 Signing Params + */ + let userKeyShare: Buffer; + let backupKeyShare: Buffer; + let commonKeyChain: string; - // Step 7: Sign the tx - signature = await this.signRecoveryTSS(userKeyCombined, backupKeyCombined, signableHex, openSSLBytes); - } else { - // DKLS - const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares( + // Step 7: Prepare the key shares for signing + if (isGG18SigningMaterial) { + // Retrofit the GG18 keys to DKLS + const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares( userKey, backupKey, params.walletPassphrase - ); // baseAddress is not extracted + ); - if (!userKeyShare || !backupKeyShare || !commonKeyChain) { - throw new Error('Missing combined key shares for user or backup or common'); + const aKeyCombine = { + xShare: userKeyCombined.xShare, + }; + const bKeyCombine = { + xShare: backupKeyCombined.xShare, + }; + const retrofitDataA: DklsTypes.RetrofitData = { + xShare: aKeyCombine.xShare, + }; + const retrofitDataB: DklsTypes.RetrofitData = { + xShare: bKeyCombine.xShare, + }; + const [user, backup] = await DklsUtils.generate2of2KeyShares(retrofitDataA, retrofitDataB); + + userKeyShare = user.getKeyShare(); + backupKeyShare = backup.getKeyShare(); + if (DklsTypes.getCommonKeychain(userKeyShare) !== DklsTypes.getCommonKeychain(backupKeyShare)) { + throw new Error('Common keychain mismatch! Ensure the correct user and backup keys where provided!'); } + commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); + } else { + // DKLS + const mpcv2KeyShares = await ECDSAUtils.getMpcV2RecoveryKeyShares(userKey, backupKey, params.walletPassphrase); // baseAddress is not extracted - // Step 7: Sign the tx - const message = unsignedTransaction.signablePayload; - const messageHash = (utils.getHashFunction() || createHash('sha256')).update(message).digest(); + userKeyShare = mpcv2KeyShares.userKeyShare; + backupKeyShare = mpcv2KeyShares.backupKeyShare; + commonKeyChain = mpcv2KeyShares.commonKeyChain; - signature = await ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain); + if (!userKeyShare || !backupKeyShare || !commonKeyChain) { + throw new Error('Missing combined key shares for user or backup or common'); + } } + // Step 8: Sign the tx + const message = unsignedTransaction.signablePayload; + const messageHash = (utils.getHashFunction() || createHash('sha256')).update(message).digest(); + const signature = await ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain); + const signableBuffer = Buffer.from(signableHex, 'hex'); MPC.verify(signableBuffer, signature, this.getHashFunction()); const cosmosKeyPair = this.getKeyPair(publicKey); diff --git a/modules/sdk-coin-atom/test/unit/atom.ts b/modules/sdk-coin-atom/test/unit/atom.ts index 39c100d054..fec6c535b5 100644 --- a/modules/sdk-coin-atom/test/unit/atom.ts +++ b/modules/sdk-coin-atom/test/unit/atom.ts @@ -574,18 +574,6 @@ describe('ATOM', function () { .should.rejectedWith('missing wallet passphrase'); }); - it('should throw error if openSSLBytes is not present', async function () { - await basecoin - .recover({ - userKey: wrwUser.userKey, - backupKey: wrwUser.backupKey, - bitgoKey: wrwUser.bitgoKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }) - .should.rejectedWith('missing openSSLBytes'); - }); - it('should throw error if there is no balance', async function () { await basecoin .recover( From b550b288036e20a40652a47c107eefce448199a4 Mon Sep 17 00:00:00 2001 From: Zahin Mohammad Date: Tue, 3 Sep 2024 17:48:39 -0400 Subject: [PATCH 2/4] feat(abstract-eth): remove GG18 recovery tx signing TICKET: WP-2954 --- .../src/abstractEthLikeNewCoins.ts | 185 +++++------------- .../test/v2/fixtures/tss/recoveryFixtures.ts | 2 - modules/bitgo/test/v2/unit/recovery.ts | 35 +++- 3 files changed, 83 insertions(+), 139 deletions(-) diff --git a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts index 1bdb28869b..59237feb75 100644 --- a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts +++ b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts @@ -6,7 +6,6 @@ import { BitGoBase, BuildNftTransferDataOptions, common, - ECDSA, Ecdsa, ECDSAMethodTypes, EthereumLibraryUnavailableError, @@ -14,7 +13,6 @@ import { FullySignedTransaction, getIsUnsignedSweep, HalfSignedTransaction, - hexToBigInt, InvalidAddressError, InvalidAddressVerificationObjectPropertyError, IWallet, @@ -36,7 +34,7 @@ import { Wallet, ECDSAUtils, } from '@bitgo/sdk-core'; -import { EcdsaPaillierProof, EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; +import { DklsTypes, DklsUtils } from '@bitgo/sdk-lib-mpc'; import { BaseCoin as StaticsBaseCoin, CoinMap, @@ -202,15 +200,19 @@ interface UnformattedTxInfo { recipient: Recipient; } +/** + * @deprecated: this type is no longer used and will be removed in future versions + */ export type RecoverOptionsWithBytes = { isTss: true; openSSLBytes: Uint8Array; }; + export type NonTSSRecoverOptions = { - isTss?: false | undefined; + isTss?: boolean; }; -export type TSSRecoverOptions = RecoverOptionsWithBytes | NonTSSRecoverOptions; +export type TSSRecoverOptions = NonTSSRecoverOptions; export type RecoverOptions = { userKey: string; @@ -1051,108 +1053,6 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { } } - /** - * Method to sign tss recovery transaction - * @param {ECDSA.KeyCombined} userKeyCombined - * @param {ECDSA.KeyCombined} backupKeyCombined - * @param {string} txHex - * @param {Object} options - * @param {EcdsaTypes.SerializedNtilde} options.rangeProofChallenge - * @returns {Promise} - */ - private async signRecoveryTSS( - userKeyCombined: ECDSA.KeyCombined, - backupKeyCombined: ECDSA.KeyCombined, - txHex: string, - openSSLBytes: Uint8Array, - { - rangeProofChallenge, - }: { - rangeProofChallenge?: EcdsaTypes.SerializedNtilde; - } = {} - ): Promise { - if (!userKeyCombined || !backupKeyCombined) { - throw new Error('Missing key combined shares for user or backup'); - } - - const MPC = new Ecdsa(); - const signerOneIndex = userKeyCombined.xShare.i; - const signerTwoIndex = backupKeyCombined.xShare.i; - - rangeProofChallenge = - rangeProofChallenge ?? EcdsaTypes.serializeNtildeWithProofs(await EcdsaRangeProof.generateNtilde(openSSLBytes)); - - const userToBackupPaillierChallenge = await EcdsaPaillierProof.generateP( - hexToBigInt(userKeyCombined.yShares[signerTwoIndex].n) - ); - const backupToUserPaillierChallenge = await EcdsaPaillierProof.generateP( - hexToBigInt(backupKeyCombined.yShares[signerOneIndex].n) - ); - - const userXShare = MPC.appendChallenge( - userKeyCombined.xShare, - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: userToBackupPaillierChallenge }) - ); - const userYShare = MPC.appendChallenge( - userKeyCombined.yShares[signerTwoIndex], - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: backupToUserPaillierChallenge }) - ); - const backupXShare = MPC.appendChallenge( - backupKeyCombined.xShare, - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: backupToUserPaillierChallenge }) - ); - const backupYShare = MPC.appendChallenge( - backupKeyCombined.yShares[signerOneIndex], - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: userToBackupPaillierChallenge }) - ); - - const signShares: ECDSA.SignShareRT = await MPC.signShare(userXShare, userYShare); - - const signConvertS21 = await MPC.signConvertStep1({ - xShare: backupXShare, - yShare: backupYShare, // YShare corresponding to the other participant signerOne - kShare: signShares.kShare, - }); - const signConvertS12 = await MPC.signConvertStep2({ - aShare: signConvertS21.aShare, - wShare: signShares.wShare, - }); - const signConvertS21_2 = await MPC.signConvertStep3({ - muShare: signConvertS12.muShare, - bShare: signConvertS21.bShare, - }); - - const [signCombineOne, signCombineTwo] = [ - MPC.signCombine({ - gShare: signConvertS12.gShare, - signIndex: { - i: signConvertS12.muShare.i, - j: signConvertS12.muShare.j, - }, - }), - MPC.signCombine({ - gShare: signConvertS21_2.gShare, - signIndex: { - i: signConvertS21_2.signIndex.i, - j: signConvertS21_2.signIndex.j, - }, - }), - ]; - - const MESSAGE = Buffer.from(txHex, 'hex'); - - const [signA, signB] = [ - MPC.sign(MESSAGE, signCombineOne.oShare, signCombineTwo.dShare, Keccak('keccak256')), - MPC.sign(MESSAGE, signCombineTwo.oShare, signCombineOne.dShare, Keccak('keccak256')), - ]; - - return MPC.constructSignature([signA, signB]); - } - /** * Helper which combines key shares of user and backup * @param {string} userPublicOrPrivateKeyShare @@ -1284,7 +1184,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { */ async recover(params: RecoverOptions): Promise { if (params.isTss === true) { - return this.recoverTSS(params, params.openSSLBytes); + return this.recoverTSS(params); } return this.recoverEthLike(params); } @@ -1939,10 +1839,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { * Recovers a tx with TSS key shares * same expected arguments as recover method, but with TSS key shares */ - protected async recoverTSS( - params: RecoverOptions, - openSSLBytes: Uint8Array - ): Promise { + protected async recoverTSS(params: RecoverOptions): Promise { this.validateRecoveryParams(params); // Clean up whitespace from entered values const userPublicOrPrivateKeyShare = params.userKey.replace(/\s/g, ''); @@ -1979,43 +1876,67 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { userPublicOrPrivateKeyShare, params.walletPassphrase ); - let signature: ECDSAMethodTypes.Signature; - let unsignedTx: EthLikeTxLib.Transaction | EthLikeTxLib.FeeMarketEIP1559Transaction; + /** + * MPCv2 Signing Params + */ + let userKeyShare: Buffer; + let backupKeyShare: Buffer; + let commonKeyChain: string; + + // Prepare the key shares for signing if (isGG18SigningMaterial) { + // Retrofit the GG18 keys to DKLS const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares( userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, params.walletPassphrase ); - const backupKeyPair = new KeyPairLib({ pub: backupKeyCombined.xShare.y }); - const baseAddress = backupKeyPair.getAddress(); - unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx; + const aKeyCombine = { + xShare: userKeyCombined.xShare, + }; + const bKeyCombine = { + xShare: backupKeyCombined.xShare, + }; + const retrofitDataA: DklsTypes.RetrofitData = { + xShare: aKeyCombine.xShare, + }; + const retrofitDataB: DklsTypes.RetrofitData = { + xShare: bKeyCombine.xShare, + }; + const [user, backup] = await DklsUtils.generate2of2KeyShares(retrofitDataA, retrofitDataB); - signature = await this.signRecoveryTSS( - userKeyCombined, - backupKeyCombined, - unsignedTx.getMessageToSign(false).toString('hex'), - openSSLBytes - ); + userKeyShare = user.getKeyShare(); + backupKeyShare = backup.getKeyShare(); + if (DklsTypes.getCommonKeychain(userKeyShare) !== DklsTypes.getCommonKeychain(backupKeyShare)) { + throw new Error('Common keychain mismatch! Ensure the correct user and backup keys where provided!'); + } + commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); } else { - const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares( + // DKLS + const mpcv2KeyShares = await ECDSAUtils.getMpcV2RecoveryKeyShares( userPublicOrPrivateKeyShare, backupPrivateOrPublicKeyShare, params.walletPassphrase ); - const MPC = new Ecdsa(); - const derivedCommonKeyChain = MPC.deriveUnhardened(commonKeyChain, 'm'); - const backupKeyPair = new KeyPairLib({ pub: derivedCommonKeyChain.slice(0, 66) }); - const baseAddress = backupKeyPair.getAddress(); + userKeyShare = mpcv2KeyShares.userKeyShare; + backupKeyShare = mpcv2KeyShares.backupKeyShare; + commonKeyChain = mpcv2KeyShares.commonKeyChain; - unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx; + if (!userKeyShare || !backupKeyShare || !commonKeyChain) { + throw new Error('Missing combined key shares for user or backup or common'); + } + } - const messageHash = unsignedTx.getMessageToSign(true); + const MPC = new Ecdsa(); + const derivedCommonKeyChain = MPC.deriveUnhardened(commonKeyChain, 'm/0'); + const keyPair = new KeyPairLib({ pub: derivedCommonKeyChain.slice(0, 66) }); + const baseAddress = keyPair.getAddress(); - signature = await ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain); - } + const unsignedTx = (await this.buildTssRecoveryTxn(baseAddress, gasPrice, gasLimit, params)).tx; + const messageHash = unsignedTx.getMessageToSign(true); + const signature = await ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain); const ethCommmon = AbstractEthLikeNewCoins.getEthLikeCommon(params.eip1559, params.replayProtectionOptions); const signedTx = this.getSignedTxFromSignature(ethCommmon, unsignedTx, signature); diff --git a/modules/bitgo/test/v2/fixtures/tss/recoveryFixtures.ts b/modules/bitgo/test/v2/fixtures/tss/recoveryFixtures.ts index 49ab43c6ef..99c165a056 100644 --- a/modules/bitgo/test/v2/fixtures/tss/recoveryFixtures.ts +++ b/modules/bitgo/test/v2/fixtures/tss/recoveryFixtures.ts @@ -1,5 +1,4 @@ export const ethLikeGG18Keycard = { - senderAddress: '0xf3243334a491b6558f3f2c791afecc6a5eb955fa', destinationAddress: '0xac05da78464520aa7c9d4c19bd7a440b111b3054', userKey: '{"iv":"bn1gTo9+ycrgiqtm0fkHlg==","v":1,"iter":10000,"ks":256,"ts":64,"mode"\n' + @@ -159,7 +158,6 @@ export const ethLikeGG18Keycard = { walletPassphrase: 'Ghghjkg!455544llll', }; export const ethLikeDKLSKeycard = { - senderAddress: '0x88c2ab227908d39f6afdb85203dca3e937bb77af', destinationAddress: '0xac05da78464520aa7c9d4c19bd7a440b111b3054', userKey: '{"iv":"82EY6GQ62/d9EzrkcIcqfA==","v":1,"iter":10000,"ks":256,"ts":64,"mode"\n' + diff --git a/modules/bitgo/test/v2/unit/recovery.ts b/modules/bitgo/test/v2/unit/recovery.ts index 9d3e3619f0..c8e1ba13a0 100644 --- a/modules/bitgo/test/v2/unit/recovery.ts +++ b/modules/bitgo/test/v2/unit/recovery.ts @@ -2,12 +2,13 @@ import * as should from 'should'; import * as nock from 'nock'; import { mockSerializedChallengeWithProofs, TestBitGo } from '@bitgo/sdk-test'; -import { BitGo } from '../../../src'; -import { krsProviders } from '@bitgo/sdk-core'; +import { BitGo, decrypt } from '../../../src'; +import { Ecdsa, ECDSAMethodTypes, ECDSAUtils, krsProviders } from '@bitgo/sdk-core'; import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; import { TransactionFactory } from '@ethereumjs/tx'; import * as sinon from 'sinon'; import { ethLikeDKLSKeycard, ethLikeGG18Keycard } from '../fixtures/tss/recoveryFixtures'; +import { KeyPair } from '@bitgo/sdk-coin-eth'; const recoveryNocks = require('../lib/recovery-nocks'); @@ -994,7 +995,22 @@ describe('Recovery:', function () { it('should construct a recovery tx with TSS', async function () { recoveryNocks.nockEthLikeRecovery(bitgo, nockTSSData); const basecoin = bitgo.coin('hteth'); - const baseAddress = ethLikeGG18Keycard.senderAddress; + const MPC = new Ecdsa(); + + const userSigningMaterial = JSON.parse( + decrypt(ethLikeGG18Keycard.walletPassphrase, ethLikeGG18Keycard.userKey.replace(/\s/g, '')) + ) as ECDSAMethodTypes.SigningMaterial; + + const userSigningKeyDerived = MPC.keyDerive( + userSigningMaterial.pShare, + [userSigningMaterial.bitgoNShare, userSigningMaterial.backupNShare!], + 'm/0' + ); + + const commonKeychain = userSigningKeyDerived.xShare.y + userSigningKeyDerived.xShare.chaincode; + const derivedCommonKeyChain = MPC.deriveUnhardened(commonKeychain, 'm/0'); + const keyPair = new KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) }); + const baseAddress = keyPair.getAddress(); const nockTSSDataWithBaseAddress = nockTSSData.map((data) => { return { @@ -1010,7 +1026,7 @@ describe('Recovery:', function () { recoveryParams = { userKey: ethLikeGG18Keycard.userKey, backupKey: ethLikeGG18Keycard.backupKey, - walletContractAddress: ethLikeGG18Keycard.senderAddress, + walletContractAddress: baseAddress, recoveryDestination: ethLikeGG18Keycard.destinationAddress, walletPassphrase: ethLikeGG18Keycard.walletPassphrase, eip1559: { @@ -1048,7 +1064,16 @@ describe('Recovery:', function () { ]) { recoveryNocks.nockEthLikeRecovery(bitgo, nockTSSData); const basecoin = bitgo.coin(coin); - const baseAddress = ethLikeDKLSKeycard.senderAddress; + const mpcv2KeyShares = await ECDSAUtils.getMpcV2RecoveryKeyShares( + ethLikeDKLSKeycard.userKey.replace(/\s/g, ''), + ethLikeDKLSKeycard.backupKey.replace(/\s/g, ''), + ethLikeDKLSKeycard.walletPassphrase + ); + const MPC = new Ecdsa(); + const derivedCommonKeyChain = MPC.deriveUnhardened(mpcv2KeyShares.commonKeyChain, 'm/0'); + const keyPair = new KeyPair({ pub: derivedCommonKeyChain.slice(0, 66) }); + const baseAddress = keyPair.getAddress(); + const nockTSSDataWithBaseAddress = nockTSSData.map((data) => { return { ...data, From 0f71cb924ba66ea83ae1c3cfc2bdea0ce6cd7968 Mon Sep 17 00:00:00 2001 From: Zahin Mohammad Date: Tue, 3 Sep 2024 19:12:27 -0400 Subject: [PATCH 3/4] chore(root): remove openSSLBytes from tests TICKET: WP-2954 --- modules/sdk-coin-atom/package.json | 1 - modules/sdk-coin-atom/test/unit/atom.ts | 76 +++++-------- modules/sdk-coin-bld/package.json | 1 - modules/sdk-coin-bld/test/unit/bld.ts | 104 +++++++----------- modules/sdk-coin-coreum/package.json | 1 - modules/sdk-coin-coreum/test/unit/coreum.ts | 83 ++++++-------- modules/sdk-coin-hash/package.json | 1 - modules/sdk-coin-hash/test/unit/hash.ts | 68 +++++------- modules/sdk-coin-injective/package.json | 1 - .../sdk-coin-injective/test/unit/injective.ts | 83 ++++++-------- modules/sdk-coin-osmo/package.json | 1 - modules/sdk-coin-osmo/test/unit/osmo.ts | 68 +++++------- modules/sdk-coin-polygon/package.json | 1 - modules/sdk-coin-polygon/test/unit/polygon.ts | 6 - modules/sdk-coin-sei/package.json | 1 - modules/sdk-coin-sei/test/unit/sei.ts | 83 ++++++-------- modules/sdk-coin-tia/package.json | 1 - modules/sdk-coin-tia/test/unit/tia.ts | 83 ++++++-------- modules/sdk-coin-zeta/package.json | 1 - modules/sdk-coin-zeta/test/unit/zeta.ts | 104 +++++++----------- 20 files changed, 292 insertions(+), 476 deletions(-) diff --git a/modules/sdk-coin-atom/package.json b/modules/sdk-coin-atom/package.json index 30a623f7ce..ab5b560fa6 100644 --- a/modules/sdk-coin-atom/package.json +++ b/modules/sdk-coin-atom/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183", "axios": "^1.3.4" diff --git a/modules/sdk-coin-atom/test/unit/atom.ts b/modules/sdk-coin-atom/test/unit/atom.ts index fec6c535b5..41d52d4f1b 100644 --- a/modules/sdk-coin-atom/test/unit/atom.ts +++ b/modules/sdk-coin-atom/test/unit/atom.ts @@ -21,10 +21,6 @@ import { } from '../resources/atom'; import should = require('should'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('ATOM', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -433,16 +429,13 @@ describe('ATOM', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userKey, - backupKey: wrwUser.backupKey, - bitgoKey: wrwUser.bitgoKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userKey, + backupKey: wrwUser.backupKey, + bitgoKey: wrwUser.bitgoKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -460,16 +453,13 @@ describe('ATOM', function () { }); it('should recover funds for non-bitgo recoveries - DKLS type', async function () { - const res = await basecoin.recover( - { - userKey: wrwUserDkls.userKey, - backupKey: wrwUserDkls.backupKey, - bitgoKey: wrwUserDkls.bitgoKey, - walletPassphrase: wrwUserDkls.walletPassphrase, - recoveryDestination: wrwUserDkls.destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUserDkls.userKey, + backupKey: wrwUserDkls.backupKey, + bitgoKey: wrwUserDkls.bitgoKey, + walletPassphrase: wrwUserDkls.walletPassphrase, + recoveryDestination: wrwUserDkls.destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -487,18 +477,15 @@ describe('ATOM', function () { }); it('should redelegate funds to new validator', async function () { - const res = await basecoin.redelegate( - { - userKey: wrwUser.userKey, - backupKey: wrwUser.backupKey, - bitgoKey: wrwUser.bitgoKey, - walletPassphrase: wrwUser.walletPassphrase, - amountToRedelegate: '10000000000000000', - validatorSrcAddress: 'cosmosvaloper1409te27da74uahh6hn0040x7l272hjs2padjuz', - validatorDstAddress: 'cosmosvaloper183aycgtstp67r6s4vd7ts2npp2ckk4xah7rxj6', - }, - openSSLBytes - ); + const res = await basecoin.redelegate({ + userKey: wrwUser.userKey, + backupKey: wrwUser.backupKey, + bitgoKey: wrwUser.bitgoKey, + walletPassphrase: wrwUser.walletPassphrase, + amountToRedelegate: '10000000000000000', + validatorSrcAddress: 'cosmosvaloper1409te27da74uahh6hn0040x7l272hjs2padjuz', + validatorDstAddress: 'cosmosvaloper183aycgtstp67r6s4vd7ts2npp2ckk4xah7rxj6', + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); @@ -576,16 +563,13 @@ describe('ATOM', function () { it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userKey, - backupKey: wrwUser.backupKey, - bitgoKey: wrwUser.bitgoKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userKey, + backupKey: wrwUser.backupKey, + bitgoKey: wrwUser.bitgoKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-bld/package.json b/modules/sdk-coin-bld/package.json index 7130d7cb0c..e7942aa9ca 100644 --- a/modules/sdk-coin-bld/package.json +++ b/modules/sdk-coin-bld/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-bld/test/unit/bld.ts b/modules/sdk-coin-bld/test/unit/bld.ts index c09d2d682d..ab729cbdb6 100644 --- a/modules/sdk-coin-bld/test/unit/bld.ts +++ b/modules/sdk-coin-bld/test/unit/bld.ts @@ -19,10 +19,6 @@ import { } from '../resources/bld'; import should = require('should'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('BLD', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -378,16 +374,13 @@ describe('BLD', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -406,18 +399,15 @@ describe('BLD', function () { }); it('should redelegate funds to new validator', async function () { - const res = await basecoin.redelegate( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - amountToRedelegate: '10000000000000000', - validatorSrcAddress: 'agoricvaloper1wy3h3gne94xpmnjwfwd3eyaytv9g4nj6yykkkj', - validatorDstAddress: 'agoricvaloper1qd0h2hj7uljjhktw9l3fjnz5u22g0xu74aw38w', - }, - openSSLBytes - ); + const res = await basecoin.redelegate({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + amountToRedelegate: '10000000000000000', + validatorSrcAddress: 'agoricvaloper1wy3h3gne94xpmnjwfwd3eyaytv9g4nj6yykkkj', + validatorDstAddress: 'agoricvaloper1qd0h2hj7uljjhktw9l3fjnz5u22g0xu74aw38w', + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); @@ -462,58 +452,46 @@ describe('BLD', function () { it('should throw error if backupkey is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); it('should throw error if userkey is not present', async function () { await basecoin - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-coreum/package.json b/modules/sdk-coin-coreum/package.json index e214cf8ba4..6407097471 100644 --- a/modules/sdk-coin-coreum/package.json +++ b/modules/sdk-coin-coreum/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38" } } diff --git a/modules/sdk-coin-coreum/test/unit/coreum.ts b/modules/sdk-coin-coreum/test/unit/coreum.ts index 02d6608a1f..59c50d74b7 100644 --- a/modules/sdk-coin-coreum/test/unit/coreum.ts +++ b/modules/sdk-coin-coreum/test/unit/coreum.ts @@ -21,10 +21,6 @@ import { } from '../resources/tcoreum'; import should = require('should'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('Coreum', function () { let bitgo: TestBitGoAPI; let coreum; @@ -427,16 +423,13 @@ describe('Coreum', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await tcoreum.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await tcoreum.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(tcoreum.getAccountBalance); @@ -482,58 +475,46 @@ describe('Coreum', function () { it('should throw error if backupkey is not present', async function () { await tcoreum - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); it('should throw error if userkey is not present', async function () { await tcoreum - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await tcoreum - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await tcoreum - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-hash/package.json b/modules/sdk-coin-hash/package.json index a73eda1f2d..3812e5c306 100644 --- a/modules/sdk-coin-hash/package.json +++ b/modules/sdk-coin-hash/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-hash/test/unit/hash.ts b/modules/sdk-coin-hash/test/unit/hash.ts index ebf893af68..bcbc521dbc 100644 --- a/modules/sdk-coin-hash/test/unit/hash.ts +++ b/modules/sdk-coin-hash/test/unit/hash.ts @@ -20,10 +20,6 @@ import { } from '../resources/hash'; import should = require('should'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('HASH', function () { let bitgo: TestBitGoAPI; let hash; @@ -418,16 +414,13 @@ describe('HASH', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await thash.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await thash.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(thash.getAccountBalance); @@ -486,44 +479,35 @@ describe('HASH', function () { it('should throw error if userkey is not present', async function () { await thash - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await thash - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await thash - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-injective/package.json b/modules/sdk-coin-injective/package.json index 87ea10bb62..28e798ad62 100644 --- a/modules/sdk-coin-injective/package.json +++ b/modules/sdk-coin-injective/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-injective/test/unit/injective.ts b/modules/sdk-coin-injective/test/unit/injective.ts index 833bc447d3..2e45d4e50f 100644 --- a/modules/sdk-coin-injective/test/unit/injective.ts +++ b/modules/sdk-coin-injective/test/unit/injective.ts @@ -21,10 +21,6 @@ import { import should = require('should'); import nock = require('nock'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('INJ', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -379,16 +375,13 @@ describe('INJ', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -434,58 +427,46 @@ describe('INJ', function () { it('should throw error if backupkey is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); it('should throw error if userkey is not present', async function () { await basecoin - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-osmo/package.json b/modules/sdk-coin-osmo/package.json index 2d03180201..55e2791f02 100644 --- a/modules/sdk-coin-osmo/package.json +++ b/modules/sdk-coin-osmo/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-osmo/test/unit/osmo.ts b/modules/sdk-coin-osmo/test/unit/osmo.ts index 596ad9057c..ed5f2daf46 100644 --- a/modules/sdk-coin-osmo/test/unit/osmo.ts +++ b/modules/sdk-coin-osmo/test/unit/osmo.ts @@ -20,10 +20,6 @@ import { } from '../resources/osmo'; import should = require('should'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('OSMO', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -436,16 +432,13 @@ describe('OSMO', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -492,15 +485,12 @@ describe('OSMO', function () { it('should throw error if backupkey is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); @@ -517,30 +507,24 @@ describe('OSMO', function () { it('should throw error if wallet passphrase is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-polygon/package.json b/modules/sdk-coin-polygon/package.json index 594bd86a42..5474d5fb67 100644 --- a/modules/sdk-coin-polygon/package.json +++ b/modules/sdk-coin-polygon/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "secp256k1": "5.0.0" } diff --git a/modules/sdk-coin-polygon/test/unit/polygon.ts b/modules/sdk-coin-polygon/test/unit/polygon.ts index 0b2b14b772..1cf0e5b2ac 100644 --- a/modules/sdk-coin-polygon/test/unit/polygon.ts +++ b/modules/sdk-coin-polygon/test/unit/polygon.ts @@ -13,10 +13,6 @@ import * as sjcl from '@bitgo/sjcl'; nock.enableNetConnect(); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('Polygon', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -661,7 +657,6 @@ describe('Polygon', function () { recoveryDestination: '0xac05da78464520aa7c9d4c19bd7a440b111b3054', walletPassphrase: TestBitGo.V2.TEST_RECOVERY_PASSCODE, isTss: true, - openSSLBytes, }; const recovery = await basecoin.recover(recoveryParams); @@ -694,7 +689,6 @@ describe('Polygon', function () { recoveryDestination: '0xac05da78464520aa7c9d4c19bd7a440b111b3054', walletPassphrase: TestBitGo.V2.TEST_RECOVERY_PASSCODE, isTss: true, - openSSLBytes, gasPrice: 20000000000, gasLimit: 500000, replayProtectionOptions: { diff --git a/modules/sdk-coin-sei/package.json b/modules/sdk-coin-sei/package.json index b8d2a3ed58..eb552271da 100644 --- a/modules/sdk-coin-sei/package.json +++ b/modules/sdk-coin-sei/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-sei/test/unit/sei.ts b/modules/sdk-coin-sei/test/unit/sei.ts index b15792ca62..f46dba575e 100644 --- a/modules/sdk-coin-sei/test/unit/sei.ts +++ b/modules/sdk-coin-sei/test/unit/sei.ts @@ -19,10 +19,6 @@ import { } from '../resources/sei'; import should = require('should'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('SEI', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -378,16 +374,13 @@ describe('SEI', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -435,58 +428,46 @@ describe('SEI', function () { it('should throw error if backupkey is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); it('should throw error if userkey is not present', async function () { await basecoin - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-tia/package.json b/modules/sdk-coin-tia/package.json index 4dc75592eb..60b66a2730 100644 --- a/modules/sdk-coin-tia/package.json +++ b/modules/sdk-coin-tia/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-tia/test/unit/tia.ts b/modules/sdk-coin-tia/test/unit/tia.ts index 931f564ed0..d6f0cc6ca6 100644 --- a/modules/sdk-coin-tia/test/unit/tia.ts +++ b/modules/sdk-coin-tia/test/unit/tia.ts @@ -24,10 +24,6 @@ import { import should = require('should'); import nock = require('nock'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('TIA', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -479,16 +475,13 @@ describe('TIA', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -534,58 +527,46 @@ describe('TIA', function () { it('should throw error if backupkey is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); it('should throw error if userkey is not present', async function () { await basecoin - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); diff --git a/modules/sdk-coin-zeta/package.json b/modules/sdk-coin-zeta/package.json index e95f8d2c37..4b1494d00c 100644 --- a/modules/sdk-coin-zeta/package.json +++ b/modules/sdk-coin-zeta/package.json @@ -51,7 +51,6 @@ }, "devDependencies": { "@bitgo/sdk-api": "^1.53.2", - "@bitgo/sdk-opensslbytes": "^2.0.0", "@bitgo/sdk-test": "^8.0.38", "@types/lodash": "^4.14.183" } diff --git a/modules/sdk-coin-zeta/test/unit/zeta.ts b/modules/sdk-coin-zeta/test/unit/zeta.ts index 476852debb..c127758c1a 100644 --- a/modules/sdk-coin-zeta/test/unit/zeta.ts +++ b/modules/sdk-coin-zeta/test/unit/zeta.ts @@ -23,10 +23,6 @@ import { import should = require('should'); import nock = require('nock'); -import { loadWebAssembly } from '@bitgo/sdk-opensslbytes'; - -const openSSLBytes = loadWebAssembly().buffer; - describe('Zeta', function () { let bitgo: TestBitGoAPI; let basecoin; @@ -381,16 +377,13 @@ describe('Zeta', function () { }); it('should recover funds for non-bitgo recoveries', async function () { - const res = await basecoin.recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ); + const res = await basecoin.recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); sandBox.assert.calledOnce(basecoin.getAccountBalance); @@ -408,18 +401,15 @@ describe('Zeta', function () { }); it('should redelegate funds to new validator', async function () { - const res = await basecoin.redelegate( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - amountToRedelegate: '10000000000000000', - validatorSrcAddress: 'zetavaloper1dhsk5v53h3xwg42pdg3r0w7zl83yxgyhs68v7l', - validatorDstAddress: 'zetavaloper19v07wvwm3zux9pawcmccr7c4hfezah0r8whsc6', - }, - openSSLBytes - ); + const res = await basecoin.redelegate({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + amountToRedelegate: '10000000000000000', + validatorSrcAddress: 'zetavaloper1dhsk5v53h3xwg42pdg3r0w7zl83yxgyhs68v7l', + validatorDstAddress: 'zetavaloper19v07wvwm3zux9pawcmccr7c4hfezah0r8whsc6', + }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); @@ -463,58 +453,46 @@ describe('Zeta', function () { it('should throw error if backupkey is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing backupKey'); }); it('should throw error if userkey is not present', async function () { await basecoin - .recover( - { - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing userKey'); }); it('should throw error if wallet passphrase is not present', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('missing wallet passphrase'); }); it('should throw error if there is no balance', async function () { await basecoin - .recover( - { - userKey: wrwUser.userPrivateKey, - backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, - walletPassphrase: wrwUser.walletPassphrase, - recoveryDestination: destinationAddress, - }, - openSSLBytes - ) + .recover({ + userKey: wrwUser.userPrivateKey, + backupKey: wrwUser.backupPrivateKey, + bitgoKey: wrwUser.bitgoPublicKey, + walletPassphrase: wrwUser.walletPassphrase, + recoveryDestination: destinationAddress, + }) .should.rejectedWith('Did not have enough funds to recover'); }); }); From af3ab2d21694e9516845ba999460ea63073e8f3b Mon Sep 17 00:00:00 2001 From: Zahin Mohammad Date: Tue, 10 Sep 2024 15:10:42 -0400 Subject: [PATCH 4/4] WIP TICKET: WP-2954 --- modules/abstract-cosmos/src/cosmosCoin.ts | 280 ++---------------- modules/abstract-cosmos/src/lib/iface.ts | 1 - .../src/abstractEthLikeNewCoins.ts | 129 +------- modules/sdk-coin-bld/test/resources/bld.ts | 1 - modules/sdk-coin-bld/test/unit/bld.ts | 83 ++++-- .../src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | 155 ++++++++-- 6 files changed, 212 insertions(+), 437 deletions(-) diff --git a/modules/abstract-cosmos/src/cosmosCoin.ts b/modules/abstract-cosmos/src/cosmosCoin.ts index 96d8d26cbb..38d80ca502 100644 --- a/modules/abstract-cosmos/src/cosmosCoin.ts +++ b/modules/abstract-cosmos/src/cosmosCoin.ts @@ -2,12 +2,9 @@ import { BaseCoin, BaseTransaction, BitGoBase, - ECDSA, Ecdsa, - ECDSAMethodTypes, ECDSAUtils, ExplanationResult, - hexToBigInt, InvalidAddressError, InvalidMemoIdError, KeyPair, @@ -22,7 +19,6 @@ import { VerifyAddressOptions, VerifyTransactionOptions, } from '@bitgo/sdk-core'; -import { EcdsaPaillierProof, EcdsaRangeProof, EcdsaTypes, DklsTypes, DklsUtils } from '@bitgo/sdk-lib-mpc'; import { BaseCoin as StaticsBaseCoin, CoinFamily } from '@bitgo/statics'; import { bip32 } from '@bitgo/utxo-lib'; import { Coin } from '@cosmjs/stargate'; @@ -138,9 +134,6 @@ export class CosmosCoin extends BaseCoin { */ async recover(params: RecoveryOptions): Promise { // Step 1: Check if params contains the required parameters - if (!params.bitgoKey) { - throw new Error('missing bitgoKey'); - } if (!params.recoveryDestination || !this.isValidAddress(params.recoveryDestination)) { throw new Error('invalid recoveryDestination'); @@ -158,18 +151,19 @@ export class CosmosCoin extends BaseCoin { throw new Error('missing wallet passphrase'); } - // Step 2: Fetch the bitgo key from params - const userKey = params.userKey.replace(/\s/g, ''); - const backupKey = params.backupKey.replace(/\s/g, ''); - const bitgoKey = params.bitgoKey.replace(/\s/g, ''); + // Step 2: Instantiate the ECDSA signer and fetch the address details + const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares( + params.userKey, + params.backupKey, + params.walletPassphrase + ); - // Step 3: Instantiate the ECDSA signer and fetch the address details const MPC = new Ecdsa(); const chainId = await this.getChainId(); - const publicKey = MPC.deriveUnhardened(bitgoKey, ROOT_PATH).slice(0, 66); + const publicKey = MPC.deriveUnhardened(commonKeyChain, ROOT_PATH).slice(0, 66); const senderAddress = this.getAddressFromPublicKey(publicKey); - // Step 4: Fetch account details such as accountNo, balance and check for sufficient funds once gasAmount has been deducted + // Step 3: Fetch account details such as accountNo, balance and check for sufficient funds once gasAmount has been deducted const [accountNumber, sequenceNo] = await this.getAccountDetails(senderAddress); const balance = new BigNumber(await this.getAccountBalance(senderAddress)); const gasBudget: FeeData = { @@ -183,7 +177,7 @@ export class CosmosCoin extends BaseCoin { throw new Error('Did not have enough funds to recover'); } - // Step 5: Once sufficient funds are present, construct the recover tx messsage + // Step 4: Once sufficient funds are present, construct the recover tx messsage const amount: Coin[] = [ { denom: this.getDenomination(), @@ -198,7 +192,7 @@ export class CosmosCoin extends BaseCoin { }, ]; - // Step 6: Build the unsigned tx using the constructed message + // Step 5: Build the unsigned tx using the constructed message const txnBuilder = this.getBuilder().getTransferBuilder(); txnBuilder .messages(sendMessage) @@ -208,61 +202,9 @@ export class CosmosCoin extends BaseCoin { .accountNumber(Number(accountNumber)) .chainId(chainId); const unsignedTransaction = (await txnBuilder.build()) as CosmosTransaction; - let serializedTx = unsignedTransaction.toBroadcastFormat(); const signableHex = unsignedTransaction.signablePayload.toString('hex'); - const isGG18SigningMaterial = ECDSAUtils.isGG18SigningMaterial(userKey, params.walletPassphrase); - - /** - * MPCv2 Signing Params - */ - let userKeyShare: Buffer; - let backupKeyShare: Buffer; - let commonKeyChain: string; - - // Step 7: Prepare the key shares for signing - if (isGG18SigningMaterial) { - // Retrofit the GG18 keys to DKLS - const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares( - userKey, - backupKey, - params.walletPassphrase - ); - - const aKeyCombine = { - xShare: userKeyCombined.xShare, - }; - const bKeyCombine = { - xShare: backupKeyCombined.xShare, - }; - const retrofitDataA: DklsTypes.RetrofitData = { - xShare: aKeyCombine.xShare, - }; - const retrofitDataB: DklsTypes.RetrofitData = { - xShare: bKeyCombine.xShare, - }; - const [user, backup] = await DklsUtils.generate2of2KeyShares(retrofitDataA, retrofitDataB); - - userKeyShare = user.getKeyShare(); - backupKeyShare = backup.getKeyShare(); - if (DklsTypes.getCommonKeychain(userKeyShare) !== DklsTypes.getCommonKeychain(backupKeyShare)) { - throw new Error('Common keychain mismatch! Ensure the correct user and backup keys where provided!'); - } - commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); - } else { - // DKLS - const mpcv2KeyShares = await ECDSAUtils.getMpcV2RecoveryKeyShares(userKey, backupKey, params.walletPassphrase); // baseAddress is not extracted - - userKeyShare = mpcv2KeyShares.userKeyShare; - backupKeyShare = mpcv2KeyShares.backupKeyShare; - commonKeyChain = mpcv2KeyShares.commonKeyChain; - - if (!userKeyShare || !backupKeyShare || !commonKeyChain) { - throw new Error('Missing combined key shares for user or backup or common'); - } - } - - // Step 8: Sign the tx + // Step 6: Sign the tx const message = unsignedTransaction.signablePayload; const messageHash = (utils.getHashFunction() || createHash('sha256')).update(message).digest(); const signature = await ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain); @@ -272,7 +214,7 @@ export class CosmosCoin extends BaseCoin { const cosmosKeyPair = this.getKeyPair(publicKey); txnBuilder.addSignature({ pub: cosmosKeyPair.getKeys().pub }, Buffer.from(signature.r + signature.s, 'hex')); const signedTransaction = await txnBuilder.build(); - serializedTx = signedTransaction.toBroadcastFormat(); + const serializedTx = signedTransaction.toBroadcastFormat(); return { serializedTx: serializedTx }; } @@ -289,13 +231,8 @@ export class CosmosCoin extends BaseCoin { validatorSrcAddress: string; validatorDstAddress: string; amountToRedelegate: string; - }, - openSSLBytes: Uint8Array - ): Promise { - if (!params.bitgoKey) { - throw new Error('missing bitgoKey'); } - + ): Promise { if (!params.validatorSrcAddress || !this.isValidAddress(params.validatorSrcAddress)) { throw new Error('invalid validatorSrcAddress'); } @@ -319,16 +256,17 @@ export class CosmosCoin extends BaseCoin { if (!params.amountToRedelegate) { throw new Error('missing amountToRedelegate'); } - if (!openSSLBytes) { - throw new Error('missing openSSLBytes'); - } - const bitgoKey = params.bitgoKey.replace(/\s/g, ''); + + const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares( + params.userKey, + params.backupKey, + params.walletPassphrase + ); const MPC = new Ecdsa(); const chainId = await this.getChainId(); - const publicKey = MPC.deriveUnhardened(bitgoKey, ROOT_PATH).slice(0, 66); + const publicKey = MPC.deriveUnhardened(commonKeyChain, ROOT_PATH).slice(0, 66); const senderAddress = this.getAddressFromPublicKey(publicKey); - const [accountNumber, sequenceNo] = await this.getAccountDetails(senderAddress); const gasBudget: FeeData = { amount: [{ denom: this.getDenomination(), amount: this.getGasAmountDetails().gasAmount }], @@ -359,192 +297,22 @@ export class CosmosCoin extends BaseCoin { .chainId(chainId); const unsignedTransaction = (await txnBuilder.build()) as CosmosTransaction; - let serializedTx = unsignedTransaction.toBroadcastFormat(); const signableHex = unsignedTransaction.signablePayload.toString('hex'); - const userKey = params.userKey.replace(/\s/g, ''); - const backupKey = params.backupKey.replace(/\s/g, ''); - const [userKeyCombined, backupKeyCombined] = ((): [ - ECDSAMethodTypes.KeyCombined | undefined, - ECDSAMethodTypes.KeyCombined | undefined - ] => { - const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares( - userKey, - backupKey, - params.walletPassphrase - ); - return [userKeyCombined, backupKeyCombined]; - })(); - if (!userKeyCombined || !backupKeyCombined) { - throw new Error('Missing combined key shares for user or backup'); - } + const message = unsignedTransaction.signablePayload; + const messageHash = (utils.getHashFunction() || createHash('sha256')).update(message).digest(); + const signature = await ECDSAUtils.signRecoveryMpcV2(messageHash, userKeyShare, backupKeyShare, commonKeyChain); - const signature = await this.signRecoveryTSS(userKeyCombined, backupKeyCombined, signableHex, openSSLBytes); const signableBuffer = Buffer.from(signableHex, 'hex'); MPC.verify(signableBuffer, signature, this.getHashFunction()); const cosmosKeyPair = this.getKeyPair(publicKey); txnBuilder.addSignature({ pub: cosmosKeyPair.getKeys().pub }, Buffer.from(signature.r + signature.s, 'hex')); const signedTransaction = await txnBuilder.build(); - serializedTx = signedTransaction.toBroadcastFormat(); + const serializedTx = signedTransaction.toBroadcastFormat(); return { serializedTx: serializedTx }; } - private getKeyCombinedFromTssKeyShares( - userPublicOrPrivateKeyShare: string, - backupPrivateOrPublicKeyShare: string, - walletPassphrase?: string - ): [ECDSAMethodTypes.KeyCombined, ECDSAMethodTypes.KeyCombined] { - let backupPrv; - let userPrv; - try { - backupPrv = this.bitgo.decrypt({ - input: backupPrivateOrPublicKeyShare, - password: walletPassphrase, - }); - userPrv = this.bitgo.decrypt({ - input: userPublicOrPrivateKeyShare, - password: walletPassphrase, - }); - } catch (e) { - throw new Error(`Error decrypting backup keychain: ${e.message}`); - } - - const userSigningMaterial = JSON.parse(userPrv) as ECDSAMethodTypes.SigningMaterial; - const backupSigningMaterial = JSON.parse(backupPrv) as ECDSAMethodTypes.SigningMaterial; - - if (!userSigningMaterial.backupNShare) { - throw new Error('Invalid user key - missing backupNShare'); - } - - if (!backupSigningMaterial.userNShare) { - throw new Error('Invalid backup key - missing userNShare'); - } - - const MPC = new Ecdsa(); - - const userKeyCombined = MPC.keyCombine(userSigningMaterial.pShare, [ - userSigningMaterial.bitgoNShare, - userSigningMaterial.backupNShare, - ]); - - const userSigningKeyDerived = MPC.keyDerive( - userSigningMaterial.pShare, - [userSigningMaterial.bitgoNShare, userSigningMaterial.backupNShare], - 'm/0' - ); - - const userKeyDerivedCombined = { - xShare: userSigningKeyDerived.xShare, - yShares: userKeyCombined.yShares, - }; - - const backupKeyCombined = MPC.keyCombine(backupSigningMaterial.pShare, [ - userSigningKeyDerived.nShares[2], - backupSigningMaterial.bitgoNShare, - ]); - - if ( - userKeyDerivedCombined.xShare.y !== backupKeyCombined.xShare.y || - userKeyDerivedCombined.xShare.chaincode !== backupKeyCombined.xShare.chaincode - ) { - throw new Error('Common keychains do not match'); - } - - return [userKeyDerivedCombined, backupKeyCombined]; - } - - // TODO(BG-78714): Reduce code duplication between this and eth.ts - private async signRecoveryTSS( - userKeyCombined: ECDSA.KeyCombined, - backupKeyCombined: ECDSA.KeyCombined, - txHex: string, - openSSLBytes: Uint8Array, - { - rangeProofChallenge, - }: { - rangeProofChallenge?: EcdsaTypes.SerializedNtilde; - } = {} - ): Promise { - const MPC = new Ecdsa(); - const signerOneIndex = userKeyCombined.xShare.i; - const signerTwoIndex = backupKeyCombined.xShare.i; - - // Since this is a user <> backup signing, we will reuse the same range proof challenge - rangeProofChallenge = - rangeProofChallenge ?? EcdsaTypes.serializeNtildeWithProofs(await EcdsaRangeProof.generateNtilde(openSSLBytes)); - - const userToBackupPaillierChallenge = await EcdsaPaillierProof.generateP( - hexToBigInt(userKeyCombined.yShares[signerTwoIndex].n) - ); - const backupToUserPaillierChallenge = await EcdsaPaillierProof.generateP( - hexToBigInt(backupKeyCombined.yShares[signerOneIndex].n) - ); - - const userXShare = MPC.appendChallenge( - userKeyCombined.xShare, - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: userToBackupPaillierChallenge }) - ); - const userYShare = MPC.appendChallenge( - userKeyCombined.yShares[signerTwoIndex], - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: backupToUserPaillierChallenge }) - ); - const backupXShare = MPC.appendChallenge( - backupKeyCombined.xShare, - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: backupToUserPaillierChallenge }) - ); - const backupYShare = MPC.appendChallenge( - backupKeyCombined.yShares[signerOneIndex], - rangeProofChallenge, - EcdsaTypes.serializePaillierChallenge({ p: userToBackupPaillierChallenge }) - ); - - const signShares: ECDSA.SignShareRT = await MPC.signShare(userXShare, userYShare); - - const signConvertS21 = await MPC.signConvertStep1({ - xShare: backupXShare, - yShare: backupYShare, // YShare corresponding to the other participant signerOne - kShare: signShares.kShare, - }); - const signConvertS12 = await MPC.signConvertStep2({ - aShare: signConvertS21.aShare, - wShare: signShares.wShare, - }); - const signConvertS21_2 = await MPC.signConvertStep3({ - muShare: signConvertS12.muShare, - bShare: signConvertS21.bShare, - }); - - const [signCombineOne, signCombineTwo] = [ - MPC.signCombine({ - gShare: signConvertS12.gShare, - signIndex: { - i: signConvertS12.muShare.i, - j: signConvertS12.muShare.j, - }, - }), - MPC.signCombine({ - gShare: signConvertS21_2.gShare, - signIndex: { - i: signConvertS21_2.signIndex.i, - j: signConvertS21_2.signIndex.j, - }, - }), - ]; - - const MESSAGE = Buffer.from(txHex, 'hex'); - - const [signA, signB] = [ - MPC.sign(MESSAGE, signCombineOne.oShare, signCombineTwo.dShare, this.getHashFunction()), - MPC.sign(MESSAGE, signCombineTwo.oShare, signCombineOne.dShare, this.getHashFunction()), - ]; - - return MPC.constructSignature([signA, signB]); - } - /** @inheritDoc **/ async verifyTransaction(params: VerifyTransactionOptions): Promise { let totalAmount = new BigNumber(0); diff --git a/modules/abstract-cosmos/src/lib/iface.ts b/modules/abstract-cosmos/src/lib/iface.ts index d1f300a335..8389b81172 100644 --- a/modules/abstract-cosmos/src/lib/iface.ts +++ b/modules/abstract-cosmos/src/lib/iface.ts @@ -30,7 +30,6 @@ export interface SendMessage { export interface RecoveryOptions { userKey?: string; // Box A backupKey?: string; // Box B - bitgoKey: string; // Box C recoveryDestination: string; krsProvider?: string; walletPassphrase?: string; diff --git a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts index 59237feb75..94560c62eb 100644 --- a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts +++ b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts @@ -34,7 +34,6 @@ import { Wallet, ECDSAUtils, } from '@bitgo/sdk-core'; -import { DklsTypes, DklsUtils } from '@bitgo/sdk-lib-mpc'; import { BaseCoin as StaticsBaseCoin, CoinMap, @@ -1053,72 +1052,6 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { } } - /** - * Helper which combines key shares of user and backup - * @param {string} userPublicOrPrivateKeyShare - * @param {string} backupPrivateOrPublicKeyShare - * @param {string} walletPassphrase - * @returns {[ECDSAMethodTypes.KeyCombined, ECDSAMethodTypes.KeyCombined]} - */ - private getKeyCombinedFromTssKeyShares( - userPublicOrPrivateKeyShare: string, - backupPrivateOrPublicKeyShare: string, - walletPassphrase?: string - ): [ECDSAMethodTypes.KeyCombined, ECDSAMethodTypes.KeyCombined] { - let backupPrv; - let userPrv; - try { - backupPrv = this.bitgo.decrypt({ - input: backupPrivateOrPublicKeyShare, - password: walletPassphrase, - }); - userPrv = this.bitgo.decrypt({ - input: userPublicOrPrivateKeyShare, - password: walletPassphrase, - }); - } catch (e) { - throw new Error(`Error decrypting backup keychain: ${e.message}`); - } - - const userSigningMaterial = JSON.parse(userPrv) as ECDSAMethodTypes.SigningMaterial; - const backupSigningMaterial = JSON.parse(backupPrv) as ECDSAMethodTypes.SigningMaterial; - - if (!userSigningMaterial.backupNShare) { - throw new Error('Invalid user key - missing backupNShare'); - } - - if (!backupSigningMaterial.userNShare) { - throw new Error('Invalid backup key - missing userNShare'); - } - - const MPC = new Ecdsa(); - - const userKeyCombined = MPC.keyCombine(userSigningMaterial.pShare, [ - userSigningMaterial.bitgoNShare, - userSigningMaterial.backupNShare, - ]); - const userSigningKeyDerived = MPC.keyDerive( - userSigningMaterial.pShare, - [userSigningMaterial.bitgoNShare, userSigningMaterial.backupNShare], - 'm/0' - ); - const userKeyDerivedCombined = { - xShare: userSigningKeyDerived.xShare, - yShares: userKeyCombined.yShares, - }; - const backupKeyCombined = MPC.keyCombine(backupSigningMaterial.pShare, [ - userSigningKeyDerived.nShares[2], - backupSigningMaterial.bitgoNShare, - ]); - if ( - userKeyDerivedCombined.xShare.y !== backupKeyCombined.xShare.y || - userKeyDerivedCombined.xShare.chaincode !== backupKeyCombined.xShare.chaincode - ) { - throw new Error('Common keychains do not match'); - } - return [userKeyDerivedCombined, backupKeyCombined]; - } - /** * Helper which Adds signatures to tx object and re-serializes tx * @param {EthLikeCommon.default} ethCommon @@ -1872,62 +1805,16 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { params.replayProtectionOptions ); } else { - const isGG18SigningMaterial = ECDSAUtils.isGG18SigningMaterial( - userPublicOrPrivateKeyShare, + const { userEncryptedPrv, backupEncryptedPrv } = { + userEncryptedPrv: userPublicOrPrivateKeyShare, + backupEncryptedPrv: backupPrivateOrPublicKeyShare, + }; + + const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares( + userEncryptedPrv, + backupEncryptedPrv, params.walletPassphrase ); - /** - * MPCv2 Signing Params - */ - let userKeyShare: Buffer; - let backupKeyShare: Buffer; - let commonKeyChain: string; - - // Prepare the key shares for signing - if (isGG18SigningMaterial) { - // Retrofit the GG18 keys to DKLS - const [userKeyCombined, backupKeyCombined] = this.getKeyCombinedFromTssKeyShares( - userPublicOrPrivateKeyShare, - backupPrivateOrPublicKeyShare, - params.walletPassphrase - ); - - const aKeyCombine = { - xShare: userKeyCombined.xShare, - }; - const bKeyCombine = { - xShare: backupKeyCombined.xShare, - }; - const retrofitDataA: DklsTypes.RetrofitData = { - xShare: aKeyCombine.xShare, - }; - const retrofitDataB: DklsTypes.RetrofitData = { - xShare: bKeyCombine.xShare, - }; - const [user, backup] = await DklsUtils.generate2of2KeyShares(retrofitDataA, retrofitDataB); - - userKeyShare = user.getKeyShare(); - backupKeyShare = backup.getKeyShare(); - if (DklsTypes.getCommonKeychain(userKeyShare) !== DklsTypes.getCommonKeychain(backupKeyShare)) { - throw new Error('Common keychain mismatch! Ensure the correct user and backup keys where provided!'); - } - commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); - } else { - // DKLS - const mpcv2KeyShares = await ECDSAUtils.getMpcV2RecoveryKeyShares( - userPublicOrPrivateKeyShare, - backupPrivateOrPublicKeyShare, - params.walletPassphrase - ); - - userKeyShare = mpcv2KeyShares.userKeyShare; - backupKeyShare = mpcv2KeyShares.backupKeyShare; - commonKeyChain = mpcv2KeyShares.commonKeyChain; - - if (!userKeyShare || !backupKeyShare || !commonKeyChain) { - throw new Error('Missing combined key shares for user or backup or common'); - } - } const MPC = new Ecdsa(); const derivedCommonKeyChain = MPC.deriveUnhardened(commonKeyChain, 'm/0'); diff --git a/modules/sdk-coin-bld/test/resources/bld.ts b/modules/sdk-coin-bld/test/resources/bld.ts index 7b76ec1d80..1f241d62e0 100644 --- a/modules/sdk-coin-bld/test/resources/bld.ts +++ b/modules/sdk-coin-bld/test/resources/bld.ts @@ -257,7 +257,6 @@ export const coinAmounts = { }; export const wrwUser = { - senderAddress: 'agoric1enx2gvcpmf4fw7vnngj2qjw2x4270vhwev49ra', destinationAddress: 'agoric1tkfnp4khzd0f7mgtznwrvr0lv2at3p8c8sz89p', userPrivateKey: '{"iv":"P1169+ix5MxunnuGnImS1A==","v":1,"iter":10000,"ks":256,"ts":64,"mode"\n' + diff --git a/modules/sdk-coin-bld/test/unit/bld.ts b/modules/sdk-coin-bld/test/unit/bld.ts index ab729cbdb6..4d402693ca 100644 --- a/modules/sdk-coin-bld/test/unit/bld.ts +++ b/modules/sdk-coin-bld/test/unit/bld.ts @@ -18,16 +18,29 @@ import { wrwUser, } from '../resources/bld'; import should = require('should'); +import { Ecdsa, ECDSAUtils, VerifyTransactionOptions } from '@bitgo/sdk-core'; describe('BLD', function () { let bitgo: TestBitGoAPI; - let basecoin; - before(function () { + let basecoin: Tbld; + + let wrwUserSenderAddress: string; + + before(async function () { bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' }); bitgo.safeRegister('bld', Bld.createInstance); bitgo.safeRegister('tbld', Tbld.createInstance); bitgo.initializeTestVars(); - basecoin = bitgo.coin('tbld'); + basecoin = bitgo.coin('tbld') as unknown as Tbld; + + const { commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares( + wrwUser.userPrivateKey, + wrwUser.backupPrivateKey, + wrwUser.walletPassphrase + ); + const MPC = new Ecdsa(); + const publicKey = MPC.deriveUnhardened(commonKeyChain, 'm/0').slice(0, 66); + wrwUserSenderAddress = basecoin.getAddressFromPublicKey(publicKey); }); it('should return the right info', function () { @@ -55,7 +68,7 @@ describe('BLD', function () { it('should get address details with memoId', function () { const addressDetails = basecoin.getAddressDetails(address.validMemoIdAddress); addressDetails.address.should.equal(address.validMemoIdAddress.split('?')[0]); - addressDetails.memoId.should.equal('2'); + addressDetails.memoId!.should.equal('2'); }); it('should throw on invalid memo id address', () => { @@ -78,7 +91,7 @@ describe('BLD', function () { memoID: '7', }, }; - const isValid = await basecoin.isWalletAddress(receiveAddress); + const isValid = await basecoin.isWalletAddress(receiveAddress as any); isValid.should.equal(true); }); @@ -121,7 +134,11 @@ describe('BLD', function () { ], }; const verification = {}; - const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification }); + const isTransactionVerified = await basecoin.verifyTransaction({ + txParams, + txPrebuild, + verification, + } as unknown as VerifyTransactionOptions); isTransactionVerified.should.equal(true); }); @@ -139,7 +156,11 @@ describe('BLD', function () { ], }; const verification = {}; - const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification }); + const isTransactionVerified = await basecoin.verifyTransaction({ + txParams, + txPrebuild, + verification, + } as unknown as VerifyTransactionOptions); isTransactionVerified.should.equal(true); }); @@ -157,7 +178,11 @@ describe('BLD', function () { ], }; const verification = {}; - const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification }); + const isTransactionVerified = await basecoin.verifyTransaction({ + txParams, + txPrebuild, + verification, + } as unknown as VerifyTransactionOptions); isTransactionVerified.should.equal(true); }); @@ -175,7 +200,11 @@ describe('BLD', function () { ], }; const verification = {}; - const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification }); + const isTransactionVerified = await basecoin.verifyTransaction({ + txParams, + txPrebuild, + verification, + } as unknown as VerifyTransactionOptions); isTransactionVerified.should.equal(true); }); @@ -186,7 +215,7 @@ describe('BLD', function () { .verifyTransaction({ txParams, txPrebuild, - }) + } as unknown as VerifyTransactionOptions) .should.rejectedWith('missing required tx prebuild property txHex'); }); }); @@ -298,14 +327,6 @@ describe('BLD', function () { }); }); - it('should fail to explain transaction with missing params', async function () { - try { - await basecoin.explainTransaction({}); - } catch (error) { - should.equal(error.message, 'missing required txHex parameter'); - } - }); - it('should fail to explain transaction with invalid params', async function () { try { await basecoin.explainTransaction({ txHex: 'randomString' }); @@ -354,12 +375,12 @@ describe('BLD', function () { const testSequenceNumber = '0'; const testChainId = 'test-chain'; - beforeEach(() => { + beforeEach(async () => { const accountBalance = sandBox.stub(Bld.prototype, 'getAccountBalance' as keyof Bld); - accountBalance.withArgs(wrwUser.senderAddress).resolves(testBalance); + accountBalance.withArgs(wrwUserSenderAddress).resolves(testBalance); const accountDetails = sandBox.stub(Bld.prototype, 'getAccountDetails' as keyof Bld); - accountDetails.withArgs(wrwUser.senderAddress).resolves([testAccountNumber, testSequenceNumber]); + accountDetails.withArgs(wrwUserSenderAddress).resolves([testAccountNumber, testSequenceNumber]); const chainId = sandBox.stub(Bld.prototype, 'getChainId' as keyof Bld); chainId.withArgs().resolves(testChainId); @@ -377,14 +398,20 @@ describe('BLD', function () { const res = await basecoin.recover({ userKey: wrwUser.userPrivateKey, backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, walletPassphrase: wrwUser.walletPassphrase, recoveryDestination: destinationAddress, }); res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore sandBox.assert.calledOnce(basecoin.getAccountBalance); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore sandBox.assert.calledOnce(basecoin.getAccountDetails); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore sandBox.assert.calledOnce(basecoin.getChainId); const txn = new CosmosTransaction(coin, utils); @@ -402,7 +429,7 @@ describe('BLD', function () { const res = await basecoin.redelegate({ userKey: wrwUser.userPrivateKey, backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, + recoveryDestination: wrwUserSenderAddress, walletPassphrase: wrwUser.walletPassphrase, amountToRedelegate: '10000000000000000', validatorSrcAddress: 'agoricvaloper1wy3h3gne94xpmnjwfwd3eyaytv9g4nj6yykkkj', @@ -411,6 +438,8 @@ describe('BLD', function () { res.should.not.be.empty(); res.should.hasOwnProperty('serializedTx'); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore sandBox.assert.calledOnce(basecoin.getChainId); const txn = new CosmosTransaction(coin, utils); @@ -433,10 +462,10 @@ describe('BLD', function () { beforeEach(() => { const accountBalance = sandBox.stub(Bld.prototype, 'getAccountBalance' as keyof Bld); - accountBalance.withArgs(wrwUser.senderAddress).resolves(testZeroBalance); + accountBalance.withArgs(wrwUserSenderAddress).resolves(testZeroBalance); const accountDetails = sandBox.stub(Bld.prototype, 'getAccountDetails' as keyof Bld); - accountDetails.withArgs(wrwUser.senderAddress).resolves([testAccountNumber, testSequenceNumber]); + accountDetails.withArgs(wrwUserSenderAddress).resolves([testAccountNumber, testSequenceNumber]); const chainId = sandBox.stub(Bld.prototype, 'getChainId' as keyof Bld); chainId.withArgs().resolves(testChainId); @@ -454,7 +483,6 @@ describe('BLD', function () { await basecoin .recover({ userKey: wrwUser.userPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, walletPassphrase: wrwUser.walletPassphrase, recoveryDestination: destinationAddress, }) @@ -465,7 +493,6 @@ describe('BLD', function () { await basecoin .recover({ backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, walletPassphrase: wrwUser.walletPassphrase, recoveryDestination: destinationAddress, }) @@ -477,7 +504,6 @@ describe('BLD', function () { .recover({ userKey: wrwUser.userPrivateKey, backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, recoveryDestination: destinationAddress, }) .should.rejectedWith('missing wallet passphrase'); @@ -488,7 +514,6 @@ describe('BLD', function () { .recover({ userKey: wrwUser.userPrivateKey, backupKey: wrwUser.backupPrivateKey, - bitgoKey: wrwUser.bitgoPublicKey, walletPassphrase: wrwUser.walletPassphrase, recoveryDestination: destinationAddress, }) diff --git a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts index 195bf0dd7e..a03d17aefc 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts @@ -1206,45 +1206,142 @@ export function isGG18SigningMaterial(keyShare: string, walletPassphrase: string /** * Retrieves the MPC v2 recovery key shares from the provided user and backup key shares. + * The user and backup keys can be + * - encrypted compressed DKLS keys (from keycard) + * - encrypted GG18 keys (from keycard) * - * @param {string} userPublicOrPrivateKeyShare - * @param {string} backupPrivateOrPublicKeyShare + * @param {string} userEncryptedPrv + * @param {string} backupEncryptedPrv * @param {string} [walletPassphrase] - The passphrase used to decrypt the key shares * @returns {Promise<{ userKeyShare: KeyShare, backupKeyShare: KeyShare, commonKeyChain: string }>} * * @async */ export async function getMpcV2RecoveryKeyShares( + userEncryptedPrv: string, + backupEncryptedPrv: string, + walletPassphrase?: string +): Promise<{ userKeyShare: Buffer; backupKeyShare: Buffer; commonKeyChain: string }> { + userEncryptedPrv = userEncryptedPrv.replace(/\s/g, ''); + backupEncryptedPrv = backupEncryptedPrv.replace(/\s/g, ''); + + let userKeyShare: Buffer; + let backupKeyShare: Buffer; + let commonKeyChain: string; + + if (isGG18SigningMaterial(userEncryptedPrv, walletPassphrase)) { + // Retrofit the GG18 keys to DKLS + const [userKeyCombined, backupKeyCombined] = getKeyCombinedFromTssKeyShares( + userEncryptedPrv, + backupEncryptedPrv, + walletPassphrase + ); + + const aKeyCombine = { + xShare: userKeyCombined.xShare, + }; + const bKeyCombine = { + xShare: backupKeyCombined.xShare, + }; + const retrofitDataA: DklsTypes.RetrofitData = { + xShare: aKeyCombine.xShare, + }; + const retrofitDataB: DklsTypes.RetrofitData = { + xShare: bKeyCombine.xShare, + }; + const [user, backup] = await DklsUtils.generate2of2KeyShares(retrofitDataA, retrofitDataB); + + userKeyShare = user.getKeyShare(); + backupKeyShare = backup.getKeyShare(); + if (DklsTypes.getCommonKeychain(userKeyShare) !== DklsTypes.getCommonKeychain(backupKeyShare)) { + throw new Error('Common keychain mismatch! Ensure the correct user and backup keys where provided!'); + } + commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); + } else { + const userPrv = Buffer.from(sjcl.decrypt(walletPassphrase, userEncryptedPrv), 'base64'); + const backupPrv = Buffer.from(sjcl.decrypt(walletPassphrase, backupEncryptedPrv), 'base64'); + const userPrvJSON: DklsTypes.ReducedKeyShare = DklsTypes.getDecodedReducedKeyShare(userPrv); + const backupPrvJSON: DklsTypes.ReducedKeyShare = DklsTypes.getDecodedReducedKeyShare(backupPrv); + const userKeyRetrofit: DklsTypes.RetrofitData = { + xShare: { + x: Buffer.from(userPrvJSON.prv).toString('hex'), + y: Buffer.from(userPrvJSON.pub).toString('hex'), + chaincode: Buffer.from(userPrvJSON.rootChainCode).toString('hex'), + }, + xiList: userPrvJSON.xList.slice(0, 2), + }; + const backupKeyRetrofit: DklsTypes.RetrofitData = { + xShare: { + x: Buffer.from(backupPrvJSON.prv).toString('hex'), + y: Buffer.from(backupPrvJSON.pub).toString('hex'), + chaincode: Buffer.from(backupPrvJSON.rootChainCode).toString('hex'), + }, + xiList: backupPrvJSON.xList.slice(0, 2), + }; + const [user, backup] = await DklsUtils.generate2of2KeyShares(userKeyRetrofit, backupKeyRetrofit); + userKeyShare = user.getKeyShare(); + backupKeyShare = backup.getKeyShare(); + commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); + } + return { userKeyShare, backupKeyShare, commonKeyChain }; +} + +export function getKeyCombinedFromTssKeyShares( userPublicOrPrivateKeyShare: string, backupPrivateOrPublicKeyShare: string, walletPassphrase?: string -) { - const userCompressedPrv = Buffer.from(sjcl.decrypt(walletPassphrase, userPublicOrPrivateKeyShare), 'base64'); - const bakcupCompressedPrv = Buffer.from(sjcl.decrypt(walletPassphrase, backupPrivateOrPublicKeyShare), 'base64'); - - const userPrvJSON: DklsTypes.ReducedKeyShare = DklsTypes.getDecodedReducedKeyShare(userCompressedPrv); - const backupPrvJSON: DklsTypes.ReducedKeyShare = DklsTypes.getDecodedReducedKeyShare(bakcupCompressedPrv); - const userKeyRetrofit: DklsTypes.RetrofitData = { - xShare: { - x: Buffer.from(userPrvJSON.prv).toString('hex'), - y: Buffer.from(userPrvJSON.pub).toString('hex'), - chaincode: Buffer.from(userPrvJSON.rootChainCode).toString('hex'), - }, - xiList: userPrvJSON.xList.slice(0, 2), - }; - const backupKeyRetrofit: DklsTypes.RetrofitData = { - xShare: { - x: Buffer.from(backupPrvJSON.prv).toString('hex'), - y: Buffer.from(backupPrvJSON.pub).toString('hex'), - chaincode: Buffer.from(backupPrvJSON.rootChainCode).toString('hex'), - }, - xiList: backupPrvJSON.xList.slice(0, 2), +): [ECDSAMethodTypes.KeyCombined, ECDSAMethodTypes.KeyCombined] { + let backupPrv; + let userPrv; + try { + userPrv = sjcl.decrypt(walletPassphrase, userPublicOrPrivateKeyShare); + backupPrv = sjcl.decrypt(walletPassphrase, backupPrivateOrPublicKeyShare); + } catch (e) { + throw new Error(`Error decrypting backup keychain: ${e.message}`); + } + + const userSigningMaterial = JSON.parse(userPrv) as ECDSAMethodTypes.SigningMaterial; + const backupSigningMaterial = JSON.parse(backupPrv) as ECDSAMethodTypes.SigningMaterial; + + if (!userSigningMaterial.backupNShare) { + throw new Error('Invalid user key - missing backupNShare'); + } + + if (!backupSigningMaterial.userNShare) { + throw new Error('Invalid backup key - missing userNShare'); + } + + const MPC = new Ecdsa(); + + const userKeyCombined = MPC.keyCombine(userSigningMaterial.pShare, [ + userSigningMaterial.bitgoNShare, + userSigningMaterial.backupNShare, + ]); + + const userSigningKeyDerived = MPC.keyDerive( + userSigningMaterial.pShare, + [userSigningMaterial.bitgoNShare, userSigningMaterial.backupNShare], + 'm/0' + ); + + const userKeyDerivedCombined = { + xShare: userSigningKeyDerived.xShare, + yShares: userKeyCombined.yShares, }; - const [user, backup] = await DklsUtils.generate2of2KeyShares(userKeyRetrofit, backupKeyRetrofit); - const userKeyShare = user.getKeyShare(); - const backupKeyShare = backup.getKeyShare(); - const commonKeyChain = DklsTypes.getCommonKeychain(userKeyShare); - return { userKeyShare, backupKeyShare, commonKeyChain }; + + const backupKeyCombined = MPC.keyCombine(backupSigningMaterial.pShare, [ + userSigningKeyDerived.nShares[2], + backupSigningMaterial.bitgoNShare, + ]); + + if ( + userKeyDerivedCombined.xShare.y !== backupKeyCombined.xShare.y || + userKeyDerivedCombined.xShare.chaincode !== backupKeyCombined.xShare.chaincode + ) { + throw new Error('Common keychains do not match'); + } + + return [userKeyDerivedCombined, backupKeyCombined]; } /** @@ -1263,7 +1360,7 @@ export async function signRecoveryMpcV2( userKeyShare: Buffer, backupKeyShare: Buffer, commonKeyChain: string -) { +): Promise<{ recid: number; r: string; s: string; y: string }> { const userDsg = new DklsDsg.Dsg(userKeyShare, 0, 'm/0', messageHash); const backupDsg = new DklsDsg.Dsg(backupKeyShare, 1, 'm/0', messageHash);