File size: 7,025 Bytes
93c7565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
 * dashboard/server.js
 * ----------------------------------------------------------
 * ProofBridge Liner — Operations Dashboard.
 *
 * A small Express server that surfaces:
 *   • Phase progress for the 72-hour MVP sprint
 *   • Configured assets and their last fetcher result
 *   • Live signer-node heartbeat (best-effort)
 *   • The CircuitBreaker contract address (once deployed)
 *
 * Trusts the proxy host header (Replit iframe preview).
 */

const express = require('express');
const fs = require('fs');
const path = require('path');

const ROOT = path.resolve(__dirname, '..');
const ASSETS_PATH = path.join(ROOT, 'config', 'assets.json');
const SIGNERS_PATH = path.join(ROOT, 'config', 'signer-nodes.json');
const STATE_PATH = path.join(ROOT, '.local', 'state', 'prover-state.json');

const PORT = Number(process.env.DASHBOARD_PORT || 5000);
const HOST = process.env.DASHBOARD_HOST || '0.0.0.0';

const PHASES = [
  { id: 0, name: 'Env scaffold',                        pct: 100 },
  { id: 1, name: 'Write & test CircuitBreaker',         pct: 100 },
  { id: 2, name: 'Deploy to Polygon Amoy',              pct: 100 },
  { id: 3, name: 'Build fetcher + submitter',           pct: 100 },
  { id: 4, name: 'Mock 3-node quorum (Docker)',         pct: 100 },
  { id: 5, name: 'Institution-grade: TEE + Registry',  pct: 75 },
  { id: 6, name: 'Coq + TLA+ formal proofs',           pct: 100 },
  { id: 7, name: 'E2E demo recording',                  pct: 0   },
  { id: 8, name: 'Ghost-risk audit & pitch',            pct: 0   },
];

const TEST_RESULTS = [
  { name: 'testInitializeSetsOwnerAndOracle',     gas: 14321, passed: true },
  { name: 'testInitializeRevertsOnSecondCall',    gas: 13902, passed: true },
  { name: 'testUpdateProofByOracle',              gas: 44241, passed: true },
  { name: 'testUpdateProofEmitsEvent',            gas: 45177, passed: true },
  { name: 'testUpdateProofRevertsIfNotOracle',    gas: 13738, passed: true },
  { name: 'testTripCircuitByOracle',              gas: 44116, passed: true },
  { name: 'testTripCircuitEmitsEvent',            gas: 45030, passed: true },
  { name: 'testTripCircuitRevertsIfNotOracle',    gas: 13671, passed: true },
  { name: 'testValidateWhenOpenAndHashMatches',   gas: 48449, passed: true },
  { name: 'testValidateWhenOpenAndHashDoesNotMatch', gas: 48473, passed: true },
  { name: 'testValidateRevertsWhenCircuitTripped',   gas: 44732, passed: true },
  { name: 'testResetByOwner',                     gas: 45034, passed: true },
  { name: 'testResetEmitsEvent',                  gas: 45925, passed: true },
  { name: 'testResetRevertsIfNotOwner',           gas: 44683, passed: true },
];

function readJsonSafe(p, fallback) {
  try {
    return JSON.parse(fs.readFileSync(p, 'utf8'));
  } catch (_) {
    return fallback;
  }
}

const app = express();
app.disable('etag');

// --- Replit preview lives behind a proxy iframe; trust the proxy ---
app.set('trust proxy', true);

// --- Dev-mode no-cache so the iframe always sees latest content ---
if (process.env.NODE_ENV !== 'production') {
  app.use((req, res, next) => {
    res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
    res.set('Pragma', 'no-cache');
    res.set('Expires', '0');
    next();
  });
}

app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));

app.get('/api/status', (_req, res) => {
  res.json({
    project: 'ProofBridge Liner',
    tagline: 'Ghost-Risk Circuit-Breaker for tokenised real-world assets',
    network: 'Polygon Amoy (testnet)',
    circuitBreakerAddress: process.env.CIRCUIT_BREAKER_ADDRESS || null,
    oracleAddress: process.env.ORACLE_ADDRESS || null,
    assetRegistryAddress: process.env.ASSET_REGISTRY_ADDRESS || null,
    teeVerifierAddress: process.env.TEE_VERIFIER_ADDRESS || null,
    enclaveAddress: process.env.ENCLAVE_ADDRESS || null,
    phases: PHASES,
    tests: {
      total: TEST_RESULTS.length,
      passed: TEST_RESULTS.filter((t) => t.passed).length,
      results: TEST_RESULTS,
    },
    architecture: {
      layers: [
        {
          id: 'logic',
          name: 'Logic Layer',
          description: 'Coq-verified total functions',
          artifact: 'proofs/SafetyKernel.v',
          theorems: [
            'unauthorized_halt_is_absorbing',
            'posterior_above_threshold_trips',
            'posterior_below_threshold_stays_open',
            'auth_can_reset',
          ],
          status: 'proven',
        },
        {
          id: 'input',
          name: 'Input Layer',
          description: 'TEE-signed attestations (EIP-191 ECDSA)',
          artifact: 'contracts/TEEVerifier.sol',
          status: 'deployed-pending',
        },
        {
          id: 'enforcement',
          name: 'Enforcement Layer',
          description: 'EVM circuit breakers — per-asset isolated kernels',
          artifact: 'contracts/AssetRegistry.sol',
          status: 'deployed-pending',
        },
      ],
      verification: [
        { name: 'Coq Proof',    status: 'complete', note: 'UNAUTH actors cannot reset' },
        { name: 'Gas analysis', status: 'complete', note: 'O(1) check() execution' },
        { name: 'TLA+ Model',   status: 'complete', note: 'No deadlocks — 4 invariants + liveness property' },
        { name: 'SOC 2 CC6',    status: 'complete', note: 'CC6.1/2/3/6/7/8 — all controls mapped' },
      ],
    },
    assets: readJsonSafe(ASSETS_PATH, []),
    signerNodes: readJsonSafe(SIGNERS_PATH, []),
    proverState: readJsonSafe(STATE_PATH, null),
    serverTime: new Date().toISOString(),
  });
});

app.get('/health', (_req, res) => res.json({ status: 'ok', uptime: process.uptime() }));

app.get('/api/health', async (req, res) => {
  const gateways = [
    'https://ipfs.io/ipfs/',
    'https://cloudflare-ipfs.com/ipfs/',
    'https://gateway.pinata.cloud/ipfs/',
  ];
  const gatewayHealth = {};
  await Promise.all(gateways.map(async (gateway) => {
    const start = Date.now();
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 4000);
    try {
      await fetch(`${gateway}QmbWqxBEKC3P8tqsKc98xmWNzrzDRRLbhtJ38WNqHVWojK`, {
        method: 'HEAD',
        signal: controller.signal,
      });
      gatewayHealth[gateway] = { status: 'healthy', latency: Date.now() - start };
    } catch (err) {
      gatewayHealth[gateway] = { status: 'unreachable', error: err.message };
    } finally {
      clearTimeout(timer);
    }
  }));

  // Read prover state if exists
  let proverState = {};
  try {
    const stateFile = path.resolve(__dirname, '..', '.local', 'state', 'prover-state.json');
    if (fs.existsSync(stateFile)) {
      proverState = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
    }
  } catch (e) { /* ignore */ }

  res.json({
    uptime: process.uptime(),
    gateways: gatewayHealth,
    proverState
  });
});

app.listen(PORT, HOST, () => {
  console.log(`[dashboard] ProofBridge Liner Ops listening on http://${HOST}:${PORT}`);
});