Web3 и блокчейн Масштабирование: L2, роллапы, шардирование, мосты и их риски
0%

Масштабирование: L2, роллапы, шардирование, мосты и их риски

Масштабирование: L2, роллапы, шардирование, мосты и их риски

В статье про Ethereum и EVM мы сформулировали неприятный факт: каждый полный узел выполняет каждую транзакцию. Это не архитектурная небрежность, а прямое следствие требования верифицируемости — узел не доверяет чужой работе, он её переделывает. Отсюда же следует, что добавление узлов не увеличивает пропускную способность ни на одну транзакцию в секунду. Сеть из десяти тысяч узлов работает со скоростью одного узла, только надёжнее.

Эта статья — про то, как из этого выбираются. Не «какой L2 лучше» (такого ответа нет и он устареет через полгода), а какие именно физические ограничения обходит каждый класс решений, чем за это платит и какие новые доверия вводит. Отдельно и подробно — про мосты, потому что это место, где Web3 теряет деньги чаще всего.

Почему блокчейн не масштабируется сам по себе

Начнём с арифметики, а не с лозунгов. Возьмём Ethereum L1: слот 12 секунд, лимит газа на блок исторически рос от 15 к 30 и далее к 36–45 млн (валидаторы голосуют за него, поэтому цифра плавает). Простой перевод ETH стоит 21 000 газа, перевод ERC-20 — примерно 45–65 тысяч (см. токены и стандарты), обмен на DEX — 120–200 тысяч.

SLOT_SEC = 12.0   # длительность слота Ethereum

def tps(block_gas_limit: int, gas_per_tx: int) -> float:
    """Сколько транзакций данного типа физически влезает в секунду."""
    return block_gas_limit / gas_per_tx / SLOT_SEC

# лимит 30 млн газа: перевод ETH 119.0 tx/s, ERC-20 45.5, своп 15.6
# лимит 45 млн газа: перевод ETH 178.6 tx/s, ERC-20 68.2, своп 23.4

Реальная средняя загрузка L1 — единицы-десятки транзакций в секунду. Для сравнения: одна нода PostgreSQL на обычном сервере делает десятки тысяч транзакций в секунду. Разница в три-четыре порядка — это и есть цена доверия без доверенной стороны.

Три разных узких места, которые постоянно путают

Слово «масштабирование» скрывает три независимые проблемы. Решения бьют по разным, и сравнивать их «в лоб» бессмысленно.

1. Исполнение (CPU). Каждый узел прогоняет каждый опкод. Увеличили лимит газа вдвое — вдвое выросло требование к процессору валидатора. Верхняя граница здесь мягкая: до неё ещё есть запас.

2. Состояние (диск и I/O). Хранилище Ethereum — дерево Меркла-Патриции (механику деревьев мы разбирали в основах блокчейна). Запись в новый слот SSTORE стоит 20 000 газа не из жадности, а потому что этот слот придётся хранить и обходить вечно, на каждом узле. Полное состояние Ethereum — сотни гигабайт, архивная нода — терабайты. Рост состояния — самое коварное ограничение: он не мешает сегодня, он мешает через пять лет, когда синхронизация нового узла станет неподъёмной.

3. Доступность данных и полоса (bandwidth). Чтобы кто-то мог проверить блок, он должен его скачать. 30 МБ блоки означают, что валидатор с домашним интернетом выпадает из сети. Именно через полосу проходит граница между «сеть проверяется энтузиастами» и «сеть проверяется тремя дата-центрами».

Ключевой вывод: роллапы решают проблему 1 и частично 2, но проблему 3 не решают — они её перекладывают, а не устраняют. Данные всё равно должны где-то лежать так, чтобы любой мог их скачать. Всё дальнейшее — про то, как это делают.

Трилемма как инженерное ограничение, а не как мантра

Формулировка Виталика Бутерина (см. vitalik.eth.limo/general/2021/04/07/sharding.html): нельзя одновременно иметь децентрализацию, безопасность и масштабируемость — из трёх выбирайте два. Это не теорема, а эмпирическое наблюдение о конкретной конструкции, и полезно понимать, почему оно держится.

Пусть каждый из $N$ узлов проверяет всю нагрузку $T$. Тогда стоимость участия $\approx T$, независимо от $N$. Хотим поднять $T$ в 100 раз — либо требуем от узла в 100 раз более мощного железа (падает $N$, растёт централизация), либо разрешаем узлу проверять только $1/100$ нагрузки (падает безопасность: за каждый кусок отвечает малая доля стейка). Обход существует ровно один: сделать проверку дешевле исполнения. Именно этим занимаются доказательства корректности, доказательства мошенничества и выборочная проверка доступности данных. Всё, что дальше в статье, — вариации на эту тему.

Эволюция подходов: почему выжили роллапы

Каналы дают бесконечную пропускную способность внутри пары участников, но требуют залога, постоянного онлайна (или watchtower) и не работают для произвольных контрактов. Ниша: массовые микроплатежи между стабильными контрагентами.

Plasma предложила дочерние цепи с корнями на L1 и «выходом» по доказательству. Она разбилась о проблему доступности данных: если оператор публикует корень, но прячет сами блоки, пользователь не может доказать своё состояние — он вынужден выходить по последнему известному, а если так делают все, начинается массовый выход и L1 захлёбывается. Урок Plasma — центральный для всей темы: корень состояния без данных бесполезен.

Роллап — это Plasma, где данные обязаны публиковаться на L1. Дороже, зато любой может воспроизвести состояние и уйти самостоятельно.

Что честно называть «L2», а что — просто другим блокчейном

Здесь много маркетинга, поэтому нужен строгий критерий. Система заслуживает названия L2, если выполнено всё:

  1. Данные, достаточные для восстановления состояния, публикуются на L1 (или в системе DA, к которой L1 может апеллировать).
  2. Корректность переходов состояния обеспечивается L1 — либо проверкой доказательства, либо возможностью доказать мошенничество.
  3. Существует аварийный выход: пользователь может вывести активы, даже если весь оператор L2 враждебен или мёртв, взаимодействуя только с контрактами на L1.

Если пункта 3 нет — это не L2. Polygon PoS, BNB Chain, Gnosis Chain — сайдчейны: у них собственный набор валидаторов и собственная безопасность, а «мост на Ethereum» охраняется подписями этих валидаторов, а не математикой. Это не приговор — сайдчейн может быть отличным инженерным выбором. Но говорить «мои деньги защищены Ethereum» в этом случае — неправда, и её стоит проговаривать вслух.

Свойство Роллап Validium Сайдчейн Канал
Данные на L1 да нет (комитет/DAC) нет нет
Кто гарантирует корректность L1 L1 (при наличии данных) свои валидаторы контрагенты
Выход без разрешения оператора да нет, если данные скрыты нет да, через L1
Худший сценарий задержка вывода заморозка средств кража большинством закрытие по старому состоянию
Стоимость транзакции средняя очень низкая очень низкая почти ноль

Устройство роллапа

Анатомия роллапа: компоненты на L1 и L2 и потоки данных между ними

Разберём роли — их обычно исполняют разные процессы, и путать их вредно.

Секвенсор принимает транзакции пользователей, назначает порядок и почти мгновенно (100–300 мс) отдаёт «мягкое подтверждение». Юридически оно не значит ничего: пока батч не на L1, секвенсор может передумать. На практике именно это ощущение мгновенности и продаёт L2.

Батчер сжимает блоки L2 и публикует их на L1 — сегодня в блобах. Это главная статья расходов роллапа.

Пропозер публикует корень состояния (outputRoot) — обязательство о том, во что превратилось состояние L2 после батча. Обычно вносит залог.

Прувер или челленджер. В ZK-роллапе прувер строит доказательство корректности. В оптимистичном роллапе никто ничего не строит, пока всё честно; при обмане любой наблюдатель запускает игру оспаривания.

Компрессия: где на самом деле деньги

До 2024 года 80–95 % себестоимости транзакции L2 составляла публикация данных на L1. Поэтому роллапы выжимают байты как встроенные системы 90-х.

Обычная транзакция Ethereum в RLP — примерно 110 байт: nonce (до 32), gasPrice, gasLimit, to (20), value (до 32), data, подпись r, s, v (65). Что делает роллап:

  • Подпись. ZK-роллап проверяет подписи внутри схемы и не публикует их вовсе — минус 65 байт. Оптимистичный вынужден публиковать (иначе нельзя переисполнить), но может агрегировать через BLS.
  • Адрес → индекс. Вместо 20 байт — 3–4 байта индекса в реестре уже виденных адресов.
  • Nonce. Выводится из состояния, публиковать не нужно.
  • Цена газа. Одна на весь батч, а не на транзакцию.
  • Значения. Суммы вида «0,1 ETH» кодируются как мантисса + экспонента: 3–4 байта вместо 32.
  • Домен-специфичное сжатие поверх — brotli в Arbitrum Nitro, zlib/brotli в OP Stack.

Итог: 12–20 байт на простую передачу вместо ~110. Публичная сводка приёмов — в документации Arbitrum (docs.arbitrum.io) и в посте Виталика про сжатие данных роллапов.

EIP-4844: блобы и отдельный рынок данных

Ключевая мысль: данные роллапа нужны временно — чтобы все успели их скачать, воспроизвести состояние и, если надо, оспорить. Хранить их в calldata вечно на каждом узле — расточительство. EIP-4844 (хардфорк Dencun, март 2024) добавил новый тип транзакции с «блобами»: данные видны сети, но не видны EVM и удаляются через ~18 суток.

Раскладка блоба EIP-4844: транзакция типа 3, сайдкар, поле элементов и обязательство KZG

Что важно инженерно:

  • Блоб — 4096 элементов поля BLS12-381 по 32 байта = 131 072 байта. Старший байт каждого элемента приходится обнулять (значение должно быть меньше модуля), поэтому полезно около 127 КБ.
  • В блок исполнения попадает только versioned hash0x01 ‖ sha256(commitment)[1:]. Контракт роллапа его записывает; содержимое блоба он не читает.
  • Обязательство KZG (48 байт) позволяет доказать значение полинома в точке за константное время. Прекомпайл 0x0A (point evaluation) проверяет такое доказательство прямо на L1 — это и есть механизм, которым спор о данных решается без загрузки 128 КБ в EVM. Про свойства хеш-обязательств см. криптографию Web3.
  • Отдельный рынок комиссий. Блоб-газ имеет свою базовую цену по правилу EIP-1559 и минимум в 1 вэй. При спросе ниже целевого цена скатывается к нулю — поэтому после Dencun комиссии L2 упали на один-два порядка. Но это же означает высокую волатильность: когда блобов не хватает, цена растёт экспоненциально и себестоимость L2 подскакивает в разы за считанные блоки.
  • Церемония доверенной настройки KZG (2023) собрала более 140 тысяч участников — схема безопасна, если хотя бы один из них уничтожил свой секрет. Это слабое, но не нулевое допущение, и о нём стоит знать.

Экономика: сколько на самом деле стоит транзакция L2

Себестоимость одной транзакции роллапа:

$$ C_{tx} = \frac{C_{data} + C_{proof} + C_{root}}{n} + C_{exec} $$

где $n$ — число транзакций в батче, $C_{data}$ — плата за блобы, $C_{proof}$ — стоимость проверки доказательства на L1 (для оптимистичного роллапа в честном случае это ноль), $C_{root}$ — публикация корня, $C_{exec}$ — исполнение на самой L2 (копейки).

BLOB_USEFUL_BYTES = 4096 * 31   # полезная ёмкость блоба: старший байт обнуляем
GAS_PER_BLOB = 2 ** 17          # 131 072 единиц блоб-газа за блоб

def per_tx_wei(tx_bytes: int, tx_count: int, blob_fee: int,
               l1_fee: int, proof_gas: int, root_gas: int = 90_000) -> float:
    """Себестоимость одной транзакции роллапа в вэй."""
    total_bytes = tx_bytes * tx_count
    blobs = -(-total_bytes // BLOB_USEFUL_BYTES)          # деление вверх
    data_cost = blobs * GAS_PER_BLOB * blob_fee           # C_data
    fixed_cost = (proof_gas + root_gas) * l1_fee          # C_proof + C_root
    return (data_cost + fixed_cost) / tx_count

gwei = 10 ** 9
for n in (100, 1_000, 5_000):
    w = per_tx_wei(tx_bytes=15, tx_count=n, blob_fee=1 * gwei,
                   l1_fee=20 * gwei, proof_gas=350_000)
    print(f"{n:5d} tx в батче -> {w / 10 ** 18:.9f} ETH за транзакцию")

Что видно из модели и что стоит запомнить:

  • Фиксированные расходы (доказательство, корень) делятся на $n$ — поэтому роллап выгоден только при потоке. Пустой L2 дороже L1 в пересчёте на транзакцию.
  • Дискретность блоба создаёт ступеньки: батч на 1 байт больше ёмкости требует второго блоба целиком.
  • Себестоимость привязана к цене блоб-газа, то есть к спросу всех роллапов сразу. Это общий ресурс, и конкуренция за него — реальный операционный риск L2.

Оптимистичные роллапы: доверяй, но имей право проверить

Модель: пропозер публикует корень и утверждает «я честно исполнил батч». Никто ничего не проверяет заранее. В течение окна оспаривания любой может заявить: «корень неверный», внести залог и запустить спор. Проиграет — потеряет залог.

Ключевое допущение: достаточно одного честного наблюдателя, у которого есть данные и который не зацензурирован на L1. Это принципиально слабее, чем «большинство честное», но принципиально сильнее, чем «доверьтесь оператору».

Игра оспаривания: бинарный поиск по трассе исполнения

Наивный подход — переисполнить весь батч на L1 — невозможен: батч по определению больше, чем влезает в блок. Решение — интерактивный протокол сведения спора к одному шагу виртуальной машины.

АЛГОРИТМ dispute(claimA, claimD, N)   # N — число шагов VM в оспариваемом сегменте
  lo := 0; hi := N
  # инвариант: стороны согласны о состоянии в lo и расходятся в hi
  пока hi - lo > 1:
      mid := (lo + hi) / 2
      # претендент обязан назвать хеш состояния VM на шаге mid
      hA := commitA(mid); hD := commitD(mid)
      если hA == hD:  lo := mid      # расхождение правее
      иначе:          hi := mid      # расхождение левее
  # остался ровно один шаг lo -> hi
  выполнить ОДИН опкод на L1 и сравнить с обязательствами
  проигравший теряет залог
def bisect(trace_a: list[str], trace_d: list[str]) -> tuple[int, int]:
    """Находит первый шаг, где трассы двух сторон расходятся, и число раундов.
    trace_x[i] — хеш состояния VM после i шагов; trace_x[0] совпадают.
    На L1 реально хранятся не трассы, а по одному хешу за раунд."""
    lo, hi = 0, len(trace_a) - 1
    assert trace_a[lo] == trace_d[lo] and trace_a[hi] != trace_d[hi]
    rounds = 0
    while hi - lo > 1:
        mid = (lo + hi) // 2
        rounds += 1
        if trace_a[mid] == trace_d[mid]:
            lo = mid
        else:
            hi = mid
    return hi, rounds   # hi — спорный шаг; далее One Step Proof на L1

Сложность. Раундов $\lceil \log_2 N \rceil$; при $N = 2^{40}$ шагов это 40 раундов. Работа L1 в каждом раунде — сравнить пару 32-байтных хешей, то есть $O(1)$ газа; память $O(1)$. Итого on-chain стоимость спора — $O(\log N)$ вместо $O(N)$ переисполнения. Это и есть «проверка дешевле исполнения» в чистом виде.

На практике вместо деления пополам используют $k$-арное деление (одним сообщением называют $k-1$ промежуточных хешей), что сокращает число раундов до $\log_k N$ — важно, потому что каждый раунд стоит календарного времени: сторонам дают часы на ход. Финальный шаг исполняет на L1 эмулятор конкретной архитектуры: MIPS в Cannon у OP Stack, WAVM (вариант WebAssembly) в Arbitrum Nitro.

Почему окно именно 7 дней

Число не взято с потолка. Окно должно превышать время, за которое честный челленджер гарантированно протолкнёт свою транзакцию на L1 даже под атакой цензуры. Если атакующий контролирует значимую долю производителей блоков и целенаправленно выкидывает транзакции челленджера, ему нужно удерживать цензуру все семь суток подряд — экономически и организационно это очень дорого (механику цензуры и форков см. в статье про консенсус). Плюс запас на баги клиентов, отпуска операторов и просто на то, чтобы люди заметили проблему.

Цена этого выбора — задержка вывода на L1 в неделю. И именно эта задержка породила отдельную индустрию рисков.

Быстрые мосты: чем вы платите за нетерпение

«Мост, выводящий с L2 за 5 минут» не ускоряет протокол. Он работает так: поставщик ликвидности отдаёт вам средства из своего запаса на L1 сейчас, а ваше право требования забирает себе и ждёт положенные семь дней. Это не мост, а дисконтированная покупка вашего требования. Ваш риск при этом почти исчезает (вы уже получили деньги), но:

  • вы платите премию за срочность;
  • вы доверяете контракту поставщика ликвидности на время атомарного обмена;
  • в момент паники (слух о баге в роллапе) ликвидность исчезает первой, а дисконт улетает вверх — ровно тогда, когда выйти хочется всем.

Роллапы с доказательством корректности (ZK)

Здесь L1 не ждёт неделю — она проверяет математику. Прувер строит SNARK/STARK-доказательство утверждения «существует последовательность транзакций, переводящая состояние из корня $S_1$ в корень $S_2$ по правилам EVM». Верификатор на L1 проверяет доказательство за фиксированный газ, не зависящий от размера батча: сотни тысяч газа для SNARK, единицы миллионов для STARK-верификатора.

Обратите внимание на асимметрию: проверка дешёвая, построение дорогое. Прувер — это кластер машин с гигабайтами RAM и GPU; его стоимость размазывается по батчу и попадает в $C_{proof}$ из формулы выше. Поэтому ZK-роллап тоже нуждается в потоке транзакций, просто по другой причине.

Типы zkEVM: чем ближе к Ethereum, тем дороже доказывать

Классификация Виталика (vitalik.eth.limo/general/2022/08/04/zkevm.html):

Тип Что означает Плата
1 Полная эквивалентность Ethereum, включая хеши и структуру состояния доказательство самое медленное и дорогое
2 Эквивалентность EVM: байткод тот же, внутренние структуры (например, дерево состояния) заменены на дружественные к схемам почти вся экосистема работает без изменений
2.5 То же, но стоимость некоторых опкодов изменена ломает часть газовых предположений контрактов
3 Почти EVM: часть прекомпайлов не поддержана или заменена часть контрактов требует правки
4 Эквивалентность на уровне языка: Solidity компилируется в свой байткод быстрее всего доказывать, но ломается всё, что зависит от адресов CREATE2, ассемблера и точного газа

Прикладной вывод для разработчика: тип zkEVM определяет, поедет ли ваш контракт «как есть». Инлайн-ассемблер, расчёты на gasleft(), детерминированные адреса из статьи про смарт-контракты — первые кандидаты на поломку в системах типа 4.

Честные риски ZK-роллапов

Не «математика доказала, значит безопасно». Доказана корректность схемы, а не системы:

  • Баг в схеме (circuit bug) — арифметизация неверно кодирует семантику EVM. Тогда доказательство «валидно» для неверного перехода. Это тихий класс уязвимостей: он не проявляется, пока его не начнут эксплуатировать.
  • Недоограниченность (underconstrained circuit) — самый частый дефект: схема допускает больше решений, чем должна. Найти можно только целевым аудитом и формальной верификацией.
  • Доверенная настройка — для SNARK семейства Groth16/PLONK нужна церемония; скомпрометированная церемония даёт возможность печатать фальшивые доказательства. STARK-системы этого не требуют, но дороже по размеру доказательства.
  • Централизованный прувер — если он один и упал, роллап встал. Это проблема живучести, а не безопасности, но пользователю от этого не легче.
  • Обновляемость контрактов на L1 — см. ниже, это самый большой практический риск и у оптимистичных, и у ZK-роллапов.

Доступность данных: сердце всей конструкции

Повторим то, чему научила Plasma: обязательство без данных бесполезно. Если корень состояния опубликован, а данные скрыты, вы не можете построить доказательство Меркла для своего вывода — значит, ваши деньги заморожены, даже если система «математически корректна».

Отсюда — практическое разделение:

  • Роллап — данные на L1 (блобы/calldata). Худший случай: неудобство и задержка.
  • Validium — данные у комитета доступности (DAC) или в отдельной DA-сети. Худший случай: средства заморожены навсегда, если комитет сговорился или просто исчез.
  • Volition — выбор на уровне транзакции: дорого и надёжно либо дёшево и с доверием комитету (подход StarkEx). Инженерно честная схема: риск виден и выбирается сознательно.

Выборочная проверка доступности данных

Как проверить, что данные опубликованы, не скачивая их целиком? Идея: закодировать блок стирающим кодом (Рид-Соломон) с двукратным расширением — тогда любых 50 % кусочков достаточно для восстановления. Чтобы спрятать хоть что-нибудь, злоумышленник обязан спрятать больше половины кусочков. Лёгкий узел запрашивает $k$ случайных кусочков; вероятность того, что все $k$ запросов случайно попали в доступную половину при сокрытии, — примерно

$$ P_{\text{провал}} \approx 2^{-k} $$

При $k = 30$ это меньше одной миллиардной. Тридцать маленьких запросов вместо мегабайтов трафика — вот как выборочная проверка обходит трилемму по третьему узкому месту. Подробности и двумерная схема — в оригинальной работе Аль-Бассама, Соннино и Бутерина «Fraud and Data Availability Proofs» (arxiv.org/abs/1809.09044) и в спецификации PeerDAS.

Тонкость, которую любят опускать: нужно ещё доказать, что кодирование выполнено правильно — иначе злоумышленник закодирует мусор, и восстановление даст не тот блок. Отсюда двумерные схемы кодирования и обязательства KZG на строки/столбцы: они делают неправильное кодирование невозможным по построению.

Тема тесно смыкается с децентрализованными хранилищами, но задача другая: DA гарантирует «данные были опубликованы и любой мог их получить в течение окна», а не «данные хранятся вечно». Это разные обещания и разные системы (Celestia, EigenDA, Avail — про первое; Filecoin и Arweave — про второе).

Шардирование: почему разделить данные проще, чем исполнение

Шардирование — классический приём из распределённых баз: разбить данные на непересекающиеся куски и обрабатывать параллельно. В блокчейне он ломается о два места.

Атомарность между шардами. Транзакция «списать с аккаунта на шарде A, начислить на шарде B» — это распределённая транзакция. Нужен двухфазный коммит либо асинхронная модель с квитанциями; и то и другое ломает привычную атомарность и добавляет задержку в несколько блоков. Для DeFi, где вся ценность в атомарной композиции нескольких контрактов в одной транзакции, это фатально: два арбитражных перевода на разных шардах перестают быть атомарными, и вся модель безопасности контракта меняется. Классику проблемы — линеаризуемость, кворумы, отказ узлов — стоит смотреть в треке про распределённые системы.

Безопасность малого шарда. Если стейк разделён на 64 шарда, для захвата одного нужно 1/64 от общего. Спасают случайное перетасовывание валидаторов между шардами и доказательства мошенничества, но сложность протокола растёт нелинейно.

Именно поэтому Ethereum в 2020 году развернулся: шардирование исполнения отложено, шардируются данные. Danksharding (и промежуточный шаг PeerDAS) делает L1 быстрым и дешёвым слоем доступности данных, а исполнение уходит роллапам, которые каждый внутри себя остаются единой атомарной средой. Композиция ломается только между роллапами — и это ровно та проблема, которую пытаются решить мосты и стандарты вроде ERC-7683 для кросс-чейн намерений.

Другие подходы, для полноты картины: Near (Nightshade) шардирует состояние с «chunk-only producers»; Polkadot даёт парачейнам общую безопасность релей-чейна и обмен сообщениями XCM; Cosmos, наоборот, делает зоны независимыми и связывает их протоколом IBC на лёгких клиентах. Здесь важно понимать разницу: общая безопасность — это заимствование гарантий (как у L2), независимая — это отдельные доверия (как у сайдчейнов).

Секвенсоры: где на самом деле живёт централизация

Правда, которую L2 не любят печатать крупным шрифтом: почти у всех сегодня один секвенсор, и он принадлежит команде проекта. Это даёт:

  • Риск живучести. Секвенсор упал — сеть встала. Такое случалось у всех крупных L2, обычно на минуты-часы.
  • Риск цензуры. Секвенсор может не включать ваши транзакции.
  • MEV. Он видит поток и определяет порядок. Arbitrum использует FCFS по времени прихода, OP Stack — приоритетную комиссию; ни то ни другое не устраняет возможности злоупотребления, а лишь меняет её форму.
  • Доход. Разница между собранными комиссиями и стоимостью публикации на L1 — выручка оператора.

Компенсация — принудительное включение. Пользователь отправляет транзакцию не секвенсору, а контракту-порталу на L1. Если секвенсор не включил её в течение окна (порядка 12–24 часов в разных реализациях), пользователь сам «проталкивает» её, и протокол обязан её принять. Это не быстро и не дёшево, но это тот самый пункт 3 в определении L2 — аварийный выход.

Направления развития, которые стоит знать по именам: based rollups (упорядочивание делает сам проповер L1 — максимальная нейтральность, худший UX по задержке), общие секвенсоры (один набор упорядочивает несколько L2, что даёт атомарность между ними), предподтверждения (валидаторы L1 берут на себя экономическое обязательство включить транзакцию, обеспеченное залогом). Живой обзор состояния — на l2beat.com.

Мосты и их риски

Дальше самая неприятная часть темы. По данным Chainalysis, в 2022 году на взломы мостов пришлось около 2 млрд USD похищенных средств — примерно две трети всего украденного в криптовалютах за год (chainalysis.com/blog/cross-chain-bridge-hacks-2022). Это не совпадение: мост концентрирует ликвидность в одном контракте и защищает её механизмом, который почти никогда не равен по прочности защищаемой сумме.

Классификация по тому, кому вы доверяете

Механика: три способа переместить актив

Lock-and-mint. Актив блокируется в контракте на цепи A, на цепи B выпускается обёртка. Ключевой момент: обёрнутый токен — это долговая расписка моста, а не актив. USDC.e на сайдчейне стоит доллар ровно до тех пор, пока мост платёжеспособен. Взлом моста мгновенно превращает обёртку в ноль, даже если исходный USDC цел.

Burn-and-mint. Эмитент признаёт токен нативным в обеих сетях: сжигаем на A, чеканим на B. Устраняет обёртки, но переносит доверие на эмитента (CCTP от Circle — пример этой модели).

Пул ликвидности. Никакой эмиссии: пользователь кладёт в пул на A, получает из пула на B. Быстро, но требует ребалансировки и упирается в глубину пула.

Разбор реальных взломов: паттерн один и тот же

Инцидент Ущерб Корневая причина
Poly Network, авг. 2021 ~611 млн USD (возвращены) Специально сконструированное кросс-чейн сообщение сменило владельца «keeper» — ошибка авторизации, а не криптографии
Wormhole, февр. 2022 ~326 млн USD На Solana не проверялся адрес системного аккаунта в устаревшей функции — подделка инструкции проверки подписей гардианов
Ronin, март 2022 ~624 млн USD Скомпрометированы 5 из 9 ключей валидаторов; часть — через оставленный открытым доступ, часть — соцынженерия
Harmony Horizon, июнь 2022 ~100 млн USD Мультисиг 2 из 5: достаточно двух украденных ключей
Nomad, авг. 2022 ~190 млн USD При обновлении нулевой корень оказался «доверенным»: любое сообщение проходило проверку, взлом копировали методом copy-paste сотни адресов
BNB Token Hub, окт. 2022 ~570 млн USD (номинально) Подделка доказательства Меркла в реализации IAVL — принят «доказанный» перевод, которого не было
Multichain, июль 2023 ~126 млн USD Ключи от мостовых контрактов фактически контролировались одним человеком

Присмотритесь к колонке причин. Ни один из этих инцидентов не был взломом консенсуса или криптографии. Это управление ключами и ошибки верификации сообщений — то есть ровно те же классы, что мы разбирали в безопасности контрактов и кошельках и ключах. Мультисиг 5 из 9 на 600 млн USD — это не «безопасность блокчейна», это девять человек и их ноутбуки.

Код: как ошибаются в кросс-чейн приёмнике

Типичная уязвимая реализация приёмника сообщений на стороне L2:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// ОПАСНО: так делать нельзя — разбор ошибок в комментариях
contract VulnerableReceiver {
    mapping(address => uint256) public credited;
    mapping(bytes32 => bool) public provenRoots;

    // 1) нет проверки, что зовёт мостовой мессенджер — вызвать может кто угодно
    // 2) нет проверки отправителя на исходной цепи
    // 3) нет nonce — одно сообщение применяется дважды
    // 4) нет chainId — сообщение переигрывается в другой сети
    function onMessage(address user, uint256 amount) external {
        credited[user] += amount;
    }

    function proveAndProcess(bytes32 root, bytes32 leaf) external view {
        // Классика Nomad: если нулевой корень помечен доверенным, проходит любое сообщение
        require(provenRoots[root], "unknown root");
    }
}

Корректный вариант для OP Stack-подобного моста:

interface ICrossDomainMessenger {
    /// Адрес отправителя на другой цепи, валиден только внутри вызова от мессенджера
    function xDomainMessageSender() external view returns (address);
}

contract SafeReceiver {
    ICrossDomainMessenger public immutable messenger;
    address public immutable trustedRemote;      // конкретный контракт на исходной цепи
    uint256 public immutable expectedSrcChain;
    mapping(bytes32 => bool) public processed;   // защита от повторного применения

    error NotMessenger();
    error UntrustedRemote();
    error AlreadyProcessed();

    constructor(address _messenger, address _remote, uint256 _srcChain) {
        messenger = ICrossDomainMessenger(_messenger);
        trustedRemote = _remote;
        expectedSrcChain = _srcChain;
    }

    /// Двойная проверка: кто зовёт локально и кто отправил на той стороне
    modifier onlyCrossDomain() {
        if (msg.sender != address(messenger)) revert NotMessenger();
        if (messenger.xDomainMessageSender() != trustedRemote) revert UntrustedRemote();
        _;
    }

    function onMessage(uint256 srcChainId, uint256 nonce, address user, uint256 amount)
        external
        onlyCrossDomain
    {
        if (srcChainId != expectedSrcChain) revert UntrustedRemote();
        // обе цепи и адрес получателя входят в идентичность сообщения,
        // иначе то же самое сообщение переиграют в соседней сети или развёртывании
        bytes32 id = keccak256(
            abi.encode(srcChainId, block.chainid, address(this), nonce, user, amount)
        );
        if (processed[id]) revert AlreadyProcessed();
        processed[id] = true;
        _credit(user, amount);
    }

    function _credit(address user, uint256 amount) internal { /* эффекты до взаимодействий */ }
}

Четыре правила, которые закрывают большинство реальных дыр:

  1. Проверяйте оба конца: локального вызывающего (мессенджер) и удалённого отправителя (xDomainMessageSender).
  2. Уникальность сообщения должна включать srcChainId, dstChainId, адрес получателя и nonce. Иначе сообщение реплеится между сетями и между развёртываниями.
  3. Никаких «доверенных по умолчанию» значений. Нулевой корень, нулевой адрес, пустая подпись — все они обязаны отвергаться явно. Nomad потерял 190 млн USD именно на этом.
  4. Помните про алиасинг адресов. В OP Stack и Arbitrum адрес контракта-отправителя с L1 на L2 приходит смещённым на 0x1111000000000000000000000000000000001111. Контракт, который сравнивает адрес «как есть», либо не работает, либо, что хуже, доверяет не тому, кому думает.

Чек-лист для оценки моста

  • Кто может обновить контракты? Мультисиг какой конфигурации, есть ли таймлок, кто держатели ключей? Это вопрос номер один, а не номер пять.
  • Какое количество ключей достаточно для кражи всей ликвидности?
  • Есть ли лимиты скорости (rate limits) на вывод? Nomad и Ronin вынесли всё за минуты — лимит на отток превратил бы катастрофу в инцидент.
  • Что происходит, если исходная цепь форкнулась или пережила глубокий реорг?
  • Публикуются ли данные, достаточные для самостоятельного вывода без участия оператора?
  • Открыт ли исходный код, есть ли аудиты, и — важнее — исправлены ли найденные в них замечания?

Как читать L2BEAT: стадии и главный практический риск

Проект L2BEAT ввёл шкалу «стадий», которая стала фактическим стандартом честной оценки:

  • Stage 0 — «тренировочные колёса»: система доказательств может отсутствовать или быть выключенной, совет безопасности способен произвольно менять состояние.
  • Stage 1 — доказательства работают, но совет безопасности может переопределить результат; требуется существенная доля независимых участников совета.
  • Stage 2 — управление системой определяется доказательствами; вмешательство возможно только при доказуемом баге, а у пользователей есть длительное окно на выход при обновлении.

Практический вывод, который нужно принять трезво: основной риск большинства L2 сегодня — не математика, а ключи от обновления контрактов. Идеальный ZK-роллап с прокси, который может мгновенно обновить мультисиг 3 из 5, защищён ровно этими тремя подписями. Механику прокси и delegatecall мы разбирали в безопасности контрактов; здесь она применяется к мосту, где лежит вся ликвидность сети.

Отсюда же — практика чтения: смотрите не на «TPS» и «комиссии», а на конфигурацию обновляемости, длительность таймлока и наличие окна выхода.

Типичные заблуждения

«L2 = дешёвый Ethereum с той же безопасностью». Безопасность исполнения — да, при работающей системе доказательств. Безопасность живучести — нет: секвенсор централизован. Безопасность обновляемости — нет: у контрактов есть админ.

«Данные можно не публиковать, если есть ZK-доказательство». Доказательство подтверждает корректность перехода, но без данных вы не знаете своего баланса и не построите доказательство вывода. Валидность и доступность — ортогональные свойства.

«Мост на 600 млн USD надёжен, раз работает третий год». Отсутствие взлома — не свидетельство прочности, а свидетельство того, что взлом ещё не произошёл. Оценивать надо худший случай, а не пройденное время.

«Больше TPS — лучше сеть». TPS без указания стоимости верификации бессмысленен: сеть с одним валидатором на мощном сервере покажет любые цифры. Правильный вопрос — «сколько стоит проверить эти TPS на обычном ноутбуке».

«Обёрнутый BTC — это BTC». Это требование к платёжеспособности эмитента или моста, номинированное в BTC. Разница проявляется ровно один раз, зато полностью.

«Разложу активы по десяти сетям — снижу риск». Каждый переход через мост добавляет доверие, а не убирает. Диверсификация по сетям часто означает мультипликацию мостовых рисков.

Мини-итог

  • Блокчейн не масштабируется репликацией: узкие места — исполнение, рост состояния и полоса для данных. Решения бьют по разным местам, сравнивать их одним числом нельзя.
  • Единственный способ обойти трилемму — сделать проверку дешевле исполнения: доказательства корректности, доказательства мошенничества, выборочная проверка доступности данных.
  • Роллап = исполнение вне L1 + обязательные данные на L1 + механизм принуждения к честности + аварийный выход. Нет аварийного выхода — это не L2.
  • Оптимистичный роллап меняет неделю ожидания на дешевизну и простоту; ZK-роллап меняет дорогое доказательство на быструю финальность. Обе модели одинаково зависят от доступности данных.
  • Шардирование данных (danksharding, DAS) оказалось реализуемым; шардирование исполнения ломает атомарную композицию контрактов, поэтому Ethereum отдал исполнение роллапам.
  • Мосты — самая слабая часть стека: почти все крупные потери случились из-за управления ключами и ошибок верификации сообщений, а не из-за криптографии. Обёрнутый актив — обязательство моста.
  • Главный практический риск L2 сегодня — админские ключи и обновляемость контрактов, а не корректность доказательств.

Источники

Что дальше

Мы разобрали, как вычисления и данные распределяются между слоями. Осталось собрать из этого работающее приложение: как фронтенд общается с цепью, зачем нужны индексаторы, что происходит на уровне RPC и почему офчейн-компоненты неизбежны.

Архитектура dApp: фронтенд, индексация, ноды, RPC, офчейн-компоненты

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов