From 734533f13444f5f9ae702bdc8c7a9314c0a03af9 Mon Sep 17 00:00:00 2001 From: Marguerite Blair Date: Mon, 4 May 2026 15:47:17 -0400 Subject: [PATCH 1/5] feat(awm): adding backup awm instance to recovery flow Ticket: WCN-363  Conflicts:  src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts  src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts  src/advancedWalletManager/handlers/utils/utils.ts  src/advancedWalletManager/kmsClient/kmsClient.ts  src/initConfig.ts --- .../recoveryMpcV2.test.ts | 40 +++++++++++++ .../recoveryMusigEth.test.ts | 60 +++++++++++++++++++ src/shared/types/index.ts | 10 ++++ 3 files changed, 110 insertions(+) diff --git a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts index 797d8ea0..a49c5f5e 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts @@ -132,6 +132,46 @@ describe('recoveryMpcV2', async () => { backupKeyProviderNock.isDone().should.be.true(); }); + it('should route backup key retrieval to backup KMS when configured', async () => { + const backupKmsUrl = 'http://backup-kms.invalid'; + + // Reconfigure app with backup KMS URL + configStub.restore(); + const dualCfg: AdvancedWalletManagerConfig = { + ...cfg, + backupKmsUrl, + }; + configStub = sinon.stub(configModule, 'initConfig').returns(dualCfg); + const dualApp = advancedWalletManagerApp(dualCfg); + const dualAgent = request.agent(dualApp); + + // User key served from primary KMS + const userKmsNock = nock(kmsUrl) + .get(`/key/${input.pub}`) + .query({ source: 'user' }) + .reply(200, mockKmsUserResponse) + .persist(); + + // Backup key served from backup KMS + const backupKmsNock = nock(backupKmsUrl) + .get(`/key/${input.pub}`) + .query({ source: 'backup' }) + .reply(200, mockKmsBackupResponse) + .persist(); + + const response = await dualAgent + .post(`/api/${ethLikeCoin}/mpcv2/recovery`) + .set('Authorization', `Bearer ${accessToken}`) + .send(input); + + response.status.should.equal(200); + response.body.should.have.property('txHex'); + response.body.should.have.property('stringifiedSignature'); + + userKmsNock.isDone().should.be.true(); + backupKmsNock.isDone().should.be.true(); + }); + // failure test case it('should throw 400 Bad Request if failed to construct eth transaction from message hex', async () => { const input = { diff --git a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts index a9436ddc..179f1fe1 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts @@ -106,6 +106,66 @@ describe('recoveryMultisigTransaction', () => { keyProviderNockBackup.done(); }); + it('should route backup key retrieval to backup KMS when configured', async () => { + const backupKmsUrl = 'http://backup-kms.invalid'; + const { userPub, backupPub, walletContractAddress, userPrv, backupPrv, txHexResult } = awmData; + const unsignedSweepPrebuildTx = unsignedSweepRecJSON as unknown as any; + + // Reconfigure app with backup KMS URL + configStub.restore(); + const dualCfg: AdvancedWalletManagerConfig = { + ...cfg, + backupKmsUrl, + }; + configStub = sinon.stub(configModule, 'initConfig').returns(dualCfg); + const dualApp = advancedWalletManagerApp(dualCfg); + const dualAgent = request.agent(dualApp); + + const mockKmsUserResponse = { + prv: userPrv, + pub: userPub, + source: 'user', + type: 'independent', + }; + + const mockKmsBackupResponse = { + prv: backupPrv, + pub: backupPub, + source: 'backup', + type: 'independent', + }; + + // User key from primary KMS + const kmsNockUser = nock(kmsUrl) + .get(`/key/${userPub}`) + .query({ source: 'user' }) + .reply(200, mockKmsUserResponse); + + // Backup key from backup KMS + const kmsNockBackup = nock(backupKmsUrl) + .get(`/key/${backupPub}`) + .query({ source: 'backup' }) + .reply(200, mockKmsBackupResponse); + + const response = await dualAgent + .post(`/api/${coin}/multisig/recovery`) + .set('Authorization', `Bearer ${accessToken}`) + .send({ + userPub, + backupPub, + apiKey: 'etherscan-api-token', + unsignedSweepPrebuildTx, + walletContractAddress, + coinSpecificParams: undefined, + }); + + response.status.should.equal(200); + response.body.should.have.property('txHex', txHexResult); + + kmsNockUser.done(); + kmsNockBackup.done(); + }); + it('should fail when prv keys non related to pub keys', async () => { const { userPub, backupPub, walletContractAddress } = awmData; const unsignedSweepPrebuildTx = unsignedSweepRecJSON as unknown as any; diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts index d07d20da..3f8550b3 100644 --- a/src/shared/types/index.ts +++ b/src/shared/types/index.ts @@ -40,6 +40,16 @@ export interface AdvancedWalletManagerConfig extends BaseConfig { keyProviderClientTlsCert?: string; keyProviderServerCertAllowSelfSigned?: boolean; + // Backup KMS settings (separate HSM for backup key) + backupKmsUrl?: string; + backupKmsServerCaCertPath?: string; + backupKmsServerCaCert?: string; + backupKmsClientTlsKeyPath?: string; + backupKmsClientTlsCertPath?: string; + backupKmsClientTlsKey?: string; + backupKmsClientTlsCert?: string; + backupKmsServerCertAllowSelfSigned?: boolean; + // mTLS server settings serverTlsKeyPath?: string; serverTlsCertPath?: string; From 2d9370cfba7ace35ad0bbf6907b9d9fc34048fa7 Mon Sep 17 00:00:00 2001 From: Marguerite Blair Date: Wed, 6 May 2026 12:12:16 -0400 Subject: [PATCH 2/5] feat(awm): adapting recovery flows to take backup awm Ticket: WCN-363 --- .../routers/advancedWalletManagerApiSpec.ts | 5 +++++ .../clients/advancedWalletManagerClient.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts b/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts index 0b84ace0..3a07cb7a 100644 --- a/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts +++ b/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts @@ -72,6 +72,11 @@ const RecoveryMultisigRequest = { bitgoPub: optional(t.string), unsignedSweepPrebuildTx: t.any, walletContractAddress: optional(t.string), + // When set, only sign with the specified key (user half-sign or backup full-sign). + // When omitted, the endpoint signs with both keys (default single-AWM behavior). + keyToSign: optional(t.union([t.literal('user'), t.literal('backup')])), + // Required when keyToSign is 'backup': the half-signed transaction from the user-key phase. + halfSignedTransaction: optional(t.any), }; // Response type for /multisig/recovery endpoint diff --git a/src/masterBitgoExpress/clients/advancedWalletManagerClient.ts b/src/masterBitgoExpress/clients/advancedWalletManagerClient.ts index 10352446..ff39ca61 100644 --- a/src/masterBitgoExpress/clients/advancedWalletManagerClient.ts +++ b/src/masterBitgoExpress/clients/advancedWalletManagerClient.ts @@ -99,6 +99,10 @@ interface RecoveryMultisigOptions { | MPCTx | RecoveryTransaction; walletContractAddress: string; + // When set, only sign with the specified key (user half-sign or backup full-sign). + keyToSign?: 'user' | 'backup'; + // Required when keyToSign is 'backup': the half-signed transaction from the user-key phase. + halfSignedTransaction?: any; } interface SignMpcCommitmentParams { From de4909905a7758068b137d3c1456d1d88fcbf2ef Mon Sep 17 00:00:00 2001 From: Marguerite Blair Date: Wed, 6 May 2026 22:39:25 -0400 Subject: [PATCH 3/5] feat(awm): fix recovery handlers to support dual key provider Ticket: WCN-363 --- .../postMpcV2Key.test.ts | 6 +++- .../recoveryMpcV2.test.ts | 30 +++++++++++++++---- .../recoveryMusigEth.test.ts | 16 ++++++---- .../handlers/ecdsaMPCV2Recovery.ts | 13 ++++---- .../handlers/utils/utils.ts | 4 ++- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts b/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts index 634aeeca..b07033ae 100644 --- a/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts +++ b/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts @@ -42,6 +42,10 @@ describe('postMpcV2Key', () => { clientCertAllowSelfSigned: true, }; + // Restore any existing stub from other test suites before re-stubbing + if (typeof (configModule.initConfig as any).restore === 'function') { + (configModule.initConfig as any).restore(); + } configStub = sinon.stub(configModule, 'initConfig').returns(cfg); // app setup @@ -93,7 +97,7 @@ describe('postMpcV2Key', () => { }); after(() => { - configStub.restore(); + configStub?.restore(); }); it('should be able to create a new MPC V2 wallet', async () => { diff --git a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts index a49c5f5e..b72d5107 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts @@ -20,7 +20,8 @@ describe('recoveryMpcV2', async () => { const cosmosLikeCoin = 'tsei'; const accessToken = 'test-token'; - // sinon stubs + // sinon sandbox + const sandbox = sinon.createSandbox(); let configStub: sinon.SinonStub; // key provider nocks setup @@ -67,7 +68,11 @@ describe('recoveryMpcV2', async () => { recoveryMode: true, }; - configStub = sinon.stub(configModule, 'initConfig').returns(cfg); + // Restore any existing stub from other test suites before re-stubbing + if (typeof (configModule.initConfig as any).restore === 'function') { + (configModule.initConfig as any).restore(); + } + configStub = sandbox.stub(configModule, 'initConfig').returns(cfg); // app setup app = advancedWalletManagerApp(cfg); @@ -79,7 +84,7 @@ describe('recoveryMpcV2', async () => { }); after(() => { - configStub.restore(); + sandbox.restore(); }); // happy path test @@ -133,15 +138,30 @@ describe('recoveryMpcV2', async () => { }); it('should route backup key retrieval to backup KMS when configured', async () => { + const kmsUrl = 'http://kms.invalid'; const backupKmsUrl = 'http://backup-kms.invalid'; + const mockKmsUserResponse = { + prv: JSON.stringify(userKeyShare), + pub: commonKeychain, + source: 'user', + type: 'tss', + }; + + const mockKmsBackupResponse = { + prv: JSON.stringify(backupKeyShare), + pub: commonKeychain, + source: 'backup', + type: 'tss', + }; + // Reconfigure app with backup KMS URL - configStub.restore(); const dualCfg: AdvancedWalletManagerConfig = { ...cfg, + keyProviderUrl: kmsUrl, backupKmsUrl, }; - configStub = sinon.stub(configModule, 'initConfig').returns(dualCfg); + configStub.returns(dualCfg); const dualApp = advancedWalletManagerApp(dualCfg); const dualAgent = request.agent(dualApp); diff --git a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts index 179f1fe1..6d9f614f 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts @@ -22,7 +22,8 @@ describe('recoveryMultisigTransaction', () => { const coin = 'hteth'; const accessToken = 'test-token'; - // sinon stubs + // sinon sandbox + const sandbox = sinon.createSandbox(); let configStub: sinon.SinonStub; before(() => { @@ -44,7 +45,11 @@ describe('recoveryMultisigTransaction', () => { recoveryMode: true, }; - configStub = sinon.stub(configModule, 'initConfig').returns(cfg); + // Restore any existing stub from other test suites before re-stubbing + if (typeof (configModule.initConfig as any).restore === 'function') { + (configModule.initConfig as any).restore(); + } + configStub = sandbox.stub(configModule, 'initConfig').returns(cfg); // app setup app = advancedWalletManagerApp(cfg); @@ -56,7 +61,7 @@ describe('recoveryMultisigTransaction', () => { }); after(() => { - configStub.restore(); + sandbox.restore(); }); it('should generate a successful txHex from unsigned sweep prebuild data', async () => { @@ -107,17 +112,18 @@ describe('recoveryMultisigTransaction', () => { }); it('should route backup key retrieval to backup KMS when configured', async () => { + const kmsUrl = 'http://kms.invalid'; const backupKmsUrl = 'http://backup-kms.invalid'; const { userPub, backupPub, walletContractAddress, userPrv, backupPrv, txHexResult } = awmData; const unsignedSweepPrebuildTx = unsignedSweepRecJSON as unknown as any; // Reconfigure app with backup KMS URL - configStub.restore(); const dualCfg: AdvancedWalletManagerConfig = { ...cfg, + keyProviderUrl: kmsUrl, backupKmsUrl, }; - configStub = sinon.stub(configModule, 'initConfig').returns(dualCfg); + configStub.returns(dualCfg); const dualApp = advancedWalletManagerApp(dualCfg); const dualAgent = request.agent(dualApp); diff --git a/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts b/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts index af957421..28ca6056 100644 --- a/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts +++ b/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts @@ -52,11 +52,14 @@ export async function ecdsaMPCv2Recovery( ); } - // setup clients and retreive the keys - // TODO: this needs to be segerated if the EBE instance cannot retrieve both keys - const keyProvider = new KeyProviderClient(req.config); - const { prv: userPrv } = await keyProvider.getKey({ pub, source: 'user' }); - const { prv: backupPrv } = await keyProvider.getKey({ pub, source: 'backup' }); + // setup clients and retrieve the keys + const userKeyProvider = new KeyProviderClient(req.config); + const backupCfg = req.config.backupKmsUrl + ? { ...req.config, keyProviderUrl: req.config.backupKmsUrl } + : req.config; + const backupKeyProvider = new KeyProviderClient(backupCfg); + const { prv: userPrv } = await userKeyProvider.getKey({ pub, source: 'user' }); + const { prv: backupPrv } = await backupKeyProvider.getKey({ pub, source: 'backup' }); // construct tx builder const txHash = await getMessageHash(coin, txHex); diff --git a/src/advancedWalletManager/handlers/utils/utils.ts b/src/advancedWalletManager/handlers/utils/utils.ts index c95ffdc3..34c61a29 100644 --- a/src/advancedWalletManager/handlers/utils/utils.ts +++ b/src/advancedWalletManager/handlers/utils/utils.ts @@ -16,7 +16,9 @@ export async function retrieveKeyProviderPrvKey({ source: string; cfg: AdvancedWalletManagerConfig; }): Promise { - const keyProvider = new KeyProviderClient(cfg); + const effectiveCfg = + source === 'backup' && cfg.backupKmsUrl ? { ...cfg, keyProviderUrl: cfg.backupKmsUrl } : cfg; + const keyProvider = new KeyProviderClient(effectiveCfg); // Retrieve the private key from key provider let prv: string; try { From 1fbe1417e3f854f63b0a3235779447bce20c6a4d Mon Sep 17 00:00:00 2001 From: Marguerite Blair Date: Thu, 7 May 2026 09:20:09 -0400 Subject: [PATCH 4/5] feat(awm): tests for eddsa backup with recovery key provider Ticket: WCN-363 --- .../advancedWalletManager/recoveryMpc.test.ts | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts index 93939f0a..971ac2a8 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts @@ -142,6 +142,85 @@ describe('recoveryMpc', () => { }); }); + describe('EdDSA dual-KMS recovery', () => { + it('should route backup key retrieval to backup KMS when configured', async () => { + const primaryKmsUrl = 'http://primary-kms.invalid'; + const backupKmsUrl = 'http://backup-kms.invalid'; + + const commonKeychain = + 'b6f5fb808f538a32735a89609e98fab75690a2c79b26f50a54c4cbf0fbca287138b733783f1590e12b4916ef0f6053b22044860117274bda44bd5d711855f174'; + + const mockKmsUserResponse = { + prv: '{"uShare":{"i":1,"t":2,"n":3,"y":"85aa6462d927329418f70f6d0863cf6cf33e7da2934f935e5927f1b13062d779","seed":"2f55c80fd6b5583dcde8037b2ee461d2e7d445a4d3e7a9b2a0d3d00b5f534169","chaincode":"66e80f2bf41a5706608352d51ceb07a5aa1729cab6c6993c124d5731546ed9a1"},"bitgoYShare":{"i":1,"j":3,"y":"483e53b72de3aa893df698d0b20b20777fb3d2716cc8483a9e9797174fd52b16","v":"e70696459e46434a2a12cc988e3ae714a61fe96da8a6764d058b849cab50d6dc","u":"49abf8144d265a77cf6d098eff784d6ce56ec77a182f6b39f47d5d8e28f2a802","chaincode":"797348468202f1d7fede0a7851f80162b02e7da306e65075dd864b6789b9bc5b"},"backupYShare":{"i":1,"j":2,"y":"249a9798d0064a989a16cd8f479edf09ffaee73f4175d2ac555ba90ff41b89da","v":"98e31d2b643e40060ba344c6a41fc096ea7e39a1ae879f65e4af645870e90ee0","u":"ac047b1bceab2e1a42d97ab540b39176e545d9c0af4a192aee8e1dae91a4240b","chaincode":"585bdc05c8f84802cbe7b9a1a07d4aa9c5fede93597a622854e9bad83a2d5b78"}}', + pub: commonKeychain, + source: 'user', + type: 'tss', + }; + + const mockKmsBackupResponse = { + prv: '{"uShare":{"i":2,"t":2,"n":3,"y":"249a9798d0064a989a16cd8f479edf09ffaee73f4175d2ac555ba90ff41b89da","seed":"abab5be2b32d07cf39b2a162af0f78bad8325b2fbdc89d14fd8b4e5767b74097","chaincode":"585bdc05c8f84802cbe7b9a1a07d4aa9c5fede93597a622854e9bad83a2d5b78"},"bitgoYShare":{"i":2,"j":3,"y":"483e53b72de3aa893df698d0b20b20777fb3d2716cc8483a9e9797174fd52b16","v":"e70696459e46434a2a12cc988e3ae714a61fe96da8a6764d058b849cab50d6dc","u":"eb54da28da3da22eb3d61797a02a96264be8940b7115aefbb90b9dd044db7f06","chaincode":"797348468202f1d7fede0a7851f80162b02e7da306e65075dd864b6789b9bc5b"},"userYShare":{"i":2,"j":1,"y":"85aa6462d927329418f70f6d0863cf6cf33e7da2934f935e5927f1b13062d779","v":"76cfdcbf0f769f21c64e0faf0072ebccbcc3aaa844522336af27f8e50ed7ca5f","u":"6ce814af82683423c8d8befd13f6eeeb0cd3f7274d1ebfdd5807fd2e4eaadb08","chaincode":"66e80f2bf41a5706608352d51ceb07a5aa1729cab6c6993c124d5731546ed9a1"}}', + pub: commonKeychain, + source: 'backup', + type: 'tss', + }; + + const dualCfg: AdvancedWalletManagerConfig = { + appMode: AppMode.ADVANCED_WALLET_MANAGER, + signingMode: SigningMode.LOCAL, + port: 0, + bind: 'localhost', + timeout: 60000, + keyProviderUrl: primaryKmsUrl, + backupKmsUrl, + httpLoggerFile: '', + tlsMode: TlsMode.DISABLED, + recoveryMode: true, + }; + + const dualApp = expressApp(dualCfg); + const dualAgent = request.agent(dualApp); + + // User key served from primary KMS + const userKmsNock = nock(primaryKmsUrl) + .get(`/key/${commonKeychain}`) + .query({ source: 'user' }) + .reply(200, mockKmsUserResponse) + .persist(); + + // Backup key served from backup KMS + const backupKmsNock = nock(backupKmsUrl) + .get(`/key/${commonKeychain}`) + .query({ source: 'backup' }) + .reply(200, mockKmsBackupResponse) + .persist(); + + const input = { + commonKeychain, + unsignedSweepPrebuildTx: { + txRequests: [ + { + unsignedTx: '', + signableHex: + 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAECvoOqYkvCPusjYyhX4GdUtzSeVIcx6GkwdpSk8SkU0/cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQtFGO2YBsrubq15CKqJLwXG3VEF1aEs36Rao6EaJDLAQECAAAMAgAAALhJxgAAAAAA', + derivationPath: 'm/0', + }, + ], + }, + }; + + const response = await dualAgent + .post(`/api/${sol}/mpc/recovery`) + .set('Authorization', `Bearer ${accessToken}`) + .send(input); + + response.status.should.equal(200); + response.body.should.have.property('txHex'); + + userKmsNock.isDone().should.be.true(); + backupKmsNock.isDone().should.be.true(); + }); + }); + describe('ECDSA sui recovery', () => { it('should successfully generate MPC sui transactions', async () => { const mockKeyProviderUserResponse = { From 86ef8bfd063a19fb3671f274c14a5201cf746395 Mon Sep 17 00:00:00 2001 From: Marguerite Blair Date: Thu, 7 May 2026 11:46:11 -0400 Subject: [PATCH 5/5] feat(awm): fixing test suite Ticket: WCN-363 --- .../postMpcV2Key.test.ts | 11 ++-- .../recoveryMpcV2.test.ts | 60 ++++++++++--------- .../recoveryMusigEth.test.ts | 4 -- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts b/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts index b07033ae..c86684bb 100644 --- a/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts +++ b/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts @@ -21,7 +21,8 @@ describe('postMpcV2Key', () => { const coin = 'hteth'; const accessToken = 'test-token'; - // sinon stubs + // sinon sandbox + const sandbox = sinon.createSandbox(); let configStub: sinon.SinonStub; before(() => { @@ -42,11 +43,7 @@ describe('postMpcV2Key', () => { clientCertAllowSelfSigned: true, }; - // Restore any existing stub from other test suites before re-stubbing - if (typeof (configModule.initConfig as any).restore === 'function') { - (configModule.initConfig as any).restore(); - } - configStub = sinon.stub(configModule, 'initConfig').returns(cfg); + configStub = sandbox.stub(configModule, 'initConfig').returns(cfg); // app setup app = advancedWalletManagerApp(cfg); @@ -97,7 +94,7 @@ describe('postMpcV2Key', () => { }); after(() => { - configStub?.restore(); + sandbox.restore(); }); it('should be able to create a new MPC V2 wallet', async () => { diff --git a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts index b72d5107..78128c11 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts @@ -9,7 +9,7 @@ import * as sinon from 'sinon'; import * as configModule from '../../../initConfig'; import { DklsTypes, DklsUtils } from '@bitgo-beta/sdk-lib-mpc'; -describe('recoveryMpcV2', async () => { +describe('recoveryMpcV2', () => { let cfg: AdvancedWalletManagerConfig; let app: express.Application; let agent: request.SuperAgentTest; @@ -25,31 +25,39 @@ describe('recoveryMpcV2', async () => { let configStub: sinon.SinonStub; // key provider nocks setup - const [userShare, backupShare] = await DklsUtils.generateDKGKeyShares(); - const userKeyShare = userShare.getKeyShare().toString('base64'); - const backupKeyShare = backupShare.getKeyShare().toString('base64'); - const commonKeychain = DklsTypes.getCommonKeychain(userShare.getKeyShare()); - - const mockKeyProviderUserResponse = { - prv: JSON.stringify(userKeyShare), - pub: commonKeychain, - source: 'user', - type: 'tss', - }; - - const mockKeyProviderBackupResponse = { - prv: JSON.stringify(backupKeyShare), - pub: commonKeychain, - source: 'backup', - type: 'tss', - }; - const input = { - txHex: - '02f6824268018502540be4008504a817c80083030d409443442e403d64d29c4f64065d0c1a0e8edc03d6c88801550f7dca700000823078c0', - pub: commonKeychain, - }; + let userKeyShare: string; + let backupKeyShare: string; + let commonKeychain: string; + let mockKeyProviderUserResponse: { prv: string; pub: string; source: string; type: string }; + let mockKeyProviderBackupResponse: { prv: string; pub: string; source: string; type: string }; + let input: { txHex: string; pub: string }; before(async () => { + const [userShare, backupShare] = await DklsUtils.generateDKGKeyShares(); + userKeyShare = userShare.getKeyShare().toString('base64'); + backupKeyShare = backupShare.getKeyShare().toString('base64'); + commonKeychain = DklsTypes.getCommonKeychain(userShare.getKeyShare()); + + mockKeyProviderUserResponse = { + prv: JSON.stringify(userKeyShare), + pub: commonKeychain, + source: 'user', + type: 'tss', + }; + + mockKeyProviderBackupResponse = { + prv: JSON.stringify(backupKeyShare), + pub: commonKeychain, + source: 'backup', + type: 'tss', + }; + + input = { + txHex: + '02f6824268018502540be4008504a817c80083030d409443442e403d64d29c4f64065d0c1a0e8edc03d6c88801550f7dca700000823078c0', + pub: commonKeychain, + }; + // nock config nock.disableNetConnect(); nock.enableNetConnect('127.0.0.1'); @@ -68,10 +76,6 @@ describe('recoveryMpcV2', async () => { recoveryMode: true, }; - // Restore any existing stub from other test suites before re-stubbing - if (typeof (configModule.initConfig as any).restore === 'function') { - (configModule.initConfig as any).restore(); - } configStub = sandbox.stub(configModule, 'initConfig').returns(cfg); // app setup diff --git a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts index 6d9f614f..b2b4ff6c 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts @@ -45,10 +45,6 @@ describe('recoveryMultisigTransaction', () => { recoveryMode: true, }; - // Restore any existing stub from other test suites before re-stubbing - if (typeof (configModule.initConfig as any).restore === 'function') { - (configModule.initConfig as any).restore(); - } configStub = sandbox.stub(configModule, 'initConfig').returns(cfg); // app setup