Skip to content

Instantly share code, notes, and snippets.

@achingbrain
Last active February 5, 2024 14:03
Show Gist options
  • Save achingbrain/88f3e3409e21ebe7efd2c24beb9ee1a7 to your computer and use it in GitHub Desktop.
Save achingbrain/88f3e3409e21ebe7efd2c24beb9ee1a7 to your computer and use it in GitHub Desktop.
Node A and Node B, both connected to Node C
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { webSockets } from '@libp2p/websockets'
import { createLibp2p } from 'libp2p'
import { identify } from '@libp2p/identify'
import * as filters from '@libp2p/websockets/filters'
const TOPIC_NAME = 'test-topic'
/**
* Creates a libp2p node, pass `listen = true` for it to listen on a WebSocket
* address or omit that to create a dial-only node
*/
async function createNode(listen) {
const node = await createLibp2p({
addresses: {
listen: listen === true ? [
// listen on a random port
'/ip4/0.0.0.0/tcp/0/ws'
] : []
},
transports: [
webSockets({
// this filter allows connecting to insecure websocket addresses, it
// should not be used in production
filter: filters.all
})
],
// stream muxers are required to run protocol streams
streamMuxers: [yamux()],
// only required for WebSocket and TCP transports
connectionEncryption: [noise()],
services: {
// required to run pubsub
pubsub: gossipsub(),
// required to run pubsub
identify: identify(),
}
})
return node
}
export async function sleep (ms) {
await new Promise((r) => setTimeout(r, ms))
}
const nodeA = await createNode()
console.info('nodeA running', nodeA.peerId)
const nodeB = await createNode()
console.info('nodeB running', nodeB.peerId)
const nodeC = await createNode(true)
console.info('nodeC running', nodeC.peerId)
console.info('nodeA dials nodeC')
await nodeA.dial(nodeC.getMultiaddrs())
console.info('nodeB dials nodeC')
await nodeB.dial(nodeC.getMultiaddrs())
// relay subscribes to topic - Important, this must be done in order to gossip
// messages between peers that are not directly connected (e.g nodeA and nodeB)
nodeC.services.pubsub.subscribe(TOPIC_NAME)
// nodeA subscribes to topic - Optional, a node does not need to be subscribed
// to a topic to send messages to it, only to receive them
nodeA.services.pubsub.subscribe(TOPIC_NAME)
// nodeB subscribes to topic - Important, must be done to receive messages
nodeB.services.pubsub.subscribe(TOPIC_NAME)
// PeerID -> name lookup
const peers = {
[nodeA.peerId.toString()]: 'nodeA',
[nodeB.peerId.toString()]: 'nodeB',
[nodeC.peerId.toString()]: 'nodeC'
}
// nodeB registers a listener for message events
nodeB.services.pubsub.addEventListener('message', (evt) => {
if (evt.detail.topic === TOPIC_NAME) {
console.info(`nodeB received message on "${TOPIC_NAME}" from ${peers[evt.detail.from.toString()]}`)
}
})
// wait for nodeA to see some subscribers to the pubsub topic - Important if you
// skip this step or mask the "InsufficientPeers" error with the
// `"allowPublishToZeroPeers"` gossipsub option, published messages will not be
// received by any peers
while (true) {
const subscribers = nodeA.services.pubsub.getSubscribers(TOPIC_NAME)
if (subscribers.length === 0) {
console.info('nodeA waiting for pubsub topic peers')
await sleep(1000)
} else {
console.info('nodeA seen subscribers', subscribers.map(peer => peers[peer.toString()]).join(', '))
break
}
}
// nodeA publishes a message
nodeA.services.pubsub.publish(TOPIC_NAME, Uint8Array.from([0, 1, 2, 3, 4]))
@achingbrain
Copy link
Author

Sample output:

$ node index.js
nodeA running PeerId(12D3KooWL1gezU8BeCY44iZchAS5asjvAGAdUUan6GT2SyDvNMT1)
nodeB running PeerId(12D3KooWCcznoYTSsvvz81PRxFZnfRsPoadyMbneT2McqvmQvo56)
nodeC running PeerId(12D3KooWM5tyRhjXjcpJQFBC8onvAk1t9uxX3FBtm5yiPwPuugE7)
nodeA dials nodeC
nodeB dials nodeC
nodeA waiting for pubsub topic peers
nodeA seen subscribers nodeC
nodeB received message on "test-topic" from nodeA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment