Consensus calibration

This commit is contained in:
2026-10-20 09:26:12 -04:00
parent 4080114085
commit 9d61c177b7
11 changed files with 259 additions and 1 deletions
+66
View File
@@ -0,0 +1,66 @@
import { CONSENSUS, type TestnetBlock } from '../../shared/testnet.ts';
import { CHAIN_ID, GENESIS_TIME, MAX_TARGET, expectedTarget, medianTime, workFor } from '../testnet/consensus.ts';
const SPACE = 1n << 256n;
/** Analysis only. Calls the actual retarget/median helpers but DOES NOT construct valid blocks,
* perform PoW, invoke Ledger, or provide a production fake-PoW selector. */
export type Scenario = 'steady' | 'rapid' | 'slow' | 'stall' | 'median-minimum' | 'future-edge';
export type TracePoint = { height: number; wallTime: number; timestamp: number; target: string;
cumulativeWork: string; expectedHashes: number; nextTarget: string };
export function expectedHashes(target: string): number {
if (!/^[a-f0-9]{64}$/.test(target)) throw new Error('Invalid target');
return Number(SPACE) / Number(BigInt(`0x${target}`) + 1n);
}
export function targetForHashrate(hashesPerSecond: number, seconds = CONSENSUS.targetSeconds): string {
if (!Number.isSafeInteger(hashesPerSecond) || hashesPerSecond < 1 || hashesPerSecond > 1_000_000_000_000 ||
!Number.isSafeInteger(seconds) || seconds < 1 || seconds > 86400) throw new Error('Hashrate/interval outside analysis bounds');
const denominator = BigInt(hashesPerSecond) * BigInt(seconds);
const target = SPACE / denominator - 1n;
const maximum = BigInt(`0x${MAX_TARGET}`);
return (target < 1n ? 1n : target > maximum ? maximum : target).toString(16).padStart(64,'0');
}
export function simulate(scenario: Scenario, count = 60): TracePoint[] {
if (!['steady','rapid','slow','stall','median-minimum','future-edge'].includes(scenario) ||
!Number.isSafeInteger(count) || count < 1 || count > 1000) throw new Error('Invalid bounded simulation');
const history: TestnetBlock[] = [], result: TracePoint[] = [];
let wallTime = GENESIS_TIME, cumulativeWork = 0n;
for (let i = 0; i < count; i++) {
const target = expectedTarget(history);
const spacing = scenario === 'rapid' ? 1 : scenario === 'slow' ? CONSENSUS.targetSeconds * 8
: scenario === 'stall' && i === 20 ? 7 * 86400 : CONSENSUS.targetSeconds;
wallTime += spacing;
const timestamp = scenario === 'median-minimum' ? medianTime(history) + 1
: scenario === 'future-edge' ? wallTime + CONSENSUS.maxFutureSeconds : wallTime;
if (timestamp <= medianTime(history) || timestamp > wallTime + CONSENSUS.maxFutureSeconds) throw new Error('Analysis generated an invalid timestamp');
// Only these fields are read by expectedTarget/medianTime. Never submit these records to a node.
history.push({ createdAt: timestamp, target } as TestnetBlock);
cumulativeWork += workFor(target);
result.push({ height:i+1,wallTime,timestamp,target,cumulativeWork:cumulativeWork.toString(),
expectedHashes:expectedHashes(target),nextTarget:expectedTarget(history) });
}
return result;
}
/** Idealized fixed-difficulty Poisson race: probability of ever catching up from a CURRENT
* known z-block deficit, q<p. NOT probability of double-spend after z confirmations. */
export function idealCatchup(attackerShare: number, deficit: number): number {
if (!Number.isFinite(attackerShare) || attackerShare < 0 || attackerShare > 1 ||
!Number.isSafeInteger(deficit) || deficit < 0 || deficit > 1000) throw new Error('Invalid race assumptions');
if (deficit === 0 || attackerShare >= .5) return 1;
return (attackerShare / (1-attackerShare)) ** deficit;
}
export function calibrationReport() {
const scenarios = Object.fromEntries((['steady','rapid','slow','stall','median-minimum','future-edge'] as const)
.map(name=>[name,simulate(name)]));
const a=scenarios.steady.at(-1)!,b=scenarios.rapid.at(-1)!;
return { schema:'wikideck-consensus-analysis-v1',chainId:CHAIN_ID,consensusChanged:false,
scope:'Deterministic header-math scenarios only; no valid blocks, mining, payments or launch.',
initialExpectedHashes:expectedHashes(MAX_TARGET),targetSeconds:CONSENSUS.targetSeconds,
assumptions:'Spacing is externally imposed, not stochastic mining at constant hashrate. Work comparisons do not imply an attacker can produce that work.',
scenarios,
partitionExample:{steadyHeight:a.height,rapidHeight:b.height,steadyWork:a.cumulativeWork,rapidWork:b.cumulativeWork,
winnerByStrictWork:BigInt(b.cumulativeWork)>BigInt(a.cumulativeWork)?'rapid':'steady',
warning:'Models fork-choice inputs, not peer synchronization, block validity or achievable hashpower.'},
knownDeficitCatchup:[.1,.25,.4,.49].map(share=>({attackerShare:share,probability:idealCatchup(share,10),currentDeficit:10})),
candidateTargets:[1,10,100,1000,10000].map(hps=>({assumedNetworkHashesPerSecond:hps,target:targetForHashrate(hps)})),
decision:'NO-GO: measurement, independent analysis, multi-host rehearsal and explicit operator approval still required.' };
}
+1 -1
View File
@@ -25,7 +25,7 @@ export function launchPreflight(env: NodeJS.ProcessEnv = process.env, now = Date
}
checks.push(
{ id: 'public-node-resource-controls', status: 'blocked', detail: 'Stage-1 global RPC budgets, bounded pages/client history and read caching exist. In-memory DAG growth, full-history UI/reconciliation and production-scale load/capacity evidence remain unresolved.' },
{ id: 'consensus-launch-calibration', status: 'blocked', detail: 'Initial RandomX difficulty, retarget behavior, final genesis and economic security have not been calibrated and independently reviewed for production.' },
{ id: 'consensus-launch-calibration', status: 'blocked', detail: 'Stage-3 bounded offline native benchmarks and deterministic retarget/timestamp analysis exist. Representative network hashrate, stochastic/multi-host adversarial evidence, reviewed launch rules/genesis and independent economic-security review remain missing.' },
{ id: 'wallet-key-protection', status: 'blocked', detail: 'Stage-2 encrypted vault storage, explicit migration/unlock and lock-aware signing exist. Legacy plaintext copies, unlocked-page compromise, migration/lifecycle coverage and independent production wallet/recovery review remain unresolved.' },
{ id: 'live-market-settlement', status: 'blocked', detail: 'No live SHKeeper adapter or reviewed payout/refund/reorg system exists. Mock payment cannot constitute real payment for valuable cards.' },
{ id: 'release-security-review', status: 'unverified', detail: 'Independent consensus/application review and release-specific dependency/native/container scans require external evidence; an operator environment flag is not evidence.' },