nem catapult バイトレベルで理解する その１ トランザクションハッシュ

# Refference

このへん

https://github.com/nemtech/nem2-library-js/blob/master/src/transactions/VerifiableTransaction.js

# 前置き

ちょっとマニアックな話になっていきます。NEMとブロックチェーンの理解を深めるために頑張ろう。

NEMのトランザクションハッシュってどうやって求めているのでしょうか。トランザクション全体のハッシュ値なんでしょうか。

# 環境

Alpaca版

# 調べる

```javascript:VerifiableTransaction.js
/**
 * @param {string} transactionPayload HexString Payload
 * @returns {*|string} Returns Transaction Payload hash
 */
static createTransactionHash(transactionPayload) {
	const byteBuffer = Array.from(convert.hexToUint8(transactionPayload));
	const signingBytes = byteBuffer
		.slice(4, 36)
		.concat(byteBuffer
			.slice(4 + 64, 4 + 64 + 32))
		.concat(byteBuffer
			.splice(4 + 64 + 32, byteBuffer.length));

	const hash = new Uint8Array(32);

	sha3Hasher.func(hash, signingBytes, 32);

	return convert.uint8ToHex(hash);
}
```

### 変数
このコードは、

- `transactionPayload`がトランザクションのデータ。
- `byteBuffer`がそれを配列に変換したもの。
- `signingBytes`がハッシュ計算対象のデータ。

それを32byteのハッシュ値にしているように見えます。

### データ
そして、ハッシュ計算対象のデータは、

- 4byte目の直後から36byte目まで（32byte分）取り出す
- 4+64byte目の直後から4+64+32byte目まで（32byte分）取り出す
- 4+64+32byte目の直後からあと全部

これを結合したものになります。

`splice`は、配列の要素を削除するメソッドですが、削除は呼び出し元の配列に作用し、戻り値は削除された要素です。なので、`signingBytes`には削除された分が追加される認識です。あってる？

### 意味
これらは、意味的には

- 署名の先頭32byte
- 署名者の公開鍵
- 残りのデータ（サイズと署名と公開鍵以外）

になります。

# やってみる

## トランザクション作成

それでは、実際にTransferTransactionを作成し、そのハッシュ値とトランザクションデータ（payload）を表示してみます。

```javascript:transfer.js
const nem2Sdk = require("nem2-sdk");

const Address = nem2Sdk.Address,
    Deadline = nem2Sdk.Deadline,
    Account = nem2Sdk.Account,
    UInt64 = nem2Sdk.UInt64,
    NetworkType = nem2Sdk.NetworkType,
    PlainMessage = nem2Sdk.PlainMessage,
    TransferTransaction = nem2Sdk.TransferTransaction,
    Mosaic = nem2Sdk.Mosaic,
    MosaicId = nem2Sdk.MosaicId;

const recipientAddress = '<your recipent address>';

const transferTransaction = TransferTransaction.create(
    Deadline.create(),
    Address.createFromRawAddress(recipientAddress),
    [new Mosaic(new MosaicId('nem:xem'), UInt64.fromUint(10000000))],
    PlainMessage.create(''),
    NetworkType.MIJIN_TEST,
);

transferTransaction.fee = UInt64.fromUint(10000000);

const privateKey = '<your private key>';

const account = Account.createFromPrivateKey(privateKey,NetworkType.MIJIN_TEST);

const signedTransaction = account.sign(transferTransaction);

console.log('HASH:    ' + signedTransaction.hash);
console.log('SIGNER:  ' + signedTransaction.signer);
console.log('payload: ' + signedTransaction.payload);
```

```
$ node transfer.js
HASH:    3178B301B2753BC4158C2E889EC878CF51B59859617F466C04D67AD45B0B3766
SIGNER:  A9DF85FAF72B1214146D0A8FD4CD4DD03C270EA6B395CDF0CDE2D4729AB6FF32
payload: A5000000EC8E88E9D1EAFAD916A72B0FC920D7732E83BF937C1A00B1506663580E8AEAC0AD1D46939FA12DF1DA7337914E82D6444B4C224AEA5FDC36B9D91BCA6C05840EA9DF85FAF72B1214146D0A8FD4CD4DD03C270EA6B395CDF0CDE2D4729AB6FF320390544180969800000000002E651E4C1000000090758EB47C28D6143BAA3DE6A8D9C319B503A1BFD8E789E9E20100010029CF5FD941AD25D58096980000000000
```

### hash

正解のハッシュはこのようになります。

```
3178B301B2753BC4158C2E889EC878CF51B59859617F466C04D67AD45B0B3766
```

### payload

payloadを4ケタずつスペース区切りしてみた。このデータから上記のハッシュを求めていきたいと思います。

A500 0000 EC8E 88E9 D1EA FAD9 16A7 2B0F C920 D773 2E83 BF93 7C1A 00B1 5066 6358 0E8A EAC0 AD1D 4693 9FA1 2DF1 DA73 3791 4E82 D644 4B4C 224A EA5F DC36 B9D9 1BCA 6C05 840E A9DF 85FA F72B 1214 146D 0A8F D4CD 4DD0 3C27 0EA6 B395 CDF0 CDE2 D472 9AB6 FF32 0390 5441 8096 9800 0000 0000 2E65 1E4C 1000 0000 9075 8EB4 7C28 D614 3BAA 3DE6 A8D9 C319 B503 A1BF D8E7 89E9 E201 0001 0029 CF5F D941 AD25 D580 9698 0000 0000 00

## ハッシュ対象のデータを抽出する

それでは、payloadから対象のデータを抜き出していきます。

##### 4byte目の直後から36byte目まで（32byte分）取り出す

EC8E 88E9 D1EA FAD9 16A7 2B0F C920 D773 2E83 BF93 7C1A 00B1 5066 6358 0E8A EAC0

##### 4+64byte目の直後から4+64+32byte目まで（32byte分）取り出す

A9DF 85FA F72B 1214 146D 0A8F D4CD 4DD0 3C27 0EA6 B395 CDF0 CDE2 D472 9AB6 FF32

##### 4+64+32byte目の直後からあと全部

0390 5441 8096 9800 0000 0000 2E65 1E4C 1000 0000 9075 8EB4 7C28 D614 3BAA 3DE6 A8D9 C319 B503 A1BF D8E7 89E9 E201 0001 0029 CF5F D941 AD25 D580 9698 0000 0000 00

## ハッシュ計算

js-sha3を使います。そして、ハッシュ値は32バイトらしいので、sha3_256を使います。

nem2-sdkをインストールしていれば、js-sha3もインストールされているはずなので、問題なく動くと思います。

```javascript:sha3.js
const jssha3 = require('js-sha3');

const sha3_256 = jssha3.sha3_256;

const payload = process.argv[2];
const hasher = sha3_256.create();
const hash = hasher.update(Buffer.from(payload, 'hex')).hex().toUpperCase();

console.log('hash :' + hash);
```

```
$ node sha3.js EC8E88E9D1EAFAD916A72B0FC920D7732E83BF937C1A00B1506663580E8AEAC0A9DF85FAF72B1214146D0A8FD4CD4DD03C270EA6B395CDF0CDE2D4729AB6FF320390544180969800000000002E651E4C1000000090758EB47C28D6143BAA3DE6A8D9C319B503A1BFD8E789E9E20100010029CF5FD941AD25D58096980000000000
hash :3178B301B2753BC4158C2E889EC878CF51B59859617F466C04D67AD45B0B3766
```

正解です！おめでとう！


# 関連

nem catapult バイトレベルで理解する その１ トランザクションハッシュ
https://qiita.com/planethouki/items/d7a7fc7adf8c8bc48668

nem catapult バイトレベルで理解する その２ TransferTransaction
https://qiita.com/planethouki/items/025d44f2ebe6cfcb4b1f

nem catapult バイトレベルで理解する その３ トランザクション署名
https://qiita.com/planethouki/items/932d96ee17bd4f305368

nem catapult バイトレベルで理解する その４ Cosignature Transaction
https://qiita.com/planethouki/items/fcac3c9d329b0278c4b7

nem catapult バイトレベルで理解する その５ SecretLock/Proof Transaction
https://qiita.com/planethouki/items/4c91c2e6c722283c50f1

nem catapult バイトレベルで理解する その６ 空ブロック
https://qiita.com/planethouki/items/da717595a1ff58d0d84f