@dignetwork/dig-sdk 0.0.1-alpha.144 → 0.0.1-alpha.147

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.
@@ -7,53 +7,41 @@ export interface PeerMetrics {
7
7
  bandwidth: number;
8
8
  }
9
9
  /**
10
- * Configuration options for the PeerRanker.
11
- */
12
- interface PeerRankerOptions {
13
- clientCertPath: string;
14
- clientKeyPath: string;
15
- bandwidthTestPath: string;
16
- pingPath?: string;
17
- timeout?: number;
18
- bandwidthTestSize?: number;
19
- }
20
- /**
21
- * Utility class to rank peers based on latency and bandwidth using HTTPS with mTLS.
10
+ * Utility class to rank peers based on latency and upload bandwidth using HTTPS with mTLS.
22
11
  */
23
12
  export declare class PeerRanker {
24
13
  private ipAddresses;
25
14
  private static certPath;
26
15
  private static keyPath;
27
- private bandwidthTestPath;
28
- private pingPath;
29
16
  private timeout;
30
- private bandwidthTestSize;
17
+ private uploadTestSize;
31
18
  private sortedPeers;
32
19
  private currentIndex;
33
20
  /**
34
21
  * Constructs a PeerRanker instance.
35
22
  * @param ipAddresses - Array of IP addresses to rank.
36
- * @param options - Configuration options including paths to client certificates.
37
23
  */
38
- constructor(ipAddresses: string[], options: PeerRankerOptions);
24
+ constructor(ipAddresses: string[], timeout?: number, uploadTestSize?: number);
39
25
  /**
40
26
  * Measures the latency of a given IP address using an HTTPS request.
41
27
  * Tries HEAD first, then falls back to GET if HEAD is not supported.
42
28
  * @param ip - The IP address of the peer.
43
- * @returns Promise resolving to the latency in milliseconds.
29
+ * @returns Promise resolving to the latency in milliseconds or rejecting if the peer fails.
44
30
  */
45
31
  private measureLatency;
46
32
  /**
47
- * Measures the bandwidth of a given IP address by downloading a file and calculating throughput.
33
+ * Measures the upload bandwidth of a given IP address by sending random data.
48
34
  * @param ip - The IP address of the peer.
49
- * @returns Promise resolving to the bandwidth in bytes per second.
35
+ * @returns Promise resolving to the upload bandwidth in bytes per second or rejecting if the peer fails.
50
36
  */
51
37
  private measureBandwidth;
52
38
  /**
53
- * Ranks the peers based on measured latency and bandwidth.
39
+ * Ranks the peers based on measured latency and upload bandwidth.
40
+ * Unresponsive peers are excluded from the final ranking.
41
+ * @param cooldown - Cooldown time in milliseconds between batches.
54
42
  * @returns Promise resolving to an array of PeerMetrics sorted by latency and bandwidth.
55
43
  */
56
- rankPeers(): Promise<PeerMetrics[]>;
44
+ rankPeers(cooldown?: number): Promise<PeerMetrics[]>;
57
45
  /**
58
46
  * Returns the next best peer based on the ranking.
59
47
  * Cycles through the list, resetting to the beginning once the end is reached.
@@ -65,5 +53,4 @@ export declare class PeerRanker {
65
53
  */
66
54
  resetIterator(): void;
67
55
  }
68
- export {};
69
56
  //# sourceMappingURL=PeerRanker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PeerRanker.d.ts","sourceRoot":"","sources":["../../src/utils/PeerRanker.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,UAAU,iBAAiB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAS;IAGlC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAa;IAEjC;;;;OAIG;gBACS,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,iBAAiB;IAY7D;;;;;OAKG;YACW,cAAc;IA8C5B;;;;OAIG;YACW,gBAAgB;IA2C9B;;;OAGG;IACU,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IA4BhD;;;;OAIG;IACI,eAAe,IAAI,WAAW,GAAG,IAAI;IAW5C;;OAEG;IACI,aAAa,IAAI,IAAI;CAG7B"}
1
+ {"version":3,"file":"PeerRanker.d.ts","sourceRoot":"","sources":["../../src/utils/PeerRanker.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAa;IAEjC;;;OAGG;gBACS,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAa,EAAE,cAAc,GAAE,MAAoB;IAW/F;;;;;OAKG;YACW,cAAc;IA2C5B;;;;OAIG;YACW,gBAAgB;IAmC9B;;;;;OAKG;IACU,SAAS,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAmCtE;;;;OAIG;IACI,eAAe,IAAI,WAAW,GAAG,IAAI;IAW5C;;OAEG;IACI,aAAa,IAAI,IAAI;CAG7B"}
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/PeerRanker.ts
3
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
4
  };
@@ -9,24 +8,23 @@ const axios_1 = __importDefault(require("axios"));
9
8
  const fs_1 = __importDefault(require("fs"));
10
9
  const https_1 = __importDefault(require("https"));
11
10
  const ssl_1 = require("./ssl");
11
+ const promiseUtils_1 = require("./promiseUtils");
12
12
  /**
13
- * Utility class to rank peers based on latency and bandwidth using HTTPS with mTLS.
13
+ * Utility class to rank peers based on latency and upload bandwidth using HTTPS with mTLS.
14
14
  */
15
15
  class PeerRanker {
16
16
  /**
17
17
  * Constructs a PeerRanker instance.
18
18
  * @param ipAddresses - Array of IP addresses to rank.
19
- * @param options - Configuration options including paths to client certificates.
20
19
  */
21
- constructor(ipAddresses, options) {
20
+ constructor(ipAddresses, timeout = 5000, uploadTestSize = 1024 * 1024) {
22
21
  // Internal properties for iteration
23
22
  this.sortedPeers = [];
24
23
  this.currentIndex = 0;
25
24
  this.ipAddresses = ipAddresses;
26
- this.bandwidthTestPath = options.bandwidthTestPath;
27
- this.pingPath = options.pingPath || '/'; // Default to root path if not provided
28
- this.timeout = options.timeout || 5000; // Default timeout: 5 seconds
29
- this.bandwidthTestSize = options.bandwidthTestSize || 1024 * 1024; // Default: 1MB
25
+ this.timeout = timeout; // Allow customizable timeout
26
+ this.uploadTestSize = uploadTestSize; // Default upload size: 1MB
27
+ // Fetch the SSL certificates used for mTLS.
30
28
  const { certPath, keyPath } = (0, ssl_1.getOrCreateSSLCerts)();
31
29
  PeerRanker.certPath = certPath;
32
30
  PeerRanker.keyPath = keyPath;
@@ -35,39 +33,36 @@ class PeerRanker {
35
33
  * Measures the latency of a given IP address using an HTTPS request.
36
34
  * Tries HEAD first, then falls back to GET if HEAD is not supported.
37
35
  * @param ip - The IP address of the peer.
38
- * @returns Promise resolving to the latency in milliseconds.
36
+ * @returns Promise resolving to the latency in milliseconds or rejecting if the peer fails.
39
37
  */
40
38
  async measureLatency(ip) {
41
- const path = this.pingPath;
42
- const url = `https://${ip}${path}`;
43
- // Configuration for HEAD request
39
+ const url = `https://${ip}:4159/diagnostics/ping`;
44
40
  const configHead = {
45
41
  url: url,
46
42
  method: 'HEAD',
47
43
  httpsAgent: new https_1.default.Agent({
48
44
  cert: fs_1.default.readFileSync(PeerRanker.certPath),
49
45
  key: fs_1.default.readFileSync(PeerRanker.keyPath),
50
- rejectUnauthorized: false, // Set to true in production
46
+ rejectUnauthorized: false,
51
47
  }),
52
48
  timeout: this.timeout,
53
- validateStatus: (status) => status < 500, // Resolve only if status is less than 500
49
+ validateStatus: (status) => status < 500,
54
50
  };
55
51
  const startTime = Date.now();
56
52
  try {
57
53
  const response = await (0, axios_1.default)(configHead);
58
- if (response.status === 405) { // Method Not Allowed
59
- // Fallback to GET with Range header to minimize data transfer
54
+ if (response.status === 405) {
60
55
  const configGet = {
61
56
  url: url,
62
57
  method: 'GET',
63
58
  httpsAgent: new https_1.default.Agent({
64
59
  cert: fs_1.default.readFileSync(PeerRanker.certPath),
65
60
  key: fs_1.default.readFileSync(PeerRanker.keyPath),
66
- rejectUnauthorized: false, // Set to true in production
61
+ rejectUnauthorized: false,
67
62
  }),
68
63
  timeout: this.timeout,
69
64
  headers: {
70
- 'Range': 'bytes=0-0', // Request only the first byte
65
+ 'Range': 'bytes=0-0',
71
66
  },
72
67
  validateStatus: (status) => status < 500,
73
68
  };
@@ -78,64 +73,69 @@ class PeerRanker {
78
73
  }
79
74
  catch (error) {
80
75
  console.error(`Latency measurement failed for IP ${ip}:`, error.message);
81
- return Infinity; // Indicate unreachable or unresponsive peer
76
+ throw new Error(`Latency measurement failed for IP ${ip}`);
82
77
  }
83
78
  }
84
79
  /**
85
- * Measures the bandwidth of a given IP address by downloading a file and calculating throughput.
80
+ * Measures the upload bandwidth of a given IP address by sending random data.
86
81
  * @param ip - The IP address of the peer.
87
- * @returns Promise resolving to the bandwidth in bytes per second.
82
+ * @returns Promise resolving to the upload bandwidth in bytes per second or rejecting if the peer fails.
88
83
  */
89
84
  async measureBandwidth(ip) {
90
- const url = `https://${ip}${this.bandwidthTestPath}`;
85
+ const url = `https://${ip}:4159/diagnostics/bandwidth`;
86
+ const randomData = Buffer.alloc(this.uploadTestSize, 'a');
91
87
  const config = {
92
88
  url: url,
93
- method: 'GET',
94
- responseType: 'stream',
89
+ method: 'POST',
90
+ data: randomData,
91
+ headers: {
92
+ 'Content-Type': 'application/octet-stream',
93
+ 'Content-Length': this.uploadTestSize,
94
+ },
95
95
  httpsAgent: new https_1.default.Agent({
96
96
  cert: fs_1.default.readFileSync(PeerRanker.certPath),
97
97
  key: fs_1.default.readFileSync(PeerRanker.keyPath),
98
- rejectUnauthorized: false, // Set to true in production
98
+ rejectUnauthorized: false,
99
99
  }),
100
100
  timeout: this.timeout,
101
+ maxContentLength: Infinity,
102
+ maxBodyLength: Infinity,
101
103
  };
102
- return new Promise((resolve) => {
103
- const startTime = Date.now();
104
- let bytesReceived = 0;
105
- (0, axios_1.default)(config)
106
- .then((response) => {
107
- response.data.on('data', (chunk) => {
108
- bytesReceived += chunk.length;
109
- });
110
- response.data.on('end', () => {
111
- const timeElapsed = (Date.now() - startTime) / 1000; // seconds
112
- const bandwidth = bytesReceived / timeElapsed; // bytes per second
113
- resolve(bandwidth);
114
- });
115
- response.data.on('error', (err) => {
116
- console.error(`Bandwidth measurement error for IP ${ip}:`, err.message);
117
- resolve(0); // Indicate failure in measuring bandwidth
118
- });
119
- })
120
- .catch((error) => {
121
- console.error(`Bandwidth measurement failed for IP ${ip}:`, error.message);
122
- resolve(0); // Indicate failure in measuring bandwidth
123
- });
124
- });
104
+ const startTime = Date.now();
105
+ try {
106
+ await (0, axios_1.default)(config);
107
+ const timeElapsed = (Date.now() - startTime) / 1000;
108
+ const bandwidth = this.uploadTestSize / timeElapsed;
109
+ return bandwidth;
110
+ }
111
+ catch (error) {
112
+ console.error(`Bandwidth measurement failed for IP ${ip}:`, error.message);
113
+ throw new Error(`Bandwidth measurement failed for IP ${ip}`);
114
+ }
125
115
  }
126
116
  /**
127
- * Ranks the peers based on measured latency and bandwidth.
117
+ * Ranks the peers based on measured latency and upload bandwidth.
118
+ * Unresponsive peers are excluded from the final ranking.
119
+ * @param cooldown - Cooldown time in milliseconds between batches.
128
120
  * @returns Promise resolving to an array of PeerMetrics sorted by latency and bandwidth.
129
121
  */
130
- async rankPeers() {
131
- const metricsPromises = this.ipAddresses.map(async (ip) => {
132
- const [latency, bandwidth] = await Promise.all([
133
- this.measureLatency(ip),
134
- this.measureBandwidth(ip),
135
- ]);
136
- return { ip, latency, bandwidth };
137
- });
138
- const peerMetrics = await Promise.all(metricsPromises);
122
+ async rankPeers(cooldown = 500) {
123
+ const limit = 5; // Limit to 5 parallel requests at a time
124
+ const iteratorFn = async (ip) => {
125
+ try {
126
+ const [latency, bandwidth] = await Promise.all([
127
+ this.measureLatency(ip),
128
+ this.measureBandwidth(ip),
129
+ ]);
130
+ return { ip, latency, bandwidth };
131
+ }
132
+ catch (error) {
133
+ // Peer failed, skip it by returning null
134
+ return null;
135
+ }
136
+ };
137
+ // Process all peers with a concurrency limit and cooldown between batches
138
+ const peerMetrics = (await (0, promiseUtils_1.asyncPool)(limit, this.ipAddresses, iteratorFn, cooldown)).filter((metrics) => metrics !== null); // Use a type guard
139
139
  // Sort by lowest latency first, then by highest bandwidth
140
140
  peerMetrics.sort((a, b) => {
141
141
  if (a.latency === b.latency) {
@@ -143,9 +143,7 @@ class PeerRanker {
143
143
  }
144
144
  return a.latency - b.latency; // Lower latency is better
145
145
  });
146
- // Update the internal sorted list
147
146
  this.sortedPeers = peerMetrics;
148
- // Reset the iterator index
149
147
  this.currentIndex = 0;
150
148
  return peerMetrics;
151
149
  }
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Processes items in sequential batches with a concurrency limit.
3
+ * Adds a cooldown between batches.
3
4
  * @param {number} limit - The maximum number of concurrent executions per batch.
4
5
  * @param {Array<T>} items - The array of items to process.
5
6
  * @param {(item: T) => Promise<R>} iteratorFn - The async function to apply to each item.
7
+ * @param {number} cooldownMs - The cooldown duration between batches in milliseconds.
6
8
  * @returns {Promise<Array<R>>} - A promise that resolves when all items have been processed.
7
9
  */
8
- export declare function asyncPool<T, R>(limit: number, items: T[], iteratorFn: (item: T) => Promise<R>): Promise<R[]>;
10
+ export declare function asyncPool<T, R>(limit: number, items: T[], iteratorFn: (item: T) => Promise<R>, cooldownMs?: number): Promise<R[]>;
9
11
  /**
10
12
  * Helper function to add a timeout to a promise.
11
13
  * @param promise The original promise.
@@ -1 +1 @@
1
- {"version":3,"file":"promiseUtils.d.ts","sourceRoot":"","sources":["../../src/utils/promiseUtils.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,CAAC,EAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,EAAE,EACV,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,EAAE,CAAC,CAgBd;AAED;;;;;;EAMC;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,WAClB,OAAO,CAAC,CAAC,CAAC,MACf,MAAM,kBACM,MAAM,KACrB,OAAO,CAAC,CAAC,CAOX,CAAC"}
1
+ {"version":3,"file":"promiseUtils.d.ts","sourceRoot":"","sources":["../../src/utils/promiseUtils.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,CAAC,EAClC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,EAAE,EACV,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,UAAU,GAAE,MAAY,GACvB,OAAO,CAAC,CAAC,EAAE,CAAC,CAkBd;AACC;;;;;;EAMC;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,WAClB,OAAO,CAAC,CAAC,CAAC,MACf,MAAM,kBACM,MAAM,KACrB,OAAO,CAAC,CAAC,CAOX,CAAC"}
@@ -4,12 +4,15 @@ exports.withTimeout = void 0;
4
4
  exports.asyncPool = asyncPool;
5
5
  /**
6
6
  * Processes items in sequential batches with a concurrency limit.
7
+ * Adds a cooldown between batches.
7
8
  * @param {number} limit - The maximum number of concurrent executions per batch.
8
9
  * @param {Array<T>} items - The array of items to process.
9
10
  * @param {(item: T) => Promise<R>} iteratorFn - The async function to apply to each item.
11
+ * @param {number} cooldownMs - The cooldown duration between batches in milliseconds.
10
12
  * @returns {Promise<Array<R>>} - A promise that resolves when all items have been processed.
11
13
  */
12
- async function asyncPool(limit, items, iteratorFn) {
14
+ async function asyncPool(limit, items, iteratorFn, cooldownMs = 500 // Default cooldown of 500ms
15
+ ) {
13
16
  const ret = [];
14
17
  for (let i = 0; i < items.length; i += limit) {
15
18
  const batchItems = items.slice(i, i + limit);
@@ -17,8 +20,10 @@ async function asyncPool(limit, items, iteratorFn) {
17
20
  // Wait for the current batch to complete before starting the next one
18
21
  const batchResults = await Promise.all(batchPromises);
19
22
  ret.push(...batchResults);
20
- // Optional: add a cooldown between batches
21
- // await new Promise((resolve) => setTimeout(resolve, 500));
23
+ // Add a cooldown between batches, except after the last batch
24
+ if (i + limit < items.length) {
25
+ await new Promise((resolve) => setTimeout(resolve, cooldownMs));
26
+ }
22
27
  }
23
28
  return ret;
24
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/dig-sdk",
3
- "version": "0.0.1-alpha.144",
3
+ "version": "0.0.1-alpha.147",
4
4
  "description": "",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",