diff --git a/src/chat/member_sync.c b/src/chat/member_sync.c index 57791d9a..77e41e61 100644 --- a/src/chat/member_sync.c +++ b/src/chat/member_sync.c @@ -118,7 +118,7 @@ static int _member_update_bucket_hash(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, adm_tags, adm_tags_sig" + " join_sig, join_ts, update_sig, update_ts, adm_tags, adm_tags_sig, source" " FROM \"%s\"" " WHERE (node_id & %lld) == %lld ORDER BY node_id ASC", peers_tbl, (long long)mask, (long long)prefix64); @@ -137,7 +137,8 @@ static int _member_update_bucket_hash(void* ctx, const char* ns, uint8_t level, uint64_t uts = (uint64_t)sqlite3_column_int64(stmt, 6); const char* atags = (const char*)sqlite3_column_text(stmt, 7); const uint8_t* atsig = (const uint8_t*)sqlite3_column_blob(stmt, 8); - if (!x25 || !ed) continue; + int src = sqlite3_column_int(stmt, 9); + if (!x25 || !ed || src != 0) continue; uint8_t mh[MT_HASH_SIZE]; _compute_member_hash(nid, x25, ed, sig, jts, usig, uts, atags, atsig, mh); @@ -162,7 +163,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, userinfo, adm_tags, adm_tags_sig" + " join_sig, join_ts, update_sig, update_ts, userinfo, adm_tags, adm_tags_sig, source" " FROM \"%s\"" " WHERE (node_id & %lld) == %lld ORDER BY node_id ASC", peers_tbl, (long long)mask, (long long)prefix); @@ -185,7 +186,8 @@ static int _member_get_items(void* ctx, const char* ns, uint8_t level, const char* nm = (const char*)sqlite3_column_text(stmt, 7); const char* atags = (const char*)sqlite3_column_text(stmt, 8); const uint8_t* atsig = (const uint8_t*)sqlite3_column_blob(stmt, 9); - if (!x25 || !ed) continue; + int src = sqlite3_column_int(stmt, 10); + if (!x25 || !ed || src != 0) continue; uint8_t nl = nm ? (uint8_t)strnlen(nm, 255) : 0; uint8_t atl = atags ? (uint8_t)strnlen(atags, 255) : 0; diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index e1b35c88..995ec260 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -781,7 +781,10 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from if (topo_node_sqlite_member_exists(sdb, node_id)) topo_node_sqlite_addrs_put(sdb, node_id, new_ni); if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->channel_id[0]) { - topo_node_sqlite_member_put(sdb, group->channel_id, node_id, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0); + /* упрощённый плейсхолдер (source=1): pubkeys из BGP, без подписей. + не участвует в merkle-хеше, дополняется полной merkle-записью */ + topo_node_sqlite_member_placeholder_put(sdb, group->channel_id, node_id, + new_ni->public_key, new_ni->ed25519_public_key); } topo_node_sqlite_nodeinfo_updated(sdb, node_id); } diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index f7fa4301..14cb3821 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -233,6 +233,9 @@ int topo_node_sqlite_init(sqlite3* db) { snprintf(isql, sizeof(isql), "ALTER TABLE \"%s\" ADD COLUMN connected INTEGER NOT NULL DEFAULT 0", tbl); sqlite3_exec(db, isql, NULL, NULL, NULL); + snprintf(isql, sizeof(isql), + "ALTER TABLE \"%s\" ADD COLUMN source INTEGER NOT NULL DEFAULT 0", tbl); + sqlite3_exec(db, isql, NULL, NULL, NULL); } sqlite3_finalize(s); } @@ -279,7 +282,9 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id, " node_RTT INTEGER," /* RTT до узла в мс, часть индекса (node_type, node_RTT) */ " connected INTEGER NOT NULL DEFAULT 0," /* 1 = было активное подключение к узлу */ " storage INTEGER NOT NULL DEFAULT 0," /* 1 = приоритетный хранитель аттачей */ - " local_nick TEXT" /* локальный ник (GUI only, не синхронизируется) */ + " local_nick TEXT," /* локальный ник (GUI only, не синхронизируется) */ + " source INTEGER NOT NULL DEFAULT 0" /* 0=полная merkle-запись 1=упрощённый плейсхолдер topo_group + (не участвует в merkle-хеше, дополняется полной записью) */ ")"; int ddl_sz = snprintf(NULL, 0, ddl_tmpl, peers_tbl); @@ -378,6 +383,29 @@ int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t no return 0; } +/* Упрощённый плейсхолдер мембера от topo_group: только node_id + pubkeys, source=1. + * Не участвует в merkle-хеше. Если запись уже есть (полная merkle) — не трогает её. + * Позже полная merkle-запись дополняет/перезаписывает блоки и сбрасывает source=0. */ +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) { + if (!db || !channel_id || !x25519_pubkey || !ed25519_pubkey) return -1; + char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); + char sql[512]; + snprintf(sql, sizeof(sql), + "INSERT INTO \"%s\"(node_id, x25519_pubkey, ed25519_pubkey, join_sig, join_ts, source)" + " VALUES(?,?,?,?,0,1)" + " ON CONFLICT(node_id) DO NOTHING", 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_bind_blob(stmt, 2, x25519_pubkey, 32, SQLITE_STATIC); + sqlite3_bind_blob(stmt, 3, ed25519_pubkey, 32, SQLITE_STATIC); + static const unsigned char zsig[PEERS_JOIN_SIG_SIZE] = {0}; + sqlite3_bind_blob(stmt, 4, zsig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); + int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); + return rc == SQLITE_DONE ? 0 : -1; +} + /* Обновляет только блок мембера (keys/join/update_sig/update_ts/userinfo), * НЕ трогая adm_tags/adm_tags_sig/storage (блок владельца) и локальные поля. */ int topo_node_sqlite_member_block_put(sqlite3* db, const char* channel_id, uint64_t node_id, @@ -400,7 +428,8 @@ int topo_node_sqlite_member_block_put(sqlite3* db, const char* channel_id, uint6 " update_sig=excluded.update_sig," " update_ts=excluded.update_ts," " userinfo=excluded.userinfo," - " creator_sig=excluded.creator_sig", peers_tbl); + " creator_sig=excluded.creator_sig," + " source=0", 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); diff --git a/src/routing_layer/topo_node_sqlite.h b/src/routing_layer/topo_node_sqlite.h index a3e85cc7..c8a331c7 100644 --- a/src/routing_layer/topo_node_sqlite.h +++ b/src/routing_layer/topo_node_sqlite.h @@ -36,6 +36,10 @@ int topo_node_sqlite_member_put(sqlite3* db, const char* channel_id, uint64_t no const uint8_t* creator_sig, const char* adm_tags, const uint8_t* adm_tags_sig, int storage); +/* Упрощённый плейсхолдер мембера от topo_group (source=1, не участвует в merkle-хеше). */ +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), не трогая блок владельца. */ 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,