Skip to content

Conversation

@mohammadalfaiyazbitgo
Copy link
Contributor

No description provided.

@mohammadalfaiyazbitgo mohammadalfaiyazbitgo force-pushed the WP-5166-eddsa-recovery-v2 branch from de845fb to cbe8005 Compare July 11, 2025 03:27
@mohammadalfaiyazbitgo mohammadalfaiyazbitgo changed the title WIP: draft for eddsa recvoery feat: sign eddsa recovery transaction Jul 11, 2025
@mohammadalfaiyazbitgo mohammadalfaiyazbitgo marked this pull request as ready for review July 11, 2025 03:29
@mohammadalfaiyazbitgo
Copy link
Contributor Author

@claude

@mohammadalfaiyazbitgo mohammadalfaiyazbitgo force-pushed the WP-5166-eddsa-recovery-v2 branch from cbe8005 to 3a3ea85 Compare July 11, 2025 03:30
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for EdDSA recovery transactions, introducing a new TSS recovery flow alongside the existing multi-signature recovery mechanism. The changes enable signing recovery transactions for EdDSA-based coins (Solana, Near, Sui, Ada, Dot) through a new MPC endpoint.

  • Implements EdDSA recovery transaction signing functionality with TSS keyshare support
  • Adds new API endpoint /api/{coin}/mpc/recovery for EdDSA recovery operations
  • Restructures existing recovery API to support both TSS and traditional multi-signature recovery modes

Reviewed Changes

Copilot reviewed 12 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/shared/coinUtils.ts Adds utility functions for Solana coin detection and EdDSA algorithm checking
src/enclavedBitgoExpress/routers/enclavedApiSpec.ts Defines new MPC recovery endpoint with request/response types and handler routing
src/api/master/routers/masterApiSpec.ts Updates recovery wallet API structure to support both TSS and multi-signature recovery parameters
src/api/master/handlers/recoveryWallet.ts Implements dual recovery flow logic handling both EdDSA TSS and traditional multi-signature recovery
src/api/master/handlers/recoverEddsaWallets.ts New module for coin-specific EdDSA wallet recovery with dynamic imports
src/api/master/clients/enclavedExpressClient.ts Adds MPC recovery client method with transaction format handling
src/api/enclaved/mpcFinalize.ts Updates signing material structure for user vs backup key differentiation
src/api/enclaved/handlers/signEddsaRecoveryTransaction.ts New handler implementing EdDSA signature generation with TSS support
src/tests/api/master/recoveryWallet.test.ts Updates test structure to match new API parameter organization
src/tests/api/master/musigRecovery.test.ts Updates test cases and adds TSS recovery validation test
package.json Adds EdDSA coin SDK dependencies and version resolution
masterBitgoExpress.json Updates OpenAPI specification for new recovery API structure

recoveryDestination,
apiKey: params.apiKey,
});
console.log('Unsigned sweep tx');
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.log statements should not be used in production code. Consider using a proper logging framework or removing this debug output.

Copilot uses AI. Check for mistakes.
apiKey: params.apiKey,
});
console.log('Unsigned sweep tx');
console.log(JSON.stringify(unsignedSweepPrebuildTx, null, 2));
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.log statements should not be used in production code. Consider using a proper logging framework or removing this debug output.

Copilot uses AI. Check for mistakes.

return { txBuilder, publicKey };
} catch (error) {
console.error(error);
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.error should be replaced with a proper logging framework for consistency with the rest of the codebase.

Suggested change
console.error(error);
logger.error(error);

Copilot uses AI. Check for mistakes.
throw new Error('Invalid user public key format');
} else if (!sdkCoin.isValidPub(backupPub)) {
throw new Error('Invalid backup public');
throw new Error('Invalid backup public key format');
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message should be 'Invalid backup public key format' to be consistent with the user key error message format.

Copilot uses AI. Check for mistakes.
userPub: string;
backupPub: string;
apiKey: string;
coinSpecificParams: any;
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using 'any' type for coinSpecificParams reduces type safety. Consider defining a proper interface or union type for coin-specific parameters.

Suggested change
coinSpecificParams: any;
coinSpecificParams: CoinSpecificParams;

Copilot uses AI. Check for mistakes.
coinFamily: CoinFamily,
signableHex: string,
accountId: string,
): Promise<{ txBuilder: any; publicKey: string }> {
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using 'any' type for txBuilder reduces type safety. Consider defining a proper interface or union type for transaction builders.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@pranavjain97 pranavjain97 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Some comments

Comment on lines 327 to 335
'v1.mpc.recovery': {
post: httpRoute({
method: 'POST',
path: `/api/{coin}/mpc/recovery`,
request: httpRequest({
params: {
coin: t.string,
},
body: RecoveryEdDSARequest,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the body specific to EDDSA?

}),
};

const RecoveryEdDSARequest = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woudn't it be similar for ECDSA?

const { recoveryDestination, userKey } = commonRecoveryParams;
try {
const unsignedSweepPrebuildTx = await recoverEddsaWallets(bitgo, sdkCoin, {
bitgoKey: userKey,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change userKey to call it commonKeychain so it isnt misleading

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol that's how they decided to name the field on the sdk. Would just mean more transforming of the parameters. I can do it in a follow up.

Comment on lines +203 to +216
if ('txRequests' in tx && Array.isArray(tx.txRequests)) {
// MPCTxs format
const firstRequest = tx.txRequests[0];
if (firstRequest && firstRequest.transactions && firstRequest.transactions[0]) {
const firstTx = firstRequest.transactions[0];
txRequest.signableHex = firstTx.unsignedTx?.serializedTx || '';
txRequest.derivationPath = firstTx.unsignedTx?.derivationPath || '';
}
} else if ('signableHex' in tx) {
// MPCTx format
txRequest.signableHex = tx.signableHex || '';
txRequest.derivationPath = tx.derivationPath || '';
} else if (Array.isArray(tx) && tx.length > 0) {
// MPCSweepTxs format
const firstTx = tx[0];
if (firstTx && firstTx.transactions && firstTx.transactions[0]) {
const transaction = firstTx.transactions[0];
txRequest.signableHex = transaction.unsignedTx?.signableHex || '';
txRequest.derivationPath = transaction.unsignedTx?.derivationPath || '';
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add comments in which scenario are which formats used? I'm surprised it differs so much just between MPC.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this to a helper. Like prepareUnsignedSweepTxRequest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client is already too big; we need to start modularizing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can do it in a follow up.

txRequest.signableHex = tx.signableHex || '';
txRequest.derivationPath = tx.derivationPath || '';
} else if (Array.isArray(tx) && tx.length > 0) {
// MPCSweepTxs format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arent the above two sweeps as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are, just different transaction formats that are returned by recovery

const hdTree = await Ed25519Bip32HdTree.initialize();
const MPC = await Eddsa.initialize(hdTree);

const accountId = MPC.deriveUnhardened(request.commonKeychain, request.derivationPath).slice(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this accountId?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accountId,
);

const txBuilder = builder;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prolly not needed

@mohammadalfaiyazbitgo mohammadalfaiyazbitgo force-pushed the WP-5166-eddsa-recovery-v2 branch 2 times, most recently from ac800dc to ff6ecf8 Compare July 11, 2025 19:50
@pranavjain97 pranavjain97 merged commit c1cfe09 into master Jul 14, 2025
3 checks passed
@pranavjain97 pranavjain97 deleted the WP-5166-eddsa-recovery-v2 branch July 14, 2025 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants