SHiNE-server/Dev_Docs/Blockchain/sync-between-servers.md

12 KiB
Raw Blame History

Синхронизация блоков и DM между серверами SHiNE

Документ описывает архитектуру и протокол синхронизации данных между партнёрскими серверами SHiNE.

1. Зачем нужна синхронизация

Пользователи SHiNE могут быть «приписаны» к разным серверам. Когда пользователь A (на сервере X) пишет пользователю B (на сервере Y):

  1. Сервер X принимает сообщение;
  2. Сервер X должен переслать DM-блок серверу Y;
  3. Сервер Y сохраняет блок и доставляет в активные сессии пользователя B.

Аналогично, блоки пользовательского блокчейна (записи AddBlock) должны синхронизироваться, чтобы любой партнёрский сервер мог отдать полную историю пользователя.

2. Список серверов синхронизации (sync_servers)

Каждый сервер регистрирует в своей Solana PDA список sync_servers — логины SHiNE-аккаунтов партнёрских серверов, с которыми он синхронизируется.

  • Список хранится в блоке ServerProfileBlock внутри user_pda сервера.
  • Адрес каждого партнёрского сервера читается из его PDA на Solana.
  • Синхронизация двусторонняя: оба сервера должны иметь друг друга в sync_servers.

3. Что синхронизируется

3.1 Личные сообщения (DM)

  • Все DM-блоки форматов типов 1/2 (текст) и 3/4 (read-receipt).
  • Сервер-отправитель: при получении пары блоков от клиента перенаправляет их серверу получателя.
  • Сервер-получатель: сохраняет блоки в signed_messages_v2, доставляет в активные сессии.
  • Дедупликация по уникальному message_key = from|to|timeMs|nonce|type.

3.2 Блоки пользовательского блокчейна

  • Все блоки AddBlock пользователей, зарегистрированных на сервере или синхронизирующихся через него.
  • Синхронизируются в обе стороны между всеми партнёрами из sync_servers.
  • Порядок блоков сохраняется (по глобальному номеру блока и хэшу).
  • Дедупликация по глобальному номеру блока и хэшу.

4. Текущая реализованная схема

На текущем этапе сервер уже умеет базовую межсерверную синхронизацию пользовательских блокчейнов.

4.1 Что уже сделано

  1. При старте сервер читает свой server.SHiNE.login.
  2. По этому логину он загружает из Solana свою server PDA.
  3. Из неё вытаскивает список sync_servers.
  4. Для каждого логина партнёра сервер читает его PDA и сохраняет локально:
    • login
    • server_address
  5. После этого:
    • новые локальные AddBlock рассылаются партнёрам в фоне;
    • при старте запускается periodic sync;
    • periodic sync повторяется каждые 12 часов после старта.

4.2 Какие server-to-server API уже используются

  • ListBlockchainHeads — список heads всех локальных цепочек партнёра;
  • GetBlockchainBlock — чтение одного конкретного блока партнёра;
  • GetSyncUserProfile — минимальный профиль пользователя для локального создания solana_users + blockchain_state без обращения в Solana RPC.

4.3 Как сейчас работает periodic sync

Для каждого сервера из локальной таблицы sync_servers:

  1. запрашивается ListBlockchainHeads;
  2. для каждой удалённой цепочки сравниваются:
    • lastBlockNumber
    • lastBlockHash
    • локальное состояние;
  3. если локальная цепочка слабее, сервер по одному блоку вызывает GetBlockchainBlock;
  4. каждый скачанный блок локально применяется через существующий AddBlock;
  5. если у сервера ещё нет локальной записи пользователя/цепочки, перед этим подготавливается локальный solana_users + blockchain_state.

4.4 Зачем понадобился GetSyncUserProfile

Изначально подготовка локальной цепочки делалась через Solana:

  • из blockchainName извлекался login;
  • сервер вызывал import пользователя из Solana PDA;
  • по данным PDA локально создавались solana_users + blockchain_state.

На практике это упёрлось в ограничение внешнего Solana RPC: при чистом старте и массовой подтяжке чужих цепочек сервер мог получать HTTP 429.

Поэтому добавлен отдельный обходной режим:

  • настройка sync.importUserProfileFromPartner.enabled=true
  • в этом режиме сервер не ходит в Solana RPC для создания локальной цепочки во время sync;
  • вместо этого он запрашивает у сервера-партнёра GetSyncUserProfile и создаёт локальную запись по данным партнёра.

Это временная практическая заплатка, чтобы clean-start sync не зависел от rate limit внешнего Solana endpoint.

4.5 Что делает настройка sync.importUserProfileFromPartner.enabled

  • false — стандартный режим, подготовка локального пользователя идёт через Solana PDA;
  • true — sync-режим обхода Solana, локальный пользователь создаётся по server-to-server GetSyncUserProfile.

Настройка влияет именно на этап подготовки отсутствующей локальной цепочки во время periodic sync.

5. Целевой протокол следующего этапа

5.1 Межсерверное соединение

  • Серверы устанавливают постоянное WebSocket-соединение друг с другом.
  • Адрес партнёра определяется по server_address из его Solana PDA.
  • Аутентификация: подпись Ed25519 корневым ключом сервера (root_key из PDA).
  • При разрыве — переподключение с экспоненциальным backoff.

5.2 Доставка новых данных (push)

  • При получении нового блока или DM сервер немедленно пушит его всем подключённым партнёрам.
  • Партнёр подтверждает приём (ACK). Без ACK — повтор с backoff.

5.3 Начальная синхронизация (backfill)

  • При первом подключении к партнёру серверы обмениваются «курсорами» состояния: последний глобальный номер блока, последний известный DM-ключ.
  • Сервер с более полной историей досылает недостающее партнёру.

5.4 Разрешение конфликтов

  • Блоки пользовательского блокчейна: порядок определяется глобальным номером блока. Конфликтующие ветки (fork) разрешаются по правилам AddBlock (см. Dev_Docs/Blockchain/README.md).
  • DM: конфликтов нет, message_key уникален.

6. Маршрутизация DM между серверами

При отправке DM от пользователя A к пользователю B:

  1. Клиент A отправляет пару блоков на свой сервер X.
  2. Сервер X определяет, на каком сервере зарегистрирован пользователь B.
    • Сначала проверяет локально (если B зарегистрирован на X).
    • Иначе читает PDA пользователя B из Solana и смотрит access_servers.
    • Выбирает первый доступный сервер из access_servers и перенаправляет туда DM.
  3. Сервер Y (из access_servers B) сохраняет и доставляет блоки.

Кэш адресов серверов: обновляется раз в сессию (при ошибке соединения).

7. Безопасность

  • Все блоки подписаны ключами пользователя на клиенте — сервер не может подделать содержимое.
  • Серверы не расшифровывают DM-контент (шифрование — задача следующего этапа).
  • При синхронизации каждый блок проходит валидацию подписи на принимающем сервере.

8. Статус реализации

Компонент Статус
Регистрация серверной PDA в Solana Реализовано
Чтение sync_servers из PDA Реализовано
Локальная таблица sync_servers Реализовано
Публичный ListBlockchainHeads Реализовано
Публичный GetBlockchainBlock Реализовано
Публичный GetSyncUserProfile Реализовано
Плановый blockchain sync при старте + каждые 12 часов Реализовано
Обход Solana RPC через sync.importUserProfileFromPartner.enabled Реализовано
Межсерверный постоянный WebSocket-канал Нужна реализация
Push новых DM партнёрам Нужна реализация
Push блоков блокчейна партнёрам Реализована базовая one-shot версия
Periodic backfill отсутствующего хвоста Реализовано
Разрешение рассинхрона / divergence Нужна реализация
Маршрутизация DM через access_servers Нужна реализация (заглушка)

Текущая версия сервера уже умеет базовую синхронизацию блокчейнов между партнёрами. Не реализованы ещё DM-sync, постоянные server-to-server соединения и автоматическое исправление рассинхрона цепочек.