]> git.codecow.com Git - libnemo.git/commitdiff
Require user activation for Block signing. Update relevant tests.
authorChris Duncan <chris@zoso.dev>
Mon, 27 Apr 2026 06:32:56 +0000 (23:32 -0700)
committerChris Duncan <chris@zoso.dev>
Mon, 27 Apr 2026 06:32:56 +0000 (23:32 -0700)
src/lib/block.ts
test/test.blocks.mjs
test/test.manage-rolodex.mjs

index e74d04c7d3fe4ba59363583ebb437fe5445fb192..d57ccc0d6b641d83bd9ef08cbae5b3f76619d3b4 100644 (file)
@@ -405,6 +405,9 @@ export class Block {
         */
        async sign (wallet: Wallet, index: number, frontier?: Block): Promise<Block>
        sign (input: unknown, index?: unknown, frontier?: unknown): Block | Promise<Block> {
+               if (navigator.userActivation?.isActive === false) {
+                       throw new Error('Signing request was blocked due to lack of user activation.')
+               }
                if (typeof input === 'string' && /^[A-F0-9]{128}$/i.test(input)) {
                        this.signature = input
                        return this
index 1b2527700170dcb00fd74c0e039c1d56a964fcbb..7f1e64f9f5d7840cb5fef92b0852d0e04d29bf03 100644 (file)
@@ -15,11 +15,16 @@ await Promise.all([
                await test('generate work automatically when processing block', { skip: isNode }, async () => {\r
                        const block = await new Block(OPEN_BLOCK.account, '0', OPEN_BLOCK.previous, OPEN_BLOCK.representative)\r
                                .receive(OPEN_BLOCK.link, OPEN_BLOCK.balance)\r
-                               .sign(OPEN_BLOCK.key)\r
-                       assert.nullish(block.work)\r
 \r
-                       const result = await assert.rejects(block.process(rpc))\r
+                       await assert.resolves(click(\r
+                               'Sign and autogen PoW when processing',\r
+                               async () => block.sign(OPEN_BLOCK.key)\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
 \r
+                       assert.nullish(block.work)\r
+                       const result = await assert.rejects(block.process(rpc))\r
                        assert.exists(block.work)\r
                        assert.equal(result.message, 'Block could not be processed')\r
                })\r
@@ -207,6 +212,7 @@ await Promise.all([
                        ))\r
                        console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
                        await new Promise(r => setTimeout(r, 6000))\r
+\r
                        assert.equal(block.hash, RECEIVE_BLOCK.hash)\r
                        assert.equal(block.signature, RECEIVE_BLOCK.signature)\r
                })\r
@@ -214,7 +220,14 @@ await Promise.all([
                await test('sign receive block without work', async () => {\r
                        const block = new Block(RECEIVE_BLOCK.account, RECEIVE_BLOCK.balance, RECEIVE_BLOCK.previous, RECEIVE_BLOCK.representative)\r
                                .receive(RECEIVE_BLOCK.link, '0')\r
-                       await block.sign(RECEIVE_BLOCK.key)\r
+\r
+                       await assert.resolves(click(\r
+                               'Sign receive block without work',\r
+                               async () => block.sign(RECEIVE_BLOCK.key)\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
+\r
                        assert.equal(block.hash, RECEIVE_BLOCK.hash)\r
                        assert.equal(block.signature, RECEIVE_BLOCK.signature)\r
                        assert.nullish(block.work)\r
@@ -247,7 +260,14 @@ await Promise.all([
                        const block = await new Block(SEND_BLOCK.account, SEND_BLOCK.balance, SEND_BLOCK.previous, SEND_BLOCK.representative)\r
                                .send(SEND_BLOCK.link, '0')\r
                                .pow(SEND_BLOCK.work)\r
-                       await block.sign(SEND_BLOCK.key)\r
+\r
+                       await assert.resolves(click(\r
+                               'Sign send block with key',\r
+                               async () => block.sign(SEND_BLOCK.key)\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
+\r
                        assert.equal(block.hash, SEND_BLOCK.hash)\r
                        assert.equal(block.signature, SEND_BLOCK.signature)\r
                })\r
@@ -255,7 +275,14 @@ await Promise.all([
                await test('sign send block without work', async () => {\r
                        const block = await new Block(SEND_BLOCK.account, SEND_BLOCK.balance, SEND_BLOCK.previous, SEND_BLOCK.representative)\r
                                .send(SEND_BLOCK.link, 0n)\r
-                               .sign(SEND_BLOCK.key)\r
+\r
+                       await assert.resolves(click(\r
+                               'Sign send block without work',\r
+                               async () => block.sign(SEND_BLOCK.key)\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
+\r
                        assert.equal(block.hash, SEND_BLOCK.hash)\r
                        assert.equal(block.signature, SEND_BLOCK.signature)\r
                        assert.nullish(block.work)\r
@@ -267,7 +294,14 @@ await Promise.all([
                        const block = await new Block('nano_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php', '3000000000000000000000000000000', '128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4')\r
                                .change('nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs')\r
                                .pow(work)\r
-                       await block.sign('781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3') // Did not find a private key at nano docs for this address\r
+\r
+                       await assert.resolves(click(\r
+                               'Sign change block with key',\r
+                               async () => block.sign('781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3') // Did not find a private key at nano docs for this address\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
+\r
                        assert.equal(block.signature?.toUpperCase(), 'A3C3C66D6519CBC0A198E56855942DEACC6EF741021A1B11279269ADC587DE1DA53CD478B8A47553231104CF24D742E1BB852B0546B87038C19BAE20F9082B0D')\r
                        assert.equal(block.work, work)\r
                })\r
@@ -275,7 +309,14 @@ await Promise.all([
                await test('sign change rep block without work', async () => {\r
                        const block = await new Block(ADDRESS_0, '0', 'F3C1D7B6EE97DA09D4C00538CEA93CBA5F74D78FD3FBE71347D2DFE7E53DF327')\r
                                .change('nano_34amtofxstsfyqcgphp8piij9u33widykq9wbz6ysjpxhbgmqu8btu1eexer')\r
-                               .sign(PRIVATE_0)\r
+\r
+                       await assert.resolves(click(\r
+                               'Sign change block without work',\r
+                               async () => block.sign(PRIVATE_0)\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
+\r
                        assert.equal(block.signature?.toUpperCase(), '2BD2F905E74B5BEE3E2277CED1D1E3F7535E5286B6E22F7B08A814AA9E5C4E1FEA69B61D60B435ADC2CE756E6EE5F5BE7EC691FE87E024A0B22A3D980CA5B305')\r
                        assert.nullish(block.work)\r
                })\r
@@ -285,17 +326,38 @@ await Promise.all([
                                .receive(OPEN_BLOCK.link, OPEN_BLOCK.balance)\r
                                .pow(OPEN_BLOCK.work)\r
 \r
-                       //@ts-expect-error\r
-                       await assert.rejects(block.sign())\r
+                       await assert.rejects(click(\r
+                               'fail to sign without args',\r
+                               //@ts-expect-error\r
+                               async () => block.sign()\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
                        assert.nullish(block.signature)\r
-                       //@ts-expect-error\r
-                       await assert.rejects(block.sign(null))\r
+\r
+                       await assert.rejects(click(\r
+                               'fail to sign with null arg',\r
+                               //@ts-expect-error\r
+                               async () => block.sign(null)\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
                        assert.nullish(block.signature)\r
-                       //@ts-expect-error\r
-                       await assert.rejects(block.sign('1'))\r
+\r
+                       await assert.rejects(click(\r
+                               'fail to sign with invalid string length',\r
+                               async () => block.sign('1')\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
                        assert.nullish(block.signature)\r
-                       //@ts-expect-error\r
-                       await assert.rejects(block.sign('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'))\r
+\r
+                       await assert.rejects(click(\r
+                               'fail to sign with invalid string characters',\r
+                               async () => block.sign('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')\r
+                       ))\r
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')\r
+                       await new Promise(r => setTimeout(r, 6000))\r
                        assert.nullish(block.signature)\r
                })\r
        })\r
index 40fe37ad2d6f1899918878d4e1afedbb034ca2fc..5e0de97f7fa7027ad029b406e6e8fe5ad341e87b 100644 (file)
@@ -4,7 +4,7 @@
 'use strict'
 
 import { Rolodex, Tools } from 'libnemo'
-import { assert, suite, test } from './GLOBALS.mjs'
+import { assert, click, suite, test } from './GLOBALS.mjs'
 import { NANO_TEST_VECTORS } from './VECTORS.mjs'
 
 await Promise.all([
@@ -183,18 +183,38 @@ await Promise.all([
 
        suite('Rolodex data signature verification', async () => {
 
-               await test('should verify valid data and signature', async () => {
+               await test('verify valid data and signature', async () => {
                        const data = 'Test data'
-                       const signature = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0 + NANO_TEST_VECTORS.PUBLIC_0, data)
+                       let signature = ''
+
+                       await assert.rejects(click(
+                               'Sign and then verify Rolodex data/signature',
+                               async () => {
+                                       signature = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0 + NANO_TEST_VECTORS.PUBLIC_0, data)
+                               }
+                       ))
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')
+                       await new Promise(r => setTimeout(r, 6000))
+
                        await assert.resolves(Rolodex.add('JohnDoe', NANO_TEST_VECTORS.ADDRESS_0))
                        const result = await Rolodex.verify('JohnDoe', signature, data)
                        await assert.resolves(Rolodex.deleteName('JohnDoe'))
                        assert.equal(result, true)
                })
 
-               await test('should reject incorrect contact for signature', async () => {
+               await test('reject incorrect contact for signature', async () => {
                        const data = 'Test data'
-                       const signature = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0 + NANO_TEST_VECTORS.PUBLIC_0, data)
+                       let signature = ''
+
+                       await assert.rejects(click(
+                               'Sign and then reject invalid Rolodex data/signature',
+                               async () => {
+                                       signature = await Tools.sign(NANO_TEST_VECTORS.PRIVATE_0 + NANO_TEST_VECTORS.PUBLIC_0, data)
+                               }
+                       ))
+                       console.log('Click done, waiting 6 seconds to reset transient user activation timer...')
+                       await new Promise(r => setTimeout(r, 6000))
+
                        await assert.resolves(Rolodex.add('JaneSmith', NANO_TEST_VECTORS.ADDRESS_1))
                        const result = await Rolodex.verify('JaneSmith', signature, data)
                        await assert.resolves(Rolodex.deleteName('JaneSmith'))