95 lines
7.1 KiB
C
95 lines
7.1 KiB
C
/* merkle_sync — обмен различиями между наборами записей по Merkle-дереву.
|
|
* Движок сверяет хеши и передаёт страницы изменившихся листьев; модель данных
|
|
* через data_ops читает записи, проверяет подписи/права и сохраняет изменения.
|
|
* Сессия относится к (пир, dataset, namespace), отправка учитывает backpressure.
|
|
* Все операции — в потоке uasync; один движок на UTUN_INSTANCE. */
|
|
#ifndef MERKLE_SYNC_H
|
|
#define MERKLE_SYNC_H
|
|
|
|
#include "merkle_tree.h"
|
|
#include <stddef.h>
|
|
|
|
struct UTUN_INSTANCE;
|
|
|
|
#define MT_OK 0
|
|
#define MT_ERR_IO -1
|
|
#define MT_ERR_PROTOCOL -2
|
|
#define MT_ERR_DATA -3
|
|
#define MT_ERR_CONFLICT -4
|
|
#define MT_ERR_DISCONNECTED -5
|
|
#define MT_ERR_TIMEOUT -6
|
|
#define MT_PAGE_SIZE 16384
|
|
#define MT_KEY_SIZE 32
|
|
#define MT_DATA_MEMBERS 0
|
|
#define MT_DATA_FEEDBACK 1
|
|
|
|
/* Полный ключ курсора, big-endian; первые 8 байт задают ветвь дерева.
|
|
* Для node_id участника: BE64(node_id), затем 24 нулевых байта. */
|
|
typedef struct { uint8_t bytes[MT_KEY_SIZE]; } merkle_key_t;
|
|
|
|
/* Namespace — каноническая десятичная строка uint64_t. Движок не знает CHAT-групп.
|
|
* SHA256 листа обновляется моделью в порядке unsigned key; родителей считает merkle_tree.
|
|
* Все операции выполняются в uasync-потоке экземпляра. */
|
|
struct merkle_sync_data_ops {
|
|
merkle_leaf_hash_fn update_bucket_hash; // хеш содержимого одного листа
|
|
/* Одна страница листа: after_valid=0 начинает обход, иначе только key > after.
|
|
* len: ёмкость -> размер; next — последний ключ; more — есть продолжение.
|
|
* Пустой результат разрешён только с more=0. Ошибка не кодируется пустой страницей. */
|
|
int (*get_page)(void* ctx, const char* ns, uint64_t prefix, int after_valid, const merkle_key_t* after,
|
|
uint8_t* buf, size_t* len, merkle_key_t* next, int* more);
|
|
/* Проверить весь формат, применить записи и обновить дерево в одной DB-транзакции.
|
|
* <0 — ошибка. Никаких broadcast/send-back из этого callback. */
|
|
int (*apply_items)(void* ctx, const char* ns, uint64_t from_peer, const uint8_t* data, size_t len);
|
|
int (*validate_peer)(void* ctx, const char* ns, uint64_t peer); // необязательная проверка: только 1 разрешает приём
|
|
/* Проверить отложенные зависимости модели после обоих проходов, до подтверждения корня. */
|
|
int (*finish)(void* ctx, const char* ns);
|
|
/* Необязательный отложенный приём: все четыре callback задаются вместе.
|
|
* stage_page накапливает страницы без изменения опубликованных хешей;
|
|
* commit_pull применяет результат; abort_pull освобождает pull и после успеха. */
|
|
int (*begin_pull)(void* ctx, const char* ns, uint64_t peer, uint64_t round, void** pull);
|
|
int (*commit_pull)(void* ctx, void* pull);
|
|
void (*abort_pull)(void* ctx, void* pull);
|
|
int (*stage_page)(void* ctx, void* pull, uint64_t prefix, int after_valid, const merkle_key_t* after,
|
|
const merkle_key_t* next, int more, const uint8_t* data, size_t len);
|
|
};
|
|
|
|
/* result — MT_OK или MT_ERR_*; ns заимствован на время callback. */
|
|
typedef void (*merkle_sync_done_cb)(uint64_t peer, const char* ns, int result, void* arg);
|
|
|
|
/* Зарегистрировать сервис; нужны ua и общая SQLite. ops/data_ctx живут до destroy. 0/-1. */
|
|
int merkle_sync_init(struct UTUN_INSTANCE* inst, uint8_t svc_id,
|
|
const struct merkle_sync_data_ops* ops, void* data_ctx);
|
|
/* Init регистрирует MEMBERS. Добавить другой набор до запуска его сессий; 0/-1.
|
|
* ops/data_ctx живут до уничтожения движка. Каждый набор имеет свой Merkle-корень. */
|
|
int merkle_sync_register(struct UTUN_INSTANCE* inst, uint8_t dataset,
|
|
const struct merkle_sync_data_ops* ops, void* data_ctx);
|
|
/* Отменить сессии без done callbacks, снять обработчики и освободить движок. */
|
|
void merkle_sync_destroy(struct UTUN_INSTANCE* inst);
|
|
|
|
/* Подключить namespace к текущему ETCP-соединению и запросить контрольную точку.
|
|
* Успех — подтверждение одинакового корня конкретного раунда, не вечный статус.
|
|
* Повторные запросы объединяются; каждый callback вызывается один раз, обход не сбрасывается.
|
|
* После первого start изменения дерева автоматически синхронизируются с этим пиром.
|
|
* <0: запрос не принят, callback не вызывается. DOWN/DELETE завершают запросы ошибкой.
|
|
* Явные cancel/destroy освобождают запросы без callback.
|
|
* Callback может отменить сессию; уничтожение всего экземпляра должно быть отложенным. */
|
|
int merkle_sync_start(struct UTUN_INSTANCE* inst, uint8_t dataset, uint64_t peer, const char* ns,
|
|
merkle_sync_done_cb cb, void* arg);
|
|
void merkle_sync_cancel(struct UTUN_INSTANCE* inst, uint8_t dataset, uint64_t peer, const char* ns);
|
|
/* Отменить все сессии namespace / пира без callbacks. */
|
|
void merkle_sync_cancel_ns(struct UTUN_INSTANCE* inst, const char* ns);
|
|
void merkle_sync_cancel_peer(struct UTUN_INSTANCE* inst, uint64_t peer);
|
|
|
|
/* Обновление индекса вызывается моделью данных.
|
|
* При внешней транзакции caller вызывает changed ПОСЛЕ commit.
|
|
* Без внешней транзакции пересчёт сам создаёт savepoint и уведомляет после commit.
|
|
* Возвращает 1=изменилось, 0=no-op, <0=ошибка. */
|
|
int merkle_sync_recompute_path(struct UTUN_INSTANCE* inst, uint8_t dataset, const char* ns, uint64_t key);
|
|
/* После commit уведомить сессии namespace о локальном изменении дерева. */
|
|
void merkle_sync_changed(struct UTUN_INSTANCE* inst, uint8_t dataset, const char* ns);
|
|
/* Прочитать хеш узла дерева в hash. 0=успех, <0=ошибка. */
|
|
int merkle_sync_read_hash(struct UTUN_INSTANCE* inst, uint8_t dataset, const char* ns,
|
|
uint8_t level, uint64_t prefix, uint8_t hash[MT_HASH_SIZE]);
|
|
|
|
#endif
|