fix(sdk-coin-avaxp): prevent credential wipe and expose incomplete signing state#9287
Draft
nvrakesh06 wants to merge 1 commit into
Draft
fix(sdk-coin-avaxp): prevent credential wipe and expose incomplete signing state#9287nvrakesh06 wants to merge 1 commit into
nvrakesh06 wants to merge 1 commit into
Conversation
…gning state hasCredentials was checking credentials.length > 0, which returns false for credentials=[] (empty array). This allowed buildAvaxTransaction() to regenerate fresh placeholder credentials during HSM signing, wiping any partial signature already placed by a prior signer. The regenerated credentials left the second signature slot as an address placeholder (r=0), causing AvalancheGo to reject the tx with "failed verifySpend: invalid signature". Three fixes: - hasCredentials now checks != null only, so credentials=[] blocks credential regeneration rather than triggering it - sign() adds an explicit length guard so empty credentials still throw - signature getter now collects real ECDSAs across all credentials instead of only credentials[0], making incompletely-signed txs visible before broadcast Relates to CECHO-1697. Production failure on ExportTx P->C (custodial 2-of-3, tx 8xbiLpsKDKkJrN3YUqcZpFcxApLCwmQkpKUuvmHU1GL9RmAyu, 2026-07-16). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hasCredentialsnow checks!= nullonly (was!== undefined && length > 0).credentials=[]previously bypassed the guard inbuildAvaxTransaction(), causing it to regenerate fresh placeholder credentials and wipe any signature already placed by a prior signer.sign()adds an explicitlength === 0guard to compensate — empty credentials still throw an appropriate error.signaturegetter now collects real ECDSAs across all credentials instead of onlycredentials[0], making an incompletely-signed tx detectable viatoJson().signaturesbefore it reaches the sendq.Background
Production incident CECHO-1697 (2026-07-16): a custodial 2-of-3 AVAXP ExportTx P→C was broadcast with sig[1] still containing an address placeholder (r=0) in every credential, causing AvalancheGo to reject with:
Tx:
8xbiLpsKDKkJrN3YUqcZpFcxApLCwmQkpKUuvmHU1GL9RmAyuRoot cause: if
credentials=[]arises during BitGo Signer 2's deserialization of the half-signed tx,hasCredentialsreturnedfalse,buildAvaxTransaction()regenerated fresh placeholders (wiping Signer 1's ECDSA), and Signer 2 signed into the wrong slot — leaving slot[1] as address placeholder with r=0.A retry the next day with a different signer succeeded on the same unsigned tx, confirming the bug is in the credential guard path, not the UTXOs or policy.
Proven by
test/diag/diag3-guard-bypass.ts(before fix: credentials regenerated; after fix: credentials preserved).Test plan
test/diag/diag3-guard-bypass.ts— confirmhasCredentials=trueforcredentials=[]after fixtest/diag/diag4-reproduce-invalid-signature.ts— confirm Phase 3 (correct path) still produces 2 valid ECDSAssdk-coin-avaxpunit testsTODO(BG-56700)intest/unit/lib/exportC2PTxBuilder.ts🤖 Generated with Claude Code