Skip to content

Commit c003789

Browse files
authored
feat!: use single DHT only by default (#2322)
We have a weird dual-DHT setup whereby we have a DHT for public addresses (amino, `/ipfs/kad/1.0.0`) and a DHT for private addresess (`/ipfs/lan/kad/1.0.0`). This is an artefact of the previous libp2p setup whereby there could only be a single DHT implementation at once. Now we have services we can configure an amino DHT service and if desired an additional lan-only DHT, or the user can just use amino and not pay the overhead of running an extra DHT. Most content is resolved via the public DHT so running the lan-only DHT gives the user no benefit. BREAKING CHANGE: the `kadDHT` function returns a single DHT - see the readme for how to configure amino/lan as before
1 parent 83dfc7d commit c003789

Some content is hidden

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

52 files changed

+2318
-1423
lines changed

packages/integration-tests/test/interop.ts

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ import { createClient } from '@libp2p/daemon-client'
88
import { createServer } from '@libp2p/daemon-server'
99
import { floodsub } from '@libp2p/floodsub'
1010
import { identify } from '@libp2p/identify'
11-
import { contentRoutingSymbol, peerDiscoverySymbol, peerRoutingSymbol } from '@libp2p/interface'
1211
import { interopTests } from '@libp2p/interop'
13-
import { kadDHT } from '@libp2p/kad-dht'
12+
import { kadDHT, passthroughMapper } from '@libp2p/kad-dht'
1413
import { logger } from '@libp2p/logger'
1514
import { mplex } from '@libp2p/mplex'
1615
import { peerIdFromKeys } from '@libp2p/peer-id'
@@ -155,41 +154,11 @@ async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
155154
}
156155

157156
if (options.dht === true) {
158-
services.dht = (components: any) => {
159-
const dht: any = kadDHT({
160-
clientMode: false
161-
})(components)
162-
163-
// go-libp2p-daemon only has the older single-table DHT instead of the dual
164-
// lan/wan version found in recent go-ipfs versions. unfortunately it's been
165-
// abandoned so here we simulate the older config with the js implementation
166-
const lan: any = dht.lan
167-
168-
const protocol = '/ipfs/kad/1.0.0'
169-
lan.protocol = protocol
170-
lan.network.protocol = protocol
171-
lan.topologyListener.protocol = protocol
172-
173-
Object.defineProperties(lan, {
174-
[contentRoutingSymbol]: {
175-
get () {
176-
return dht[contentRoutingSymbol]
177-
}
178-
},
179-
[peerRoutingSymbol]: {
180-
get () {
181-
return dht[peerRoutingSymbol]
182-
}
183-
},
184-
[peerDiscoverySymbol]: {
185-
get () {
186-
return dht[peerDiscoverySymbol]
187-
}
188-
}
189-
})
190-
191-
return lan
192-
}
157+
services.dht = kadDHT({
158+
protocol: '/ipfs/kad/1.0.0',
159+
peerInfoMapper: passthroughMapper,
160+
clientMode: false
161+
})
193162
}
194163

195164
const node: any = await createLibp2p({

packages/kad-dht/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,84 @@
55

66
> JavaScript implementation of the Kad-DHT for libp2p
77
8+
# About
9+
10+
This module implements the [libp2p Kademlia spec](https://github.com/libp2p/specs/blob/master/kad-dht/README.md) in TypeScript.
11+
12+
The Kademlia DHT allow for several operations such as finding peers, searching for providers of DHT records, etc.
13+
14+
## Example - Using with libp2p
15+
16+
```TypeScript
17+
import { kadDHT } from '@libp2p/kad-dht'
18+
import { createLibp2p } from 'libp2p'
19+
import { peerIdFromString } from '@libp2p/peer-id'
20+
21+
const node = await createLibp2p({
22+
services: {
23+
dht: kadDHT()
24+
}
25+
})
26+
27+
const peerId = peerIdFromString('QmFoo')
28+
const peerInfo = await libp2p.peerRouting.findPeer(peerId)
29+
30+
console.info(peerInfo) // peer id, multiaddrs
31+
```
32+
33+
## Example - Connecting to the IPFS Amino DHT
34+
35+
The [Amino DHT](https://blog.ipfs.tech/2023-09-amino-refactoring/) is a public-good DHT used by IPFS to fetch content, find peers, etc.
36+
37+
If you are trying to access content on the public internet, this is the implementation you want.
38+
39+
```TypeScript
40+
import { kadDHT, removePrivateAddressesMapper } from '@libp2p/kad-dht'
41+
import { createLibp2p } from 'libp2p'
42+
import { peerIdFromString } from '@libp2p/peer-id'
43+
44+
const node = await createLibp2p({
45+
services: {
46+
aminoDHT: kadDHT({
47+
protocol: '/ipfs/kad/1.0.0',
48+
addressFilter: removePrivateAddressesMapper
49+
})
50+
}
51+
})
52+
53+
const peerId = peerIdFromString('QmFoo')
54+
const peerInfo = await libp2p.peerRouting.findPeer(peerId)
55+
56+
console.info(peerInfo) // peer id, multiaddrs
57+
```
58+
59+
## Example - Connecting to a LAN-only DHT
60+
61+
This DHT only works with privately dialable peers.
62+
63+
This is for use when peers are on the local area network.
64+
65+
```TypeScript
66+
import { kadDHT, removePublicAddressesMapper } from '@libp2p/kad-dht'
67+
import { createLibp2p } from 'libp2p'
68+
import { peerIdFromString } from '@libp2p/peer-id'
69+
70+
const node = await createLibp2p({
71+
services: {
72+
lanDHT: kadDHT({
73+
protocol: '/ipfs/lan/kad/1.0.0',
74+
addressFilter: removePublicAddressesMapper,
75+
clientMode: false
76+
})
77+
}
78+
})
79+
80+
const peerId = peerIdFromString('QmFoo')
81+
const peerInfo = await libp2p.peerRouting.findPeer(peerId)
82+
83+
console.info(peerInfo) // peer id, multiaddrs
84+
```
85+
886
# Install
987

1088
```console

packages/kad-dht/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"@libp2p/interface-internal": "^1.0.3",
6262
"@libp2p/peer-collections": "^5.1.1",
6363
"@libp2p/peer-id": "^4.0.2",
64+
"@libp2p/utils": "^5.0.3",
6465
"@multiformats/multiaddr": "^12.1.10",
6566
"@types/sinon": "^17.0.0",
6667
"any-signal": "^4.1.1",
@@ -104,6 +105,7 @@
104105
"execa": "^8.0.1",
105106
"it-filter": "^3.0.1",
106107
"it-last": "^3.0.3",
108+
"it-pair": "^2.0.6",
107109
"lodash.random": "^3.2.0",
108110
"lodash.range": "^3.2.0",
109111
"p-retry": "^6.1.0",

packages/kad-dht/src/constants.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ export const hour = 60 * minute
1111

1212
export const MAX_RECORD_AGE = 36 * hour
1313

14-
export const LAN_PREFIX = '/lan'
15-
16-
export const PROTOCOL_PREFIX = '/ipfs'
17-
18-
export const PROTOCOL_DHT = '/kad/1.0.0'
14+
export const PROTOCOL = '/ipfs/kad/1.0.0'
1915

2016
export const RECORD_KEY_PREFIX = '/dht/record'
2117

@@ -39,19 +35,19 @@ export const K = 20
3935
export const ALPHA = 3
4036

4137
// How often we look for our closest DHT neighbours
42-
export const QUERY_SELF_INTERVAL = Number(5 * minute)
38+
export const QUERY_SELF_INTERVAL = 5 * minute
4339

4440
// How often we look for the first set of our closest DHT neighbours
45-
export const QUERY_SELF_INITIAL_INTERVAL = Number(Number(second))
41+
export const QUERY_SELF_INITIAL_INTERVAL = second
4642

4743
// How long to look for our closest DHT neighbours for
48-
export const QUERY_SELF_TIMEOUT = Number(5 * second)
44+
export const QUERY_SELF_TIMEOUT = 5 * second
4945

5046
// How often we try to find new peers
51-
export const TABLE_REFRESH_INTERVAL = Number(5 * minute)
47+
export const TABLE_REFRESH_INTERVAL = 5 * minute
5248

5349
// How how long to look for new peers for
54-
export const TABLE_REFRESH_QUERY_TIMEOUT = Number(30 * second)
50+
export const TABLE_REFRESH_QUERY_TIMEOUT = 30 * second
5551

5652
// When a timeout is not specified, run a query for this long
57-
export const DEFAULT_QUERY_TIMEOUT = Number(30 * second)
53+
export const DEFAULT_QUERY_TIMEOUT = 30 * second

packages/kad-dht/src/content-fetching/index.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
66
import {
77
ALPHA
88
} from '../constants.js'
9-
import { Message, MESSAGE_TYPE } from '../message/index.js'
9+
import { MessageType } from '../message/dht.js'
1010
import {
1111
valueEvent,
1212
queryErrorEvent
@@ -15,20 +15,21 @@ import { Libp2pRecord } from '../record/index.js'
1515
import { bestRecord } from '../record/selectors.js'
1616
import { verifyRecord } from '../record/validators.js'
1717
import { createPutRecord, bufferToRecordKey } from '../utils.js'
18-
import type { KadDHTComponents, Validators, Selectors, ValueEvent, QueryOptions, QueryEvent } from '../index.js'
18+
import type { KadDHTComponents, Validators, Selectors, ValueEvent, QueryEvent } from '../index.js'
19+
import type { Message } from '../message/dht.js'
1920
import type { Network } from '../network.js'
2021
import type { PeerRouting } from '../peer-routing/index.js'
2122
import type { QueryManager } from '../query/manager.js'
2223
import type { QueryFunc } from '../query/types.js'
23-
import type { AbortOptions, Logger } from '@libp2p/interface'
24+
import type { Logger, RoutingOptions } from '@libp2p/interface'
2425

2526
export interface ContentFetchingInit {
2627
validators: Validators
2728
selectors: Selectors
2829
peerRouting: PeerRouting
2930
queryManager: QueryManager
3031
network: Network
31-
lan: boolean
32+
logPrefix: string
3233
}
3334

3435
export class ContentFetching {
@@ -41,10 +42,10 @@ export class ContentFetching {
4142
private readonly network: Network
4243

4344
constructor (components: KadDHTComponents, init: ContentFetchingInit) {
44-
const { validators, selectors, peerRouting, queryManager, network, lan } = init
45+
const { validators, selectors, peerRouting, queryManager, network, logPrefix } = init
4546

4647
this.components = components
47-
this.log = components.logger.forComponent(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:content-fetching`)
48+
this.log = components.logger.forComponent(`${logPrefix}:content-fetching`)
4849
this.validators = validators
4950
this.selectors = selectors
5051
this.peerRouting = peerRouting
@@ -81,7 +82,7 @@ export class ContentFetching {
8182
/**
8283
* Send the best record found to any peers that have an out of date record
8384
*/
84-
async * sendCorrectionRecord (key: Uint8Array, vals: ValueEvent[], best: Uint8Array, options: AbortOptions = {}): AsyncGenerator<QueryEvent> {
85+
async * sendCorrectionRecord (key: Uint8Array, vals: ValueEvent[], best: Uint8Array, options: RoutingOptions = {}): AsyncGenerator<QueryEvent> {
8586
this.log('sendCorrection for %b', key)
8687
const fixupRec = createPutRecord(key, best)
8788

@@ -107,8 +108,11 @@ export class ContentFetching {
107108

108109
// send correction
109110
let sentCorrection = false
110-
const request = new Message(MESSAGE_TYPE.PUT_VALUE, key, 0)
111-
request.record = Libp2pRecord.deserialize(fixupRec)
111+
const request: Partial<Message> = {
112+
type: MessageType.PUT_VALUE,
113+
key,
114+
record: fixupRec
115+
}
112116

113117
for await (const event of this.network.sendRequest(from, request, options)) {
114118
if (event.name === 'PEER_RESPONSE' && (event.record != null) && uint8ArrayEquals(event.record.value, Libp2pRecord.deserialize(fixupRec).value)) {
@@ -129,7 +133,7 @@ export class ContentFetching {
129133
/**
130134
* Store the given key/value pair in the DHT
131135
*/
132-
async * put (key: Uint8Array, value: Uint8Array, options: AbortOptions = {}): AsyncGenerator<unknown, void, undefined> {
136+
async * put (key: Uint8Array, value: Uint8Array, options: RoutingOptions = {}): AsyncGenerator<unknown, void, undefined> {
133137
this.log('put key %b value %b', key, value)
134138

135139
// create record in the dht format
@@ -151,8 +155,11 @@ export class ContentFetching {
151155

152156
const events = []
153157

154-
const msg = new Message(MESSAGE_TYPE.PUT_VALUE, key, 0)
155-
msg.record = Libp2pRecord.deserialize(record)
158+
const msg: Partial<Message> = {
159+
type: MessageType.PUT_VALUE,
160+
key,
161+
record
162+
}
156163

157164
this.log('send put to %p', event.peer.id)
158165
for await (const putEvent of this.network.sendRequest(event.peer.id, msg, options)) {
@@ -185,7 +192,7 @@ export class ContentFetching {
185192
/**
186193
* Get the value to the given key
187194
*/
188-
async * get (key: Uint8Array, options: QueryOptions = {}): AsyncGenerator<QueryEvent | ValueEvent> {
195+
async * get (key: Uint8Array, options: RoutingOptions = {}): AsyncGenerator<QueryEvent | ValueEvent> {
189196
this.log('get %b', key)
190197

191198
const vals: ValueEvent[] = []
@@ -229,7 +236,7 @@ export class ContentFetching {
229236
/**
230237
* Get the `n` values to the given key without sorting
231238
*/
232-
async * getMany (key: Uint8Array, options: QueryOptions = {}): AsyncGenerator<QueryEvent> {
239+
async * getMany (key: Uint8Array, options: RoutingOptions = {}): AsyncGenerator<QueryEvent> {
233240
this.log('getMany values for %b', key)
234241

235242
try {

0 commit comments

Comments
 (0)