Files
utun2/src/chat/db_sync.h
T

141 lines
9.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* db_sync — репликация подписанных записей SQLite по прямым ETCP-соединениям.
* Каждый DB_SYNC_INSTANCE связывает таблицу с group_id. Различия находятся по
* общему Merkle-дереву; изменения рассылаются PUSH. Постоянный ключ — message_id,
* порядок отображения — (timestamp, message_id). Подпись защищает весь конверт и data.
* Ядро создаёт модуль, чат включает его и регистрирует таблицы каналов.
* Обычно используется общая SQLite ядра; remove отключает таблицу от обмена,
* сохраняя данные. Все вызовы и callbacks выполняются в потоке uasync. */
#ifndef DB_SYNC_H
#define DB_SYNC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
struct UTUN_INSTANCE;
struct DB_SYNC_INSTANCE;
// etcp service ID: ETCP_ID_DB_SYNC (etcp_bind, см. etcp_api.h)
// Message types
#define DB_MSG_PUSH 0x05
#define DB_MSG_ACK_PUSH 0x06
/* Максимальная длина подписанного тела. Транспортный буфер также учитывает identity и подпись. */
#define DB_SYNC_MESSAGE_MAX 4000
// PUSH/MESSAGES: [id:32][ts:8BE][author:8BE][nonce:16][ver:8BE][edited_at:8BE][dlen:4BE][data][sig:64].
// ACK_PUSH: [id:32][ver:8BE]. Подтверждает конкретную версию; повторы идемпотентны.
struct db_message_revision {
uint8_t nonce[16]; /* Случайность создания, неизменна во всех версиях. */
uint64_t ver; /* 1..INT64_MAX; принимается только более высокая версия. */
uint64_t edited_at; /* Unix ms последней правки; 0 для ver=1. */
};
/* Подписать канонический конверт, включая канал; 0/-1. Не обращается к БД, nonce задаёт вызывающий. */
int db_sync_sign_message(uint64_t group, uint64_t timestamp, uint64_t author,
const struct db_message_revision* revision, const char* data, size_t len,
const uint8_t private_key[32], uint8_t signature[64]);
// Интервалы служебных таймеров в секундах.
#define DB_SYNC_PEER_CHECK_INTERVAL 5
#define DB_SYNC_TTL_INTERVAL 3600
// Record flags
#define DB_REC_FLAG_WAS_SENT 0x01
// Ed25519 signature size
#define DB_SIG_SIZE 64
// Создать контекст и учесть db_sync_enabled из конфига. 0/-1.
int db_sync_init(struct UTUN_INSTANCE* inst);
// Включить ранее созданный модуль независимо от флага конфига. Повтор допустим; 0/-1.
int db_sync_enable(struct UTUN_INSTANCE* inst);
// Подключить MESSAGES после создания общего Merkle-движка; запланировать проверку своей истории. 0/-1.
int db_sync_register_model(struct UTUN_INSTANCE* inst);
// Отменить таймеры/обмен, снять обработчики; общую SQLite не закрывает.
void db_sync_destroy(struct UTUN_INSTANCE* inst);
// Зарегистрировать таблицу существующей группы без сетевого обмена; первоначально gated. NULL при ошибке.
struct DB_SYNC_INSTANCE* db_sync_instance_add(struct UTUN_INSTANCE* inst, const char* table_name, uint64_t group_id);
// Освободить экземпляр синхронизации; сама таблица остаётся в БД.
void db_sync_instance_remove(struct DB_SYNC_INSTANCE* si);
// Заимствованный указатель по group_id; действителен до remove/destroy, NULL если нет.
struct DB_SYNC_INSTANCE* db_sync_instance_find(struct UTUN_INSTANCE* inst, uint64_t group_id);
// group_id инстанса (для chat-таблиц равен числовому channel_id). Возвращает 0 при si==NULL.
uint64_t db_sync_instance_group_id(struct DB_SYNC_INSTANCE* si);
// Data operations (per-instance)
// db_sync_insert_signed: sig MUST be non-NULL, 64 bytes.
// sig создаётся db_sync_sign_message; revision обязателен, автор — локальный узел.
// ts — call db_sync_next_timestamp(si) before signing to reserve monotonically increasing timestamp.
// Возвращает 0 (вставлено), 1 (повтор), -1 (операционная ошибка/ключ ещё неизвестен), -2 (неверные данные).
// Корректная сохранённая копия переживает неверный входящий повтор. Удаляем только строку,
// чьи собственные байты имеют битый формат либо подпись не совпала при завершённой проверке.
int db_sync_insert_signed(struct DB_SYNC_INSTANCE* si, const char* json_data, size_t len,
const uint8_t* sig, size_t sig_len, uint64_t ts,
const char* local_attrs, const struct db_message_revision* revision);
uint32_t db_sync_count(struct DB_SYNC_INSTANCE* si);
// Последний известный/зарезервированный timestamp в миллисекундах Unix; 0 для NULL.
uint64_t db_sync_get_last_timestamp(struct DB_SYNC_INSTANCE* si);
// Зарезервировать следующий timestamp перед подписью; растёт даже в пределах одной ms.
uint64_t db_sync_next_timestamp(struct DB_SYNC_INSTANCE* si);
// Обход по (timestamp, message_id), начиная с offset; limit=0 — без ограничения.
// Возвращает число записей или -1. Данные callback заимствованы на время вызова.
typedef void (*db_sync_select_cb)(void* arg, uint64_t id, uint64_t timestamp,
const char* data, size_t data_len, uint64_t author,
const uint8_t* author_sig, size_t sig_len,
int delivered_peers, const char* delivery_chain);
int db_sync_select(struct DB_SYNC_INSTANCE* si, uint32_t offset, uint32_t limit,
db_sync_select_cb cb, void* arg);
// Callback после вставки/обновления. updated=1 заменяет строку без нового unread/звука.
// author_node_id — исходный автор записи, не обязательно переславший её пир.
// initial_sync = 1 для страницы MESSAGES, 0 для live-PUSH и локальной вставки.
// Вызов после commit: для страниц отложен до выхода из обработчика Merkle.
typedef void (*db_sync_insert_cb)(struct DB_SYNC_INSTANCE* si,
const uint8_t message_id[32],
uint64_t record_timestamp,
const char* json_data, size_t len,
uint64_t author_node_id, int initial_sync, int updated, void* arg);
void db_sync_set_insert_cb(struct DB_SYNC_INSTANCE* si, db_sync_insert_cb cb, void* arg);
// Диагностика: синхронно перепроверить/очистить сохранённые записи и восстановить индекс.
// 0 — готов, 1 — ожидается ключ автора, -1 — операционная ошибка. В обычной работе обход порционный.
int db_sync_verify(struct DB_SYNC_INSTANCE* si);
// Прочитать полный корень: 0 — успех, 1 — preflight ещё не завершён, -1 — ошибка. root[32] обязателен.
int db_sync_read_root(struct DB_SYNC_INSTANCE* si, uint8_t root[32]);
// Пауза обмена с выбранным пиром для диагностических сценариев; reinitiate снимает паузу.
void db_sync_peer_set_paused(struct DB_SYNC_INSTANCE* si, uint64_t node_id, int paused);
// Per-(chat,peer) «play sound on message» flag: sound_off + sleeping peer → throttled.
void db_sync_set_peer_sound(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t peer_node_id, int sound_on);
// До успешного member_sync блокирует входящий и исходящий обмен. После снятия
// блокировки запускает историю только через READY-пиров этой группы.
void db_sync_instance_set_gated(struct DB_SYNC_INSTANCE* si, int gated);
// Force re-initiate sync to a specific peer (for testing)
void db_sync_reinitiate(struct DB_SYNC_INSTANCE* si, uint64_t node_id);
// После sleep/sound переоценить работу истории; после изменения MEMBERS также повторить ожидание общих ключей.
void db_sync_peer_policy_changed(struct UTUN_INSTANCE* inst, uint64_t peer);
void db_sync_members_changed(struct UTUN_INSTANCE* inst, const char* ns);
// Глобальная подписка на подтверждённый полный Merkle-корень истории; callback может удалить подписку/канал.
typedef void (*db_sync_done_fn)(struct DB_SYNC_INSTANCE* si, uint64_t peer_node_id, void* arg);
// 0 — подписан/та же пара уже подписана,-1 — отказ без изменения списка.
int db_sync_add_done_cbk(struct UTUN_INSTANCE* inst, db_sync_done_fn fn, void* arg);
void db_sync_remove_done_cbk(struct UTUN_INSTANCE* inst, db_sync_done_fn fn, void* arg);
#ifdef __cplusplus
}
#endif
#endif // DB_SYNC_H