'use client'; import React, { useCallback, useEffect, useState } from 'react'; import { ChevronLeft, Cpu, Loader, AlertCircle, CheckCircle2, XCircle, Server } from 'lucide-react'; import { buildBootstrapResolutionVotePayload, fetchBootstrapMarketState, fetchInfonetStatus, type BootstrapMarketState, type InfonetStatus, } from '@/mesh/infonetEconomyClient'; import { generateNodeKeys, getNodeIdentity } from '@/mesh/meshIdentity'; import { fetchInfonetNodeStatusSnapshot, setInfonetNodeEnabled, type InfonetNodeStatusSnapshot, } from '@/mesh/controlPlaneStatusClient'; import { useSignAndAppend } from '@/hooks/useSignAndAppend'; interface BootstrapViewProps { marketId?: string; onBack: () => void; } export default function BootstrapView({ marketId, onBack }: BootstrapViewProps) { const [status, setStatus] = useState(null); const [market, setMarket] = useState(null); const [nodeStatus, setNodeStatus] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [nodeToggleBusy, setNodeToggleBusy] = useState(false); const [nodeToggleError, setNodeToggleError] = useState(null); const [voteSide, setVoteSide] = useState<'yes' | 'no'>('yes'); const [powNonce, setPowNonce] = useState('0'); const voteAction = useSignAndAppend(); const reload = useCallback(async () => { setLoading(true); setError(null); try { const [s, m, n] = await Promise.all([ fetchInfonetStatus(), marketId ? fetchBootstrapMarketState(marketId).catch(() => null) : Promise.resolve(null), fetchInfonetNodeStatusSnapshot(true).catch(() => null), ]); setStatus(s); setMarket(m); setNodeStatus(n); } catch (err) { setError(err instanceof Error ? err.message : 'network error'); } finally { setLoading(false); } }, [marketId]); const nodeEnabled = Boolean(nodeStatus?.node_enabled); const nodeMode = String(nodeStatus?.node_mode || 'participant').toUpperCase(); const syncOutcome = String(nodeStatus?.sync_runtime?.last_outcome || 'idle').toLowerCase(); const seedPeerCount = Number( nodeStatus?.bootstrap?.bootstrap_seed_peer_count ?? nodeStatus?.bootstrap?.default_sync_peer_count ?? 0, ); const syncPeerCount = Number(nodeStatus?.bootstrap?.sync_peer_count || 0); const lastPeerUrl = String(nodeStatus?.sync_runtime?.last_peer_url || '').trim(); const privateTransportRequired = Boolean(nodeStatus?.private_transport_required); const toggleNode = useCallback(async (enabled: boolean) => { setNodeToggleBusy(true); setNodeToggleError(null); try { if (enabled && !getNodeIdentity()) { await generateNodeKeys(); } await setInfonetNodeEnabled(enabled); const next = await fetchInfonetNodeStatusSnapshot(true); setNodeStatus(next); } catch (err) { setNodeToggleError(err instanceof Error ? err.message : 'node settings update failed'); } finally { setNodeToggleBusy(false); } }, []); const hasActivePhase = !!market && market.tally.total_eligible >= 0 && market.tally.yes + market.tally.no < market.tally.total_eligible; useEffect(() => { void reload(); const interval = setInterval(() => void reload(), hasActivePhase ? 8_000 : 30_000); return () => clearInterval(interval); }, [reload, hasActivePhase]); const submitVote = useCallback(async () => { if (!marketId) return; const nonce = Number(powNonce); if (!Number.isFinite(nonce) || nonce < 0) return; const built = buildBootstrapResolutionVotePayload(marketId, voteSide, Math.floor(nonce)); const res = await voteAction.submit(built.event_type, built.payload); if (res.ok) { void reload(); } }, [marketId, voteSide, powNonce, voteAction, reload]); return (
BOOTSTRAP MODE
The first bootstrap_market_count (default 100) markets resolve via eligible-node-one-vote instead of oracle-rep-weighted staking. Eligibility: identity age ≥ 3 days vs market.snapshot.frozen_at, NOT in the predictor exclusion set, and a valid Argon2id PoW (Heavy-Node-only — requires ≥64MB RAM per computation). Once node count crosses bootstrap_threshold (default 1000), new markets default to staked resolution. Existing bootstrap-indexed markets continue under bootstrap rules until they resolve.
{error && (
{error}
)}
Network Seed
Transport
{privateTransportRequired ? 'ONION / RNS ONLY' : 'CLEARNET DEV OVERRIDE'}
Local Node
{nodeEnabled ? `${nodeMode} ONLINE` : `${nodeMode} OFF`}
Sync Path
{syncPeerCount} peers / {seedPeerCount} seeds
{nodeEnabled ? `Infonet sync is ${syncOutcome || 'active'}${lastPeerUrl ? ` via ${lastPeerUrl}` : ''}.` : 'Start a local participant node to sync through available Wormhole onion/RNS peers while this backend is running.'}
{nodeToggleError && (
{nodeToggleError}
)}
{status && (
Network Ramp
Distinct Nodes
{status.ramp.node_count}
Bootstrap Resolution
{status.ramp.bootstrap_resolution_active ? 'ACTIVE' : 'TRANSITIONED'}
Staked Resolution
{status.ramp.staked_resolution_active ? 'ACTIVE' : 'LOCKED'}
Petitions
{status.ramp.governance_petitions_active ? 'ACTIVE' : 'LOCKED'}
Upgrade Governance
{status.ramp.upgrade_governance_active ? 'ACTIVE' : 'LOCKED'}
CommonCoin
{status.ramp.commoncoin_active ? 'ACTIVE' : 'LOCKED'}
)} {market && (
Market: {market.market_id}
YES votes
{market.tally.yes}
NO votes
{market.tally.no}
Total Eligible
{market.tally.total_eligible}
Min Required
{market.tally.min_market_participants}
Cast Bootstrap Vote
Eligibility: identity age ≥{' '} {status ? '3 days' : 'configured threshold'}{' '} vs market.snapshot.frozen_at, NOT in predictor exclusion set, and a valid Argon2id PoW (Heavy-Node-only). The PoW nonce input is for testnet — production wires the Argon2id solver via privacy-core when the Rust binding lands.
setPowNonce(e.target.value)} placeholder="pow_nonce" className="bg-black/60 border border-gray-700 px-2 py-1 text-white font-mono w-32" />
{voteAction.result && !voteAction.result.ok && (
{voteAction.result.reason}
)}
All Submitted Votes
{market.votes.map((v) => (
{v.node_id.slice(0, 16)}… {v.side?.toUpperCase()} {v.eligible ? ( ) : ( {v.ineligible_reason} )}
))}
)} {!marketId && (
Open a bootstrap-indexed market from the Markets view to see its eligible-node-one-vote tally here.
)}
); }