@@ -2,12 +2,9 @@ import {
22 BaseCoin ,
33 BaseTransaction ,
44 BitGoBase ,
5- ECDSA ,
65 Ecdsa ,
7- ECDSAMethodTypes ,
86 ECDSAUtils ,
97 ExplanationResult ,
10- hexToBigInt ,
118 InvalidAddressError ,
129 InvalidMemoIdError ,
1310 KeyPair ,
@@ -22,7 +19,6 @@ import {
2219 VerifyAddressOptions ,
2320 VerifyTransactionOptions ,
2421} from '@bitgo/sdk-core' ;
25- import { EcdsaPaillierProof , EcdsaRangeProof , EcdsaTypes } from '@bitgo/sdk-lib-mpc' ;
2622import { BaseCoin as StaticsBaseCoin , CoinFamily } from '@bitgo/statics' ;
2723import { bip32 } from '@bitgo/utxo-lib' ;
2824import { Coin } from '@cosmjs/stargate' ;
@@ -136,11 +132,8 @@ export class CosmosCoin extends BaseCoin {
136132 * @returns {CosmosLikeCoinRecoveryOutput } the serialized transaction hex string and index
137133 * of the address being swept
138134 */
139- async recover ( params : RecoveryOptions , openSSLBytes : Uint8Array ) : Promise < CosmosLikeCoinRecoveryOutput > {
135+ async recover ( params : RecoveryOptions ) : Promise < CosmosLikeCoinRecoveryOutput > {
140136 // Step 1: Check if params contains the required parameters
141- if ( ! params . bitgoKey ) {
142- throw new Error ( 'missing bitgoKey' ) ;
143- }
144137
145138 if ( ! params . recoveryDestination || ! this . isValidAddress ( params . recoveryDestination ) ) {
146139 throw new Error ( 'invalid recoveryDestination' ) ;
@@ -157,19 +150,20 @@ export class CosmosCoin extends BaseCoin {
157150 if ( ! params . walletPassphrase ) {
158151 throw new Error ( 'missing wallet passphrase' ) ;
159152 }
160- if ( ! openSSLBytes ) {
161- throw new Error ( 'missing openSSLBytes' ) ;
162- }
163153
164154 // Step 2: Fetch the bitgo key from params
165155 const userKey = params . userKey . replace ( / \s / g, '' ) ;
166156 const backupKey = params . backupKey . replace ( / \s / g, '' ) ;
167- const bitgoKey = params . bitgoKey . replace ( / \s / g, '' ) ;
168157
158+ const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils . getMpcV2RecoveryKeyShares (
159+ userKey ,
160+ backupKey ,
161+ params . walletPassphrase
162+ ) ; // baseAddress is not extracted
169163 // Step 3: Instantiate the ECDSA signer and fetch the address details
170164 const MPC = new Ecdsa ( ) ;
171165 const chainId = await this . getChainId ( ) ;
172- const publicKey = MPC . deriveUnhardened ( bitgoKey , ROOT_PATH ) . slice ( 0 , 66 ) ;
166+ const publicKey = MPC . deriveUnhardened ( commonKeyChain , ROOT_PATH ) . slice ( 0 , 66 ) ;
173167 const senderAddress = this . getAddressFromPublicKey ( publicKey ) ;
174168
175169 // Step 4: Fetch account details such as accountNo, balance and check for sufficient funds once gasAmount has been deducted
@@ -214,47 +208,11 @@ export class CosmosCoin extends BaseCoin {
214208 let serializedTx = unsignedTransaction . toBroadcastFormat ( ) ;
215209 const signableHex = unsignedTransaction . signablePayload . toString ( 'hex' ) ;
216210
217- const isGG18SigningMaterial = ECDSAUtils . isGG18SigningMaterial ( userKey , params . walletPassphrase ) ;
218- let signature : ECDSA . Signature ;
219-
220- if ( isGG18SigningMaterial ) {
221- // GG18
222- const [ userKeyCombined , backupKeyCombined ] = ( ( ) : [
223- ECDSAMethodTypes . KeyCombined | undefined ,
224- ECDSAMethodTypes . KeyCombined | undefined
225- ] => {
226- const [ userKeyCombined , backupKeyCombined ] = this . getKeyCombinedFromTssKeyShares (
227- userKey ,
228- backupKey ,
229- params . walletPassphrase
230- ) ;
231- return [ userKeyCombined , backupKeyCombined ] ;
232- } ) ( ) ;
233-
234- if ( ! userKeyCombined || ! backupKeyCombined ) {
235- throw new Error ( 'Missing combined key shares for user or backup' ) ;
236- }
237-
238- // Step 7: Sign the tx
239- signature = await this . signRecoveryTSS ( userKeyCombined , backupKeyCombined , signableHex , openSSLBytes ) ;
240- } else {
241- // DKLS
242- const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils . getMpcV2RecoveryKeyShares (
243- userKey ,
244- backupKey ,
245- params . walletPassphrase
246- ) ; // baseAddress is not extracted
247-
248- if ( ! userKeyShare || ! backupKeyShare || ! commonKeyChain ) {
249- throw new Error ( 'Missing combined key shares for user or backup or common' ) ;
250- }
251-
252- // Step 7: Sign the tx
253- const message = unsignedTransaction . signablePayload ;
254- const messageHash = ( utils . getHashFunction ( ) || createHash ( 'sha256' ) ) . update ( message ) . digest ( ) ;
211+ // Step 7: Sign the tx
212+ const message = unsignedTransaction . signablePayload ;
213+ const messageHash = ( utils . getHashFunction ( ) || createHash ( 'sha256' ) ) . update ( message ) . digest ( ) ;
255214
256- signature = await ECDSAUtils . signRecoveryMpcV2 ( messageHash , userKeyShare , backupKeyShare , commonKeyChain ) ;
257- }
215+ const signature = await ECDSAUtils . signRecoveryMpcV2 ( messageHash , userKeyShare , backupKeyShare , commonKeyChain ) ;
258216
259217 const signableBuffer = Buffer . from ( signableHex , 'hex' ) ;
260218 MPC . verify ( signableBuffer , signature , this . getHashFunction ( ) ) ;
@@ -278,13 +236,8 @@ export class CosmosCoin extends BaseCoin {
278236 validatorSrcAddress : string ;
279237 validatorDstAddress : string ;
280238 amountToRedelegate : string ;
281- } ,
282- openSSLBytes : Uint8Array
283- ) : Promise < CosmosLikeCoinRecoveryOutput > {
284- if ( ! params . bitgoKey ) {
285- throw new Error ( 'missing bitgoKey' ) ;
286239 }
287-
240+ ) : Promise < CosmosLikeCoinRecoveryOutput > {
288241 if ( ! params . validatorSrcAddress || ! this . isValidAddress ( params . validatorSrcAddress ) ) {
289242 throw new Error ( 'invalid validatorSrcAddress' ) ;
290243 }
@@ -308,14 +261,19 @@ export class CosmosCoin extends BaseCoin {
308261 if ( ! params . amountToRedelegate ) {
309262 throw new Error ( 'missing amountToRedelegate' ) ;
310263 }
311- if ( ! openSSLBytes ) {
312- throw new Error ( 'missing openSSLBytes' ) ;
313- }
314- const bitgoKey = params . bitgoKey . replace ( / \s / g, '' ) ;
264+
265+ const userKey = params . userKey . replace ( / \s / g, '' ) ;
266+ const backupKey = params . backupKey . replace ( / \s / g, '' ) ;
267+
268+ const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils . getMpcV2RecoveryKeyShares (
269+ userKey ,
270+ backupKey ,
271+ params . walletPassphrase
272+ ) ; // baseAddress is not extracted
315273
316274 const MPC = new Ecdsa ( ) ;
317275 const chainId = await this . getChainId ( ) ;
318- const publicKey = MPC . deriveUnhardened ( bitgoKey , ROOT_PATH ) . slice ( 0 , 66 ) ;
276+ const publicKey = MPC . deriveUnhardened ( commonKeyChain , ROOT_PATH ) . slice ( 0 , 66 ) ;
319277 const senderAddress = this . getAddressFromPublicKey ( publicKey ) ;
320278
321279 const [ accountNumber , sequenceNo ] = await this . getAccountDetails ( senderAddress ) ;
@@ -350,25 +308,9 @@ export class CosmosCoin extends BaseCoin {
350308 const unsignedTransaction = ( await txnBuilder . build ( ) ) as CosmosTransaction ;
351309 let serializedTx = unsignedTransaction . toBroadcastFormat ( ) ;
352310 const signableHex = unsignedTransaction . signablePayload . toString ( 'hex' ) ;
353- const userKey = params . userKey . replace ( / \s / g, '' ) ;
354- const backupKey = params . backupKey . replace ( / \s / g, '' ) ;
355- const [ userKeyCombined , backupKeyCombined ] = ( ( ) : [
356- ECDSAMethodTypes . KeyCombined | undefined ,
357- ECDSAMethodTypes . KeyCombined | undefined
358- ] => {
359- const [ userKeyCombined , backupKeyCombined ] = this . getKeyCombinedFromTssKeyShares (
360- userKey ,
361- backupKey ,
362- params . walletPassphrase
363- ) ;
364- return [ userKeyCombined , backupKeyCombined ] ;
365- } ) ( ) ;
366-
367- if ( ! userKeyCombined || ! backupKeyCombined ) {
368- throw new Error ( 'Missing combined key shares for user or backup' ) ;
369- }
370-
371- const signature = await this . signRecoveryTSS ( userKeyCombined , backupKeyCombined , signableHex , openSSLBytes ) ;
311+ const message = unsignedTransaction . signablePayload ;
312+ const messageHash = ( utils . getHashFunction ( ) || createHash ( 'sha256' ) ) . update ( message ) . digest ( ) ;
313+ const signature = await ECDSAUtils . signRecoveryMpcV2 ( messageHash , userKeyShare , backupKeyShare , commonKeyChain ) ;
372314 const signableBuffer = Buffer . from ( signableHex , 'hex' ) ;
373315 MPC . verify ( signableBuffer , signature , this . getHashFunction ( ) ) ;
374316 const cosmosKeyPair = this . getKeyPair ( publicKey ) ;
@@ -379,161 +321,6 @@ export class CosmosCoin extends BaseCoin {
379321 return { serializedTx : serializedTx } ;
380322 }
381323
382- private getKeyCombinedFromTssKeyShares (
383- userPublicOrPrivateKeyShare : string ,
384- backupPrivateOrPublicKeyShare : string ,
385- walletPassphrase ?: string
386- ) : [ ECDSAMethodTypes . KeyCombined , ECDSAMethodTypes . KeyCombined ] {
387- let backupPrv ;
388- let userPrv ;
389- try {
390- backupPrv = this . bitgo . decrypt ( {
391- input : backupPrivateOrPublicKeyShare ,
392- password : walletPassphrase ,
393- } ) ;
394- userPrv = this . bitgo . decrypt ( {
395- input : userPublicOrPrivateKeyShare ,
396- password : walletPassphrase ,
397- } ) ;
398- } catch ( e ) {
399- throw new Error ( `Error decrypting backup keychain: ${ e . message } ` ) ;
400- }
401-
402- const userSigningMaterial = JSON . parse ( userPrv ) as ECDSAMethodTypes . SigningMaterial ;
403- const backupSigningMaterial = JSON . parse ( backupPrv ) as ECDSAMethodTypes . SigningMaterial ;
404-
405- if ( ! userSigningMaterial . backupNShare ) {
406- throw new Error ( 'Invalid user key - missing backupNShare' ) ;
407- }
408-
409- if ( ! backupSigningMaterial . userNShare ) {
410- throw new Error ( 'Invalid backup key - missing userNShare' ) ;
411- }
412-
413- const MPC = new Ecdsa ( ) ;
414-
415- const userKeyCombined = MPC . keyCombine ( userSigningMaterial . pShare , [
416- userSigningMaterial . bitgoNShare ,
417- userSigningMaterial . backupNShare ,
418- ] ) ;
419-
420- const userSigningKeyDerived = MPC . keyDerive (
421- userSigningMaterial . pShare ,
422- [ userSigningMaterial . bitgoNShare , userSigningMaterial . backupNShare ] ,
423- 'm/0'
424- ) ;
425-
426- const userKeyDerivedCombined = {
427- xShare : userSigningKeyDerived . xShare ,
428- yShares : userKeyCombined . yShares ,
429- } ;
430-
431- const backupKeyCombined = MPC . keyCombine ( backupSigningMaterial . pShare , [
432- userSigningKeyDerived . nShares [ 2 ] ,
433- backupSigningMaterial . bitgoNShare ,
434- ] ) ;
435-
436- if (
437- userKeyDerivedCombined . xShare . y !== backupKeyCombined . xShare . y ||
438- userKeyDerivedCombined . xShare . chaincode !== backupKeyCombined . xShare . chaincode
439- ) {
440- throw new Error ( 'Common keychains do not match' ) ;
441- }
442-
443- return [ userKeyDerivedCombined , backupKeyCombined ] ;
444- }
445-
446- // TODO(BG-78714): Reduce code duplication between this and eth.ts
447- private async signRecoveryTSS (
448- userKeyCombined : ECDSA . KeyCombined ,
449- backupKeyCombined : ECDSA . KeyCombined ,
450- txHex : string ,
451- openSSLBytes : Uint8Array ,
452- {
453- rangeProofChallenge,
454- } : {
455- rangeProofChallenge ?: EcdsaTypes . SerializedNtilde ;
456- } = { }
457- ) : Promise < ECDSAMethodTypes . Signature > {
458- const MPC = new Ecdsa ( ) ;
459- const signerOneIndex = userKeyCombined . xShare . i ;
460- const signerTwoIndex = backupKeyCombined . xShare . i ;
461-
462- // Since this is a user <> backup signing, we will reuse the same range proof challenge
463- rangeProofChallenge =
464- rangeProofChallenge ?? EcdsaTypes . serializeNtildeWithProofs ( await EcdsaRangeProof . generateNtilde ( openSSLBytes ) ) ;
465-
466- const userToBackupPaillierChallenge = await EcdsaPaillierProof . generateP (
467- hexToBigInt ( userKeyCombined . yShares [ signerTwoIndex ] . n )
468- ) ;
469- const backupToUserPaillierChallenge = await EcdsaPaillierProof . generateP (
470- hexToBigInt ( backupKeyCombined . yShares [ signerOneIndex ] . n )
471- ) ;
472-
473- const userXShare = MPC . appendChallenge (
474- userKeyCombined . xShare ,
475- rangeProofChallenge ,
476- EcdsaTypes . serializePaillierChallenge ( { p : userToBackupPaillierChallenge } )
477- ) ;
478- const userYShare = MPC . appendChallenge (
479- userKeyCombined . yShares [ signerTwoIndex ] ,
480- rangeProofChallenge ,
481- EcdsaTypes . serializePaillierChallenge ( { p : backupToUserPaillierChallenge } )
482- ) ;
483- const backupXShare = MPC . appendChallenge (
484- backupKeyCombined . xShare ,
485- rangeProofChallenge ,
486- EcdsaTypes . serializePaillierChallenge ( { p : backupToUserPaillierChallenge } )
487- ) ;
488- const backupYShare = MPC . appendChallenge (
489- backupKeyCombined . yShares [ signerOneIndex ] ,
490- rangeProofChallenge ,
491- EcdsaTypes . serializePaillierChallenge ( { p : userToBackupPaillierChallenge } )
492- ) ;
493-
494- const signShares : ECDSA . SignShareRT = await MPC . signShare ( userXShare , userYShare ) ;
495-
496- const signConvertS21 = await MPC . signConvertStep1 ( {
497- xShare : backupXShare ,
498- yShare : backupYShare , // YShare corresponding to the other participant signerOne
499- kShare : signShares . kShare ,
500- } ) ;
501- const signConvertS12 = await MPC . signConvertStep2 ( {
502- aShare : signConvertS21 . aShare ,
503- wShare : signShares . wShare ,
504- } ) ;
505- const signConvertS21_2 = await MPC . signConvertStep3 ( {
506- muShare : signConvertS12 . muShare ,
507- bShare : signConvertS21 . bShare ,
508- } ) ;
509-
510- const [ signCombineOne , signCombineTwo ] = [
511- MPC . signCombine ( {
512- gShare : signConvertS12 . gShare ,
513- signIndex : {
514- i : signConvertS12 . muShare . i ,
515- j : signConvertS12 . muShare . j ,
516- } ,
517- } ) ,
518- MPC . signCombine ( {
519- gShare : signConvertS21_2 . gShare ,
520- signIndex : {
521- i : signConvertS21_2 . signIndex . i ,
522- j : signConvertS21_2 . signIndex . j ,
523- } ,
524- } ) ,
525- ] ;
526-
527- const MESSAGE = Buffer . from ( txHex , 'hex' ) ;
528-
529- const [ signA , signB ] = [
530- MPC . sign ( MESSAGE , signCombineOne . oShare , signCombineTwo . dShare , this . getHashFunction ( ) ) ,
531- MPC . sign ( MESSAGE , signCombineTwo . oShare , signCombineOne . dShare , this . getHashFunction ( ) ) ,
532- ] ;
533-
534- return MPC . constructSignature ( [ signA , signB ] ) ;
535- }
536-
537324 /** @inheritDoc **/
538325 async verifyTransaction ( params : VerifyTransactionOptions ) : Promise < boolean > {
539326 let totalAmount = new BigNumber ( 0 ) ;
0 commit comments