Разработка 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
    1149
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    613
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    886

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

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

Выбор 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 — правильная отправная точка.

Разработка circuits: где сидят настоящие баги

Circom и его подводные камни

Circom — DSL для написания arithmetic circuits. Circuit компилируется в R1CS (Rank-1 Constraint System), затем через 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 (хранится on-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. Если сигнал используется в вычислении, но не имеет достаточного количества 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:

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–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;

    // Хранение 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 = детерминированный хэш от секретного ввода, который невозможно связать с идентичностью, но можно проверить на уникальность.

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

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

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

Подходит для: wallet-уровневых операций, единичных доказательств. Используется 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) или использовать streaming загрузку.

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

Для схем с большим числом constraints (>1M) — клиент не справится. Архитектура:

Client → API (создание задачи) → Queue (Bull/RabbitMQ) → Prover Worker → S3 (proof) → Webhook

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

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

Trusted Setup Ceremony

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

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

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

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

Фаза Содержание Срок
Спецификация схемы Формализация задачи, выбор 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 месяцев команды.