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.