@@ -49,7 +49,9 @@ import {
4949 TssTxRecipientSource ,
5050 TxRequest ,
5151 isV2Envelope ,
52+ SignableTransaction ,
5253} from '../baseTypes' ;
54+ import { shouldUsePreHashedSignable } from '../preHashedSignable' ;
5355import { BaseEcdsaUtils } from './base' ;
5456import { EcdsaMPCv2KeyGenSendFn , KeyGenSenderForEnterprise } from './ecdsaMPCv2KeyGenSender' ;
5557import { envRequiresBitgoPubGpgKeyConfig , isBitgoMpcPubKey } from '../../../tss/bitgoPubKeys' ;
@@ -837,8 +839,8 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
837839 // serializedTxHex is the PVM/EVM atomic tx (codec prefix 0x0000).
838840 // For all other coins, signableHex IS the unsigned transaction (e.g. RLP bytes).
839841 const isIcp = this . baseCoin . getConfig ( ) . family === 'icp' ;
840- const isAvalancheAtomic = unsignedTx . serializedTxHex && unsignedTx . serializedTxHex . startsWith ( '0000' ) ;
841- if ( isIcp || isAvalancheAtomic ) {
842+ const isPreHashed = shouldUsePreHashedSignable ( this . baseCoin , unsignedTx ) ;
843+ if ( isIcp || isPreHashed ) {
842844 await this . baseCoin . verifyTransaction ( {
843845 txPrebuild : { txHex : unsignedTx . serializedTxHex , txInfo : unsignedTx . signableHex } ,
844846 txParams : params . txParams || { recipients : [ ] } ,
@@ -870,19 +872,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
870872 // pre-hashed digest. Use it directly as the DKLS message hash instead of
871873 // applying the coin's hash function (keccak256 for EVM coins).
872874 // Same logic as getHashStringAndDerivationPath (external signer path).
873- let hashBuffer : Buffer ;
874- if ( serializedTxHex && serializedTxHex . startsWith ( '0000' ) ) {
875- hashBuffer = bufferContent ;
876- assert ( hashBuffer . length === 32 , `Avalanche pre-hashed signableHex must be 32 bytes, got ${ hashBuffer . length } ` ) ;
877- } else {
878- let hash : Hash ;
879- try {
880- hash = this . baseCoin . getHashFunction ( ) ;
881- } catch ( err ) {
882- hash = createKeccakHash ( 'keccak256' ) as Hash ;
883- }
884- hashBuffer = hash . update ( bufferContent ) . digest ( ) ;
885- }
875+ const hashBuffer = this . getMpcv2HashBuffer ( serializedTxHex , txOrMessageToSign , bufferContent ) ;
886876 const otherSigner = new DklsDsg . Dsg (
887877 userKeyShare ,
888878 params . mpcv2PartyId !== undefined ? params . mpcv2PartyId : 0 ,
@@ -1059,18 +1049,27 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
10591049 throw new Error ( 'Invalid request type, got: ' + requestType ) ;
10601050 }
10611051
1062- // For Avalanche atomic transactions (cross-chain export/import between
1063- // C-chain and P-chain), signableHex is already SHA-256(txBody) — a 32-byte
1064- // pre-hashed digest. Use it directly as the DKLS message hash instead of
1065- // applying the coin's hash function (keccak256 for EVM coins).
1066- // This matches the WP/HSM BitGo-party behaviour (MPCv2Signer.isPreHashed)
1067- // so both DKLS parties agree on the same message hash.
1068- // Detection: Avalanche codec type ID prefix is 0x0000; standard EVM RLP
1069- // starts with 0xf8xx, so there is no collision.
1070- if ( serializedTxHex && serializedTxHex . startsWith ( '0000' ) ) {
1071- const hashBuffer = Buffer . from ( txToSign , 'hex' ) ;
1072- assert ( hashBuffer . length === 32 , `Avalanche pre-hashed signableHex must be 32 bytes, got ${ hashBuffer . length } ` ) ;
1073- return { hashBuffer, derivationPath } ;
1052+ const hashBuffer = this . getMpcv2HashBuffer ( serializedTxHex , txToSign , Buffer . from ( txToSign , 'hex' ) ) ;
1053+ return { hashBuffer, derivationPath } ;
1054+ }
1055+
1056+ /**
1057+ * Build the DKLS message hash for MPCv2 signing.
1058+ * For pre-hashed signable material (Avalanche atomic txs), signableHex is
1059+ * already SHA-256(txBody) and is used directly. Otherwise the coin hash
1060+ * function is applied (keccak256 for EVM coins by default).
1061+ */
1062+ private getMpcv2HashBuffer ( serializedTxHex : string | undefined , signableHex : string , bufferContent : Buffer ) : Buffer {
1063+ const unsignedTx : SignableTransaction = {
1064+ serializedTxHex : serializedTxHex ?? '' ,
1065+ signableHex,
1066+ } ;
1067+ if ( serializedTxHex && shouldUsePreHashedSignable ( this . baseCoin , unsignedTx ) ) {
1068+ assert (
1069+ bufferContent . length === 32 ,
1070+ `Avalanche pre-hashed signableHex must be 32 bytes, got ${ bufferContent . length } `
1071+ ) ;
1072+ return bufferContent ;
10741073 }
10751074
10761075 let hash : Hash ;
@@ -1079,9 +1078,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
10791078 } catch ( err ) {
10801079 hash = createKeccakHash ( 'keccak256' ) as Hash ;
10811080 }
1082- const hashBuffer = hash . update ( Buffer . from ( txToSign , 'hex' ) ) . digest ( ) ;
1083-
1084- return { hashBuffer, derivationPath } ;
1081+ return hash . update ( bufferContent ) . digest ( ) ;
10851082 }
10861083
10871084 // #endregion
0 commit comments