From e1dc6752efd276542113d80a9d61bb732cbe586e Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Tue, 12 May 2026 13:24:58 -0700 Subject: [PATCH] Fix Ledger tests. --- test/test.ledger.mjs | 113 +++++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/test/test.ledger.mjs b/test/test.ledger.mjs index e90957c..1004cb4 100644 --- a/test/test.ledger.mjs +++ b/test/test.ledger.mjs @@ -8,6 +8,7 @@ import { assert, click, env, isNode, suite, test } from './GLOBALS.mjs' import { CUSTOM_TEST_VECTORS, NANO_TEST_VECTORS } from './VECTORS.mjs' const rpc = new Rpc(env.NODE_URL ?? '', env.API_KEY_NAME) +const isUnsupported = isNode || navigator?.usb == null /** * HID interactions require user gestures, so to reduce clicks, the variables @@ -19,7 +20,7 @@ const rpc = new Rpc(env.NODE_URL ?? '', env.API_KEY_NAME) * addresses from their own Ledger hardware wallets. */ await Promise.all([ - suite('Ledger unsupported', { skip: !(isNode || navigator?.usb == null) }, async () => { + suite('Ledger unsupported', { skip: !isUnsupported }, async () => { await test('status UNSUPPORTED', async () => { assert.equal(Ledger.status, 'UNSUPPORTED') @@ -27,7 +28,7 @@ await Promise.all([ }) }), - suite('Ledger hardware wallet', { skip: isNode || navigator?.usb == null }, async () => { + suite('Ledger hardware wallet', { skip: isUnsupported }, async () => { const { LEDGER_NANOS, LEDGER_NANOSP } = CUSTOM_TEST_VECTORS const { OPEN_BLOCK, RECEIVE_BLOCK, SEND_BLOCK } = NANO_TEST_VECTORS @@ -36,14 +37,14 @@ await Promise.all([ /** @type {Block} */ openBlock, /** @type {Block} */ sendBlock, /** @type {Block} */ receiveBlock, - restored + /** @type {Wallet} */ restored try { wallet = await Wallet.create('Ledger') } catch { return } - await test('request permissions', { skip: true }, async () => { + await test('request permissions', { skip: isUnsupported }, async () => { let status = Ledger.status Ledger.addEventListener('ledgerstatuschanged', (event) => { @@ -152,7 +153,7 @@ await Promise.all([ assert.equal(status, 'CONNECTED') }) - await test('switch between interfaces', { skip: false || isNode || navigator?.usb == null }, async () => { + await test('switch between interfaces', { skip: isUnsupported }, async () => { await assert.resolves(click( 'Verify current interface is HID, switch to unlocked Bluetooth device, then click to continue', async () => wallet.config({ connection: 'ble' }) @@ -177,29 +178,34 @@ await Promise.all([ await test('verify mnemonic', async () => { await click( - 'Open Nano app, then click to continue', - async () => wallet.unlock() + 'Open Nano app, then click to verify mnemonic', + async () => { + await wallet.config({ connection: 'hid' }) + await wallet.unlock() + const isVerified = await wallet.verify(LEDGER_NANOS.MNEMONIC) + // const isVerified = await wallet.verify(LEDGER_NANOSP.MNEMONIC) + assert.exists(isVerified) + assert.equal(typeof isVerified, 'boolean') + assert.equal(isVerified, true) + } ) - const isVerified = await wallet.verify(LEDGER_NANOS.MNEMONIC) - // const isVerified = await wallet.verify(LEDGER_NANOSP.MNEMONIC) - - assert.exists(isVerified) - assert.equal(typeof isVerified, 'boolean') - assert.equal(isVerified, true) }) await test('verify seed', async () => { - const isVerified = await wallet.verify(LEDGER_NANOS.SEED) - // const isVerified = await wallet.verify(LEDGER_NANOSP.SEED) - - assert.exists(isVerified) - assert.equal(typeof isVerified, 'boolean') - assert.equal(isVerified, true) + await click( + 'Click to verify seed', + async () => { + const isVerified = await wallet.verify(LEDGER_NANOS.SEED) + // const isVerified = await wallet.verify(LEDGER_NANOSP.SEED) + assert.exists(isVerified) + assert.equal(typeof isVerified, 'boolean') + assert.equal(isVerified, true) + } + ) }) await test('get first account', async () => { account = await wallet.account() - assert.exists(account) assert.ok(account instanceof Account) assert.exists(account.publicKey) @@ -212,7 +218,6 @@ await Promise.all([ await test('get second and third accounts', async () => { const accounts = await wallet.accounts(1, 2) - assert.exists(accounts) assert.equal(accounts.size, 2) for (const account of accounts.values()) { @@ -225,7 +230,6 @@ await Promise.all([ // skip since accounts must already be opened to be refreshed await test('refresh first three accounts', { skip: true }, async () => { const accounts = await wallet.refresh(rpc, 0, 2) - assert.exists(accounts) for (const account of accounts.values()) { assert.ok(account instanceof Account) @@ -244,14 +248,23 @@ await Promise.all([ assert.nullish(openBlock.signature) assert.equal(openBlock.account.publicKey, account.publicKey) - await wallet.sign(0, openBlock) + await click( + 'Click to sign open block from Ledger wallet', + async () => wallet.sign(0, openBlock) + ) + const { signature } = openBlock - assert.ok(/^[A-F0-9]{128}$/i.test(openBlock.signature ?? '')) + assert.ok(/^[A-F0-9]{128}$/i.test(signature ?? '')) + openBlock.signature = undefined - await openBlock.sign(wallet, 0) + await click( + 'Click to sign open block from block using Ledger', + async () => openBlock.sign(wallet, 0) + ) assert.exists(openBlock.signature) assert.ok(/^[A-F0-9]{128}$/i.test(openBlock.signature ?? '')) + assert.equal(signature, openBlock.signature) }) await test('sign send block from wallet which requires cache to be up-to-date', async () => { @@ -262,7 +275,10 @@ await Promise.all([ assert.nullish(sendBlock.signature) assert.equal(sendBlock.account.publicKey, account.publicKey) - await wallet.sign(0, sendBlock, openBlock) + await click( + 'Click to sign send block which requires up-to-date cache', + async () => wallet.sign(0, sendBlock, openBlock) + ) assert.ok(/^[A-F0-9]{128}$/i.test(sendBlock.signature ?? '')) }) @@ -275,7 +291,10 @@ await Promise.all([ assert.nullish(receiveBlock.signature) assert.equal(receiveBlock.account.publicKey, account.publicKey) - await receiveBlock.sign(wallet, 0, sendBlock) + await click( + 'Click to sign receive block from block passing previous block for cache', + async () => receiveBlock.sign(wallet, 0, sendBlock) + ) assert.exists(receiveBlock.signature) assert.ok(/^[A-F0-9]{128}$/i.test(receiveBlock.signature ?? '')) @@ -300,7 +319,10 @@ await Promise.all([ assert.nullish(sendBlock.signature) assert.equal(sendBlock.account.publicKey, account.publicKey) - await wallet.sign(0, sendBlock, receiveBlock) + await click( + 'Click to sign send block from Ledger including frontier block for cache', + async () => wallet.sign(0, sendBlock, receiveBlock) + ) assert.exists(sendBlock.signature) assert.ok(/^[A-F0-9]{128}$/i.test(sendBlock.signature ?? '')) @@ -309,18 +331,28 @@ await Promise.all([ await test('sign a 16-byte nonce', async () => { const nonce = 'hello nano world' - const signature = await wallet.sign(0, nonce) - - assert.exists(signature) - assert.ok(/^[A-F0-9]{128}$/i.test(signature)) + await click( + 'Click to sign send block from Ledger including frontier block for cache', + async () => { + const signature = await wallet.sign(0, nonce) + assert.exists(signature) + assert.ok(/^[A-F0-9]{128}$/i.test(signature)) + } + ) }) await test('fail to sign invalid length nonces', async () => { const nonceShort = 'hello world' const nonceLong = 'hello world foobar' - await assert.rejects(wallet.sign(0, nonceShort)) - await assert.rejects(wallet.sign(0, nonceLong)) + await assert.rejects(click( + 'fail to sign short nonce', + async () => wallet.sign(0, nonceShort) + )) + await assert.rejects(click( + 'fail to sign long nonce', + async () => wallet.sign(0, nonceLong) + )) }) await test('fail when using new', async () => { @@ -330,7 +362,10 @@ await Promise.all([ await test('fail to sign a block without caching frontier', async () => { sendBlock = new Block(account, receiveBlock.balance, receiveBlock.hash, SEND_BLOCK.representative) .send(account.address, '0') - await assert.rejects(sendBlock.sign(wallet, 0)) + await assert.rejects(click( + 'fail to sign without frontier cache', + async () => sendBlock.sign(wallet, 0) + )) }) await test('restore from db then destroy', async () => { @@ -338,7 +373,13 @@ await Promise.all([ assert.exists(restored) await assert.resolves(restored.unlock()) - assert.equal(await restored.verify(LEDGER_NANOS.MNEMONIC), true) + await assert.resolves(click( + 'Click to verify Ledger mnemonic', + async () => { + const verification = await restored.verify(LEDGER_NANOS.MNEMONIC) + assert.equal(verification, true) + } + )) // assert.equal(await restored.verify(LEDGER_NANOSP.MNEMONIC), true) await click( -- 2.47.3