]> zoso.dev Git - nano-pow.git/commitdiff
Ignore all arguments when starting server and use envvars instead. Read NANO_POW_EFFO...
authorChris Duncan <chris@zoso.dev>
Wed, 26 Mar 2025 15:04:11 +0000 (08:04 -0700)
committerChris Duncan <chris@zoso.dev>
Wed, 26 Mar 2025 15:04:11 +0000 (08:04 -0700)
README.md
docs/index.js
docs/nano-pow.1
src/bin/cli.ts
src/bin/nano-pow.sh
src/bin/server.ts
test/script.sh

index 5a8664c42af74b373e4464f05f15bb938bfd90b8..ba02a2a2c39096adfc1762d451a42e2d59cc61e4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -17,8 +17,10 @@ https://docs.nano.org/integration-guides/work-generation/#work-calculation-detai
 
 ## Installation
 ```console
-npm i nano-pow
+npm i nano-pow
 ```
+NanoPow can also be installed globally to add the `nano-pow` command to your
+environment. To learn more, see [#Executables](#executables).
 
 ## Usage
 ### Import
@@ -46,17 +48,17 @@ Use it directly on a webpage with a script module:
 <script type="module">
        (async () => {
                const { NanoPow } = await import('https://cdn.jsdelivr.net/npm/nano-pow@latest')
-               const work = await NanoPow.search(some_hash)
+               const { work } = await NanoPow.work_generate(some_hash)
                console.log(work)
        })()
 </script>
 ```
 
-### Search
+### Generate
 ```javascript
 // `hash` is a 64-char hex string
 const hash = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
-const work = await NanoPow.search(hash)
+const { work } = await NanoPow.work_generate(hash)
 // Result is a 16-char hex string
 ```
 
@@ -66,29 +68,112 @@ const work = await NanoPow.search(hash)
 const work = 'fedcba0987654321'
 // `hash` is a 64-char hex string
 const hash = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
-const isValid = await NanoPow.validate(work, hash)
+const { valid } = await NanoPow.work_validate(work, hash)
 // Result is a boolean
 ```
 
 ### Options
 ```javascript
 const options = {
-       // default 0xFFFFFFF8 for send/change blocks
+       // default 0xFFFFFFF800000000 for send/change blocks
        threshold: number,
        // default 8, valid range 1-32
        effort: number,
        // default false
        debug: true
 }
-const work = await NanoPow.search(hash, options)
+const { work } = await NanoPow.work_generate(hash, options)
 ```
 
-### Command Line
+## Executables
 NanoPow can be installed globally and executed from the command line. This is
 useful for systems without a graphical interface.
 ```console
 $ npm -g i nano-pow
-$ nano-pow --help  # view command documentation
+$ nano-pow --help    # view abbreviated CLI help
+$ man nano-pow       # view full manual
+```
+Ensure you have proper permissions on your
+[npm `prefix`](https://docs.npmjs.com/cli/v11/commands/npm-prefix) directory and
+have configured your `PATH` accordingly.
+
+For example, this adds a user-specific directory for local binaries to `PATH`
+upon login and configures `npm prefix` to use it for global installations:
+```console
+$ echo 'PATH="$HOME/.local/bin:$PATH"' >> .bashrc
+$ npm config set prefix="$HOME/.local"
+```
+### Command Line
+NanoPow provides a shell command&mdash;`nano-pow`&mdash;to accomodate systems
+without a graphical user interface. It launches a headless Chrome browser using
+`puppeteer` to access the required WebGPU or WebGL APIs. Use the `--global` flag
+when installing to add the executable script to your system.
+```console
+$ npm i -g nano-pow
+```
+Some examples are provided below, and for full documentation, read the manual
+with `man nano-pow`.
+
+```console
+$ # Generate a work value using default settings and debugging output enabled.
+$ nano-pow --debug 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+```
+```console
+$ # Generate work using customized behavior with options.
+$ nano-pow --effort 32 --threshold FFFFFFC000000000 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+```
+```console
+$ # Validate an existing work nonce against a blockhash.
+nano-pow --validate fedcba9876543210 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+```
+```console
+$ # Process blockhashes in batches to reduce the initial startup overhead.
+$ nano-pow 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef [...]
+$ # OR
+nano-pow $(cat /path/to/hashes/file)
+$ # OR
+$ cat /path/to/hashes/file | nano-pow
+```
+
+### Server
+NanoPow also provides a basic work server similar to the one included in the
+official Nano node software. The installed command will launch the server in a
+detached process, and you can also start it yourself to customize behavior by
+executing the server script directly.
+
+`PORT` can be passed as an environment variable and defaults to 3000 if not
+specified.
+
+`NANO_POW_EFFORT` can also be passed as an environment variable to increase
+or decrease the demand on the GPU and defaults to 8 if not specified.
+
+```console
+$ # Launch the server and detach from the current session
+$ PORT=8080 nano-pow --server
+$ # View process ID for "NanoPow Server"
+$ cat ~/.nano-pow/server.pid
+$ # Display list of server logs
+$ ls ~/.nano-pow/logs/
+$ # Find process ID manually
+$ ps aux | grep NanoPow
+```
+Work is generated or validated by sending an HTTP `POST` request to the
+configured hostname or IP address of the machine. Some basic help is available
+via `GET` request.
+```console
+$ # Generate a work value
+$ curl -d '{
+       "action": "work_generate",
+       "hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+}' localhost:3000
+```
+```console
+$ # Validate a work value
+$ curl -d '{
+       "action": "work_validate",
+       "work": "e45835c3b291c3d1",
+       "hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+}' localhost:3000
 ```
 
 ## Notes
@@ -106,21 +191,16 @@ for all other accounts.
 * 𝘵𝘩𝘳𝘦𝘴𝘩𝘰𝘭𝘥 is 0xFFFFFFF800000000 for send/change blocks and 0xFFFFFE0000000000
 for receive/open/epoch blocks.
 
-The threshold is implemented in code as only the first 32 bits due to WGSL only
-supporting u32 integer types, but the result is the same. For example, if
-checking whether a two-digit number xx > 55, and it is known that the first
-digit is 6, it is also automatically known that xx is greater than 55. The
-default threshold used is the send/change difficulty. Any other threshold can be
-specified in practice.
-
 The BLAKE2b implementation has been optimized to the extreme for this package
-due to the very narrow use case to which it is applied. The compute shader is
-consequently immense, but the goal is to squeeze every last bit of speed and
-performance out of it.
+due to the very narrow use case to which it is applied. The compute shader used
+by the WebGPU implementation is consequently immense, but the goal is to squeeze
+every last bit of speed and performance out of it.
 
 ## Tests
-`test.html` in the source repository contains some basic tests to compare the
-speed of this tool. Feel free to check out how your system fares.
+A few basic tests are availabe in the source repository.
+* `test/index.html` in the source repository contains a web interface to change
+execution options and compare results.
+* `test/script.sh` starts the `nano-pow` server and sends some basic requests.
 
 ## Building
 1. Clone source
@@ -128,11 +208,10 @@ speed of this tool. Feel free to check out how your system fares.
 1. Install dev dependencies
 1. Compile, minify, and bundle
 
-```bash
-git clone https://zoso.dev/nano-pow.git
-cd nano-pow
-npm i
-npm run build
+```console
+$ git clone https://zoso.dev/nano-pow.git
+$ cd nano-pow
+$ npm i
 ```
 
 ## Reporting Bugs
index da2b3f75f14b10782e9855a57875557f7b7f10c5..5e104102ab177b1a9aee817883c04bf3ae86f0b6 100644 (file)
@@ -8,14 +8,14 @@ Prints a 16-character hexadecimal work value to standard output. If using --vali
 
   -h, --help                  show this dialog
   -d, --debug                 enable additional logging output
-       -j, --json                  format output as JSON
+       -j, --json                  gather all results and output them at once as JSON
   -e, --effort=<value>        increase demand on GPU processing
   -t, --threshold=<value>     override the minimum threshold value
   -v, --validate=<value>      check an existing work value instead of searching for one
 
 If validating a nonce, it must be a 16-character hexadecimal value.
 Effort must be a decimal number between 1-32.
-Threshold must be a hexadecimal string between 0-FFFFFFFF.
+Threshold must be a hexadecimal string between 1-FFFFFFFFFFFFFFFF.
 
 Report bugs: <bug-nano-pow@zoso.dev>
 Full documentation: <https://www.npmjs.com/package/nano-pow>
index aa67ac23ded98f7cddd6a7a53dd717e2870e483d..2c0ff2bc8199437bb591f8ef80cd7e80809e56cf 100644 (file)
@@ -12,12 +12,19 @@ nano-pow \- proof-of-work generation and validation for Nano cryptocurrency
 .SH DESCRIPTION
 Generate work for \fIBLOCKHASH\fR, or multiple work values for \fIBLOCKHASH\fR(es).
 .PP
+If the \fI--server\fR option is provided as the first argument, all other options are ignored and the NanoPow server is started.
+.PP
 \fIBLOCKHASH\fR is a 64-character hexadecimal string. Multiple blockhashes must be separated by whitespace or line breaks.
 .PP
-Prints a 16-character hexadecimal work value to standard output. If \fB--validate\fR is used, prints 'true' or 'false' to standard output.
+Prints a 16-character hexadecimal work value, the BLAKE2b result, and the originating hash to standard output as a Javascript object.
+.PP
+If \fB--validate\fR is used, the original work value is returned instead along with validation flags.
 
 .SH OPTIONS
 .TP
+\fB--server\fR
+Start work server (see SERVER below). Must be the first argument in order to be recognized.
+.TP
 \fB\-h\fR, \fB\-\-help\fR
 Show this help dialog and exit.
 .TP
@@ -25,20 +32,64 @@ Show this help dialog and exit.
 Enable additional logging output.
 .TP
 \fB\-j\fR, \fB\-\-json\fR
-Format output as JSON.
+Format final output of all hashes as a JSON array of stringified values instead of incrementally returning Javascript objects for each result.
 .TP
 \fB\-e\fR, \fB\-\-effort\fR=\fIEFFORT\fR
 Increase demand on GPU processing. Must be between 1 and 32 inclusive.
 .TP
 \fB\-t\fR, \fB\-\-threshold\fR=\fITHRESHOLD\fR
-Override the minimum threshold value. Higher values increase difficulty. Must be a hexadecimal string between 0 and FFFFFFFF inclusive.
+Override the minimum threshold value. Higher values increase difficulty. Must be a hexadecimal string between 1 and FFFFFFFFFFFFFFFF inclusive.
 .TP
 \fB\-v\fR, \fB\-\-validate\fR=\fIWORK\fR
-Check an existing work value instead of searching for one.
+Check an existing work value instead of searching for one. If you pass multiple blockhashes, the work value will be validated against each one.
+
+.SH SERVER
+Calling \fBnano-pow\fR with the \fI--server\fR option will start the NanoPow work server in a detached process.
+.PP
+It provides work generation and validation via HTTP requests in a similar, but not identical, fashion as the official Nano node.
+.PP
+More specifically, it does not support the \fIuse_peers\fR, \fImultiplier\fR, \fIaccount\fR, \fIversion\fR, \fIblock\fR, and \fIjson_block\fR options.
+.PP
+By default, the server listens on port 3000. To use a different port, set the \fBPORT\fR environment variable before starting the server.
+
+.PP
+.EX
+$ PORT=8080 nano-pow --server
+.EE
+
+.PP
+The server process ID (PID) is saved to \fB~/.nano-pow/server.pid\fR. Log files are stored in \fB~/.nano-pow/logs/\fR.
+.TP
+To stop the server, terminate it using its PID.
+.EX
+$ cat ~/.nano-pow/server.pid
+12345
+$ kill 12345
+.EE
+.TP
+Alternatively, use \fBpgrep\fR to find the PID by process name:
+.EX
+$ pgrep "NanoPow Server"
+12345
+$ kill $(pgrep "NanoPow Server")
+.EE
+
+.PP
+The server accepts HTTP \fBPOST\fR requests to the server's root path (\fB/\fR). Requests should be in JSON format and include an \fBaction\fR field to specify the desired operation.
+.PP
+The following actions are supported:
+.TP
+\fBwork_generate\fR
+Generate a work value for a given blockhash. Requires a \fBhash\fR field containing the 64-character hexadecimal blockhash.
+.TP
+\fBwork_validate\fR
+Validate an existing work value against a blockhash. Requires \fBwork\fR field containing the 16-character hexadecimal work value and a \fBhash\fR field containing the 64-character hexadecimal blockhash.
+.PP
+
 
-.SH EXAMPLES
+.SH EXAMPLES - CLI
 .PP
-Search for a work nonce for a blockhash using the default threshold 0xFFFFFFF8:
+Search for a work nonce for a blockhash using the default threshold 0xFFFFFFF800000000:
 .EX
 $ nano-pow \fB0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\fR
 .EE
@@ -46,7 +97,7 @@ $ nano-pow \fB0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\f
 .PP
 Search for a work nonce using a custom threshold and increased effort:
 .EX
-$ nano-pow \fB\-t fffffe00 \-e 32 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\fR
+$ nano-pow \fB\-t fffffe0000000000 \-e 32 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\fR
 .EE
 
 .PP
@@ -61,6 +112,26 @@ Validate an existing work nonce against a blockhash and show debugging output:
 $ nano-pow \fB\-d \-v fedcba9876543210 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\fR
 .EE
 
+.SH EXAMPLES - SERVER
+.PP
+Generate a work value:
+.EX
+$ curl -d '{
+    "action": "work_generate",
+    "hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+}' localhost:3000
+.EE
+
+.PP
+Validate a work value:
+.EX
+$ curl -d '{
+    "action": "work_validate",
+    "work": "e45835c3b291c3d1",
+    "hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+}' localhost:3000
+.EE
+
 .SH AUTHOR
 Written by Chris Duncan.
 
index 9d0f518a117a8e09295fb988db659c8269a8eac2..5181b65894e8efb8b97d472a82b070d9814c460e 100755 (executable)
@@ -2,6 +2,7 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 /// <reference types="@webgpu/types" />
+
 import * as crypto from 'node:crypto'
 import * as fs from 'node:fs/promises'
 import * as readline from 'node:readline/promises'
index c0a4e4aa3e810cad6fd3fa104624b5297113179c..4337ead4b0eadae7bf9426dc24806bbfa043aa9f 100755 (executable)
@@ -10,7 +10,7 @@ NANO_POW_LOGS="$NANO_POW_HOME"/logs;
 mkdir -p "$NANO_POW_LOGS";
 if [ "$1" = '--server' ]; then
        shift;
-       node "$SCRIPT_DIR"/server.js "$@" > "$NANO_POW_LOGS"/nano-pow-server-$(date +%s).log 2>&1 & echo "$!" > "$NANO_POW_HOME"/server.pid;
+       node "$SCRIPT_DIR"/server.js > "$NANO_POW_LOGS"/nano-pow-server-$(date +%s).log 2>&1 & echo "$!" > "$NANO_POW_HOME"/server.pid;
 else
        node "$SCRIPT_DIR"/cli.js "$@";
 fi;
index 834617f290743d3f3990abb5cd48f1ab6899b1d8..9f4078f407719dd24a30b3391a23d60ed6b91eff 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/env node
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
+
 import * as crypto from 'node:crypto'
 import * as dns from 'node:dns/promises'
 import * as fs from 'node:fs/promises'
@@ -11,6 +12,7 @@ import { serverHelp } from '../../docs/index.js'
 import { NanoPowOptions, WorkGenerateRequest, WorkGenerateResponse, WorkValidateRequest, WorkValidateResponse } from '../types.js'
 
 const PORT = process.env.PORT || 3000
+const EFFORT = +(process.env.NANO_POW_EFFORT || 8)
 
 function log (...args) {
        console.log(new Date(Date.now()).toLocaleString(), 'NanoPow', args)
@@ -37,14 +39,11 @@ async function work_generate (res: http.ServerResponse, json: WorkGenerateReques
        }
 
        try {
-               const result = await page.evaluate(async (args: WorkGenerateRequest): Promise<WorkGenerateResponse> => {
-                       const options: NanoPowOptions = {
-                               debug: true
-                       }
-                       if (args.difficulty) options.threshold = BigInt(`0x${args.difficulty}`)
+               const result = await page.evaluate(async (json: WorkGenerateRequest, options: NanoPowOptions): Promise<WorkGenerateResponse> => {
+                       if (json.difficulty) options.threshold = BigInt(`0x${json.difficulty}`)
                        // @ts-expect-error
-                       return await window.NanoPow.work_generate(args.hash, options)
-               }, json)
+                       return await window.NanoPow.work_generate(json.hash, options)
+               }, json, { debug: true, effort: EFFORT })
                res.writeHead(200, { 'Content-Type': 'application/json' })
                res.end(JSON.stringify(result))
        } catch (err) {
@@ -72,14 +71,11 @@ async function work_validate (res: http.ServerResponse, json: WorkValidateReques
        }
 
        try {
-               const result: WorkValidateResponse = await page.evaluate(async (args: WorkValidateRequest): Promise<WorkValidateResponse> => {
-                       const options: NanoPowOptions = {
-                               debug: true
-                       }
-                       if (args.difficulty) options.threshold = BigInt(`0x${args.difficulty}`)
+               const result: WorkValidateResponse = await page.evaluate(async (json: WorkValidateRequest, options: NanoPowOptions): Promise<WorkValidateResponse> => {
+                       if (json.difficulty) options.threshold = BigInt(`0x${json.difficulty}`)
                        //@ts-expect-error
-                       return await window.NanoPow.work_validate(args.work, args.hash, options)
-               }, json)
+                       return await window.NanoPow.work_validate(json.work, json.hash, options)
+               }, json, { debug: true, effort: EFFORT })
                res.writeHead(200, { 'Content-Type': 'application/json' })
                res.end(JSON.stringify(result))
        } catch (err) {
index 4aea454a5e7060b07508c63d2500af0e82732546..6376fe9343931bee29b5f006759f50dc62aa1995 100755 (executable)
@@ -1,38 +1,39 @@
 # SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+export NANO_POW_EFFORT=24
 SCRIPT_LINK=$(readlink -f "$0");
 SCRIPT_DIR=$(dirname "$SCRIPT_LINK");
 NANO_POW_HOME="$HOME"/.nano-pow;
 NANO_POW_LOGS="$NANO_POW_HOME"/logs;
 
-"$SCRIPT_DIR"/../dist/bin/nano-pow.sh --server
+PORT=3001 "$SCRIPT_DIR"/../dist/bin/nano-pow.sh --server
 sleep 2s
 
 printf '\nGet documentation\n'
-curl localhost:3000
+curl localhost:3001
 
 printf '\nExpect error. Server should not crash when bad data is received like missing end quote\n'
-curl -d '{ "action": "work_validate", "work": "47c83266398728cf", "hash: "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D }' localhost:3000
+curl -d '{ "action": "work_validate", "work": "47c83266398728cf", "hash: "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D }' localhost:3001
 
 printf '\nValidate good hashes\n'
-curl -d '{ "action": "work_validate", "work": "47c83266398728cf", "hash": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "4a8fb104eebbd336", "hash": "8797585D56B8AEA3A62899C31FC088F9BE849BA8298A88E94F6E3112D4E55D01" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "326f310d629a8a98", "hash": "204076E3364D16A018754FF67D418AB2FBEB38799FF9A29A1D5F9E34F16BEEEA", "difficulty": "ffffffff00000000" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "c5d5d6f7c5d6ccd1", "hash": "281E89AC73B1082B464B9C3C1168384F846D39F6DF25105F8B4A22915E999117" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "6866c1ac3831a891", "hash": "7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090", "difficulty": "fffffe0000000000" }' localhost:3000
+curl -d '{ "action": "work_validate", "work": "47c83266398728cf", "hash": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "4a8fb104eebbd336", "hash": "8797585D56B8AEA3A62899C31FC088F9BE849BA8298A88E94F6E3112D4E55D01" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "326f310d629a8a98", "hash": "204076E3364D16A018754FF67D418AB2FBEB38799FF9A29A1D5F9E34F16BEEEA", "difficulty": "ffffffff00000000" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "c5d5d6f7c5d6ccd1", "hash": "281E89AC73B1082B464B9C3C1168384F846D39F6DF25105F8B4A22915E999117" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "6866c1ac3831a891", "hash": "7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090", "difficulty": "fffffe0000000000" }' localhost:3001
 
 printf '\nValidate bad hashes\n'
-curl -d '{ "action": "work_validate", "work": "0000000000000000", "hash": "0000000000000000000000000000000000000000000000000000000000000000" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "c5d5d6f7c5d6ccd1", "hash": "BA1E946BA3D778C2F30A83D44D2132CC6EEF010D8D06FF10A8ABD0100D8FB47E" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "ae238556213c3624", "hash": "BF41D87DA3057FDC6050D2B00C06531F89F4AA6195D7C6C2EAAF15B6E703F8F6", "difficulty": "ffffffff00000000" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "29a9ae0236990e2e", "hash": "32721F4BD2AFB6F6A08D41CD0DF3C0D9C0B5294F68D0D12422F52B28F0800B5F" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "7d903b18d03f9820", "hash": "39C57C28F904DFE4012288FFF64CE80C0F42601023A9C82108E8F7B2D186C150", "difficulty": "fffffe0000000000" }' localhost:3000
-curl -d '{ "action": "work_validate", "work": "e45835c3b291c3d1", "hash": "9DCD89E2B92FD59D7358C2C2E4C225DF94C88E187B27882F50FEFC3760D3994F", "difficulty": "ffffffff00000000" }' localhost:3000
+curl -d '{ "action": "work_validate", "work": "0000000000000000", "hash": "0000000000000000000000000000000000000000000000000000000000000000" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "c5d5d6f7c5d6ccd1", "hash": "BA1E946BA3D778C2F30A83D44D2132CC6EEF010D8D06FF10A8ABD0100D8FB47E" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "ae238556213c3624", "hash": "BF41D87DA3057FDC6050D2B00C06531F89F4AA6195D7C6C2EAAF15B6E703F8F6", "difficulty": "ffffffff00000000" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "29a9ae0236990e2e", "hash": "32721F4BD2AFB6F6A08D41CD0DF3C0D9C0B5294F68D0D12422F52B28F0800B5F" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "7d903b18d03f9820", "hash": "39C57C28F904DFE4012288FFF64CE80C0F42601023A9C82108E8F7B2D186C150", "difficulty": "fffffe0000000000" }' localhost:3001
+curl -d '{ "action": "work_validate", "work": "e45835c3b291c3d1", "hash": "9DCD89E2B92FD59D7358C2C2E4C225DF94C88E187B27882F50FEFC3760D3994F", "difficulty": "ffffffff00000000" }' localhost:3001
 
 
 printf '\nGenerate\n'
-curl -d '{ "action": "work_generate", "hash": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D" }' localhost:3000
-curl -d '{ "action": "work_generate", "hash": "204076E3364D16A018754FF67D418AB2FBEB38799FF9A29A1D5F9E34F16BEEEA", "difficulty": "ffffffff00000000" }' localhost:3000
-curl -d '{ "action": "work_generate", "hash": "7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090", "difficulty": "fffffe0000000000" }' localhost:3000
+curl -d '{ "action": "work_generate", "hash": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D" }' localhost:3001
+curl -d '{ "action": "work_generate", "hash": "204076E3364D16A018754FF67D418AB2FBEB38799FF9A29A1D5F9E34F16BEEEA", "difficulty": "ffffffff00000000" }' localhost:3001
+curl -d '{ "action": "work_generate", "hash": "7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090", "difficulty": "fffffe0000000000" }' localhost:3001
 kill $(cat "$HOME"/.nano-pow/server.pid) && rm "$HOME"/.nano-pow/server.pid