Files
utun2/src/chat/member_sync.h
T

216 lines
13 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.
/*
* member_sync — подписанные записи участников каналов и их обмен через merkle_sync.
* Запись содержит блок участника, блок владельца и подпись дерева приглашений.
* Версии блоков сравниваются отдельно; запись и Merkle-хеш меняются атомарно.
* Локальный put запускает распространение после commit; start ждёт сверки с пиром.
* Участие в CHAT-группе определяется локальной таблицей peers_<channel_id>.
* Сетевые адреса принадлежат topo_node и не входят в блок участника.
* Модуль живёт внутри чат-сервиса; все операции и callbacks — в потоке uasync.
*/
#ifndef MEMBER_SYNC_H
#define MEMBER_SYNC_H
#include "merkle_sync.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
struct UTUN_INSTANCE;
struct TOPO_GROUP;
struct sqlite3;
/* Результат применения рекорда мембера (compare+update по версиям двух блоков). */
/* Полная проверка членства, join и дерева приглашений; возвращает доверенный ключ, 0/-1. */
int member_sync_verified_ed25519(struct UTUN_INSTANCE* inst, const char* channel, uint64_t author, uint8_t key[32]);
#define MS_APPLY_CHANGED 0x01 /* запись и дерево обновлены */
#define MS_APPLY_STALE 0x02 /* локальная версия новее; обратный проход доставит её пиру */
/* UTF-8 JSON bytes, excluding NUL. A complete record fits in one Merkle page. */
#define MS_ADM_TAGS_MAX 8192
#define MS_USERINFO_MAX 1024
/* Байты для подписи владельца: tags без NUL, затем node_id[8] в порядке байтов хоста.
* Возвращает длину или -1, если данные/буфер не подходят. */
int member_sync_build_owner_msg(uint64_t node_id, const char* tags, uint8_t* out, size_t capacity);
/* Представление записи; все указатели заимствованы на время вызова. */
struct ms_member_rec {
uint64_t node_id; /* derive(x25519), ключ записи участника */
const uint8_t* x25519; /* 32 байта, обязательно */
const uint8_t* ed25519; /* 32 байта, обязательно */
const uint8_t* join_sig; /* 64 байта, может быть NULL */
uint64_t join_ts; /* эпоха вступления, Unix seconds */
const uint8_t* update_sig; /* 64 байта, может быть NULL */
uint64_t update_ts; /* ver блока мембера */
const char* userinfo; /* JSON {"name":...}, может быть NULL */
const char* adm_tags; /* JSON владельца с "ver", может быть NULL */
const uint8_t* adm_tags_sig; /* 64 байта, может быть NULL */
int storage; /* 0/1, производная от adm_tags.storage */
uint64_t signed_by; /* дерево приглашений: 0 = корень (подписан ключом группы), иначе node_id инвайтера */
const uint8_t* signature; /* 64 байта, Ed25519 подпись x25519||"group_join" ключом родителя (или канальным ed25519) */
};
/* ── Дерево приглашений (подпись pubkey узла) ── */
#define MEMBER_SIGN_SUFFIX "group_join"
#define MEMBER_SIGN_SUFFIX_LEN 10
#define MEMBER_SIGN_MSG_SIZE (SC_PUBKEY_SIZE + MEMBER_SIGN_SUFFIX_LEN) /* 42 */
/* Собрать сообщение подписи: x25519(32) || "group_join". Возвращает 42 или -1. */
int member_sync_build_sign_msg(const uint8_t* x25519, uint8_t* out, int out_sz);
/* Подписать pubkey узла: sign(signer_ed_priv, x25519||"group_join"). Возвращает 0/-1. */
int member_sync_sign_pubkey(const uint8_t* signer_ed_priv, const uint8_t* x25519, uint8_t sig[64]);
/* Верифицировать подпись дерева мембера:
* signed_by==0 → ключ канала (ch_ed25519), иначе ed25519 pubkey узла signed_by.
* Возвращает 1 если подпись валидна, 0 если невалидна, -1 если подписанта нет в БД
* (родитель ещё не пришёл по merkle — верификацию откладываем). */
int member_sync_verify_signature(struct sqlite3* db, const char* ch_id, const struct ms_member_rec* m);
/* Проверить, что x25519 pubkey (из ETCP-соединения) есть в таблице мемберов канала.
* node_id = derive(x25519) → SELECT 1 FROM peers_<ch_id>. Возвращает 1/0. */
int member_sync_validate_pubkey(struct UTUN_INSTANCE* inst, const char* ch_id,
const uint8_t* x25519_pubkey);
/* ── Коллбэк приёма/применения рекорда мембера (ready-хендлинг join) ── */
typedef void (*member_apply_cbk_fn)(const char* ch_id, uint64_t node_id,
const uint8_t* x25519, uint64_t signed_by,
const uint8_t* signature, void* arg);
void member_sync_add_apply_cbk(struct UTUN_INSTANCE* inst, member_apply_cbk_fn fn, void* arg);
void member_sync_remove_apply_cbk(struct UTUN_INSTANCE* inst, member_apply_cbk_fn fn, void* arg);
/*
* Инициализировать модуль: создаёт merkle_sync с коллбэками для мемберов
* (ETCP_RT_ID_MEMBER_SYNC), проверяет локальные записи, восстанавливает деревья
* и подписывается на события CHAT-групп. 0/-1.
*/
int member_sync_init(struct UTUN_INSTANCE* inst);
/* Завершить модуль. */
void member_sync_destroy(struct UTUN_INSTANCE* inst);
/* Подписаться на BGP node-события chat-группы (BGP → member_sync → node_props_changed).
Вызывается при создании канала (chat_core_ensure_channel_ready). */
void member_sync_subscribe_group(struct UTUN_INSTANCE* inst, struct TOPO_GROUP* group);
/*
* Запустить синхронизацию мемберов канала ch_id с пиром peer.
* Делегирует в merkle_sync_start().
*/
int member_sync_start(struct UTUN_INSTANCE* inst, uint64_t peer,
const char* ch_id, merkle_sync_done_cb done_cb, void* arg);
/*
* Отменить синхронизацию. Делегирует в merkle_sync_cancel().
*/
void member_sync_cancel(struct UTUN_INSTANCE* inst, uint64_t peer, const char* ch_id);
/*
* Отменить все merkle-сессии канала (локальное удаление канала).
* Делегирует в merkle_sync_cancel_ns().
*/
void member_sync_cancel_channel(struct UTUN_INSTANCE* inst, const char* ch_id);
/*
* Добавить/обновить мембера (локальная запись, доверенная) и пересчитать дерево.
* Делегирует в member_sync_apply_record() с from_peer = inst->node_id.
*
* ch_id — ID канала
* member_id — node_id участника
* x25519 — X25519 публичный ключ (32 байта)
* ed25519 — Ed25519 публичный ключ (32 байта)
* join_sig — Ed25519 подпись join-сообщения (64 байта)
* сообщение: ch_x25519(32) || ch_ed25519(32) || node_id(8LE) || x25519(32) || join_ts(8LE)
* join_ts — NTP-синхронизированное время подписи (unix epoch seconds)
* update_sig — подпись блока A (64 байта), update_ts — версия блока A
* userinfo — метаданные узла (имя) в канале
* adm_tags — атрибуты владельца (JSON с ver/storage), adm_tags_sig — подпись канальным ключом
* storage — производная от adm_tags.storage
*
* Пишет блоки через topo_node_sqlite_member_block_put/member_owner_put, узел —
* через topo_node_sqlite_node_update_verified, затем merkle_sync_recompute_path().
*
* Возвращает маску MS_APPLY_* (0 = no-op) или -1 при ошибке.
*/
int member_sync_put(struct UTUN_INSTANCE* inst, const char* ch_id,
uint64_t member_id, const uint8_t* x25519,
const uint8_t* ed25519,
const uint8_t* join_sig, uint64_t join_ts,
const uint8_t* update_sig, uint64_t update_ts,
const char* userinfo,
const char* adm_tags, const uint8_t* adm_tags_sig, int storage,
uint64_t signed_by, const uint8_t* signature);
/*
* Строит сообщение для update_sig блока A (мембер):
* join_sig(64) || update_ts(8) || userinfo(с NUL)
* Используется и для подписи, и для верификации — байты обязаны совпадать.
* Возвращает длину сообщения или -1.
*/
int member_sync_build_update_msg(const uint8_t* join_sig, uint64_t update_ts,
const char* userinfo,
uint8_t* out, int out_sz);
/*
* Строит сообщение для join_sig (каноническая раскладка 112 байт):
* ch_x25519(32) || ch_ed25519(32) || node_id(8) || node_x25519(32) || join_ts(8)
* Единственный источник истины для подписи и верификации join_sig во всех модулях.
* Возвращает длину сообщения (112) или -1.
*/
int member_sync_build_join_msg(const uint8_t* ch_x25519, const uint8_t* ch_ed25519,
uint64_t node_id, const uint8_t* node_x25519, uint64_t join_ts,
uint8_t* out, int out_sz);
/*
* Применить рекорд мембера с по-блочным сравнением версий:
* - блок A (мембер): ver = update_ts (внутри эпохи join_ts), подписан ключом мембера;
* - блок B (владелец): ver = adm_tags.ver, подписан канальным ключом (adm_tags_sig).
* Каждый блок обновляется независимо, только если его ver увеличился.
* Если ver пришедшего блока меньше локального — блок игнорируется и выставляется
* MS_APPLY_STALE (локальная версия новее; её доставит обратный проход).
* Возвращает битовую маску MS_APPLY_*, <0 — ошибка.
*/
int member_sync_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
uint64_t from_peer, const struct ms_member_rec* m);
/*
* Верифицировать локальную запись мембера (чтение из БД). Единственный сценарий
* удаления: подпись присутствует и невалидна, либо node_id != derive(x25519).
* Отсутствие подписи = «не верифицировано» (self-add/плейсхолдер) — не битая.
* Возвращает 1 если запись валидна/не подписана, 0 если битая.
*/
int member_sync_verify_local_record(struct sqlite3* db, const char* ch_id,
const struct ms_member_rec* m);
/* Прогнать все записи канала: битую удалить + пересчитать хеши. Возвращает число удалённых. */
int member_sync_verify_and_purge(struct UTUN_INSTANCE* inst, const char* ch_id);
/* Прогнать очистку битых записей по всем каналам (при init). */
void member_sync_verify_and_purge_all(struct UTUN_INSTANCE* inst);
/* Количество мемберов в канале. */
int member_sync_count(struct UTUN_INSTANCE* inst, const char* ch_id);
/*
* Коллбэк: изменились adm_tags любого узла (включая себя).
* channel_id — канал (namespace), в котором произошло изменение.
* Уведомляет об изменении свойств после commit и при обновлении состояния узла в BGP.
* Многоподписочный — можно добавить несколько подписчиков.
*/
typedef void (*node_props_changed_fn)(uint64_t node_id, const char* adm_tags, const char* channel_id, void* arg);
/* 0 — зарегистрирован, -1 — неверные аргументы/память; потребитель может откатить свой init. */
int member_sync_add_props_cbk(struct UTUN_INSTANCE* inst, node_props_changed_fn fn, void* arg);
void member_sync_remove_props_cbk(struct UTUN_INSTANCE* inst, node_props_changed_fn fn, void* arg);
#ifdef __cplusplus
}
#endif
#endif /* MEMBER_SYNC_H */