diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 3caabe40..6eb90f07 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -406,7 +406,7 @@ int topo_node_sqlite_channel_put(sqlite3* db, const char* channel_id, " adm_tags_sig BLOB," /* Ed25519 подпись над adm_tags (64 байта) канальным ключом */ " node_type INTEGER NOT NULL DEFAULT 0," /* вычисляемый тип узла (topo_node_sqlite_nodeinfo_updated): 0=неизвестно 1=прямой(addr_type NETIF/DIRECT/NAT_STRICT) - 2=EIM NAT 3=(не исп.) 4=суперузел(DIRECT+adm_tags.supernode=yes) */ + 2=EIM NAT 3=(не исп.) 4=суперузел(adm_tags.supernode=yes) */ " node_RTT INTEGER," /* RTT до узла в мс, часть индекса (node_type, node_RTT) */ " connected INTEGER NOT NULL DEFAULT 0," /* 1 = было активное подключение к узлу */ " storage INTEGER NOT NULL DEFAULT 0," /* 1 = приоритетный хранитель аттачей */ @@ -837,8 +837,8 @@ int topo_node_sqlite_nodeinfo_updated(sqlite3* db, uint64_t node_id) { const char* tags = (const char*)sqlite3_column_text(a, 0); if (tags) { char buf[64]; + if (json_flat_get(tags, "supernode", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) node_type = 4; if (best_at == ADDR_TYPE_DIRECT && base_type == 1) { - if (json_flat_get(tags, "supernode", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) node_type = 4; if (json_flat_get(tags, "storage", buf, sizeof(buf)) == 0 && strcmp(buf, "yes") == 0) is_storage = 1; } is_deleted = adm_tags_deleted(tags); @@ -1146,6 +1146,61 @@ int topo_node_sqlite_set_connected(sqlite3* db, const char* channel_id, uint64_t return (rc == SQLITE_DONE) ? 0 : -1; } +/* Читаем тип из уже проверенного snapshot, а не из устаревшей проекции nodes.client_type. */ +int topo_node_sqlite_get_connect_peers(sqlite3* db, const char* channel_id, + struct topo_connect_peer** out, size_t* out_count) { + if (!db || !channel_id || !out || !out_count) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid group candidate query arguments"); return -1; + } + *out = NULL; *out_count = 0; + char table[80]; peers_table_name(channel_id, table, sizeof(table)); + char sql[512]; + snprintf(sql, sizeof(sql), + "SELECT p.node_id,p.adm_tags,s.timestamp,s.record FROM \"%s\" p" + " LEFT JOIN node_snapshots s ON s.node_id=p.node_id WHERE p.deleted=0" + " ORDER BY p.connected DESC,p.node_RTT IS NULL,p.node_RTT,p.node_id", table); + sqlite3_stmt* stmt = NULL; + int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group candidate query channel=%s: %s", channel_id, sqlite3_errmsg(db)); return -1; + } + struct topo_connect_peer* peers = NULL; + size_t count = 0, capacity = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + if (count == capacity) { + size_t next = capacity ? capacity * 2 : 16; + struct topo_connect_peer* list = u_realloc(peers, next * sizeof(*list)); + if (!list) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group candidate query allocation failed channel=%s", channel_id); break; } + peers = list; capacity = next; + } + struct topo_connect_peer* peer = &peers[count++]; + *peer = (struct topo_connect_peer){ .node_id = (uint64_t)sqlite3_column_int64(stmt, 0), .client_type = UINT8_MAX }; + const char* tags = (const char*)sqlite3_column_text(stmt, 1); + char value[16]; + peer->supernode = tags && json_flat_get(tags, "supernode", value, sizeof(value)) == 0 && strcmp(value, "yes") == 0; + const void* record = sqlite3_column_blob(stmt, 3); + if (record) { + struct TOPOMSG_NODE header; + if (sqlite3_column_bytes(stmt, 3) < (int)sizeof(header)) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "short candidate snapshot channel=%s node=%016llx", channel_id, + (unsigned long long)peer->node_id); break; + } + memcpy(&header, record, sizeof(header)); + if (header.node_id != peer->node_id || header.timestamp != (uint64_t)sqlite3_column_int64(stmt, 2) || + header.client_type > CLIENT_TYPE_MOBILE) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid candidate snapshot channel=%s node=%016llx type=%u", channel_id, + (unsigned long long)peer->node_id, header.client_type); break; + } + peer->client_type = header.client_type; + } + } + if (rc != SQLITE_DONE) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group candidate read failed channel=%s rc=%d: %s", channel_id, rc, sqlite3_errmsg(db)); + sqlite3_finalize(stmt); u_free(peers); return -1; + } + sqlite3_finalize(stmt); *out = peers; *out_count = count; return 0; +} + int topo_node_sqlite_get_connected_peers(sqlite3* db, const char* channel_id, uint64_t** out_ids, int* out_count) { if (!db || !channel_id || !out_ids || !out_count) return -1; @@ -1198,8 +1253,7 @@ int topo_node_sqlite_get_supernode_peers(sqlite3* db, const char* channel_id, *out_ids = NULL; *out_count = 0; char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); char sql[256]; snprintf(sql, sizeof(sql), - "SELECT p.node_id FROM \"%s\" p WHERE p.node_type=4 AND p.deleted=0" - " AND EXISTS (SELECT 1 FROM node_addresses na WHERE na.node_id=p.node_id AND na.family IN (4,6))", + "SELECT p.node_id FROM \"%s\" p WHERE p.node_type=4 AND p.deleted=0", peers_tbl); sqlite3_stmt* st = NULL; if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) return -1; diff --git a/src/routing_layer/topo_node_sqlite.h b/src/routing_layer/topo_node_sqlite.h index e01a9517..d6263e4c 100644 --- a/src/routing_layer/topo_node_sqlite.h +++ b/src/routing_layer/topo_node_sqlite.h @@ -107,6 +107,18 @@ void topo_node_sqlite_update_rtt(sqlite3* db, uint64_t node_id, uint16_t rtt); struct TOPO_NODE* topo_node_sqlite_node_load(sqlite3* db, struct TOPO_GROUPS* groups, uint64_t node_id); 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; +}; + +/* Все неудалённые участники, включая 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); 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);