18 KiB
18 KiB
Edge-Net Genesis Nodes on Google Cloud
Deploy genesis relay nodes as Google Cloud Functions for global edge distribution. Manage rUv (Resource Utility Vouchers) ledger and bootstrap the network until self-sustaining.
┌─────────────────────────────────────────────────────────────────────────┐
│ GENESIS NODE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ GLOBAL EDGE NETWORK │ │
│ │ │ │
│ │ us-east1 europe-west1 asia-east1 │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │Genesis │ │Genesis │ │Genesis │ │ │
│ │ │Node 1 │◄──────►│Node 2 │◄─────────►│Node 3 │ │ │
│ │ └───┬────┘ └───┬────┘ └───┬────┘ │ │
│ │ │ │ │ │ │
│ │ └─────────────────┼────────────────────┘ │ │
│ │ │ │ │
│ │ ┌───────────▼───────────┐ │ │
│ │ │ Cloud Firestore │ │ │
│ │ │ (QDAG Ledger Sync) │ │ │
│ │ └───────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Browser Nodes Connect to Nearest Genesis Node via Edge CDN │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Why Google Cloud Functions?
| Feature | Benefit |
|---|---|
| Global Edge | 35+ regions, <50ms latency worldwide |
| Auto-scaling | 0 to millions of requests |
| Pay-per-use | $0 when idle, pennies under load |
| Cold start | <100ms with min instances |
| WebSocket | Via Cloud Run for persistent connections |
Prerequisites
# Install Google Cloud SDK
curl https://sdk.cloud.google.com | bash
# Login and set project
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
# Enable required APIs
gcloud services enable \
cloudfunctions.googleapis.com \
run.googleapis.com \
firestore.googleapis.com \
secretmanager.googleapis.com
Deployment Steps
1. Create Firestore Database
# Create Firestore in Native mode (for QDAG ledger sync)
gcloud firestore databases create \
--region=nam5 \
--type=firestore-native
2. Store Genesis Keys
# Generate genesis keypair
node -e "
const crypto = require('crypto');
const keypair = crypto.generateKeyPairSync('ed25519');
console.log(JSON.stringify({
public: keypair.publicKey.export({type: 'spki', format: 'der'}).toString('hex'),
private: keypair.privateKey.export({type: 'pkcs8', format: 'der'}).toString('hex')
}));
" > genesis-keys.json
# Store in Secret Manager
gcloud secrets create edge-net-genesis-keys \
--data-file=genesis-keys.json
# Clean up local file
rm genesis-keys.json
3. Deploy Genesis Functions
# Deploy to multiple regions
for REGION in us-east1 europe-west1 asia-east1; do
gcloud functions deploy edge-net-genesis-$REGION \
--gen2 \
--runtime=nodejs20 \
--region=$REGION \
--source=. \
--entry-point=genesisHandler \
--trigger-http \
--allow-unauthenticated \
--memory=256MB \
--timeout=60s \
--min-instances=1 \
--max-instances=100 \
--set-env-vars=REGION=$REGION,NODE_ENV=production
done
4. Deploy WebSocket Relay (Cloud Run)
# Build and push container
gcloud builds submit \
--tag gcr.io/YOUR_PROJECT/edge-net-relay
# Deploy to Cloud Run
gcloud run deploy edge-net-relay \
--image gcr.io/YOUR_PROJECT/edge-net-relay \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--memory 512Mi \
--min-instances 1 \
--max-instances 10 \
--concurrency 1000 \
--timeout 3600
Genesis Node Code
index.js (Cloud Function)
const functions = require('@google-cloud/functions-framework');
const { Firestore } = require('@google-cloud/firestore');
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');
const firestore = new Firestore();
const secrets = new SecretManagerServiceClient();
// Genesis node state
let genesisKeys = null;
let ledgerState = null;
// Initialize genesis node
async function init() {
if (genesisKeys) return;
// Load genesis keys from Secret Manager
const [version] = await secrets.accessSecretVersion({
name: 'projects/YOUR_PROJECT/secrets/edge-net-genesis-keys/versions/latest',
});
genesisKeys = JSON.parse(version.payload.data.toString());
// Load or create genesis ledger
const genesisDoc = await firestore.collection('edge-net').doc('genesis').get();
if (!genesisDoc.exists) {
// Create genesis transaction
ledgerState = await createGenesisLedger();
await firestore.collection('edge-net').doc('genesis').set(ledgerState);
} else {
ledgerState = genesisDoc.data();
}
}
// Create genesis ledger with initial supply
async function createGenesisLedger() {
const crypto = require('crypto');
const genesis = {
id: crypto.randomBytes(32).toString('hex'),
type: 'genesis',
amount: 1_000_000_000_000_000, // 1 billion rUv (Resource Utility Vouchers)
recipient: genesisKeys.public,
timestamp: Date.now(),
transactions: [],
tips: [],
totalSupply: 1_000_000_000_000_000,
networkCompute: 0,
nodeCount: 0,
// Genesis sunset thresholds
sunsetPhase: 0, // 0=active, 1=transition, 2=read-only, 3=retired
sunsetThresholds: {
stopNewConnections: 10_000,
readOnlyMode: 50_000,
safeRetirement: 100_000,
},
};
return genesis;
}
// Main handler
functions.http('genesisHandler', async (req, res) => {
// CORS
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.set('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
return res.status(204).send('');
}
await init();
const { action, data } = req.body || {};
try {
switch (action) {
case 'status':
return res.json({
nodeId: `genesis-${process.env.REGION}`,
region: process.env.REGION,
ledger: {
totalSupply: ledgerState.totalSupply,
networkCompute: ledgerState.networkCompute,
nodeCount: ledgerState.nodeCount,
tipCount: ledgerState.tips.length,
},
multiplier: calculateMultiplier(ledgerState.networkCompute),
currency: 'rUv', // Resource Utility Vouchers
sunsetStatus: getSunsetStatus(ledgerState),
});
case 'register':
return await handleRegister(data, res);
case 'submitTransaction':
return await handleTransaction(data, res);
case 'getTips':
return res.json({ tips: ledgerState.tips.slice(-10) });
case 'sync':
return await handleSync(data, res);
default:
return res.status(400).json({ error: 'Unknown action' });
}
} catch (error) {
console.error('Error:', error);
return res.status(500).json({ error: error.message });
}
});
// Handle node registration
async function handleRegister(data, res) {
const { nodeId, pubkey, stake } = data;
// Validate registration
if (!nodeId || !pubkey) {
return res.status(400).json({ error: 'Missing nodeId or pubkey' });
}
// Store node in Firestore
await firestore.collection('edge-net').doc('nodes').collection(nodeId).set({
pubkey,
stake: stake || 0,
registeredAt: Date.now(),
region: process.env.REGION,
reputation: 0.5,
});
ledgerState.nodeCount++;
return res.json({
success: true,
nodeId,
multiplier: calculateMultiplier(ledgerState.networkCompute),
});
}
// Handle QDAG transaction
async function handleTransaction(data, res) {
const { transaction, signature } = data;
// Validate transaction
if (!validateTransaction(transaction, signature)) {
return res.status(400).json({ error: 'Invalid transaction' });
}
// Apply to ledger
await applyTransaction(transaction);
// Store in Firestore
await firestore.collection('edge-net').doc('transactions')
.collection(transaction.id).set(transaction);
// Update tips
ledgerState.tips = ledgerState.tips.filter(
tip => !transaction.validates.includes(tip)
);
ledgerState.tips.push(transaction.id);
// Sync to other genesis nodes
await syncToOtherNodes(transaction);
return res.json({
success: true,
txId: transaction.id,
newBalance: await getBalance(transaction.sender),
});
}
// Handle ledger sync from other genesis nodes
async function handleSync(data, res) {
const { transactions, fromNode } = data;
let imported = 0;
for (const tx of transactions) {
if (!ledgerState.transactions.find(t => t.id === tx.id)) {
if (validateTransaction(tx, tx.signature)) {
await applyTransaction(tx);
imported++;
}
}
}
return res.json({ imported, total: ledgerState.transactions.length });
}
// Validate transaction signature and structure
function validateTransaction(tx, signature) {
// TODO: Implement full Ed25519 verification
return tx && tx.id && tx.sender && tx.recipient && tx.amount >= 0;
}
// Apply transaction to ledger state
async function applyTransaction(tx) {
ledgerState.transactions.push(tx);
// Update network compute for reward calculation
if (tx.type === 'compute_reward') {
ledgerState.networkCompute += tx.computeHours || 0;
}
// Persist to Firestore
await firestore.collection('edge-net').doc('genesis').update({
transactions: ledgerState.transactions,
tips: ledgerState.tips,
networkCompute: ledgerState.networkCompute,
});
}
// Calculate contribution curve multiplier
function calculateMultiplier(networkCompute) {
const MAX_BONUS = 10.0;
const DECAY_CONSTANT = 1_000_000;
return 1 + (MAX_BONUS - 1) * Math.exp(-networkCompute / DECAY_CONSTANT);
}
// Get genesis sunset status
function getSunsetStatus(ledger) {
const thresholds = ledger.sunsetThresholds || {
stopNewConnections: 10_000,
readOnlyMode: 50_000,
safeRetirement: 100_000,
};
let phase = 0;
let phaseName = 'active';
if (ledger.nodeCount >= thresholds.safeRetirement) {
phase = 3;
phaseName = 'retired';
} else if (ledger.nodeCount >= thresholds.readOnlyMode) {
phase = 2;
phaseName = 'read_only';
} else if (ledger.nodeCount >= thresholds.stopNewConnections) {
phase = 1;
phaseName = 'transition';
}
return {
phase,
phaseName,
nodeCount: ledger.nodeCount,
nextThreshold: phase === 0 ? thresholds.stopNewConnections :
phase === 1 ? thresholds.readOnlyMode :
phase === 2 ? thresholds.safeRetirement : 0,
canRetire: phase >= 3,
message: phase >= 3 ?
'Network is self-sustaining. Genesis nodes can be safely retired.' :
`${((ledger.nodeCount / thresholds.safeRetirement) * 100).toFixed(1)}% to self-sustaining`
};
}
// Get balance for a node
async function getBalance(nodeId) {
let balance = 0;
for (const tx of ledgerState.transactions) {
if (tx.recipient === nodeId) balance += tx.amount;
if (tx.sender === nodeId) balance -= tx.amount;
}
return balance;
}
// Sync transaction to other genesis nodes
async function syncToOtherNodes(transaction) {
const regions = ['us-east1', 'europe-west1', 'asia-east1'];
const currentRegion = process.env.REGION;
for (const region of regions) {
if (region === currentRegion) continue;
try {
const url = `https://${region}-YOUR_PROJECT.cloudfunctions.net/edge-net-genesis-${region}`;
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'sync',
data: {
transactions: [transaction],
fromNode: `genesis-${currentRegion}`,
},
}),
});
} catch (error) {
console.error(`Failed to sync to ${region}:`, error.message);
}
}
}
package.json
{
"name": "edge-net-genesis",
"version": "1.0.0",
"main": "index.js",
"engines": {
"node": ">=20"
},
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0",
"@google-cloud/firestore": "^7.0.0",
"@google-cloud/secret-manager": "^5.0.0"
}
}
WebSocket Relay (Cloud Run)
Dockerfile
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 8080
CMD ["node", "relay.js"]
relay.js
const WebSocket = require('ws');
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Edge-Net Relay\n');
});
const wss = new WebSocket.Server({ server });
// Connected nodes
const nodes = new Map();
// Handle WebSocket connections
wss.on('connection', (ws, req) => {
const nodeId = req.headers['x-node-id'] || `anon-${Date.now()}`;
nodes.set(nodeId, ws);
console.log(`Node connected: ${nodeId}`);
ws.on('message', (data) => {
try {
const message = JSON.parse(data);
handleMessage(nodeId, message, ws);
} catch (error) {
console.error('Invalid message:', error);
}
});
ws.on('close', () => {
nodes.delete(nodeId);
console.log(`Node disconnected: ${nodeId}`);
});
// Send welcome message
ws.send(JSON.stringify({
type: 'welcome',
nodeId,
peers: nodes.size,
}));
});
// Handle incoming messages
function handleMessage(fromId, message, ws) {
switch (message.type) {
case 'broadcast':
// Broadcast to all other nodes
for (const [id, peer] of nodes) {
if (id !== fromId && peer.readyState === WebSocket.OPEN) {
peer.send(JSON.stringify({
type: 'message',
from: fromId,
data: message.data,
}));
}
}
break;
case 'direct':
// Send to specific node
const target = nodes.get(message.to);
if (target && target.readyState === WebSocket.OPEN) {
target.send(JSON.stringify({
type: 'message',
from: fromId,
data: message.data,
}));
}
break;
case 'peers':
// Return list of connected peers
ws.send(JSON.stringify({
type: 'peers',
peers: Array.from(nodes.keys()).filter(id => id !== fromId),
}));
break;
default:
console.warn('Unknown message type:', message.type);
}
}
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`Edge-Net Relay listening on port ${PORT}`);
});
Monitoring
Cloud Monitoring Dashboard
# Create dashboard
gcloud monitoring dashboards create \
--config-from-file=dashboard.json
dashboard.json
{
"displayName": "Edge-Net Genesis Nodes",
"mosaicLayout": {
"columns": 12,
"tiles": [
{
"width": 6,
"height": 4,
"widget": {
"title": "Request Count by Region",
"xyChart": {
"dataSets": [{
"timeSeriesQuery": {
"timeSeriesFilter": {
"filter": "resource.type=\"cloud_function\" AND metric.type=\"cloudfunctions.googleapis.com/function/execution_count\""
}
}
}]
}
}
},
{
"xPos": 6,
"width": 6,
"height": 4,
"widget": {
"title": "Execution Latency",
"xyChart": {
"dataSets": [{
"timeSeriesQuery": {
"timeSeriesFilter": {
"filter": "resource.type=\"cloud_function\" AND metric.type=\"cloudfunctions.googleapis.com/function/execution_times\""
}
}
}]
}
}
}
]
}
}
Cost Estimate
| Component | Monthly Cost (Low Traffic) | Monthly Cost (High Traffic) |
|---|---|---|
| Cloud Functions (3 regions) | $5 | $50 |
| Cloud Run (WebSocket) | $10 | $100 |
| Firestore | $1 | $25 |
| Secret Manager | $0.06 | $0.06 |
| Total | ~$16 | ~$175 |
Security Checklist
- Enable Cloud Armor for DDoS protection
- Configure VPC Service Controls
- Set up Cloud Audit Logs
- Enable Binary Authorization
- Configure IAM least privilege
- Enable Secret Manager rotation
- Set up alerting policies
Next Steps
- Deploy to all regions
- Initialize genesis ledger
- Configure DNS with global load balancer
- Set up monitoring and alerting
- Run load tests
- Enable Cloud CDN for static assets