Files
wifi-densepose/vendor/ruvector/examples/edge/pkg/zk-demo.html

585 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZK Financial Proofs Demo - RuVector Edge</title>
<style>
:root {
--bg: #0a0a0f;
--card: #12121a;
--border: #2a2a3a;
--text: #e0e0e8;
--text-dim: #8888a0;
--accent: #8b5cf6;
--accent-glow: rgba(139, 92, 246, 0.3);
--success: #22c55e;
--warning: #f59e0b;
--error: #ef4444;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
line-height: 1.6;
}
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; }
header { text-align: center; margin-bottom: 3rem; }
h1 {
font-size: 2.5rem;
background: linear-gradient(135deg, var(--accent), #ec4899);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle { color: var(--text-dim); font-size: 1.1rem; margin-top: 0.5rem; }
.privacy-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: rgba(139, 92, 246, 0.1);
border: 1px solid rgba(139, 92, 246, 0.3);
color: var(--accent);
padding: 0.5rem 1rem;
border-radius: 2rem;
margin-top: 1rem;
font-size: 0.9rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 1rem;
padding: 1.5rem;
}
.card h2 {
font-size: 1.2rem;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-group { margin-bottom: 1rem; }
label {
display: block;
font-size: 0.9rem;
color: var(--text-dim);
margin-bottom: 0.25rem;
}
input, select {
background: var(--bg);
border: 1px solid var(--border);
color: var(--text);
padding: 0.75rem;
border-radius: 0.5rem;
font-size: 1rem;
width: 100%;
}
input:focus, select:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-glow);
}
button {
background: var(--accent);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 20px var(--accent-glow);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
button.secondary {
background: transparent;
border: 1px solid var(--border);
color: var(--text);
}
.proof-display {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1rem;
font-family: 'Fira Code', monospace;
font-size: 0.85rem;
white-space: pre-wrap;
word-break: break-all;
max-height: 300px;
overflow-y: auto;
}
.verification-result {
padding: 1rem;
border-radius: 0.5rem;
margin-top: 1rem;
}
.verification-result.valid {
background: rgba(34, 197, 94, 0.1);
border: 1px solid rgba(34, 197, 94, 0.3);
}
.verification-result.invalid {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
}
.flow-diagram {
background: var(--bg);
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1.5rem;
text-align: center;
margin-bottom: 1.5rem;
}
.flow-step {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: var(--card);
border-radius: 0.5rem;
margin: 0 0.25rem;
}
.flow-arrow {
color: var(--text-dim);
font-size: 1.5rem;
}
.tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
.tab {
padding: 0.5rem 1rem;
background: transparent;
border: 1px solid var(--border);
color: var(--text-dim);
cursor: pointer;
border-radius: 0.5rem;
}
.tab.active {
background: var(--accent);
border-color: var(--accent);
color: white;
}
.hidden { display: none; }
.info-box {
background: rgba(139, 92, 246, 0.05);
border: 1px solid rgba(139, 92, 246, 0.2);
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 1rem;
font-size: 0.9rem;
}
footer {
text-align: center;
margin-top: 3rem;
padding-top: 2rem;
border-top: 1px solid var(--border);
color: var(--text-dim);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🔐 Zero-Knowledge Financial Proofs</h1>
<p class="subtitle">Prove financial statements without revealing actual numbers</p>
<div class="privacy-badge">
🛡️ Your actual income, balance, and transactions are NEVER revealed
</div>
</header>
<!-- Flow Diagram -->
<div class="flow-diagram">
<span class="flow-step">📊 Your Private Data</span>
<span class="flow-arrow"></span>
<span class="flow-step">🔮 ZK Circuit (WASM)</span>
<span class="flow-arrow"></span>
<span class="flow-step">📜 Proof (~1KB)</span>
<span class="flow-arrow"></span>
<span class="flow-step">✅ Verifier</span>
</div>
<div class="grid">
<!-- Prover Panel -->
<div class="card">
<h2>👤 Prover (Your Data - Private)</h2>
<div class="info-box">
<strong>How it works:</strong> Enter your real financial data below.
The ZK system will generate a proof that ONLY reveals the statement is true,
not your actual numbers.
</div>
<div class="form-group">
<label>Monthly Income ($)</label>
<input type="number" id="income" value="6500" placeholder="e.g., 6500">
</div>
<div class="form-group">
<label>Current Savings ($)</label>
<input type="number" id="savings" value="15000" placeholder="e.g., 15000">
</div>
<div class="form-group">
<label>Monthly Rent ($)</label>
<input type="number" id="rent" value="2000" placeholder="e.g., 2000">
</div>
<div class="form-group">
<label>Proof Type</label>
<select id="proof-type">
<option value="affordability">Rental Affordability (Income ≥ 3× Rent)</option>
<option value="income">Income Above Threshold</option>
<option value="savings">Savings Above Threshold</option>
<option value="no-overdraft">No Overdrafts (90 days)</option>
<option value="full-application">Complete Rental Application</option>
</select>
</div>
<button id="generate-btn" onclick="generateProof()">
🔮 Generate ZK Proof
</button>
<div id="prover-result" style="margin-top: 1rem;"></div>
</div>
<!-- Verifier Panel -->
<div class="card">
<h2>🏢 Verifier (Landlord/Bank - No Private Data)</h2>
<div class="info-box">
<strong>What verifier sees:</strong> Only the proof and statement.
Cannot determine actual income, savings, or any other numbers.
</div>
<div class="tabs">
<button class="tab active" onclick="showTab('paste')">Paste Proof</button>
<button class="tab" onclick="showTab('received')">Received Proof</button>
</div>
<div id="tab-paste">
<div class="form-group">
<label>Proof JSON</label>
<textarea id="proof-input" class="proof-display" rows="8"
placeholder="Paste proof JSON here..."></textarea>
</div>
</div>
<div id="tab-received" class="hidden">
<div class="proof-display" id="received-proof">
No proof received yet. Generate one from the Prover panel.
</div>
</div>
<button onclick="verifyProof()">
✅ Verify Proof
</button>
<div id="verification-result"></div>
</div>
<!-- What's Proven vs Hidden -->
<div class="card">
<h2>🔍 What's Proven vs What's Hidden</h2>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="border-bottom: 1px solid var(--border);">
<th style="padding: 0.5rem; text-align: left;">Statement</th>
<th style="padding: 0.5rem; text-align: center;">Proven</th>
<th style="padding: 0.5rem; text-align: center;">Hidden</th>
</tr>
</thead>
<tbody id="proof-breakdown">
<tr>
<td style="padding: 0.5rem;">Income ≥ 3× Rent</td>
<td style="padding: 0.5rem; text-align: center; color: var(--success);">✓ Yes/No</td>
<td style="padding: 0.5rem; text-align: center; color: var(--error);">🔒 Exact amount</td>
</tr>
</tbody>
</table>
<div style="margin-top: 1rem; padding: 1rem; background: var(--bg); border-radius: 0.5rem;">
<strong>Privacy Guarantee:</strong>
<p style="color: var(--text-dim); margin-top: 0.5rem; font-size: 0.9rem;">
The verifier mathematically CANNOT extract your actual numbers from the proof.
They only learn whether the statement is true or false.
</p>
</div>
</div>
<!-- Use Cases -->
<div class="card">
<h2>💡 Real-World Use Cases</h2>
<div style="display: flex; flex-direction: column; gap: 1rem;">
<div style="padding: 1rem; background: var(--bg); border-radius: 0.5rem;">
<strong>🏠 Rental Applications</strong>
<p style="color: var(--text-dim); font-size: 0.9rem;">
Prove you can afford rent without revealing exact salary
</p>
</div>
<div style="padding: 1rem; background: var(--bg); border-radius: 0.5rem;">
<strong>💳 Credit Applications</strong>
<p style="color: var(--text-dim); font-size: 0.9rem;">
Prove debt-to-income ratio without revealing all debts
</p>
</div>
<div style="padding: 1rem; background: var(--bg); border-radius: 0.5rem;">
<strong>💼 Employment Verification</strong>
<p style="color: var(--text-dim); font-size: 0.9rem;">
Prove you earn above minimum without revealing exact pay
</p>
</div>
<div style="padding: 1rem; background: var(--bg); border-radius: 0.5rem;">
<strong>🏦 Account Stability</strong>
<p style="color: var(--text-dim); font-size: 0.9rem;">
Prove no overdrafts without revealing transaction history
</p>
</div>
</div>
</div>
</div>
<footer>
<p>Powered by <strong>RuVector Edge</strong> • Bulletproofs-style ZK Proofs • 100% Browser-Local</p>
</footer>
</div>
<script type="module">
// Simulated ZK proof generation (in production, uses WASM)
let lastProof = null;
window.generateProof = async function() {
const btn = document.getElementById('generate-btn');
btn.disabled = true;
btn.innerHTML = '⏳ Generating...';
const income = parseFloat(document.getElementById('income').value);
const savings = parseFloat(document.getElementById('savings').value);
const rent = parseFloat(document.getElementById('rent').value);
const proofType = document.getElementById('proof-type').value;
// Simulate proof generation
await new Promise(r => setTimeout(r, 500));
let statement, canProve;
switch (proofType) {
case 'affordability':
canProve = income >= rent * 3;
statement = `Income ≥ 3× monthly rent of $${rent}`;
break;
case 'income':
canProve = income >= 5000;
statement = `Average monthly income ≥ $5,000`;
break;
case 'savings':
canProve = savings >= rent * 2;
statement = `Current savings ≥ $${rent * 2}`;
break;
case 'no-overdraft':
canProve = savings > 0;
statement = `No overdrafts in the past 90 days`;
break;
case 'full-application':
canProve = income >= rent * 3 && savings >= rent * 2;
statement = `Complete rental application for $${rent}/month`;
break;
}
if (!canProve) {
document.getElementById('prover-result').innerHTML = `
<div style="color: var(--error); padding: 1rem; background: rgba(239,68,68,0.1); border-radius: 0.5rem;">
❌ Cannot generate proof: Your data doesn't meet the requirement.
<br><small>Actual numbers never leave your browser.</small>
</div>
`;
btn.disabled = false;
btn.innerHTML = '🔮 Generate ZK Proof';
return;
}
// Generate proof structure
lastProof = {
proof_type: proofType === 'affordability' ? 'Affordability' : 'Range',
proof_data: Array.from({length: 256}, () => Math.floor(Math.random() * 256)),
public_inputs: {
commitments: [{
point: Array.from({length: 32}, () => Math.floor(Math.random() * 256))
}],
bounds: [rent * 100, 3],
statement: statement,
},
generated_at: Math.floor(Date.now() / 1000),
expires_at: Math.floor(Date.now() / 1000) + 86400 * 30,
};
const proofJson = JSON.stringify(lastProof, null, 2);
document.getElementById('prover-result').innerHTML = `
<div style="color: var(--success); margin-bottom: 0.5rem;">
✅ Proof generated successfully!
</div>
<div class="proof-display" style="max-height: 200px;">
${proofJson}
</div>
<button class="secondary" style="margin-top: 0.5rem;" onclick="copyProof()">
📋 Copy Proof
</button>
`;
document.getElementById('received-proof').textContent = proofJson;
document.getElementById('proof-input').value = proofJson;
updateBreakdown(income, savings, rent, proofType);
btn.disabled = false;
btn.innerHTML = '🔮 Generate ZK Proof';
};
window.verifyProof = function() {
const proofJson = document.getElementById('proof-input').value ||
document.getElementById('received-proof').textContent;
if (!proofJson || proofJson.includes('No proof')) {
alert('Please generate or paste a proof first');
return;
}
try {
const proof = JSON.parse(proofJson);
// Simulate verification
const result = {
valid: true,
statement: proof.public_inputs.statement,
verified_at: Math.floor(Date.now() / 1000),
};
document.getElementById('verification-result').innerHTML = `
<div class="verification-result ${result.valid ? 'valid' : 'invalid'}">
<h3>${result.valid ? '✅ Proof Valid' : '❌ Proof Invalid'}</h3>
<p style="margin-top: 0.5rem;"><strong>Statement:</strong> ${result.statement}</p>
<p style="margin-top: 0.5rem; color: var(--text-dim); font-size: 0.9rem;">
${result.valid
? 'The prover has demonstrated the statement is TRUE without revealing actual values.'
: 'The proof could not be verified.'}
</p>
</div>
`;
} catch (e) {
document.getElementById('verification-result').innerHTML = `
<div class="verification-result invalid">
<h3>❌ Invalid Proof Format</h3>
<p>${e.message}</p>
</div>
`;
}
};
window.copyProof = function() {
const proofJson = JSON.stringify(lastProof);
navigator.clipboard.writeText(proofJson);
alert('Proof copied to clipboard!');
};
window.showTab = function(tab) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelector(`[onclick="showTab('${tab}')"]`).classList.add('active');
document.getElementById('tab-paste').classList.toggle('hidden', tab !== 'paste');
document.getElementById('tab-received').classList.toggle('hidden', tab !== 'received');
};
function updateBreakdown(income, savings, rent, proofType) {
const tbody = document.getElementById('proof-breakdown');
const rows = {
'affordability': [
['Income ≥ 3× Rent', '✓ True/False', '🔒 $' + income.toLocaleString()],
['Rent amount', '✓ $' + rent.toLocaleString(), '—'],
],
'income': [
['Income ≥ $5,000', '✓ True/False', '🔒 $' + income.toLocaleString()],
],
'savings': [
['Savings ≥ $' + (rent * 2).toLocaleString(), '✓ True/False', '🔒 $' + savings.toLocaleString()],
],
'no-overdraft': [
['No overdrafts (90 days)', '✓ True/False', '🔒 All balances'],
],
'full-application': [
['Income ≥ 3× Rent', '✓ True/False', '🔒 $' + income.toLocaleString()],
['No overdrafts', '✓ True/False', '🔒 All balances'],
['Savings ≥ 2× Rent', '✓ True/False', '🔒 $' + savings.toLocaleString()],
],
};
tbody.innerHTML = (rows[proofType] || rows['affordability']).map(([stmt, proven, hidden]) => `
<tr>
<td style="padding: 0.5rem;">${stmt}</td>
<td style="padding: 0.5rem; text-align: center; color: var(--success);">${proven}</td>
<td style="padding: 0.5rem; text-align: center; color: var(--error);">${hidden}</td>
</tr>
`).join('');
}
// Initialize
updateBreakdown(6500, 15000, 2000, 'affordability');
</script>
</body>
</html>