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/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 =
|
|
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 =
|
|
102
|
-
const data =
|
|
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 =
|
|
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
|
-
//
|
|
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
|
-
|
|
295
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
//
|
|
516
|
-
const encodedData = Buffer.concat(dataToSign
|
|
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 =
|
|
526
|
-
|
|
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
|
-
|
|
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) {
|