Accept verified DM originals after storage expiry
This commit is contained in:
1 parent
ab8713d88f
commit
25a8b905fc
2 files changed
+47
-6
No files matched your search
+5
-1
@@ -581,7 +581,8 @@ static void dm_file_done(void* arg, int error) {
|
||||
uint64_t expires = rc == SQLITE_ROW ? (uint64_t)sqlite3_column_int64(st, 4) : 0;
|
||||
if (len <= sizeof(body) && len) memcpy(body, sqlite3_column_blob(st, 0), len);
|
||||
sqlite3_finalize(st);
|
||||
if (ready < 0 || !len || len > sizeof(body) || (custody && expires <= ntp_time_get_seconds(inst))) {
|
||||
/* EXPIRED завершает копию M; endpoint может принять проверенный оригинал в актуальном поколении. */
|
||||
if (!len || len > sizeof(body) || (custody && (ready < 0 || expires <= ntp_time_get_seconds(inst)))) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DM, "dm_media: completed task no longer admissible custody=%d ready=%d expiry=%llu",
|
||||
custody, ready, (unsigned long long)expires);
|
||||
goto fail;
|
||||
@@ -603,6 +604,9 @@ static void dm_file_done(void* arg, int error) {
|
||||
if (!custody) sqlite3_bind_blob(st, idx++, receipt, sizeof(receipt), SQLITE_STATIC);
|
||||
sqlite3_bind_blob(st, idx, job->id, 16, SQLITE_STATIC);
|
||||
if (dm_media_step(inst, st) || sqlite3_changes(inst->topo_sqlite_db) != 1) goto fail;
|
||||
if (!custody && ready < 0)
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DM, "dm_media: recovered expired file conv=%s generation=%llu bytes=%llu",
|
||||
job->conv, (unsigned long long)job->generation, (unsigned long long)job->size);
|
||||
if (custody) {
|
||||
uint8_t stored[24]; memcpy(stored, job->id, 16); memcpy(stored + 16, &expires, 8);
|
||||
dm_media_send(inst, group, peer, DM_MEDIA_STORED, stored, sizeof(stored));
|
||||
|
||||
+42
-5
@@ -1,6 +1,6 @@
|
||||
/* test_dm_media — реальные A–S–B и S–M, независимые роли supernode/storage.
|
||||
* Прямая доставка, offline custody, restart S/M при выключенном A, TTL,
|
||||
* квота, точные квитанции, поздние STORE/STORED и отзыв активных операций.
|
||||
* квота, точные квитанции, поздние STORE/STORED, возврат оригинала после EXPIRED и отзыв активных операций.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -45,6 +45,7 @@ struct media_test {
|
||||
uint8_t body[2048], id[16], receipt[DM_MEDIA_RECEIPT_SIZE];
|
||||
size_t body_len;
|
||||
unsigned phase, ticks, heartbeats, work_ticks;
|
||||
uint64_t recovery_start;
|
||||
int failed;
|
||||
void* timer;
|
||||
};
|
||||
@@ -604,13 +605,49 @@ static void tick(void* arg) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "DM expiry test: release checks=%u blocked=%u lost EXPIRED=%u restart=1 recovered PASS",
|
||||
release_checks, expiry_blocks, expiry_notices);
|
||||
#endif
|
||||
utun_instance_destroy(a); t->nodes[A] = NULL;
|
||||
if (start_node(t, B)) { failure(t, "receiver return after TTL"); return; }
|
||||
t->phase++;
|
||||
uasync_stop(t->ua); return;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (value(b, "SELECT count(*) FROM dm_files WHERE dir=0 AND seq=3 AND ready=-1 AND receipt IS NULL") == 1) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "DM recovery test: B received EXPIRED while A offline; returning original author");
|
||||
if (start_node(t, A)) { failure(t, "source return after EXPIRED"); return; }
|
||||
t->phase++;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (live(a, t->ids[B]) && live(b, t->ids[A])) {
|
||||
t->recovery_start = ntp_time_get_seconds(b);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "DM recovery test: A/B routes restored; waiting for verified original");
|
||||
t->phase++;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
if (file_correct(t) && value(a, "SELECT count(*) FROM dm_files WHERE dir=1 AND seq=3 AND receipt IS NOT NULL") == 1 &&
|
||||
value(s, "SELECT count(*) FROM dm_media_jobs WHERE seq=3 AND receipt IS NOT NULL AND body IS NULL") == 1) {
|
||||
if (dm_media_verify_receipt(a, t->receipt, t->body, t->body_len)) {
|
||||
failure(t, "recovered file receipt does not match original body"); return;
|
||||
}
|
||||
control(t, B, t->ids[S], DM_MEDIA_EXPIRED, t->id, 16);
|
||||
if (!file_correct(t) || value(m, "SELECT count(*) FROM dm_custody") ||
|
||||
value(b, "SELECT count(*) FROM dm_files WHERE dir=0 AND seq=3 AND ready=1 AND receipt IS NOT NULL") != 1) {
|
||||
failure(t, "late EXPIRED changed verified file or revived custody"); return;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "DM recovery test: exact original, signed receipt, S cleanup, late EXPIRED PASS");
|
||||
t->phase++; uasync_stop(t->ua); return;
|
||||
}
|
||||
if (ntp_time_get_seconds(b) >= t->recovery_start + 16) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM recovery test: timeout A_to_B=%d B_to_A=%d B_ready=%d B_receipt=%d",
|
||||
live(a, t->ids[B]), live(b, t->ids[A]), value(b, "SELECT ready FROM dm_files WHERE dir=0 AND seq=3"),
|
||||
value(b, "SELECT count(*) FROM dm_files WHERE dir=0 AND seq=3 AND receipt IS NOT NULL"));
|
||||
failure(t, "available original cannot complete receive after EXPIRED"); return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (t->failed) return;
|
||||
if (t->ticks > 6000) { failure(t, "timeout"); return; }
|
||||
if (t->ticks > 9000) { failure(t, "timeout"); return; }
|
||||
t->timer = uasync_set_timeout(t->ua, 100, t, tick, "dm_media_test");
|
||||
}
|
||||
|
||||
@@ -666,6 +703,6 @@ int main(void) {
|
||||
printf("DM media allocations: %zu->%zu\n", allocations, u_get_allocated_count());
|
||||
if (allocations != u_get_allocated_count()) t.failed = 1;
|
||||
printf("test_dm_media: phase=%u heartbeats=%u %s artifacts=%s\n", t.phase, t.heartbeats,
|
||||
!t.failed && t.phase == 9 ? "PASS" : "FAIL", t.dir);
|
||||
return t.failed || t.phase != 9;
|
||||
!t.failed && t.phase == 12 ? "PASS" : "FAIL", t.dir);
|
||||
return t.failed || t.phase != 12;
|
||||
}
|
||||
Reference in new issue
Block a user