Catapultにクリスマスをアグリゲートトランザクションで詰め込む!

この記事は [nem Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nem "nem Advent Calendar 2019") :calendar_spiral:  の記事です。
前日は @hhatto さんの 「[NEM2 SDK for Rustを作り始めました](https://qiita.com/hhatto/items/9560fb156a883d063e01)」でした。

# はじめに

アドベントと言えば真っ先に龍騎[^1]が思い浮かぶのは私だけ？最強の不死鳥[^2]も出てくるしね。
2019年NEMアドベント、不死鳥ファイナルベント！

ひょんな事[^3]からアドベントカレンダー参加することになりました。しかもトリ:bird:なんて。。。
普段はリング設営なる裏方仕事が多いので、お手柔らかにお願いいたします。:bow_tone1:

## テーマ：Catapultにクリスマスを詰め込みたい!
クリスマスにちなんだ何かを、Catapultを使ってやってみようと思案すること数日。
**メッセージ**と**アグリゲートトランザクション**を使って、 **クリスマスカード画像データそのもの**:christmas_tree: をブロックチェーンへ保存してみることに。

ファイルハッシュ値じゃなく、ファイルのデータそのものを。

# NEM特有の単語 簡単な説明

## メッセージについて
NEM/mijinブロックチェーンの転送トランザクションにはメッセージを付与できます。
改竄不可能データを永続保存することができる機能です。応用アイデア次第で様々な用途に使えます。

[アポスティーユ](https://nem.io/project/apostille/) [^4]は、メッセージ領域に__ファイルハッシュ値__を保存し、ブロックチェーンへタイムスタンプすることで実現していますね。

このメッセージには **1023byteまで** という制限があります。

<details><summary>**nemtechからの引用文 (クリックで表示)**</summary><div>

> パブリックネットワーク では、転送トランザクションは 1023 文字までの長さのメッセージを持つことができ、ブロックチェーンにデータを永続的にタイムスタンプするのに適しています。

>In the public network, transfer transactions can hold a message up to 1023 characters in length, making them suitable for timestamping data permanently on the blockchain.

引用: https://nemtech.github.io/ja/concepts/transfer-transaction.html#message

</div></details>


## アグリゲートトランザクションについて

Catapult新機能　**アグリゲートトランザクション**
最大1000トランザクションを**1つにまとめる**ことができます。

<details><summary>**nemtechからの引用文　(クリックで表示)**</summary><div>

> Catapult パブリックネットワーク は 1,000 までのインナートランザクションを内包し、 25 の連署者を参加させたアグリゲートトランザクションをサポートしています。

> Catapult’s public network supports aggregate transaction containing up to 1,000 inner transactions involving up to 25 different cosignatories. 

引用: https://nemtech.github.io/ja/concepts/aggregate-transaction.html

</div></details>


[nem Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nem "nem Advent Calendar 2019")にもアグリゲートトランザクションについての記事があります。
[NEMの十八番、アグリゲートトランザクションについて](https://qiita.com/Akazawa_Naoki/items/ca41a7876c5a7fbbd783)

# 最大メッセージデータ量が1000倍に？！

* 1転送トランザクションに格納できるメッセージは1023byte
* 最大1000トランザクションを1つにまとめることができる

と、いうことは？！

Catapultでは
`1023 * 1000 = 1023000 byte`
のメッセージデータを**1度にまとめて送信**できる
ということになりますね！:thumbsup:

1023000 byte
**約0.975MB**

このサイズまでの画像、PDF、音声データ等を1つのアグリゲートトランザクション内にまとめて格納可能とな！
やってみよう！

2018年のnemアドベントカレンダーで投稿された
[NEMでファイル共有システムを作ってみたらこうなった](https://qiita.com/scrpgil/items/07839aaebfe32ea777ed)
のCatapult アグリゲートトランザクション利用版イメージです。
アプリ画面は作ってないです。コマンド叩きのみです。すいません:bow: 

# 処理の流れ
## 1.データ送信
imageToAggregateTransaction.js
![data_to_aggregateTransaction__.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/7015847b-8824-3aa6-4136-ce845951fac0.png)

1. ファイルをBase64エンコードする
1. 1023byte毎に分割して、インナートランザクションメッセージに格納する（宛先アドレスは全て同一）
1. アグリゲートコンプリートトランザクションとして送信する

## 2.データ受信
imageFromTxHash.js
![data_from_TxHash.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/e17bec19-0559-f2de-6b03-fb102abda1ea.png)

1. トランザクションハッシュ値指定で、アグリゲートトランザクション内容を受け取る
1. インナートランザクション内のメッセージを全て結合する
1. Base64デコードしてファイルに保存

## Base64はおっきくなる
Base64エンコードすると約3割強データが太ります:pig:
(データ量が4/3になる)

Base64エンコードした結果が 1023000 byteまで。
Base64エンコードする前の元データとしては
767250 byte
**約0.731MB** [^5]
1アグリゲートトランザクションで送信できる、実ファイルサイズ上限になります。

## メッセージ暗号化について
メッセージを暗号化することもできますが、格納できるサイズが小さくなります。
今回のサンプルでは行いません。

# 開発環境

### クライアント
```
macOS 10.15.2
Node js v12.14.0
npm 6.13.4
```

### 使用ライブラリ

```
nem2-sdk@0.15.1 
image-to-base64@2.0.1
file-type@12.4.2
```

### Catapult ノード環境
Catapultパブリックチェーンがローンチ前です。mijin Catapultプライベートブロックチェーンを構築します。

```
Catapult Fushicho2
NetworkType.MIJIN

http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000
```

![mijin fushicho2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/67031dbe-6b3b-19a0-c080-2b7f734c06a2.png)


| node | 台数 | spec | vcpu | メモリ | SSD |
|:-:|:-:|:-:|:-:|:-:|:-:|
| api  | 1  | Standard F2s_v2  | 2  | 4 GiB  | 32 GiB |
| peer  | 3  |Standard F2s_v2   | 2  | 4 GiB | 32 GiB |

NEMアドカレ用 [mijin Catapult](https://mijin.io/) Fushicho2を立ててみました。2019年内は公開のままにする予定です。
簡単にCatapultプライベート・コンソーシアムブロックチェーンを構築できるプラットフォームのプレビュー版、ちょっとだけちらみせな感じで:sunglasses:

Azure上に4台の仮想マシン、2つのロードバランサーが立ち上がっています。
マシンスペックは低めにしていますが、今回の要件はトランザクション速度を必要としないので問題なし。
このあたりの構築について、**ワンチーム**:football: から別記事がでるかも？！（ム茶ぶり）:tea:

Tx手数料の設定　`minFeeMultiplier=0` で送金手数料無料チェーンとしています。


* catapult-service-bootstrapの場合

`ruby/catapult-templates/api_node/resources/config-node.properties.mt`
`ruby/catapult-templates/peer-node/resources/config-node.properties.mt`

```diff:config-node.properties.mt
# 23 行目
- minFeeMultiplier = 100
+ minFeeMultiplier = 0
```


# 前準備
## クリスマスカード作り:snowman: 

今回のアドカレで**一番時間を食った**のが、このクリスマスカード作り。
私はデザイン能力ゼロと言うかマイナスのため、普段は懇意にしているデザイナーさんに頼むんですが。
今回は自分で頑張りました。（頼めばヨカッタよ。。失敗した）
:christmas_tree:は梅田のグランフロントで撮りました。

インナートランザクション数がきっちり`1000`になるように画像byte数もちょいちょい調整しました。
もっとNEMっぽくしたかったんですがね。。。

###### CatapultChristmas2019.jpg
(766788 byte) 
![CatapultChristmas2019.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/2b856393-284d-9b4a-ca6e-931dd5208540.jpeg)
不死鳥イラストはイラストACを使っております。[Oyu](https://![nemgraph.net_ring_validation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/0aca92c1-6b16-ea3a-77e4-6d476f22744a.png)
![nemgraph.net_ring_validation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/46a7bb17-f4ca-a359-800e-7d23e5d39a9c.png)
www.ac-illust.com/main/profile.php?id=rcfTi1QV&amp;area=1)さんによる[イラストAC](https://www.ac-illust.com/)からのイラストです。

## git clone

githubにサンプルを置いておきます。cloneしてください。

```shell:clone
$ git clone https://github.com/falcon-man-stone-river/qiita-nem-advent-calendar-samples.git
$ cd qiita-nem-advent-calendar-samples
```

```shell:tree
$ tree -L 1
.
├── CatapultChristmas2019.jpg # ChristmasCardSample file
├── README.md
├── imageFromTxHash.js # Sample2
├── imageToAggregateTransaction.js # Sample1
├── package-lock.json
└── package.json
```

## ライブラリのインストール

```console
$ npm install
```

## ジェネレーションハッシュ値確認
`curl -s http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/block/1 | jq -r '.meta.generationHash'
`

```
$ curl -s http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/block/1 | jq -r '.meta.generationHash'
13C3F1DEFCE3ECFC9018E8A75261068C55F0315528B778D83320BFD7660497AA
```

## 環境変数設定

今回のサンプルは環境変数に必要データを入れて、process.envで処理します。
`NODE_URL` `GENERATION_HASH` `IMAGE_FILENAME`
3つの環境変数をセットしてください。

```shell:環境変数
$ export NODE_URL=http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000
$ export GENERATION_HASH=13C3F1DEFCE3ECFC9018E8A75261068C55F0315528B778D83320BFD7660497AA
$ export IMAGE_FILENAME=CatapultChristmas2019.jpg
```

環境変数 `IMAGE_FILENAME` に　サンプルクリスマスカード画像　`CatapultChristmas2019.jpg`を入れています。
他のファイルをアップしてみたい場合は、`IMAGE_FILENAME`を変更してみてください。

ご自身で建てられたCatapultノードに切り替える場合は
環境変数 `NODE_URL` `GENERATION_HASH` 
をそれぞれ変更してください。


# Sample1　
## 画像データをBase64配列化し アグリゲートトランザクション送信

<details><summary>imageToAggregateTransaction.js （クリックで表示）</summary><div>


```javascript:imageToAggregateTransaction.js
const { NODE_URL, GENERATION_HASH, IMAGE_FILENAME } = process.env;
const { AggregateTransaction, Account, TransactionHttp, TransferTransaction, Deadline, PlainMessage, NetworkCurrencyMosaic, NetworkType } = require('nem2-sdk');
const image2base64 = require('image-to-base64');

// アカウント新規作成
const account = Account.generateNewAccount(NetworkType.MIJIN);

// Base64配列作成
const splitBase64List = async (data, splitSize) => {
  const dataSpread = [...data];
  return new Promise((resolve, reject) => {
    resolve(dataSpread.reduce(
      (acc, c, i) => i % splitSize ? acc : [...acc, dataSpread.slice(i, i + splitSize).join('')], []
    ));
  });
};

// トランザクション作成
const createTransction = (address, message) => {
  return TransferTransaction.create(
    Deadline.create(),
    address,
    [NetworkCurrencyMosaic.createRelative(0)], // 0
    PlainMessage.create(message), // メッセージ
    NetworkType.MIJIN
  );
};

(async() => {
  try {
    const base64Str = await image2base64(IMAGE_FILENAME);
    console.log('image to base64:', IMAGE_FILENAME, base64Str.length);

    // 最大byte数 1023000を超える場合、エラーで終了
    if (base64Str.length > 1023000) {
      throw new Error('file size over');
    }

    // Base64文字列を1023byte分割
    const base64List = await splitBase64List(base64Str, 1023);
    console.log('base64List size:', base64List.length);

    // Base64配列数分、インナートランザクション作成
    const innerTransactions = base64List.map((r) => {
      return createTransction(account.address, r).toAggregate(account.publicAccount);
    });

    // アグリゲートコンプリート トランザクション作成
    const aggregateTransaction = AggregateTransaction.createComplete(
      Deadline.create(),
      innerTransactions, // Base64分割メッセージ格納 インナートランザクション
      NetworkType.MIJIN,
      []
    );

    // アグリゲートトランザクション署名
    const signedTransaction = account.sign(aggregateTransaction, GENERATION_HASH);
    console.log('- signedTransaction hash -');
    console.log(signedTransaction.hash);

    const transactionHttp = new TransactionHttp(NODE_URL);
    console.log('- sendTransaction -');

    // トランザクション送信
    transactionHttp
      .announce(signedTransaction)
      .subscribe(
        (x) => console.log(x.message),
        (err) => console.error(err)
      );
  } catch (e) {
    console.log('ERROR!', e);
    if (NODE_URL === void 0 || GENERATION_HASH === void 0 || IMAGE_FILENAME === void 0) {
      console.log('Please set the environment variable NODE_URL or GENERATION_HASH or IMAGE_FILENAME');
    }
  }
})();

```
</div></details>

参考:[【JavaScript】文字列を任意の文字数で分割し、配列を作成する方法](https://qiita.com/turmericN/items/0819317b5c075d971bfa)

## Sample1の実行
[3つの環境変数をセットしてから](https://qiita.com/Hishikawa/private/403f22af69891ee78870#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E8%A8%AD%E5%AE%9A)実行します。

実行　 `$ node imageToAggregateTransaction.js`

```bash:Sample1実行
$ node imageToAggregateTransaction.js
image to base64: CatapultChristmas2019.jpg 1022384
base64List size: 1000 # インナートランザクション数
- signedTransaction hash -
A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594
- sendTransaction -
packet 9 was pushed to the network via /transaction
```

* 元画像のバイト数
   `766788 byte`

* Base64エンコード後のバイト数
   `1022384 byte`

* インナートランザクション数(1023byte毎に分解）
   `1000`

* トランザクションハッシュ値
   `A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594`

注）ここで得られるトランザクションハッシュ値は、次のSample2で使用します。

トランザクションのステータスは様々な方法で取得できます。
今回は簡単なREST API（/transaction/{トランザクションハッシュ値}/status）を叩いてみます。 [^6]

`http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/transaction/A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594/status`


```bash:status確認
$ curl http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/transaction/A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594/status | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   155  100   155    0     0    729      0 --:--:-- --:--:-- --:--:--   731
{
  "group": "confirmed",
  "code": "Success",
  "hash": "A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594",
  "deadline": "117701505382",
  "height": "27542"
}
```

`"group": "confirmed"`
になっていればブロックチェーンに取り込まれております。

さて、ほんとにクリスマスカード画像データはブロックチェーンに取り込まれているのでしょうか？！ :thinking: 
確認してみましょう。 

REST API（/transaction/{トランザクションハッシュ値}）を叩いてみます。 [^7]

`http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/transaction/A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594`

```bash:transaction確認
$ curl http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/transaction/A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594 | jq
# ずらずらー
# 大量のJSONが流れます。
# (1023Byte毎に別れた1000個のBase64 hex dump messageを含んでます) 
```

![REST-API-TX-HASH3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/3d527cb0-ea48-8972-6d25-2a44aaf02f71.gif)

Sample2では、1000インナートランザクションメッセージを結合、Base64デコードして新規画像ファイル保存をします。

# Sample2
## トランザクションメッセージから画像データを取り出す

<details><summary>imageFromTxHash.js （クリックで表示）</summary><div>

```javascript:imageFromTxHash.js
const { NODE_URL, TX_HASH } = process.env;
const { TransactionHttp } = require('nem2-sdk');
const fs = require('fs').promises;
const fileType = require('file-type');

(async() => {
  try {
    console.log(NODE_URL);
    console.log(TX_HASH);

    // Transaction内容取得
    console.log('- getTransaction -');
    const resTx = await new TransactionHttp(NODE_URL).getTransaction(TX_HASH).toPromise();

    console.log('txTimestamp: ', new Date(parseInt(resTx.transactionInfo.id.substring(0, 8), 16) * 1000));
    console.log('isConfirmed: ', resTx.isConfirmed());

    // Confirmedチェック
    if (resTx.isConfirmed()) {
      // トランザクションサイズ
      console.log('Transaction size: ', resTx.size);
      const resTxJSON = resTx.toJSON();

      // インナートランザクション内のメッセージを取得
      const txMessages = resTxJSON.transaction.transactions.map((res) => res.transaction.message.payload);

      // decode
      const decode = new Buffer.from(txMessages.join(), 'base64');

      // file Type取得
      const fileExtension = fileType(decode).ext;

      // トランザクションハッシュ値＋fileTypeで保存
      const fileName = TX_HASH + '.' + fileExtension;

      console.log('fileName:', fileName);

      console.log('- writeFile -');
      const res = await fs.writeFile(fileName, decode);
    }
  } catch (e) {
    console.log('ERROR!', e);
    if (NODE_URL === void 0 || TX_HASH === void 0) {
      console.log('Please set the environment variable NODE_URL or TX_HASH');
    }
  }
})();

```
</div></details>

## Sample2の実行

まず今回Sample1で送信した際に取得したトランザクションハッシュ値
`A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594`
を環境変数 `TX_HASH` に指定して実行します。

```bash:環境変数設定
$ export TX_HASH=A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594
$ node imageFromTxHash.js
```

または

```bash:環境変数指定と実行
$ TX_HASH=A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594 node imageFromTxHash.js
```


```shell:Sample2実行
$ export TX_HASH=A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594
$ node imageFromTxHash.js
http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000 #NODE_URL
A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594 #TX_HASH
- getTransaction -
txTimestamp:  2019-12-24T04:52:14.000Z #トランザクションのタイムスタンプ(UTC)
isConfirmed:  true
Transaction size:  1119552 #トランザクションのサイズ
fileName: A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594.jpg #ファイル名
- writeFile -
```

* トランザクションのタイムスタンプ
    * `2019-12-24T04:52:14.000Z`　2019年クリスマスイブ！

保存する際のファイル名は`TX_HASH+'.'+fileType(拡張子)` にしています。

* 今回のファイル名
    *  `A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594.jpg`

保存されてるかな？:thinking:

```bash:tree
$ tree -s -L 1
.
├── [     766788]  A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594.jpg
├── [     766788]  CatapultChristmas2019.jpg
├── [       2444]  README.md
├── [       1556]  imageFromTxHash.js
├── [       2742]  imageToAggregateTransaction.js
├── [       2592]  node_modules
├── [      21788]  package-lock.json
└── [        740]  package.json
```

保存されています。byte数は`766788`。全く一緒ですね。

## トランザクションメッセージからデコード作成した画像を開いてみる
お好みの画像ビュワーで開いてみてください。


![727035f5-a410-658f-3128-3824b98066fb (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/374b1110-dc5d-b734-3d7c-902bee21a1f8.png)

OK!いけました！:ok_woman: :tada:

サンプル動作がうまくいかなかったらお気軽にコメントで質問してくださいね。

# REST API -> 直接画像ファイル作る [追記]

Sample2の処理内容は、以下ワンライナーでも可能です。

REST API（/transaction/{トランザクションハッシュ値}）
で得られるJSON文字列から、直接画像ファイルを生成する。

* curl
* jq

`curl -s http://{ノードのURL}:3000/transaction/{トランザクションハッシュ値} | jq -r -c '.transaction.transactions[].transaction.message.payload' | awk '{printf "%s",$0}' | xxd -r -p |  base64 -D > output.jpg`

注）内容から拡張子判定はしていないため、出力ファイル名は "output.jpg" 固定にしてます。

### macOS

```
$ curl -s http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/transaction/A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594 | jq -r -c '.transaction.transactions[].transaction.message.payload' | awk '{printf "%s",$0}' | xxd -r -p |  base64 -D > output.jpg
```

### Linux系(ubuntu)
base64 -d (オプションが小文字になる)

```
$ curl -s http://lb-7qvhzx74i6vzy.japaneast.cloudapp.azure.com:3000/transaction/A1E3C523C67FEA8F4D333C662CF8918FCC1BA91F147F9EF7FAE070CEE787E594 | jq -r -c '.transaction.transactions[].transaction.message.payload' | awk '{printf "%s",$0}' | xxd -r -p |  base64 -d > output.jpg
```

# ここまでのまとめ

* mijin Catapult Fushicho2 プライベートブロックチェーンを利用
* (Sample1) クリスマスカード画像データそのものをアグリゲートトランザクション送信
* (Sample2) トランザクションハッシュ値指定で画像データを取得し保存

# 備考：手数料について

今回はCatapultプライベートチェーン、かつ**送金手数料を0**にてサンプル実装しました。
Catapultは2020年春にパブリックローンチが予定されています。
気になるのはパブリックチェーン上で同じことした場合、手数料はどのくらいになるのか？

## 手数料計算
先ほどのクリスマスカード　アグリゲートトランザクションサイズは
`Transaction size:  1119552`

minFeeMultiplier 100
基軸通貨　nem:xem 
可分性6
とした場合

`1119552 * 100=111955200`
`111.9552xem`

2019・12・24時点のxemの価格　3.5円としますと
392円ぐらいになります。（あくまで暫定の計算です）

参考: [NEM1 と NEM2 で変わる仕様まとめ](https://qiita.com/44uk_i3/items/53ad306d2c82df41803f#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E6%89%8B%E6%95%B0%E6%96%99%E3%81%AE%E4%BB%95%E6%A7%98%E5%A4%89%E6%9B%B4) / [fushicho-2のTIPS](https://qiita.com/m1z0/items/3560771261318f1c5af4)

パブリックチェーンへデータ送信する際は、基軸通貨での送金手数料がかかります。
逆にデータ取得する場合は、送金手数料はかかりません。

今回の処理をパブリックチェーン上で行うかどうか？は、アプリやサービス要件次第になりますね。

注）プライベート・コンソーシアムチェーンはカスタマイズによって送金手数料を0にすることが可能です。[^8]

* パブリックチェーンにはファイルハッシュ値だけを記録
* プライベート・コンソーシアムチェーンにはファイルデータそのものを記録
* アンカリングする

パブリックチェーン<->プライベート・コンソーシアムチェーン 
ハイブリッド型で構築することも可能ですね。

# nemgraphでRiNG公証
ということでハイブリッドっぽく
今回のクリスマスカード画像ハッシュをパブリックブロックチェーン NEM にあげてみよう！

https://nemgraph.net/
を使います。 [^9]

NEM（NIS1）パブリックブロックチェーンに nemgraph **RiNG公証**をしてみます。[^10]

![nemgraph.net_ring_validation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253638/3a12e2e3-6dd6-2d3d-a6e5-e6be96a6df71.png)
[この画像はNEMブロックチェーンによって証明されています](https://nemgraph.net/pic/1252/8c9cac22fb0ee13dafa8ec49f1f4511cc4bee00bc8b32dfff84d869ea36ce5135e01ad3f0ccda)

パブリックブロックチェーンNEM上に
登録者アドレス、登録日時、画像ハッシュ、Txハッシュ
が書き込まれました！

# 懸念点
定番クリスマスソング「ジングルベル」「きよしこの夜」のmp3 :musical_note: を書き込むことも考えたのですが、
著作権フリー楽曲とはいえ、音源を使う場合はリンク先を明記する必要があるなどなど。断念:sob:
（自分で歌ったら？と言われましたが、、、いやいや。。。）

ブロックチェーンに書き込まれたトランザクションデータは改竄できない＝消すこと自体ができません。
個人情報や著作権問題を考えると、パブリックチェーン上でファイルオブジェクトを閲覧可能にする事は、リスキーな一面があります。

[NEMでファイル共有システムを作ってみたらこうなった](https://qiita.com/scrpgil/items/07839aaebfe32ea777ed)
でもあげられていましたが、プライベート・コンソーシアム型ブロックチェーン [mijin Catapult](https://mijin.io/) の利用でリスク対処することは可能です。

# まとめ

世の中には不正改竄があふれています。悲しいですが性善説だけでは人間界はまわらない、まわせない:imp:

アポスティーユ [^4]を使えばファイルハッシュ値とタイムスタンプ、元データと突合、改竄されていないことを証明できます。

しかし。
**"元データを破棄したので、もうどこにもありません？？？"** :thinking: 
なんて、おかしな言葉も最近よく聞きますね:weary:

2019年12月25日は「消してはいけない元データを破棄(破壊)される憂き目」にあう世界。 :earth_asia::earth_africa::earth_americas: 

我々は、ブロックチェーンが生まれて苦しみながらも成長する途上を見ながら生きています。
これから徐々に、":baby:生まれたときから、ブロックチェーンが存在していた世代"が社会にやってきます。

---
「それ、そもそもブロックチェーンでやる必要あるのか？？否:imp::no_good_tone1:」（今。国内回るとまだ多め）
↓
「それ、もしかしてブロックチェーンでやったらいいんじゃない？:thinking:」（徐々にの感覚アリ）
↓
（ここからは想像と希望）
↓
「それ、なんでブロックチェーンで管理をしてないのヨ！？早よせえ！:anger:」（ブロックチェーンネイティブ世代台頭時）

---

 
**元データをタイムスタンプ:stopwatch:とともにブロックチェーン:link:へ記録する＝改竄破棄不可能** 
こんな思いを込めた？サンプルの"発展系"が、未来選択の一つとして考えられるのではないでしょうか。

--- 
クリスマスカードがテーマ＆クリスマスイブナイトなのにメチャ重いまとめをかいてしまった！
改竄不可能な「愛:heart:」でもいいんですよ?（無理やり）

*"Love is the one thing we're capable of perceiving that transcends dimensions of time and space."*
*Dr. Amelia Brand (Interstellar)*

2020年nemアドベントカレンダーでは
"愛"を、稼働しているCatapultパブリックチェーンへ、 Morse codeで送信してみたい。
（送金手数料めちゃくちゃ高くなってたらどうしよう、、、ｗ）

愛の可視化か。方法を探そう:eyeglasses: 


# 今回やりきれなかったこと

* アプリ画面作るまで至らず（デザインをいつもまかっせきりだからこうなる。。。）
* mijin Catapult **Fushicho3**での同処理を実施済み。記事修正間に合わず。
* Catapult新機能 **メタデータ** [^11] を合わせて使う。
* 複数アグリゲートトランザクションに分割し、もっと大きいファイルを送れるようにする。
* インナートランザクション上限設定を変更してやってみる
  * インナートランザクション最大数1000はパブリックチェーンの設定値です。
  * 最大数上限をあげて、マシンスペック別プライベートチェーンテストをする。
      * catapult-service-bootstrapの場合

`ruby/catapult-templates/api_node/resources/config-network.properties.mt`
`ruby/catapult-templates/peer-node/resources/config-network.properties.mt`

```diff:config-network.properties.mt

#44行目
[plugin:catapult.plugins.aggregate]

- maxTransactionsPerAggregate = 1'000
+ maxTransactionsPerAggregate = 数値を変更
```

--- 

ここまで読んでいただきありがとうございました！
 [nem Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nem "nem Advent Calendar 2019") に参加できて光栄です。

メリークリスマス！素敵な2019年クリスマスをお過ごしください:santa:

*May the Joy and Happiness spread like the lights of the Christmas tree and wings of the phoenix "Fushicho"！*

[^1]: [仮面ライダー龍騎 (2002年)]( https://dic.pixiv.net/a/%E4%BB%AE%E9%9D%A2%E3%83%A9%E3%82%A4%E3%83%80%E3%83%BC%E9%BE%8D%E9%A8%8E) 契約モンスター召喚の際のデジタル音声「アドベント-召喚-」。同じく「コントラクト-契約-」があるとこがニヤリ。

[^2]: [不死鳥](https://bandai-a.akamaihd.net/bc/images/net-s_tokushu/20180625_csmdragon_vcard_25.png) "ゴルトフェニックス (GOLD PHOENIX)" 不死鳥型モンスター 劇中最強の設定。不死鳥に"GOLD"とついてるとこもニヤリ。

[^3]: 若手「hさん、(nemアドカレ)25日空いてますw」　私「Congrats on your engagement!:congratulations: 」

[^4]: [アポスティーユ　ホワイトペーパー日本語訳(トレストさん)](https://www.cryptostream.jp/wp/wp-content/uploads/ApostilleWhitePaper_Japanese.pdf) / [アポスティーユ　ホワイトペーパー英語版](https://nem.io/wp-content/themes/nem/files/ApostilleWhitePaper.pdf)

[^5]:  [3.5インチFDD 2DD形式] (https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%AD%E3%83%83%E3%83%94%E3%83%BC%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF#3.5%E3%82%A4%E3%83%B3%E3%83%81%E3%83%95%E3%83%AD%E3%83%83%E3%83%94%E3%83%BC%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E5%90%84%E5%BD%A2%E5%BC%8F%E3%81%AE%E8%A9%B3%E7%B4%B0)  :floppy_disk:約1枚分。隔世の感を禁じ得ない？
 
[^6]: https://nemtech.github.io/nem2-openapi/#operation/getTransactionStatus

[^7]: https://nemtech.github.io/nem2-openapi/#operation/getTransaction

[^8]: プライベート・コンソーシアムチェーンノードの維持費用が必要です。

[^9]: [nemgraph](https://nemgraph.net/) 引用 "nemgraphは暗号通貨NEMコミュニティの声から誕生したサービスです。 本サービスは写真の投稿を通じてユーザー同士がつながる場所を提供する コミュニケーションツールです。"

[^10]: [RiNG公証](https://nemgraph.com/ring/) 引用 "NEMブロックチェーンに刻むことで、自身の写真であることを証明できる画期的な機能です"

[^11]: [nemtech メタデータ](https://nemtech.github.io/ja/concepts/metadata.html)　引用 "Catapult にはトランザクションとともに アカウント, モザイク, ネームスペース にメタデータを関連付ける手段が用意されています。"