Document verified history deletion and message-specific delivery
This commit is contained in:
1 parent
aa97e053cd
commit
143788cdcd
2 files changed
+108
-25
No files matched your search
+32
-23
@@ -1,10 +1,9 @@
|
||||
# db_sync — децентрализованная реплицируемая таблица с цепным хешированием
|
||||
|
||||
Аудит 26.10.2026 частичный. Подтверждён дефект: отказ инициализации OpenSSL при
|
||||
повторной вставке удаляет корректную историю. Испорченный входящий дубликат также
|
||||
может удалить корректную сохранённую копию. Runtime-код не исправлялся;
|
||||
продолжение остановлено по AGENTS.md. Условия удаления и воспроизведение —
|
||||
[отчёт, раздел10](../../doc/member_merkle_audit_findings.txt).
|
||||
Аудит 26.10.2026 частичный. Исправлены удаление корректной истории при отказе
|
||||
проверки/неверном входящем повторе и SQL доставки. ACK_PUSH теперь содержит
|
||||
message_id; старый формат не поддерживается. Условия удаления, проверки и
|
||||
оставшиеся границы аудита — [отчёт, разделы10–11](../../doc/member_merkle_audit_findings.txt).
|
||||
|
||||
## 1. Архитектура
|
||||
|
||||
@@ -47,7 +46,7 @@
|
||||
- `next_id` — автоинкрементный ID записей
|
||||
- `last_timestamp_ms` — последний использованный timestamp (монотонный)
|
||||
- `peers[]` — динамический массив SI_PEER (состояние синхронизации с каждым пиром)
|
||||
- `recalc` — отложенный пересчёт цепных хешей (асинхронно, батчами по 50)
|
||||
- `recalc` — позиция, pending и таймер пересчёта; ошибка сохраняет pending, батчи по50
|
||||
- `ttl_timer` — периодическая TTL-очистка (каждый час)
|
||||
- `on_insert` / `on_insert_arg` — коллбэк при вставке записи
|
||||
|
||||
@@ -73,6 +72,7 @@ CREATE TABLE "msg_GENERAL" (
|
||||
flags INTEGER DEFAULT 0, -- DB_REC_FLAG_WAS_SENT (0x01)
|
||||
data BLOB, -- JSON-данные записи
|
||||
author_signature BLOB NOT NULL, -- Ed25519(ts || data), 64 байта
|
||||
message_id BLOB NOT NULL UNIQUE CHECK(length(message_id)=32),
|
||||
local_attrs TEXT DEFAULT '', -- локальные атрибуты (не реплицируются)
|
||||
delivered_peers INTEGER DEFAULT 0, -- счётчик пиров, принявших PUSH
|
||||
delivery_chain TEXT DEFAULT '', -- цепочка peer_id hex через запятую
|
||||
@@ -95,7 +95,7 @@ chain_hash[0] = SHA256(0x00..00 [32] || id_0 || ts_0 || author_0 || sig_0)
|
||||
|----------|----------|
|
||||
| **Цепной хеш** | Каждая запись ссылается на хеш предыдущей — защита от вставки/удаления в середине цепочки |
|
||||
| **Ed25519-подписи** | Каждая запись подписана автором. Проверяется при вставке (и локальной, и от пира) |
|
||||
| **Async recalc** | Пересчёт `chain_hash` батчами по 50 записей (через `uasync_set_timeout`) — не блокирует event loop |
|
||||
| **Async recalc** | Батчи по50 с атомарным продвижением после COMMIT; перед обменом flush завершается синхронно, ошибка прерывает раунд |
|
||||
| **Master/Slave** | Узел с бОльшим `node_id` — мастер (шлёт `INIT_SYNC`). Узел с меньшим — слейв (шлёт `REQUEST_SYNC`) |
|
||||
| **Периодическая проверка** | Каждые 5 секунд — поиск пиров с sync_state=0 и запуск синхронизации, детекция таймаутов (15с) |
|
||||
| **TTL-очистка** | Каждый час — удаление неподтверждённых (`flags&1==0`) записей локального автора старше `db_sync_ttl` |
|
||||
@@ -371,20 +371,22 @@ tp=10 → entry[0]: pos=9 (tp - 1)
|
||||
|
||||
### 3.7 DB_MSG_ACK_PUSH (0x06)
|
||||
|
||||
**Отправитель:** получатель PUSH → автору
|
||||
**Отправитель:** получатель PUSH → переславшему пиру
|
||||
**Назначение:** подтверждение получения PUSH-записи
|
||||
|
||||
```
|
||||
┌──────┬──────────┬──────────────┐
|
||||
│ 0x06 │ ts (8) │ author (8) │
|
||||
└──────┴──────────┴──────────────┘
|
||||
total: 17 bytes
|
||||
┌──────┬──────────────────┐
|
||||
│ 0x06 │ message_id (32) │
|
||||
└──────┴──────────────────┘
|
||||
total: 33 bytes
|
||||
```
|
||||
|
||||
- `ts` — timestamp записи (uint64, host byte order)
|
||||
- `author` — node_id автора (uint64, host byte order)
|
||||
- `message_id` — SHA256 идентичности записи (group_id, timestamp, подпись; см. chat_feedback.h).
|
||||
|
||||
При получении ACK_PUSH автор помечает запись флагом `DB_REC_FLAG_WAS_SENT` (0x01) и обновляет `delivery_chain`/`delivered_peers`.
|
||||
Получатель ACK одним UPDATE находит ровно эту запись, добавляет peer_id в delivery_chain
|
||||
и увеличивает delivered_peers только при первом ACK от этого пира. Для локального автора
|
||||
также устанавливается DB_REC_FLAG_WAS_SENT. Постановка PUSH в очередь доставкой не считается.
|
||||
ACK отправляется и при корректном дубликате; записи с одинаковыми автором/временем независимы.
|
||||
|
||||
### 3.8 DB_MSG_SYNC_DONE (0x07)
|
||||
|
||||
@@ -488,7 +490,8 @@ tp=10 → entry[0]: pos=9 (tp - 1)
|
||||
1. `db_sync_flush_recalc()` — синхронно завершает все отложенные пересчёты
|
||||
2. Парсит `from`, `count`, `vp`, `want_from`
|
||||
3. Для каждой записи (макс. 32): `si_parse_record()` → `db_record_insert()`
|
||||
- Проверка Ed25519-подписи (если неверна — удаление записи)
|
||||
- Проверка длины тела и подписи до чтения64 байт; проверка подписи конкретных входящих байтов
|
||||
- Если строка уже есть, независимо проверяются сохранённые байты: только доказанная порча допускает DELETE
|
||||
- Проверка дубликата по `(timestamp, author_signature)`
|
||||
- Вычисление chain_hash
|
||||
- Вставка в SQLite, запуск async recalc
|
||||
@@ -497,6 +500,11 @@ tp=10 → entry[0]: pos=9 (tp - 1)
|
||||
5. Если `want_from != DB_WANT_FROM_NONE`: отправляет встречный SEND_DATA
|
||||
6. Если `want_from == DB_WANT_FROM_NONE` и `sync_state == 1`: отправляет `SYNC_DONE`
|
||||
|
||||
Ошибка формата, подписи, ключа, SQL или пересчёта прерывает раунд без подтверждения его позиций.
|
||||
Уже успешно вставленные записи остаются в БД; повторный раунд принимает их как дубликаты.
|
||||
При выдаче пачки сохранённая порча удаляется после завершения SELECT в своей транзакции;
|
||||
пачка старого раунда не отправляется. После COMMIT сбрасываются позиции всех пиров и пересчёт начинается с0.
|
||||
|
||||
### 4.5 Обработка SYNC_DONE
|
||||
|
||||
1. `db_sync_flush_recalc()`
|
||||
@@ -511,14 +519,13 @@ tp=10 → entry[0]: pos=9 (tp - 1)
|
||||
1. При локальной вставке (`db_sync_insert_signed`):
|
||||
- Формирует PUSH-сообщение
|
||||
- Рассылает всем пирам с `sync_state >= 1`
|
||||
- Обновляет `delivery_chain` для каждого пира
|
||||
2. При получении PUSH:
|
||||
- Вставляет запись через `db_record_insert()`
|
||||
- Шлёт `ACK_PUSH` обратно автору
|
||||
- После успешной вставки/корректного повтора шлёт `ACK_PUSH(message_id)` переславшему пиру
|
||||
- Если запись вставлена в середину (не в конец) — сбрасывает `synced_pos` всех пиров до позиции вставки-1
|
||||
3. При получении ACK_PUSH:
|
||||
- Помечает запись флагом `WAS_SENT`
|
||||
- Обновляет `delivery_chain`
|
||||
- Для локального автора помечает запись флагом `WAS_SENT`
|
||||
- Идемпотентно обновляет `delivery_chain`/`delivered_peers` по message_id
|
||||
|
||||
---
|
||||
|
||||
@@ -528,14 +535,16 @@ tp=10 → entry[0]: pos=9 (tp - 1)
|
||||
Требуемый контракт удаления: завершённая проверка установила неверность подписи самих сохранённых байтов
|
||||
либо доказан повреждённый формат этой записи. Отсутствие ключа, отказ OpenSSL/памяти/SQL не доказывают порчу.
|
||||
Неверная входящая копия не доказывает неверность корректной строки с тем же ключом.
|
||||
Текущий db_verify_author_sig объединяет эти причины, а db_record_insert и si_send_data_batch используют отказ для DELETE;
|
||||
это подтверждённый неисправленный дефект, а не реализованный требуемый контракт.
|
||||
db_verify_author_sig различает VALID/BAD_SIGNATURE/BAD_FORMAT/DEFERRED/ERROR.
|
||||
Операционные ошибки, включая SQLITE_NOMEM при чтении BLOB, сохраняют строки.
|
||||
DELETE и замена повреждённой строки атомарны; сброс позиций и планирование цепочки выполняются после COMMIT.
|
||||
2. **Цепной хеш:** SHA256-цепочка защищает от вставки/удаления записей в середине истории. При проверке (`db_sync_chain_verify`) или при обнаружении расхождения во время отправки — запускается пересчёт с позиции ошибки.
|
||||
3. **Публичные ключи:** Ed25519 публичный ключ пира получается из трёх источников (по приоритету):
|
||||
- Локальный `inst->my_ed25519_pubkey` (для своих записей)
|
||||
- `topo_node_sqlite_get_ed25519_pubkey()` (из БД узлов)
|
||||
- `conn->peer_ed25519_pubkey` (из ETCP handshake)
|
||||
4. **Отсутствие удаления/редактирования:** записи не редактируются и не удаляются явно — только TTL-очистка неподтверждённых.
|
||||
4. **Удаление:** кроме TTL неподтверждённых, удаляются доказанно повреждённые сохранённые строки.
|
||||
Полный аудит TTL, финального сравнения хешей и callbacks ещё не завершён.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user