SHA256
Что сделано: вычистили неиспользуемый user-settings sync/DM sync хвост, сохранили сборку, обновили bundle.sh так, чтобы gradle-wrapper.jar всегда попадал в архив. Проверено: compileJava и deploy на t2 (server + UI). Не проверяли: полные интеграционные сценарии, ручные UI-флоу и продовый деплой.
85 lines
4.5 KiB
Markdown
85 lines
4.5 KiB
Markdown
# Доставка личных сообщений
|
|
|
|
## 1. Инвариант
|
|
|
|
У каждого пользователя действует ровно один сервер доступа: первый элемент
|
|
access_servers[0] в его Solana PDA.
|
|
|
|
Остальные элементы старой PDA, если они существуют, не участвуют в
|
|
маршрутизации и не являются fallback. UI позволяет только заменить текущий
|
|
сервер другим.
|
|
|
|
Межсерверной репликации личной переписки между серверами одного пользователя
|
|
нет. При смене сервера история автоматически не переносится.
|
|
|
|
## 2. Подписанные копии
|
|
|
|
Формат SHiNE_DM не меняется. Для сообщения клиент создаёт:
|
|
|
|
- входящую копию для получателя;
|
|
- исходящую копию для отправителя.
|
|
|
|
Сервер отправителя принимает пару через SendMessagePair, атомарно сохраняет её
|
|
и создаёт изменяемое состояние доставки.
|
|
|
|
## 3. Основной маршрут
|
|
|
|
1. UI отправителя вызывает SendMessagePair на своём access-сервере.
|
|
2. Сервер сохраняет исходящую копию отправителя и входящую копию, необходимую
|
|
для доставки и повторных попыток.
|
|
3. Сервер читает единственный маршрут получателя из
|
|
user_access_servers_current.
|
|
4. Если это другой физический сервер, он вызывает внутреннюю операцию
|
|
ReceiveIncomingMessage и передаёт только входящую копию.
|
|
5. Сервер получателя проверяет пользовательскую подпись, идемпотентно сохраняет
|
|
входящую копию и уведомляет активные сессии.
|
|
6. Успешное сохранение означает delivered.
|
|
|
|
ReceiveIncomingMessage не является синхронизацией двух серверов одного
|
|
пользователя. Это основная доставка между серверами разных пользователей.
|
|
|
|
## 4. Повторные попытки
|
|
|
|
Попытки привязаны ко времени первоначального принятия:
|
|
|
|
- сразу;
|
|
- через 30 секунд;
|
|
- через 5 минут;
|
|
- через 25 минут;
|
|
- через 1 час.
|
|
|
|
Перед каждой попыткой заново читается первый актуальный access-сервер
|
|
получателя. После финальной неудачи состояние становится failed.
|
|
|
|
Второй сервер отправителя не опрашивается, полная пара ему не передаётся.
|
|
|
|
## 5. Состояния
|
|
|
|
- accepted — пара сохранена сервером отправителя;
|
|
- delivered — входящая копия сохранена единственным сервером получателя;
|
|
- failed — финальная попытка завершилась неудачей;
|
|
- read-receipt — отдельное подписанное DM-событие.
|
|
|
|
Подписанные DM-контейнеры остаются криптографическими фактами. Сетевое
|
|
deliveryState остаётся изменяемой серверной метаданной.
|
|
|
|
## 6. Удаления
|
|
|
|
DeleteMessage и DeleteConversation доставляют подписанный tombstone только на
|
|
единственные access-серверы обеих сторон. Повторное применение идемпотентно.
|
|
|
|
## 7. Удалённая репликация
|
|
|
|
После перехода на один сервер доступа удалены:
|
|
|
|
- ReceiveOutcomingMessage;
|
|
- DmSyncBatch;
|
|
- GetDmDeliveryStatus;
|
|
- dm_sync_outbox;
|
|
- dm_sync_peer_state;
|
|
- ACK-флаги synced;
|
|
- периодический DM-backfill между access-серверами.
|
|
|
|
Блокчейн-синхронизация через server-PDA sync_servers остаётся отдельным
|
|
механизмом и продолжает работать.
|