Skip to content

Commit b1ec9ba

Browse files
feat(mbe): throw valid error when server is down
1 parent 36ac2de commit b1ec9ba

File tree

2 files changed

+144
-50
lines changed

2 files changed

+144
-50
lines changed

src/api/master/clients/advancedWalletManagerClient.ts

Lines changed: 105 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -178,51 +178,6 @@ export interface SignMpcV2Round3Response {
178178
}
179179

180180
export class AdvancedWalletManagerClient {
181-
async recoveryMPC(params: {
182-
unsignedSweepPrebuildTx: MPCTx | MPCSweepTxs | MPCTxs | RecoveryTxRequest;
183-
userPub: string;
184-
backupPub: string;
185-
apiKey: string;
186-
coinSpecificParams?: Record<string, unknown>;
187-
walletContractAddress: string;
188-
}): Promise<SignedTransaction> {
189-
if (!this.coin) {
190-
throw new Error('Coin must be specified to recover MPC');
191-
}
192-
193-
try {
194-
logger.info('Recovering MPC for coin: %s', this.coin);
195-
196-
// Extract the required information from the sweep tx using our utility function
197-
const tx = params.unsignedSweepPrebuildTx;
198-
const { signableHex, derivationPath } = extractTransactionRequestInfo(tx);
199-
200-
const txRequest = {
201-
unsignedTx: '',
202-
signableHex,
203-
derivationPath,
204-
};
205-
206-
let request = this.apiClient['v1.mpc.recovery'].post({
207-
coin: this.coin,
208-
commonKeychain: params.userPub,
209-
unsignedSweepPrebuildTx: {
210-
txRequests: [txRequest],
211-
},
212-
});
213-
214-
if (this.tlsMode === TlsMode.MTLS) {
215-
request = request.agent(this.createHttpsAgent());
216-
}
217-
218-
const response = await request.decodeExpecting(200);
219-
return response.body;
220-
} catch (error) {
221-
const err = error as Error;
222-
logger.error('Failed to recover MPC: %s', err.message);
223-
throw err;
224-
}
225-
}
226181
private readonly baseUrl: string;
227182
private readonly advancedWalletManagerCert: string;
228183
private readonly tlsKey?: string;
@@ -274,6 +229,77 @@ export class AdvancedWalletManagerClient {
274229
});
275230
}
276231

232+
private checkServerRunning(error: any): void {
233+
// Check for specific connection errors
234+
if (
235+
error.code === 'ECONNREFUSED' ||
236+
error.code === 'ENOTFOUND' ||
237+
error.code === 'ECONNRESET'
238+
) {
239+
throw new Error(
240+
'Advanced Wallet Manager API is not running or unreachable. Please check if the service is available.',
241+
);
242+
}
243+
if (error.code === 'ETIMEDOUT' || error.timeout) {
244+
throw new Error(
245+
'Advanced Wallet Manager API request timed out. The service may be overloaded or unreachable.',
246+
);
247+
}
248+
if (error.status === 404) {
249+
throw new Error(
250+
'Advanced Wallet Manager API endpoint not found. Please verify the URL configuration.',
251+
);
252+
}
253+
}
254+
255+
async recoveryMPC(params: {
256+
unsignedSweepPrebuildTx: MPCTx | MPCSweepTxs | MPCTxs | RecoveryTxRequest;
257+
userPub: string;
258+
backupPub: string;
259+
apiKey: string;
260+
coinSpecificParams?: Record<string, unknown>;
261+
walletContractAddress: string;
262+
}): Promise<SignedTransaction> {
263+
if (!this.coin) {
264+
throw new Error('Coin must be specified to recover MPC');
265+
}
266+
267+
try {
268+
logger.info('Recovering MPC for coin: %s', this.coin);
269+
270+
// Extract the required information from the sweep tx using our utility function
271+
const tx = params.unsignedSweepPrebuildTx;
272+
const { signableHex, derivationPath } = extractTransactionRequestInfo(tx);
273+
274+
const txRequest = {
275+
unsignedTx: '',
276+
signableHex,
277+
derivationPath,
278+
};
279+
280+
let request = this.apiClient['v1.mpc.recovery'].post({
281+
coin: this.coin,
282+
commonKeychain: params.userPub,
283+
unsignedSweepPrebuildTx: {
284+
txRequests: [txRequest],
285+
},
286+
});
287+
288+
if (this.tlsMode === TlsMode.MTLS) {
289+
request = request.agent(this.createHttpsAgent());
290+
}
291+
292+
const response = await request.decodeExpecting(200);
293+
return response.body;
294+
} catch (error) {
295+
this.checkServerRunning(error);
296+
297+
const err = error as Error;
298+
logger.error('Failed to recover MPC: %s', err.message);
299+
throw err;
300+
}
301+
}
302+
277303
/**
278304
* Create an independent multisig key for a given source and coin
279305
*/
@@ -299,6 +325,8 @@ export class AdvancedWalletManagerClient {
299325
const response = await request.decodeExpecting(200);
300326
return response.body;
301327
} catch (error) {
328+
this.checkServerRunning(error);
329+
302330
logger.error(
303331
'Failed to create independent keychain: %s',
304332
(error as DecodeError).decodedResponse.body,
@@ -331,6 +359,8 @@ export class AdvancedWalletManagerClient {
331359

332360
return response.body;
333361
} catch (error) {
362+
this.checkServerRunning(error);
363+
334364
logger.error('Failed to sign multisig: %s', (error as DecodeError).decodedResponse.body);
335365
throw error;
336366
}
@@ -354,6 +384,8 @@ export class AdvancedWalletManagerClient {
354384
logger.info('Enclaved express service ping successful');
355385
return response.body;
356386
} catch (error) {
387+
this.checkServerRunning(error);
388+
357389
logger.error(
358390
'Failed to ping enclaved express service: %s',
359391
(error as DecodeError).decodedResponse.body,
@@ -379,6 +411,8 @@ export class AdvancedWalletManagerClient {
379411
logger.info('Successfully retrieved version information');
380412
return response.body;
381413
} catch (error) {
414+
this.checkServerRunning(error);
415+
382416
logger.error(
383417
'Failed to get version information: %s',
384418
(error as DecodeError).decodedResponse.body,
@@ -406,6 +440,8 @@ export class AdvancedWalletManagerClient {
406440

407441
return res.body;
408442
} catch (error) {
443+
this.checkServerRunning(error);
444+
409445
logger.error('Failed to recover multisig: %s', (error as DecodeError).decodedResponse.body);
410446
throw error;
411447
}
@@ -437,6 +473,8 @@ export class AdvancedWalletManagerClient {
437473
const response = await request.decodeExpecting(200);
438474
return response.body;
439475
} catch (error) {
476+
this.checkServerRunning(error);
477+
440478
logger.error(
441479
'Failed to initialize MPC key generation: %s',
442480
(error as DecodeError).decodedResponse.body,
@@ -485,6 +523,8 @@ export class AdvancedWalletManagerClient {
485523
const response = await request.decodeExpecting(200);
486524
return response.body;
487525
} catch (error) {
526+
this.checkServerRunning(error);
527+
488528
logger.error(
489529
'Failed to finalize MPC key generation: %s',
490530
(error as DecodeError).decodedResponse.body,
@@ -511,6 +551,8 @@ export class AdvancedWalletManagerClient {
511551
const response = await request.decodeExpecting(200);
512552
return response.body;
513553
} catch (error) {
554+
this.checkServerRunning(error);
555+
514556
logger.error(
515557
'Failed to sign mpc commitment: %s',
516558
(error as DecodeError).decodedResponse.body,
@@ -537,6 +579,8 @@ export class AdvancedWalletManagerClient {
537579
const response = await request.decodeExpecting(200);
538580
return response.body;
539581
} catch (error) {
582+
this.checkServerRunning(error);
583+
540584
logger.error('Failed to sign mpc r-share: %s', (error as DecodeError).decodedResponse.body);
541585
throw error;
542586
}
@@ -560,6 +604,8 @@ export class AdvancedWalletManagerClient {
560604
const response = await request.decodeExpecting(200);
561605
return response.body;
562606
} catch (error) {
607+
this.checkServerRunning(error);
608+
563609
logger.error('Failed to sign mpc g-share: %s', (error as DecodeError).decodedResponse.body);
564610
throw error;
565611
}
@@ -589,6 +635,8 @@ export class AdvancedWalletManagerClient {
589635
const response = await request.decodeExpecting(200);
590636
return response.body;
591637
} catch (error) {
638+
this.checkServerRunning(error);
639+
592640
logger.error(
593641
'Failed to initialize MPCv2 key generation: %s',
594642
(error as DecodeError).decodedResponse.body,
@@ -628,6 +676,8 @@ export class AdvancedWalletManagerClient {
628676
const response = await request.decodeExpecting(200);
629677
return response.body;
630678
} catch (error) {
679+
this.checkServerRunning(error);
680+
631681
logger.error(
632682
'Failed to execute MPCv2 round: %s',
633683
(error as DecodeError).decodedResponse.body,
@@ -664,6 +714,8 @@ export class AdvancedWalletManagerClient {
664714
const response = await request.decodeExpecting(200);
665715
return response.body;
666716
} catch (error) {
717+
this.checkServerRunning(error);
718+
667719
logger.error(
668720
'Failed to finalize MPCv2 key generation: %s',
669721
(error as DecodeError).decodedResponse.body,
@@ -699,6 +751,8 @@ export class AdvancedWalletManagerClient {
699751
const response = await request.decodeExpecting(200);
700752
return response.body;
701753
} catch (error) {
754+
this.checkServerRunning(error);
755+
702756
logger.error('Failed to sign MPCv2 round 1: %s', (error as DecodeError).decodedResponse.body);
703757
throw error;
704758
}
@@ -731,6 +785,8 @@ export class AdvancedWalletManagerClient {
731785
const response = await request.decodeExpecting(200);
732786
return response.body;
733787
} catch (error) {
788+
this.checkServerRunning(error);
789+
734790
logger.error('Failed to sign MPCv2 round 2: %s', (error as DecodeError).decodedResponse.body);
735791
throw error;
736792
}
@@ -763,6 +819,8 @@ export class AdvancedWalletManagerClient {
763819
const response = await request.decodeExpecting(200);
764820
return response.body;
765821
} catch (error) {
822+
this.checkServerRunning(error);
823+
766824
logger.error('Failed to sign MPCv2 round 3: %s', (error as DecodeError).decodedResponse.body);
767825
throw error;
768826
}
@@ -790,6 +848,8 @@ export class AdvancedWalletManagerClient {
790848
const response = await request.decodeExpecting(200);
791849
return response.body;
792850
} catch (error: any) {
851+
this.checkServerRunning(error);
852+
793853
logger.error(
794854
'Failed to recover MPCv2 wallet: %s',
795855
(error as DecodeError).decodedResponse.body,

src/kms/kmsClient.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@ export class KmsClient {
7171
}
7272
}
7373

74+
private checkServerRunning(error: any): void {
75+
// Check for specific connection errors
76+
if (
77+
error.code === 'ECONNREFUSED' ||
78+
error.code === 'ENOTFOUND' ||
79+
error.code === 'ECONNRESET'
80+
) {
81+
throw new Error(
82+
'KMS API is not running or unreachable. Please check if the KMS service is available.',
83+
);
84+
}
85+
if (error.code === 'ETIMEDOUT' || error.timeout) {
86+
throw new Error('KMS API request timed out. The service may be overloaded or unreachable.');
87+
}
88+
if (error.status === 404) {
89+
throw new Error('KMS API endpoint not found. Please verify the KMS URL configuration.');
90+
}
91+
}
92+
7493
async postKey(params: PostKeyParams): Promise<PostKeyResponse> {
7594
logger.info('Posting key to KMS with pub: %s and source: %s', params.pub, params.source);
7695

@@ -81,7 +100,11 @@ export class KmsClient {
81100
if (this.agent) req = req.agent(this.agent);
82101
kmsResponse = await req;
83102
} catch (error: any) {
84-
this.errorHandler(error, 'Error posting key to KMS');
103+
logger.error('Error posting key to KMS', error);
104+
105+
this.checkServerRunning(error);
106+
107+
throw new Error(`Failed to post key to KMS: ${error.message}`);
85108
}
86109

87110
// validate the response
@@ -112,8 +135,11 @@ export class KmsClient {
112135
if (this.agent) req = req.agent(this.agent);
113136
kmsResponse = await req;
114137
} catch (error: any) {
115-
console.log('Error getting key from KMS:', error);
116-
this.errorHandler(error, 'Error getting key from KMS');
138+
logger.error('Error getting key from KMS', error);
139+
140+
this.checkServerRunning(error);
141+
142+
throw new Error(`Failed to get key from KMS: ${error.message}`);
117143
}
118144

119145
// validate the response
@@ -141,7 +167,11 @@ export class KmsClient {
141167
if (this.agent) req = req.agent(this.agent);
142168
kmsResponse = await req;
143169
} catch (error: any) {
144-
this.errorHandler(error, 'Error generating data key from KMS');
170+
logger.error('Error generating data key from KMS when generating data key', error);
171+
172+
this.checkServerRunning(error);
173+
174+
throw new Error(`Failed to generate data key from KMS: ${error.message}`);
145175
}
146176

147177
// validate the response
@@ -172,7 +202,11 @@ export class KmsClient {
172202
if (this.agent) req = req.agent(this.agent);
173203
kmsResponse = await req;
174204
} catch (error: any) {
175-
this.errorHandler(error, 'Error decrypting data key from KMS');
205+
logger.error('Error decrypting data key from KMS', error);
206+
207+
this.checkServerRunning(error);
208+
209+
throw new Error(`Failed to decrypt data key from KMS: ${error.message}`);
176210
}
177211

178212
// validate the response

0 commit comments

Comments
 (0)