# Blockchain Consensus Design - Proof of Uptime with Game Theory ## Current State Analysis ### How It Works Now (Line 199-201 in Blockchain.js) ```javascript static start() { log("Blockchain process started. New block will be proposed every 10 minutes."); setInterval(this.createBlock.bind(this), 10 * 60 * 1000); // Every 10 minutes } ``` **Current Flow:** 1. **Every town independently** creates blocks every 10 minutes 2. Each town broadcasts its block to peers 3. **Problem**: No consensus, no merge, each town has different blocks! 4. **Result**: Network diverges, no single truth **Issues:** - ❌ Multiple conflicting blocks every 10 minutes - ❌ No leader election - ❌ No block validation voting - ❌ No chain merge mechanism - ❌ Network splits into N separate chains ## Proposed Solution: Proof of Uptime + Rotating Leader + Byzantine Fault Tolerance ### Core Principles 1. **Proof of Uptime (PoU)**: Towns earn validation rights by staying online 2. **Rotating Leader**: One validator per round, selected by uptime score 3. **Byzantine Fault Tolerance**: Tolerates up to 33% malicious nodes 4. **Economic Incentives**: Aligned with human behavior (game theory) 5. **Sybil Resistance**: High cost to create fake towns ## System Architecture ### 1. Uptime Tracking Each town tracks its **uptime score** based on: - **Availability**: % time online and reachable - **Block participation**: % of rounds where town voted - **Data quality**: % of valid transactions submitted ```javascript // Uptime Score Calculation uptimeScore = { availability: 0.0 - 1.0, // 0% to 100% uptime in last 7 days participation: 0.0 - 1.0, // % of consensus rounds participated reputation: 0.0 - 1.0, // % of valid blocks/votes submitted totalScore: (availability * 0.5) + (participation * 0.3) + (reputation * 0.2) } ``` **Data Structure:** ```json { "townId": "farm", "uptimeMetrics": { "lastSeen": 1705750800000, "totalUptime": 604800000, // 7 days in ms "availabilityScore": 0.98, "participationCount": 985, "totalRounds": 1000, "participationScore": 0.985, "validBlocksSubmitted": 50, "totalBlocksSubmitted": 52, "reputationScore": 0.96, "totalScore": 0.973 } } ``` ### 2. Leader Selection (Weighted Random) **Algorithm: Weighted Random Selection** ```javascript // Select leader based on uptime scores function selectLeader(towns, roundNumber) { // Filter active towns (seen in last 15 minutes) const activeTowns = towns.filter(t => Date.now() - t.uptimeMetrics.lastSeen < 15 * 60 * 1000 ); // Calculate total weight const totalWeight = activeTowns.reduce((sum, t) => sum + t.uptimeMetrics.totalScore, 0 ); // Use round number as seed for deterministic selection // All towns calculate the same leader! const seed = roundNumber; const randomValue = seededRandom(seed) * totalWeight; let cumulativeWeight = 0; for (const town of activeTowns) { cumulativeWeight += town.uptimeMetrics.totalScore; if (randomValue <= cumulativeWeight) { return town.townId; } } } // Deterministic random (same seed = same result on all nodes) function seededRandom(seed) { const x = Math.sin(seed) * 10000; return x - Math.floor(x); } ``` **Why This Works:** - ✅ **Deterministic**: All towns calculate same leader - ✅ **Fair**: Higher uptime = higher chance to lead - ✅ **Sybil resistant**: New towns have 0 uptime, can't dominate - ✅ **No communication**: Pure math, no coordination needed ### 3. Consensus Round (10-Minute Cycle) #### Phase 1: Transaction Collection (0:00 - 8:00) - All towns collect transactions locally - Transactions added to pending pool #### Phase 2: Block Proposal (8:00 - 8:30) - **Leader** (selected by uptime) creates block proposal - Leader aggregates its own transactions - Leader broadcasts proposal to all peers #### Phase 3: Validation Vote (8:30 - 9:30) - **All towns** verify block proposal: - Check transactions are valid - Check signatures are correct - Check no double-spends - Each town votes: APPROVE or REJECT - Vote includes their signature #### Phase 4: Finalization (9:30 - 10:00) - Leader counts votes - If **>66% APPROVE**: Block is finalized - Leader broadcasts final block with votes - All towns apply transactions and update blockchain ``` Timeline (10 minutes): |-------- 8 min --------|--30s--|--60s--|--30s--| [Tx Collection] [Proposal][Vote][Final] Leader All Leader creates vote counts ``` ## Implementation ### Phase 1: Uptime Heartbeat System ```javascript class UptimeTracker { /** * Each town broadcasts heartbeat every 2 minutes */ static async sendHeartbeat() { const heartbeat = { townId: process.env.TOWN, timestamp: Date.now(), blockHeight: blockchain.length, pendingTxCount: pendingTransactions.length, signature: await this._sign({ townId: process.env.TOWN, timestamp: Date.now() }) }; await Blockchain._broadcast('/heartbeat', heartbeat); } /** * Receive and store peer heartbeats */ static async receiveHeartbeat(heartbeat) { if (!await this._verifyHeartbeat(heartbeat)) return; const townFile = path.join(DATA_PATH, 'apxtri/blockchain/uptime', `${heartbeat.townId}.json`); let uptimeData = await fs.readJson(townFile).catch(() => ({ townId: heartbeat.townId, firstSeen: Date.now(), heartbeats: [] })); uptimeData.heartbeats.push({ timestamp: heartbeat.timestamp, blockHeight: heartbeat.blockHeight }); // Keep last 5000 heartbeats (~7 days at 2-minute intervals) if (uptimeData.heartbeats.length > 5000) { uptimeData.heartbeats = uptimeData.heartbeats.slice(-5000); } // Calculate metrics uptimeData.metrics = this._calculateUptimeMetrics(uptimeData.heartbeats); await fs.outputJson(townFile, uptimeData); } /** * Calculate uptime metrics from heartbeat history */ static _calculateUptimeMetrics(heartbeats) { const now = Date.now(); const sevenDays = 7 * 24 * 60 * 60 * 1000; const recentHeartbeats = heartbeats.filter(h => now - h.timestamp < sevenDays); // Expected: 1 heartbeat every 2 minutes = 5040 in 7 days const expectedHeartbeats = sevenDays / (2 * 60 * 1000); const availabilityScore = Math.min(recentHeartbeats.length / expectedHeartbeats, 1.0); return { lastSeen: heartbeats[heartbeats.length - 1]?.timestamp || 0, totalHeartbeats: recentHeartbeats.length, availabilityScore, totalScore: availabilityScore // Can add participation and reputation later }; } static start() { // Send heartbeat every 2 minutes setInterval(this.sendHeartbeat.bind(this), 2 * 60 * 1000); // Clean old heartbeats daily setInterval(this._cleanOldHeartbeats.bind(this), 24 * 60 * 60 * 1000); } } ``` ### Phase 2: Leader-Based Consensus ```javascript class Consensus { /** * Main consensus round (runs every 10 minutes) */ static async runConsensusRound() { const roundNumber = Math.floor(Date.now() / (10 * 60 * 1000)); const currentTown = process.env.TOWN; log(`[Consensus] Starting round #${roundNumber}`); // 1. Determine leader for this round const allTowns = await this._getAllTownsWithUptime(); const leader = this._selectLeader(allTowns, roundNumber); log(`[Consensus] Leader for round #${roundNumber}: ${leader}`); if (leader === currentTown) { // I am the leader await this._leaderProposeBlock(roundNumber); } else { // I am a validator await this._validatorWaitForProposal(roundNumber, leader); } } /** * Leader creates and proposes block */ static async _leaderProposeBlock(roundNumber) { log(`[Consensus] I am the leader. Creating block proposal...`); if (pendingTransactions.length === 0) { log(`[Consensus] No transactions. Skipping round.`); return; } // Collect transactions from all towns (optional: request from peers) const block = { index: blockchain.length, roundNumber, timestamp: Date.now(), transactions: [...pendingTransactions], previousHash: blockchain.length > 0 ? blockchain[blockchain.length - 1].hash : "0".repeat(64), proposer: process.env.TOWN, votes: [] }; block.hash = Blockchain._calculateHash(block); block.signature = await Blockchain._sign(block); // Broadcast proposal log(`[Consensus] Broadcasting block proposal #${block.index}`); await Blockchain._broadcast('/propose', block); // Wait 60 seconds for votes await new Promise(resolve => setTimeout(resolve, 60 * 1000)); // Count votes const votes = await this._collectVotes(block.hash); const totalVotes = votes.length; const approveVotes = votes.filter(v => v.vote === 'approve').length; const approvalRate = approveVotes / totalVotes; log(`[Consensus] Votes: ${approveVotes}/${totalVotes} (${(approvalRate * 100).toFixed(1)}%)`); // Require 66% approval (Byzantine fault tolerance) if (approvalRate >= 0.66) { block.votes = votes; blockchain.push(block); pendingTransactions = []; // Generate APX3 reward for leader const APX3 = require('./APX3'); await APX3.generateBlockReward(block, process.env.TOWN_MAYOR || process.env.TOWN); log(`[Consensus] ✅ Block #${block.index} FINALIZED with ${approveVotes} votes`); // Broadcast final block await Blockchain._broadcast('/finalize', block); } else { log(`[Consensus] ❌ Block REJECTED. Not enough votes (${approvalRate * 100}% < 66%)`); } } /** * Validator receives proposal and votes */ static async _validatorWaitForProposal(roundNumber, leader) { log(`[Consensus] Waiting for proposal from leader ${leader}...`); // Wait up to 90 seconds for proposal const startTime = Date.now(); while (Date.now() - startTime < 90 * 1000) { const proposal = await this._checkForProposal(roundNumber); if (proposal) { log(`[Consensus] Received proposal. Validating...`); const isValid = await this._validateProposal(proposal); const vote = { roundNumber, blockHash: proposal.hash, voter: process.env.TOWN, vote: isValid ? 'approve' : 'reject', timestamp: Date.now(), signature: await Blockchain._sign({ roundNumber, blockHash: proposal.hash, vote: isValid ? 'approve' : 'reject' }) }; log(`[Consensus] Voting: ${vote.vote.toUpperCase()}`); // Send vote to leader const leaderTown = await this._getTownByID(leader); const voteUrl = `http://${leaderTown.dns[0]}/apxtri/blockchain/vote`; await axios.post(voteUrl, vote).catch(err => log(`Failed to send vote to leader: ${err.message}`) ); // Wait for finalization await this._waitForFinalization(roundNumber); return; } await new Promise(resolve => setTimeout(resolve, 1000)); } log(`[Consensus] Timeout waiting for proposal from ${leader}`); } /** * Validate block proposal */ static async _validateProposal(proposal) { try { // 1. Verify signature if (!await Blockchain._verifySignature(proposal)) { log(`[Consensus] Invalid signature`); return false; } // 2. Check transactions are valid for (const tx of proposal.transactions) { // Check transaction format if (!tx.id || !tx.type || !tx.model) { log(`[Consensus] Invalid transaction format`); return false; } // For APX3 transactions, verify balance if (tx.model === 'apx3') { const APX3 = require('./APX3'); const balance = await APX3.getBalance(tx.from); if (balance < tx.amount) { log(`[Consensus] Insufficient balance for APX3 tx`); return false; } } } // 3. Check no duplicate transactions const txIds = new Set(); for (const tx of proposal.transactions) { if (txIds.has(tx.id)) { log(`[Consensus] Duplicate transaction detected`); return false; } txIds.add(tx.id); } return true; } catch (error) { log(`[Consensus] Validation error: ${error.message}`); return false; } } /** * Select leader using weighted random based on uptime */ static _selectLeader(towns, roundNumber) { const activeTowns = towns.filter(t => Date.now() - t.uptimeMetrics.lastSeen < 15 * 60 * 1000 && t.uptimeMetrics.totalScore > 0.5 // Minimum 50% uptime to lead ); if (activeTowns.length === 0) { return process.env.TOWN; // Fallback } const totalWeight = activeTowns.reduce((sum, t) => sum + t.uptimeMetrics.totalScore, 0 ); // Deterministic selection const seed = roundNumber + 12345; // Add constant to avoid predictability const x = Math.sin(seed) * 10000; const randomValue = (x - Math.floor(x)) * totalWeight; let cumulativeWeight = 0; for (const town of activeTowns) { cumulativeWeight += town.uptimeMetrics.totalScore; if (randomValue <= cumulativeWeight) { return town.townId; } } return activeTowns[0].townId; // Fallback } static start() { // Start consensus rounds every 10 minutes // Offset by 30 seconds to allow transaction collection setTimeout(() => { this.runConsensusRound(); setInterval(this.runConsensusRound.bind(this), 10 * 60 * 1000); }, 30 * 1000); } } ``` ## Game Theory Analysis ### Incentive Structure #### Honest Behavior Rewards 1. **Block Rewards**: 10 APX3 per validated block (only if >66% approve) 2. **Reputation Score**: Higher chance to be selected as future leader 3. **Transaction Fees**: Collect fees from all transactions in block 4. **Network Trust**: Better uptime = more influence #### Malicious Behavior Costs 1. **Stake at Risk**: Towns must register with DNS (provable cost) 2. **Reputation Loss**: Bad blocks reduce uptime score 3. **Block Rejection**: Lose 10 minutes of potential earnings 4. **Network Exclusion**: Consistent bad behavior = ignored by peers 5. **Economic Loss**: Wasted electricity, server costs with no reward ### Attack Scenarios & Defenses #### Attack 1: Double-Spend Attack **Scenario**: Malicious leader includes double-spend transaction **Defense**: - ✅ Validators check transaction validity - ✅ >66% reject block - ✅ Leader gets 0 reward - ✅ Leader's reputation drops **Game Theory**: Cost (lost reward + reputation) > Benefit (failed double-spend) #### Attack 2: Sybil Attack (Fake Towns) **Scenario**: Attacker creates 100 fake towns to dominate voting **Defense**: - ✅ Each town needs registered DNS (cost: $10-50/domain) - ✅ New towns have 0 uptime (0% chance to lead) - ✅ Takes 7 days to build uptime score - ✅ Heartbeat verification with signatures **Game Theory**: Cost ($1000+ for 100 domains + 7 days) > Benefit (might not work) #### Attack 3: Censorship (Leader Ignores Transactions) **Scenario**: Malicious leader excludes specific users' transactions **Defense**: - ✅ Validators can see missing transactions (compare with their pool) - ✅ Can reject block if suspicious - ✅ Next round, different leader includes them - ✅ Max delay: 10 minutes **Game Theory**: Cost (block rejection + reputation) > Benefit (temporary delay) #### Attack 4: DDoS on Leader **Scenario**: Attacker floods leader to prevent block creation **Defense**: - ✅ If no proposal in 90 seconds, round fails gracefully - ✅ Next round, different leader (rotating) - ✅ Leader changes every 10 minutes - ✅ DDoS protection at Caddy level **Game Theory**: Cost (DDoS service) > Benefit (delay 1 round) #### Attack 5: 34% Collusion (Byzantine Attack) **Scenario**: 34% of towns collude to approve bad blocks **Defense**: - ✅ Requires >66% approval (Byzantine fault tolerance) - ✅ Need to compromise 67% of network - ✅ High uptime towns harder to compromise (established reputation) - ✅ Economic cost very high **Game Theory**: - To control 67% of voting power: ~67 high-uptime towns - Cost: $670 domains + 7 days + server costs - Benefit: Can manipulate blockchain - Defense: Social layer - community notices suspicious pattern ### Optimal Strategy (Nash Equilibrium) **For Honest Town**: 1. Maximize uptime → Higher leader selection chance 2. Validate honestly → Build reputation 3. Vote correctly → Block gets approved, earn fees 4. Participate consistently → Compound reputation **For Malicious Town**: 1. Create bad block → 0 reward (rejected) 2. Vote dishonestly → Block fails anyway (minority) 3. Stay offline → 0 uptime, never selected 4. **Conclusion**: Malicious behavior is not profitable! **Nash Equilibrium**: All towns have incentive to behave honestly. ## Comparison with Alternatives | Mechanism | Pros | Cons | Suitability | |-----------|------|------|-------------| | **Proof of Work (PoW)** | Battle-tested, secure | Wasteful energy, slow | ❌ Overkill for apxtri | | **Proof of Stake (PoS)** | Energy efficient | Rich get richer, complex | ⚠️ Requires staking mechanism | | **Proof of Authority (PoA)** | Fast, efficient | Centralized, trust-based | ❌ Against decentralization | | **Proof of Uptime (PoU)** | Meritocratic, efficient | New mechanism | ✅ Perfect for apxtri! | ## Migration Path ### Phase 1: Heartbeat System (Week 1) - Implement uptime tracking - All towns start broadcasting heartbeats - Build 7 days of uptime data ### Phase 2: Leader Selection (Week 2) - Implement deterministic leader selection - Test leader rotation logic - Verify all towns calculate same leader ### Phase 3: Consensus Voting (Week 3) - Implement proposal/vote/finalize flow - Test with small network (3-5 towns) - Verify Byzantine fault tolerance ### Phase 4: Full Deployment (Week 4) - Deploy to production network - Monitor for 2 weeks - Tune parameters if needed ## Configuration ### Environment Variables (.env) ```bash # Consensus Settings CONSENSUS_ENABLED=true BLOCK_TIME=600000 # 10 minutes HEARTBEAT_INTERVAL=120000 # 2 minutes MIN_UPTIME_TO_LEAD=0.5 # 50% minimum VOTE_THRESHOLD=0.66 # 66% approval needed PROPOSAL_TIMEOUT=90000 # 90 seconds VOTE_COLLECTION_TIME=60000 # 60 seconds ``` ## Summary **Proof of Uptime + Rotating Leader** is ideal for apxtri because: ✅ **Merit-Based**: Towns that stay online get rewarded ✅ **Sybil Resistant**: New towns can't instantly dominate ✅ **Energy Efficient**: No wasteful mining ✅ **Fast**: 10-minute blocks with 90-second finalization ✅ **Byzantine Tolerant**: Works with 33% malicious nodes ✅ **Economically Sound**: Honest behavior is most profitable ✅ **Decentralized**: No single point of failure ✅ **Human-Aligned**: Rewards reliability and good citizenship **Game Theory Conclusion**: The system creates a **positive-sum game** where cooperation benefits everyone more than defection. The optimal strategy is to be an honest, reliable validator.