Bitcoin Coreを触りながらBitcoinについて理解する - その１(構築～アドレス生成まで)

#概要
Bitcoinをなんとなく知っているけど、なんとなくしか知らない人です。

今回、勉強のためにBitcoinフルノードの運用に取り組んでみたので備忘録として残しておきます。
各種参考にさせていただいた記事のうち代表的なものを随所にリンク張っておきます。
`Bitcoin Core(v0.19.0.1)`を前提とした内容であることを念頭に置いてください。
あと、勉強しながら平行して記事を書き進めているので、まだ理解が進んでいないポイントは盛大に間違っている可能性があります。

##環境
今回使用するマシンのスペック

 - CPU:8 core
 - memory:16GB
 - HDD:1TB

な仮想マシンをVirtualBoxで作成。
OSは`CentOS 7`です。

#1.Bitcoin Coreインストール

```bash:OS環境
$ cat /etc/redhat-release 
CentOS Linux release 7.7.1908 (Core)
```

CentOSの初期設定が終わった状態からスタート。

```bash:事前準備
#依存パッケージインストール
yum install -y yum-utils

#拡張リポジトリ(Extra Packages for Enterprise Linux)追加
yum install -y epel-release 

#もろもろ追加(不要なものもあると思うが細かいことは気にしない)
yum install -y gcc gcc-c++ cpp libtool patch patchutils bzip2 zip unzip rsync wget ntp pciutils make autoconf automake libxslt openssl-devel zlib-devel curl-devel bind-utils ncurses ncurses-devel ncurses-static ncurses-term libevent libevent-devel libevent-doc
```

準備が整ったらrpmから`bitcoin-server`と`bitcoin-utils`をインストールする。

```bash:インストール
#リポジトリを設定
rpm -Uvh https://linux.ringingliberty.com/bitcoin/el7/x86_64/bitcoin-release-3-1.noarch.rpm
yum-config-manager --enable rhel-7-server-optional-rpms

# bitcoin coreインストール
yum -y install bitcoin-server bitcoin-utils
```

```bash:バージョン確認
#bitcoin coreのバージョン確認
$ bitcoind -version
Bitcoin Core version v0.19.0.1-g1bc9988993ee84bc814e5a7f33cc90f670a19f6a
Copyright (C) 2009-2019 The Bitcoin Core developers

#bitcoin-cliのバージョン確認
$ bitcoin-cli -version
Bitcoin Core RPC client version v0.19.0.1-g1bc9988993ee84bc814e5a7f33cc90f670a19f6a
```

#2.初期設定

ホームディレクトリ配下に`.bitcoin`ディレクトリを作成し、`bitcoin.conf`ファイルを配置する。

```bash:初期設定
$ mkdir ~/.bitcoin
```

```bash:~/.bitcoin/bitcoin.conf
### -- 同期するブロックチェーンを指定する -- ###
mainnet=1    ## 本番環境
# testnet=3  ## テストネット(3代目)で試験する場合
# regtest=1  ## オフラインでクローズドな試験をする場合

### -- 動作モード -- ###
server=1     ## JSON RPCサーバとして起動する

### -- RPCポート -- ###
### mainnetなら8332、testnetなら18332がデフォルト値
# rpcport=8332
# rpcport=18332

### -- 認証情報 -- ###
### localhostのみ許可するのであれば凝った設定は不要
rpcuser=user
rpcpassword=pass

### -- アクセス制御 -- ###
### RPCはlocalhostのみから受け付けるのが一般的だが
### 下記設定を追加することにより外部からRPC接続も可能。
rpcallowip=192.168.0.1/32

### -- トランザクション検索インデックスの有効化 -- ###
### トランザクション情報を参照したい場合は必要
txindex=1
```

>(参考)https://qiita.com/Yuto421/items/f790a6c136ee0d530135


設定項目は基本的には以上で完了。
メインネットに接続すると自動的にブロックの同期が始まるため初回はかなりの時間を要するので、もしもお試しや検証目的であればまずはテストネットを使用してみるとよいかと。


#3.起動(メインネット接続)

bitcoindを起動しメインネットへの接続を開始。

```bash:bitcoind起動
$ bitcoind -daemon
Bitcoin Core starting
```

デーモンを終了する場合は下記のコマンド

```bash:bitcoind停止
$ bitcoin-cli stop
Bitcoin Core stopping
```

もしうまく動作しないようなら下記の通りRPCまわりからチェックしてみるとよい。

##3.1 RPC稼働確認

```bash:rpcbindの状態を確認する
$ systemctl status rpcbind

● rpcbind.service - RPC bind service
   Loaded: loaded (/usr/lib/systemd/system/rpcbind.service; enabled; vendor preset: enabled)
   Active: active (running) since 火 2019-12-17 11:57:56 JST; 3 weeks 1 days ago
 Main PID: 1459 (rpcbind)
    Tasks: 1
   CGroup: /system.slice/rpcbind.service
           └─1459 /sbin/rpcbind -w

12月 17 11:57:52 BTC systemd[1]: Starting RPC bind service...
12月 17 11:57:56 BTC systemd[1]: Started RPC bind service.
```

```bash:rpcbindが自動起動設定になっているか確認する
$ systemctl list-unit-files -t service | grep "rpcbind"
rpcbind.service enabled 

$ systemctl list-unit-files | grep "rpcbind"
rpcbind.service enabled 
rpcbind.socket enabled 
rpcbind.target static 
```

```bash:rpcの起動確認
$ rpcinfo -p
program vers proto port service
100000 4 tcp 111 portmapper
100000 3 tcp 111 portmapper
100000 2 tcp 111 portmapper
100000 4 udp 111 portmapper
100000 3 udp 111 portmapper
100000 2 udp 111 portmapper
```

必要に応じてFWのポート開放も行う。

```bash:port開放
$sudo firewall-cmd --zone=manage --add-port=18832/tcp --permanent
$sudo firewall-cmd --zone=manage --add-port=18832/udp --permanent
$sudo firewall-cmd --reload
$sudo firewall-cmd --list-all-zones
```

##3.2 ネットワーク状況確認

bitcoinネットワークの接続状況を確認する。

```bash:TCPコネクション確認
$ netstat -pant | grep bitcoin

tcp 0 0 127.0.0.1:18332 0.0.0.0:* LISTEN 12531/bitcoind 
tcp 0 0 0.0.0.0:18333 0.0.0.0:* LISTEN 12531/bitcoind 
tcp 0 0 192.168.1.1:44054 xxx.xxx.xxx.xxx:18333 ESTABLISHED 12531/bitcoind 
<snip>
tcp6 0 0 ::1:18332 :::* LISTEN 12531/bitcoind 
tcp6 0 0 :::18333 :::* LISTEN 12531/bitcoind 
```

ローカルノードの内部情報を参照するには`bitcoin-cli`コマンドを使用する。

```bash:ネットワークコネクション数
$ bitcoin-cli getconnectioncount
10
```

```bash:ネットワーク接続状況
$ bitcoin-cli getnetworkinfo
{
  "version": 190001,
  "subversion": "/Satoshi:0.19.0.1/",
  "protocolversion": 70015,
  "localservices": "0000000000000409",
  "localservicesnames": [
    "NETWORK",
    "WITNESS",
    "NETWORK_LIMITED"
  ],
  "localrelay": true,
  "timeoffset": 0,
  "networkactive": true,
  "connections": 10,
  "networks": [
    {
      "name": "ipv4",
      "limited": false,
      "reachable": true,
      "proxy": "",
      "proxy_randomize_credentials": false
    },
    {
      "name": "ipv6",
      "limited": false,
      "reachable": true,
      "proxy": "",
      "proxy_randomize_credentials": false
    },
    {
      "name": "onion",
      "limited": true,
      "reachable": false,
      "proxy": "",
      "proxy_randomize_credentials": false
    }
  ],
  "relayfee": 0.00001000,
  "incrementalfee": 0.00001000,
  "localaddresses": [
  ],
  "warnings": ""
}
```

#3.3 ローカルノードの稼働状況確認

動作バージョンやブロックチェーン、ウォレット残高などの概況サマリーを確認する。

```python:サマリー情報
$ bitcoin-cli -getinfo
{
  "version": 190001,
  "protocolversion": 70015,
  "blocks": 611853, ## 最新ブロック高
  "timeoffset": 0,
  "connections": 10,
  "proxy": "",
  "difficulty": 13798783827516.42, ## 採掘難易度
  "chain": "main",
  "walletversion": 169900,
  "balance": 0.00000000,  ## wallet残高
  "keypoololdest": 1576991493,
  "keypoolsize": 1000,
  "unlocked_until": 0, ## wallet複合時の終了期限(暗号化済みの場合に項目表示)
  "paytxfee": 0.00000000,
  "relayfee": 0.00001000,
  "warnings": "" ## 警告メッセージ
}
```

#3.4 ブロックチェーン同期状況の確認

Bitcoinネットワークと接続されれば自動的にネットワーク上の最新のブロックチェーンとのローカル同期が始まります。
初回起動時は初期ブロック(genesisブロック)から最新ブロックまでの全ブロックを順次ダウンロード＆検証するのでだいたい20時間以上は見ておいたほうがいいです。

同期状況の進捗は`getblockchaininfo`というRPCコマンドにより確認可能。
`blocks`と`headers`が同値になれば同期完了ということらしい。

```python:ブロックチェーン同期状況
$ bitcoin-cli getblockchaininfo
{
  "chain": "main",
  "blocks": 615023,  ## ローカル同期が完了したブロック長
  "headers": 615023,  ## ネットワーク上の最新ブロック長
  "bestblockhash": "0000000000000000000597fb45aa1f4ca78081352cb2d26487ba7d019bd76a08",
  "difficulty": 15466098935554.65,
  "mediantime": 1580285702,  ## タイムスタンプ(中央値)
  "verificationprogress": 0.9999997039397914,
  "initialblockdownload": false,
  "chainwork": "00000000000000000000000000000000000000000c2d3de90e0990276f7b9f70",
  "size_on_disk": 295504268933,
  "pruned": false,
  "softforks": {
    "bip34": {
      "type": "buried",
      "active": true,
      "height": 227931
    },
    "bip66": {
      "type": "buried",
      "active": true,
      "height": 363725
    },
    "bip65": {
      "type": "buried",
      "active": true,
      "height": 388381
    },
    "csv": {
      "type": "buried",
      "active": true,
      "height": 419328
    },
    "segwit": {
      "type": "buried",
      "active": true,
      "height": 481824
    }
  },
  "warnings": ""
}
```

進捗状況の確認は`blocks`でもざっくりわかるが、より具体的に知りたい場合は、ブロックのタイムスタンプ`mediantime`を確認できる。タイムスタンプはUNIX時間で与えられるので`date`コマンドを用いれば読める。

```bash:
$ date --date @1580285702
2020年  1月 29日 水曜日 17:15:02 JST
```

`mediantime`は同期が完了した最終ブロックに含まれるタイムスタンプの一つなのだが、厳密にはブロックの生成日時ではなく、当該ブロックを含む過去11ブロックの生成日時の中央値を示しているらしい。

>https://bitcoin.stackexchange.com/questions/67618/difference-between-time-and-mediantime-in-getblock

通常1ブロックの生成間隔は凡そ10分なので`mediantime`はブロックの生成日時から概ね1時間程度過去の値として算出されることになる。Bitcoinネットワークのハッシュレートは割と変動するし、分岐も頻繁に発生しているので、そういった不確定要因を極力除外したいとの思想から平均値ではなく中央値を利用しているのかな？
多少余談になるが難易度調整アルゴリズムについては下記のレポートが勉強になった。

>[Bitcoin の難易度調整アルゴリズムの欠陥と、その解決策について](https://static1.squarespace.com/static/5c03f791e749407cc26158fc/t/5d1427f5205fd600014e71f5/1561602042416/A-Lucas-Critique-to-the-Difficulty-Adjustment-Algorithm-of-the-Bitcoin-System-ja.pdf)

###同期完了後のディスク使用量
実際にサーバのディスク容量をどの程度使うのかは気になるところ。
`.bitcoin`ディレクトリのディスク使用量を確認すると2020年1月執筆時点で`300GB`を超えており、そのうち90%以上がブロックチェーン本体のバイナリデータで占められている模様だった。

```hash:ディスク使用量
$ du -h
93M     ./blocks/index
274G    ./blocks
3.7G    ./chainstate
24G     ./indexes/txindex
24G     ./indexes
60K     ./database
301G    .
```


#4.ブロックやトランザクションの中身を調べてみる

同期が完了したので、とりあえず過去のブロックやトランザクションがどうなっているか覗いてみた。
ここでは他サイトで紹介されていた方法を真似しているが他にもやり方があるかもしれない。

#4.1 ブロックの中身を確認する
参照したいブロックを指定するには各ブロックに一意なブロックハッシュ値`blockhash`を指定する必要がある。ハッシュ値が分からなければ(普通はそんなもの覚えているわけがない・・・)、ブロックチェーン上の位置`height`を指定すれば、そのブロックに対応するブロックハッシュ値を返してくれるRPCコマンドが別に用意されているので、それを併用するようだ。具体的には
　`getblockhash <height>`：heightに対応するブロックのブロックハッシュ値を特定する
　`getblock <blockhash>`：ブロックハッシュ値に対応するブロック情報を表示する
という2段階を経ることになる。

例として100001番目に位置するブロックを取得した実行結果を示す。
（heightは0始まりであることに注意/ genesis=0)

```bash:ブロック情報出力　実行例
# height=100000のブロックハッシュを特定する
$ bitcoin-cli getblockhash 100000
000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506

# 上記ブロックハッシュに対応するブロックの中身を表示する
$ bitcoin-cli getblock 000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506
{
  "hash": "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506",
  "confirmations": 511995,
  "strippedsize": 957,
  "size": 957,
  "weight": 3828,
  "height": 100000, ## ブロックHeight
  "version": 1,
  "versionHex": "00000001",
  "merkleroot": "f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766",
  "tx": [
    "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",
    "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4",
    "6359f0868171b1d194cbee1af2f16ea598ae8fad666d9b012c8ed2b79a236ec4",
    "e9a66845e05d5abc0ad04ec80f774a7e585c6e8db975962d069a522137b80c1d"
  ],
  "time": 1293623863,
  "mediantime": 1293622620,
  "nonce": 274148111,
  "bits": "1b04864c",
  "difficulty": 14484.1623612254,
  "chainwork": "0000000000000000000000000000000000000000000000000644cb7f5234089e",
  "nTx": 4,  ## トランザクション数
  "previousblockhash": "000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250",
  "nextblockhash": "00000000000080b66c911bd5ba14a74260057311eaeb1982802f7010f1a9f090"
}
```

これは2010年頃のブロックになるのでトランザクションは僅か4件しか記録されていない。こういった黎明期を維持してくれた先駆者に感謝の思いを馳せる。
一点補足すると、ここに出力されている情報のうち実際のブロックチェーン上のブロックには記載されていないものがある。分かりやすい例としては末尾の`nextblockhash`がそうだ。これは次のブロックのブロックハッシュを示しているが、ブロックが実際に生成(マイニング)された時点でまだ存在しない未来のブロックのハッシュ値が確定できるはずもない。すなわちこれはビットコインソフトウェアがブロックチェーンを解析して独自に付与しているということを意味している。


### TIPS

`getblockhash`から`getblock`を実行するコンボはこれからもよく使いそうなのでシェルスクリプト化しておくと便利

```bash:getblk.sh
#!/bin/bash
### heightを引数に指定しブロック情報を出力する
blockhash=`bitcoin-cli getblockhash $1`
bitcoin-cli getblock $blockhash
```


その他、最新ブロックの情報を取得するコマンド

```bash:
## 最新ブロックの位置を特定する
$ bitcoin-cli getblockcount
615022

## (または)最新ブロックのブロックハッシュを特定する
$ bitcoin-cli getbestblockhash
0000000000000000000074bc934c66bccb7e1963d1376e46613f715b13077045
```

##4-2.トランザクションの中身を確認する

次にトランザクションの中身を覗いてみる。
先ほどブロック情報を表示した際に`tx`セクションが含まれていたが、この文字列がそれぞれのトランザクションを特定するトランザクションID`txid`なるものらしい。

```bash:(再掲)
  "tx": [
    "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",
    "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4",
    "6359f0868171b1d194cbee1af2f16ea598ae8fad666d9b012c8ed2b79a236ec4",
    "e9a66845e05d5abc0ad04ec80f774a7e585c6e8db975962d069a522137b80c1d"
  ],
```

トランザクションの中身を表示したい場合は`txid`でインデックスを検索して`rawデータ`を取得し、それをデコードするという手順となる。具体的には下記のRPCコマンドを用いる。
　`getrawtransaction <txid>`：txidからrawデータを取得する
　`decoderawtransaction <raw>`：rawデータをデコードする



```bash:トランザクション情報出力　実行例

## txidからrawデータを取得する
$ bitcoin-cli getrawtransaction 8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87
01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020602ffffffff0100f2052a010000004341041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84ac00000000

## rawデータをデコードする
$ bitcoin-cli decoderawtransaction 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020602ffffffff0100f2052a010000004341041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84ac00000000
{
  "txid": "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",
  "hash": "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",
  "version": 1,
  "size": 135,
  "vsize": 135,
  "weight": 540,
  "locktime": 0,
  "vin": [
    {
      "coinbase": "044c86041b020602",
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 50.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84 OP_CHECKSIG",
        "hex": "41041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf84ac",
        "type": "pubkey"
      }
    }
  ]
}

```

`vin`がインプット情報(出金元UTXO)
`vout`がアウトプット情報(送金先UTXO)

この出力結果には`vin`セクションに`coinbase`という記述があるが、これはマイニングの報酬として新たに生み出されたコインであることを意味している。つまりこのトランザクションは『採掘報酬50BTCを採掘者に送付しますよ』という取引であることがわかる。

こういった情報はBlockchain Explorerと呼ばれるWEBサイトにも公開されているので併せて確認することで理解が深まると思う。
(https://www.blockchain.com/btc/tx/8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87)


### TIPS

トランザクションについてもブロックと同じく`getrawtransaction`から`decoderawtransaction `を実行するコンボは有用なのでシェルスクリプト化しておくとよい。

```bash:gettx.sh
#!/bin/bash
### トランザクションIDを引数に指定しトランザクション情報を出力する
txid=`bitcoin-cli getrawtransaction $1`
bitcoin-cli decoderawtransaction $txid
```


#5.ウォレットの管理

次はウォレットやアドレスの仕組みについて確認していきたい。この章あたりから、いよいよビットコインの真髄が見えてきそうだ。

ウォレットの実体は`.bitcoin`ディレクトリ直下にある`wallet.dat`というバイナリファイルである。`getwalletinfo`コマンドで内容の確認ができる。


```bash:ウォレット情報
$ bitcoin-cli getwalletinfo
{
  "walletname": "",
  "walletversion": 169900,
  "balance": 0.00000000, ## BTC残高
  "unconfirmed_balance": 0.00000000,
  "immature_balance": 0.00000000,
  "txcount": 0,
  "keypoololdest": 1576991493,
  "keypoolsize": 1000,
  "keypoolsize_hd_internal": 1000,
  "unlocked_until": 0, ## 暗号化のアンロック期限
  "paytxfee": 0.00000000,
  "hdseedid": "2d819a2d1265e61aeb1dfa80e53f1c0d07536ccb",
  "private_keys_enabled": true,
  "avoid_reuse": false,
  "scanning": false
}
```

初期状態では`unlocked_until`の項目が存在しない。これは暗号化walletを一時的にアンロックした場合にその期限を表示するためのフィールドである（0の場合は暗号化されていることを意味する）。walletを暗号化すれば項目が出現する。

##5-1. wallet暗号化/複合化

walletを暗号化するためには`encryptwallet`を用いる

```bash:wallet暗号化
$ bitcoin-cli encryptwallet <passphrase>
```

暗号化することでパスフレーズによってそのウォレットは保護され秘密鍵が秘匿される。このサーバに不正アクセスされたとしてもパスフレーズが漏洩しなければ勝手に送金されてしまうといった事態が避けられる。
複合化したい場合は制限時間を設けて時限的に複合化することができる。

```bash:wallet複合化(制限時間を120秒に指定する場合)
$ bitcoin-cli walletpassphrase <passphrase> 120
```

>(参考)https://bitcoin.clock-up.jp/contents/wallet/passphrase


##5-2. ビットコインアドレスの作成

ビットコインアドレスはBTCを受け取るための口座番号みたいなもの。
これはいくらでも好きなだけ(!)自分で生成することができる。
アドレス生成には`getnewaddress`コマンドを用いる。
またアドレスには識別子として任意のラベル`label`を付けることができる。これはあくまでローカルだけで通用するものであり、アドレスとラベルを紐付けることで人間が管理しやすくするためのものである。一つのラベルに複数のアドレスを紐付けることも可能である。

```bash:ビットコインアドレスの生成
## ラベル:hoge11に2つ、ラベル:hoge22に1つのアドレスをそれぞれ生成する
$ bitcoin-cli getnewaddress hoge11
3Lr2tGo21MLmR8De4Jw1dA8zhDdPy2wRbi

$ bitcoin-cli getnewaddress hoge11
35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss

$ bitcoin-cli getnewaddress hoge22
3Do3Uenc4WwZ5pgDBANhDfpZHbwN25hWFg
```

作成したアドレスは`getaddressbylabel`メソッドで確認できる。

```bash:ラベル毎に紐付けたアドレス一覧を確認
$ bitcoin-cli getaddressesbylabel hoge11
{
  "35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss": {
    "purpose": "receive"
  },
  "3Lr2tGo21MLmR8De4Jw1dA8zhDdPy2wRbi": {
    "purpose": "receive"
  }
}

$ bitcoin-cli getaddressesbylabel hoge22
{
  "3Do3Uenc4WwZ5pgDBANhDfpZHbwN25hWFg": {
    "purpose": "receive"
  }
}
```

ラベル一覧を表示する：`listlabels`

```bash:ラベル一覧の表示
$ bitcoin-cli listlabels
[
  "hoge11",
  "hoge22",
]
```

アドレスを生成するということは、それに対応する秘密鍵と公開鍵も同時に生成しそれらを自らのウォレットで保管し管理していくということに等しい。
ここらへんはかなり重要なポイントになってくるのでしっかり勉強しておく。

### 秘密鍵と公開鍵とビットコインアドレスの関係

ブロックチェーンとトランザクションを銀行に例えて

　- ブロックチェーン　＝　全口座の全ての入出金履歴
　- トランザクション　＝　１回の取引記録

というイメージであるとすると、３者(秘密鍵、公開鍵、ビットコインアドレス)の関係性は

　- `秘密鍵`　＝　暗証番号
　- `公開鍵`　＝　口座番号(入出金兼用)
　- `ビットコインアドレス`　＝　口座番号(入金専用)

という表現が適切だろうか。`公開鍵`と`ビットコインアドレス`はいずれも形を変えただけで本質は同一の口座を指し示す識別子として利用されるが以下の違いがある。`公開鍵`または`ビットコインアドレス`を指定すれば誰でも入金(その口座に送金)することはできるのに対し、出金(別の口座に送金)するためには`ビットコインアドレス`ではなく必ず`公開鍵`で指定する必要がある。
また出金する際には暗証番号として`秘密鍵`を用いたデジタル署名で取引書類にサインする必要がある。
>なお入金に`公開鍵`を指定する方法(`P2PK`=`Pay to Public Key`)はかなり初期こそ使われていたが、安全性などの理由から新たに`ビットコインアドレス`で指定する方法(`P2PKH`=`Pay to Public Key Hash`)が開発され、現在では`P2PK`を利用する動機はほとんど無い。ここらへんについては、別途`P2SH`や`Seqwit`などと併せて整理したい。


ビットコインソフトウェアがビットコインアドレスを生成する際、内部的には下記の処理が行われている。

　１．疑似乱数を用いて`秘密鍵(一般的に256bitの数値)`を生成する
　２．ECDSA(楕円曲線デジタル署名アルゴリズム)を用いて`秘密鍵`から`公開鍵`を求める
　３．ハッシュ関数を用いて`公開鍵`から`ビットコインアドレス`を生成する

　１から２、２から３の処理は不可逆的であり、アドレスから公開鍵を求めたり、公開鍵から秘密鍵を求めたりすることは数学的困難さを伴う。この特性を利用してビットコインを安全にやり取りする仕組みが作られている。


#### 秘密鍵(Private Key)

　秘密鍵はそれから生成される公開鍵ならびにビットコインアドレスの唯一の所有者であることを証明するもの。
　秘密鍵はそれ自体は乱数などから生成された任意の数値だが、ビットコインソフトウェアにおいてはそこにいくつかの属性情報(プレフィックス、チェックサムなど)を付加して以下のいずれかの形式で定義される。

　- `WIF形式`・・・・・先頭文字が`5`
　- `WIF圧縮形式`・・・先頭文字が`K`or`L`

　WIFというのは`Wallet Import Format`というビットコイン独自のフォーマット。`Base58`というビットコインのために定義された58種のキャラクタセットを用いて58進数の数値に変換することで、人間が誤読しにくいようにという配慮がされている。
　圧縮形式というのは後述する公開鍵が`非圧縮タイプ`であるか`圧縮タイプ`であるかによって確定的に決まる。

　試しに先ほど作成したアドレス`35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss`の元となった秘密鍵を確認してみる。秘密鍵を表示するには`dumpprivkey`コマンドを用いる。もちろん自らの保有する秘密鍵でなければ確認することはできない。

```bash:秘密鍵の表示
## walletを60秒間だけ複合化する(※秘密鍵にアクセスする際には事前に暗号化を解いておく必要があるため)
$ bitcoin-cli walletpassphrase <passphrase> 60

## アドレスに対応する秘密鍵を表示
$ bitcoin-cli dumpprivkey 35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss
KzUwAsGmHLKrpp9dK4SNDiu6DucckKq4uLThjx8grZfaLe6be5he
```

　`K`から始まっているため`WIF圧縮形式`であることがわかる。
　この記事をご覧になる方にはもはや釈迦に説法だと思うが、秘密鍵は暗証番号に相当するものなので、通常は絶対に秘匿しておかなければならないし、仮に紛失してしまうと、二度とその口座にアクセスすることは叶わないと思った方が良い。実際に十分な強度の256bit秘密鍵を用いている場合、その値がとりうる選択肢は2^256≒10^77となり、これは観測可能な宇宙に存在する全ての素粒子の数(10^80)より少しばかり少ないくらいの数となると言われている。文字通り、万が一にも見つけようと思って見つけられるような次元の話ではないのである。

#### 公開鍵(Public Key)

　公開鍵は秘密鍵(32byte)を楕円曲線デジタル署名アルゴリズム(ECDSA)により変換した2軸の座標(整数X,整数Y)として求められる。ECDSAにも各種方式があり、ビットコインでは米国国立標準技術研究所(NIST)が標準化したSecp256k1という曲線ならびにパラメータを使用している。ビットコインプロトコルにおいて公開鍵は下記の2種類の形式のいずれかを用いることとされている。

　- `非圧縮形式`・・・先頭文字が`0x04`、65byte長、(X,Y)を利用
　- `圧縮形式`・・・・先頭文字が`0x02`or`0x03`、33byte長、Xのみを利用

　座標(X,Y)は楕円曲線上にあるので、多項式を計算すればXからYは簡単に導出できる関係にある。よって(X,Y)の組を公開鍵として扱うことと、Xのみを公開鍵として扱うことは暗号強度的には等価であり違いはない。当初のビットコインソフトウェアは(X,Y)の組を公開鍵として扱っていたが、トランザクションの容量を削減する目的で、公開鍵の長さが約半分で済むXのみを公開鍵として扱う方式も仕様追加された。これらの経緯から、前者を`非圧縮形式`、後者を`圧縮形式`と呼んで区別している。

>(参考)[【Mastering Bitcoin】秘密鍵から楕円曲線暗号を使って公開鍵を生成する](http://landau.jp/blog/149/)
>(参考)[【Mastering Bitcoin】 ビットコインアドレスについて（圧縮形式など）](http://landau.jp/blog/275/)

　具体的に公開鍵を確認してみる。先ほどに引き続きアドレス`35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss`に対応する公開鍵を表示するため`getaddressinfo`コマンドを使用する。このコマンドを利用するとアドレスの情報が一覧表示されるためここではひとまず公開鍵の部分だけをピックアップする。

```bash:公開鍵の表示
$ bitcoin-cli getaddressinfo 35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss | grep "pubkey"
  "pubkey": "022d0c1a116d24cef338f53071edae8177901010085f93f170f1d577dd3f6f06d6",
    "pubkey": "022d0c1a116d24cef338f53071edae8177901010085f93f170f1d577dd3f6f06d6",
```

　先頭文字が`0x02`なので`圧縮形式`を使用していることがわかる。これは先ほど確認した秘密鍵も`WIF圧縮形式`となっていたことと連動している。 `圧縮形式`においてはプレフィックス1byte(ここでは`0x02`)の後に続く32byte(`2d0c1a116d24cef338f53071edae8177901010085f93f170f1d577dd3f6f06d6`)が公開鍵の片割れである座標値Xである。
なお上記実行例で公開鍵が2回表示されている理由についてはここでは割愛する(後述のSegwitラッピングアドレスに関連)。

　参考までに`非圧縮形式`も確認しておく。`非圧縮形式`がメインで使われていた最初期のトランザクションを出力してみたい。
　先ほど作成したシェルスクリプトを使用する。

```bash:2番目のブロックに含まれるトランザクションの内容を確認する
## 2番目(Height=1)のブロック情報を出力
$ ./getblk.sh 1
{
  "hash": "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048",
  "confirmations": 614991,
  "strippedsize": 215,
  "size": 215,
  "weight": 860,
  "height": 1,
  "version": 1,
  "versionHex": "00000001",
  "merkleroot": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
  "tx": [
    "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" ## トランザクションID
  ],
  "time": 1231469665,
  "mediantime": 1231469665,
  "nonce": 2573394689,
  "bits": "1d00ffff",
  "difficulty": 1,
  "chainwork": "0000000000000000000000000000000000000000000000000000000200020002",
  "nTx": 1,
  "previousblockhash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
  "nextblockhash": "000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"
}

## トランザクションIDからトランザクション情報を出力
$ ./gettx.sh 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
{
  "txid": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
  "hash": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
  "version": 1,
  "size": 134,
  "vsize": 134,
  "weight": 536,
  "locktime": 0,
  "vin": [
    {
      "coinbase": "04ffff001d0104",
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 50.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee OP_CHECKSIG",
        "hex": "410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac",
        "type": "pubkey"
      }
    }
  ]
}
```

これはgenesisブロックの次のブロックであり、まだまだ取引らしい取引はなく、coinbaseトランザクションだけが記録されている状況だ。このトランザクションの`vout`の中にある`scriptPubKey`に送金先の情報が含まれている。詳しくは別途、トランザクションスクリプトという項目で整理したいと思っているが、ここでは`asm`の項目に注目すると、`0x04`に続く長い64byte(`96b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee`)が見つかる。これが`非圧縮形式`で記述された公開鍵である。座標(X,Y)の両方が含まれているので1+32×2=65byteもの情報量になってしまうわけである。
トランザクション容量の圧縮はビットコインの処理性能に深く関わってくる課題なので、`圧縮形式`の採用もそうだが、後述する`ビットコインアドレス`や`Segwit`の発明へと繋がっていく。こういった歴史的背景を紐解きつつ、理解していくことは非常に重要なことだと思う。


#### ビットコインアドレス

　`ビットコインアドレス`は公開鍵をハッシュ演算して導出したものであり、その実体は`公開鍵ハッシュ`と呼ばれることも多い。前項からの文脈で表現するならば、公開鍵の代わりに『公開鍵をハッシュ化したもの』を`scriptPubKey`に指定しようぜ、という発想でできたものがであるから`公開鍵ハッシュ`という呼び方は非常にしっくりくる。そして`公開鍵ハッシュ`にプレフィックスやチェックサムなどの属性情報を付加して`Base58`エンコードしたものを便宜的に`ビットコインアドレス`と呼んでいるわけである。

　ハッシュ演算は具体的には下記の順序で行われる。

```none:公開鍵から公開鍵ハッシュを導出する過程(HASH160)
　　公開鍵 →　SHA256(256bit)　→　RIPEMD160(160bit)　→　公開鍵ハッシュ(160bit)
```

　このように2回ハッシュ演算を行うことを一般的に`ダブルハッシュ`と呼び、ビットコインでは`HASH160`と表記したりする。なぜ2回もハッシュ化する必要があるのかという疑問が沸くが、採用した理由についてはどうやらサトシ・ナカモトも言及していないようだが、いくつかの参考資料では[Length Extension Attack (長さ拡張攻撃)](https://en.wikipedia.org/wiki/Length_extension_attack)に対する対策ではないかとの推測が見られた。つまるところハッシュ関数が将来的に部分的に破られたときのリスクに備えて、二重化することで強度を高めておくという事前の予防策、と考えるのが妥当なところだろうか。

>(参考)[Hashing or encrypting twice to increase security?](https://crypto.stackexchange.com/questions/779/hashing-or-encrypting-twice-to-increase-security/884#884)

　`公開鍵ハッシュ`ができたら、プレフィックスとチェックサムを付加するのだが、チェックサムは`公開鍵ハッシュ`をさらに`SHA256`と`SHA256`で`ダブルハッシュ`する(これを`SHA256d`と呼ぶ)。

```none:公開鍵ハッシュからチェックサムを導出する過程(SHA256d)
　　｛プレフィックス＋公開鍵ハッシュ｝ →　SHA256(256bit)　→　SHA256(256bit)　→　先頭4byteをチェックサムとする
```

　とにかく随所でハッシュ関数が活躍しまくっている。最後にまとめてBase58エンコードにかけて完成。

```none:ビットコインアドレスの完成
　　｛プレフィックス(1byte)＋公開鍵ハッシュ(20byte)＋チェックサム(4byte)｝→　Base58エンコード(27～34文字)
```

　詳細については参考資料の方が100倍わかりやすいのでここでは概要を追いかける。

>(参考)[秘密鍵から公開鍵とビットコインアドレスを生成する方法](https://tomokazu-kozuma.com/a-method-of-generating-a-public-key-and-a-bitcoin-address-from-a-private-key/)

　最終的に`ビットコインアドレス`は`1`or`3`で始まる27～34文字の英数字として表記される。
　それでは実際に最初に作成したアドレス`35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss`の詳細情報を確認してみたい。
　`getaddressinfo`コマンドを使うと

```bash:アドレスの詳細情報
$ bitcoin-cli getaddressinfo 35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss
{
  "address": "35YzatN8GgtGFBpjT7HWkZHNgcKQ2FmHss",
  "scriptPubKey": "a9142a5c0ec9cefb797bdef84b3e57ee8561c40b450987",
  "ismine": true,
  "solvable": true,
  "desc": "sh(wpkh([2f0ff834/0'/0'/9']022d0c1a116d24cef338f53071edae8177901010085f93f170f1d577dd3f6f06d6))#6hhgrklp",
  "iswatchonly": false,
  "isscript": true,
  "iswitness": false,
  "script": "witness_v0_keyhash",
  "hex": "00140797b322026a926da788b9396240556e08ecb217",
  "pubkey": "022d0c1a116d24cef338f53071edae8177901010085f93f170f1d577dd3f6f06d6",
  "embedded": {
    "isscript": false,
    "iswitness": true,
    "witness_version": 0,
    "witness_program": "0797b322026a926da788b9396240556e08ecb217",
    "pubkey": "022d0c1a116d24cef338f53071edae8177901010085f93f170f1d577dd3f6f06d6",
    "address": "bc1qq7tmxgszd2fxmfughyukysz4dcywevshqpjf7w",
    "scriptPubKey": "00140797b322026a926da788b9396240556e08ecb217"
  },
  "label": "hoge11",
  "ischange": false,
  "timestamp": 1576991492,
  "hdkeypath": "m/0'/0'/9'",
  "hdseedid": "2d819a2d1265e61aeb1dfa80e53f1c0d07536ccb",
  "hdmasterfingerprint": "2f0ff834",
  "labels": [
    {
      "name": "hoge11",
      "purpose": "receive"
    }
  ]
}
```

　ここに記述されている内容を完全に理解するにはまだ学習ステップが足りていないことがよくわかる。。

　とはいえ、こうして定義された`ビットコインアドレス`は送金先情報としてトランザクションの`scriptPubKey`に利用できる運びとなった(`P2PKH(Pay to Public Key Hash)`)。これには主に２つのメリットがあったとサトシ・ナカモトが言及している。

 　**①(受け取りの時点では)自身の公開鍵を開示する必要がなくなり、公開鍵暗号への攻撃耐性が向上した。**
　　　量子コンピュータでショアのアルゴリズムを用いると、多項式時間で公開鍵から秘密鍵の導出ができてしまうと言われており、公開鍵が公知になっている状態はそれ自体が将来的なリスク要因となり得る。送金先情報としてビットコインアドレスを用いると公開鍵は秘匿できる。また出金時は残高をお釣りアドレスに転送するような使い捨てタイプのウォレット実装も進んでいるので、これらを併用すれば、実質的に公開鍵暗号への攻撃を無力化できると言える。

 　**②文字数圧縮(256bit→160bit)により、可読性の向上であったり、QRコードへの搭載が簡易になった。**
　　　主に実用面への寄与も大きく、ビットコインの普及拡大に貢献したことは間違いないだろう。

>(参考)[P2PKH (Pay to Public Key Hash)](https://programmingblockchain.gitbook.io/programmingblockchain-japanese/other_types_of_ownership/p2pk-h-_pay_to_public_key_-hash)

　ところでここで一つの疑問が沸いてくる。見てきたように`ビットコインアドレス`の実体は`公開鍵ハッシュ`であり、これは言い換えれば公開鍵の情報量(`圧縮形式`で256bit)を160bitに弱体化させただけのものだ。先ほど秘密鍵は宇宙の素粒子の数だけあると書いたが、100bit近くも減らしたということは、逆に考えると、同じハッシュを導出する秘密鍵・公開鍵ペアの数が100bit近く増加したと考えることができる。本来、全く無関係なはずの秘密鍵・公開鍵ペアにも関わらずそれらは同一のビットコインアドレスへのアクセス権を有している。こういった衝突が起こってしまう確率はどのくらいあるのだろうか？
　検索してみる一応文献に辿り着いた(権利関係が微妙なのでリンクは割愛)。`誕生日のパラドックス`という一度は聞いたことがあるアレと同じ問題と捉えると、『**1.7×10^23**個のアドレスを生成した時点で、いずれかのアドレスが衝突する確率が1%を超える』という試算結果らしい。これは桁数だけ見ればまだ現実的な脅威と感じるかもしれないが、それでも大概大きな数値であり、そうそう起こりえないことは確かである。また、衝突するのが『自分のアドレス』に限定するなら、その確率は天文学的なものになるため、暗号学的には十分な強度があると言えると思う。
　いずれにせよこれに対する解決策は既に提示されており、`scriptPubKey`に公開鍵やアドレスを直接指定するのではなく、スクリプトを指定してしまおうという`P2SH(Pay to Script Hash)`というどこかの頭の良い人が考えた仕組みが既に実装されており、広く使われている。次章ではここらへんに踏み込んでいきたい。

というわけで、キリが良いのでここらで一度締めて投稿します。