[Bitcoin]ウォレットアプリで見かけるMnemonic codeについて 

金曜日です！
今年も[去年と同様](https://qiita.com/pokochi/items/4d536a4a24067505abea)に、アドカレでビットコインネタです。

今年は去年とは対照的に、ビットコインの価格は急落ですね！
その影響で年末年始を(´・ω・｀)な感じで過ごす人が沢山いそうです m9(^Д^)。

ですが裏ではブロックチェーン業界の技術は確実に進化しており、ようやく投機対象としてだけでなく、本格的な開発フェーズに入ったという感じの今日この頃。
今回はビットコインなどの暗号通貨を管理するウォレットアプリでよく見かける、**Mnemonic**（ニーモニック）について書きます。

## Mnemonicとは
Mnemonicは、作成したビットコインアドレスを復元する際に使う、12個の英単語のことです。
**Mnemonic code**とも呼ばれ、ウォレットで管理しているビットコインのバックアップとして利用され、パスフレーズとも言います。

ウォレットアプリを利用したことがある人は、1度は目にしたことがあると思います。
下図はESSTYというウォレットアプリで作成した、テスト用のビットコインアドレスのMnemonicです。
黒塗りにしている箇所にも英単語が表示されてます。

![mnemonic.png](https://qiita-image-store.s3.amazonaws.com/0/84601/6518aee2-6319-7a8a-4210-42d7ae1e166d.png)

**Hierarchical Deterministic（階層的決定性）**という仕組みみで実装されたウォレットアプリは、**HDウォレット**と呼ばれますが、HDウォレットは1つのマスターキーから乱数を用いずに、階層的に無数のビットコインアドレスを作り出すことを可能にする技術です。
そのマスタキーを作成するたのSeedデータにMnemonicが利用されます。

HDウォレットではMnemonicだけ覚えておけば、ビットコインアドレスを作成するたびに秘密鍵をバックアップする必要がなく、管理コストやリスクを大幅に軽減するメリットを享受できます。
またMnemonicさえ覚えておけば、他のウォレットへのインポートも可能です。
HDウォレットの詳細はここでは割愛して、Mnemonicにフォーカスして説明を続けます。
もしMnemonicが他人に知られてしまうと、そのMnemonicから生成したビットコインアドレスの残高が、他人の手に渡ってしまう事を意味します。つまり盗まれると同義ですね。

## エントロピー
Mnimonicでよく使われるワードの数は12個か24個ですが、ワードが24個もあると長すぎるので、12個が一般的です。
12個は128ビットの**エントロピー**と呼ばれる暗号学的にランダムな値から、24個は256ビットのエントロピーから生成されます。
サイズが大きいエントロピーではセキュリティは向上しますが、ワードの数が増加するため利便性とトレードオフです。
初期エントロピーの長さをENTとも表記し、ENTの許容サイズは32ビット区切りで128〜256ビットの範囲です。32ビット区切りなので、**エントロピーの長さは128、160、192、224、256ビットの何れか**が許容されている事になります。

## BIP39
このMnemonicですが、利用するワードのルールや作成方法などは**BIP39**という規約で決められており、基本的にはBIPに準拠して実装します。
BIPとはBitcoin Improvement Proposalsの略で、ビットコインの改善案のことです。その39番目にMnemonicの仕様が定義されてます。ちなみにHDウォレット仕様は、BIP32・BIP44で定められてます。

[BIP39について](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)

Mnemonicに利用できるワードリストもBIP39で定義されてます。
英語以外の言語も用意されてますが、特に理由がない限りは英語にするのが無難だと思います。

[BIP39のワードリスト（英語）](https://github.com/bitcoin/bips/tree/master/bip-0039/english.txt)

URLを開くと英単語がびっしり記載されてます。このリストから12個のワードを選択するわけです。
最近はBIP39に準拠したMnemonicからマスタキーを作成し、そのマスタキーを利用してBIP44に準拠したHDウォレットを実装するのが主流のようです。

## Mnemonicの生成手順
それでは、BIP39を参照してMnemonicの生成手順を簡潔に纏めてみます。

1. 始めに、エントロピーを128〜256ビットの間の大きさ (32ビット区切り) で生成する
2. エントロピーのSHA256ハッシュ値をとり、はじめのエントロピーサイズ÷32ビットをチェックサムとする
3. 上記のチェックサムをエントロピーの末尾に連結する
4. 連結してできたデータを11ビットごとに区切る
5. ワードリストから、上記で得られた11ビットの数字をインデックスとして単語を抜き出す

上記の手順を実行すると、Mnemonicが取得できます。

## Mnemonicの生成コード

ありがたいことに、この手順をラップしてMnemonicを取得するライブラリが既にいくつか存在しますが、**ここでは敢えてライブラリに頼らずに、**node.jsで実装してみたいと思います。
ライブラリに頼らず自分で実装することで、Mnemonicの生成アルゴリズムを考えてみます。

サンプルコードです。

```js:generateMnemonic.js
const fs = require('fs');
const crypto = require('crypto');

// ワードリストを準備。特に理由がなければワードは英語を使う
// https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
const words = fs.readFileSync('mnemonic_wordlist.txt').toString().trim().split('\n');

// エントロピー（今回は例として7f固定の128ビット）
const ent = Buffer.from('7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f', 'hex');

// エントロピーのSHA256ハッシュ値をとり、はじめのentropyLength/32ビットをチェックサムとする
//（コードでは32ビット = 4バイトとして4で割っている）
const checksum = crypto.createHash('sha256').update(ent).digest().slice(0, ent.length/4);

// 上記で取得したチェックサムを、エントロピーの末尾に連結する
const data = Buffer.concat([ent, checksum]);

// データをバイナリ文字列に変換
let bin = '';
for(let d of data) {
    bin += ('00000000' + d.toString(2)).slice(-8);
}

// 12文字のニーモニックを格納する配列
let mnemonic = [];
for(var i=0; i<3*ent.length/4; i++) {
    // ワードリストから、11ビットの数字をインデックスとしてワードを取得する
    var idx = parseInt(bin.slice(11*i, 11*(i+1)), 2);
    mnemonic.push(words[idx]);
}

// ニーモニックをスペース区切りで表示する
console.log(mnemonic.join(' '));
```

コードが完成したら保存して、ターミナルから実行します。
成功するとエントロピーを元に、ワードリストから12個のワードを取得して表示します。

```
$ node generateMnemonic.js
legal winner thank year wave sausage worth useful legal winner thank yellow
```

無事に12個のワードを取得できました！
これがMnemonicです。ちなみにMnemonicはワードの並び順も関係してます。

（おまけ）
ついでなので、上記で取得したMnemonicが正しいか検証してみましょう。
検証にはBIP39のライブラリを利用します。
※Mnemonic自体もライブラリで簡単に作成できちゃいますが、今回の趣旨ではないのでツッコミなしで

```js:
var bip39 = require('bip39');

// 上記で取得したMnemonic
const mnemonic = 'legal winner thank year wave sausage worth useful legal winner thank yellow';
console.log(bip39.validateMnemonic(mnemonic));
=> true
```

はい、無事にtrue（検証OK）がでました。
Mnemonicのワードを変えたり、並び順を変えると結果はfalseになります。

ビットコインなどの暗号通貨ウォレットを実装するにはMnemonicだけでなく、HDウォレットの知識も必須です。つまりBIP39だけでなく、BIP32とBIP44も理解する必要があります。
ここでは触れてませんが、他のBIPに興味のある方はぜひ学習してみて下さい。

最後まで読んで頂き、ありがとうございました。

以上