@@ -33,6 +33,13 @@ import { TransactionType } from './TransactionType';
3333 */
3434export abstract class Transaction {
3535
36+ /**
37+ * Transaction header size
38+ *
39+ * @var {number}
40+ */
41+ public static readonly Header_Size = 8 + 64 + 32 + 4 ;
42+
3643 /**
3744 * @constructor
3845 * @param type
@@ -81,29 +88,54 @@ export abstract class Transaction {
8188
8289 /**
8390 * Generate transaction hash hex
91+ *
92+ * @see https://github.com/nemtech/catapult-server/blob/master/src/catapult/model/EntityHasher.cpp#L32
93+ * @see https://github.com/nemtech/catapult-server/blob/master/src/catapult/model/EntityHasher.cpp#L35
94+ * @see https://github.com/nemtech/catapult-server/blob/master/sdk/src/extensions/TransactionExtensions.cpp#L46
8495 * @param {string } transactionPayload HexString Payload
8596 * @param {Array<number> } generationHashBuffer Network generation hash byte
8697 * @param {NetworkType } networkType Catapult network identifier
8798 * @returns {string } Returns Transaction Payload hash
8899 */
89100 public static createTransactionHash ( transactionPayload : string , generationHashBuffer : number [ ] , networkType : NetworkType ) : string {
90- const type = parseInt ( Convert . uint8ToHex ( Convert . hexToUint8 ( transactionPayload . substring ( 220 , 224 ) ) . reverse ( ) ) , 16 ) ;
91- const byteBuffer = Array . from ( Convert . hexToUint8 ( transactionPayload ) ) ;
92- const byteBufferWithoutHeader = byteBuffer . slice ( 4 + 64 + 32 + 8 ) ;
93- const dataBytes = type === TransactionType . AGGREGATE_BONDED || type === TransactionType . AGGREGATE_COMPLETE ?
94- generationHashBuffer . concat ( byteBufferWithoutHeader . slice ( 0 , 52 ) ) :
95- generationHashBuffer . concat ( byteBufferWithoutHeader ) ;
96- const signingBytes = byteBuffer
97- . slice ( 8 , 40 ) // first half of signature
98- . concat ( byteBuffer
99- . slice ( 4 + 4 + 64 , 8 + 64 + 32 ) ) // signer
100- . concat ( dataBytes ) ;
101101
102- const hash = new Uint8Array ( 32 ) ;
103- const signSchema = SHA3Hasher . resolveSignSchema ( networkType ) ;
104- SHA3Hasher . func ( hash , signingBytes , 32 , signSchema ) ;
102+ // prepare
103+ const entityHash : Uint8Array = new Uint8Array ( 32 ) ;
104+ const signSchema : SignSchema = SHA3Hasher . resolveSignSchema ( networkType ) ;
105+ const transactionBytes : Uint8Array = Convert . hexToUint8 ( transactionPayload ) ;
106+
107+ // 1) take "R" part of a signature (first 32 bytes)
108+ const signatureR : Uint8Array = transactionBytes . slice ( 8 , 8 + 32 ) ;
109+
110+ // 2) add public key to match sign/verify behavior (32 bytes)
111+ const pubKeyIdx : number = signatureR . length ;
112+ const publicKey : Uint8Array = transactionBytes . slice ( 8 + 64 , 8 + 64 + 32 ) ;
113+
114+ // 3) add generationHash (32 bytes)
115+ const generationHashIdx : number = pubKeyIdx + publicKey . length ;
116+ const generationHash : Uint8Array = Uint8Array . from ( generationHashBuffer ) ;
117+
118+ // 4) add transaction data without header (EntityDataBuffer)
119+ // @link https://github.com/nemtech/catapult-server/blob/master/src/catapult/model/EntityHasher.cpp#L30
120+ const transactionBodyIdx : number = generationHashIdx + generationHash . length ;
121+ const transactionBody : Uint8Array = transactionBytes . slice ( Transaction . Header_Size ) ;
122+
123+ // 5) concatenate binary hash parts
124+ // layout: `signature_R || signerPublicKey || generationHash || EntityDataBuffer`
125+ const entityHashBytes : Uint8Array = new Uint8Array (
126+ signatureR . length
127+ + publicKey . length
128+ + generationHash . length
129+ + transactionBody . length ,
130+ ) ;
131+ entityHashBytes . set ( signatureR , 0 ) ;
132+ entityHashBytes . set ( publicKey , pubKeyIdx ) ;
133+ entityHashBytes . set ( generationHash , generationHashIdx ) ;
134+ entityHashBytes . set ( transactionBody , transactionBodyIdx ) ;
105135
106- return Convert . uint8ToHex ( hash ) ;
136+ // 6) create SHA3 or Keccak hash depending on `signSchema`
137+ SHA3Hasher . func ( entityHash , entityHashBytes , 32 , signSchema ) ;
138+ return Convert . uint8ToHex ( entityHash ) ;
107139 }
108140
109141 /**
0 commit comments