// Explicit opt-in live-network test. Creates disposable wallets and mines three test NFTs. import assert from 'node:assert/strict'; import { generateKeyPairSync, sign } from 'node:crypto'; import { setTimeout as delay } from 'node:timers/promises'; import { RandomX } from '../server/testnet/randomx.ts'; import { address, CHAIN_ID, miningBlob, mintId, sha256 } from '../server/testnet/consensus.ts'; import { blockClaimMessage, nftTransferMessage, NETWORK, testnetOwnerOf, transactionCardIds, type MiningTemplate, type TestnetSnapshot } from '../shared/testnet.ts'; if(!process.argv.includes('--consent'))throw new Error('This test mines three blocks with a native CPU worker. Pass --consent explicitly. Disposable NFT rewards are not recoverable.'); const nodes=(process.env.WIKIDECK_SMOKE_NODES??'http://127.0.0.1:3101,http://127.0.0.1:3102,http://127.0.0.1:3103').split(','); for(const endpoint of nodes)if(!/^http:\/\/127\.0\.0\.1:\d+$/.test(endpoint))throw new Error('Smoke test only accepts explicit loopback test nodes'); async function api(node:string,path:string,body?:unknown){ const r=await fetch(node+path,{method:body?'POST':'GET',headers:body?{'Content-Type':'application/json'}:{}, body:body?JSON.stringify(body):undefined,signal:AbortSignal.timeout(30000),redirect:'error'}); const value=await r.json();if(!r.ok)throw new Error(value.error??`HTTP${r.status}`);return value; } const keys=generateKeyPairSync('ec',{namedCurve:'prime256v1'}); const publicKey=keys.publicKey.export({format:'der',type:'spki'}).toString('base64'),owner=address(publicKey); const receiver=generateKeyPairSync('ec',{namedCurve:'prime256v1'}); const to=address(receiver.publicKey.export({format:'der',type:'spki'}).toString('base64')); const signed=(m:string)=>sign('sha256',Buffer.from(m),{key:keys.privateKey,dsaEncoding:'ieee-p1363'}).toString('base64'); const engine=new RandomX(); try { const initial=await api(nodes[0],'/api/state') as TestnetSnapshot; assert.equal(initial.chainId,CHAIN_ID); console.log(`Testing ${nodes.length} independent nodes on ${CHAIN_ID}; starting height ${initial.blocks.length}`); let first=''; for(let count=0;count<3;count++) { if(count===2){ const tx={network:NETWORK,chainId:CHAIN_ID,cardHash:first,from:owner,to,sequence:0}; await api(nodes[0],'/api/transfer',{...tx,publicKey,signature:signed(nftTransferMessage(tx))}); const pending=await api(nodes[0],'/api/state') as TestnetSnapshot; assert.equal(testnetOwnerOf(pending,first),owner); assert.ok(pending.pendingTransfers.some(t=>transactionCardIds(t).includes(first))); } const template=await api(nodes[0],'/api/mining/template',{owner,publicKey}) as MiningTemplate; let found; for(let batch=0;batch<100;batch++){ found=await engine.mine(template.header.key,miningBlob(template.header),template.header.target,32,32); if(found)break;template.header.nonce+=32; } assert.ok(found,'Native proof not found within smoke budget'); template.header.nonce=found.nonce; const hash=sha256(miningBlob(template.header)),tokenId=mintId(template.header); await api(nodes[0],'/api/blocks',{...template.header,hash,tokenId,powHash:found.hash,signature:signed(blockClaimMessage(hash)),transactions:template.transactions}); if(count===0)first=tokenId; console.log(`Native RandomX block #${template.header.height} accepted: ${hash}`); } const canonical=await api(nodes[0],'/api/state') as TestnetSnapshot; const deadline=Date.now()+60000; for(;;){ const states=await Promise.all(nodes.map(n=>api(n,'/api/state') as Promise)); if(states.every(s=>s.tip===canonical.tip&&s.cumulativeWork===canonical.cumulativeWork&&testnetOwnerOf(s,first)===to)){ console.log(`PASS: ${states.length} independent nodes agreed on work ${canonical.cumulativeWork}, tip, NFT issuance and mined transfer.`);break; } if(Date.now()>deadline)throw new Error('Peer convergence timeout'); await delay(500); } }finally{engine.close();}