* Do not send any funds to the test vectors below!
*
* Sources:
+* https://docs.nano.org/integration-guides/the-basics/#seed
* https://docs.nano.org/integration-guides/key-management/#test-vectors
* https://docs.nano.org/integration-guides/key-management/#creating-transactions
* https://github.com/trezor/python-mnemonic/blob/master/vectors.json
* https://tools.nanos.cc/?tool=seed
+* https://github.com/BLAKE2/BLAKE2/blob/master/testvectors/blake2b-kat.txt
+* https://github.com/emilbayes/blake2b/blob/master/test-vectors.json#L514-L577
*/
export const GENESIS_ADDRESS = 'nano_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3'
export const RAW_MAX = '340282366920938463463374607431768211455'
export const SUPPLY_MAX = '133248297920938463463374607431768211455'
export const NANO_TEST_VECTORS = Object.freeze({
+ // from nano.org python sample code
+ BLAKE2B_SEED: '0000000000000000000000000000000000000000000000000000000000000001',
+ BLAKE2B_PRIVATE_1: '1495F2D49159CC2EAAAA97EBB42346418E1268AFF16D7FCA90E6BAD6D0965520',
+
+ // from nano.org test vectors
MNEMONIC: 'edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur',
PASSWORD: 'some password',
BIP39_SEED: '0DC285FDE768F7FF29B66CE7252D56ED92FE003B605907F7A4F683C3DC8586D34A914D3C71FC099BB38EE4A59E5B081A3497B7A323E90CC68F67B5837690310C',
PUBLIC_2: 'A46DA51986E25A14D82E32D765DCEE69B9EECCD4405411430D91DDB61B717566',
ADDRESS_2: 'nano_3b5fnnerfrkt4me4wepqeqggwtfsxu8fai4n473iu6gxprfq4xd8pk9gh1dg',
+ // from nano.org transaction examples
SEND_BLOCK: {
account: "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx",
previous: "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
ADDRESS_3: "nano_1ddgc1m71fpyef9wg8jhmhnc8fodm6frgng3dp65om8s3hymqufp8jefijxu",
})
+// from blake2b by Emil Bay who derived them using libsodium
export const BLAKE2B_TEST_VECTORS = Object.freeze({
LIBSODIUM: [
{ "outlen": 1, "out": "ba", "in": "", "key": "00", "salt": "35623662343165643962333433666530", "personal": "35313236666232613337343030643261" },
{ "outlen": 63, "out": "19c14de35fe19c92cc0e624280e4136355d4cfa9a0a98b090c4b06f5665021920725852ff1f566b0c8c37157b25fb9f947a2e70b40577a17860a0732c170ac", "in": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d", "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e", "salt": "35623662343165643962333433666530", "personal": "35313236666232613337343030643261" },
{ "outlen": 64, "out": "5fcdcc02be7714a0dbc77df498bf999ea9225d564adca1c121c9af03af92cac8177b9b4a86bcc47c79aa32aac58a3fef967b2132e9352d4613fe890beed2571b", "in": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e", "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "salt": "35623662343165643962333433666530", "personal": "35313236666232613337343030643261" }
],
+
+ // from BLAKE2 reference C implementation
REFERENCE: [
{
"in": "",
import { NANO_TEST_VECTORS } from './VECTORS.js'\r
import { SendBlock, ReceiveBlock, ChangeBlock } from '../dist/main.min.js'\r
\r
-await suite('Valid blocks', async () => {\r
+await suite('Block format', async () => {\r
\r
- await test('should not allow negative balances', async () => {\r
+ await test('throw on negative balances', async () => {\r
assert.throws(() => {\r
new SendBlock(\r
NANO_TEST_VECTORS.ADDRESS_0,\r
}, { message: 'Negative balance' })\r
})\r
\r
- await test('should allow zero balances', async () => {\r
+ await test('allow zero balances', async () => {\r
const block = new SendBlock(\r
NANO_TEST_VECTORS.ADDRESS_0,\r
'9007199254740991',\r
assert.equals(block.balance, BigInt(0))\r
})\r
\r
- await test('should subtract balance from SendBlock correctly', async () => {\r
+ await test('subtract balance from SendBlock correctly', async () => {\r
const block = new SendBlock(\r
NANO_TEST_VECTORS.ADDRESS_0,\r
'3000000000000000000000000000000',\r
assert.equals(block.balance, 1000000000000000000000000000000n)\r
})\r
\r
- await test('should add balance from ReceiveBlock correctly', async () => {\r
+ await test('add balance from ReceiveBlock correctly', async () => {\r
const block = new ReceiveBlock(\r
NANO_TEST_VECTORS.ADDRESS_0,\r
'2000000000000000000000000000000',\r
})\r
})\r
\r
-await suite('Block signing tests using official test vectors', async () => {\r
+await suite('Block signing using official test vectors', async () => {\r
\r
- await test('should create a valid signature for an open block', async () => {\r
+ await test('sign open block', async () => {\r
const block = new ReceiveBlock(\r
NANO_TEST_VECTORS.OPEN_BLOCK.account,\r
'0',\r
assert.equals(block.signature, NANO_TEST_VECTORS.OPEN_BLOCK.signature)\r
})\r
\r
- await test('should create a valid signature for a receive block', async () => {\r
+ await test('sign receive block', async () => {\r
const block = new ReceiveBlock(\r
NANO_TEST_VECTORS.RECEIVE_BLOCK.account,\r
NANO_TEST_VECTORS.RECEIVE_BLOCK.balance,\r
assert.equals(block.signature, NANO_TEST_VECTORS.RECEIVE_BLOCK.signature)\r
})\r
\r
- await test('should create a valid signature for a receive block without work', async () => {\r
+ await test('sign receive block without work', async () => {\r
const block = new ReceiveBlock(\r
NANO_TEST_VECTORS.RECEIVE_BLOCK.account,\r
NANO_TEST_VECTORS.RECEIVE_BLOCK.balance,\r
assert.equals(block.work, '')\r
})\r
\r
- await test('should create a valid signature for a send block', async () => {\r
+ await test('sign send block', async () => {\r
const block = new SendBlock(\r
NANO_TEST_VECTORS.SEND_BLOCK.account,\r
NANO_TEST_VECTORS.SEND_BLOCK.balance,\r
assert.equals(block.signature, NANO_TEST_VECTORS.SEND_BLOCK.signature)\r
})\r
\r
- await test('should create a valid signature for a send block without work', async () => {\r
+ await test('sign send block without work', async () => {\r
const block = new SendBlock(\r
NANO_TEST_VECTORS.SEND_BLOCK.account,\r
NANO_TEST_VECTORS.SEND_BLOCK.balance,\r
assert.equals(block.work, '')\r
})\r
\r
- await test('should create a valid signature for a change rep block', async () => {\r
+ await test('sign change rep block', async () => {\r
const work = '0000000000000000'\r
const block = new ChangeBlock(\r
'nano_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php',\r
assert.equals(block.work, work)\r
})\r
\r
- await test('should create a valid signature for a change rep block without work', async () => {\r
+ await test('sign change rep block without work', async () => {\r
const block = new ChangeBlock(\r
NANO_TEST_VECTORS.ADDRESS_0,\r
'0',\r
import { NANO_TEST_VECTORS } from './VECTORS.js'\r
import { Bip44Wallet, Blake2bWallet } from '../dist/main.min.js'\r
\r
-await suite('BIP-44 account derivation', async () => {\r
- await test('should derive the first account from the given BIP-44 seed', async () => {\r
+await suite('Derive accounts from BIP-44 wallet', async () => {\r
+\r
+ await test('derive the first account from the given BIP-44 seed', async () => {\r
const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED)\r
await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
const account = await wallet.account()\r
assert.equals(privateKey, NANO_TEST_VECTORS.PRIVATE_0)\r
assert.equals(account.publicKey, NANO_TEST_VECTORS.PUBLIC_0)\r
assert.equals(account.address, NANO_TEST_VECTORS.ADDRESS_0)\r
+ assert.equals(account.index, 0)\r
\r
const accounts = await wallet.accounts()\r
+ assert.exists(accounts[0])\r
assert.equals(account, accounts[0])\r
\r
await wallet.destroy()\r
})\r
\r
- await test('should derive low indexed accounts from the given BIP-44 seed', async () => {\r
+ await test('derive low indexed accounts from the given BIP-44 seed', async () => {\r
const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED)\r
await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
const accounts = await wallet.accounts(1, 2)\r
assert.equals(privateKey1, NANO_TEST_VECTORS.PRIVATE_1)\r
assert.equals(accounts[1].publicKey, NANO_TEST_VECTORS.PUBLIC_1)\r
assert.equals(accounts[1].address, NANO_TEST_VECTORS.ADDRESS_1)\r
+ assert.equals(accounts[1].index, 1)\r
assert.equals(privateKey2, NANO_TEST_VECTORS.PRIVATE_2)\r
assert.equals(accounts[2].publicKey, NANO_TEST_VECTORS.PUBLIC_2)\r
assert.equals(accounts[2].address, NANO_TEST_VECTORS.ADDRESS_2)\r
+ assert.equals(accounts[2].index, 2)\r
\r
await wallet.destroy()\r
})\r
\r
- await test('should derive high indexed accounts from the given seed', async () => {\r
+ await test('derive high indexed accounts from the given seed', async () => {\r
const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED)\r
await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
const accounts = await wallet.accounts(0x70000000, 0x7000000f)\r
for (let i = 0x70000000; i < 0x7000000f; i++) {\r
const a = accounts[i]\r
assert.exists(a)\r
+ assert.equals(a.index, i)\r
assert.exists(a.address)\r
assert.exists(a.publicKey)\r
const privateKey = await a.exportPrivateKey(wallet.seed, 'hex')\r
})\r
})\r
\r
-await suite('BLAKE2b account derivation', async () => {\r
- await test('should derive accounts for a BLAKE2b wallet', async () => {\r
- const wallet = await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD)\r
+await suite('Derive accounts from BLAKE2b wallet', async () => {\r
+\r
+ await test('derive the second account from the given BLAKE2b seed', async () => {\r
+ const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BLAKE2B_SEED)\r
await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
- const lowAccounts = await wallet.accounts(0, 2)\r
+ const account = await wallet.account(1)\r
+ const privateKey = await account.exportPrivateKey(wallet.seed, 'hex')\r
\r
- assert.equals(lowAccounts.length, 3)\r
- for (const a of lowAccounts) {\r
- assert.exists(a)\r
- assert.exists(a.address)\r
- assert.exists(a.publicKey)\r
- const privateKey = await a.exportPrivateKey(wallet.seed, 'hex')\r
- assert.exists(privateKey)\r
- }\r
+ assert.equals(privateKey, NANO_TEST_VECTORS.BLAKE2B_PRIVATE_1)\r
+ // assert.equals(account.publicKey, NANO_TEST_VECTORS.BLAKE2B_PUBLIC_0)\r
+ // assert.equals(account.address, NANO_TEST_VECTORS.BLAKE2B_ADDRESS_0)\r
+ assert.equals(account.index, 1)\r
+\r
+ const accounts = await wallet.accounts(1)\r
+ assert.exists(accounts[1])\r
+ assert.equals(account, accounts[1])\r
+\r
+ await wallet.destroy()\r
+ })\r
\r
- const highAccounts = await wallet.accounts(0x70000000, 0x7000000f)\r
+ await test('derive low indexed accounts from the given BLAKE2B seed', async () => {\r
+ const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BLAKE2B_SEED)\r
+ await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
+ const accounts = await wallet.accounts(1, 2)\r
+ // const privateKey1 = await accounts[1].exportPrivateKey(wallet.seed, 'hex')\r
+ // const privateKey2 = await accounts[2].exportPrivateKey(wallet.seed, 'hex')\r
+\r
+ assert.equals(accounts.length, 2)\r
+ // assert.equals(privateKey1, NANO_TEST_VECTORS.BLAKE2B_PRIVATE_1)\r
+ // assert.equals(accounts[1].publicKey, NANO_TEST_VECTORS.BLAKE2B_PUBLIC_1)\r
+ // assert.equals(accounts[1].address, NANO_TEST_VECTORS.BLAKE2B_ADDRESS_1)\r
+ assert.equals(accounts[1].index, 1)\r
+ // assert.equals(privateKey2, NANO_TEST_VECTORS.BLAKE2B_PRIVATE_2)\r
+ // assert.equals(accounts[2].publicKey, NANO_TEST_VECTORS.BLAKE2B_PUBLIC_2)\r
+ // assert.equals(accounts[2].address, NANO_TEST_VECTORS.BLAKE2B_ADDRESS_2)\r
+ assert.equals(accounts[2].index, 2)\r
+\r
+ await wallet.destroy()\r
+ })\r
\r
- assert.equals(highAccounts.length, 0x10)\r
- for (const a of highAccounts) {\r
+ await test('derive high indexed accounts from the given seed', async () => {\r
+ const wallet = await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BLAKE2B_SEED)\r
+ await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
+ const accounts = await wallet.accounts(0x70000000, 0x7000000f)\r
+\r
+ assert.equals(accounts.length, 0x10)\r
+ for (let i = 0x70000000; i < 0x7000000f; i++) {\r
+ const a = accounts[i]\r
assert.exists(a)\r
+ assert.equals(a.index, i)\r
assert.exists(a.address)\r
assert.exists(a.publicKey)\r
const privateKey = await a.exportPrivateKey(wallet.seed, 'hex')\r
await assert.rejects(Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_1.replace(/./, 'g')),\r
'Seed contains invalid hexadecimal characters.')\r
})\r
-})\r
-\r
-await suite('Retrieve wallets from session storage using a wallet-generated ID', async () => {\r
\r
- await test('Bip44Wallet', async () => {\r
+ await test('Import BIP-44 wallet from session storage using a wallet-generated ID', async () => {\r
const id = (await Bip44Wallet.fromMnemonic(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.MNEMONIC, NANO_TEST_VECTORS.PASSWORD)).id\r
const wallet = await Bip44Wallet.restore(id)\r
\r
await wallet.destroy()\r
})\r
\r
- await test('Blake2bWallet', async () => {\r
+ await test('Import BLAKE2B wallet from session storage using a wallet-generated ID', async () => {\r
const id = (await Blake2bWallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, TREZOR_TEST_VECTORS.ENTROPY_0)).id\r
const wallet = await Blake2bWallet.restore(id)\r
\r
// SPDX-License-Identifier: GPL-3.0-or-later
import './test.blake2b.mjs'
+import './test.blocks.mjs'
import './test.calculate-pow.mjs'
import './test.create-wallet.mjs'
import './test.derive-accounts.mjs'
import './test.lock-unlock.mjs'
import './test.manage-rolodex.mjs'
import './test.refresh-accounts.mjs'
-import './test.sign-blocks.mjs'
import './test.tools.mjs'
console.log('%cTESTING COMPLETE', 'color:orange;font-weight:bold')
const rpc = new Rpc(process.env.NODE_URL ?? '', process.env.API_KEY_NAME)
-await suite('refreshing account info', { skip: true }, async () => {
+await suite('Refreshing account info', { skip: true }, async () => {
await test('fetch balance, frontier, and representative', async () => {
const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED)