FiNANCiE Lab

ブロックチェーンで世界を変える、株式会社フィナンシェのテックチームブログです

BlockchainはP2Pでどうやって通信しているのか

BlockchainのP2Pな通信レイヤーについてご存知だろうか。それこそ、POWやPOSのようなコンセンサスアルゴリズムについては情報量が多く詳しい方が多いと思う。一方、P2Pな通信レイヤーは知らない方が多いのではないか。かく言う私も詳しくない。

そこで、BlockchainはどうやってP2P通信しているのかについて調査した。 - Author: tak

どうやって通信しているのか?

EthereumとIPFSはKademliaというネットワークプロトコルを使っている。(EthereumはKademliaの改良版)他にも、P2Pアプリケーションとして有名なBitTorrentもKademliaをベースにしたものを使っている。なお、Bitcoinは使っていない。理由は、Bitcoinは全てのNodeにトランザクションを送信したいから、らしい。

Kademliaとは

P2Pネットワークのために設計された分散ハッシュテーブルであり、Node間の通信において仮想的なネットワークを形成する。具体的には接続先のNodeを選んだり、Nodeのデータベースを管理する。

原文:Kademlia: A Peer-to-peer Information System Based on the XOR Metric

Kademiliaの詳細については、以下の記事が大変参考になる。

よりセキュアなS/Kademlia

Kademliaをセキュリティの観点で改良したものがS/Kademliaである。変更点としては主に2つ。

Proof of Work

1つ目が、NodeIDを作る時にProof of Workを使う点。これにより、シビルアタックに強くなる。 Blockchainの文脈でProof of WorkといえばBitcoinのNakamoto Consensusを指すことが多い。しかし、ここで言うProof of Workは本来の使われ方を意味する。本来のProof of Workは、スパムメールを防止するために、メールに一定量の計算をしたこと(労働)の証明を付与した。

NodeIDを作成するためには、コンピューターのリソースを消費するようなCrypto Puzzleを解く必要がある。これは、低コストで大量のIDを作れないことを意味し、不正なIDを大量に作ってネットワークを攻撃する、シビルアタックを経済的に実現しずらくしている。

f:id:re795h:20200108111453p:plain

Lookup over disjoint paths

2つ目が、Lookup over disjoint pathsと呼ばれるもの。簡単に言えば、NodeIDを聞かれた時(FIND_NODE)に、聞かれたNodeIDと(XORの距離が)近しいNodeIDも一緒に返却すること。

これはネットワークのLivenessを保つのに有効である。たとえば、聞かれたNodeIDが悪意あるNodeで接続に失敗したとする。この場合でも、返却されたNodeIDの中に正常に接続できるものがあれば、ネットワークのLivenessは維持される。

f:id:re795h:20200108110957p:plain

悪意あるNodeが増えても、返却するNodeID数を増やすことによって、接続成功率が高まる。

※この2つ以外にも、Route Tableの管理方法やSibling Listの数を改良しているが、詳しくは理解していない。以下原文を参照して欲しい。

S/Kademlia: A Practicable Approach Towards Secure Key-Based Routing

より下のレイヤーの通信

S/Kademliaより下の通信方法についてnoiseというプロジェクトを例に解説する。選んだ理由はドキュメントが整理されていてわかりやすいから。noiseはPerlinというSnowballというコンセンサスアルゴリズムを利用したDAG系のBlockchainの通信レイヤー部分に相当する。ちなみに、PerlinのCTOはKenta Iwasakiという日本人である。

P2Pの通信を確立するプロセスは大きく3段階に分かれている。

  1. 共通鍵の生成(楕円曲線ディフィー・ヘルマン鍵共有)
  2. メッセージ認証によってセキュアなセッション通信を確立(HMAC)
  3. S/KademliaによってNodeIDを交換しルーティングテーブルを更新

共通鍵の生成

前提知識となる楕円曲線ディフィー・ヘルマン鍵共有についてはwikiを参照。

AとBが通信を開始する場合を例に手順を解説する。

まず、互いにEd25519なキーペアを作成する。この鍵は一時的なものですぐに破棄される。そして相互にHandshake messageを送る。これには「公開鍵」と「規定のメッセージ(Noiseの場合は".noise_handshake"という文字列)の署名」が含む。

仮に、どちらか一方でも規定の時間内にHandshake messageのレスポンスを受信出来ない場合は、接続は失敗する。

受信した場合は、署名を検証し、正しければ互いに共有鍵を作成する。 Aの場合はAの秘密鍵とBの公開鍵からA鍵を生成し、Bの場合はBの秘密鍵とAの公開鍵からB鍵を生成する。A鍵とB鍵は楕円曲線上の点で、等しい。

参考: 4.1. Elliptic Curve Diffie-Hellman Handshake (ECDH)

メッセージ認証

前提知識としてHMACについてはMAC(メッセージ認証コード)とはを参照。

まず、前プロセスで生成した共通鍵をSHA256-basedのHKDFに渡して、認証鍵を作成する。次に乱数を生し、生成した乱数とACKを認証鍵で暗号化する。

そして、暗号化したメッセージを送信する。

レスポンスを受信したら、認証鍵で復号してメッセージの正当性を確認する。

参考: 4.2. Authenticated Encryption w/ Authenticated Data (AEAD)

S/Kademlia

まず断っておくと、S/Kademliaは仮想的な通信レイヤーである。実際の通信はメッセージ認証によってセキュリティを担保する。

はじめに、Nodeは自身のIDをProof of Workによって作成する。十分な計算力を費やされていない不正な形式のNodeID(たとえば、先頭の0ビット数が少ない)は、他のNodeから拒絶される。

Nodeを立ち上げると、規定のNodeリスト(IPFSの場合はBootstrap Peers Listと呼ぶ)へ接続しようとする。

接続に成功するとFIND_NODEを実行する。FIND_NODEはS/Kademliaが定めるプロトコルメッセージで、指定のNodeID(立ち上げ時は自身のNodeID)と距離の近しいNodeのリストを返却する。

返却結果をもとも、自身のRouting Tableを更新する。

参考: 4.3. S/Kademlia