Skip to content

Conversation

@dpkjnr
Copy link
Contributor

@dpkjnr dpkjnr commented Oct 15, 2025

Fixes Express API gap where re-signing partially signed Full TxRequests would fail. The /signtxtss route now automatically detects and cleans up partial signature shares before attempting to sign.

Changes:

  • Add ensureCleanSigSharesAndSignTransaction() to Wallet class
  • Update Express handleV2SignTSSWalletTx() to use new method
  • Add comprehensive unit tests for signature cleanup logic
  • Check signature shares presence instead of transaction state

Benefits:

  • Fixes re-signing failures for partially signed transactions
  • Backward compatible with existing signing flows
  • No API changes required
  • Keeps tssUtils properly encapsulated

Ticket: COIN-5989

@dpkjnr dpkjnr marked this pull request as ready for review October 16, 2025 08:05
@dpkjnr dpkjnr requested review from a team as code owners October 16, 2025 08:05
nvjsr
nvjsr previously approved these changes Oct 16, 2025
@nvjsr
Copy link
Contributor

nvjsr commented Oct 16, 2025

@claude review

@dpkjnr dpkjnr requested a review from Copilot October 16, 2025 08:38
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 fixes an API gap in the Express TSS signing endpoint where re-signing partially signed Full TxRequests would fail due to stale signature shares. The solution adds automatic signature cleanup before signing operations.

  • Adds signature share cleanup logic to detect and clean partial signatures before signing
  • Updates Express TSS signing endpoint to use the new cleanup method
  • Provides comprehensive test coverage for the signature cleanup scenarios

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
modules/sdk-core/src/bitgo/wallet/wallet.ts Adds ensureCleanSigSharesAndSignTransaction method with signature cleanup logic
modules/express/src/clientRoutes.ts Updates Express TSS signing to use new cleanup method
modules/bitgo/test/v2/unit/wallet.ts Adds comprehensive unit tests for signature cleanup functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 3594 to 3599
isPartiallySigned = txRequest.transactions.some((tx) => (tx.signatureShares ?? []).length > 0);
} else if (txRequest.messages && txRequest.messages.length > 0) {
isPartiallySigned = txRequest.messages.some((msg) => (msg.signatureShares ?? []).length > 0);
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

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

The partial signature detection logic is duplicated between transactions and messages. Consider extracting this into a helper function to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

+1,

can do something like

const signabeResources: { signatureShares: [] } = txRequest.transactions?.length ?  txRequest.transactions : txRequest.messages ?? [];

kamleshmugdiya
kamleshmugdiya previously approved these changes Oct 16, 2025
): Promise<SignedTransaction | TxRequest> {
const txRequestId = params.txRequestId || params.txPrebuild?.txRequestId;

if (txRequestId && this.tssUtils && this.multisigType() === 'tss') {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we also separate the cleaning part in different method. It think it will make the method ensureCleanSigSharesAndSignTransaction more readable.

@dpkjnr dpkjnr dismissed stale reviews from kamleshmugdiya and nvjsr via 43e90f9 October 17, 2025 07:40
@dpkjnr dpkjnr force-pushed the delete-sigshare-on-signTss branch 2 times, most recently from b8a43f0 to ae9c08f Compare October 17, 2025 10:42
@dpkjnr dpkjnr marked this pull request as draft October 17, 2025 11:08
@dpkjnr dpkjnr force-pushed the delete-sigshare-on-signTss branch from ae9c08f to f8fcd9e Compare October 22, 2025 05:12
@dpkjnr dpkjnr marked this pull request as ready for review October 22, 2025 07:34
let isPartiallySigned = false;

if (txRequest.transactions && txRequest.transactions.length > 0) {
isPartiallySigned = txRequest.transactions.some((tx) => (tx.signatureShares ?? []).length > 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we not considering any transaction state for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I saw something similar in wallet-platform also, would there be any case where sigShares are there but state is postSign or something where we should not delete sig shares?

Copy link
Contributor

Choose a reason for hiding this comment

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

The state could be readyToCombineShares which means it has been fully signed, but has not been broadcasted yet (the end state of v2.wallet.txrequest.sign is readyToCombineShares, and this is validated in the send api)

@dpkjnr dpkjnr force-pushed the delete-sigshare-on-signTss branch from f8fcd9e to 71b889c Compare October 22, 2025 14:58
@dpkjnr dpkjnr requested a review from lokesh-bitgo October 22, 2025 16:33
Comment on lines 3602 to 3604
if (isPartiallySigned) {
await this.tssUtils.deleteSignatureShares(txRequestId);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we always just rebuild/delete sig shares before signing? Or are we avoiding this for latency reasons?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that looks cleaner but we would make additional delete sigshares API call even in case of fresh transaction. If it looks fine to your team then I can make it default for txRequest full

let isPartiallySigned = false;

if (txRequest.transactions && txRequest.transactions.length > 0) {
isPartiallySigned = txRequest.transactions.some((tx) => (tx.signatureShares ?? []).length > 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

The state could be readyToCombineShares which means it has been fully signed, but has not been broadcasted yet (the end state of v2.wallet.txrequest.sign is readyToCombineShares, and this is validated in the send api)

Comment on lines 3594 to 3599
isPartiallySigned = txRequest.transactions.some((tx) => (tx.signatureShares ?? []).length > 0);
} else if (txRequest.messages && txRequest.messages.length > 0) {
isPartiallySigned = txRequest.messages.some((msg) => (msg.signatureShares ?? []).length > 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

+1,

can do something like

const signabeResources: { signatureShares: [] } = txRequest.transactions?.length ?  txRequest.transactions : txRequest.messages ?? [];

@dpkjnr dpkjnr requested a review from zahin-mohammad October 27, 2025 05:15
Fixes Express API gap where re-signing partially signed Full TxRequests
would fail. The /signtxtss route now always cleans up signature shares
for txRequest full before attempting to sign.

Changes:
- Add ensureCleanSigSharesAndSignTransaction() to Wallet class
- Update Express handleV2SignTSSWalletTx() to use new method
- Add comprehensive unit tests for signature cleanup logic

Ticket: COIN-5989
@dpkjnr dpkjnr force-pushed the delete-sigshare-on-signTss branch from b17eb7d to c222e83 Compare October 27, 2025 11:25
const txRequestId = params.txRequestId || params.txPrebuild?.txRequestId;

if (txRequestId && this.tssUtils && this.multisigType() === 'tss') {
const txRequest = await this.tssUtils.getTxRequest(txRequestId);
Copy link
Contributor

@kaustubhbitgo kaustubhbitgo Oct 28, 2025

Choose a reason for hiding this comment

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

Would it make sense to move this whole logic to backend via a params.rebuild flag in signTransaction call? This way we can abstract the logic for multiple wallet types (future requirement) and control the implementation details.

@zahin-mohammad zahin-mohammad removed their request for review October 28, 2025 14:55
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.

8 participants