schema: rm is_dm, rename name→userinfo (json_flat), fix join_msg format

- Remove is_dm column from channels table (dm detection by member count)
- Rename member name column to userinfo — extensible json_flat format
- Add json_flat.c/h library for compact JSON serialization
- Fix join_sig message format: use ch_x25519+ch_ed25519 instead of channel_id string
- Remove name from join_msg body (now in userinfo via json_flat)
- Update chat_core, chat_sync, member_sync, db_manager to new format
- Add topo_strategy.txt — архитектурные заметки по топологии
This commit is contained in:
Evgeny committed 2026-07-21 00:11:50 +03:00
1 parent 88d51349ea
commit 3dda701c67
26 files changed
+494 -325

No files matched your search

+9 -10
View File
@@ -95,7 +95,7 @@ QByteArray DbManager::colBlob(sqlite3_stmt* stmt, int col) {
QList<ChannelRow> DbManager::getChannels() const {
QList<ChannelRow> list;
sqlite3_stmt* stmt = prepareOrNull(
"SELECT channel_id, name, owner_node_id, is_dm,"
"SELECT channel_id, name, owner_node_id,"
" x25519_pubkey, x25519_privkey, ed25519_pubkey, ed25519_privkey, signature,"
" last_read_msg_id, last_pos_msg_id, created_at"
" FROM channels ORDER BY created_at ASC");
@@ -105,15 +105,14 @@ QList<ChannelRow> DbManager::getChannels() const {
r.channelId = colText(stmt, 0);
r.name = colText(stmt, 1);
r.ownerNodeId = (quint64)sqlite3_column_int64(stmt, 2);
r.isDm = sqlite3_column_int(stmt, 3) != 0;
r.x25519_pubkey = colBlob(stmt, 4);
r.x25519_privkey = colBlob(stmt, 5);
r.ed25519_pubkey = colBlob(stmt, 6);
r.ed25519_privkey = colBlob(stmt, 7);
r.signature = colBlob(stmt, 8);
r.lastReadMsgId = sqlite3_column_int64(stmt, 9);
r.lastPosMsgId = sqlite3_column_int64(stmt, 10);
r.createdAt = sqlite3_column_int64(stmt, 11);
r.x25519_pubkey = colBlob(stmt, 3);
r.x25519_privkey = colBlob(stmt, 4);
r.ed25519_pubkey = colBlob(stmt, 5);
r.ed25519_privkey = colBlob(stmt, 6);
r.signature = colBlob(stmt, 7);
r.lastReadMsgId = sqlite3_column_int64(stmt, 8);
r.lastPosMsgId = sqlite3_column_int64(stmt, 9);
r.createdAt = sqlite3_column_int64(stmt, 10);
list.append(r);
}
sqlite3_finalize(stmt);
-2
View File
@@ -12,8 +12,6 @@ struct ChannelRow {
QString channelId;
QString name;
quint64 ownerNodeId = 0;
bool isDm = false;
QByteArray x25519_pubkey;
QByteArray x25519_privkey;
QByteArray ed25519_pubkey;
-2
View File
@@ -109,7 +109,6 @@ 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,
@@ -126,7 +125,6 @@ CREATE TABLE IF NOT EXISTS channels (
| `channel_id` | Уникальный ID канала (напр. `"ch:general"`) |
| `name` | Отображаемое имя (напр. `"#general"`) |
| `owner_node_id` | ID создателя канала |
| `is_dm` | 1 = личная переписка (direct message) |
| `x25519_pubkey` | Публичный ключ канала (32 байта) |
| `x25519_privkey` | Приватный ключ канала, NULL если локальный узел не админ |
| `ed25519_pubkey` | Публичный ключ подписи канала (32 байта) |
+1
View File
@@ -22,6 +22,7 @@ set(UASYNC_SOURCES
${LIB_DIR}/mem.c
${LIB_DIR}/radix.c
${LIB_DIR}/serialize.c
${LIB_DIR}/json_flat.c
${LIB_DIR}/tcp_io.c
${LIB_DIR}/sqlite3.c
${LIB_DIR}/liblmdb/mdb.c
-1
View File
@@ -477,7 +477,6 @@ void MainWindow::onCreateGroupRequested() {
snprintf(req->channel_id, sizeof(req->channel_id), "%s", qPrintable(channelId));
snprintf(req->name, sizeof(req->name), "%s", qPrintable(name));
req->owner_node_id = ownerNodeId;
req->is_dm = 0;
memcpy(req->x25519_pubkey, x25519_pub, 32);
memcpy(req->x25519_privkey, x25519_priv, 32);
memcpy(req->ed25519_pubkey, ed25519_pub, 32);
+16 -12
View File
@@ -9,6 +9,7 @@
#include "chat_conn_mgr.h"
#include "db_sync.h"
#include "gui_bridge.h"
#include "../../../lib/json_flat.h"
#include "topo_node_sqlite.h"
#include "member_sync.h"
@@ -275,11 +276,13 @@ void chat_core_update_my_name(const char* name) {
/* recompute join_sigs for all channels where I'm a member */
uint64_t myid = g_cc.my_node_id;
sqlite3_stmt* cs = NULL;
sqlite3_prepare_v2(g_cc.db, "SELECT channel_id FROM channels", -1, &cs, NULL);
sqlite3_prepare_v2(g_cc.db, "SELECT channel_id, x25519_pubkey, ed25519_pubkey FROM channels", -1, &cs, NULL);
if (cs) {
while (sqlite3_step(cs) == SQLITE_ROW) {
const char* ch = (const char*)sqlite3_column_text(cs, 0);
if (!ch) continue;
const uint8_t* ch_x25519 = sqlite3_column_blob(cs, 1);
const uint8_t* ch_ed25519 = sqlite3_column_blob(cs, 2);
if (!ch || !ch_x25519 || !ch_ed25519) continue;
char tbl[80]; peers_table_name(ch, tbl, sizeof(tbl));
char buf[256]; snprintf(buf, sizeof(buf), "SELECT 1 FROM \"%s\" WHERE node_id=?", tbl);
sqlite3_stmt* ps = NULL;
@@ -287,11 +290,10 @@ void chat_core_update_my_name(const char* name) {
sqlite3_bind_int64(ps, 1, (sqlite3_int64)myid);
if (sqlite3_step(ps) == SQLITE_ROW) {
uint8_t join_msg[256]; size_t mlen = 0;
mlen += snprintf((char*)join_msg + mlen, sizeof(join_msg) - mlen, "%s", ch) + 1;
memcpy(join_msg + mlen, ch_x25519, 32); mlen += 32;
memcpy(join_msg + mlen, ch_ed25519, 32); mlen += 32;
memcpy(join_msg + mlen, &myid, 8); mlen += 8;
memcpy(join_msg + mlen, g_cc.inst->my_keys.public_key, 32); mlen += 32;
{ const char* nm = name[0] ? name : "";
size_t nl = strlen(nm); memcpy(join_msg + mlen, nm, nl); mlen += nl; join_msg[mlen++] = '\0'; }
uint64_t join_ts = (uint64_t)ntp_time_get_seconds(g_cc.inst);
memcpy(join_msg + mlen, &join_ts, 8); mlen += 8;
uint8_t new_sig[64]; memset(new_sig, 0, 64);
@@ -306,11 +308,12 @@ void chat_core_update_my_name(const char* name) {
}
EVP_PKEY_free(pkey);
}
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", name);
int r1 = topo_node_sqlite_member_put(g_cc.db, ch, myid, new_sig, join_ts, NULL, 0,
g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, name, NULL);
g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, juser, NULL);
if (r1 != 0) DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: member_put(my_name) FAILED ch=%s rc=%d", CC_ID, ch, r1);
int r2 = member_sync_put(g_cc.inst, ch, myid, g_cc.inst->my_keys.public_key,
g_cc.inst->my_ed25519_pubkey, new_sig, join_ts, NULL, 0, name, NULL, 0);
g_cc.inst->my_ed25519_pubkey, new_sig, join_ts, NULL, 0, juser, NULL, 0);
if (r2 != 0) DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: member_sync_put(my_name) FAILED ch=%s rc=%d", CC_ID, ch, r2);
uint8_t evt[65]; uint8_t cl = (uint8_t)strlen(ch);
evt[0] = cl; memcpy(evt + 1, ch, cl);
@@ -695,7 +698,7 @@ void chat_core_create_channel(struct chat_channel_create* req) {
/* записываем канал в БД ПЕРВЫМ — создаст полную схему peers_<ch_id> (11 колонок) */
int rc = topo_node_sqlite_channel_put(g_cc.db,
req->channel_id, req->name, req->is_dm, req->owner_node_id,
req->channel_id, req->name, req->owner_node_id,
req->x25519_pubkey, req->x25519_privkey,
req->ed25519_pubkey, req->ed25519_privkey,
req->signature);
@@ -719,11 +722,10 @@ void chat_core_create_channel(struct chat_channel_create* req) {
{
uint64_t myid = g_cc.inst->node_id;
uint8_t join_msg[256]; size_t mlen = 0;
mlen += snprintf((char*)join_msg + mlen, sizeof(join_msg) - mlen, "%s", req->channel_id) + 1;
memcpy(join_msg + mlen, req->x25519_pubkey, 32); mlen += 32;
memcpy(join_msg + mlen, req->ed25519_pubkey, 32); mlen += 32;
memcpy(join_msg + mlen, &myid, 8); mlen += 8;
memcpy(join_msg + mlen, g_cc.inst->my_keys.public_key, 32); mlen += 32;
{ const char* nm = g_cc.inst->name[0] ? g_cc.inst->name : "";
size_t nl = strlen(nm); memcpy(join_msg + mlen, nm, nl); mlen += nl; join_msg[mlen++] = '\0'; }
uint64_t join_ts = (uint64_t)ntp_time_get_seconds(g_cc.inst);
memcpy(join_msg + mlen, &join_ts, 8); mlen += 8;
uint8_t join_sig[64];
@@ -760,9 +762,11 @@ void chat_core_create_channel(struct chat_channel_create* req) {
s = s->next;
}
}
char juser3[256]; snprintf(juser3, sizeof(juser3), "{\"name\":\"%s\"}",
g_cc.inst->name[0] ? g_cc.inst->name : "");
int mrc = member_sync_put(g_cc.inst, req->channel_id, myid,
g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey,
join_sig, join_ts, NULL, 0, g_cc.inst->name, my_addrs, my_addr_cnt);
join_sig, join_ts, NULL, 0, juser3, my_addrs, my_addr_cnt);
if (mrc != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: member_sync_put(self) FAILED ch=%s rc=%d",
CC_ID, req->channel_id, mrc);
-1
View File
@@ -51,7 +51,6 @@ struct chat_channel_create {
char channel_id[64];
char name[128];
uint64_t owner_node_id;
int is_dm;
uint8_t x25519_pubkey[32];
uint8_t x25519_privkey[32];
uint8_t ed25519_pubkey[32];
+82 -65
View File
@@ -3,6 +3,7 @@
#include "chat_conn_mgr.h"
#include "gui_bridge.h"
#include "topo_node_sqlite.h"
#include "../../../lib/json_flat.h"
#include "member_sync.h"
#include "merkle_sync.h"
@@ -916,9 +917,9 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer,
const char* ch_id, const uint8_t* pl, size_t len) {
(void)pl; (void)len;
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: INFO_REQ ch=%s from=%016llx", CS_ID, ch_id, (unsigned long long)peer);
char name[128]; int is_dm; uint64_t owner; uint8_t x25519[32], ed_pub[32], ch_sig[64];
char name[128]; uint64_t owner; uint8_t x25519[32], ed_pub[32], ch_sig[64];
if (topo_node_sqlite_channel_get(cs->inst->topo_sqlite_db,
ch_id, name, (int)sizeof(name), &is_dm, &owner, x25519, ed_pub, ch_sig) != 0) {
ch_id, name, (int)sizeof(name), &owner, x25519, ed_pub, ch_sig) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_REQ unknown ch=%s from=%016llx", CS_ID, ch_id, (unsigned long long)peer);
uint8_t err[1] = { CS_MSG_ERROR }; cs_send(cs, ch_id, peer, err, 1);
return;
@@ -933,7 +934,8 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer,
sqlite3* vdb = cs->inst->topo_sqlite_db;
if (topo_node_sqlite_member_get_join(vdb, ch_id, myid, my_join_sig, &my_join_ts) != 0) {
uint8_t join_msg[256]; size_t mlen = 0;
mlen += snprintf((char*)join_msg + mlen, sizeof(join_msg) - mlen, "%s", ch_id) + 1;
memcpy(join_msg + mlen, x25519, 32); mlen += 32;
memcpy(join_msg + mlen, ed_pub, 32); mlen += 32;
memcpy(join_msg + mlen, &myid, 8); mlen += 8;
memcpy(join_msg + mlen, cs->inst->my_keys.public_key, 32); mlen += 32;
my_join_ts = (uint64_t)ntp_time_get_seconds(cs->inst);
@@ -946,13 +948,13 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer,
/* generate update_sig */
uint8_t my_update_sig[64] = {0}; uint64_t my_update_ts;
{
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}",
cs->inst->name[0] ? cs->inst->name : "");
uint8_t umsg[256]; size_t ulen = 0;
ulen += snprintf((char*)umsg + ulen, sizeof(umsg) - ulen, "%s", ch_id) + 1;
memcpy(umsg + ulen, &myid, 8); ulen += 8;
memcpy(umsg + ulen, cs->inst->my_keys.public_key, 32); ulen += 32;
memcpy(umsg + ulen, my_join_sig, 64); ulen += 64;
my_update_ts = (uint64_t)ntp_time_get_seconds(cs->inst);
memcpy(umsg + ulen, &my_update_ts, 8); ulen += 8;
memcpy(umsg + ulen, my_join_sig, 64); ulen += 64;
size_t jl = strlen(juser); memcpy(umsg + ulen, juser, jl); umsg[ulen + jl] = '\0'; ulen += jl + 1;
cs_ed25519_sign(cs->inst->my_ed25519_privkey, umsg, ulen, my_update_sig);
}
@@ -961,7 +963,6 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer,
uint8_t nl = (uint8_t)strlen(name);
buf[boff++] = nl; memcpy(buf + boff, name, nl); boff += nl;
memcpy(buf + boff, &owner, 8); boff += 8;
buf[boff++] = (uint8_t)is_dm;
memcpy(buf + boff, x25519, 32); boff += 32;
memcpy(buf + boff, ed_pub, 32); boff += 32;
memcpy(buf + boff, ch_sig, 64); boff += 64;
@@ -973,9 +974,10 @@ static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer,
}
memcpy(buf + boff, my_update_sig, 64); boff += 64;
memcpy(buf + boff, &my_update_ts, 8); boff += 8;
{ const char* iname = cs->inst->name[0] ? cs->inst->name : "";
uint8_t il = (uint8_t)strlen(iname);
buf[boff++] = il; memcpy(buf + boff, iname, il); boff += il; }
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}",
cs->inst->name[0] ? cs->inst->name : "");
uint8_t il = (uint8_t)strlen(juser);
buf[boff++] = il; memcpy(buf + boff, juser, il); boff += il;
cs_send(cs, ch_id, peer, buf, boff);
}
@@ -987,11 +989,10 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
if (cs->info_req_timer) { uasync_cancel_timeout(cs->inst->ua, cs->info_req_timer); cs->info_req_timer = NULL; }
if (len < 1) { DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_RESP too short len=%zu peer=%016llx", CS_ID, len, (unsigned long long)peer); return; }
uint8_t nl = pl[0];
if (1 + nl + 8 + 1 + 32 + 32 + 64 + 1 + 64 + 8 + 1 > len) { DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_RESP truncated len=%zu", CS_ID, len); return; }
if (1 + nl + 8 + 32 + 32 + 64 + 1 + 64 + 8 + 1 > len) { DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_RESP truncated len=%zu", CS_ID, len); return; }
const uint8_t* p = pl + 1;
char name[128]; memcpy(name, p, nl); name[nl] = '\0'; p += nl;
uint64_t owner; memcpy(&owner, p, 8); p += 8;
uint8_t is_dm = *p++;
const uint8_t* x25519 = p; p += 32;
const uint8_t* ed_pub = p; p += 32;
const uint8_t* ch_sig = p; p += 64;
@@ -1001,9 +1002,9 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
if (p + 72 > pl + len) return; inviter_join_sig = p; p += 64; memcpy(&inviter_join_ts, p, 8); p += 8; }
const uint8_t* inviter_update_sig = p; p += 64; uint64_t inviter_update_ts;
memcpy(&inviter_update_ts, p, 8); p += 8;
uint8_t inv_name_len = *p++;
char inv_name[128] = "";
if (p + inv_name_len <= pl + len) { memcpy(inv_name, p, inv_name_len); inv_name[inv_name_len] = '\0'; }
uint8_t inv_userinfo_len = *p++;
char inv_userinfo[128] = "";
if (p + inv_userinfo_len <= pl + len) { memcpy(inv_userinfo, p, inv_userinfo_len); inv_userinfo[inv_userinfo_len] = '\0'; }
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_RESP from peer=%016llx owner=%016llx ch_x25519=%016llx ch_ed=%016llx name=%s",
CS_ID, (unsigned long long)peer, (unsigned long long)owner,
@@ -1023,7 +1024,7 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
/* save channel to local DB */
topo_node_sqlite_channel_put(cs->inst->topo_sqlite_db,
ch_id, name, (int)is_dm, owner, x25519, NULL, ed_pub, NULL, ch_sig);
ch_id, name, owner, x25519, NULL, ed_pub, NULL, ch_sig);
chat_core_ensure_channel_ready(ch_id);
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: channel ready for sync ch=%s, db_sync will pick up via periodic check or active conn",
CS_ID, ch_id);
@@ -1037,21 +1038,18 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
const uint8_t* inv_x25519 = inv_conn->crypto_ctx.peer_public_key;
const uint8_t* inv_ed = inv_conn->peer_ed25519_pubkey;
uint8_t ivmsg[256]; size_t ilen = 0;
ilen += snprintf((char*)ivmsg + ilen, sizeof(ivmsg) - ilen, "%s", ch_id) + 1;
memcpy(ivmsg + ilen, &peer, 8); ilen += 8;
memcpy(ivmsg + ilen, inv_x25519, 32); ilen += 32;
memcpy(ivmsg + ilen, &inviter_update_ts, 8); ilen += 8;
{ const char* nm = inv_name[0] ? inv_name : "";
size_t nl2 = strlen(nm); (void)nl2; }
if (inviter_join_sig) memcpy(ivmsg + ilen, inviter_join_sig, 64); else memset(ivmsg + ilen, 0, 64);
ilen += 64;
memcpy(ivmsg + ilen, &inviter_update_ts, 8); ilen += 8;
{ const char* nm = inv_userinfo[0] ? inv_userinfo : "";
size_t nl2 = strlen(nm); memcpy(ivmsg + ilen, nm, nl2 + 1); ilen += nl2 + 1; }
if (cs_ed25519_verify(inv_ed, ivmsg, ilen, inviter_update_sig) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_RESP invalid inviter_update_sig peer=%016llx ts=%llu",
CS_ID, (unsigned long long)peer, (unsigned long long)inviter_update_ts);
}
/* save inviter node_info to local DB */
sqlite3* vdb = cs->inst->topo_sqlite_db;
if (vdb && inv_name[0]) {
if (vdb && inv_userinfo[0]) {
sqlite3_stmt* ns = NULL;
sqlite3_prepare_v2(vdb,
"INSERT INTO nodes(node_id,name,x25519_pubkey,ed25519_pubkey) VALUES(?,?,?,?)"
@@ -1059,7 +1057,7 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
" name=excluded.name, x25519_pubkey=excluded.x25519_pubkey, ed25519_pubkey=excluded.ed25519_pubkey",
-1, &ns, NULL);
if (ns) { sqlite3_bind_int64(ns, 1, (sqlite3_int64)peer);
sqlite3_bind_text(ns, 2, inv_name, -1, SQLITE_STATIC);
sqlite3_bind_text(ns, 2, inv_userinfo, -1, SQLITE_STATIC);
sqlite3_bind_blob(ns, 3, inv_x25519, 32, SQLITE_STATIC);
sqlite3_bind_blob(ns, 4, inv_ed, 32, SQLITE_STATIC);
sqlite3_step(ns); sqlite3_finalize(ns); }
@@ -1093,8 +1091,9 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] INFO_RESP DONE peer=%016llx: %d links, %d written",
CS_ID, (unsigned long long)peer, lk_count, lk_written);
}
int rc = topo_node_sqlite_member_put(vdb, ch_id, peer, inviter_join_sig, inviter_join_ts, inviter_update_sig, inviter_update_ts, inv_x25519, inv_ed, inv_name, inviter_join_sig);
int rc = topo_node_sqlite_member_put(vdb, ch_id, peer, inviter_join_sig, inviter_join_ts, inviter_update_sig, inviter_update_ts, inv_x25519, inv_ed, inv_userinfo, inviter_join_sig);
if (rc != 0) DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_put(inviter) FAILED ch=%s peer=%016llx rc=%d", CS_ID, ch_id, (unsigned long long)peer, rc);
char inv_name[128]; json_flat_get(inv_userinfo, "name", inv_name, sizeof(inv_name));
topo_node_sqlite_node_update_verified(vdb, peer, inv_name, inv_x25519, inv_ed, inviter_join_ts);
}
@@ -1105,11 +1104,10 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
uint8_t join_sig[64]; uint64_t join_ts;
{
uint8_t msg[256]; size_t mlen = 0;
mlen += snprintf((char*)msg + mlen, sizeof(msg) - mlen, "%s", ch_id) + 1;
memcpy(msg + mlen, x25519, 32); mlen += 32;
memcpy(msg + mlen, ed_pub, 32); mlen += 32;
memcpy(msg + mlen, &myid, 8); mlen += 8;
memcpy(msg + mlen, my_x25519, 32); mlen += 32;
{ const char* nm = cs->inst->name[0] ? cs->inst->name : "";
size_t nl = strlen(nm); memcpy(msg + mlen, nm, nl); mlen += nl; msg[mlen++] = '\0'; }
join_ts = (uint64_t)ntp_time_get_seconds(cs->inst);
memcpy(msg + mlen, &join_ts, 8); mlen += 8;
cs_ed25519_sign(cs->inst->my_ed25519_privkey, msg, mlen, join_sig);
@@ -1123,9 +1121,10 @@ static void cs_handle_channel_info_resp(struct chat_sync* cs, uint64_t peer,
memcpy(jbuf + joff, cs->inst->my_ed25519_pubkey, 32); joff += 32;
memcpy(jbuf + joff, join_sig, 64); joff += 64;
memcpy(jbuf + joff, &join_ts, 8); joff += 8;
{ const char* nm = cs->inst->name[0] ? cs->inst->name : "";
uint8_t nml = (uint8_t)strlen(nm);
jbuf[joff++] = nml; memcpy(jbuf + joff, nm, nml); joff += nml; }
char juser2[256]; snprintf(juser2, sizeof(juser2), "{\"name\":\"%s\"}",
cs->inst->name[0] ? cs->inst->name : "");
uint8_t nml = (uint8_t)strlen(juser2);
jbuf[joff++] = nml; memcpy(jbuf + joff, juser2, nml); joff += nml;
/* add local addresses */
uint8_t addr_cnt = 0; size_t ac_pos = joff;
jbuf[joff++] = 0;
@@ -1178,33 +1177,39 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer,
const uint8_t* ed_pub = p; p += 32;
const uint8_t* join_sig = p; p += 64;
uint64_t join_ts; memcpy(&join_ts, p, 8); p += 8;
uint8_t name_len = *p++;
char joiner_name[128] = "";
if (p + name_len <= pl + len) { memcpy(joiner_name, p, name_len); joiner_name[name_len] = '\0'; p += name_len; }
uint8_t userinfo_len = *p++;
char joiner_userinfo[128] = "";
if (p + userinfo_len <= pl + len) { memcpy(joiner_userinfo, p, userinfo_len); joiner_userinfo[userinfo_len] = '\0'; p += userinfo_len; }
uint8_t addr_cnt = *p++;
/* verify join_sig */
uint8_t ch_x25519[32], ch_ed25519[32];
if (topo_node_sqlite_channel_get(cs->inst->topo_sqlite_db, ch_id,
NULL, 0, NULL, ch_x25519, ch_ed25519, NULL) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: JOIN channel not in DB ch=%s", CS_ID, ch_id);
return;
}
uint8_t vmsg[256]; size_t vlen = 0;
vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_id) + 1;
memcpy(vmsg + vlen, ch_x25519, 32); vlen += 32;
memcpy(vmsg + vlen, ch_ed25519, 32); vlen += 32;
memcpy(vmsg + vlen, &node_id, 8); vlen += 8;
memcpy(vmsg + vlen, x25519, 32); vlen += 32;
{ const char* nm = joiner_name[0] ? joiner_name : "";
size_t nl = strlen(nm); memcpy(vmsg + vlen, nm, nl); vlen += nl; vmsg[vlen++] = '\0'; }
memcpy(vmsg + vlen, &join_ts, 8); vlen += 8;
if (cs_ed25519_verify(ed_pub, vmsg, vlen, join_sig) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: JOIN invalid sig node=0x%016llx ch=%s — signed(node=%016llx x25519=%016llx ed=%016llx name=%s ts=%llu)", CS_ID,
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: JOIN invalid sig node=0x%016llx ch=%s — signed(node=%016llx x25519=%016llx ed=%016llx ts=%llu)", CS_ID,
(unsigned long long)node_id, ch_id,
(unsigned long long)node_id, *(const uint64_t*)x25519, *(const uint64_t*)ed_pub, joiner_name, (unsigned long long)join_ts);
(unsigned long long)node_id, *(const uint64_t*)x25519, *(const uint64_t*)ed_pub, (unsigned long long)join_ts);
return;
}
sqlite3* db = cs->inst->topo_sqlite_db;
int rc = topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, joiner_name, NULL);
int rc = topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, joiner_userinfo, NULL);
if (rc != 0) DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_put(joiner) FAILED ch=%s node=0x%016llx rc=%d", CS_ID, ch_id, (unsigned long long)node_id, rc);
topo_node_sqlite_node_update_verified(db, node_id, joiner_name, x25519, ed_pub, join_ts);
{ char j_name[128]; json_flat_get(joiner_userinfo, "name", j_name, sizeof(j_name));
topo_node_sqlite_node_update_verified(db, node_id, j_name, x25519, ed_pub, join_ts); }
chat_conn_mgr_add_node(chat_conn_mgr_get(), node_id);
/* save joiner node_info to local DB */
if (db && joiner_name[0]) {
if (db && joiner_userinfo[0]) {
sqlite3_stmt* ns = NULL;
sqlite3_prepare_v2(db,
"INSERT INTO nodes(node_id,name,x25519_pubkey,ed25519_pubkey) VALUES(?,?,?,?)"
@@ -1212,7 +1217,7 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer,
" name=excluded.name, x25519_pubkey=excluded.x25519_pubkey, ed25519_pubkey=excluded.ed25519_pubkey",
-1, &ns, NULL);
if (ns) { sqlite3_bind_int64(ns, 1, (sqlite3_int64)node_id);
sqlite3_bind_text(ns, 2, joiner_name, -1, SQLITE_STATIC);
sqlite3_bind_text(ns, 2, joiner_userinfo, -1, SQLITE_STATIC);
sqlite3_bind_blob(ns, 3, x25519, 32, SQLITE_STATIC);
sqlite3_bind_blob(ns, 4, ed_pub, 32, SQLITE_STATIC);
sqlite3_step(ns); sqlite3_finalize(ns); }
@@ -1271,12 +1276,12 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer,
memcpy(ubuf + uoff, ed_pub, 32); uoff += 32;
memcpy(ubuf + uoff, join_sig, 64); uoff += 64;
memcpy(ubuf + uoff, &join_ts, 8); uoff += 8;
ubuf[uoff++] = name_len;
memcpy(ubuf + uoff, joiner_name, name_len); uoff += name_len;
ubuf[uoff++] = userinfo_len;
memcpy(ubuf + uoff, joiner_userinfo, userinfo_len); uoff += userinfo_len;
ubuf[uoff++] = addr_cnt;
size_t addr_data_sz = (size_t)(p - (pl + 8 + 32 + 32 + 64 + 8 + 1 + name_len + 1));
size_t addr_data_sz = (size_t)(p - (pl + 8 + 32 + 32 + 64 + 8 + 1 + userinfo_len + 1));
if (uoff + addr_data_sz <= sizeof(ubuf)) {
memcpy(ubuf + uoff, pl + 8 + 32 + 32 + 64 + 8 + 1 + name_len + 1, addr_data_sz);
memcpy(ubuf + uoff, pl + 8 + 32 + 32 + 64 + 8 + 1 + userinfo_len + 1, addr_data_sz);
uoff += addr_data_sz;
}
cs_propagate(cs, ch_id, peer, ubuf, uoff);
@@ -1336,18 +1341,23 @@ static void cs_handle_welcome(struct chat_sync* cs, uint64_t peer,
uint64_t update_ts; memcpy(&update_ts, p, 8); p += 8;
(void)update_sig; (void)update_ts;
uint8_t nl = *p++;
char peer_name[256] = "";
if (nl && p + nl <= pl + len) { memcpy(peer_name, p, nl); peer_name[nl] = '\0'; p += nl; }
char peer_userinfo[256] = "";
if (nl && p + nl <= pl + len) { memcpy(peer_userinfo, p, nl); peer_userinfo[nl] = '\0'; p += nl; }
uint8_t ac = *p++;
/* verify join_sig if present */
if (join_sig) {
uint8_t ch_x2[32], ch_ed2[32];
if (topo_node_sqlite_channel_get(db, ch_id,
NULL, 0, NULL, ch_x2, ch_ed2, NULL) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: WELCOME channel not in DB ch=%s", CS_ID, ch_id);
continue;
}
uint8_t vmsg[256]; size_t vlen = 0;
vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_id) + 1;
memcpy(vmsg + vlen, ch_x2, 32); vlen += 32;
memcpy(vmsg + vlen, ch_ed2, 32); vlen += 32;
memcpy(vmsg + vlen, &node_id, 8); vlen += 8;
memcpy(vmsg + vlen, x25519, 32); vlen += 32;
{ const char* nm = peer_name[0] ? peer_name : "";
size_t nls = strlen(nm); memcpy(vmsg + vlen, nm, nls); vlen += nls; vmsg[vlen++] = '\0'; }
memcpy(vmsg + vlen, &join_ts, 8); vlen += 8;
EVP_PKEY* pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed_pub, 32);
if (pkey) {
@@ -1365,9 +1375,10 @@ static void cs_handle_welcome(struct chat_sync* cs, uint64_t peer,
}
}
int rc = topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, peer_name, NULL);
int rc = topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, peer_userinfo, NULL);
if (rc != 0) DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_put(welcome) FAILED ch=%s node=0x%016llx rc=%d", CS_ID, ch_id, (unsigned long long)node_id, rc);
topo_node_sqlite_node_update_verified(db, node_id, peer_name, x25519, ed_pub, join_ts);
{ char pn[256]; json_flat_get(peer_userinfo, "name", pn, sizeof(pn));
topo_node_sqlite_node_update_verified(db, node_id, pn, x25519, ed_pub, join_ts); }
chat_conn_mgr_add_node(chat_conn_mgr_get(), node_id);
@@ -1442,18 +1453,23 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer,
const uint8_t* ed_pub = p; p += 32;
const uint8_t* join_sig = p; p += 64;
uint64_t join_ts; memcpy(&join_ts, p, 8); p += 8;
uint8_t name_len = *p++;
char peer_name[128] = "";
if (p + name_len <= pl + len) { memcpy(peer_name, p, name_len); peer_name[name_len] = '\0'; p += name_len; }
uint8_t userinfo_len = *p++;
char peer_userinfo[128] = "";
if (p + userinfo_len <= pl + len) { memcpy(peer_userinfo, p, userinfo_len); peer_userinfo[userinfo_len] = '\0'; p += userinfo_len; }
uint8_t ac = *p++;
/* verify join_sig */
uint8_t ch_x3[32], ch_ed3[32];
if (topo_node_sqlite_channel_get(cs->inst->topo_sqlite_db, ch_id,
NULL, 0, NULL, ch_x3, ch_ed3, NULL) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: PEER_UPSERT channel not in DB ch=%s", CS_ID, ch_id);
return;
}
uint8_t vmsg[256]; size_t vlen = 0;
vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_id) + 1;
memcpy(vmsg + vlen, ch_x3, 32); vlen += 32;
memcpy(vmsg + vlen, ch_ed3, 32); vlen += 32;
memcpy(vmsg + vlen, &node_id, 8); vlen += 8;
memcpy(vmsg + vlen, x25519, 32); vlen += 32;
{ const char* nm = peer_name[0] ? peer_name : "";
size_t nl = strlen(nm); memcpy(vmsg + vlen, nm, nl); vlen += nl; vmsg[vlen++] = '\0'; }
memcpy(vmsg + vlen, &join_ts, 8); vlen += 8;
if (cs_ed25519_verify(ed_pub, vmsg, vlen, join_sig) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: PEER_UPSERT invalid sig node=0x%016llx", CS_ID,
@@ -1463,11 +1479,12 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer,
sqlite3* db = cs->inst->topo_sqlite_db;
int rc = topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, peer_name, NULL);
int rc = topo_node_sqlite_member_put(db, ch_id, node_id, join_sig, join_ts, NULL, 0, x25519, ed_pub, peer_userinfo, NULL);
if (rc != 0) DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_put(peer_upsert) FAILED ch=%s node=0x%016llx rc=%d", CS_ID, ch_id, (unsigned long long)node_id, rc);
topo_node_sqlite_node_update_verified(db, node_id, peer_name, x25519, ed_pub, join_ts);
{ char pn2[128]; json_flat_get(peer_userinfo, "name", pn2, sizeof(pn2));
topo_node_sqlite_node_update_verified(db, node_id, pn2, x25519, ed_pub, join_ts); }
chat_conn_mgr_add_node(chat_conn_mgr_get(), node_id);
if (db && peer_name[0]) {
if (db && peer_userinfo[0]) {
sqlite3_stmt* ns = NULL;
sqlite3_prepare_v2(db,
"INSERT INTO nodes(node_id,name,x25519_pubkey,ed25519_pubkey) VALUES(?,?,?,?)"
@@ -1475,7 +1492,7 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer,
" name=excluded.name, x25519_pubkey=excluded.x25519_pubkey, ed25519_pubkey=excluded.ed25519_pubkey",
-1, &ns, NULL);
if (ns) { sqlite3_bind_int64(ns, 1, (sqlite3_int64)node_id);
sqlite3_bind_text(ns, 2, peer_name, -1, SQLITE_STATIC);
sqlite3_bind_text(ns, 2, peer_userinfo, -1, SQLITE_STATIC);
sqlite3_bind_blob(ns, 3, x25519, 32, SQLITE_STATIC);
sqlite3_bind_blob(ns, 4, ed_pub, 32, SQLITE_STATIC);
sqlite3_step(ns); sqlite3_finalize(ns); }
+8 -10
View File
@@ -139,7 +139,7 @@ static int _member_get_items(void* ctx, const char* ns, uint8_t level,
char sql[512]; snprintf(sql, sizeof(sql),
"SELECT node_id, x25519_pubkey, ed25519_pubkey,"
" join_sig, join_ts, update_sig, update_ts, name"
" join_sig, join_ts, update_sig, update_ts, userinfo"
" FROM \"%s\""
" WHERE (node_id & %lld) == %lld ORDER BY node_id ASC",
peers_tbl, (long long)mask, (long long)prefix);
@@ -254,11 +254,9 @@ static int _member_apply_items(void* ctx, const char* ns,
/* verify update_sig if available */
if (usig && uts && jsig && ed) {
uint8_t vmsg[256]; size_t vlen = 0;
vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ns) + 1;
memcpy(vmsg + vlen, &nid, 8); vlen += 8;
memcpy(vmsg + vlen, x25, 32); vlen += 32;
memcpy(vmsg + vlen, &uts, 8); vlen += 8;
memcpy(vmsg + vlen, jsig, 64); vlen += 64;
memcpy(vmsg + vlen, &uts, 8); vlen += 8;
size_t nl2 = nm ? strlen(nm) : 0; memcpy(vmsg + vlen, nm ? nm : "", nl2 + 1); vlen += nl2 + 1;
{ EVP_PKEY* pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed, 32);
if (pkey) {
EVP_MD_CTX* ver = EVP_MD_CTX_new();
@@ -332,18 +330,18 @@ int member_sync_put(struct UTUN_INSTANCE* inst, const char* ch_id,
const uint8_t* ed25519,
const uint8_t* join_sig, uint64_t join_ts,
const uint8_t* update_sig, uint64_t update_ts,
const char* name,
const char* userinfo,
const uint8_t* addrs_data, int addr_count) {
if (!inst || !ch_id) return -1;
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: put ch=%s nid=%016llx name=%s ac=%d", MS_ID, ch_id, (unsigned long long)member_id, name ? name : "", addr_count);
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: put ch=%s nid=%016llx userinfo=%s ac=%d", MS_ID, ch_id, (unsigned long long)member_id, userinfo ? userinfo : "", addr_count);
sqlite3* db = _db(inst); if (!db) return -1;
int rc = topo_node_sqlite_member_put(db, ch_id, member_id,
join_sig, join_ts, update_sig, update_ts,
x25519, ed25519, name, NULL);
x25519, ed25519, userinfo, NULL);
if (rc != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_put FAILED ch=%s id=0x%016llx name=%s rc=%d",
MS_ID, ch_id, (unsigned long long)member_id, name ? name : "", rc);
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: member_put FAILED ch=%s id=0x%016llx userinfo=%s rc=%d",
MS_ID, ch_id, (unsigned long long)member_id, userinfo ? userinfo : "", rc);
return -1;
}
+3 -3
View File
@@ -84,9 +84,9 @@ void member_sync_cancel(struct UTUN_INSTANCE* inst, uint64_t peer, const char* c
* x25519 — X25519 публичный ключ (32 байта)
* ed25519 — Ed25519 публичный ключ (32 байта)
* join_sig — Ed25519 подпись join-сообщения (64 байта)
* сообщение: ch_id || '\0' || node_id(8LE) || x25519(32) || name || '\0' || join_ts(8LE)
* сообщение: ch_x25519(32) || ch_ed25519(32) || node_id(8LE) || x25519(32) || join_ts(8LE)
* join_ts — NTP-синхронизированное время подписи (unix epoch seconds)
* name — имя узла (из подписанного сообщения)
* userinfo — метаданные узла (имя) в канале
* addrs_data — адреса в wire-формате: [family:1][ip:4|16][port:2]...
* addr_count — количество адресов (0 если нет)
*
@@ -101,7 +101,7 @@ int member_sync_put(struct UTUN_INSTANCE* inst, const char* ch_id,
const uint8_t* ed25519,
const uint8_t* join_sig, uint64_t join_ts,
const uint8_t* update_sig, uint64_t update_ts,
const char* name,
const char* userinfo,
const uint8_t* addrs_data, int addr_count);
/* Удалить мембера из канала и пересчитать дерево. */
+1 -1
View File
@@ -120,7 +120,7 @@ struct merkle_sync_data_ops {
* *len — [in] размер буфера, [out] записанный размер
*
* Wire-формат для мемберов:
* [count:2][node_id:8][x25519:32][ed25519:32][flags:1]([join_sig:64][join_ts:8])[update_sig:64][update_ts:8][name_len:1][name:var][addr_cnt:1][addrs:var]...
* [count:2][node_id:8][x25519:32][ed25519:32][flags:1]([join_sig:64][join_ts:8])[update_sig:64][update_ts:8][userinfo_len:1][userinfo:var][addr_cnt:1][addrs:var]...
*
* Возвращает 0 при успехе, <0 при ошибке, -2 если буфер мал.
*/