@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.
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +0 -5
- package/dist/blockchain/DataStore.js +2 -2
- package/dist/blockchain/FullNodePeer.d.ts +17 -5
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +120 -56
- package/package.json +2 -1
|
@@ -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;
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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;
|
|
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 =
|
|
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
|
|
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
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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",
|