The published repository served the 0.3.0 line from May while the install page already pointed here. This carries the uncommitted line: 35+ typed contract clients over the canonical address book, the compliance surface, and the hybrid PQC wallet helpers.
66 lines
3.3 KiB
JavaScript
66 lines
3.3 KiB
JavaScript
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Runnable demo: node examples/threshold-account-demo.mjs (after `npm run build`)
|
|
//
|
|
// End-to-end proof that the AereThresholdAccount SDK produces cryptographically valid
|
|
// authorizing legs for a non-custodial t-of-n POST-QUANTUM account, using REAL Falcon-512
|
|
// signing/verification off-chain (the same envelope the live precompile verifies). It does not
|
|
// touch a chain: it shows exactly what each committee member signs, that ANY t members authorize,
|
|
// and that below-threshold or forged legs do not.
|
|
import { getBytes } from 'ethers';
|
|
import { keygen, verifyLocal, SCHEME, signLeg, encodeLegs, thresholdExecChallenge } from '../dist/index.js';
|
|
|
|
const line = (s = '') => console.log(s);
|
|
const SCH = SCHEME.FALCON512;
|
|
const N = 3, T = 2;
|
|
|
|
line('=== AERE non-custodial post-quantum threshold account — SDK demo (2-of-3 Falcon-512) ===\n');
|
|
|
|
// 1. A committee of 3 independent post-quantum keys (in production each lives on a separate device).
|
|
const members = [];
|
|
for (let i = 0; i < N; i++) {
|
|
const kp = keygen(SCH, getBytes('0x' + (i + 1).toString(16).padStart(96, '0'))); // 48-byte seed, deterministic for the demo
|
|
members.push({ memberIndex: i, publicKey: kp.publicKey, secretKey: kp.secretKey });
|
|
line(` member ${i}: Falcon-512 pubkey ${kp.publicKey.length} bytes`);
|
|
}
|
|
|
|
// 2. The account + the call to authorize. (Addresses are placeholders — this demo is off-chain.)
|
|
const account = '0x1111111111111111111111111111111111111111';
|
|
const target = '0x2222222222222222222222222222222222222222';
|
|
const value = 0n;
|
|
const data = '0xa1e78d8b'; // some function selector the account would call
|
|
const nonce = 0n;
|
|
|
|
// 3. The EXACT challenge every member must sign, derived byte-for-byte like the contract.
|
|
const challenge = thresholdExecChallenge(account, nonce, target, value, data, 2800n);
|
|
line(`\n exec challenge @ nonce ${nonce}: ${challenge}`);
|
|
|
|
// 4. TWO members (0 and 2) each PQC-sign the challenge -> authorizing legs.
|
|
const signers = [members[0], members[2]];
|
|
const legs = signers.map((m) => signLeg(SCH, { memberIndex: m.memberIndex, secretKey: m.secretKey }, challenge));
|
|
|
|
// 5. Verify every leg locally with REAL Falcon verification (what the precompile enforces on-chain).
|
|
let ok = 0;
|
|
for (const leg of legs) {
|
|
const member = members[leg.memberIndex];
|
|
const valid = verifyLocal(SCH, getBytes(challenge), getBytes(leg.signature), member.publicKey);
|
|
line(` leg from member ${leg.memberIndex}: real Falcon verify = ${valid}`);
|
|
if (valid) ok++;
|
|
}
|
|
line(`\n distinct valid signers = ${ok} / threshold ${T} -> ${ok >= T ? 'AUTHORIZED' : 'REJECTED'}`);
|
|
|
|
// 6. The abi-encoded blob / call args the account.executeThreshold(...) would receive.
|
|
const blob = encodeLegs(legs);
|
|
line(`\n encoded Leg[] (${(blob.length - 2) / 2} bytes) ready for executeThreshold(target,value,data,legs)`);
|
|
|
|
// 7. Negative cases: below threshold, and a forged leg.
|
|
const oneLeg = [legs[0]];
|
|
line(`\n below-threshold (1 leg): distinct valid ${oneLeg.length} < ${T} -> REJECTED (as expected)`);
|
|
|
|
const forged = getBytes(legs[1].signature);
|
|
forged[41] ^= 0x01; // flip a byte inside the Falcon esig
|
|
const forgedValid = verifyLocal(SCH, getBytes(challenge), forged, members[2].publicKey);
|
|
line(` forged leg: real Falcon verify = ${forgedValid} -> not counted (as expected)`);
|
|
|
|
line('\n=== demo complete: any 2 of the 3 post-quantum keys authorize; 1 or a forgery does not. ===');
|