@@ -30,6 +30,10 @@ import {
3030 AuditDecryptedKeyParams ,
3131 verifyEddsaTssWalletAddress ,
3232 TxIntentMismatchRecipientError ,
33+ getEddsaSigningMaterial as sharedGetEddsaSigningMaterial ,
34+ signEddsaMpcV2RecoveryTx ,
35+ EddsaSigningMaterial ,
36+ decryptKeychainPrivateKey ,
3337} from '@bitgo/sdk-core' ;
3438import { BaseCoin as StaticsBaseCoin , coins , PolkadotSpecNameType } from '@bitgo/statics' ;
3539import {
@@ -347,7 +351,7 @@ export class Dot extends BaseCoin {
347351 * @returns {MPCTx } the serialized transaction hex string and index
348352 * of the address being swept
349353 */
350- async recover ( params : MPCRecoveryOptions ) : Promise < MPCTx | MPCSweepTxs > {
354+ async recover ( params : MPCRecoveryOptions , precomputedMaterial ?: EddsaSigningMaterial ) : Promise < MPCTx | MPCSweepTxs > {
351355 if ( ! params . bitgoKey ) {
352356 throw new Error ( 'missing bitgoKey' ) ;
353357 }
@@ -403,43 +407,43 @@ export class Dot extends BaseCoin {
403407 throw new Error ( 'missing wallet passphrase' ) ;
404408 }
405409
406- // Clean up whitespace from entered values
407410 const userKey = params . userKey . replace ( / \s / g, '' ) ;
408411 const backupKey = params . backupKey . replace ( / \s / g, '' ) ;
409-
410- // Decrypt private keys from KeyCard values
411- let userPrv ;
412- try {
413- userPrv = await this . bitgo . decrypt ( {
414- input : userKey ,
415- password : params . walletPassphrase ,
416- } ) ;
417- } catch ( e ) {
418- throw new Error ( `Error decrypting user keychain: ${ e . message } ` ) ;
419- }
420- /** TODO BG-52419 Implement Codec for parsing */
421- const userSigningMaterial = JSON . parse ( userPrv ) as EDDSAMethodTypes . UserSigningMaterial ;
422-
423- let backupPrv ;
424- try {
425- backupPrv = await this . bitgo . decrypt ( {
426- input : backupKey ,
427- password : params . walletPassphrase ,
412+ const dotKeyPair = new DotKeyPair ( { pub : accountId } ) ;
413+ const signingMaterial =
414+ precomputedMaterial ?? ( await this . getEddsaSigningMaterial ( userKey , params . walletPassphrase ) ) ;
415+
416+ if ( signingMaterial . version === 'v2' ) {
417+ const signature = await this . signDotMpcV2Recovery ( {
418+ message : unsignedTransaction . signablePayload ,
419+ userKey : signingMaterial . encryptedUserKey ,
420+ backupKey,
421+ walletPassphrase : params . walletPassphrase ,
422+ bitgoKey,
423+ derivationPath : currPath ,
424+ bitgo : this . bitgo ,
428425 } ) ;
429- } catch ( e ) {
430- throw new Error ( `Error decrypting backup keychain: ${ e . message } ` ) ;
426+ txnBuilder . addSignature ( { pub : dotKeyPair . getKeys ( ) . pub } , signature ) ;
427+ } else {
428+ /** TODO BG-52419 Implement Codec for parsing */
429+ const userSigningMaterial = JSON . parse ( signingMaterial . userPrv ) as EDDSAMethodTypes . UserSigningMaterial ;
430+ const backupPrv = await decryptKeychainPrivateKey (
431+ this . bitgo ,
432+ { encryptedPrv : backupKey } ,
433+ params . walletPassphrase
434+ ) ;
435+ if ( ! backupPrv ) {
436+ throw new Error ( 'Error decrypting backup keychain: invalid password or corrupted key' ) ;
437+ }
438+ const backupSigningMaterial = JSON . parse ( backupPrv ) as EDDSAMethodTypes . BackupSigningMaterial ;
439+ const signatureHex = await EDDSAMethods . getTSSSignature (
440+ userSigningMaterial ,
441+ backupSigningMaterial ,
442+ currPath ,
443+ unsignedTransaction
444+ ) ;
445+ txnBuilder . addSignature ( { pub : dotKeyPair . getKeys ( ) . pub } , signatureHex ) ;
431446 }
432- const backupSigningMaterial = JSON . parse ( backupPrv ) as EDDSAMethodTypes . BackupSigningMaterial ;
433-
434- // add signature
435- const signatureHex = await EDDSAMethods . getTSSSignature (
436- userSigningMaterial ,
437- backupSigningMaterial ,
438- currPath ,
439- unsignedTransaction
440- ) ;
441- const dotKeyPair = new DotKeyPair ( { pub : accountId } ) ;
442- txnBuilder . addSignature ( { pub : dotKeyPair . getKeys ( ) . pub } , signatureHex ) ;
443447 const signedTransaction = await txnBuilder . build ( ) ;
444448 serializedTx = signedTransaction . toBroadcastFormat ( ) ;
445449 } else {
@@ -506,16 +510,24 @@ export class Dot extends BaseCoin {
506510 }
507511
508512 const bitgoKey = params . bitgoKey . replace ( / \s / g, '' ) ;
509- const MPC = await EDDSAMethods . getInitializedMpcInstance ( ) ;
513+ const userKey = params . userKey ?. replace ( / \s / g, '' ) ?? '' ;
514+
515+ // Detect signing material once to avoid re-decrypting the keycard on every loop iteration.
516+ const signingMaterial = params . walletPassphrase
517+ ? await this . getEddsaSigningMaterial ( userKey , params . walletPassphrase )
518+ : undefined ;
519+
510520 const baseIndex = 0 ;
511521 const basePath = params . seed ? getDerivationPath ( params . seed ) + `/${ baseIndex } ` : `m/${ baseIndex } ` ;
512- const accountId = MPC . deriveUnhardened ( bitgoKey , basePath ) . slice ( 0 , 64 ) ;
522+ const accountId = ( await EDDSAMethods . getInitializedMpcInstance ( ) )
523+ . deriveUnhardened ( bitgoKey , basePath )
524+ . slice ( 0 , 64 ) ;
513525 const baseAddress = this . getAddressFromPublicKey ( accountId ) ;
514526
515527 const consolidationTransactions : any [ ] = [ ] ;
516528 let lastScanIndex = startIdx ;
517529 for ( let i = startIdx ; i < endIdx ; i ++ ) {
518- const recoverParams = {
530+ const recoverParams : MPCRecoveryOptions = {
519531 userKey : params . userKey ,
520532 backupKey : params . backupKey ,
521533 bitgoKey : params . bitgoKey ,
@@ -527,7 +539,7 @@ export class Dot extends BaseCoin {
527539
528540 let recoveryTransaction ;
529541 try {
530- recoveryTransaction = await this . recover ( recoverParams ) ;
542+ recoveryTransaction = await this . recover ( recoverParams , signingMaterial ) ;
531543 } catch ( e ) {
532544 if ( e . message === 'Did not find address with funds to recover' ) {
533545 lastScanIndex = i ;
@@ -742,6 +754,14 @@ export class Dot extends BaseCoin {
742754 return new TransactionBuilderFactory ( coins . get ( this . getChain ( ) ) ) ;
743755 }
744756
757+ protected async getEddsaSigningMaterial ( userKey : string , walletPassphrase : string ) : Promise < EddsaSigningMaterial > {
758+ return sharedGetEddsaSigningMaterial ( userKey . replace ( / \s / g, '' ) , walletPassphrase , this . bitgo ) ;
759+ }
760+
761+ protected async signDotMpcV2Recovery ( params : Parameters < typeof signEddsaMpcV2RecoveryTx > [ 0 ] ) : Promise < Buffer > {
762+ return signEddsaMpcV2RecoveryTx ( params ) ;
763+ }
764+
745765 /** @inheritDoc */
746766 auditDecryptedKey ( { publicKey, prv, multiSigType } : AuditDecryptedKeyParams ) {
747767 if ( multiSigType !== 'tss' ) {
0 commit comments