Skip to content
This repository was archived by the owner on Jul 21, 2023. It is now read-only.

Commit 1f8bc6a

Browse files
authored
fix: refactor query logic (#237)
1. Queries are plain functions now 1. Queries are run by the QueryManager 1. Query functions yield events - dial, send request, receive response, value, error, etc 1. Timeouts have been replaced with AbortSignals 1. Halting in-progress queries is done by aborting an AbortSignal from the top level or breaking out of the `for await..of` loop 1. Internal components do not depend on the top-level DHT component any more 1. Some methods have been moved to other components to break circular dependencies 1. The top level KadDHT class now has a ts interface and a factory function 1. There are two DHTs, lan (for private addresses) and wan (for public addresses) 1. Client mode is now on by default for the wan DHT, off for the lan DHT 1. DHT queries for CIDs search for the multihash rather than the full CID
1 parent eff54bf commit 1f8bc6a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+4436
-4623
lines changed

.github/workflows/test.yml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: ci
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request:
7+
branches:
8+
- master
9+
10+
jobs:
11+
check:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v2
15+
- run: npm install
16+
- run: npx aegir lint
17+
- uses: gozala/[email protected]
18+
- run: npx aegir build
19+
- run: npx aegir dep-check
20+
- uses: ipfs/aegir/actions/bundle-size@master
21+
name: size
22+
with:
23+
github_token: ${{ secrets.GITHUB_TOKEN }}
24+
test-node:
25+
needs: check
26+
runs-on: ${{ matrix.os }}
27+
strategy:
28+
matrix:
29+
os: [windows-latest, ubuntu-latest, macos-latest]
30+
node: [14, 16]
31+
fail-fast: true
32+
steps:
33+
- uses: actions/checkout@v2
34+
- uses: actions/setup-node@v1
35+
with:
36+
node-version: ${{ matrix.node }}
37+
- run: npm install
38+
- run: npx nyc --reporter=lcov aegir test -t node -- --bail
39+
- uses: codecov/codecov-action@v1

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ test/test-data/go-ipfs-repo/LOG.old
4141
# while testing npm5
4242
package-lock.json
4343
yarn.lock
44+
.nyc_output

.travis.yml

-42
This file was deleted.

README.md

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# js-libp2p-kad-dht
1+
# js-libp2p-kad-dht <!-- omit in toc -->
22

33
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
44
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
55
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
66
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
7-
[![Build Status](https://travis-ci.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://travis-ci.org/libp2p/js-libp2p-kad-dht)
7+
[![Build status](https://github.com/libp2p/js-libp2p-kad-dht/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/libp2p/js-libp2p-kad-dht/actions/workflows/test.yml)
88
[![Coverage Status](https://coveralls.io/repos/github/libp2p/js-libp2p-kad-dht/badge.svg?branch=master)](https://coveralls.io/github/libp2p/js-libp2p-kad-dht?branch=master)
99
[![Dependency Status](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht)
1010
[![Bundle Size](https://flat.badgen.net/bundlephobia/minzip/libp2p-kad-dht)](https://bundlephobia.com/result?p=libp2p-kad-dht)
@@ -15,19 +15,23 @@
1515

1616
> JavaScript implementation of the Kademlia DHT for libp2p, based on [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht).
1717
18-
## Lead Maintainer
18+
## Lead Maintainer <!-- omit in toc -->
1919

2020
[Vasco Santos](https://github.com/vasco-santos).
2121

22-
## Table of Contents
22+
## Table of Contents <!-- omit in toc -->
2323

2424
- [Install](#install)
2525
- [npm](#npm)
2626
- [Use in Node.js](#use-in-nodejs)
2727
- [API](#api)
28+
- [Custom secondary DHT in libp2p](#custom-secondary-dht-in-libp2p)
29+
- [Peer Routing](#peer-routing)
30+
- [Content Routing](#content-routing)
31+
- [Peer Discovery](#peer-discovery)
32+
- [Implementation Summary](#implementation-summary)
2833
- [Contribute](#contribute)
2934
- [License](#license)
30-
3135
## Install
3236

3337
### npm
@@ -39,7 +43,7 @@
3943
### Use in Node.js
4044

4145
```js
42-
const KadDHT = require('libp2p-kad-dht')
46+
import { create } from 'libp2p-kad-dht'
4347
```
4448

4549
## API
@@ -51,20 +55,18 @@ The libp2p-kad-dht module offers 3 APIs: Peer Routing, Content Routing and Peer
5155
### Custom secondary DHT in libp2p
5256

5357
```js
58+
import { create } from 'libp2p-kad-dht'
59+
5460
/**
5561
* @param {Libp2p} libp2p
5662
*/
5763
function addDHT(libp2p) {
58-
const customDHT = new KadDHT({
64+
const customDHT = create({
5965
libp2p,
60-
dialer: libp2p.dialer,
61-
peerId: libp2p.peerId,
62-
peerStore: libp2p.peerStore,
63-
registrar: libp2p.registrar,
6466
protocolPrefix: '/custom'
6567
})
6668
customDHT.start()
67-
customDHT.on('peer', libp2p._onDiscoveryPeer)
69+
6870
return customDHT
6971
}
7072
```

docs/IMPL_SUMMARY.MD

+14-55
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,26 @@
1-
# js-libp2p-kad-dht
1+
# js-libp2p-kad-dht <!-- omit in toc -->
22

3-
js-libp2p-kad-dht is a JavaScript implementation of the [Kademlia DHT](http://www.scs.stanford.edu/%7Edm/home/papers/kpos.pdf) with some features of S/Kademlia. A "provider" node uses the DHT to advertise that it has a particular piece of content, and "querying" nodes will search the DHT for peers that have a particular piece of content. Content is modeled as a value that is identified by a key, where the key and value are Buffers.
3+
js-libp2p-kad-dht is a JavaScript implementation of the [Kademlia DHT][] with some features of [S/Kademlia][]. A "provider" node uses the DHT to advertise that it has a particular piece of content, and "querying" nodes will search the DHT for peers that have a particular piece of content. Content is modelled as a value that is identified by a key, where the key and value are Uint8Arrays.
44

5-
#### DHT Identifiers
5+
## Table of contents <!-- omit in toc -->
66

7-
The DHT uses a sha2-256 hash for identifiers:
8-
- For peers the DHT identifier is the hash of the [PeerId][PeerId]
9-
- For content the DHT identifier is the hash of the key (eg a Block CID)
7+
- [Spec](#spec)
8+
- [Extensions](#extensions)
9+
- [Disjoint paths](#disjoint-paths)
1010

11-
#### FIND_NODE
11+
## Spec
1212

13-
`findPeer (PeerId):` [PeerInfo][PeerInfo]
13+
js-libp2p-kad-dht follows the [libp2p/kad-dht spec](https://github.com/libp2p/specs/tree/master/kad-dht) and implements the algorithms described in the [IPFS DHT documentation](https://docs.ipfs.io/concepts/dht/).
1414

15-
The address space is so large (256 bits) that there are big gaps between DHT ids, and nodes frequently join and leave the DHT.
15+
## Extensions
1616

17-
To find a particular node
18-
- the `querying node` converts the [PeerId][PeerId] to a DHT id
19-
- the `querying node` sends a request to the nearest peers to that DHT id that it knows about
20-
- those peers respond with the nearest peers to the DHT id that they know about
21-
- the `querying node` sorts the responses and recursively queries the closest peers to the DHT id, continuing until it finds the node or it has queried all the closest peers.
17+
js-libp2p-kad-dht implements some additional functionality not described in the libp2p KAD-DHT spec.
2218

23-
#### PUT
19+
### Disjoint paths
2420

25-
`put (Key, Value)`
26-
27-
To store a value in the DHT, the `provider node`
28-
- converts the key to a DHT id
29-
- follows the "closest peers" algorithm as above to find the nearest peers to the DHT id
30-
- sends the value to those nearest peers
31-
32-
Note that DHT nodes will only store values that are accepted by its "validators", configurable functions that validate the key/value to ensure the node can control what kind of content it stores (eg IPNS records).
33-
34-
#### GET
35-
36-
`get (Key): [Value]`
37-
38-
To retrieve a value from the DHT
39-
- the `querying node` converts the key to a DHT id
40-
- the `querying node` follows the "closest peers" algorithm to find the nearest peers to the DHT id
41-
- at each iteration of the algorithm, if the peer has the value it responds with the value itself in addition to closer peers.
42-
43-
Note that the value for a particular key is stored by many nodes, and these nodes receive `PUT` requests asynchronously, so it's possible that nodes may have distinct values for the same key. For example if node A `PUT`s the value `hello` to key `greeting` and node B concurrently `PUT`s the value `bonjour` to key `greeting`, some nodes close to the key `greeting` may receive `hello` first and others may receive `bonjour` first.
44-
45-
Therefore a `GET` request to the DHT may collect distinct values (eg `hello` and `bonjour`) for a particular key from the nodes close to the key. The DHT has "selectors", configurable functions that choose the "best" value (for example IPNS records include a sequence number, and the "best" value is the record with the highest sequence number).
46-
47-
#### PROVIDE
48-
49-
`provide (Key)`
50-
51-
To advertise that it has the content for a particular key
52-
- the `provider node` converts the key to a DHT id
53-
- the `provider node` follows the "closest peers" algorithm to find the nearest peers to the DHT id
54-
- the `provider node` sends a "provide" message to each of the nearest peers
55-
- each of the nearest peers saves the association between the "provider" peer and the key
56-
57-
#### FIND_PROVIDERS
58-
59-
`findProviders (Key):` [[PeerInfo][PeerInfo]]
60-
61-
To find providers for a particular key
62-
- the `querying node` converts the key to a DHT id
63-
- the `querying node` follows the "closest peers" algorithm to find the nearest peers to the DHT id
64-
- at each iteration of the algorithm, if the peer knows which nodes are providing the value it responds with the provider nodes in addition to closer peers.
21+
js-libp2p-kad-dht uses disjoint paths when performing DHT queries. These are described in the [S/Kademlia][] paper.
6522

23+
[Kademlia DHT]: (http://www.scs.stanford.edu/%7Edm/home/papers/kpos.pdf)
24+
[S/Kademlia]: (https://git.gnunet.org/bibliography.git/plain/docs/SKademlia2007.pdf)
6625
[PeerId]: https://github.com/libp2p/js-peer-id
6726
[PeerInfo]: https://github.com/libp2p/js-peer-info

package.json

+17-10
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"leadMaintainer": "Vasco Santos <[email protected]>",
66
"main": "src/index.js",
77
"scripts": {
8-
"lint": "aegir ts -p check && aegir lint",
98
"prepare": "npm run build",
9+
"lint": "aegir ts -p check && aegir lint",
1010
"build": "npm run build:proto && npm run build:proto-types && aegir build",
1111
"build:proto": "pbjs -t static-module -w commonjs -r libp2p-dht-message --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/message/dht.js ./src/message/dht.proto",
1212
"build:proto-types": "pbts -o src/message/dht.d.ts src/message/dht.js",
@@ -18,7 +18,8 @@
1818
"release-major": "aegir release --type major --docs -t node",
1919
"coverage": "aegir coverage",
2020
"coverage-publish": "aegir-coverage publish",
21-
"sim": "node test/simulation/index.js"
21+
"sim": "node test/simulation/index.js",
22+
"dep-check": "aegir dep-check"
2223
},
2324
"files": [
2425
"src",
@@ -47,47 +48,53 @@
4748
"homepage": "https://github.com/libp2p/js-libp2p-kad-dht",
4849
"types": "dist/src/index.d.ts",
4950
"dependencies": {
51+
"any-signal": "^2.1.2",
5052
"datastore-core": "^6.0.7",
5153
"debug": "^4.3.1",
5254
"err-code": "^3.0.0",
5355
"hashlru": "^2.3.0",
54-
"heap": "~0.2.6",
5556
"interface-datastore": "^6.0.2",
57+
"it-all": "^1.0.5",
58+
"it-drain": "^1.0.4",
5659
"it-first": "^1.0.4",
5760
"it-length": "^1.0.3",
5861
"it-length-prefixed": "^5.0.2",
62+
"it-map": "^1.0.5",
63+
"it-merge": "^1.0.3",
64+
"it-parallel": "^2.0.1",
5965
"it-pipe": "^1.1.0",
66+
"it-take": "^1.0.2",
6067
"k-bucket": "^5.1.0",
6168
"libp2p-crypto": "^0.19.5",
6269
"libp2p-interfaces": "^1.0.0",
6370
"libp2p-record": "^0.10.4",
6471
"multiaddr": "^10.0.0",
6572
"multiformats": "^9.4.5",
73+
"native-abort-controller": "^1.0.4",
74+
"p-defer": "^3.0.0",
6675
"p-map": "^4.0.0",
6776
"p-queue": "^6.6.2",
68-
"p-timeout": "^4.1.0",
6977
"peer-id": "^0.15.0",
78+
"private-ip": "^2.3.3",
7079
"protobufjs": "^6.10.2",
7180
"streaming-iterables": "^6.0.0",
72-
"timeout-abort-controller": "^1.1.1",
81+
"timeout-abort-controller": "^2.0.0",
7382
"uint8arrays": "^3.0.0",
7483
"varint": "^6.0.0"
7584
},
7685
"devDependencies": {
7786
"@types/debug": "^4.1.7",
7887
"aegir": "^35.0.1",
7988
"async-iterator-all": "^1.0.0",
80-
"crypto-browserify": "^3.12.0",
8189
"datastore-level": "^7.0.1",
8290
"delay": "^5.0.0",
8391
"execa": "^5.1.1",
92+
"it-filter": "^1.0.3",
93+
"it-last": "^1.0.6",
8494
"it-pair": "^1.0.0",
85-
"libp2p": "^0.32.3",
95+
"libp2p": "^0.33.0",
8696
"lodash.random": "^3.2.0",
8797
"lodash.range": "^3.2.0",
88-
"p-defer": "^3.0.0",
89-
"p-each-series": "^2.1.0",
90-
"p-map-series": "^2.1.0",
9198
"p-retry": "^4.2.0",
9299
"sinon": "^11.1.1",
93100
"which": "^2.0.2"

src/constants.js

+3
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ exports.K = 20
3333

3434
// Alpha is the concurrency for asynchronous requests
3535
exports.ALPHA = 3
36+
37+
// How often we look for our closest DHT neighbours
38+
exports.QUERY_SELF_INTERVAL = Number(5 * minute)

0 commit comments

Comments
 (0)