Разработка ZK-SNARK приложений
ZK-SNARK (Zero-Knowledge Succinct Non-interactive ARgument of Knowledge) — это доказательство того, что вы знаете секрет, не раскрывая секрет. Звучит абстрактно, пока не столкнёшься с конкретной задачей: доказать, что пользователь старше 18 лет без передачи даты рождения, или подтвердить баланс кошелька без раскрытия адреса, или верифицировать исполнение программы без повторного её запуска.
Tornado Cash (до санкций) переместил ~7 млрд долларов, используя ZK-SNARK для доказательства права на вывод без связи с адресом депозита. Zcash защищает транзакции через ту же технологию. Polygon zkEVM доказывает корректность пакета из тысяч транзакций одним компактным proof.
Как работают ZK-SNARK: достаточно глубоко, чтобы строить
От задачи к схеме
Любая вычислительная задача, которую можно записать как набор арифметических ограничений (arithmetic circuit), может быть доказана через ZK-SNARK. Схема (circuit) — это не обычная программа. Это описание вычисления как системы уравнений над конечным полем.
Возьмём простую задачу: доказать, что я знаю x такое, что x² + x + 5 = y, где y — публичное значение. Схема:
signal input x;
signal output y;
signal x_squared;
x_squared <== x * x;
y <== x_squared + x + 5;
Это Circom — основной язык для описания ZK-схем. Компилятор преобразует схему в набор ограничений R1CS (Rank-1 Constraint System), затем в QAP (Quadratic Arithmetic Program), который является математической основой для SNARK.
Groth16 vs PLONK vs FFLONK
Выбор proof system определяет все остальные параметры: размер proof, время генерации, размер trusted setup, время верификации в контракте (и соответственно gas cost).
| Система | Proof size | Verify gas | Trusted setup | Prover time |
|---|---|---|---|---|
| Groth16 | ~200 bytes | ~250k gas | Per-circuit | Быстрый |
| PLONK | ~800 bytes | ~450k gas | Universal | Медленнее |
| FFLONK | ~800 bytes | ~200k gas | Universal | Медленнее |
| STARKs | >40 KB | >1M gas | Не нужен | Быстрый |
Groth16 — наименьший proof и наименьший gas на верификацию. Недостаток: каждая схема требует отдельной церемонии trusted setup. Если схема изменилась — нужна новая церемония. Использует: Tornado Cash, Zcash, большинство production ZK-приложений до 2022.
PLONK — universal trusted setup (Powers of Tau), который годится для любой схемы до определённого размера. Изменить схему можно без новой ceremoni. Proof больше, но для большинства приложений это приемлемо. Использует: zkSync Era, Aztec Protocol.
FFLONK — оптимизированная версия PLONK с меньшим gas на верификацию. Используется в Polygon zkEVM.
Мы рекомендуем Groth16 для production приложений с фиксированной схемой и высоким объёмом транзакций (минимальный gas на верификацию). PLONK — для прототипов и приложений, где схема может меняться.
Trusted Setup и почему это важно
Trusted setup — это криптографическая церемония, которая генерирует параметры для доказательств. Если кто-то сохранит "токсичные отходы" (промежуточные значения) — он сможет генерировать поддельные доказательства. Это не теоретическая угроза: если setup скомпрометирован, вся система доверия рушится.
Groth16 требует двухэтапной ceremoni:
- Powers of Tau — универсальная часть, независимая от схемы. Существуют публичные trusted setup от Ethereum Foundation (Hermez 1, 2), которые поддержали тысячи участников. Мы используем их, не генерируем свои.
- Phase 2 — схемо-специфичная часть. Для production систем организуем церемонию с несколькими участниками через snarkjs.
Стек и инструментарий
Circom 2 — язык для написания схем. Компилятор на Rust, значительно быстрее первой версии. Поддерживает шаблоны (templates) для переиспользования схем.
snarkjs — JavaScript библиотека для генерации и верификации доказательств, проведения trusted setup, экспорта верификатора в Solidity.
circomlibjs — библиотека стандартных схем: hash функции (Poseidon, MiMC, SHA256 в схеме), подпись (EdDSA, ECDSA), деревья Меркла.
Noir (Aztec) — альтернативный язык с более высоким уровнем абстракции, компилирует в PLONK. Проще для разработчиков, знакомых с Rust-синтаксисом.
SnarkVM / Leo (Aleo) — для Aleo blockchain, если задача требует privacy-first L1.
Типичный проект: ZK Age Verification
Задача: пользователь доказывает, что старше 18 лет, используя данные из верифицированного credential (например, от KYC-провайдера). Провайдер подписал дату рождения своим ключом. Пользователь не раскрывает дату рождения, только доказывает факт.
Схема (упрощённо):
template AgeVerification(merkleDepth) {
// Публичные входы
signal input currentDate; // текущая дата (публичная)
signal input issuerPubKeyHash; // хэш публичного ключа провайдера (публичная)
// Приватные входы (witness)
signal input birthDate; // дата рождения (приватная)
signal input signature[2]; // подпись провайдера (приватная)
signal input issuerPubKey[2]; // публичный ключ провайдера (приватная)
// Проверяем подпись провайдера
component sigVerifier = EdDSAVerifier();
sigVerifier.msg <== birthDate;
sigVerifier.pubKey <== issuerPubKey;
sigVerifier.sig <== signature;
// Проверяем, что pubKey соответствует публичному хэшу
component hasher = Poseidon(2);
hasher.inputs <== issuerPubKey;
issuerPubKeyHash === hasher.out;
// Проверяем возраст
signal age;
age <== currentDate - birthDate;
component ageCheck = GreaterThan(32);
ageCheck.in[0] <== age;
ageCheck.in[1] <== 18 * 365; // 18 лет в днях
ageCheck.out === 1;
}
Верификатор в Solidity генерируется автоматически через snarkjs и содержит precompile-вызовы для проверки elliptic curve pairing (EIP-197). Gas на верификацию — около 250k для Groth16.
Производительность и ограничения
Время генерации proof (proving time) зависит от размера схемы (количества ограничений). Ориентиры для Groth16 на современном железе:
| Ограничений в схеме | Prover time (CPU) | Prover time (GPU) |
|---|---|---|
| 100k | ~5 сек | ~0.5 сек |
| 1M | ~60 сек | ~5 сек |
| 10M | ~15 мин | ~60 сек |
Для web-приложений прувинг в браузере реален для схем до 500k ограничений (через WASM компиляцию). Более тяжёлые схемы требуют серверного prover или специализированного prover-сервиса (Sindri, Succinct).
Poseidon hash в схеме намного эффективнее SHA256: Poseidon — ~250 ограничений на хэш, SHA256 — ~27000. Поэтому все ZK-дружественные протоколы используют Poseidon.
Процесс разработки
Исследование и дизайн схемы (1-2 недели). Переводим бизнес-задачу в арифметические ограничения. Оцениваем размер схемы и proving time. Выбираем proof system. Это самый важный этап — ошибка в дизайне схемы может требовать полного перепроектирования.
Разработка схемы на Circom (1-2 недели). Пишем схему, покрываем unit-тестами через Jest + circomlibjs. Отдельно верифицируем математическую корректность ограничений.
Trusted Setup. Для прототипа — используем тестовый entropy. Для production — организуем ceremony с несколькими участниками.
Разработка верификатора и интеграция (1 неделя). Генерируем Solidity verifier через snarkjs. Интегрируем в основной смарт-контракт. Пишем TypeScript SDK для frontend.
Аудит схемы. ZK-схемы имеют специфичные уязвимости: underconstraining (схема не проверяет все нужные условия), overconstraining (схема делает невозможным легитимные доказательства), signal aliasing. Это отдельный тип аудита, который требует специализации.
Сроки: от 1 недели (простая схема, PLONK) до 3 месяцев (сложный zkApp с кастомными криптографическими примитивами). Стоимость рассчитывается после детального анализа требований.







