diodejs 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,19 @@
1
- # diode_js
1
+ # DiodeJs
2
2
 
3
3
  ## Overview
4
- `diode_js` is a JavaScript client for interacting with the Diode network. It provides functionalities to bind and publish ports, send RPC commands, and handle responses.
4
+ `diodejs` is 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
 
6
6
  ## Installation
7
7
  ```bash
8
8
  npm install diodejs
9
9
  ```
10
- ## Quick Start
11
10
 
12
- To get started, you need to generate a device certificate using OpenSSL. You can use this command:
11
+ ### Quick Start
13
12
 
14
- ```bash
15
- openssl ecparam -name secp256k1 -out secp256k1_params.pem
16
- openssl req -newkey ec:./secp256k1_params.pem -nodes -keyout device_certificate.pem -x509 -days 365 -out device_certificate.pem -subj "/CN=device"
17
- ```
13
+ If you want to enable logs, set environment variable LOG to true.
14
+ If you want to enable debug logs, set environment variable DEBUG to true.
15
+
16
+ Can also use .env files
18
17
 
19
18
  ### Test RPC
20
19
 
@@ -96,3 +95,65 @@ async function main() {
96
95
  main();
97
96
 
98
97
  ```
98
+
99
+ ## Reference
100
+
101
+ ### Classes and Methods
102
+
103
+ #### `DiodeConnection`
104
+
105
+ - **Constructor**: `new DiodeConnection(host, port, certPath)`
106
+ - `host` (string): The host address of the Diode server.
107
+ - `port` (number): The port number of the Diode server.
108
+ - `certPath` (string)(default: ./cert/device_certificate.pem): The path to the device certificate. If doesn't exist, generates automaticly.
109
+
110
+ - **Methods**:
111
+ - `connect()`: Connects to the Diode server. Returns a promise.
112
+ - `sendCommand(commandArray)`: Sends a command to the Diode server. Returns a promise.
113
+ - `sendCommandWithSessionId(commandArray, sessionId)`: Sends a command with a session ID. Returns a promise.
114
+ - `getEthereumAddress()`: Returns the Ethereum address derived from the device certificate.
115
+ - `getServerEthereumAddress()`: Returns the Ethereum address of the server.
116
+ - `createTicketCommand()`: Creates a ticket command for authentication. Returns a promise.
117
+ - `close()`: Closes the connection to the Diode server.
118
+
119
+ #### `DiodeRPC`
120
+
121
+ - **Constructor**: `new DiodeRPC(connection)`
122
+ - `connection` (DiodeConnection): An instance of `DiodeConnection`.
123
+
124
+ - **Methods**:
125
+ - `getBlockPeak()`: Retrieves the current block peak. Returns a promise.
126
+ - `getBlockHeader(index)`: Retrieves the block header for a given index. Returns a promise.
127
+ - `getBlock(index)`: Retrieves the block for a given index. Returns a promise.
128
+ - `ping()`: Sends a ping command. Returns a promise.
129
+ - `portOpen(deviceId, port, flags)`: Opens a port on the device. Returns a promise.
130
+ - `portSend(ref, data)`: Sends data to the device. Returns a promise.
131
+ - `portClose(ref)`: Closes a port on the device. Returns a promise.
132
+ - `sendError(sessionId, ref, error)`: Sends an error response. Returns a promise.
133
+ - `sendResponse(sessionId, ref, response)`: Sends a response. Returns a promise.
134
+ - `getEpoch()`: Retrieves the current epoch. Returns a promise.
135
+ - `parseTimestamp(blockHeader)`: Parses the timestamp from a block header. Returns a number.
136
+
137
+ #### `BindPort`
138
+
139
+ - **Constructor**: `new BindPort(connection, localPort, targetPort, deviceIdHex)`
140
+ - `connection` (DiodeConnection): An instance of `DiodeConnection`.
141
+ - `localPort` (number): The local port to bind.
142
+ - `targetPort` (number): The target port on the device.
143
+ - `deviceIdHex` (string): The device ID in hexadecimal format.
144
+
145
+ - **Methods**:
146
+ - `bind()`: Binds the local port to the target port on the device.
147
+
148
+ #### `PublishPort`
149
+
150
+ - **Constructor**: `new PublishPort(connection, publishedPorts, certPath)`
151
+ - `connection` (DiodeConnection): An instance of `DiodeConnection`.
152
+ - `publishedPorts` (array): An array of ports to publish.
153
+ - `certPath` (string): The path to the device certificate.
154
+
155
+ - **Methods**:
156
+ - `startListening()`: Starts listening for unsolicited messages.
157
+ - `handlePortOpen(sessionIdRaw, messageContent)`: Handles port open requests.
158
+ - `handlePortSend(sessionIdRaw, messageContent)`: Handles port send requests.
159
+ - `handlePortClose(sessionIdRaw, messageContent)`: Handles port close requests.
package/bindPort.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const net = require('net');
2
2
  const { Buffer } = require('buffer');
3
3
  const DiodeRPC = require('./rpc');
4
+ const logger = require('./logger');
4
5
 
5
6
  class BindPort {
6
7
  constructor(connection, localPort, targetPort,deviceIdHex) {
@@ -35,7 +36,7 @@ class BindPort {
35
36
  if (clientSocket) {
36
37
  clientSocket.write(data);
37
38
  } else {
38
- console.warn(`No client socket found for ref: ${dataRef.toString('hex')}`);
39
+ logger.warn(`No client socket found for ref: ${dataRef.toString('hex')}`);
39
40
  }
40
41
  } else if (messageType === 'portclose') {
41
42
  const refRaw = messageContent[1];
@@ -47,24 +48,30 @@ class BindPort {
47
48
  if (clientSocket) {
48
49
  clientSocket.end();
49
50
  clientSockets.delete(dataRef.toString('hex'));
50
- console.log(`Port closed for ref: ${dataRef.toString('hex')}`);
51
+ logger.info(`Port closed for ref: ${dataRef.toString('hex')}`);
51
52
  }
52
53
  } else {
53
- console.warn(`Unknown unsolicited message type: ${messageType}`);
54
+ logger.warn(`Unknown unsolicited message type: ${messageType}`);
54
55
  }
55
56
  });
56
57
 
57
58
  // Set up local server
58
59
  const server = net.createServer(async (clientSocket) => {
59
- console.log('Client connected to local server');
60
+ logger.info('Client connected to local server');
60
61
 
61
62
  // Open a new port on the device for this client
62
63
  let ref;
63
64
  try {
64
65
  ref = await rpc.portOpen(deviceId, this.targetPort, 'rw');
65
- console.log(`Port opened on device with ref: ${ref.toString('hex')} for client`);
66
+ if (!ref) {
67
+ logger.error('Error opening port on device');
68
+ clientSocket.destroy();
69
+ return
70
+ } else {
71
+ logger.info(`Port opened on device with ref: ${ref.toString('hex')} for client`);
72
+ }
66
73
  } catch (error) {
67
- console.error('Error opening port on device:', error);
74
+ logger.error(`Error opening port on device: ${error}`);
68
75
  clientSocket.destroy();
69
76
  return;
70
77
  }
@@ -77,41 +84,46 @@ class BindPort {
77
84
  try {
78
85
  await rpc.portSend(ref, data);
79
86
  } catch (error) {
80
- console.error('Error sending data to device:', error);
87
+ logger.error(`Error sending data to device: ${error}`);
81
88
  clientSocket.destroy();
82
89
  }
83
90
  });
84
91
 
85
92
  // Handle client socket closure
86
93
  clientSocket.on('end', async () => {
87
- console.log('Client disconnected');
88
- clientSockets.delete(ref.toString('hex'));
89
- try {
90
- console.log(`Port closed on device for ref: ${ref.toString('hex')}`);
91
- } catch (error) {
92
- console.error('Error closing port on device:', error);
94
+ logger.info('Client disconnected');
95
+ if (ref && clientSockets.has(ref.toString('hex'))) {
96
+ try {
97
+ await rpc.portClose(ref);
98
+ logger.info(`Port closed on device for ref: ${ref.toString('hex')}`);
99
+ clientSockets.delete(ref.toString('hex'));
100
+ } catch (error) {
101
+ logger.error(`Error closing port on device: ${error}`);
102
+ }
103
+ } else {
104
+ logger.warn('Ref is invalid or no longer in clientSockets.');
93
105
  }
94
106
  });
95
107
 
96
108
  // Handle client socket errors
97
109
  clientSocket.on('error', (err) => {
98
- console.error('Client socket error:', err);
110
+ logger.error('Client socket error:', err);
99
111
  });
100
112
  });
101
113
 
102
114
  server.listen(this.localPort, () => {
103
- console.log(`Local server listening on port ${this.localPort}`);
115
+ logger.info(`Local server listening on port ${this.localPort}`);
104
116
  });
105
117
 
106
118
  // Handle device disconnect
107
119
  this.connection.on('end', () => {
108
- console.log('Disconnected from Diode.io server');
120
+ logger.info('Disconnected from Diode.io server');
109
121
  server.close();
110
122
  });
111
123
 
112
124
  // Handle connection errors
113
125
  this.connection.on('error', (err) => {
114
- console.error('Connection error:', err);
126
+ logger.error(`Connection error: ${err}`);
115
127
  server.close();
116
128
  });
117
129
  }
package/connection.js CHANGED
@@ -3,7 +3,7 @@ const tls = require('tls');
3
3
  const fs = require('fs');
4
4
  const { RLP } = require('@ethereumjs/rlp');
5
5
  const EventEmitter = require('events');
6
- const { makeReadable, parseRequestId, parseResponseType, parseReason } = require('./utils');
6
+ const { makeReadable, parseRequestId, parseResponseType, parseReason, generateCert } = require('./utils');
7
7
  const { Buffer } = require('buffer'); // Import Buffer
8
8
  const asn1 = require('asn1.js');
9
9
  const secp256k1 = require('secp256k1');
@@ -11,8 +11,9 @@ const ethUtil = require('ethereumjs-util');
11
11
  const crypto = require('crypto');
12
12
  const DiodeRPC = require('./rpc');
13
13
  const abi = require('ethereumjs-abi');
14
+ const logger = require('./logger');
14
15
  class DiodeConnection extends EventEmitter {
15
- constructor(host, port, certPath) {
16
+ constructor(host, port, certPath = './cert/device_certificate.pem') {
16
17
  super();
17
18
  this.host = host;
18
19
  this.port = port;
@@ -25,6 +26,13 @@ class DiodeConnection extends EventEmitter {
25
26
  // Add buffer to handle partial data
26
27
  this.receiveBuffer = Buffer.alloc(0);
27
28
  this.RPC = new DiodeRPC(this);
29
+ this.isReconnecting = false;
30
+ this.connectPromise = null;
31
+
32
+ // Check if certPath exists, if not generate the certificate
33
+ if (!fs.existsSync(this.certPath)) {
34
+ generateCert(this.certPath);
35
+ }
28
36
  }
29
37
 
30
38
  connect() {
@@ -40,7 +48,7 @@ class DiodeConnection extends EventEmitter {
40
48
  };
41
49
 
42
50
  this.socket = tls.connect(this.port, this.host, options, async () => {
43
- console.log('Connected to Diode.io server');
51
+ logger.info('Connected to Diode.io server');
44
52
  // Set keep-alive to prevent connection timeout forever
45
53
  this.socket.setKeepAlive(true, 0);
46
54
 
@@ -48,33 +56,55 @@ class DiodeConnection extends EventEmitter {
48
56
  try {
49
57
  const ticketCommand = await this.createTicketCommand();
50
58
  const response = await this.sendCommand(ticketCommand).catch(reject);
51
- console.log('Ticket accepted:', response);
59
+ logger.info(`Ticket accepted: ${makeReadable(response)}`);
52
60
  resolve();
53
61
  } catch (error) {
54
- console.error('Error sending ticket:', error);
62
+ logger.error(`Error sending ticket: ${error}`);
55
63
  reject(error);
56
64
  }
57
65
  });
58
66
 
59
67
  this.socket.on('data', (data) => {
68
+ logger.debug(`Received data: ${data.toString('hex')}`);
60
69
  try {
61
70
  this._handleData(data);
62
71
  } catch (error) {
63
- console.error('Error handling data:', error);
72
+ logger.error(`Error handling data: ${error}`);
64
73
  }
65
74
  });
66
75
  this.socket.on('error', (err) => {
67
- console.error('Connection error:', err);
76
+ logger.error(`Connection error: ${err}`);
68
77
  reject(err);
69
78
  });
70
- this.socket.on('end', () => console.log('Disconnected from server'));
79
+ this.socket.on('end', () => logger.info('Disconnected from server'));
71
80
  });
72
81
  }
73
82
 
83
+ _ensureConnected() {
84
+ if (this.socket && !this.socket.destroyed) {
85
+ return Promise.resolve();
86
+ }
87
+ if (this.connectPromise) {
88
+ return this.connectPromise;
89
+ }
90
+ this.isReconnecting = true;
91
+ this.connectPromise = this.connect()
92
+ .then(() => {
93
+ this.isReconnecting = false;
94
+ this.connectPromise = null;
95
+ })
96
+ .catch((err) => {
97
+ this.isReconnecting = false;
98
+ this.connectPromise = null;
99
+ throw err;
100
+ });
101
+ return this.connectPromise;
102
+ }
103
+
74
104
  _handleData(data) {
75
105
  // Append new data to the receive buffer
76
106
  this.receiveBuffer = Buffer.concat([this.receiveBuffer, data]);
77
- console.log('Received data:', data.toString('hex'));
107
+ logger.debug(`Received data: ${data.toString('hex')}`);
78
108
 
79
109
  let offset = 0;
80
110
  while (offset + 2 <= this.receiveBuffer.length) {
@@ -92,7 +122,7 @@ class DiodeConnection extends EventEmitter {
92
122
 
93
123
  try {
94
124
  const decodedMessage = RLP.decode(Uint8Array.from(messageBuffer));
95
- console.log('Decoded message:', makeReadable(decodedMessage));
125
+ logger.debug(`Decoded message: ${makeReadable(decodedMessage)}`);
96
126
 
97
127
  if (Array.isArray(decodedMessage) && decodedMessage.length > 1) {
98
128
  const requestIdRaw = decodedMessage[0];
@@ -102,8 +132,8 @@ class DiodeConnection extends EventEmitter {
102
132
  const requestId = parseRequestId(requestIdRaw);
103
133
 
104
134
  // Debug statements
105
- console.log('requestIdRaw:', requestIdRaw);
106
- console.log('Parsed requestId:', requestId);
135
+ logger.debug(`requestIdRaw: ${requestIdRaw}`);
136
+ logger.debug(`Parsed requestId: ${requestId}`);
107
137
 
108
138
  if (requestId !== null && this.pendingRequests.has(requestId)) {
109
139
  // This is a response to a pending request
@@ -111,16 +141,14 @@ class DiodeConnection extends EventEmitter {
111
141
  const responseRaw = responseData[0];
112
142
 
113
143
  // Debug statements
114
- console.log('responseTypeRaw:', responseTypeRaw);
115
- console.log('Type of responseTypeRaw:', typeof responseTypeRaw);
116
- console.log('Instance of responseTypeRaw:', responseTypeRaw instanceof Uint8Array);
117
- console.log('Is Array:', Array.isArray(responseTypeRaw));
144
+ logger.debug(`responseTypeRaw: ${responseTypeRaw}`);
145
+ logger.debug(`Type of responseTypeRaw: ${typeof responseTypeRaw}`);
118
146
 
119
147
  // Parse responseType
120
148
  const responseType = parseResponseType(responseTypeRaw);
121
149
 
122
- console.log(`Received response for requestId: ${requestId}`);
123
- console.log(`Response Type: '${responseType}'`);
150
+ logger.debug(`Received response for requestId: ${requestId}`);
151
+ logger.debug(`Response Type: '${responseType}'`);
124
152
 
125
153
  const { resolve, reject } = this.pendingRequests.get(requestId);
126
154
  try{
@@ -146,20 +174,20 @@ class DiodeConnection extends EventEmitter {
146
174
  resolve(responseData);
147
175
  }
148
176
  } catch (error) {
149
- console.error('Error handling response:', error);
177
+ logger.error(`Error handling response: ${error}`);
150
178
  }
151
179
  this.pendingRequests.delete(requestId);
152
180
  } else {
153
181
  // This is an unsolicited message
154
- console.log('Received unsolicited message:', decodedMessage);
182
+ logger.debug(`Received unsolicited message: ${makeReadable(decodedMessage)}`);
155
183
  this.emit('unsolicited', decodedMessage);
156
184
  }
157
185
  } else {
158
186
  // Invalid message format
159
- console.error('Invalid message format:', decodedMessage);
187
+ logger.error(`Invalid message format: ${makeReadable(decodedMessage)}`);
160
188
  }
161
189
  } catch (error) {
162
- console.error('Error decoding message:', error);
190
+ logger.error(`Error decoding message: ${error}`);
163
191
  }
164
192
  }
165
193
 
@@ -190,67 +218,55 @@ class DiodeConnection extends EventEmitter {
190
218
 
191
219
  sendCommand(commandArray) {
192
220
  return new Promise((resolve, reject) => {
193
- //check if connection is alive
194
- if (!this.socket || this.socket.destroyed) {
195
- //reconnect
196
- this.connect().then(() => {
197
- this.sendCommand(commandArray).then(resolve).catch(reject);
198
- }).catch(reject);
199
- return;
200
- }
201
- const requestId = this._getNextRequestId();
202
- // Build the message as [requestId, [commandArray]]
203
- const commandWithId = [requestId, commandArray];
204
-
205
- // Store the promise callbacks to resolve/reject later
206
- this.pendingRequests.set(requestId, { resolve, reject });
207
-
208
- const commandBuffer = RLP.encode(commandWithId);
209
- const byteLength = Buffer.byteLength(commandBuffer);
210
-
211
- // Create a 2-byte length buffer
212
- const lengthBuffer = Buffer.alloc(2);
213
- lengthBuffer.writeUInt16BE(byteLength, 0);
214
-
215
- const message = Buffer.concat([lengthBuffer, commandBuffer]);
216
-
217
- console.log(`Sending command with requestId ${requestId}:`, commandArray);
218
- console.log('Command buffer:', message.toString('hex'));
219
-
220
- this.socket.write(message);
221
+ this._ensureConnected().then(() => {
222
+ const requestId = this._getNextRequestId();
223
+ // Build the message as [requestId, [commandArray]]
224
+ const commandWithId = [requestId, commandArray];
225
+
226
+ // Store the promise callbacks to resolve/reject later
227
+ this.pendingRequests.set(requestId, { resolve, reject });
228
+
229
+ const commandBuffer = RLP.encode(commandWithId);
230
+ const byteLength = Buffer.byteLength(commandBuffer);
231
+
232
+ // Create a 2-byte length buffer
233
+ const lengthBuffer = Buffer.alloc(2);
234
+ lengthBuffer.writeUInt16BE(byteLength, 0);
235
+
236
+ const message = Buffer.concat([lengthBuffer, commandBuffer]);
237
+
238
+ logger.debug(`Sending command with requestId ${requestId}: ${commandArray}`);
239
+ logger.debug(`Command buffer: ${message.toString('hex')}`);
240
+
241
+ this.socket.write(message);
242
+ }).catch(reject);
221
243
  });
222
244
  }
223
245
 
224
246
  sendCommandWithSessionId(commandArray, sessionId) {
225
247
  return new Promise((resolve, reject) => {
226
- //check if connection is alive
227
- if (!this.socket || this.socket.destroyed) {
228
- //reconnect
229
- this.connect().then(() => {
230
- this.sendCommand(commandArray).then(resolve).catch(reject);
231
- }).catch(reject);
232
- return;
233
- }
234
- const requestId = sessionId;
235
- // Build the message as [requestId, [commandArray]]
236
- const commandWithId = [requestId, commandArray];
237
-
238
- // Store the promise callbacks to resolve/reject later
239
- this.pendingRequests.set(requestId, { resolve, reject });
240
-
241
- const commandBuffer = RLP.encode(commandWithId);
242
- const byteLength = Buffer.byteLength(commandBuffer);
243
-
244
- // Create a 2-byte length buffer
245
- const lengthBuffer = Buffer.alloc(2);
246
- lengthBuffer.writeUInt16BE(byteLength, 0);
247
-
248
- const message = Buffer.concat([lengthBuffer, commandBuffer]);
249
-
250
- console.log(`Sending command with requestId ${requestId}:`, commandArray);
251
- console.log('Command buffer:', message.toString('hex'));
252
-
253
- this.socket.write(message);
248
+ this._ensureConnected().then(() => {
249
+ const requestId = sessionId;
250
+ // Build the message as [requestId, [commandArray]]
251
+ const commandWithId = [requestId, commandArray];
252
+
253
+ // Store the promise callbacks to resolve/reject later
254
+ this.pendingRequests.set(requestId, { resolve, reject });
255
+
256
+ const commandBuffer = RLP.encode(commandWithId);
257
+ const byteLength = Buffer.byteLength(commandBuffer);
258
+
259
+ // Create a 2-byte length buffer
260
+ const lengthBuffer = Buffer.alloc(2);
261
+ lengthBuffer.writeUInt16BE(byteLength, 0);
262
+
263
+ const message = Buffer.concat([lengthBuffer, commandBuffer]);
264
+
265
+ logger.debug(`Sending command with requestId ${requestId}: ${commandArray}`);
266
+ logger.debug(`Command buffer: ${message.toString('hex')}`);
267
+
268
+ this.socket.write(message);
269
+ }).catch(reject);
254
270
  });
255
271
  }
256
272
 
@@ -300,7 +316,7 @@ class DiodeConnection extends EventEmitter {
300
316
 
301
317
  const ecPrivateKey = ECPrivateKeyASN.decode(privateKeyOctetString, 'der');
302
318
  privateKeyBytes = ecPrivateKey.privateKey;
303
- console.log('Private key bytes:', privateKeyBytes.toString('hex'));
319
+ logger.debug(`Private key bytes: ${privateKeyBytes.toString('hex')}`);
304
320
  } else {
305
321
  throw new Error('Unsupported key format. Expected EC PRIVATE KEY or PRIVATE KEY in PEM format.');
306
322
  }
@@ -317,10 +333,10 @@ class DiodeConnection extends EventEmitter {
317
333
  const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
318
334
  const address = '0x' + addressBuffer.toString('hex');
319
335
 
320
- console.log('Ethereum address:', address);
336
+ logger.info(`Ethereum address: ${address}`);
321
337
  return address;
322
338
  } catch (error) {
323
- console.error('Error extracting Ethereum address:', error);
339
+ logger.error(`Error extracting Ethereum address: ${error}`);
324
340
  throw error;
325
341
  }
326
342
  }
@@ -336,15 +352,15 @@ class DiodeConnection extends EventEmitter {
336
352
  ? serverCert.pubkey
337
353
  : Buffer.from(serverCert.pubkey);
338
354
 
339
- console.log('Public key Server:', publicKeyBuffer.toString('hex'));
355
+ logger.debug(`Public key Server: ${publicKeyBuffer.toString('hex')}`);
340
356
 
341
357
  const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
342
358
  const address = '0x' + addressBuffer.toString('hex');
343
359
 
344
- console.log('Server Ethereum address:', address);
360
+ logger.info(`Server Ethereum address: ${address}`);
345
361
  return address;
346
362
  } catch (error) {
347
- console.error('Error extracting server Ethereum address:', error);
363
+ logger.error(`Error extracting server Ethereum address: ${error}`);
348
364
  throw error;
349
365
  }
350
366
  }
@@ -448,7 +464,7 @@ class DiodeConnection extends EventEmitter {
448
464
 
449
465
  return privateKeyBytes;
450
466
  } catch (error) {
451
- console.error('Error extracting Ethereum address:', error);
467
+ logger.error(`Error extracting Ethereum address: ${error}`);
452
468
  throw error;
453
469
  }
454
470
  }
@@ -475,17 +491,17 @@ class DiodeConnection extends EventEmitter {
475
491
  // Convert each element in dataToSign to bytes32 and concatenate them
476
492
  const encodedData = Buffer.concat(dataToSign.map(item => abi.rawEncode(['bytes32'], [item])));
477
493
 
478
- console.log('Encoded data:', encodedData.toString('hex'));
494
+ logger.debug(`Encoded data: ${encodedData.toString('hex')}`);
479
495
 
480
- console.log('Data to sign:', makeReadable(dataToSign));
496
+ logger.debug(`Data to sign: ${makeReadable(dataToSign)}`);
481
497
 
482
498
 
483
499
  // Sign the data
484
500
  const privateKey = this.getPrivateKey();
485
501
  const msgHash = ethUtil.keccak256(encodedData);
486
- console.log('Message hash:', msgHash.toString('hex'));
502
+ logger.debug(`Message hash: ${msgHash.toString('hex')}`);
487
503
  const signature = secp256k1.ecdsaSign(msgHash, privateKey);
488
- console.log('Signature:', signature);
504
+ logger.debug(`Signature: ${signature.signature.toString('hex')}`);
489
505
 
490
506
  const signatureBuffer = Buffer.concat([
491
507
  ethUtil.toBuffer([signature.recid]),
@@ -519,7 +535,7 @@ class DiodeConnection extends EventEmitter {
519
535
  localAddress,
520
536
  epoch
521
537
  );
522
- console.log('Signature hex:', signature.toString('hex'));
538
+ logger.debug(`Signature hex: ${signature.toString('hex')}`);
523
539
 
524
540
 
525
541
  // Construct the ticket command
@@ -8,7 +8,7 @@ async function main() {
8
8
  const connection = new DiodeConnection(host, port, certPath);
9
9
  await connection.connect();
10
10
 
11
- const portForward = new BindPort(connection, 3002, 80, "5365baf29cb7ab58de588dfc448913cb609283e2");
11
+ const portForward = new BindPort(connection, 3002, 8080, "5365baf29cb7ab58de588dfc448913cb609283e2");
12
12
  portForward.bind();
13
13
 
14
14
  }
package/index.js CHANGED
@@ -4,4 +4,5 @@ const DiodeRPC = require('./rpc');
4
4
  const BindPort = require('./bindPort');
5
5
  const PublishPort = require('./publishPort');
6
6
  const makeReadable = require('./utils').makeReadable;
7
- module.exports = { DiodeConnection, DiodeRPC, BindPort , PublishPort, makeReadable };
7
+ const logger = require('./logger');
8
+ module.exports = { DiodeConnection, DiodeRPC, BindPort , PublishPort, makeReadable, logger };
package/logger.js ADDED
@@ -0,0 +1,28 @@
1
+ const setupLogger = require('dera-logger');
2
+ require('dotenv').config();
3
+ const isDebug = (process.env.DEBUG === 'true'); // Simple debug flag
4
+ const isLogEnabled = (process.env.LOG === 'true'); // Simple log flag
5
+
6
+ const options = {
7
+ logDirectory: 'logs',
8
+ timestampFormat: 'HH:mm:ss',
9
+ fileDatePattern: 'YYYY-MM-DD',
10
+ zippedArchive: false,
11
+ maxLogFileSize: null,
12
+ maxFiles: '14d',
13
+ addConsoleInNonProduction: true,
14
+ transports: [
15
+ { filename: 'combined', level: 'silly', source: 'app' },
16
+ { filename: 'error', level: 'warn', source: 'app' }
17
+ ]
18
+ };
19
+
20
+ const logger = setupLogger(options);
21
+
22
+ // Wrap logger calls to respect debug mode
23
+ 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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diodejs",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
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,18 +11,20 @@
11
11
  "dependencies": {
12
12
  "@ethereumjs/rlp": "^5.0.2",
13
13
  "asn1.js": "^5.4.1",
14
- "axios": "^1.6.8",
15
14
  "buffer": "^6.0.3",
16
15
  "crypto": "^1.0.1",
16
+ "dera-logger": "^2.0.0",
17
17
  "dgram": "^1.0.1",
18
+ "dotenv": "^16.4.7",
18
19
  "ethereumjs-abi": "^0.6.8",
19
20
  "ethereumjs-util": "^7.1.5",
20
21
  "ethers": "^6.13.2",
21
22
  "fs": "^0.0.1-security",
23
+ "jsrsasign": "^11.1.0",
22
24
  "net": "^1.0.2",
23
25
  "node-fetch": "^2.7.0",
24
26
  "rlp": "^3.0.0",
25
- "secp256k1": "^5.0.0",
27
+ "secp256k1": "^5.0.1",
26
28
  "tls": "^0.0.1"
27
29
  }
28
30
  }
package/publishPort.js CHANGED
@@ -9,6 +9,8 @@ const EventEmitter = require('events');
9
9
  const { Duplex } = require('stream');
10
10
  const DiodeRPC = require('./rpc');
11
11
  const { makeReadable } = require('./utils');
12
+ const logger = require('./logger');
13
+
12
14
  class DiodeSocket extends Duplex {
13
15
  constructor(ref, rpc) {
14
16
  super();
@@ -37,7 +39,7 @@ class PublishPort extends EventEmitter {
37
39
  constructor(connection, publishedPorts, certPath) {
38
40
  super();
39
41
  this.connection = connection;
40
- this.publishedPorts = publishedPorts; // Array of ports to publish
42
+ this.publishedPorts = new Set(publishedPorts); // Convert array to a Set
41
43
  this.connections = new Map(); // Map to store active connections
42
44
  this.startListening();
43
45
  this.rpc = new DiodeRPC(connection);
@@ -58,7 +60,7 @@ class PublishPort extends EventEmitter {
58
60
  } else if (messageType === 'portclose') {
59
61
  this.handlePortClose(sessionIdRaw, messageContent);
60
62
  } else {
61
- console.warn(`Unknown unsolicited message type: ${messageType}`);
63
+ logger.warn(`Unknown unsolicited message type: ${messageType}`);
62
64
  }
63
65
  });
64
66
  }
@@ -74,7 +76,7 @@ class PublishPort extends EventEmitter {
74
76
  const ref = Buffer.from(refRaw);
75
77
  const deviceId = Buffer.from(deviceIdRaw).toString('hex');
76
78
 
77
- console.log(`Received portopen request for portString ${portString} with ref ${ref.toString('hex')} from device ${deviceId}`);
79
+ logger.info(`Received portopen request for portString ${portString} with ref ${ref.toString('hex')} from device ${deviceId}`);
78
80
 
79
81
  // Extract protocol and port number from portString
80
82
  var protocol = 'tcp';
@@ -92,8 +94,8 @@ class PublishPort extends EventEmitter {
92
94
  }
93
95
 
94
96
  // Check if the port is published
95
- if (!this.publishedPorts.includes(port)) {
96
- console.warn(`Port ${port} is not published. Rejecting request.`);
97
+ if (!this.publishedPorts.has(port)) { // Use .has() instead of .includes()
98
+ logger.warn(`Port ${port} is not published. Rejecting request.`);
97
99
  // Send error response
98
100
  this.rpc.sendError(sessionId, ref, 'Port is not published');
99
101
  return;
@@ -107,7 +109,7 @@ class PublishPort extends EventEmitter {
107
109
  } else if (protocol === 'udp') {
108
110
  this.handleUDPConnection(sessionId, ref, port);
109
111
  } else {
110
- console.warn(`Unsupported protocol: ${protocol}`);
112
+ logger.warn(`Unsupported protocol: ${protocol}`);
111
113
  this.rpc.sendError(sessionId, ref, `Unsupported protocol: ${protocol}`);
112
114
  }
113
115
  }
@@ -122,14 +124,14 @@ class PublishPort extends EventEmitter {
122
124
  });
123
125
 
124
126
  localSocket.on('end', () => {
125
- console.log(`Local service disconnected`);
127
+ logger.info(`Local service disconnected`);
126
128
  // Send portclose message to Diode
127
129
  this.rpc.portClose(ref);
128
130
  this.connections.delete(ref.toString('hex'));
129
131
  });
130
132
 
131
133
  localSocket.on('error', (err) => {
132
- console.error(`Error with local service:`, err);
134
+ logger.error(`Error with local service: ${err}`);
133
135
  // Send portclose message to Diode
134
136
  this.rpc.portClose(ref);
135
137
  this.connections.delete(ref.toString('hex'));
@@ -140,7 +142,7 @@ class PublishPort extends EventEmitter {
140
142
  handleTCPConnection(sessionId, ref, port) {
141
143
  // Create a TCP connection to the local service on the specified port
142
144
  const localSocket = net.connect({ port: port }, () => {
143
- console.log(`Connected to local TCP service on port ${port}`);
145
+ logger.info(`Connected to local TCP service on port ${port}`);
144
146
  // Send success response
145
147
  this.rpc.sendResponse(sessionId, ref, 'ok');
146
148
  });
@@ -175,7 +177,7 @@ class PublishPort extends EventEmitter {
175
177
 
176
178
  // Connect to the local service (TCP or TLS as needed)
177
179
  const localSocket = net.connect({ port: port }, () => {
178
- console.log(`Connected to local TCP service on port ${port}`);
180
+ logger.info(`Connected to local TCP service on port ${port}`);
179
181
  // Send success response
180
182
  this.rpc.sendResponse(sessionId, ref, 'ok');
181
183
  });
@@ -185,7 +187,7 @@ class PublishPort extends EventEmitter {
185
187
 
186
188
  // Handle errors and cleanup
187
189
  tlsSocket.on('error', (err) => {
188
- console.error('TLS Socket error:', err);
190
+ logger.error(`TLS Socket error: ${err}`);
189
191
  this.rpc.portClose(ref);
190
192
  this.connections.delete(ref.toString('hex'));
191
193
  });
@@ -221,6 +223,8 @@ class PublishPort extends EventEmitter {
221
223
  remoteInfo,
222
224
  });
223
225
 
226
+ logger.info(`UDP connection set up on port ${port}`);
227
+
224
228
  // Handle messages from the local UDP service
225
229
  localSocket.on('message', (msg, rinfo) => {
226
230
  //need to add 4 bytes of data length to the beginning of the message but it's Big Endian
@@ -232,7 +236,7 @@ class PublishPort extends EventEmitter {
232
236
  });
233
237
 
234
238
  localSocket.on('error', (err) => {
235
- console.error(`UDP Socket error:`, err);
239
+ logger.error(`UDP Socket error: ${err}`);
236
240
  this.rpc.portClose(ref);
237
241
  this.connections.delete(ref.toString('hex'));
238
242
  });
@@ -273,7 +277,7 @@ class PublishPort extends EventEmitter {
273
277
  diodeSocket.pushData(data);
274
278
  }
275
279
  } else {
276
- console.warn(`No local connection found for ref ${ref.toString('hex')}. Sending portclose.`);
280
+ logger.warn(`No local connection found for ref ${ref.toString('hex')}. Sending portclose.`);
277
281
  this.rpc.sendError(sessionId, ref, 'No local connection found');
278
282
  }
279
283
  }
@@ -283,7 +287,7 @@ class PublishPort extends EventEmitter {
283
287
  const sessionId = Buffer.from(sessionIdRaw);
284
288
  const ref = Buffer.from(refRaw);
285
289
 
286
- console.log(`Received portclose for ref ${ref.toString('hex')}`);
290
+ logger.info(`Received portclose for ref ${ref.toString('hex')}`);
287
291
 
288
292
  const connectionInfo = this.connections.get(ref.toString('hex'));
289
293
  if (connectionInfo) {
package/rpc.js CHANGED
@@ -1,5 +1,6 @@
1
1
  //rpc.js
2
2
  const { makeReadable, parseRequestId, parseResponseType, parseReason } = require('./utils');
3
+ const logger = require('./logger');
3
4
 
4
5
  class DiodeRPC {
5
6
  constructor(connection) {
@@ -24,9 +25,10 @@ class DiodeRPC {
24
25
  } else {
25
26
  throw new Error('Invalid block number format. response:', makeReadable(responseData));
26
27
  }
28
+ logger.debug(`Block number is: ${blockNumber}`);
27
29
  return blockNumber;
28
30
  }).catch((error) => {
29
- console.error('Error during get block peak:', error);
31
+ logger.error(`Error during get block peak: ${error}`);
30
32
  return;
31
33
  });
32
34
  }
@@ -34,7 +36,7 @@ class DiodeRPC {
34
36
  return this.connection.sendCommand(['getblockheader', index]).then((responseData) => {
35
37
  return responseData[0]; // block_header
36
38
  }).catch((error) => {
37
- console.error('Error during get block header:', error);
39
+ logger.error(`Error during get block header: ${error}`);
38
40
  return;
39
41
  });
40
42
  }
@@ -43,7 +45,7 @@ class DiodeRPC {
43
45
  return this.connection.sendCommand(['getblock', index]).then((responseData) => {
44
46
  return responseData[0]; // block
45
47
  }).catch((error) => {
46
- console.error('Error during get block:', error);
48
+ logger.error(`Error during get block: ${error}`);
47
49
  return;
48
50
  });
49
51
  }
@@ -62,7 +64,7 @@ class DiodeRPC {
62
64
  throw new Error(`Unknown status in response: '${status}'`);
63
65
  }
64
66
  }).catch((error) => {
65
- console.error('Error during ping:', error);
67
+ logger.error(`Error during ping: ${error}`);
66
68
  return false;
67
69
  })
68
70
  }
@@ -90,7 +92,7 @@ class DiodeRPC {
90
92
  throw new Error(`Unknown status in response: '${status}'`);
91
93
  }
92
94
  }).catch((error) => {
93
- console.error('Error during port open:', error);
95
+ logger.error(`Error during port open: ${error}`);
94
96
  return;
95
97
  });
96
98
  }
@@ -110,12 +112,12 @@ class DiodeRPC {
110
112
  try {
111
113
  const ticketCommand = await this.connection.createTicketCommand();
112
114
  const ticketResponse = await this.connection.sendCommand(ticketCommand).catch((error) => {
113
- console.error('Error during ticket command:', error);
115
+ logger.error(`Error during ticket command: ${error}`);
114
116
  throw error;
115
117
  });
116
- console.log('Ticket updated:', ticketResponse);
118
+ logger.debug(`Ticket updated: ${makeReadable(ticketResponse)}`);
117
119
  } catch (error) {
118
- console.error('Error updating ticket:', error);
120
+ logger.error(`Error updating ticket: ${error}`);
119
121
  throw error;
120
122
  }
121
123
  return;
@@ -125,7 +127,7 @@ class DiodeRPC {
125
127
  throw new Error(`Unknown status in response: '${status}'`);
126
128
  }
127
129
  }).catch((error) => {
128
- console.error('Error during port send:', error);
130
+ logger.error(`Error during port send: ${error}`);
129
131
  return;
130
132
  });
131
133
  }
@@ -146,21 +148,21 @@ class DiodeRPC {
146
148
  throw new Error(`Unknown status in response: '${status}'`);
147
149
  }
148
150
  }).catch((error) => {
149
- console.error('Error during port close:', error);
151
+ logger.error(`Error during port close: ${error}`);
150
152
  return;
151
153
  });
152
154
  }
153
155
 
154
156
  sendError(sessionId, ref, error) {
155
157
  return this.connection.sendCommandWithSessionId(['response', ref, 'error', error], sessionId).catch((error) => {
156
- console.error('Error during send error:', error);
158
+ logger.error(`Error during send error: ${error}`);
157
159
  return;
158
160
  });
159
161
  }
160
162
 
161
163
  sendResponse(sessionId, ref, response) {
162
164
  return this.connection.sendCommandWithSessionId(['response', ref, response], sessionId).catch((error) => {
163
- console.error('Error during send response:', error);
165
+ logger.error(`Error during send response: ${error}`);
164
166
  return;
165
167
  });
166
168
  }
@@ -168,10 +170,10 @@ class DiodeRPC {
168
170
  async getEpoch() {
169
171
  const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
170
172
  if (this.epochCache.expiry && this.epochCache.expiry > currentTime) {
171
- console.log('Using cached epoch:', this.epochCache.epoch);
173
+ logger.debug(`Using cached epoch: ${this.epochCache.epoch}`);
172
174
  return this.epochCache.epoch;
173
175
  }
174
- console.log('Fetching new epoch. Expiry:', this.epochCache.expiry);
176
+ logger.debug(`Fetching new epoch. Expiry: ${this.epochCache.expiry}, Current time: ${currentTime}`);
175
177
  const blockPeak = await this.getBlockPeak();
176
178
  const blockHeader = await this.getBlockHeader(blockPeak);
177
179
 
@@ -205,4 +207,3 @@ class DiodeRPC {
205
207
  }
206
208
 
207
209
  module.exports = DiodeRPC;
208
-
package/utils.js CHANGED
@@ -1,5 +1,11 @@
1
1
  // utils.js
2
2
  const { Buffer } = require('buffer');
3
+ const logger = require('./logger');
4
+ const { KJUR } = require("jsrsasign");
5
+ const { KEYUTIL } = require("jsrsasign");
6
+ const fs = require('fs');
7
+ var path = require('path');
8
+
3
9
  function makeReadable(decodedMessage) {
4
10
  if (Array.isArray(decodedMessage)) {
5
11
  return decodedMessage.map((item) => makeReadable(item));
@@ -46,10 +52,10 @@ function parseRequestId(requestIdRaw) {
46
52
  }
47
53
 
48
54
  function parseResponseType(responseTypeRaw) {
49
- console.log('responseTypeRaw:', responseTypeRaw);
50
- console.log('Type of responseTypeRaw:', typeof responseTypeRaw);
51
- console.log('Instance of responseTypeRaw:', responseTypeRaw instanceof Uint8Array);
52
- console.log('Is Array:', Array.isArray(responseTypeRaw));
55
+ logger.debug(`responseTypeRaw: ${responseTypeRaw}`);
56
+ logger.debug(`Type of responseTypeRaw: ${typeof responseTypeRaw}`);
57
+ logger.debug(`Instance of responseTypeRaw: ${responseTypeRaw instanceof Uint8Array}`);
58
+ logger.debug(`Is Array: ${Array.isArray(responseTypeRaw)}`);
53
59
  if (responseTypeRaw instanceof Uint8Array || Buffer.isBuffer(responseTypeRaw)) {
54
60
  return Buffer.from(responseTypeRaw).toString('utf8');
55
61
  } else if (Array.isArray(responseTypeRaw)) {
@@ -74,4 +80,45 @@ function parseReason(reasonRaw) {
74
80
  }
75
81
  }
76
82
 
77
- module.exports = { makeReadable, parseRequestId, parseResponseType, parseReason };
83
+ function generateCert(path) {
84
+ var kp = KEYUTIL.generateKeypair("EC", "secp256k1");
85
+
86
+ var priv = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS8PRV");
87
+
88
+ pub = KEYUTIL.getPEM(kp.pubKeyObj, "PKCS8PUB");
89
+
90
+ var x = new KJUR.asn1.x509.Certificate({
91
+ version: 3,
92
+ serial: { int: 4 },
93
+ issuer: { str: "/CN=device" },
94
+ subject: { str: "/CN=device" },
95
+ sbjpubkey: kp.pubKeyObj,
96
+ ext: [
97
+ { extname: "basicConstraints", cA: false },
98
+ { extname: "keyUsage", critical: true, names: ["digitalSignature"] },
99
+ {
100
+ extname: "cRLDistributionPoints",
101
+ array: [{ fulluri: 'https://diode.io/' }]
102
+ }
103
+ ],
104
+ sigalg: "SHA256withECDSA",
105
+ cakey: kp.prvKeyObj
106
+ });
107
+
108
+
109
+ const pemFile = priv + x.getPEM();
110
+ ensureDirectoryExistence(path);
111
+
112
+ fs.writeFileSync(path, pemFile , 'utf8');
113
+ }
114
+
115
+ function ensureDirectoryExistence(filePath) {
116
+ var dirname = path.dirname(filePath);
117
+ if (fs.existsSync(dirname)) {
118
+ return true;
119
+ }
120
+ ensureDirectoryExistence(dirname);
121
+ fs.mkdirSync(dirname);
122
+ }
123
+
124
+ module.exports = { makeReadable, parseRequestId, parseResponseType, parseReason, generateCert };