Alex Potsides 199395de4d
feat: convert to typescript (#1172)
Converts this module to typescript.

- Ecosystem modules renamed from (e.g.) `libp2p-tcp` to `@libp2p/tcp`
- Ecosystem module now have named exports
- Configuration has been updated, now pass instances of modules instead of classes:
- Some configuration keys have been renamed to make them more descriptive.  `transport` -> `transports`, `connEncryption` -> `connectionEncryption`.  In general where we pass multiple things, the key is now plural, e.g. `streamMuxer` -> `streamMuxers`, `contentRouting` -> `contentRouters`, etc.  Where we are configuring a singleton the config key is singular, e.g. `connProtector` -> `connectionProtector` etc.
- Properties of the `modules` config key have been moved to the root
- Properties of the `config` config key have been moved to the root
```js
// before
import Libp2p from 'libp2p'
import TCP from 'libp2p-tcp'

await Libp2p.create({
  modules: {
    transport: [
      TCP
    ],
  }
  config: {
    transport: {
      [TCP.tag]: {
        foo: 'bar'
      }
    },
    relay: {
      enabled: true,
      hop: {
        enabled: true,
        active: true
      }
    }
  }
})
```
```js
// after
import { createLibp2p } from 'libp2p'
import { TCP } from '@libp2p/tcp'

await createLibp2p({
  transports: [
    new TCP({ foo: 'bar' })
  ],
  relay: {
    enabled: true,
    hop: {
      enabled: true,
      active: true
    }
  }
})
```
- Use of `enabled` flag has been reduced - previously you could pass a module but disable it with config.  Now if you don't want a feature, just don't pass an implementation.   Eg:
```js
// before
await Libp2p.create({
  modules: {
    transport: [
      TCP
    ],
    pubsub: Gossipsub
  },
  config: {
    pubsub: {
      enabled: false
    }
  }
})
```
```js
// after
await createLibp2p({
  transports: [
    new TCP()
  ]
})
```
- `.multiaddrs` renamed to `.getMultiaddrs()` because it's not a property accessor, work is done by that method to calculate announce addresses, observed addresses, etc
- `/p2p/${peerId}` is now appended to all addresses returned by `.getMultiaddrs()` so they can be used opaquely (every consumer has to append the peer ID to the address to actually use it otherwise).  If you need low-level unadulterated addresses, call methods on the address manager.

BREAKING CHANGE: types are no longer hand crafted, this module is now ESM only
2022-03-28 14:30:27 +01:00
..
2022-03-28 14:30:27 +01:00
2022-03-28 14:30:27 +01:00
2022-03-28 14:30:27 +01:00
2022-03-28 14:30:27 +01:00

Publish Subscribe

Publish Subscribe is also included on the stack. Currently, we have two PubSub implementation available libp2p-floodsub and libp2p-gossipsub, with many more being researched at research-pubsub.

We've seen many interesting use cases appear with this, here are some highlights:

0. Set up the example

Before moving into the examples, you should run npm install on the top level js-libp2p folder, in order to install all the dependencies needed for this example. In addition, you will need to install the example related dependencies by doing cd examples && npm install. Once the install finishes, you should move into the example folder with cd pubsub.

1. Setting up a simple PubSub network on top of libp2p

For this example, we will use MulticastDNS for automatic Peer Discovery. This example is based the previous examples found in Discovery Mechanisms. You can find the complete version at 1.js.

Using PubSub is super simple, you only need to provide the implementation of your choice and you are ready to go. No need for extra configuration.

First, let's update our libp2p configuration with a pubsub implementation.

import { createLibp2p } from 'libp2p'
import { Gossipsub } from 'libp2p-gossipsub'

const node = await createLibp2p({
  addresses: {
    listen: ['/ip4/0.0.0.0/tcp/0']
  },
  transports: [
    new TCP()
  ],
  streamMuxers: [
    new Mplex()
  ],
  connectionEncryption: [
    new Noise()
  ],
  // we add the Pubsub module we want
  pubsub: new Gossipsub()
})

Once that is done, we only need to create a few libp2p nodes, connect them and everything is ready to start using pubsub.

const { fromString } from 'uint8arrays/from-string')
const { toString } from 'uint8arrays/to-string')
const topic = 'news'

const node1 = nodes[0]
const node2 = nodes[1]

// Add node's 2 data to the PeerStore
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
await node1.dial(node2.peerId)

node1.pubsub.on(topic, (msg) => {
  console.log(`node1 received: ${toString(msg.data)}`)
})
await node1.pubsub.subscribe(topic)

// Will not receive own published messages by default
node2.pubsub.on(topic, (msg) => {
  console.log(`node2 received: ${toString(msg.data)}`)
})
await node2.pubsub.subscribe(topic)

// node2 publishes "news" every second
setInterval(() => {
  node2.pubsub.publish(topic, fromString('Bird bird bird, bird is the word!'))
}, 1000)

The output of the program should look like:

> node 1.js
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
node1 received: Bird bird bird, bird is the word!
node1 received: Bird bird bird, bird is the word!

You can change the pubsub emitSelf option if you want the publishing node to receive its own messages.

const defaults = {
  config: {
    pubsub: {
      enabled: true,
      emitSelf: true
    }
  }
}

The output of the program should look like:

> node 1.js
connected to QmWpvkKm6qHLhoxpWrTswY6UMNWDyn8hN265Qp9ZYvgS82
node1 received: Bird bird bird, bird is the word!
node2 received: Bird bird bird, bird is the word!
node1 received: Bird bird bird, bird is the word!
node2 received: Bird bird bird, bird is the word!

2. Future work

libp2p/IPFS PubSub is enabling a whole set of Distributed Real Time applications using CRDT (Conflict-Free Replicated Data Types). It is still going through heavy research (and hacking) and we invite you to join the conversation at research-CRDT. Here is a list of some of the exciting examples: