Skip to content
Learning Platform
Intermediate
30 minutes
Tower BFT Консенсус Lockout Голосования Gulf Stream Финализация

Prerequisites:

  • 05-solana/02-proof-of-history

Tower BFT

Зачем это блокчейну?

В предыдущем уроке мы разобрали PoH — криптографические часы, которые доказывают порядок событий. Но часы сами по себе не решают главную задачу: как валидаторы соглашаются на одну версию истории?

Ethereum использует Casper FFG + LMD GHOST — два протокола, работающих вместе. Финализация занимает ~12.8 минут (2 эпохи по 32 слота). Tower BFT — это вариант PBFT (Practical Byzantine Fault Tolerance), оптимизированный для использования с PoH. Он финализирует блоки за ~13 секунд.

Ключевая идея: PoH заменяет обмен сообщениями для синхронизации времени. Благодаря этому Tower BFT требует один раунд голосования вместо нескольких. Это фундаментальное отличие от классического PBFT, где нужны фазы pre-prepare, prepare и commit.

# Классический PBFT (3 фазы, O(n^2) сообщений):
# 1. Pre-prepare: лидер предлагает блок
# 2. Prepare: валидаторы подтверждают получение
# 3. Commit: валидаторы подтверждают готовность зафиксировать
# Итого: 3 раунда сообщений, ~3 * network_latency

# Tower BFT (1 фаза, O(n) сообщений):
# 1. Лидер публикует блок с PoH-меткой
# 2. Валидаторы голосуют (один раунд)
# PoH заменяет фазы prepare/commit -- "часы" уже синхронизированы
# Итого: 1 раунд сообщений, ~1 * network_latency

Интуитивное объяснение: аналогия с ставками

Представьте покерный турнир, где каждый игрок делает ставки на “правильную” руку:

  • Первая ставка: 2 фишки. “Я верю, что рука #100 выиграет.”
  • Вторая ставка: ваша первая ставка удваивается до 4 фишек, плюс новая ставка в 2 фишки.
  • Третья ставка: первая = 8, вторая = 4, третья = 2.
  • После 32 ставок: первая ставка заблокирована на 2^32 = 4 миллиарда раундов.

Отменить глубокий голос становится экспоненциально дороже. Это и есть Tower BFT.

Башня голосований: пошагово

Каждый валидатор поддерживает “башню” (tower) своих голосов. При каждом новом голосе lockout всех предыдущих голосов удваивается.

Tower BFT: башня голосований
Башня пуста -- нет голосов
Шаг 1 из 6Каждый валидатор поддерживает "башню" (tower) своих голосов. Пока башня пуста -- валидатор еще ни за что не голосовал.

Алгоритмический уровень

class VoteTower:
    """Башня голосований валидатора в Tower BFT"""

    def __init__(self):
        self.tower = []  # Стек: [(slot, lockout, confirmations)]

    def vote(self, slot: int):
        """Проголосовать за слот"""
        # Удалить голоса, чей lockout истек
        self.tower = [
            (s, lo, conf)
            for (s, lo, conf) in self.tower
            if s + lo > slot  # lockout еще не истек
        ]

        # Каждый существующий голос удваивает свой lockout
        self.tower = [
            (s, lo * 2, conf + 1)
            for (s, lo, conf) in self.tower
        ]

        # Добавить новый голос на вершину
        self.tower.append((slot, 2, 1))  # lockout=2, confirmations=1

        # Проверить rooted (32+ confirmations)
        rooted = [
            (s, lo, conf)
            for (s, lo, conf) in self.tower
            if conf >= 32
        ]
        for r in rooted:
            self.tower.remove(r)
            print(f"  ROOTED (finalized): slot {r[0]}")

    def can_switch_fork(self, new_slot: int) -> bool:
        """Можно ли переключиться на другой форк?"""
        for (s, lo, conf) in self.tower:
            if s + lo > new_slot:
                return False  # Lockout не истек -- нельзя!
        return True

    def display(self):
        print("  Tower (снизу вверх):")
        for s, lo, conf in self.tower:
            print(f"    Slot {s}: lockout={lo}, confirmations={conf}")

# Демонстрация:
tower = VoteTower()
for slot in [100, 101, 102, 103]:
    print(f"\nГолос за слот {slot}:")
    tower.vote(slot)
    tower.display()

# Вывод:
# Голос за слот 100: lockout=2
# Голос за слот 101: slot 100 lockout=4, slot 101 lockout=2
# Голос за слот 102: slot 100 lockout=8, slot 101 lockout=4, slot 102 lockout=2
# Голос за слот 103: slot 100 lockout=16, slot 101 lockout=8, ...

Почему экспоненциальный lockout?

# Lockout растет экспоненциально с каждым подтверждением:
# confirmations: 1   2   3   4    5    ...  32
# lockout:       2   4   8   16   32   ...  2^32 = 4,294,967,296

# Это значит:
# - Отменить ПОСЛЕДНИЙ голос: нужно подождать 2 слота (~0.8s)
# - Отменить 4-й голос снизу: нужно подождать 16 слотов (~6.4s)
# - Отменить 10-й голос: нужно подождать 1,024 слота (~6.8 минут)
# - Отменить 32-й голос: нужно подождать 2^32 слотов (~54 ЛЕТ)

# Экономический эффект: если валидатор голосует нечестно
# и потом хочет переключиться на правильный форк,
# его старые голоса "заблокированы" -- он теряет вознаграждение
# за все слоты, пока lockout не истечет

Математический уровень

Определение (Vote Tower):
  Стек голосов V = {(s_1, l_1, c_1), ..., (s_n, l_n, c_n)} где:
    s_i -- номер слота
    l_i = 2^{c_i} -- lockout (экспоненциальный)
    c_i -- количество подтверждений

При голосовании за слот s_{n+1}:
  1. Для всех i: c_i := c_i + 1, l_i := 2^{c_i}
  2. Добавить (s_{n+1}, 2, 1) на вершину
  3. Если c_i >= 32: голос s_i финализирован (rooted)

Безопасность:
  Валидатор не может проголосовать за форк F', если
  существует голос (s, l, c) такой что s + l > current_slot
  и s не в форке F'.

  => Чем глубже голос, тем длиннее lockout,
     тем дороже обман (потеря вознаграждений за l слотов).

Три уровня подтверждения

Solana предоставляет три уровня подтверждения транзакций:

УровеньУсловиеВремяГарантияАналог в Ethereum
ProcessedЛидер обработал~400msМожет быть отмененоTx в mempool
Confirmed2/3 валидаторов проголосовали~6.4sОчень вероятно финально1 подтверждение блока
Finalized32+ confirmations (rooted)~13sНеобратимо2 эпохи (~12.8 мин)
# Выбор уровня подтверждения при запросе:
from solana.rpc.api import Client

client = Client("http://localhost:8899")

# Самый быстрый -- для UI (может быть отменено):
result = client.get_transaction(sig, commitment="processed")

# Оптимальный баланс -- для большинства dApp:
result = client.get_transaction(sig, commitment="confirmed")

# Максимальная безопасность -- для бирж, крупных переводов:
result = client.get_transaction(sig, commitment="finalized")

# Сравнение с Ethereum:
# Ethereum "finalized" = 2 эпохи = ~12.8 минут
# Solana "finalized" = 32 confirmations = ~13 секунд
# Разница: ~60x быстрее!

Когда использовать какой уровень

СценарийУровеньПочему
Отображение баланса в кошелькеconfirmedБыстрый UI, вероятность отката минимальна
Получение депозита на биржеfinalizedНужна абсолютная уверенность
Игровая транзакцияprocessedСкорость важнее, можно повторить
DeFi свапconfirmedБаланс скорости и безопасности
NFT минтconfirmedПользователь ждет подтверждения

Расписание лидеров и Gulf Stream

Расписание лидеров

В Solana один валидатор выбирается лидером для последовательности из 4 слотов (~1.6 секунды). Расписание определяется псевдослучайно, с весом пропорциональным stake.

Расписание лидеров и Gulf Stream
Epoch = 432,000 слотов (~2-3 дня). Каждый лидер получает 4 слота (~1.6s).
Лидер A
Лидер B
Лидер C
0
A
1
2
3
4
B
5
6
7
8
C
9
10
11
Gulf Stream: транзакции без mempool
Solana:
Клиент
Текущий лидер
Следующий лидер
Транзакции пересылаются заранее следующему лидеру
Ethereum (для сравнения):
Клиент
Mempool
Ожидание блока...
Транзакции ждут в mempool, пока пропозер их не включит
Gulf StreamGulf Stream устраняет mempool. Транзакции отправляются напрямую текущему и следующему лидеру, что сокращает задержку подтверждения. Лидер начинает обработку до начала своих слотов.

Алгоритмический уровень: расписание

# Упрощенная модель расписания лидеров

# Epoch = 432,000 слотов (~2-3 дня)
# Каждый лидер получает 4 последовательных слота

SLOTS_PER_EPOCH = 432_000
SLOTS_PER_LEADER = 4

# Расписание определяется в начале эпохи:
# 1. Берем stake-weight каждого валидатора
# 2. Используем seed (хеш предыдущей эпохи) для pseudo-random shuffle
# 3. Каждый валидатор получает слоты пропорционально stake

# Пример: 3 валидатора
validators = {"A": 1000, "B": 2000, "C": 3000}  # SOL staked
total_stake = sum(validators.values())

# Вероятность быть выбранным лидером:
for v, stake in validators.items():
    prob = stake / total_stake
    expected_slots = SLOTS_PER_EPOCH * prob
    expected_rotations = expected_slots / SLOTS_PER_LEADER
    print(f"  {v}: stake={stake}, P={prob:.1%}, ~{expected_rotations:.0f} ротаций за эпоху")
# A: 16.7%, ~18,000 ротаций
# B: 33.3%, ~36,000 ротаций
# C: 50.0%, ~54,000 ротаций

Gulf Stream: транзакции без mempool

Gulf Stream — одна из 8 инноваций Solana, тесно связанная с расписанием лидеров:

# Ethereum: транзакция ждет в mempool
# 1. Клиент -> mempool
# 2. Mempool хранит тысячи транзакций
# 3. Пропозер выбирает транзакции из mempool (MEV!)
# 4. Время ожидания: от 12 секунд до минут

# Solana: Gulf Stream -- нет mempool
# 1. Клиент отправляет tx текущему лидеру
# 2. Текущий лидер пересылает tx СЛЕДУЮЩЕМУ лидеру
# 3. Следующий лидер уже имеет tx до начала своих слотов
# 4. Время ожидания: практически 0 (forwarded ahead)

# Преимущества:
# - Нет mempool = нет "зависших" транзакций
# - Следующий лидер начинает обработку заранее
# - Уменьшает latency подтверждения
# - Снижает давление памяти на валидаторы

# Ограничения:
# - Если следующий лидер офлайн, tx теряется
# - Клиент должен знать расписание лидеров
# - Пересылка добавляет сетевой hop

Tower BFT vs Casper FFG (Ethereum)

АспектTower BFT (Solana)Casper FFG (Ethereum)
ТипPoH-optimized PBFTPoS FFG + LMD GHOST
Раунды голосования1 (PoH = часы)Несколько (2 эпохи)
Финализация~13 секунд~12.8 минут
Lockout механизмЭкспоненциальный (2^n)Slashing conditions
Переключение форкаОграничено lockoutОграничено slashing
Наказание нечестныхПотеря вознаграждений + lockoutSlashing (потеря stake)
Fork choiceHeaviest towerLMD GHOST (heaviest subtree)
# Стоимость атаки (упрощенно):

# Ethereum: чтобы отменить финализированный блок,
# нужно 1/3 stake (slashing condition + fork)
# При total stake = 34M ETH, это ~11.3M ETH (~$35B)

# Solana: чтобы отменить rooted блок,
# нужно ждать 2^32 слотов (~54 года)
# Экономически: потеря вознаграждений за это время
# При ~400 SOL/slot reward: потеря ~1.7 триллиона SOL (невозможно)

# Обе системы достигают практической безопасности,
# но разными механизмами:
# Ethereum: "заберем ваш deposit" (slashing)
# Solana: "вы не сможете голосовать" (lockout)

Alpenglow: планируемая замена

Alpenglow заменит PoH + Tower BFT новым протоколом с двумя компонентами:

  • Votor: Быстрое голосование за блоки (без PoH)
  • Rotor: Ротация лидеров с улучшенной устойчивостью к сбоям

Целевая финальность: 100-150ms (vs 13s у Tower BFT).

Важно для разработчиков: Alpenglow не затрагивает модель аккаунтов, программы, инструкции, PDA или Anchor. Весь код, написанный для текущей Solana, продолжит работать.

Формальная модель безопасности

Теорема (Tower BFT Safety):
  Если f < n/3 Byzantine валидаторов (из n общих),
  то два конфликтующих блока не могут оба стать rooted.

Доказательство (идея):
  1. Rooted = 32+ confirmations = lockout >= 2^32 слотов
  2. Для конфликтующего форка нужны голоса 2/3 валидаторов
  3. Но голоса заблокированы lockout -- нельзя переголосовать
  4. Если 2/3 проголосовали за блок A, и A получил 32 confirmations,
     то 1/3 из этих 2/3 должны были бы нарушить lockout для блока B
  5. Но lockout = 2^32 слотов -- нарушение невозможно в разумное время
  6. Если нарушат -- теряют все вознаграждения (экономическая безопасность)

Связь с предыдущими уроками

КонцепцияОткудаКак используется в Tower BFT
PoH (часы)SOL-02Tower BFT использует PoH для синхронизации голосований
Хеш-функцииCRYPTO-05SHA-256 в основе PoH-цепочки
Proof of StakeETH-10Tower BFT — альтернатива Casper FFG
ФинализацияETH-10Сравнение: 13s vs 12.8 мин
Byzantine Fault ToleranceETH-01Tower BFT — оптимизированный PBFT

Практика

# Упражнение: симуляция Tower BFT

# 1. Реализуйте VoteTower из псевдокода выше
# 2. Проголосуйте за 40 последовательных слотов
# 3. Наблюдайте: когда первый голос станет rooted?
# 4. Попробуйте переключить форк после 10 голосов
# 5. Сравните lockout с Ethereum slashing conditions

# Ожидаемый результат:
# - Первый голос станет rooted после 32 последующих голосов
# - Переключение форка после 10 голосов потребует ожидания
#   2^10 = 1024 слотов (~6.8 минут)

Что дальше?

В следующем уроке мы перейдем к модели аккаунтов Solana — самому важному отличию от Ethereum для разработчиков. Вы узнаете, почему программы в Solana stateless, как данные хранятся в отдельных аккаунтах, и как работают Program Derived Addresses (PDA).

Finished the lesson?

Mark it as complete to track your progress