Files
apxtri/documents/08-CONSENSUS_DESIGN.md
T
2026-05-18 12:13:27 +02:00

613 lines
19 KiB
Markdown
Executable File

# 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.