diodejs 0.0.4 → 0.1.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/README.md +69 -8
- package/bindPort.js +43 -25
- package/connection.js +144 -90
- package/examples/portForwardTest.js +1 -1
- package/examples/publishAndBind.js +23 -0
- package/index.js +2 -1
- package/logger.js +28 -0
- package/package.json +5 -3
- package/publishPort.js +41 -30
- 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) {
|
|
@@ -12,8 +13,7 @@ class BindPort {
|
|
|
12
13
|
|
|
13
14
|
bind () {
|
|
14
15
|
const deviceId = Buffer.from(this.deviceIdHex, 'hex');
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
// Remove local clientSockets map and use the one from connection
|
|
17
17
|
const rpc = new DiodeRPC(this.connection);
|
|
18
18
|
|
|
19
19
|
// Listen for data events from the device
|
|
@@ -30,12 +30,17 @@ class BindPort {
|
|
|
30
30
|
const dataRef = Buffer.from(refRaw);
|
|
31
31
|
const data = Buffer.from(dataRaw);
|
|
32
32
|
|
|
33
|
-
// Find the associated client socket
|
|
34
|
-
const clientSocket =
|
|
33
|
+
// Find the associated client socket from connection
|
|
34
|
+
const clientSocket = this.connection.getClientSocket(dataRef);
|
|
35
35
|
if (clientSocket) {
|
|
36
36
|
clientSocket.write(data);
|
|
37
37
|
} else {
|
|
38
|
-
|
|
38
|
+
const connectionInfo = this.connection.getConnection(dataRef);
|
|
39
|
+
if (connectionInfo) {
|
|
40
|
+
logger.debug(`No client socket found for ref: ${dataRef.toString('hex')}, but connection exists for ${connectionInfo.host}:${connectionInfo.port}`);
|
|
41
|
+
} else {
|
|
42
|
+
logger.warn(`No client socket found for ref: ${dataRef.toString('hex')}`);
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
} else if (messageType === 'portclose') {
|
|
41
46
|
const refRaw = messageContent[1];
|
|
@@ -43,75 +48,88 @@ class BindPort {
|
|
|
43
48
|
const dataRef = Buffer.from(refRaw);
|
|
44
49
|
|
|
45
50
|
// Close the associated client socket
|
|
46
|
-
const clientSocket =
|
|
51
|
+
const clientSocket = this.connection.getClientSocket(dataRef);
|
|
47
52
|
if (clientSocket) {
|
|
48
53
|
clientSocket.end();
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
this.connection.deleteClientSocket(dataRef);
|
|
55
|
+
logger.info(`Port closed for ref: ${dataRef.toString('hex')}`);
|
|
51
56
|
}
|
|
52
57
|
} else {
|
|
53
|
-
|
|
58
|
+
if (messageType != 'portopen') {
|
|
59
|
+
logger.warn(`Unknown unsolicited message type: ${messageType}`);
|
|
60
|
+
}
|
|
54
61
|
}
|
|
55
62
|
});
|
|
56
63
|
|
|
57
64
|
// Set up local server
|
|
58
65
|
const server = net.createServer(async (clientSocket) => {
|
|
59
|
-
|
|
66
|
+
logger.info('Client connected to local server');
|
|
60
67
|
|
|
61
68
|
// Open a new port on the device for this client
|
|
62
69
|
let ref;
|
|
63
70
|
try {
|
|
64
71
|
ref = await rpc.portOpen(deviceId, this.targetPort, 'rw');
|
|
65
|
-
|
|
72
|
+
if (!ref) {
|
|
73
|
+
logger.error('Error opening port on device');
|
|
74
|
+
clientSocket.destroy();
|
|
75
|
+
return
|
|
76
|
+
} else {
|
|
77
|
+
logger.info(`Port opened on device with ref: ${ref.toString('hex')} for client`);
|
|
78
|
+
}
|
|
66
79
|
} catch (error) {
|
|
67
|
-
|
|
80
|
+
logger.error(`Error opening port on device: ${error}`);
|
|
68
81
|
clientSocket.destroy();
|
|
69
82
|
return;
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
// Store the client socket with the ref
|
|
73
|
-
|
|
85
|
+
// Store the client socket with the ref using connection's method
|
|
86
|
+
this.connection.addClientSocket(ref, clientSocket);
|
|
74
87
|
|
|
75
88
|
// When data is received from the client, send it to the device
|
|
76
89
|
clientSocket.on('data', async (data) => {
|
|
77
90
|
try {
|
|
78
91
|
await rpc.portSend(ref, data);
|
|
79
92
|
} catch (error) {
|
|
80
|
-
|
|
93
|
+
logger.error(`Error sending data to device: ${error}`);
|
|
81
94
|
clientSocket.destroy();
|
|
82
95
|
}
|
|
83
96
|
});
|
|
84
97
|
|
|
85
98
|
// Handle client socket closure
|
|
86
99
|
clientSocket.on('end', async () => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
logger.info('Client disconnected');
|
|
101
|
+
if (ref && this.connection.hasClientSocket(ref)) {
|
|
102
|
+
try {
|
|
103
|
+
await rpc.portClose(ref);
|
|
104
|
+
logger.info(`Port closed on device for ref: ${ref.toString('hex')}`);
|
|
105
|
+
this.connection.deleteClientSocket(ref);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.error(`Error closing port on device: ${error}`);
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
logger.warn('Ref is invalid or no longer in clientSockets.');
|
|
93
111
|
}
|
|
94
112
|
});
|
|
95
113
|
|
|
96
114
|
// Handle client socket errors
|
|
97
115
|
clientSocket.on('error', (err) => {
|
|
98
|
-
|
|
116
|
+
logger.error('Client socket error:', err);
|
|
99
117
|
});
|
|
100
118
|
});
|
|
101
119
|
|
|
102
120
|
server.listen(this.localPort, () => {
|
|
103
|
-
|
|
121
|
+
logger.info(`Local server listening on port ${this.localPort}`);
|
|
104
122
|
});
|
|
105
123
|
|
|
106
124
|
// Handle device disconnect
|
|
107
125
|
this.connection.on('end', () => {
|
|
108
|
-
|
|
126
|
+
logger.info('Disconnected from Diode.io server');
|
|
109
127
|
server.close();
|
|
110
128
|
});
|
|
111
129
|
|
|
112
130
|
// Handle connection errors
|
|
113
131
|
this.connection.on('error', (err) => {
|
|
114
|
-
|
|
132
|
+
logger.error(`Connection error: ${err}`);
|
|
115
133
|
server.close();
|
|
116
134
|
});
|
|
117
135
|
}
|
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,17 @@ 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
|
+
// Add maps for storing client sockets and connections
|
|
33
|
+
this.clientSockets = new Map(); // For BindPort
|
|
34
|
+
this.connections = new Map(); // For PublishPort
|
|
35
|
+
|
|
36
|
+
// Check if certPath exists, if not generate the certificate
|
|
37
|
+
if (!fs.existsSync(this.certPath)) {
|
|
38
|
+
generateCert(this.certPath);
|
|
39
|
+
}
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
connect() {
|
|
@@ -40,7 +52,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
40
52
|
};
|
|
41
53
|
|
|
42
54
|
this.socket = tls.connect(this.port, this.host, options, async () => {
|
|
43
|
-
|
|
55
|
+
logger.info('Connected to Diode.io server');
|
|
44
56
|
// Set keep-alive to prevent connection timeout forever
|
|
45
57
|
this.socket.setKeepAlive(true, 0);
|
|
46
58
|
|
|
@@ -48,33 +60,55 @@ class DiodeConnection extends EventEmitter {
|
|
|
48
60
|
try {
|
|
49
61
|
const ticketCommand = await this.createTicketCommand();
|
|
50
62
|
const response = await this.sendCommand(ticketCommand).catch(reject);
|
|
51
|
-
|
|
63
|
+
logger.info(`Ticket accepted: ${makeReadable(response)}`);
|
|
52
64
|
resolve();
|
|
53
65
|
} catch (error) {
|
|
54
|
-
|
|
66
|
+
logger.error(`Error sending ticket: ${error}`);
|
|
55
67
|
reject(error);
|
|
56
68
|
}
|
|
57
69
|
});
|
|
58
70
|
|
|
59
71
|
this.socket.on('data', (data) => {
|
|
72
|
+
logger.debug(`Received data: ${data.toString('hex')}`);
|
|
60
73
|
try {
|
|
61
74
|
this._handleData(data);
|
|
62
75
|
} catch (error) {
|
|
63
|
-
|
|
76
|
+
logger.error(`Error handling data: ${error}`);
|
|
64
77
|
}
|
|
65
78
|
});
|
|
66
79
|
this.socket.on('error', (err) => {
|
|
67
|
-
|
|
80
|
+
logger.error(`Connection error: ${err}`);
|
|
68
81
|
reject(err);
|
|
69
82
|
});
|
|
70
|
-
this.socket.on('end', () =>
|
|
83
|
+
this.socket.on('end', () => logger.info('Disconnected from server'));
|
|
71
84
|
});
|
|
72
85
|
}
|
|
73
86
|
|
|
87
|
+
_ensureConnected() {
|
|
88
|
+
if (this.socket && !this.socket.destroyed) {
|
|
89
|
+
return Promise.resolve();
|
|
90
|
+
}
|
|
91
|
+
if (this.connectPromise) {
|
|
92
|
+
return this.connectPromise;
|
|
93
|
+
}
|
|
94
|
+
this.isReconnecting = true;
|
|
95
|
+
this.connectPromise = this.connect()
|
|
96
|
+
.then(() => {
|
|
97
|
+
this.isReconnecting = false;
|
|
98
|
+
this.connectPromise = null;
|
|
99
|
+
})
|
|
100
|
+
.catch((err) => {
|
|
101
|
+
this.isReconnecting = false;
|
|
102
|
+
this.connectPromise = null;
|
|
103
|
+
throw err;
|
|
104
|
+
});
|
|
105
|
+
return this.connectPromise;
|
|
106
|
+
}
|
|
107
|
+
|
|
74
108
|
_handleData(data) {
|
|
75
109
|
// Append new data to the receive buffer
|
|
76
110
|
this.receiveBuffer = Buffer.concat([this.receiveBuffer, data]);
|
|
77
|
-
|
|
111
|
+
logger.debug(`Received data: ${data.toString('hex')}`);
|
|
78
112
|
|
|
79
113
|
let offset = 0;
|
|
80
114
|
while (offset + 2 <= this.receiveBuffer.length) {
|
|
@@ -92,7 +126,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
92
126
|
|
|
93
127
|
try {
|
|
94
128
|
const decodedMessage = RLP.decode(Uint8Array.from(messageBuffer));
|
|
95
|
-
|
|
129
|
+
logger.debug(`Decoded message: ${makeReadable(decodedMessage)}`);
|
|
96
130
|
|
|
97
131
|
if (Array.isArray(decodedMessage) && decodedMessage.length > 1) {
|
|
98
132
|
const requestIdRaw = decodedMessage[0];
|
|
@@ -102,8 +136,8 @@ class DiodeConnection extends EventEmitter {
|
|
|
102
136
|
const requestId = parseRequestId(requestIdRaw);
|
|
103
137
|
|
|
104
138
|
// Debug statements
|
|
105
|
-
|
|
106
|
-
|
|
139
|
+
logger.debug(`requestIdRaw: ${requestIdRaw}`);
|
|
140
|
+
logger.debug(`Parsed requestId: ${requestId}`);
|
|
107
141
|
|
|
108
142
|
if (requestId !== null && this.pendingRequests.has(requestId)) {
|
|
109
143
|
// This is a response to a pending request
|
|
@@ -111,16 +145,14 @@ class DiodeConnection extends EventEmitter {
|
|
|
111
145
|
const responseRaw = responseData[0];
|
|
112
146
|
|
|
113
147
|
// Debug statements
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.log('Instance of responseTypeRaw:', responseTypeRaw instanceof Uint8Array);
|
|
117
|
-
console.log('Is Array:', Array.isArray(responseTypeRaw));
|
|
148
|
+
logger.debug(`responseTypeRaw: ${responseTypeRaw}`);
|
|
149
|
+
logger.debug(`Type of responseTypeRaw: ${typeof responseTypeRaw}`);
|
|
118
150
|
|
|
119
151
|
// Parse responseType
|
|
120
152
|
const responseType = parseResponseType(responseTypeRaw);
|
|
121
153
|
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
logger.debug(`Received response for requestId: ${requestId}`);
|
|
155
|
+
logger.debug(`Response Type: '${responseType}'`);
|
|
124
156
|
|
|
125
157
|
const { resolve, reject } = this.pendingRequests.get(requestId);
|
|
126
158
|
try{
|
|
@@ -146,20 +178,20 @@ class DiodeConnection extends EventEmitter {
|
|
|
146
178
|
resolve(responseData);
|
|
147
179
|
}
|
|
148
180
|
} catch (error) {
|
|
149
|
-
|
|
181
|
+
logger.error(`Error handling response: ${error}`);
|
|
150
182
|
}
|
|
151
183
|
this.pendingRequests.delete(requestId);
|
|
152
184
|
} else {
|
|
153
185
|
// This is an unsolicited message
|
|
154
|
-
|
|
186
|
+
logger.debug(`Received unsolicited message: ${makeReadable(decodedMessage)}`);
|
|
155
187
|
this.emit('unsolicited', decodedMessage);
|
|
156
188
|
}
|
|
157
189
|
} else {
|
|
158
190
|
// Invalid message format
|
|
159
|
-
|
|
191
|
+
logger.error(`Invalid message format: ${makeReadable(decodedMessage)}`);
|
|
160
192
|
}
|
|
161
193
|
} catch (error) {
|
|
162
|
-
|
|
194
|
+
logger.error(`Error decoding message: ${error}`);
|
|
163
195
|
}
|
|
164
196
|
}
|
|
165
197
|
|
|
@@ -190,67 +222,55 @@ class DiodeConnection extends EventEmitter {
|
|
|
190
222
|
|
|
191
223
|
sendCommand(commandArray) {
|
|
192
224
|
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);
|
|
225
|
+
this._ensureConnected().then(() => {
|
|
226
|
+
const requestId = this._getNextRequestId();
|
|
227
|
+
// Build the message as [requestId, [commandArray]]
|
|
228
|
+
const commandWithId = [requestId, commandArray];
|
|
229
|
+
|
|
230
|
+
// Store the promise callbacks to resolve/reject later
|
|
231
|
+
this.pendingRequests.set(requestId, { resolve, reject });
|
|
232
|
+
|
|
233
|
+
const commandBuffer = RLP.encode(commandWithId);
|
|
234
|
+
const byteLength = Buffer.byteLength(commandBuffer);
|
|
235
|
+
|
|
236
|
+
// Create a 2-byte length buffer
|
|
237
|
+
const lengthBuffer = Buffer.alloc(2);
|
|
238
|
+
lengthBuffer.writeUInt16BE(byteLength, 0);
|
|
239
|
+
|
|
240
|
+
const message = Buffer.concat([lengthBuffer, commandBuffer]);
|
|
241
|
+
|
|
242
|
+
logger.debug(`Sending command with requestId ${requestId}: ${commandArray}`);
|
|
243
|
+
logger.debug(`Command buffer: ${message.toString('hex')}`);
|
|
244
|
+
|
|
245
|
+
this.socket.write(message);
|
|
246
|
+
}).catch(reject);
|
|
221
247
|
});
|
|
222
248
|
}
|
|
223
249
|
|
|
224
250
|
sendCommandWithSessionId(commandArray, sessionId) {
|
|
225
251
|
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);
|
|
252
|
+
this._ensureConnected().then(() => {
|
|
253
|
+
const requestId = sessionId;
|
|
254
|
+
// Build the message as [requestId, [commandArray]]
|
|
255
|
+
const commandWithId = [requestId, commandArray];
|
|
256
|
+
|
|
257
|
+
// Store the promise callbacks to resolve/reject later
|
|
258
|
+
this.pendingRequests.set(requestId, { resolve, reject });
|
|
259
|
+
|
|
260
|
+
const commandBuffer = RLP.encode(commandWithId);
|
|
261
|
+
const byteLength = Buffer.byteLength(commandBuffer);
|
|
262
|
+
|
|
263
|
+
// Create a 2-byte length buffer
|
|
264
|
+
const lengthBuffer = Buffer.alloc(2);
|
|
265
|
+
lengthBuffer.writeUInt16BE(byteLength, 0);
|
|
266
|
+
|
|
267
|
+
const message = Buffer.concat([lengthBuffer, commandBuffer]);
|
|
268
|
+
|
|
269
|
+
logger.debug(`Sending command with requestId ${requestId}: ${commandArray}`);
|
|
270
|
+
logger.debug(`Command buffer: ${message.toString('hex')}`);
|
|
271
|
+
|
|
272
|
+
this.socket.write(message);
|
|
273
|
+
}).catch(reject);
|
|
254
274
|
});
|
|
255
275
|
}
|
|
256
276
|
|
|
@@ -300,7 +320,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
300
320
|
|
|
301
321
|
const ecPrivateKey = ECPrivateKeyASN.decode(privateKeyOctetString, 'der');
|
|
302
322
|
privateKeyBytes = ecPrivateKey.privateKey;
|
|
303
|
-
|
|
323
|
+
logger.debug(`Private key bytes: ${privateKeyBytes.toString('hex')}`);
|
|
304
324
|
} else {
|
|
305
325
|
throw new Error('Unsupported key format. Expected EC PRIVATE KEY or PRIVATE KEY in PEM format.');
|
|
306
326
|
}
|
|
@@ -317,10 +337,10 @@ class DiodeConnection extends EventEmitter {
|
|
|
317
337
|
const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
|
|
318
338
|
const address = '0x' + addressBuffer.toString('hex');
|
|
319
339
|
|
|
320
|
-
|
|
340
|
+
logger.info(`Ethereum address: ${address}`);
|
|
321
341
|
return address;
|
|
322
342
|
} catch (error) {
|
|
323
|
-
|
|
343
|
+
logger.error(`Error extracting Ethereum address: ${error}`);
|
|
324
344
|
throw error;
|
|
325
345
|
}
|
|
326
346
|
}
|
|
@@ -336,15 +356,15 @@ class DiodeConnection extends EventEmitter {
|
|
|
336
356
|
? serverCert.pubkey
|
|
337
357
|
: Buffer.from(serverCert.pubkey);
|
|
338
358
|
|
|
339
|
-
|
|
359
|
+
logger.debug(`Public key Server: ${publicKeyBuffer.toString('hex')}`);
|
|
340
360
|
|
|
341
361
|
const addressBuffer = ethUtil.pubToAddress(publicKeyBuffer, true);
|
|
342
362
|
const address = '0x' + addressBuffer.toString('hex');
|
|
343
363
|
|
|
344
|
-
|
|
364
|
+
logger.info(`Server Ethereum address: ${address}`);
|
|
345
365
|
return address;
|
|
346
366
|
} catch (error) {
|
|
347
|
-
|
|
367
|
+
logger.error(`Error extracting server Ethereum address: ${error}`);
|
|
348
368
|
throw error;
|
|
349
369
|
}
|
|
350
370
|
}
|
|
@@ -448,7 +468,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
448
468
|
|
|
449
469
|
return privateKeyBytes;
|
|
450
470
|
} catch (error) {
|
|
451
|
-
|
|
471
|
+
logger.error(`Error extracting Ethereum address: ${error}`);
|
|
452
472
|
throw error;
|
|
453
473
|
}
|
|
454
474
|
}
|
|
@@ -475,17 +495,17 @@ class DiodeConnection extends EventEmitter {
|
|
|
475
495
|
// Convert each element in dataToSign to bytes32 and concatenate them
|
|
476
496
|
const encodedData = Buffer.concat(dataToSign.map(item => abi.rawEncode(['bytes32'], [item])));
|
|
477
497
|
|
|
478
|
-
|
|
498
|
+
logger.debug(`Encoded data: ${encodedData.toString('hex')}`);
|
|
479
499
|
|
|
480
|
-
|
|
500
|
+
logger.debug(`Data to sign: ${makeReadable(dataToSign)}`);
|
|
481
501
|
|
|
482
502
|
|
|
483
503
|
// Sign the data
|
|
484
504
|
const privateKey = this.getPrivateKey();
|
|
485
505
|
const msgHash = ethUtil.keccak256(encodedData);
|
|
486
|
-
|
|
506
|
+
logger.debug(`Message hash: ${msgHash.toString('hex')}`);
|
|
487
507
|
const signature = secp256k1.ecdsaSign(msgHash, privateKey);
|
|
488
|
-
|
|
508
|
+
logger.debug(`Signature: ${signature.signature.toString('hex')}`);
|
|
489
509
|
|
|
490
510
|
const signatureBuffer = Buffer.concat([
|
|
491
511
|
ethUtil.toBuffer([signature.recid]),
|
|
@@ -519,7 +539,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
519
539
|
localAddress,
|
|
520
540
|
epoch
|
|
521
541
|
);
|
|
522
|
-
|
|
542
|
+
logger.debug(`Signature hex: ${signature.toString('hex')}`);
|
|
523
543
|
|
|
524
544
|
|
|
525
545
|
// Construct the ticket command
|
|
@@ -546,6 +566,40 @@ class DiodeConnection extends EventEmitter {
|
|
|
546
566
|
close() {
|
|
547
567
|
this.socket.end();
|
|
548
568
|
}
|
|
569
|
+
|
|
570
|
+
// Client sockets management methods (for BindPort)
|
|
571
|
+
addClientSocket(ref, socket) {
|
|
572
|
+
this.clientSockets.set(ref.toString('hex'), socket);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
getClientSocket(ref) {
|
|
576
|
+
return this.clientSockets.get(ref.toString('hex'));
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
deleteClientSocket(ref) {
|
|
580
|
+
return this.clientSockets.delete(ref.toString('hex'));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
hasClientSocket(ref) {
|
|
584
|
+
return this.clientSockets.has(ref.toString('hex'));
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Connections management methods (for PublishPort)
|
|
588
|
+
addConnection(ref, connectionInfo) {
|
|
589
|
+
this.connections.set(ref.toString('hex'), connectionInfo);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
getConnection(ref) {
|
|
593
|
+
return this.connections.get(ref.toString('hex'));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
deleteConnection(ref) {
|
|
597
|
+
return this.connections.delete(ref.toString('hex'));
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
hasConnection(ref) {
|
|
601
|
+
return this.connections.has(ref.toString('hex'));
|
|
602
|
+
}
|
|
549
603
|
}
|
|
550
604
|
|
|
551
605
|
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const DiodeConnection = require('../connection')
|
|
2
|
+
const PublishPort = require('../publishPort')
|
|
3
|
+
const BindPort = require('../bindPort')
|
|
4
|
+
|
|
5
|
+
const host = 'us2.prenet.diode.io';
|
|
6
|
+
const port = 41046;
|
|
7
|
+
const certPath = 'device_certificate.pem';
|
|
8
|
+
|
|
9
|
+
const connection = new DiodeConnection(host, port, certPath);
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
await connection.connect();
|
|
13
|
+
const publishedPorts = [8080]; // Ports you want to publish
|
|
14
|
+
const publishPort = new PublishPort(connection, publishedPorts, certPath);
|
|
15
|
+
|
|
16
|
+
const portForward = new BindPort(connection, 3002, 8080, "5365baf29cb7ab58de588dfc448913cb609283e2");
|
|
17
|
+
portForward.bind();
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
main();
|
|
22
|
+
|
|
23
|
+
|
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.1.0",
|
|
4
4
|
"description": "A JavaScript client for interacting with the Diode network. It provides functionalities to bind and publish ports, send RPC commands, and handle responses.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,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,8 +39,8 @@ class PublishPort extends EventEmitter {
|
|
|
37
39
|
constructor(connection, publishedPorts, certPath) {
|
|
38
40
|
super();
|
|
39
41
|
this.connection = connection;
|
|
40
|
-
this.publishedPorts = publishedPorts; //
|
|
41
|
-
|
|
42
|
+
this.publishedPorts = new Set(publishedPorts); // Convert array to a Set
|
|
43
|
+
// Remove local connections map and use the one from connection
|
|
42
44
|
this.startListening();
|
|
43
45
|
this.rpc = new DiodeRPC(connection);
|
|
44
46
|
this.certPath = certPath;
|
|
@@ -58,7 +60,9 @@ class PublishPort extends EventEmitter {
|
|
|
58
60
|
} else if (messageType === 'portclose') {
|
|
59
61
|
this.handlePortClose(sessionIdRaw, messageContent);
|
|
60
62
|
} else {
|
|
61
|
-
|
|
63
|
+
if (messageType != 'data') {
|
|
64
|
+
logger.warn(`Unknown unsolicited message type: ${messageType}`);
|
|
65
|
+
}
|
|
62
66
|
}
|
|
63
67
|
});
|
|
64
68
|
}
|
|
@@ -74,7 +78,7 @@ class PublishPort extends EventEmitter {
|
|
|
74
78
|
const ref = Buffer.from(refRaw);
|
|
75
79
|
const deviceId = Buffer.from(deviceIdRaw).toString('hex');
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
logger.info(`Received portopen request for portString ${portString} with ref ${ref.toString('hex')} from device ${deviceId}`);
|
|
78
82
|
|
|
79
83
|
// Extract protocol and port number from portString
|
|
80
84
|
var protocol = 'tcp';
|
|
@@ -92,8 +96,8 @@ class PublishPort extends EventEmitter {
|
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
// Check if the port is published
|
|
95
|
-
if (!this.publishedPorts.
|
|
96
|
-
|
|
99
|
+
if (!this.publishedPorts.has(port)) { // Use .has() instead of .includes()
|
|
100
|
+
logger.warn(`Port ${port} is not published. Rejecting request.`);
|
|
97
101
|
// Send error response
|
|
98
102
|
this.rpc.sendError(sessionId, ref, 'Port is not published');
|
|
99
103
|
return;
|
|
@@ -107,7 +111,7 @@ class PublishPort extends EventEmitter {
|
|
|
107
111
|
} else if (protocol === 'udp') {
|
|
108
112
|
this.handleUDPConnection(sessionId, ref, port);
|
|
109
113
|
} else {
|
|
110
|
-
|
|
114
|
+
logger.warn(`Unsupported protocol: ${protocol}`);
|
|
111
115
|
this.rpc.sendError(sessionId, ref, `Unsupported protocol: ${protocol}`);
|
|
112
116
|
}
|
|
113
117
|
}
|
|
@@ -122,17 +126,17 @@ class PublishPort extends EventEmitter {
|
|
|
122
126
|
});
|
|
123
127
|
|
|
124
128
|
localSocket.on('end', () => {
|
|
125
|
-
|
|
129
|
+
logger.info(`Local service disconnected`);
|
|
126
130
|
// Send portclose message to Diode
|
|
127
131
|
this.rpc.portClose(ref);
|
|
128
|
-
this.
|
|
132
|
+
this.connection.deleteConnection(ref);
|
|
129
133
|
});
|
|
130
134
|
|
|
131
135
|
localSocket.on('error', (err) => {
|
|
132
|
-
|
|
136
|
+
logger.error(`Error with local service: ${err}`);
|
|
133
137
|
// Send portclose message to Diode
|
|
134
138
|
this.rpc.portClose(ref);
|
|
135
|
-
this.
|
|
139
|
+
this.connection.deleteConnection(ref);
|
|
136
140
|
});
|
|
137
141
|
}
|
|
138
142
|
}
|
|
@@ -140,7 +144,7 @@ class PublishPort extends EventEmitter {
|
|
|
140
144
|
handleTCPConnection(sessionId, ref, port) {
|
|
141
145
|
// Create a TCP connection to the local service on the specified port
|
|
142
146
|
const localSocket = net.connect({ port: port }, () => {
|
|
143
|
-
|
|
147
|
+
logger.info(`Connected to local TCP service on port ${port}`);
|
|
144
148
|
// Send success response
|
|
145
149
|
this.rpc.sendResponse(sessionId, ref, 'ok');
|
|
146
150
|
});
|
|
@@ -148,8 +152,8 @@ class PublishPort extends EventEmitter {
|
|
|
148
152
|
// Handle data, end, and error events
|
|
149
153
|
this.setupLocalSocketHandlers(localSocket, ref, 'tcp');
|
|
150
154
|
|
|
151
|
-
// Store the local socket with the ref
|
|
152
|
-
this.
|
|
155
|
+
// Store the local socket with the ref using connection's method
|
|
156
|
+
this.connection.addConnection(ref, { socket: localSocket, protocol: 'tcp' });
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
handleTLSConnection(sessionId, ref, port) {
|
|
@@ -175,7 +179,7 @@ class PublishPort extends EventEmitter {
|
|
|
175
179
|
|
|
176
180
|
// Connect to the local service (TCP or TLS as needed)
|
|
177
181
|
const localSocket = net.connect({ port: port }, () => {
|
|
178
|
-
|
|
182
|
+
logger.info(`Connected to local TCP service on port ${port}`);
|
|
179
183
|
// Send success response
|
|
180
184
|
this.rpc.sendResponse(sessionId, ref, 'ok');
|
|
181
185
|
});
|
|
@@ -185,18 +189,18 @@ class PublishPort extends EventEmitter {
|
|
|
185
189
|
|
|
186
190
|
// Handle errors and cleanup
|
|
187
191
|
tlsSocket.on('error', (err) => {
|
|
188
|
-
|
|
192
|
+
logger.error(`TLS Socket error: ${err}`);
|
|
189
193
|
this.rpc.portClose(ref);
|
|
190
|
-
this.
|
|
194
|
+
this.connection.deleteConnection(ref);
|
|
191
195
|
});
|
|
192
196
|
|
|
193
197
|
tlsSocket.on('close', () => {
|
|
194
198
|
console.log('TLS Socket closed');
|
|
195
|
-
this.
|
|
199
|
+
this.connection.deleteConnection(ref);
|
|
196
200
|
});
|
|
197
201
|
|
|
198
|
-
// Store the connection info
|
|
199
|
-
this.
|
|
202
|
+
// Store the connection info using connection's method
|
|
203
|
+
this.connection.addConnection(ref, {
|
|
200
204
|
diodeSocket,
|
|
201
205
|
tlsSocket,
|
|
202
206
|
localSocket,
|
|
@@ -214,13 +218,15 @@ class PublishPort extends EventEmitter {
|
|
|
214
218
|
// Send success response
|
|
215
219
|
this.rpc.sendResponse(sessionId, ref, 'ok');
|
|
216
220
|
|
|
217
|
-
// Store the connection info
|
|
218
|
-
this.
|
|
221
|
+
// Store the connection info using connection's method
|
|
222
|
+
this.connection.addConnection(ref, {
|
|
219
223
|
socket: localSocket,
|
|
220
224
|
protocol: 'udp',
|
|
221
225
|
remoteInfo,
|
|
222
226
|
});
|
|
223
227
|
|
|
228
|
+
logger.info(`UDP connection set up on port ${port}`);
|
|
229
|
+
|
|
224
230
|
// Handle messages from the local UDP service
|
|
225
231
|
localSocket.on('message', (msg, rinfo) => {
|
|
226
232
|
//need to add 4 bytes of data length to the beginning of the message but it's Big Endian
|
|
@@ -232,9 +238,9 @@ class PublishPort extends EventEmitter {
|
|
|
232
238
|
});
|
|
233
239
|
|
|
234
240
|
localSocket.on('error', (err) => {
|
|
235
|
-
|
|
241
|
+
logger.error(`UDP Socket error: ${err}`);
|
|
236
242
|
this.rpc.portClose(ref);
|
|
237
|
-
this.
|
|
243
|
+
this.connection.deleteConnection(ref);
|
|
238
244
|
});
|
|
239
245
|
}
|
|
240
246
|
|
|
@@ -246,7 +252,7 @@ class PublishPort extends EventEmitter {
|
|
|
246
252
|
const ref = Buffer.from(refRaw);
|
|
247
253
|
const data = Buffer.from(dataRaw)//.slice(4);
|
|
248
254
|
|
|
249
|
-
const connectionInfo = this.
|
|
255
|
+
const connectionInfo = this.connection.getConnection(ref);
|
|
250
256
|
if (connectionInfo) {
|
|
251
257
|
const { socket: localSocket, protocol, remoteInfo } = connectionInfo;
|
|
252
258
|
|
|
@@ -273,8 +279,13 @@ class PublishPort extends EventEmitter {
|
|
|
273
279
|
diodeSocket.pushData(data);
|
|
274
280
|
}
|
|
275
281
|
} else {
|
|
276
|
-
|
|
277
|
-
|
|
282
|
+
const clientSocket = this.connection.getClientSocket(ref);
|
|
283
|
+
if (clientSocket) {
|
|
284
|
+
logger.debug(`No local connection found for ref: ${ref.toString('hex')}, but client socket exists`);
|
|
285
|
+
} else {
|
|
286
|
+
logger.warn(`No local connection found for ref ${ref.toString('hex')}. Sending portclose.`);
|
|
287
|
+
this.rpc.sendError(sessionId, ref, 'No local connection found');
|
|
288
|
+
}
|
|
278
289
|
}
|
|
279
290
|
}
|
|
280
291
|
|
|
@@ -283,9 +294,9 @@ class PublishPort extends EventEmitter {
|
|
|
283
294
|
const sessionId = Buffer.from(sessionIdRaw);
|
|
284
295
|
const ref = Buffer.from(refRaw);
|
|
285
296
|
|
|
286
|
-
|
|
297
|
+
logger.info(`Received portclose for ref ${ref.toString('hex')}`);
|
|
287
298
|
|
|
288
|
-
const connectionInfo = this.
|
|
299
|
+
const connectionInfo = this.connection.getConnection(ref);
|
|
289
300
|
if (connectionInfo) {
|
|
290
301
|
const { diodeSocket, tlsSocket, socket: localSocket } = connectionInfo;
|
|
291
302
|
// End all sockets
|
|
@@ -298,7 +309,7 @@ class PublishPort extends EventEmitter {
|
|
|
298
309
|
localSocket.end();
|
|
299
310
|
}
|
|
300
311
|
}
|
|
301
|
-
this.
|
|
312
|
+
this.connection.deleteConnection(ref);
|
|
302
313
|
}
|
|
303
314
|
}
|
|
304
315
|
}
|
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 };
|