Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
584
vendor/ruvector/examples/edge/pkg/zk-demo.html
vendored
Normal file
584
vendor/ruvector/examples/edge/pkg/zk-demo.html
vendored
Normal file
@@ -0,0 +1,584 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user