8章_ピアツーピアネットワーク
担当: 淀川
概要
今まではブロックを共有フォルダを使用していた。
共有フォルダは管理者がいるため、検閲の可能性が残っている。
マイナーを含むフルノードをピアツーピアネットワークで接続することで共有フォルダという中央集権の仕組みから脱却することができる。
ウォレットもマイナーにメールを送ることなく(マイナーの完全なリストを所有することなく)、ピアツーピアネットワークを使うことですべてのマイナーにトランザクションを送ることができる。
共有フォルダをピアツーピアネットワークに置き換えることでクッキートークンを取り巻く仕組みはすべてビットコインのそれと等価となるため、これ以降はクッキートークンの例え話は出てこなくなる。
8.1 共有フォルダー
共有フォルダーは
1. 管理者による検閲が可能
2. ブロックの数やサイズが大きくなってくるとノードとのやりとりが重くなっていく
-> 別の仕組みを導入しよう。
8.2 ピアツーピアネットワークを構築しよう
ピアツーピアはフルノード同士を直接つなぐ
すべてのノードが直接つながっているわけではない
フルノードは近くのいくつかのノード(=ピア)とつながっている
各ノードがピアに情報を転送することで情報はネットワーク内に伝わっていく
→ ゴシップネットワーク (gossip network)
あるノードがブロックの転送を止めても別のノードが共通のピアにブロックを送る
-→ 各ノードは独立して動いているため、ブロックがネットワーク全体に伝わることを止めるのは難しい = 検閲困難
あるノードにトランザクションを送ればすべてのノード(=すべてのマイナーを含む)に届く
→ ウォレットはマイナーのリストを持っておく必要がなく、信頼しているノードに接続できていればよい
8.3 ふたつのピアはどのようにして情報をやり取りするのか
前提
トムとリサが通信をする例を考えてみる
トムとリサはすでに互いをピアとして認識している(ピアを見つける仕組みは8.6章)
通信はTCPを用いる
トムのノードはリサのノードのIPアドレスとポート番号(192.0.2.1:8333)を知っているとして、
トムのノードがリサのポートにリッスンをする(SYN)。
リサのノードは通信の許可を出し、(SYN/ACK)、トムのノードがそれに対して返信(ACK)、この後に実際のデータのやりとりが始まる。
(要は3wayハンドシェイクの話。この章必要ある?)
8.4 ネットワークプロトコル
トムとリサがデータをやりとりするための共通言語、プロトコルが必要。
クッキートークンではやりとりするためのプロトコルとしてメッセージの種類を定義している。
inv(inventory)メッセージはその1つ。
例:
自分が知らせたい情報を持っていることを示すinvメッセージで、
トムのノードはリサに2個のトランザクションと1個のブロックを持っていることを伝える。
invメッセージの中にはトランザクション、ブロックのハッシュが入っている。
(invメッセージを受け取った側のノードがどうするのかは以降で説明)
8.4.1 ジョンがトランザクションを送る
ジョンがウォレットでカフェからクッキーを買う(=クッキートークンをカフェに送る)例を見ていく。
ジョンのウォレットがトムのノードにクッキー代(トランザクション)を送る
ジョンのウォレットがトムのノードに、トランザクションのハッシュ値を含むinvメッセージを送る
トムのノードはそのトランザクションを持っていないので、ジョンのウォレットにgetdataメッセージを送ってトランザクションを要求する
要求を受けたウォレットはトランザクション本体を送る
もしトムのノードがすでにトランザクションを持っていた場合は、invメッセージを無視する
-> トランザクション本体を送る必要がなくなるのでネットワーク帯域の節約になる
8.4.2 トムがトランザクションを転送する
新しく手に入れたトランザクションの情報を、トムのノードがピアにinvメッセージで送る
ピアであるリサ、チー、ラシッドがinvメッセージを受け取り、8.4.1章と同じやりとりでトランザクションを受け取る
リサはあらたにピアであるトム、チー、カフェにinvメッセージを送る、かに見えるが、
トムはもともとリサにトランザクションを送ったノードであるので、ピアではあるがinvメッセージは送らない
チーとカフェがトランザクションを持っているかはわからないので、チーとカフェにはinvメッセージを送る
カフェはトランザクションを持っていないのでリサにgetdataメッセージを送る
チーはすでにトムからトランザクションを受け取っているので、リサからのinvメッセージを無視する(リサがtxを持っていることは覚えておく)
8.4.3 カフェの簡易ウォレットが通知を受け取る
カフェのフルノードが受け取ったトランザクションについてピアにinvを送る
トランザクションを持っているかどうか分からないノードにはinvを送る
カフェのウォレットにはトランザクションがブルームフィルタにマッチしていればinvメッセージを送る
カフェのオーナーは保留中(ブロックに含まれていない)のトランザクションについて2つの判断ができる
0承認トランザクションを信用する
信用せずトランザクションが承認されるのを待つ
今回の例では承認されるのを待つケースの説明となる。
8.4.4 ブロックにトランザクションを入れる
ピアツーピアネットワーク内に行き渡ったトランザクションはマイナーにも届く
マイナーはトランザクションをブロックに入れるかどうかを選択できる
ラシッドがプルーフ・オブ・ワークを見つけてトランザクションをブロックに入れる
ラシッドはピアにブロック情報をheadersメッセージで送る
まだブロックを持っていないピアはラシッドにgetdataメッセージを送る
invと同様に検証済みのブロックを持っているピアはheadersメッセージを無視する
getdataメッセージを受け取ったラシッドはピアにブロックを送る
ラシッドのピアは自分のピアにheadersメッセージを送る
もちろんラシッドには送らない
8.4.5 ウォレットに通知する
ジョン(トランザクションを最初に送った人)のウォレットにもheadersメッセージが届く
ジョンのウォレットはブロックを要求しない
ブロックの代わりに自分のトランザクションと関係のあるブロックかを確認するためmerkleblockメッセージを要求する
ブロックヘッダとmerkleblockを使って自分のトランザクションがブロックに含まれていることを検証する
検証後、「あなたのトランザクションは1回の承認を受けています」というメッセージを表示する
カフェにも同様の通知が送られる
これでジョンの「クッキートークンの送金」が完了する。
8.4.6 承認の追加
時間の経過とともに多くのブロックが作られていくので、その過程でジョンのトランザクションの承認回数が増えていく
-> 二重払いの犠牲になるリスクが下がっていく
8.5 クッキートークンシステムからの卒業
クッキートークンシステムを構成していたものはすべてビットコインの仕組みと同じになったため、クッキートークンの例えは不要となった。
8.5.1 ビットコインの現状
8.5.2 そもそも何の話でしたっけ?
省略
8.6 ネットワークのブートストラップ
ピアツーピアのネットワークに新しいノードを追加するには?
1. ノードプログラムをダウンロード、検証、起動
2. プログラムがほかのノードに接続
3. ノードがピアからブロックをダウンロード
4. ノードが通常の運用に入る
以下で細かく説明。
8.6.1 ステップ1 ―― ソフトウェアの実行
フルノード運用をするためのプログラムをダウンロードする。
Bitcoin Core, libbitcoin, bcoin, Bitcoinj, btcd などなど。
各プログラムはバージョンごとに検証用の署名があるため、この署名用の鍵を手に入れて検証する。
検証に問題がなければインストールし、プログラムを実行する(今回の例ではBitcoin Core)
8.6.2 ステップ2 ―― ノードへの接続
8.6.2.1 最初のピアの探し方
次に接続すべきノード(=ピア)を探す。ピアノ探し方は3種類紹介されており、
1. 知人のノードのIPアドレス + ポート番号を使う
2. DNSを使ってピアを探す
3. フルノードプログラムにハードコードされているアドレスを使う
2の場合、DNSはIPアドレスしか返してくれないので、ポート番号はデフォルト値である8333を使用することになる。
また接続するピアは最初から複数接続しておくほうが安全度が高い
8.6.2.2 ハンドシェーク
自分のノードと接続先のピアとで、最初にハンドシェークと呼ばれるやりとりが発生する。
ハンドシェークの内容は、
やりとりするプロトコルのバージョン
自分の持っているブロック高
自分のノードはもちろん最初のジェネシスブロックなのでblock高は0
ソフトウェアID
など。これらはversionメッセージで送信され、ピアはそれらに対してverackメッセージを返してくる。
これにさらにverackメッセージを返すとハンドシェークが完了し、ピアへの接続完了となる。
8.6.2.3 ピアのピアを見つける
追加されたノードはピアに他のピア情報を聞き出してピアを増やす。
ノードがgetaddrメッセージを送り、ピアは自分が持っているピアからIPアドレスとポート番号の組み合わせを返す。
8.6.3 ステップ3 ―― 同期
自分のノードがビットコインネットワークに接続したので、次に最新のブロックまでを含むブロックチェーン全体をダウンロードする。
自分のノードはピアにブロックヘッダを要求する(getheaders)と、ピアが2000個のブロックヘッダを返す。
自分のノードはピアが返してくるブロックヘッダが2000個未満になるまでブロックヘッダを要求する。
ブロックヘッダのリストが揃うと、ノードはピアにブロック本体を要求する(getdata)。
ブロック本体の要求は別々のピアに対して並列で行って効率化される。
既存のブロックをダウンロードしている間に新しいブロックが追加されると、接続されているピアからheadersメッセージが届くので、
通常のノードと同じく新しいブロックをピアに要求する。
こうして完全なブロックチェーンの同期が行われていく。
8.6.4 ステップ4 ―― 通常の運用
フルノードとしてネットワークに接続され、完全なブロックチェーンの同期も終わったので、自分のノードは通常通り運用されていく。
8.7章以降
省略