Decentralized programming with libp2p
Tags: javascript, programming, tutorial, node
Published: 2023-09-01 21:27:32

# Distributed everything
I thought that I would write a quick post about running libp2p. If you haven't come across it yet, then you should definitely have a look at it. 

The main website is here: https://libp2p.io/

Libp2p supports multiple languages, rust, golang, javascript and so on, today I will be using javascript.

# Why?
I started to explore libp2p a little while ago. The main reason was personal interest. When I started playing around with it, I figured out how useful it was for a bunch of projects I'm working on (namely project tetsuo - https://tetsuo.dev).

# What is it - what does it do for me?
Libp2p is a modular networking stack that you can use in your projects. It allows a developer to quickly and easily create a project that has **safe, easy, decentralized networking**. 

That's right **decentralized**.

This means that multiple libp2p nodes can discover each other without needing a centralized gateway or broker (you can have this of course, but decentralized interests me more).

If you have two nodes, they will discover each other.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3y87r6n71rak2gew5x8f.jpg)


If you have three nodes, they will also discover each other.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yttsvtwu7haty51o9h74.jpg)

# Decentralized applications
If you are following along, this means that you can run a completely decentralized topology. This also means that your entire application can be decentralized.

This is important for applications where you may only want clients to connect to each other.

It is also important where you want clients to discover and automatically connect to each other. 

Some of the use cases here are for things like:
- Completely decentralized peer to peer applications.
- Cluster services.
- Disconnected applications.

...the possibilities are endless.

# What am I showing you today?
Today, I am going to walk through the code to be able to get a basic node to node automatic discovery happening. 

# Set up a project
To start with, I need to initialize a project. Since I'm using javascript, we will go ahead and start.


```Shell
# create a directory for the project
mkdir test
cd test

# Initialize the project
npm init es6
```
Accept the defaults for your npm init. This will create a package.json file. We are good to go from here. 

We are initializing the project as a module.

# Get the scaffold running
To begin with, we will be creating a scaffold. All this will do at this point is create a libp2p node.

Install the pre-requisites
```
npm install @libp2p/tcp 
npm install libp2p
```

Place the code below into a file called **test.js**
 
```Javascript
import { tcp } from '@libp2p/tcp'
import { createLibp2p } from 'libp2p'

const createNode = async () => {
  const node = await createLibp2p({
    addresses: { listen: ['/ip4/0.0.0.0/tcp/0'] },
    transports: [ tcp() ],
  }) //end node

  return node
} //end createNode function

;(async () => {
  const [node] = await Promise.all([
    createNode(),
  ]) //end node creation as promise

  console.log('listening on addresses:')
    node.getMultiaddrs().forEach((addr) => {
      console.log(addr.toString())
  }) //end logging
})()
```

The code creates a new libp2p node that listens on all addresses, using TCP.

```Javascript
const createNode = async () => {
  const node = await createLibp2p({
    addresses: { listen: ['/ip4/0.0.0.0/tcp/0'] },
    transports: [ tcp() ],
  }) //end node

  return node
} //end createNode function
```

Then there is a main function that creates the node and prints out all of the addresses that the node is listening on.

```Javascript
;(async () => {
  const [node] = await Promise.all([
    createNode(),
  ]) //end node creation as promise

  console.log('listening on addresses:')
    node.getMultiaddrs().forEach((addr) => {
      console.log(addr.toString())
  }) //end logging
})()
```

You can run this using the command

```Shell
node test.js
# node test.js
listening on addresses:
/ip4/127.0.0.1/tcp/33389/p2p/12D3KooWCR4TBnsBq6S7gkJuxSMFCBsQmXT4V4Z5hpEwbixsE6Va
/ip4/10.1.1.150/tcp/33389/p2p/12D3KooWCR4TBnsBq6S7gkJuxSMFCBsQmXT4V4Z5hpEwbixsE6Va
/ip4/192.168.224.129/tcp/33389/p2p/12D3KooWCR4TBnsBq6S7gkJuxSMFCBsQmXT4V4Z5hpEwbixsE6Va
```

# Node discovery
So far we have a single libp2p node that listens. This is not that useful. Next step is to allow our node to discover other nodes on the network.

Today, we will use the multicast DNS module for this. The multicast DNS module is limited to local networks, but works well for testing and understanding.

Install the multicast DNS module.

```Shell
npm install @libp2p/mdns 
```

Update the test.js file with the following content.
```Javascript
import { mdns } from '@libp2p/mdns'
import { tcp } from '@libp2p/tcp'
import { createLibp2p } from 'libp2p'

const createNode = async () => {
  const node = await createLibp2p({
    addresses: { listen: ['/ip4/0.0.0.0/tcp/0'] },
    transports: [ tcp() ],
    peerDiscovery: [ mdns() ] //default discovery period 10 seconds
  }) //end node

  return node
} //end createNode function

;(async () => {
  const [node] = await Promise.all([
    createNode(),
  ]) //end node creation as promise

  console.log('listening on addresses:')
    node.getMultiaddrs().forEach((addr) => {
      console.log(addr.toString())
  }) //end logging

  node.addEventListener('peer:discovery', (evt) => console.log('Discovered:', evt.de
tail.id.toString()))
})()

```

We have added some pieces to our codebase. We have added a **peerDiscovery** piece into the **createNode** async function. This essentially enables peer discovery using multicast DNS. 

There are other options for peer discovery, but in this example, I am using multicast DNS.

```Javascript
const createNode = async () => {
  const node = await createLibp2p({
    addresses: { listen: ['/ip4/0.0.0.0/tcp/0'] },
    transports: [ tcp() ],
    peerDiscovery: [ mdns() ] //default discovery period 10 seconds
  }) //end node
```
This means that your node will now automatically listen on all addresses, and will perform a peer discovery search via multicast every 10 seconds. The 10 second time is fine for testing, but can be extended for real scenarios.

At the end of our main async function we place the following.
```Javascript
;(async () => {
  node.addEventListener('peer:discovery', (evt) => console.log('Discovered:', evt.detail.id.toString()))
})()
```
This piece of code adds a listener that listens specifically for peer discovery events.


# Running the new code
Let's run the new codebase.

```Shell
# node test.js
listening on addresses:
/ip4/127.0.0.1/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
/ip4/10.1.1.150/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
/ip4/192.168.224.129/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
```

At this point, it looks exactly the same as the last time we ran it. The reason for this is that we need to run **two** nodes in order for us to discover anything.

Let's fire up a second instance of test.js in a separate window

## node1 output
```Shell
# node test.js
listening on addresses:
/ip4/127.0.0.1/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
/ip4/10.1.1.150/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
/ip4/192.168.224.129/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
```

## node2 output
```
# node test.js
listening on addresses:
/ip4/127.0.0.1/tcp/35725/p2p/12D3KooWPJSvTFo7jhaLACx1vZVKq7bHhNHKXALXWCjKqFu5LK47
/ip4/10.1.1.150/tcp/35725/p2p/12D3KooWPJSvTFo7jhaLACx1vZVKq7bHhNHKXALXWCjKqFu5LK47
/ip4/192.168.224.129/tcp/35725/p2p/12D3KooWPJSvTFo7jhaLACx1vZVKq7bHhNHKXALXWCjKqFu5LK47
Discovered: 12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
```
You will note that the second node has automatically discovered the first node.

```Shell
Discovered: 12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
```

## node1 output - check again
If you check the output of node1 again, you will note that it too has automatically discovered the other node.

```Shell
# node test.js
listening on addresses:
/ip4/127.0.0.1/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
/ip4/10.1.1.150/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
/ip4/192.168.224.129/tcp/33667/p2p/12D3KooWC5c39mad27di37ubxUymmoohVhWNedMbPrUzYN7tiV2R
Discovered: 12D3KooWPJSvTFo7jhaLACx1vZVKq7bHhNHKXALXWCjKqFu5LK47
```
Each node is broadcasting and discovering it's peer on the network.

# Full code
The full code is listed below.

```Javascript
import { mdns } from '@libp2p/mdns'
import { tcp } from '@libp2p/tcp'
import { createLibp2p } from 'libp2p'

const createNode = async () => {
  const node = await createLibp2p({
    addresses: { listen: ['/ip4/0.0.0.0/tcp/0'] },
    transports: [ tcp() ],
    peerDiscovery: [ mdns() ] //default discovery period 10 seconds

//    streamMuxers: [ yamux(), mplex() ],
//    connectionEncryption: [ noise() ],
  }) //end node

  return node
} //end createNode function

;(async () => {
  const [node] = await Promise.all([
    createNode(),
  ]) //end node creation as promise

  console.log('listening on addresses:')
    node.getMultiaddrs().forEach((addr) => {
      console.log(addr.toString())
  }) //end logging

  node.addEventListener('peer:discovery', (evt) => console.log('Discovered:', evt.detail.id.toString()))

// ADD YOUR APPLICATION CODE HERE

})()
```

# What Now?
This project is a scaffold of how you can set up node discovery using libp2p. Now that you have a scaffold running, you can add your application code to the scaffold.

In my case, I am writing a cluster mechanism for application servers to automatically exchange configuration events. Every member of my application cluster will automatically receive updates to its configuration no matter which node receives the update. 

In order to do this, I will be using a peer to peer message bus (also another feature of **libp2p**). Look out for my next post where I walk through how to get pub sub events working!