Перейти к содержимому

Запуск Control Plane

Эта страница ведёт от звезды с ролью CP к Control Plane, который находят узлы и клиенты: запустить star, опубликовать эндпоинт и ключ фронта и проверить, что всё работает.

Роль CP не выдаётся при регистрации: каждая новая звезда — Node. На стадии теста её выдаёт основатель вручную, а дальше звезда будет получать её, внеся стейк. См. как стать Control Plane.

Эта страница исходит из того, что у вашей звезды роль уже есть.

Всё остальное ничьего согласия не требует: список эндпоинтов открыт, клиенты сами опрашивают его, а чтобы войти в федерацию, разрешение остальных участников не нужно.

  • То же, что для узла: Linux-сервер, Docker и кошелёк на одну транзакцию.
  • Публичное DNS-имя и терминация TLS перед портом gRPC.
  • ETH на адресе подписанта Control Plane. В отличие от узла, Control Plane отправляет транзакции: публикует отработанное время и ретранслирует операции без газа, и за то и другое платит горячий подписант.

Ровно как для узла — см. запуск узла. На выходе у вас starId с ролью Node.

Как только роль появилась, программа переключается сама: читает роль из блокчейна и поднимает gRPC на 0.0.0.0:9090. Перезапуск не нужен.

Скопируйте адрес подписанта со страницы звезды в Stargate и отправьте на него ETH — но немного. Подписант — горячий ключ на сервере, и всё, что на нём лежит, пропадёт, если сервер взломают.

Транзакция в Arbitrum стоит доли цента. Отправка пачки интервалов в блокчейн тратит примерно от 50 000 до 1 000 000 газа, и при обычной цене газа около 0,02 gwei это не больше 0,00002 ETH. Таких транзакций Control Plane отправляет несколько в день, плюс операции без газа, которые он ретранслирует за клиентов. 0,005 ETH хватает примерно на месяц, даже если все пачки тяжёлые, а обычно дольше. Пополняйте, когда заканчивается, а не держите большой баланс.

Следите за этим балансом: когда он дойдёт до нуля, Control Plane перестанет публиковать отработанное время, и его узлы перестанут зарабатывать.

Почти все значения по умолчанию подходят. Внимания требует одно:

environment:
- BLOCKCHAIN_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
- CHAIN_ID=421614
- REVERSE_PROXY=cp.example.com:443

REVERSE_PROXY — публичный адрес, который этот Control Plane сообщает соседям по федерации, когда представляется им; в блокчейн он не записывается. Эндпоинт в блокчейне публикует владелец отдельно, на шаге 5. Укажите в обоих местах один и тот же адрес.

Адреса контрактов не настраиваются. При запуске программа читает их все из SingulaProtocol, директории сети, так что передеплоенный контракт доходит до каждого Control Plane через одну запись в блокчейне. Переменная адреса вроде SUBSCRIBER_REGISTRY_ADDRESS, оставшаяся в compose-файле, игнорируется, если директория говорит иначе, и в лог пишется предупреждение. Если директорию прочитать не удалось, программа не запускается: работать на угаданных адресах было бы хуже. SINGULA_PROTOCOL_ADDRESS указывает на другую директорию, а off отключает её для локальной сети — тогда используются переменные адресов.

Стоит знать:

ПеременнаяПо умолчаниюЧто делает
GRPC_ADDRESS0.0.0.0:9090Где слушают все четыре сервиса
ATTESTATION_PUSH_INTERVAL4чКак часто отработанное время уходит в блокчейн
ACCESS_SWEEP_INTERVAL1чКак часто этот Control Plane убирает истёкших подписчиков из множества — в блокчейне и за свой газ
CP_FRONT_ENABLEDtrueФронт
CP_FRONT_PORT8443Где слушает фронт; клиенты приходят на 443 через прокси
REALITY_KEY_VERSION1Если поднять, ключ фронта сменится
BADGER_DIR/var/lib/star/badgerЛокальное хранилище
METRICS_HTTP_PORT9101Порт /status (JSON) и /metrics (Prometheus); 0 выключает
METRICS_BIND127.0.0.1Где он слушает. Расширяйте только за файрволом или паролем

Правила сети здесь не настраиваются: сколько живут учётные данные, сколько сессия должна передать, чтобы время засчиталось, и ограничения анонимного уровня. Каждый Control Plane читает их из директории. См. ограничения.

CPEndpointRegistry.setEndpoint(starId, host, port) вызывает владелец, и только пока у звезды роль CP.

В Stargate на карточке Control Plane для этого две кнопки: Set Endpoint, которая принимает host или host:port (без порта сохранится 0, то есть «порт протокола по умолчанию»), и Clear Endpoint.

Когда транзакция пройдёт, вы появитесь в getActiveEndpoints(), и узлы с клиентами подхватят вас при следующем опросе.

Каждый Control Plane держит фронт: вход по REALITY, который ведёт к его собственному gRPC и больше никуда. Он нужен клиентам из сетей, где эндпоинт заблокирован. Параметры фронта задаёт протокол, поэтому любой клиент доберётся до фронта любого Control Plane с тем, что у него уже есть. Свой у вас только ключ.

Ключ выводится из ключа подписанта и REALITY_KEY_VERSION. Ничего нового хранить не нужно, и при переезде на другую машину ключ сохраняется. Control Plane печатает его при каждом запуске:

REALITY public key (version 1): Vfv6lG2gjLQSbkLJdcH72Eo7zW2LrNRhAyrvU_YPJGQ

Опубликуйте его действием REALITY key на карточке Control Plane в Stargate или вызовом CPEndpointRegistry.setRealityKey(starId, key) — это 32 байта, в которые декодируется текст base64url. Клиенты читают ключ оттуда. Если подняли версию, опубликуйте новый ключ.

Фронт слушает на CP_FRONT_PORT. Клиенты приходят на порт 443 с именем сервера account.adobe.com, поэтому направьте это имя на фронт, пропуская TLS нетронутым: REALITY должен сам увидеть ClientHello. С Traefik:

labels:
- traefik.tcp.routers.cp-front.rule=HostSNI(`account.adobe.com`)
- traefik.tcp.routers.cp-front.entrypoints=websecure
- traefik.tcp.routers.cp-front.tls.passthrough=true
- traefik.tcp.routers.cp-front.service=cp-front
- traefik.tcp.services.cp-front.loadbalancer.server.port=8443

Этого имени нет среди пресетов узла, поэтому прокси, который делит 443 между Control Plane и узлом, разводит их по имени без конфликтов.

  • grpcurl <host>:<port> singula.admin.v1.AdminService/GetHealth отвечает SERVING.
  • Вы есть в getActiveEndpoints() в блокчейне.
  • В Stargate появляются живые сессии и подключённые узлы.
  • Через несколько часов работы с трафиком в EpochAttestations появляются события Pushed от вашего подписанта.

Программа говорит на обычном gRPC, а TLS завершается перед ней. Прокси общается с ней по HTTP/2 без TLS — это и есть h2c.

cp.example.com {
reverse_proxy h2c://127.0.0.1:9090
}
Traefik
services:
traefik:
image: traefik:v3.1
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.websecure.address=:443
- --entrypoints.web.address=:80
- --certificatesresolvers.le.acme.tlschallenge=true
- --certificatesresolvers.le.acme.email=ops@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
ports: ["443:443", "80:80"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
star:
image: singulanetwork/star:alpha
container_name: singula-cp
restart: unless-stopped
env_file: .env
labels:
- traefik.enable=true
- traefik.http.routers.cp.rule=Host(`cp.example.com`)
- traefik.http.routers.cp.entrypoints=websecure
- traefik.http.routers.cp.tls.certresolver=le
- traefik.http.services.cp.loadbalancer.server.port=9090
- traefik.http.services.cp.loadbalancer.server.scheme=h2c
HAProxy
services:
star:
image: singulanetwork/star:alpha
container_name: singula-cp
restart: unless-stopped
env_file: .env
expose: ["9090"]
networks: [edge]
haproxy:
image: haproxy:2.9-alpine
restart: unless-stopped
depends_on: [star]
ports: ["443:443"]
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- ./certs/cp.pem:/etc/ssl/cp.pem:ro # cat fullchain.pem privkey.pem > cp.pem
networks: [edge]
networks:
edge: {}
# haproxy.cfg
# global
# log stdout format raw local0
# defaults
# mode http
# timeout connect 5s
# timeout client 1h
# timeout server 1h
# frontend grpc_in
# bind *:443 ssl crt /etc/ssl/cp.pem alpn h2
# default_backend grpc_cp
# backend grpc_cp
# server cp star:9090 proto h2

Таймауты делайте длинными: потоки клиентов держатся часами.

Control Plane рассказывает о себе по тем же двум адресам, что и узел: /status отдаёт JSON, /metrics — формат Prometheus, оба на METRICS_HTTP_PORT.

Окно терминала
curl -s localhost:9101/status | jq

В ответе есть эпоха, сколько узлов подключено и сколько ждут регистрации, сколько открыто клиентских сессий, сколько интервалов ждут отправки и сколько прошло с последней записи в блокчейн, а ещё время работы, процессор и память. Блок info называет адрес подписанта и starId.

Стоит поставить отдельные оповещения:

  • singula_cp_attestation_pushes_total равен нулю. Ничего из того, что обслужил этот Control Plane, ни разу не было оплачено.
  • Баланс ETH у подписанта. На нуле публикация останавливается, и ваши узлы перестают зарабатывать.
  • Место на диске под BadgerDB.
  • Нет событий Pushed в EpochAttestations, хотя трафик идёт: интервалы отбрасываются, а не отправляются.
  • Строки relay error в логе — см. если что-то не работает.