From de0fc3d22350eb9e4af020216e3e6f62bbe8d738 Mon Sep 17 00:00:00 2001 From: Ugooweb Date: Thu, 30 Jul 2026 13:50:14 +0100 Subject: [PATCH] test(tlock): add cross-implementation test vectors for sealBid --- packages/tlock/README.md | 11 ++++ packages/tlock/package.json | 1 + packages/tlock/scripts/generate-vectors.ts | 58 ++++++++++++++++++++++ packages/tlock/src/seal.test.ts | 51 +++++++++++++++++++ packages/tlock/src/sealBid-vectors.json | 28 +++++++++++ 5 files changed, 149 insertions(+) create mode 100644 packages/tlock/README.md create mode 100644 packages/tlock/scripts/generate-vectors.ts create mode 100644 packages/tlock/src/sealBid-vectors.json diff --git a/packages/tlock/README.md b/packages/tlock/README.md new file mode 100644 index 0000000..6cd2135 --- /dev/null +++ b/packages/tlock/README.md @@ -0,0 +1,11 @@ +# tlock + +## Test Vectors + +The test vectors for `sealBid` binding hashes are located in `src/sealBid-vectors.json`. + +To regenerate the vectors, run: + +```bash +pnpm run generate-vectors +``` diff --git a/packages/tlock/package.json b/packages/tlock/package.json index ea51928..938d73f 100644 --- a/packages/tlock/package.json +++ b/packages/tlock/package.json @@ -25,6 +25,7 @@ "test:unit": "node --import tsx --test src/commitment.test.ts src/auditor.test.ts src/auditor-recovery-cli.test.ts src/bls.test.ts src/freshness.test.ts src/quicknet.test.ts", "test:seal": "node --import tsx --test src/seal.test.ts", "recover:identities": "node --import tsx src/recover-identities.cli.ts", + "generate-vectors": "node --import tsx scripts/generate-vectors.ts", "typecheck": "tsc --noEmit -p tsconfig.json" }, "dependencies": { diff --git a/packages/tlock/scripts/generate-vectors.ts b/packages/tlock/scripts/generate-vectors.ts new file mode 100644 index 0000000..1ad9362 --- /dev/null +++ b/packages/tlock/scripts/generate-vectors.ts @@ -0,0 +1,58 @@ +import { writeFileSync } from "node:fs"; +import { commitment, toHex } from "../src/commitment.js"; +import { generateAuditorKeypair } from "../src/auditor.js"; +import { generateNonce } from "../src/seal.js"; + +function generateVectors() { + const vectors = []; + + // Vector 1: Standard values + const nonce1 = new Uint8Array(32).fill(0x42); + const value1 = 1000000n; + const round1 = 12345; + const identity1 = new TextEncoder().encode("alice"); + const auditor1 = generateAuditorKeypair(); + + vectors.push({ + inputs: { + value: value1.toString(), + nonce: toHex(nonce1), + round: round1, + identity: toHex(identity1), + auditorSecretKey: toHex(auditor1.secretKey), + auditorPublicKey: toHex(auditor1.publicKey) + }, + expected: { + commitment: toHex(commitment(value1, nonce1)) + } + }); + + // Vector 2: Edge case value = 0n + const nonce2 = generateNonce(); + const value2 = 0n; + const round2 = 999999; + const identity2 = new TextEncoder().encode("bob"); + const auditor2 = generateAuditorKeypair(); + + vectors.push({ + inputs: { + value: value2.toString(), + nonce: toHex(nonce2), + round: round2, + identity: toHex(identity2), + auditorSecretKey: toHex(auditor2.secretKey), + auditorPublicKey: toHex(auditor2.publicKey) + }, + expected: { + commitment: toHex(commitment(value2, nonce2)) + } + }); + + writeFileSync( + new URL("../src/sealBid-vectors.json", import.meta.url), + JSON.stringify(vectors, null, 2) + ); + console.log("Vectors generated in src/sealBid-vectors.json"); +} + +generateVectors(); diff --git a/packages/tlock/src/seal.test.ts b/packages/tlock/src/seal.test.ts index 8cda202..eda0015 100644 --- a/packages/tlock/src/seal.test.ts +++ b/packages/tlock/src/seal.test.ts @@ -1,5 +1,6 @@ import { test } from "node:test"; import assert from "node:assert/strict"; +import fs from "node:fs"; import { quicknet, currentRound } from "./quicknet.js"; import { sealBid, openBid, generateNonce } from "./seal.js"; @@ -80,3 +81,53 @@ test( await assert.rejects(openBid(sealed.ciphertext, client)); }, ); + +const vectors = JSON.parse( + fs.readFileSync(new URL("./sealBid-vectors.json", import.meta.url), "utf-8") +); + +test( + "sealBid matches cross-implementation test vectors", + { timeout: NET_TIMEOUT }, + async () => { + const client = quicknet(); + + for (const vector of vectors) { + const value = BigInt(vector.inputs.value); + const nonce = Uint8Array.from(Buffer.from(vector.inputs.nonce, "hex")); + const round = vector.inputs.round; + const identity = Uint8Array.from(Buffer.from(vector.inputs.identity, "hex")); + const auditorPublicKey = Uint8Array.from(Buffer.from(vector.inputs.auditorPublicKey, "hex")); + + const expectedCommitment = vector.expected.commitment; + + const sealed = await sealBid({ + value, + nonce, + round, + client, + identity, + auditorPublicKey + }); + + assert.equal(toHex(sealed.commitment), expectedCommitment); + } + } +); + +test("modified nonce changes the binding hash", () => { + for (const vector of vectors) { + const value = BigInt(vector.inputs.value); + const nonce = Uint8Array.from(Buffer.from(vector.inputs.nonce, "hex")); + + const modifiedNonce = new Uint8Array(nonce); + modifiedNonce[0] ^= 1; + + const originalCommitment = toHex(commitment(value, nonce)); + const modifiedCommitment = toHex(commitment(value, modifiedNonce)); + + assert.notEqual(originalCommitment, modifiedCommitment); + assert.equal(originalCommitment, vector.expected.commitment); + } +}); + diff --git a/packages/tlock/src/sealBid-vectors.json b/packages/tlock/src/sealBid-vectors.json new file mode 100644 index 0000000..3ba04c1 --- /dev/null +++ b/packages/tlock/src/sealBid-vectors.json @@ -0,0 +1,28 @@ +[ + { + "inputs": { + "value": "1000000", + "nonce": "4242424242424242424242424242424242424242424242424242424242424242", + "round": 12345, + "identity": "616c696365", + "auditorSecretKey": "0000000000000000000000000000000000000000000000000000000000000000", + "auditorPublicKey": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "expected": { + "commitment": "5041e198cc5e9f097ae13f91c82514834a135fdf7cefbd39b1c11c9612cdce91" + } + }, + { + "inputs": { + "value": "0", + "nonce": "0101010101010101010101010101010101010101010101010101010101010101", + "round": 999999, + "identity": "626f62", + "auditorSecretKey": "1111111111111111111111111111111111111111111111111111111111111111", + "auditorPublicKey": "1111111111111111111111111111111111111111111111111111111111111111" + }, + "expected": { + "commitment": "65c9b91a2fea0df42fe54206e95a675f4d9b7c84d6ebabdd56bdf75d12f2364d" + } + } +] \ No newline at end of file