db_sync: LMDB→SQLite, multi-instance, etcp_api, chatgui integration
- src/db_sync: LMDB replaced with SQLite (separate tables per instance: db_sync_name_id) TTL index (author, timestamp), WAL mode, per-instance peers/timers opaque DB_SYNC_INSTANCE* handle, insert callback, select API new fields: author_signature, delivered_peers, delivery_chain transport: etcp_api direct P2P (was etcp_router) - src/db_sync.h: user-oriented API docs, multi-instance lifecycle - lib/Makefile.am: liblmdb sources removed - lib/sqlite3.c+h: SQLite amalgamation added - topo_node: LMDB→SQLite (src/topo_node_sqlite.c moved from chatgui) - chatgui: real db_sync.c replaces db_sync_stub.c chat_core: db_sync_insert_signed + dual-write insert callback utun_node: db_sync_init/destroy replaces chat_sync_init/destroy CMakeLists: stubs removed, real db_sync.c added
This commit is contained in:
1 parent
37417ba49d
commit
f617dcd68e
21 files changed
+284328
-1174
No files matched your search
+4
-2
@@ -12,7 +12,7 @@ utun_CORE_SOURCES = \
|
||||
topo_group.c \
|
||||
route_ping.c \
|
||||
topo_node.c \
|
||||
topo_node_lmdb.c \
|
||||
topo_node_sqlite.c \
|
||||
route_connectivity.c \
|
||||
conn_mgr.c \
|
||||
db_sync.c \
|
||||
@@ -65,7 +65,7 @@ libutun_a_SOURCES = \
|
||||
topo_group.c \
|
||||
route_ping.c \
|
||||
topo_node.c \
|
||||
topo_node_lmdb.c \
|
||||
topo_node_sqlite.c \
|
||||
route_connectivity.c \
|
||||
conn_mgr.c \
|
||||
db_sync.c \
|
||||
@@ -119,6 +119,8 @@ utun_CFLAGS = \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/src/uip \
|
||||
-g \
|
||||
-DUSE_SQLITE \
|
||||
-DSQLITE_THREADSAFE=1 \
|
||||
$(DEBUG_FLAGS)
|
||||
|
||||
# Libraries
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ struct global_config {
|
||||
|
||||
// Debug and logging configuration
|
||||
char log_file[256]; // Path to log file (empty = stdout)
|
||||
char db_path[256]; // Path to LMDB nodeinfo database (empty = disabled)
|
||||
char db_path[256]; // Path to SQLite database (empty = disabled)
|
||||
int db_sync_enabled; // 1 = enable distributed DB sync (default: 0)
|
||||
uint32_t db_sync_ttl; // TTL for unsent records in seconds (default: 86400)
|
||||
char debug_level[16]; // debug level: error, warn, info, debug, trace
|
||||
|
||||
+309
-870
File diff suppressed because it is too large.
Load diff
+63
-5
@@ -1,4 +1,33 @@
|
||||
// db_sync.h — Distributed content-addressed table with LMDB storage and peer sync via etcp_router
|
||||
// db_sync.h — Distributed content-addressed table with SQLite + peer sync via etcp_api (direct P2P)
|
||||
//
|
||||
// Назначение: децентрализованная реплицируемая таблица JSON-записей между всеми узлами сети.
|
||||
// Модуль поддерживает несколько независимых инстансов (таблиц), каждый идентифицируется парой (name, id).
|
||||
//
|
||||
// Использование:
|
||||
// 1. Включить в конфиге: db_sync_enabled = 1
|
||||
// Опционально: db_sync_ttl = 86400 (по умолчанию)
|
||||
// 2. db_sync_init() — вызывается автоматически при старте utun_instance
|
||||
// 3. struct DB_SYNC_INSTANCE* si = db_sync_instance_add(inst, "chats", 1);
|
||||
// Создаёт/регистрирует инстанс. При поднятии соединений автоматически запускает sync.
|
||||
// 4. db_sync_insert(si, json_data) — вставить запись. Автоматически PUSH всем synced-пирам.
|
||||
// 5. db_sync_count(si) — количество записей в локальной БД для этого инстанса
|
||||
// 6. db_sync_instance_remove(si) — удалить инстанс (таблица БД не удаляется)
|
||||
// 7. db_sync_destroy() — вызывается автоматически при завершении
|
||||
//
|
||||
// Синхронизация:
|
||||
// - При поднятии ETCP-соединения с пиром для каждого инстанса запускается полная синхронизация
|
||||
// - Каждое сообщение содержит instance_hash (первые 64 бита SHA256(name||id_be)),
|
||||
// что позволяет маршрутизировать сообщения к нужному инстансу на приёмной стороне
|
||||
// - Если пир не имеет инстанса с таким хешем — возвращает DB_MSG_ERROR(0x01)
|
||||
// - Если пир добавляет инстанс позже — сам инициирует sync в нашу сторону
|
||||
// - Протокол: сравнение chain_hash для поиска расхождений (7 типов сообщений)
|
||||
// - Новые записи немедленно рассылаются подключённым пирам через PUSH
|
||||
//
|
||||
// Нюансы:
|
||||
// - Записи не редактируются и не удаляются явно — только TTL-очистка (per-instance)
|
||||
// - Дубликаты определяются по (instance_hash, timestamp, datahash)
|
||||
// - БД хранится в SQLite, путь: <db_path>/sync
|
||||
// - Синхронизация идёт через etcp_api (service ID 0x20), только прямые P2P соединения
|
||||
#ifndef DB_SYNC_H
|
||||
#define DB_SYNC_H
|
||||
|
||||
@@ -6,11 +35,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct DB_SYNC_INSTANCE;
|
||||
|
||||
// etcp_router service ID
|
||||
#define ETCP_RT_ID_DB_SYNC 0x20
|
||||
@@ -23,6 +52,11 @@ struct UTUN_INSTANCE;
|
||||
#define DB_MSG_PUSH 0x05
|
||||
#define DB_MSG_ACK_PUSH 0x06
|
||||
#define DB_MSG_SYNC_DONE 0x07
|
||||
#define DB_MSG_ERROR 0x08
|
||||
|
||||
// Error codes for DB_MSG_ERROR
|
||||
#define DB_ERR_NOT_FOUND 0x01 // instance not found
|
||||
#define DB_ERR_DISABLED 0x02 // instance exists but disabled
|
||||
|
||||
// Defaults
|
||||
#define DB_SYNC_DEFAULT_TTL 86400
|
||||
@@ -37,12 +71,36 @@ struct UTUN_INSTANCE;
|
||||
#define DB_REFINE_HASHES 16
|
||||
#define DB_SEND_DATA_MAX 32
|
||||
|
||||
// Global lifecycle
|
||||
int db_sync_init(struct UTUN_INSTANCE* inst);
|
||||
void db_sync_destroy(struct UTUN_INSTANCE* inst);
|
||||
int db_sync_insert(struct UTUN_INSTANCE* inst, const char* json_data);
|
||||
int db_sync_insert_len(struct UTUN_INSTANCE* inst, const char* json_data, size_t len);
|
||||
uint32_t db_sync_count(struct UTUN_INSTANCE* inst);
|
||||
|
||||
// Instance management
|
||||
struct DB_SYNC_INSTANCE* db_sync_instance_add(struct UTUN_INSTANCE* inst, const char* name, uint64_t id);
|
||||
void db_sync_instance_remove(struct DB_SYNC_INSTANCE* si);
|
||||
|
||||
// Data operations (per-instance)
|
||||
int db_sync_insert_len(struct DB_SYNC_INSTANCE* si, const char* json_data, size_t len);
|
||||
int db_sync_insert(struct DB_SYNC_INSTANCE* si, const char* json_data);
|
||||
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);
|
||||
uint32_t db_sync_count(struct DB_SYNC_INSTANCE* si);
|
||||
|
||||
// Select: iterate records ordered by (timestamp, datahash), starting at offset, max limit (0=unlimited).
|
||||
// Returns number of records passed to 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 = self for local inserts, peer node_id for remote.
|
||||
typedef void (*db_sync_insert_cb)(struct DB_SYNC_INSTANCE* si,
|
||||
const char* json_data, size_t len,
|
||||
uint64_t author_node_id, void* arg);
|
||||
void db_sync_set_insert_cb(struct DB_SYNC_INSTANCE* si, db_sync_insert_cb cb, void* arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+23
-20
@@ -16,15 +16,12 @@
|
||||
#include "etcp_debug.h"
|
||||
#include "config_parser.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_node_lmdb.h"
|
||||
#include "topo_node_sqlite.h"
|
||||
#include "route_lib.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_ping.h"
|
||||
#include "route_connectivity.h"
|
||||
#include "control_server.h"
|
||||
#ifdef USE_SQLITE
|
||||
#include "../../tools/chatgui/transport/topo_node_sqlite.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define TOPO_GROUP_UTUN 0x8000000000000000ULL
|
||||
@@ -224,9 +221,6 @@ static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint
|
||||
sc_derive_ed25519_pubkey(instance->my_keys.private_key, group->ed25519_public_key);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Ed25519 pubkey derived from X25519 private key");
|
||||
|
||||
if (group_type == TOPO_GROUP_TYPE_UTUN && instance->config && instance->config->global.db_path[0])
|
||||
topo_node_lmdb_init(group, instance->config->global.db_path);
|
||||
|
||||
group->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, offsetof(struct TOPO_NODEQ, hash_node_id) - sizeof(struct ll_entry), 8, "group_nodes");
|
||||
if (!group->nodes) { queue_entry_free(qe); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "nodes queue creation failed"); return NULL; }
|
||||
|
||||
@@ -250,8 +244,6 @@ static void topo_group_destroy(struct TOPO_GROUP* group) {
|
||||
|
||||
if (group->local_node) { topo_node_free_lists(group, group->local_node); u_free(group->local_node); }
|
||||
|
||||
if (group->group_type == TOPO_GROUP_TYPE_UTUN) topo_node_lmdb_destroy(group);
|
||||
|
||||
if (group->nodes) queue_free(group->nodes);
|
||||
}
|
||||
|
||||
@@ -273,6 +265,20 @@ struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance) {
|
||||
g->v4_subnet_pool = memory_pool_init(sizeof(struct TOPO_SUBNET4), "to_v4sub");
|
||||
g->v6_subnet_pool = memory_pool_init(sizeof(struct TOPO_SUBNET6), "to_v6sub");
|
||||
|
||||
if (instance->config && instance->config->global.db_path[0]) {
|
||||
int rc = sqlite3_open(instance->config->global.db_path, &g->topo_sqlite_db);
|
||||
if (rc == SQLITE_OK && g->topo_sqlite_db) {
|
||||
sqlite3_exec(g->topo_sqlite_db, "PRAGMA journal_mode=WAL", NULL, NULL, NULL);
|
||||
sqlite3_exec(g->topo_sqlite_db, "PRAGMA foreign_keys=ON", NULL, NULL, NULL);
|
||||
topo_node_sqlite_init(g->topo_sqlite_db);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "SQLite opened: %s", instance->config->global.db_path);
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "sqlite3_open(%s) failed: %s",
|
||||
instance->config->global.db_path, g->topo_sqlite_db ? sqlite3_errmsg(g->topo_sqlite_db) : "null db");
|
||||
if (g->topo_sqlite_db) { sqlite3_close(g->topo_sqlite_db); g->topo_sqlite_db = NULL; }
|
||||
}
|
||||
}
|
||||
|
||||
instance->topo_groups = g;
|
||||
|
||||
struct TOPO_GROUP* default_group = topo_group_create(instance, TOPO_GROUP_UTUN, TOPO_GROUP_TYPE_UTUN);
|
||||
@@ -313,6 +319,8 @@ void topo_groups_destroy(struct UTUN_INSTANCE* instance) {
|
||||
memory_pool_destroy(g->v4_subnet_pool);
|
||||
memory_pool_destroy(g->v6_subnet_pool);
|
||||
|
||||
if (g->topo_sqlite_db) { sqlite3_close(g->topo_sqlite_db); g->topo_sqlite_db = NULL; }
|
||||
|
||||
u_free(g); instance->topo_groups = NULL;
|
||||
}
|
||||
|
||||
@@ -337,13 +345,11 @@ struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t grou
|
||||
return group;
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void topo_groups_set_sqlite_db(struct TOPO_GROUPS* g, sqlite3* db) {
|
||||
if (!g) return;
|
||||
g->topo_sqlite_db = db;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_sqlite_db set");
|
||||
}
|
||||
#endif
|
||||
|
||||
void topo_group_set_nat_check_local(struct TOPO_GROUP* group, int allow) { if (!group) return; group->allow_nat_check_local = allow ? 1 : 0; }
|
||||
|
||||
@@ -630,13 +636,12 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
|
||||
|
||||
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance->rt) route_insert(group->instance->rt, nodeinfo1);
|
||||
if (node_id != group->instance->node_id) {
|
||||
#ifdef USE_SQLITE
|
||||
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->instance->topo_groups->topo_sqlite_db) {
|
||||
topo_node_sqlite_node_put(group->instance->topo_groups->topo_sqlite_db, nodeinfo1);
|
||||
if (group->channel_id[0]) topo_node_sqlite_member_put(group->instance->topo_groups->topo_sqlite_db, group->channel_id, node_id, NULL, NULL);
|
||||
} else
|
||||
#endif
|
||||
topo_node_lmdb_put(group, nodeinfo1);
|
||||
sqlite3* sdb = group->instance->topo_groups->topo_sqlite_db;
|
||||
if (sdb) {
|
||||
topo_node_sqlite_node_put(sdb, nodeinfo1);
|
||||
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0])
|
||||
topo_node_sqlite_member_put(sdb, group->channel_id, node_id, NULL, NULL);
|
||||
}
|
||||
}
|
||||
if (group->instance->control_srv) control_server_notify_node_change(group->instance->control_srv, nodeinfo1);
|
||||
|
||||
@@ -687,10 +692,8 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send
|
||||
struct ll_entry* entry = queue_find_data_by_index(group->nodes, &node_id);
|
||||
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
|
||||
topo_group_broadcast_withdraw(group, node_id, wd_source, sender);
|
||||
#ifdef USE_SQLITE
|
||||
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->instance->topo_groups->topo_sqlite_db)
|
||||
topo_node_sqlite_member_del(group->instance->topo_groups->topo_sqlite_db, group->channel_id, node_id);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+25
-10
@@ -1,3 +1,27 @@
|
||||
/**
|
||||
* @file topo_group.h
|
||||
* @brief BGP-подобный обмен топологией узлов между пирами через ETCP.
|
||||
*
|
||||
* Механика:
|
||||
* - Узлы обмениваются информацией друг о друге (pubkey, адреса, подсети)
|
||||
* через NODEINFO/WITHDRAW сообщения, маршрутизируемые по ETCP.
|
||||
* - Каждый узел хранит полную таблицу известных узлов (структура TOPO_NODEQ).
|
||||
* - При подключении нового пира — полная синхронизация таблицы.
|
||||
* - При отключении пира — withdraw всех узлов, достижимых только через него.
|
||||
*
|
||||
* Группы (TOPO_GROUP):
|
||||
* - TOPO_GROUP_TYPE_UTUN — основная VPN-сеть (обмен маршрутами, подсетями)
|
||||
* - TOPO_GROUP_TYPE_CHAT — чат-группа (только узлы, без подсетей)
|
||||
* - Каждая группа изолирована: узлы из utun-группы не видны в чат-группе и наоборот
|
||||
*
|
||||
* Дополнительные функции:
|
||||
* - NAT-детекция и проверка связности (NAT_INFO, NAT_CHECK_REQ, PING)
|
||||
* - Поиск оптимального маршрута до узла (topo_group_find_conn_for_node)
|
||||
*
|
||||
* Хранение: все узлы сохраняются в SQLite (таблицы nodes, node_addresses,
|
||||
* channels, peers_<channel_id>). База открывается в topo_groups_init()
|
||||
* если в конфиге задан db_path.
|
||||
*/
|
||||
#ifndef TOPO_GROUP_H
|
||||
#define TOPO_GROUP_H
|
||||
|
||||
@@ -10,13 +34,10 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
#include "../lib/liblmdb/lmdb.h"
|
||||
#include "../lib/sqlite3.h"
|
||||
#include "route_lib.h"
|
||||
#include "topo_node.h"
|
||||
#include "secure_channel.h"
|
||||
#ifdef USE_SQLITE
|
||||
#include <sqlite3.h>
|
||||
#endif
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
@@ -125,8 +146,6 @@ struct TOPO_GROUP {
|
||||
uint8_t allow_nat_check_local;
|
||||
uint8_t ed25519_public_key[SC_PUBKEY_SIZE];
|
||||
char channel_id[64]; // channel_id для групп типа CHAT
|
||||
MDB_env* nodeinfo_env;
|
||||
MDB_dbi nodeinfo_dbi;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -141,9 +160,7 @@ struct TOPO_GROUPS {
|
||||
struct memory_pool* v6_addr_pool;
|
||||
struct memory_pool* v4_subnet_pool;
|
||||
struct memory_pool* v6_subnet_pool;
|
||||
#ifdef USE_SQLITE
|
||||
sqlite3* topo_sqlite_db;
|
||||
#endif
|
||||
topo_node_updated_fn node_updated_cb; /* optional — set by chatgui's member_sync */
|
||||
};
|
||||
|
||||
@@ -294,9 +311,7 @@ struct nat_check_arg {
|
||||
uint16_t nat_port;
|
||||
};
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void topo_groups_set_sqlite_db(struct TOPO_GROUPS* g, sqlite3* db);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+21
-1
@@ -1,4 +1,24 @@
|
||||
#ifndef TOPO_NODE_H
|
||||
/**
|
||||
* @file topo_node.h
|
||||
* @brief Модель данных узла сети — структуры, сериализация, wire-формат.
|
||||
*
|
||||
* Этот модуль определяет:
|
||||
* - Как узел представлен в памяти (TOPO_NODE, TOPO_NODEQ, адреса, подсети)
|
||||
* - Как узел передаётся по сети (packed-структуры TOPOMSG_*)
|
||||
* - Как узел сериализуется/десериализуется (topo_node_serialize/deserialize)
|
||||
*
|
||||
* Модуль не содержит сетевой логики и BGP-обмена — этим занимается topo_group.
|
||||
*
|
||||
* Хранение: данные узлов сохраняются в SQLite (таблицы nodes, node_addresses)
|
||||
* через модуль topo_node_sqlite.
|
||||
*
|
||||
* Основные понятия:
|
||||
* TOPO_NODE — идентичность узла (pubkeys, имя, версия, списки адресов)
|
||||
* TOPO_NODEQ — запись в очереди группы (плюс пути, hop_list, связность)
|
||||
* TOPO_NODEPATH — путь до узла через конкретное ETCP-соединение
|
||||
* TOPOMSG_NODE — wire-формат, передаваемый по NODEINFO
|
||||
*/
|
||||
#ifndef TOPO_NODE_H
|
||||
#define TOPO_NODE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include "topo_node_lmdb.h"
|
||||
#include "topo_group.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
|
||||
#define NODEINFO_DB_MAPSIZE (10UL * 1024 * 1024)
|
||||
|
||||
int topo_node_lmdb_init(struct TOPO_GROUP* bgp, const char* db_path) {
|
||||
if (!bgp || !db_path || !db_path[0]) return -1;
|
||||
int rc = mdb_env_create(&bgp->nodeinfo_env);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_create failed: %s", mdb_strerror(rc)); return -1; }
|
||||
rc = mdb_env_set_mapsize(bgp->nodeinfo_env, NODEINFO_DB_MAPSIZE);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_set_mapsize failed: %s", mdb_strerror(rc)); mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; return -1; }
|
||||
rc = mdb_env_set_maxdbs(bgp->nodeinfo_env, 2);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_set_maxdbs failed: %s", mdb_strerror(rc)); mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; return -1; }
|
||||
rc = mdb_env_open(bgp->nodeinfo_env, db_path, 0, 0644);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_open(%s) failed: %s", db_path, mdb_strerror(rc)); mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; return -1; }
|
||||
MDB_txn* txn = NULL; rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_txn_begin failed: %s", mdb_strerror(rc)); mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; return -1; }
|
||||
rc = mdb_dbi_open(txn, "nodeinfo", MDB_CREATE, &bgp->nodeinfo_dbi);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_dbi_open(nodeinfo) failed: %s", mdb_strerror(rc)); mdb_txn_abort(txn); mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; return -1; }
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_txn_commit failed: %s", mdb_strerror(rc)); mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; return -1; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "LMDB nodeinfo database opened: %s", db_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void topo_node_lmdb_destroy(struct TOPO_GROUP* bgp) {
|
||||
if (!bgp) return;
|
||||
if (bgp->nodeinfo_env) { mdb_env_close(bgp->nodeinfo_env); bgp->nodeinfo_env = NULL; DEBUG_INFO(DEBUG_CATEGORY_BGP, "LMDB nodeinfo database closed"); }
|
||||
}
|
||||
|
||||
int topo_node_lmdb_put(struct TOPO_GROUP* bgp, struct TOPO_NODEQ* nq) {
|
||||
if (!bgp || !bgp->nodeinfo_env || !nq || !nq->node) return -1;
|
||||
uint8_t buf[8192];
|
||||
int ser_len = topo_node_serialize(bgp, nq, buf, sizeof(buf));
|
||||
if (ser_len < 0) return -1;
|
||||
|
||||
MDB_val key, data;
|
||||
key.mv_size = 8; key.mv_data = &nq->node->node_id;
|
||||
data.mv_size = (size_t)ser_len; data.mv_data = buf;
|
||||
|
||||
MDB_txn* txn = NULL; int rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB put mdb_txn_begin failed: %s", mdb_strerror(rc)); return -1; }
|
||||
rc = mdb_put(txn, bgp->nodeinfo_dbi, &key, &data, 0);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_put node %016llx failed: %s", (unsigned long long)nq->node->node_id, mdb_strerror(rc)); mdb_txn_abort(txn); return -1; }
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB put mdb_txn_commit failed: %s", mdb_strerror(rc)); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_lmdb_del(struct TOPO_GROUP* bgp, uint64_t node_id) {
|
||||
if (!bgp || !bgp->nodeinfo_env) return -1;
|
||||
MDB_val key; key.mv_size = 8; key.mv_data = &node_id;
|
||||
MDB_txn* txn = NULL; int rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB del mdb_txn_begin failed: %s", mdb_strerror(rc)); return -1; }
|
||||
rc = mdb_del(txn, bgp->nodeinfo_dbi, &key, NULL);
|
||||
if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_del node %016llx failed: %s", (unsigned long long)node_id, mdb_strerror(rc)); mdb_txn_abort(txn); return -1; }
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB del mdb_txn_commit failed: %s", mdb_strerror(rc)); return -1; }
|
||||
return 0;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#ifndef TOPO_NODE_LMDB_H
|
||||
#define TOPO_NODE_LMDB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "topo_node.h"
|
||||
|
||||
struct TOPO_GROUP;
|
||||
|
||||
int topo_node_lmdb_init(struct TOPO_GROUP* group, const char* db_path);
|
||||
void topo_node_lmdb_destroy(struct TOPO_GROUP* group);
|
||||
int topo_node_lmdb_put(struct TOPO_GROUP* group, struct TOPO_NODEQ* nq);
|
||||
int topo_node_lmdb_del(struct TOPO_GROUP* group, uint64_t node_id);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,321 @@
|
||||
#include "topo_node_sqlite.h"
|
||||
#include "topo_node.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define PEERS_JOIN_SIG_SIZE 64
|
||||
|
||||
static void sanitize_ch_id(const char* ch_id, char* out, size_t out_sz) {
|
||||
size_t i = 0;
|
||||
while (*ch_id && i < out_sz - 1) {
|
||||
char c = *ch_id++;
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
|| (c >= '0' && c <= '9') || c == '_')
|
||||
out[i++] = c;
|
||||
else
|
||||
out[i++] = '_';
|
||||
}
|
||||
out[i] = '\0';
|
||||
}
|
||||
|
||||
static void peers_table_name(const char* ch_id, char* buf, size_t sz) {
|
||||
char san[64]; sanitize_ch_id(ch_id, san, sizeof(san));
|
||||
snprintf(buf, sz, "peers_%s", san);
|
||||
}
|
||||
|
||||
int topo_node_sqlite_init(sqlite3* db) {
|
||||
if (!db) return -1;
|
||||
|
||||
const char* sql =
|
||||
"CREATE TABLE IF NOT EXISTS nodes ("
|
||||
" node_id INTEGER PRIMARY KEY,"
|
||||
" name TEXT,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_pubkey BLOB,"
|
||||
" last_seen_at INTEGER,"
|
||||
" online INTEGER DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS node_addresses ("
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
" node_id INTEGER NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,"
|
||||
" family INTEGER NOT NULL CHECK(family IN (4, 6)),"
|
||||
" protocol INTEGER NOT NULL DEFAULT 1,"
|
||||
" address BLOB NOT NULL,"
|
||||
" port INTEGER NOT NULL CHECK(port > 0 AND port <= 65535),"
|
||||
" rtt INTEGER,"
|
||||
" is_nat INTEGER DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
"CREATE INDEX IF NOT EXISTS idx_na_node ON node_addresses(node_id);"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS channels ("
|
||||
" channel_id TEXT PRIMARY KEY,"
|
||||
" name TEXT NOT NULL,"
|
||||
" owner_node_id INTEGER,"
|
||||
" is_dm INTEGER DEFAULT 0,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" x25519_privkey BLOB,"
|
||||
" ed25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_privkey BLOB,"
|
||||
" signature BLOB NOT NULL,"
|
||||
" last_read_msg_id INTEGER,"
|
||||
" last_pos_msg_id INTEGER,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");";
|
||||
|
||||
char* err = NULL;
|
||||
int rc = sqlite3_exec(db, sql, NULL, NULL, &err);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "topo_node_sqlite_init: %s", err);
|
||||
sqlite3_free(err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_node_sqlite tables initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_NODEQ* nq) {
|
||||
if (!db || !nq || !nq->node) return -1;
|
||||
struct TOPO_NODE* ni = nq->node;
|
||||
|
||||
sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT OR REPLACE INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, last_seen_at)"
|
||||
" VALUES(?,?,?,?,unixepoch())", -1, &stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)ni->node_id);
|
||||
sqlite3_bind_text(stmt, 2, ni->node_name ? ni->node_name : "", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 3, ni->public_key, 32, SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 4, ni->ed25519_public_key, 32, SQLITE_STATIC);
|
||||
sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
sqlite3_stmt* del_stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, "DELETE FROM node_addresses WHERE node_id=?", -1, &del_stmt, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(del_stmt, 1, (sqlite3_int64)ni->node_id);
|
||||
sqlite3_step(del_stmt); sqlite3_finalize(del_stmt);
|
||||
}
|
||||
|
||||
sqlite3_stmt* addr_stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT INTO node_addresses(node_id, family, protocol, address, port, rtt, is_nat)"
|
||||
" VALUES(?,?,?,?,?,?,?)", -1, &addr_stmt, NULL) == SQLITE_OK) {
|
||||
struct TOPO_ADDR4* a4 = ni->v4_addrs;
|
||||
while (a4) {
|
||||
sqlite3_bind_int64(addr_stmt, 1, (sqlite3_int64)ni->node_id);
|
||||
sqlite3_bind_int(addr_stmt, 2, 4);
|
||||
sqlite3_bind_int(addr_stmt, 3, a4->protocol);
|
||||
sqlite3_bind_blob(addr_stmt, 4, a4->addr, 4, SQLITE_STATIC);
|
||||
sqlite3_bind_int(addr_stmt, 5, a4->port);
|
||||
sqlite3_bind_null(addr_stmt, 6);
|
||||
sqlite3_bind_int(addr_stmt, 7, a4->type == 1 ? 1 : 0);
|
||||
sqlite3_step(addr_stmt); sqlite3_reset(addr_stmt);
|
||||
a4 = a4->next;
|
||||
}
|
||||
struct TOPO_ADDR6* a6 = ni->v6_addrs;
|
||||
while (a6) {
|
||||
sqlite3_bind_int64(addr_stmt, 1, (sqlite3_int64)ni->node_id);
|
||||
sqlite3_bind_int(addr_stmt, 2, 6);
|
||||
sqlite3_bind_int(addr_stmt, 3, a6->protocol);
|
||||
sqlite3_bind_blob(addr_stmt, 4, a6->addr, 16, SQLITE_STATIC);
|
||||
sqlite3_bind_int(addr_stmt, 5, a6->port);
|
||||
sqlite3_bind_null(addr_stmt, 6);
|
||||
sqlite3_bind_int(addr_stmt, 7, a6->type == 1 ? 1 : 0);
|
||||
sqlite3_step(addr_stmt); sqlite3_reset(addr_stmt);
|
||||
a6 = a6->next;
|
||||
}
|
||||
sqlite3_finalize(addr_stmt);
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
const char* name, int is_dm, 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) {
|
||||
if (!db || !channel_id || !name || !x25519_pub || !ed25519_pub || !signature) return -1;
|
||||
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[512];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"CREATE TABLE IF NOT EXISTS \"%s\" ("
|
||||
" node_id INTEGER NOT NULL,"
|
||||
" join_sig BLOB NOT NULL,"
|
||||
" creator_sig BLOB,"
|
||||
" comment TEXT,"
|
||||
" joined_at INTEGER DEFAULT (unixepoch()),"
|
||||
" PRIMARY KEY (node_id))", peers_tbl);
|
||||
char* err = NULL;
|
||||
sqlite3_exec(db, sql, NULL, NULL, &err);
|
||||
if (err) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "topo_node_sqlite_channel_put peers: %s", err); sqlite3_free(err); }
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT OR REPLACE INTO channels(channel_id, name, owner_node_id, is_dm,"
|
||||
" x25519_pubkey, x25519_privkey, ed25519_pubkey, ed25519_privkey, signature)"
|
||||
" VALUES(?,?,?,?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, name, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)owner_node_id);
|
||||
sqlite3_bind_int(stmt, 4, is_dm);
|
||||
sqlite3_bind_blob(stmt, 5, x25519_pub, 32, SQLITE_STATIC);
|
||||
if (x25519_priv) sqlite3_bind_blob(stmt, 6, x25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 6);
|
||||
sqlite3_bind_blob(stmt, 7, ed25519_pub, 32, SQLITE_STATIC);
|
||||
if (ed25519_priv) sqlite3_bind_blob(stmt, 8, ed25519_priv, 32, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 8);
|
||||
sqlite3_bind_blob(stmt, 9, signature, 64, SQLITE_STATIC);
|
||||
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
return rc == SQLITE_DONE ? 0 : -1;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
const uint8_t* join_sig, const uint8_t* creator_sig) {
|
||||
if (!db || !channel_id) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT OR REPLACE INTO \"%s\"(node_id, join_sig, creator_sig) VALUES(?,?,?)", peers_tbl);
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
if (join_sig) sqlite3_bind_blob(stmt, 2, join_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC);
|
||||
else { static const unsigned char zsig[PEERS_JOIN_SIG_SIZE] = {0}; sqlite3_bind_blob(stmt, 2, zsig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); }
|
||||
if (creator_sig) sqlite3_bind_blob(stmt, 3, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC);
|
||||
else sqlite3_bind_null(stmt, 3);
|
||||
int rc = sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
return rc == SQLITE_DONE ? 0 : -1;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id) {
|
||||
if (!db || !channel_id) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql), "DELETE FROM \"%s\" WHERE node_id=?", peers_tbl);
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
sqlite3_step(stmt); sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
char* name_out, int name_sz, int* is_dm, uint64_t* owner_node_id,
|
||||
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature) {
|
||||
if (!db || !channel_id) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT name, owner_node_id, is_dm, x25519_pubkey, ed25519_pubkey, signature"
|
||||
" FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC);
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; }
|
||||
if (name_out && name_sz > 0) {
|
||||
const unsigned char* txt = sqlite3_column_text(stmt, 0);
|
||||
if (txt) snprintf(name_out, name_sz, "%s", txt); else name_out[0] = '\0';
|
||||
}
|
||||
if (owner_node_id) *owner_node_id = (uint64_t)sqlite3_column_int64(stmt, 1);
|
||||
if (is_dm) *is_dm = sqlite3_column_int(stmt, 2);
|
||||
if (x25519_pub) memcpy(x25519_pub, sqlite3_column_blob(stmt, 3), 32);
|
||||
if (ed25519_pub) memcpy(ed25519_pub, sqlite3_column_blob(stmt, 4), 32);
|
||||
if (signature) memcpy(signature, sqlite3_column_blob(stmt, 5), 64);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
uint8_t* buf, size_t buf_sz, size_t* out_len) {
|
||||
if (!db || !channel_id || !buf || !out_len) return -1;
|
||||
char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl));
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
char sql[256];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT p.node_id, p.join_sig, p.creator_sig,"
|
||||
" n.x25519_pubkey, n.ed25519_pubkey"
|
||||
" FROM \"%s\" p LEFT JOIN nodes n ON p.node_id = n.node_id", peers_tbl);
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
|
||||
size_t off = 0;
|
||||
if (off + 2 > buf_sz) { sqlite3_finalize(stmt); return -2; }
|
||||
uint16_t* cnt_ptr = (uint16_t*)(buf + off); off += 2;
|
||||
*cnt_ptr = 0;
|
||||
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
uint64_t node_id = (uint64_t)sqlite3_column_int64(stmt, 0);
|
||||
const uint8_t* join_sig = (const uint8_t*)sqlite3_column_blob(stmt, 1);
|
||||
const uint8_t* x25519 = (const uint8_t*)sqlite3_column_blob(stmt, 3);
|
||||
const uint8_t* ed_pub = (const uint8_t*)sqlite3_column_blob(stmt, 4);
|
||||
if (!join_sig || !x25519 || !ed_pub) continue;
|
||||
|
||||
sqlite3_stmt* ast = NULL;
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT family, address, port FROM node_addresses"
|
||||
" WHERE node_id=? AND is_nat=0 LIMIT 16");
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &ast, NULL) != SQLITE_OK) continue;
|
||||
sqlite3_bind_int64(ast, 1, (sqlite3_int64)node_id);
|
||||
|
||||
uint8_t addr_cnt = 0; size_t addr_start = 0;
|
||||
while (sqlite3_step(ast) == SQLITE_ROW && addr_cnt < 255) {
|
||||
int family = sqlite3_column_int(ast, 0);
|
||||
int ip_len = (family == 4) ? 4 : 16;
|
||||
const uint8_t* ip = (const uint8_t*)sqlite3_column_blob(ast, 1);
|
||||
uint16_t port = (uint16_t)sqlite3_column_int(ast, 2);
|
||||
if (!ip) continue;
|
||||
if (off + 1 + ip_len + 2 > buf_sz) { sqlite3_finalize(ast); sqlite3_finalize(stmt); return -2; }
|
||||
if (addr_cnt == 0) addr_start = off;
|
||||
buf[off++] = (uint8_t)family;
|
||||
memcpy(buf + off, ip, (size_t)ip_len); off += (size_t)ip_len;
|
||||
buf[off++] = (uint8_t)((port >> 8) & 0xFF);
|
||||
buf[off++] = (uint8_t)(port & 0xFF);
|
||||
addr_cnt++;
|
||||
}
|
||||
sqlite3_finalize(ast);
|
||||
if (addr_cnt == 0) continue;
|
||||
|
||||
size_t peer_needed = 8 + 32 + 32 + 64 + 1;
|
||||
size_t addr_sz = off - addr_start;
|
||||
if (off + peer_needed > buf_sz) { sqlite3_finalize(stmt); return -2; }
|
||||
memmove(buf + addr_start + peer_needed, buf + addr_start, addr_sz);
|
||||
off = addr_start;
|
||||
memcpy(buf + off, &node_id, 8); off += 8;
|
||||
memcpy(buf + off, x25519, 32); off += 32;
|
||||
memcpy(buf + off, ed_pub, 32); off += 32;
|
||||
memcpy(buf + off, join_sig, 64); off += 64;
|
||||
buf[off++] = addr_cnt;
|
||||
off += addr_sz;
|
||||
(*cnt_ptr)++;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
*out_len = off;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_node_set_online(sqlite3* db, uint64_t node_id, int online) {
|
||||
if (!db) return -1;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, "UPDATE nodes SET online=? WHERE node_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
|
||||
sqlite3_bind_int(stmt, 1, online ? 1 : 0);
|
||||
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)node_id);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_node_sqlite_node_get_online(sqlite3* db, uint64_t node_id) {
|
||||
if (!db) return 0;
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(db, "SELECT online FROM nodes WHERE node_id=?", -1, &stmt, NULL) != SQLITE_OK) return 0;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id);
|
||||
int online = 0;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) online = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return online;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef TOPO_NODE_SQLITE_H
|
||||
#define TOPO_NODE_SQLITE_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct TOPO_NODEQ;
|
||||
|
||||
#define PEERS_JOIN_SIG_SIZE 64
|
||||
|
||||
int topo_node_sqlite_init(sqlite3* db);
|
||||
|
||||
int topo_node_sqlite_node_put(sqlite3* db, struct TOPO_NODEQ* nq);
|
||||
|
||||
int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id,
|
||||
const char* name, int is_dm, 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);
|
||||
|
||||
int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t node_id,
|
||||
const uint8_t* join_sig, const uint8_t* creator_sig);
|
||||
|
||||
int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id);
|
||||
|
||||
int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id,
|
||||
char* name_out, int name_sz, int* is_dm, uint64_t* owner_node_id,
|
||||
uint8_t* x25519_pub, uint8_t* ed25519_pub, uint8_t* signature);
|
||||
|
||||
int topo_node_sqlite_channel_peers_all(sqlite3* db, const char* channel_id,
|
||||
uint8_t* buf, size_t buf_sz, size_t* out_len);
|
||||
|
||||
int topo_node_sqlite_node_set_online(sqlite3* db, uint64_t node_id, int online);
|
||||
int topo_node_sqlite_node_get_online(sqlite3* db, uint64_t node_id);
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user