Разработка Zero-Knowledge Proof приложений под ключ

Большинство проектов, которые приходят к нам с запросом на ZKP, столкнулись с одной из двух проблем: либо нужно доказать факт без раскрытия данных (возраст, баланс, принадлежность к множеству), либо нужно перенести тяжёлые вычисления off-chain с верификацией on-chain. Это разные задачи с разными инс

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1451
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1309
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1005
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1270
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    719
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1011

Большинство проектов, которые приходят к нам с запросом на ZKP, столкнулись с одной из двух проблем: либо нужно доказать факт без раскрытия данных (возраст, баланс, принадлежность к множеству), либо нужно перенести тяжёлые вычисления off-chain с верификацией on-chain. Это разные задачи с разными инструментальными стеками, и путать их — первая и самая дорогостоящая ошибка на старте. Мы поможем не ошибиться: оценим проект, подберём стек и реализуем под ключ за 6–12 недель. Свяжитесь с нами для бесплатной консультации.

Как правильно выбрать proof-систему?

Выбор между Groth16, PLONK, STARK, Halo2 и FRI определяет всё: размер proof, время генерации, наличие trusted setup, стоимость верификации on-chain. Ниже — таблица для сравнения.

Система Trusted setup Proof size Verification cost (EVM) Prover time Рекурсия
Groth16 Да (per-circuit) ~200 bytes ~270k gas Быстрый Сложная
PLONK (KZG) Да (универсальный) ~800 bytes ~400k gas Средний Проще
PLONK (IPA) Нет ~1.5KB Дороже Медленный Хорошая
STARK Нет 40–200KB Очень дорого в EVM Медленный Отличная
Halo2 Нет ~1–5KB Не нативный Средний Встроена

Groth16 — выбор для production систем с фиксированной схемой и требованиями к минимальному газу. Используют: Tornado Cash (был), Zcash Sapling, большинство zkSNARK мостов. Минус: каждое изменение circuit требует новой ceremony. PLONK с KZG — де-факто стандарт для zkRollup-подобных систем. Gnosis, zkSync Lite, Polygon Hermez используют варианты PLONK. Универсальный trusted setup (Powers of Tau) переиспользуется — не нужна ceremony под каждую схему. STARKs — выбор для задач, где нет trusted setup и нужна рекурсия: StarkNet, Cairo VM. Огромный proof размер — верификация в EVM нативно нецелесообразна, нужен отдельный verifier контракт или L3-подход. Halo2 — используют Zcash Orchard, Scroll. Не требует trusted setup, встроенная рекурсия. Tooling менее зрелый, экосистема меньше, но активно развивается.

Для большинства практических задач (приватное голосование, proof of membership, zkKYC, age verification) — Groth16 через circom/snarkjs или PLONK через gnark/noir — правильная отправная точка. При газе 20 gwei одна верификация Groth16 обходится ~$2-5 на Ethereum mainnet, а на Arbitrum — ~$0.02-0.05, что делает L2-деплой экономически оправданным.

Что уязвимо в ZK circuits? Разбираем на circom

Circom — DSL для написания arithmetic circuits. Circuit компилируется в R1CS, затем через snarkjs или rapidsnark генерируется proof.

Базовая схема для доказательства знания preim хэша:

pragma circom 2.1.4; include "circomlib/circuits/poseidon.circom"; include "circomlib/circuits/comparators.circom"; template ProveBalance() { signal input balance; // private signal input salt; // private signal input commitment; // public signal input threshold; // public component hasher = Poseidon(2); hasher.inputs[0] <== balance; hasher.inputs[1] <== salt; hasher.out === commitment; component rangeCheck = Num2Bits(64); rangeCheck.in <== balance; component gte = GreaterEqThan(64); gte.in[0] <== balance; gte.in[1] <== threshold; gte.out === 1; } component main {public [commitment, threshold]} = ProveBalance(); 

«Under-constrained signals — наиболее частый класс багов в ZK circuits» (документация circom, раздел безопасности). Если сигнал используется в вычислении, но не имеет достаточного количества constraints — прувер может передать произвольное значение, и верификатор примет proof.

Пример уязвимого кода:

// УЯЗВИМО: нет constraint что out это bit template IsZero() { signal input in; signal output out; signal inv; inv <-- in != 0 ? 1/in : 0; out <-- in == 0 ? 1 : 0; // ЗАБЫЛИ: in * out === 0 и (in * inv - 1 + out) === 0 } 

Верификатор принимает любой out, потому что нет constraints, связывающих out с in.

Overflow в field arithmetic: Circom работает в простом поле p = 21888242871839275222246405745257275088548364400416034343698204186575808495617. Любая операция выполняется по модулю p. Если входные данные — числа из реального мира (возраст, timestamp) — диапазон безопасен. Но при умножении больших чисел нужен явный range check через Num2Bits, как показано в примере.

Gnark (Go) для более сложных схем

Отметим: когда схема слишком сложна для circom (рекурсивные доказательства, BLS signature verification, zkEVM-подобные компоненты) — gnark:

type Circuit struct { PreImage frontend.Variable `gnark:",secret"` Hash frontend.Variable `gnark:",public"` } func (c *Circuit) Define(api frontend.API) error { mimc, err := mimc.NewMiMC(api) if err != nil { return err } mimc.Write(c.PreImage) result := mimc.Sum() api.AssertIsEqual(result, c.Hash) return nil } 

gnark в 10–30x быстрее snarkjs в prover time для одинаковых схем. Для production с реальными пользователями это важно: генерация proof в браузере через WASM занимает 3–15 секунд на Groth16 схеме умеренной сложности, в Go сервере — 0.1–1 секунду.

On-chain верификация

Solidity verifier генерируется автоматически — snarkjs делает это через snarkjs zkey export solidityverifier. Но в production контракт нужно адаптировать:

contract BalanceProofVerifier { IGroth16Verifier public immutable verifier; mapping(bytes32 => bool) public usedNullifiers; function verifyAndExecute( uint[2] calldata a, uint[2][2] calldata b, uint[2] calldata c, uint[2] calldata publicInputs // [commitment, threshold] ) external { bytes32 nullifier = keccak256(abi.encodePacked(a, b, c)); require(!usedNullifiers[nullifier], "Proof already used"); require(verifier.verifyProof(a, b, c, publicInputs), "Invalid proof"); usedNullifiers[nullifier] = true; // ... основная логика } } 

Gas cost верификации Groth16 — около 270k gas. На Ethereum mainnet при 20 gwei это ~$2–5 за верификацию. Для систем с высокой частотой — деплой на L2 (Arbitrum, Base) снижает стоимость в 10–50x.

Инфраструктура для proof generation

Подробнее о trusted setup ceremony

Для Groth16 — обязательна. Процесс:

  1. Универсальный Powers of Tau (берём готовый от Hermez/EthSnarks — это публично верифицированные параметры)
  2. Phase 2 ceremony специфична для вашей схемы: каждый участник добавляет свою рандомность
  3. Final beacon — публичный рандомный источник (Bitcoin block hash)

Если хотя бы один участник честен — параметры безопасны. Для production проектов: минимум 10–20 участников, публичная верификация транскрипта.

Клиентская генерация (браузер)

Для wallet-уровневых операций используется WebAssembly сборка snarkjs. .zkey файл для сложных схем весит 10–500MB — решение: разбить на части (chunked zkey) или использовать streaming загрузку. Для production нагрузок эффективнее серверный prover на Go с gnark.

Серверная генерация (proving service)

Архитектура: Client → API → Queue (Bull/RabbitMQ) → Prover Worker → S3 → Webhook. Prover worker — Go сервис с gnark. Горизонтальное масштабирование: каждый worker независим, задачи идемпотентны. Для zkEVM-уровневых схем (миллиарды constraints) — GPU proving через CUDA с ускорением 100–1000x.

Как начать разработку ZK-приложения: 5 шагов

  1. Спецификация схемы: формализация задачи, выбор proof-системы, определение public/private inputs.
  2. Circuit разработка: написание кода на circom/gnark/noir, unit тесты constraints.
  3. Аудит circuits: поиск under-constrained сигналов, проверка soundness с помощью формальной верификации.
  4. Verifier контракт: генерация Solidity verifier, добавление nullifier логики, интеграция с протоколом.
  5. Prover инфраструктура: настройка WASM сборки или серверного прувера, API для вызова.

Сроки и объём работ

Фаза Содержание Срок
Спецификация схемы Формализация задачи, выбор proof-системы, проектирование public/private inputs 1 нед
Circuit разработка Написание circom/gnark/noir, unit тесты constraints 2–4 нед
Аудит circuits Поиск under-constrained сигналов, проверка soundness 1–2 нед
Verifier контракт Solidity verifier + nullifier логика + интеграция с основным протоколом 1–2 нед
Prover инфраструктура WASM сборка или серверный прувер, API 1–2 нед
Trusted setup Организация ceremony (если Groth16/PLONK-KZG) 1 нед

Итого для типового ZKP приложения (proof of membership, zkKYC, приватные транзакции): 6–12 недель от спецификации до mainnet. Сложные zkRollup-подобные системы — 6–18 месяцев команды.

Хотите обсудить ваш проект? Закажите разработку ZK-приложения — мы бесплатно оценим задачу и предложим архитектуру. Перейдите на Wikipedia для общего понимания технологии. Обращайтесь к нам за консультацией.