Browse Source

Read stream supernode role directly by member ID

master
evgeny 5 days ago
parent
commit
a4f508feed
  1. 29
      src/stream/stream_member.c
  2. 25
      tests/test_stream_service.c

29
src/stream/stream_member.c

@ -221,13 +221,30 @@ int stream_member_online(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t
return node && topo_node_best_path(node) != NULL; return node && topo_node_best_path(node) != NULL;
} }
/* Роль относится к каналу, не к устройству или глобальному media_delivery. */ /* Роль читается по ID из блока владельца канала, без загрузки остальных участников и снимков узлов. */
int stream_member_supernode(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t member_id) { int stream_member_supernode(struct UTUN_INSTANCE *inst, uint64_t group_id, uint64_t member_id) {
struct TOPO_GROUP *group = inst && inst->topo_groups ? topo_groups_find(inst->topo_groups, group_id) : NULL; struct TOPO_GROUP *group = inst && inst->topo_groups ? topo_groups_find(inst->topo_groups, group_id) : NULL;
if (!group || group->stopping || group->group_type != TOPO_GROUP_TYPE_CHAT) return 0; if (!group || group->stopping || group->group_type != TOPO_GROUP_TYPE_CHAT) return 0;
struct topo_connect_peer *peers = NULL; size_t count = 0; if (!inst->topo_sqlite_db || !group->channel_id[0]) {
if (topo_node_sqlite_get_connect_peers(inst->topo_sqlite_db, group->channel_id, &peers, &count) < 0) return -1; DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "member role missing DB/channel group=%llx", (unsigned long long)group_id); return -1;
int supernode = 0; }
for (size_t i = 0; i < count; i++) if (peers[i].node_id == member_id) { supernode = peers[i].supernode; break; } for (const char *p = group->channel_id; *p; p++) if (*p < '0' || *p > '9') {
u_free(peers); return supernode; DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "member role invalid channel namespace group=%llx", (unsigned long long)group_id); return -1;
}
char query[192];
snprintf(query, sizeof(query), "SELECT adm_tags FROM \"peers_%s\" WHERE node_id=? AND deleted=0", group->channel_id);
sqlite3 *db = inst->topo_sqlite_db; sqlite3_stmt *statement = NULL;
int result = -1, rc = sqlite3_prepare_v2(db, query, -1, &statement, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_int64(statement, 1, (sqlite3_int64)member_id);
if (rc == SQLITE_OK) rc = sqlite3_step(statement);
if (rc == SQLITE_ROW) {
const char *tags = (const char *)sqlite3_column_text(statement, 0); char value[16];
result = tags && json_flat_get(tags, "supernode", value, sizeof(value)) == 0 && !strcmp(value, "yes");
} else if (rc == SQLITE_DONE) result = 0;
else DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "member role query failed group=%llx member=%llx rc=%d: %s",
(unsigned long long)group_id, (unsigned long long)member_id, rc, sqlite3_errmsg(db));
sqlite3_finalize(statement);
DEBUG_TRACE(DEBUG_CATEGORY_STREAM, "member role group=%llx member=%llx supernode=%d",
(unsigned long long)group_id, (unsigned long long)member_id, result);
return result;
} }

25
tests/test_stream_service.c

@ -1046,6 +1046,30 @@ static void relay_role(unsigned member, int enabled, unsigned version) {
} }
} }
/* Роль немедленно следует за подписанным блоком канала; deleted и чужой snapshot проверяются отдельно. */
static void test_supernode_lookup(void) {
uint64_t member = nodes[1]->node_id;
CHECK(stream_member_supernode(nodes[0], GROUP2, member) == 0);
relay_role(1, 1, 90);
CHECK(stream_member_supernode(nodes[0], GROUP1, member) == 0);
CHECK(stream_member_supernode(nodes[0], GROUP2, UINT64_MAX) == 0);
sqlite3 *db = nodes[0]->topo_sqlite_db; sqlite3_stmt *statement = NULL;
CHECK(sqlite3_exec(db, "SAVEPOINT role_lookup", NULL, NULL, NULL) == SQLITE_OK);
CHECK(sqlite3_prepare_v2(db, "UPDATE peers_710002 SET deleted=1 WHERE node_id=?", -1, &statement, NULL) == SQLITE_OK);
sqlite3_bind_int64(statement, 1, (sqlite3_int64)member);
CHECK(sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) == 1); sqlite3_finalize(statement);
CHECK(stream_member_supernode(nodes[0], GROUP2, member) == 0);
CHECK(sqlite3_exec(db, "ROLLBACK TO role_lookup", NULL, NULL, NULL) == SQLITE_OK);
CHECK(stream_member_supernode(nodes[0], GROUP2, member) == 1);
CHECK(sqlite3_prepare_v2(db, "UPDATE node_snapshots SET record=x'00' WHERE node_id=?", -1, &statement, NULL) == SQLITE_OK);
sqlite3_bind_int64(statement, 1, (sqlite3_int64)nodes[2]->node_id);
CHECK(sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) == 1); sqlite3_finalize(statement);
CHECK(stream_member_supernode(nodes[0], GROUP2, member) == 1);
CHECK(sqlite3_exec(db, "ROLLBACK TO role_lookup; RELEASE role_lookup", NULL, NULL, NULL) == SQLITE_OK);
relay_role(1, 0, 91);
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] supernode lookup: signed grant/revoke, channel isolation, absent/deleted member, unrelated snapshot");
}
/* Прямой stream-вход источника блокируем только у зрителей; Merkle/BGP и получение суперузла остаются настоящими. */ /* Прямой stream-вход источника блокируем только у зрителей; Merkle/BGP и получение суперузла остаются настоящими. */
static void relay_test_raw(struct ETCP_CONN *conn, struct ll_entry *entry) { static void relay_test_raw(struct ETCP_CONN *conn, struct ll_entry *entry) {
unsigned index = 0; while (index < 4 && conn->instance != nodes[index]) index++; CHECK(index < 4); unsigned index = 0; while (index < 4 && conn->instance != nodes[index]) index++; CHECK(index < 4);
@ -1586,6 +1610,7 @@ int main(int argc, char **argv) {
CHECK(identity && topo_node_sqlite_snapshot_put(nodes[destination]->topo_sqlite_db, identity) >= 0); CHECK(identity && topo_node_sqlite_snapshot_put(nodes[destination]->topo_sqlite_db, identity) >= 0);
} }
} }
test_supernode_lookup();
struct TOPO_PEER_REQUEST *connections[3] = {0}; struct TOPO_PEER_REQUEST *connections[3] = {0};
for (unsigned i = 1; i < 4; i++) for (unsigned i = 1; i < 4; i++)
CHECK(topo_group_peer_open(topo_groups_find(nodes[0]->topo_groups, GROUP1), nodes[i]->node_id, &connections[i - 1]) == 0); CHECK(topo_group_peer_open(topo_groups_find(nodes[0]->topo_groups, GROUP1), nodes[i]->node_id, &connections[i - 1]) == 0);

Loading…
Cancel
Save