最近、OrbitDB という面白いデータベースプロジェクトを見つけました。分散型の公共アニメメタデータベースを作りたいと思っていますが、その分散化の実現方法は libp2p を使用しています。開発の過程で、libp2p に関連する内容が最も理解と設定が難しいですが、幸いにも公式が質の高い入門チュートリアルを更新してくれましたので、関連する知識をたくさん学ぶことができました。内容をまとめるためにブログ記事を書く価値があります。
WebRTC with js-libp2p
WebRTC を選ぶ理由#
WebRTC とは#
WebRTC は、Web Real-Time Communication の略で、ウェブブラウザでリアルタイムの音声やビデオの通話を行うための API です。WebRTC を使用すると、ブラウザ間で直接 P2P 接続を確立し、データの送受信ができます。現在の主要なブラウザはすべて WebRTC プロトコルをサポートしており、js-libp2p と組み合わせることで、ブラウザを libp2p ノードとして直接 libp2p ネットワークに参加させることができます。
JS-libp2p の接続性と能力#
libp2p にはさまざまな言語での実装がありますが、各実装の進捗状況と制約は異なります。公式ウェブサイトでは、各言語の実装の進捗状況と制約について提供しています。
libp2p の実装状況
ブラウザの制約により、ブラウザは直接 TCP や QUIC プロトコルを使用して通信することはできません。ブラウザで独立した libp2p ノードを作成するためには、現時点では WebSocket、WebTransport、WebRTC の 3 つのプロトコルから選択することができます。また、公式ウェブサイトでは、libp2p の各プロトコル間の接続性の結果と、なぜブラウザが TCP と QUIC プロトコルをサポートしていないのかについて説明しています。
libp2p の各プロトコル間の接続性
公式ウェブサイトの接続性の結果から、現時点では WebSocket と WebTransport はサーバーへの接続のみをサポートし、ブラウザへの接続はサポートしていません。現時点でのすべての実装の中で、js-libp2p と WebRTC を組み合わせることで、ブラウザを独立したノードとして扱うことができます。
WebRTC の制約と解決策#
P2P 接続を行う際に最も重要な問題は、2 つのブラウザがお互いを見つける方法であり、ノードが接続を行うための準備ができるようにすることです。そのために 2 つの解決策が提供されています。それぞれ STUN サーバーと TURN サーバーです。
- STUN サーバー:ネットワークには多くの NAT 構造が存在するため、ブラウザが相手を直接グローバル IP アドレスで見つけることは困難です。STUN サーバーは、ブラウザがお互いのノードを見つけ、相互に接続するためのサーバーです。多くの無料の公共 STUN サーバーが利用できます。
無料の STUN 公共サーバー - TURN(リレー)サーバー:ブラウザ間で直接 P2P 接続を確立することができない場合、すなわち STUN サービスによってノードがお互いを見つけたが接続できない場合、TURN サーバーを介して P2P 接続のトラフィックを転送することができます。そのため、TURN の使用コストが高く、良い選択肢ではありません。
ブラウザで P2P 接続を行う際には、相互にメタデータ情報や対話記述情報を送信する必要があります。これはシグナリング(Signaling)交換と呼ばれます。WebRTC の標準では、これらの内容は強制的に規定されていません。開発者は自分でシグナリング交換の方法を実装する必要があります。
libp2p では、これらの 2 つの問題を解決するための新しいアプローチが設計されています。libp2p リレーノードを使用して解決します。libp2p リレーノードは、ネットワーク内で 2 つの役割を果たします。
- Circuit Relay V2:ブラウザノードが直接 P2P 接続を確立できない場合、TURN サービスのコストが高すぎるため、Circuit Relay V2 はこの問題をうまく解決することができます。なぜなら、それは分散型であり、ネットワーク内のアクティブなノードがブラウザ間の相互発見を助け、P2P 接続ができない場合に必要なトラフィックを転送するからです。
- PubSub Peer Discovery:上記の内容はノード間の接続性の問題を解決しますが、PubSub Peer Discovery はノード間の自動的な相互接続の問題を解決します。GossipSub モードは PubSub Peer Discovery の実装方法の 1 つであり、ノードは特定のトピックを購読し、新しいノードが参加し、同じトピックを購読した場合、PubSub Peer Discovery はすべてのトピックを購読しているノードに新しいノードのアドレスをブロードキャストします。各ノードはアドレスを受け取った後、接続を試みます。ただし、大規模なネットワークでは、ブロードキャストは非常にリソースを消費する行為であり、適切に使用しないと信頼性を失う可能性があるため、このアプローチは本番環境では適していないかもしれません。
接続プロセス#
2 つのノードが P2P 接続を作成する方法、および libp2p リレーノードが接続中にどのような役割を果たすかについて、公式ドキュメントでは大まかなワークフローを説明しています。
このワークフローについて、大まかなアイデアは、2 つのノードが接続を確立するためには、まずリレーノードのアドレスを知る必要があります。そして、2 つのノードがリレーノードと WebSocket 接続を確立し、2 つのノードがリレーノードと安定した接続を確立し、GossipSub と同じトピックを購読した後、リレーノードが Circuit Relay V2 を介して同じトピックを購読しているネットワーク内のノードに相手のマルチアドレス(multiaddr)を通知します。マルチアドレスは以下のような形式です。
//ノードのマルチアドレス
//WebSocketポート9001を使用し、WebRTCをサポート
/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ/p2p-circuit/webrtc/p2p/12D3KooWR3oBwBzZxSTd5RTKGJC2WsCxqWumpBUP1WjsixjeMQ9s
//WebSocketポート9002を使用せず、WebRTCをサポート
/ip4/127.0.0.1/tcp/9002/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ/p2p-circuit/webrtc/p2p/12D3KooWR3oBwBzZxSTd5RTKGJC2WsCxqWumpBUP1WjsixjeMQ9s
//WebSocketポート9001を使用し、WebRTCをサポートしない
/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ/p2p-circuit/p2p/12D3KooWR3oBwBzZxSTd5RTKGJC2WsCxqWumpBUP1WjsixjeMQ9s
//WebSocketポート9002を使用せず、WebRTCをサポートしない
/ip4/127.0.0.1/tcp/9002/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ/p2p-circuit/p2p/12D3KooWR3oBwBzZxSTd5RTKGJC2WsCxqWumpBUP1WjsixjeMQ9s
最後に、2 つのノードはマルチアドレスを使用して WebRTC 接続を試み、P2P 接続を完了させます。
公式ドキュメントでは、より詳細な WebRTC 接続のワークフローも提供されています。
libp2p リレーノードを介してお互いを発見した後、WebRTC 接続を確立するための手順が始まります。まず、各ノードは STUN サーバーから自身のパブリック IP とポートを取得します。次に、接続を開始するノードは、STUN からの情報に基づいて RTCPeerConnection オブジェクト(WebRTC 接続の状態を管理するオブジェクト)を作成し、DataChannel と SDP(セッション記述プロトコル、対話情報を記述するためのもの)を作成して接続の準備をします。接続を開始する側は、libp2p で定義されたシグナリング(webrtc-singnaling)を作成し、libp2p リレーノードを介して SDP を接続先に送信します。接続先が SDP を受け取った後、同様に RTCPeerConnection と SDP を作成し、送信元に応答として返信します。送信元は SDP を受け取り、記録します。これにより、2 つのノードはハンドシェイクを行い、SDP 情報を交換します。その後、ICE(P2P 接続で最適な接続パスを見つけるためのもの)を使用して相手のノードを RTCPeerConnection に追加します。最後に、ノイズ(暗号化通信プロトコル)を使用して接続チャネルを暗号化し、接続を完了させます。
以上で、libp2p においてブラウザノード間の直接接続を WebRTC を使用してどのように解決するかについて大まかに理解することができました。ここで多くの用語が以前は理解していなかったため、矛盾や誤りが生じる可能性があるため、公式ドキュメントを参考にしてください。
WebRTC with js-libp2p
libp2p Connectivity
Peers - libp2p
SDP - MDN Web
mutiaddr - libp2p
WebRTC - MDN Web
Circuit Relay - libp2p
lib2p2 signaling-protocol -Github
js-libp2p-pubsub-peer-discovery -Github
以上が翻訳されたテキストです。