2 changed files with 89 additions and 0 deletions
@ -0,0 +1,81 @@ |
|||||||
|
Аватарка CHAT-группы |
||||||
|
=================== |
||||||
|
|
||||||
|
Модуль src/chat/group_avatar.c. Картинка — бинарный BLOB до 16384 байт в общей |
||||||
|
SQLite каждого участника: channels.avatar_data/version/hash/signature. |
||||||
|
Storage-сервер, каталоги файлов и пользовательские квоты не используются. |
||||||
|
При старте добавляются отсутствующие колонки одной транзакцией, записи/ключи |
||||||
|
каналов сохраняются. channel_put использует UPSERT и сохраняет аватарку, |
||||||
|
UI-состояние и локальные приватные ключи; смена публичных ключей существующего |
||||||
|
channel_id запрещена. Удаление группы удаляет и её аватарку. |
||||||
|
|
||||||
|
Право публикации — приватный Ed25519 ключ группы плюс собственное действующее |
||||||
|
членство. creator UID/admin-флаг не заменяют ключ. group_avatar_set подписывает: |
||||||
|
ASCII "group-avatar-v1" без NUL, group:u64 LE, version:u64 LE, size:u16 LE, bytes. |
||||||
|
Версия — max(NTP microseconds/1000, previous+1), ограничена INT64_MAX. |
||||||
|
SHA256 считается по картинке; при одинаковой версии побеждает больший SHA256. |
||||||
|
Обе конкурирующие записи должны иметь правильную подпись группы; конфликт — WARN. |
||||||
|
Публикация одинаковой картинки не создаёт новую версию. Удаление — подписанная |
||||||
|
запись с size=0 и новой версией; подпись/версия сохраняются, старый пир не может |
||||||
|
восстановить удалённую картинку. Чтение проверяет сохранённый хеш и подпись. |
||||||
|
|
||||||
|
Транспорт |
||||||
|
--------- |
||||||
|
|
||||||
|
ETCP_RT_ID_GROUP_AVATAR=0x3A, router wire version=1. Отправитель/группа берутся |
||||||
|
из проверенного router-заголовка. Все пакеты подписаны ключом узла и зашифрованы |
||||||
|
end-to-end; в DATA дополнительно передаётся подпись группы над всей картинкой. |
||||||
|
Обмен только между READY-соседями CHAT-группы с действующим подписанным членством. |
||||||
|
Любой участник пересылает сохранённую запись, владелец ключа может быть offline. |
||||||
|
|
||||||
|
Все числа протокола LE. Общая часть, 44 байта: |
||||||
|
version:u8=1, command:u8, avatar_version:u64, total_size:u16, SHA256:32. |
||||||
|
STATE=1 объявляет локальную версию/хеш; начальное отсутствие — version=0, hash=0. |
||||||
|
GET=2 запрашивает актуальную запись. Объявление не меняет БД без полной DATA. |
||||||
|
DATA=3 добавляет group_signature:64, offset:u16, block_size:u16, bytes. |
||||||
|
DATA header=112 байт. Картинка разбивается на блоки по 4096 байт; последний |
||||||
|
короче. Полная картинка 16 КиБ — ровно четыре DATA. Удаление — один DATA без bytes. |
||||||
|
|
||||||
|
Порядок блоков гарантирует router. Сборка проверяет offset, постоянные version, |
||||||
|
total_size/hash/signature и допустимые размеры. До полной сборки прежняя картинка |
||||||
|
остаётся в БД. После SHA256/Ed25519 вся запись заменяется одним UPDATE, затем |
||||||
|
CHANNEL_UPDATED доставляет изменение GUI и STATE распространяется соседям. |
||||||
|
Промежуточные блоки не публикуются, ACK/retransmit каждого блока — только router. |
||||||
|
|
||||||
|
Отправка заполняет router до нижнего порога send_q count<=0 и ждёт send_ready; |
||||||
|
планировщик отправляет максимум четыре пакета за callback. Snapshot отправителя |
||||||
|
не меняется при новом локальном обновлении. Максимум 16 tx и отдельно 16 rx |
||||||
|
сборок на экземпляр, каждая до 16 КиБ. receive_ready при заполнении rx сохраняет |
||||||
|
пакет в router recv_q без продвижения ACK; освобождение слота возобновляет приём. |
||||||
|
Несовпадение эпох/закрытие router отменяет незавершённую сборку и начинает сверку. |
||||||
|
Эпоха нового контекста берётся из текущей READY-сессии, не сбрасывает первый приём. |
||||||
|
Обнаружение соседей/таймауты — таймер 1 с; STATE также раз в 30 с. Нет прогресса |
||||||
|
15 с — отмена незавершённого snapshot, повтор запроса/сверки. Остановка сервиса |
||||||
|
отменяет таймеры/waiters/call_soon до удаления группы/ядра. |
||||||
|
|
||||||
|
ChatGUI |
||||||
|
------- |
||||||
|
|
||||||
|
Настройки группы: Upload group avatar/Remove group avatar, предпросмотр и статус. |
||||||
|
Кнопки доступны только при локальном приватном ключе. Применение сразу, отдельно |
||||||
|
от Save локальных настроек. Worker обрезает центральный квадрат, создаёт JPEG |
||||||
|
128×128, снижает качество/размер до лимита 16 КиБ. Вход ограничен 16 мегапикселями. |
||||||
|
GUI отправляет байты в uasync; ядро повторно проверяет размер/членство/ключ. |
||||||
|
|
||||||
|
SQLite — постоянный кеш. GroupAvatarManager хранит QImage до 32 МиБ, ключ кеша: |
||||||
|
group/version/hash. Декодирование в отдельном worker, проверка SHA256, JPEG и |
||||||
|
квадратных размеров до 128×128. Поздний результат старой версии не заменяет |
||||||
|
новую. Готовая картинка обновляет только строку группы; без аватарки — инициалы. |
||||||
|
Приватные ключи/байты картинки не выводятся в диагностику. |
||||||
|
|
||||||
|
Проверки |
||||||
|
-------- |
||||||
|
|
||||||
|
test_group_avatar: настоящие два ядра/UDP/router, 16 КиБ четырьмя блоками, |
||||||
|
атомарность сборки, hash/signature/cross-group отказ, приостановка receive, |
||||||
|
перезапуск канала, stale/conflict, редактор с ключом вне creator UID, подписанное |
||||||
|
удаление, повторное описание группы и restart БД. Schema failure откатывает |
||||||
|
частичные ALTER, сохраняет прежние данные/ключи, повторное добавление идемпотентно. |
||||||
|
test_group_avatars (Qt): нормализация, лимит/отображение, кеш, права кнопок и отказ |
||||||
|
ядра без ключа, удаление. chat=debug показывает состояния и каждый 4КиБ блок; |
||||||
|
chat=info — публикации и жизненный цикл. Ошибки содержат group/peer/version/offset. |
||||||
Loading…
Reference in new issue