IOTA:【技術解説】MAMの全容。

English [here](https://medium.com/@abmushi/iota-mam-eloquently-explained-d7505863b413)
　去年の終わりに公開した記事**[『IOTA:【旧版】MAMとは。IOTAのIoT性。』](https://qiita.com/ABmushi/items/ab523d838bf71ca385d4)**が早くも古くなってしまったので最新版を書くことにした。また、そちらの記事はいろんなアイデアがゴチャゴチャしてMAMの実態を掴みにくい構造になっていたので頑張って分かりやすくなるように努力した。

　本記事のMAMを試したこちらの記事[『IOTAのMAMを試してみる。』](https://qiita.com/ABmushi/items/5490a62f9b38f8461c2a)ではライブラリの使い方を説明した。

# IOTA（アイオータ ）とは
おなじみ初心者向けの解説サイトを念のためここに列挙しておく。
>[**IOTA日本語ファンサイト**](https://iotafan.jp/)　実質IOTA日本公式サイト。というのもIOTA公式の情報を日本語訳して掲載しているからである。初心者向けの情報も多く掲載されている。
**ホワイトペーパー**[英語](https://www.dropbox.com/s/i4l3g88wh6gpi5d/IOTA_Whitepaper%20v1.1.pdf?dl=0) [日本語](https://www.dropbox.com/s/1w5vtu7s4idquc9/IOTA_Whitepaper%20v1.1%20in%20Japanese.pdf?dl=0)　Tangleについての概要は序盤までで、それ以降は安全性、想定される攻撃とその耐性について高度な数学で説明している。後半は初心者向きではない。
[**Redditの初心者向けスレッド（英語）**](https://www.reddit.com/r/Iota/comments/61rc0c/for_newcomers_all_information_links_you_probably/)　僕はここから始まった。
[**IOTA Guide（英語）**](https://domschiener.gitbooks.io/iota-guide/content/)　ホワイトペーパーが理学部なら、これは工学部。


# 前提知識
> [IOTA:【入門】トランザクション大解剖！ウォレットは裏で何をやっているか。](https://qiita.com/ABmushi/items/e271ff05884a7d47658d)
> [IOTA【技術解説】署名と承認。 - 改訂版](https://qiita.com/ABmushi/items/422d1bf94be0c919583a)

# MAM とは
　MAMはTangle上の情報のストリームである。世の中の情報を発信する色々なメディア、ラジオやテレビ、新聞、天気予報や監視カメラを想像してみる。それらは時々刻々と新しい情報を名前のついた一括りのコンテンツとして発信しているはずだ。

## チャンネル
　例えばYoutubeのチャンネルのように、MAMもその単位を**チャンネル**と呼ぶ。MAMを閲覧するときは見たいチャンネルにアクセスし、また投稿するときは自分のチャンネルに投稿する。ここで自分というのは、自分の**Seed**という意味である。つまり、チャンネルのアイデンティティもまたSeedに由来する。（IOTAにおけるSeedは全てである。プライバシーと個人の所有権の全てがここに詰まっている。）

## チャンネルのモード
　チャンネルにMAMを発行する際、チャンネル所有者は３つの投稿タイプを選ぶことができる。誰でも見れる**Public（公開）**、自分（Seed所有者）しか見れない**Private（非公開）**、特定の人にだけみることのできる**Restricted（限定公開）**。ここでいう特定の人というのは、チャンネル所有者が設定した**sideKey（チャンネル鍵）**を教えられた人のみという意味である。

> **Public（公開）**：保管されたメッセージはアドレスを使って復号。（アドレス自身が復号に使われるため、アドレスが分かればメッセージも見れる。）
> **Private（非公開）**：`アドレス＝hash(root)`。**rootを使って復号**。（アドレスはハッシュ化されており、ハッシュ化前の生`root`を知っていないと、例えアドレスにアクセスしてもそのメッセージを復号できない。）
> **Restricted（限定公開）**：`アドレス＝hash(root)`。**keyを使って復号**。（rootを教えることはPrivateチャンネルを公開することになるため、あくまでroot+keyを限定公開する。そのroot+keyを知っている人だけがRestrictedチャンネルにアクセスしてメッセージを復号できる。）

# メッセージチェーン
　IOTA自体に元々メッセージをトランザクションに貼り付ける機能はあった。しかし、そのままだとメッセージの連続性は表現できない。例えば、15分ごとに気温をTangleに記録していくとする。最初にアドレスAにメッセージ`12:15 06/02, 18.2`という気温情報を送った。しかし、次のメッセージをまたアドレスAに送るとすると、この気温情報のストリームはアドレスAがずっと保持しなければならず第三者にその情報が筒抜けである。また、誰かからの妨害メッセージ付きトランザクションをアドレスA宛に送金されるなどの恐れも出てくる。
　しかしMAMは一つの**投稿**を別々のアドレスに送っても、紐解くように連続した情報のストリームを取り出すことができるように設計されているためそれらの心配はいらない。
　MAMのメッセージチェーンには言うなれば**世代**の概念がある。ある世代から次の世代へとMAMメッセージは一方通行に鎖に繋がれていく。

# 基礎
　MAMも送金と同じようにBundleに必要情報をまとめてTangleに保管される。MAMのBundleは大きく分けて**署名部**と**MAM部**に分かれる。それらはトランザクションの`signatureFragment`として保管される。署名というのはMAMの発行者を見分けるため＝MAMメッセージの本人確認に使われる。詳しくは後述する。
![mam_bundle.png](https://qiita-image-store.s3.amazonaws.com/0/187795/3904b8e2-f123-12e5-c3d3-a982c06d4878.png)

　MAMを閲覧する際、閲覧者はMAMをTangle上から探すために`root`や`sideKey`と呼ばれる値をMAM発行者から与えられる。公開モードでは`root`のみ。限定公開モードでは`root`と`sideKey`を与えられる。

## アドレス生成
　アドレスというのはMAMメッセージが保管されるTangle上の場所である。これは`root`を使って計算される。

```js:mam.node.js
if (channel.mode !== 'public') {
    // 公開モード以外は アドレス＝hash(root)
    address = Crypto.converter.trytes(Encryption.hash(81, Crypto.converter.trits(mam.root.slice())));
  } else {
    address = mam.root;
  }
```

> **おことわり**
> Paul Handyさんの[記事](https://blog.iota.org/introducing-masked-authenticated-messaging-e55c1822d50e)では限定公開のアドレス＝`hash(root+sideKey)`となっているがソースコード内に認められないので、本記事ではソースコードにのっとり、アドレス＝`hash(root)`で統一した。これは誤っている可能性があるので、随時確認いたしますが詳しい方にコメント欄等で報告していただけると大変助かります。（追記：実際にAPIで試して比べてみましたが、やはりアドレス＝`hash(root)`のようです。）

## 公開モード
> `root`がメッセージの保管アドレス。
> `root`でメッセージを復号する。

![public.png](https://qiita-image-store.s3.amazonaws.com/0/187795/2add74a3-ba43-a89b-2fb2-b51669343f43.png)


　ここで`nextRoot`というのは、MAMのメッセージチェーンのつなぎ目となる値である。一つのメッセージを復号するとそれに含まれる`nextRoot`という値によって閲覧者は続きのメッセージにたどり着くことができる。RPG風に例えるなら、最初の鍵で開けた宝箱の中に、次の宝箱を開ける鍵を入れるようなものだ。これを繰り返すことで、最初のジェネシス世代の`root`を知っている人はメッセージの復号によって得られる`nextRoot`を使って、チャンネルのメッセージチェーン上の全ての世代を連鎖的に追っていくことができる。
　また、チェーン上の途中の世代のメッセージを開ける`nextRoot`を所有している人は、それを使って途中からチャンネル閲覧を始めることができる。しかしその場合、過去の世代のメッセージに遡ることはできない。

## 限定公開モード
> `hash(root)`がメッセージの保管アドレス。
> `sideKey`でメッセージを復号する。

![restricted.png](https://qiita-image-store.s3.amazonaws.com/0/187795/9575cb56-4264-fe2c-cd31-b628644716ae.png)


　メッセージチェーンの仕組みは公開モードと変わらない。ただ、`sideKey`という復号鍵を閲覧者は所有していなければメッセージの内容を読むことができない。

# チャンネルの所有権問題
　チャンネルに初投稿したAさんはBさんにチャンネルを見て欲しいと考えた。そこで、AさんはBさんに`root`を教えた。BさんはAさんにもらった`root`でアドレス`=hash(root)`を訪れ、メッセージを`sideKey`で復号しAさんの投稿を閲覧した。続きが気になったBさんは復号の際手に入った`nextRoot`で次のアドレス`=hash(nextRoot)`を取得し、次のメッセージを見ることにした。しかし、Aさんはまだ次の投稿をしていなかったので、そのアドレス宛てにはまだ何のトランザクションも発行されていなかった。

　BさんはAさんに嫌がらせをしたいと突然思いついた。見渡すとBさんの手には、`nextRoot`と`sideKey`があった。これがあれば、Bさんは次のメッセージをBさんが勝手に決めて、`sideKey`で暗号化して次のアドレス`=hash(nextRoot)`宛てにAさんより先に投稿すれば、Aさんのチャンネルを乗っ取れると考えた。

　これは上手くいくだろうか？今までの説明ではこの盲点をカバーできていない。ここでMAMにも送金と同じように**署名**という概念が必要になる。

　ここでIOTAにおける、アドレスの残高の所有権はどう証明したかを思い出してみよう。アドレスの残高を引き出す際には、Seedから生成される**Private Key**で[署名](https://qiita.com/ABmushi/items/422d1bf94be0c919583a#%E7%BD%B2%E5%90%8D%E3%81%AE%E6%96%B9%E6%B3%95)したトランザクションが承認されることで残高を引き出すことができた。

　MAMでは**Private Key**を`root`生成に使った**Merkle Tree**と組み合わせることでチャンネルの所有権を証明するのに利用する。

## Merkle tree based signature scheme 
　MAMにおける**Merkle Tree（マークル木）**を使った独特な署名方法について説明をしたい。[Merkle tree based signature scheme](https://www.imperialviolet.org/2013/07/18/hashsig.html)と言う技術らしいので興味ある人はリンク先を読んで見ると良い。

# **投稿** - 基礎
　MAMを投稿するにあたってまず必要となるのは投稿する場所＝アドレスだ。MAMは上で説明した通り、`root`という値がアドレスに直接関係するため、まず`root`の生成方法から説明する。ここで先ほどちらっと出てきた**マークル木（Merkle Tree）**の説明も入る。

```js:mam.client.js/blob/master/src/node.js
/*
　　　　SEED: シード
　　　　MESSAGE: 送りたいメッセージ
　　　　SIDE_KEY: sideKey
　　　　CHANNEL: オブジェクト{security,start,count,index}
　　　　　　　　　*indexはbranch_index（後述）
*/
function createMessage(SEED, MESSAGE, SIDE_KEY, CHANNEL) {
    if (!SIDE_KEY)
        SIDE_KEY = `999999999999999999999999999999999999999999999999999999999999999999999999999999999`
    // MAM settings
    let SEED_trits = string_to_ctrits_trits(SEED)
    let MESSAGE_trits = string_to_ctrits_trits(MESSAGE)
    let SIDE_KEY_trits = string_to_ctrits_trits(SIDE_KEY)

    const SECURITY = CHANNEL.security
    const START = CHANNEL.start
    const COUNT = CHANNEL.count
    const NEXT_START = START + COUNT
    const NEXT_COUNT = CHANNEL.next_count
    const INDEX = CHANNEL.index

    const HASH_LENGTH = 81

    // set up merkle tree
    let root_merkle = iota_merkle_create(SEED_trits, START, COUNT, SECURITY)
...
```

## `root`の取得
　`root`とはマークル木のルート（根）を意味する。そのため`root`取得にはまずMerkle Treeを生成する必要がある。
　MAMのMerkle TreeはSeedから生成する。（種が木へ！）Merkle Treeは`start`と`size`という引数をとる。これはSeedから生成されうる`address`の[連鎖](https://iotafan.jp/developer/iota_universe_abmushi_20171213/)のどの部分をMerkle Treeの葉として使うかに用いられる。SeedからPrivate Keyを生成し、`address`を生成するために、`index`という引数をとったことを覚えているだろうか？（忘れてしまった人のための記事は[こちら](https://qiita.com/ABmushi/items/e271ff05884a7d47658d#%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9)。）下の図のA、B、C、Dはそれぞれ`index`が0~3で生成されるPrivate Keyだ。A'、B'、C'、D'は該当Private Keyから生成される`address`である。
![mam_merkle_1-2.png](https://qiita-image-store.s3.amazonaws.com/0/187795/06ea21b7-6427-90b4-1c82-4a64c53a0df1.png)
　Merkle Treeの葉A"、B"、C"、D"には`address`にハッシュ関数を通したものを用いる。葉から`root`へ枝が根に集まるようにハッシュ関数に通していく。図で言うと下のノードから葉の方向へ逆生成させることはできない。
　こうして取得できる`root`をアドレスとして（限定公開モードなら`root`にハッシュ関数をかける）使う。



# **投稿** - MAM部
　まず、MAMとして送りたいメッセージ（`messsage`）はこのMAM部に含まれる。`message`となりうるものは、気温データなどのセンサー情報や文字データである。現在のところ日本語などの全角文字には対応しておらず生データは**asciiコード**で用意する。ライブラリの`asciiToTrytes.js`の`toTrytes(ascii)`で変換して`message`に格納する。
## `nextRoot`の取得
　一世代のMAMメッセージにつきMerkle Treeは実は二つ生成される。その世代と次の世代の分だ。どちらとも生成方法は同じである。ソースコードでいうとパラメーターは`START`と`COUNT`が渡される。

```js:
const START = CHANNEL.start           //　一つ目のMerkle Treeの開始index。
const COUNT = CHANNEL.count           //　一つ目のMerkle Treeの終了index（の一個手前）。
const NEXT_START = START + COUNT      //　二つ目のMerkle Treeの開始index。
const NEXT_COUNT = CHANNEL.next_count //　二つ目のMerkle Treeの終了index（の一個手前）。
const INDEX = CHANNEL.index           //　枝番号（後述）
```
　第０世代の`tree0`は`start0 = START`、`end0 = COUNT`。第１世代の`tree1`は`start1 = START+COUNT`、`end1 = CHANNEL.next_count`。言い換えると生成されるPrivate Keyの連鎖で１つ目のMerkle Treeが使っていない次の部分を使う。下図参考。

![two_merkle.png](https://qiita-image-store.s3.amazonaws.com/0/187795/ada55666-53a9-bfd5-9b44-e7027bf942f3.png)

　ちなみに全ての世代のMerkle Treeが同じサイズである必要はない。

## 枝番号 `branch_index`
（旧MAMの記事では葉番号と仮に名付けたが後ほど説明するチェーンフォークの枝分かれを考えると**枝番号（`branch_index`）**という名前に早くも変更させていただいた。）
　枝番号は`root`取得の際に使ったMerkle Treeの葉から任意に`index`を選んで決める。
　先ほどの例で言うと、Merkle Treeは`start=0`、`count=4`で生成されたので`branch_index`は`0,1,2,3`のどれかの任意の値だ。
![branch_index.png](https://qiita-image-store.s3.amazonaws.com/0/187795/10a71f38-3707-f023-bff6-9a5a9e2c4d21.png)
　ちなみに`branch_index`は整数である。上の図で見るとPrivate Keyを囲んでいるが枝”番号”なので値はあくまでもデータ型はintの`0,1,2,3,...`となる。
## Siblings
　Siblings（兄弟）という概念について説明する。
　まず、上のMerkle Tree上の葉`A'`が与えられたとする。`root`を得るためには全ての葉`B'C'D'`が必要になるが全ての葉が分からない場合どうやって`root`を導き出せるか。
　Siblingsとは葉`A'`とともに`root`を得るために使われるMerkle Tree上の途中の枝の部分である。図を見る方が早いかも知れない。
![mam2_siblings.png](https://qiita-image-store.s3.amazonaws.com/0/187795/f7d3ef55-c8b2-48d7-342d-076441778eb2.png)
　上の図は`branch_index=0`の例である。枝番号に対応する葉`A'`だけが与えられた。その場合は`B"`と`Hash(C"D")`があれば**全ての葉を知らなくとも`root`を求めることができる**。このような働きをする、`B"`と`Hash(C"D")`を`A'`の**Siblings**と呼ぶ。
　また、この際は`branch_index=0`の**Siblings**と呼ぶ。`branch_index`が変わるとSiblingsの中身も変わる。
## MAM部の生成
　MAM部にはこれまで説明した`message`、`nextRoot`、`branch_index`、`Siblings`を公開モードなら`root`で、限定公開モードなら`sideKey`で暗号化する。下の図は限定公開モードの例。`sideKey`で暗号化をする。

![mam2_mam_section.png](https://qiita-image-store.s3.amazonaws.com/0/187795/d800db9f-8121-fafc-17f9-d300123375f8.png)


# **投稿** - 署名部
　発行者はMAM部の内容の正当性を証明するために**署名**をBundleに載せる必要がある。その署名を`signatureFragment`に保管するのがこの署名部の役割だ。
## 署名の生成
　署名は、`key(seed,branch_index,security)`で生成される**Private Key**をMAM部の`messageTrytes`で署名して生成される。署名生成の仕組みは[こちら](https://qiita.com/ABmushi/items/422d1bf94be0c919583a#%E7%BD%B2%E5%90%8D%E3%81%AE%E6%96%B9%E6%B3%95)の記事を参照。
![mam2_sig.png](https://qiita-image-store.s3.amazonaws.com/0/187795/0c5b702c-5f78-7cba-b67b-bb2cb42b3c25.png)

# **閲覧**
　閲覧するのに必要なのは、`root`と、限定公開なら`sideKey`だ。
　まずは`root`からアドレスを計算し、Tangleにそのアドレスで検索をかける。見つかったBundleのMAM部の`messageTryte`を公開モードなら`root`で、限定公開モードなら`sideKey`で復号する。この時点で`message`、`nextRoot`、`branch_index`、`Siblings`が手に入る。次にこのメッセージの正当性を確認する必要がある。署名部の出番だ。
　署名部にある署名を、**`messageTryte`を署名されるデータとして**承認作業をする。承認作業の仕組みは[こちら](https://qiita.com/ABmushi/items/422d1bf94be0c919583a#%E6%89%BF%E8%AA%8D%E3%81%AE%E6%96%B9%E6%B3%95%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%AE%E9%80%86%E7%94%9F%E6%88%90)の記事を参照。承認作業で得られるのはアドレスだ。このアドレスをMerkle Treeの`branch_index`に位置する葉として、`Siblings`と組み合わせることでRootを計算する（`temp_root`と呼ぶ）。**`temp_root`がチャンネル発行者から教えてもらった`root`と一致すれば**今閲覧しているBundleは正当な公式MAMメッセージということになる。
　もし、一致しなければ今閲覧しようとしているBundleは悪意を持った発行者以外の人間にアタッチされたMAMメッセージである。
# チェーンフォーク
　MAMのメッセージチェーンは分裂できる。方法も簡単で、枝番号`=branch_index`が違うメッセージを投稿するだけだ。ただし、一つの世代につき、その世代のMerkle Treeのサイズ分までしか分裂できない。というのも`branch_index`が次の世代の方へはみ出てしまうからだ。
　また、分裂する際にそれぞれの投稿モード（公開や限定公開）は同じである必要はなく、`sideKey`も統一する必要もない。こうすることでサブチェーンごとに役割を住み分けることもできる。

![fork.png](https://qiita-image-store.s3.amazonaws.com/0/187795/d817a049-0cea-3499-6b9c-ddaf85a18766.png)

# 何故こんなにも面倒な署名方法なのか
　Merkle Treeの署名方法など使わずに、１つの秘密鍵から生成した１つのアドレスに送りたいだけの数のメッセージを送信することもできる。本人確認はそのメッセージごとに署名をBundleに含めればいいように思える。しかし、IOTAでは１つの秘密鍵ごとに署名は一回しか使えない。
　Merkle Treeをわざわざ使ったのは、異なる秘密鍵でそれぞれのメッセージを署名しつつ、送信先のアドレスを一つのアドレス（`root`）に集約させるためである。

# スナップショット
　さてMAMはトランザクションの`signatureFragment`に保管されている。つまり、スナップショット（＝残高プラスのアドレス以外削除）が行われると全てのTangle上のデータは削除される。そのため、MAMはスナップショットが行われない**Permanode（パーマノード）**という特殊なフルノードに依存する形となる。
　巨大な容量と高速な処理スペックが要求されるPermanodeだが、現在のところ金銭的インセンティブはない。MAMはIOTAのIoT性を左右する要となる機能なだけに今後どうなっていくのか注目される。

# 参考文献
> MAM JS　公式ライブラリ https://github.com/iotaledger/mam.client.js
> IOTA github https://github.com/iotaledger/iota.lib.js
> Introducing MAM https://blog.iota.org/introducing-masked-authenticated-messaging-e55c1822d50e