@libp2p/perf 1.1.15-78db573f9 → 1.1.15-8b82e68e8
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/README.md +33 -44
- package/dist/index.min.js +1 -1
- package/dist/src/constants.d.ts +1 -4
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +1 -4
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +34 -63
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +138 -46
- package/dist/src/index.js.map +1 -1
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +107 -0
- package/dist/src/main.js.map +1 -0
- package/package.json +16 -11
- package/src/constants.ts +1 -4
- package/src/index.ts +160 -68
- package/src/main.ts +118 -0
- package/dist/src/perf-service.d.ts +0 -21
- package/dist/src/perf-service.d.ts.map +0 -1
- package/dist/src/perf-service.js +0 -169
- package/dist/src/perf-service.js.map +0 -1
- package/src/perf-service.ts +0 -207
package/dist/src/main.js
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
import { noise } from '@chainsafe/libp2p-noise';
|
2
|
+
import { yamux } from '@chainsafe/libp2p-yamux';
|
3
|
+
import { unmarshalPrivateKey } from '@libp2p/crypto/keys';
|
4
|
+
import { createFromPrivKey } from '@libp2p/peer-id-factory';
|
5
|
+
import { tcp } from '@libp2p/tcp';
|
6
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
7
|
+
import { createLibp2p } from 'libp2p';
|
8
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
|
9
|
+
import yargs from 'yargs';
|
10
|
+
import { hideBin } from 'yargs/helpers';
|
11
|
+
import { defaultInit, perfService } from '../src/index.js';
|
12
|
+
const argv = yargs(hideBin(process.argv))
|
13
|
+
.options({
|
14
|
+
'run-server': {
|
15
|
+
type: 'boolean',
|
16
|
+
demandOption: true,
|
17
|
+
default: false,
|
18
|
+
description: 'Whether to run as a server'
|
19
|
+
},
|
20
|
+
'server-address': {
|
21
|
+
type: 'string',
|
22
|
+
demandOption: false,
|
23
|
+
description: 'Server IP address',
|
24
|
+
default: ''
|
25
|
+
},
|
26
|
+
transport: {
|
27
|
+
type: 'string',
|
28
|
+
demandOption: false,
|
29
|
+
description: 'Transport to use',
|
30
|
+
default: 'tcp'
|
31
|
+
},
|
32
|
+
'upload-bytes': {
|
33
|
+
type: 'number',
|
34
|
+
demandOption: false,
|
35
|
+
description: 'Number of bytes to upload',
|
36
|
+
default: 0
|
37
|
+
},
|
38
|
+
'download-bytes': {
|
39
|
+
type: 'number',
|
40
|
+
demandOption: false,
|
41
|
+
description: 'Number of bytes to download',
|
42
|
+
default: 0
|
43
|
+
}
|
44
|
+
})
|
45
|
+
.command('help', 'Print usage information', yargs.help)
|
46
|
+
.parseSync();
|
47
|
+
export async function main(runServer, serverIpAddress, transport, uploadBytes, downloadBytes) {
|
48
|
+
const listenAddrs = [];
|
49
|
+
const { host, port } = splitHostPort(serverIpAddress);
|
50
|
+
// #TODO: right now we only support tcp
|
51
|
+
const tcpMultiaddr = multiaddr(`/ip4/${host}/tcp/${port}`);
|
52
|
+
const config = {
|
53
|
+
transports: [tcp()],
|
54
|
+
streamMuxers: [yamux()],
|
55
|
+
connectionEncryption: [
|
56
|
+
noise()
|
57
|
+
],
|
58
|
+
services: {
|
59
|
+
perf: perfService(defaultInit)
|
60
|
+
}
|
61
|
+
};
|
62
|
+
const testPrivKey = 'CAASpgkwggSiAgEAAoIBAQC2SKo/HMFZeBml1AF3XijzrxrfQXdJzjePBZAbdxqKR1Mc6juRHXij6HXYPjlAk01BhF1S3Ll4Lwi0cAHhggf457sMg55UWyeGKeUv0ucgvCpBwlR5cQ020i0MgzjPWOLWq1rtvSbNcAi2ZEVn6+Q2EcHo3wUvWRtLeKz+DZSZfw2PEDC+DGPJPl7f8g7zl56YymmmzH9liZLNrzg/qidokUv5u1pdGrcpLuPNeTODk0cqKB+OUbuKj9GShYECCEjaybJDl9276oalL9ghBtSeEv20kugatTvYy590wFlJkkvyl+nPxIH0EEYMKK9XRWlu9XYnoSfboiwcv8M3SlsjAgMBAAECggEAZtju/bcKvKFPz0mkHiaJcpycy9STKphorpCT83srBVQi59CdFU6Mj+aL/xt0kCPMVigJw8P3/YCEJ9J+rS8BsoWE+xWUEsJvtXoT7vzPHaAtM3ci1HZd302Mz1+GgS8Epdx+7F5p80XAFLDUnELzOzKftvWGZmWfSeDnslwVONkL/1VAzwKy7Ce6hk4SxRE7l2NE2OklSHOzCGU1f78ZzVYKSnS5Ag9YrGjOAmTOXDbKNKN/qIorAQ1bovzGoCwx3iGIatQKFOxyVCyO1PsJYT7JO+kZbhBWRRE+L7l+ppPER9bdLFxs1t5CrKc078h+wuUr05S1P1JjXk68pk3+kQKBgQDeK8AR11373Mzib6uzpjGzgNRMzdYNuExWjxyxAzz53NAR7zrPHvXvfIqjDScLJ4NcRO2TddhXAfZoOPVH5k4PJHKLBPKuXZpWlookCAyENY7+Pd55S8r+a+MusrMagYNljb5WbVTgN8cgdpim9lbbIFlpN6SZaVjLQL3J8TWH6wKBgQDSChzItkqWX11CNstJ9zJyUE20I7LrpyBJNgG1gtvz3ZMUQCn3PxxHtQzN9n1P0mSSYs+jBKPuoSyYLt1wwe10/lpgL4rkKWU3/m1Myt0tveJ9WcqHh6tzcAbb/fXpUFT/o4SWDimWkPkuCb+8j//2yiXk0a/T2f36zKMuZvujqQKBgC6B7BAQDG2H2B/ijofp12ejJU36nL98gAZyqOfpLJ+FeMz4TlBDQ+phIMhnHXA5UkdDapQ+zA3SrFk+6yGk9Vw4Hf46B+82SvOrSbmnMa+PYqKYIvUzR4gg34rL/7AhwnbEyD5hXq4dHwMNsIDq+l2elPjwm/U9V0gdAl2+r50HAoGALtsKqMvhv8HucAMBPrLikhXP/8um8mMKFMrzfqZ+otxfHzlhI0L08Bo3jQrb0Z7ByNY6M8epOmbCKADsbWcVre/AAY0ZkuSZK/CaOXNX/AhMKmKJh8qAOPRY02LIJRBCpfS4czEdnfUhYV/TYiFNnKRj57PPYZdTzUsxa/yVTmECgYBr7slQEjb5Onn5mZnGDh+72BxLNdgwBkhO0OCdpdISqk0F0Pxby22DFOKXZEpiyI9XYP1C8wPiJsShGm2yEwBPWXnrrZNWczaVuCbXHrZkWQogBDG3HGXNdU4MAWCyiYlyinIBpPpoAJZSzpGLmWbMWh28+RJS6AQX6KHrK1o2uw==';
|
63
|
+
const encoded = uint8ArrayFromString(testPrivKey, 'base64pad');
|
64
|
+
const privateKey = await unmarshalPrivateKey(encoded);
|
65
|
+
const peerId = await createFromPrivKey(privateKey);
|
66
|
+
const tcpMultiaddrAddress = `${tcpMultiaddr.toString()}/p2p/${peerId.toString()}`;
|
67
|
+
if (runServer) {
|
68
|
+
listenAddrs.push(tcpMultiaddrAddress);
|
69
|
+
Object.assign(config, {
|
70
|
+
peerId,
|
71
|
+
addresses: {
|
72
|
+
listen: listenAddrs
|
73
|
+
}
|
74
|
+
});
|
75
|
+
}
|
76
|
+
const node = await createLibp2p(config);
|
77
|
+
await node.start();
|
78
|
+
const startTime = Date.now();
|
79
|
+
if (!runServer) {
|
80
|
+
const connection = await node.dial(multiaddr(tcpMultiaddrAddress));
|
81
|
+
const duration = await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes));
|
82
|
+
// Output latency to stdout in seconds
|
83
|
+
// eslint-disable-next-line no-console
|
84
|
+
console.log(JSON.stringify({ latency: duration / 1000 }));
|
85
|
+
await node.stop();
|
86
|
+
}
|
87
|
+
}
|
88
|
+
function splitHostPort(address) {
|
89
|
+
try {
|
90
|
+
const parts = address.split(':');
|
91
|
+
const host = parts[0];
|
92
|
+
const port = parts[1];
|
93
|
+
return {
|
94
|
+
host,
|
95
|
+
port
|
96
|
+
};
|
97
|
+
}
|
98
|
+
catch (error) {
|
99
|
+
throw Error('Invalid server address');
|
100
|
+
}
|
101
|
+
}
|
102
|
+
main(argv['run-server'], argv['server-address'], argv.transport, argv['upload-bytes'], argv['download-bytes']).catch((err) => {
|
103
|
+
// eslint-disable-next-line no-console
|
104
|
+
console.error(err);
|
105
|
+
process.exit(1);
|
106
|
+
});
|
107
|
+
//# sourceMappingURL=main.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE1D,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KACtC,OAAO,CAAC;IACP,YAAY,EAAE;QACZ,IAAI,EAAE,SAAS;QACf,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,4BAA4B;KAC1C;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,mBAAmB;QAChC,OAAO,EAAE,EAAE;KACZ;IACD,SAAS,EAAE;QACT,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,kBAAkB;QAC/B,OAAO,EAAE,KAAK;KACf;IACD,cAAc,EAAE;QACd,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,2BAA2B;QACxC,OAAO,EAAE,CAAC;KACX;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,6BAA6B;QAC1C,OAAO,EAAE,CAAC;KACX;CACF,CAAC;KACD,OAAO,CAAC,MAAM,EAAE,yBAAyB,EAAE,KAAK,CAAC,IAAI,CAAC;KACtD,SAAS,EAAE,CAAA;AAEd,MAAM,CAAC,KAAK,UAAU,IAAI,CAAE,SAAkB,EAAE,eAAuB,EAAE,SAAiB,EAAE,WAAmB,EAAE,aAAqB;IACpI,MAAM,WAAW,GAAa,EAAE,CAAA;IAEhC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,eAAe,CAAC,CAAA;IACrD,uCAAuC;IACvC,MAAM,YAAY,GAAG,SAAS,CAAC,QAAQ,IAAI,QAAQ,IAAI,EAAE,CAAC,CAAA;IAE1D,MAAM,MAAM,GAAG;QACb,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC;QACnB,YAAY,EAAE,CAAC,KAAK,EAAE,CAAC;QACvB,oBAAoB,EAAE;YACpB,KAAK,EAAE;SACR;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,WAAW,CAAC,WAAW,CAAC;SAC/B;KACF,CAAA;IAED,MAAM,WAAW,GAAG,8jDAA8jD,CAAA;IACllD,MAAM,OAAO,GAAG,oBAAoB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IAC9D,MAAM,UAAU,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,UAAU,CAAC,CAAA;IAClD,MAAM,mBAAmB,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAA;IAEjF,IAAI,SAAS,EAAE;QACb,WAAW,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;QAErC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;YACpB,MAAM;YACN,SAAS,EAAE;gBACT,MAAM,EAAE,WAAW;aACpB;SACF,CAAC,CAAA;KACH;IAED,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAEvC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IAElB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAE5B,IAAI,CAAC,SAAS,EAAE;QACd,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAClE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAA;QAC/H,sCAAsC;QACtC,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,EAAE,CAAC,CAAC,CAAA;QACzD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;KAClB;AACH,CAAC;AAED,SAAS,aAAa,CAAE,OAAe;IACrC,IAAI;QACF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,OAAO;YACL,IAAI;YACJ,IAAI;SACL,CAAA;KACF;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,KAAK,CAAC,wBAAwB,CAAC,CAAA;KACtC;AACH,CAAC;AAED,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC3H,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@libp2p/perf",
|
3
|
-
"version": "1.1.15-
|
3
|
+
"version": "1.1.15-8b82e68e8",
|
4
4
|
"description": "Implementation of Perf Protocol",
|
5
5
|
"author": "@maschad / @marcopolo",
|
6
6
|
"license": "Apache-2.0 OR MIT",
|
@@ -48,17 +48,22 @@
|
|
48
48
|
"renderResults": "node dist/src/renderResults.js"
|
49
49
|
},
|
50
50
|
"dependencies": {
|
51
|
-
"@libp2p
|
52
|
-
"@libp2p
|
53
|
-
"@libp2p/
|
54
|
-
"@
|
55
|
-
"
|
51
|
+
"@chainsafe/libp2p-noise": "^13.0.0",
|
52
|
+
"@chainsafe/libp2p-yamux": "^5.0.0",
|
53
|
+
"@libp2p/crypto": "2.0.7-8b82e68e8",
|
54
|
+
"@libp2p/interface": "0.1.5-8b82e68e8",
|
55
|
+
"@libp2p/interface-compliance-tests": "4.1.3-8b82e68e8",
|
56
|
+
"@libp2p/interface-internal": "0.1.8-8b82e68e8",
|
57
|
+
"@libp2p/interfaces": "3.3.2",
|
58
|
+
"@libp2p/logger": "3.0.5-8b82e68e8",
|
59
|
+
"@libp2p/peer-id-factory": "3.0.7-8b82e68e8",
|
60
|
+
"@libp2p/tcp": "8.0.11-8b82e68e8",
|
61
|
+
"@multiformats/multiaddr": "^12.1.5",
|
62
|
+
"libp2p": "0.46.18-8b82e68e8",
|
63
|
+
"uint8arrays": "^4.0.6",
|
64
|
+
"yargs": "^17.7.2"
|
56
65
|
},
|
57
66
|
"devDependencies": {
|
58
|
-
"
|
59
|
-
"aegir": "^41.0.2",
|
60
|
-
"it-last": "^3.0.3",
|
61
|
-
"it-pair": "^2.0.6",
|
62
|
-
"sinon-ts": "^2.0.0"
|
67
|
+
"aegir": "^41.0.2"
|
63
68
|
}
|
64
69
|
}
|
package/src/constants.ts
CHANGED
package/src/index.ts
CHANGED
@@ -1,95 +1,71 @@
|
|
1
1
|
/**
|
2
2
|
* @packageDocumentation
|
3
3
|
*
|
4
|
-
* The
|
4
|
+
* The `performanceService` implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which is used to measure performance within and across libp2p implementations
|
5
|
+
* addresses.
|
5
6
|
*
|
6
7
|
* @example
|
7
8
|
*
|
8
9
|
* ```typescript
|
9
|
-
* import {
|
10
|
-
* import {
|
11
|
-
* import { mplex } from '@libp2p/mplex'
|
12
|
-
* import { tcp } from '@libp2p/tcp'
|
13
|
-
* import { createLibp2p, type Libp2p } from 'libp2p'
|
14
|
-
* import { plaintext } from 'libp2p/insecure'
|
15
|
-
* import { perfService, type PerfService } from '@libp2p/perf'
|
10
|
+
* import { createLibp2p } from 'libp2p'
|
11
|
+
* import { perfService } from '@libp2p/perf'
|
16
12
|
*
|
17
|
-
* const
|
18
|
-
*
|
19
|
-
*
|
13
|
+
* const node = await createLibp2p({
|
14
|
+
* service: [
|
15
|
+
* perfService()
|
16
|
+
* ]
|
17
|
+
* })
|
18
|
+
* ```
|
19
|
+
*
|
20
|
+
* The `measurePerformance` function can be used to measure the latency and throughput of a connection.
|
21
|
+
* server. This will not work in browsers.
|
20
22
|
*
|
21
|
-
*
|
22
|
-
*
|
23
|
-
*
|
24
|
-
*
|
25
|
-
*
|
26
|
-
* ]
|
27
|
-
* },
|
28
|
-
* transports: [
|
29
|
-
* tcp()
|
30
|
-
* ],
|
31
|
-
* connectionEncryption: [
|
32
|
-
* noise(), plaintext()
|
33
|
-
* ],
|
34
|
-
* streamMuxers: [
|
35
|
-
* yamux(), mplex()
|
36
|
-
* ],
|
37
|
-
* services: {
|
38
|
-
* perf: perfService()
|
39
|
-
* }
|
40
|
-
* })
|
41
|
-
* }
|
23
|
+
* @example
|
24
|
+
*
|
25
|
+
* ```typescript
|
26
|
+
* import { createLibp2p } from 'libp2p'
|
27
|
+
* import { perfService } from 'libp2p/perf'
|
42
28
|
*
|
43
|
-
* const
|
44
|
-
*
|
29
|
+
* const node = await createLibp2p({
|
30
|
+
* services: [
|
31
|
+
* perf: perfService()
|
32
|
+
* ]
|
33
|
+
* })
|
45
34
|
*
|
46
|
-
*
|
47
|
-
*
|
48
|
-
*
|
35
|
+
* const connection = await node.dial(multiaddr(multiaddrAddress))
|
36
|
+
*
|
37
|
+
* const startTime = Date.now()
|
38
|
+
*
|
39
|
+
* await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
|
49
40
|
*
|
50
|
-
* await libp2p1.stop()
|
51
|
-
* await libp2p2.stop()
|
52
41
|
* ```
|
53
42
|
*/
|
54
43
|
|
55
|
-
import {
|
56
|
-
import
|
44
|
+
import { logger } from '@libp2p/logger'
|
45
|
+
import { PROTOCOL_NAME, WRITE_BLOCK_SIZE } from './constants.js'
|
46
|
+
import type { Connection } from '@libp2p/interface/connection'
|
47
|
+
import type { Startable } from '@libp2p/interface/startable'
|
57
48
|
import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
|
58
|
-
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
59
|
-
import type {
|
60
|
-
|
61
|
-
export interface PerfOptions extends AbortOptions {
|
62
|
-
/**
|
63
|
-
* By default measuring perf should include the time it takes to establish a
|
64
|
-
* connection, so a new connection will be opened for every performance run.
|
65
|
-
*
|
66
|
-
* To override this and re-use an existing connection if one is present, pass
|
67
|
-
* `true` here. (default: false)
|
68
|
-
*/
|
69
|
-
reuseExistingConnection?: boolean
|
70
|
-
}
|
49
|
+
import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar'
|
50
|
+
import type { AbortOptions } from '@libp2p/interfaces'
|
71
51
|
|
72
|
-
|
73
|
-
|
52
|
+
const log = logger('libp2p:perf')
|
53
|
+
|
54
|
+
export const defaultInit: PerfServiceInit = {
|
55
|
+
protocolName: '/perf/1.0.0',
|
56
|
+
writeBlockSize: BigInt(64 << 10)
|
74
57
|
}
|
75
58
|
|
76
|
-
export interface
|
77
|
-
|
78
|
-
timeSeconds: number
|
79
|
-
uploadBytes: number
|
80
|
-
downloadBytes: number
|
59
|
+
export interface PerfService {
|
60
|
+
measurePerformance(startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options?: AbortOptions): Promise<number>
|
81
61
|
}
|
82
62
|
|
83
63
|
export interface PerfServiceInit {
|
84
64
|
protocolName?: string
|
85
65
|
maxInboundStreams?: number
|
86
66
|
maxOutboundStreams?: number
|
87
|
-
|
88
|
-
|
89
|
-
/**
|
90
|
-
* Data sent/received will be sent in chunks of this size (default: 64KiB)
|
91
|
-
*/
|
92
|
-
writeBlockSize?: number
|
67
|
+
timeout?: number
|
68
|
+
writeBlockSize?: bigint
|
93
69
|
}
|
94
70
|
|
95
71
|
export interface PerfServiceComponents {
|
@@ -97,6 +73,122 @@ export interface PerfServiceComponents {
|
|
97
73
|
connectionManager: ConnectionManager
|
98
74
|
}
|
99
75
|
|
76
|
+
class DefaultPerfService implements Startable, PerfService {
|
77
|
+
public readonly protocol: string
|
78
|
+
private readonly components: PerfServiceComponents
|
79
|
+
private started: boolean
|
80
|
+
private readonly databuf: ArrayBuffer
|
81
|
+
private readonly writeBlockSize: bigint
|
82
|
+
|
83
|
+
constructor (components: PerfServiceComponents, init: PerfServiceInit) {
|
84
|
+
this.components = components
|
85
|
+
this.started = false
|
86
|
+
this.protocol = init.protocolName ?? PROTOCOL_NAME
|
87
|
+
this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE
|
88
|
+
this.databuf = new ArrayBuffer(Number(init.writeBlockSize))
|
89
|
+
}
|
90
|
+
|
91
|
+
async start (): Promise<void> {
|
92
|
+
await this.components.registrar.handle(this.protocol, (data: IncomingStreamData) => {
|
93
|
+
void this.handleMessage(data).catch((err) => {
|
94
|
+
log.error('error handling perf protocol message', err)
|
95
|
+
})
|
96
|
+
})
|
97
|
+
this.started = true
|
98
|
+
}
|
99
|
+
|
100
|
+
async stop (): Promise<void> {
|
101
|
+
await this.components.registrar.unhandle(this.protocol)
|
102
|
+
this.started = false
|
103
|
+
}
|
104
|
+
|
105
|
+
isStarted (): boolean {
|
106
|
+
return this.started
|
107
|
+
}
|
108
|
+
|
109
|
+
async handleMessage (data: IncomingStreamData): Promise<void> {
|
110
|
+
const { stream } = data
|
111
|
+
|
112
|
+
const writeBlockSize = this.writeBlockSize
|
113
|
+
|
114
|
+
let bytesToSendBack: bigint | null = null
|
115
|
+
|
116
|
+
for await (const buf of stream.source) {
|
117
|
+
if (bytesToSendBack === null) {
|
118
|
+
bytesToSendBack = BigInt(buf.getBigUint64(0, false))
|
119
|
+
}
|
120
|
+
// Ingest all the bufs and wait for the read side to close
|
121
|
+
}
|
122
|
+
|
123
|
+
const uint8Buf = new Uint8Array(this.databuf)
|
124
|
+
|
125
|
+
if (bytesToSendBack === null) {
|
126
|
+
throw new Error('bytesToSendBack was not set')
|
127
|
+
}
|
128
|
+
|
129
|
+
await stream.sink(async function * () {
|
130
|
+
while (bytesToSendBack > 0n) {
|
131
|
+
let toSend: bigint = writeBlockSize
|
132
|
+
if (toSend > bytesToSendBack) {
|
133
|
+
toSend = bytesToSendBack
|
134
|
+
}
|
135
|
+
bytesToSendBack = bytesToSendBack - toSend
|
136
|
+
yield uint8Buf.subarray(0, Number(toSend))
|
137
|
+
}
|
138
|
+
}())
|
139
|
+
}
|
140
|
+
|
141
|
+
async measurePerformance (startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options: AbortOptions = {}): Promise<number> {
|
142
|
+
log('opening stream on protocol %s to %p', this.protocol, connection.remotePeer)
|
143
|
+
|
144
|
+
const uint8Buf = new Uint8Array(this.databuf)
|
145
|
+
|
146
|
+
const writeBlockSize = this.writeBlockSize
|
147
|
+
|
148
|
+
const stream = await connection.newStream([this.protocol], options)
|
149
|
+
|
150
|
+
// Convert sendBytes to uint64 big endian buffer
|
151
|
+
const view = new DataView(this.databuf)
|
152
|
+
view.setBigInt64(0, recvBytes, false)
|
153
|
+
|
154
|
+
log('sending %i bytes to %p', sendBytes, connection.remotePeer)
|
155
|
+
try {
|
156
|
+
await stream.sink((async function * () {
|
157
|
+
// Send the number of bytes to receive
|
158
|
+
yield uint8Buf.subarray(0, 8)
|
159
|
+
// Send the number of bytes to send
|
160
|
+
while (sendBytes > 0n) {
|
161
|
+
let toSend: bigint = writeBlockSize
|
162
|
+
if (toSend > sendBytes) {
|
163
|
+
toSend = sendBytes
|
164
|
+
}
|
165
|
+
sendBytes = sendBytes - toSend
|
166
|
+
yield uint8Buf.subarray(0, Number(toSend))
|
167
|
+
}
|
168
|
+
})())
|
169
|
+
|
170
|
+
// Read the received bytes
|
171
|
+
let actualRecvdBytes = BigInt(0)
|
172
|
+
for await (const buf of stream.source) {
|
173
|
+
actualRecvdBytes += BigInt(buf.length)
|
174
|
+
}
|
175
|
+
|
176
|
+
if (actualRecvdBytes !== recvBytes) {
|
177
|
+
throw new Error(`Expected to receive ${recvBytes} bytes, but received ${actualRecvdBytes}`)
|
178
|
+
}
|
179
|
+
} catch (err) {
|
180
|
+
log('error sending %i bytes to %p: %s', sendBytes, connection.remotePeer, err)
|
181
|
+
throw err
|
182
|
+
} finally {
|
183
|
+
log('performed %s to %p', this.protocol, connection.remotePeer)
|
184
|
+
await stream.close()
|
185
|
+
}
|
186
|
+
|
187
|
+
// Return the latency
|
188
|
+
return Date.now() - startTime
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
100
192
|
export function perfService (init: PerfServiceInit = {}): (components: PerfServiceComponents) => PerfService {
|
101
|
-
return (components) => new
|
193
|
+
return (components) => new DefaultPerfService(components, init)
|
102
194
|
}
|
package/src/main.ts
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
import { noise } from '@chainsafe/libp2p-noise'
|
2
|
+
import { yamux } from '@chainsafe/libp2p-yamux'
|
3
|
+
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
|
4
|
+
import { createFromPrivKey } from '@libp2p/peer-id-factory'
|
5
|
+
import { tcp } from '@libp2p/tcp'
|
6
|
+
import { multiaddr } from '@multiformats/multiaddr'
|
7
|
+
import { createLibp2p } from 'libp2p'
|
8
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
9
|
+
import yargs from 'yargs'
|
10
|
+
import { hideBin } from 'yargs/helpers'
|
11
|
+
import { defaultInit, perfService } from '../src/index.js'
|
12
|
+
|
13
|
+
const argv = yargs(hideBin(process.argv))
|
14
|
+
.options({
|
15
|
+
'run-server': {
|
16
|
+
type: 'boolean',
|
17
|
+
demandOption: true,
|
18
|
+
default: false,
|
19
|
+
description: 'Whether to run as a server'
|
20
|
+
},
|
21
|
+
'server-address': {
|
22
|
+
type: 'string',
|
23
|
+
demandOption: false,
|
24
|
+
description: 'Server IP address',
|
25
|
+
default: ''
|
26
|
+
},
|
27
|
+
transport: {
|
28
|
+
type: 'string',
|
29
|
+
demandOption: false,
|
30
|
+
description: 'Transport to use',
|
31
|
+
default: 'tcp'
|
32
|
+
},
|
33
|
+
'upload-bytes': {
|
34
|
+
type: 'number',
|
35
|
+
demandOption: false,
|
36
|
+
description: 'Number of bytes to upload',
|
37
|
+
default: 0
|
38
|
+
},
|
39
|
+
'download-bytes': {
|
40
|
+
type: 'number',
|
41
|
+
demandOption: false,
|
42
|
+
description: 'Number of bytes to download',
|
43
|
+
default: 0
|
44
|
+
}
|
45
|
+
})
|
46
|
+
.command('help', 'Print usage information', yargs.help)
|
47
|
+
.parseSync()
|
48
|
+
|
49
|
+
export async function main (runServer: boolean, serverIpAddress: string, transport: string, uploadBytes: number, downloadBytes: number): Promise<void> {
|
50
|
+
const listenAddrs: string[] = []
|
51
|
+
|
52
|
+
const { host, port } = splitHostPort(serverIpAddress)
|
53
|
+
// #TODO: right now we only support tcp
|
54
|
+
const tcpMultiaddr = multiaddr(`/ip4/${host}/tcp/${port}`)
|
55
|
+
|
56
|
+
const config = {
|
57
|
+
transports: [tcp()],
|
58
|
+
streamMuxers: [yamux()],
|
59
|
+
connectionEncryption: [
|
60
|
+
noise()
|
61
|
+
],
|
62
|
+
services: {
|
63
|
+
perf: perfService(defaultInit)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
const testPrivKey = 'CAASpgkwggSiAgEAAoIBAQC2SKo/HMFZeBml1AF3XijzrxrfQXdJzjePBZAbdxqKR1Mc6juRHXij6HXYPjlAk01BhF1S3Ll4Lwi0cAHhggf457sMg55UWyeGKeUv0ucgvCpBwlR5cQ020i0MgzjPWOLWq1rtvSbNcAi2ZEVn6+Q2EcHo3wUvWRtLeKz+DZSZfw2PEDC+DGPJPl7f8g7zl56YymmmzH9liZLNrzg/qidokUv5u1pdGrcpLuPNeTODk0cqKB+OUbuKj9GShYECCEjaybJDl9276oalL9ghBtSeEv20kugatTvYy590wFlJkkvyl+nPxIH0EEYMKK9XRWlu9XYnoSfboiwcv8M3SlsjAgMBAAECggEAZtju/bcKvKFPz0mkHiaJcpycy9STKphorpCT83srBVQi59CdFU6Mj+aL/xt0kCPMVigJw8P3/YCEJ9J+rS8BsoWE+xWUEsJvtXoT7vzPHaAtM3ci1HZd302Mz1+GgS8Epdx+7F5p80XAFLDUnELzOzKftvWGZmWfSeDnslwVONkL/1VAzwKy7Ce6hk4SxRE7l2NE2OklSHOzCGU1f78ZzVYKSnS5Ag9YrGjOAmTOXDbKNKN/qIorAQ1bovzGoCwx3iGIatQKFOxyVCyO1PsJYT7JO+kZbhBWRRE+L7l+ppPER9bdLFxs1t5CrKc078h+wuUr05S1P1JjXk68pk3+kQKBgQDeK8AR11373Mzib6uzpjGzgNRMzdYNuExWjxyxAzz53NAR7zrPHvXvfIqjDScLJ4NcRO2TddhXAfZoOPVH5k4PJHKLBPKuXZpWlookCAyENY7+Pd55S8r+a+MusrMagYNljb5WbVTgN8cgdpim9lbbIFlpN6SZaVjLQL3J8TWH6wKBgQDSChzItkqWX11CNstJ9zJyUE20I7LrpyBJNgG1gtvz3ZMUQCn3PxxHtQzN9n1P0mSSYs+jBKPuoSyYLt1wwe10/lpgL4rkKWU3/m1Myt0tveJ9WcqHh6tzcAbb/fXpUFT/o4SWDimWkPkuCb+8j//2yiXk0a/T2f36zKMuZvujqQKBgC6B7BAQDG2H2B/ijofp12ejJU36nL98gAZyqOfpLJ+FeMz4TlBDQ+phIMhnHXA5UkdDapQ+zA3SrFk+6yGk9Vw4Hf46B+82SvOrSbmnMa+PYqKYIvUzR4gg34rL/7AhwnbEyD5hXq4dHwMNsIDq+l2elPjwm/U9V0gdAl2+r50HAoGALtsKqMvhv8HucAMBPrLikhXP/8um8mMKFMrzfqZ+otxfHzlhI0L08Bo3jQrb0Z7ByNY6M8epOmbCKADsbWcVre/AAY0ZkuSZK/CaOXNX/AhMKmKJh8qAOPRY02LIJRBCpfS4czEdnfUhYV/TYiFNnKRj57PPYZdTzUsxa/yVTmECgYBr7slQEjb5Onn5mZnGDh+72BxLNdgwBkhO0OCdpdISqk0F0Pxby22DFOKXZEpiyI9XYP1C8wPiJsShGm2yEwBPWXnrrZNWczaVuCbXHrZkWQogBDG3HGXNdU4MAWCyiYlyinIBpPpoAJZSzpGLmWbMWh28+RJS6AQX6KHrK1o2uw=='
|
68
|
+
const encoded = uint8ArrayFromString(testPrivKey, 'base64pad')
|
69
|
+
const privateKey = await unmarshalPrivateKey(encoded)
|
70
|
+
const peerId = await createFromPrivKey(privateKey)
|
71
|
+
const tcpMultiaddrAddress = `${tcpMultiaddr.toString()}/p2p/${peerId.toString()}`
|
72
|
+
|
73
|
+
if (runServer) {
|
74
|
+
listenAddrs.push(tcpMultiaddrAddress)
|
75
|
+
|
76
|
+
Object.assign(config, {
|
77
|
+
peerId,
|
78
|
+
addresses: {
|
79
|
+
listen: listenAddrs
|
80
|
+
}
|
81
|
+
})
|
82
|
+
}
|
83
|
+
|
84
|
+
const node = await createLibp2p(config)
|
85
|
+
|
86
|
+
await node.start()
|
87
|
+
|
88
|
+
const startTime = Date.now()
|
89
|
+
|
90
|
+
if (!runServer) {
|
91
|
+
const connection = await node.dial(multiaddr(tcpMultiaddrAddress))
|
92
|
+
const duration = await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
|
93
|
+
// Output latency to stdout in seconds
|
94
|
+
// eslint-disable-next-line no-console
|
95
|
+
console.log(JSON.stringify({ latency: duration / 1000 }))
|
96
|
+
await node.stop()
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
function splitHostPort (address: string): { host: string, port?: string } {
|
101
|
+
try {
|
102
|
+
const parts = address.split(':')
|
103
|
+
const host = parts[0]
|
104
|
+
const port = parts[1]
|
105
|
+
return {
|
106
|
+
host,
|
107
|
+
port
|
108
|
+
}
|
109
|
+
} catch (error) {
|
110
|
+
throw Error('Invalid server address')
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
main(argv['run-server'], argv['server-address'], argv.transport, argv['upload-bytes'], argv['download-bytes']).catch((err) => {
|
115
|
+
// eslint-disable-next-line no-console
|
116
|
+
console.error(err)
|
117
|
+
process.exit(1)
|
118
|
+
})
|
@@ -1,21 +0,0 @@
|
|
1
|
-
import type { PerfOptions, PerfOutput, PerfServiceComponents, PerfServiceInit, PerfService as PerfServiceInterface } from './index.js';
|
2
|
-
import type { Startable } from '@libp2p/interface/startable';
|
3
|
-
import type { IncomingStreamData } from '@libp2p/interface-internal/registrar';
|
4
|
-
import type { Multiaddr } from '@multiformats/multiaddr';
|
5
|
-
export declare class PerfService implements Startable, PerfServiceInterface {
|
6
|
-
readonly protocol: string;
|
7
|
-
private readonly components;
|
8
|
-
private started;
|
9
|
-
private readonly databuf;
|
10
|
-
private readonly writeBlockSize;
|
11
|
-
private readonly maxInboundStreams;
|
12
|
-
private readonly maxOutboundStreams;
|
13
|
-
private readonly runOnTransientConnection;
|
14
|
-
constructor(components: PerfServiceComponents, init?: PerfServiceInit);
|
15
|
-
start(): Promise<void>;
|
16
|
-
stop(): Promise<void>;
|
17
|
-
isStarted(): boolean;
|
18
|
-
handleMessage(data: IncomingStreamData): Promise<void>;
|
19
|
-
measurePerformance(ma: Multiaddr, sendBytes: number, receiveBytes: number, options?: PerfOptions): AsyncGenerator<PerfOutput>;
|
20
|
-
}
|
21
|
-
//# sourceMappingURL=perf-service.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"perf-service.d.ts","sourceRoot":"","sources":["../../src/perf-service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACtI,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAA;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAIxD,qBAAa,WAAY,YAAW,SAAS,EAAE,oBAAoB;IACjE,SAAgB,QAAQ,EAAE,MAAM,CAAA;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAQ;IAC3C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAS;gBAErC,UAAU,EAAE,qBAAqB,EAAE,IAAI,GAAE,eAAoB;IAWpE,KAAK,IAAK,OAAO,CAAC,IAAI,CAAC;IAavB,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC;IAK5B,SAAS,IAAK,OAAO;IAIf,aAAa,CAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCrD,kBAAkB,CAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,cAAc,CAAC,UAAU,CAAC;CAmH3I"}
|