nano-pow 4.1.7 → 5.0.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 CHANGED
@@ -8,7 +8,8 @@ _Proof-of-work generation and validation with WebGPU/WebGL for Nano cryptocurren
8
8
 
9
9
  NanoPow uses WebGPU to generate proof-of-work nonces meeting the requirements
10
10
  of the Nano cryptocurrency. WebGPU is cutting edge technology, so for browsers
11
- which do not yet support it, a WebGL 2.0 implementation is used as a fallback.
11
+ which do not yet support it, WebGL 2.0 and WASM implementations are available
12
+ as fallbacks.
12
13
 
13
14
  All calculations take place client-side, so nonces can be generated offline and
14
15
  cached for the next transaction block. For more information about the
@@ -36,12 +37,6 @@ import { NanoPow } from 'nano-pow'
36
37
  import NanoPow from 'nano-pow'
37
38
  ```
38
39
 
39
- A specific API can be explicitly imported:
40
-
41
- ```javascript
42
- import { NanoPowGpu, NanoPowGl } from 'nano-pow'
43
- ```
44
-
45
40
  Use it directly on a webpage with a script module:
46
41
 
47
42
  ```html
@@ -75,16 +70,34 @@ const { valid } = await NanoPow.work_validate(work, hash)
75
70
  ### Options
76
71
  ```javascript
77
72
  const options = {
78
- // default 0xFFFFFFF800000000 for send/change blocks
73
+ // default best available in order: webgpu => webgl => wasm => cpu
74
+ api: 'webgpu',
75
+ // default FFFFFFF800000000 for send/change blocks
79
76
  difficulty: 'FFFFFFC000000000',
80
- // default 8, valid range 1-32
81
- effort: 16,
77
+ // default 4, valid range 1-32
78
+ effort: 2,
82
79
  // default false
83
80
  debug: true
84
81
  }
85
82
  const { work } = await NanoPow.work_generate(hash, options)
86
83
  ```
87
84
 
85
+ #### What is "effort"?
86
+ NanoPow's "effort" metric is an abstraction of various GPU and CPU capabilities.
87
+ Different systems will have different optimal settings, but as a general rule of
88
+ thumb:
89
+
90
+ * WebGPU must strike a balance between the overhead of dispatching work to the
91
+ GPU and the time it takes to compute the dispatch itself. Start with a low value
92
+ like 2 or 4.
93
+ * WegGL works by drawing to an invisible 2-D canvas that is `effort * 256`
94
+ pixels long on each side. Since PoW speed in this case depends on resolution
95
+ _and_ framerate, push for a value as high as the GPU can support. For example, a
96
+ GPU that can draw 4096 x 4096 at 15 FPS should be set around 16 effort.
97
+ * WASM does not use the GPU at all and instead depends on Web Workers for CPU
98
+ multi-threading capabilities. Set effort equal to the number of physical cores
99
+ in the CPU.
100
+
88
101
  ## Executables
89
102
  NanoPow can be installed globally and executed from the command line. This is
90
103
  useful for systems without a graphical interface.
@@ -137,11 +150,11 @@ detached process, and it can also be started manually to customize behavior by
137
150
  executing the server script directly.
138
151
 
139
152
  #### Environment Variables
140
- `NANO_POW_PORT`: override the default port 5040
153
+ `NANO_POW_DEBUG`: enable additional logging saved to the HOME directory
141
154
 
142
- `NANO_POW_EFFORT` increase or decrease demand on the GPU
155
+ `NANO_POW_EFFORT`: increase or decrease demand on the GPU
143
156
 
144
- `NANO_POW_DEBUG` enable additional logging saved to the HOME directory
157
+ `NANO_POW_PORT`: override the default port 5040
145
158
 
146
159
  ```console
147
160
  $ # Launch the server and detach from the current session
@@ -196,7 +209,8 @@ every last bit of speed and performance out of it.
196
209
  A few basic tests are availabe in the source repository.
197
210
  * `test/index.html` in the source repository contains a web interface to change
198
211
  execution options and compare results.
199
- * `test/script.sh` starts the `nano-pow` server and sends some basic requests.
212
+ * `test/script.sh` runs some basic benchmarks to check the CLI, and then it
213
+ starts the `nano-pow` server and sends some validate and generate requests.
200
214
 
201
215
  ## Building
202
216
  1. Clone source
package/dist/bin/cli.js CHANGED
@@ -5,69 +5,238 @@ import { spawn } from "node:child_process";
5
5
  import { getRandomValues } from "node:crypto";
6
6
  import { createInterface } from "node:readline/promises";
7
7
 
8
+ // src/utils/api-support/wasm.ts
9
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
10
+ //! SPDX-License-Identifier: GPL-3.0-or-later
11
+ var wasm = { isSupported: false };
12
+ Object.defineProperty(wasm, "isSupported", {
13
+ get: async function() {
14
+ let isWasmSupported = false;
15
+ try {
16
+ const wasmBuffer = new Uint8Array([
17
+ 0,
18
+ 97,
19
+ 115,
20
+ 109,
21
+ 1,
22
+ 0,
23
+ 0,
24
+ 0,
25
+ // WASM magic header and version
26
+ 1,
27
+ 4,
28
+ // Type section, 4 byte descriptor
29
+ 1,
30
+ 96,
31
+ 0,
32
+ 0,
33
+ // 1 type, type function, 0 params, 0 returns
34
+ 3,
35
+ 2,
36
+ // Function section, 2 byte descriptor
37
+ 1,
38
+ 0,
39
+ // 1 function, ID 0
40
+ 10,
41
+ 23,
42
+ // Code section, 23 bytes
43
+ 1,
44
+ 21,
45
+ // 1 function body, 21 bytes
46
+ 0,
47
+ // 0 local variables
48
+ 253,
49
+ 12,
50
+ // v128.const
51
+ 0,
52
+ 0,
53
+ 0,
54
+ 0,
55
+ 0,
56
+ 0,
57
+ 0,
58
+ 0,
59
+ 0,
60
+ 0,
61
+ 0,
62
+ 0,
63
+ 0,
64
+ 0,
65
+ 0,
66
+ 0,
67
+ // assign i64x2 values
68
+ 26,
69
+ 11
70
+ // Drop result, end function
71
+ ]);
72
+ const module = await WebAssembly.compile(wasmBuffer);
73
+ const instance = await WebAssembly.instantiate(module);
74
+ isWasmSupported = instance instanceof WebAssembly.Instance;
75
+ } catch (err) {
76
+ console.warn("WASM is not supported in this environment.\n", err);
77
+ isWasmSupported = false;
78
+ } finally {
79
+ delete this.isSupported;
80
+ this.isSupported = isWasmSupported;
81
+ return this.isSupported;
82
+ }
83
+ }
84
+ });
85
+
86
+ // src/utils/api-support/webgl.ts
87
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
88
+ //! SPDX-License-Identifier: GPL-3.0-or-later
89
+ var webgl = { isSupported: false };
90
+ Object.defineProperty(webgl, "isSupported", {
91
+ get: async function() {
92
+ let isWebglSupported = false;
93
+ try {
94
+ const gl = new OffscreenCanvas(0, 0)?.getContext?.("webgl2");
95
+ isWebglSupported = gl instanceof WebGL2RenderingContext;
96
+ } catch (err) {
97
+ console.warn("WebGL is not supported in this environment.\n", err);
98
+ isWebglSupported = false;
99
+ } finally {
100
+ delete this.isSupported;
101
+ this.isSupported = isWebglSupported;
102
+ return this.isSupported;
103
+ }
104
+ }
105
+ });
106
+
107
+ // src/utils/api-support/webgpu.ts
108
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
109
+ //! SPDX-License-Identifier: GPL-3.0-or-later
110
+ var webgpu = { isSupported: false };
111
+ Object.defineProperty(webgpu, "isSupported", {
112
+ get: async function() {
113
+ let isWebgpuSupported = false;
114
+ try {
115
+ const adapter = await navigator?.gpu?.requestAdapter?.();
116
+ isWebgpuSupported = adapter instanceof GPUAdapter;
117
+ } catch (err) {
118
+ console.warn("WebGPU is not supported in this environment.\n", err);
119
+ isWebgpuSupported = false;
120
+ } finally {
121
+ delete this.isSupported;
122
+ this.isSupported = isWebgpuSupported;
123
+ return this.isSupported;
124
+ }
125
+ }
126
+ });
127
+
128
+ // src/utils/api-support/index.ts
129
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
130
+ //! SPDX-License-Identifier: GPL-3.0-or-later
131
+
132
+ // src/utils/bigint.ts
133
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
134
+ //! SPDX-License-Identifier: GPL-3.0-or-later
135
+
136
+ // src/utils/logger.ts
137
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
138
+ //! SPDX-License-Identifier: GPL-3.0-or-later
139
+ var Logger = class {
140
+ isEnabled = false;
141
+ groups = {};
142
+ groupStart(name) {
143
+ if (this.isEnabled) {
144
+ console.groupCollapsed(name);
145
+ this.groups[name] = true;
146
+ }
147
+ }
148
+ groupEnd(name) {
149
+ if (this.groups[name]) {
150
+ console.groupEnd();
151
+ delete this.groups[name];
152
+ }
153
+ }
154
+ log(...args2) {
155
+ if (this.isEnabled) {
156
+ const datetime = new Date(Date.now()).toLocaleString(Intl.DateTimeFormat().resolvedOptions().locale ?? "en-US", { hour12: false, dateStyle: "medium", timeStyle: "medium" });
157
+ for (let i = 0; i < args2.length; i++) {
158
+ if (typeof args2[i] === "string") {
159
+ args2[i] = args2[i].replace(datetime, "").trimStart();
160
+ }
161
+ if (args2[i] instanceof Error) {
162
+ if ("stack" in args2[i]) {
163
+ args2.splice(i + 1, 0, args2[i].stack);
164
+ } else if ("message" in args2[i]) {
165
+ args2.splice(i + 1, 0, args2[i].message);
166
+ }
167
+ }
168
+ }
169
+ const entry = `${datetime} ${globalThis.process?.title ?? "NanoPow"}[${globalThis.process?.pid ?? "browser"}]:`;
170
+ console.log(entry, ...args2);
171
+ globalThis.process?.send?.({ type: "console", data: `${entry} ${args2}` });
172
+ }
173
+ }
174
+ };
175
+
176
+ // src/utils/queue.ts
177
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
178
+ //! SPDX-License-Identifier: GPL-3.0-or-later
179
+
8
180
  // src/utils/index.ts
9
- function average(times) {
10
- if (times == null || times.length === 0) return {};
11
- let count = times.length;
181
+ //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
182
+ //! SPDX-License-Identifier: GPL-3.0-or-later
183
+ function stats(times) {
184
+ if (times == null || times.length === 0) return null;
185
+ const count = times.length;
186
+ const truncatedStart = Math.floor(count * 0.1);
187
+ const truncatedEnd = count - truncatedStart;
188
+ const truncatedCount = truncatedEnd - truncatedStart;
12
189
  let min = Number.MAX_SAFE_INTEGER;
13
- let logarithms, max, median, rate, reciprocals, total, truncated, truncatedCount;
14
- logarithms = max = median = rate = reciprocals = total = truncated = truncatedCount = 0;
190
+ let logarithms, max, median, reciprocals, total;
191
+ logarithms = max = median = reciprocals = total = 0;
192
+ let truncatedMin = Number.MAX_SAFE_INTEGER;
193
+ let truncatedLogarithms, truncatedMax, truncatedReciprocals, truncatedTotal;
194
+ truncatedLogarithms = truncatedMax = truncatedReciprocals = truncatedTotal = 0;
15
195
  times.sort((a, b) => a - b);
16
196
  for (let i = 0; i < count; i++) {
17
197
  const time = times[i];
18
198
  total += time;
19
- reciprocals += 1 / time;
20
199
  logarithms += Math.log(time);
200
+ reciprocals += 1 / time;
21
201
  min = Math.min(min, time);
22
202
  max = Math.max(max, time);
23
- if (i + 1 === Math.ceil(count / 2)) median = time;
24
- if (count < 3 || i > 0.1 * count && i < 0.9 * (count - 1)) {
25
- truncated += time;
26
- truncatedCount++;
27
- }
203
+ if (i === Math.floor((count - 1) / 2)) median = time;
204
+ if (i === Math.floor(count / 2) && count % 2 === 0) median = (median + time) / 2;
205
+ }
206
+ for (let i = truncatedStart; i < truncatedEnd; i++) {
207
+ const time = times[i];
208
+ truncatedTotal += time;
209
+ truncatedLogarithms += Math.log(time);
210
+ truncatedReciprocals += 1 / time;
211
+ truncatedMin = Math.min(truncatedMin, time);
212
+ truncatedMax = Math.max(truncatedMax, time);
28
213
  }
29
214
  return {
30
215
  count,
31
216
  total,
217
+ rate: 1e3 * count / total,
32
218
  min,
33
219
  max,
34
220
  median,
35
221
  arithmetic: total / count,
36
- harmonic: count / reciprocals,
37
222
  geometric: Math.exp(logarithms / count),
38
- truncated: truncated / truncatedCount,
39
- rate: 1e3 * truncatedCount / (truncated || total)
223
+ harmonic: count / reciprocals,
224
+ truncatedCount,
225
+ truncatedTotal,
226
+ truncatedRate: 1e3 * truncatedCount / truncatedTotal,
227
+ truncatedMin,
228
+ truncatedMax,
229
+ truncatedArithmetic: truncatedTotal / truncatedCount,
230
+ truncatedGeometric: Math.exp(truncatedLogarithms / truncatedCount),
231
+ truncatedHarmonic: truncatedCount / truncatedReciprocals
40
232
  };
41
233
  }
42
- function isHex(input, min, max) {
43
- if (typeof input !== "string") {
44
- return false;
45
- }
46
- if (typeof min !== "undefined" && typeof min !== "number") {
47
- throw new Error(`Invalid argument for parameter 'min'`);
48
- }
49
- if (typeof max !== "undefined" && typeof max !== "number") {
50
- throw new Error(`Invalid argument for parameter 'max'`);
51
- }
52
- const range = min === void 0 && max === void 0 ? "+" : `{${min ?? "0"},${max ?? ""}}`;
53
- const regexp = new RegExp(`^[0-9A-Fa-f]${range}$`, "m");
54
- return regexp.test(input);
55
- }
56
- function isNotHex(input, min, max) {
57
- return !isHex(input, min, max);
58
- }
59
- function log(...args2) {
60
- if (process?.env?.NANO_POW_DEBUG) {
61
- const entry = `${new Date(Date.now()).toLocaleString(Intl.DateTimeFormat().resolvedOptions().locale ?? "en-US", { hour12: false, dateStyle: "medium", timeStyle: "medium" })} NanoPow[${process.pid}]: ${args2}`;
62
- console.log(entry);
63
- process.send?.({ type: "console", message: entry });
64
- }
65
- }
66
234
 
67
235
  // src/bin/cli.ts
68
236
  //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
69
237
  //! SPDX-License-Identifier: GPL-3.0-or-later
70
238
  process.title = "NanoPow CLI";
239
+ var logger = new Logger();
71
240
  delete process.env.NANO_POW_DEBUG;
72
241
  delete process.env.NANO_POW_EFFORT;
73
242
  delete process.env.NANO_POW_PORT;
@@ -80,7 +249,7 @@ if (!process.stdin.isTTY) {
80
249
  let i = 0;
81
250
  for await (const line of stdin) {
82
251
  i++;
83
- if (isHex(line, 64)) {
252
+ if (/^[A-Fa-f\d]{64}$/.test(line)) {
84
253
  hashes.push(line);
85
254
  } else {
86
255
  stdinErrors.push(`Skipping invalid stdin input line ${i}`);
@@ -89,13 +258,13 @@ if (!process.stdin.isTTY) {
89
258
  }
90
259
  var args = process.argv.slice(2);
91
260
  if (hashes.length === 0 && args.length === 0 || args.some((v) => v === "--help" || v === "-h")) {
92
- console.log(
93
- `Usage: nano-pow [OPTION]... BLOCKHASH...
261
+ console.log(`Usage: nano-pow [OPTION]... BLOCKHASH...
94
262
  Generate work for BLOCKHASH, or multiple work values for BLOCKHASH(es)
95
263
  BLOCKHASH is a 64-character hexadecimal string. Multiple blockhashes must be separated by whitespace or line breaks.
96
264
  Prints the result as a Javascript object to standard output as soon as it is calculated.
97
265
  If using --batch, results are printed only after all BLOCKHASH(es) have be processed.
98
266
  If using --validate, results will also include validity properties.
267
+
99
268
  -b, --batch process all data before returning final results as array
100
269
  -d, --difficulty <value> override the minimum difficulty value
101
270
  -e, --effort <value> increase demand on GPU processing
@@ -104,6 +273,7 @@ If using --validate, results will also include validity properties.
104
273
  -h, --help show this dialog
105
274
  --debug enable additional logging output
106
275
  --benchmark <value> generate work for specified number of random hashes
276
+ --score <value> used with --benchmark to run specified number of times and calculate work-per-second
107
277
 
108
278
  If validating a nonce, it must be a 16-character hexadecimal value.
109
279
  Effort must be a decimal number between 1-32.
@@ -111,46 +281,31 @@ Difficulty must be a hexadecimal string between 0-FFFFFFFFFFFFFFFF.
111
281
 
112
282
  Report bugs: <bug-nano-pow@zoso.dev>
113
283
  Full documentation: <https://www.npmjs.com/package/nano-pow>
114
- `
115
- );
284
+ `);
116
285
  process.exit(0);
117
286
  }
118
287
  var inArgs = [];
119
- while (isHex(args[args.length - 1], 64)) {
120
- inArgs.unshift(args.pop());
288
+ var isParsingHash = true;
289
+ while (isParsingHash) {
290
+ if (!/^[A-Fa-f\d]{16}$/.test(args[args.length - 1])) break;
291
+ try {
292
+ inArgs.unshift(args.pop());
293
+ } catch {
294
+ isParsingHash = false;
295
+ }
121
296
  }
122
297
  hashes.push(...inArgs);
123
298
  var isBatch = false;
124
299
  var isBenchmark = false;
300
+ var runs = 1;
125
301
  var body = {
126
302
  action: "work_generate"
127
303
  };
128
304
  for (let i = 0; i < args.length; i++) {
129
305
  switch (args[i]) {
130
- case "--validate":
131
- case "-v": {
132
- const v = args[i + 1];
133
- if (v == null) throw new Error("Missing argument for work validation");
134
- if (isNotHex(v, 16)) throw new Error("Invalid work to validate");
135
- if (hashes.length !== 1) throw new Error("Validate accepts exactly one hash");
136
- body.action = "work_validate";
137
- body.work = v;
138
- break;
139
- }
140
- case "--difficulty":
141
- case "-d": {
142
- const d = args[i + 1];
143
- if (d == null) throw new Error("Missing argument for difficulty");
144
- if (isNotHex(d, 1, 16)) throw new Error("Invalid difficulty");
145
- body.difficulty = d;
146
- break;
147
- }
148
- case "--effort":
149
- case "-e": {
150
- const e = args[i + 1];
151
- if (e == null) throw new Error("Missing argument for effort");
152
- if (parseInt(e) < 1 || parseInt(e) > 32) throw new Error("Invalid effort");
153
- process.env.NANO_POW_EFFORT = e;
306
+ case "--batch":
307
+ case "-b": {
308
+ isBatch = true;
154
309
  break;
155
310
  }
156
311
  case "--benchmark": {
@@ -167,12 +322,42 @@ for (let i = 0; i < args.length; i++) {
167
322
  break;
168
323
  }
169
324
  case "--debug": {
325
+ logger.isEnabled = true;
170
326
  process.env.NANO_POW_DEBUG = "true";
171
327
  break;
172
328
  }
173
- case "--batch":
174
- case "-b": {
175
- isBatch = true;
329
+ case "--difficulty":
330
+ case "-d": {
331
+ const d = args[i + 1];
332
+ if (d == null) throw new Error("Missing argument for difficulty");
333
+ if (!/^[A-Fa-f\d]{16}$/.test(d)) throw new Error("Invalid argument for difficulty");
334
+ body.difficulty = d;
335
+ break;
336
+ }
337
+ case "--effort":
338
+ case "-e": {
339
+ const e = args[i + 1];
340
+ if (e == null) throw new Error("Missing argument for effort");
341
+ if (parseInt(e) < 1 || parseInt(e) > 32) throw new Error("Invalid effort");
342
+ process.env.NANO_POW_EFFORT = e;
343
+ break;
344
+ }
345
+ case "--score": {
346
+ const s = args[i + 1];
347
+ if (s == null) throw new Error("Missing argument for score");
348
+ const count = +s;
349
+ if (count < 1) throw new Error("Invalid score runs count");
350
+ runs = count;
351
+ break;
352
+ }
353
+ case "--validate":
354
+ case "-v": {
355
+ const v = args[i + 1];
356
+ if (v == null) throw new Error("Missing argument for work validation");
357
+ if (!/^[A-Fa-f\d]{16}$/.test(v)) throw new Error("Invalid argument for work validation");
358
+ if (hashes.length !== 1) throw new Error("Validate accepts exactly one hash");
359
+ body.action = "work_validate";
360
+ body.work = v;
176
361
  break;
177
362
  }
178
363
  }
@@ -181,71 +366,121 @@ if (hashes.length === 0) {
181
366
  console.error("Invalid block hash input");
182
367
  process.exit(1);
183
368
  }
184
- log("CLI args:", ...args);
369
+ logger.log("CLI args: ", ...args);
185
370
  for (const stdinErr of stdinErrors) {
186
- log(stdinErr);
371
+ logger.log(stdinErr);
187
372
  }
188
- log("Starting NanoPow CLI");
373
+ logger.log("launching server");
189
374
  var server = spawn(
190
375
  process.execPath,
191
376
  [new URL(import.meta.resolve("./server.js")).pathname],
192
377
  { stdio: ["pipe", "pipe", "pipe", "ipc"] }
193
378
  );
194
379
  server.once("error", (err) => {
195
- log(err);
380
+ logger.log(err);
196
381
  process.exit(1);
197
382
  });
198
383
  server.on("message", async (msg) => {
199
- if (msg.type === "console") {
200
- log(msg.message);
201
- }
202
- if (msg.type === "listening") {
203
- const port = +msg.message;
204
- if (port > -1) {
205
- log(`CLI server listening on port ${port}`);
206
- try {
207
- await execute(port);
208
- } catch {
209
- log(`Error executing ${body.action}`);
384
+ if (typeof msg === "object" && msg != null && "type" in msg && typeof msg.type === "string" && "data" in msg && typeof msg.data === "string") {
385
+ if (msg.type === "console") {
386
+ logger.log(msg.data);
387
+ }
388
+ if (msg.type === "listening") {
389
+ if (msg.data === "ipc") {
390
+ logger.log(`CLI connected to server over IPC`);
391
+ try {
392
+ if (isBenchmark) {
393
+ if (runs > 1) {
394
+ await score();
395
+ } else {
396
+ await benchmark();
397
+ }
398
+ } else {
399
+ await execute();
400
+ }
401
+ } catch {
402
+ logger.log(`Error executing ${body.action}`);
403
+ } finally {
404
+ server.kill();
405
+ }
406
+ } else {
407
+ logger.log("CLI server failed to connect over IPC");
210
408
  }
211
- } else {
212
- log("Server failed to provide port");
213
409
  }
410
+ } else {
411
+ logger.log("received invalid message from server", msg);
214
412
  }
215
413
  });
216
414
  server.on("close", (code) => {
217
- log(`Server closed with exit code ${code}`);
415
+ logger.log(`Server closed with exit code ${code}`);
218
416
  process.exit(code);
219
417
  });
220
- async function execute(port) {
221
- const results = [];
222
- if (isBenchmark) console.log("Running benchmark...");
223
- let start = 0;
418
+ async function benchmark() {
419
+ console.log("Running benchmark...");
420
+ let start = 0, end = 0;
224
421
  const times = [];
225
422
  for (const hash of hashes) {
423
+ const kill = setTimeout(() => {
424
+ throw new Error("cli execution timed out");
425
+ }, 6e4);
426
+ body.hash = hash;
226
427
  try {
227
- const aborter = new AbortController();
228
- const kill = setTimeout(() => aborter.abort(), 6e4);
229
- body.hash = hash;
230
428
  start = performance.now();
231
- const response = await fetch(`http://localhost:${port}`, {
232
- method: "POST",
233
- body: JSON.stringify(body),
234
- signal: aborter.signal
235
- });
429
+ await request(body);
430
+ end = performance.now();
431
+ times.push(end - start);
432
+ } catch (err) {
433
+ logger.log(err);
434
+ } finally {
236
435
  clearTimeout(kill);
237
- const result = await response.json();
238
- if (isBatch || isBenchmark) {
239
- results.push(result);
240
- times.push(performance.now() - start);
241
- } else {
242
- console.log(result);
436
+ }
437
+ }
438
+ console.log(stats(times));
439
+ return stats(times);
440
+ }
441
+ async function execute() {
442
+ const results = [];
443
+ for (const hash of hashes) {
444
+ const kill = setTimeout(() => {
445
+ throw new Error("cli execution timed out");
446
+ }, 6e4);
447
+ body.hash = hash;
448
+ try {
449
+ const result = await request(body);
450
+ isBatch ? results.push(result) : console.log(result);
451
+ } catch (err) {
452
+ logger.log(err);
453
+ } finally {
454
+ clearTimeout(kill);
455
+ }
456
+ }
457
+ if (isBatch) console.log(results);
458
+ }
459
+ async function request(body2) {
460
+ return new Promise((resolve, reject) => {
461
+ const listener = async (msg) => {
462
+ if (typeof msg === "object" && msg != null && "type" in msg && typeof msg.type === "string" && "data" in msg && typeof msg.data === "string") {
463
+ if (msg.type === "ipc") {
464
+ resolve(JSON.parse(msg.data));
465
+ server.off("message", listener);
466
+ }
243
467
  }
468
+ };
469
+ server.on("message", listener);
470
+ server.send({ type: "ipc", data: JSON.stringify(body2) }, (cb) => cb ? reject(cb) : logger.log("cli ipc request to server"));
471
+ });
472
+ }
473
+ async function score() {
474
+ console.log("Calculating work per second...");
475
+ const rates = [];
476
+ for (let i = 0; i < runs; i++) {
477
+ try {
478
+ const result = await benchmark();
479
+ logger.log(result);
480
+ if (result != null) rates.push(result.truncatedRate);
244
481
  } catch (err) {
245
- log(err);
482
+ logger.log(err);
246
483
  }
247
484
  }
248
- if (isBatch && !isBenchmark) console.log(results);
249
- if (process.env.NANO_POW_DEBUG || isBenchmark) console.log(average(times));
250
- server.kill();
485
+ console.log(stats(rates)?.truncatedHarmonic, "wps");
251
486
  }
@@ -10,11 +10,11 @@ NANO_POW_LOGS="$NANO_POW_HOME"/logs;
10
10
  mkdir -p "$NANO_POW_LOGS";
11
11
  if [ "$1" = '--server' ]; then
12
12
  shift;
13
- node "$SCRIPT_DIR"/server.js --max-http-header-size=1024 --max-old-space-size=256 >> "$NANO_POW_LOGS"/nano-pow-server-$(date -I).log 2>&1 & echo "$!" > "$NANO_POW_HOME"/server.pid;
14
- sleep 0.1;
13
+ node --max-http-header-size=1024 --max-old-space-size=256 "$SCRIPT_DIR"/server.js >> "$NANO_POW_LOGS"/nano-pow-server-$(date -I).log 2>&1 &
14
+ sleep 1;
15
15
  if [ "$(ps | grep $(cat $NANO_POW_HOME/server.pid))" = '' ]; then
16
16
  cat $(ls -td "$NANO_POW_LOGS"/* | head -n1);
17
17
  fi;
18
18
  else
19
- node "$SCRIPT_DIR"/cli.js --max-old-space-size=256 "$@";
19
+ node --max-old-space-size=256 "$SCRIPT_DIR"/cli.js "$@";
20
20
  fi;