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 +69 -8
- package/bindPort.js +29 -17
- package/connection.js +106 -90
- package/examples/portForwardTest.js +1 -1
- package/index.js +2 -1
- package/logger.js +28 -0
- package/package.json +5 -3
- package/publishPort.js +18 -14
- package/rpc.js +16 -15
- package/utils.js +52 -5
package/README.md
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DiodeJs
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
|
-
`
|
|
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
|
-
|
|
11
|
+
### Quick Start
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
+
logger.info(`Port closed for ref: ${dataRef.toString('hex')}`);
|
|
51
52
|
}
|
|
52
53
|
} else {
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
clientSockets.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
110
|
+
logger.error('Client socket error:', err);
|
|
99
111
|
});
|
|
100
112
|
});
|
|
101
113
|
|
|
102
114
|
server.listen(this.localPort, () => {
|
|
103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
logger.info(`Ticket accepted: ${makeReadable(response)}`);
|
|
52
60
|
resolve();
|
|
53
61
|
} catch (error) {
|
|
54
|
-
|
|
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
|
-
|
|
72
|
+
logger.error(`Error handling data: ${error}`);
|
|
64
73
|
}
|
|
65
74
|
});
|
|
66
75
|
this.socket.on('error', (err) => {
|
|
67
|
-
|
|
76
|
+
logger.error(`Connection error: ${err}`);
|
|
68
77
|
reject(err);
|
|
69
78
|
});
|
|
70
|
-
this.socket.on('end', () =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
187
|
+
logger.error(`Invalid message format: ${makeReadable(decodedMessage)}`);
|
|
160
188
|
}
|
|
161
189
|
} catch (error) {
|
|
162
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
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
|
-
|
|
336
|
+
logger.info(`Ethereum address: ${address}`);
|
|
321
337
|
return address;
|
|
322
338
|
} catch (error) {
|
|
323
|
-
|
|
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
|
-
|
|
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
|
-
|
|
360
|
+
logger.info(`Server Ethereum address: ${address}`);
|
|
345
361
|
return address;
|
|
346
362
|
} catch (error) {
|
|
347
|
-
|
|
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
|
-
|
|
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
|
-
|
|
494
|
+
logger.debug(`Encoded data: ${encodedData.toString('hex')}`);
|
|
479
495
|
|
|
480
|
-
|
|
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
|
-
|
|
502
|
+
logger.debug(`Message hash: ${msgHash.toString('hex')}`);
|
|
487
503
|
const signature = secp256k1.ecdsaSign(msgHash, privateKey);
|
|
488
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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; //
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
+
logger.error(`Error during ticket command: ${error}`);
|
|
114
116
|
throw error;
|
|
115
117
|
});
|
|
116
|
-
|
|
118
|
+
logger.debug(`Ticket updated: ${makeReadable(ticketResponse)}`);
|
|
117
119
|
} catch (error) {
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
173
|
+
logger.debug(`Using cached epoch: ${this.epochCache.epoch}`);
|
|
172
174
|
return this.epochCache.epoch;
|
|
173
175
|
}
|
|
174
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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 };
|