diodejs 0.2.0 → 0.2.1
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 +12 -2
- package/connection.js +93 -3
- package/package.json +1 -1
- package/rpc.js +36 -23
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ Connection retry behavior can be configured via environment variables:
|
|
|
25
25
|
| DIODE_RETRY_DELAY | Initial delay between retries (ms) | 1000 |
|
|
26
26
|
| DIODE_MAX_RETRY_DELAY | Maximum delay between retries (ms) | 30000 |
|
|
27
27
|
| DIODE_AUTO_RECONNECT | Whether to automatically reconnect | true |
|
|
28
|
+
| DIODE_TICKET_BYTES_THRESHOLD | Bytes threshold for ticket updates | 512000 (512KB) |
|
|
29
|
+
| DIODE_TICKET_UPDATE_INTERVAL | Time interval for ticket updates (ms) | 30000 (30s) |
|
|
28
30
|
|
|
29
31
|
Example `.env` file:
|
|
30
32
|
```
|
|
@@ -32,6 +34,8 @@ DIODE_MAX_RETRIES=10
|
|
|
32
34
|
DIODE_RETRY_DELAY=2000
|
|
33
35
|
DIODE_MAX_RETRY_DELAY=20000
|
|
34
36
|
DIODE_AUTO_RECONNECT=true
|
|
37
|
+
DIODE_TICKET_BYTES_THRESHOLD=512000
|
|
38
|
+
DIODE_TICKET_UPDATE_INTERVAL=30000
|
|
35
39
|
```
|
|
36
40
|
|
|
37
41
|
These settings can also be configured programmatically:
|
|
@@ -40,7 +44,9 @@ connection.setReconnectOptions({
|
|
|
40
44
|
maxRetries: 10,
|
|
41
45
|
retryDelay: 2000,
|
|
42
46
|
maxRetryDelay: 20000,
|
|
43
|
-
autoReconnect: true
|
|
47
|
+
autoReconnect: true,
|
|
48
|
+
ticketBytesThreshold: 512000,
|
|
49
|
+
ticketUpdateInterval: 30000
|
|
44
50
|
});
|
|
45
51
|
```
|
|
46
52
|
|
|
@@ -63,7 +69,9 @@ async function main() {
|
|
|
63
69
|
maxRetries: Infinity, // Unlimited reconnection attempts
|
|
64
70
|
retryDelay: 1000, // Initial delay of 1 second
|
|
65
71
|
maxRetryDelay: 30000, // Maximum delay of 30 seconds
|
|
66
|
-
autoReconnect: true
|
|
72
|
+
autoReconnect: true, // Automatically reconnect on disconnection
|
|
73
|
+
ticketBytesThreshold: 512000, // Bytes threshold for ticket updates
|
|
74
|
+
ticketUpdateInterval: 30000 // Time interval for ticket updates
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
// Listen for reconnection events (optional)
|
|
@@ -219,6 +227,8 @@ main();
|
|
|
219
227
|
- `retryDelay` (number): Initial delay between retries in ms (default: 1000)
|
|
220
228
|
- `maxRetryDelay` (number): Maximum delay between retries in ms (default: 30000)
|
|
221
229
|
- `autoReconnect` (boolean): Whether to automatically reconnect on disconnection (default: true)
|
|
230
|
+
- `ticketBytesThreshold` (number): Bytes threshold for ticket updates (default: 512000)
|
|
231
|
+
- `ticketUpdateInterval` (number): Time interval for ticket updates in ms (default: 30000)
|
|
222
232
|
|
|
223
233
|
- **Events**:
|
|
224
234
|
- `reconnecting`: Emitted when a reconnection attempt is about to start, with `attempt` and `delay` information
|
package/connection.js
CHANGED
|
@@ -62,6 +62,16 @@ class DiodeConnection extends EventEmitter {
|
|
|
62
62
|
logger.info(`Connection settings - Auto Reconnect: ${this.autoReconnect}, Max Retries: ${
|
|
63
63
|
this.maxRetries === Infinity ? 'Infinity' : this.maxRetries
|
|
64
64
|
}, Retry Delay: ${this.retryDelay}ms, Max Retry Delay: ${this.maxRetryDelay}ms`);
|
|
65
|
+
|
|
66
|
+
// Add ticket batching configuration
|
|
67
|
+
this.lastTicketUpdate = Date.now();
|
|
68
|
+
this.accumulatedBytes = 0;
|
|
69
|
+
this.ticketUpdateThreshold = parseInt(process.env.DIODE_TICKET_BYTES_THRESHOLD, 10) || 512000; // 512KB default
|
|
70
|
+
this.ticketUpdateInterval = parseInt(process.env.DIODE_TICKET_UPDATE_INTERVAL, 10) || 30000; // 30 seconds default
|
|
71
|
+
this.ticketUpdateTimer = null;
|
|
72
|
+
|
|
73
|
+
// Log the ticket batching settings
|
|
74
|
+
logger.info(`Ticket batching settings - Bytes Threshold: ${this.ticketUpdateThreshold} bytes, Update Interval: ${this.ticketUpdateInterval}ms`);
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
connect() {
|
|
@@ -96,7 +106,6 @@ class DiodeConnection extends EventEmitter {
|
|
|
96
106
|
try {
|
|
97
107
|
const ticketCommand = await this.createTicketCommand();
|
|
98
108
|
const response = await this.sendCommand(ticketCommand).catch(reject);
|
|
99
|
-
logger.info(`Ticket accepted: ${makeReadable(response)}`);
|
|
100
109
|
resolve();
|
|
101
110
|
} catch (error) {
|
|
102
111
|
logger.error(`Error sending ticket: ${error}`);
|
|
@@ -113,6 +122,11 @@ class DiodeConnection extends EventEmitter {
|
|
|
113
122
|
}
|
|
114
123
|
});
|
|
115
124
|
|
|
125
|
+
// Start the periodic ticket update timer after successful connection
|
|
126
|
+
this.socket.on('connect', () => {
|
|
127
|
+
this._startTicketUpdateTimer();
|
|
128
|
+
});
|
|
129
|
+
|
|
116
130
|
this.socket.on('error', (err) => {
|
|
117
131
|
logger.error(`Connection error: ${err}`);
|
|
118
132
|
reject(err);
|
|
@@ -242,6 +256,11 @@ class DiodeConnection extends EventEmitter {
|
|
|
242
256
|
|
|
243
257
|
// Update close method to prevent reconnection when intentionally closing
|
|
244
258
|
close() {
|
|
259
|
+
if (this.ticketUpdateTimer) {
|
|
260
|
+
clearTimeout(this.ticketUpdateTimer);
|
|
261
|
+
this.ticketUpdateTimer = null;
|
|
262
|
+
}
|
|
263
|
+
|
|
245
264
|
this.autoReconnect = false;
|
|
246
265
|
if (this.retryTimeoutId) {
|
|
247
266
|
clearTimeout(this.retryTimeoutId);
|
|
@@ -431,7 +450,6 @@ class DiodeConnection extends EventEmitter {
|
|
|
431
450
|
const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
|
|
432
451
|
const address = '0x' + addressBuffer.toString('hex');
|
|
433
452
|
|
|
434
|
-
logger.info(`Ethereum address: ${address}`);
|
|
435
453
|
return address;
|
|
436
454
|
} catch (error) {
|
|
437
455
|
logger.error(`Error extracting Ethereum address: ${error}`);
|
|
@@ -455,7 +473,6 @@ class DiodeConnection extends EventEmitter {
|
|
|
455
473
|
const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
|
|
456
474
|
const address = '0x' + addressBuffer.toString('hex');
|
|
457
475
|
|
|
458
|
-
logger.info(`Server Ethereum address: ${address}`);
|
|
459
476
|
return address;
|
|
460
477
|
} catch (error) {
|
|
461
478
|
logger.error(`Error extracting server Ethereum address: ${error}`);
|
|
@@ -605,6 +622,79 @@ class DiodeConnection extends EventEmitter {
|
|
|
605
622
|
hasConnection(ref) {
|
|
606
623
|
return this.connections.has(ref.toString('hex'));
|
|
607
624
|
}
|
|
625
|
+
|
|
626
|
+
// Start timer for periodic ticket updates
|
|
627
|
+
_startTicketUpdateTimer() {
|
|
628
|
+
if (this.ticketUpdateTimer) {
|
|
629
|
+
clearTimeout(this.ticketUpdateTimer);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
this.ticketUpdateTimer = setTimeout(() => {
|
|
633
|
+
this._updateTicketIfNeeded(true);
|
|
634
|
+
}, this.ticketUpdateInterval);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Method to check if ticket update is needed and perform it
|
|
638
|
+
async _updateTicketIfNeeded(force = false) {
|
|
639
|
+
// If socket is not connected, don't try to update
|
|
640
|
+
if (!this.socket || this.socket.destroyed) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const timeSinceLastUpdate = Date.now() - this.lastTicketUpdate;
|
|
645
|
+
|
|
646
|
+
if (force ||
|
|
647
|
+
this.accumulatedBytes >= this.ticketUpdateThreshold ||
|
|
648
|
+
timeSinceLastUpdate >= this.ticketUpdateInterval) {
|
|
649
|
+
|
|
650
|
+
try {
|
|
651
|
+
if (this.accumulatedBytes > 0 || force) {
|
|
652
|
+
logger.debug(`Updating ticket: accumulated ${this.accumulatedBytes} bytes, ${timeSinceLastUpdate}ms since last update`);
|
|
653
|
+
const ticketCommand = await this.createTicketCommand();
|
|
654
|
+
await this.sendCommand(ticketCommand);
|
|
655
|
+
|
|
656
|
+
// Reset counters
|
|
657
|
+
this.accumulatedBytes = 0;
|
|
658
|
+
this.lastTicketUpdate = Date.now();
|
|
659
|
+
}
|
|
660
|
+
} catch (error) {
|
|
661
|
+
logger.error(`Error updating ticket: ${error}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Restart the timer
|
|
666
|
+
this._startTicketUpdateTimer();
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Add method to track bytes without immediate ticket update
|
|
670
|
+
addBytes(bytesCount) {
|
|
671
|
+
this.totalBytes += bytesCount;
|
|
672
|
+
this.accumulatedBytes += bytesCount;
|
|
673
|
+
|
|
674
|
+
// Optionally check if we should update ticket now
|
|
675
|
+
if (this.accumulatedBytes >= this.ticketUpdateThreshold) {
|
|
676
|
+
this._updateTicketIfNeeded();
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Method to set ticket batching options
|
|
681
|
+
setTicketBatchingOptions(options = {}) {
|
|
682
|
+
if (typeof options.threshold === 'number') {
|
|
683
|
+
this.ticketUpdateThreshold = options.threshold;
|
|
684
|
+
}
|
|
685
|
+
if (typeof options.interval === 'number') {
|
|
686
|
+
this.ticketUpdateInterval = options.interval;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
logger.info(`Updated ticket batching settings - Bytes Threshold: ${this.ticketUpdateThreshold} bytes, Update Interval: ${this.ticketUpdateInterval}ms`);
|
|
690
|
+
|
|
691
|
+
// Reset the timer with new interval
|
|
692
|
+
if (this.socket && !this.socket.destroyed) {
|
|
693
|
+
this._startTicketUpdateTimer();
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return this;
|
|
697
|
+
}
|
|
608
698
|
}
|
|
609
699
|
|
|
610
700
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diodejs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A JavaScript client for interacting with the Diode network. It provides functionalities to bind and publish ports, send RPC commands, and handle responses.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
package/rpc.js
CHANGED
|
@@ -98,9 +98,9 @@ class DiodeRPC {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
async portSend(ref, data) {
|
|
101
|
-
// Update
|
|
101
|
+
// Update bytes count but don't update ticket yet
|
|
102
102
|
const bytesToSend = data.length;
|
|
103
|
-
this.connection.
|
|
103
|
+
this.connection.addBytes(bytesToSend);
|
|
104
104
|
|
|
105
105
|
// Now send the data
|
|
106
106
|
return this.connection.sendCommand(['portsend', ref, data]).then(async (responseData) => {
|
|
@@ -109,17 +109,7 @@ class DiodeRPC {
|
|
|
109
109
|
const status = parseResponseType(statusRaw);
|
|
110
110
|
|
|
111
111
|
if (status === 'ok') {
|
|
112
|
-
|
|
113
|
-
const ticketCommand = await this.connection.createTicketCommand();
|
|
114
|
-
const ticketResponse = await this.connection.sendCommand(ticketCommand).catch((error) => {
|
|
115
|
-
logger.error(`Error during ticket command: ${error}`);
|
|
116
|
-
throw error;
|
|
117
|
-
});
|
|
118
|
-
logger.debug(`Ticket updated: ${makeReadable(ticketResponse)}`);
|
|
119
|
-
} catch (error) {
|
|
120
|
-
logger.error(`Error updating ticket: ${error}`);
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
112
|
+
// No ticket update here anymore - handled by batching mechanism
|
|
123
113
|
return;
|
|
124
114
|
} else if (status === 'error') {
|
|
125
115
|
throw new Error('Error during port send');
|
|
@@ -192,17 +182,40 @@ class DiodeRPC {
|
|
|
192
182
|
return epoch;
|
|
193
183
|
}
|
|
194
184
|
|
|
195
|
-
parseTimestamp(blockHeader) {
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
185
|
+
parseTimestamp(blockHeader) {
|
|
186
|
+
// Search for the timestamp field by name
|
|
187
|
+
if (Array.isArray(blockHeader)) {
|
|
188
|
+
for (const field of blockHeader) {
|
|
189
|
+
if (Array.isArray(field) && field.length >= 2 && field[0] === 'timestamp') {
|
|
190
|
+
const timestampValue = field[1];
|
|
191
|
+
|
|
192
|
+
// Handle different timestamp value types
|
|
193
|
+
if (typeof timestampValue === 'number') {
|
|
194
|
+
return timestampValue;
|
|
195
|
+
} else if (typeof timestampValue === 'string' && timestampValue.startsWith('0x')) {
|
|
196
|
+
// Handle hex string
|
|
197
|
+
return parseInt(timestampValue.slice(2), 16);
|
|
198
|
+
} else if (typeof timestampValue === 'string') {
|
|
199
|
+
// Handle decimal string
|
|
200
|
+
return parseInt(timestampValue, 10);
|
|
201
|
+
} else if (timestampValue instanceof Uint8Array || Buffer.isBuffer(timestampValue)) {
|
|
202
|
+
// Handle buffer - carefully determine the byte length
|
|
203
|
+
const buf = Buffer.from(timestampValue);
|
|
204
|
+
// Use a safe approach to read the value based on buffer length
|
|
205
|
+
if (buf.length <= 6) {
|
|
206
|
+
return buf.readUIntBE(0, buf.length);
|
|
207
|
+
} else {
|
|
208
|
+
// For larger buffers, convert to hex string first
|
|
209
|
+
return parseInt(buf.toString('hex'), 16);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
205
214
|
}
|
|
215
|
+
|
|
216
|
+
// Fallback: if we couldn't find the timestamp or parse it correctly
|
|
217
|
+
logger.warn('Could not find or parse timestamp in block header, using current time');
|
|
218
|
+
return Math.floor(Date.now() / 1000);
|
|
206
219
|
}
|
|
207
220
|
}
|
|
208
221
|
|