You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
7.7 KiB
137 lines
7.7 KiB
/* db_sync — репликация подписанных записей SQLite по прямым ETCP-соединениям. |
|
* Каждый DB_SYNC_INSTANCE связывает таблицу с group_id. Различия находятся по |
|
* цепочке хешей; новые записи рассылаются PUSH. Ключ и порядок записей: |
|
* (timestamp, author_signature). Подпись автора покрывает timestamp || 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_RT_ID_DB_SYNC (etcp_bind, см. etcp_api.h) |
|
|
|
// Message types |
|
#define DB_MSG_INIT_SYNC 0x01 |
|
#define DB_MSG_INIT_RESP 0x02 |
|
#define DB_MSG_SEND_DATA 0x04 |
|
#define DB_MSG_PUSH 0x05 |
|
#define DB_MSG_ACK_PUSH 0x06 |
|
#define DB_MSG_SYNC_DONE 0x07 |
|
#define DB_MSG_ERROR 0x08 |
|
#define DB_MSG_REQUEST_SYNC 0x09 |
|
|
|
// SEND_DATA want_from sentinel |
|
#define DB_WANT_FROM_NONE 0xFFFFFFFF // no request for peer's data |
|
|
|
// SEND_DATA wire: [from:4][count:2][vp:4][want_from:4][records...] |
|
// Record: [id:8][ts:8][node_id:8][dlen:4][data][sig_len:1=64][sig:64] |
|
|
|
// SYNC_DONE wire: [count:4][chain_hash8:8] |
|
|
|
// Error codes for DB_MSG_ERROR |
|
#define DB_ERR_NOT_FOUND 0x01 // instance not found |
|
#define DB_ERR_DISABLED 0x02 // instance exists but disabled |
|
#define DB_ERR_NOT_READY 0x03 // участники или групповая сессия ещё не готовы |
|
|
|
// Defaults |
|
#define DB_SYNC_DEFAULT_TTL 86400 |
|
#define DB_SYNC_DEFAULT_MAPSIZE (100UL * 1024 * 1024) |
|
#define DB_SYNC_PEER_CHECK_INTERVAL 5 |
|
#define DB_SYNC_SYNC_TIMEOUT 15 |
|
#define DB_SYNC_TTL_INTERVAL 3600 |
|
|
|
// Record flags |
|
#define DB_REC_FLAG_WAS_SENT 0x01 |
|
|
|
// Sync protocol constants |
|
#define DB_SEND_DATA_MAX 32 |
|
|
|
// 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); |
|
// Отменить таймеры/обмен, снять обработчики; общую 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 = Ed25519(ts[8] || json); ts записан в порядке байтов хоста. |
|
// ts — call db_sync_next_timestamp(si) before signing to reserve monotonically increasing timestamp. |
|
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); |
|
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, author_signature), начиная с 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); |
|
|
|
// Insert callback: fired after local or peer insert succeeds. |
|
// author_node_id — исходный автор записи, не обязательно переславший её пир. |
|
// initial_sync = 1 если запись пришла в рамках bulk-обмена SEND_DATA (initial/re-sync), |
|
// 0 для live-PUSH и локальных вставок. |
|
typedef void (*db_sync_insert_cb)(struct DB_SYNC_INSTANCE* si, |
|
uint64_t record_timestamp, |
|
const char* json_data, size_t len, |
|
uint64_t author_node_id, int initial_sync, void* arg); |
|
void db_sync_set_insert_cb(struct DB_SYNC_INSTANCE* si, db_sync_insert_cb cb, void* arg); |
|
|
|
// Verify chain integrity: returns 0 if all chain_hashes are correct, 1 if any mismatch found |
|
int db_sync_chain_verify(struct DB_SYNC_INSTANCE* si); |
|
|
|
// Peer state control (for testing — disable PUSH to isolate sync protocol) |
|
void db_sync_peer_set_state(struct DB_SYNC_INSTANCE* si, uint64_t node_id, uint8_t state); |
|
|
|
// 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); |
|
|
|
// Get last chain hash8 (for cross-peer consistency check in tests) |
|
int db_sync_last_chain_hash8(struct DB_SYNC_INSTANCE* si, uint64_t* out_hash8); |
|
|
|
// Sync completion callback (global, per-instance: fires when SYNC_DONE sent/received for any group) |
|
typedef void (*db_sync_done_fn)(struct DB_SYNC_INSTANCE* si, uint64_t peer_node_id, void* arg); |
|
void 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
|
|
|