Private Channels не прошёл аудит безопасности и не рекомендуется для использования в продакшене с реальными средствами без тщательной проверки безопасности.
Развёртываете экземпляр? Перейдите к руководству оператора. Интегрируетесь с существующим экземпляром? Перейдите к Quickstart. Эта страница является справочником по архитектуре для обеих аудиторий.
Архитектура
Private Channels состоит из четырёх компонентов: двух on-chain программ Solana (Escrow и Withdraw) и двух off-chain сервисов (Gateway и Auth Service). Вместе они образуют протокол state channel, где средства хранятся в Mainnet, а переводы проводятся вне цепочки.
Программа Escrow
Программа Escrow — это on-chain программа Solana, которая хранит депонированные SPL-токены. Она является якорем доверия системы: все средства в конечном счёте находятся в эскроу до тех пор, пока оператор не предоставит корректное доказательство исключения Sparse Merkle Tree для их освобождения.
- ID программы:
9tgHa1DcnaSSUtmMsst8ovKTe1Gfxzezn27KnH9xXYeU - Этот ID компилируется в бинарный файл программы через
declare_id!(). Off-chain сервисы считывают тот же ID во время компиляции из сгенерированного клиентского крейта, а не из переменной окружения. - Управляет PDA-аккаунтами
Instance,AllowedMintиOperator - Инструкции:
CreateInstance,AllowMint,BlockMint,AddOperator,RemoveOperator,SetNewAdmin,Deposit,ReleaseFunds,ResetSmtRoot
Программа Withdraw
Программа Withdraw работает в сети приватного канала, а не в Solana Mainnet.
Пользователи вызывают WithdrawFunds для сжигания баланса токенов на стороне канала. Это сжигание
не освобождает средства автоматически; оно сигнализирует оператору о том, что
запрос на вывод ожидает обработки. Затем оператор вызывает ReleaseFunds в программе Escrow
с корректным SMT-доказательством для завершения расчёта.
- ID программы:
J231K9UEpS4y4KAPwGc4gsMNCjKFRMYcQBcjVW7vBhVi - Этот ID компилируется в бинарный файл программы. Off-chain сервисы считывают тот же ID во время компиляции из сгенерированного клиентского крейта, а не из переменной окружения.
Gateway
Gateway — это совместимый с Solana JSON-RPC прокси, который направляет клиентские запросы к
write-узлу сети канала (для отправки транзакций) и read-узлу (для
запросов). Настраивается через переменные окружения: GATEWAY_PORT,
GATEWAY_WRITE_URL, GATEWAY_READ_URL.
Эндпоинты проверки работоспособности (аутентификация не требуется):
GET /health— проверка доступности; возвращает200 {"status":"ok"}GET /ready— глубокая проверка готовности, опрашивает write- и read-узлы; возвращает200 {"status":"ready"}или503 {"status":"degraded"}
Маршрутизация RPC-методов и управление доступом
Gateway направляет sendTransaction к write-узлу, а все остальные методы — к
read-узлу. Запросы размером более 64 КБ отклоняются с HTTP 413. При
включённой аутентификации доступ к методам контролируется JWT-ролью. Полную матрицу методов
см. в разделе Аутентификация и роли.
Auth Service
Auth Service — это опциональный компонент, который выдаёт HS256 JWT (срок действия 24 часа)
для контроля доступа к gateway. Он активируется при наличии переменной окружения JWT_SECRET.
Без неё gateway принимает все подключения.
JWT-клеймы: sub (UUID пользователя), role ("user" или "operator"), iss
("private-channel-auth"), aud ("private-channel-gateway"), exp (Unix-временная метка).
iss и aud проверяются конфигурацией JWT в gateway, а не десериализуются в структуру
клеймов приложения: на уровне приложения доступны только sub, role и exp.
Роли:
user— доступ ограничен собственными верифицированными кошельками; не может вызыватьgetBlock,getTransactionилиsimulateTransactionoperator— обходит все проверки владения; полный доступ ко всем RPC-методам; должен быть предоставлен в базе данных (самостоятельное повышение привилегий невозможно)
Streamer
Streamer — это WebSocket-сервер, который в реальном времени передаёт обновления состояния канала подключённым клиентам, исключая необходимость опроса RPC. Он опрашивает PostgreSQL на наличие изменений состояния. Streamer входит в базовый стек Docker Compose, а не в devnet-стек, который развёртывается в этом руководстве; см. справочник конфигурации.
- Порт:
8902, настраивается черезSTREAMER_PORT - Подключение:
ws://localhost:8902 - Эндпоинт проверки работоспособности:
GET /health— возвращает503, если любой внутренний цикл опроса зависает дольше 30 секунд
Схема событий WebSocket ещё не задокументирована публично. До появления официальной
документации обращайтесь к core/src/bin/streamer.rs
для получения подробностей реализации.
Конвейер транзакций
Transaction -> [1:Dedup] -> [2:SigVerify] -> [3:Sequencer] -> [4:Executor] -> [5:Settler] -> Database
Транзакции, отправленные в Gateway, проходят через пятиэтапный конвейер, прежде чем их состояние будет зафиксировано:
- Dedup — фильтрует дублирующиеся транзакции до их поступления в конвейер
- SigVerify — проверяет подписи транзакций по открытому ключу подписанта
- Sequencer — детерминированно упорядочивает корректные транзакции для формирования канонической истории
- Executor — выполняет транзакции против уровня аккаунтов канала (BOB Cache + AccountsDB), обновляя балансы вне цепочки
- Settler — фиксирует накопленные результаты транзакций в PostgreSQL и
обновляет кэш Redis; генерирует новые blockhash для следующего цикла блоков.
Расчёт в Mainnet (вызов
ReleaseFunds) обрабатывается отдельно сервисомoperator-private-channel
Ключевые особенности
Конфиденциальность
Переводы между участниками канала не фиксируются в Solana Mainnet. На блокчейне отображаются только депозиты (вход в канал) и окончательные выводы средств (выход из канала). Идентификаторы контрагентов и суммы переводов не видны сторонним наблюдателям во время работы канала.
Производительность
Off-chain конвейер исключает время блока Solana из критического пути. Переводы подтверждаются в момент их обработки секвенсером, а не при подтверждении блока Solana. Это обеспечивает финализацию за доли секунды и пропускную способность, превышающую нативный TPS Solana для переводов на уровне приложений.
Расчёт
Каждый вывод средств защищён on-chain доказательством Sparse Merkle Tree. Корень SMT
хранится в Instance.withdrawal_transactions_root в программе Escrow.
При вызове ReleaseFunds программа сначала проверяет доказательство исключения для
неиспользованного нонса относительно текущего on-chain корня, затем проверяет отдельное
доказательство включения для этого нонса относительно нового корня, предоставленного вызывающей стороной.
Только после прохождения обеих проверок новый корень сохраняется, что делает двойное
расходование невозможным даже в случае компрометации ключа оператора.
Модель безопасности
Ключ администратора — управляет созданием экземпляров (CreateInstance) и провижнингом
операторов (AddOperator / RemoveOperator). Компрометация ключа администратора
позволяет произвольно добавлять операторов. SetNewAdmin передаёт права администратора
необратимо за один шаг; защищайте ключ администратора соответствующим образом.
Ключи операторов — могут вызывать ReleaseFunds и ResetSmtRoot. Они не могут
освобождать средства без корректного доказательства исключения SMT относительно текущего on-chain
корня. On-chain проверка verify_smt_exclusion_proof — последний рубеж защиты
от несанкционированных выводов: одной лишь компрометации ключа оператора недостаточно
для опустошения эскроу.
Корень SMT — хранится on-chain в Instance.withdrawal_transactions_root.
Атомарно обновляется при каждом вызове ReleaseFunds. Поскольку каждое доказательство
должно ссылаться на неиспользованный нонс, двойное расходование одного и того же баланса канала
невозможно даже в случае компрометации ключа оператора.
Ротация дерева — Instance.current_tree_index отслеживает epoch'и дерева. При
вызове ResetSmtRoot индекс дерева увеличивается, а все нонсы из предыдущего epoch'а
дерева аннулируются, что обеспечивает чистое состояние для новых расчётных циклов.
Операционная безопасность ключей
Off-chain сервисы используют собственный набор ключей подписантов, не связанный с
on-chain полномочиями администратора/оператора, описанными в разделе «Модель безопасности» выше.
ADMIN_PRIVATE_KEY обязателен для каждого сервиса оператора и оплачивает комиссии
за транзакции; отдельный, необязательный OPERATOR_PRIVATE_KEY предоставляет on-chain
подпись Operator для ReleaseFunds и ResetSmtRoot и использует значение
ADMIN_PRIVATE_KEY, если не задан. Никогда не помещайте ключ администратора экземпляра
на уровне протокола (используемый для CreateInstance / AddOperator / SetNewAdmin)
ни в одну из этих переменных и не раскрывайте его во время выполнения; держите этот ключ
в холодном хранилище и офлайн.
ReleaseFunds и ResetSmtRoot требуют двух on-chain подписей: плательщика комиссии
(из ADMIN_PRIVATE_KEY) и авторизующей стороны PDA Operator (из
OPERATOR_PRIVATE_KEY, или ADMIN_PRIVATE_KEY, если первый не задан). В devnet-инструкции
этого руководства сгенерированный keypair оператора помещается в
ADMIN_PRIVATE_KEY, а OPERATOR_PRIVATE_KEY остаётся незаданным, так что один и тот же keypair
выполняет обе роли подписанта. Обращайтесь с ключом, находящимся в ADMIN_PRIVATE_KEY,
с теми же мерами предосторожности, что и с приватным ключом горячего кошелька:
- Храните его только в файле
.env, добавленном в gitignore, никогда — в.env.devnetили в каком-либо зафиксированном конфиге - Для продакшен-развёртываний рассмотрите использование менеджера секретов (AWS Secrets Manager, HashiCorp Vault) вместо переменной окружения в открытом виде
- keypair администратора экземпляра на уровне протокола (используемый для вызова
AddOperator/SetNewAdmin) следует держать в холодном хранилище; он нужен только при настройке экземпляра и провижнинге операторов, но не во время выполнения
SetNewAdmin передаёт права администратора необратимо в одной транзакции:
текущий администратор не имеет пути к восстановлению без содействия нового администратора.
Не вызывайте эту инструкцию, не проверив целевой адрес.
Следующие шаги
Is this page helpful?