Розробка Zero-Knowledge Proof застосунків

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка Zero-Knowledge Proof застосунків
Складний
від 1 тижня до 3 місяців
Часті запитання

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

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1308
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1222
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    921
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1150
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    614
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    886

Розробка застосунків Zero-Knowledge Proof

Більшість проектів, що звертаються за підтримкою ZKP, стикаються з однією з двох проблем: або потрібно довести факт без розкриття даних (вік, баланс, належність до множини), або потрібно перемістити важкі обчислення поза блокчейн з верифікацією в мережі. Це різні завдання з різними наборами інструментів, і плутанина між ними — перша і найдорожча помилка на старті.

Вибір proof-системи: це не академічне питання

Вибір між Groth16, PLONK, STARK, Halo2 та FRI визначає все: розмір proof, час генерування, наявність trusted setup, вартість верифікації в мережі.

Порівняння основних систем

Система Trusted setup Розмір proof Вартість верифікації (EVM) Час прувера Рекурсія
Groth16 Так (per-circuit) ~200 байт ~270k газу Швидкий Складна
PLONK (KZG) Так (універсальний) ~800 байт ~400k газу Середній Простіша
PLONK (IPA) Ні ~1.5KB Дороговартісна Повільний Хороша
STARK Ні 40–200KB Дуже дорога в EVM Повільний Відмінна
Halo2 Ні ~1–5KB Не рідний Середній Вбудована

Groth16 — вибір для production-систем з фіксованою схемою та мінімальними вимогами до газу. Використовують: Tornado Cash (колишній), Zcash Sapling, більшість zkSNARK мостів. Недолік: кожна зміна circuit вимагає нової церемонії.

PLONK з KZG — de facto стандарт для zkRollup-подібних систем. Gnosis, zkSync Lite, Polygon Hermez використовують варіанти PLONK. Універсальний trusted setup (Powers of Tau) переважно використовується — церемонія не потрібна для кожної схеми.

STARKs — вибір для завдань без trusted setup та що вимагають рекурсії: StarkNet, Cairo VM. Величезний розмір proof — верифікація в EVM не практична, потребує окремого верифікаційного контракту або L3 підходу.

Halo2 — використовується Zcash Orchard, Scroll. Не вимагає trusted setup, вбудована рекурсія. Інструменти менш зрілі, менша екосистема, але активно розвиваються.

Для більшості практичних завдань (приватне голосування, proof of membership, zkKYC, перевірка віку) — Groth16 через circom/snarkjs або PLONK через gnark/noir — правильна стартова точка.

Розробка circuits: де сидять справжні баги

Circom та його підводні каміння

Circom — DSL для написання arithmetic circuits. Circuit компілюється в R1CS (Rank-1 Constraint System), потім через snarkjs або rapidsnark генерується proof.

Базова схема для доказу знання preimage хешу:

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 (зберігається in-chain)
    signal input threshold;       // public

    // Доводимо: hash(balance, salt) == commitment
    component hasher = Poseidon(2);
    hasher.inputs[0] <== balance;
    hasher.inputs[1] <== salt;
    hasher.out === commitment;

    // Доводимо: balance >= threshold (без розкриття балансу)
    component gte = GreaterEqThan(64);
    gte.in[0] <== balance;
    gte.in[1] <== threshold;
    gte.out === 1;
}

component main {public [commitment, threshold]} = ProveBalance();

Критична уразливість: недостатньо обмежені сигнали. Це найпоширеніший клас багів у ZK circuits. Якщо сигнал використовується в обчисленні, але немає достатнього числа обмежень — прувер може передати довільне значення, і верифікатор прийме proof.

Приклад уразливого коду:

// УРАЗЛИВО: немає обмеження, що 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, тому що немає обмежень, пов'язаних out з in.

Переповнення в field arithmetic. Circom працює у простому полі p = 21888242871839275222246405745257275088548364400416034343698204186575808495617. Будь-яка операція виконується за модулем p. Якщо вхідні дані з реального світу (вік, timestamp), діапазон безпечний. Але при множенні великих чисел потрібна явна перевірка діапазону через Num2Bits:

component rangeCheck = Num2Bits(64);
rangeCheck.in <== balance;
// Тепер баланс гарантовано < 2^64

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–30 разів швидший за snarkjs у часі прувера для еквівалентних схем. Для production з реальними користувачами це важливо: генерація proof у браузері через WASM займає 3–15 секунд на помірній Groth16 схемі, у Go сервері — 0.1–1 секунду.

Верифікація in-chain

Solidity верифікатор генерується автоматично — snarkjs робить це через snarkjs zkey export solidityverifier. Але production контракт потребує адаптації:

contract BalanceProofVerifier {
    IGroth16Verifier public immutable verifier;

    // Зберігання nullifier для захисту від replay
    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;
        // ... основна логіка
    }
}

Nullifier pattern — обов'язковий для proof of membership та будь-яких систем, де один proof не повинен використовуватися двічі. Nullifier = детермінований хеш від секретного вводу, який неможливо пов'язати з ідентичністю, але можна перевірити на унікальність.

Вартість газу для Groth16 верифікації — близько 270k газу. На Ethereum mainnet при 20 gwei це ~$2–5 за верифікацію. Для систем з високою частотою — деплой на L2 (Arbitrum, Base) зменшує вартість в 10–50 разів.

Інфраструктура для генерування proof

Генерування на клієнті (браузер)

Придатне для: операцій на рівні гаманця, окремих доказів. Використовує WebAssembly збірку snarkjs:

import { groth16 } from "snarkjs";

const { proof, publicSignals } = await groth16.fullProve(
    { balance: "5000", salt: randomSalt, commitment: onChainCommitment, threshold: "1000" },
    "/circuits/balance_proof.wasm",
    "/circuits/balance_proof_final.zkey"
);

.zkey файли для складних схем важать 10–500MB — це проблема для браузера. Рішення: розбити на частини (chunked zkey) або використовувати потокове завантаження.

Генерування на сервері (proving service)

Для схем з великою кількістю обмежень (>1M) — клієнт не впорається. Архітектура:

Клієнт → API (створення завдання) → Queue (Bull/RabbitMQ) → Prover Worker → S3 (proof) → Webhook

Prover worker — Go сервіс з gnark або Rust з bellman/arkworks. Горизонтальне масштабування: кожен worker незалежний, завдання ідемпотентні.

Для zkEVM-рівневих схем (мільярди обмежень) — GPU proving через CUDA. Прискорення 100–1000x в порівнянні з CPU. Провайдери: Ingonyama, Ulvetanna.

Trusted Setup Ceremony

Для Groth16 — обов'язкова. Процес:

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

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

Часові рамки та обсяг робіт

Фаза Зміст Тривалість
Специфікація схеми Формалізація завдання, вибір proof-системи, проектування public/private inputs 1 тиждень
Розробка circuit Написання circom/gnark/noir, unit тести обмежень 2–4 тижні
Аудит circuits Пошук under-constrained сигналів, перевірка soundness 1–2 тижні
Верифікаційний контракт Solidity верифікатор + nullifier логіка + інтеграція з протоколом 1–2 тижні
Інфраструктура прувера WASM збірка або серверний прувер, API 1–2 тижні
Trusted setup Організація церемонії (якщо Groth16/PLONK-KZG) 1 тиждень

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