@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.
@@ -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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libp2p/perf",
3
- "version": "1.1.15-78db573f9",
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/interface": "0.1.5-78db573f9",
52
- "@libp2p/interface-internal": "0.1.8-78db573f9",
53
- "@libp2p/logger": "3.0.5-78db573f9",
54
- "@multiformats/multiaddr": "^12.1.10",
55
- "it-pushable": "^3.2.1"
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
- "@libp2p/interface-compliance-tests": "4.1.3-78db573f9",
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
@@ -1,5 +1,2 @@
1
1
  export const PROTOCOL_NAME = '/perf/1.0.0'
2
- export const WRITE_BLOCK_SIZE = 64 << 10
3
- export const MAX_INBOUND_STREAMS = 1
4
- export const MAX_OUTBOUND_STREAMS = 1
5
- export const RUN_ON_TRANSIENT_CONNECTION = false
2
+ export const WRITE_BLOCK_SIZE = BigInt(64 << 10)
package/src/index.ts CHANGED
@@ -1,95 +1,71 @@
1
1
  /**
2
2
  * @packageDocumentation
3
3
  *
4
- * The {@link PerfService} implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which can be used to measure transfer performance within and across libp2p implementations.
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 { noise } from '@chainsafe/libp2p-noise'
10
- * import { yamux } from '@chainsafe/libp2p-yamux'
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 ONE_MEG = 1024 * 1024
18
- * const UPLOAD_BYTES = ONE_MEG * 1024
19
- * const DOWNLOAD_BYTES = ONE_MEG * 1024
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
- * async function createNode (): Promise<Libp2p<{ perf: PerfService }>> {
22
- * return createLibp2p({
23
- * addresses: {
24
- * listen: [
25
- * '/ip4/0.0.0.0/tcp/0'
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 libp2p1 = await createNode()
44
- * const libp2p2 = await createNode()
29
+ * const node = await createLibp2p({
30
+ * services: [
31
+ * perf: perfService()
32
+ * ]
33
+ * })
45
34
  *
46
- * for await (const output of libp2p1.services.perf.measurePerformance(libp2p2.getMultiaddrs()[0], UPLOAD_BYTES, DOWNLOAD_BYTES)) {
47
- * console.info(output)
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 { PerfService as PerfServiceClass } from './perf-service.js'
56
- import type { AbortOptions } from '@libp2p/interface'
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 { Multiaddr } from '@multiformats/multiaddr'
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
- export interface PerfService {
73
- measurePerformance(multiaddr: Multiaddr, sendBytes: number, recvBytes: number, options?: PerfOptions): AsyncGenerator<PerfOutput>
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 PerfOutput {
77
- type: 'intermediary' | 'final'
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
- runOnTransientConnection?: boolean
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 PerfServiceClass(components, init)
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"}