JS を使用して WebRTC を介して libp2p ネットワークを作成し、相互接続する(理論)
前回の理論の記事では、JS を使用して WebRTC を介して libp2p ネットワークを作成し、相互接続する原理について紹介しました。このブログでは、理論のさまざまな特性がどのように具体的に実践されるかを段階的に紹介します。
プロジェクトの準備#
実践には、公式が提供する libp2p-webrtc-guide プロジェクトを使用します。このプロジェクトは、リレー ノード プロジェクトと、ブラウザで対話できる libp2p Web プロジェクトを提供します。まず、このプロジェクトを git clone し、依存関係をインストールします。
#git clone プロジェクト
git clone https://github.com/libp2p/libp2p-webrtc-guide
#npm依存関係をインストール
cd libp2p-webrtc-guide
npm install
libp2p Relay ノードを起動#
理論の記事で述べたように、libp2p Relay ノードはネットワーク内で 2 つの役割を持っています。1 つは Crituit Relay V2 の役割で、ネットワーク内の他のノードが互いに発見し、トラフィックを転送するのを助けます。もう 1 つは PubSub Peer Discovery の役割で、ノード間の自動接続を助けます。このプロジェクトでは、JS の Relay 実装が提供されており、以下のコマンドを入力して libp2p Relay ノードを起動します。
#libp2p relayノードを起動
npm run start:relay
上記のコマンドは、実際には node 環境で src/relay.js ファイルを実行しています。relay.js ファイルを観察します。
async function main() {
// enable('*')
const libp2p = await createLibp2p({
addresses: {
listen: [
'/ip4/0.0.0.0/tcp/9001/ws',
'/ip4/0.0.0.0/tcp/9002',
],
},
transports: [
webSockets(),
tcp(),
],
connectionEncryption: [noise()],
streamMuxers: [yamux()],
connectionGater: {
// ローカルテスト用にプライベートアドレスを許可
denyDialMultiaddr: async () => false,
},
services: {
identify: identify(),
autoNat: autoNAT(),
// Relayノードの最も重要な2つの役割を設定しました。
relay: circuitRelayServer(),
pubsub: gossipsub(),
},
})
libp2p.services.pubsub.subscribe(PUBSUB_PEER_DISCOVERY)
console.log('PeerID: ', libp2p.peerId.toString())
console.log('Multiaddrs: ', libp2p.getMultiaddrs())
}
main()
上記のコードは libp2p ノードを作成し、すべてのアドレスの 9001 ポートと 9002 ポートでリッスンするように設定しています。9001 は WebSocket プロトコルを使用し、暗号化方式は noise に設定され、yamux がストリームマルチプレクサとして定義されています。最も重要なのは、services で Relay ノードの重要な CircuitRelayServer と gossipsub 情報を設定したことです。また、PUBSUB_PEER_DISCOVERY という変数をサブスクライブしました。Relay ノードを実行すると、コンソールに以下の情報が表示されます。
> node src/relay.js
PeerID: 12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ
Multiaddrs: [
Multiaddr(/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/172.30.63.206/tcp/9001/ws/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/192.168.5.101/tcp/9001/ws/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/0.0.1.1/tcp/9001/ws/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/127.0.0.1/tcp/9002/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/172.30.63.206/tcp/9002/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/192.168.5.101/tcp/9002/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ),
Multiaddr(/ip4/0.0.1.1/tcp/9002/p2p/12D3KooWDpJEwVdPBrQf6ZcwRYPzynBU2PZNGTQs3X8uh6FpxRTZ)
]
各 libp2p ノードには一意のPeerIDがあり、この ID は Services 設定で identity () を呼び出して新しく作成されたアイデンティティによって自動生成されます。特定のアイデンティティを指定しない場合、毎回異なる PeerID が生成されます。
コンソールには 8 つのアドレスも表示されます。これは libp2p ノードの multiaddr であり、実際にはローカルのすべての ipv4 アドレスに対応し、9001 ポートで WebSocket を使用し、9002 ポートで TCP を使用します。
ブラウザノードを起動#
これで libp2p Relay ノードが正常に起動しました。次に、ブラウザノードを起動し、libp2p Relay ノードに接続を試みることができます。新しいターミナルを開き、以下のコマンドを実行し、ブラウザを開くことで新しいブラウザノードを作成できます。
npm run start
しかし、この時点でブラウザを開くと、ノードの PeerID が Unknown になっていることに気づくでしょう。ブラウザのコンソールを開くと、以下のエラーが表示されます。
CodeError: Service "@libp2p/webrtc" required capability "@libp2p/circuit-relay-v2-transport" but it was not provided by any component, you may need to add additional configuration when creating your node.
at checkServiceDependencies (components.ts:173:15)
at new Libp2pNode (libp2p.ts:193:5)
at createLibp2pNode (libp2p.ts:423:10)
at async createLibp2p (index.ts:167:16)
at async App (index.js:19:18)
(anonymous) @ index.js:121
Promise.catch (async)
(anonymous) @ index.js:120
(anonymous) @ index.js:122
エラーメッセージから、@libp2p/webrtc が @libp2p/circuit-relay-v2-transport のサポートを必要としていることがわかります。前回の理論の記事によると、libp2p が WebRTC の P2P 接続を実現するには Circuit Relay V2 が必要であり、両者は切り離せません。src/index.js ファイルを開くと、ブラウザの libp2p ノード情報が設定されており、createLibp2p メソッドの transports 設定に @libp2p/circuit-relay-v2-transport のサポートを追加します。
transports: [
webSockets({
// TLSなしを含むすべてのWebSocket接続を許可
filter: filters.all,
}),
webTransport(),
webRTC({
rtcConfiguration: {
iceServers: [
{
// STUNサーバーはブラウザが自分の公開IPを発見するのを助けます
urls: ['stun:stun.l.google.com:19302', 'stun:global.stun.twilio.com:3478'],
},
],
},
}),
// 👇 ブラウザ間のWebRTC接続をホールパンチするためにCircuit Relay予約を作成するために必要
// @libp2p/circuit-relay-v2-transportのサポートを追加
circuitRelayTransport({
discoverRelays: 1,
}),
]
この時点でブラウザを開くと、ブラウザが libp2p ノードを正常に作成できることがわかります。毎回リフレッシュするたびに異なる PeerID が生成されます。
理論の記事によると、ブラウザが libp2p ネットワークに参加するには Relay ノードを利用する必要があります。したがって、ブラウザが最初に行うべきことは、Relay ノードに接続することです。開いているブラウザページに、任意の Relay ノードの multiaddr アドレスを入力します(注意:WebSocket をサポートするアドレスである必要があります。理論の記事によると、ブラウザと Relay ノードは WebSocket を介して接続を確立します)。接続をクリックすると、接続情報が表示されます。
ブラウザ内の情報を観察すると、このブラウザが WebSockets を介してノードに正常に接続されており、ブラウザの multiaddr が表示されていることがわかります。この multiaddr は 2 つの部分で構成されており、前半部分は Relay ノードの multiaddr、後半部分は /p2p-circuit/p2p / 本ノードの PeerID です。
ブラウザが Relay サーバーに接続した後、新しいブラウザノードを使用して接続を試みることができ、2 つのブラウザと 1 つの Relay ノードで構成される libp2p ネットワークを構成します。
新しいブラウザを作成すると、前のブラウザとは異なる PeerID が生成されます。入力ボックスに元のブラウザの multiaddr を入力し、接続をクリックすると、新しいブラウザが古いブラウザに直接接続できるだけでなく、Relay ノードにも正常に接続されることがわかります。そして、Circuit Relay 接続数が 1 であることが表示されます。この時点で新しいブラウザを閉じると、古いブラウザもすぐに新しいブラウザとの接続が切れたことが表示されます。
これでブラウザの接続が正常に実現されましたが、ブラウザを開くたびに手動でアドレスを入力して Relay ノードに接続する必要があります。この問題を解決するために、src/index.js の createLibp2p メソッドの peerDiscovery 設定に bootstrap を追加し、Relay ノードのアドレスを設定します。
peerDiscovery: [
bootstrap({
list: ['RelayノードのWebSocketsアドレス'],
}),
新しいブラウザを開くと、ブラウザノードが自動的に Relay ノードに接続されることがわかります。
WebRTC 接続を起動#
理論記事の接続プロセス図では、ブラウザノードが最初に Circuit Relay V2 を介してブラウザノードを発見し、その後標準の WebRTC 接続を確立することが説明されています。これまでのステップでは、Circuit Relay V2 を介してブラウザを接続することに成功しました。次に、WebRTC 接続のサポートを起動します。
src/index.js で、createLibp2p メソッドの addresses にリッスンアドレスを追加します。
addresses: {
listen: [
// 👇 WebRTC接続のためにリッスン
'/webrtc',
],
}
追加が完了したら、新しいブラウザを開いて Relay ノードに接続すると、そのブラウザの multiaddr の数が元の 2 倍になっていることがわかります。追加された multiaddr を観察すると、新しいアドレスは元のアドレスに /webrtc が追加されています。これらのアドレスは、元の基盤に WebRTC のサポートが追加されたものです。
同様に、新しいブラウザを開き、WebSockets と WebRTC の両方をサポートするアドレスを使用して古いブラウザに接続すると、接続が完了した後に WebRTC の接続数が 1 に変わります。これで 2 つのブラウザ間で WebRTC 接続が正常に確立されました。
PubSub ピアディスカバリーの設定#
理論記事では、libp2p Relay ノードには 2 つの役割があることが説明されています。1 つは Circuit Relay V2 で、ノード間の接続の実現可能性の問題を解決します。もう 1 つは PubSub Peer Discovery で、ノード間の自動接続の問題を解決します。
この実践では、GossipSub を使用して、同じトピックをサブスクライブし、接続されたノードにブロードキャストすることで、ノード間の自動接続の問題を実現します。
src/index.js ファイルで、ノードに pubsubPeerDiscovery と gossipsub を設定します。
peerDiscovery: [
bootstrap({
list: ['/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooWKsQgy75zYnHWqoMw4fgE9R7FmjzFDD8grnsjABuXtAoN'],
}),
// 新しい設定、トピックをサブスクライブ
pubsubPeerDiscovery({
interval: 10_000,
topics: [PUBSUB_PEER_DISCOVERY],
}),
],
services: {
// 新しい設定、Gossipsubサービスを追加
pubsub: gossipsub(),
identify: identify(),
},
最後に、複数のブラウザを新しく作成し、何も操作せずにしばらく待つと、ブラウザが自動的に相互接続します。これで、JS を介して WebRTC を使用してブラウザ間に基本的に利用可能な libp2p ネットワークが正常に構築されました。