Js-libp2p "peer and content routing" / kad-dht example

Yes, per “It would be acceptable or even desirable if the DHT I am searching consists exclusively of instances of my app.”, you want to not be connected to the public libp2p network. So, you should use your own bootstrap nodes and webrtc star server, so that your application peers only discover each other.

The DHT needs to have at least one entry in its routing table to start. This way, at least one bootstrap node should be running the DHT protocol. Once you connect to the bootstrap nodes, the identify protocol will kick in and peers will exchange the protocols they support. With that, a DHT protocol stream is created between both parties and they are now part of an overlay network. After this, if you have the random walk discovery enabled in the config, your node will periodically try to find other nodes via the DHT peer it already knows. Once new peers are discovered, the node might connect to them until the connection manager gets to its maximum threshold.

Yes! So, grabbing the above explanation, the flow is:

  • Connection established
  • Identify protocol starts and peers exchange their known protocols
  • Peer Protocols are stored in the PeerStore’s ProtoBook
  • Libp2p topologies (DHT, pubsub are some of the topologies we have) will verify if the new “discovered” protocols for a peer include their protocols. If they have, the topology onConnect handler is triggered.
  • The protocol implements the onConnect logic. While this is not mandatory to happen, both DHT and pubsub open streams on their protocol to these peers and they become part of the DHT/pubsub topology automatically.

You can use the libp2p topology to help with this. You can take a look on the DHT network. This does not solve the disconnect+poison.
So, with the latest comment you might need to explore making autoDial to be false and manually trigger dials (if you use your own network with own bootstraps and no connectivity to the public network this should not be necessary, right?). With this, when peers are discovered (you can listen on the discovery event), you can manually dial them if you don’t have any information for them in the ProtoBook (be aware that peer protocols might also change over time) you can dial them and listen for the events on protocols change. When the protocol event is triggered, if your protocol is not there, you can “poison” the peer (use the metadataBook within the peerStore for this). So, you can add logic in your decision to dial on discovery the metadataBook check for poison.

FYI in this discovery+connection flows, we will be working on improving the developer experience for this soon libp2p/js-libp2p#744. You can follow it and also provide your input according to your experience while working on this. We want to get rid of the autoDial and to enable libp2p to become more intelligent on the peers it should connect to. This should include essential peers like rendezvous servers, relays, protocol peers (according to the topology configuration) and distance.

Thank you again. So I am trying to follow these instructions…

After reading the js-libp2p-websockets README I made a very simple standalone script bounce.js:

// Based on peer-and-content-routing 1.js
/* eslint-disable no-console */
'use strict'

const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
//const WebRTCStar = require('libp2p-webrtc-star')
const Mplex = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const KadDHT = require('libp2p-kad-dht')

const delay = require('delay')

const createNode = async () => {
  const node = await Libp2p.create({
    addresses: {
      listen: ['/ip4/0.0.0.0/tcp/10001/ws']
    },
    modules: {
      transport: [Websockets],
      streamMuxer: [Mplex],
      connEncryption: [NOISE],
      dht: KadDHT
    },
    config: {
      dht: {
        enabled: true
      }
    }
  })

  await node.start()
  return node
}

let node1
;(async () => {
  node1 = await createNode()
  console.log("Self is", node1.peerId.toB58String())
})();

I then set my web app to have only /ip4/127.0.0.1/tcp/10001/ws in the bootstrap list. Though this web app works fine connecting to the public IPs in the libp2p-in-the-browser script, it does not manage to discover the bounce.js peer. ("/ip4/127.0.0.1/tcp/10001" also did not work). Probing I find port 10001 is open on localhost, so the problem does not seem to be at the bounce.js end. Am I missing something?

A couple related questions about webrtc-star…

  1. Reading the js-libp2p-webrtc-star README it’s not so obvious how to set oneself up as a WStar listener. They give the example of creating a WStar object manually with WStar({ wrtc: require('wrtc') }), but how do I feed that into Libp2p.create? Can I get Libp2p.create to create the WStar object for me given the wrtc object?

  2. In webrtc-star , if a direct connect fails, particularly due to NAT traversal or firewalls, WebRTC ICE uses a relay TURN server. In other words, ICE will first use STUN with UDP to directly connect peers and, if that fails, will fall back to a TURN relay server.

    Okay. So would it be correct to say that (for my ‘application’ p2p network) the utility of hosting a webrtc “bootstrap” node on the public internet (assuming I already have the websocket “bootstrap” node working) would be only to allow nodes that can connect via webrtc-star but not websockets to enter the network? Webrtc-star peers do not help with initiating connections to other webrtc-star peers (except by gossiping about their existence)? (If this is correct it differs from the impression the comment in the libp2p-in-the-browser example initially gave me.)

    Are nodes able to discover websocket nodes through webrtc-star peers and vice versa? That is, do nodes gossip about the multiaddrs of peers with different connection types?

As a piece of feedback— if every libp2p app effectively has to host its own bootstrap server, that does seem to reduce the usefulness of libp2p. Once I have gone to the bother of setting up a host to run the bootstrap node, for many applications it would have been about as simple to just set up a traditional server for hosting the data. (Setting up a traditional server might even be easier because it could potentially be done inside application hosts such as Apache.) On the other hand, if it were somehow possible for participants in an app to find each other through public gateways such as the bootstrap.libp2p․io servers, this would make libp2p much more useful. Is this something that the “rendezvous” spec (referenced in the issue 744 you link) could possibly help with? A related question— if I understand everything correctly, the bootstrap.libp2p․io and wrtc-star1…dwebops.pub nodes listed in the libp2p-in-the-browser examples could never be used as the basis of any app, because the only thing those nodes can definitely do is gossip about other peers to discover— it seems that there’s no guarantee that any node in that bootstrap list (or among the peers you discover from the bootstrap nodes) will ever support any specific “protocol”, not even the libp2p “official” protocols like kad-dht. What exactly is the purpose of hosting those bootstrap.libp2p․io peers, then? Do they exist only for the sample-code apps like libp2p-in-the-browser to work? Have I missed something?

Thanks for your patience with my many questions.

Two possibilities here:

  • Does your web app have the websockets transport in? If you can show me the config might help.
  • Are you running the web app on localhost as well?

I recommend that you use the debug logs to try to gather more information. You can do it in the browser console by:

  • localStorage.debug = ‘libp2p*’
  • and you can increase the specificity of it, maybe in this case see the transport level logs: localStorage.debug = ‘libp2p:websockets*’

If I understand your point correctly, webrtc-star signal server is not a bootstrap node. Bootstrap nodes are other libp2p nodes, while the signal server is a server that enables the network to overcome some challenges that otherwise it would not be able to at this moment.

webrtc-star comes with built in discovery, but what this means? The star server will keep connections with peers that listen for connections through it. This way, every time a new node “joins”, the star server will emit signals to the already connected nodes, to inform them of this new peer, which they can reach through this same server. So, no websocket discovery at this point. However, once they establish a connection and the identify protocol starts, both peers will exchange their protocols and multiaddrs. With this, a peer could get to know websockets addresses from the other peer, if it has them. For further connections, it might use this instead of the webrtc-star address.

The main advantages of using the webrtc-star are the built in discovery and NAT traversal support.

I am not sure if I entirely answered all your questions, feel free to redo them.

It does not need to. You can be connected to the public network instead, using the default bootstrap nodes.

Yes totally! I am implementing the rendezvous protocol in JS, which should land for the next release: https://github.com/libp2p/js-libp2p/issues/655

While there is no guarantee, in theory the bootstrap list should contain regular libp2p nodes with its protocols enabled. The purpose of the bootstrap nodes is not more that showing you a way into the network, which will enable you to discover and connect to other peers that are meaningful to you. We are heading towards a flow like:

  • node starts for the first time
  • node connects to its bootstrap nodes (this should include the public bootstrap nodes, rendezvous server nodes, relay nodes)
  • node will try to discover its closest peers on the network leveraging the connected nodes and connect to them
  • node will try to find its important nodes on the network via service discovery (https://github.com/libp2p/js-libp2p/issues/768), which will use rendezvous/content routing
  • node will try to find important pubsub peers (shared topic subscriptions)
  • node will connect to all the important peers discovered
  • node will disconnect from the bootstrap nodes once it already has “enough” peers for its subsystems
  • if a node stops and starts again, it will have all its discovered nodes persisted and it can try to connect back to them. It will only leverage the bootstrap nodes again if needed. The Peerstore stores peers’ protocols and metadata which will enable us to figure out what peers to connect.

This is the path we are going, but most of this is still being worked on. The next 2 releases should probably support most of this. You can read a bit more about this on:

I think I understand what was confusing me before about webrtc-star, thanks. I will test more with setting up my bounce.js tonight.

Just to triple check…

This one step relies on future features, not currently supported features, correct? So with 2.9.2, either you must get lucky and find an “important” node through chance, or run app-specific bootstrap+signal nodes which will ensure that all nodes are “important”. Correct?

Yes. It depends on what features you use and how you use them. For example, using webrtc-star for a given dapp, will make all peers to discover and connect to each other out of the box. This way, if they use pubsub all the peers will be connected and everything will just work.
If you are talking about network querying with DHT for example, or not shared bootstrap nodes, than yes.
This is the case in JS. go-libp2p is more advanced by the way.

1 Like

So, I’m sorry, I tried both localStorage.debug = 'libp2p*' and localStorage.debug = 'libp2p:websockets*' and neither one did anything. I tried these lines both in my bundle.js right before require()ing the libp2p and calling libp2p.create(), and I tried them in the console before starting the libp2p node. They did nothing at all, there are no additional messages in the console. Am I missing something?

Here is my browser-bundle.ts.

// Adapted from index.js, libp2p-js "libp2p-in-the-browser" example, git tag v0.29.0

declare let require:any

//localStorage.debug = 'libp2p:websockets*'
localStorage.debug = 'libp2p*'

const Libp2p = require('libp2p')
const Websockets = require('libp2p-websockets')
const WebRTCStar = require('libp2p-webrtc-star')
const NOISE = require('libp2p-noise')
const Mplex = require('libp2p-mplex')
const Bootstrap = require('libp2p-bootstrap')
const KadDHT = require('libp2p-kad-dht')

// Starting peers
const bootstrapList = [
  '/ip4/127.0.0.1/tcp/10001/ws'
]

// Used for inbound connections when NATed
// libp2p-in-the-browser comment claims these are "added to our multiaddrs list"
const signalingList = [
  '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
  '/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
]

const Node:any = Libp2p.create({
  addresses: {
    //listen: signalingList
  },
  modules: {
    transport: [Websockets, WebRTCStar],
    connEncryption: [NOISE],
    streamMuxer: [Mplex],
    peerDiscovery: [Bootstrap],
    dht: KadDHT,
  },
  config: {
    peerDiscovery: {
      bootstrap: {
        enabled: true,
        list: bootstrapList
      }
    }
  }
})

// Note export is a promise
export { Node }

Can go-libp2p be run in a browser (for example with wasm?)

Oh… this is very weird. So I have two test apps. One is using 0.29.0 from npm and the other is using 0.29.2 from npm. The 0.29.0 app IMMEDIATELY picks up the localStorage.debug and is giving me an enormous amount of console spew. The 0.29.2 app (the one I want to debug…) does not. Is this surprising?

EDIT: Upgraded my 2.29.2 test app to 0.29.3, cleared all cache, still no console prints.

I believe there were some experiments, not sure if go or rust. But, I would not recommend that at this point.

Is this surprising?

Yes, could you try in another browser just to double check? We simply use npm debug package for the logs, nothing should have changed

For what I saw on your code, I would not recommend to use that as you are. I recommend you simply use the browser console for this:

  • Open a tab, and open the console
  • Do the localStorage command on the console
  • Get in your web app / Refresh

Thanks, I tried it in Firefox and worked now.

Now that I can see the libp2p debug messages, I notice this important looking error:

libp2p:bootstrap:error Invalid multiaddr +0ms

Just as feedback, this error message would probably be more helpful if it included the multiaddr it doesn’t like. But it doesn’t matter because I only have one multiaddr in my bootstrap list in these tests.

The multiaddr it’s failing on is:

/ip4/127.0.0.1/tcp/10001/ws

I also saw it fail on

/dns4/THE.DOMAIN.NAME.OF.MY.VPS.com/tcp/10001/ws

I’m sorry to be asking all these basic questions. But, are either of these multiaddrs incorrectly formed (given I’m trying to connect to the standalone bounce.js in this post run via node)?

Ok good! Strange that you are having issues on other browsers though. I usually use { Chrome, Brave, Firefox } and never found issues with the debug.

Yes! The error is quite bad though. The problem is that it must be a valid P2P address. This means that it must have a peerId: /dns4/THE.DOMAIN.NAME.OF.MY.VPS.com/tcp/10001/ws/p2p/YOUR_PEER_ID.

When you dial to a given address, you must know its ID. This is the only way we actually have guarantees on who you are talking to.

Would you like to improve the error on https://github.com/libp2p/js-libp2p-bootstrap/blob/master/src/index.js#L58 ? I think it makes sense to log the multiaddr as you suggested and perhaps mentioning it must include the PeerId of the target.

Maybe something associated with the Chrome profile needs to be cleared or deleted. I will experiment.

Hm, OK. But I notice that every time I run my server script it has a different PeerID. I guess I need to save a PeerID object and store it in a file or something. There’s no existing example code for that, right?

My standalone bounce server is starting to seem a bit complicated. Possibly once I have it working with the peer ID storage I will submit it to js-libp2p as an example.

I will attempt a PR but since I am still learning the requirements I may miss some things…

How do you run your bounder node?
My recommendation is to create a PeerId with its CLI like peer-id --type ed25519 --bits 2048 and save it in JSON. After this, you create a PeerId to provide to Libp2p.create({ peerId }) and the peerId is created by PeerId.createFromJSON

Just with node bounce.js. Thanks for the suggestions

So, I got a little further this time.

Here is my bounce.js now: https://gist.github.com/mcclure/c05ffc31329cfd80b40b04b9a9618bbe

I don’t get a successful peer connection. It does attempt a connection however. I get these in the developer console with debug = libp2p*:

The main error seems to be: “Failed to upgrade outbound connection +0ms Error: protocol selection failed”

My browser-bundle.js is still unchanged from my earlier comment #9. I’m not trying to use -star yet. The only thing I changed is the bootstrap list now contains

/ip4/127.0.0.1/tcp/10001/ws/p2p/12D3KooWGsdpzkAqD7vqpPdNjSxQWy66mSZTrmnYZTVnyjUhJCcR

I’m not trying yet to create a custom protocol or dial() or anything.

Any advice?

Not sure where the issue is. Somehow, the crypto protocol is not well set. Just ran your bouncer code and set the address in the bootstrap addresses from the browser example and it worked:

libp2p:upgrader Starting the outbound connection upgrade +0ms
common.js:111 libp2p:upgrader selecting outbound crypto protocol +0ms ["/noise"]
common.js:111 libp2p:upgrader encrypting outbound connection to {"id":"12D3KooWHvP1HJGRDkieJtT9RoZY12m2J4jiPrWhVwyUHWXFevij","pubKey":"CAESIHhn7S7T30TeAuukS6bzLFPp6CKz/08dBjZSG7DzOimQ"} +9ms
common.js:114 libp2p:noise Stage 0 - Initiator starting to send first message. +0ms
common.js:114 libp2p:noise Stage 0 - Initiator finished sending first message. +6ms
common.js:114 libp2p:noise Stage 1 - Initiator waiting to receive first message from responder... +1ms
common.js:114 libp2p:noise Stage 1 - Initiator received the message. +71ms
common.js:114 libp2p:noise Initiator going to check remote's signature... +0ms
common.js:114 libp2p:noise All good with the signature! +34ms
common.js:114 libp2p:noise Stage 2 - Initiator sending third handshake message. +0ms
common.js:114 libp2p:noise Stage 2 - Initiator sent message with signed payload. +25ms

I noticed you had const NOISE = require('libp2p-noise') in Js-libp2p "peer and content routing" / kad-dht example

Could be that the problem, since it should be const { Noise } = require('libp2p-noise'). Let me know

oh, this fixed it! :open_mouth: thanks

I am going to try to get WebRTC-star working next, if it doesn’t work I will check back here…

1 Like

So, WebRTC-Star didn’t seem to work…

Here’s what I did.

In my bounce.js, I added const wsPort = 10001, wssPort = 10002 at the top, and changed my listen to

    listen: [
        `/ip4/0.0.0.0/tcp/${wsPort}/ws`,
        `/ip4/0.0.0.0/tcp/${wssPort}/wss`
      ]

Meanwhile, I changed my browserbundle to

const familyIp = "ip4/127.0.0.1", wsPort = 10001, wssPort = 10002, publicKey = "12D3KooWGsdpzkAqD7vqpPdNjSxQWy66mSZTrmnYZTVnyjUhJCcR"

// Starting peers
const bootstrapList = [
  `/${familyIp}/tcp/${wsPort}/ws/p2p/${publicKey}`
]

// Used for inbound connections when NATed
// libp2p-in-the-browser comment claims these are "added to our multiaddrs list"
const signalingList = [
  `/${familyIp}/tcp/${wssPort}/wss/p2p-webrtc-star`
]

const Node:any = Libp2p.create({
  addresses: {
    listen: signalingList
  },
  modules: {
    transport: [Websockets, WebRTCStar],
    connEncryption: [NOISE],
    streamMuxer: [Mplex],
    peerDiscovery: [Bootstrap],
    dht: KadDHT,
  },
  config: {
    peerDiscovery: {
      bootstrap: {
        enabled: true,
        list: bootstrapList
      }
    }
  }
})

So I am assuming I can run a websockets transport and a webrtc-star signaler in the same script, as long as I do it on two different ports. I did not put a public key in the signaling server multiaddr because I tried that and it immediately threw an error saying that [keyname] is not a protocol.

Anyway, the above did not work. Here is the libp2p* debug:

The main error seems to be: Error: Transport (WebRTCStar) could not listen on any available address. (By comparison if I replace my 127.0.0.1 server with '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star', I instead get many messages like dialing dwebops, connection opened to dwebops etc.)

Any suggestions? I can post my full up to date browser-bundle and bounce.js text if it helps.

So, as far as I understand, you are not running your own star signal server, is that correct? If that is the case, you should spin up your own: https://github.com/libp2p/js-libp2p-webrtc-star/blob/master/DEPLOYMENT.md . The dns ones you pointed out are these signal servers hosted by the Libp2p Project for people to play with.

You can also do something like https://suda.pl/free-webrtc-star-heroku/

Next year we should add the capability for distributed signalling via libp2p relays. When we support that, you should be able to leverage a set of libp2p nodes for this. Meanwhile, this is the feasible approach.

Hm, OK, I think I was confused. I was trying to create a star signal server. I thought I could turn the bounce.js node into a star signal server by just telling it to act as a wss node and passing it wrtc. I guess that’s not how it works.

I find these instructions a little confusing. If I need to use docker then I need to use docker!, but there seems to be a “sig-server” directory in js-libp2p-webrtc star. Is the docker image just a container for this “sig-server” node script? It seems weird to bring up docker when I could just run the sig-server directly in node. What does the docker image in the DEPLOYMENT.md provide that just running node sig-server/bin.js does not? Can I use my local SSL certificates with bin.js?

One more question about the -star server— earlier you said

Is there a way when running the -star signaling server to set up the server to volunteer for STUN peer connection, but not volunteer to relay actual content from peer to peer?

Thanks again for taking the time to answer my questions.

Where did got this confusion? Any docs hinted you that?

You can test it locally just running the bin. But for production, you will need to setup the SSL certificates, which you cannot do at this point in libp2p. You also do not need to use docker, but you would need to setup your nginx reverse proxy and use the bin.js with it. I think the docker setup is easier and faster, but you can move on from it after, if you like. Heroku is a nice temporary solution for experimenting .

After the connection is established, the peers will directly communicate over webrtc. So, the star server will not relay the actual content.