<!--
-SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
+SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
SPDX-License-Identifier: GPL-3.0-or-later
-->
-# libnemo
-libnemo is a fork of the nanocurrency-web toolkit. It is used for client-side
-implementations of Nano cryptocurrency wallets and enables building web-based
-applications that can work even while offline. libnemo supports managing
-wallets, deriving accounts, signing blocks, and more.
+# nano-pow
+NanoPow uses WebGPU to generate proof-of-work nonces meeting the requirements
+of the Nano cryptocurrency. WebGPU is cutting edge technology, so for browsers
+which do not yet support it, a WebGL 2.0 implementation is also included.
-It utilizes the Web Crypto API which is native to all modern browsers; as such,
-it has only a required dependency in order to work with the BLAKE2b algorithm.
-Optionally, Ledger device dependencies can be installed to enable Ledger
-hardware wallet support.
-
-## Features
-* Generate new BIP-32 hierarchial deterministic (HD) wallets with a BIP-39
-mnemonic phrase and the Nano path registered with BIP-44. Used by Ledger
-hardware wallet.
-* Generate new BLAKE2b wallets with a BIP-39 mnemonic phrases. Original method
-described by nano spec.
-* Import wallets with a mnemonic phrase or a seed.
-* Derive indexed accounts with a Nano address and a public-private keypair.
-* Create, sign, and verify send, receive, and change blocks.
-* Get account info and process blocks on the network while online.
-* Manage known addresses with a rolodex.
-* Sign and verify arbitrary strings with relevant keys.
-* Validate entropy, seeds, mnemonic phrases, and Nano addresses.
-* Convert Nano unit denominations.
-* Run in modern web browsers and mobile frameworks built with Javascript without
-server-side NodeJS functions.
+All calculations take place client-side, so nonces can be generated offline and
+cached for the next transaction block. For more information about the
+proof-of-work equation defined by Nano, see
+https://docs.nano.org/integration-guides/work-generation/#work-calculation-details
## Installation
-### From NPM
```console
-npm install libnemo
+npm i nano-pow
```
## Usage
-#### ⚠️ The examples below should never be used for real transactions! ⚠️
-
-### Wallets and accounts
-At its core, a wallet is a hexadecimal string called a seed. From this seed,
-millions of unique accounts can be deterministically derived. The first account
-in a wallet starts at index 0.
-
-For clarity, the following terms are used throughout the library:
- * BIP-32 - Defines how hierarchical determinstic (HD) wallets are generated
- * BIP-39 - Defines how mnemonic phrases are generated
- * BIP-44 - Expands on BIP-32 to define how an enhanced derivation path can
- allow a single wallet to store multiple currencies
-
-libnemo is able to generate and import HD and BLAKE2b wallets, and it can derive
-accounts for both. An HD wallet seed is 128 characters while a BLAKE2b wallet
-seed is 64 characters. For enhanced security, libnemo requires a password to
-create or import wallets, and wallets are initialized in a locked state. More
-advanced implementations can provide their own CryptoKey instead of a password.
-Refer to the documentation on each class factory method for specific usage.
-
-```javascript
-import { Bip44Wallet, Blake2bWallet } from 'libnemo'
-
-const wallet = await Bip44Wallet.create(password)
-const wallet = await Bip44Wallet.fromEntropy(password, entropy, salt?)
-const wallet = await Bip44Wallet.fromMnemonic(password, mnemonic, salt?)
-const wallet = await Bip44Wallet.fromSeed(password, seed)
-
-const wallet = await Bip44Wallet.create(password)
-const wallet = await Bip44Wallet.fromSeed(password, seed)
-const wallet = await Bip44Wallet.fromMnemonic(password, mnemonic)
-```
```javascript
-try {
- const unlockResult = await wallet.unlock(password)
-} catch(err) {
- console.log(err)
-}
-console.log(unlockResult) // true if successfully unlocked
+import { NanoPowGpu, NanoPowGl } from 'nano-pow'
-const { mnemonic, seed } = wallet
-
-const accounts = await wallet.accounts(from?, to?)
-const firstAccount = accounts[0]
-const { address, publicKey, privateKey, index } = firstAccount
-
-const nodeUrl = 'https://nano-node.example.com/'
-await firstAccount.refresh(nodeUrl) // online
-const { frontier, balance, representative } = firstAccount
+// `hash` is a 64-char hex string
+// `threshold` is optional and defaults to "0xFFFFFFF8"
+const workGpu = await NanoPowGpu.search(hash, threshold)
+const workGl = await NanoPowGl.search(hash, threshold)
```
-### Blocks
-Blocks do not contain transaction amounts. Instead, they contain stateful
-balance changes only. For example, if sending Ӿ5 from an account with a balance
-of Ӿ20, the send block would contain `balance: Ӿ15` (psuedocode for
-demonstration purposes and not a literal depiction). This can be difficult to
-track, so libnemo provides the convenience of specifying an amount to send or
-receive and calculates the balance change itself.
-
-All blocks are 'state' types, but they are interpreted as one of three different
-subtypes based on the data they contain: send, receive, or change
-representative. libnemo implements them as the following classes:
+## Notes
+The `work` field in a Nano transaction block contains an 8-byte nonce that
+satisfies the following equation:
-* SendBlock: the Nano balance of the account decreases
-* ReceivBlock: the Nano balance of the account increases and requires a matching
-SendBlock
-* ChangeBlock: the representative for the account changes while the Nano balance
-does not
+> 𝘣𝘭𝘢𝘬𝘦2𝘣(𝘯𝘰𝘯𝘤𝘦 || 𝘣𝘭𝘰𝘤𝘬𝘩𝘢𝘴𝘩) ≥ 𝘵𝘩𝘳𝘦𝘴𝘩𝘰𝘭𝘥
-_Nano protocol allows changing the representative at the same time as a balance
-change. libnemo does not implement this for purposes of clarity; all
-ChangeBlock objects will maintain the same Nano balance._
+* 𝘣𝘭𝘢𝘬𝘦2𝘣() is the cryptographic hash function BLAKE2b.
+* 𝘯𝘰𝘯𝘤𝘦, an 8-byte value, is generated for the transaction.
+* || is concatenation.
+* 𝘣𝘭𝘰𝘤𝘬𝘩𝘢𝘴𝘩, a 32-byte value, is either the public key of brand new accounts without transactions or the
+hash of the most recent block in the account chain for all other accounts.
+* 𝘵𝘩𝘳𝘦𝘴𝘩𝘰𝘭𝘥 is 0xFFFFFFF800000000 for send/change blocks and 0xFFFFFE0000000000
+for receive/open/epoch blocks.
-Always fetch the most up to date information for the account from the network
-using the
-[account_info RPC command](https://docs.nano.org/commands/rpc-protocol/#account_info)
-which can then be used to populate the block parameters.
+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 one
+wants to know whether a two-digit number xx > 55, and you know the first digit is
+6, you automatically know it is greather than 55.
-Blocks require a small proof-of-work that must be calculated for the block to be
-accepted by the network. This can be provided when creating the block, or a
-public node that allows the
-[work_generate RPC command](https://docs.nano.org/commands/rpc-protocol/#work_generate)
-can be used.
+The default threshold used is the send/change difficulty. Any other threshold
+can be specified in practice. Also, NanoPow technically compares the nonce to see if it is only greater than, and not necessarily equal to, the threshold, but the difference is trivial for a range of 2⁶⁴-1 nonces.
-Finally, the block must be signed with the private key of the account. libnemo
-enables this to be done offline if desired. After being signed, the block can
-be published to the network with the
-[process RPC command](https://docs.nano.org/commands/rpc-protocol/#process).
+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.
-#### Creating blocks
-```javascript
-import { SendBlock, ReceiveBlock, ChangeBlock } from 'libnemo'
-
-const send = new SendBlock(
- 'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d', // sender
- '5618869000000000000000000000000', // current balance
- 'nano_3phqgrqbso99xojkb1bijmfryo7dy1k38ep1o3k3yrhb7rqu1h1k47yu78gz', // recipient
- '2000000000000000000000000000000', // amount to send
- 'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou', // representative
- '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D', // hash of previous block
- 'fbffed7c73b61367' // PoW nonce (optional at first but required to process)
-)
-
-const receive = new ReceiveBlock(
- 'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d', // recipient
- '18618869000000000000000000000000', // current balance
- 'CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783', // origin (hash of matching send block)
- '7000000000000000000000000000000', // amount that was sent
- 'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou', // representative
- '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D', // hash of previous block
- 'c5cf86de24b24419' // PoW nonce (optional at first but required to process)
-)
-
-const change = new ChangeBlock(
- 'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d', // account redelegating vote weight
- '3000000000000000000000000000000', // current balance
- 'nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs', // new representative
- '128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4', // hash of previous block
- '0000000000000000' // PoW nonce (optional at first but required to process)
-)
-```
-
-#### Signing a block
-```javascript
-const privateKey = '3BE4FC2EF3F3B7374E6FC4FB6E7BB153F8A2998B3B3DAB50853EABE128024143'
-try {
- await block.sign(privateKey)
-} catch (err) {
- console.log(err)
-}
-```
-
-#### Caculating proof-of-work from an online service
-```javascript
-const node = new Rpc('https://nano-node.example.com/')
-try {
- await block.pow('https://nano-node.example.com/')
-} catch (err) {
- console.log(err)
-}
-```
-
-#### Processing a block on the network
-```javascript
-const node = new Rpc('https://nano-node.example.com', 'nodes-api-key')
-try {
- const hash = await block.process('https://nano-node.example.com/')
-} catch (err) {
- console.log(err)
-}
-```
-
-### Tools
-#### Converting Nano denominations
-Raw values are the native unit of exchange throughout libnemo and are
-represented by the primitive bigint data type. Other supported denominations
-are as follows:
-| Unit | Raw |
-|-------|-----|
-| RAI | 10<sup>24</sup> raw |
-| NYANO | 10<sup>24</sup> raw |
-| KRAI | 10<sup>27</sup> raw |
-| PICO | 10<sup>27</sup> raw |
-| MRAI | 10<sup>30</sup> raw |
-| NANO | 10<sup>30</sup> raw |
-| KNANO | 10<sup>33</sup> raw |
-| MNANO | 10<sup>36</sup> raw |
-
-```javascript
-import { Tools } from 'libnemo'
-// Denominations are case-insensitive
-const oneNanoToRaw = Tools.convert('1', 'NANO', 'RAW') // 1000000000000000000000000000000
-const oneNonillionRawToNano = Tools.convert('1000000000000000000000000000000', 'RAW', 'NANO') // 1
-const oneThousandNyanoToPico = Tools.convert('1000', 'nYaNo', 'pico') //1
-const oneThousandPicoToNano = Tools.convert('1000', 'pico', 'NANO') // 1
-```
-
-#### Verifying signatures and signing anything with the private key
-Since cryptocurrencies like Nano uses asymmetric keys to sign and verify blocks
-and transactions, a Nano account itself can be used to sign arbitrary data
-with its private key and verify signatures from other accounts with their public
-keys.
-
-For example, a client-side login can be implemented by challenging an account
-owner to sign their email address using their private key:
-
-```javascript
-import { Tools } from 'libnemo'
-
-const privateKey = '3BE4FC2EF3F3B7374E6FC4FB6E7BB153F8A2998B3B3DAB50853EABE128024143'
-const publicKey = '5B65B0E8173EE0802C2C3E6C9080D1A16B06DE1176C938A924F58670904E82C4'
-const signature = await Tools.sign(privateKey, 'johndoe@example.com')
-const isValid = await Tools.verify(publicKey, signature, 'johndoe@example.com')
-```
-
-Ownership of a Nano address can also be proven by challenging the account owner
-to sign an arbitrary string and then validating the signature with the Nano
-account address.
-```javascript
-import { Tools } from 'libnemo'
-
-const address = 'nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d'
-const privateKey = '3BE4FC2EF3F3B7374E6FC4FB6E7BB153F8A2998B3B3DAB50853EABE128024143'
-const randomData = new Entropy().hex
-
-const signature = await Tools.sign(privateKey, randomData)
-const publicKey = new Account(address).publicKey
-const isValid = await Tools.verify(publicKey, signature, randomData)
-```
-
-#### Validate a Nano account address
-```javascript
-import { Tools } from 'libnemo'
-
-const valid = Account.validate('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
-```
+## Validating work
+Although the nonces generated by this package have been tested extensively, a
+validation function is not currently implemented. There are other tools
+available to perform this trivial task, but if there is demand for it, then it
+can be delivered in a future update.
## Tests
-Test vectors were retrieved from the following publicly-available locations:
- * Nano (BIP-44): https://docs.nano.org/integration-guides/key-management/#test-vectors
- * Trezor (BIP-39): https://github.com/trezor/python-mnemonic/blob/master/vectors.json
- * BIP-32: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#user-content-Test_Vectors
-Another set of test vectors were created for libnemo based on the Trezor set.
-These extra test vectors were generated purely to test uncommon yet valid
-mnemonic phrase lengths like 15 or 18 words.
-#### ⚠️ The test vectors should never be used for real transactions! ⚠️
+`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.
## Building
-* `npm run build`: compile and build
-* `npm run test`: all of the above, run tests, and print results to the console
-* `npm run test:coverage`: all of the above, calculate code coverage, and print
-code coverage to the console
-* `npm run test:coverage:report`: all of the above, and open an HTML code
-coverage report in the browser (requires lcov and xdg-open)
+* `npm run build`: compile TypeScript, then minify and bundle with esbuild
## Donations
-If you find this library helpful, please consider tipping the developer.
+If you find this package helpful, please consider tipping the developer.
```
nano_1zosoqs47yt47bnfg7sdf46kj7asn58b7uzm9ek95jw7ccatq37898u1zoso
```