ビットコインアドレスの秘密鍵は何から出来ている？WIFって？

# これはなに？
ビットコインの秘密鍵ってそもそも何から作られているの？WIFってたまに聞くけど何？という疑問をなんとなく解消するための，初心者による初心者のためのメモ書きです．"なんとなく"というのはあくまでも作り方がメインであって「なぜその操作をするのか」や「いきさつ」はほとんど扱わないからです．

## 秘密鍵は何からできている？
ビットコインアドレスにおける秘密鍵とは 

$ 1 $ ~ **約** $ 2^{256} $ **までの数字のどれか** です．

とても大きくて美しい数字が出てきました．$ 2^{256} $ は78桁にも及ぶ非常に大きな数であり、衝突(秘密鍵が被ること)が起こる確率は限りなく0に近いと言えます．
**約** $ 2^{256} $とありますが，正確な値は $ 2^{256} $ よりすこし小さな

$ 115792089237316195423570985008687907852837564279074904382605163141518161494336 $

となります．この値が上限となる理由は楕円曲線暗号だとかSecp256k1といったものが絡んでいます．気になる場合は調べると良いでしょう．

以上のことより，もう少し正確に秘密鍵を定義します．

**秘密鍵を$p \in \mathbb{N}$とすると**
$ 1 \leq p \leq 115792089237316195423570985008687907852837564279074904382605163141518161494336$

ちなみに，範囲外の値を用いるとウォレットに取り込む際に `Private key outside allowed range (code -5)` と怒られます．

## WIFってなに？
`Wallet Import Format` の頭字語

長くて扱いにくい生の秘密鍵(先ほどのただの整数)を変形して便利に表そう，というものです．名前の通りウォレットに取り込む際はこの形式を用います．コピーミスを減らすためチェックサムがついており，以下で示すように可逆的な変換です．

### 生の秘密鍵をWIFに変形してみる

<font color="Red">**2019年11月4日追記あり**</font>

この例では，この文を書いている今現在のUnix time`1572787251`を使います．

##### ①16進数に変換
`1572787251`を16進数に変換します．`5dbed433`$_{(16)}$ となります．
秘密鍵は256bitである必要があるので適切に0埋めします(16進数なので合計64文字)．

```
000000000000000000000000000000000000000000000000000000005dbed433
```

##### ②バージョンバイトを先頭に付ける
バージョンバイトとは？: この秘密鍵がメインネットで使われるのか、テストネットで使われるのかを判別するもの

メインネットなら `0x80`，テストネットなら `0xEF` を追加します．分かりやすい[表](https://en.bitcoin.it/wiki/List_of_address_prefixes)があります．

```
80000000000000000000000000000000000000000000000000000000005dbed433
```

なお，ビットコイン以外のオルトコインはバージョンバイトの値が異なる場合があります．

##### ③コンプレッションバイトを後ろに付ける(オプション)
コンプレッションバイトとは？: 圧縮公開鍵を使いたければ後ろに `0x01` を追加する

ここでは圧縮公開鍵についての説明はしません．
<font color="Red">***---追記はじめ---***</font>
>現状圧縮鍵(と言いつつbyte数は増えている())の方が主流になっているのと圧縮の意味ではなくなる可能性があります。

とTwitterで[教えていただきました](https://twitter.com/WakiyamaP/status/1191235979425677313?s=20&t=lP1JFBUuC3bOFAH15CJzGA)．現在アドレスのフォーマットには P2PK, P2PKH, P2SH, P2WPKH, P2WSH, P2SH-P2WPKH など複数あります．これらはすべて同じ秘密鍵から計算した公開鍵を使って生成できますが，WIFな秘密鍵を見ただけではどの種類のアドレスなのか判別できません．そこでsuffix(接尾辞)で判別できるようにしようという[提案](https://github.com/bitcoin/bitcoin/pull/12869)が為されているようです．分かりやすく[説明](https://techmedia-think.hatenablog.com/entry/2018/06/02/224152)されている方がいます．

つまり: 本来suffixには圧縮公開鍵か否かを示すものしかなかったが，それ以外の判別用suffixを追加しようとしているため`suffix=コンプレッションバイト`ではなくなる可能性がある
<font color="Red">***---追記おわり---***</font>

```
80000000000000000000000000000000000000000000000000000000005dbed43301
```
ここで `0x01` を付けると最終的なWIFが`K`か`L`で，付けなければ`5`で始まります．

##### ④チェックサム
チェックサムとは？: 調べるとたくさん出てきます

チェックサムは2回sha256にかけた値の先頭4byteを用い，それを値の後ろに追加します．

```shell:sha256にかける際のコマンド
echo -n '値' | xxd -r -p | sha256sum
```

sha256 1回目

```
a5ca0c4c17ff8b619920593d2eb4147399b21db61c0619b63e209c269dd0b913
```
2回目

```
df0fdd1a514374630c07177dac8bef7194b73529bf33ea9463f7abe862cabaf9
```
この先頭4byte(16進数なので8文字)を抜きます．`df0fdd1a`$_{(16)}$ これを元々の値の後ろに付けます．

```
80000000000000000000000000000000000000000000000000000000005dbed43301df0fdd1a
```

##### ⑤Base58 エンコードする
Base58とは？: Base64に似たもの．間違えの元になるいくつかの紛らわしい文字を使わないようにしている．可逆的な変換．

実験的に行うなら[このサイト](https://incoherency.co.uk/base58/)でBase58が使えます．
ところで，実際に使用することを想定して秘密鍵を作るならば**絶対にオンライン上に晒さずローカルな環境で行うべき**です．

これに関連したよく知られた事実: 安全に秘密鍵を作るならば，施錠可能な何もない部屋に紙と鉛筆と表裏の区別が付くコインのみを持ち込み，そのコインを256回振って秘密鍵を生成したのち手計算のみでアドレスの生成まで一貫して行うべきである．しかし現実的ではないためコンピュータを利用せざるをえない．

```
KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M93TaondtKu5Nh
```
これでWIFへの変換は完了です．ウォレットにインポートすると以下のアドレスが得られます(秘密鍵から公開鍵及びアドレスを生成する手順はより複雑です)．

```
15wDihorPChZK9huff49nf7EQoTJYxHNUc
```
このアドレスは使用可能です(が，もちろん使用するべきではありません)．