feat(sdk-core): Add support for preHashed flr atomic txn in EcdsaMPCv…#8814
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support in sdk-core ECDSA MPCv2 DKLS signing to correctly handle FLR Avalanche atomic (C-chain ↔ P-chain) transactions where signableHex is already a final 32-byte SHA-256 digest, preventing the SDK from incorrectly applying keccak256 and causing signature-share combination failures.
Changes:
- Detect Avalanche atomic transactions via
serializedTxHexcodec prefix (0000) and treatsignableHexas a pre-hashed digest (skip coin hash function). - Add an end-to-end DKLS offline signing unit test for a real Avalanche atomic export transaction using
shouldHash=false. - Add a regression unit test intended to ensure regular FLR EVM transactions still use keccak256.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | Adds Avalanche-atomic detection and bypasses keccak256 by using signableHex directly as the DKLS message hash. |
| modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | Adds a full DKLS signing test for pre-hashed Avalanche atomic txs and a regression test for the EVM keccak path. |
Comments suppressed due to low confidence (1)
modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts:1036
- The comment says “standard EVM RLP starts with 0xf8xx”, but EVM transactions can also be EIP-2718 typed txs whose serialized form starts with a type byte (e.g.
01,02,03) followed by RLP. Consider updating the comment to avoid misleading readers; the non-collision argument can still hold, but should account for typed tx prefixes as well.
// This matches the WP/HSM BitGo-party behaviour (MPCv2Signer.isPreHashed)
// so both DKLS parties agree on the same message hash.
// Detection: Avalanche codec type ID prefix is 0x0000; standard EVM RLP
// starts with 0xf8xx, so there is no collision.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
prithvishet2503
approved these changes
May 20, 2026
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
signableHexfor all transactions, but Avalanche atomic txs havesignableHex = SHA-256(txBody)— already a final signing hash. The WP/HSM BitGo-party uses thisSHA-256 directly (
isPreHashed=true), so the two DKLS parties disagreed on the message hash, causing "Failed to combine signature shares"0x0000vs EVM RLP0xf8xx) and skip keccak256, usingsignableHexas the DKLS message hash directlyProblem
FLR cross-chain atomic exports (C→P) use Avalanche's codec format, not EVM RLP. The
signableHexreturned bybuildTransactionWithIntentisSHA-256(txBody)— a 32-byte pre-hashed digest ready for ECDSAsigning.
EcdsaMPCv2Utils.getHashStringAndDerivationPath()unconditionally applied keccak256 tosignableHex, producing:keccak256(SHA-256(txBody))— wrongSHA-256(txBody)— correct (viaMPCv2Signer.isPreHashed)The two DKLS parties initialized their DSG sessions with different message hashes, making it impossible to combine partial signatures into a valid ECDSA signature.
Fix
In
getHashStringAndDerivationPath(), extractserializedTxHexfrom thetxRequestand check if it starts with0000(Avalanche codec type ID). If so, returnBuffer.from(signableHex, 'hex')directly ashashBuffer, bypassing keccak256. This matches the WP/HSM behavior so both DKLS parties agree on the same 32-byte message hash.There is no collision risk: Avalanche codec type ID
0x0000cannot appear as a valid EVM RLP prefix (RLP-encoded transactions start with0xf8xxor higher).Changes
modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.tsserializedTxHexfromtxRequest.transactions[0].unsignedTxserializedTxHex.startsWith('0000')to usesignableHexas raw DKLS hashmodules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts0x0000prefix). BitGo party uses raw SHA-256 hash (matching WPisPreHashed=true). Verifies withshouldHash=false.Asserts
recid:R:S:pubkeyformat, 32-byte R/S components, and valid recovery ID.0xf86cRLP prefix) still applies keccak256. BitGo party useskeccak256(signableHex). Ensures standard EVM signing is unaffected.Test plan
c2pMpcToMpcTss.ts)Ticket: CECHO-1138