Consensus Mechanisms

Consensus is how distributed systems agree on a single truth. In blockchains, this means agreeing on transaction ordering and validity without a central authority.

The Fundamental Problem

Byzantine Generals Problem

Imagine several generals surrounding a city. They must all attack or all retreat—partial action fails. But:

  • They can only communicate by messenger
  • Messengers might be intercepted
  • Some generals might be traitors
Text
         [General A] ──"Attack!"──► [General B]
              │                         │
        "Attack!"                  "Attack!"
              │                         │
              ▼                         ▼
         [General C] ◄──"Retreat!"── [Traitor D]

How does C know which message to trust?

Blockchains face the same problem:

  • Nodes must agree on transaction order
  • Messages travel over unreliable networks
  • Some nodes might be malicious

What Consensus Must Achieve

  1. Safety: All honest nodes agree on the same state
  2. Liveness: The system continues making progress
  3. Fault tolerance: Works despite some failures/attacks

Proof of Work (PoW)

Bitcoin's innovation: use computational work as a "vote."

How PoW Works

Text
1. Collect pending transactions
2. Build a block header
3. Find a nonce where hash(header) < target
4. Broadcast block
5. Other nodes verify and build on it
6. Longest chain = consensus

Mining Race:
Miner A: hash, hash, hash... FOUND! Broadcasts block
Miner B: hash, hash, hash...        Receives block, starts over
Miner C: hash, hash, hash...        Receives block, starts over

Security Through Economics

Text
Attack cost calculation:
─────────────────────────
Bitcoin hash rate: ~500 EH/s
Attacker needs: >250 EH/s (51%)
Hardware cost: ~$10 billion
Electricity cost: ~$15 million/day
Expected profit: Negative (attack destroys value)

Result: Economic incentives align with honesty

PoW Code Concept

TypeScript
interface BlockHeader {
  previousHash: string;
  merkleRoot: string;
  timestamp: number;
  difficulty: number;
  nonce: number;
}

function mineBlock(
  header: BlockHeader,
  targetDifficulty: bigint,
): BlockHeader | null {
  const maxNonce = 2 ** 32;

  for (let nonce = 0; nonce < maxNonce; nonce++) {
    header.nonce = nonce;
    const hash = sha256(sha256(serializeHeader(header)));

    if (BigInt("0x" + hash) < targetDifficulty) {
      return header; // Found valid block!
    }
  }

  return null; // Exhausted nonce space
}

// Difficulty adjustment (simplified)
function adjustDifficulty(
  blocks: Block[],
  targetBlockTime: number, // e.g., 600 seconds for Bitcoin
): bigint {
  const recentBlocks = blocks.slice(-2016);
  const timespan =
    recentBlocks[recentBlocks.length - 1].timestamp - recentBlocks[0].timestamp;

  const expectedTimespan = targetBlockTime * 2016;
  const currentDifficulty = BigInt(recentBlocks[0].difficulty);

  // Adjust proportionally (with limits)
  let newDifficulty =
    (currentDifficulty * BigInt(expectedTimespan)) / BigInt(timespan);

  // Limit adjustment to 4x in either direction
  const maxAdjust = currentDifficulty * 4n;
  const minAdjust = currentDifficulty / 4n;

  newDifficulty = newDifficulty > maxAdjust ? maxAdjust : newDifficulty;
  newDifficulty = newDifficulty < minAdjust ? minAdjust : newDifficulty;

  return newDifficulty;
}

PoW Pros and Cons

ProsCons
Battle-tested (15+ years)High energy consumption
Simple security modelSlow finality
Permissionless entryMining centralization risk
Sybil resistant51% attack possible

Proof of Stake (PoS)

Instead of computational work, validators stake capital as collateral.

How PoS Works

Text
1. Validators lock up stake (collateral)
2. Protocol selects proposer (weighted by stake)
3. Proposer creates block
4. Other validators vote/attest
5. Supermajority agreement = finality
6. Bad behavior = stake slashed

Validator Selection:
┌─────────────────────────────────────────┐
│ Validator A: 1000 ETH (10% of total)    │ → 10% chance to propose
│ Validator B: 3000 ETH (30% of total)    │ → 30% chance to propose
│ Validator C: 6000 ETH (60% of total)    │ → 60% chance to propose
└─────────────────────────────────────────┘

Slashing Conditions

Text
Behaviors that get you slashed:
───────────────────────────────
1. Double voting (voting for two blocks at same height)
2. Surround voting (contradictory votes)
3. Proposing invalid blocks
4. Extended downtime (in some protocols)

Slashing severity:
- Minor offense: 1-5% of stake
- Major offense: 10-100% of stake
- Coordinated attack: Higher penalties

PoS Code Concept

TypeScript
interface Validator {
  publicKey: string;
  stake: bigint;
  isActive: boolean;
  slashingHistory: SlashingEvent[];
}

interface Vote {
  validatorPubkey: string;
  blockHash: string;
  slot: number;
  signature: string;
}

function selectProposer(
  validators: Validator[],
  slot: number,
  randomness: string,
): Validator {
  const totalStake = validators
    .filter((v) => v.isActive)
    .reduce((sum, v) => sum + v.stake, 0n);

  // Deterministic selection weighted by stake
  const seed = sha256(randomness + slot.toString());
  const target = BigInt("0x" + seed) % totalStake;

  let cumulative = 0n;
  for (const validator of validators) {
    if (!validator.isActive) continue;
    cumulative += validator.stake;
    if (cumulative > target) {
      return validator;
    }
  }

  throw new Error("No validator selected");
}

function checkSlashing(
  vote1: Vote,
  vote2: Vote,
): "double-vote" | "surround-vote" | null {
  if (vote1.validatorPubkey !== vote2.validatorPubkey) {
    return null; // Different validators
  }

  // Double vote: same slot, different block
  if (vote1.slot === vote2.slot && vote1.blockHash !== vote2.blockHash) {
    return "double-vote";
  }

  // Surround vote: contradictory attestations
  // (Simplified - real check is more complex)

  return null;
}

function slashValidator(
  validator: Validator,
  offense: "double-vote" | "surround-vote",
  validators: Validator[],
): void {
  // Calculate penalty
  let penaltyPercent: number;

  switch (offense) {
    case "double-vote":
      // Higher penalty if many validators slashed simultaneously
      const recentSlashings = validators.filter(
        (v) => v.slashingHistory.length > 0,
      ).length;
      penaltyPercent = Math.min(100, 1 + recentSlashings * 3);
      break;
    case "surround-vote":
      penaltyPercent = 50;
      break;
  }

  const penalty = (validator.stake * BigInt(penaltyPercent)) / 100n;
  validator.stake -= penalty;
  validator.slashingHistory.push({ offense, penalty, timestamp: Date.now() });

  if (validator.stake < MIN_STAKE) {
    validator.isActive = false;
  }
}

PoS Variants

Text
Delegated PoS (Solana, Cosmos):
- Token holders delegate to validators
- Smaller set of active validators
- Higher throughput

Liquid Staking:
- Stake and receive liquid token (stSOL, stETH)
- Use liquid token in DeFi
- Stake compounds

Nominated PoS (Polkadot):
- Nominators back validators
- Rewards and slashing shared

Practical Byzantine Fault Tolerance (pBFT)

Classical consensus for known validator sets.

How pBFT Works

Text
Phase 1: Pre-prepare
Leader sends proposal to all validators

Phase 2: Prepare
Validators broadcast "prepared" messages
Wait for 2f+1 prepare messages (f = max faulty nodes)

Phase 3: Commit
Validators broadcast "commit" messages
Wait for 2f+1 commit messages

Block is finalized when 2f+1 commits received

For n validators, tolerates f faulty where n ≥ 3f+1
Example: 4 validators can tolerate 1 faulty
         100 validators can tolerate 33 faulty

pBFT Message Flow

Text
    Leader      Validator1   Validator2   Validator3
       │            │            │            │
       │─Pre-prepare─►           │            │
       │            │─Pre-prepare─►           │
       │            │            │─Pre-prepare─►
       │            │            │            │
       │◄──Prepare──│            │            │
       │            │◄──Prepare──│            │
       │            │            │◄──Prepare──│
       │◄──Prepare──────────────┤            │
       │            │◄──Prepare──────────────│
       │            │            │◄──Prepare──│
       │            │            │            │
       │◄──Commit───│            │            │
       │            │◄──Commit───│            │
       │            │            │◄──Commit───│
       │            │            │            │
    FINALIZED    FINALIZED    FINALIZED    FINALIZED

pBFT Trade-offs

ProsCons
Instant finalityO(n²) message complexity
Low latencyDoesn't scale well
Energy efficientRequires known validators
DeterministicLeader bottleneck

Solana's Consensus: Tower BFT

Solana uses Tower BFT, an optimized pBFT variant leveraging Proof of History.

Key Innovation: PoH as Time

Text
Traditional pBFT:
- Nodes must wait for messages
- "Did I receive enough votes?"
- "Is the timeout expired?"

Tower BFT with PoH:
- PoH provides verifiable time
- No waiting for wall-clock timeouts
- Votes expire based on PoH ticks

Vote Lockout Mechanism

Text
When a validator votes on slot N:
- They're locked out of voting for conflicting forks
- Lockout doubles with each consecutive vote

Vote history:
Slot 100: lockout = 2 slots
Slot 101: lockout = 4 slots
Slot 102: lockout = 8 slots
Slot 103: lockout = 16 slots
...

After 32 consecutive votes, lockout = 2^32 slots
= effectively permanent commitment

This prevents flip-flopping and enables fast finality

Tower BFT Code Concept

TypeScript
interface TowerVote {
  slot: number;
  hash: string;
  lockout: number; // Slots locked out from voting differently
}

class TowerBFT {
  private voteStack: TowerVote[] = [];

  vote(slot: number, hash: string): boolean {
    // Check if any existing votes would be violated
    for (const existingVote of this.voteStack) {
      const slotsElapsed = slot - existingVote.slot;
      if (slotsElapsed < existingVote.lockout) {
        // Would violate lockout
        return false;
      }
    }

    // Pop expired votes
    this.voteStack = this.voteStack.filter((v) => slot - v.slot < v.lockout);

    // Calculate new lockout (doubles each level)
    const newLockout = Math.pow(2, this.voteStack.length + 1);

    // Add new vote
    this.voteStack.push({ slot, hash, lockout: newLockout });

    return true;
  }

  getRoot(): number | null {
    // Root slot is the oldest vote that's been built upon
    // enough times to have very high lockout
    const rootVote = this.voteStack.find((v) => v.lockout > 1000000);
    return rootVote?.slot ?? null;
  }
}

Comparison: Consensus Mechanisms

MechanismFinalityThroughputEnergyDecentralization
PoW~60 minLowHighMedium
PoS (Eth)~13 minMediumLowMedium
pBFTInstantLowLowLow (fixed set)
Tower BFT~400msHighLowMedium

Understanding Finality

Probabilistic vs Absolute Finality

Text
Probabilistic (PoW):
───────────────────
Block depth    Reversal probability
1              ~50%
2              ~25%
3              ~12.5%
6              ~1.5%
12             ~0.02%

"Final enough" depends on transaction value

Absolute (PoS, Tower BFT):
─────────────────────────
Once supermajority votes and lockouts expire:
Block is FINAL. Cannot be reversed without:
- Slashing >1/3 of stake
- Coordinated protocol violation

Solana Finality Levels

TypeScript
type Commitment = "processed" | "confirmed" | "finalized";

// processed: Seen by connected RPC node
// May be on a fork that gets abandoned
// Latency: ~400ms

// confirmed: Voted by 2/3+ of stake
// Very likely final
// Latency: ~400ms

// finalized: 31+ confirmed blocks deep
// Absolutely final
// Latency: ~12 seconds

Key Takeaways

  1. PoW uses computational work as votes—secure but slow and energy-intensive
  2. PoS uses economic stake—efficient but requires careful incentive design
  3. pBFT provides instant finality—but doesn't scale to many validators
  4. Tower BFT (Solana) combines PoH with pBFT concepts for fast finality
  5. No consensus is perfect—each optimizes for different properties

Common Mistakes

  1. Confusing confirmation with finality: Seeing a transaction ≠ it being final
  2. Ignoring reorg risk: On probabilistic finality chains, recent blocks can change
  3. Assuming instant finality means no delays: Network latency still exists

Try It Yourself

  1. Calculate attack cost: If Ethereum has $50B staked and slashes 33% for coordinated attacks, what's the minimum attack cost?

  2. Analyze lockouts: In Tower BFT, after voting on 10 consecutive slots, what's the lockout on the oldest vote?

  3. Compare finality: You're building a payment app. How many confirmations would you wait for on Bitcoin vs Solana?


Next: Double-Spend Problem - Understanding and preventing the core attack blockchains are designed to stop.