メインコンテンツへスキップ
Change page

ネットワークレイヤー

最終編集者: @HiroyukiNaito(opens in a new tab), 2024年7月3日

イーサリアムは、何千ものノードからなるピアツーピアネットワークですが、標準プロトコルを使用して、複数のノード間で直接的に相互に通信できる必要があります。 「ネットワークレイヤー」とは、これらのノードが互いを見つけて情報を交換可能とする為のプロトコルが集まったものです。 イーサリアムのネットワークレイヤーには、特定のノード間でのリクエストとレスポンスの交換(1対1の通信)だけでなく、ネットワーク上での情報の「ゴシップ」(1対多の通信)も含まれます。 各ノードは、正しい情報を送受信するために、特定のネットワークルールを遵守する必要があります。

クライアントソフトウェアには2つの部分(実行クライアントとコンセンサスクライアント)があり、それぞれ独自のネットワークスタックを備えています。 他のイーサリアムノードと通信するだけでなく、実行クライアントとコンセンサスクライアントは互いに通信する必要があります。 このページでは、初心者向けに、この通信を可能にするプロトコルを説明をします。

実行クライアントは、実行レイヤーのピアツーピアネットワーク上でトランザクションをゴシップします。 これには、認証されたピア同士の暗号化通信が必要です。 ブロックを提案するバリデータが選ばれると、そのノードのローカルトランザクションプールからトランザクションがローカルRPC接続を介してコンセンサスクライアントに渡され、ビーコンブロックにパッケージ化されます。 コンセンサスクライアントはその後、ピアツーピアネットワーク上でビーコンブロックをゴシップします。 これは2つの別々のピアツーピアネットワークを必要とします。1つはトランザクションゴシップのための実行クライアントを接続するもので、もう1つはブロックゴシップのためのコンセンサスクライアントを接続するものです。

前提知識

このページを理解する上で、あらかじめ、イーサリアム ノードとクライアント についてある程度理解を深めておくと良いでしょう。

実行レイヤー

実行レイヤーのネットワークプロトコルは、2つのスタックに分割されています:

  • ディスカバリースタック: UDP上に構築され、新しいノードがピアに接続できるようにする

  • DevP2Pスタック:TCP上に構築され、ノードが情報交換できるようにする

両スタックは、並列的に動作します。 ディスカバリースタックは新しいネットワーク参加者をネットワークに送り込み、DevP2Pスタックによって相互通信が可能になります。

ディスカバリー(Discovery)

ディスカバリーとは、ネットワークの他のノードを見つけるプロセスです。 これは、小規模なブートノード(クライアントをすぐに見つけピアに接続できるように、アドレスがクライアントに ハードコードされている(opens in a new tab)ノード)を使用してブートストラップされています。 これらのブートノードは、新しいノードをピアのセットに追加するためにのみ存在します。これが唯一の目的で、チェーンの同期などの通常のクライアントタスクには参加せず、クライアントが初回起動した時にのみ使用されます。

ノードとブートノードとのやり取りに使用されるプロトコルは、Kademlia(opens in a new tab)が修正された形式です。これは、分散ハッシュテーブル(opens in a new tab)を使用してノードのリストを共有します。 各ノードには、最も近いピアに接続するために必要な情報を含む、分散ハッシュテーブルのバージョンがあります。 この「近さ」とは地理的なものではありません。ここでの距離はノードのIDの類似性によって定義されるものです。 各ノードのテーブルは、セキュリティ機能として定期的に更新されます。 例えば、 Discv5(opens in a new tab)では、ディスカバリープロトコルのノードは、クライアントがサポートするサブプロトコルを表示する「広告」を送信することもでき、ピアは両者が通信に使用できるプロトコルについて取り決めることができます。

ディスカバリーは、PING-PONGから始まります。 ピンポンが成功すると、新しいノードはブートノードに「結合」されます。 ネットワークに入る新しいノードの存在をブートノードに通知する最初のメッセージは PING です。 このPINGには、新しいノード、ブートノード、および期限切れのタイムスタンプに関するハッシュ化された情報が含まれています。 ブートノードはPINGを受信し、 PING のハッシュを含むPONGを返します。 PINGPONGのハッシュが一致すると、新しいノードとブートノードの接続が確認され、「結合」されたと言うことになります。

一度結合されると、新しいノードはブートノードにFIND-NEIGHBOURSリクエストを送信できるようになります。 ブートノードから返されるデータには、新しいノードが接続できるピアのリストが含まれています。 ノードが結合されていない場合、FIND-NEIGHBOURSリクエストは失敗となるため、新しいノードはネットワークに入ることができません。

新しいノードは、ブートノードから近隣ノードのリストを受け取ると、それぞれのノードとPING-PONGを開始します。 PING-PONGが成功すると、新しいノードとその隣接ノードが結合され、メッセージの交換が可能になります。

1start client --> connect to bootnode --> bond to bootnode --> find neighbours --> bond to neighbours

実行クライアントは、現在Discv4(opens in a new tab)ディスカバリープロトコルを使っています。Discv5(opens in a new tab)プロトコルに移行するための積極的に取り組んでいます。

ENR: イーサリアムノードレコード(Ethereum Node Record)

イーサリアムノードレコード (ENR) とは、署名(合意された 認証スキームに従って作成されたレコード内容のハッシュ)、レコードへの変更を追跡するシーケンス番号、およびキーと値のペアの任意のリストという3つの基本要素を含むオブジェクトのことです。 これは、新しいピア間で識別情報の交換を容易にする、将来性のあるフォーマットで、イーサリアムノードのネットワークアドレスの優先フォーマットです。

ディスカバリーがUDPで構築されている理由

UDPはエラーチェック、失敗したパケットの再送、接続の動的な開閉をサポートしません。UDPは、受信に成功したかどうかにかかわらず、単にターゲットに対して連続的な情報ストリームを送信するだけです。 こうした最小限の機能により、オーバーヘッドも最小限に抑えられ、接続は非常に高速になります。 ノードが単に自分の存在を知らせ、相手との正式な接続を確立するためのディスカバリーにとっては、UDPで十分に要件を満たすことができます。 しかし、ディスカバリー以外の残りのネットワークスタックにとっては、UDPでは目的を満たすことはできません。 ノード間の情報交換は非常に複雑であるため、再送信やエラーチェックなどに対応できる、より高機能なプロトコルが必要です。 TCPに付随する追加のオーバーヘッドは、まさにこうした追加機能として必要な要件を満たしています。 したがって、P2Pスタックの大部分はTCPで動作することになります。

DevP2P

DevP2Pは、それ自体がピアツーピアネットワークの確立と維持するためにイーサリアムが実装しているプロトコルのスタックをすべてを包括しています。 新しいノードがネットワークに参加した後、その相互通信はDevP2P(opens in a new tab)スタックのプロトコルによって制御されます。 これらはすべてTCP上にあり、RLPxトランスポートプロトコル、ワイヤプロトコル、およびいくつかのサブプロトコルが含まれています。 RLPx(opens in a new tab)は、ノード間のセッションを開始、認証、維持するためのプロトコルです。 RLPxはデータをノード間で送信するための最小限の構造にエンコードする非常にスペース効率の良いRLP (再帰的な長さのプレフィックス)を使ってメッセージをエンコードします。

2つのノード間のRLPxセッションは、最初の暗号化ハンドシェイクで始まります。 このプロセスには、ノードがauthメッセージを送信し、ピアによって検証されることが含まれます。 検証に成功すると、ピアはauth-acknowledgementメッセージを生成し、イニシエーター・ノードに返します。 これは、ノードが非公開で安全に通信できるようにするための鍵交換プロセスです。 暗号化ハンドシェイクが成功すると、両ノードに「Hello」メッセージを互いに「ワイヤ上」で送信するようにトリガーします。 Helloメッセージの交換に成功すると、ワイヤプロトコルが開始されます。

Helloメッセージには以下が含まれます。

  • プロトコルバージョン
  • クライアントID
  • ポート
  • ノードID
  • サポートされるサブプロトコルのリスト

これらは両ノード間で相互作用を成功させるために、共有される機能を定義し、通信を構成するのに必要な情報です。 各ノードがサポートするサブプロトコルのリストを比較し、両ノードに共通するものをセッションで使用できるようにするサブプロトコルネゴシエーションというプロセスがあります。

ワイヤプロトコルは、Helloメッセージとともに、接続が終了することを相手に警告する「切断」メッセージも送信することができます。 ワイヤプロトコルは、セッションを開いたままにするために定期的に送信されるPINGとPONGメッセージも含んでいます。 したがって、RLPxとワイヤプロトコルの交換は、ノード間の通信の基礎を確立し、特定のサブプロトコルに従って交換される有用な情報のための土台を提供します。

サブプロトコル

ワイヤプロトコル

ピアが接続され、RLPxセッションが開始されると、ワイヤプロトコルはピアがどのように通信するかを定義します。 当初、ワイヤプロトコルは、チェーンの同期、ブロックの伝搬、トランザクションの交換という3つの主要なタスクを定義していました。 しかし、イーサリアムがプルーフ・オブ・ステーク(PoS)に移行すると、ブロック伝搬とチェーン同期はコンセンサスレイヤーの一部となりました。 トランザクションの交換は、依然として実行クライアントの範疇にあります。 トランザクション交換とは、ノード間で保留中のトランザクションを交換し、ブロックビルダーが次のブロックに含めるためにそれらの一部を選択できるようにすることを指します。 これらのタスクの詳細については、こちら(opens in a new tab)をご覧ください。 これらのサブプロトコルをサポートするクライアントは、JSON-RPCを介してそれらを公開します。

ライト・イーサリアム・サブプロトコル(les)

これは、ライトクライアントの同期用の最小限のプロトコルです。 フルノードはインセンティブなしにライトクライアントにデータを提供する必要があるため、従来このプロトコルはほとんど使用されてきませんでした。 実行クライアントのデフォルトの動作は、lesでライトクライアントのデータを提供しないようになっています。 詳細については、les 仕様(opens in a new tab)をご確認ください。

スナップ(Snap)

スナッププロトコル(opens in a new tab)は、ピアが最近の状態のスナップショットを交換できるようにするオプションの拡張機能で、ピアがマークルツリーの中間ノードをダウンロードせずにアカウントとストレージデータを検証できるようにするものです。

ウィットネスプロトコル(Wit)

ウィットネスプロトコル(opens in a new tab) は、ピア間で状態のウィットネスを交換できるようにするオプションの拡張機能で、クライアントをチェーンの先頭に同期させるのに役立ちます。

ウィスパー(Whisper)

ウィスパーは、ブロックチェーンに情報を書き込むことなくピア間で安全なメッセージングを提供することを目的としたプロトコルです。 DevP2Pワイヤープロトコルの一部でしたが、現在は非推奨となっています。 他にも同様の目的を持つ関連プロジェクト(opens in a new tab)があります。

コンセンサスレイヤー(consensus layer)

コンセンサスクライアントは、仕様が異なる別のピアツーピアネットワークに参加します。 コンセンサスクライアントは、ピアから新しいブロックを受け取り、自分がブロックを提案する番が来たらブロードキャストできるよう、ブロック・ゴシップに参加する必要があります。 実行レイヤーと同様に、ノードがピアを見つけてブロックや認証などを取引するための安全なセッションを確立できるよう、まずディスカバリー・プロトコルが必要です。

ディスカバリ

実行クライアントと同様に、コンセンサスクライアントもピアを見つけるためにUDP上の discv5(opens in a new tab) を使用します。 コンセンサスレイヤーのdiscv5の実装は、discv5をlibP2P(opens in a new tab)スタックに接続するアダプターを含んでおり、DevP2Pを非推奨としている点のみ、実行クライアントの実装と異なります。 実行レイヤーのRLPxセッションは廃止され、libP2Pのノイズセキュアチャネル・ハンドシェイクが採用されています。

ENR

コンセンサスノードのENRには、ノードの公開鍵、IPアドレス、UDPおよび TCP ポート、コンセンサス特有の2つのフィールド(認証サブネットビットフィールドとeth2)が含まれます。 前者は、ノードが特定の認証ゴシップ・サブネットワークに参加しているピアを見つけやすくします。 eth2キーには、ノードが使用しているイーサリアムフォークのバージョンに関する情報が含まれており、ピアが正しいイーサリアムに接続していることを確認できます。

libP2P

libP2Pスタックは、ディスカバリー後のすべての通信をサポートします。 クライアントは、ENRで定義されたIPv4および/またはIPv6でダイヤルおよびリッスンできます。 libP2Pレイヤーのプロトコルは、ゴシップとリクエスト/レスポンスのドメインに細分化されます。

ゴシップ(Gossip)

ゴシップドメインは、ネットワーク全体に直ぐに広まる必要のあるすべての情報を含みます。 これには、ビーコンブロック、証明、アテステーション、イグジット、スラッシングが含まれます。 これはlibP2Pゴシップサブ v1を使って送信され、受信・送信するゴシップペイロードの最大サイズなどの各ノードにローカルに保存されている様々なメタデータに依存します。 ゴシップドメインに関する詳細な情報は、こちら(opens in a new tab)をご覧ください。

リクエスト/レスポンス(Request-response)

リクエスト/レスポンス・ドメインには、クライアントがピアに特定の情報を要求するためのプロトコルが含まれます。 例えば、あるルートハッシュに一致する特定のビーコンブロックや、スロット範囲内のビーコンブロックのリクエストなどがあります。 レスポンスは常にsnappy(圧縮アルゴリズムの一つ)圧縮されたSSZエンコードバイトとして返されます。

コンセンサスクライアントでRLPよりSSZが好まれる理由

SSZは、シンプル・シリアライゼーションの略です。 SSZは、固定オフセットを使うことで、構造全体をデコードすることなく、エンコードされたメッセージの個々の部分を簡単にデコードすることができます。これは、エンコードされたメッセージから特定の情報を効率的に取得できるため、コンセンサスクライアントにとって非常に便利な機能です。 また、マークルプロトコルと統合するように特別に設計されており、マークル化に関連した効率化も得られます。 コンセンサスレイヤーのハッシュはすべてマークルルートであるため、これは大きな改善となります。 また、SSZは値の一意性も保証します。

実行クライアントとコンセンサスクライアントの接続

コンセンサスクライアントと実行クライアントは、並列に動作します。 コンセンサスクライアントが実行クライアントに指示を出し、実行クライアントがコンセンサスクライアントにトランザクション・バンドルを渡してビーコンブロックに含めることができるように、両者は接続されている必要があります。 両クライアント間の通信は、ローカルRPC接続を使用して実現することができます。 「エンジンAPI」(opens in a new tab) と呼ばれるAPIが、両クライアント間で送信される命令を定義します。 両クライアントは単一のネットワークIDの背後に位置するため、各クライアントの個別のキー(eth1キーとeth2キー)を含むENR(イーサリアムノードレコード)を共有します。

制御フローの概要を以下に示します。括弧内は関連するネットワークスタックです。

コンセンサスクライアントがブロック生成者でない場合:

  • コンセンサスクライアントがブロック・ゴシップ・プロトコル(コンセンサスp2p)を介してブロックを受信する
  • コンセンサスクライアントはブロックを事前に検証し、確正しいメタデータを持つ有効な送信者からのものであることを確実にする
  • ブロックのトランザクションが実行ペイロードとして実行レイヤーに送信される(ローカルRPC接続)
  • 実行レイヤーはトランザクションを実行し、ブロックヘッダーの状態を検証する(ハッシュ値の一致をチェックする)
  • 実行レイヤーは検証データをコンセンサスレイヤーに返し、ブロックは検証済みとみなされる(ローカルRPC接続)
  • コンセンサスレイヤーはブロックを自分のブロックチェーンの先頭に追加して証明し、そのアテステーション(証明)をネットワーク上にブロードキャストする(コンセンサスp2p)

コンセンサスクライアントがブロック生成者の場合:

  • コンセンサスクライアントが次のブロック生成者であることを通知される(consensus p2p)
  • コンセンサスレイヤーが実行クライアントのcreate blockメソッドを呼び出す(ローカルRPC)
  • 実行レイヤーは、トランザクション・ゴシップ・プロトコルによって生成されたトランザクション・メンプールにアクセスする(実行p2p)
  • 実行クライアントはトランザクションをブロックにまとめ、トランザクションを実行し、ブロックハッシュを生成する
  • コンセンサスクライアントは実行クライアントからトランザクションとブロックハッシュを取得し、ビーコンブロックに追加する(ローカルRPC)
  • コンセンサスクライアントは、ブロック・ゴシップ・プロトコルでブロックをブロードキャストする(コンセンサスp2p)
  • 他のクライアントが、ブロック・ゴシップ・プロトコルで提案されたブロックを受信し、上記のように検証する(コンセンサスp2p)

十分な数のバリデータによってブロックが認証されると、チェーンの先頭に追加され、正当性が確認された後、最終的に確定(ファイナライズ)される。

コンセンサスクライアントと実行クライアントのネットワークレイヤー概略図( ethresear.ch(opens in a new tab) より)

参考文献

DevP2P(opens in a new tab) LibP2p(opens in a new tab) コンセンサスレイヤーネットワークの仕様(opens in a new tab) カデムリアからdiscv5(opens in a new tab) カデムリアペーパー(opens in a new tab) Ethereumピアツーピア入門(opens in a new tab) eth1eth2の関係(opens in a new tab) マージとeth2クライアントの詳細に関するビデオ(opens in a new tab)

この記事は役に立ちましたか?