Протокол целиком
Один абзац: пользователь шифрует запись на своём устройстве сетевым FHE-ключом и публикует только шифротекст. Покупатель кладёт бюджет в USDC и публикует спек модели. Любой узел может выполнить обучение по шифротекстам — арифметика FHE даёт шифротексты. Узлы коммитят каждую эпоху в ZK proof-of-compute; по принятии эскроу стримится контрибьюторам и узлам. Веса покупатель получает через пороговый комитет и только после settlement. Отдельные записи не расшифровываются никем и никогда.
Коротко своими словами
Чужие таблицы учат твою модель, не показывая тебе ни одной строки. Деньги лежат в сейфе-контракте и раздаются только за проверенную работу. Ниже — детали для любопытных, в лаборатории — всё руками.
Модель доверия и ключи
| Сторона | Доверено | Не доверено |
|---|---|---|
| Пользователь | свой plaintext, свой ключ | всё остальное |
| Web2 + SDK | ничего — plaintext не покидает браузер | — |
| Покупатель | бюджет, спек модели | plaintext чужих строк |
| Узел | набор шифротекстов (уже публичен в DA) | plaintext, секретный ключ |
| Комитет (t-of-n) | совместно — только выходные шифротексты | индивидуальные записи |
| Секвенсор | порядок транзакций | plaintext |
| fhEVM-сопроцессор | операции над шифротекстом, ротация ключей | plaintext |
Иерархия ключей
s_pub публикуется в params-бандле и пинится в NetworkParams (защита от подмены). committeePub — BLS-агрегат для reveal без транзакции на члена. nodeId = keccak(blsPubKey[0:20] ‖ operator) связывает узел со стейком.
Правило раскладки
В цепочке — обязательства и зашифрованное состояние; bulk-шифротекст — в DA. STARK проверяется по ончейн-ctRoot, поэтому fhEVM-нагрузка ограничена, а проверяемость сохранена.
Сквозной путь: 29 шагов
| # | Шаг | Кто → куда |
|---|---|---|
| 1–3 | GET s_pub → вектор + Groth16 «знаю строку такой формы» → FHEWasm-шифрование батчами по 8 | устройство |
| 4–6 | PUT bundle в DA → EIP-712 привязка хэша к кошельку → submitContribution | SDK → DA → секвенсор |
| 7–9 | tx в CipherTask.submitContribution → учёт эскроу → событие нового ctRoot | L3 |
| 10–13 | openEpoch → чекпоинт encState → EpochOpened → узлы тянут шарды range-pull | покупатель → узлы |
| 14–18 | TFHE SGD без расшифровки → хэш-чейн трейса → STARK → PUT в DA → commitEpoch | узел → цепочка |
| 19–23 | verify по publicInputs → слайс узлу; при споре fraud-окно, ре-экзекьюшн, слэш | контракт |
| 24–29 | requestReveal → BLS-гейт → t частичных расшифровок выходов → withdrawWeights | покупатель + комитет |
createTask (escrow) → OPENING → COLLECTING → SEALED → EPOCH_OPEN → EPOCH_COMMITTING → EPOCH_SETTLED (×E) → SETTLING → REVEALING → DISCLOSED fraud quorum / timeout → ABORTED (refund незапущенного)
FHE-выбор и математика выплат
| Вопрос | Решение |
|---|---|
| Схема | TFHE (tfhe-rs / Zama fhEVM) — программируемый бутстреп для глубоких конвейеров |
| Лейны | euint32: табличные данные и веса без взрыва шума CKKS |
| Непрерывные признаки | квантование в u32, scale 1e4 — целочисленный SGD точен и дешёв для STARK |
| Батчинг | 8 лейнов на шифротекст — амортизация бутстрепов |
| Шум | бюджет 2^40 safe-msg-bits; рекей каждые 2 эпохи (RekeyGuard) |
Деление бюджета B, эпох E
contributorPool = B·8000/10000; nodePool = B·1500/10000; committeePool — в treasury, а нераспределённый остаток — возврат покупателю. contributor_i = pool · w_i / Σw, где w_i = rows_i·liveness — линейно: дележ не даёт и не отнимает, китов душат minRows и бонд. node_n = nodePool / (E·q) поровну по валидным коммитам. Пыль contributor-пула — участнику с наибольшим address (детерминировано); остаток epoch lock и невостребованное уходят покупателю.
Атаки и контрмеры
| # | Атака | Контрмера |
|---|---|---|
| A1 | Мусорные строки ради эскроу | Groth16-привязка к shape-корню покупателя + слэш кворумом |
| A2 | STARK под другой датасет | public input: ctRoot, эпоха, chainId, адрес контракта |
| A3 | Веса с чужого набора | хэш-чейн AIR по дайджестам шардов до ctRoot |
| A4 | Накрутка прогресса ради микроплатежей | слайсы подписываются лишь за EPOCH_SETTLED |
| A5–A6 | Сговор комитета (< t / ≥ t) | расшифровка только выходов settled-задач; сговор большинства — остаточный риск F-10 |
| A7 | Подмена s_pub секвенсором | пин keccak(s_pub) в NetworkParams; SDK отказывается при настроенном пине, узел проверяет пин на старте |
| A8 | Фронтраннинг чужого шарда | один шард на адрес + уникальность CID + EIP-712 привязка к отправителю |
| A9 | Abort после трат узлов | эскроу лочится на openEpoch; возврат — только незапущенные эпохи |
| A10 | DoS размером датасета | лимиты maxContributors/maxShards/maxCiphertextBytes/minRowsPerShard |
| A11 | Реорг settled-эпохи | вотчеры только по finalized; settlement идемпотентен |
| A12 | Декомпрессионная бомба на узел | range-pull с бюджетом + проверка дайджеста до парсинга |
Код: контракты, SDK, узел
| Контракт | Роль |
|---|---|
CipherTask | эскроу + FSM + дивиденды + fraud-окно; только коммиты по 32 байта |
PaymentVault | стриминг-каналы EIP-712; инвариант totalLocked == subBalances + channelLocks за O(1) |
DecryptionGate | BLS-гейт частичных расшифровок; withdraw требует реального threshold |
NetworkParams | пин FHE-ключа; ротация: предложение → таймлок → исполнение |
ProofVerifier | сэмплированная ончейн-проверка + 7-дневный таймлок ротации |
uploadAndMonetize
sdk.uploadAndMonetize(data, taskId, { features, allowUnencrypted }) — шифрует локально, кладёт в DA, проверяет все условия submitContribution до траты газа. publishWeights — генезис-веса shroud/weights/v1. Key-pin, chainId-check, hardened DA-клиент с getVerified.
Исполнитель эпох
Reorg-safe вотчер → derived fold; reference-cpu движок (golden-тесты) + tfhe-rs за prover; контент-адресный DA (da — remote); споры из собственных коммитов; сбор каналов finalize/settle/redeem/reclaim/claim/bond.
Запуск и роли
Покупатель
fund a taskcreateTask(budget, params) → sealTask → openEpoch на каждую эпоху → requestReveal и withdraw. Пример: packages/contracts/scripts/devnet-e2e-check.ts.Контрибьютор
sell value, not datasdk.uploadAndMonetize(data, taskId, { features }). В проде — @shroud/sdk/relayer. Механику попробуй в лаборатории.Оператор узла
earn epochsdocker compose up -d node-a (ручки — infra/README.md §3) или собери node/. Узел регистрируется, исполняет, коммитит, спорит и забирает каналы.Наблюдатель
ops-botbot/ (токен + allowlist в bot/.env): /menu-хаб, /pool, /task <id>, алерты споров, ротаций ключей и казны.# contracts cd packages/contracts && npm ci && npx hardhat test && npx tsc --noEmit # sdk cd packages/sdk && npm ci && npm test # bot cd bot && npm ci && npm test # node cd node && cargo check --all-features # site cd web && python -m http.server 4173
Параметры для прошаренных
| Узел | Поля и форматы |
|---|---|
| TaskParams | minRowsPerShard, maxContributors, maxShards, maxCiphertextBytes, окно взносов, minContributors до seal — всё валидируется при createTask |
| Транскрипт эпохи | taskId · epoch · chainId · CipherTask · ctRoot · prevDigest · features · scale · lrQ16 · l2Q16 · minibatch · seed · nRows · lossMeanQ · листья · nextDigest |
| Proof-референс | 0x53544152 («STAR») + 28 байт keccak(транскрипт); верифицируется только на mock-верифаере |
| Шард shroud/noop/v1 | magic + «features\\0rows\\0» + f64 LE-матрица; commitment — keccak по строкам |
| Сеттлмент | settleFrom(taskId, cursor) по 200; финальная страница платит комитету и возвращает остаток покупателю |
Сделано и дальше
Ядро готово
FSM + эскроу + каналы + threshold-reveal + таймлоки; кворум споров со слэшем бонда + commit-reveal репортов и PoP-привязкой BLS-ключей; бонд взносов (opt-in); детерминированный движок + tfhe-rs; SDK с key-pin и DA-закалкой; бот с покрытием; devnet; fuzz и газ-снапшот.
Доказательства и экономика
zkVM-гость поверх 8-полевого транскрипта + рекурсивная агрегация (ончейн-проверка буквально); stake-взвешенные ре-экзекьюторы со слэшем в обе стороны, Gateway re-encryption, Foundry-инварианты, бенчмарки CPU/GPU.
Документы: 01-architecture · 02-cryptography · 03-proof-of-compute · 04-threat-model · 05-honest-limitations. docs →