From a98af04f12d8d20b4493b554db13d016399febf8 Mon Sep 17 00:00:00 2001 From: Pranav Jain Date: Tue, 29 Jul 2025 12:35:16 -0400 Subject: [PATCH 1/2] fix(mbe): fix validation params order for utxo Ticket: WP-5454 --- src/api/master/handlers/recoveryWallet.ts | 58 +++++++++++++++-------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/api/master/handlers/recoveryWallet.ts b/src/api/master/handlers/recoveryWallet.ts index cff157e8..2390a7cf 100644 --- a/src/api/master/handlers/recoveryWallet.ts +++ b/src/api/master/handlers/recoveryWallet.ts @@ -53,42 +53,62 @@ interface EnclavedRecoveryParams { walletContractAddress: string; } -function validateRecoveryParams(sdkCoin: BaseCoin, params?: CoinSpecificParams) { +function validateRecoveryParams( + sdkCoin: BaseCoin, + params?: CoinSpecificParams, + isMpcRecovery = false, +) { if (!params) { return; } + if (isUtxoCoin(sdkCoin)) { + if (params.solanaRecoveryOptions || params.evmRecoveryOptions) { + throw new ValidationError('Invalid parameters provided for UTXO coin recovery'); + } + return; + } + + if (!isMpcRecovery && isEthLikeCoin(sdkCoin)) { + if (params.solanaRecoveryOptions || params.utxoRecoveryOptions) { + throw new ValidationError('Invalid parameters provided for ETH-like coin recovery'); + } + return; + } + if (isEddsaCoin(sdkCoin)) { if (params.evmRecoveryOptions || params.utxoRecoveryOptions) { throw new ValidationError('Invalid parameters provided for Solana coin recovery'); } - } else if (isEcdsaCoin(sdkCoin)) { + return; + } + + if (isEcdsaCoin(sdkCoin)) { if (isEthLikeCoin(sdkCoin)) { - if (!params.ecdsaEthLikeRecoverySpecificParams) { - throw new ValidationError('Invalid parameters provided for ETH-like MPC V2 coin recovery'); + if (isMpcRecovery) { + if (!params.ecdsaEthLikeRecoverySpecificParams) { + throw new ValidationError( + 'Invalid parameters provided for ETH-like MPC V2 coin recovery', + ); + } } + return; } else if (isCosmosLikeCoin(sdkCoin)) { - if (!params.ecdsaCosmosLikeRecoverySpecificParams) { - throw new ValidationError( - 'Invalid parameters provided for Cosmos-like MPC V2 coin recovery', - ); + if (isMpcRecovery) { + if (!params.ecdsaCosmosLikeRecoverySpecificParams) { + throw new ValidationError( + 'Invalid parameters provided for Cosmos-like MPC V2 coin recovery', + ); + } } } else { throw new NotImplementedError( `MPC V2 recovery is not supported for coin family: ${sdkCoin.getFamily()}`, ); } - } else { - if (isUtxoCoin(sdkCoin)) { - if (params.solanaRecoveryOptions || params.evmRecoveryOptions) { - throw new ValidationError('Invalid parameters provided for UTXO coin recovery'); - } - } else if (isEthLikeCoin(sdkCoin)) { - if (params.solanaRecoveryOptions || params.utxoRecoveryOptions) { - throw new ValidationError('Invalid parameters provided for ETH-like coin recovery'); - } - } + return; } + throw new ValidationError('Recovery parameters are not valid'); } async function handleEthLikeRecovery( @@ -216,7 +236,7 @@ export async function handleRecoveryWalletOnPrem( const sdkCoin = await coinFactory.getCoin(coin, bitgo); // Validate that we have correct parameters for recovery - validateRecoveryParams(sdkCoin, coinSpecificParams); + validateRecoveryParams(sdkCoin, coinSpecificParams, req.decoded.isTssRecovery); // Handle TSS recovery if (req.decoded.isTssRecovery) { From d316e35d6f5af5f6febc3199e94aedd55a50fb11 Mon Sep 17 00:00:00 2001 From: Pranav Jain Date: Tue, 29 Jul 2025 20:58:19 -0400 Subject: [PATCH 2/2] chore(mbe): check for required params instead of other coin params Ticket: WP-5454 --- .../api/master/recoveryWallet.test.ts | 23 +++++---- .../api/master/recoveryWalletMpcV2.test.ts | 2 +- src/api/master/handlers/recoveryWallet.ts | 48 +++++++++---------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/__tests__/api/master/recoveryWallet.test.ts b/src/__tests__/api/master/recoveryWallet.test.ts index 5fb3f6be..99d3283a 100644 --- a/src/__tests__/api/master/recoveryWallet.test.ts +++ b/src/__tests__/api/master/recoveryWallet.test.ts @@ -232,7 +232,9 @@ describe('Recovery Tests', () => { response.status.should.equal(422); response.body.should.have.property('error'); response.body.should.have.property('details'); - response.body.details.should.containEql('Invalid parameters provided for UTXO coin recovery'); + response.body.details.should.containEql( + 'UTXO recovery options are required for UTXO coin recovery', + ); }); it('should reject incorrect Solana parameters for a UTXO coin', async () => { @@ -267,7 +269,9 @@ describe('Recovery Tests', () => { response.status.should.equal(422); response.body.should.have.property('error'); response.body.should.have.property('details'); - response.body.details.should.containEql('Invalid parameters provided for UTXO coin recovery'); + response.body.details.should.containEql( + 'UTXO recovery options are required for UTXO coin recovery', + ); }); it('should reject using legacy coinSpecificParams format', async () => { @@ -294,9 +298,12 @@ describe('Recovery Tests', () => { }, }); - response.status.should.equal(500); - // Since we test that the incorrect format doesn't work anymore + response.status.should.equal(422); response.body.should.have.property('error'); + response.body.should.have.property('details'); + response.body.details.should.containEql( + 'UTXO recovery options are required for UTXO coin recovery', + ); }); }); @@ -355,7 +362,7 @@ describe('Recovery Tests', () => { response.body.should.have.property('error'); response.body.should.have.property('details'); response.body.details.should.containEql( - 'Invalid parameters provided for ETH-like coin recovery', + 'EVM recovery options are required for ETH-like coin recovery', ); }); @@ -393,7 +400,7 @@ describe('Recovery Tests', () => { response.body.should.have.property('error'); response.body.should.have.property('details'); response.body.details.should.containEql( - 'Invalid parameters provided for ETH-like coin recovery', + 'EVM recovery options are required for ETH-like coin recovery', ); }); }); @@ -448,7 +455,7 @@ describe('Recovery Tests', () => { response.body.should.have.property('error'); response.body.should.have.property('details'); response.body.details.should.containEql( - 'Invalid parameters provided for Solana coin recovery', + 'Solana recovery options are required for EdDSA coin recovery', ); }); @@ -479,7 +486,7 @@ describe('Recovery Tests', () => { response.body.should.have.property('error'); response.body.should.have.property('details'); response.body.details.should.containEql( - 'Invalid parameters provided for Solana coin recovery', + 'Solana recovery options are required for EdDSA coin recovery', ); }); }); diff --git a/src/__tests__/api/master/recoveryWalletMpcV2.test.ts b/src/__tests__/api/master/recoveryWalletMpcV2.test.ts index dfc4cfdf..a01dbf7c 100644 --- a/src/__tests__/api/master/recoveryWalletMpcV2.test.ts +++ b/src/__tests__/api/master/recoveryWalletMpcV2.test.ts @@ -203,7 +203,7 @@ describe('MBE mpcv2 recovery', () => { response.status.should.equal(422); response.body.should.have.property('error'); response.body.error.should.equal( - 'Invalid parameters provided for ETH-like MPC V2 coin recovery', + 'ECDSA ETH-like recovery specific parameters are required for MPC recovery', ); }); }); diff --git a/src/api/master/handlers/recoveryWallet.ts b/src/api/master/handlers/recoveryWallet.ts index 2390a7cf..f8072cfc 100644 --- a/src/api/master/handlers/recoveryWallet.ts +++ b/src/api/master/handlers/recoveryWallet.ts @@ -63,52 +63,48 @@ function validateRecoveryParams( } if (isUtxoCoin(sdkCoin)) { - if (params.solanaRecoveryOptions || params.evmRecoveryOptions) { - throw new ValidationError('Invalid parameters provided for UTXO coin recovery'); - } - return; - } - - if (!isMpcRecovery && isEthLikeCoin(sdkCoin)) { - if (params.solanaRecoveryOptions || params.utxoRecoveryOptions) { - throw new ValidationError('Invalid parameters provided for ETH-like coin recovery'); + // UTXO coins need utxoRecoveryOptions for standard recovery + if (!isMpcRecovery && !params.utxoRecoveryOptions) { + throw new ValidationError('UTXO recovery options are required for UTXO coin recovery'); } return; } if (isEddsaCoin(sdkCoin)) { - if (params.evmRecoveryOptions || params.utxoRecoveryOptions) { - throw new ValidationError('Invalid parameters provided for Solana coin recovery'); + // EdDSA coins (like Solana) need solanaRecoveryOptions for standard recovery + if (!params.solanaRecoveryOptions) { + throw new ValidationError('Solana recovery options are required for EdDSA coin recovery'); } return; } - if (isEcdsaCoin(sdkCoin)) { + if (isEcdsaCoin(sdkCoin) && isMpcRecovery) { if (isEthLikeCoin(sdkCoin)) { - if (isMpcRecovery) { - if (!params.ecdsaEthLikeRecoverySpecificParams) { - throw new ValidationError( - 'Invalid parameters provided for ETH-like MPC V2 coin recovery', - ); - } + if (!params.ecdsaEthLikeRecoverySpecificParams) { + throw new ValidationError( + 'ECDSA ETH-like recovery specific parameters are required for MPC recovery', + ); } - return; } else if (isCosmosLikeCoin(sdkCoin)) { - if (isMpcRecovery) { - if (!params.ecdsaCosmosLikeRecoverySpecificParams) { - throw new ValidationError( - 'Invalid parameters provided for Cosmos-like MPC V2 coin recovery', - ); - } + // ECDSA Cosmos-like MPC recovery needs ecdsaCosmosLikeRecoverySpecificParams + if (!params.ecdsaCosmosLikeRecoverySpecificParams) { + throw new ValidationError( + 'ECDSA Cosmos-like recovery specific parameters are required for MPC recovery', + ); } } else { throw new NotImplementedError( `MPC V2 recovery is not supported for coin family: ${sdkCoin.getFamily()}`, ); } + } + if (!isMpcRecovery && isEthLikeCoin(sdkCoin)) { + // Non-ECDSA ETH-like coins need evmRecoveryOptions for standard recovery + if (!params.evmRecoveryOptions) { + throw new ValidationError('EVM recovery options are required for ETH-like coin recovery'); + } return; } - throw new ValidationError('Recovery parameters are not valid'); } async function handleEthLikeRecovery(