@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,95 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import errcode from 'err-code'
|
|
3
|
+
import { Message } from '../../message/index.js'
|
|
4
|
+
import {
|
|
5
|
+
removePrivateAddresses,
|
|
6
|
+
removePublicAddresses
|
|
7
|
+
} from '../../utils.js'
|
|
8
|
+
import { logger } from '@libp2p/logger'
|
|
9
|
+
import type { DHTMessageHandler } from '../index.js'
|
|
10
|
+
import type { Providers } from '../../providers.js'
|
|
11
|
+
import type { AddressBook } from '@libp2p/interfaces/peer-store'
|
|
12
|
+
import type { PeerRouting } from '../../peer-routing/index.js'
|
|
13
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
14
|
+
import type { PeerData } from '@libp2p/interfaces/peer-data'
|
|
15
|
+
|
|
16
|
+
const log = logger('libp2p:kad-dht:rpc:handlers:get-providers')
|
|
17
|
+
|
|
18
|
+
export interface GetProvidersHandlerOptions {
|
|
19
|
+
peerRouting: PeerRouting
|
|
20
|
+
providers: Providers
|
|
21
|
+
addressBook: AddressBook
|
|
22
|
+
lan: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class GetProvidersHandler implements DHTMessageHandler {
|
|
26
|
+
private readonly peerRouting: PeerRouting
|
|
27
|
+
private readonly providers: Providers
|
|
28
|
+
private readonly addressBook: AddressBook
|
|
29
|
+
private readonly lan: boolean
|
|
30
|
+
|
|
31
|
+
constructor (options: GetProvidersHandlerOptions) {
|
|
32
|
+
const { peerRouting, providers, addressBook, lan } = options
|
|
33
|
+
|
|
34
|
+
this.peerRouting = peerRouting
|
|
35
|
+
this.providers = providers
|
|
36
|
+
this.addressBook = addressBook
|
|
37
|
+
this.lan = Boolean(lan)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async handle (peerId: PeerId, msg: Message) {
|
|
41
|
+
let cid
|
|
42
|
+
try {
|
|
43
|
+
cid = CID.decode(msg.key)
|
|
44
|
+
} catch (err: any) {
|
|
45
|
+
throw errcode(new Error('Invalid CID'), 'ERR_INVALID_CID')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
log('%p asking for providers for %s', peerId, cid)
|
|
49
|
+
|
|
50
|
+
const [peers, closer] = await Promise.all([
|
|
51
|
+
this.providers.getProviders(cid),
|
|
52
|
+
this.peerRouting.getCloserPeersOffline(msg.key, peerId)
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
const providerPeers = await this._getPeers(peers)
|
|
56
|
+
const closerPeers = await this._getPeers(closer.map(({ id }) => id))
|
|
57
|
+
const response = new Message(msg.type, msg.key, msg.clusterLevel)
|
|
58
|
+
|
|
59
|
+
if (providerPeers.length > 0) {
|
|
60
|
+
response.providerPeers = providerPeers
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (closerPeers.length > 0) {
|
|
64
|
+
response.closerPeers = closerPeers
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
log('got %s providers %s closerPeers', providerPeers.length, closerPeers.length)
|
|
68
|
+
return response
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async _getAddresses (peerId: PeerId) {
|
|
72
|
+
const addrs = await this.addressBook.get(peerId)
|
|
73
|
+
|
|
74
|
+
return addrs.map(address => address.multiaddr)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async _getPeers (peerIds: PeerId[]) {
|
|
78
|
+
const output: PeerData[] = []
|
|
79
|
+
const addrFilter = this.lan ? removePublicAddresses : removePrivateAddresses
|
|
80
|
+
|
|
81
|
+
for (const peerId of peerIds) {
|
|
82
|
+
const peer = addrFilter({
|
|
83
|
+
id: peerId,
|
|
84
|
+
multiaddrs: await this._getAddresses(peerId),
|
|
85
|
+
protocols: []
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
if (peer.multiaddrs.length > 0) {
|
|
89
|
+
output.push(peer)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return output
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Libp2pRecord } from '@libp2p/record'
|
|
2
|
+
import errcode from 'err-code'
|
|
3
|
+
import { Message, MESSAGE_TYPE } from '../../message/index.js'
|
|
4
|
+
import {
|
|
5
|
+
MAX_RECORD_AGE
|
|
6
|
+
} from '../../constants.js'
|
|
7
|
+
import { bufferToRecordKey, isPublicKeyKey, fromPublicKeyKey } from '../../utils.js'
|
|
8
|
+
import { logger } from '@libp2p/logger'
|
|
9
|
+
import type { DHTMessageHandler } from '../index.js'
|
|
10
|
+
import type { Datastore } from 'interface-datastore'
|
|
11
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
12
|
+
import type { KeyBook } from '@libp2p/interfaces/peer-store'
|
|
13
|
+
import type { PeerRouting } from '../../peer-routing/index.js'
|
|
14
|
+
|
|
15
|
+
const log = logger('libp2p:kad-dht:rpc:handlers:get-value')
|
|
16
|
+
|
|
17
|
+
export interface GetValueHandlerOptions {
|
|
18
|
+
keyBook: KeyBook
|
|
19
|
+
peerRouting: PeerRouting
|
|
20
|
+
datastore: Datastore
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class GetValueHandler implements DHTMessageHandler {
|
|
24
|
+
private readonly keyBook: KeyBook
|
|
25
|
+
private readonly peerRouting: PeerRouting
|
|
26
|
+
private readonly datastore: Datastore
|
|
27
|
+
|
|
28
|
+
constructor (options: GetValueHandlerOptions) {
|
|
29
|
+
const { keyBook, peerRouting, datastore } = options
|
|
30
|
+
|
|
31
|
+
this.keyBook = keyBook
|
|
32
|
+
this.peerRouting = peerRouting
|
|
33
|
+
this.datastore = datastore
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async handle (peerId: PeerId, msg: Message) {
|
|
37
|
+
const key = msg.key
|
|
38
|
+
|
|
39
|
+
log('%p asked for key %b', peerId, key)
|
|
40
|
+
|
|
41
|
+
if (key == null || key.length === 0) {
|
|
42
|
+
throw errcode(new Error('Invalid key'), 'ERR_INVALID_KEY')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const response = new Message(MESSAGE_TYPE.GET_VALUE, key, msg.clusterLevel)
|
|
46
|
+
|
|
47
|
+
if (isPublicKeyKey(key)) {
|
|
48
|
+
log('is public key')
|
|
49
|
+
const idFromKey = fromPublicKeyKey(key)
|
|
50
|
+
let pubKey: Uint8Array | undefined
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const key = await this.keyBook.get(idFromKey)
|
|
54
|
+
|
|
55
|
+
if (key == null) {
|
|
56
|
+
throw errcode(new Error('No public key found in key book'), 'ERR_NOT_FOUND')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pubKey = key
|
|
60
|
+
} catch (err: any) {
|
|
61
|
+
if (err.code !== 'ERR_NOT_FOUND') {
|
|
62
|
+
throw err
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (pubKey != null) {
|
|
67
|
+
log('returning found public key')
|
|
68
|
+
response.record = new Libp2pRecord(key, pubKey)
|
|
69
|
+
return response
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const [record, closer] = await Promise.all([
|
|
74
|
+
this._checkLocalDatastore(key),
|
|
75
|
+
this.peerRouting.getCloserPeersOffline(msg.key, peerId)
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
if (record != null) {
|
|
79
|
+
log('had record for %b in local datastore', key)
|
|
80
|
+
response.record = record
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (closer.length > 0) {
|
|
84
|
+
log('had %s closer peers in routing table', closer.length)
|
|
85
|
+
response.closerPeers = closer
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return response
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Try to fetch a given record by from the local datastore.
|
|
93
|
+
* Returns the record iff it is still valid, meaning
|
|
94
|
+
* - it was either authored by this node, or
|
|
95
|
+
* - it was received less than `MAX_RECORD_AGE` ago.
|
|
96
|
+
*/
|
|
97
|
+
async _checkLocalDatastore (key: Uint8Array) {
|
|
98
|
+
log('checkLocalDatastore looking for %b', key)
|
|
99
|
+
const dsKey = bufferToRecordKey(key)
|
|
100
|
+
|
|
101
|
+
// Fetch value from ds
|
|
102
|
+
let rawRecord
|
|
103
|
+
try {
|
|
104
|
+
rawRecord = await this.datastore.get(dsKey)
|
|
105
|
+
} catch (err: any) {
|
|
106
|
+
if (err.code === 'ERR_NOT_FOUND') {
|
|
107
|
+
return undefined
|
|
108
|
+
}
|
|
109
|
+
throw err
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Create record from the returned bytes
|
|
113
|
+
const record = Libp2pRecord.deserialize(rawRecord)
|
|
114
|
+
|
|
115
|
+
if (record == null) {
|
|
116
|
+
throw errcode(new Error('Invalid record'), 'ERR_INVALID_RECORD')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check validity: compare time received with max record age
|
|
120
|
+
if (record.timeReceived == null ||
|
|
121
|
+
Date.now() - record.timeReceived.getTime() > MAX_RECORD_AGE) {
|
|
122
|
+
// If record is bad delete it and return
|
|
123
|
+
await this.datastore.delete(dsKey)
|
|
124
|
+
return undefined
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Record is valid
|
|
128
|
+
return record
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { logger } from '@libp2p/logger'
|
|
2
|
+
import type { Message } from '../../message/index.js'
|
|
3
|
+
import type { DHTMessageHandler } from '../index.js'
|
|
4
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
5
|
+
|
|
6
|
+
const log = logger('libp2p:kad-dht:rpc:handlers:ping')
|
|
7
|
+
|
|
8
|
+
export class PingHandler implements DHTMessageHandler {
|
|
9
|
+
async handle (peerId: PeerId, msg: Message) {
|
|
10
|
+
log('ping from %p', peerId)
|
|
11
|
+
return msg
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { bufferToRecordKey } from '../../utils.js'
|
|
2
|
+
import errcode from 'err-code'
|
|
3
|
+
import { verifyRecord } from '@libp2p/record/validators'
|
|
4
|
+
import { Logger, logger } from '@libp2p/logger'
|
|
5
|
+
import type { DHTMessageHandler } from '../index.js'
|
|
6
|
+
import type { Validators } from '@libp2p/interfaces/dht'
|
|
7
|
+
import type { Datastore } from 'interface-datastore'
|
|
8
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
9
|
+
import type { Message } from '../../message/index.js'
|
|
10
|
+
import { base58btc } from 'multiformats/bases/base58'
|
|
11
|
+
|
|
12
|
+
export interface PutValueHandlerOptions {
|
|
13
|
+
peerId: PeerId
|
|
14
|
+
validators: Validators
|
|
15
|
+
datastore: Datastore
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class PutValueHandler implements DHTMessageHandler {
|
|
19
|
+
private readonly validators: Validators
|
|
20
|
+
private readonly datastore: Datastore
|
|
21
|
+
private readonly log: Logger
|
|
22
|
+
|
|
23
|
+
constructor (options: PutValueHandlerOptions) {
|
|
24
|
+
const { validators, datastore, peerId } = options
|
|
25
|
+
|
|
26
|
+
this.log = logger('libp2p:kad-dht:rpc:handlers:put-value:' + peerId.toString())
|
|
27
|
+
|
|
28
|
+
this.validators = validators
|
|
29
|
+
this.datastore = datastore
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async handle (peerId: PeerId, msg: Message) {
|
|
33
|
+
const key = msg.key
|
|
34
|
+
this.log('%p asked us to store value for key %b', peerId, key)
|
|
35
|
+
|
|
36
|
+
const record = msg.record
|
|
37
|
+
|
|
38
|
+
if (record == null) {
|
|
39
|
+
const errMsg = `Empty record from: ${peerId.toString(base58btc)}`
|
|
40
|
+
|
|
41
|
+
this.log.error(errMsg)
|
|
42
|
+
throw errcode(new Error(errMsg), 'ERR_EMPTY_RECORD')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await verifyRecord(this.validators, record)
|
|
47
|
+
|
|
48
|
+
record.timeReceived = new Date()
|
|
49
|
+
const recordKey = bufferToRecordKey(record.key)
|
|
50
|
+
await this.datastore.put(recordKey, record.serialize())
|
|
51
|
+
this.log('put record for %b into datastore under key %k', key, recordKey)
|
|
52
|
+
} catch (err: any) {
|
|
53
|
+
this.log('did not put record for key %b into datastore %o', key, err)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return msg
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/rpc/index.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { pipe } from 'it-pipe'
|
|
2
|
+
import * as lp from 'it-length-prefixed'
|
|
3
|
+
import { Logger, logger } from '@libp2p/logger'
|
|
4
|
+
import type { RoutingTable } from '../routing-table'
|
|
5
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
6
|
+
import { Message, MESSAGE_TYPE, MESSAGE_TYPE_LOOKUP } from '../message/index.js'
|
|
7
|
+
import { AddProviderHandler } from './handlers/add-provider.js'
|
|
8
|
+
import { FindNodeHandler } from './handlers/find-node.js'
|
|
9
|
+
import { GetProvidersHandler } from './handlers/get-providers.js'
|
|
10
|
+
import { GetValueHandler } from './handlers/get-value.js'
|
|
11
|
+
import { PingHandler } from './handlers/ping.js'
|
|
12
|
+
import { PutValueHandler } from './handlers/put-value.js'
|
|
13
|
+
import type { IncomingStreamData } from '@libp2p/interfaces/registrar'
|
|
14
|
+
import type { KeyBook, AddressBook } from '@libp2p/interfaces/peer-store'
|
|
15
|
+
import type { Providers } from '../providers'
|
|
16
|
+
import type { PeerRouting } from '../peer-routing'
|
|
17
|
+
import type { Datastore } from 'interface-datastore'
|
|
18
|
+
import type { Validators } from '@libp2p/interfaces/dht'
|
|
19
|
+
|
|
20
|
+
export interface DHTMessageHandler {
|
|
21
|
+
handle: (peerId: PeerId, msg: Message) => Promise<Message | undefined>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RPCOptions {
|
|
25
|
+
peerId: PeerId
|
|
26
|
+
routingTable: RoutingTable
|
|
27
|
+
keyBook: KeyBook
|
|
28
|
+
addressBook: AddressBook
|
|
29
|
+
providers: Providers
|
|
30
|
+
peerRouting: PeerRouting
|
|
31
|
+
datastore: Datastore
|
|
32
|
+
validators: Validators
|
|
33
|
+
lan: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class RPC {
|
|
37
|
+
private readonly handlers: Record<number, DHTMessageHandler>
|
|
38
|
+
private readonly routingTable: RoutingTable
|
|
39
|
+
private readonly log: Logger
|
|
40
|
+
|
|
41
|
+
constructor (options: RPCOptions) {
|
|
42
|
+
const { keyBook, addressBook, providers, peerRouting, datastore, validators, lan, peerId } = options
|
|
43
|
+
|
|
44
|
+
this.log = logger('libp2p:kad-dht:rpc:' + peerId.toString())
|
|
45
|
+
|
|
46
|
+
this.routingTable = options.routingTable
|
|
47
|
+
this.handlers = {
|
|
48
|
+
[MESSAGE_TYPE.GET_VALUE]: new GetValueHandler({ keyBook, peerRouting, datastore }),
|
|
49
|
+
[MESSAGE_TYPE.PUT_VALUE]: new PutValueHandler({ peerId, validators, datastore }),
|
|
50
|
+
[MESSAGE_TYPE.FIND_NODE]: new FindNodeHandler({ peerRouting, lan }),
|
|
51
|
+
[MESSAGE_TYPE.ADD_PROVIDER]: new AddProviderHandler({ providers }),
|
|
52
|
+
[MESSAGE_TYPE.GET_PROVIDERS]: new GetProvidersHandler({ peerRouting, providers, addressBook, lan }),
|
|
53
|
+
[MESSAGE_TYPE.PING]: new PingHandler()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Process incoming DHT messages
|
|
59
|
+
*/
|
|
60
|
+
async handleMessage (peerId: PeerId, msg: Message) {
|
|
61
|
+
try {
|
|
62
|
+
await this.routingTable.add(peerId)
|
|
63
|
+
} catch (err: any) {
|
|
64
|
+
this.log.error('Failed to update the kbucket store', err)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// get handler & execute it
|
|
68
|
+
const handler = this.handlers[msg.type]
|
|
69
|
+
|
|
70
|
+
if (handler == null) {
|
|
71
|
+
this.log.error(`no handler found for message type: ${msg.type}`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return await handler.handle(peerId, msg)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Handle incoming streams on the dht protocol
|
|
80
|
+
*/
|
|
81
|
+
onIncomingStream (evt: CustomEvent<IncomingStreamData>) {
|
|
82
|
+
Promise.resolve().then(async () => {
|
|
83
|
+
const { stream, connection } = evt.detail
|
|
84
|
+
const peerId = connection.remotePeer
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
await this.routingTable.add(peerId)
|
|
88
|
+
} catch (err: any) {
|
|
89
|
+
this.log.error(err)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const self = this // eslint-disable-line @typescript-eslint/no-this-alias
|
|
93
|
+
|
|
94
|
+
await pipe(
|
|
95
|
+
stream.source,
|
|
96
|
+
lp.decode(),
|
|
97
|
+
source => (async function * () {
|
|
98
|
+
for await (const msg of source) {
|
|
99
|
+
// handle the message
|
|
100
|
+
const desMessage = Message.deserialize(msg.slice())
|
|
101
|
+
self.log('incoming %s from %p', MESSAGE_TYPE_LOOKUP[desMessage.type], peerId)
|
|
102
|
+
const res = await self.handleMessage(peerId, desMessage)
|
|
103
|
+
|
|
104
|
+
// Not all handlers will return a response
|
|
105
|
+
if (res != null) {
|
|
106
|
+
yield res.serialize()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})(),
|
|
110
|
+
lp.encode(),
|
|
111
|
+
stream.sink
|
|
112
|
+
)
|
|
113
|
+
})
|
|
114
|
+
.catch(err => {
|
|
115
|
+
this.log.error(err)
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Topology } from '@libp2p/topology'
|
|
2
|
+
import { CustomEvent, EventEmitter } from '@libp2p/interfaces'
|
|
3
|
+
import { logger } from '@libp2p/logger'
|
|
4
|
+
import type { Registrar } from '@libp2p/interfaces/registrar'
|
|
5
|
+
import type { Logger } from '@libp2p/logger'
|
|
6
|
+
import type { Startable } from '@libp2p/interfaces'
|
|
7
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
8
|
+
|
|
9
|
+
export interface TopologyListenerOptions {
|
|
10
|
+
registrar: Registrar
|
|
11
|
+
protocol: string
|
|
12
|
+
lan: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TopologyListenerEvents {
|
|
16
|
+
'peer': CustomEvent<PeerId>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Receives notifications of new peers joining the network that support the DHT protocol
|
|
21
|
+
*/
|
|
22
|
+
export class TopologyListener extends EventEmitter<TopologyListenerEvents> implements Startable {
|
|
23
|
+
private readonly log: Logger
|
|
24
|
+
private readonly registrar: Registrar
|
|
25
|
+
private readonly protocol: string
|
|
26
|
+
private running: boolean
|
|
27
|
+
private registrarId?: string
|
|
28
|
+
|
|
29
|
+
constructor (options: TopologyListenerOptions) {
|
|
30
|
+
super()
|
|
31
|
+
|
|
32
|
+
const { registrar, protocol, lan } = options
|
|
33
|
+
|
|
34
|
+
this.log = logger(`libp2p:kad-dht:topology-listener:${lan ? 'lan' : 'wan'}`)
|
|
35
|
+
this.running = false
|
|
36
|
+
this.registrar = registrar
|
|
37
|
+
this.protocol = protocol
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isStarted () {
|
|
41
|
+
return this.running
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Start the network
|
|
46
|
+
*/
|
|
47
|
+
async start () {
|
|
48
|
+
if (this.running) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.running = true
|
|
53
|
+
|
|
54
|
+
// register protocol with topology
|
|
55
|
+
const topology = new Topology({
|
|
56
|
+
onConnect: (peerId) => {
|
|
57
|
+
this.log('observed peer %p with protocol %s', this.protocol, peerId)
|
|
58
|
+
this.dispatchEvent(new CustomEvent('peer', {
|
|
59
|
+
detail: peerId
|
|
60
|
+
}))
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
this.registrarId = await this.registrar.register(this.protocol, topology)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Stop all network activity
|
|
68
|
+
*/
|
|
69
|
+
stop () {
|
|
70
|
+
this.running = false
|
|
71
|
+
|
|
72
|
+
// unregister protocol and handlers
|
|
73
|
+
if (this.registrarId != null) {
|
|
74
|
+
this.registrar.unregister(this.registrarId)
|
|
75
|
+
this.registrarId = undefined
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { sha256 } from 'multiformats/hashes/sha2'
|
|
2
|
+
import { Key } from 'interface-datastore/key'
|
|
3
|
+
import { Libp2pRecord } from '@libp2p/record'
|
|
4
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
5
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
6
|
+
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
|
|
7
|
+
import isPrivateIp from 'private-ip'
|
|
8
|
+
import type { PeerData } from '@libp2p/interfaces/peer-data'
|
|
9
|
+
import { peerIdFromBytes } from '@libp2p/peer-id'
|
|
10
|
+
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
|
11
|
+
import { RECORD_KEY_PREFIX } from './constants.js'
|
|
12
|
+
|
|
13
|
+
// const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
|
|
14
|
+
const PK_PREFIX = uint8ArrayFromString('/pk/')
|
|
15
|
+
|
|
16
|
+
export function removePrivateAddresses (peer: PeerData): PeerData {
|
|
17
|
+
return {
|
|
18
|
+
...peer,
|
|
19
|
+
multiaddrs: peer.multiaddrs.filter(multiaddr => {
|
|
20
|
+
const [[type, addr]] = multiaddr.stringTuples()
|
|
21
|
+
|
|
22
|
+
if (type !== 4 && type !== 6) {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// @ts-expect-error types are wrong https://github.com/frenchbread/private-ip/issues/18
|
|
27
|
+
return !isPrivateIp(addr)
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function removePublicAddresses (peer: PeerData): PeerData {
|
|
33
|
+
return {
|
|
34
|
+
...peer,
|
|
35
|
+
multiaddrs: peer.multiaddrs.filter(multiaddr => {
|
|
36
|
+
const [[type, addr]] = multiaddr.stringTuples()
|
|
37
|
+
|
|
38
|
+
if (type !== 4 && type !== 6) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// @ts-expect-error types are wrong https://github.com/frenchbread/private-ip/issues/18
|
|
43
|
+
return isPrivateIp(addr)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a DHT ID by hashing a given Uint8Array
|
|
50
|
+
*/
|
|
51
|
+
export async function convertBuffer (buf: Uint8Array) {
|
|
52
|
+
const multihash = await sha256.digest(buf)
|
|
53
|
+
|
|
54
|
+
return multihash.digest
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates a DHT ID by hashing a Peer ID
|
|
59
|
+
*/
|
|
60
|
+
export async function convertPeerId (peerId: PeerId) {
|
|
61
|
+
return await convertBuffer(peerId.toBytes())
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Convert a Uint8Array to their SHA2-256 hash
|
|
66
|
+
*/
|
|
67
|
+
export function bufferToKey (buf: Uint8Array) {
|
|
68
|
+
return new Key('/' + uint8ArrayToString(buf, 'base32'), false)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Convert a Uint8Array to their SHA2-256 hash
|
|
73
|
+
*/
|
|
74
|
+
export function bufferToRecordKey (buf: Uint8Array) {
|
|
75
|
+
return new Key(`${RECORD_KEY_PREFIX}/${uint8ArrayToString(buf, 'base32')}`, false)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate the key for a public key.
|
|
80
|
+
*/
|
|
81
|
+
export function keyForPublicKey (peer: PeerId) {
|
|
82
|
+
return uint8ArrayConcat([
|
|
83
|
+
PK_PREFIX,
|
|
84
|
+
peer.toBytes()
|
|
85
|
+
])
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function isPublicKeyKey (key: Uint8Array) {
|
|
89
|
+
return uint8ArrayToString(key.slice(0, 4)) === '/pk/'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function isIPNSKey (key: Uint8Array) {
|
|
93
|
+
return uint8ArrayToString(key.slice(0, 4)) === '/ipns/'
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function fromPublicKeyKey (key: Uint8Array) {
|
|
97
|
+
return peerIdFromBytes(key.slice(4))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a new put record, encodes and signs it if enabled
|
|
102
|
+
*/
|
|
103
|
+
export function createPutRecord (key: Uint8Array, value: Uint8Array) {
|
|
104
|
+
const timeReceived = new Date()
|
|
105
|
+
const rec = new Libp2pRecord(key, value, timeReceived)
|
|
106
|
+
|
|
107
|
+
return rec.serialize()
|
|
108
|
+
}
|