import { DatabaseSync } from 'node:sqlite'; import { mkdtemp, rm, stat } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { performance } from 'node:perf_hooks'; import { randomUUID } from 'node:crypto'; import { Market } from '../server/market/store.ts'; import { GENESIS_HASH } from '../server/testnet/consensus.ts'; import { NETWORK } from '../shared/testnet.ts'; // Synthetic terminal-order journal only. It is isolated in a temporary SQLite database and // never connects to a Ledger, peer, payment adapter, wallet, or production data directory. const text = process.argv[2] ?? '10000'; if (!/^[1-9]\d{0,5}$/.test(text) || Number(text) > 100000) throw new Error('Fixture size must be 1..100000 orders'); const count = Number(text); const directory = await mkdtemp(join(tmpdir(), 'wikideck-market-reconciliation-')); const streamId = randomUUID(), tip = 'a'.repeat(64); let market: Market | undefined; try { // Indexed-only adapter: the benchmark must not fall back to materializing chain snapshots. const chain = { snapshot: () => { throw new Error('Benchmark must not request a full chain snapshot'); }, summary: () => ({ tip, revision: 0, height: 1 }), nft: () => undefined, canonicalTransaction: () => undefined, pendingTransactions: () => [], canonicalHead: () => 0, canonicalEvents: (after: number) => ({ streamId, previousAnchor: GENESIS_HASH, anchor: GENESIS_HASH, events: [], cursor: after, head: 0, caughtUp: true }), submitTransfer: async () => { throw new Error('Benchmark never submits transfers'); }, }; market = await Market.open(directory, chain, { feeBps: 250, confirmations: 2 }); const db = new DatabaseSync(join(directory, NETWORK.includes('mainnet') ? 'mainnet' : 'testnet', 'market.sqlite')); try { const insertListing = db.prepare('INSERT INTO listings(id,card,status,data) VALUES(?,?,?,?)'); const insertOrder = db.prepare('INSERT INTO orders(id,data) VALUES(?,?)'); const seller = 'synthetic-seller'; db.exec('BEGIN IMMEDIATE'); try { for (let i = 0; i < count; i++) { const id = i.toString().padStart(12, '0'); const listingId = `listing-${id}`, orderId = `order-${id}`, cardHash = i.toString(16).padStart(64, '0'); const listing = { id: listingId, cardHash, seller, sequence: 0, kind: 'fixed', status: 'cancelled' }; const order = { id: orderId, listingId, seller, buyer: 'synthetic-buyer', cardHash, sequence: 0, status: 'refunded' }; insertListing.run(listingId, cardHash, 'cancelled', JSON.stringify(listing)); insertOrder.run(orderId, JSON.stringify(order)); } db.exec('COMMIT'); } catch (error) { db.exec('ROLLBACK'); throw error; } } finally { db.close(); } const latencies: number[] = []; const maxPasses = Math.ceil(count / 128) * 2 + 4; const start = performance.now(); for (let pass = 0; pass < maxPasses && (pass === 0 || market.hasReconciliationBacklog()); pass++) { const before = performance.now(); market.browse(); latencies.push(performance.now() - before); } const elapsedMs = performance.now() - start; if (market.hasReconciliationBacklog()) throw new Error(`Reconciliation did not finish within ${maxPasses} bounded passes`); market.close(); market = undefined; const bytes = (await stat(join(directory, NETWORK.includes('mainnet') ? 'mainnet' : 'testnet', 'market.sqlite'))).size; latencies.sort((a, b) => a - b); const percentile = (p: number) => latencies[Math.min(latencies.length - 1, Math.ceil(latencies.length * p) - 1)]; console.log(JSON.stringify({ fixture: 'synthetic-terminal-orders-and-listings', orders: count, listings: count, batchSizePerTable: 128, reconciliationPasses: latencies.length, elapsedMs, rowsPerSecond: Math.round((count * 2 * 1000) / elapsedMs), passLatencyP50Ms: percentile(0.50), passLatencyP95Ms: percentile(0.95), passLatencyMaxMs: latencies.at(-1), sqliteBytes: bytes, warning: 'Synthetic terminal records on this development host only; excludes live chain validation, active-order logic, concurrent HTTP load, PostgreSQL, and production SLO certification.' }, null, 2)); } finally { market?.close(); await rm(directory, { recursive: true, force: true }); }