zkSync
November 25, 2023

zkSync глубокое погружение 

Привет всем! В данной статье я попытаюсь рассказать, как работает zkSync под капотом, как формируются блоки и транзакции. Все это будет представлено простым языком для понимания широкой аудитории.

На чем основан zkSync:

Доказательства нулевого разглашения (ZKPs):

  • Определение: Это криптографические протоколы, которые позволяют доказать, что утверждение верно, не раскрывая информацию о самом утверждении.
  • Применение: ZKP используются для повышения приватности, например в блокчейне, где они подтверждают действительность утверждения, не раскрывая основных данных.

ZK Rollups:

  • Определение: Это масштабируемое решение для блокчейнов, которое повышает пропускную способность и снижает затраты на транзакции, перемещая обработку большей части транзакций вне цепи блоков. Они используют доказательства нулевого разглашения для обеспечения криптографических гарантий о действительности транзакций, не раскрывая деталей транзакций.
  • Принцип работы: В 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.

ZkEvm:

  • Виртуальная машина: Выполняет смарт-контракты способом, совместимым с вычислениями с нулевым разглашением данных.

Приложение Prover:

  • Приложение для проверки: Принимает блоки и метаданные, создает для них доказательство достоверности zk.

Уровень хранения:

  • Различные компоненты и подкомпоненты взаимодействуют через уровень хранения БД.

State Keeper:

  • Доступ к хранилищу ВМ для State Keeper: Компонент, отвечающий за обработку выполнения транзакций и создание миниблоков и пакетов L1.

Данный список отображает главные механизмы работы, но я не буду останавливаться на каждом компоненте подробно, только на неочевидных моментах.

Генерация блоков цепочки zkSync:

Из документации zksync:

Блоки L2 в настоящее время не играют основной роли в системе. Это функция совместимости для поддержки инструментов, таких как Metamask, ожидающих частого изменения блока. Это позволяет инструментам предоставлять пользователям обратную связь, подтверждая, что их транзакция была добавлена.

Грубо говоря отправив транзакцию, вы моментально увидите ее выполнение, но она не будет еще добавлена в основную цепочку и сохранена на L1.

Основные шаги для завершения транзакции:

  • Выполнить транзакцию в блоке State Keeper & Seal
  • Создать свидетеля
  • Создайте доказательство
  • Проверка доказательства на L1

Выполнить транзакцию в блоке State Keeper & Seal

Задача State Keeper — брать транзакции из мемпула и помещать их в пакет L1. Это делается с помощью process_l1_batch() метода (метод написан на языке Rust)

Этот метод берет следующую транзакцию из мемпула (которая может быть либо L1Tx, либо L2Tx, но L1Tx всегда имеет приоритет и они выполняются первыми), выполняет ее и проверяет, готов ли пакет L1 к запечатыванию

L1Tx - это в основном депозиты или выводы средств из системы.

Теперь про пакеты L1

картинка взята из документации zksync

На данной картине очевидно показано как работает запечатывание пакетов L1.

У нас есть транзакции L1tx и L2tx, транзакции L1tx разумеется идут в приоритете, и все эти транзакции собираются в миниблоки L2, эти миниблоки в свою очередь собираются в пакет L1, который как мы знаем State Keeper готовит к запечатыванию.

У пакетов L1 (State Keeper) есть ограничение в 750 транзакций и еще ряд ограничений связанных с газлимитом и публикационными данными в 120кб

После того как State Keeper собрал пакет L1, нужно создать свидетеля

Так как State Keeper выполняет операции, но не заявляет об их выполнении открыто, нам нужно создать доказательство zk или перейти на новый этап выполнения транзакции

Создать свидетеля.

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

Например:

Я хочу присвоить a = 44

В обычном случае мы бы просто присвоили переменную считав данные из хранилища.

Но в нашем случае мы должны проверить пути меркла и создать набор хешей, чтоб показать что эта операция возможна, и что мы не обманываем.

Еще более простым языком мы должны найти текущий корень дерева меркла и взять его хеш, после чего взять наше число 44 и доказать ее существование в хранилище, то есть проверить, что хеш дерева меркла не поменялся. Дальше мы должны создать новый хеш, который показывает что мы присвоили a=44 и таким образом докажем, что 44 существует в хранилище и создаем новый хеш в дереве меркла

Если вы не знакомы с деревьями и деревом меркла, то этот этап будет трудно понять, поэтому советую ознакомиться.

И так:

Благодаря такой системе мы создаем свидетеля, который говорит нам, что да, транзакции возможны, и мы ему верим.

Следующий этап, это генерация доказательства

Создайте доказательство

Тут мы берем наши данные свидетеля из прошлого пункта и разбиваем его на более мелкие части, создаем из них полиномы (математическое выражение), после чего в силу вступают zkSNARK. Чтоб сгенерировать само доказательство мы вычисляем это выражение в разных точках, и для этого требуется очень большие вычислительные мощности.

Вот наглядный пример как происходит вычисление доказательства вида ZK

Проверка доказательства на L1

Попробую объяснит на палацах:

У нас есть 4 переменных:

  • C : это ключи проверки
  • In : представляет корневой хеш перед блоком транзакции.
  • Out : представляет корневой хеш после блока транзакции.
  • P : Это доказательство, предоставленное доказывающим.

Логика этого заключается в том, что совпадающее доказательство «P» может существовать только в том случае, если C(In) == Out.

Мы просто говорим, что наше вычисленное доказательство P имеет смысл, если наши хеши In и Out согласованы при помощи ключа C.

Главная задача этого пункта - сохранять синхронизацию L1 и L2

Все данные в технологии ZK Rollups хранятся как calldata и zksync не исключение

Смарт-контракты:

Рассмотрим различие ВМ эфира и zksync

Поток Эфириума:

  1. Написание Кода в Solidity: Начинаем с написания смарт-контракта на языке Solidity.
  2. Компиляция с Solc: Используем компилятор solc для преобразования кода в байт-код EVM, байт-код развертывания и ABI.
  3. Развертывание на Ethereum: Отправляем байт-код развертывания на адрес 0x000 в сеть Ethereum, где происходит выполнение байт-кода развертывания и размещение контракта по уникальному адресу.
  4. Взаимодействие с Контрактом: Теперь можем отправлять транзакции на адрес контракта, используя ABI для правильной передачи аргументов функций.
  5. Выполнение на EVM: Весь байт-код выполняется на Ethereum Virtual Machine (EVM), имеющей стек, память и хранилище.

Поток zkSync:

  1. Система Проверки (zkEVM): Основная часть zkSync - это система проверки. Используется виртуальная машина zkEVM, имеющая другой набор кодов операций и регистров.
  2. Компиляция с zk-solc: Используем отдельный компилятор zk-solc для создания байт-кода, совместимого с zkEVM.
  3. Развертывание в zkSync: Отправляем скомпилированный код в zkSync, где его выполнение происходит на zkEVM. Новые контракты могут быть развернуты в системе контрактов zkSync.
  4. Преимущества отдельного компилятора: Использование отдельного компилятора позволяет более быстрые и дешевые модификации, а также увеличивает гибкость системы.

Таким образом, в случае 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), которая перенаправляет запросы к различным контрактам (кранам), которые могут быть независимо обновлены и/или заморожены.

кратинка взята из документации zkSync

Diamond

Технически этот смарт-контракт L1 действует как соединитель между Ethereum (L1) и zkSync (L2). Этот контракт проверяет подтверждение действительности и доступность данных, обрабатывает связь L2 <-> L1, завершает переход состояния L2 и многое другое.

На уровне L2 также развернуты важные контракты, которые также могут выполнять логику, называемую системными контрактами . Использование связи L2 <-> L1 может повлиять как на L1, так и на L2

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

На этом все. Я постарался донести вам достаточно трудную информацию, которую сложно осознать с первого раза, так что если вы что то поняли, уже хорошо. Теперь вы знаете как на самом деле происходят транзакции и формирования блоков в zkSync это далеко не все решения, которые есть в zkSync, но для понимания я думаю достаточно. На самом деле очень сильный проект, который подает большие надежды.

Ресурсы:

Документация zkSync

Мой телеграмм канал