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 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 // Automatically reconnect on disconnection
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.0",
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 totalBytes
101
+ // Update bytes count but don't update ticket yet
102
102
  const bytesToSend = data.length;
103
- this.connection.totalBytes += bytesToSend;
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
- try {
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
- // Implement parsing of timestamp from blockHeader
197
- const timestampRaw = blockHeader[0][1]; // Adjust index based on actual structure
198
- //Timestamp Raw: [ 'timestamp', 1726689425 ]
199
- if (timestampRaw instanceof Uint8Array || Buffer.isBuffer(timestampRaw)) {
200
- return Buffer.from(timestampRaw).readUIntBE(0, timestampRaw.length);
201
- } else if (typeof timestampRaw === 'number') {
202
- return timestampRaw;
203
- } else {
204
- throw new Error('Invalid timestamp format in block header');
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