From 5c4c08b19a453cd44d4ec1341502365ba6a95e35 Mon Sep 17 00:00:00 2001 From: Pranish Nepal Date: Mon, 8 Jun 2026 15:00:58 -0400 Subject: [PATCH] feat: add ASYNC_MODE mode config and env vars Introduces ASYNC_MODE configuration support to Master BitGo Express. `ADVANCED_WALLET_MANAGER_URL` isn't required when `ASYNC_MODE=true` now. Instead, `AWM_ASYNC_URL` is required when ASYNC mode is enabled. Ticket: WCN-743 --- src/__tests__/api/master/accelerate.test.ts | 3 +- .../api/master/awmBackupClient.test.ts | 2 + src/__tests__/api/master/consolidate.test.ts | 3 +- .../api/master/consolidateUnspents.test.ts | 3 +- .../api/master/generateWallet.test.ts | 6 +- .../api/master/musigRecovery.test.ts | 3 +- src/__tests__/api/master/nonRecovery.test.ts | 3 +- .../recoveryConsolidationsWallet.test.ts | 3 +- .../api/master/recoveryWallet.test.ts | 3 +- .../api/master/recoveryWalletMpcV2.test.ts | 3 +- src/__tests__/api/master/sendMany.test.ts | 8 +- .../api/master/signAndSendTxRequest.test.ts | 2 + src/__tests__/api/master/testUtils.ts | 9 ++ src/__tests__/config.test.ts | 87 +++++++++++++++++++ .../integration/health.integ.test.ts | 2 + src/__tests__/integration/helpers/setup.ts | 2 + src/initConfig.ts | 35 +++++++- src/shared/appUtils.ts | 3 + src/shared/types/index.ts | 9 ++ 19 files changed, 178 insertions(+), 11 deletions(-) diff --git a/src/__tests__/api/master/accelerate.test.ts b/src/__tests__/api/master/accelerate.test.ts index 62ce1f8a..4ca3a6ff 100644 --- a/src/__tests__/api/master/accelerate.test.ts +++ b/src/__tests__/api/master/accelerate.test.ts @@ -7,7 +7,7 @@ import { Tbtc } from '@bitgo-beta/sdk-coin-btc'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; import { Environments, Wallet } from '@bitgo-beta/sdk-core'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; const TBTC_PREBUILD_PSBT_HEX = utxolib.bitgo .createPsbtForNetwork({ network: utxolib.networks.testnet }) @@ -64,6 +64,7 @@ describe('POST /api/v1/:coin/advancedwallet/:walletId/accelerate', () => { awmServerCaCert: 'test-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); diff --git a/src/__tests__/api/master/awmBackupClient.test.ts b/src/__tests__/api/master/awmBackupClient.test.ts index 4a7b9f6d..0489475a 100644 --- a/src/__tests__/api/master/awmBackupClient.test.ts +++ b/src/__tests__/api/master/awmBackupClient.test.ts @@ -1,5 +1,6 @@ import 'should'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; +import { DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; import { createAwmClient, createAwmBackupClient, @@ -19,6 +20,7 @@ describe('AWM Backup Client', () => { awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; describe('createAwmBackupClient', () => { diff --git a/src/__tests__/api/master/consolidate.test.ts b/src/__tests__/api/master/consolidate.test.ts index c5d79be8..4dfb8f70 100644 --- a/src/__tests__/api/master/consolidate.test.ts +++ b/src/__tests__/api/master/consolidate.test.ts @@ -7,7 +7,7 @@ import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; import { Environments } from '@bitgo-beta/sdk-core'; import { Hteth } from '@bitgo-beta/sdk-coin-eth'; import * as transactionRequests from '../../../masterBitgoExpress/handlers/transactionRequests'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; describe('POST /api/v1/:coin/advancedwallet/:walletId/consolidate', () => { let agent: request.SuperAgentTest; @@ -61,6 +61,7 @@ describe('POST /api/v1/:coin/advancedwallet/:walletId/consolidate', () => { awmServerCaCert: 'test-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); diff --git a/src/__tests__/api/master/consolidateUnspents.test.ts b/src/__tests__/api/master/consolidateUnspents.test.ts index 2826deb2..5697d1c6 100644 --- a/src/__tests__/api/master/consolidateUnspents.test.ts +++ b/src/__tests__/api/master/consolidateUnspents.test.ts @@ -7,7 +7,7 @@ import { Btc } from '@bitgo-beta/sdk-coin-btc'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; import { Environments, Wallet } from '@bitgo-beta/sdk-core'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; const BTC_PREBUILD_PSBT_HEX = utxolib.bitgo .createPsbtForNetwork({ network: utxolib.networks.bitcoin }) @@ -64,6 +64,7 @@ describe('POST /api/v1/:coin/advancedwallet/:walletId/consolidateunspents', () = awmServerCaCert: 'test-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); diff --git a/src/__tests__/api/master/generateWallet.test.ts b/src/__tests__/api/master/generateWallet.test.ts index b169c0e8..10ecf73d 100644 --- a/src/__tests__/api/master/generateWallet.test.ts +++ b/src/__tests__/api/master/generateWallet.test.ts @@ -10,7 +10,7 @@ import { Environments } from '@bitgo-beta/sdk-core'; import { BitGoAPI } from '@bitgo-beta/sdk-api'; import * as middleware from '../../../shared/middleware'; import { BitGoRequest } from '../../../types/request'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; function mockWalletResponse(id: string, coinName: string, overrides: Record = {}) { return { @@ -86,6 +86,7 @@ describe('POST /api/v1/:coin/advancedwallet/generate', () => { awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; // Setup middleware stubs before creating app @@ -125,6 +126,7 @@ describe('POST /api/v1/:coin/advancedwallet/generate', () => { awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; sinon.stub(middleware, 'prepareBitGo').callsFake(() => (req, res, next) => { @@ -366,6 +368,7 @@ describe('POST /api/v1/:coin/advancedwallet/generate', () => { awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; sinon.stub(middleware, 'prepareBitGo').callsFake(() => (req, res, next) => { @@ -1018,6 +1021,7 @@ describe('POST /api/v1/:coin/advancedwallet/generate', () => { awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; sinon.stub(middleware, 'prepareBitGo').callsFake(() => (req, res, next) => { diff --git a/src/__tests__/api/master/musigRecovery.test.ts b/src/__tests__/api/master/musigRecovery.test.ts index 1a8aaf5f..376c5f72 100644 --- a/src/__tests__/api/master/musigRecovery.test.ts +++ b/src/__tests__/api/master/musigRecovery.test.ts @@ -6,7 +6,7 @@ import * as request from 'supertest'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; import { data as ethRecoveryData } from '../../mocks/ethRecoveryMusigMockData'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; describe('POST /api/v1/:coin/advancedwallet/recovery', () => { let agent: request.SuperAgentTest; @@ -32,6 +32,7 @@ describe('POST /api/v1/:coin/advancedwallet/recovery', () => { tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, recoveryMode: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); diff --git a/src/__tests__/api/master/nonRecovery.test.ts b/src/__tests__/api/master/nonRecovery.test.ts index 2a553720..4668c020 100644 --- a/src/__tests__/api/master/nonRecovery.test.ts +++ b/src/__tests__/api/master/nonRecovery.test.ts @@ -4,7 +4,7 @@ import nock from 'nock'; import sinon from 'sinon'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; describe('Non Recovery Tests', () => { let agent: request.SuperAgentTest; @@ -24,6 +24,7 @@ describe('Non Recovery Tests', () => { httpLoggerFile: '', clientCertAllowSelfSigned: true, recoveryMode: false, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; before(() => { diff --git a/src/__tests__/api/master/recoveryConsolidationsWallet.test.ts b/src/__tests__/api/master/recoveryConsolidationsWallet.test.ts index 73bfda09..224e0712 100644 --- a/src/__tests__/api/master/recoveryConsolidationsWallet.test.ts +++ b/src/__tests__/api/master/recoveryConsolidationsWallet.test.ts @@ -5,7 +5,7 @@ import nock from 'nock'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; import { Trx } from '@bitgo-beta/sdk-coin-trx'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; describe('POST /api/v1/:coin/advancedwallet/recoveryconsolidations', () => { let agent: request.SuperAgentTest; @@ -143,6 +143,7 @@ describe('POST /api/v1/:coin/advancedwallet/recoveryconsolidations', () => { tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, recoveryMode: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); agent = request.agent(app); diff --git a/src/__tests__/api/master/recoveryWallet.test.ts b/src/__tests__/api/master/recoveryWallet.test.ts index 32e7ab26..2fbb8f5c 100644 --- a/src/__tests__/api/master/recoveryWallet.test.ts +++ b/src/__tests__/api/master/recoveryWallet.test.ts @@ -4,7 +4,7 @@ import nock from 'nock'; import sinon from 'sinon'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; describe('Recovery Tests', () => { let agent: request.SuperAgentTest; @@ -24,6 +24,7 @@ describe('Recovery Tests', () => { tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, recoveryMode: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; before(() => { diff --git a/src/__tests__/api/master/recoveryWalletMpcV2.test.ts b/src/__tests__/api/master/recoveryWalletMpcV2.test.ts index e249762b..9c1b0110 100644 --- a/src/__tests__/api/master/recoveryWalletMpcV2.test.ts +++ b/src/__tests__/api/master/recoveryWalletMpcV2.test.ts @@ -4,7 +4,7 @@ import nock from 'nock'; import sinon from 'sinon'; import { app as expressApp } from '../../../masterBitGoExpressApp'; import { AppMode, MasterExpressConfig, TlsMode } from '../../../shared/types'; -import { BitGoAPITestHarness } from './testUtils'; +import { BitGoAPITestHarness, DEFAULT_ASYNC_MODE_CONFIG } from './testUtils'; describe('MBE mpcv2 recovery', () => { let agent: request.SuperAgentTest; @@ -31,6 +31,7 @@ describe('MBE mpcv2 recovery', () => { tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, recoveryMode: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); diff --git a/src/__tests__/api/master/sendMany.test.ts b/src/__tests__/api/master/sendMany.test.ts index 2cb8f3f1..ae4ebea9 100644 --- a/src/__tests__/api/master/sendMany.test.ts +++ b/src/__tests__/api/master/sendMany.test.ts @@ -10,7 +10,11 @@ import * as utxolib from '@bitgo-beta/utxo-lib'; import { Tbtc } from '@bitgo-beta/sdk-coin-btc'; import { Tsol } from '@bitgo-beta/sdk-coin-sol'; import assert from 'assert'; -import { BitGoAPITestHarness, nockEcdsaMpcv2SendManySigningFlow } from './testUtils'; +import { + BitGoAPITestHarness, + DEFAULT_ASYNC_MODE_CONFIG, + nockEcdsaMpcv2SendManySigningFlow, +} from './testUtils'; const testWalletId = 'test-wallet-id'; const testBitgoApiUrl = Environments.test.uri; @@ -129,6 +133,7 @@ describe('POST /api/v1/:coin/advancedwallet/:walletId/sendMany', () => { awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); @@ -812,6 +817,7 @@ describe('POST /api/v1/:coin/advancedwallet/:walletId/sendMany', () => { authVersion: 2, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; try { diff --git a/src/__tests__/api/master/signAndSendTxRequest.test.ts b/src/__tests__/api/master/signAndSendTxRequest.test.ts index 2d71a4e1..88f6d564 100644 --- a/src/__tests__/api/master/signAndSendTxRequest.test.ts +++ b/src/__tests__/api/master/signAndSendTxRequest.test.ts @@ -11,6 +11,7 @@ import { DEFAULT_ECDSA_MPCV2_TX_REQUEST_ID, DEFAULT_ECDSA_MPCV2_WALLET_ID, nockEcdsaMpcv2SigningFlow, + DEFAULT_ASYNC_MODE_CONFIG, } from './testUtils'; const walletId = DEFAULT_ECDSA_MPCV2_WALLET_ID; @@ -40,6 +41,7 @@ describe('POST /api/v1/:coin/advancedwallet/:walletId/txrequest/:txRequestId/sig awmServerCaCert: 'dummy-cert', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }; const app = expressApp(config); diff --git a/src/__tests__/api/master/testUtils.ts b/src/__tests__/api/master/testUtils.ts index 4edaacf5..d4129cc2 100644 --- a/src/__tests__/api/master/testUtils.ts +++ b/src/__tests__/api/master/testUtils.ts @@ -1,6 +1,15 @@ import { BitGoAPI } from '@bitgo-beta/sdk-api'; import { SignatureShareRecord, SignatureShareType } from '@bitgo-beta/sdk-core'; import nock from 'nock'; +import { AsyncModeConfig } from '../../../shared/types'; + +export const DEFAULT_ASYNC_MODE_CONFIG: AsyncModeConfig = { + enabled: false, + awmAsyncUrl: '', + pollIntervalInMs: 30000, + jobTtlInSeconds: 3600, + jobTtlMpcInSeconds: 7200, +}; export class BitGoAPITestHarness extends BitGoAPI { static clearConstantsCache(): void { diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index 1ee549bf..4e2e3b98 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -60,6 +60,11 @@ describe('Configuration', () => { delete process.env.AWM_BACKUP_CLIENT_TLS_CERT_PATH; delete process.env.AWM_BACKUP_CLIENT_TLS_KEY; delete process.env.AWM_BACKUP_CLIENT_TLS_CERT; + delete process.env.ASYNC_MODE; + delete process.env.AWM_ASYNC_URL; + delete process.env.MBE_POLL_INTERVAL_MS; + delete process.env.MBE_JOB_TTL_S; + delete process.env.MBE_JOB_TTL_MPC_S; }); after(() => { @@ -650,5 +655,87 @@ describe('Configuration', () => { (cfg.awmBackupClientTlsCert === undefined).should.be.true(); } }); + + it('should default asyncModeConfig to disabled with default values', () => { + process.env.TLS_MODE = 'disabled'; + const cfg = initConfig(); + isMasterExpressConfig(cfg).should.be.true(); + if (isMasterExpressConfig(cfg)) { + cfg.asyncModeConfig.enabled.should.be.false(); + cfg.asyncModeConfig.awmAsyncUrl.should.equal(''); + cfg.asyncModeConfig.pollIntervalInMs.should.equal(30000); + cfg.asyncModeConfig.jobTtlInSeconds.should.equal(3600); + cfg.asyncModeConfig.jobTtlMpcInSeconds.should.equal(7200); + } + }); + + it('should load asyncModeConfig from env vars when ASYNC_MODE is true', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + process.env.AWM_ASYNC_URL = 'http://awm-async:8080'; + process.env.MBE_POLL_INTERVAL_MS = '5000'; + process.env.MBE_JOB_TTL_S = '1800'; + process.env.MBE_JOB_TTL_MPC_S = '3600'; + const cfg = initConfig(); + isMasterExpressConfig(cfg).should.be.true(); + if (isMasterExpressConfig(cfg)) { + cfg.asyncModeConfig.enabled.should.be.true(); + cfg.asyncModeConfig.awmAsyncUrl.should.equal('http://awm-async:8080'); + cfg.asyncModeConfig.pollIntervalInMs.should.equal(5000); + cfg.asyncModeConfig.jobTtlInSeconds.should.equal(1800); + cfg.asyncModeConfig.jobTtlMpcInSeconds.should.equal(3600); + } + }); + + it('should use default numeric values when async mode numeric env vars are not set', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + process.env.AWM_ASYNC_URL = 'http://awm-async:8080'; + const cfg = initConfig(); + isMasterExpressConfig(cfg).should.be.true(); + if (isMasterExpressConfig(cfg)) { + cfg.asyncModeConfig.pollIntervalInMs.should.equal(30000); + cfg.asyncModeConfig.jobTtlInSeconds.should.equal(3600); + cfg.asyncModeConfig.jobTtlMpcInSeconds.should.equal(7200); + } + }); + + it('should throw when ASYNC_MODE is true but AWM_ASYNC_URL is missing', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + (() => initConfig()).should.throw('AWM_ASYNC_URL is required when ASYNC_MODE is true'); + }); + + it('should allow missing ADVANCED_WALLET_MANAGER_URL when ASYNC_MODE is true', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + process.env.AWM_ASYNC_URL = 'http://awm-async:8080'; + delete process.env.ADVANCED_WALLET_MANAGER_URL; + (() => initConfig()).should.not.throw(); + }); + + it('should throw when MBE_POLL_INTERVAL_MS is negative', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + process.env.AWM_ASYNC_URL = 'http://awm-async:8080'; + process.env.MBE_POLL_INTERVAL_MS = '-1'; + (() => initConfig()).should.throw('MBE_POLL_INTERVAL_MS must be a positive number, got -1'); + }); + + it('should throw when MBE_JOB_TTL_S is negative', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + process.env.AWM_ASYNC_URL = 'http://awm-async:8080'; + process.env.MBE_JOB_TTL_S = '-1'; + (() => initConfig()).should.throw('MBE_JOB_TTL_S must be a positive number, got -1'); + }); + + it('should throw when MBE_JOB_TTL_MPC_S is negative', () => { + process.env.TLS_MODE = 'disabled'; + process.env.ASYNC_MODE = 'true'; + process.env.AWM_ASYNC_URL = 'http://awm-async:8080'; + process.env.MBE_JOB_TTL_MPC_S = '-1'; + (() => initConfig()).should.throw('MBE_JOB_TTL_MPC_S must be a positive number, got -1'); + }); }); }); diff --git a/src/__tests__/integration/health.integ.test.ts b/src/__tests__/integration/health.integ.test.ts index e58357a4..50684696 100644 --- a/src/__tests__/integration/health.integ.test.ts +++ b/src/__tests__/integration/health.integ.test.ts @@ -3,6 +3,7 @@ import * as http from 'http'; import { app as awmApp } from '../../advancedWalletManagerApp'; import { app as mbeApp } from '../../masterBitGoExpressApp'; import { AppMode, TlsMode, SigningMode } from '../../shared/types'; +import { DEFAULT_ASYNC_MODE_CONFIG } from '../api/master/testUtils'; import { listen, close, LOCALHOST } from './helpers/servers'; describe('Integration Test — health checks', () => { @@ -38,6 +39,7 @@ describe('Integration Test — health checks', () => { disableEnvCheck: true, advancedWalletManagerUrl: `http://${LOCALHOST}:${awmPort}`, awmServerCertAllowSelfSigned: true, + asyncModeConfig: DEFAULT_ASYNC_MODE_CONFIG, }), ); mbePort = await listen(mbeServer); diff --git a/src/__tests__/integration/helpers/setup.ts b/src/__tests__/integration/helpers/setup.ts index 7deea057..f9210594 100644 --- a/src/__tests__/integration/helpers/setup.ts +++ b/src/__tests__/integration/helpers/setup.ts @@ -2,6 +2,7 @@ import * as http from 'http'; import { app as awmApp } from '../../../advancedWalletManagerApp'; import { app as mbeApp } from '../../../masterBitGoExpressApp'; import { AppMode, TlsMode, SigningMode } from '../../../shared/types'; +import { DEFAULT_ASYNC_MODE_CONFIG } from '../../api/master/testUtils'; import { listen, close, LOCALHOST } from './servers'; import { startMockKeyProviderServer, MockKeyProviderServer } from './mockKeyProviderServer'; import { startMockBitgoServer, MockBitgoServer } from './mockBitgoServer'; @@ -50,6 +51,7 @@ export async function startServices(opts: StartServicesOptions = {}): Promise { + const isAsyncMode = readEnvVar('ASYNC_MODE') === 'true'; const advancedWalletManagerUrl = readEnvVar('ADVANCED_WALLET_MANAGER_URL'); const advancedWalletManagerBackupUrl = readEnvVar('ADVANCED_WALLET_MANAGER_BACKUP_URL'); const awmServerCaCertPath = readEnvVar('AWM_SERVER_CA_CERT_PATH'); @@ -350,7 +381,7 @@ function masterExpressEnvConfig(): Partial { const awmServerCertAllowSelfSigned = readEnvVar('AWM_SERVER_CERT_ALLOW_SELF_SIGNED') === 'true'; const tlsMode = determineTlsMode(); - if (!advancedWalletManagerUrl) { + if (!isAsyncMode && !advancedWalletManagerUrl) { throw new Error( 'ADVANCED_WALLET_MANAGER_URL environment variable is required and cannot be empty', ); @@ -407,6 +438,7 @@ function masterExpressEnvConfig(): Partial { mtlsAllowedClientFingerprints: readEnvVar('MTLS_ALLOWED_CLIENT_FINGERPRINTS')?.split(','), clientCertAllowSelfSigned, recoveryMode: readEnvVar('RECOVERY_MODE') === 'true', + asyncModeConfig: readAsyncModeConfig(isAsyncMode), }; } @@ -459,6 +491,7 @@ function mergeMasterExpressConfigs( mtlsAllowedClientFingerprints: get('mtlsAllowedClientFingerprints'), clientCertAllowSelfSigned: get('clientCertAllowSelfSigned'), recoveryMode: get('recoveryMode'), + asyncModeConfig: get('asyncModeConfig'), }; } diff --git a/src/shared/appUtils.ts b/src/shared/appUtils.ts index 52e90034..27a65eb3 100644 --- a/src/shared/appUtils.ts +++ b/src/shared/appUtils.ts @@ -216,4 +216,7 @@ export function validateMasterExpressConfig(config: MasterExpressConfig) { throw new Error('AWM_SERVER_CA_CERT_PATH is required when TLS mode is MTLS'); } } + if (config.asyncModeConfig.enabled && !config.asyncModeConfig.awmAsyncUrl) { + throw new Error('AWM_ASYNC_URL is required when ASYNC_MODE is true'); + } } diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts index c8fc25d3..31b08e29 100644 --- a/src/shared/types/index.ts +++ b/src/shared/types/index.ts @@ -69,6 +69,14 @@ export interface AdvancedWalletManagerConfig extends BaseConfig { signingMode: SigningMode; } +export interface AsyncModeConfig { + enabled: boolean; + awmAsyncUrl: string; + pollIntervalInMs: number; + jobTtlInSeconds: number; + jobTtlMpcInSeconds: number; +} + // Master Express mode specific configuration export interface MasterExpressConfig extends BaseConfig { appMode: AppMode.MASTER_EXPRESS; @@ -104,6 +112,7 @@ export interface MasterExpressConfig extends BaseConfig { mtlsAllowedClientFingerprints?: string[]; clientCertAllowSelfSigned?: boolean; recoveryMode?: boolean; + asyncModeConfig: AsyncModeConfig; } // Union type for the configuration