LinSoap

LinSoap

Null

使用JS通过WebRTC创建libp2p网络并相互连接(实践)

使用 JS 通过 WebRTC 创建 libp2p 网络并相互连接(理论)
在上一篇理论的文章中介绍了使用 JS 通过 WebRTC 创建 libp2p 网络并相互连接的原理,在这篇博客中会逐步介绍理论中的各种特性如何具体实践。

准备项目#

实践使用的是官方提供的 libp2p-webrtc-guide 项目,这个项目提供了一个 relay 节点项目,以及一个可以在浏览器中进行交互的 libp2p 网页项目,首先 git clone 这个项目并安装依赖

#git clone 项目
git clone https://github.com/libp2p/libp2p-webrtc-guide
#安装npm依赖
cd libp2p-webrtc-guide
npm install

启动 libp2p Relay 节点#

在介绍理论的文章中说到,libp2p Relay 节点在网络中有两个角色,一个是 Crituit Relay V2 角色,帮助网络中的其他节点发现对方,并转发流量。还有一个是 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: {
      // Allow private addresses for local testing
      denyDialMultiaddr: async () => false,
    },
    services: {
      identify: identify(),
      autoNat: autoNAT(),
      // 配置了Relay节点最重要的两个角色。
      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 节点的 PeerID 信息和 Multiaddrs 信息。运行 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()新建的身份自动生成的,如果不指定特定的 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 节点添加上 @libp2p/circuit-relay-v2-transport 支持。

   transports: [
      webSockets({
        // Allow all WebSocket connections inclusing 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
      // 添加@libp2p/circuit-relay-v2-transport支持
      circuitRelayTransport({
        discoverRelays: 1,
      }),
    ]

此时打开浏览器,可以发现浏览器已经可以成功创建 libp2p 节点,每次刷新都会产生不同的 PeerID。
根据理论文章,浏览器想要加入到 libp2p 网络,需要借助 Relay 节点,那么现在浏览器要做的第一步,就是先连接 Relay 节点,在打开的浏览器页面中,输入任意一个 Relay 节点的 multiaddr 地址(注意,需要是支持 WebSocket 的地址,根据理论文章,浏览器和 Relay 节点是通过 WebSocket 建立连接的),点击 connect,成功连接后即可看到连接信息。

浏览器连接到 Relay 节点

观察浏览器中的信息,可以发现该浏览器已经成功用 WebSockets 连接了一个节点,并且给出了该浏览器的 multiaddr。观察该 multiaddr,可以发现由两部分构成,前半部分为 Relay 节点的 multiaddr,后半部分为 /p2p-circuit/p2p / 本节点 PeerID。
在完成一个浏览器连接到 Relay 服务器后,可以尝试使用一个新的浏览器节点与之连接,构成一个 2 个浏览器,一个 relay 节点组成的 libp2p 网络。
新建一个浏览器,此时会产生一个与之前浏览器不同的 PeerID,在输入框中输入原浏览器的 multiaddr,点击 connect,此时会发现新浏览器不仅能够直接链接旧浏览器,并且也成功连接上了 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 添加 listen 地址

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

完成添加后,打开新的浏览器并连接到 Relay 节点后,会发现该浏览器的 multiaddr 数量变成了原来的两倍,观察多出的 multiaddr,可以发现新地址比原地址多添加了一个 /webrtc,这些地址在原先的基础上添加了对 WebRTC 的支持。
同样打开新的浏览器,尝试支持 WebSockets 和 WebRTC 的地址去连接旧浏览器,完成连接后可以发现 WebRTC 的连接数变为 1,至此两个浏览器之间已经成功建立 WebRTC 连接。

WebRTC 连接

配置 PubSub peer discovery#

在理论文章中,说明了 libp2p Relay 节点有两个作用,一个是 Circuit Relay V2,解决节点之间连接可行性问题。一个是 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 网络成功建立。

image

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。