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/index.js CHANGED
@@ -5,4 +5,10 @@ const BindPort = require('./bindPort');
5
5
  const PublishPort = require('./publishPort');
6
6
  const makeReadable = require('./utils').makeReadable;
7
7
  const logger = require('./logger');
8
+ process.on('unhandledRejection', (reason) => {
9
+ try { logger.warn(() => `Unhandled promise rejection: ${reason}`); } catch {}
10
+ });
11
+ process.on('uncaughtException', (err) => {
12
+ try { logger.error(() => `Uncaught exception: ${err.stack || err.message}`); } catch {}
13
+ });
8
14
  module.exports = { DiodeConnection, DiodeRPC, BindPort , PublishPort, makeReadable, logger };
package/logger.js CHANGED
@@ -19,10 +19,15 @@ const options = {
19
19
 
20
20
  const logger = setupLogger(options);
21
21
 
22
+ // Evaluate function args lazily to avoid building strings if logs are disabled
23
+ const evalArg = (a) => (typeof a === 'function' ? a() : a);
24
+ const mapArgs = (args) => args.map(evalArg);
25
+ const shouldDebug = isDebug && isLogEnabled;
26
+
22
27
  // Wrap logger calls to respect debug mode
23
28
  module.exports = {
24
- debug: (...args) => { if (isDebug && isLogEnabled) logger.debug(...args, 'app'); },
25
- info: (...args) => { if (isLogEnabled) logger.info(...args, 'app'); },
26
- warn: (...args) => { if (isLogEnabled) logger.warn(...args, 'app'); },
27
- error: (...args) => { if (isLogEnabled) logger.error(...args, 'app'); },
28
- };
29
+ debug: (...args) => { if (shouldDebug) logger.debug(...mapArgs(args), 'app'); },
30
+ info: (...args) => { if (isLogEnabled) logger.info(...mapArgs(args), 'app'); },
31
+ warn: (...args) => { if (isLogEnabled) logger.warn(...mapArgs(args), 'app'); },
32
+ error: (...args) => { if (isLogEnabled) logger.error(...mapArgs(args), 'app'); },
33
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diodejs",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
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": {
@@ -11,20 +11,18 @@
11
11
  "dependencies": {
12
12
  "@ethereumjs/rlp": "^5.0.2",
13
13
  "asn1.js": "^5.4.1",
14
- "buffer": "^6.0.3",
15
- "crypto": "^1.0.1",
16
14
  "dera-logger": "^2.0.0",
17
- "dgram": "^1.0.1",
18
15
  "dotenv": "^16.4.7",
19
16
  "ethereumjs-abi": "^0.6.8",
20
17
  "ethereumjs-util": "^7.1.5",
21
18
  "ethers": "^6.13.2",
22
- "fs": "^0.0.1-security",
23
19
  "jsrsasign": "^11.1.0",
24
- "net": "^1.0.2",
20
+ "keccak": "^3.0.4",
25
21
  "node-fetch": "^2.7.0",
26
22
  "rlp": "^3.0.0",
27
- "secp256k1": "^5.0.1",
28
- "tls": "^0.0.1"
23
+ "secp256k1": "^5.0.1"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
29
27
  }
30
28
  }
package/publishPort.js CHANGED
@@ -8,12 +8,12 @@ const { Buffer } = require('buffer');
8
8
  const EventEmitter = require('events');
9
9
  const { Duplex } = require('stream');
10
10
  const DiodeRPC = require('./rpc');
11
- const { makeReadable } = require('./utils');
11
+ const { makeReadable, toBufferView } = require('./utils');
12
12
  const logger = require('./logger');
13
13
 
14
14
  class DiodeSocket extends Duplex {
15
15
  constructor(ref, rpc) {
16
- super();
16
+ super({ readableHighWaterMark: 256 * 1024, writableHighWaterMark: 256 * 1024, allowHalfOpen: false });
17
17
  this.ref = ref;
18
18
  this.rpc = rpc;
19
19
  }
@@ -40,6 +40,7 @@ class PublishPort extends EventEmitter {
40
40
  super();
41
41
  this.connection = connection;
42
42
  this.rpc = new DiodeRPC(connection);
43
+ this._listening = false; // ensure startListening is idempotent
43
44
 
44
45
  // Convert publishedPorts to a Map with configurations
45
46
  this.publishedPorts = new Map();
@@ -51,9 +52,9 @@ class PublishPort extends EventEmitter {
51
52
 
52
53
  this.startListening();
53
54
  if (this.publishedPorts.size > 0) {
54
- logger.info(`Publishing ports: ${Array.from(this.publishedPorts.keys())}`);
55
+ logger.info(() => `Publishing ports: ${Array.from(this.publishedPorts.keys())}`);
55
56
  } else {
56
- logger.info("No ports published initially");
57
+ logger.info(() => "No ports published initially");
57
58
  }
58
59
  }
59
60
 
@@ -69,7 +70,7 @@ class PublishPort extends EventEmitter {
69
70
 
70
71
  // Add to map
71
72
  this.publishedPorts.set(portNum, portConfig);
72
- logger.info(`Added published port ${portNum} with mode: ${portConfig.mode}`);
73
+ logger.info(() => `Added published port ${portNum} with mode: ${portConfig.mode}`);
73
74
 
74
75
  return true;
75
76
  }
@@ -79,7 +80,7 @@ class PublishPort extends EventEmitter {
79
80
  const portNum = parseInt(port, 10);
80
81
 
81
82
  if (!this.publishedPorts.has(portNum)) {
82
- logger.warn(`Port ${portNum} is not published`);
83
+ logger.warn(() => `Port ${portNum} is not published`);
83
84
  return false;
84
85
  }
85
86
 
@@ -90,12 +91,12 @@ class PublishPort extends EventEmitter {
90
91
  .filter(conn => conn.port === portNum);
91
92
 
92
93
  if (activeConnections.length > 0) {
93
- logger.warn(`Removing port ${portNum} with ${activeConnections.length} active connections`);
94
+ logger.warn(() => `Removing port ${portNum} with ${activeConnections.length} active connections`);
94
95
  // We could close these connections, but they'll be rejected naturally on next data transfer
95
96
  }
96
97
 
97
98
  this.publishedPorts.delete(portNum);
98
- logger.info(`Removed published port ${portNum}`);
99
+ logger.info(() => `Removed published port ${portNum}`);
99
100
 
100
101
  return true;
101
102
  }
@@ -126,29 +127,41 @@ class PublishPort extends EventEmitter {
126
127
  clearPorts() {
127
128
  const portCount = this.publishedPorts.size;
128
129
  this.publishedPorts.clear();
129
- logger.info(`Cleared ${portCount} published ports`);
130
+ logger.info(() => `Cleared ${portCount} published ports`);
130
131
  return portCount;
131
132
  }
132
133
 
133
134
  startListening() {
135
+ if (this._listening) return this; // idempotent
134
136
  // Listen for unsolicited messages from the connection
135
- this.connection.on('unsolicited', (message) => {
137
+ this._onUnsolicited = (message) => {
136
138
  const [sessionIdRaw, messageContent] = message;
137
139
  const messageTypeRaw = messageContent[0];
138
- const messageType = Buffer.from(messageTypeRaw).toString('utf8');
140
+ const messageType = toBufferView(messageTypeRaw).toString('utf8');
139
141
 
140
142
  if (messageType === 'portopen') {
141
143
  this.handlePortOpen(sessionIdRaw, messageContent);
142
- } else if (messageType === 'portsend') {
144
+ } else if (messageType === 'portsend' || messageType === 'data') {
145
+ // Accept both synonyms for payload delivery
143
146
  this.handlePortSend(sessionIdRaw, messageContent);
144
147
  } else if (messageType === 'portclose') {
145
148
  this.handlePortClose(sessionIdRaw, messageContent);
146
149
  } else {
147
- if (messageType != 'data') {
148
- logger.warn(`Unknown unsolicited message type: ${messageType}`);
149
- }
150
+ logger.warn(() => `Unknown unsolicited message type: ${messageType}`);
150
151
  }
151
- });
152
+ };
153
+ this.connection.on('unsolicited', this._onUnsolicited);
154
+ this._listening = true;
155
+ return this;
156
+ }
157
+
158
+ stopListening() {
159
+ if (!this._listening) return this;
160
+ if (this._onUnsolicited) {
161
+ this.connection.off('unsolicited', this._onUnsolicited);
162
+ }
163
+ this._listening = false;
164
+ return this;
152
165
  }
153
166
 
154
167
  handlePortOpen(sessionIdRaw, messageContent) {
@@ -157,12 +170,12 @@ class PublishPort extends EventEmitter {
157
170
  const refRaw = messageContent[2];
158
171
  const deviceIdRaw = messageContent[3];
159
172
 
160
- const sessionId = Buffer.from(sessionIdRaw);
173
+ const sessionId = toBufferView(sessionIdRaw);
161
174
  const portString = makeReadable(portStringRaw);
162
- const ref = Buffer.from(refRaw);
163
- const deviceId = `0x${Buffer.from(deviceIdRaw).toString('hex')}`;
175
+ const ref = toBufferView(refRaw);
176
+ const deviceId = `0x${toBufferView(deviceIdRaw).toString('hex')}`;
164
177
 
165
- logger.info(`Received portopen request for portString ${portString} with ref ${ref.toString('hex')} from device ${deviceId}`);
178
+ logger.info(() => `Received portopen request for portString ${portString} with ref ${ref.toString('hex')} from device ${deviceId}`);
166
179
 
167
180
  // Extract protocol and port number from portString
168
181
  var protocol = 'tcp';
@@ -180,7 +193,7 @@ class PublishPort extends EventEmitter {
180
193
 
181
194
  // Check if the port is published
182
195
  if (!this.publishedPorts.has(port)) {
183
- logger.warn(`Port ${port} is not published. Rejecting request.`);
196
+ logger.warn(() => `Port ${port} is not published. Rejecting request.`);
184
197
  // Send error response
185
198
  this.rpc.sendError(sessionId, ref, 'Port is not published');
186
199
  return;
@@ -190,11 +203,11 @@ class PublishPort extends EventEmitter {
190
203
  const portConfig = this.publishedPorts.get(port);
191
204
  if (portConfig.mode === 'private' && Array.isArray(portConfig.whitelist)) {
192
205
  if (!portConfig.whitelist.includes(deviceId)) {
193
- logger.warn(`Device ${deviceId} is not whitelisted for port ${port}. Rejecting request.`);
206
+ logger.warn(() => `Device ${deviceId} is not whitelisted for port ${port}. Rejecting request.`);
194
207
  this.rpc.sendError(sessionId, ref, 'Device not whitelisted');
195
208
  return;
196
209
  }
197
- logger.info(`Device ${deviceId} is whitelisted for port ${port}. Accepting request.`);
210
+ logger.info(() => `Device ${deviceId} is whitelisted for port ${port}. Accepting request.`);
198
211
  }
199
212
 
200
213
  // Handle based on protocol
@@ -205,7 +218,7 @@ class PublishPort extends EventEmitter {
205
218
  } else if (protocol === 'udp') {
206
219
  this.handleUDPConnection(sessionId, ref, port, deviceId);
207
220
  } else {
208
- logger.warn(`Unsupported protocol: ${protocol}`);
221
+ logger.warn(() => `Unsupported protocol: ${protocol}`);
209
222
  this.rpc.sendError(sessionId, ref, `Unsupported protocol: ${protocol}`);
210
223
  }
211
224
  }
@@ -220,14 +233,14 @@ class PublishPort extends EventEmitter {
220
233
  });
221
234
 
222
235
  localSocket.on('end', () => {
223
- logger.info(`Local service disconnected`);
236
+ logger.info(() => `Local service disconnected`);
224
237
  // Send portclose message to Diode
225
238
  this.rpc.portClose(ref);
226
239
  this.connection.deleteConnection(ref);
227
240
  });
228
241
 
229
242
  localSocket.on('error', (err) => {
230
- logger.error(`Error with local service: ${err}`);
243
+ logger.error(() => `Error with local service: ${err}`);
231
244
  // Send portclose message to Diode
232
245
  this.rpc.portClose(ref);
233
246
  this.connection.deleteConnection(ref);
@@ -238,7 +251,8 @@ class PublishPort extends EventEmitter {
238
251
  handleTCPConnection(sessionId, ref, port, deviceId) {
239
252
  // Create a TCP connection to the local service on the specified port
240
253
  const localSocket = net.connect({ port: port }, () => {
241
- logger.info(`Connected to local TCP service on port ${port}`);
254
+ localSocket.setNoDelay(true);
255
+ logger.info(() => `Connected to local TCP service on port ${port}`);
242
256
  // Send success response
243
257
  this.rpc.sendResponse(sessionId, ref, 'ok');
244
258
  });
@@ -272,10 +286,10 @@ class PublishPort extends EventEmitter {
272
286
  isServer: true,
273
287
  ...tlsOptions,
274
288
  });
275
-
289
+ tlsSocket.setNoDelay(true);
276
290
  // Connect to the local service (TCP or TLS as needed)
277
291
  const localSocket = net.connect({ port: port }, () => {
278
- logger.info(`Connected to local TCP service on port ${port}`);
292
+ logger.info(() => `Connected to local TCP service on port ${port}`);
279
293
  // Send success response
280
294
  this.rpc.sendResponse(sessionId, ref, 'ok');
281
295
  });
@@ -285,7 +299,7 @@ class PublishPort extends EventEmitter {
285
299
 
286
300
  // Handle errors and cleanup
287
301
  tlsSocket.on('error', (err) => {
288
- logger.error(`TLS Socket error: ${err}`);
302
+ logger.error(() => `TLS Socket error: ${err}`);
289
303
  this.rpc.portClose(ref);
290
304
  this.connection.deleteConnection(ref);
291
305
  });
@@ -309,6 +323,16 @@ class PublishPort extends EventEmitter {
309
323
  // Create a UDP socket
310
324
  const localSocket = dgram.createSocket('udp4');
311
325
 
326
+ // Try larger kernel buffers if available
327
+ localSocket.on('listening', () => {
328
+ try {
329
+ localSocket.setRecvBufferSize(1 << 20); // ~1MB
330
+ localSocket.setSendBufferSize(1 << 20);
331
+ } catch (e) {
332
+ logger.debug(() => `UDP buffer sizing not supported: ${e.message}`);
333
+ }
334
+ });
335
+
312
336
  // Store the remote address and port from the Diode client
313
337
  const remoteInfo = {port, address: '127.0.0.1'};
314
338
 
@@ -324,20 +348,15 @@ class PublishPort extends EventEmitter {
324
348
  deviceId
325
349
  });
326
350
 
327
- logger.info(`UDP connection set up on port ${port}`);
351
+ logger.info(() => `UDP connection set up on port ${port}`);
328
352
 
329
353
  // Handle messages from the local UDP service
330
354
  localSocket.on('message', (msg, rinfo) => {
331
- //need to add 4 bytes of data length to the beginning of the message but it's Big Endian
332
- const dataLength = Buffer.alloc(4);
333
- dataLength.writeUInt32LE(msg.length, 0);
334
- const data = Buffer.concat([dataLength, msg]);
335
- // Send the data back to the Diode client via portSend
336
- this.rpc.portSend(ref, data);
355
+ this.rpc.portSend(ref, msg);
337
356
  });
338
357
 
339
358
  localSocket.on('error', (err) => {
340
- logger.error(`UDP Socket error: ${err}`);
359
+ logger.error(() => `UDP Socket error: ${err}`);
341
360
  this.rpc.portClose(ref);
342
361
  this.connection.deleteConnection(ref);
343
362
  });
@@ -347,9 +366,9 @@ class PublishPort extends EventEmitter {
347
366
  const refRaw = messageContent[1];
348
367
  const dataRaw = messageContent[2];
349
368
 
350
- const sessionId = Buffer.from(sessionIdRaw);
351
- const ref = Buffer.from(refRaw);
352
- const data = Buffer.from(dataRaw)//.slice(4);
369
+ const sessionId = toBufferView(sessionIdRaw);
370
+ const ref = toBufferView(refRaw);
371
+ const data = toBufferView(dataRaw);//.slice(4);
353
372
 
354
373
  const connectionInfo = this.connection.getConnection(ref);
355
374
  // Check if the port is still open and address is still in whitelist
@@ -357,7 +376,7 @@ class PublishPort extends EventEmitter {
357
376
  const { socket: localSocket, protocol, remoteInfo, port, deviceId } = connectionInfo;
358
377
 
359
378
  if (!this.publishedPorts.has(port)) {
360
- logger.warn(`Port ${port} is not published. Sending portclose.`);
379
+ logger.warn(() => `Port ${port} is not published. Sending portclose.`);
361
380
  this.rpc.portClose(ref);
362
381
  this.connection.deleteConnection(ref);
363
382
  return;
@@ -366,7 +385,7 @@ class PublishPort extends EventEmitter {
366
385
  const portConfig = this.publishedPorts.get(port);
367
386
  if (portConfig.mode === 'private' && Array.isArray(portConfig.whitelist)) {
368
387
  if (!portConfig.whitelist.includes(deviceId)) {
369
- logger.warn(`Device ${deviceId} is not whitelisted for port ${port}. Sending portclose.`);
388
+ logger.warn(() => `Device ${deviceId} is not whitelisted for port ${port}. Sending portclose.`);
370
389
  this.rpc.portClose(ref);
371
390
  this.connection.deleteConnection(ref);
372
391
  return;
@@ -398,9 +417,9 @@ class PublishPort extends EventEmitter {
398
417
  } else {
399
418
  const clientSocket = this.connection.getClientSocket(ref);
400
419
  if (clientSocket) {
401
- logger.debug(`No local connection found for ref: ${ref.toString('hex')}, but client socket exists`);
420
+ logger.debug(() => `No local connection found for ref: ${ref.toString('hex')}, but client socket exists`);
402
421
  } else {
403
- logger.warn(`No local connection found for ref ${ref.toString('hex')}. Sending portclose.`);
422
+ logger.warn(() => `No local connection found for ref ${ref.toString('hex')}. Sending portclose.`);
404
423
  this.rpc.sendError(sessionId, ref, 'No local connection found');
405
424
  }
406
425
  }
@@ -408,10 +427,10 @@ class PublishPort extends EventEmitter {
408
427
 
409
428
  handlePortClose(sessionIdRaw, messageContent) {
410
429
  const refRaw = messageContent[1];
411
- const sessionId = Buffer.from(sessionIdRaw);
412
- const ref = Buffer.from(refRaw);
430
+ const sessionId = toBufferView(sessionIdRaw);
431
+ const ref = toBufferView(refRaw);
413
432
 
414
- logger.info(`Received portclose for ref ${ref.toString('hex')}`);
433
+ logger.info(() => `Received portclose for ref ${ref.toString('hex')}`);
415
434
 
416
435
  const connectionInfo = this.connection.getConnection(ref);
417
436
  if (connectionInfo) {
package/rpc.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //rpc.js
2
- const { makeReadable, parseRequestId, parseResponseType, parseReason } = require('./utils');
2
+ const { makeReadable, parseRequestId, parseResponseType, parseReason, toBufferView } = require('./utils');
3
3
  const logger = require('./logger');
4
4
 
5
5
  class DiodeRPC {
@@ -17,7 +17,8 @@ class DiodeRPC {
17
17
  const blockNumberRaw = responseData[0];
18
18
  let blockNumber;
19
19
  if (blockNumberRaw instanceof Uint8Array) {
20
- blockNumber = Buffer.from(blockNumberRaw).readUIntBE(0, blockNumberRaw.length);
20
+ const buf = toBufferView(blockNumberRaw);
21
+ blockNumber = buf.readUIntBE(0, buf.length);
21
22
  } else if (Buffer.isBuffer(blockNumberRaw)) {
22
23
  blockNumber = blockNumberRaw.readUIntBE(0, blockNumberRaw.length);
23
24
  } else if (typeof blockNumberRaw === 'number') {
@@ -25,10 +26,10 @@ class DiodeRPC {
25
26
  } else {
26
27
  throw new Error('Invalid block number format. response:', makeReadable(responseData));
27
28
  }
28
- logger.debug(`Block number is: ${blockNumber}`);
29
+ logger.debug(() => `Block number is: ${blockNumber}`);
29
30
  return blockNumber;
30
31
  }).catch((error) => {
31
- logger.error(`Error during get block peak: ${error}`);
32
+ logger.error(() => `Error during get block peak: ${error}`);
32
33
  return;
33
34
  });
34
35
  }
@@ -36,7 +37,7 @@ class DiodeRPC {
36
37
  return this.connection.sendCommand(['getblockheader', index]).then((responseData) => {
37
38
  return responseData[0]; // block_header
38
39
  }).catch((error) => {
39
- logger.error(`Error during get block header: ${error}`);
40
+ logger.error(() => `Error during get block header: ${error}`);
40
41
  return;
41
42
  });
42
43
  }
@@ -45,7 +46,7 @@ class DiodeRPC {
45
46
  return this.connection.sendCommand(['getblock', index]).then((responseData) => {
46
47
  return responseData[0]; // block
47
48
  }).catch((error) => {
48
- logger.error(`Error during get block: ${error}`);
49
+ logger.error(() => `Error during get block: ${error}`);
49
50
  return;
50
51
  });
51
52
  }
@@ -64,7 +65,7 @@ class DiodeRPC {
64
65
  throw new Error(`Unknown status in response: '${status}'`);
65
66
  }
66
67
  }).catch((error) => {
67
- logger.error(`Error during ping: ${error}`);
68
+ logger.error(() => `Error during ping: ${error}`);
68
69
  return false;
69
70
  })
70
71
  }
@@ -82,7 +83,7 @@ class DiodeRPC {
82
83
  if (status === 'ok') {
83
84
  let ref = refOrReasonRaw;
84
85
  if (Buffer.isBuffer(ref) || ref instanceof Uint8Array) {
85
- ref = Buffer.from(ref);
86
+ ref = toBufferView(ref);
86
87
  }
87
88
  return ref;
88
89
  } else if (status === 'error') {
@@ -92,7 +93,7 @@ class DiodeRPC {
92
93
  throw new Error(`Unknown status in response: '${status}'`);
93
94
  }
94
95
  }).catch((error) => {
95
- logger.error(`Error during port open: ${error}`);
96
+ logger.error(() => `Error during port open: ${error}`);
96
97
  return;
97
98
  });
98
99
  }
@@ -108,7 +109,7 @@ class DiodeRPC {
108
109
  try {
109
110
  // If data is too large, split it into chunks
110
111
  if (data.length > MAX_CHUNK_SIZE) {
111
- logger.debug(`Chunking large data of ${data.length} bytes into pieces of max ${MAX_CHUNK_SIZE} bytes`);
112
+ logger.debug(() => `Chunking large data of ${data.length} bytes into pieces of max ${MAX_CHUNK_SIZE} bytes`);
112
113
  let offset = 0;
113
114
 
114
115
  while (offset < data.length) {
@@ -144,7 +145,7 @@ class DiodeRPC {
144
145
  });
145
146
  }
146
147
  } catch (error) {
147
- logger.error(`Error during port send: ${error}`);
148
+ logger.error(() => `Error during port send: ${error}`);
148
149
  throw error; // Rethrow to allow proper error handling upstream
149
150
  }
150
151
  }
@@ -165,21 +166,21 @@ class DiodeRPC {
165
166
  throw new Error(`Unknown status in response: '${status}'`);
166
167
  }
167
168
  }).catch((error) => {
168
- logger.error(`Error during port close: ${error}`);
169
+ logger.error(() => `Error during port close: ${error}`);
169
170
  return;
170
171
  });
171
172
  }
172
173
 
173
174
  sendError(sessionId, ref, error) {
174
175
  return this.connection.sendCommandWithSessionId(['response', ref, 'error', error], sessionId).catch((error) => {
175
- logger.error(`Error during send error: ${error}`);
176
+ logger.error(() => `Error during send error: ${error}`);
176
177
  return;
177
178
  });
178
179
  }
179
180
 
180
181
  sendResponse(sessionId, ref, response) {
181
182
  return this.connection.sendCommandWithSessionId(['response', ref, response], sessionId).catch((error) => {
182
- logger.error(`Error during send response: ${error}`);
183
+ logger.error(() => `Error during send response: ${error}`);
183
184
  return;
184
185
  });
185
186
  }
@@ -187,10 +188,10 @@ class DiodeRPC {
187
188
  async getEpoch() {
188
189
  const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
189
190
  if (this.epochCache.expiry && this.epochCache.expiry > currentTime) {
190
- logger.debug(`Using cached epoch: ${this.epochCache.epoch}`);
191
+ logger.debug(() => `Using cached epoch: ${this.epochCache.epoch}`);
191
192
  return this.epochCache.epoch;
192
193
  }
193
- logger.debug(`Fetching new epoch. Expiry: ${this.epochCache.expiry}, Current time: ${currentTime}`);
194
+ logger.debug(() => `Fetching new epoch. Expiry: ${this.epochCache.expiry}, Current time: ${currentTime}`);
194
195
  const blockPeak = await this.getBlockPeak();
195
196
  const blockHeader = await this.getBlockHeader(blockPeak);
196
197
 
@@ -209,39 +210,34 @@ class DiodeRPC {
209
210
  return epoch;
210
211
  }
211
212
 
212
- parseTimestamp(blockHeader) {
213
- // Search for the timestamp field by name
213
+ parseTimestamp(blockHeader) {
214
+ // Search for the timestamp field by name (robust to Buffer/Uint8Array)
214
215
  if (Array.isArray(blockHeader)) {
215
216
  for (const field of blockHeader) {
216
- if (Array.isArray(field) && field.length >= 2 && field[0] === 'timestamp') {
217
- const timestampValue = field[1];
218
-
219
- // Handle different timestamp value types
220
- if (typeof timestampValue === 'number') {
221
- return timestampValue;
222
- } else if (typeof timestampValue === 'string' && timestampValue.startsWith('0x')) {
223
- // Handle hex string
224
- return parseInt(timestampValue.slice(2), 16);
225
- } else if (typeof timestampValue === 'string') {
226
- // Handle decimal string
227
- return parseInt(timestampValue, 10);
228
- } else if (timestampValue instanceof Uint8Array || Buffer.isBuffer(timestampValue)) {
229
- // Handle buffer - carefully determine the byte length
230
- const buf = Buffer.from(timestampValue);
231
- // Use a safe approach to read the value based on buffer length
232
- if (buf.length <= 6) {
233
- return buf.readUIntBE(0, buf.length);
234
- } else {
235
- // For larger buffers, convert to hex string first
217
+ if (Array.isArray(field) && field.length >= 2) {
218
+ const key = typeof field[0] === 'string' ? field[0] : toBufferView(field[0]).toString('utf8');
219
+ if (key === 'timestamp') {
220
+ const timestampValue = field[1];
221
+ // Handle different timestamp value types
222
+ if (typeof timestampValue === 'number') {
223
+ return timestampValue;
224
+ } else if (typeof timestampValue === 'string' && timestampValue.startsWith('0x')) {
225
+ return parseInt(timestampValue.slice(2), 16);
226
+ } else if (typeof timestampValue === 'string') {
227
+ return parseInt(timestampValue, 10);
228
+ } else if (timestampValue instanceof Uint8Array || Buffer.isBuffer(timestampValue)) {
229
+ const buf = toBufferView(timestampValue);
230
+ if (buf.length <= 6) {
231
+ return buf.readUIntBE(0, buf.length);
232
+ }
236
233
  return parseInt(buf.toString('hex'), 16);
237
234
  }
238
235
  }
239
236
  }
240
237
  }
241
238
  }
242
-
243
- // Fallback: if we couldn't find the timestamp or parse it correctly
244
- logger.warn('Could not find or parse timestamp in block header, using current time');
239
+ // Fallback
240
+ logger.warn(() => 'Could not find or parse timestamp in block header, using current time');
245
241
  return Math.floor(Date.now() / 1000);
246
242
  }
247
243
  }
@@ -15,11 +15,10 @@ server.on('message',function(msg,info){
15
15
  //sending msg
16
16
  server.send(msg,info.port,'localhost',function(error){
17
17
  if(error){
18
- client.close();
18
+ server.close(); // was client.close();
19
19
  }else{
20
20
  console.log('Data sent !!!');
21
21
  }
22
-
23
22
  });
24
23
 
25
24
  });
package/utils.js CHANGED
@@ -6,11 +6,20 @@ const { KEYUTIL } = require("jsrsasign");
6
6
  const fs = require('fs');
7
7
  var path = require('path');
8
8
 
9
+ // Zero-copy view for Uint8Array -> Buffer where possible
10
+ function toBufferView(u8) {
11
+ if (Buffer.isBuffer(u8)) return u8;
12
+ if (u8 && u8.buffer && typeof u8.byteOffset === 'number') {
13
+ return Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength);
14
+ }
15
+ return Buffer.from(u8);
16
+ }
17
+
9
18
  function makeReadable(decodedMessage) {
10
19
  if (Array.isArray(decodedMessage)) {
11
20
  return decodedMessage.map((item) => makeReadable(item));
12
21
  } else if (decodedMessage instanceof Uint8Array) {
13
- const buffer = Buffer.from(decodedMessage);
22
+ const buffer = toBufferView(decodedMessage);
14
23
  // Try to interpret the Buffer as a UTF-8 string
15
24
  const str = buffer.toString('utf8');
16
25
  if (/^[\x20-\x7E]+$/.test(str)) {
@@ -42,7 +51,7 @@ function makeReadable(decodedMessage) {
42
51
  // Helper functions
43
52
  function parseRequestId(requestIdRaw) {
44
53
  if (requestIdRaw instanceof Uint8Array || Buffer.isBuffer(requestIdRaw)) {
45
- const buffer = Buffer.from(requestIdRaw);
54
+ const buffer = toBufferView(requestIdRaw);
46
55
  return buffer.readUIntBE(0, buffer.length);
47
56
  } else if (typeof requestIdRaw === 'number') {
48
57
  return requestIdRaw;
@@ -53,10 +62,10 @@ function parseRequestId(requestIdRaw) {
53
62
 
54
63
  function parseResponseType(responseTypeRaw) {
55
64
  if (responseTypeRaw instanceof Uint8Array || Buffer.isBuffer(responseTypeRaw)) {
56
- return Buffer.from(responseTypeRaw).toString('utf8');
65
+ return toBufferView(responseTypeRaw).toString('utf8');
57
66
  } else if (Array.isArray(responseTypeRaw)) {
58
67
  // Convert each element to Buffer and concatenate
59
- const buffers = responseTypeRaw.map((item) => Buffer.from(item));
68
+ const buffers = responseTypeRaw.map((item) => toBufferView(item));
60
69
  const concatenated = Buffer.concat(buffers);
61
70
  return concatenated.toString('utf8');
62
71
  } else if (typeof responseTypeRaw === 'string') {
@@ -68,7 +77,7 @@ function parseResponseType(responseTypeRaw) {
68
77
 
69
78
  function parseReason(reasonRaw) {
70
79
  if (Buffer.isBuffer(reasonRaw) || reasonRaw instanceof Uint8Array) {
71
- return Buffer.from(reasonRaw).toString('utf8');
80
+ return toBufferView(reasonRaw).toString('utf8');
72
81
  } else if (typeof reasonRaw === 'string') {
73
82
  return reasonRaw;
74
83
  } else {
@@ -128,7 +137,7 @@ function loadOrGenerateKeyPair(keyLocation) {
128
137
 
129
138
  // Try to load existing keys
130
139
  if (fs.existsSync(keyLocation)) {
131
- logger.info(`Loading keys from ${keyLocation}`);
140
+ logger.info(() => `Loading keys from ${keyLocation}`);
132
141
  const keyData = JSON.parse(fs.readFileSync(keyLocation, 'utf8'));
133
142
 
134
143
  // Convert the stored JSON back to keypair objects
@@ -138,7 +147,7 @@ function loadOrGenerateKeyPair(keyLocation) {
138
147
  return { prvKeyObj, pubKeyObj };
139
148
  } else {
140
149
  // Generate new keypair
141
- logger.info(`Generating new key pair at ${keyLocation}`);
150
+ logger.info(() => `Generating new key pair at ${keyLocation}`);
142
151
  const kp = KEYUTIL.generateKeypair("EC", "secp256k1");
143
152
 
144
153
  // Store the keys in a serializable format
@@ -154,7 +163,7 @@ function loadOrGenerateKeyPair(keyLocation) {
154
163
  return kp;
155
164
  }
156
165
  } catch (error) {
157
- logger.error(`Error loading or generating key pair: ${error}`);
166
+ logger.error(() => `Error loading or generating key pair: ${error}`);
158
167
  throw error;
159
168
  }
160
169
  }
@@ -175,5 +184,6 @@ module.exports = {
175
184
  parseReason,
176
185
  generateCert,
177
186
  loadOrGenerateKeyPair,
178
- ensureDirectoryExistence
187
+ ensureDirectoryExistence,
188
+ toBufferView,
179
189
  };