From 4e06db77af661ae436422905a57562cb438908da Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 14 Aug 2026 16:25:09 +0300 Subject: [PATCH] chat: gate broadcast on CHANGED, my_name via update_sig, set_online local-only gating --- src/chat/chat_channel.c | 4 +- src/chat/chat_core.c | 4 +- src/chat/chat_profile.c | 84 +++++++++++++++++++++-------------------- src/chat/chat_sync.c | 22 ++++++----- 4 files changed, 62 insertions(+), 52 deletions(-) diff --git a/src/chat/chat_channel.c b/src/chat/chat_channel.c index 5b19f8a8..9422c5b5 100644 --- a/src/chat/chat_channel.c +++ b/src/chat/chat_channel.c @@ -193,9 +193,11 @@ void chat_core_create_channel(struct chat_channel_create* req) { 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, juser3, my_addrs, my_addr_cnt, NULL, NULL, 0); - if (mrc != 0) { + if (mrc < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: member_sync_put(self) FAILED ch=%s rc=%d", CC_ID, req->channel_id, mrc); + } else if (mrc & MS_APPLY_CHANGED) { + member_sync_broadcast_one(g_cc.inst, req->channel_id, myid); } chat_core_sync_my_addresses(); } diff --git a/src/chat/chat_core.c b/src/chat/chat_core.c index 673604b9..fd17c59c 100644 --- a/src/chat/chat_core.c +++ b/src/chat/chat_core.c @@ -669,8 +669,8 @@ int chat_member_tags_commit(struct chat_member_tags* t) { int rc = member_sync_put(g_cc.inst, t->ch_id, t->node_id, x25, ed, jsig, jts, usig, uts, uinfo ? uinfo : "", NULL, 0, json, sig, stg); sqlite3_finalize(st); - if (rc != 0) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_commit — member_sync_put failed rc=%d", CC_ID, rc); return -1; } - member_sync_broadcast_one(g_cc.inst, t->ch_id, t->node_id); + if (rc < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_commit — member_sync_put failed rc=%d", CC_ID, rc); return -1; } + if (rc & MS_APPLY_CHANGED) member_sync_broadcast_one(g_cc.inst, t->ch_id, t->node_id); DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_commit ch=%s nid=0x%016llx ver=%d keys=%d json=%s", CC_ID, t->ch_id, (unsigned long long)t->node_id, ver, t->key_count, json); diff --git a/src/chat/chat_profile.c b/src/chat/chat_profile.c index d41fe867..a52f7792 100644 --- a/src/chat/chat_profile.c +++ b/src/chat/chat_profile.c @@ -35,53 +35,57 @@ void chat_core_update_my_name(const char* name) { uint64_t myid = g_cc.my_node_id; sqlite3_stmt* cs = NULL; - sqlite3_prepare_v2(g_cc.db, "SELECT channel_id, x25519_pubkey, ed25519_pubkey FROM channels", -1, &cs, NULL); + sqlite3_prepare_v2(g_cc.db, "SELECT channel_id FROM channels", -1, &cs, NULL); if (cs) { while (sqlite3_step(cs) == SQLITE_ROW) { const char* ch = (const char*)sqlite3_column_text(cs, 0); - 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; + if (!ch) 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); + char buf[256]; snprintf(buf, sizeof(buf), "SELECT update_ts FROM \"%s\" WHERE node_id=?", tbl); sqlite3_stmt* ps = NULL; - if (sqlite3_prepare_v2(g_cc.db, buf, -1, &ps, NULL) == SQLITE_OK) { - sqlite3_bind_int64(ps, 1, (sqlite3_int64)myid); - if (sqlite3_step(ps) == SQLITE_ROW) { - uint8_t join_msg[256]; size_t mlen = 0; - 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; - 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); - EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, - g_cc.inst->my_ed25519_privkey, 32); - if (pkey) { - EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); - if (mdctx) { - if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) == 1) - EVP_DigestSign(mdctx, new_sig, &(size_t){64}, join_msg, mlen); - EVP_MD_CTX_free(mdctx); - } - 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, juser, NULL, NULL, NULL, 0); - if (r1 != 0) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%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, juser, NULL, 0, NULL, NULL, 0); - if (r2 != 0) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: member_sync_put(my_name) FAILED ch=%s rc=%d", CC_ID, ch, r2); - else member_sync_broadcast_one(g_cc.inst, ch, myid); - uint8_t mevt[1 + 64 + 1 + CHAT_MEMBER_DISPLAY_SIZE]; uint8_t cl = (uint8_t)strlen(ch); - mevt[0] = cl; memcpy(mevt + 1, ch, cl); mevt[1 + cl] = 1; - if (chat_core_get_single_member(ch, myid, mevt + 1 + cl + 1) == 0) - chat_event_post(CHAT_EVT_MEMBER_UPDATED, mevt, 1 + cl + 1 + CHAT_MEMBER_DISPLAY_SIZE); + if (sqlite3_prepare_v2(g_cc.db, buf, -1, &ps, NULL) != SQLITE_OK) continue; + sqlite3_bind_int64(ps, 1, (sqlite3_int64)myid); + if (sqlite3_step(ps) != SQLITE_ROW) { sqlite3_finalize(ps); continue; } + uint64_t local_uts = (uint64_t)sqlite3_column_int64(ps, 0); + sqlite3_finalize(ps); + + /* join неизменен при смене имени — читаем существующий join_sig/join_ts */ + uint8_t join_sig[64]; uint64_t join_ts = 0; + if (topo_node_sqlite_member_get_join(g_cc.db, ch, myid, join_sig, &join_ts) != 0) continue; + + char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", name); + uint64_t update_ts = (uint64_t)ntp_time_get_seconds(g_cc.inst); + if (update_ts <= local_uts) update_ts = local_uts + 1; /* гарантируем рост ver */ + + /* update_sig = sign(join_sig || update_ts || userinfo) ключом мембера */ + uint8_t umsg[256 + 64 + 8]; size_t ulen = 0; + memcpy(umsg + ulen, join_sig, 64); ulen += 64; + memcpy(umsg + ulen, &update_ts, 8); ulen += 8; + size_t jl = strlen(juser); + memcpy(umsg + ulen, juser, jl + 1); ulen += jl + 1; + uint8_t update_sig[64]; memset(update_sig, 0, 64); + EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, + g_cc.inst->my_ed25519_privkey, 32); + if (pkey) { + EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); + if (mdctx) { + if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) == 1) + EVP_DigestSign(mdctx, update_sig, &(size_t){64}, umsg, ulen); + EVP_MD_CTX_free(mdctx); } - sqlite3_finalize(ps); + EVP_PKEY_free(pkey); } + + int r2 = member_sync_put(g_cc.inst, ch, myid, g_cc.inst->my_keys.public_key, + g_cc.inst->my_ed25519_pubkey, join_sig, join_ts, + update_sig, update_ts, juser, NULL, 0, NULL, NULL, 0); + if (r2 < 0) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: member_sync_put(my_name) FAILED ch=%s rc=%d", CC_ID, ch, r2); + else if (r2 & MS_APPLY_CHANGED) member_sync_broadcast_one(g_cc.inst, ch, myid); + + uint8_t mevt[1 + 64 + 1 + CHAT_MEMBER_DISPLAY_SIZE]; uint8_t cl = (uint8_t)strlen(ch); + mevt[0] = cl; memcpy(mevt + 1, ch, cl); mevt[1 + cl] = 1; + if (chat_core_get_single_member(ch, myid, mevt + 1 + cl + 1) == 0) + chat_event_post(CHAT_EVT_MEMBER_UPDATED, mevt, 1 + cl + 1 + CHAT_MEMBER_DISPLAY_SIZE); } sqlite3_finalize(cs); } diff --git a/src/chat/chat_sync.c b/src/chat/chat_sync.c index a08f46f6..087f2356 100644 --- a/src/chat/chat_sync.c +++ b/src/chat/chat_sync.c @@ -378,8 +378,8 @@ static void cs_on_remote_status_changed(uint64_t peer, int online) { } static void cs_on_peer_status_changed(uint64_t peer, int online) { - /* 1. Локальная БД */ - member_sync_set_online(g_cs->inst, peer, online); + /* 1. Локальная БД (онлайн НЕ в merkle sync — только локальная запись) */ + int online_changed = member_sync_set_online(g_cs->inst, peer, online); /* 2. GUI + online-бар */ int found_in_channel = 0; @@ -397,9 +397,11 @@ static void cs_on_peer_status_changed(uint64_t peer, int online) { chat_event_post(CHAT_EVT_MEMBER_UPDATED, mevt, 1 + (int)cl + 1 + CHAT_MEMBER_DISPLAY_SIZE); cs_post_channel_online(g_cs, ch_id); - /* 3. Рассылка дельты всем synced-соседям */ - uint8_t st = (uint8_t)online; - merkle_sync_push_update(g_cs->inst, g_cs->channels[i].channel_id, peer, 0x01, &st, 1); + /* 3. Рассылка дельты всем synced-соседям — только если online реально изменился */ + if (online_changed) { + uint8_t st = (uint8_t)online; + merkle_sync_push_update(g_cs->inst, g_cs->channels[i].channel_id, peer, 0x01, &st, 1); + } break; } } @@ -435,10 +437,12 @@ static void _on_invite_sync_done(uint64_t peer, const char* ns, int result, void CS_ID, (void*)sa, (unsigned long long)sa->node_id, sa->ch_id); } else { cs_post_channel_online(g_cs, sa->ch_id); - uint8_t one = 1; - merkle_sync_push_update(g_cs->inst, sa->ch_id, sa->node_id, 0x01, &one, 1); - DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invite_sync push_update node=0x%016llx online=1 ch=%s", - CS_ID, (unsigned long long)sa->node_id, sa->ch_id); + if (member_sync_set_online(g_cs->inst, sa->node_id, 1)) { + uint8_t one = 1; + merkle_sync_push_update(g_cs->inst, sa->ch_id, sa->node_id, 0x01, &one, 1); + DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: invite_sync push_update node=0x%016llx online=1 ch=%s", + CS_ID, (unsigned long long)sa->node_id, sa->ch_id); + } } } u_free(sa);