zkSync глубокое погружение
Привет всем! В данной статье я попытаюсь рассказать, как работает zkSync под капотом, как формируются блоки и транзакции. Все это будет представлено простым языком для понимания широкой аудитории.
Доказательства нулевого разглашения (ZKPs):
- Определение: Это криптографические протоколы, которые позволяют доказать, что утверждение верно, не раскрывая информацию о самом утверждении.
- Применение: ZKP используются для повышения приватности, например в блокчейне, где они подтверждают действительность утверждения, не раскрывая основных данных.
- Определение: Это масштабируемое решение для блокчейнов, которое повышает пропускную способность и снижает затраты на транзакции, перемещая обработку большей части транзакций вне цепи блоков. Они используют доказательства нулевого разглашения для обеспечения криптографических гарантий о действительности транзакций, не раскрывая деталей транзакций.
- Принцип работы: В ZK Rollups большинство транзакций происходит вне цепи блоков, и лишь краткая информация периодически отправляется на основной блокчейн. Затем доказательства нулевого разглашения используются для подтверждения действительности вычислений вне цепи.
ZK-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge):
- Определение: Это конкретный тип систем доказательств нулевого разглашения, обеспечивающий лаконичные и неинтерактивные доказательства знания.
- Применение: ZK-SNARKs известны в блокчейн-приложениях, таких как Zcash, где они позволяют проверять транзакции без раскрытия отправителя, получателя или суммы транзакции.
- Принцип работы: ZK-SNARKs используют математические конструкции, такие как эллиптические кривые и алгебраическая геометрия, для обеспечения эффективных и безопасных доказательств нулевого разглашения. Их краткость особенно ценна в блокчейне, где эффективность критически важна.
Подробнее про данные концепции можно узнать здесь.
Итак, про технологии я кратко рассказал, давайте уже ближе к теме:
Основные компоненты zkSync:
- Смарт-контракт L1 и L2: Обеспечивают взаимодействие между L1 (основной блокчейн) и L2 (сайдчейн zkSync).
- Мостовые контракты: Организуют перемещение активов между L1 и L2.
- Контракт объединения zkSync на Ethereum: Объединяет L2-активы в контракты на L1.
- Контракт верификатора доказательства L1: Проверяет доказательства от серверной части перед добавлением блоков в L1.
Серверная часть (Узел сети zkSync):
- Мониторинг смарт-контракта L1: Отслеживает депозиты или приоритетные операции.
- Поддержание мемпула: Принимает транзакции (Postgres).
- Извлечение транзакций из мемпула: Выполняет транзакции на виртуальной машине и изменяет состояние.
- Генерация блоков цепочки zkSync: Подготавливает схемы для блоков, подлежащих проверке.
- Отправка блоков и доказательств в смарт-контракт L1: Публикация API web3, совместимого с Ethereum.
- Виртуальная машина: Выполняет смарт-контракты способом, совместимым с вычислениями с нулевым разглашением данных.
- Приложение для проверки: Принимает блоки и метаданные, создает для них доказательство достоверности zk.
- Доступ к хранилищу ВМ для State Keeper: Компонент, отвечающий за обработку выполнения транзакций и создание миниблоков и пакетов L1.
Данный список отображает главные механизмы работы, но я не буду останавливаться на каждом компоненте подробно, только на неочевидных моментах.
Генерация блоков цепочки zkSync:
Блоки L2 в настоящее время не играют основной роли в системе. Это функция совместимости для поддержки инструментов, таких как Metamask, ожидающих частого изменения блока. Это позволяет инструментам предоставлять пользователям обратную связь, подтверждая, что их транзакция была добавлена.
Грубо говоря отправив транзакцию, вы моментально увидите ее выполнение, но она не будет еще добавлена в основную цепочку и сохранена на L1.
Основные шаги для завершения транзакции:
- Выполнить транзакцию в блоке State Keeper & Seal
- Создать свидетеля
- Создайте доказательство
- Проверка доказательства на L1
Выполнить транзакцию в блоке State Keeper & Seal
Задача State Keeper — брать транзакции из мемпула и помещать их в пакет L1. Это делается с помощью process_l1_batch() метода (метод написан на языке Rust)
Этот метод берет следующую транзакцию из мемпула (которая может быть либо L1Tx, либо L2Tx, но L1Tx всегда имеет приоритет и они выполняются первыми), выполняет ее и проверяет, готов ли пакет L1 к запечатыванию
L1Tx - это в основном депозиты или выводы средств из системы.
На данной картине очевидно показано как работает запечатывание пакетов L1.
У нас есть транзакции L1tx и L2tx, транзакции L1tx разумеется идут в приоритете, и все эти транзакции собираются в миниблоки L2, эти миниблоки в свою очередь собираются в пакет L1, который как мы знаем State Keeper готовит к запечатыванию.
У пакетов L1 (State Keeper) есть ограничение в 750 транзакций и еще ряд ограничений связанных с газлимитом и публикационными данными в 120кб
После того как State Keeper собрал пакет L1, нужно создать свидетеля
Так как State Keeper выполняет операции, но не заявляет об их выполнении открыто, нам нужно создать доказательство zk или перейти на новый этап выполнения транзакции
Создать свидетеля.
Если говорить простым языком, то нам нужно доказать заранее, что операция, которую мы выполнили, возможна. Алгоритм берет одну транзакцию и проверяет ее.
В обычном случае мы бы просто присвоили переменную считав данные из хранилища.
Но в нашем случае мы должны проверить пути меркла и создать набор хешей, чтоб показать что эта операция возможна, и что мы не обманываем.
Еще более простым языком мы должны найти текущий корень дерева меркла и взять его хеш, после чего взять наше число 44 и доказать ее существование в хранилище, то есть проверить, что хеш дерева меркла не поменялся. Дальше мы должны создать новый хеш, который показывает что мы присвоили a=44 и таким образом докажем, что 44 существует в хранилище и создаем новый хеш в дереве меркла
Если вы не знакомы с деревьями и деревом меркла, то этот этап будет трудно понять, поэтому советую ознакомиться.
Благодаря такой системе мы создаем свидетеля, который говорит нам, что да, транзакции возможны, и мы ему верим.
Следующий этап, это генерация доказательства
Создайте доказательство
Тут мы берем наши данные свидетеля из прошлого пункта и разбиваем его на более мелкие части, создаем из них полиномы (математическое выражение), после чего в силу вступают zkSNARK. Чтоб сгенерировать само доказательство мы вычисляем это выражение в разных точках, и для этого требуется очень большие вычислительные мощности.
Вот наглядный пример как происходит вычисление доказательства вида ZK
- C : это ключи проверки
- In : представляет корневой хеш перед блоком транзакции.
- Out : представляет корневой хеш после блока транзакции.
- P : Это доказательство, предоставленное доказывающим.
Логика этого заключается в том, что совпадающее доказательство «P» может существовать только в том случае, если C(In) == Out
.
Мы просто говорим, что наше вычисленное доказательство P имеет смысл, если наши хеши In и Out согласованы при помощи ключа C.
Главная задача этого пункта - сохранять синхронизацию L1 и L2
Все данные в технологии ZK Rollups хранятся как calldata и zksync не исключение
Смарт-контракты:
Рассмотрим различие ВМ эфира и zksync
- Написание Кода в Solidity: Начинаем с написания смарт-контракта на языке Solidity.
- Компиляция с Solc: Используем компилятор solc для преобразования кода в байт-код EVM, байт-код развертывания и ABI.
- Развертывание на Ethereum: Отправляем байт-код развертывания на адрес 0x000 в сеть Ethereum, где происходит выполнение байт-кода развертывания и размещение контракта по уникальному адресу.
- Взаимодействие с Контрактом: Теперь можем отправлять транзакции на адрес контракта, используя ABI для правильной передачи аргументов функций.
- Выполнение на EVM: Весь байт-код выполняется на Ethereum Virtual Machine (EVM), имеющей стек, память и хранилище.
- Система Проверки (zkEVM): Основная часть zkSync - это система проверки. Используется виртуальная машина zkEVM, имеющая другой набор кодов операций и регистров.
- Компиляция с zk-solc: Используем отдельный компилятор zk-solc для создания байт-кода, совместимого с zkEVM.
- Развертывание в zkSync: Отправляем скомпилированный код в zkSync, где его выполнение происходит на zkEVM. Новые контракты могут быть развернуты в системе контрактов zkSync.
- Преимущества отдельного компилятора: Использование отдельного компилятора позволяет более быстрые и дешевые модификации, а также увеличивает гибкость системы.
Таким образом, в случае zkSync, используется отдельная виртуальная машина (zkEVM) и компилятор (zk-solc) для обеспечения возможности эффективного проведения доказательств в системе проверки.
Сжатие байт кода смарт-контрактов
Поскольку zkSync являемся накопителем — все байт-коды, которые контракты используют в нашей цепочке, должны быть скопированы в L1 (чтобы при необходимости цепочку можно было реконструировать из L1).
Учитывая необходимость сократить используемое пространство, байт-код сжимается перед отправкой в L1.
На высоком уровне байт-код разбивается на коды операций (размером 8 байт), присваивается двухбайтовый индекс, а вновь сформированная последовательность байтов (индексы) проверяется и отправляется на L1
000000000000000A
000000000000000D
000000000000000A
000000000000000C
000000000000000B
000000000000000B
000000000000000D
000000000000000A
0 -> 0xA (count: 3)
1 -> 0xD (count: 2, first seen: 1)
2 -> 0xB (count: 2, first seen: 4)
3 -> 0xC (count: 1)
0008 0000
000000000000000A
000000000000000D
000000000000000B
000000000000000C
0000 0001 0000 0003 0002 0002 0001 0000
Дальше данный байт-код проходит проверку и публикуется на L1. Для этого есть отдельный смарт-контракт.
На самом деле данные или транзакции, которые мы просто отправляем, тоже сжимаются своими алгоритмами.
Также есть таблица, где показаны основные коды операций
Как вы могли заметить я часто упоминал серверную часть, это нам говорит, что zkSync на данный момент не полностью децентрализованный протокол, и поэтому может прослеживаться цензура, но как нам заверяют разработчики, это не произойдет и они стремятся перейти к полностью децентрализованной системе. Будем ждать.
Как происходят обращение к смарт-контрактам L1:
ZkSync используем настройку DiamondProxy, которая позволяет иметь фиксированную неизменяемую точку входа (DiamondProxy), которая перенаправляет запросы к различным контрактам (кранам), которые могут быть независимо обновлены и/или заморожены.
Diamond
Технически этот смарт-контракт L1 действует как соединитель между Ethereum (L1) и zkSync (L2). Этот контракт проверяет подтверждение действительности и доступность данных, обрабатывает связь L2 <-> L1, завершает переход состояния L2 и многое другое.
На уровне L2 также развернуты важные контракты, которые также могут выполнять логику, называемую системными контрактами . Использование связи L2 <-> L1 может повлиять как на L1, так и на L2
На самом деле в zkSync есть кучу разных контрактов и каждый выполняет свою роль, но если я начну о всех них рассказывать, то на это уйдет слишком много времени, поэтому оставлю данную тему на другой раз.
На этом все. Я постарался донести вам достаточно трудную информацию, которую сложно осознать с первого раза, так что если вы что то поняли, уже хорошо. Теперь вы знаете как на самом деле происходят транзакции и формирования блоков в zkSync это далеко не все решения, которые есть в zkSync, но для понимания я думаю достаточно. На самом деле очень сильный проект, который подает большие надежды.