Требуемые знания:
- 05-solana/01-solana-architecture
Proof of History
Зачем это блокчейну?
Главная проблема распределенных систем — время. Когда два события происходят на разных узлах, как определить, какое было раньше? В Ethereum валидаторы обмениваются сообщениями для согласования порядка — это требует нескольких раундов коммуникации и стоит времени.
Solana решает эту проблему иначе: вместо того чтобы спрашивать остальных, который сейчас час, она создает криптографические часы — верифицируемое доказательство того, что время прошло.
Ключевое заблуждение: PoH — это НЕ механизм консенсуса. Это часы. Консенсус в Solana обеспечивает Tower BFT (следующий урок). PoH лишь дает валидаторам общую шкалу времени.
# Проблема: как доказать, что событие A произошло ДО события B?
# Ethereum:
# 1. Валидатор A: "Я видел tx_A в 14:00:01"
# 2. Валидатор B: "Я видел tx_B в 14:00:02"
# 3. Проблема: кто врет? Нужен консенсус о времени -> несколько раундов сообщений
# Solana (PoH):
# 1. hash_1000 = SHA-256(hash_999)
# 2. hash_1001 = SHA-256(hash_1000 || tx_A) <- tx_A вмешана в хеш
# 3. hash_1002 = SHA-256(hash_1001)
# 4. hash_1003 = SHA-256(hash_1002 || tx_B) <- tx_B вмешана позже
# Доказательство: tx_A определенно РАНЬШЕ tx_B (1001 < 1003)
# Никаких переговоров не нужно -- порядок криптографически доказан!
Интуитивное объяснение: аналогия с газетой
Представьте, что вам нужно доказать, что документ существовал до определенной даты. Классический трюк: сфотографируйтесь с сегодняшней газетой. Дата на газете доказывает, что фото не могло быть сделано раньше.
PoH работает так же, но вместо газеты — цепочка хешей:
- Каждый хеш = “газета” с уникальной датой
- Вычислить следующий хеш можно только имея предыдущий
- Событие, вмешанное в хеш N, доказуемо произошло после N-1 и до N+1
Цепочка хешей: пошагово
PoH — это последовательная цепочка SHA-256 хешей. Каждый хеш вычисляется из предыдущего, создавая верифицируемую шкалу времени.
Алгоритмический уровень
import hashlib
def sha256(data: bytes) -> bytes:
return hashlib.sha256(data).digest()
class ProofOfHistory:
def __init__(self, seed: bytes):
self.current_hash = sha256(seed)
self.counter = 0
self.history = [(0, self.current_hash.hex(), None)]
def tick(self):
"""Один тик часов -- чистый хеш без события"""
self.current_hash = sha256(self.current_hash)
self.counter += 1
self.history.append((self.counter, self.current_hash.hex(), None))
def record_event(self, event_data: bytes):
"""Записать событие в цепочку -- вмешать в хеш"""
combined = self.current_hash + event_data
self.current_hash = sha256(combined)
self.counter += 1
self.history.append((self.counter, self.current_hash.hex(), event_data))
def verify(self, start_hash: bytes, entries: list) -> bool:
"""Верификация: воспроизвести цепочку и проверить хеши"""
h = start_hash
for entry in entries:
if entry.event_data:
h = sha256(h + entry.event_data)
else:
h = sha256(h)
if h != entry.hash:
return False
return True
# Демонстрация:
poh = ProofOfHistory(b"solana-genesis")
poh.tick() # H(1) = SHA-256(H(0))
poh.tick() # H(2) = SHA-256(H(1))
poh.record_event(b"Transfer 5 SOL") # H(3) = SHA-256(H(2) || event)
poh.tick() # H(4) = SHA-256(H(3))
poh.record_event(b"Create Account") # H(5) = SHA-256(H(4) || event)
for counter, hash_hex, event in poh.history:
event_str = event.decode() if event else "-"
print(f" H({counter}): {hash_hex[:16]}... event={event_str}")
Формальное определение
PoH — это верифицируемая функция задержки (VDF) на основе SHA-256:
Определение: Последовательность хешей (h_0, h_1, ..., h_N) где:
h_0 = SHA-256(seed)
h_i = SHA-256(h_{i-1}) -- тик (чистый хеш)
h_i = SHA-256(h_{i-1} || event_data) -- запись события
Свойства:
1. Последовательность: h_i вычислим ТОЛЬКО из h_{i-1}
(нельзя вычислить h_N, не вычислив все предыдущие)
2. Верифицируемость: любой может проверить цепочку,
повторив вычисления (и это можно делать параллельно!)
3. Временная метка: позиция события в цепочке однозначно
определяет его порядок относительно других событий
Генерация vs верификация: ключевая асимметрия
Самое важное свойство PoH — это асимметрия между генерацией и верификацией:
- Генерация: последовательная, один CPU, O(N) времени
- Верификация: параллельная, K ядер, O(N/K) времени
Почему верификация параллельна?
# Генерация (последовательная -- нельзя распараллелить):
h[0] = SHA-256(seed)
h[1] = SHA-256(h[0]) # нужен h[0]
h[2] = SHA-256(h[1]) # нужен h[1]
h[3] = SHA-256(h[2]) # нужен h[2]
...
# Каждый шаг зависит от предыдущего!
# Верификация (параллельная):
# У верификатора уже есть ВСЯ цепочка: h[0], h[1], ..., h[N]
# Он знает входы и выходы каждого шага
# Core 1 проверяет: SHA-256(h[0]) == h[1]? SHA-256(h[1]) == h[2]? ...
# Core 2 проверяет: SHA-256(h[100]) == h[101]? SHA-256(h[101]) == h[102]? ...
# Core 3 проверяет: SHA-256(h[200]) == h[201]? ...
# Core 4 проверяет: SHA-256(h[300]) == h[301]? ...
# 4 ядра => 4x быстрее! GPU с 4000 ядрами => ...
# Аналогия: написать книгу нужно последовательно (слово за словом),
# но проверить орфографию могут 10 корректоров параллельно,
# каждый проверяет свою главу
Математический уровень
Теорема (асимметрия PoH):
Пусть f = SHA-256 (прообраз-стойкая хеш-функция)
Генерация цепочки длины N: T_gen = N * t_hash (последовательно)
Верификация цепочки длины N на K ядрах: T_ver = (N/K) * t_hash
Ускорение: T_gen / T_ver = K
Это работает потому, что:
- Генерация: h_i зависит от h_{i-1} (цепная зависимость)
- Верификация: проверка h_i = f(h_{i-1}) не зависит от
проверки h_j = f(h_{j-1}) для j != i (при известных h)
PoH в контексте Solana
Как лидер использует PoH
В каждый момент один валидатор является лидером (мы разберем расписание лидеров в SOL-03). Лидер непрерывно генерирует PoH-хеши:
# Упрощенный цикл лидера:
while is_leader(current_slot):
# 1. Генерировать тики (доказательство времени)
poh.tick()
# 2. Если есть транзакции -- вмешать их в цепочку
if pending_transactions:
tx = pending_transactions.pop()
poh.record_event(tx.serialize())
# Теперь tx имеет криптографическую временную метку
# 3. После набора тиков -- сформировать entry
if poh.counter % ticks_per_entry == 0:
entry = create_entry(poh.current_hash, poh.counter, transactions)
broadcast_via_turbine(entry)
# Скорость: ~400,000 хешей/секунду на одном ядре
# Это дает ~400,000 "тиков" часов в секунду
PoH vs другие подходы к времени
| Подход | Как работает | Преимущества | Недостатки |
|---|---|---|---|
| NTP (Network Time Protocol) | Синхронизация часов через сеть | Простота | Не криптографический, можно обмануть |
| Ethereum (slot time) | Фиксированные 12-секундные слоты | Предсказуемость | Нет доказательства между слотами |
| Bitcoin (timestamps) | Таймстемп в заголовке блока | Простота | Неточный (допустимое отклонение ~2 часа) |
| PoH (Solana) | Криптографическая цепочка хешей | Верифицируемый, точный, параллельная верификация | Один ядро на генерацию, CPU-интенсивный |
Ограничения PoH
PoH — мощный инструмент, но у него есть ограничения:
- Не заменяет консенсус. PoH доказывает порядок, но не то, что порядок “правильный”. Для этого нужен Tower BFT.
- Зависит от скорости CPU. Если у атакующего CPU в 2x быстрее, он может генерировать PoH-цепочку в 2x быстрее. Однако SHA-256 — это хорошо изученная функция, и разница в скорости между процессорами невелика.
- Один поток. PoH генерируется на одном ядре CPU. Это создает потенциальное узкое место, хотя на практике скорость SHA-256 достаточна.
Alpenglow: что придет после PoH?
Alpenglow — планируемый протокол, который заменит PoH + Tower BFT новым механизмом с целевой финальностью ~100-150ms. Важно: программы, аккаунты и Anchor-разработка не затрагиваются — меняется только консенсусный слой.
Связь с криптографией
| Криптографическая основа | Применение в PoH | Уроки |
|---|---|---|
| SHA-256 | Основа цепочки хешей | CRYPTO-05, CRYPTO-06 |
| Прообраз-стойкость | Нельзя вычислить h_{i-1} из h_i — гарантия последовательности | CRYPTO-05 |
| VDF (Verifiable Delay Function) | PoH — это VDF с параллельной верификацией | — |
# Связь PoH с хеш-функциями из модуля 1:
# PoH эксплуатирует ТРИ свойства SHA-256:
# 1. Прообраз-стойкость: нельзя найти x из H(x)
# => нельзя "перескочить" шаги в цепочке
# 2. Лавинный эффект: малое изменение входа -> кардинальное изменение выхода
# => вмешанное событие полностью меняет все последующие хеши
# 3. Детерминизм: один вход -> один выход
# => верификация дает тот же результат, что и генерация
Практика
# Упражнение: реализуйте мини-PoH и измерьте производительность
import hashlib
import time
def mini_poh(seed: bytes, n_hashes: int) -> list:
"""Генерация мини-PoH цепочки"""
chain = [hashlib.sha256(seed).digest()]
for _ in range(n_hashes):
chain.append(hashlib.sha256(chain[-1]).digest())
return chain
def verify_poh(chain: list) -> bool:
"""Верификация PoH цепочки"""
for i in range(1, len(chain)):
if hashlib.sha256(chain[i-1]).digest() != chain[i]:
return False
return True
# Замер:
start = time.time()
chain = mini_poh(b"test-seed", 100_000)
gen_time = time.time() - start
start = time.time()
assert verify_poh(chain)
ver_time = time.time() - start
print(f"Generation: {gen_time:.3f}s (100K hashes)")
print(f"Verification: {ver_time:.3f}s (sequential)")
print(f"With 4 cores: ~{ver_time/4:.3f}s (parallel)")
Что дальше?
В следующем уроке мы разберем Tower BFT — механизм консенсуса Solana, который использует PoH как часы. Вы увидите, как голоса валидаторов формируют “башню” с экспоненциально растущим lockout, и почему это обеспечивает быструю финализацию.
Закончили урок?
Отметьте его как пройденный, чтобы отслеживать свой прогресс