ChainSafe / js-libp2p-gossipsub

TypeScript implementation of Gossipsub
Apache License 2.0
151 stars 43 forks source link

example doesn't work in the browser (insufficent peers) #452

Closed uriva closed 1 month ago

uriva commented 1 year ago
Uncaught (in promise) Error: PublishError.InsufficientPeers
    at GossipSub.publish (index.ts:2074:13)
import { webRTC, webRTCDirect } from "@libp2p/webrtc";

import { bootstrap } from "@libp2p/bootstrap";
import { circuitRelayTransport } from "libp2p/circuit-relay";
import { createLibp2p } from "libp2p";
import { gossipsub } from "@chainsafe/libp2p-gossipsub";
import { identifyService } from "libp2p/identify";
import { kadDHT } from "@libp2p/kad-dht";
import { mplex } from "@libp2p/mplex";
import { noise } from "@chainsafe/libp2p-noise";
import { webSockets } from "@libp2p/websockets";
import { webTransport } from "@libp2p/webtransport";
import { yamux } from "@chainsafe/libp2p-yamux";

export const start = async () => {
  // Create our libp2p node
  const libp2p = await createLibp2p({
    // transports allow us to dial peers that support certain types of addresses
    transports: [
      webSockets(),
      webTransport(),
      webRTC(),
      webRTCDirect(),
      circuitRelayTransport({
        // use content routing to find a circuit relay server we can reserve a
        // slot on
        discoverRelays: 1,
      }),
    ],
    connectionEncryption: [noise()],
    streamMuxers: [yamux(), mplex()],
    peerDiscovery: [
      bootstrap({
        list: [
          "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
          "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
          "/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp",
          "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
          "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
        ],
      }),
    ],
    services: {
      pubsub: gossipsub({}),
      identify: identifyService(),
      dht: kadDHT({ clientMode: true }),
    },
  });

  libp2p.addEventListener("peer:discovery", (evt) => {
    const peerInfo = evt.detail;
    console.log(`Found peer ${peerInfo.id.toString()}`);
    libp2p.dial(peerInfo.id).catch((err) => {
      console.log(`Could not dial ${peerInfo.id.toString()}`, err);
    });
  });

  libp2p.addEventListener("peer:connect", (evt) => {
    const peerId = evt.detail;
    console.log(`Connected to ${peerId.toString()}`);
  });

  libp2p.addEventListener("peer:disconnect", (evt) => {
    const peerId = evt.detail;
    console.log(`Disconnected from ${peerId.toString()}`);
  });

  console.log(`libp2p id is ${libp2p.peerId.toString()}`);

  libp2p.services.pubsub.addEventListener("message", (message) => {
    console.log(
      `${message.detail.topic}:`,
      new TextDecoder().decode(message.detail.data),
    );
  });
  libp2p.services.pubsub.subscribe("fruit");

  setInterval(() => {
    libp2p.services.pubsub.publish("fruit", new TextEncoder().encode("banana"));
  }, 2000);

  return libp2p;
};
x48115 commented 1 year ago

I'm also seeing this. A complete working end-to-end example with libp2p would be great

maschad commented 1 year ago

It seems that the browser nodes are not being subscribed to the topic, those bootstrap nodes are not subscribed to the fruit topic and so you would need to be running other - in this case browser - nodes which are subscribed to the fruit topic which would be discoverable by Kademlia (you could utilize the protocol prefix to ensure only your nodes are discovered).

You could take a look at the universal connectivity example example for more detailed usage of gossipsub for browser nodes.

jiakun9707 commented 1 year ago

I met the same problem. After debugging, I realized that the subscribe operation of node1 does not use rpc to add a topic in node2. This is because there is no link established between node1 and node2. So just add another node's information (directPeers) in the gossipsub's constructor parameter.

const createNode1 = async () => {
    const node = await createLibp2p({
        addresses: {
            listen: ['/ip4/127.0.0.1/tcp/0']
        },
        transports: [tcp()],
        streamMuxers: [yamux(), mplex()],
        connectionEncryption: [noise()],
        services: {
            // we add the Pubsub module we want
            pubsub: gossipsub({
                allowPublishToZeroPeers: true,
                awaitRpcMessageHandler: true,
            })
        }
    })
    return node
}
const node1 = await createNode1()

const createNode2 = async () => {
    const node = await createLibp2p({
        addresses: {
            listen: ['/ip4/127.0.0.1/tcp/0']
        },
        transports: [tcp()],
        streamMuxers: [yamux(), mplex()],
        connectionEncryption: [noise()],
        services: {
            // we add the Pubsub module we want
            pubsub: gossipsub({
                allowPublishToZeroPeers: true,
                directPeers: [{id: node1.peerId, addrs: node1.getMultiaddrs()}]
            })
        }
    })
    return node
}
const node2 = await createNode2()
wemeetagain commented 1 month ago

Example can be found here: https://github.com/libp2p/js-libp2p-examples/tree/main/examples/js-libp2p-example-pubsub