Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 06.03 · 30 мин
Средний
Tower BFTКонсенсусLockoutГолосованияGulf StreamФинализация
Требуемые знания:

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 слотов).
Проверка знанийKnowledge check
Как работает экспоненциальный lockout в Tower BFT и почему он делает переключение форка экономически невыгодным?
ОтветAnswer
При каждом новом голосе lockout всех предыдущих голосов удваивается: l_i = 2^{c_i}. После 32 подтверждений lockout = 2^32 слотов (~54 года). Чтобы переключить форк, валидатор должен ждать истечения lockout, теряя вознаграждения за весь этот период.

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

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Пользователь ждет подтверждения
Проверка знанийKnowledge check
Какие три уровня подтверждения предоставляет Solana и какой рекомендуется для DeFi-приложений?
ОтветAnswer
Processed (~400ms, может быть отменено), Confirmed (~6.4s, 2/3 валидаторов проголосовали), Finalized (~13s, 32+ confirmations, необратимо). Для DeFi рекомендуется 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
Проверка знанийKnowledge check
Как Gulf Stream устраняет mempool и почему это ускоряет обработку транзакций?
ОтветAnswer
Gulf Stream пересылает транзакции напрямую текущему и следующему лидеру. Следующий лидер уже имеет транзакции до начала своих слотов, что устраняет задержку mempool. Нет 'зависших' транзакций, снижается давление памяти на валидаторы.

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

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 8