@dignetwork/dig-sdk 0.0.1-alpha.133 → 0.0.1-alpha.135

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.
@@ -1 +1 @@
1
- {"version":3,"file":"PropagationServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/PropagationServer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAkBrC,qBAAa,iBAAiB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;gBAErC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAY9C;;OAEG;IACG,gBAAgB;IAOtB;;OAEG;IACH,gBAAgB;IAQhB;;;OAGG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BpD;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2CjD;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM;IAyB3D;;OAEG;IACG,gBAAgB,CACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IA4C7D;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAyDzC;;OAEG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;IA2BlD;;;OAGG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,OAAe;IAsH7B;;OAEG;IACG,mBAAmB;IAkCzB;;OAEG;WACU,WAAW,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;IA0DnB;;;OAGG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkFlD;;OAEG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B7C;;;OAGG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM;IA0GjB;;OAEG;WACU,aAAa,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;CA2FpB"}
1
+ {"version":3,"file":"PropagationServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/PropagationServer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAkBrC,qBAAa,iBAAiB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;gBAErC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAY9C;;OAEG;IACG,gBAAgB;IAOtB;;OAEG;IACH,gBAAgB;IAQhB;;;OAGG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BpD;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCjD;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM;IAyB3D;;OAEG;IACG,gBAAgB,CACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IA4C7D;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAyDzC;;OAEG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;IA2BlD;;;OAGG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,OAAe;IAsH7B;;OAEG;IACG,mBAAmB;IAkCzB;;OAEG;WACU,WAAW,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;IA0DnB;;;OAGG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkFlD;;OAEG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B7C;;;OAGG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM;IA0GjB;;OAEG;WACU,aAAa,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;CA2FpB"}
@@ -120,7 +120,6 @@ class PropagationServer {
120
120
  * @param rootHash - The root hash for the store update.
121
121
  */
122
122
  async pingUpdate(rootHash) {
123
- const spinner = (0, nanospinner_1.createSpinner)(`Pinging peer ${this.ipAddress}...`).start();
124
123
  try {
125
124
  const httpsAgent = this.createHttpsAgent();
126
125
  const url = `https://${(0, network_1.formatHost)(this.ipAddress)}:${PropagationServer.port}/update`;
@@ -139,9 +138,6 @@ class PropagationServer {
139
138
  try {
140
139
  const response = await axios_1.default.post(url, data, config);
141
140
  console.log((0, colorette_1.green)(`✔ Successfully pinged peer: ${this.ipAddress}`));
142
- spinner.success({
143
- text: (0, colorette_1.green)(`✔ Successfully pinged peer ${this.ipAddress} with rootHash ${rootHash}`),
144
- });
145
141
  return response.data;
146
142
  }
147
143
  catch (error) {
@@ -151,7 +147,6 @@ class PropagationServer {
151
147
  }
152
148
  }
153
149
  catch (error) {
154
- spinner.error({ text: (0, colorette_1.red)("✖ Error pinging network peer.") });
155
150
  console.error((0, colorette_1.red)(error.message));
156
151
  }
157
152
  }
@@ -197,9 +197,9 @@ class DataStore {
197
197
  }
198
198
  static getAllStores() {
199
199
  const storeFolders = fs_1.default.readdirSync(config_1.STORE_PATH);
200
- const storIds = storeFolders.filter((folder) => /^[a-f0-9]{64}$/.test(folder) &&
200
+ const storeIds = storeFolders.filter((folder) => /^[a-f0-9]{64}$/.test(folder) &&
201
201
  fs_1.default.lstatSync(path_1.default.join(config_1.STORE_PATH, folder)).isDirectory());
202
- return storIds.map((storeId) => DataStore.from(storeId));
202
+ return storeIds.map((storeId) => DataStore.from(storeId));
203
203
  }
204
204
  /**
205
205
  * Fetches the latest coin information using the StoreMonitorRegistry.
@@ -11,6 +11,7 @@ export declare class FullNodePeer {
11
11
  private static prioritizedPeers;
12
12
  private static peerInfos;
13
13
  private static peerIPCache;
14
+ private static limiter;
14
15
  private constructor();
15
16
  /**
16
17
  * Retrieves the singleton instance of FullNodePeer.
@@ -86,13 +87,18 @@ export declare class FullNodePeer {
86
87
  */
87
88
  private static getBestPeer;
88
89
  /**
89
- * Creates a proxy for the peer to handle errors and implement retries.
90
+ * Creates a proxy for the peer to handle errors, implement retries, and enforce throttling.
90
91
  * @param {Peer} peer - The Peer instance.
91
92
  * @param {string} peerIP - The IP address of the peer.
92
93
  * @param {number} [retryCount=0] - The current retry attempt.
93
94
  * @returns {Peer} The proxied Peer instance.
94
95
  */
95
96
  private static createPeerProxy;
97
+ /**
98
+ * Handles peer disconnection by marking it in cooldown and updating internal states.
99
+ * @param {string} peerIP - The IP address of the disconnected peer.
100
+ */
101
+ private static handlePeerDisconnection;
96
102
  /**
97
103
  * Extracts the IP address from a Peer instance.
98
104
  * @param {Peer} peer - The Peer instance.
@@ -100,10 +106,16 @@ export declare class FullNodePeer {
100
106
  */
101
107
  private static extractPeerIP;
102
108
  /**
103
- * Waits for a coin to be confirmed (spent) on the blockchain.
104
- * @param {Buffer} parentCoinInfo - The parent coin information.
105
- * @returns {Promise<boolean>} Whether the coin was confirmed.
106
- */
109
+ * Waits for a coin to be confirmed (spent) on the blockchain.
110
+ * @param {Buffer} parentCoinInfo - The parent coin information.
111
+ * @returns {Promise<boolean>} Whether the coin was confirmed.
112
+ */
107
113
  static waitForConfirmation(parentCoinInfo: Buffer): Promise<boolean>;
114
+ /**
115
+ * Delays execution for a specified amount of time.
116
+ * @param {number} ms - Milliseconds to delay.
117
+ * @returns {Promise<void>} A promise that resolves after the delay.
118
+ */
119
+ private static delay;
108
120
  }
109
121
  //# sourceMappingURL=FullNodePeer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAO,MAAM,8BAA8B,CAAC;AAiCzD;;GAEG;AACH,qBAAa,YAAY;IAuBH,OAAO,CAAC,IAAI;IArBhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IAGpD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAG3E,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuD;IAGnF,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkC;IAG5D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAgB;IAG/C,OAAO,CAAC,MAAM,CAAC,SAAS,CAAoC;IAG5D,OAAO,CAAC,MAAM,CAAC,WAAW,CAAoD;IAG9E,OAAO;IAEP;;;OAGG;WACW,WAAW,IAAI,YAAY;IAOzC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;WACiB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAuB9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IASjC;;;;OAIG;mBACkB,eAAe;IAyFpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAgBpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAwBjC;;;OAGG;mBACkB,UAAU;IAK/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB;;;OAGG;mBACkB,WAAW;IAyEhC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAuE9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAS1B;;;;KAIC;WACmB,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAyBtB"}
1
+ {"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAO,MAAM,8BAA8B,CAAC;AA6CzD;;GAEG;AACH,qBAAa,YAAY;IA6BH,OAAO,CAAC,IAAI;IA3BhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IAGpD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAG3E,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuD;IAGnF,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkC;IAG5D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAgB;IAG/C,OAAO,CAAC,MAAM,CAAC,SAAS,CAAoC;IAG5D,OAAO,CAAC,MAAM,CAAC,WAAW,CAAoD;IAG9E,OAAO,CAAC,MAAM,CAAC,OAAO,CAGnB;IAGH,OAAO;IAEP;;;OAGG;WACW,WAAW,IAAI,YAAY;IAOzC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;WACiB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAuB9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IASjC;;;;OAIG;mBACkB,eAAe;IAyFpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAgBpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAwBjC;;;OAGG;mBACkB,UAAU;IAK/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB;;;OAGG;mBACkB,WAAW;IA2EhC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAsF9B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,uBAAuB;IA4BtC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAS5B;;;;OAIG;WACiB,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;IAkCnB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,KAAK;CAGrB"}
@@ -14,6 +14,7 @@ const nanospinner_1 = require("nanospinner");
14
14
  const config_1 = require("../utils/config");
15
15
  const Environment_1 = require("../utils/Environment");
16
16
  const node_cache_1 = __importDefault(require("node-cache"));
17
+ const bottleneck_1 = __importDefault(require("bottleneck"));
17
18
  // Constants
18
19
  const FULLNODE_PORT = 8444;
19
20
  const LOCALHOST = "127.0.0.1";
@@ -26,9 +27,10 @@ const DNS_HOSTS = [
26
27
  ];
27
28
  const CONNECTION_TIMEOUT = 2000; // in milliseconds
28
29
  const CACHE_DURATION = 30000; // in milliseconds
29
- const COOLDOWN_DURATION = 60000; // in milliseconds
30
+ const COOLDOWN_DURATION = 300000; // 5 minutes in milliseconds
30
31
  const MAX_PEERS_TO_FETCH = 5; // Maximum number of peers to fetch from DNS
31
32
  const MAX_RETRIES = 3; // Maximum number of retry attempts
33
+ const MAX_REQUESTS_PER_MINUTE = 100; // Throttle limit
32
34
  /**
33
35
  * FullNodePeer manages connections to full nodes, prioritizing certain peers and handling reliability.
34
36
  */
@@ -268,9 +270,10 @@ class FullNodePeer {
268
270
  */
269
271
  static async getBestPeer() {
270
272
  const now = Date.now();
271
- // Refresh cachedPeer if expired
273
+ // Refresh cachedPeer if expired or disconnected
272
274
  if (FullNodePeer.cachedPeer &&
273
- now - FullNodePeer.cachedPeer.timestamp < CACHE_DURATION) {
275
+ now - FullNodePeer.cachedPeer.timestamp < CACHE_DURATION &&
276
+ FullNodePeer.peerInfos.get(FullNodePeer.extractPeerIP(FullNodePeer.cachedPeer.peer) || "")?.isConnected) {
274
277
  return FullNodePeer.cachedPeer.peer;
275
278
  }
276
279
  // Fetch peer IPs
@@ -318,6 +321,7 @@ class FullNodePeer {
318
321
  peer: proxiedPeer,
319
322
  weight: FullNodePeer.peerWeights.get(selectedPeerIP) || 1,
320
323
  address: selectedPeerIP,
324
+ isConnected: true, // Mark as connected
321
325
  });
322
326
  // Cache the peer
323
327
  FullNodePeer.cachedPeer = { peer: proxiedPeer, timestamp: now };
@@ -325,75 +329,114 @@ class FullNodePeer {
325
329
  return proxiedPeer;
326
330
  }
327
331
  /**
328
- * Creates a proxy for the peer to handle errors and implement retries.
332
+ * Creates a proxy for the peer to handle errors, implement retries, and enforce throttling.
329
333
  * @param {Peer} peer - The Peer instance.
330
334
  * @param {string} peerIP - The IP address of the peer.
331
335
  * @param {number} [retryCount=0] - The current retry attempt.
332
336
  * @returns {Peer} The proxied Peer instance.
333
337
  */
334
338
  static createPeerProxy(peer, peerIP, retryCount = 0) {
339
+ // Listen for close events if the Peer class supports it
340
+ // This assumes that the Peer class emits a 'close' event when the connection is closed
341
+ // Adjust accordingly based on the actual Peer implementation
342
+ if (typeof peer.on === "function") {
343
+ peer.on("close", () => {
344
+ console.warn(`Peer ${peerIP} connection closed.`);
345
+ FullNodePeer.handlePeerDisconnection(peerIP);
346
+ });
347
+ peer.on("error", (error) => {
348
+ console.error(`Peer ${peerIP} encountered an error: ${error.message}`);
349
+ FullNodePeer.handlePeerDisconnection(peerIP);
350
+ });
351
+ }
335
352
  return new Proxy(peer, {
336
353
  get: (target, prop) => {
337
354
  const originalMethod = target[prop];
338
355
  if (typeof originalMethod === "function") {
339
- return async (...args) => {
340
- try {
341
- const result = await originalMethod.apply(target, args);
342
- // On successful operation, increase the weight slightly
343
- const currentWeight = FullNodePeer.peerWeights.get(peerIP) || 1;
344
- FullNodePeer.peerWeights.set(peerIP, currentWeight + 0.1); // Increment weight
345
- return result;
346
- }
347
- catch (error) {
348
- console.error(`Peer ${peerIP} encountered an error: ${error.message}`);
349
- // Check if the error is related to WebSocket or Operation timed out
350
- if (error.message.includes("WebSocket") ||
351
- error.message.includes("Operation timed out")) {
352
- // Add the faulty peer to the cooldown cache
353
- FullNodePeer.cooldownCache.set(peerIP, true);
354
- // Decrease weight or remove peer
356
+ return (...args) => {
357
+ // Wrap the method call with Bottleneck's scheduling
358
+ return FullNodePeer.limiter.schedule(async () => {
359
+ const peerInfo = FullNodePeer.peerInfos.get(peerIP);
360
+ if (!peerInfo || !peerInfo.isConnected) {
361
+ throw new Error(`Cannot perform operation: Peer ${peerIP} is disconnected.`);
362
+ }
363
+ try {
364
+ const result = await originalMethod.apply(target, args);
365
+ // On successful operation, increase the weight slightly
355
366
  const currentWeight = FullNodePeer.peerWeights.get(peerIP) || 1;
356
- if (currentWeight > 1) {
357
- FullNodePeer.peerWeights.set(peerIP, currentWeight - 1);
367
+ FullNodePeer.peerWeights.set(peerIP, currentWeight + 0.1); // Increment weight
368
+ return result;
369
+ }
370
+ catch (error) {
371
+ console.error(`Peer ${peerIP} encountered an error: ${error.message}`);
372
+ // Check if the error is related to WebSocket or Operation timed out
373
+ if (error.message.includes("WebSocket") ||
374
+ error.message.includes("Operation timed out")) {
375
+ // Handle the disconnection and mark the peer accordingly
376
+ FullNodePeer.handlePeerDisconnection(peerIP);
377
+ // If maximum retries reached, throw the error
378
+ if (retryCount >= MAX_RETRIES) {
379
+ console.error(`Max retries reached for method ${String(prop)} on peer ${peerIP}.`);
380
+ throw error;
381
+ }
382
+ // Attempt to select a new peer and retry the method
383
+ try {
384
+ console.info(`Selecting a new peer to retry method ${String(prop)}...`);
385
+ const newPeer = await FullNodePeer.getBestPeer();
386
+ // Extract new peer's IP address
387
+ const newPeerIP = FullNodePeer.extractPeerIP(newPeer);
388
+ if (!newPeerIP) {
389
+ throw new Error("Unable to extract IP from the new peer.");
390
+ }
391
+ // Wrap the new peer with a proxy, incrementing the retry count
392
+ const proxiedNewPeer = FullNodePeer.createPeerProxy(newPeer, newPeerIP, retryCount + 1);
393
+ // Retry the method on the new peer
394
+ return await proxiedNewPeer[prop](...args);
395
+ }
396
+ catch (retryError) {
397
+ console.error(`Retry failed on a new peer: ${retryError.message}`);
398
+ throw retryError;
399
+ }
358
400
  }
359
401
  else {
360
- FullNodePeer.peerWeights.delete(peerIP);
361
- }
362
- // If maximum retries reached, throw the error
363
- if (retryCount >= MAX_RETRIES) {
364
- console.error(`Max retries reached for method ${String(prop)} on peer ${peerIP}.`);
402
+ // For other errors, handle normally
365
403
  throw error;
366
404
  }
367
- // Attempt to select a new peer and retry the method
368
- try {
369
- console.info(`Selecting a new peer to retry method ${String(prop)}...`);
370
- const newPeer = await FullNodePeer.getBestPeer();
371
- // Extract new peer's IP address
372
- const newPeerIP = FullNodePeer.extractPeerIP(newPeer);
373
- if (!newPeerIP) {
374
- throw new Error("Unable to extract IP from the new peer.");
375
- }
376
- // Wrap the new peer with a proxy, incrementing the retry count
377
- const proxiedNewPeer = FullNodePeer.createPeerProxy(newPeer, newPeerIP, retryCount + 1);
378
- // Retry the method on the new peer
379
- return await proxiedNewPeer[prop](...args);
380
- }
381
- catch (retryError) {
382
- console.error(`Retry failed on a new peer: ${retryError.message}`);
383
- throw retryError;
384
- }
385
405
  }
386
- else {
387
- // For other errors, handle normally
388
- throw error;
389
- }
390
- }
406
+ });
391
407
  };
392
408
  }
393
409
  return originalMethod;
394
410
  },
395
411
  });
396
412
  }
413
+ /**
414
+ * Handles peer disconnection by marking it in cooldown and updating internal states.
415
+ * @param {string} peerIP - The IP address of the disconnected peer.
416
+ */
417
+ static handlePeerDisconnection(peerIP) {
418
+ // Mark the peer in cooldown
419
+ FullNodePeer.cooldownCache.set(peerIP, true);
420
+ // Decrease weight or remove peer
421
+ const currentWeight = FullNodePeer.peerWeights.get(peerIP) || 1;
422
+ if (currentWeight > 1) {
423
+ FullNodePeer.peerWeights.set(peerIP, currentWeight - 1);
424
+ }
425
+ else {
426
+ FullNodePeer.peerWeights.delete(peerIP);
427
+ }
428
+ // Update the peer's connection status
429
+ const peerInfo = FullNodePeer.peerInfos.get(peerIP);
430
+ if (peerInfo) {
431
+ peerInfo.isConnected = false;
432
+ FullNodePeer.peerInfos.set(peerIP, peerInfo);
433
+ }
434
+ // If the disconnected peer was the cached peer, invalidate the cache
435
+ if (FullNodePeer.cachedPeer &&
436
+ FullNodePeer.extractPeerIP(FullNodePeer.cachedPeer.peer) === peerIP) {
437
+ FullNodePeer.cachedPeer = null;
438
+ }
439
+ }
397
440
  /**
398
441
  * Extracts the IP address from a Peer instance.
399
442
  * @param {Peer} peer - The Peer instance.
@@ -408,13 +451,21 @@ class FullNodePeer {
408
451
  return null;
409
452
  }
410
453
  /**
411
- * Waits for a coin to be confirmed (spent) on the blockchain.
412
- * @param {Buffer} parentCoinInfo - The parent coin information.
413
- * @returns {Promise<boolean>} Whether the coin was confirmed.
414
- */
454
+ * Waits for a coin to be confirmed (spent) on the blockchain.
455
+ * @param {Buffer} parentCoinInfo - The parent coin information.
456
+ * @returns {Promise<boolean>} Whether the coin was confirmed.
457
+ */
415
458
  static async waitForConfirmation(parentCoinInfo) {
416
459
  const spinner = (0, nanospinner_1.createSpinner)("Waiting for confirmation...").start();
417
- const peer = await FullNodePeer.connect();
460
+ let peer;
461
+ try {
462
+ peer = await FullNodePeer.connect();
463
+ }
464
+ catch (error) {
465
+ spinner.error({ text: "Failed to connect to a peer." });
466
+ console.error(`waitForConfirmation connection error: ${error.message}`);
467
+ throw error;
468
+ }
418
469
  try {
419
470
  while (true) {
420
471
  const confirmed = await peer.isCoinSpent(parentCoinInfo, config_1.MIN_HEIGHT, Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
@@ -422,7 +473,7 @@ class FullNodePeer {
422
473
  spinner.success({ text: "Coin confirmed!" });
423
474
  return true;
424
475
  }
425
- await new Promise((resolve) => setTimeout(resolve, 5000));
476
+ await FullNodePeer.delay(5000);
426
477
  }
427
478
  }
428
479
  catch (error) {
@@ -431,6 +482,14 @@ class FullNodePeer {
431
482
  throw error;
432
483
  }
433
484
  }
485
+ /**
486
+ * Delays execution for a specified amount of time.
487
+ * @param {number} ms - Milliseconds to delay.
488
+ * @returns {Promise<void>} A promise that resolves after the delay.
489
+ */
490
+ static delay(ms) {
491
+ return new Promise((resolve) => setTimeout(resolve, ms));
492
+ }
434
493
  }
435
494
  exports.FullNodePeer = FullNodePeer;
436
495
  // Singleton instance
@@ -447,3 +506,8 @@ FullNodePeer.prioritizedPeers = [];
447
506
  FullNodePeer.peerInfos = new Map();
448
507
  // Cache for fetched peer IPs
449
508
  FullNodePeer.peerIPCache = new node_cache_1.default({ stdTTL: CACHE_DURATION / 1000 });
509
+ // Bottleneck instance for global throttling
510
+ FullNodePeer.limiter = new bottleneck_1.default({
511
+ maxConcurrent: 1, // Ensures only one request is processed at a time
512
+ minTime: 60000 / MAX_REQUESTS_PER_MINUTE, // Calculated delay between requests
513
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/dig-sdk",
3
- "version": "0.0.1-alpha.133",
3
+ "version": "0.0.1-alpha.135",
4
4
  "description": "",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",
@@ -30,6 +30,7 @@
30
30
  "archiver": "^7.0.1",
31
31
  "axios": "^1.7.7",
32
32
  "bip39": "^3.1.0",
33
+ "bottleneck": "^2.19.5",
33
34
  "chia-bls": "^1.0.2",
34
35
  "chia-config-loader": "^1.0.1",
35
36
  "chia-root-resolver": "^1.0.0",