@@ -3,12 +3,13 @@ import * as sinon from 'sinon';
33import 'should' ;
44
55import { Wallets } from '../../../../src/bitgo/wallet/wallets' ;
6- import { ECDSAUtils } from '../../../../src/bitgo/utils' ;
6+ import { ECDSAUtils , EDDSAUtils } from '../../../../src/bitgo/utils' ;
77import { CoinFeature } from '@bitgo/statics' ;
88import {
99 CreateKeychainCallback ,
1010 EcdsaMPCv2KeyGenCallbacks ,
1111 EddsaKeyGenCallbacks ,
12+ EddsaMPCv2KeyGenCallbacks ,
1213} from '../../../../src/bitgo/wallet/iWallets' ;
1314import { Wallet } from '../../../../src/bitgo/wallet/wallet' ;
1415
@@ -713,6 +714,216 @@ describe('Wallets - external signer onchain wallet generation', function () {
713714 } ) ;
714715 } ) ;
715716
717+ describe ( 'generateWalletWithExternalSigner - EdDSA MPCv2 TSS' , function ( ) {
718+ let eddsaMPCv2Callbacks : EddsaMPCv2KeyGenCallbacks ;
719+ let eddsaMPCv2MockBaseCoin : any ;
720+ let eddsaMPCv2Wallets : Wallets ;
721+
722+ const commonKeychain = 'eddsampcv2-common-keychain' ;
723+
724+ beforeEach ( function ( ) {
725+ const mpcState = { encryptedData : 'data' , encryptedDataKey : 'data-key' } ;
726+ eddsaMPCv2Callbacks = {
727+ initializeCallback : sinon . stub ( ) . resolves ( {
728+ userGpgPublicKey : 'user-gpg-pub' ,
729+ backupGpgPublicKey : 'backup-gpg-pub' ,
730+ userState : mpcState ,
731+ backupState : mpcState ,
732+ } ) ,
733+ round1Callback : sinon . stub ( ) . resolves ( {
734+ userSignedMsg1 : { from : 0 , payload : '' , signature : '' } ,
735+ backupSignedMsg1 : { from : 1 , payload : '' , signature : '' } ,
736+ userState : mpcState ,
737+ backupState : mpcState ,
738+ } ) ,
739+ round2Callback : sinon . stub ( ) . resolves ( {
740+ userSignedMsg2 : { from : 0 , payload : '' , signature : '' } ,
741+ backupSignedMsg2 : { from : 1 , payload : '' , signature : '' } ,
742+ userState : mpcState ,
743+ backupState : mpcState ,
744+ } ) ,
745+ finalizeCallback : sinon . stub ( ) . resolves ( { commonKeychain } ) ,
746+ } ;
747+
748+ const mockEddsaMPCv2BitGo = {
749+ post : sinon . stub ( ) . returns ( {
750+ send : sinon . stub ( ) . returns ( { result : sinon . stub ( ) . resolves ( { id : 'eddsampcv2-wallet-id' } ) } ) ,
751+ } ) ,
752+ setRequestTracer : sinon . stub ( ) ,
753+ } ;
754+
755+ eddsaMPCv2MockBaseCoin = {
756+ isEVM : sinon . stub ( ) . returns ( false ) ,
757+ supportsTss : sinon . stub ( ) . returns ( true ) ,
758+ getMPCAlgorithm : sinon . stub ( ) . returns ( 'eddsa' ) ,
759+ getFamily : sinon . stub ( ) . returns ( 'sol' ) ,
760+ getChain : sinon . stub ( ) . returns ( 'tsol' ) ,
761+ getDefaultMultisigType : sinon . stub ( ) . returns ( 'tss' ) ,
762+ keychains : sinon . stub ( ) . returns ( { add : sinon . stub ( ) } ) ,
763+ url : sinon . stub ( ) . returns ( '/api/v2/tsol/wallet/add' ) ,
764+ getConfig : sinon . stub ( ) . returns ( { features : [ ] } ) ,
765+ supplementGenerateWallet : sinon . stub ( ) . callsFake ( ( params : any ) => Promise . resolve ( params ) ) ,
766+ } ;
767+
768+ eddsaMPCv2Wallets = new Wallets ( mockEddsaMPCv2BitGo as any , eddsaMPCv2MockBaseCoin ) ;
769+ sinon . stub ( eddsaMPCv2Wallets as any , 'generateMpcWalletWithExternalSigner' ) . resolves ( {
770+ responseType : 'WalletWithKeychains' as const ,
771+ wallet : sinon . createStubInstance ( Wallet ) ,
772+ userKeychain : { id : 'user-key-id' , commonKeychain, type : 'tss' } ,
773+ backupKeychain : { id : 'backup-key-id' , commonKeychain, type : 'tss' } ,
774+ bitgoKeychain : { id : 'bitgo-key-id' , commonKeychain, type : 'tss' } ,
775+ } ) ;
776+ } ) ;
777+
778+ it ( 'should route to generateMpcWalletWithExternalSigner for tss EdDSA MPCv2 coin' , async function ( ) {
779+ const result = await eddsaMPCv2Wallets . generateWalletWithExternalSigner ( {
780+ label : 'EdDSA MPCv2 TSS Wallet' ,
781+ multisigType : 'tss' ,
782+ enterprise : 'enterprise-id' ,
783+ eddsaMPCv2Callbacks,
784+ } ) ;
785+
786+ result . responseType . should . equal ( 'WalletWithKeychains' ) ;
787+ assert . strictEqual ( ( eddsaMPCv2Wallets as any ) . generateMpcWalletWithExternalSigner . calledOnce , true ) ;
788+ } ) ;
789+
790+ it ( 'should route to EddsaMPCv2Utils.createKeychainsWithExternalSigner when eddsaMPCv2Callbacks provided' , async function ( ) {
791+ sinon . restore ( ) ;
792+ eddsaMPCv2MockBaseCoin . getMPCAlgorithm = sinon . stub ( ) . returns ( 'eddsa' ) ;
793+
794+ const keychainsTriplet = {
795+ userKeychain : { id : 'user-key-id' , commonKeychain, type : 'tss' } ,
796+ backupKeychain : { id : 'backup-key-id' , commonKeychain, type : 'tss' } ,
797+ bitgoKeychain : { id : 'bitgo-key-id' , commonKeychain, type : 'tss' } ,
798+ } ;
799+
800+ // createKeychainsWithExternalSigner is added by WCI-916; inject a stub here so the
801+ // routing logic can be tested before that PR lands.
802+ const createKeychainsStub = sinon . stub ( ) . resolves ( keychainsTriplet ) ;
803+ ( EDDSAUtils . EddsaMPCv2Utils . prototype as any ) . createKeychainsWithExternalSigner = createKeychainsStub ;
804+
805+ const mockBitGoForIntegration = {
806+ post : sinon . stub ( ) . returns ( {
807+ send : sinon . stub ( ) . returns ( { result : sinon . stub ( ) . resolves ( { id : 'eddsampcv2-wallet-id' } ) } ) ,
808+ } ) ,
809+ setRequestTracer : sinon . stub ( ) ,
810+ } ;
811+
812+ const integrationWallets = new Wallets ( mockBitGoForIntegration as any , eddsaMPCv2MockBaseCoin ) ;
813+
814+ await integrationWallets . generateWalletWithExternalSigner ( {
815+ label : 'EdDSA MPCv2 TSS Wallet' ,
816+ multisigType : 'tss' ,
817+ enterprise : 'enterprise-id' ,
818+ eddsaMPCv2Callbacks,
819+ } ) ;
820+
821+ assert . strictEqual ( createKeychainsStub . calledOnce , true ) ;
822+ createKeychainsStub . firstCall . args [ 0 ] . should . deepEqual ( {
823+ enterprise : 'enterprise-id' ,
824+ callbacks : eddsaMPCv2Callbacks ,
825+ } ) ;
826+
827+ delete ( EDDSAUtils . EddsaMPCv2Utils . prototype as any ) . createKeychainsWithExternalSigner ;
828+ } ) ;
829+
830+ it ( 'should reject when both eddsaMPCv2Callbacks and eddsaCallbacks are provided' , async function ( ) {
831+ sinon . restore ( ) ;
832+ eddsaMPCv2MockBaseCoin . getMPCAlgorithm = sinon . stub ( ) . returns ( 'eddsa' ) ;
833+
834+ const eddsaCallbacks : EddsaKeyGenCallbacks = {
835+ initializeCallback : sinon . stub ( ) as any ,
836+ finalizeCallback : sinon . stub ( ) as any ,
837+ } ;
838+
839+ await eddsaMPCv2Wallets
840+ . generateWalletWithExternalSigner ( {
841+ label : 'EdDSA MPCv2 Conflict Wallet' ,
842+ multisigType : 'tss' ,
843+ enterprise : 'enterprise-id' ,
844+ eddsaMPCv2Callbacks,
845+ eddsaCallbacks,
846+ } )
847+ . should . be . rejectedWith (
848+ 'eddsaMPCv2Callbacks and eddsaCallbacks cannot both be provided; use eddsaMPCv2Callbacks for EdDSA MPCv2'
849+ ) ;
850+ } ) ;
851+
852+ it ( 'should reject without any eddsa callbacks for eddsa coin' , async function ( ) {
853+ sinon . restore ( ) ;
854+ eddsaMPCv2MockBaseCoin . getMPCAlgorithm = sinon . stub ( ) . returns ( 'eddsa' ) ;
855+
856+ await eddsaMPCv2Wallets
857+ . generateWalletWithExternalSigner ( {
858+ label : 'EdDSA MPCv2 No Callbacks Wallet' ,
859+ multisigType : 'tss' ,
860+ enterprise : 'enterprise-id' ,
861+ } )
862+ . should . be . rejectedWith ( 'eddsaCallbacks is required for EdDSA TSS wallet generation with external signer' ) ;
863+ } ) ;
864+
865+ it ( 'should not call EddsaMPCv2Utils when eddsaCallbacks (MPCv1) is provided for eddsa coin' , async function ( ) {
866+ sinon . restore ( ) ;
867+ eddsaMPCv2MockBaseCoin . getMPCAlgorithm = sinon . stub ( ) . returns ( 'eddsa' ) ;
868+
869+ const eddsaCallbacks : EddsaKeyGenCallbacks = {
870+ initializeCallback : sinon . stub ( ) as any ,
871+ finalizeCallback : sinon . stub ( ) as any ,
872+ } ;
873+
874+ // createKeychainsWithExternalSigner is added by WCI-916; inject a stub here so the
875+ // routing logic can be tested before that PR lands.
876+ const mpcv2Stub = sinon . stub ( ) . resolves ( {
877+ userKeychain : { id : 'user-key-id' , commonKeychain, type : 'tss' } ,
878+ backupKeychain : { id : 'backup-key-id' , commonKeychain, type : 'tss' } ,
879+ bitgoKeychain : { id : 'bitgo-key-id' , commonKeychain, type : 'tss' } ,
880+ } ) ;
881+ ( EDDSAUtils . EddsaMPCv2Utils . prototype as any ) . createKeychainsWithExternalSigner = mpcv2Stub ;
882+
883+ const mpcv1Stub = sinon . stub ( EDDSAUtils . default . prototype , 'createKeychainsWithExternalSigner' ) . resolves ( {
884+ userKeychain : { id : 'user-key-id' , commonKeychain, type : 'tss' } ,
885+ backupKeychain : { id : 'backup-key-id' , commonKeychain, type : 'tss' } ,
886+ bitgoKeychain : { id : 'bitgo-key-id' , commonKeychain, type : 'tss' } ,
887+ } ) ;
888+
889+ const mockBitGoForIntegration = {
890+ post : sinon . stub ( ) . returns ( {
891+ send : sinon . stub ( ) . returns ( { result : sinon . stub ( ) . resolves ( { id : 'eddsa-wallet-id' } ) } ) ,
892+ } ) ,
893+ setRequestTracer : sinon . stub ( ) ,
894+ } ;
895+
896+ const integrationWallets = new Wallets ( mockBitGoForIntegration as any , eddsaMPCv2MockBaseCoin ) ;
897+
898+ await integrationWallets . generateWalletWithExternalSigner ( {
899+ label : 'EdDSA MPCv1 TSS Wallet' ,
900+ multisigType : 'tss' ,
901+ enterprise : 'enterprise-id' ,
902+ eddsaCallbacks,
903+ } ) ;
904+
905+ assert . strictEqual ( mpcv2Stub . callCount , 0 ) ;
906+ assert . strictEqual ( mpcv1Stub . calledOnce , true ) ;
907+
908+ delete ( EDDSAUtils . EddsaMPCv2Utils . prototype as any ) . createKeychainsWithExternalSigner ;
909+ } ) ;
910+
911+ it ( 'should include eddsaMPCv2Callbacks in hasMpcCallbacks check' , async function ( ) {
912+ const hasMpcCallbacks = ! ! ( eddsaMPCv2Callbacks as any ) ;
913+
914+ await eddsaMPCv2Wallets
915+ . generateWalletWithExternalSigner ( {
916+ label : 'EdDSA MPCv2 No Callbacks Conflict' ,
917+ enterprise : 'enterprise-id' ,
918+ createKeychainCallback : sinon . stub ( ) as any ,
919+ eddsaMPCv2Callbacks,
920+ } )
921+ . should . be . rejectedWith ( 'createKeychainCallback cannot be used together with MPC TSS key generation callbacks' ) ;
922+
923+ assert . ok ( hasMpcCallbacks ) ;
924+ } ) ;
925+ } ) ;
926+
716927 describe ( 'generateWallet with createKeychainCallback' , function ( ) {
717928 it ( 'should delegate to generateWalletWithExternalSigner' , async function ( ) {
718929 const generateWalletWithExternalSignerStub = sinon . stub ( wallets , 'generateWalletWithExternalSigner' ) . resolves ( {
0 commit comments