213 lines
19 KiB
C
213 lines
19 KiB
C
/* topo_node_sqlite — хранение подписанных снимков узлов, адресов, каналов и блоков мемберов в общей SQLite.
|
||
* Модель/проверка подписей/registry — topo_node и member_sync; этот модуль реализует SQL-операции в uasync-потоке.
|
||
* Только snapshot_put/load проверяют подпись; остальные записи требуют проверенных вызывающим данных.
|
||
* snapshot_put сохраняет более свежий timestamp; node_load предпочитает snapshot.
|
||
* db/groups заимствованы. Возвращённый TOPO_NODE без registry ref: передать registry либо уничтожить через topo_node_destroy.
|
||
* Массивы out_ids/out_ch_ids/out_peers принадлежат вызывающему и освобождаются u_free. */
|
||
#ifndef TOPO_NODE_SQLITE_H
|
||
#define TOPO_NODE_SQLITE_H
|
||
|
||
#include <sqlite3.h>
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
#include <time.h>
|
||
#include "topo_node.h"
|
||
|
||
#define PEERS_JOIN_SIG_SIZE 64
|
||
#define PEERS_JOIN_TS_SIZE 8
|
||
#define PEERS_UPDATE_SIG_SIZE 64
|
||
|
||
/* Результаты channel_get/get_ed25519_pubkey: отсутствие зависимости и отказ чтения различаются. */
|
||
#define TOPO_SQLITE_LOOKUP_MISSING (-1)
|
||
#define TOPO_SQLITE_LOOKUP_ERROR (-2)
|
||
|
||
/* flags byte in wire format */
|
||
#define PEERS_FLAG_HAS_JOIN 0x01
|
||
|
||
/* Создать/обновить nodes, node_addresses, node_snapshots, channels; 0/-1. db открывает и закрывает вызывающий. */
|
||
int topo_node_sqlite_init(sqlite3* db);
|
||
/* Проверить и сохранить полный подписанный NODEINFO без групповых полей;старый/равный timestamp даёт0 без замены.
|
||
* Возврат0/-1,свой savepoint проверяет bind/step/finalize/RELEASE;отказ откатывает запись.
|
||
* При неудачном rollback открытая транзакция требует rollback/закрытия БД владельцем. */
|
||
int topo_node_sqlite_snapshot_put(sqlite3* db, struct TOPO_NODE* ni);
|
||
/* Сохранённый timestamp (Unix microseconds),в том числе после инвалидирования record;отсутствие даёт0.
|
||
* Возвращает0/-1;при SQL/форматной ошибке *out не меняется. out обязателен. */
|
||
int topo_node_sqlite_snapshot_timestamp(sqlite3* db, uint64_t node_id, uint64_t* out);
|
||
/* Незарегистрированный проверенный снимок, включая снимок без адресов; NULL — нет/невалиден/ошибка.
|
||
* groups и groups->instance->topo_groups должны обозначать один контейнер с пулами. */
|
||
struct TOPO_NODE* topo_node_sqlite_snapshot_load(sqlite3* db, struct TOPO_GROUPS* groups, uint64_t node_id);
|
||
|
||
/* Персистентная запись адресов узла в node_addresses (источник истины — BGP/topo_group).
|
||
* DELETE + INSERT из ni->v4_addrs/v6_addrs под savepoint, включая пустой набор; 0/-1, отказ откатывает замену.
|
||
* mapping/inbound/source берутся из SOCKMETA; проверку ni выполняет вызывающий. RTT при замене не сохраняется. */
|
||
int topo_node_sqlite_addrs_put(sqlite3* db, uint64_t node_id, struct TOPO_NODE* ni);
|
||
|
||
/* Есть ли строка хотя бы в одной peers_*: включая source=1 и deleted=1; 1/0, при ошибке также 0. */
|
||
int topo_node_sqlite_member_exists(sqlite3* db, uint64_t node_id);
|
||
|
||
/* Числовые ненулевые channel_id таблиц peers_*, содержащих строку узла; включает placeholders/deleted.
|
||
* 0/-1; пустой результат NULL/0, при ошибке частичный массив не выдаётся. */
|
||
int topo_node_sqlite_get_member_channels(sqlite3* db, uint64_t node_id,
|
||
uint64_t** out_ch_ids, int* out_count);
|
||
|
||
/* Создать peers_<id> и сохранить канал; 0/-1. Ключи — по 32, signature — 64 байта, privkey необязательны.
|
||
* При обновлении публичные ключи обязаны совпасть; имя/владелец/подпись заменяются, NULL privkey сохраняет прежний.
|
||
* Свой savepoint включает peers/индексы/описание; проверяет DDL,все bind/step/changes1/finalize/release.
|
||
* Ошибка сохраняет прежние данные/схему. Внешняя транзакция допустима и не завершается.
|
||
* Отказ rollback не выполняет release:caller должен откатить/закрыть оставшуюся транзакцию.
|
||
* Подпись/пары ключей не проверяет. Табличный суффикс: первые 63 символа, вне [A-Za-z0-9_] заменены на '_'. */
|
||
int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||
const char* name, uint64_t owner_node_id,
|
||
const uint8_t* x25519_pub, const uint8_t* x25519_priv,
|
||
const uint8_t* ed25519_pub, const uint8_t* ed25519_priv,
|
||
const uint8_t* signature);
|
||
|
||
/* Добавить radio_muted=0 в существующую peers_<id>, если поля ещё нет; 0/-1, идемпотентно. */
|
||
int topo_node_sqlite_member_local_init(sqlite3* db, const char* channel_id);
|
||
|
||
/* Полное локальное удаление канала: DROP peers_<id>/msg_<id>, DELETE channels,
|
||
* Удаляются feedback_<id> и merkle_tree_hash всех datasets канала (если таблица есть).
|
||
* Проверяются finalize и отсутствие канала/хешей после DELETE, включая RAISE(IGNORE).
|
||
* Локальные каскадные triggers (в том числе attachment) входят в ту же транзакцию.
|
||
* 0/-1; savepoint откатывает в том числе DROP TABLE, внешняя транзакция сохраняется.
|
||
* При ошибке rollback вызывающий проверяет autocommit; открытая транзакция требует остановки/закрытия БД владельцем. */
|
||
int topo_node_sqlite_channel_delete(sqlite3* db, const char* channel_id);
|
||
|
||
/* Вставить source=1 с нулевой join_sig; существующую строку не менять. 0/-1; ключи по 32 байта.
|
||
* Плейсхолдер не участвует в Merkle-хеше; канал/peers_<id> должны уже существовать. */
|
||
int topo_node_sqlite_member_placeholder_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey);
|
||
|
||
/* Обновляет только блок мембера (keys/join/update_sig/update_ts/userinfo), не трогая блок владельца.
|
||
* signed_by/signature — дерево приглашений: кто подписал pubkey узла (0 = корень, ключом группы).
|
||
* 0/-1; подписи по 64, ключи по 32 байта. NULL join_sig даёт нулевую подпись, userinfo=NULL даёт ''.
|
||
* Сбрасывает source=0; свежесть/подписи проверяет вызывающий, owner/local-поля сохраняются. */
|
||
/* Проверяет все bind,step,changes1 и finalize;при отказе caller откатывает свою транзакцию записи/индекса. */
|
||
int topo_node_sqlite_member_block_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||
const uint8_t* join_sig, uint64_t join_ts,
|
||
const uint8_t* update_sig, uint64_t update_ts,
|
||
const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* userinfo,
|
||
const uint8_t* creator_sig, uint64_t signed_by, const uint8_t* signature);
|
||
|
||
/* Прочитать дерево приглашений мембера (signed_by + signature). 0=ок, -1=нет записи/полей. */
|
||
int topo_node_sqlite_member_get_sign(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||
uint64_t* signed_by_out, uint8_t signature_out[64]);
|
||
|
||
/* Есть ли строка в peers_<id>, включая placeholders/deleted. 1/0; ошибка также даёт 0. */
|
||
int topo_node_sqlite_member_in_channel(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||
|
||
/* Допуск участника: source=0, deleted=0 и admitted=1 последней контрольной точки политики/цепочек.
|
||
* 1/0, ошибка логируется и даёт 0. BGP-placeholder и неподходящая цепочка доступа не дают. */
|
||
int topo_node_sqlite_member_allowed(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||
/* Транспорт для первой MEMBERS сверки: полная неудалённая запись, ещё без admitted. 1/0; ошибка запрещает. */
|
||
int topo_node_sqlite_member_transport_allowed(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||
/* Проверить точный X25519-ключ полного действующего мембера: 1 — есть, 0 — нет/ошибка (закрытый отказ).
|
||
* node_id вычисляется из key, SQL также сверяет все 32 байта; заглушки source=1 и deleted исключены. */
|
||
int topo_node_sqlite_member_key_allowed(sqlite3* db, const char* channel_id, const uint8_t key[32]);
|
||
|
||
/* Обновить owner-блок и deleted из adm_tags в одной существующей строке;0/-1,отсутствующая строка — ошибка.
|
||
* adm_tags/adm_tags_sig могут быть NULL;ненулевая подпись64 байта,проверяется вызывающим.
|
||
* Проверяет все bind/step/changes=1/finalize.
|
||
* Работает в транзакции caller;при отказе caller откатывает её до любых runtime-уведомлений. */
|
||
int topo_node_sqlite_member_owner_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||
const char* adm_tags, const uint8_t* adm_tags_sig, int storage);
|
||
|
||
/* Прочитать ровно 64 байта join_sig и join_ts; 0/-1, выходы обязательны и при неуспехе не меняются. */
|
||
int topo_node_sqlite_member_get_join(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||
uint8_t* join_sig_out, uint64_t* join_ts_out);
|
||
|
||
/* Физически удалить строку участника; 0/-1. Отсутствующая строка даёт 0; tombstone/Merkle не создаёт. */
|
||
int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||
|
||
/* Записать проверенные вызывающим ключи/имя в nodes; 0/-1. Пустое имя сохраняет прежнее при UPDATE.
|
||
* Ключи заменяются независимо от update_ts; update_ts хранит максимум, created_at сохраняется, если не NULL.
|
||
* Новая строка online=1, при UPDATE online сохраняется. Подпись и свежесть здесь не проверяются.
|
||
* Проверяет changes=1:подавленная trigger запись не считается успехом. Транзакцией управляет caller. */
|
||
int topo_node_sqlite_node_update_verified(sqlite3* db, uint64_t node_id,
|
||
const char* name, const uint8_t* x25519, const uint8_t* ed25519, uint64_t update_ts, time_t created_at);
|
||
|
||
/* Метаданные канала: 0 — успех, LOOKUP_MISSING — нет строки/запрошенного ключа,
|
||
* LOOKUP_ERROR — аргументы/SQL/повреждённый размер. Выходы действительны только при 0.
|
||
* Все выходы необязательны; name_sz включает NUL, ключи по 32, подпись 64 байта. */
|
||
int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||
char* name_out, int name_sz, uint64_t* owner_node_id,
|
||
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature);
|
||
|
||
/* Прочитать Ed25519 privkey ровно32 байта BLOB; 0/-1, отсутствие/повреждение/SQL-ошибка даёт -1.
|
||
* Проверяются bind/step/finalize; при отказе выход не меняется. */
|
||
int topo_node_sqlite_channel_get_priv(sqlite3* db, const char* channel_id,
|
||
uint8_t* ed25519_priv_out);
|
||
|
||
/* Прочитать оба канальных privkey (x25519 + ed25519), каждый ровно32 байта BLOB.
|
||
* 0 — оба прочитаны, -1 — отказ с сохранением обоих выходов. Временные ключи очищаются. */
|
||
int topo_node_sqlite_channel_get_privs(sqlite3* db, const char* channel_id,
|
||
uint8_t* x25519_priv_out, uint8_t* ed25519_priv_out);
|
||
|
||
/* Атомарно записать оба канальных privkey[32] в одну существующую строку; 0/-1.
|
||
* Savepoint проверяет все SQL-операции и откатывает запись при отказе; отсутствие строки — ошибка.
|
||
* При внешней транзакции её commit остаётся вызывающему; событие успеха до него запрещено.
|
||
* Соответствие пары/подпись проверяет вызывающий. Аргументы обязательны; rollback-отказ логируется. */
|
||
int topo_node_sqlite_channel_set_privs(sqlite3* db, const char* channel_id,
|
||
const uint8_t* x25519_priv, const uint8_t* ed25519_priv);
|
||
|
||
/* Установить online=0/1 существующей строки; 0/-1. Отсутствующая строка даёт 0 без вставки. */
|
||
int topo_node_sqlite_node_set_online(sqlite3* db, uint64_t node_id, int online);
|
||
/* online из nodes; 0, если записи нет или запрос не выполнен. */
|
||
int topo_node_sqlite_node_get_online(sqlite3* db, uint64_t node_id);
|
||
/* Скопировать ровно 32 байта Ed25519 из nodes: 0 — найден, LOOKUP_MISSING — нет строки/ключа,
|
||
* LOOKUP_ERROR — аргументы/SQL/повреждённый размер. При отказе выход не меняется.
|
||
* Значение байтов не определяет наличие ключа; криптографическую пригодность проверяет операция verify. */
|
||
int topo_node_sqlite_get_ed25519_pubkey(sqlite3* db, uint64_t node_id, uint8_t pubkey_out[32]);
|
||
|
||
/* Атомарно пересчитать node_type/storage/deleted во всех peers_* по адресам и adm_tags; 0/-1.
|
||
* node_type: 0 нет адресов, 1 прямой/локальный/strict, 2 NAT/contact, 4 supernode=yes.
|
||
* storage=yes действует только при наличии ADDR_TYPE_DIRECT, deleted берётся из adm_tags.deleted=yes. */
|
||
int topo_node_sqlite_nodeinfo_updated(sqlite3* db, uint64_t node_id);
|
||
|
||
/* Обновить node_addresses.rtt и все peers_*.node_RTT, единицы 0.1 ms; ошибки логируются, обновление неатомарно. */
|
||
void topo_node_sqlite_update_rtt(sqlite3* db, uint64_t node_id, uint16_t rtt);
|
||
|
||
/**
|
||
* @brief Загружает TOPO_NODE из SQLite (ref_count=0).
|
||
* @param groups нужен для выделения адресов из memory_pool
|
||
* @return TOPO_NODE* или NULL, если нет пригодной записи/адресов bootstrap либо при ошибке
|
||
*
|
||
* Сначала загружает и проверяет подписанный snapshot; при его отсутствии собирает сведения nodes/node_addresses.
|
||
* Snapshot может быть без адресов. Bootstrap требует адресов и получает timestamp=0, профиль UNKNOWN.
|
||
* При отказе SQL/памяти частичная запись освобождается. groups->instance->topo_groups должен совпадать с groups.
|
||
* Вызывающий передаёт владение через topo_node_registry_store() либо вызывает topo_node_destroy().
|
||
*/
|
||
struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* groups, uint64_t node_id);
|
||
/* Загрузить только известную идентичность nodes, без адресов/подписи/timestamp.
|
||
* Для NCD с независимо подтверждённым LAN-адресом. Проверяет размеры ключей и node_id=derive(X25519).
|
||
* Владельческий TOPO_NODE или NULL; уничтожить topo_node_destroy либо передать registry. */
|
||
struct TOPO_NODE* topo_node_sqlite_identity_load(sqlite3* db, uint64_t node_id);
|
||
|
||
/* Записать connected=0/1 существующего участника; 0/-1, без вставки отсутствующей строки. */
|
||
int topo_node_sqlite_set_connected(sqlite3* db, const char* channel_id, uint64_t node_id, int connected);
|
||
|
||
/* Кандидат CHAT-подключения: роль канальная, тип устройства из подписанного snapshot.
|
||
* UINT8_MAX означает отсутствие сведений о типе; адреса не ограничивают выборку. */
|
||
struct topo_connect_peer {
|
||
uint64_t node_id;
|
||
uint8_t client_type, supernode;
|
||
};
|
||
|
||
/* Все полные неудалённые участники (source=0, deleted=0), включая self; сначала connected, затем RTT/node_id.
|
||
* 0/-1; массив принадлежит вызывающему и освобождается u_free(). */
|
||
int topo_node_sqlite_get_connect_peers(sqlite3* db, const char* channel_id,
|
||
struct topo_connect_peer** out, size_t* out_count);
|
||
/* Нижеследующие выборки: 0/-1, пусто/ошибка — NULL/0, частичный массив не выдаётся; освобождение u_free.
|
||
* connected/supernode ограничены участниками канала, self не исключают, deleted=1 исключают.
|
||
* public/local — глобальные адреса с записью nodes, исключены self и deleted=1 в указанном канале;
|
||
* наличие членства в канале не требуется. public: addr_type 1/2, local: 0/3; фильтра port!=0 нет. */
|
||
int topo_node_sqlite_get_connected_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count);
|
||
int topo_node_sqlite_get_public_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count, uint64_t self_node_id);
|
||
int topo_node_sqlite_get_local_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count, uint64_t self_node_id);
|
||
int topo_node_sqlite_get_supernode_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count);
|
||
|
||
/* node_type узла в канале (peers_<ch>.node_type); -1 если нет записи/ошибка. */
|
||
int topo_node_sqlite_get_node_type(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||
|
||
#endif
|