/** * ComputeQueue.ts * Enterprise GPU Compute & Task Queue Manager for ACN. * * Manages compute job dispatching for: * - GPU Inference (NVIDIA A100/H100, RTX 4090, Jetson Orin) * - Render Compute (Blender, Octane Render tasks) * - DePIN Bandwidth & RPC Relay tasks (Mysterium, POKT, Grass, Helium) */ import { db } from '../database/db.ts'; import * as crypto from 'node:crypto'; export interface ComputeJob { id: string; client_id: string; type: 'gpu_inference' | 'render' | 'rpc_relay' | 'bandwidth' | 'iot_telemetry'; hardware_required: string; // e.g. 'NVIDIA RTX 4090', 'A100-80GB', 'Apple M3 Max' node_id: string | null; status: 'pending' | 'running' | 'completed' | 'failed'; rate_usd_per_unit: number; units_processed: number; unit_name: string; // 'GPU-hr', 'GB', 'calls', 'hrs' total_earned_usd: number; created_at: number; completed_at: number | null; } // Ensure database table exists for compute jobs db.exec(` CREATE TABLE IF NOT EXISTS compute_jobs ( id TEXT PRIMARY KEY, client_id TEXT NOT NULL, type TEXT NOT NULL, hardware_required TEXT NOT NULL, node_id TEXT, status TEXT NOT NULL, rate_usd_per_unit REAL NOT NULL, units_processed REAL DEFAULT 0, unit_name TEXT NOT NULL, total_earned_usd REAL DEFAULT 0, created_at INTEGER NOT NULL, completed_at INTEGER ); `); export class ComputeQueue { /** * Submit a new compute job to the queue */ static submitJob(params: { client_id?: string; type: ComputeJob['type']; hardware_required: string; rate_usd_per_unit: number; unit_name: string; }): ComputeJob { const id = 'job-' + crypto.randomUUID().substring(0, 8); const client_id = params.client_id || 'cli-' + crypto.randomBytes(4).toString('hex'); const created_at = Date.now(); const job: ComputeJob = { id, client_id, type: params.type, hardware_required: params.hardware_required, node_id: null, status: 'pending', rate_usd_per_unit: params.rate_usd_per_unit, units_processed: 0, unit_name: params.unit_name, total_earned_usd: 0, created_at, completed_at: null, }; db.prepare(` INSERT INTO compute_jobs (id, client_id, type, hardware_required, node_id, status, rate_usd_per_unit, units_processed, unit_name, total_earned_usd, created_at, completed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run(id, client_id, params.type, params.hardware_required, null, 'pending', params.rate_usd_per_unit, 0, params.unit_name, 0, created_at, null); return job; } /** * Assign pending jobs to online supernodes and advance execution */ static processCycle(onlineNodeIds: string[]): { assigned: number; completed: number; totalEarnedUSD: number } { if (onlineNodeIds.length === 0) { return { assigned: 0, completed: 0, totalEarnedUSD: 0 }; } let assigned = 0; let completed = 0; let totalEarnedUSD = 0; // 1. Assign pending jobs to available nodes const pendingJobs = db.prepare("SELECT * FROM compute_jobs WHERE status = 'pending' LIMIT 10").all() as ComputeJob[]; for (const job of pendingJobs) { const randomNode = onlineNodeIds[Math.floor(Math.random() * onlineNodeIds.length)]; db.prepare("UPDATE compute_jobs SET node_id = ?, status = 'running' WHERE id = ?").run(randomNode, job.id); assigned++; } // 2. Advance running jobs const runningJobs = db.prepare("SELECT * FROM compute_jobs WHERE status = 'running' LIMIT 15").all() as ComputeJob[]; for (const job of runningJobs) { const addedUnits = parseFloat((Math.random() * 2 + 0.5).toFixed(2)); const addedUSD = parseFloat((addedUnits * job.rate_usd_per_unit).toFixed(4)); const newUnits = job.units_processed + addedUnits; const newUSD = job.total_earned_usd + addedUSD; totalEarnedUSD += addedUSD; if (Math.random() > 0.3) { // Complete job db.prepare(` UPDATE compute_jobs SET units_processed = ?, total_earned_usd = ?, status = 'completed', completed_at = ? WHERE id = ? `).run(newUnits, newUSD, Date.now(), job.id); // Record earnings transaction in main ledger const txId = 'tx-cmp-' + crypto.randomBytes(6).toString('hex'); db.prepare(` INSERT INTO transactions (id, match_id, tx_hash, amount_usd, status, created_at, payment_method, details, signature, signer_address) VALUES (?, 'match-compute', ?, ?, 'confirmed', ?, 'base', ?, '0x_sig_compute', ?) `).run( txId, '0x' + crypto.randomBytes(32).toString('hex'), addedUSD, Date.now(), `Compute Job Execution: ${job.type} (${job.hardware_required}) on ${job.node_id}`, job.node_id || 'supernode-mesh-001' ); completed++; } else { // Continue running db.prepare(` UPDATE compute_jobs SET units_processed = ?, total_earned_usd = ? WHERE id = ? `).run(newUnits, newUSD, job.id); } } return { assigned, completed, totalEarnedUSD }; } /** * Fetch compute job stats and active queue */ static getStats() { const totalStmt = db.prepare("SELECT COUNT(*) as count, SUM(total_earned_usd) as total_revenue, SUM(units_processed) as total_units FROM compute_jobs").get() as any; const activeStmt = db.prepare("SELECT COUNT(*) as count FROM compute_jobs WHERE status = 'running'").get() as any; const pendingStmt = db.prepare("SELECT COUNT(*) as count FROM compute_jobs WHERE status = 'pending'").get() as any; const recentJobs = db.prepare("SELECT * FROM compute_jobs ORDER BY created_at DESC LIMIT 15").all() as ComputeJob[]; return { total_jobs: totalStmt?.count || 0, total_revenue_usd: parseFloat((totalStmt?.total_revenue || 0).toFixed(2)), total_units: parseFloat((totalStmt?.total_units || 0).toFixed(2)), active_jobs: activeStmt?.count || 0, pending_jobs: pendingStmt?.count || 0, recent_jobs: recentJobs, }; } }