SHA256
Добавить sync-профиль пользователя и обход Solana RPC
This commit is contained in:
@@ -2,11 +2,12 @@
|
||||
|
||||
Этот файл описывает технические WebSocket-запросы, которые нужны для служебной работы клиента с сервером. Часть операций доступна без авторизации, часть требует успешной авторизованной сессии.
|
||||
|
||||
Сейчас здесь семь методов:
|
||||
Сейчас здесь восемь методов:
|
||||
|
||||
- `Ping` — keep-alive запрос для поддержания живого WebSocket-соединения;
|
||||
- `GetServerInfo` — запрос базовой публичной информации о сервере для выбора узла в децентрализованной сети;
|
||||
- `ListBlockchainHeads` — краткая сводка по всем локальным блокчейнам сервера для межсерверной синхронизации;
|
||||
- `GetSyncUserProfile` — межсерверный профиль пользователя для создания локальной цепочки без Solana RPC;
|
||||
- `GetCallIceConfig` — выдача STUN/TURN конфигурации для звонков;
|
||||
- `ClientErrorLog` — отправка клиентской ошибки в серверный лог;
|
||||
- `ClientDebugLog` — отправка клиентского debug-события в серверный буфер;
|
||||
@@ -17,6 +18,7 @@
|
||||
- `Ping` нужен для регулярной проверки, что соединение всё ещё живо;
|
||||
- `GetServerInfo` нужен до авторизации и до работы с данными, чтобы клиент понял, что сервер доступен, и показал пользователю краткую карточку этого узла.
|
||||
- `ListBlockchainHeads` нужен для сервер-сервер сверки: партнёр получает список heads по всем цепочкам, сравнивает его со своим состоянием и затем добирает недостающие блоки по диапазону.
|
||||
- `GetSyncUserProfile` нужен для server-to-server режима, когда принимающий сервер хочет создать у себя локальные `solana_users + blockchain_state` без прямого обращения в Solana. Это используется как временный обход ограничений внешнего Solana RPC.
|
||||
|
||||
Ниже сначала описаны назначение методов, затем точные форматы запросов и ответов.
|
||||
|
||||
@@ -197,7 +199,90 @@
|
||||
|
||||
---
|
||||
|
||||
## 4. `GetCallIceConfig`
|
||||
## 4. `GetSyncUserProfile`
|
||||
|
||||
### Назначение
|
||||
|
||||
Запрос минимального профиля пользователя для межсерверной синхронизации.
|
||||
|
||||
Нужен в сценарии, когда сервер во время periodic sync увидел чужой блокчейн, которого у него локально ещё нет. Вместо обращения в Solana PDA он может запросить у партнёра:
|
||||
|
||||
- `login`
|
||||
- `blockchainName`
|
||||
- `solanaKey`
|
||||
- `blockchainKey`
|
||||
- `clientKey`
|
||||
- `blockchainSizeLimitBytes`
|
||||
|
||||
После этого принимающий сервер может локально создать записи в `solana_users` и `blockchain_state`, а затем уже докачивать блоки через `GetBlockchainBlock`.
|
||||
|
||||
Этот запрос доступен без авторизации и предназначен именно для server-to-server sync.
|
||||
|
||||
### Запрос
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "GetSyncUserProfile",
|
||||
"requestId": "sync-user-001",
|
||||
"payload": {
|
||||
"login": "alice"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ: пользователь не найден
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "GetSyncUserProfile",
|
||||
"requestId": "sync-user-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"exists": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Успешный ответ: пользователь найден
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "GetSyncUserProfile",
|
||||
"requestId": "sync-user-001",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"exists": true,
|
||||
"login": "alice",
|
||||
"blockchainName": "alice-001",
|
||||
"solanaKey": "BASE64_32",
|
||||
"blockchainKey": "BASE64_32",
|
||||
"clientKey": "BASE64_32",
|
||||
"blockchainSizeLimitBytes": 100000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Поля ответа
|
||||
|
||||
- `exists` — найден ли пользователь на сервере-партнёре.
|
||||
- `login` — канонический login из БД сервера-партнёра.
|
||||
- `blockchainName` — имя основной цепочки пользователя.
|
||||
- `solanaKey` — публичный ключ логина.
|
||||
- `blockchainKey` — публичный ключ блокчейна.
|
||||
- `clientKey` — публичный клиентский ключ, который в текущей модели используется при создании локальной записи.
|
||||
- `blockchainSizeLimitBytes` — лимит размера файла блокчейна, который будет записан в локальный `blockchain_state`.
|
||||
|
||||
### Специфические коды ошибок `GetSyncUserProfile`
|
||||
|
||||
- `400 / BAD_FIELDS` — пустой или некорректный `login`.
|
||||
- `404 / BLOCKCHAIN_STATE_NOT_FOUND` — пользователь найден, но на сервере-партнёре отсутствует `blockchain_state` для его цепочки.
|
||||
- При непредвиденной ошибке сервер вернёт общую ошибку из раздела `00`, обычно `500 / INTERNAL_ERROR`.
|
||||
|
||||
---
|
||||
|
||||
## 5. `GetCallIceConfig`
|
||||
|
||||
Доступно только после успешной авторизации.
|
||||
|
||||
@@ -247,7 +332,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 5. `ClientErrorLog`
|
||||
## 6. `ClientErrorLog`
|
||||
|
||||
### Запрос
|
||||
|
||||
@@ -294,7 +379,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 6. `ClientDebugLog`
|
||||
## 7. `ClientDebugLog`
|
||||
|
||||
### Запрос
|
||||
|
||||
@@ -332,7 +417,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 7. `CallDeliveryReport`
|
||||
## 8. `CallDeliveryReport`
|
||||
|
||||
### Запрос
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
| `Ping` | `05_Technical_Requests_API.md` | keep-alive |
|
||||
| `GetServerInfo` | `05_Technical_Requests_API.md` | публичная информация о сервере |
|
||||
| `ListBlockchainHeads` | `05_Technical_Requests_API.md` | список heads всех локальных блокчейнов |
|
||||
| `GetSyncUserProfile` | `05_Technical_Requests_API.md` | межсерверный профиль пользователя для синхронизации |
|
||||
| `GetCallIceConfig` | `05_Technical_Requests_API.md` | STUN/TURN конфигурация звонков |
|
||||
| `ClientErrorLog` | `05_Technical_Requests_API.md` | логирование клиентской ошибки |
|
||||
| `ClientDebugLog` | `05_Technical_Requests_API.md` | клиентский debug-лог |
|
||||
|
||||
@@ -39,33 +39,94 @@
|
||||
- Порядок блоков сохраняется (по глобальному номеру блока и хэшу).
|
||||
- Дедупликация по глобальному номеру блока и хэшу.
|
||||
|
||||
## 4. Протокол синхронизации (целевой, не реализован)
|
||||
## 4. Текущая реализованная схема
|
||||
|
||||
### 4.1 Межсерверное соединение
|
||||
На текущем этапе сервер уже умеет базовую межсерверную синхронизацию пользовательских блокчейнов.
|
||||
|
||||
### 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.
|
||||
|
||||
### 4.2 Доставка новых данных (push)
|
||||
### 5.2 Доставка новых данных (push)
|
||||
|
||||
- При получении нового блока или DM сервер немедленно пушит его всем подключённым партнёрам.
|
||||
- Партнёр подтверждает приём (ACK). Без ACK — повтор с backoff.
|
||||
|
||||
### 4.3 Начальная синхронизация (backfill)
|
||||
### 5.3 Начальная синхронизация (backfill)
|
||||
|
||||
- При первом подключении к партнёру серверы обмениваются «курсорами» состояния:
|
||||
последний глобальный номер блока, последний известный DM-ключ.
|
||||
- Сервер с более полной историей досылает недостающее партнёру.
|
||||
|
||||
### 4.4 Разрешение конфликтов
|
||||
### 5.4 Разрешение конфликтов
|
||||
|
||||
- Блоки пользовательского блокчейна: порядок определяется глобальным номером блока.
|
||||
Конфликтующие ветки (fork) разрешаются по правилам `AddBlock` (см. `Dev_Docs/Blockchain/README.md`).
|
||||
- DM: конфликтов нет, `message_key` уникален.
|
||||
|
||||
## 5. Маршрутизация DM между серверами
|
||||
## 6. Маршрутизация DM между серверами
|
||||
|
||||
При отправке DM от пользователя A к пользователю B:
|
||||
|
||||
@@ -78,23 +139,30 @@
|
||||
|
||||
Кэш адресов серверов: обновляется раз в сессию (при ошибке соединения).
|
||||
|
||||
## 6. Безопасность
|
||||
## 7. Безопасность
|
||||
|
||||
- Все блоки подписаны ключами пользователя на клиенте — сервер не может подделать содержимое.
|
||||
- Серверы не расшифровывают DM-контент (шифрование — задача следующего этапа).
|
||||
- При синхронизации каждый блок проходит валидацию подписи на принимающем сервере.
|
||||
|
||||
## 7. Статус реализации
|
||||
## 8. Статус реализации
|
||||
|
||||
| Компонент | Статус |
|
||||
|-----------|--------|
|
||||
| Регистрация серверной PDA в Solana | ✅ Реализовано |
|
||||
| Чтение `sync_servers` из PDA | ✅ Реализовано |
|
||||
| Межсерверный WebSocket-канал | Нужна реализация |
|
||||
| Локальная таблица `sync_servers` | ✅ Реализовано |
|
||||
| Публичный `ListBlockchainHeads` | ✅ Реализовано |
|
||||
| Публичный `GetBlockchainBlock` | ✅ Реализовано |
|
||||
| Публичный `GetSyncUserProfile` | ✅ Реализовано |
|
||||
| Плановый blockchain sync при старте + каждые 12 часов | ✅ Реализовано |
|
||||
| Обход Solana RPC через `sync.importUserProfileFromPartner.enabled` | ✅ Реализовано |
|
||||
| Межсерверный постоянный WebSocket-канал | Нужна реализация |
|
||||
| Push новых DM партнёрам | Нужна реализация |
|
||||
| Push блоков блокчейна партнёрам | ✅ Реализована базовая one-shot версия |
|
||||
| Backfill при первом подключении | Нужна реализация |
|
||||
| Periodic backfill отсутствующего хвоста | ✅ Реализовано |
|
||||
| Разрешение рассинхрона / divergence | Нужна реализация |
|
||||
| Маршрутизация DM через access_servers | Нужна реализация (заглушка) |
|
||||
|
||||
Текущая версия сервера работает без межсерверной синхронизации.
|
||||
Синхронизация — задача следующего этапа разработки.
|
||||
Текущая версия сервера уже умеет базовую синхронизацию блокчейнов между партнёрами.
|
||||
Не реализованы ещё DM-sync, постоянные server-to-server соединения и автоматическое исправление рассинхрона цепочек.
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
# Периодическая межсерверная синхронизация блокчейнов
|
||||
|
||||
- Краткое описание:
|
||||
- При старте сервер подтягивает `sync_servers` из server PDA и сохраняет адреса партнёров в локальную таблицу.
|
||||
- После успешного локального `AddBlock` работает фоновая one-shot отправка нового блока партнёрам.
|
||||
- Добавлен публичный `GetBlockchainBlock` для чтения одного блока.
|
||||
- Добавлен публичный `GetSyncUserProfile` для подготовки отсутствующей локальной цепочки без прямого Solana RPC.
|
||||
- Добавлен плановый sync блокчейнов при старте сервера и затем каждые `12` часов.
|
||||
- Синхронизация пока умеет только докачивать отсутствующий хвост цепочки.
|
||||
- Добавлена настройка `sync.importUserProfileFromPartner.enabled`, которая включает создание локального `solana_users + blockchain_state` по ответу сервера-партнёра вместо Solana PDA.
|
||||
- Случай рассинхрона цепочек пока не исправляется автоматически: он только логируется как не реализованный сценарий.
|
||||
|
||||
- Что именно проверять:
|
||||
- После старта сервера в логах появляется запуск периодического sync.
|
||||
- Сервер может запросить у партнёра `ListBlockchainHeads`.
|
||||
- Сервер может запросить у партнёра `GetSyncUserProfile`, если включён `sync.importUserProfileFromPartner.enabled=true`.
|
||||
- Сервер может запросить у партнёра `GetBlockchainBlock` и локально применить блок через существующий `AddBlock`.
|
||||
- На чистом тестовом сервере после удаления БД и файлов блокчейнов сервер сам подтягивает блоки при старте.
|
||||
- При включённом режиме обхода Solana сервер восстанавливает локальные цепочки без запросов в Solana RPC.
|
||||
- После первичного старта новые блоки продолжают догоняться без ручного вмешательства.
|
||||
- При рассинхроне цепочек в логах появляется явное сообщение, что reconciliation пока не реализован.
|
||||
|
||||
- Ожидаемый результат:
|
||||
- Чистый сервер после старта сам восстанавливает локальные цепочки от партнёра синхронизации.
|
||||
- В режиме обхода Solana чистый сервер не упирается в `Solana RPC 429` при создании локальных chain-state для уже существующих на партнёре пользователей.
|
||||
- Периодический sync не мешает обычной работе сервера и не ломает локальный `AddBlock`.
|
||||
- Нереализованный случай рассинхрона не приводит к падению сервера и явно отражается в логах.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user