Tower BFT
Tower BFT is Solana's consensus mechanism—a modified version of Practical Byzantine Fault Tolerance (PBFT) that leverages Proof of History for radical efficiency improvements.
What Is Tower BFT?
Tower BFT is a voting-based consensus algorithm that achieves:
- Byzantine fault tolerance (survives malicious actors)
- High throughput (no waiting for network timeouts)
- Fast finality (~400ms to confirmation, ~13s to finalization)
Text
TOWER BFT vs TRADITIONAL PBFT
═════════════════════════════
Traditional PBFT:
├── 3-phase protocol (pre-prepare, prepare, commit)
├── O(n²) message complexity
├── Network timeout for each phase
├── ~1000 TPS maximum
└── ~3-10 seconds finality
Tower BFT:
├── 1-phase voting
├── O(n) message complexity (via gossip)
├── PoH provides timing (no timeouts needed)
├── ~65,000 TPS theoretical
└── ~400ms confirmation, ~13s finalization
The Core Mechanism
Vote Tower
Each validator maintains a "tower" of votes:
Text
VOTE TOWER VISUALIZATION
════════════════════════
Lockout
Slot Vote Period
─────────────────────────────────
500 ████ 2 slots
499 ████████ 4 slots
498 ████████████ 8 slots
497 ██████████████████16 slots
496 ███████████████████████32 slots
... ... ...
(oldest) (most locked)
Key insight: Each vote DOUBLES the lockout of all previous votes.
Switching cost increases exponentially with tower height.
After 32 votes: Finalized (locked for 2³¹ slots ≈ never)
Lockout Rules
TypeScript
interface Vote {
slot: number; // Slot being voted on
confirmations: number; // How many newer votes confirm this
lockout: number; // 2^confirmations slots
}
interface VoteTower {
votes: Vote[];
root: number; // Oldest finalized slot
// Rule: Can only vote on slot S if:
// For all votes V in tower:
// S >= V.slot + V.lockout
// OR
// S is descendant of V.slot
}
// Example tower state
const tower: VoteTower = {
votes: [
{ slot: 100, confirmations: 5, lockout: 32 }, // Locked
{ slot: 101, confirmations: 4, lockout: 16 },
{ slot: 102, confirmations: 3, lockout: 8 },
{ slot: 103, confirmations: 2, lockout: 4 },
{ slot: 104, confirmations: 1, lockout: 2 },
{ slot: 105, confirmations: 0, lockout: 1 }, // Latest
],
root: 95, // Everything before 95 is finalized
};
Vote Progression
Text
VOTING EXAMPLE
══════════════
Validator sees slot 100, votes:
Tower: [100 (lockout=1)]
Slot 101 arrives (builds on 100), votes:
Tower: [100 (lockout=2), 101 (lockout=1)]
└─── 100's lockout doubled!
Slot 102 arrives (builds on 101), votes:
Tower: [100 (lockout=4), 101 (lockout=2), 102 (lockout=1)]
... continues ...
After 32 slots building on 100:
Tower: [100 (lockout=2³¹ ≈ ∞), ...]
└─── Slot 100 is now FINALIZED
└─── Cannot switch to any fork not containing 100
Fork Choice Rule
When the chain forks, how does a validator choose?
Text
FORK CHOICE SCENARIO
════════════════════
┌──[102A]──[103A]──[104A]
│ 15% 10% 5%
[100]──[101]┤
50% 40% │
└──[102B]──[103B]
25% 20%
Numbers = percentage of total stake voted
Fork choice algorithm:
1. Start at root
2. At each fork, choose branch with most stake-weighted votes
3. Continue until tip
Here: 100 → 101 → 102B → 103B
(102B has 25% vs 102A's 15%)
Implementation
TypeScript
// Simplified fork choice
function chooseFork(
bankForks: Map<number, { parent: number; votes: number }>,
root: number,
): number {
let current = root;
while (true) {
// Find children of current slot
const children = Array.from(bankForks.entries())
.filter(([_, data]) => data.parent === current)
.map(([slot, data]) => ({ slot, votes: data.votes }));
if (children.length === 0) {
// Reached tip
return current;
}
// Choose child with most votes
children.sort((a, b) => b.votes - a.votes);
current = children[0].slot;
}
}
Optimistic Confirmation
Solana provides confirmation before finalization:
Text
CONFIRMATION LEVELS
═══════════════════
PROCESSED (~400ms):
├── Block exists in leader's output
├── Not yet voted on
└── Can be rolled back
CONFIRMED (~400-800ms):
├── Supermajority (>66%) of stake voted
├── Very unlikely to roll back
├── Would require >33% malicious stake
└── Safe for most applications
FINALIZED (~12-13 seconds):
├── Maximum lockout reached
├── Cannot be rolled back
├── Would require coordinated attack
└── Required for exchanges/high-value
Confirmation Math
Text
WHY 66% SUPERMAJORITY?
══════════════════════
Byzantine Fault Tolerance requires:
├── n > 3f (n nodes, f byzantine)
├── Need 2f+1 honest votes (>66%)
└── At most f can be dishonest (<33%)
With 66% voted:
├── At most 33% didn't vote
├── Even if ALL non-voters are malicious
├── Plus some voters switch (impossible due to lockout)
├── Still have honest majority
└── Fork cannot succeed
Safety: 66% voted means CERTAIN confirmation
(assuming <33% byzantine)
Vote Transactions
Votes are submitted as on-chain transactions:
TypeScript
import { VoteProgram, PublicKey, Transaction } from "@solana/web3.js";
// Vote account structure (on-chain)
interface VoteState {
nodePubkey: PublicKey; // Validator identity
authorizedVoter: PublicKey; // Who can vote
authorizedWithdrawer: PublicKey;
commission: number; // Reward commission %
// Tower state
votes: Lockout[]; // Recent votes
rootSlot: number; // Finalized slot
// Reward tracking
priorVoters: PriorVoter[];
epochCredits: EpochCredit[]; // For rewards
}
// Creating a vote instruction
function createVoteInstruction(
votePubkey: PublicKey,
authorizedVoterPubkey: PublicKey,
vote: {
slots: number[];
hash: string;
timestamp: number;
},
): Transaction {
return VoteProgram.vote({
votePubkey,
authorizedVoterPubkey,
vote: {
slots: vote.slots,
hash: Buffer.from(vote.hash, "hex"),
timestamp: vote.timestamp,
},
});
}
Vote Account Queries
TypeScript
import { Connection, VoteAccountStatus } from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
// Get all vote accounts
const voteAccounts: VoteAccountStatus = await connection.getVoteAccounts();
console.log("Current validators:", voteAccounts.current.length);
console.log("Delinquent validators:", voteAccounts.delinquent.length);
// Analyze a vote account
for (const va of voteAccounts.current.slice(0, 3)) {
console.log({
validator: va.nodePubkey,
voteAccount: va.votePubkey,
stake: va.activatedStake / 1e9, // Convert lamports to SOL
commission: va.commission,
lastVote: va.lastVote,
rootSlot: va.rootSlot,
epochCredits: va.epochCredits.slice(-3), // Last 3 epochs
});
}
// Get specific vote account
const voteAccount = await connection.getAccountInfo(votePubkey);
// Parse vote state from account data...
Optimistic Concurrency Control
Tower BFT enables speculative execution:
Text
OPTIMISTIC EXECUTION
════════════════════
Traditional approach:
1. Wait for consensus
2. Then execute
3. Slow but safe
Solana approach:
1. Execute speculatively
2. Continue building
3. Roll back if fork switch needed
Timeline:
─────────────────────────────────────────────────────
Slot 100 Slot 101 Slot 102 Slot 103
│ │ │ │
▼ ▼ ▼ ▼
Execute Execute Execute Execute
Update Update Update Update
state state state state
│ │ │ │
│ │ │ │
└────────────┴────────────┴────────────┘
│
▼
If fork switch:
Replay from last
common ancestor
─────────────────────────────────────────────────────
Benefits:
├── No waiting for votes
├── Continuous progress
├── Pipelining throughput
└── Fork switches are rare
Consensus Safety
Slashing Conditions
Currently, Solana doesn't implement slashing, but violations are detectable:
TypeScript
// Theoretically slashable conditions
interface SlashableViolation {
// Double voting: Voting for two different blocks at same slot
doubleVote: {
slot: number;
vote1Hash: string;
vote2Hash: string;
signature1: string;
signature2: string;
};
// Surround voting: Vote A surrounds vote B
// (A.source < B.source && A.target > B.target)
surroundVote: {
voteA: { source: number; target: number };
voteB: { source: number; target: number };
};
// Tower violation: Voting against locked slot
lockoutViolation: {
lockedSlot: number;
lockoutRemaining: number;
votedSlot: number;
notDescendant: boolean;
};
}
// Detection (would be verified by network)
function detectDoubleVote(vote1: SignedVote, vote2: SignedVote): boolean {
return (
vote1.slot === vote2.slot &&
vote1.hash !== vote2.hash &&
vote1.validator === vote2.validator &&
verifySignature(vote1) &&
verifySignature(vote2)
);
}
Performance Characteristics
Vote Timing
Text
VOTE PROPAGATION TIMELINE
═════════════════════════
T+0ms: Leader produces block
T+50ms: Block reaches validators via Turbine
T+100ms: Validators process block
T+150ms: Validators create vote transactions
T+200ms: Votes propagate via gossip
T+300ms: Leader of voting slot sees votes
T+400ms: Vote transactions in next block
Full confirmation cycle: ~400-800ms
│
┌───────────┼───────────┐
│ │ │
▼ ▼ ▼
Block 100 Block 101 Block 102
│ │ │
│ Votes for 100 │
│ │ │
└───────────┼───────────┘
│
▼
100 confirmed
after 101-102
Stake Distribution Impact
Text
STAKE CONCENTRATION EFFECTS
═══════════════════════════
More concentrated stake = faster confirmation
(fewer votes needed for supermajority)
Example scenarios:
─────────────────────────────────────────────────
Distribution | Votes for 66% | Speed
─────────────────────────────────────────────────
3 validators @ 33% | 2 votes | Fastest
10 validators @ 10% | 7 votes | Fast
100 validators @ 1% | 67 votes | Moderate
1700 validators mixed | ~50 votes | Current
─────────────────────────────────────────────────
Solana reality (~1700 validators):
├── Top ~20 validators have ~33% stake
├── Top ~100 have ~66%
├── Confirmation needs votes from ~50-100 validators
└── Fast confirmation despite decentralization
Handling Network Partitions
Text
PARTITION SCENARIO
══════════════════
Network splits into two groups:
Partition A (40% stake) Partition B (60% stake)
[V1, V2, V3, ...] [V4, V5, V6, ...]
│ │
▼ ▼
Fork A grows Fork B grows
No confirmation Confirms at 60%
(< 66%) (< 66%, no finalization)
When partition heals:
├── Fork B has more stake
├── All validators switch to B
├── Fork A transactions replayed or dropped
└── Network converges
Tower BFT ensures:
├── A validators were locked on A
├── Must wait for lockout expiry
├── Then can switch to B
└── No safety violation
Practical Monitoring
TypeScript
// Monitor consensus health
async function monitorConsensus(connection: Connection) {
// 1. Check slot progression
const slot = await connection.getSlot("confirmed");
const finalizedSlot = await connection.getSlot("finalized");
const confirmationLag = slot - finalizedSlot;
console.log(`Confirmed: ${slot}, Finalized: ${finalizedSlot}`);
console.log(`Confirmation lag: ${confirmationLag} slots`);
// 2. Check validator participation
const voteAccounts = await connection.getVoteAccounts();
const activeStake = voteAccounts.current.reduce(
(sum, v) => sum + v.activatedStake,
0,
);
const delinquentStake = voteAccounts.delinquent.reduce(
(sum, v) => sum + v.activatedStake,
0,
);
const totalStake = activeStake + delinquentStake;
console.log(
`Active stake: ${((activeStake / totalStake) * 100).toFixed(1)}%`,
);
console.log(
`Delinquent: ${((delinquentStake / totalStake) * 100).toFixed(1)}%`,
);
// 3. Healthy if > 66% active (can reach consensus)
const isHealthy = activeStake / totalStake > 0.66;
console.log(`Consensus healthy: ${isHealthy}`);
return {
slot,
finalizedSlot,
confirmationLag,
activeStakePercent: (activeStake / totalStake) * 100,
isHealthy,
};
}
Key Takeaways
- Tower BFT combines PBFT safety with PoH efficiency
- Exponential lockouts create economic finality
- 66% supermajority provides Byzantine fault tolerance
- Optimistic confirmation (~400ms) is safe for most uses
- Finalization (~13s) guarantees irreversibility
Next: Account Structure - Deep dive into Solana's account model.