LinSoap

LinSoap

Null

Creating a libp2p network and connecting with each other using JS through WebRTC (Practice)

Creating a libp2p Network and Connecting with Each Other Using JS via WebRTC (Theory)
The previous theoretical article introduced the principles of creating a libp2p network and connecting with each other using JS via WebRTC. This blog will gradually introduce how various features in the theory can be practically implemented.

Preparing the Project#

The practice uses the official libp2p-webrtc-guide project, which provides a relay node project and a libp2p web project that can interact in the browser. First, git clone this project and install the dependencies.

#git clone the project
git clone https://github.com/libp2p/libp2p-webrtc-guide
#install npm dependencies
cd libp2p-webrtc-guide
npm install

Starting the libp2p Relay Node#

In the theoretical article, it was mentioned that the libp2p Relay node has two roles in the network: one is the Circuit Relay V2 role, which helps other nodes in the network discover each other and forward traffic. The other is the PubSub Peer Discovery role, which helps nodes connect automatically. This project provides a JS implementation of the Relay, and you can start the libp2p Relay node by entering the following command.

#start the libp2p relay node
npm run start:relay

The above command essentially runs the src/relay.js file in a node environment. Let's take a look at the relay.js file.

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: {
      // Allow private addresses for local testing
      denyDialMultiaddr: async () => false,
    },
    services: {
      identify: identify(),
      autoNat: autoNAT(),
      // Configured the two most important roles of the Relay node.
      relay: circuitRelayServer(),
      pubsub: gossipsub(),
    },
  })

  libp2p.services.pubsub.subscribe(PUBSUB_PEER_DISCOVERY)

  console.log('PeerID: ', libp2p.peerId.toString())
  console.log('Multiaddrs: ', libp2p.getMultiaddrs())
}

main()

The above code creates a libp2p node, configured to listen on ports 9001 and 9002 on all addresses, where 9001 uses the WebSocket protocol. It defines the encryption method as noise and defines yamux as the stream multiplexing method. The most important part is the configuration of the Relay node's key CircuitRelayServer and gossipsub information in the services. It also subscribes to the topic represented by the variable PUBSUB_PEER_DISCOVERY. Finally, it prints the PeerID information and Multiaddrs information of the Relay node. After running the Relay node, the console prints the following information.

> 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)
]

Each libp2p node has a unique PeerID, which is automatically generated by calling identity() in the Services configuration. If a specific identity is not specified, a different PeerID will be generated each time.

The console also prints 8 addresses, which are the multiaddrs of this libp2p node, corresponding to all the IPv4 addresses on the local machine, using WebSocket on port 9001 and TCP on port 9002.

Starting the Browser Node#

At this point, the libp2p Relay node has been successfully started. Next, you can start the browser node and try to connect to the libp2p Relay node. Open a new terminal and run the following command, then open the browser to create a new browser node.

npm run start

However, if you open the browser at this point, you will find that the PeerID of the node is Unknown. Opening the browser console will reveal the following error.

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

Based on the error message, it can be determined that @libp2p/webrtc requires support from @libp2p/circuit-relay-v2-transport. According to the previous theoretical article, libp2p needs to use Circuit Relay V2 to achieve WebRTC P2P connections, and the two are inseparable. Open the src/index.js file, where the browser's libp2p node information is configured. In the createLibp2p method's transports configuration, add support for @libp2p/circuit-relay-v2-transport to the libp2p node.

   transports: [
      webSockets({
        // Allow all WebSocket connections including without TLS
        filter: filters.all,
      }),
      webTransport(),
      webRTC({
        rtcConfiguration: {
          iceServers: [
            {
              // STUN servers help the browser discover its own public IPs
              urls: ['stun:stun.l.google.com:19302', 'stun:global.stun.twilio.com:3478'],
            },
          ],
        },
      }),
      // 👇 Required to create circuit relay reservations in order to hole punch browser-to-browser WebRTC connections
      // Add support for @libp2p/circuit-relay-v2-transport
      circuitRelayTransport({
        discoverRelays: 1,
      }),
    ]

At this point, when you open the browser, you will find that the browser can successfully create a libp2p node, and each refresh will generate a different PeerID.
According to the theoretical article, for the browser to join the libp2p network, it needs to rely on the Relay node. Therefore, the first step for the browser is to connect to the Relay node. In the open browser page, enter any multiaddr address of the Relay node (note that it needs to be a WebSocket-supported address; according to the theoretical article, the browser and Relay node establish a connection via WebSocket), click connect, and after successfully connecting, you will see the connection information.

Browser connected to Relay node

Observing the information in the browser, you can see that the browser has successfully connected to a node using WebSockets and provided the multiaddr of the browser. Observing this multiaddr, you can see that it consists of two parts: the first half is the multiaddr of the Relay node, and the second half is /p2p-circuit/p2p/ followed by the PeerID of this node.
After completing the connection of one browser to the Relay server, you can try to connect a new browser node to it, forming a libp2p network consisting of two browsers and one relay node.
Open a new browser, which will generate a different PeerID from the previous browser. Enter the multiaddr of the original browser in the input box and click connect. At this point, you will find that the new browser can not only directly link to the old browser but also successfully connect to the Relay node, showing that the Circuit Relay connection count is 1. If you close the new browser at this point, the old browser will quickly show that it has disconnected from the new browser.

Two browsers connected
At this point, the connection between the browsers has been successfully established. However, every time you open the browser, you need to manually enter the address to connect to the Relay node. To solve this problem, you can configure the bootstrap in the peerDiscovery configuration of the createLibp2p method in src/index.js to the address of the Relay node.

    peerDiscovery: [
      bootstrap({
        list: ['WebSockets address of the Relay node'],
      }),

Then, when you open a new browser, you will find that the browser node will automatically connect to the Relay node.

Starting the WebRTC Connection#

The connection process diagram in the theoretical article explains that the browser node will first discover the browser node through Circuit Relay V2 and then begin to establish a standard WebRTC connection. In the above steps, we have successfully connected the browser using Circuit Relay V2. Next, we will start the WebRTC connection support.
In src/index.js, add the listen address to the addresses of the createLibp2p method.

    addresses: {
      listen: [
        // 👇 Listen for webRTC connection
        '/webrtc',
      ],
    }

After completing the addition, when you open a new browser and connect to the Relay node, you will find that the number of multiaddrs of this browser has doubled. Observing the additional multiaddr, you can see that the new address has added a /webrtc compared to the original address, indicating that WebRTC support has been added to these addresses.
Similarly, open a new browser and try to connect to the old browser using addresses that support both WebSockets and WebRTC. After completing the connection, you will find that the WebRTC connection count has become 1, indicating that a successful WebRTC connection has been established between the two browsers.

WebRTC connection

Configuring PubSub Peer Discovery#

The theoretical article explains that the libp2p Relay node has two functions: one is Circuit Relay V2, which solves the feasibility problem of connections between nodes, and the other is PubSub Peer Discovery, which solves the problem of automatic connections between nodes.
In this practice, GossipSub is used to achieve automatic connection between nodes by subscribing to the same topic and broadcasting to connected nodes.
In the src/index.js file, configure pubsubPeerDiscovery and gossipsub for the node.

    peerDiscovery: [
      bootstrap({
        list: ['/ip4/127.0.0.1/tcp/9001/ws/p2p/12D3KooWKsQgy75zYnHWqoMw4fgE9R7FmjzFDD8grnsjABuXtAoN'],
      }),
      // New configuration, subscribe to the topic
      pubsubPeerDiscovery({
        interval: 10_000,
        topics: [PUBSUB_PEER_DISCOVERY],
      }),
    ],
    services: {
      // New configuration, add Gossipsub service
      pubsub: gossipsub(),
      identify: identify(),
    },

Finally, create multiple browsers without any operation, and after a while, the browsers will automatically connect to each other. Thus, a basic usable libp2p network based on JS through WebRTC between browsers has been successfully established.

image

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.