diodejs 0.0.3
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/.gitattributes +2 -0
- package/.github/workflows/main.yml +33 -0
- package/LICENSE +674 -0
- package/README.md +98 -0
- package/bindPort.js +120 -0
- package/connection.js +552 -0
- package/examples/RPCTest.js +29 -0
- package/examples/portForwardTest.js +16 -0
- package/examples/publishPortTest.js +19 -0
- package/index.js +7 -0
- package/package.json +28 -0
- package/publishPort.js +295 -0
- package/rpc.js +178 -0
- package/testServers/udpTest.js +45 -0
- package/utils.js +77 -0
package/publishPort.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// publishPort.js
|
|
2
|
+
|
|
3
|
+
const net = require('net');
|
|
4
|
+
const tls = require('tls');
|
|
5
|
+
const dgram = require('dgram');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const { Buffer } = require('buffer');
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
const { Duplex } = require('stream');
|
|
10
|
+
const DiodeRPC = require('./rpc');
|
|
11
|
+
|
|
12
|
+
class DiodeSocket extends Duplex {
|
|
13
|
+
constructor(ref, rpc) {
|
|
14
|
+
super();
|
|
15
|
+
this.ref = ref;
|
|
16
|
+
this.rpc = rpc;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_write(chunk, encoding, callback) {
|
|
20
|
+
// Send data to the Diode client via portSend
|
|
21
|
+
this.rpc.portSend(this.ref, chunk)
|
|
22
|
+
.then(() => callback())
|
|
23
|
+
.catch((err) => callback(err));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_read(size) {
|
|
27
|
+
// No need to implement this method
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Method to push data received from Diode client
|
|
31
|
+
pushData(data) {
|
|
32
|
+
this.push(data);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class PublishPort extends EventEmitter {
|
|
37
|
+
constructor(connection, publishedPorts, certPath) {
|
|
38
|
+
super();
|
|
39
|
+
this.connection = connection;
|
|
40
|
+
this.publishedPorts = publishedPorts; // Array of ports to publish
|
|
41
|
+
this.connections = new Map(); // Map to store active connections
|
|
42
|
+
this.startListening();
|
|
43
|
+
this.rpc = new DiodeRPC(connection);
|
|
44
|
+
this.certPath = certPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
startListening() {
|
|
48
|
+
// Listen for unsolicited messages from the connection
|
|
49
|
+
this.connection.on('unsolicited', (message) => {
|
|
50
|
+
const [sessionIdRaw, messageContent] = message;
|
|
51
|
+
const messageTypeRaw = messageContent[0];
|
|
52
|
+
const messageType = Buffer.from(messageTypeRaw).toString('utf8');
|
|
53
|
+
|
|
54
|
+
if (messageType === 'portopen') {
|
|
55
|
+
this.handlePortOpen(sessionIdRaw, messageContent);
|
|
56
|
+
} else if (messageType === 'portsend') {
|
|
57
|
+
this.handlePortSend(sessionIdRaw, messageContent);
|
|
58
|
+
} else if (messageType === 'portclose') {
|
|
59
|
+
this.handlePortClose(sessionIdRaw, messageContent);
|
|
60
|
+
} else {
|
|
61
|
+
console.warn(`Unknown unsolicited message type: ${messageType}`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
handlePortOpen(sessionIdRaw, messageContent) {
|
|
67
|
+
// messageContent: ['portopen', portString, ref, deviceId]
|
|
68
|
+
const portStringRaw = messageContent[1];
|
|
69
|
+
const refRaw = messageContent[2];
|
|
70
|
+
const deviceIdRaw = messageContent[3];
|
|
71
|
+
|
|
72
|
+
const sessionId = Buffer.from(sessionIdRaw);
|
|
73
|
+
const portString = Buffer.from(portStringRaw).toString('utf8');
|
|
74
|
+
const ref = Buffer.from(refRaw);
|
|
75
|
+
const deviceId = Buffer.from(deviceIdRaw).toString('hex');
|
|
76
|
+
|
|
77
|
+
console.log(`Received portopen request for portString ${portString} with ref ${ref.toString('hex')} from device ${deviceId}`);
|
|
78
|
+
|
|
79
|
+
// Extract protocol and port number from portString
|
|
80
|
+
const [protocol, portStr] = portString.split(':');
|
|
81
|
+
const port = parseInt(portStr, 10);
|
|
82
|
+
|
|
83
|
+
// Check if the port is published
|
|
84
|
+
if (!this.publishedPorts.includes(port)) {
|
|
85
|
+
console.warn(`Port ${port} is not published. Rejecting request.`);
|
|
86
|
+
// Send error response
|
|
87
|
+
this.rpc.sendError(sessionId, ref, 'Port is not published');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Handle based on protocol
|
|
92
|
+
if (protocol === 'tcp') {
|
|
93
|
+
this.handleTCPConnection(sessionId, ref, port);
|
|
94
|
+
} else if (protocol === 'tls') {
|
|
95
|
+
this.handleTLSConnection(sessionId, ref, port);
|
|
96
|
+
} else if (protocol === 'udp') {
|
|
97
|
+
this.handleUDPConnection(sessionId, ref, port);
|
|
98
|
+
} else {
|
|
99
|
+
console.warn(`Unsupported protocol: ${protocol}`);
|
|
100
|
+
this.rpc.sendError(sessionId, ref, `Unsupported protocol: ${protocol}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
setupLocalSocketHandlers(localSocket, ref, protocol) {
|
|
105
|
+
if (protocol === 'udp') {
|
|
106
|
+
|
|
107
|
+
} else {
|
|
108
|
+
localSocket.on('data', (data) => {
|
|
109
|
+
// When data is received from the local service, send it back via Diode
|
|
110
|
+
this.rpc.portSend(ref, data);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
localSocket.on('end', () => {
|
|
114
|
+
console.log(`Local service disconnected`);
|
|
115
|
+
// Send portclose message to Diode
|
|
116
|
+
this.rpc.portClose(ref);
|
|
117
|
+
this.connections.delete(ref.toString('hex'));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
localSocket.on('error', (err) => {
|
|
121
|
+
console.error(`Error with local service:`, err);
|
|
122
|
+
// Send portclose message to Diode
|
|
123
|
+
this.rpc.portClose(ref);
|
|
124
|
+
this.connections.delete(ref.toString('hex'));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
handleTCPConnection(sessionId, ref, port) {
|
|
130
|
+
// Create a TCP connection to the local service on the specified port
|
|
131
|
+
const localSocket = net.connect({ port: port }, () => {
|
|
132
|
+
console.log(`Connected to local TCP service on port ${port}`);
|
|
133
|
+
// Send success response
|
|
134
|
+
this.rpc.sendResponse(sessionId, ref, 'ok');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Handle data, end, and error events
|
|
138
|
+
this.setupLocalSocketHandlers(localSocket, ref, 'tcp');
|
|
139
|
+
|
|
140
|
+
// Store the local socket with the ref
|
|
141
|
+
this.connections.set(ref.toString('hex'), { socket: localSocket, protocol: 'tcp' });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
handleTLSConnection(sessionId, ref, port) {
|
|
145
|
+
// Create a DiodeSocket instance
|
|
146
|
+
const diodeSocket = new DiodeSocket(ref, this.rpc);
|
|
147
|
+
|
|
148
|
+
// TLS options with your server's certificate and key
|
|
149
|
+
const tlsOptions = {
|
|
150
|
+
cert: fs.readFileSync(this.certPath),
|
|
151
|
+
key: fs.readFileSync(this.certPath),
|
|
152
|
+
rejectUnauthorized: false,
|
|
153
|
+
ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384',
|
|
154
|
+
ecdhCurve: 'secp256k1',
|
|
155
|
+
minVersion: 'TLSv1.2',
|
|
156
|
+
maxVersion: 'TLSv1.2',
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Create a TLS socket in server mode using the DiodeSocket
|
|
160
|
+
const tlsSocket = new tls.TLSSocket(diodeSocket, {
|
|
161
|
+
isServer: true,
|
|
162
|
+
...tlsOptions,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Connect to the local service (TCP or TLS as needed)
|
|
166
|
+
const localSocket = net.connect({ port: port }, () => {
|
|
167
|
+
console.log(`Connected to local TCP service on port ${port}`);
|
|
168
|
+
// Send success response
|
|
169
|
+
this.rpc.sendResponse(sessionId, ref, 'ok');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Pipe data between the TLS socket and the local service
|
|
173
|
+
tlsSocket.pipe(localSocket).pipe(tlsSocket);
|
|
174
|
+
|
|
175
|
+
// Handle errors and cleanup
|
|
176
|
+
tlsSocket.on('error', (err) => {
|
|
177
|
+
console.error('TLS Socket error:', err);
|
|
178
|
+
this.rpc.portClose(ref);
|
|
179
|
+
this.connections.delete(ref.toString('hex'));
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
tlsSocket.on('close', () => {
|
|
183
|
+
console.log('TLS Socket closed');
|
|
184
|
+
this.connections.delete(ref.toString('hex'));
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Store the connection info
|
|
188
|
+
this.connections.set(ref.toString('hex'), {
|
|
189
|
+
diodeSocket,
|
|
190
|
+
tlsSocket,
|
|
191
|
+
localSocket,
|
|
192
|
+
protocol: 'tls',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
handleUDPConnection(sessionId, ref, port) {
|
|
197
|
+
// Create a UDP socket
|
|
198
|
+
const localSocket = dgram.createSocket('udp4');
|
|
199
|
+
|
|
200
|
+
// Store the remote address and port from the Diode client
|
|
201
|
+
const remoteInfo = {port, address: '127.0.0.1'};
|
|
202
|
+
|
|
203
|
+
// Send success response
|
|
204
|
+
this.rpc.sendResponse(sessionId, ref, 'ok');
|
|
205
|
+
|
|
206
|
+
// Store the connection info
|
|
207
|
+
this.connections.set(ref.toString('hex'), {
|
|
208
|
+
socket: localSocket,
|
|
209
|
+
protocol: 'udp',
|
|
210
|
+
remoteInfo,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Handle messages from the local UDP service
|
|
214
|
+
localSocket.on('message', (msg, rinfo) => {
|
|
215
|
+
//need to add 4 bytes of data length to the beginning of the message but it's Big Endian
|
|
216
|
+
const dataLength = Buffer.alloc(4);
|
|
217
|
+
dataLength.writeUInt32LE(msg.length, 0);
|
|
218
|
+
const data = Buffer.concat([dataLength, msg]);
|
|
219
|
+
// Send the data back to the Diode client via portSend
|
|
220
|
+
this.rpc.portSend(ref, data);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
localSocket.on('error', (err) => {
|
|
224
|
+
console.error(`UDP Socket error:`, err);
|
|
225
|
+
this.rpc.portClose(ref);
|
|
226
|
+
this.connections.delete(ref.toString('hex'));
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
handlePortSend(sessionIdRaw, messageContent) {
|
|
231
|
+
const refRaw = messageContent[1];
|
|
232
|
+
const dataRaw = messageContent[2];
|
|
233
|
+
|
|
234
|
+
const sessionId = Buffer.from(sessionIdRaw);
|
|
235
|
+
const ref = Buffer.from(refRaw);
|
|
236
|
+
const data = Buffer.from(dataRaw).slice(4);
|
|
237
|
+
|
|
238
|
+
const connectionInfo = this.connections.get(ref.toString('hex'));
|
|
239
|
+
if (connectionInfo) {
|
|
240
|
+
const { socket: localSocket, protocol, remoteInfo } = connectionInfo;
|
|
241
|
+
|
|
242
|
+
if (protocol === 'udp') {
|
|
243
|
+
// Send data to the local UDP service
|
|
244
|
+
// Since UDP is connectionless, we need to specify the address and port
|
|
245
|
+
localSocket.send(data, remoteInfo.port, remoteInfo.address, (err) => {
|
|
246
|
+
if (err) {
|
|
247
|
+
console.error(`Error sending UDP data:`, err);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Update remoteInfo if not set
|
|
252
|
+
if (!localSocket.remoteAddress) {
|
|
253
|
+
localSocket.remoteAddress = '127.0.0.1'; // Assuming local service is on localhost
|
|
254
|
+
localSocket.remotePort = port;
|
|
255
|
+
}
|
|
256
|
+
} else if (protocol === 'tcp') {
|
|
257
|
+
// Write data to the local service
|
|
258
|
+
localSocket.write(data);
|
|
259
|
+
} else if (protocol === 'tls') {
|
|
260
|
+
const { diodeSocket } = connectionInfo;
|
|
261
|
+
// Push data into the DiodeSocket
|
|
262
|
+
diodeSocket.pushData(data);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
console.warn(`No local connection found for ref ${ref.toString('hex')}. Sending portclose.`);
|
|
266
|
+
this.rpc.sendError(sessionId, ref, 'No local connection found');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
handlePortClose(sessionIdRaw, messageContent) {
|
|
271
|
+
const refRaw = messageContent[1];
|
|
272
|
+
const sessionId = Buffer.from(sessionIdRaw);
|
|
273
|
+
const ref = Buffer.from(refRaw);
|
|
274
|
+
|
|
275
|
+
console.log(`Received portclose for ref ${ref.toString('hex')}`);
|
|
276
|
+
|
|
277
|
+
const connectionInfo = this.connections.get(ref.toString('hex'));
|
|
278
|
+
if (connectionInfo) {
|
|
279
|
+
const { diodeSocket, tlsSocket, socket: localSocket } = connectionInfo;
|
|
280
|
+
// End all sockets
|
|
281
|
+
if (diodeSocket) diodeSocket.end();
|
|
282
|
+
if (tlsSocket) tlsSocket.end();
|
|
283
|
+
if (localSocket) {
|
|
284
|
+
if (localSocket.type === 'udp4' || localSocket.type === 'udp6') {
|
|
285
|
+
localSocket.close();
|
|
286
|
+
} else {
|
|
287
|
+
localSocket.end();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
this.connections.delete(ref.toString('hex'));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = PublishPort;
|
package/rpc.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//rpc.js
|
|
2
|
+
const { makeReadable, parseRequestId, parseResponseType, parseReason } = require('./utils');
|
|
3
|
+
|
|
4
|
+
class DiodeRPC {
|
|
5
|
+
constructor(connection) {
|
|
6
|
+
this.connection = connection;
|
|
7
|
+
this.epochCache = {
|
|
8
|
+
epoch: null,
|
|
9
|
+
expiry: null,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getBlockPeak() {
|
|
14
|
+
return this.connection.sendCommand(['getblockpeak']).then((responseData) => {
|
|
15
|
+
// responseData is an array containing [blockNumber]
|
|
16
|
+
const blockNumberRaw = responseData[0];
|
|
17
|
+
let blockNumber;
|
|
18
|
+
if (blockNumberRaw instanceof Uint8Array) {
|
|
19
|
+
blockNumber = Buffer.from(blockNumberRaw).readUIntBE(0, blockNumberRaw.length);
|
|
20
|
+
} else if (Buffer.isBuffer(blockNumberRaw)) {
|
|
21
|
+
blockNumber = blockNumberRaw.readUIntBE(0, blockNumberRaw.length);
|
|
22
|
+
} else if (typeof blockNumberRaw === 'number') {
|
|
23
|
+
blockNumber = blockNumberRaw;
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error('Invalid block number format. response:', makeReadable(responseData));
|
|
26
|
+
}
|
|
27
|
+
return blockNumber;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
getBlockHeader(index) {
|
|
31
|
+
return this.connection.sendCommand(['getblockheader', index]).then((responseData) => {
|
|
32
|
+
return responseData[0]; // block_header
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getBlock(index) {
|
|
37
|
+
return this.connection.sendCommand(['getblock', index]).then((responseData) => {
|
|
38
|
+
return responseData[0]; // block
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ping() {
|
|
43
|
+
return this.connection.sendCommand(['ping']).then((responseData) => {
|
|
44
|
+
// responseData is an array containing [status]
|
|
45
|
+
const statusRaw = responseData[0];
|
|
46
|
+
const status = parseResponseType(statusRaw);
|
|
47
|
+
|
|
48
|
+
if (status === 'pong') {
|
|
49
|
+
return true;
|
|
50
|
+
} else if (status === 'error') {
|
|
51
|
+
throw new Error('Ping failed');
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error(`Unknown status in response: '${status}'`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
portOpen(deviceId, port, flags = 'rw') {
|
|
61
|
+
return this.connection.sendCommand(['portopen', deviceId, port, flags]).then((responseData) => {
|
|
62
|
+
// responseData is [status, refOrReason]
|
|
63
|
+
const [statusRaw, refOrReasonRaw] = responseData;
|
|
64
|
+
|
|
65
|
+
// Convert status to string
|
|
66
|
+
const status = parseResponseType(statusRaw);
|
|
67
|
+
|
|
68
|
+
if (status === 'ok') {
|
|
69
|
+
let ref = refOrReasonRaw;
|
|
70
|
+
if (Buffer.isBuffer(ref) || ref instanceof Uint8Array) {
|
|
71
|
+
ref = Buffer.from(ref);
|
|
72
|
+
}
|
|
73
|
+
return ref;
|
|
74
|
+
} else if (status === 'error') {
|
|
75
|
+
let reason = parseReason(refOrReasonRaw);
|
|
76
|
+
throw new Error(reason);
|
|
77
|
+
} else {
|
|
78
|
+
throw new Error(`Unknown status in response: '${status}'`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async portSend(ref, data) {
|
|
84
|
+
// Update totalBytes
|
|
85
|
+
const bytesToSend = data.length;
|
|
86
|
+
this.connection.totalBytes += bytesToSend;
|
|
87
|
+
|
|
88
|
+
// Now send the data
|
|
89
|
+
return this.connection.sendCommand(['portsend', ref, data]).then(async (responseData) => {
|
|
90
|
+
// responseData is [status]
|
|
91
|
+
const [statusRaw] = responseData;
|
|
92
|
+
const status = parseResponseType(statusRaw);
|
|
93
|
+
|
|
94
|
+
if (status === 'ok') {
|
|
95
|
+
try {
|
|
96
|
+
const ticketCommand = await this.connection.createTicketCommand();
|
|
97
|
+
const ticketResponse = await this.connection.sendCommand(ticketCommand);
|
|
98
|
+
console.log('Ticket updated:', ticketResponse);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Error updating ticket:', error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
} else if (status === 'error') {
|
|
105
|
+
throw new Error('Error during port send');
|
|
106
|
+
} else {
|
|
107
|
+
throw new Error(`Unknown status in response: '${status}'`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
portClose(ref) {
|
|
113
|
+
return this.connection.sendCommand(['portclose', ref]).then((responseData) => {
|
|
114
|
+
const [statusRaw] = responseData;
|
|
115
|
+
|
|
116
|
+
const status = Buffer.isBuffer(statusRaw) || statusRaw instanceof Uint8Array
|
|
117
|
+
? Buffer.from(statusRaw).toString('utf8')
|
|
118
|
+
: statusRaw;
|
|
119
|
+
|
|
120
|
+
if (status === 'ok') {
|
|
121
|
+
return;
|
|
122
|
+
} else if (status === 'error') {
|
|
123
|
+
throw new Error('Error during port close');
|
|
124
|
+
} else {
|
|
125
|
+
throw new Error(`Unknown status in response: '${status}'`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
sendError(sessionId, ref, error) {
|
|
131
|
+
return this.connection.sendCommandWithSessionId(['response', ref, 'error', error], sessionId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
sendResponse(sessionId, ref, response) {
|
|
135
|
+
return this.connection.sendCommandWithSessionId(['response', ref, response], sessionId);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getEpoch() {
|
|
139
|
+
const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
|
|
140
|
+
if (this.epochCache.expiry && this.epochCache.expiry > currentTime) {
|
|
141
|
+
console.log('Using cached epoch:', this.epochCache.epoch);
|
|
142
|
+
return this.epochCache.epoch;
|
|
143
|
+
}
|
|
144
|
+
console.log('Fetching new epoch. Expiry:', this.epochCache.expiry);
|
|
145
|
+
const blockPeak = await this.getBlockPeak();
|
|
146
|
+
const blockHeader = await this.getBlockHeader(blockPeak);
|
|
147
|
+
|
|
148
|
+
// Assuming blockHeader is an object with a timestamp property
|
|
149
|
+
const timestamp = this.parseTimestamp(blockHeader);
|
|
150
|
+
const epochDuration = 2592000; // 30 days in seconds
|
|
151
|
+
const epoch = Math.floor(timestamp / epochDuration);
|
|
152
|
+
|
|
153
|
+
// Calculate the time left for the next epoch
|
|
154
|
+
const timeLeft = epochDuration - (timestamp % epochDuration);
|
|
155
|
+
|
|
156
|
+
// Cache the epoch and the expiry time
|
|
157
|
+
this.epochCache.epoch = epoch;
|
|
158
|
+
this.epochCache.expiry = currentTime + timeLeft;
|
|
159
|
+
|
|
160
|
+
return epoch;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
parseTimestamp(blockHeader) {
|
|
164
|
+
// Implement parsing of timestamp from blockHeader
|
|
165
|
+
const timestampRaw = blockHeader[0][1]; // Adjust index based on actual structure
|
|
166
|
+
//Timestamp Raw: [ 'timestamp', 1726689425 ]
|
|
167
|
+
if (timestampRaw instanceof Uint8Array || Buffer.isBuffer(timestampRaw)) {
|
|
168
|
+
return Buffer.from(timestampRaw).readUIntBE(0, timestampRaw.length);
|
|
169
|
+
} else if (typeof timestampRaw === 'number') {
|
|
170
|
+
return timestampRaw;
|
|
171
|
+
} else {
|
|
172
|
+
throw new Error('Invalid timestamp format in block header');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = DiodeRPC;
|
|
178
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const dgram = require('dgram');
|
|
2
|
+
const server = dgram.createSocket('udp4');
|
|
3
|
+
|
|
4
|
+
// emits when any error occurs
|
|
5
|
+
server.on('error',function(error){
|
|
6
|
+
console.log('Error: ' + error);
|
|
7
|
+
server.close();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// emits on new datagram msg
|
|
11
|
+
server.on('message',function(msg,info){
|
|
12
|
+
console.log('Data received from client : ' + msg.toString());
|
|
13
|
+
console.log('Received %d bytes from %s:%d\n',msg.length, info.address, info.port);
|
|
14
|
+
|
|
15
|
+
//sending msg
|
|
16
|
+
server.send(msg,info.port,'localhost',function(error){
|
|
17
|
+
if(error){
|
|
18
|
+
client.close();
|
|
19
|
+
}else{
|
|
20
|
+
console.log('Data sent !!!');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
//emits when socket is ready and listening for datagram msgs
|
|
28
|
+
server.on('listening',function(){
|
|
29
|
+
var address = server.address();
|
|
30
|
+
var port = address.port;
|
|
31
|
+
var family = address.family;
|
|
32
|
+
var ipaddr = address.address;
|
|
33
|
+
console.log('Server is listening at port' + port);
|
|
34
|
+
console.log('Server ip :' + ipaddr);
|
|
35
|
+
console.log('Server is IP4/IP6 : ' + family);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
//emits after the socket is closed using socket.close();
|
|
39
|
+
server.on('close',function(){
|
|
40
|
+
console.log('Socket is closed !');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
server.bind(8080, () => {
|
|
44
|
+
console.log('UDP server listening on port 8080');
|
|
45
|
+
});
|
package/utils.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// utils.js
|
|
2
|
+
const { Buffer } = require('buffer');
|
|
3
|
+
function makeReadable(decodedMessage) {
|
|
4
|
+
if (Array.isArray(decodedMessage)) {
|
|
5
|
+
return decodedMessage.map((item) => makeReadable(item));
|
|
6
|
+
} else if (decodedMessage instanceof Uint8Array) {
|
|
7
|
+
const buffer = Buffer.from(decodedMessage);
|
|
8
|
+
// Try to interpret the Buffer as a UTF-8 string
|
|
9
|
+
const str = buffer.toString('utf8');
|
|
10
|
+
if (/^[\x20-\x7E]+$/.test(str)) {
|
|
11
|
+
// If it's printable ASCII, return the string
|
|
12
|
+
return str;
|
|
13
|
+
} else if (buffer.length <= 6) {
|
|
14
|
+
// If it's a small Buffer, interpret it as an integer
|
|
15
|
+
return buffer.length > 0 && buffer.length <= 6 ? buffer.readUIntBE(0, buffer.length) : '0x' + buffer.toString('hex');
|
|
16
|
+
} else {
|
|
17
|
+
// Otherwise, return the hex representation
|
|
18
|
+
return '0x' + buffer.toString('hex');
|
|
19
|
+
}
|
|
20
|
+
} else if (Buffer.isBuffer(decodedMessage)) {
|
|
21
|
+
// Similar handling for Buffer
|
|
22
|
+
const str = decodedMessage.toString('utf8');
|
|
23
|
+
if (/^[\x20-\x7E]+$/.test(str)) {
|
|
24
|
+
return str;
|
|
25
|
+
} else if (decodedMessage.length <= 6) {
|
|
26
|
+
return decodedMessage.readUIntBE(0, decodedMessage.length);
|
|
27
|
+
} else {
|
|
28
|
+
return '0x' + decodedMessage.toString('hex');
|
|
29
|
+
}
|
|
30
|
+
} else if (typeof decodedMessage === 'number') {
|
|
31
|
+
return decodedMessage;
|
|
32
|
+
}
|
|
33
|
+
return decodedMessage;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Helper functions
|
|
37
|
+
function parseRequestId(requestIdRaw) {
|
|
38
|
+
if (requestIdRaw instanceof Uint8Array || Buffer.isBuffer(requestIdRaw)) {
|
|
39
|
+
const buffer = Buffer.from(requestIdRaw);
|
|
40
|
+
return buffer.readUIntBE(0, buffer.length);
|
|
41
|
+
} else if (typeof requestIdRaw === 'number') {
|
|
42
|
+
return requestIdRaw;
|
|
43
|
+
} else {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
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));
|
|
53
|
+
if (responseTypeRaw instanceof Uint8Array || Buffer.isBuffer(responseTypeRaw)) {
|
|
54
|
+
return Buffer.from(responseTypeRaw).toString('utf8');
|
|
55
|
+
} else if (Array.isArray(responseTypeRaw)) {
|
|
56
|
+
// Convert each element to Buffer and concatenate
|
|
57
|
+
const buffers = responseTypeRaw.map((item) => Buffer.from(item));
|
|
58
|
+
const concatenated = Buffer.concat(buffers);
|
|
59
|
+
return concatenated.toString('utf8');
|
|
60
|
+
} else if (typeof responseTypeRaw === 'string') {
|
|
61
|
+
return responseTypeRaw;
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error('Invalid responseType type');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseReason(reasonRaw) {
|
|
68
|
+
if (Buffer.isBuffer(reasonRaw) || reasonRaw instanceof Uint8Array) {
|
|
69
|
+
return Buffer.from(reasonRaw).toString('utf8');
|
|
70
|
+
} else if (typeof reasonRaw === 'string') {
|
|
71
|
+
return reasonRaw;
|
|
72
|
+
} else {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { makeReadable, parseRequestId, parseResponseType, parseReason };
|