Commit exact DM custody tombstones before acknowledging deletion

This commit is contained in:
evgeny committed 2026-10-26 19:45:51 +01:00
1 parent 3f195c058b
commit 497792da73
3 files changed
+112 -11

No files matched your search

+28 -8
View File
@@ -68,8 +68,10 @@ int dm_media_step(struct UTUN_INSTANCE* inst, sqlite3_stmt* st) {
int rc = sqlite3_step(st);
if (rc != SQLITE_DONE) DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: SQL step rc=%d error=%s statement=%s",
rc, sqlite3_errmsg(inst->topo_sqlite_db), sqlite3_sql(st));
sqlite3_finalize(st);
return rc == SQLITE_DONE ? 0 : -1;
int final = sqlite3_finalize(st);
if (final != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: SQL finalize rc=%d step=%d error=%s",
final, rc, sqlite3_errmsg(inst->topo_sqlite_db));
return rc == SQLITE_DONE && final == SQLITE_OK ? 0 : -1;
}
int dm_media_exec(struct UTUN_INSTANCE* inst, const char* sql) {
@@ -879,8 +881,12 @@ static void dm_custody_finalize(struct dm_media_state* media, const uint8_t id[1
struct UTUN_INSTANCE* inst = media->inst;
sqlite3_stmt* st = dm_media_prepare(inst, "SELECT body_hash,receipt,supernode,group_id FROM dm_custody WHERE id=? AND ready=-1");
if (!st) return;
sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC);
int rc = sqlite3_step(st);
int rc = sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC);
if (rc != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: delete state bind failed rc=%d; retry retained", rc);
sqlite3_finalize(st); return;
}
rc = sqlite3_step(st);
uint8_t hash[32], receipt[DM_MEDIA_RECEIPT_SIZE];
if (rc != SQLITE_ROW || sqlite3_column_bytes(st, 0) != sizeof(hash)) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: delete state missing/corrupt rc=%d", rc);
@@ -890,23 +896,37 @@ static void dm_custody_finalize(struct dm_media_state* media, const uint8_t id[1
int delivered = sqlite3_column_bytes(st, 1) == sizeof(receipt);
if (delivered) memcpy(receipt, sqlite3_column_blob(st, 1), sizeof(receipt));
uint64_t super = (uint64_t)sqlite3_column_int64(st, 2), group = (uint64_t)sqlite3_column_int64(st, 3);
sqlite3_finalize(st);
int final = sqlite3_finalize(st);
if (final != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: delete state finalize failed rc=%d; retry retained", final); return;
}
if (dm_media_exec(inst, "BEGIN IMMEDIATE")) return;
st = dm_media_prepare(inst, "INSERT INTO dm_custody_done(id,body_hash,receipt) VALUES(?,?,?)"
" ON CONFLICT(id) DO UPDATE SET receipt=COALESCE(excluded.receipt,receipt)");
if (!st) goto rollback;
sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC); sqlite3_bind_blob(st, 2, hash, 32, SQLITE_STATIC);
if (delivered) sqlite3_bind_blob(st, 3, receipt, sizeof(receipt), SQLITE_STATIC); else sqlite3_bind_null(st, 3);
rc = sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_bind_blob(st, 2, hash, 32, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = delivered ? sqlite3_bind_blob(st, 3, receipt, sizeof(receipt), SQLITE_STATIC) : sqlite3_bind_null(st, 3);
if (rc != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: tombstone bind failed rc=%d; custody retained", rc);
sqlite3_finalize(st); goto rollback;
}
if (dm_media_step(inst, st)) goto rollback;
st = dm_media_prepare(inst, "DELETE FROM dm_custody WHERE id=? AND ready=-1");
if (!st) goto rollback;
sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC);
rc = sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC);
if (rc != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: custody delete bind failed rc=%d; tombstone rolled back", rc);
sqlite3_finalize(st); goto rollback;
}
if (dm_media_step(inst, st) || dm_media_exec(inst, "COMMIT")) goto rollback;
dm_media_send(inst, group, super, delivered ? DM_MEDIA_DELETE_ACK : DM_MEDIA_EXPIRED, id, 16);
DEBUG_INFO(DEBUG_CATEGORY_DM, "dm_media: custody copy removed reason=%s super=%llu",
delivered ? "delivered" : "expired", (unsigned long long)super);
return;
rollback:
DEBUG_WARN(DEBUG_CATEGORY_DM, "dm_media: custody retirement rolled back super=%llu; retry retained, no ACK",
(unsigned long long)super);
dm_media_exec(inst, "ROLLBACK");
}
+1 -1
View File
@@ -12,7 +12,7 @@
* inst/db/sql обязательны; вызывающий связывает параметры и освобождает statement. */
sqlite3_stmt* dm_media_prepare(struct UTUN_INSTANCE* inst, const char* sql);
/* Шаг записи, затем finalize (включая ошибку): statement передаётся во владение helper.
* 0 только при SQLITE_DONE, иначе -1 с логом; результат finalize отдельно не проверяется. */
* 0 только при SQLITE_DONE и успешном finalize, иначе -1 с логом; внутри транзакции rollback делает вызывающий. */
int dm_media_step(struct UTUN_INSTANCE* inst, sqlite3_stmt* st);
/* Выполнить SQL без результата: 0/-1 с логом. Владение транзакцией определяется SQL вызывающего. */
int dm_media_exec(struct UTUN_INSTANCE* inst, const char* sql);