export const POW = { memorySize: 16384, iterations: 2, parallelism: 1, hashLength: 32 } as const; export const DIFFICULTY = 1; // One leading hex zero: ~16 attempts, intentionally approachable demo difficulty. export const GENESIS = '0'.repeat(64); export type Article = { pageId: number; title: string; extract: string; image?: string; url: string; revisionId: number; source: 'live' | 'offline'; category: string; }; export type Challenge = { height: number; previousHash: string; seed: string; article: Article; difficulty: number; createdAt: number; }; export type Block = Challenge & { hash: string; nonce: number; owner: string; publicKey: string; signature: string; }; export type Transfer = { cardHash: string; from: string; to: string; sequence: number; publicKey: string; signature: string; }; export type ChainState = { blocks: Block[]; transfers: Transfer[] }; export function proofMessage(c: Challenge, owner: string, nonce: number): string { // Fixed-order tuple; the complete immutable article snapshot is committed by the proof. return JSON.stringify(['wikideck:pow:v1', c.height, c.previousHash, c.seed, c.article.pageId, c.article.revisionId, c.article.title, c.article.extract, c.article.image ?? '', c.article.url, c.article.source, c.article.category, c.difficulty, c.createdAt, owner, nonce]); } export function claimMessage(c: Challenge, owner: string, nonce: number, hash: string): string { return JSON.stringify(['wikideck:claim:v1', proofMessage(c, owner, nonce), hash]); } export function transferMessage(t: Pick): string { return JSON.stringify(['wikideck:transfer:v1', t.cardHash, t.from, t.to, t.sequence]); } export function ownerOf(state: ChainState, hash: string): string | undefined { let owner = state.blocks.find(b => b.hash === hash)?.owner; for (const t of state.transfers) if (t.cardHash === hash) owner = t.to; return owner; } export function rarity(hash: string): 'Legendary' | 'Epic' | 'Rare' | 'Common' { const roll = parseInt(hash.slice(-4), 16) % 100; return roll < 3 ? 'Legendary' : roll < 15 ? 'Epic' : roll < 40 ? 'Rare' : 'Common'; } export function stats(hash: string) { return { power: 30 + parseInt(hash.slice(8, 10), 16) % 70, defense: 30 + parseInt(hash.slice(10, 12), 16) % 70, knowledge: 30 + parseInt(hash.slice(12, 14), 16) % 70 }; }