Skip to content

Commit 14770d7

Browse files
feat(mbe): throw valid error when server is down
1 parent d258bc2 commit 14770d7

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;
@@ -276,6 +231,77 @@ export class AdvancedWalletManagerClient {
276231
});
277232
}
278233

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

334362
return response.body;
335363
} catch (error) {
364+
this.checkServerRunning(error);
365+
336366
logger.error('Failed to sign multisig: %s', (error as DecodeError).decodedResponse.body);
337367
throw error;
338368
}
@@ -356,6 +386,8 @@ export class AdvancedWalletManagerClient {
356386
logger.info('Advanced Wallet Manager ping successful');
357387
return response.body;
358388
} catch (error) {
389+
this.checkServerRunning(error);
390+
359391
logger.error(
360392
'Failed to ping Advanced Wallet Manager: %s',
361393
(error as DecodeError).decodedResponse.body,
@@ -381,6 +413,8 @@ export class AdvancedWalletManagerClient {
381413
logger.info('Successfully retrieved version information');
382414
return response.body;
383415
} catch (error) {
416+
this.checkServerRunning(error);
417+
384418
logger.error(
385419
'Failed to get version information: %s',
386420
(error as DecodeError).decodedResponse.body,
@@ -408,6 +442,8 @@ export class AdvancedWalletManagerClient {
408442

409443
return res.body;
410444
} catch (error) {
445+
this.checkServerRunning(error);
446+
411447
logger.error('Failed to recover multisig: %s', (error as DecodeError).decodedResponse.body);
412448
throw error;
413449
}
@@ -439,6 +475,8 @@ export class AdvancedWalletManagerClient {
439475
const response = await request.decodeExpecting(200);
440476
return response.body;
441477
} catch (error) {
478+
this.checkServerRunning(error);
479+
442480
logger.error(
443481
'Failed to initialize MPC key generation: %s',
444482
(error as DecodeError).decodedResponse.body,
@@ -487,6 +525,8 @@ export class AdvancedWalletManagerClient {
487525
const response = await request.decodeExpecting(200);
488526
return response.body;
489527
} catch (error) {
528+
this.checkServerRunning(error);
529+
490530
logger.error(
491531
'Failed to finalize MPC key generation: %s',
492532
(error as DecodeError).decodedResponse.body,
@@ -513,6 +553,8 @@ export class AdvancedWalletManagerClient {
513553
const response = await request.decodeExpecting(200);
514554
return response.body;
515555
} catch (error) {
556+
this.checkServerRunning(error);
557+
516558
logger.error(
517559
'Failed to sign mpc commitment: %s',
518560
(error as DecodeError).decodedResponse.body,
@@ -539,6 +581,8 @@ export class AdvancedWalletManagerClient {
539581
const response = await request.decodeExpecting(200);
540582
return response.body;
541583
} catch (error) {
584+
this.checkServerRunning(error);
585+
542586
logger.error('Failed to sign mpc r-share: %s', (error as DecodeError).decodedResponse.body);
543587
throw error;
544588
}
@@ -562,6 +606,8 @@ export class AdvancedWalletManagerClient {
562606
const response = await request.decodeExpecting(200);
563607
return response.body;
564608
} catch (error) {
609+
this.checkServerRunning(error);
610+
565611
logger.error('Failed to sign mpc g-share: %s', (error as DecodeError).decodedResponse.body);
566612
throw error;
567613
}
@@ -591,6 +637,8 @@ export class AdvancedWalletManagerClient {
591637
const response = await request.decodeExpecting(200);
592638
return response.body;
593639
} catch (error) {
640+
this.checkServerRunning(error);
641+
594642
logger.error(
595643
'Failed to initialize MPCv2 key generation: %s',
596644
(error as DecodeError).decodedResponse.body,
@@ -630,6 +678,8 @@ export class AdvancedWalletManagerClient {
630678
const response = await request.decodeExpecting(200);
631679
return response.body;
632680
} catch (error) {
681+
this.checkServerRunning(error);
682+
633683
logger.error(
634684
'Failed to execute MPCv2 round: %s',
635685
(error as DecodeError).decodedResponse.body,
@@ -666,6 +716,8 @@ export class AdvancedWalletManagerClient {
666716
const response = await request.decodeExpecting(200);
667717
return response.body;
668718
} catch (error) {
719+
this.checkServerRunning(error);
720+
669721
logger.error(
670722
'Failed to finalize MPCv2 key generation: %s',
671723
(error as DecodeError).decodedResponse.body,
@@ -701,6 +753,8 @@ export class AdvancedWalletManagerClient {
701753
const response = await request.decodeExpecting(200);
702754
return response.body;
703755
} catch (error) {
756+
this.checkServerRunning(error);
757+
704758
logger.error('Failed to sign MPCv2 round 1: %s', (error as DecodeError).decodedResponse.body);
705759
throw error;
706760
}
@@ -733,6 +787,8 @@ export class AdvancedWalletManagerClient {
733787
const response = await request.decodeExpecting(200);
734788
return response.body;
735789
} catch (error) {
790+
this.checkServerRunning(error);
791+
736792
logger.error('Failed to sign MPCv2 round 2: %s', (error as DecodeError).decodedResponse.body);
737793
throw error;
738794
}
@@ -765,6 +821,8 @@ export class AdvancedWalletManagerClient {
765821
const response = await request.decodeExpecting(200);
766822
return response.body;
767823
} catch (error) {
824+
this.checkServerRunning(error);
825+
768826
logger.error('Failed to sign MPCv2 round 3: %s', (error as DecodeError).decodedResponse.body);
769827
throw error;
770828
}
@@ -792,6 +850,8 @@ export class AdvancedWalletManagerClient {
792850
const response = await request.decodeExpecting(200);
793851
return response.body;
794852
} catch (error: any) {
853+
this.checkServerRunning(error);
854+
795855
logger.error(
796856
'Failed to recover MPCv2 wallet: %s',
797857
(error as DecodeError).decodedResponse.body,

src/kms/kmsClient.ts

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

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

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

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

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

148178
// validate the response
@@ -173,7 +203,11 @@ export class KmsClient {
173203
if (this.agent) req = req.agent(this.agent);
174204
kmsResponse = await req;
175205
} catch (error: any) {
176-
this.errorHandler(error, 'Error decrypting data key from KMS');
206+
logger.error('Error decrypting data key from KMS', error);
207+
208+
this.checkServerRunning(error);
209+
210+
throw new Error(`Failed to decrypt data key from KMS: ${error.message}`);
177211
}
178212

179213
// validate the response

0 commit comments

Comments
 (0)