Handling multiple topics in hypercore protocol's hyperswarm

88 Views Asked by At

I'm attempting to have peers converse with each other based on multiple topics. Based on the hyperswarm hello world example, I have this:

const Hyperswarm = require('hyperswarm')
const swarm1 = new Hyperswarm()
const swarm2 = new Hyperswarm()
;(async () => {
    swarm1.on('connection', (conn, info) => {
      // swarm1 will receive server connections, expect empty topics for server mode...
      console.log('swarm1 got connection', info.topics.map(x=>x.toString()),info._seenTopics)
      conn.write('this is a server connection')
      conn.end()
    })

    swarm2.on('connection', (conn, info) => {
        console.log('swarm2 got connection', info.topics.map(x=>x.toString()),info._seenTopics)
      conn.on('data', data => console.log('client got message:', data.toString()))
    })

    const topicA = Buffer.alloc(32).fill('TopicA') 
    const topicB = Buffer.alloc(32).fill('TopicB') 
    const discovery = swarm1.join(topicA, { server: true, client: false })
    await discovery.flushed()
    console.log('discovery flushed')
    const discovery1 = swarm1.join(topicB, { server: true, client: false })
    await discovery1.flushed() // Waits for the topic to be fully announced on the DHT
    console.log('discovery1 flushed')

    swarm2.join(topicA, { server: false, client: true })
    console.log('swarm2 joined topicA:')
    await swarm2.flush() // Waits for the swarm to connect to pending peers.
    console.log('swarm2 topicA flushed')

    swarm2.join(topicB, { server: false, client: true })
    console.log('swarm2 joined topicB:')
    await swarm2.flush()
    console.log('swarm2 topicB flushed')
})();

This gives an output of:

discovery flushed
discovery1 flushed
swarm2 joined topicA:
swarm1 got connection [] Set(0) {}

This seems to hang on the first await swarm2.flush() call. Is there an alternative approach? Might it be better to create a swarm object per topic?


Update:

Here is a version with per-swarm topics:

const Hyperswarm = require('hyperswarm')
const TopicAServerSwarm = new Hyperswarm()
const TopicBServerSwarm = new Hyperswarm()
const TopicAClientSwarm = new Hyperswarm()
const TopicBClientSwarm = new Hyperswarm()
;(async () => {
    TopicAServerSwarm.on('connection', (conn, info) => {
      console.log('TopicAServerSwarm got connection', info.topics.map(x=>x.toString(0)),info._seenTopics)
      conn.write('this is a server connection for TopicA')
      conn.end()
    })
    TopicBServerSwarm.on('connection', (conn, info) => {
      console.log('TopicBServerSwarm got connection', info.topics.map(x=>x.toString(0)),info._seenTopics)
      conn.write('this is a server connection for TopicB')
      conn.end()
    })
    TopicAClientSwarm.on('connection', (conn, info) => {
        console.log('TopicAClientSwarm got connection', info.topics.map(x=>x.toString(0)),info._seenTopics)
      conn.on('data', data => console.log('client got message:', data.toString()))
    })
    TopicBClientSwarm.on('connection', (conn, info) => {
        console.log('TopicBClientSwarm got connection', info.topics.map(x=>x.toString(0)),info._seenTopics)
      conn.on('data', data => console.log('client got message:', data.toString()))
    })
    const topicA = Buffer.alloc(32).fill('TopicA') 
    const topicB = Buffer.alloc(32).fill('TopicB') 
    const discovery = TopicAServerSwarm.join(topicA, { server: true, client: false })
    await discovery.flushed()
    console.log('TopicAServerSwarm flushed')
    const discovery1 = TopicBServerSwarm.join(topicB, { server: true, client: false })
    await discovery1.flushed() // Waits for the topic to be fully announced on the DHT
    console.log('TopicBServerSwarm flushed')
    const discoverycliA = TopicAClientSwarm.join(topicA, { server: false, client: true })
    console.log('TopicAClientSwarm joined topicA:')
    await discoverycliA.flushed() // Waits for the swarm to connect to pending peers.
    console.log('TopicAClientSwarm flushed')
    const discoverycliB = TopicBClientSwarm.join(topicB, { server: false, client: true })
    console.log('TopicBClientSwarm joined topicB:')
    await discoverycliB.flushed()
    console.log('TopicBClientSwarm flushed')
})();

Again, here, the server swarms get connections but not the clients? Why might this be happening?

1

There are 1 best solutions below

0
On

There were a few errors in my code in the question. I modelled two simulated peers with two IIFEs like so:

const Hyperswarm = require('hyperswarm')
const swarm0 = new Hyperswarm()
const swarm1 = new Hyperswarm()
const topicA = Buffer.alloc(32).fill('TopicA') 
const topicB = Buffer.alloc(32).fill('TopicB') 
const pause = sec => new Promise(r => setTimeout(()=>r(),sec*1000));
// PEER 0
;(async () => {
    swarm0.on('connection', (conn, info) => {
      conn.on('error',e => console.log(e))
      conn.on('data',(msg)=>console.log(msg.toString()))
      console.log('swarm0 got connection', info.topics.map(x=>x.toString(0)),info._seenTopics)
      conn.write('this is a message from peer0')
    })
    swarm0.on('error',e => console.log(e))
    let d0 =  swarm0.join(topicA)
    let d1 =  swarm0.join(topicB)
    await d0.flushed()
    await d1.flushed()
    console.log('swarm0 flushed')
})();
// PEER 1
;(async () => {
    await pause(5);
    swarm1.on('connection', (conn, info) => {
      conn.on('error',e => console.log(e))
      conn.on('data',(msg)=>console.log(msg.toString()))
      console.log('swarm1 got connection', info.topics.map(x=>x.toString(0)),info._seenTopics)
      conn.write('this is a message from peer1')
    })
    swarm1.on('error',e => console.log(e))
    let d0 =  swarm1.join(topicA)
    let d1 =   swarm1.join(topicB)
    await d0.flushed()
    await d1.flushed()
    console.log('swarm1 flushed')
})(); 

This gave:

swarm0 flushed
swarm0 got connection [] Set(0) {}
swarm1 got connection [
  'TopicATopicATopicATopicATopicATo',
  'TopicBTopicBTopicBTopicBTopicBTo'
] Set(2) {
  '546f70696341546f70696341546f70696341546f70696341546f70696341546f',
  '546f70696342546f70696342546f70696342546f70696342546f70696342546f'
}
PEER1: this is a message from peer0
PEER0: this is a message from peer1
swarm1 flushed

The client is able to detect the topics and can react as necessary, from the readme:

... When server connections are emitted, they are not associated with a specific topic -- the server only knows it received an incoming connection.

When you join a topic as a client, the swarm will do a query to discover available servers, and will eagerly connect to them. As with server mode, these connections will be emitted as connection events, but in client mode they will be associated with the topic (info.topics will be set in the connection event).

Essentially, hyperswarm gives you a websocket connection to a peer that is interested in given topic(s) and assigns client and server roles. If there are multiple topics, it's up to the developer to use the provided websocket to create the necessary procedures to send/receive information pertaining to those topics.

Though I'm still not sure how it decides which peers acts as a client and which as a server, but that's another question...