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 +41 -38
- package/connection.js +86 -65
- package/index.js +6 -0
- package/logger.js +10 -5
- package/package.json +6 -8
- package/publishPort.js +67 -48
- package/rpc.js +37 -41
- package/testServers/udpTest.js +1 -2
- package/utils.js +19 -9
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 (
|
|
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.
|
|
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
|
-
"
|
|
20
|
+
"keccak": "^3.0.4",
|
|
25
21
|
"node-fetch": "^2.7.0",
|
|
26
22
|
"rlp": "^3.0.0",
|
|
27
|
-
"secp256k1": "^5.0.1"
|
|
28
|
-
|
|
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.
|
|
137
|
+
this._onUnsolicited = (message) => {
|
|
136
138
|
const [sessionIdRaw, messageContent] = message;
|
|
137
139
|
const messageTypeRaw = messageContent[0];
|
|
138
|
-
const messageType =
|
|
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
|
-
|
|
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 =
|
|
173
|
+
const sessionId = toBufferView(sessionIdRaw);
|
|
161
174
|
const portString = makeReadable(portStringRaw);
|
|
162
|
-
const ref =
|
|
163
|
-
const deviceId = `0x${
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
351
|
-
const ref =
|
|
352
|
-
const data =
|
|
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 =
|
|
412
|
-
const ref =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
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
|
}
|
package/testServers/udpTest.js
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
};
|