Skip to content

Commit

Permalink
feat: add peer/content routing example (#174)
Browse files Browse the repository at this point in the history
Restores peer/content routing example
  • Loading branch information
achingbrain authored Sep 20, 2024
0 parents commit b778e60
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# ⚠️ IMPORTANT ⚠️

# Please do not create a Pull Request for this repository

The contents of this repository are automatically synced from the parent [js-libp2p Examples Project](https://github.com/libp2p/js-libp2p-examples) so any changes made to the standalone repository will be lost after the next sync.

Please open a PR against [js-libp2p Examples](https://github.com/libp2p/js-libp2p-examples) instead.

## Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.

1. Fork the [js-libp2p Examples Project](https://github.com/libp2p/js-libp2p-examples)
2. Create your Feature Branch (`git checkout -b feature/amazing-example`)
3. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`)
4. Push to the Branch (`git push origin feature/amazing-example`)
5. Open a Pull Request
19 changes: 19 additions & 0 deletions .github/workflows/sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: pull

on:
workflow_dispatch

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Pull from another repository
uses: ipfs-examples/actions-pull-directory-from-repo@main
with:
source-repo: libp2p/js-libp2p-examples
source-folder-path: examples/${{ github.event.repository.name }}
source-branch: main
target-branch: main
git-username: github-actions
git-email: [email protected]
49 changes: 49 additions & 0 deletions 1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-disable no-console */

import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identify, identifyPush } from '@libp2p/identify'
import { kadDHT, removePublicAddressesMapper } from '@libp2p/kad-dht'
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()],
streamMuxers: [yamux()],
connectionEncrypters: [noise()],
services: {
// configure Kad-DHT to run on the local network
dht: kadDHT({
protocol: '/ipfs/lan/kad/1.0.0',
peerInfoMapper: removePublicAddressesMapper,
clientMode: false
}),
identify: identify(),
identifyPush: identifyPush()
}
})

return node
}

const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode()
])

// Connect the nodes 1 -> 2 -> 3
await Promise.all([
node1.dial(node2.getMultiaddrs()),
node2.dial(node3.getMultiaddrs())
])

// find peer 3 from peer 1 (there is no direct connection)
const peer = await node1.peerRouting.findPeer(node3.peerId)

console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
52 changes: 52 additions & 0 deletions 2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint-disable no-console */

import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identify, identifyPush } from '@libp2p/identify'
import { kadDHT, removePublicAddressesMapper } from '@libp2p/kad-dht'
import { tcp } from '@libp2p/tcp'
import all from 'it-all'
import { createLibp2p } from 'libp2p'
import { CID } from 'multiformats/cid'

const createNode = async () => {
const node = await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
transports: [tcp()],
streamMuxers: [yamux()],
connectionEncrypters: [noise()],
services: {
dht: kadDHT({
protocol: '/ipfs/lan/kad/1.0.0',
peerInfoMapper: removePublicAddressesMapper,
clientMode: false
}),
identify: identify(),
identifyPush: identifyPush()
}
})

return node
}

const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode()
])

await Promise.all([
node1.dial(node2.getMultiaddrs()),
node2.dial(node3.getMultiaddrs())
])

const cid = CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
await node1.contentRouting.provide(cid)

console.log('Node %s is providing %s', node1.peerId.toString(), cid.toString())

const providers = await all(node3.contentRouting.findProviders(cid, { timeout: 3000 }))

console.log('Found provider:', providers[0].id.toString())
4 changes: 4 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This project is dual licensed under MIT and Apache-2.0.

MIT: https://www.opensource.org/licenses/mit
Apache-2.0: https://www.apache.org/licenses/license-2.0
5 changes: 5 additions & 0 deletions LICENSE-APACHE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
19 changes: 19 additions & 0 deletions LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
124 changes: 124 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# @libp2p/example-peer-and-content-routing

[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-examples.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-examples)
[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-examples/ci.yml?branch=main\&style=flat-square)](https://github.com/libp2p/js-libp2p-examples/actions/workflows/ci.yml?query=branch%3Amain)

> How to use peer and content routing
Peer Routing is the category of modules that offer a way to find other peers in the network by intentionally issuing queries, iterative or recursive, until a Peer is found or the closest Peers, given the Peer Routing algorithm strategy are found.

Content Routing is the category of modules that offer a way to find where content lives in the network, it works in two steps: 1) Peers provide (announce) to the network that they are holders of specific content (multihashes) and 2) Peers issue queries to find where that content lives. A Content Routing mechanism could be as complex as a Kademlia DHT or a simple registry somewhere in the network.

## 1. Using Peer Routing to find other peers

This example builds on top of the [Protocol and Stream Muxing](../protocol-and-stream-muxing). We need to install `@libp2p/kad-dht`, go ahead and `npm install @libp2p/kad-dht`. If you want to see the final version, open [1.js](./1.js).

First, let's update our config to support Peer Routing and Content Routing.

```JavaScript
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identify, identifyPush } from '@libp2p/identify'
import { kadDHT, removePublicAddressesMapper } from '@libp2p/kad-dht'
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()],
streamMuxers: [yamux()],
connectionEncrypters: [noise()],
services: {
// configure Kad-DHT to run on the local network
dht: kadDHT({
protocol: '/ipfs/lan/kad/1.0.0',
peerInfoMapper: removePublicAddressesMapper,
clientMode: false
}),
identify: identify(),
identifyPush: identifyPush()
}
})

return node
}
```

Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3

```JavaScript
const [node1, node2, node3] = await Promise.all([
createNode(),
createNode(),
createNode()
])

await Promise.all([
node1.dial(node2.getMultiaddrs()),
node2.dial(node3.getMultiaddrs())
])

const peer = await node1.peerRouting.findPeer(node3.peerId)

console.log('Found it, multiaddrs are:')
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
```

You should see the output being something like:

```Bash
> node 1.js
Found it, multiaddrs are:
/ip4/127.0.0.1/tcp/63617
/ip4/192.168.86.41/tcp/63617
```

You have successfully used Peer Routing to find a peer that you were not directly connected. Now all you have to do is to dial to the multiaddrs you discovered.

## 2. Using Content Routing to find providers of content

With Content Routing, you can create records that are stored in multiple points in the network, these records can be resolved by you or other peers and they act as memos or rendezvous points. A great usage of this feature is to support discovery of content, where one node holds a file and instead of using a centralized tracker to inform other nodes that it holds that file, it simply puts a record in the network that can be resolved by other peers. Peer Routing and Content Routing are commonly known as Distributed Hash Tables, DHT.

You can find this example completed in [2.js](./2.js), however as you will see it is very simple to update the previous example.

Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.

```JavaScript
import { CID } from 'multiformats/cid'
import all from 'it-all'

const cid = CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
await node1.contentRouting.provide(cid)

console.log('Node %s is providing %s', node1.peerId.toString(), cid.toString())

const providers = await all(node3.contentRouting.findProviders(cid, { timeout: 5000 }))

console.log('Found provider:', providers[0].id.toString())
```

The output of your program should look like:

```bash
> node 2.js
Node QmSsmVPoTy3WpzwiNPnsKmonBaZjK2HitFs2nWUvwK31Pz is providing QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL
Found provider: QmSsmVPoTy3WpzwiNPnsKmonBaZjK2HitFs2nWUvwK31Pz
```

That's it, now you know how to find peers that have pieces of information that interest you!

## License

Licensed under either of

- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
33 changes: 33 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@libp2p/example-peer-and-content-routing",
"version": "0.0.0",
"description": "How to use peer and content routing",
"license": "Apache-2.0 OR MIT",
"homepage": "https://github.com/libp2p/js-libp2p-examples/tree/master/examples/js-libp2p-example-peer-and-content-routing#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/libp2p/js-libp2p-examples.git"
},
"bugs": {
"url": "https://github.com/libp2p/js-libp2p-examples/issues"
},
"type": "module",
"scripts": {
"lint": "aegir lint",
"test": "test-node-example test/*"
},
"dependencies": {
"@chainsafe/libp2p-noise": "^16.0.0",
"@chainsafe/libp2p-yamux": "^7.0.0",
"@libp2p/identify": "^3.0.1",
"@libp2p/kad-dht": "^13.0.0",
"@libp2p/tcp": "^10.0.0",
"it-all": "^3.0.2",
"libp2p": "^2.0.0",
"multiformats": "^13.1.1"
},
"devDependencies": {
"test-ipfs-example": "^1.1.0"
},
"private": true
}
5 changes: 5 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { test as test1 } from './test-1.js'
import { test as test2 } from './test-2.js'

await test1()
await test2()
13 changes: 13 additions & 0 deletions test/test-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import path from 'path'
import { fileURLToPath } from 'url'
import { waitForOutput } from 'test-ipfs-example/node'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

export async function test () {
process.stdout.write('1.js\n')

await waitForOutput('Found it, multiaddrs are:', 'node', [path.join(__dirname, '../1.js')], {
cwd: __dirname
})
}
13 changes: 13 additions & 0 deletions test/test-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import path from 'path'
import { fileURLToPath } from 'url'
import { waitForOutput } from 'test-ipfs-example/node'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

export async function test () {
process.stdout.write('2.js\n')

await waitForOutput('Found provider:', 'node', [path.join(__dirname, '../2.js')], {
cwd: __dirname
})
}

0 comments on commit b778e60

Please sign in to comment.