Розробка Zero-Knowledge Proof застосунків під ключ
Більшість проектів, які приходять до нас із запитом на ZKP, стикаються з однією з двох проблем: або потрібно довести факт без розкриття даних (вік, баланс, належність до множини), або потрібно перенести важкі обчислення off-chain з верифікацією on-chain. Це різні завдання з різними інструментальними стеками, і плутати їх — перша і найдорожча помилка на старті. Ми допоможемо не помилитися: оцінимо проект, підберемо стек і реалізуємо під ключ за 6–12 тижнів. Маємо 5+ років досвіду в ZK, реалізували 20+ проектів та провели 10+ аудитів. Зв'яжіться з нами для безкоштовної консультації.
Як правильно вибрати proof-систему для розробки Zero-Knowledge 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 забезпечує proof у 4 рази менший за PLONK, а генерація на Go gnark у 10-30 разів швидше за snarkjs у браузері. 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-застосунку починається від $20,000 до $150,000 залежно від складності.
Що вразливо в 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 — обов'язкова. Процес:
- Універсальний Powers of Tau (беремо готовий від Hermez/EthSnarks — це публічно верифіковані параметри)
- Phase 2 ceremony специфічна для вашої схеми: кожен учасник додає свою рандомність
- 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 кроків
- Специфікація схеми: формалізація завдання, вибір proof-системи, визначення public/private inputs.
- Circuit розробка: написання коду на circom/gnark/noir, unit тести constraints.
- Аудит circuits: пошук under-constrained сигналів, перевірка soundness за допомогою формальної верифікації.
- Verifier контракт: генерація Solidity verifier, додавання nullifier логіки, інтеграція з протоколом.
- 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 місяців команди.
Що входить в роботу
- Формалізація завдання та вибір оптимальної proof-системи
- Написання та тестування circuits (circom/gnark/noir)
- Організація trusted setup (якщо потрібно)
- Аудит circuits на under-constrained сигнали та overflow
- Генерація та адаптація Solidity verifier контракту
- Реалізація nullifier механізму для запобігання повторному використанню proof
- Налаштування інфраструктури для генерації proof (клієнтська або серверна)
- Інтеграція з вашим протоколом
Хочете обговорити ваш проект? Замовте розробку ZK-застосунку — ми безкоштовно оцінимо завдання та запропонуємо архітектуру. Перейдіть на Wikipedia для загального розуміння технології. Звертайтесь до нас за консультацією. Вартість розробки ZK-застосунку починається від $20,000 до $150,000 залежно від складності. Ми маємо 5+ років досвіду в ZK, реалізували 20+ проектів, провели 10+ аудитів.







