Metadataを刻みたい、日本語で


nem Advent Calendar 2021のn日目ですね。(n = 10)

近畿大学でブロックチェーンや秘密鍵周りのことを学んでいるいなたつです。

早速本題

Metadataなんか、SymbolWalletで見れないんだけど。。。日本語(全角文字)入れれないんだけど。。。

って全人類思ったことがあるかと思います。


## Metadataとは

アカウント、モザイク、ネームスペースに対してカスタムデータを関連付ける方法として、Symbolが提供するもの。

[詳しくはこちら](https://docs.symbolplatform.com/ja/concepts/metadata.html)

## プログラムでMetadataを付与する
[公式様](https://docs.symbolplatform.com/ja/guides/metadata/assigning-metadata-entries-to-a-mosaic.html)を見る。

```ts
const key = KeyGenerator.generateUInt64Key('TEST');

// replace with network type
const networkType = NetworkType.TEST_NET;

// replace with public key
const alicePublicKey =
  'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const alicePublicAccount = PublicAccount.createFromPublicKey(
  alicePublicKey,
  networkType,
);
// replace with value
const value = 'TEST METADATA';

const accountMetadataTransaction = AccountMetadataTransaction.create(
  Deadline.create(epochAdjustment),
  alicePublicAccount.address,
  key,
  value.length,
  value,
  networkType,
);
```
keyはUInt64で表現します。今回は「TEST」にします。
valueは「TEST METADATA」という12文字(空白込み13文字)の文字列となってます。

### 結果を確認する
まずはSymbolWalletで確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/3ea8c438-2c8f-b636-7aaa-32226c890f25.png)

見れん。

次にexplorerで確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/eedb9b59-89f9-30ae-14f3-ce539daff3f5.png)

TEST METADATAっぽい文字が見えてますね。

## SymbolWalletでMetadataを付与する
今回は「Symbol Wallet」と刻みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/067e06b7-d6f9-10b5-3281-cab15f30a1bb.png)


### 結果
同じくまずはSymbolWalletから
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/aa168d44-8cc0-1afd-8fcd-85bc28446edf.png)


見れるじゃん

次にexplorer
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/7ee90e53-9886-1e29-e0cc-e4624c268803.png)

なんか数字の列ができてますね

## 差異について考える
公式を参考に書いたプログラムとSymbolWalletでMetadataにどのような違いがあったかを確認してみます。

- どちらの確認方法で表示でができている

プログラムで作った際はexplorerで、SymbolWalletで作った際はSymbolWalletで表示ができている

- ValueSizeDeltaの数値

プログラムで生成した「TEST METADATA」では`value.length`で取得しているため文字数の13がValueSizeDeltaになっています。
SymbolWalletで生成した「Symbol Wallet」では13文字に対してValueSizeDeltaは26になっていますね。

## 日本語のメタデータをSymbolWalletから付与する

「メタデータ」と付与します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/7f3e8da4-82d3-d0ee-f5e7-21a015784510.png)

結果
SymbolWallet
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/60e51753-6a6c-fdb6-fe36-72718e5d2f86.png)
explorer
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/0e3df2ec-2ea0-39b9-cb3d-8714e65679d9.png)

「メタデータ」という5文字に対してValueSizeDeltaは30ですね。

explorerで確認するとvalueは「E383A1E382BFE38387E383BCE382BF」ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/0f5a54e1-9960-4db5-5e8f-1a56cb863680.png)

## なぜこんなことになってるのか
とりあえず状況整理しましょう

- アルファベットのときにValueSizeDeltaが文字数の2倍になっている
- 日本語のときにValueSizeDeltaが文字数の6倍になっている
- なんやらvalueが16進数っぽい

16進数文字列だと仮定し先程の「メタデータ」という文字列を16進数に変換してみます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/d116f711-c00a-8890-4ba3-0fd7bf2ef588.png)

`e3,83,a1,e3,82,bf,e3,83,87,e3,83,bc,e3,82,bf`

同じですね、つまりSymbolWalletではメタデータとして入力した文字列を16進数に変換したものをSymbolブロックチェーンに刻んでいることがわかりました。

## よくない原因
公式のドキュメントで示しているソースコードでは半角文字のみを考慮していることもあり、valueをそのままメタデータとして使っている。
そのため、日本語などの全角文字をドキュメントにある実装で扱おうとすると、ValueSizeDeltaが6倍じゃないためエラーが出てしまう。
じゃあ日本語の場合6倍すればいいのか？っていうと文字化けするのでだめ。ちゃんと16進数にしないと載せれない。

公式に従ってプログラム作ったとき(ドキュメント、explorer)と公式が作ったやつ(Symbol Wallet)で実装が食い違っている。

個人的にはドキュメントで16進数で刻む実装を示してexplorerで復号して表示するようにしたら幸せになれると思う。互換があれなのでそのまま表示もできるようにしたほうがいいとは思いますが。。。

## 実装的にはどうするか

```ts
const tx = AccountMetadataTransaction.create(
  Deadline.create(epochAdjustment),
  apostilleAccount.publicAccount.address,
  MetadataKeyHelper.keyToKeyId(k),
  Convert.utf8ToHex(v).length,
  Convert.utf8ToHex(v),
  this.networkType
);
```

こんな感じでutf8からHexに変換したものと、長さでメタデータを作成する

```ts
Convert.decodeHex('E383A1E382BFE38387E383BCE382BF') // メタデータ
```

表示する際はこんな感じにデコードしてあげれば良い。

Convertクラスでutf8からhexにして刻んで、刻まれたhexを表示する際にutf8に戻せばちゃんと日本語も表示されます。