From 60942e542bd50128d8c73ed3fe1a45f99760e27c Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 04:36:09 +0100 Subject: [PATCH] Propagate all member block binding and finalization failures before index publication --- src/routing_layer/topo_node_sqlite.c | 56 ++++++++++++++++++---------- src/routing_layer/topo_node_sqlite.h | 1 + 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index fce210f9..628980e7 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -726,7 +726,9 @@ int topo_node_sqlite_member_block_put(sqlite3* db, const char* channel_id, uint6 const uint8_t* x25519_pubkey, const uint8_t* ed25519_pubkey, const char* userinfo, const uint8_t* creator_sig, uint64_t signed_by, const uint8_t* signature) { - if (!db || !channel_id || !x25519_pubkey || !ed25519_pubkey) return -1; + if (!db || !channel_id || !x25519_pubkey || !ed25519_pubkey) { + DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member block: invalid database/channel/key arguments"); return -1; + } char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); char sql[768]; snprintf(sql, sizeof(sql), @@ -746,24 +748,40 @@ int topo_node_sqlite_member_block_put(sqlite3* db, const char* channel_id, uint6 " signature=excluded.signature," " 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); - sqlite3_bind_blob(stmt, 2, x25519_pubkey, 32, SQLITE_STATIC); - sqlite3_bind_blob(stmt, 3, ed25519_pubkey, 32, SQLITE_STATIC); - if (join_sig) sqlite3_bind_blob(stmt, 4, join_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); - else { static const unsigned char zsig[PEERS_JOIN_SIG_SIZE] = {0}; sqlite3_bind_blob(stmt, 4, zsig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); } - sqlite3_bind_int64(stmt, 5, (sqlite3_int64)join_ts); - if (update_sig) sqlite3_bind_blob(stmt, 6, update_sig, PEERS_UPDATE_SIG_SIZE, SQLITE_STATIC); - else sqlite3_bind_null(stmt, 6); - sqlite3_bind_int64(stmt, 7, (sqlite3_int64)update_ts); - sqlite3_bind_text(stmt, 8, userinfo ? userinfo : "", -1, SQLITE_STATIC); - if (creator_sig) sqlite3_bind_blob(stmt, 9, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); - else sqlite3_bind_null(stmt, 9); - sqlite3_bind_int64(stmt, 10, (sqlite3_int64)signed_by); - if (signature) sqlite3_bind_blob(stmt, 11, signature, 64, SQLITE_STATIC); - else sqlite3_bind_null(stmt, 11); - int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); - return rc == SQLITE_DONE ? 0 : -1; + const char* stage = "prepare"; + int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + if (rc == SQLITE_OK) { stage = "bind node"; rc = sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); } + if (rc == SQLITE_OK) { stage = "bind X25519"; rc = sqlite3_bind_blob(stmt, 2, x25519_pubkey, 32, SQLITE_STATIC); } + if (rc == SQLITE_OK) { stage = "bind Ed25519"; rc = sqlite3_bind_blob(stmt, 3, ed25519_pubkey, 32, SQLITE_STATIC); } + if (rc == SQLITE_OK) { + static const uint8_t zero_signature[PEERS_JOIN_SIG_SIZE] = {0}; stage = "bind JOIN signature"; + rc = sqlite3_bind_blob(stmt, 4, join_sig ? join_sig : zero_signature, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC); + } + if (rc == SQLITE_OK) { stage = "bind JOIN timestamp"; rc = sqlite3_bind_int64(stmt, 5, (sqlite3_int64)join_ts); } + if (rc == SQLITE_OK) { + stage = "bind profile signature"; + rc = update_sig ? sqlite3_bind_blob(stmt, 6, update_sig, PEERS_UPDATE_SIG_SIZE, SQLITE_STATIC) : sqlite3_bind_null(stmt, 6); + } + if (rc == SQLITE_OK) { stage = "bind profile timestamp"; rc = sqlite3_bind_int64(stmt, 7, (sqlite3_int64)update_ts); } + if (rc == SQLITE_OK) { stage = "bind profile"; rc = sqlite3_bind_text(stmt, 8, userinfo ? userinfo : "", -1, SQLITE_STATIC); } + if (rc == SQLITE_OK) { + stage = "bind creator signature"; + rc = creator_sig ? sqlite3_bind_blob(stmt, 9, creator_sig, PEERS_JOIN_SIG_SIZE, SQLITE_STATIC) : sqlite3_bind_null(stmt, 9); + } + if (rc == SQLITE_OK) { stage = "bind inviter"; rc = sqlite3_bind_int64(stmt, 10, (sqlite3_int64)signed_by); } + if (rc == SQLITE_OK) { + stage = "bind invitation signature"; + rc = signature ? sqlite3_bind_blob(stmt, 11, signature, 64, SQLITE_STATIC) : sqlite3_bind_null(stmt, 11); + } + if (rc == SQLITE_OK) { stage = "write"; rc = sqlite3_step(stmt); } + int changed = rc == SQLITE_DONE ? sqlite3_changes(db) : 0; + if (rc != SQLITE_DONE || changed != 1) DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, + "member block: channel=%s node=%016llx stage=%s rc=%d (%s) changed=%d db=%s", + channel_id, (unsigned long long)node_id, stage, rc, sqlite3_errstr(rc), changed, sqlite3_errmsg(db)); + int final = sqlite3_finalize(stmt); + if (final != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member block: finalize channel=%s node=%016llx rc=%d", + channel_id, (unsigned long long)node_id, final); + return rc == SQLITE_DONE && changed == 1 && final == SQLITE_OK ? 0 : -1; } /* Обновляет только блок владельца (adm_tags/adm_tags_sig/storage), diff --git a/src/routing_layer/topo_node_sqlite.h b/src/routing_layer/topo_node_sqlite.h index e5b86dfb..bf6ab18f 100644 --- a/src/routing_layer/topo_node_sqlite.h +++ b/src/routing_layer/topo_node_sqlite.h @@ -79,6 +79,7 @@ int topo_node_sqlite_member_placeholder_put(sqlite3* db, const char* channel_id, * signed_by/signature — дерево приглашений: кто подписал pubkey узла (0 = корень, ключом группы). * 0/-1; подписи по 64, ключи по 32 байта. NULL join_sig даёт нулевую подпись, userinfo=NULL даёт ''. * Сбрасывает source=0; свежесть/подписи проверяет вызывающий, owner/local-поля сохраняются. */ +/* Проверяет все bind,step,changes1 и finalize;при отказе caller откатывает свою транзакцию записи/индекса. */ 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, const uint8_t* update_sig, uint64_t update_ts,