Retain the last admission checkpoint across interrupted reconciliation
This commit is contained in:
1 parent
0c0233600c
commit
8cb2a85dc9
4 files changed
+39
-3
No files matched your search
@@ -98,6 +98,7 @@ extern "C" {
|
||||
#define CHAT_EVT_NETWORK_STATS 69 /* Native POD снимок ETCP-подключений и линков, chat_network_stats.h. */
|
||||
#define CHAT_EVT_DM_START_RESULT 72 /* request/conv/generation:8 native + success:1; только результат конкретного запроса UI. */
|
||||
#define CHAT_EVT_DM_MEMBERS 73 /* conv_len:1,conv,generation:LE8,count:LE2,(side:1,member:wire260)*count. */
|
||||
#define CHAT_EVT_GROUP_POLICY_RESULT 74 /* request/group:8LE + JSON: preview или результат смены прав приглашения. */
|
||||
|
||||
// ── Подключение получателя событий ──
|
||||
|
||||
|
||||
+24
-1
@@ -28,7 +28,8 @@ static uint64_t gp_read64(const uint8_t* p) { uint64_t n = 0; for (int i = 0; i
|
||||
|
||||
int group_policy_schema(sqlite3* db) {
|
||||
int rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS group_policy(channel_id TEXT PRIMARY KEY,"
|
||||
"version INTEGER NOT NULL,all_members INTEGER NOT NULL,signature BLOB NOT NULL)", NULL, NULL, NULL);
|
||||
"version INTEGER NOT NULL,all_members INTEGER NOT NULL,signature BLOB NOT NULL);"
|
||||
"CREATE TABLE IF NOT EXISTS group_admission_pending(channel_id TEXT PRIMARY KEY)", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "group policy schema: %s", sqlite3_errmsg(db));
|
||||
return rc == SQLITE_OK ? 0 : -1;
|
||||
}
|
||||
@@ -199,9 +200,27 @@ static int gp_evaluate(struct gp_member* m, size_t count, int all, uint64_t owne
|
||||
}
|
||||
|
||||
/* Изменение admitted не входит в Merkle: это производный допуск по уже сверенным данным. */
|
||||
int group_policy_pending(sqlite3* db, const char* channel) {
|
||||
sqlite3_stmt* st = NULL;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT 1 FROM group_admission_pending WHERE channel_id=?", -1, &st, NULL);
|
||||
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 1, channel, -1, SQLITE_STATIC);
|
||||
if (rc == SQLITE_OK) rc = sqlite3_step(st);
|
||||
int final = sqlite3_finalize(st);
|
||||
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "group admission checkpoint read ch=%s rc=%d", channel, rc); return -1;
|
||||
}
|
||||
return rc == SQLITE_ROW;
|
||||
}
|
||||
|
||||
int group_policy_refresh(struct UTUN_INSTANCE* inst, const char* channel) {
|
||||
sqlite3* db = inst ? inst->topo_sqlite_db : NULL; struct group_policy policy;
|
||||
struct gp_member* m = NULL; size_t count = 0; uint64_t owner;
|
||||
if (!db) return -1;
|
||||
int pending = group_policy_pending(db, channel);
|
||||
if (pending) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_MEMBER_SYNC, "group admission retains previous checkpoint ch=%s pending=%d", channel, pending);
|
||||
return pending < 0 ? -1 : 0;
|
||||
}
|
||||
if (!db || !chat_channel_id(channel) || !sqlite3_get_autocommit(db) || group_policy_get(db, channel, &policy) ||
|
||||
topo_node_sqlite_channel_get(db, channel, NULL, 0, &owner, NULL, NULL, NULL) || gp_load(inst, channel, &m, &count)) return -1;
|
||||
if (gp_evaluate(m, count, policy.all_members, owner)) { u_free(m); return -1; }
|
||||
@@ -249,6 +268,9 @@ done:
|
||||
int group_policy_can_invite(struct UTUN_INSTANCE* inst, const char* channel, uint64_t inviter) {
|
||||
struct group_policy policy; struct gp_member* m = NULL; size_t count = 0; uint64_t owner;
|
||||
sqlite3* db = inst ? inst->topo_sqlite_db : NULL;
|
||||
if (!db || group_policy_pending(db, channel)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "invitation waits for membership checkpoint ch=%s", channel); return 0;
|
||||
}
|
||||
if (!db || !chat_channel_id(channel) || group_policy_get(db, channel, &policy) ||
|
||||
topo_node_sqlite_channel_get(db, channel, NULL, 0, &owner, NULL, NULL, NULL) || gp_load(inst, channel, &m, &count)) return -1;
|
||||
int rc = gp_evaluate(m, count, policy.all_members, owner);
|
||||
@@ -294,6 +316,7 @@ int group_policy_command(struct UTUN_INSTANCE* inst, const char* channel, int al
|
||||
struct strbuf json = strbuf_new(); char expected[65]; int result = -1, transaction = 0;
|
||||
const char* error = "unavailable";
|
||||
if (!output || !db || !chat_channel_id(channel) || (all != 0 && all != 1) || !sqlite3_get_autocommit(db)) goto done;
|
||||
if (group_policy_pending(db, channel)) { error = "sync_pending"; goto done; }
|
||||
if (group_policy_get(db, channel, &policy) || topo_node_sqlite_channel_get(db, channel, NULL, 0, &owner, NULL, NULL, NULL) ||
|
||||
merkle_sync_read_hash(inst, MT_DATA_MEMBERS, channel, 0, 0, root) || gp_load(inst, channel, &m, &count)) goto done;
|
||||
for (int i = 0; i < 32; i++) snprintf(expected + i * 2, 3, "%02x", root[i]);
|
||||
|
||||
@@ -30,6 +30,8 @@ int group_policy_decode(struct sqlite3* db, const char* channel, const uint8_t*
|
||||
int group_policy_apply(struct sqlite3* db, const char* channel, const struct group_policy* policy);
|
||||
/* Пересчитать admitted атомарно, затем снять недопущенные сессии; 0/-1. */
|
||||
int group_policy_refresh(struct UTUN_INSTANCE* inst, const char* channel);
|
||||
/* Незавершённый приём страниц: 1/0/-1. Маркер хранится локально, не входит в Merkle. */
|
||||
int group_policy_pending(struct sqlite3* db, const char* channel);
|
||||
/* Текущее право подписи приглашения; 1/0/-1, ошибка никогда не разрешает действие. */
|
||||
int group_policy_can_invite(struct UTUN_INSTANCE* inst, const char* channel, uint64_t inviter);
|
||||
/* Предпросмотр/применение: token=NULL — preview; иначе проверка Merkle token и commit.
|
||||
|
||||
+12
-2
@@ -1208,7 +1208,8 @@ static int _member_apply_items(void* ctx, const char* ns, uint64_t from_peer, co
|
||||
if (sqlite3_exec(db, "SAVEPOINT member_page", NULL, NULL, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: page begin: %s", MS_ID, sqlite3_errmsg(db)); u_free(changed); return MT_ERR_IO;
|
||||
}
|
||||
int rc = group_policy_apply(db, ns, &policy);
|
||||
char mark[128]; snprintf(mark, sizeof(mark), "INSERT OR IGNORE INTO group_admission_pending VALUES('%s')", ns);
|
||||
int rc = sqlite3_exec(db, mark, NULL, NULL, NULL) == SQLITE_OK ? group_policy_apply(db, ns, &policy) : MT_ERR_IO;
|
||||
if (rc > 0) rc = merkle_sync_recompute_path(inst, MT_DATA_MEMBERS, ns, 0) < 0 ? MT_ERR_IO : 0;
|
||||
off = 2 + GROUP_POLICY_WIRE_SIZE;
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
@@ -1292,7 +1293,16 @@ static int member_finish(void* ctx, const char* ns) {
|
||||
static void member_policy_result(void* ctx, const char* ns, uint64_t peer, int result) {
|
||||
(void)peer;
|
||||
struct UTUN_INSTANCE* inst = ctx;
|
||||
if (result != MT_OK || group_policy_refresh(inst, ns)) return;
|
||||
if (result != MT_OK) return;
|
||||
sqlite3_stmt* checkpoint = NULL;
|
||||
int marked = sqlite3_prepare_v2(_db(inst), "DELETE FROM group_admission_pending WHERE channel_id=?", -1, &checkpoint, NULL);
|
||||
if (marked == SQLITE_OK) marked = sqlite3_bind_text(checkpoint, 1, ns, -1, SQLITE_STATIC);
|
||||
if (marked == SQLITE_OK) marked = sqlite3_step(checkpoint);
|
||||
int finalized = sqlite3_finalize(checkpoint);
|
||||
if (marked != SQLITE_DONE || finalized != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "group admission checkpoint commit ch=%s rc=%d", ns, marked); return;
|
||||
}
|
||||
if (group_policy_refresh(inst, ns)) return;
|
||||
struct ready_member { uint64_t node, signer; uint8_t x[32], signature[64]; };
|
||||
struct ready_member* rows = NULL; size_t count = 0;
|
||||
char sql[192]; sqlite3_stmt* st = NULL;
|
||||
|
||||
Reference in new issue
Block a user