Часть II · Глава 5 из 43

Как узел синхронизируется

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

Обновлено 13 сент. 2026 г. · 14 мин
Предполагается
  • как Ethereum хранит состояние
  • финальность

Вы устанавливаете новый узел и нажимаете «Запустить». Сеть работает уже годы; там сотни миллионов аккаунтов и цепочка блоков, уходящая назад до genesis. Ваш узел не знает ничего из этого. Прежде чем он сможет валидировать хотя бы один новый блок или ответить на вопрос «какой баланс у Алисы?», он должен наверстать упущенное — достичь точно того же текущего состояния, что и все остальные, и убедиться самостоятельно, что это состояние верно.

Это можно сделать не одним способом. Их пять, и они не произвольны: каждый — исправление конкретного недостатка предыдущего. Начнём с самого медленного и параноидального метода из возможных, а затем по делу заработаем каждое сокращение. (Это напрямую опирается на trie, которое хранит состояние, и финальность — если они туманны, сначала пробегитесь по ним.)

1 шаг Что должна делать синхронизация

Две вещи и противоречие между ними

«Быть синхронизированным» означает, что ваш узел хранит две вещи: текущее состояние — каждый аккаунт и контракт, то самое, что зафиксировано в stateRoot 32-байтовый корень дерева Меркла-Патриции, который является отпечатком всего мирового состояния. Он есть в каждом заголовке блока; два узла с одинаковым stateRoot хранят идентичное состояние. последнего блока — и достаточно цепочки, чтобы продолжать валидировать новые блоки по мере их поступления.

Вся тема — это одно противоречие. Можно пересчитать всё самостоятельно с самого начала и никому не доверять — медленно, но пуленепробиваемо. Или можно скачать готовое состояние от пира и доверять его правильности — быстро, но легковерно. Каждый режим синхронизации — это разная точка на шкале между доверием и скоростью (и третья ось: сколько диска вы готовы сжечь). Посмотрите, где оказывается каждый.

→ Шаг 2: никому не доверяем. Повторяем всю историю.

2 шаг Полная синхронизация

Повторяем каждый блок начиная с genesis

Дизайн с нулевым доверием: скачиваем каждый блок от genesis до головы и повторно выполняем каждую транзакцию по порядку, применяя каждую к нашей локальной копии состояния — точно так же, как это делали исходные валидаторы. После последнего блока вычисленный нами stateRoot совпадёт с stateRoot блока на голове, и мы будем знать это, потому что прошли каждый шаг сами. Это полная синхронизация (full sync) — точка отсчёта, с которой сравнивают все остальные методы.

генезис голова #1 #2 #3 #4 #5 #6 #7 #8 переисполнение восстановлено 46% переисполняем каждую транзакцию, блок за блоком верно, но медленно: дни работы CPU
Полная синхронизация повторно выполняет каждую транзакцию начиная с genesis, восстанавливая состояние с нуля. Максимально без доверия — и жестоко медленно.

Ничто не заслуживает большего доверия: вы лично верифицировали каждый переход состояния в истории Ethereum. (Если сохранить все промежуточные состояния, получим архивный узел — то, что запускают блок-эксплореры: несколько терабайт на современной path-based раскладке, до ~15–20 ТБ на старой hash-based.)

→ Шаг 3: прекращаем повторную обработку. Просто скачиваем состояние.

3 шаг Быстрая синхронизация

Скачиваем trie вместо того, чтобы его восстанавливать

Вот прыжок. Состояние — это дерево Меркла (Merkle trie), а дерево Меркла самоверифицируется: каждый узел ссылается на дочерние по их хешу, поэтому если скачанный узел хешируется в значение, которое ожидал родитель, — он не может быть поддельным. Выбираем недавний блок pivot, временно принимаем его stateRoot на веру и скачиваем лежащее под ним дерево напрямую от пиров — сначала корень, потом каждый дочерний по его хешу, проверяя каждый узел по мере поступления. Никакого повторного выполнения. Это была быстрая синхронизация (fast sync).

корень✓ узел✓ узел✓ узел✓ листлистлистлистлист качаем trie узел за узлом, каждый сверяем по хешу миллионы мелких запросов; pivot всё время движется
Быстрая синхронизация скачивает trie состояния узел за узлом, проверяя каждый по хешу родителя — без повтора транзакций. Но в trie сотни миллионов разбросанных узлов.

Вы верифицируете только голову, следя за заголовками блоков и консенсусом, а математика Меркла подтверждает скачанное состояние. Дни сжимаются до часов.

→ Шаг 4: забываем о форме дерева. Скачиваем сырые данные оптом.

4 шаг Snap sync

Скачиваем плоские диапазоны, затем лечим

trie — это просто индекс над простыми парами key → value. Так что прекращаем запрашивать его узел за узлом. Вместо этого запрашиваем у пиров непрерывные диапазоны плоских данных аккаунтов — «все аккаунты от 0x00… до 0x20…» — отдаваемые прямо из плоского снимка (snapshot) большими последовательными кусками, каждый с доказательством диапазона (небольшое доказательство Меркла, подтверждающее, что кусок корректен и полон относительно stateRoot). Скачиваем всё состояние несколькими толстыми запросами диапазонов, локально восстанавливаем trie из плоских данных, а затем проходим один финальный heal-проход, чтобы залатать немногие узлы, сместившиеся во время скачивания. Это snap sync — сегодняшний стандарт в geth и reth.

плоское состояние, диапазоны 0x00…0f ✓ 0x10…1f ✓ 0x20…2f ✓ 0x30…3f ✓ перестройка перестроено локально корень a…f g…z залечено ✓ несколько больших диапазонов, а не миллион узлов: часы, не дни
Snap sync забирает состояние большими плоскими диапазонами (каждый с доказательством), локально восстанавливает trie и залечивает части, которые сдвинулись. Несколько крупных запросов вместо миллионов мелких.

Та же модель доверия, что у быстрой синхронизации — верифицируем голову, доверяем доказательствам для состояния — но полоса пропускания используется эффективно. Полный узел mainnet синхронизируется за несколько часов.

→ Шаг 5: вообще не храним состояние.

5 шаг Лёгкие клиенты

Храним только заголовки; запрашиваем остальное с доказательством

Отбрасываем состояние полностью. Заголовок блока крошечный — несколько сотен байт — и он несёт stateRoot. Храним только цепочку заголовков. Когда нам что-то нужно — баланс Алисы, слот хранилища — запрашиваем у полного узла вместе с доказательством Меркла: короткую цепочку хешей соседей от листа до stateRoot, который у нас уже есть. Проверяем доказательство сами по доверенному корню. Если сходится — ответ подлинный; полный узел не может солгать, не сломав хеш. Это лёгкий клиент (light client).

root #0root #1root #2root #3root #4root #5 баланс 0xA7? 3a… b1… acct ✓ 12 Ξ хранит только заголовки; спрашивает узел и проверяет Merkle-доказательство проверяет состояние сам, не храня его
Лёгкий клиент хранит только заголовки. Он запрашивает каждый фрагмент состояния по требованию с доказательством Меркла и проверяет его по stateRoot заголовка — доверенные ответы при почти нулевом хранилище.

Вы храните килобайты вместо сотен гигабайт, и каждый ответ по-прежнему криптографически верифицирован — вы обменяли хранение состояния на доказывание каждого фрагмента по требованию.

→ Шаг 6: начинаем с недавней точки, которой можно доверять.

6 шаг Checkpoint sync

Доверяем одному недавнему финализированному блоку и пропускаем остальное

Все методы выше тихо идут вперёд от genesis, доверяя самой тяжёлой цепочке. Но у Proof of Stake есть тонкость: узел, синхронизирующийся от genesis, может быть обманут давно мёртвыми валидаторами, подписывающими альтернативную древнюю историю (атака дальнего диапазона). Исправление одновременно является самым большим ускорением во всей теме. Не начинайте с genesis — начните с недавней финализированной контрольной точки (checkpoint): корня блока возрастом в несколько недель, взятого из доверенного источника (ваш клиент поставляется со списком; вы можете перепроверить по блок-эксплореру или у друга). Поскольку она финализирована, её откат сжёг бы треть всего застейканного ETH — так что это надёжный якорь. Отсюда выполняем snap sync состояния и идём вперёд. Это checkpoint sync (он же weak-subjectivity sync) — именно так реальные узлы и запускаются.

gen голова переигрывает всю историю с генезиса: недели вместо этого: контр. точкафинализир. голова доверенный свежий якорь → snap вперёд → минуты
Вместо повтора истории от genesis, checkpoint sync доверяет одному недавнему финализированному корню и делает snap вперёд оттуда — недели работы превращаются в минуты.

Это единственный режим, меняющий точку начала, а не метод загрузки, и он сочетается с остальными: современные клиенты объединяют слой консенсуса, синхронизированный через checkpoint, со слоем выполнения, синхронизированным через snap, — и узел, бывший офлайн месяцами, может снова выйти в сеть за минуты.

03 Спека и код Вся лестница с первого взгляда

Что вы обменяли — явно

МетодМодель доверияВремя синхронизацииДиск
Полная / архивнаяповторяет всю историю — не доверяет ничемудни–неделисотни ГБ – ТБ
Быстрая (устаревшая)верифицирует голову, доверяет Меркл для состояниячасы+~размер состояния
Snap (стандарт)верифицирует голову, доверяет доказательствам диапазоновнесколько часов~размер состояния
Лёгкий клиентцепочка заголовков + доказательство на каждый запроссекундыКБ–МБ
Checkpointдоверяет одному недавнему финализированному корнюминутыв паре со snap

Столбец, который важен — модель доверия: всё справа — это лишь цена, которую вы платите, а всё слева — то, что вы готовы принять на веру. Snap-от-checkpoint победил, потому что он жертвует почти ничем реальным — финализированный корень настолько же надёжен, насколько вообще что-то может быть надёжным — ради почти всей скорости.

04 Глубже Куда двигаться дальше