import {readFile,writeFile,stat} from 'node:fs/promises'; import {blake3} from '@noble/hashes/blake3.js'; import {createEpb3Candidate,generateEpb3Page,epb3Identity} from './epb3.js'; const args=process.argv.slice(2),alphabet='abcdefghijklmnopqrstuvwxyz ,.'; if(args.length===1&&args[0]==='--self-test'){ const fixtures=JSON.parse(await readFile(new URL('golden-vectors.json',import.meta.url),'utf8')); const hash=value=>Buffer.from(blake3(typeof value==='string'?new TextEncoder().encode(value):value)).toString('hex'); for(const v of fixtures.vectors){ const digits=Uint8Array.from({length:v.length},(_,i)=>i%29),candidate=createEpb3Candidate(digits,v.index); if(hash(candidate.address)!==v.addressHash||hash(generateEpb3Page(candidate.address,0))!==v.firstPageHash ||hash(generateEpb3Page(candidate.address,409))!==v.lastPageHash||epb3Identity(candidate.address).digest!==v.digest)throw Error('Reference vector mismatch'); } console.log('EPB3 fixed examples reproduced.'); }else{ if(args.length!==6||args[0]!=='--address-file'||args[2]!=='--page'||args[4]!=='--output'||!/^\d+$/.test(args[3]))throw Error('Usage: node reconstruct.mjs --address-file address.epb --page 1 --output page.txt'); const page=Number(args[3]);if(!Number.isInteger(page)||page<1||page>410)throw Error('Page must be 1..410'); const metadata=await stat(args[1]);if(!metadata.isFile()||metadata.size>2_000_001)throw Error('Address file exceeds limit'); const value=await readFile(args[1],'utf8'),address=value.endsWith('\n')?value.slice(0,-1):value; const digits=generateEpb3Page(address,page-1),text=Array.from(digits,d=>alphabet[d]).join(''); const lines=Array.from({length:40},(_,i)=>text.slice(i*80,(i+1)*80)).join('\n'); await writeFile(args[5],lines+'\n',{mode:0o600,flag:'wx'}); console.log('Page written to the requested new private output file.'); }