@libp2p/daemon-client 0.0.0
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 +84 -0
- package/dist/src/dht.d.ts +40 -0
- package/dist/src/dht.d.ts.map +1 -0
- package/dist/src/dht.js +230 -0
- package/dist/src/dht.js.map +1 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +195 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/pubsub.d.ts +21 -0
- package/dist/src/pubsub.d.ts.map +1 -0
- package/dist/src/pubsub.js +81 -0
- package/dist/src/pubsub.js.map +1 -0
- package/dist/src/stream-handler.d.ts +28 -0
- package/dist/src/stream-handler.d.ts.map +1 -0
- package/dist/src/stream-handler.js +47 -0
- package/dist/src/stream-handler.js.map +1 -0
- package/dist/src/util/index.d.ts +12 -0
- package/dist/src/util/index.d.ts.map +1 -0
- package/dist/src/util/index.js +24 -0
- package/dist/src/util/index.js.map +1 -0
- package/package.json +160 -0
- package/src/dht.ts +285 -0
- package/src/index.ts +263 -0
- package/src/pubsub.ts +106 -0
- package/src/stream-handler.ts +65 -0
- package/src/util/index.ts +27 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import errcode from 'err-code';
|
|
2
|
+
import { Request, Response, PSRequest, PSMessage } from '@libp2p/daemon-protocol';
|
|
3
|
+
export class Pubsub {
|
|
4
|
+
constructor(client) {
|
|
5
|
+
this.client = client;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Get a list of topics the node is subscribed to.
|
|
9
|
+
*
|
|
10
|
+
* @returns {Array<string>} topics
|
|
11
|
+
*/
|
|
12
|
+
async getTopics() {
|
|
13
|
+
const sh = await this.client.send({
|
|
14
|
+
type: Request.Type.PUBSUB,
|
|
15
|
+
pubsub: {
|
|
16
|
+
type: PSRequest.Type.GET_TOPICS
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const message = await sh.read();
|
|
20
|
+
const response = Response.decode(message);
|
|
21
|
+
await sh.close();
|
|
22
|
+
if (response.type !== Response.Type.OK) {
|
|
23
|
+
throw errcode(new Error(response.error?.msg ?? 'Pubsub get topics failed'), 'ERR_PUBSUB_GET_TOPICS_FAILED');
|
|
24
|
+
}
|
|
25
|
+
if (response.pubsub == null || response.pubsub.topics == null) {
|
|
26
|
+
throw errcode(new Error('Invalid response'), 'ERR_PUBSUB_GET_TOPICS_FAILED');
|
|
27
|
+
}
|
|
28
|
+
return response.pubsub.topics;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Publish data under a topic
|
|
32
|
+
*/
|
|
33
|
+
async publish(topic, data) {
|
|
34
|
+
if (typeof topic !== 'string') {
|
|
35
|
+
throw errcode(new Error('invalid topic received'), 'ERR_INVALID_TOPIC');
|
|
36
|
+
}
|
|
37
|
+
if (!(data instanceof Uint8Array)) {
|
|
38
|
+
throw errcode(new Error('data received is not a Uint8Array'), 'ERR_INVALID_DATA');
|
|
39
|
+
}
|
|
40
|
+
const sh = await this.client.send({
|
|
41
|
+
type: Request.Type.PUBSUB,
|
|
42
|
+
pubsub: {
|
|
43
|
+
type: PSRequest.Type.PUBLISH,
|
|
44
|
+
topic,
|
|
45
|
+
data
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const message = await sh.read();
|
|
49
|
+
const response = Response.decode(message);
|
|
50
|
+
await sh.close();
|
|
51
|
+
if (response.type !== Response.Type.OK) {
|
|
52
|
+
throw errcode(new Error(response.error?.msg ?? 'Pubsub publish failed'), 'ERR_PUBSUB_PUBLISH_FAILED');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Request to subscribe a certain topic
|
|
57
|
+
*/
|
|
58
|
+
async *subscribe(topic) {
|
|
59
|
+
if (typeof topic !== 'string') {
|
|
60
|
+
throw errcode(new Error('invalid topic received'), 'ERR_INVALID_TOPIC');
|
|
61
|
+
}
|
|
62
|
+
const sh = await this.client.send({
|
|
63
|
+
type: Request.Type.PUBSUB,
|
|
64
|
+
pubsub: {
|
|
65
|
+
type: PSRequest.Type.SUBSCRIBE,
|
|
66
|
+
topic
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
let message = await sh.read();
|
|
70
|
+
let response = Response.decode(message);
|
|
71
|
+
if (response.type !== Response.Type.OK) {
|
|
72
|
+
throw errcode(new Error(response.error?.msg ?? 'Pubsub publish failed'), 'ERR_PUBSUB_PUBLISH_FAILED');
|
|
73
|
+
}
|
|
74
|
+
// stream messages
|
|
75
|
+
while (true) {
|
|
76
|
+
message = await sh.read();
|
|
77
|
+
yield PSMessage.decode(message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=pubsub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pubsub.js","sourceRoot":"","sources":["../../src/pubsub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Duplex } from 'it-stream-types';
|
|
2
|
+
export interface StreamHandlerOptions {
|
|
3
|
+
stream: Duplex<Uint8Array>;
|
|
4
|
+
maxLength?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class StreamHandler {
|
|
7
|
+
private stream;
|
|
8
|
+
private shake;
|
|
9
|
+
private decoder;
|
|
10
|
+
/**
|
|
11
|
+
* Create a stream handler for connection
|
|
12
|
+
*/
|
|
13
|
+
constructor(opts: StreamHandlerOptions);
|
|
14
|
+
/**
|
|
15
|
+
* Read and decode message
|
|
16
|
+
*/
|
|
17
|
+
read(): Promise<any>;
|
|
18
|
+
write(msg: Uint8Array): void;
|
|
19
|
+
/**
|
|
20
|
+
* Return the handshake rest stream and invalidate handler
|
|
21
|
+
*/
|
|
22
|
+
rest(): Duplex<Uint8Array, Uint8Array, Promise<void>>;
|
|
23
|
+
/**
|
|
24
|
+
* Close the stream
|
|
25
|
+
*/
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=stream-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-handler.d.ts","sourceRoot":"","sources":["../../src/stream-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAU,MAAM,iBAAiB,CAAA;AAKrD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,OAAO,CAAoB;IACnC;;OAEG;gBACU,IAAI,EAAE,oBAAoB;IAQvC;;OAEG;IACG,IAAI;IAWV,KAAK,CAAE,GAAG,EAAE,UAAU;IAOtB;;OAEG;IACH,IAAI;IAKJ;;OAEG;IACH,KAAK;CAIN"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as lp from 'it-length-prefixed';
|
|
2
|
+
import { handshake } from 'it-handshake';
|
|
3
|
+
import { logger } from '@libp2p/logger';
|
|
4
|
+
const log = logger('libp2p:daemon-client:stream-handler');
|
|
5
|
+
export class StreamHandler {
|
|
6
|
+
/**
|
|
7
|
+
* Create a stream handler for connection
|
|
8
|
+
*/
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
const { stream, maxLength } = opts;
|
|
11
|
+
this.stream = stream;
|
|
12
|
+
this.shake = handshake(this.stream);
|
|
13
|
+
this.decoder = lp.decode.fromReader(this.shake.reader, { maxDataLength: maxLength ?? 4096 });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read and decode message
|
|
17
|
+
*/
|
|
18
|
+
async read() {
|
|
19
|
+
// @ts-expect-error decoder is really a generator
|
|
20
|
+
const msg = await this.decoder.next();
|
|
21
|
+
if (msg.value) {
|
|
22
|
+
return msg.value.slice();
|
|
23
|
+
}
|
|
24
|
+
log('read received no value, closing stream');
|
|
25
|
+
// End the stream, we didn't get data
|
|
26
|
+
this.close();
|
|
27
|
+
}
|
|
28
|
+
write(msg) {
|
|
29
|
+
log('write message');
|
|
30
|
+
this.shake.write(lp.encode.single(msg).slice());
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Return the handshake rest stream and invalidate handler
|
|
34
|
+
*/
|
|
35
|
+
rest() {
|
|
36
|
+
this.shake.rest();
|
|
37
|
+
return this.shake.stream;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Close the stream
|
|
41
|
+
*/
|
|
42
|
+
close() {
|
|
43
|
+
log('closing the stream');
|
|
44
|
+
this.rest().sink([]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=stream-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-handler.js","sourceRoot":"","sources":["../../src/stream-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAIvC,MAAM,GAAG,GAAG,MAAM,CAAC,qCAAqC,CAAC,CAAA;AAOzD,MAAM,OAAO,aAAa;IAIxB;;OAEG;IACH,YAAa,IAA0B;QACrC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAA;QAElC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,SAAS,IAAI,IAAI,EAAE,CAAC,CAAA;IAC9F,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,iDAAiD;QACjD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QACrC,IAAI,GAAG,CAAC,KAAK,EAAE;YACb,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;SACzB;QACD,GAAG,CAAC,wCAAwC,CAAC,CAAA;QAC7C,qCAAqC;QACrC,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;IAED,KAAK,CAAE,GAAe;QACpB,GAAG,CAAC,eAAe,CAAC,CAAA;QACpB,IAAI,CAAC,KAAK,CAAC,KAAK,CACd,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAC9B,CAAA;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,GAAG,CAAC,oBAAoB,CAAC,CAAA;QACzB,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Upgrader } from '@libp2p/interfaces/transport';
|
|
2
|
+
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
3
|
+
export declare const passThroughUpgrader: Upgrader;
|
|
4
|
+
/**
|
|
5
|
+
* Converts the multiaddr to a nodejs NET compliant option
|
|
6
|
+
* for .connect or .listen
|
|
7
|
+
*
|
|
8
|
+
* @param {Multiaddr} addr
|
|
9
|
+
* @returns {string|object} A nodejs NET compliant option
|
|
10
|
+
*/
|
|
11
|
+
export declare function multiaddrToNetConfig(addr: Multiaddr): string | import("@multiformats/multiaddr").NodeAddress;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGxD,eAAO,MAAM,mBAAmB,EAAE,QAKjC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAE,IAAI,EAAE,SAAS,0DAQpD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
export const passThroughUpgrader = {
|
|
3
|
+
// @ts-expect-error
|
|
4
|
+
upgradeInbound: maConn => maConn,
|
|
5
|
+
// @ts-expect-error
|
|
6
|
+
upgradeOutbound: maConn => maConn
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Converts the multiaddr to a nodejs NET compliant option
|
|
10
|
+
* for .connect or .listen
|
|
11
|
+
*
|
|
12
|
+
* @param {Multiaddr} addr
|
|
13
|
+
* @returns {string|object} A nodejs NET compliant option
|
|
14
|
+
*/
|
|
15
|
+
export function multiaddrToNetConfig(addr) {
|
|
16
|
+
const listenPath = addr.getPath();
|
|
17
|
+
// unix socket listening
|
|
18
|
+
if (listenPath) {
|
|
19
|
+
return resolve(listenPath);
|
|
20
|
+
}
|
|
21
|
+
// tcp listening
|
|
22
|
+
return addr.nodeAddress();
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/util/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAE9B,MAAM,CAAC,MAAM,mBAAmB,GAAa;IAC3C,mBAAmB;IACnB,cAAc,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM;IAChC,mBAAmB;IACnB,eAAe,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM;CAClC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAE,IAAe;IACnD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;IACjC,wBAAwB;IACxB,IAAI,UAAU,EAAE;QACd,OAAO,OAAO,CAAC,UAAU,CAAC,CAAA;KAC3B;IACD,gBAAgB;IAChB,OAAO,IAAI,CAAC,WAAW,EAAE,CAAA;AAC3B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@libp2p/daemon-client",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "libp2p-daemon client implementation",
|
|
5
|
+
"license": "Apache-2.0 OR MIT",
|
|
6
|
+
"homepage": "https://github.com/libp2p/js-libp2p-daemon/tree/master/packages/libp2p-daemon-client#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/libp2p/js-libp2p-daemon.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/libp2p/js-libp2p-daemon/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"libp2p"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=16.0.0",
|
|
19
|
+
"npm": ">=7.0.0"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"types": "./dist/src/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"dist/src",
|
|
26
|
+
"!dist/test",
|
|
27
|
+
"!**/*.tsbuildinfo"
|
|
28
|
+
],
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./dist/src/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"eslintConfig": {
|
|
35
|
+
"extends": "ipfs",
|
|
36
|
+
"parserOptions": {
|
|
37
|
+
"sourceType": "module"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"release": {
|
|
41
|
+
"branches": [
|
|
42
|
+
"master"
|
|
43
|
+
],
|
|
44
|
+
"plugins": [
|
|
45
|
+
[
|
|
46
|
+
"@semantic-release/commit-analyzer",
|
|
47
|
+
{
|
|
48
|
+
"preset": "conventionalcommits",
|
|
49
|
+
"releaseRules": [
|
|
50
|
+
{
|
|
51
|
+
"breaking": true,
|
|
52
|
+
"release": "major"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"revert": true,
|
|
56
|
+
"release": "patch"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"type": "feat",
|
|
60
|
+
"release": "minor"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "fix",
|
|
64
|
+
"release": "patch"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "chore",
|
|
68
|
+
"release": "patch"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"type": "docs",
|
|
72
|
+
"release": "patch"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "test",
|
|
76
|
+
"release": "patch"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"scope": "no-release",
|
|
80
|
+
"release": false
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
"@semantic-release/release-notes-generator",
|
|
87
|
+
{
|
|
88
|
+
"preset": "conventionalcommits",
|
|
89
|
+
"presetConfig": {
|
|
90
|
+
"types": [
|
|
91
|
+
{
|
|
92
|
+
"type": "feat",
|
|
93
|
+
"section": "Features"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"type": "fix",
|
|
97
|
+
"section": "Bug Fixes"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"type": "chore",
|
|
101
|
+
"section": "Trivial Changes"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"type": "docs",
|
|
105
|
+
"section": "Trivial Changes"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"type": "test",
|
|
109
|
+
"section": "Tests"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"@semantic-release/changelog",
|
|
116
|
+
"@semantic-release/npm",
|
|
117
|
+
"@semantic-release/github",
|
|
118
|
+
"@semantic-release/git"
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
"scripts": {
|
|
122
|
+
"lint": "aegir lint",
|
|
123
|
+
"build": "tsc",
|
|
124
|
+
"pretest": "npm run build",
|
|
125
|
+
"test": "aegir test -t node -f ./dist/test/*.js",
|
|
126
|
+
"test:node": "aegir test -t node -f ./dist/test/*.js",
|
|
127
|
+
"release": "semantic-release"
|
|
128
|
+
},
|
|
129
|
+
"dependencies": {
|
|
130
|
+
"@libp2p/daemon-protocol": "^0.0.0",
|
|
131
|
+
"@libp2p/interfaces": "^1.3.17",
|
|
132
|
+
"@libp2p/logger": "^1.1.2",
|
|
133
|
+
"@libp2p/peer-id": "^1.1.8",
|
|
134
|
+
"@multiformats/multiaddr": "^10.1.8",
|
|
135
|
+
"err-code": "^3.0.1",
|
|
136
|
+
"it-handshake": "^3.0.1",
|
|
137
|
+
"it-length-prefixed": "^7.0.1",
|
|
138
|
+
"it-stream-types": "^1.0.4",
|
|
139
|
+
"libp2p-daemon": "^0.10.0",
|
|
140
|
+
"libp2p-tcp": "^0.17.1",
|
|
141
|
+
"multiaddr": "^10.0.0",
|
|
142
|
+
"multiformats": "^9.4.2",
|
|
143
|
+
"peer-id": "^0.16.0"
|
|
144
|
+
},
|
|
145
|
+
"devDependencies": {
|
|
146
|
+
"@libp2p/peer-id-factory": "^1.0.8",
|
|
147
|
+
"@libp2p/tcp": "^1.0.6",
|
|
148
|
+
"aegir": "^36.0.0",
|
|
149
|
+
"it-buffer": "^0.1.3",
|
|
150
|
+
"it-pipe": "^2.0.3",
|
|
151
|
+
"it-pushable": "^2.0.1",
|
|
152
|
+
"mocha": "^9.1.1",
|
|
153
|
+
"net": "^1.0.2",
|
|
154
|
+
"os": "^0.1.2",
|
|
155
|
+
"path": "^0.12.7",
|
|
156
|
+
"sinon": "^13.0.1",
|
|
157
|
+
"streaming-iterables": "^6.0.0",
|
|
158
|
+
"uint8arrays": "^3.0.0"
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/dht.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import { Multiaddr } from '@multiformats/multiaddr'
|
|
3
|
+
import errcode from 'err-code'
|
|
4
|
+
import {
|
|
5
|
+
Request,
|
|
6
|
+
Response,
|
|
7
|
+
DHTRequest,
|
|
8
|
+
DHTResponse
|
|
9
|
+
} from '@libp2p/daemon-protocol'
|
|
10
|
+
import type { DaemonClient } from './index.js'
|
|
11
|
+
import { isPeerId, PeerId } from '@libp2p/interfaces/peer-id'
|
|
12
|
+
import type { PeerData } from '@libp2p/interfaces/peer-data'
|
|
13
|
+
import { peerIdFromBytes } from '@libp2p/peer-id'
|
|
14
|
+
|
|
15
|
+
export class DHT {
|
|
16
|
+
private client: DaemonClient
|
|
17
|
+
|
|
18
|
+
constructor (client: DaemonClient) {
|
|
19
|
+
this.client = client
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Write a value to a key in the DHT
|
|
24
|
+
*/
|
|
25
|
+
async put (key: Uint8Array, value: Uint8Array): Promise<void> {
|
|
26
|
+
if (!(key instanceof Uint8Array)) {
|
|
27
|
+
throw errcode(new Error('invalid key received'), 'ERR_INVALID_KEY')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!(value instanceof Uint8Array)) {
|
|
31
|
+
throw errcode(new Error('value received is not a Uint8Array'), 'ERR_INVALID_VALUE')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sh = await this.client.send({
|
|
35
|
+
type: Request.Type.DHT,
|
|
36
|
+
dht: {
|
|
37
|
+
type: DHTRequest.Type.PUT_VALUE,
|
|
38
|
+
key,
|
|
39
|
+
value
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const message = await sh.read()
|
|
44
|
+
const response = Response.decode(message)
|
|
45
|
+
|
|
46
|
+
await sh.close()
|
|
47
|
+
|
|
48
|
+
if (response.type !== Response.Type.OK) {
|
|
49
|
+
throw errcode(new Error(response.error?.msg ?? 'DHT put failed'), 'ERR_DHT_PUT_FAILED')
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Query the DHT for a value stored at a key in the DHT
|
|
55
|
+
*/
|
|
56
|
+
async get (key: Uint8Array): Promise<Uint8Array> {
|
|
57
|
+
if (!(key instanceof Uint8Array)) {
|
|
58
|
+
throw errcode(new Error('invalid key received'), 'ERR_INVALID_KEY')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sh = await this.client.send({
|
|
62
|
+
type: Request.Type.DHT,
|
|
63
|
+
dht: {
|
|
64
|
+
type: DHTRequest.Type.GET_VALUE,
|
|
65
|
+
key
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const message = await sh.read()
|
|
70
|
+
const response = Response.decode(message)
|
|
71
|
+
|
|
72
|
+
await sh.close()
|
|
73
|
+
|
|
74
|
+
if (response.type !== Response.Type.OK) {
|
|
75
|
+
throw errcode(new Error(response.error?.msg ?? 'DHT get failed'), 'ERR_DHT_GET_FAILED')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (response.dht == null || response.dht.value == null) {
|
|
79
|
+
throw errcode(new Error('Invalid DHT get response'), 'ERR_DHT_GET_FAILED')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return response.dht.value
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Query the DHT for a given peer's known addresses.
|
|
87
|
+
*
|
|
88
|
+
* @param {PeerId} peerId
|
|
89
|
+
* @returns {PeerInfo}
|
|
90
|
+
*/
|
|
91
|
+
async findPeer (peerId: PeerId): Promise<PeerData> {
|
|
92
|
+
if (!isPeerId(peerId)) {
|
|
93
|
+
throw errcode(new Error('invalid peer id received'), 'ERR_INVALID_PEER_ID')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const sh = await this.client.send({
|
|
97
|
+
type: Request.Type.DHT,
|
|
98
|
+
dht: {
|
|
99
|
+
type: DHTRequest.Type.FIND_PEER,
|
|
100
|
+
peer: peerId.toBytes()
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const message = await sh.read()
|
|
105
|
+
const response = Response.decode(message)
|
|
106
|
+
|
|
107
|
+
await sh.close()
|
|
108
|
+
|
|
109
|
+
if (response.type !== Response.Type.OK) {
|
|
110
|
+
throw errcode(new Error(response.error?.msg ?? 'DHT find peer failed'), 'ERR_DHT_FIND_PEER_FAILED')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (response.dht == null || response.dht.peer == null || response.dht.peer.addrs == null) {
|
|
114
|
+
throw errcode(new Error('Invalid response'), 'ERR_DHT_FIND_PEER_FAILED')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
id: peerIdFromBytes(response.dht.peer.id),
|
|
119
|
+
multiaddrs: response.dht.peer.addrs.map((a) => new Multiaddr(a)),
|
|
120
|
+
protocols: []
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Announce to the network that the peer have data addressed by the provided CID
|
|
126
|
+
*/
|
|
127
|
+
async provide (cid: CID) {
|
|
128
|
+
if (cid == null || CID.asCID(cid) == null) {
|
|
129
|
+
throw errcode(new Error('invalid cid received'), 'ERR_INVALID_CID')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const sh = await this.client.send({
|
|
133
|
+
type: Request.Type.DHT,
|
|
134
|
+
dht: {
|
|
135
|
+
type: DHTRequest.Type.PROVIDE,
|
|
136
|
+
cid: cid.bytes
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const message = await sh.read()
|
|
141
|
+
const response = Response.decode(message)
|
|
142
|
+
|
|
143
|
+
await sh.close()
|
|
144
|
+
|
|
145
|
+
if (response.type !== Response.Type.OK) {
|
|
146
|
+
throw errcode(new Error(response.error?.msg ?? 'DHT provide failed'), 'ERR_DHT_PROVIDE_FAILED')
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Query the DHT for peers that have a piece of content, identified by a CID
|
|
152
|
+
*/
|
|
153
|
+
async * findProviders (cid: CID, count: number = 1): AsyncIterator<PeerData> {
|
|
154
|
+
if (cid == null || CID.asCID(cid) == null) {
|
|
155
|
+
throw errcode(new Error('invalid cid received'), 'ERR_INVALID_CID')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const sh = await this.client.send({
|
|
159
|
+
type: Request.Type.DHT,
|
|
160
|
+
dht: {
|
|
161
|
+
type: DHTRequest.Type.FIND_PROVIDERS,
|
|
162
|
+
cid: cid.bytes,
|
|
163
|
+
count
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
let message = await sh.read()
|
|
168
|
+
|
|
169
|
+
// stream begin message
|
|
170
|
+
let response = Response.decode(message)
|
|
171
|
+
|
|
172
|
+
if (response.type !== Response.Type.OK) {
|
|
173
|
+
await sh.close()
|
|
174
|
+
throw errcode(new Error(response.error?.msg || 'DHT find providers failed'), 'ERR_DHT_FIND_PROVIDERS_FAILED')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
while (true) {
|
|
178
|
+
message = await sh.read()
|
|
179
|
+
const response = DHTResponse.decode(message)
|
|
180
|
+
|
|
181
|
+
// Stream end
|
|
182
|
+
if (response.type === DHTResponse.Type.END) {
|
|
183
|
+
await sh.close()
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Stream values
|
|
188
|
+
if (response.type === DHTResponse.Type.VALUE && response.peer != null && response.peer?.addrs != null) {
|
|
189
|
+
yield {
|
|
190
|
+
id: peerIdFromBytes(response.peer.id),
|
|
191
|
+
multiaddrs: response.peer.addrs.map((a) => new Multiaddr(a)),
|
|
192
|
+
protocols: []
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
// Unexpected message received
|
|
196
|
+
await sh.close()
|
|
197
|
+
throw errcode(new Error('unexpected message received'), 'ERR_UNEXPECTED_MESSAGE_RECEIVED')
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Query the DHT routing table for peers that are closest to a provided key.
|
|
204
|
+
*/
|
|
205
|
+
async * getClosestPeers (key: Uint8Array): AsyncIterable<PeerData> {
|
|
206
|
+
if (!(key instanceof Uint8Array)) {
|
|
207
|
+
throw errcode(new Error('invalid key received'), 'ERR_INVALID_KEY')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const sh = await this.client.send({
|
|
211
|
+
type: Request.Type.DHT,
|
|
212
|
+
dht: {
|
|
213
|
+
type: DHTRequest.Type.GET_CLOSEST_PEERS,
|
|
214
|
+
key
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// stream begin message
|
|
219
|
+
let message = await sh.read()
|
|
220
|
+
let response = Response.decode(message)
|
|
221
|
+
|
|
222
|
+
if (response.type !== Response.Type.OK) {
|
|
223
|
+
await sh.close()
|
|
224
|
+
throw errcode(new Error(response.error?.msg ?? 'DHT find providers failed'), 'ERR_DHT_FIND_PROVIDERS_FAILED')
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
while (true) {
|
|
228
|
+
message = await sh.read()
|
|
229
|
+
const response = DHTResponse.decode(message)
|
|
230
|
+
|
|
231
|
+
// Stream end
|
|
232
|
+
if (response.type === DHTResponse.Type.END) {
|
|
233
|
+
await sh.close()
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Stream values
|
|
238
|
+
if (response.type === DHTResponse.Type.VALUE && response.value != null) {
|
|
239
|
+
const peerId = peerIdFromBytes(response.value)
|
|
240
|
+
|
|
241
|
+
yield {
|
|
242
|
+
id: peerId,
|
|
243
|
+
multiaddrs: [],
|
|
244
|
+
protocols: []
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
// Unexpected message received
|
|
248
|
+
await sh.close()
|
|
249
|
+
throw errcode(new Error('unexpected message received'), 'ERR_UNEXPECTED_MESSAGE_RECEIVED')
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Query the DHT routing table for a given peer's public key.
|
|
256
|
+
*/
|
|
257
|
+
async getPublicKey (peerId: PeerId) {
|
|
258
|
+
if (!isPeerId(peerId)) {
|
|
259
|
+
throw errcode(new Error('invalid peer id received'), 'ERR_INVALID_PEER_ID')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const sh = await this.client.send({
|
|
263
|
+
type: Request.Type.DHT,
|
|
264
|
+
dht: {
|
|
265
|
+
type: DHTRequest.Type.GET_PUBLIC_KEY,
|
|
266
|
+
peer: peerId.toBytes()
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const message = await sh.read()
|
|
271
|
+
const response = Response.decode(message)
|
|
272
|
+
|
|
273
|
+
await sh.close()
|
|
274
|
+
|
|
275
|
+
if (response.type !== Response.Type.OK) {
|
|
276
|
+
throw errcode(new Error(response.error?.msg ?? 'DHT get public key failed'), 'ERR_DHT_GET_PUBLIC_KEY_FAILED')
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (response.dht == null) {
|
|
280
|
+
throw errcode(new Error('Invalid response'), 'ERR_DHT_GET_PUBLIC_KEY_FAILED')
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return response.dht.value
|
|
284
|
+
}
|
|
285
|
+
}
|