diodejs 0.2.2 → 0.3.0

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/bindPort.js CHANGED
@@ -2,6 +2,7 @@ const net = require('net');
2
2
  const tls = require('tls');
3
3
  const dgram = require('dgram');
4
4
  const { Buffer } = require('buffer');
5
+ const { toBufferView } = require('./utils');
5
6
  const { Duplex } = require('stream');
6
7
  const DiodeRPC = require('./rpc');
7
8
  const logger = require('./logger');
@@ -9,7 +10,7 @@ const logger = require('./logger');
9
10
  // Custom Duplex stream to handle the Diode connection
10
11
  class DiodeSocket extends Duplex {
11
12
  constructor(ref, rpc) {
12
- super();
13
+ super({ readableHighWaterMark: 256 * 1024, writableHighWaterMark: 256 * 1024, allowHalfOpen: false });
13
14
  this.ref = ref;
14
15
  this.rpc = rpc;
15
16
  this.destroyed = false;
@@ -92,14 +93,14 @@ class BindPort {
92
93
  this.connection.on('unsolicited', (message) => {
93
94
  const [messageIdRaw, messageContent] = message;
94
95
  const messageTypeRaw = messageContent[0];
95
- const messageType = Buffer.from(messageTypeRaw).toString('utf8');
96
+ const messageType = toBufferView(messageTypeRaw).toString('utf8');
96
97
 
97
98
  if (messageType === 'data' || messageType === 'portsend') {
98
99
  const refRaw = messageContent[1];
99
100
  const dataRaw = messageContent[2];
100
101
 
101
- const dataRef = Buffer.from(refRaw);
102
- const data = Buffer.from(dataRaw);
102
+ const dataRef = toBufferView(refRaw);
103
+ const data = toBufferView(dataRaw);
103
104
 
104
105
  // Find the associated client socket from connection
105
106
  const clientSocket = this.connection.getClientSocket(dataRef);
@@ -114,14 +115,14 @@ class BindPort {
114
115
  } else {
115
116
  const connectionInfo = this.connection.getConnection(dataRef);
116
117
  if (connectionInfo) {
117
- logger.debug(`No client socket found for ref: ${dataRef.toString('hex')}, but connection exists for ${connectionInfo.host}:${connectionInfo.port}`);
118
+ logger.debug(() => `No client socket found for ref: ${dataRef.toString('hex')}, but connection exists for ${connectionInfo.host}:${connectionInfo.port}`);
118
119
  } else {
119
- logger.warn(`No client socket found for ref: ${dataRef.toString('hex')}`);
120
+ logger.warn(() => `No client socket found for ref: ${dataRef.toString('hex')}`);
120
121
  }
121
122
  }
122
123
  } else if (messageType === 'portclose') {
123
124
  const refRaw = messageContent[1];
124
- const dataRef = Buffer.from(refRaw);
125
+ const dataRef = toBufferView(refRaw);
125
126
 
126
127
  // Close the associated client socket
127
128
  const clientSocket = this.connection.getClientSocket(dataRef);
@@ -131,31 +132,31 @@ class BindPort {
131
132
  }
132
133
  clientSocket.end();
133
134
  this.connection.deleteClientSocket(dataRef);
134
- logger.info(`Port closed for ref: ${dataRef.toString('hex')}`);
135
+ logger.info(() => `Port closed for ref: ${dataRef.toString('hex')}`);
135
136
  }
136
137
  } else {
137
138
  if (messageType != 'portopen') {
138
- logger.warn(`Unknown unsolicited message type: ${messageType}`);
139
+ logger.warn(() => `Unknown unsolicited message type: ${messageType}`);
139
140
  }
140
141
  }
141
142
  });
142
143
 
143
144
  // Handle device disconnect
144
145
  this.connection.on('end', () => {
145
- logger.info('Disconnected from Diode.io server');
146
+ logger.info(() => 'Disconnected from Diode.io server');
146
147
  this.closeAllServers();
147
148
  });
148
149
 
149
150
  // Handle connection errors
150
151
  this.connection.on('error', (err) => {
151
- logger.error(`Connection error: ${err}`);
152
+ logger.error(() => `Connection error: ${err}`);
152
153
  this.closeAllServers();
153
154
  });
154
155
  }
155
156
 
156
157
  addPort(localPort, targetPort, deviceIdHex, protocol = 'tls') {
157
158
  if (this.servers.has(localPort)) {
158
- logger.warn(`Port ${localPort} is already bound`);
159
+ logger.warn(() => `Port ${localPort} is already bound`);
159
160
  return false;
160
161
  }
161
162
 
@@ -172,7 +173,7 @@ class BindPort {
172
173
 
173
174
  removePort(localPort) {
174
175
  if (!this.portsConfig[localPort]) {
175
- logger.warn(`Port ${localPort} is not configured`);
176
+ logger.warn(() => `Port ${localPort} is not configured`);
176
177
  return false;
177
178
  }
178
179
 
@@ -180,7 +181,7 @@ class BindPort {
180
181
  if (this.servers.has(localPort)) {
181
182
  const server = this.servers.get(localPort);
182
183
  server.close(() => {
183
- logger.info(`Server on port ${localPort} closed`);
184
+ logger.info(() => `Server on port ${localPort} closed`);
184
185
  });
185
186
  this.servers.delete(localPort);
186
187
  }
@@ -193,7 +194,7 @@ class BindPort {
193
194
  closeAllServers() {
194
195
  for (const [localPort, server] of this.servers.entries()) {
195
196
  server.close();
196
- logger.info(`Server on port ${localPort} closed`);
197
+ logger.info(() => `Server on port ${localPort} closed`);
197
198
  }
198
199
  this.servers.clear();
199
200
  }
@@ -201,7 +202,7 @@ class BindPort {
201
202
  bindSinglePort(localPort) {
202
203
  const config = this.portsConfig[localPort];
203
204
  if (!config) {
204
- logger.error(`No configuration found for port ${localPort}`);
205
+ logger.error(() => `No configuration found for port ${localPort}`);
205
206
  return false;
206
207
  }
207
208
 
@@ -210,14 +211,14 @@ class BindPort {
210
211
 
211
212
  // Format the target port with protocol prefix for the remote connection
212
213
  const formattedTargetPort = `${protocol}:${targetPort}`;
213
- logger.info(`Binding local port ${localPort} to remote ${formattedTargetPort}`);
214
+ logger.info(() => `Binding local port ${localPort} to remote ${formattedTargetPort}`);
214
215
 
215
216
  // For udp protocol, use udp server
216
217
  if (protocol === 'udp') {
217
218
  const server = dgram.createSocket('udp4');
218
219
 
219
220
  server.on('listening', () => {
220
- logger.info(`udp server listening on port ${localPort} forwarding to device port ${targetPort}`);
221
+ logger.info(() => `udp server listening on port ${localPort} forwarding to device port ${targetPort}`);
221
222
  });
222
223
 
223
224
  server.on('message', async (data, rinfo) => {
@@ -229,10 +230,10 @@ class BindPort {
229
230
  try {
230
231
  ref = await this.rpc.portOpen(deviceId, formattedTargetPort, 'rw');
231
232
  if (!ref) {
232
- logger.error(`Error opening port ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
233
+ logger.error(() => `Error opening port ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
233
234
  return;
234
235
  } else {
235
- logger.info(`Port ${formattedTargetPort} opened on device with ref: ${ref.toString('hex')} for udp client ${clientKey}`);
236
+ logger.info(() => `Port ${formattedTargetPort} opened on device with ref: ${ref.toString('hex')} for udp client ${clientKey}`);
236
237
  if (!server.clientRefs) server.clientRefs = {};
237
238
  server.clientRefs[clientKey] = ref;
238
239
 
@@ -247,7 +248,7 @@ class BindPort {
247
248
  });
248
249
  }
249
250
  } catch (error) {
250
- logger.error(`Error opening port ${formattedTargetPort} on device: ${error}`);
251
+ logger.error(() => `Error opening port ${formattedTargetPort} on device: ${error}`);
251
252
  return;
252
253
  }
253
254
  }
@@ -256,12 +257,12 @@ class BindPort {
256
257
  try {
257
258
  await this.rpc.portSend(ref, data);
258
259
  } catch (error) {
259
- logger.error(`Error sending udp data to device: ${error}`);
260
+ logger.error(() => `Error sending udp data to device: ${error}`);
260
261
  }
261
262
  });
262
263
 
263
264
  server.on('error', (err) => {
264
- logger.error(`udp Server error: ${err}`);
265
+ logger.error(() => `udp Server error: ${err}`);
265
266
  });
266
267
 
267
268
  server.bind(localPort);
@@ -269,21 +270,22 @@ class BindPort {
269
270
  } else {
270
271
  // For TCP and tls protocols, use TCP server locally
271
272
  const server = net.createServer(async (clientSocket) => {
272
- logger.info(`Client connected to local server on port ${localPort}`);
273
+ logger.info(() => `Client connected to local server on port ${localPort}`);
274
+ clientSocket.setNoDelay(true);
273
275
 
274
276
  // Open a new port on the device for this client
275
277
  let ref;
276
278
  try {
277
279
  ref = await this.rpc.portOpen(deviceId, formattedTargetPort, 'rw');
278
280
  if (!ref) {
279
- logger.error(`Error opening port ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
281
+ logger.error(() => `Error opening port ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
280
282
  clientSocket.destroy();
281
283
  return;
282
284
  } else {
283
- logger.info(`Port ${formattedTargetPort} opened on device with ref: ${ref.toString('hex')} for client`);
285
+ logger.info(() => `Port ${formattedTargetPort} opened on device with ref: ${ref.toString('hex')} for client`);
284
286
  }
285
287
  } catch (error) {
286
- logger.error(`Error opening port ${formattedTargetPort} on device: ${error}`);
288
+ logger.error(() => `Error opening port ${formattedTargetPort} on device: ${error}`);
287
289
  clientSocket.destroy();
288
290
  return;
289
291
  }
@@ -316,15 +318,16 @@ class BindPort {
316
318
  socket: diodeSocket,
317
319
  ...tlsOptions
318
320
  }, () => {
319
- logger.info(`tls connection established to device ${deviceIdHex}`);
321
+ logger.info(() => `tls connection established to device ${deviceIdHex}`);
320
322
  });
323
+ tlsSocket.setNoDelay(true);
321
324
 
322
325
  // Pipe data between the client socket and the tls socket
323
326
  tlsSocket.pipe(clientSocket).pipe(tlsSocket);
324
327
 
325
328
  // Handle tls socket errors
326
329
  tlsSocket.on('error', (err) => {
327
- logger.error(`tls Socket error: ${err}`);
330
+ logger.error(() => `tls Socket error: ${err}`);
328
331
  clientSocket.destroy();
329
332
  });
330
333
 
@@ -342,7 +345,7 @@ class BindPort {
342
345
  this.connection.addClientSocket(ref, socketWrapper);
343
346
 
344
347
  } catch (error) {
345
- logger.error(`Error setting up tls connection: ${error}`);
348
+ logger.error(() => `Error setting up tls connection: ${error}`);
346
349
  clientSocket.destroy();
347
350
  return;
348
351
  }
@@ -355,7 +358,7 @@ class BindPort {
355
358
  try {
356
359
  await this.rpc.portSend(ref, data);
357
360
  } catch (error) {
358
- logger.error(`Error sending data to device: ${error}`);
361
+ logger.error(() => `Error sending data to device: ${error}`);
359
362
  clientSocket.destroy();
360
363
  }
361
364
  });
@@ -363,28 +366,28 @@ class BindPort {
363
366
 
364
367
  // Handle client socket closure (common for all protocols)
365
368
  clientSocket.on('end', async () => {
366
- logger.info('Client disconnected');
369
+ logger.info(() => 'Client disconnected');
367
370
  if (ref && this.connection.hasClientSocket(ref)) {
368
371
  try {
369
372
  await this.rpc.portClose(ref);
370
- logger.info(`Port closed on device for ref: ${ref.toString('hex')}`);
373
+ logger.info(() => `Port closed on device for ref: ${ref.toString('hex')}`);
371
374
  this.connection.deleteClientSocket(ref);
372
375
  } catch (error) {
373
- logger.error(`Error closing port on device: ${error}`);
376
+ logger.error(() => `Error closing port on device: ${error}`);
374
377
  }
375
378
  } else {
376
- logger.warn('Ref is invalid or no longer in clientSockets.');
379
+ logger.warn(() => 'Ref is invalid or no longer in clientSockets.');
377
380
  }
378
381
  });
379
382
 
380
383
  // Handle client socket errors
381
384
  clientSocket.on('error', (err) => {
382
- logger.error(`Client socket error: ${err}`);
385
+ logger.error(() => `Client socket error: ${err}`);
383
386
  });
384
387
  });
385
388
 
386
389
  server.listen(localPort, () => {
387
- logger.info(`Local server listening on port ${localPort} forwarding to device ${protocol} port ${targetPort}`);
390
+ logger.info(() => `Local server listening on port ${localPort} forwarding to device ${protocol} port ${targetPort}`);
388
391
  });
389
392
 
390
393
  this.servers.set(parseInt(localPort), server);
@@ -404,4 +407,4 @@ class BindPort {
404
407
  }
405
408
  }
406
409
 
407
- module.exports = BindPort;
410
+ module.exports = BindPort;
package/connection.js CHANGED
@@ -16,6 +16,15 @@ const path = require('path');
16
16
  // Add dotenv for environment variables
17
17
  require('dotenv').config();
18
18
 
19
+ // Try to use native keccak if available (optional perf boost)
20
+ let nativeKeccak = null;
21
+ try {
22
+ // eslint-disable-next-line import/no-extraneous-dependencies
23
+ nativeKeccak = require('keccak');
24
+ } catch (_) {
25
+ // optional dependency; fallback to ethereumjs-util.keccak256
26
+ }
27
+
19
28
  class DiodeConnection extends EventEmitter {
20
29
  constructor(host, port, keyLocation = './db/keys.json') {
21
30
  super();
@@ -37,6 +46,7 @@ class DiodeConnection extends EventEmitter {
37
46
  this.clientSockets = new Map(); // For BindPort
38
47
  this.connections = new Map(); // For PublishPort
39
48
  this.certPem = null;
49
+ this._serverEthAddress = null; // cache after first read
40
50
  // Load or generate keypair
41
51
  this.keyPair = loadOrGenerateKeyPair(this.keyLocation);
42
52
 
@@ -59,7 +69,7 @@ class DiodeConnection extends EventEmitter {
59
69
  this.retryTimeoutId = null;
60
70
 
61
71
  // Log the reconnection settings
62
- logger.info(`Connection settings - Auto Reconnect: ${this.autoReconnect}, Max Retries: ${
72
+ logger.info(() => `Connection settings - Auto Reconnect: ${this.autoReconnect}, Max Retries: ${
63
73
  this.maxRetries === Infinity ? 'Infinity' : this.maxRetries
64
74
  }, Retry Delay: ${this.retryDelay}ms, Max Retry Delay: ${this.maxRetryDelay}ms`);
65
75
 
@@ -71,7 +81,7 @@ class DiodeConnection extends EventEmitter {
71
81
  this.ticketUpdateTimer = null;
72
82
 
73
83
  // Log the ticket batching settings
74
- logger.info(`Ticket batching settings - Bytes Threshold: ${this.ticketUpdateThreshold} bytes, Update Interval: ${this.ticketUpdateInterval}ms`);
84
+ logger.info(() => `Ticket batching settings - Bytes Threshold: ${this.ticketUpdateThreshold} bytes, Update Interval: ${this.ticketUpdateInterval}ms`);
75
85
  }
76
86
 
77
87
  connect() {
@@ -96,54 +106,59 @@ class DiodeConnection extends EventEmitter {
96
106
  };
97
107
 
98
108
  this.socket = tls.connect(this.port, this.host, options, async () => {
99
- logger.info('Connected to Diode.io server');
109
+ logger.info(() => 'Connected to Diode.io server');
100
110
  // Reset retry counter on successful connection
101
111
  this.retryCount = 0;
102
112
  // Set keep-alive to prevent connection timeout forever
103
113
  this.socket.setKeepAlive(true, 1500);
104
-
114
+ this.socket.setNoDelay(true);
115
+ // Cache server address after handshake
116
+ try {
117
+ this._serverEthAddress = this.getServerEthereumAddress();
118
+ } catch (e) {
119
+ logger.warn(() => `Failed caching server address: ${e}`);
120
+ }
121
+ // Start periodic ticket updates now that we are fully connected
122
+ this._startTicketUpdateTimer();
105
123
  // Send the ticketv2 command
106
124
  try {
107
125
  const ticketCommand = await this.createTicketCommand();
108
126
  const response = await this.sendCommand(ticketCommand).catch(reject);
109
127
  resolve();
110
128
  } catch (error) {
111
- logger.error(`Error sending ticket: ${error}`);
129
+ logger.error(() => `Error sending ticket: ${error}`);
112
130
  reject(error);
113
131
  }
114
132
  });
115
133
 
116
134
  this.socket.on('data', (data) => {
117
- // logger.debug(`Received data: ${data.toString('hex')}`);
135
+ // logger.debug(() => `Received data: ${data.toString('hex')}`);
118
136
  try {
119
137
  this._handleData(data);
120
138
  } catch (error) {
121
- logger.error(`Error handling data: ${error}`);
139
+ logger.error(() => `Error handling data: ${error}`);
122
140
  }
123
141
  });
124
142
 
125
- // Start the periodic ticket update timer after successful connection
126
- this.socket.on('connect', () => {
127
- this._startTicketUpdateTimer();
128
- });
143
+ // No-op: rely on secure handshake callback above for timers/caching
129
144
 
130
145
  this.socket.on('error', (err) => {
131
- logger.error(`Connection error: ${err}`);
146
+ logger.error(() => `Connection error: ${err}`);
132
147
  reject(err);
133
148
  });
134
149
 
135
150
  this.socket.on('end', () => {
136
- logger.info('Disconnected from server');
151
+ logger.info(() => 'Disconnected from server');
137
152
  this._handleDisconnect();
138
153
  });
139
154
 
140
155
  this.socket.on('close', (hadError) => {
141
- logger.info(`Connection closed${hadError ? ' due to error' : ''}`);
156
+ logger.info(() => `Connection closed${hadError ? ' due to error' : ''}`);
142
157
  this._handleDisconnect();
143
158
  });
144
159
 
145
160
  this.socket.on('timeout', () => {
146
- logger.warn('Connection timeout');
161
+ logger.warn(() => 'Connection timeout');
147
162
  this._handleDisconnect();
148
163
  });
149
164
  });
@@ -156,7 +171,7 @@ class DiodeConnection extends EventEmitter {
156
171
  this.retryCount++;
157
172
 
158
173
  if (this.maxRetries !== Infinity && this.retryCount > this.maxRetries) {
159
- logger.error(`Maximum reconnection attempts (${this.maxRetries}) reached. Giving up.`);
174
+ logger.error(() => `Maximum reconnection attempts (${this.maxRetries}) reached. Giving up.`);
160
175
  this.emit('reconnect_failed');
161
176
  return;
162
177
  }
@@ -164,7 +179,7 @@ class DiodeConnection extends EventEmitter {
164
179
  // Calculate delay with exponential backoff
165
180
  const delay = Math.min(this.retryDelay * Math.pow(1.5, this.retryCount - 1), this.maxRetryDelay);
166
181
 
167
- logger.info(`Reconnecting in ${delay}ms... (Attempt ${this.retryCount})`);
182
+ logger.info(() => `Reconnecting in ${delay}ms... (Attempt ${this.retryCount})`);
168
183
  this.emit('reconnecting', { attempt: this.retryCount, delay });
169
184
 
170
185
  this.retryTimeoutId = setTimeout(() => {
@@ -184,11 +199,11 @@ class DiodeConnection extends EventEmitter {
184
199
  .then(() => {
185
200
  this.isReconnecting = false;
186
201
  this.emit('reconnected');
187
- logger.info('Successfully reconnected to Diode.io server');
202
+ logger.info(() => 'Successfully reconnected to Diode.io server');
188
203
  })
189
204
  .catch((err) => {
190
205
  this.isReconnecting = false;
191
- logger.error(`Reconnection attempt failed: ${err}`);
206
+ logger.error(() => `Reconnection attempt failed: ${err}`);
192
207
  });
193
208
  }, delay);
194
209
  }
@@ -274,7 +289,7 @@ class DiodeConnection extends EventEmitter {
274
289
  _handleData(data) {
275
290
  // Append new data to the receive buffer
276
291
  this.receiveBuffer = Buffer.concat([this.receiveBuffer, data]);
277
- // logger.debug(`Received data: ${data.toString('hex')}`);
292
+ // logger.debug(() => `Received data: ${data.toString('hex')}`);
278
293
 
279
294
  let offset = 0;
280
295
  while (offset + 2 <= this.receiveBuffer.length) {
@@ -291,8 +306,9 @@ class DiodeConnection extends EventEmitter {
291
306
  offset += 2 + length;
292
307
 
293
308
  try {
294
- const decodedMessage = RLP.decode(Uint8Array.from(messageBuffer));
295
- // logger.debug(`Decoded message: ${makeReadable(decodedMessage)}`);
309
+ // Avoid copying: pass Buffer directly to RLP.decode
310
+ const decodedMessage = RLP.decode(messageBuffer);
311
+ // logger.debug(() => `Decoded message: ${makeReadable(decodedMessage)}`);
296
312
 
297
313
  if (Array.isArray(decodedMessage) && decodedMessage.length > 1) {
298
314
  const requestIdRaw = decodedMessage[0];
@@ -302,8 +318,8 @@ class DiodeConnection extends EventEmitter {
302
318
  const requestId = parseRequestId(requestIdRaw);
303
319
 
304
320
  // Debug statements
305
- logger.debug(`requestIdRaw: ${requestIdRaw}`);
306
- logger.debug(`Parsed requestId: ${requestId}`);
321
+ logger.debug(() => `requestIdRaw: ${requestIdRaw}`);
322
+ logger.debug(() => `Parsed requestId: ${requestId}`);
307
323
 
308
324
  if (requestId !== null && this.pendingRequests.has(requestId)) {
309
325
  // This is a response to a pending request
@@ -311,14 +327,14 @@ class DiodeConnection extends EventEmitter {
311
327
  const responseRaw = responseData[0];
312
328
 
313
329
  // Debug statements
314
- logger.debug(`responseTypeRaw: ${responseTypeRaw}`);
315
- logger.debug(`Type of responseTypeRaw: ${typeof responseTypeRaw}`);
330
+ logger.debug(() => `responseTypeRaw: ${responseTypeRaw}`);
331
+ logger.debug(() => `Type of responseTypeRaw: ${typeof responseTypeRaw}`);
316
332
 
317
333
  // Parse responseType
318
334
  const responseType = parseResponseType(responseTypeRaw);
319
335
 
320
- logger.debug(`Received response for requestId: ${requestId}`);
321
- logger.debug(`Response Type: '${responseType}'`);
336
+ logger.debug(() => `Received response for requestId: ${requestId}`);
337
+ logger.debug(() => `Response Type: '${responseType}'`);
322
338
 
323
339
  const { resolve, reject } = this.pendingRequests.get(requestId);
324
340
  try{
@@ -344,20 +360,20 @@ class DiodeConnection extends EventEmitter {
344
360
  resolve(responseData);
345
361
  }
346
362
  } catch (error) {
347
- logger.error(`Error handling response: ${error}`);
363
+ logger.error(() => `Error handling response: ${error}`);
348
364
  }
349
365
  this.pendingRequests.delete(requestId);
350
366
  } else {
351
367
  // This is an unsolicited message
352
- logger.debug(`Received unsolicited message`);
368
+ logger.debug(() => `Received unsolicited message`);
353
369
  this.emit('unsolicited', decodedMessage);
354
370
  }
355
371
  } else {
356
372
  // Invalid message format
357
- logger.error(`Invalid message format: ${makeReadable(decodedMessage)}`);
373
+ logger.error(() => `Invalid message format: ${makeReadable(decodedMessage)}`);
358
374
  }
359
375
  } catch (error) {
360
- logger.error(`Error decoding message: ${error}`);
376
+ logger.error(() => `Error decoding message: ${error}`);
361
377
  }
362
378
  }
363
379
 
@@ -392,21 +408,21 @@ class DiodeConnection extends EventEmitter {
392
408
  const requestId = this._getNextRequestId();
393
409
  // Build the message as [requestId, [commandArray]]
394
410
  const commandWithId = [requestId, commandArray];
395
-
411
+
396
412
  // Store the promise callbacks to resolve/reject later
397
413
  this.pendingRequests.set(requestId, { resolve, reject });
398
-
414
+
399
415
  const commandBuffer = RLP.encode(commandWithId);
400
- const byteLength = Buffer.byteLength(commandBuffer);
401
-
416
+ const byteLength = commandBuffer.length; // Buffer/Uint8Array length is bytes
417
+
402
418
  // Create a 2-byte length buffer
403
419
  const lengthBuffer = Buffer.alloc(2);
404
420
  lengthBuffer.writeUInt16BE(byteLength, 0);
405
-
421
+
406
422
  const message = Buffer.concat([lengthBuffer, commandBuffer]);
407
423
 
408
- logger.debug(`Sending command with requestId ${requestId}: ${commandArray}`);
409
- // logger.debug(`Command buffer: ${message.toString('hex')}`);
424
+ logger.debug(() => `Sending command with requestId ${requestId}: ${commandArray}`);
425
+ // logger.debug(() => `Command buffer: ${message.toString('hex')}`);
410
426
 
411
427
  this.socket.write(message);
412
428
  }).catch(reject);
@@ -419,23 +435,24 @@ class DiodeConnection extends EventEmitter {
419
435
  const requestId = sessionId;
420
436
  // Build the message as [requestId, [commandArray]]
421
437
  const commandWithId = [requestId, commandArray];
422
-
438
+
423
439
  // Store the promise callbacks to resolve/reject later
424
440
  this.pendingRequests.set(requestId, { resolve, reject });
425
-
441
+
426
442
  const commandBuffer = RLP.encode(commandWithId);
427
- const byteLength = Buffer.byteLength(commandBuffer);
428
-
443
+ const byteLength = commandBuffer.length; // Buffer/Uint8Array length is bytes
444
+
429
445
  // Create a 2-byte length buffer
430
446
  const lengthBuffer = Buffer.alloc(2);
431
447
  lengthBuffer.writeUInt16BE(byteLength, 0);
432
-
448
+
433
449
  const message = Buffer.concat([lengthBuffer, commandBuffer]);
434
450
 
435
- logger.debug(`Sending command with requestId ${requestId}: ${commandArray}`);
436
- // logger.debug(`Command buffer: ${message.toString('hex')}`);
451
+ logger.debug(() => `Sending command with requestId ${requestId}: ${commandArray}`);
452
+ // logger.debug(() => `Command buffer: ${message.toString('hex')}`);
437
453
 
438
454
  this.socket.write(message);
455
+ resolve();
439
456
  }).catch(reject);
440
457
  });
441
458
  }
@@ -452,13 +469,16 @@ class DiodeConnection extends EventEmitter {
452
469
 
453
470
  return address;
454
471
  } catch (error) {
455
- logger.error(`Error extracting Ethereum address: ${error}`);
472
+ logger.error(() => `Error extracting Ethereum address: ${error}`);
456
473
  throw error;
457
474
  }
458
475
  }
459
476
 
460
477
  getServerEthereumAddress() {
461
478
  try {
479
+ if (this._serverEthAddress) {
480
+ return this._serverEthAddress;
481
+ }
462
482
  const serverCert = this.socket.getPeerCertificate(true);
463
483
  if (!serverCert.raw) {
464
484
  throw new Error('Failed to get server certificate.');
@@ -468,14 +488,14 @@ class DiodeConnection extends EventEmitter {
468
488
  ? serverCert.pubkey
469
489
  : Buffer.from(serverCert.pubkey);
470
490
 
471
- logger.debug(`Public key Server: ${publicKeyBuffer.toString('hex')}`);
491
+ logger.debug(() => `Public key Server: ${publicKeyBuffer.toString('hex')}`);
472
492
 
473
493
  const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
474
494
  const address = '0x' + addressBuffer.toString('hex');
475
-
476
- return address;
495
+ this._serverEthAddress = address;
496
+ return this._serverEthAddress;
477
497
  } catch (error) {
478
- logger.error(`Error extracting server Ethereum address: ${error}`);
498
+ logger.error(() => `Error extracting server Ethereum address: ${error}`);
479
499
  throw error;
480
500
  }
481
501
  }
@@ -488,13 +508,12 @@ class DiodeConnection extends EventEmitter {
488
508
  const privateKeyBytes = Buffer.from(privateKeyHex, 'hex');
489
509
  return privateKeyBytes;
490
510
  } catch (error) {
491
- logger.error(`Error extracting private key: ${error}`);
511
+ logger.error(() => `Error extracting private key: ${error}`);
492
512
  throw error;
493
513
  }
494
514
  }
495
515
 
496
516
  async createTicketSignature(serverIdBuffer, totalConnections, totalBytes, localAddress, epoch) {
497
- this.getEthereumAddress()
498
517
  const chainId = 1284;
499
518
  const fleetContractBuffer = ethUtil.toBuffer('0x6000000000000000000000000000000000000000'); // 20-byte Buffer
500
519
 
@@ -512,23 +531,25 @@ class DiodeConnection extends EventEmitter {
512
531
  ethUtil.setLengthLeft(localAddressHash, 32),
513
532
  ];
514
533
 
515
- // Convert each element in dataToSign to bytes32 and concatenate them
516
- const encodedData = Buffer.concat(dataToSign.map(item => abi.rawEncode(['bytes32'], [item])));
534
+ // Elements are already bytes32; concatenate directly to avoid ABI overhead
535
+ const encodedData = Buffer.concat(dataToSign);
517
536
 
518
- logger.debug(`Encoded data: ${encodedData.toString('hex')}`);
537
+ logger.debug(() => `Encoded data: ${encodedData.toString('hex')}`);
519
538
 
520
- logger.debug(`Data to sign: ${makeReadable(dataToSign)}`);
539
+ logger.debug(() => `Data to sign: ${makeReadable(dataToSign)}`);
521
540
 
522
541
 
523
542
  // Sign the data
524
543
  const privateKey = this.getPrivateKey();
525
- const msgHash = ethUtil.keccak256(encodedData);
526
- logger.debug(`Message hash: ${msgHash.toString('hex')}`);
544
+ const msgHash = nativeKeccak
545
+ ? nativeKeccak('keccak256').update(encodedData).digest()
546
+ : ethUtil.keccak256(encodedData);
547
+ logger.debug(() => `Message hash: ${msgHash.toString('hex')}`);
527
548
  const signature = secp256k1.ecdsaSign(msgHash, privateKey);
528
- logger.debug(`Signature: ${signature.signature.toString('hex')}`);
549
+ logger.debug(() => `Signature: ${signature.signature.toString('hex')}`);
529
550
 
530
551
  const signatureBuffer = Buffer.concat([
531
- ethUtil.toBuffer([signature.recid]),
552
+ Buffer.from([signature.recid]),
532
553
  signature.signature
533
554
  ]);
534
555
 
@@ -559,7 +580,7 @@ class DiodeConnection extends EventEmitter {
559
580
  localAddress,
560
581
  epoch
561
582
  );
562
- logger.debug(`Signature hex: ${signature.toString('hex')}`);
583
+ logger.debug(() => `Signature hex: ${signature.toString('hex')}`);
563
584
 
564
585
 
565
586
  // Construct the ticket command
@@ -650,7 +671,7 @@ class DiodeConnection extends EventEmitter {
650
671
 
651
672
  try {
652
673
  if (this.accumulatedBytes > 0 || force) {
653
- logger.debug(`Updating ticket: accumulated ${this.accumulatedBytes} bytes, ${timeSinceLastUpdate}ms since last update`);
674
+ logger.debug(() => `Updating ticket: accumulated ${this.accumulatedBytes} bytes, ${timeSinceLastUpdate}ms since last update`);
654
675
  const ticketCommand = await this.createTicketCommand();
655
676
  await this.sendCommand(ticketCommand);
656
677
 
@@ -659,7 +680,7 @@ class DiodeConnection extends EventEmitter {
659
680
  this.lastTicketUpdate = Date.now();
660
681
  }
661
682
  } catch (error) {
662
- logger.error(`Error updating ticket: ${error}`);
683
+ logger.error(() => `Error updating ticket: ${error}`);
663
684
  }
664
685
  }
665
686
 
@@ -687,7 +708,7 @@ class DiodeConnection extends EventEmitter {
687
708
  this.ticketUpdateInterval = options.interval;
688
709
  }
689
710
 
690
- logger.info(`Updated ticket batching settings - Bytes Threshold: ${this.ticketUpdateThreshold} bytes, Update Interval: ${this.ticketUpdateInterval}ms`);
711
+ logger.info(() => `Updated ticket batching settings - Bytes Threshold: ${this.ticketUpdateThreshold} bytes, Update Interval: ${this.ticketUpdateInterval}ms`);
691
712
 
692
713
  // Reset the timer with new interval
693
714
  if (this.socket && !this.socket.destroyed) {