@libp2p/kad-dht 0.28.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +4 -0
- package/README.md +105 -0
- package/dist/src/constants.d.ts +20 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +34 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/content-fetching/index.d.ts +55 -0
- package/dist/src/content-fetching/index.d.ts.map +1 -0
- package/dist/src/content-fetching/index.js +190 -0
- package/dist/src/content-fetching/index.js.map +1 -0
- package/dist/src/content-routing/index.d.ts +42 -0
- package/dist/src/content-routing/index.d.ts.map +1 -0
- package/dist/src/content-routing/index.js +129 -0
- package/dist/src/content-routing/index.js.map +1 -0
- package/dist/src/dual-kad-dht.d.ts +65 -0
- package/dist/src/dual-kad-dht.d.ts.map +1 -0
- package/dist/src/dual-kad-dht.js +191 -0
- package/dist/src/dual-kad-dht.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +15 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/kad-dht.d.ts +131 -0
- package/dist/src/kad-dht.d.ts.map +1 -0
- package/dist/src/kad-dht.js +268 -0
- package/dist/src/kad-dht.js.map +1 -0
- package/dist/src/message/dht.d.ts +297 -0
- package/dist/src/message/dht.js +921 -0
- package/dist/src/message/index.d.ts +32 -0
- package/dist/src/message/index.d.ts.map +1 -0
- package/dist/src/message/index.js +81 -0
- package/dist/src/message/index.js.map +1 -0
- package/dist/src/network.d.ts +60 -0
- package/dist/src/network.d.ts.map +1 -0
- package/dist/src/network.js +124 -0
- package/dist/src/network.js.map +1 -0
- package/dist/src/peer-list/index.d.ts +29 -0
- package/dist/src/peer-list/index.d.ts.map +1 -0
- package/dist/src/peer-list/index.js +44 -0
- package/dist/src/peer-list/index.js.map +1 -0
- package/dist/src/peer-list/peer-distance-list.d.ts +34 -0
- package/dist/src/peer-list/peer-distance-list.d.ts.map +1 -0
- package/dist/src/peer-list/peer-distance-list.js +64 -0
- package/dist/src/peer-list/peer-distance-list.js.map +1 -0
- package/dist/src/peer-routing/index.d.ts +71 -0
- package/dist/src/peer-routing/index.d.ts.map +1 -0
- package/dist/src/peer-routing/index.js +256 -0
- package/dist/src/peer-routing/index.js.map +1 -0
- package/dist/src/providers.d.ts +64 -0
- package/dist/src/providers.d.ts.map +1 -0
- package/dist/src/providers.js +208 -0
- package/dist/src/providers.js.map +1 -0
- package/dist/src/query/events.d.ts +46 -0
- package/dist/src/query/events.d.ts.map +1 -0
- package/dist/src/query/events.js +73 -0
- package/dist/src/query/events.js.map +1 -0
- package/dist/src/query/manager.d.ts +40 -0
- package/dist/src/query/manager.d.ts.map +1 -0
- package/dist/src/query/manager.js +140 -0
- package/dist/src/query/manager.js.map +1 -0
- package/dist/src/query/query-path.d.ts +58 -0
- package/dist/src/query/query-path.d.ts.map +1 -0
- package/dist/src/query/query-path.js +171 -0
- package/dist/src/query/query-path.js.map +1 -0
- package/dist/src/query/types.d.ts +16 -0
- package/dist/src/query/types.d.ts.map +1 -0
- package/dist/src/query/types.js +2 -0
- package/dist/src/query/types.js.map +1 -0
- package/dist/src/query-self.d.ts +31 -0
- package/dist/src/query-self.d.ts.map +1 -0
- package/dist/src/query-self.js +73 -0
- package/dist/src/query-self.js.map +1 -0
- package/dist/src/routing-table/generated-prefix-list-browser.d.ts +3 -0
- package/dist/src/routing-table/generated-prefix-list-browser.d.ts.map +1 -0
- package/dist/src/routing-table/generated-prefix-list-browser.js +1027 -0
- package/dist/src/routing-table/generated-prefix-list-browser.js.map +1 -0
- package/dist/src/routing-table/generated-prefix-list.d.ts +3 -0
- package/dist/src/routing-table/generated-prefix-list.d.ts.map +1 -0
- package/dist/src/routing-table/generated-prefix-list.js +4099 -0
- package/dist/src/routing-table/generated-prefix-list.js.map +1 -0
- package/dist/src/routing-table/index.d.ts +91 -0
- package/dist/src/routing-table/index.d.ts.map +1 -0
- package/dist/src/routing-table/index.js +183 -0
- package/dist/src/routing-table/index.js.map +1 -0
- package/dist/src/routing-table/refresh.d.ts +50 -0
- package/dist/src/routing-table/refresh.d.ts.map +1 -0
- package/dist/src/routing-table/refresh.js +204 -0
- package/dist/src/routing-table/refresh.js.map +1 -0
- package/dist/src/routing-table/types.d.ts +24 -0
- package/dist/src/routing-table/types.d.ts.map +1 -0
- package/dist/src/rpc/handlers/add-provider.d.ts +13 -0
- package/dist/src/rpc/handlers/add-provider.d.ts.map +1 -0
- package/dist/src/rpc/handlers/add-provider.js +42 -0
- package/dist/src/rpc/handlers/add-provider.js.map +1 -0
- package/dist/src/rpc/handlers/find-node.d.ts +18 -0
- package/dist/src/rpc/handlers/find-node.d.ts.map +1 -0
- package/dist/src/rpc/handlers/find-node.js +32 -0
- package/dist/src/rpc/handlers/find-node.js.map +1 -0
- package/dist/src/rpc/handlers/get-providers.d.ts +24 -0
- package/dist/src/rpc/handlers/get-providers.d.ts.map +1 -0
- package/dist/src/rpc/handlers/get-providers.js +60 -0
- package/dist/src/rpc/handlers/get-providers.js.map +1 -0
- package/dist/src/rpc/handlers/get-value.d.ts +27 -0
- package/dist/src/rpc/handlers/get-value.d.ts.map +1 -0
- package/dist/src/rpc/handlers/get-value.js +94 -0
- package/dist/src/rpc/handlers/get-value.js.map +1 -0
- package/dist/src/rpc/handlers/index.d.ts +13 -0
- package/dist/src/rpc/handlers/index.d.ts.map +1 -0
- package/dist/src/rpc/handlers/ping.d.ts +7 -0
- package/dist/src/rpc/handlers/ping.d.ts.map +1 -0
- package/dist/src/rpc/handlers/ping.js +9 -0
- package/dist/src/rpc/handlers/ping.js.map +1 -0
- package/dist/src/rpc/handlers/put-value.d.ts +18 -0
- package/dist/src/rpc/handlers/put-value.d.ts.map +1 -0
- package/dist/src/rpc/handlers/put-value.js +35 -0
- package/dist/src/rpc/handlers/put-value.js.map +1 -0
- package/dist/src/rpc/index.d.ts +38 -0
- package/dist/src/rpc/index.d.ts.map +1 -0
- package/dist/src/rpc/index.js +75 -0
- package/dist/src/rpc/index.js.map +1 -0
- package/dist/src/rpc/types.d.ts +6 -0
- package/dist/src/rpc/types.d.ts.map +1 -0
- package/dist/src/topology-listener.d.ts +33 -0
- package/dist/src/topology-listener.d.ts.map +1 -0
- package/dist/src/topology-listener.js +50 -0
- package/dist/src/topology-listener.js.map +1 -0
- package/dist/src/types.d.ts +143 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/utils.d.ts +33 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +89 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +200 -0
- package/src/constants.ts +50 -0
- package/src/content-fetching/index.ts +276 -0
- package/src/content-routing/index.ts +202 -0
- package/src/dual-kad-dht.ts +257 -0
- package/src/index.ts +21 -0
- package/src/kad-dht.ts +396 -0
- package/src/message/dht.d.ts +297 -0
- package/src/message/dht.js +921 -0
- package/src/message/dht.proto +75 -0
- package/src/message/index.ts +111 -0
- package/src/network.ts +185 -0
- package/src/peer-list/index.ts +54 -0
- package/src/peer-list/peer-distance-list.ts +93 -0
- package/src/peer-routing/index.ts +332 -0
- package/src/providers.ts +278 -0
- package/src/query/events.ts +126 -0
- package/src/query/manager.ts +188 -0
- package/src/query/query-path.ts +263 -0
- package/src/query/types.ts +22 -0
- package/src/query-self.ts +106 -0
- package/src/routing-table/generated-prefix-list-browser.ts +1026 -0
- package/src/routing-table/generated-prefix-list.ts +4098 -0
- package/src/routing-table/index.ts +265 -0
- package/src/routing-table/refresh.ts +263 -0
- package/src/rpc/handlers/add-provider.ts +63 -0
- package/src/rpc/handlers/find-node.ts +57 -0
- package/src/rpc/handlers/get-providers.ts +95 -0
- package/src/rpc/handlers/get-value.ts +130 -0
- package/src/rpc/handlers/ping.ts +13 -0
- package/src/rpc/handlers/put-value.ts +58 -0
- package/src/rpc/index.ts +118 -0
- package/src/topology-listener.ts +78 -0
- package/src/utils.ts +108 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import errcode from 'err-code'
|
|
2
|
+
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
3
|
+
import { Libp2pRecord } from '@libp2p/record'
|
|
4
|
+
import { verifyRecord } from '@libp2p/record/validators'
|
|
5
|
+
import { bestRecord } from '@libp2p/record/selectors'
|
|
6
|
+
import parallel from 'it-parallel'
|
|
7
|
+
import map from 'it-map'
|
|
8
|
+
import {
|
|
9
|
+
valueEvent,
|
|
10
|
+
queryErrorEvent
|
|
11
|
+
} from '../query/events.js'
|
|
12
|
+
import { Message, MESSAGE_TYPE } from '../message/index.js'
|
|
13
|
+
import { pipe } from 'it-pipe'
|
|
14
|
+
import {
|
|
15
|
+
ALPHA
|
|
16
|
+
} from '../constants.js'
|
|
17
|
+
import { createPutRecord, convertBuffer, bufferToRecordKey } from '../utils.js'
|
|
18
|
+
import { logger } from '@libp2p/logger'
|
|
19
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
20
|
+
import type { Validators, Selectors, ValueEvent, QueryOptions } from '@libp2p/interfaces/dht'
|
|
21
|
+
import type { Datastore } from 'interface-datastore'
|
|
22
|
+
import type { PeerRouting } from '../peer-routing/index.js'
|
|
23
|
+
import type { QueryManager } from '../query/manager.js'
|
|
24
|
+
import type { RoutingTable } from '../routing-table/index.js'
|
|
25
|
+
import type { Network } from '../network.js'
|
|
26
|
+
import type { Logger } from '@libp2p/logger'
|
|
27
|
+
import type { AbortOptions } from '@libp2p/interfaces'
|
|
28
|
+
import type { QueryFunc } from '../query/types.js'
|
|
29
|
+
|
|
30
|
+
export interface ContentFetchingOptions {
|
|
31
|
+
peerId: PeerId
|
|
32
|
+
datastore: Datastore
|
|
33
|
+
validators: Validators
|
|
34
|
+
selectors: Selectors
|
|
35
|
+
peerRouting: PeerRouting
|
|
36
|
+
queryManager: QueryManager
|
|
37
|
+
routingTable: RoutingTable
|
|
38
|
+
network: Network
|
|
39
|
+
lan: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class ContentFetching {
|
|
43
|
+
private readonly log: Logger
|
|
44
|
+
private readonly peerId: PeerId
|
|
45
|
+
private readonly datastore: Datastore
|
|
46
|
+
private readonly validators: Validators
|
|
47
|
+
private readonly selectors: Selectors
|
|
48
|
+
private readonly peerRouting: PeerRouting
|
|
49
|
+
private readonly queryManager: QueryManager
|
|
50
|
+
private readonly routingTable: RoutingTable
|
|
51
|
+
private readonly network: Network
|
|
52
|
+
|
|
53
|
+
constructor (options: ContentFetchingOptions) {
|
|
54
|
+
const { peerId, datastore, validators, selectors, peerRouting, queryManager, routingTable, network, lan } = options
|
|
55
|
+
this.log = logger(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:content-fetching:${peerId.toString()}`)
|
|
56
|
+
this.peerId = peerId
|
|
57
|
+
this.datastore = datastore
|
|
58
|
+
this.validators = validators
|
|
59
|
+
this.selectors = selectors
|
|
60
|
+
this.peerRouting = peerRouting
|
|
61
|
+
this.queryManager = queryManager
|
|
62
|
+
this.routingTable = routingTable
|
|
63
|
+
this.network = network
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async putLocal (key: Uint8Array, rec: Uint8Array) { // eslint-disable-line require-await
|
|
67
|
+
const dsKey = bufferToRecordKey(key)
|
|
68
|
+
await this.datastore.put(dsKey, rec)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Attempt to retrieve the value for the given key from
|
|
73
|
+
* the local datastore
|
|
74
|
+
*/
|
|
75
|
+
async getLocal (key: Uint8Array) {
|
|
76
|
+
this.log('getLocal %b', key)
|
|
77
|
+
|
|
78
|
+
const dsKey = bufferToRecordKey(key)
|
|
79
|
+
|
|
80
|
+
this.log('fetching record for key %k', dsKey)
|
|
81
|
+
|
|
82
|
+
const raw = await this.datastore.get(dsKey)
|
|
83
|
+
this.log('found %k in local datastore', dsKey)
|
|
84
|
+
|
|
85
|
+
const rec = Libp2pRecord.deserialize(raw)
|
|
86
|
+
|
|
87
|
+
await verifyRecord(this.validators, rec)
|
|
88
|
+
|
|
89
|
+
return rec
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Send the best record found to any peers that have an out of date record
|
|
94
|
+
*/
|
|
95
|
+
async * sendCorrectionRecord (key: Uint8Array, vals: ValueEvent[], best: Uint8Array, options: AbortOptions = {}) {
|
|
96
|
+
this.log('sendCorrection for %b', key)
|
|
97
|
+
const fixupRec = await createPutRecord(key, best)
|
|
98
|
+
|
|
99
|
+
for (const { value, from } of vals) {
|
|
100
|
+
// no need to do anything
|
|
101
|
+
if (uint8ArrayEquals(value, best)) {
|
|
102
|
+
this.log('record was ok')
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// correct ourself
|
|
107
|
+
if (this.peerId.equals(from)) {
|
|
108
|
+
try {
|
|
109
|
+
const dsKey = bufferToRecordKey(key)
|
|
110
|
+
this.log(`Storing corrected record for key ${dsKey.toString()}`)
|
|
111
|
+
await this.datastore.put(dsKey, fixupRec)
|
|
112
|
+
} catch (err: any) {
|
|
113
|
+
this.log.error('Failed error correcting self', err)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// send correction
|
|
120
|
+
let sentCorrection = false
|
|
121
|
+
const request = new Message(MESSAGE_TYPE.PUT_VALUE, key, 0)
|
|
122
|
+
request.record = Libp2pRecord.deserialize(fixupRec)
|
|
123
|
+
|
|
124
|
+
for await (const event of this.network.sendRequest(from, request, options)) {
|
|
125
|
+
if (event.name === 'PEER_RESPONSE' && (event.record != null) && uint8ArrayEquals(event.record.value, Libp2pRecord.deserialize(fixupRec).value)) {
|
|
126
|
+
sentCorrection = true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
yield event
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!sentCorrection) {
|
|
133
|
+
yield queryErrorEvent({ from, error: errcode(new Error('value not put correctly'), 'ERR_PUT_VALUE_INVALID') })
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.log.error('Failed error correcting entry')
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Store the given key/value pair in the DHT
|
|
142
|
+
*/
|
|
143
|
+
async * put (key: Uint8Array, value: Uint8Array, options: AbortOptions = {}) {
|
|
144
|
+
this.log('put key %b value %b', key, value)
|
|
145
|
+
|
|
146
|
+
// create record in the dht format
|
|
147
|
+
const record = await createPutRecord(key, value)
|
|
148
|
+
|
|
149
|
+
// store the record locally
|
|
150
|
+
const dsKey = bufferToRecordKey(key)
|
|
151
|
+
this.log(`storing record for key ${dsKey.toString()}`)
|
|
152
|
+
await this.datastore.put(dsKey, record)
|
|
153
|
+
|
|
154
|
+
// put record to the closest peers
|
|
155
|
+
yield * pipe(
|
|
156
|
+
this.peerRouting.getClosestPeers(key, { signal: options.signal }),
|
|
157
|
+
(source) => map(source, (event) => {
|
|
158
|
+
return async () => {
|
|
159
|
+
if (event.name !== 'FINAL_PEER') {
|
|
160
|
+
return [event]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const events = []
|
|
164
|
+
|
|
165
|
+
const msg = new Message(MESSAGE_TYPE.PUT_VALUE, key, 0)
|
|
166
|
+
msg.record = Libp2pRecord.deserialize(record)
|
|
167
|
+
|
|
168
|
+
for await (const putEvent of this.network.sendRequest(event.peer.id, msg, options)) {
|
|
169
|
+
events.push(putEvent)
|
|
170
|
+
|
|
171
|
+
if (putEvent.name !== 'PEER_RESPONSE') {
|
|
172
|
+
continue
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!(putEvent.record != null && uint8ArrayEquals(putEvent.record.value, Libp2pRecord.deserialize(record).value))) {
|
|
176
|
+
events.push(queryErrorEvent({ from: event.peer.id, error: errcode(new Error('value not put correctly'), 'ERR_PUT_VALUE_INVALID') }))
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return events
|
|
181
|
+
}
|
|
182
|
+
}),
|
|
183
|
+
(source) => parallel(source, {
|
|
184
|
+
ordered: false,
|
|
185
|
+
concurrency: ALPHA
|
|
186
|
+
}),
|
|
187
|
+
async function * (source) {
|
|
188
|
+
for await (const events of source) {
|
|
189
|
+
yield * events
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get the value to the given key
|
|
197
|
+
*/
|
|
198
|
+
async * get (key: Uint8Array, options: QueryOptions = {}) {
|
|
199
|
+
this.log('get %b', key)
|
|
200
|
+
|
|
201
|
+
const vals: ValueEvent[] = []
|
|
202
|
+
|
|
203
|
+
for await (const event of this.getMany(key, options)) {
|
|
204
|
+
if (event.name === 'VALUE') {
|
|
205
|
+
vals.push(event)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
yield event
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (vals.length === 0) {
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const records = vals.map((v) => v.value)
|
|
216
|
+
let i = 0
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
i = bestRecord(this.selectors, key, records)
|
|
220
|
+
} catch (err: any) {
|
|
221
|
+
// Assume the first record if no selector available
|
|
222
|
+
if (err.code !== 'ERR_NO_SELECTOR_FUNCTION_FOR_RECORD_KEY') {
|
|
223
|
+
throw err
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const best = records[i]
|
|
228
|
+
this.log('GetValue %b %b', key, best)
|
|
229
|
+
|
|
230
|
+
if (best == null) {
|
|
231
|
+
throw errcode(new Error('best value was not found'), 'ERR_NOT_FOUND')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
yield * this.sendCorrectionRecord(key, vals, best, options)
|
|
235
|
+
|
|
236
|
+
yield vals[i]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get the `n` values to the given key without sorting
|
|
241
|
+
*/
|
|
242
|
+
async * getMany (key: Uint8Array, options: QueryOptions = {}) {
|
|
243
|
+
this.log('getMany values for %b', key)
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const localRec = await this.getLocal(key)
|
|
247
|
+
|
|
248
|
+
yield valueEvent({
|
|
249
|
+
value: localRec.value,
|
|
250
|
+
from: this.peerId
|
|
251
|
+
})
|
|
252
|
+
} catch (err: any) {
|
|
253
|
+
this.log('error getting local value for %b', key, err)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const id = await convertBuffer(key)
|
|
257
|
+
const rtp = this.routingTable.closestPeers(id)
|
|
258
|
+
|
|
259
|
+
this.log('found %d peers in routing table', rtp.length)
|
|
260
|
+
|
|
261
|
+
const self = this // eslint-disable-line @typescript-eslint/no-this-alias
|
|
262
|
+
|
|
263
|
+
const getValueQuery: QueryFunc = async function * ({ peer, signal }) {
|
|
264
|
+
for await (const event of self.peerRouting.getValueOrPeers(peer, key, { signal })) {
|
|
265
|
+
yield event
|
|
266
|
+
|
|
267
|
+
if (event.name === 'PEER_RESPONSE' && (event.record != null)) {
|
|
268
|
+
yield valueEvent({ from: peer, value: event.record.value })
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// we have peers, lets send the actual query to them
|
|
274
|
+
yield * this.queryManager.run(key, rtp, getValueQuery, options)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Message, MESSAGE_TYPE } from '../message/index.js'
|
|
2
|
+
import parallel from 'it-parallel'
|
|
3
|
+
import map from 'it-map'
|
|
4
|
+
import { convertBuffer } from '../utils.js'
|
|
5
|
+
import { ALPHA } from '../constants.js'
|
|
6
|
+
import { pipe } from 'it-pipe'
|
|
7
|
+
import {
|
|
8
|
+
queryErrorEvent,
|
|
9
|
+
peerResponseEvent,
|
|
10
|
+
providerEvent
|
|
11
|
+
} from '../query/events.js'
|
|
12
|
+
import { logger } from '@libp2p/logger'
|
|
13
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
14
|
+
import type { QueryEvent, QueryOptions } from '@libp2p/interfaces/dht'
|
|
15
|
+
import type { PeerRouting } from '../peer-routing/index.js'
|
|
16
|
+
import type { QueryManager } from '../query/manager.js'
|
|
17
|
+
import type { RoutingTable } from '../routing-table/index.js'
|
|
18
|
+
import type { Network } from '../network.js'
|
|
19
|
+
import type { Logger } from '@libp2p/logger'
|
|
20
|
+
import type { Providers } from '../providers.js'
|
|
21
|
+
import type { PeerStore } from '@libp2p/interfaces/peer-store'
|
|
22
|
+
import type { QueryFunc } from '../query/types.js'
|
|
23
|
+
import type { CID } from 'multiformats/cid'
|
|
24
|
+
import type { AbortOptions } from '@libp2p/interfaces'
|
|
25
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
26
|
+
import type { PeerData } from '@libp2p/interfaces/peer-data'
|
|
27
|
+
import { base58btc } from 'multiformats/bases/base58'
|
|
28
|
+
|
|
29
|
+
export interface ContentRoutingOptions {
|
|
30
|
+
peerId: PeerId
|
|
31
|
+
network: Network
|
|
32
|
+
peerRouting: PeerRouting
|
|
33
|
+
queryManager: QueryManager
|
|
34
|
+
routingTable: RoutingTable
|
|
35
|
+
providers: Providers
|
|
36
|
+
peerStore: PeerStore
|
|
37
|
+
lan: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class ContentRouting {
|
|
41
|
+
private readonly log: Logger
|
|
42
|
+
private readonly peerId: PeerId
|
|
43
|
+
private readonly network: Network
|
|
44
|
+
private readonly peerRouting: PeerRouting
|
|
45
|
+
private readonly queryManager: QueryManager
|
|
46
|
+
private readonly routingTable: RoutingTable
|
|
47
|
+
private readonly providers: Providers
|
|
48
|
+
private readonly peerStore: PeerStore
|
|
49
|
+
|
|
50
|
+
constructor (options: ContentRoutingOptions) {
|
|
51
|
+
const { peerId, network, peerRouting, queryManager, routingTable, providers, peerStore, lan } = options
|
|
52
|
+
|
|
53
|
+
this.log = logger(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:content-routing`)
|
|
54
|
+
this.peerId = peerId
|
|
55
|
+
this.network = network
|
|
56
|
+
this.peerRouting = peerRouting
|
|
57
|
+
this.queryManager = queryManager
|
|
58
|
+
this.routingTable = routingTable
|
|
59
|
+
this.providers = providers
|
|
60
|
+
this.peerStore = peerStore
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Announce to the network that we can provide the value for a given key and
|
|
65
|
+
* are contactable on the given multiaddrs
|
|
66
|
+
*/
|
|
67
|
+
async * provide (key: CID, multiaddrs: Multiaddr[], options: AbortOptions = {}) {
|
|
68
|
+
this.log('provide %s', key)
|
|
69
|
+
|
|
70
|
+
// Add peer as provider
|
|
71
|
+
await this.providers.addProvider(key, this.peerId)
|
|
72
|
+
|
|
73
|
+
const msg = new Message(MESSAGE_TYPE.ADD_PROVIDER, key.bytes, 0)
|
|
74
|
+
msg.providerPeers = [{
|
|
75
|
+
id: this.peerId,
|
|
76
|
+
multiaddrs,
|
|
77
|
+
protocols: []
|
|
78
|
+
}]
|
|
79
|
+
|
|
80
|
+
let sent = 0
|
|
81
|
+
|
|
82
|
+
const maybeNotifyPeer = (event: QueryEvent) => {
|
|
83
|
+
return async () => {
|
|
84
|
+
if (event.name !== 'FINAL_PEER') {
|
|
85
|
+
return [event]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const events = []
|
|
89
|
+
|
|
90
|
+
this.log('putProvider %s to %p', key, event.peer.id)
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
this.log('sending provider record for %s to %p', key, event.peer.id)
|
|
94
|
+
|
|
95
|
+
for await (const sendEvent of this.network.sendMessage(event.peer.id, msg, options)) {
|
|
96
|
+
if (sendEvent.name === 'PEER_RESPONSE') {
|
|
97
|
+
this.log('sent provider record for %s to %p', key, event.peer.id)
|
|
98
|
+
sent++
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
events.push(sendEvent)
|
|
102
|
+
}
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
this.log.error('error sending provide record to peer %p', event.peer.id, err)
|
|
105
|
+
events.push(queryErrorEvent({ from: event.peer.id, error: err }))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return events
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Notify closest peers
|
|
113
|
+
yield * pipe(
|
|
114
|
+
this.peerRouting.getClosestPeers(key.multihash.bytes, options),
|
|
115
|
+
(source) => map(source, (event) => maybeNotifyPeer(event)),
|
|
116
|
+
(source) => parallel(source, {
|
|
117
|
+
ordered: false,
|
|
118
|
+
concurrency: ALPHA
|
|
119
|
+
}),
|
|
120
|
+
async function * (source) {
|
|
121
|
+
for await (const events of source) {
|
|
122
|
+
yield * events
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
this.log('sent provider records to %d peers', sent)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Search the dht for up to `K` providers of the given CID.
|
|
132
|
+
*/
|
|
133
|
+
async * findProviders (key: CID, options: QueryOptions) {
|
|
134
|
+
const toFind = this.routingTable.kBucketSize
|
|
135
|
+
const target = key.multihash.bytes
|
|
136
|
+
const id = await convertBuffer(target)
|
|
137
|
+
const self = this // eslint-disable-line @typescript-eslint/no-this-alias
|
|
138
|
+
|
|
139
|
+
this.log('findProviders %c', key)
|
|
140
|
+
|
|
141
|
+
const provs = await this.providers.getProviders(key)
|
|
142
|
+
|
|
143
|
+
// yield values if we have some, also slice because maybe we got lucky and already have too many?
|
|
144
|
+
if (provs.length > 0) {
|
|
145
|
+
const providers: PeerData[] = []
|
|
146
|
+
|
|
147
|
+
for (const peerId of provs.slice(0, toFind)) {
|
|
148
|
+
providers.push({
|
|
149
|
+
id: peerId,
|
|
150
|
+
multiaddrs: ((await this.peerStore.addressBook.get(peerId)) ?? []).map(address => address.multiaddr),
|
|
151
|
+
protocols: []
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
yield peerResponseEvent({ from: this.peerId, messageType: MESSAGE_TYPE.GET_PROVIDERS, providers })
|
|
156
|
+
yield providerEvent({ from: this.peerId, providers: providers })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// All done
|
|
160
|
+
if (provs.length >= toFind) {
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The query function to use on this particular disjoint path
|
|
166
|
+
*/
|
|
167
|
+
const findProvidersQuery: QueryFunc = async function * ({ peer, signal }) {
|
|
168
|
+
const request = new Message(MESSAGE_TYPE.GET_PROVIDERS, target, 0)
|
|
169
|
+
|
|
170
|
+
yield * self.network.sendRequest(peer, request, { signal })
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const providers = new Set(provs.map(p => p.toString(base58btc)))
|
|
174
|
+
|
|
175
|
+
for await (const event of this.queryManager.run(target, this.routingTable.closestPeers(id), findProvidersQuery, options)) {
|
|
176
|
+
yield event
|
|
177
|
+
|
|
178
|
+
if (event.name === 'PEER_RESPONSE') {
|
|
179
|
+
this.log('Found %d provider entries for %c and %d closer peers', event.providers.length, key, event.closer.length)
|
|
180
|
+
|
|
181
|
+
const newProviders = []
|
|
182
|
+
|
|
183
|
+
for (const peer of event.providers) {
|
|
184
|
+
if (providers.has(peer.id.toString(base58btc))) {
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
providers.add(peer.id.toString(base58btc))
|
|
189
|
+
newProviders.push(peer)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (newProviders.length > 0) {
|
|
193
|
+
yield providerEvent({ from: event.from, providers: newProviders })
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (providers.size === toFind) {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|