|
|
|
|
@ -133,9 +133,6 @@ static int fb_author_key(struct UTUN_INSTANCE* inst, const char* channel, uint64
|
|
|
|
|
int chat_feedback_ensure_channel(struct UTUN_INSTANCE* inst, const char* channel) { |
|
|
|
|
char table[40], sql[1024]; uint64_t id; |
|
|
|
|
if (fb_table(inst, channel, table, &id) < 0) return -1; |
|
|
|
|
if (topo_node_sqlite_channel_get(inst->topo_sqlite_db, channel, NULL, 0, NULL, NULL, NULL, NULL) < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: unknown channel=%s", channel); return -1; |
|
|
|
|
} |
|
|
|
|
snprintf(sql, sizeof(sql), "CREATE TABLE IF NOT EXISTS %s(" |
|
|
|
|
"record_key BLOB PRIMARY KEY CHECK(length(record_key)=32)," |
|
|
|
|
"bucket INTEGER NOT NULL,message_id BLOB NOT NULL CHECK(length(message_id)=32)," |
|
|
|
|
@ -189,6 +186,8 @@ static int fb_apply_row(struct UTUN_INSTANCE* inst, const char* channel, const s
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: invalid signature ch=%s node=%016llx kind=%u ver=%llu", |
|
|
|
|
channel, (unsigned long long)r->node_id, r->kind, (unsigned long long)r->ver); return -1; |
|
|
|
|
} |
|
|
|
|
char message_hex[65]; |
|
|
|
|
for (unsigned i = 0; i < 32; i++) snprintf(message_hex + 2 * i, 3, "%02x", r->message_id[i]); |
|
|
|
|
struct chat_feedback_record old; |
|
|
|
|
int found = chat_feedback_get(inst, channel, r->message_id, r->node_id, r->kind, &old); |
|
|
|
|
if (found < 0) return -1; |
|
|
|
|
@ -217,8 +216,8 @@ static int fb_apply_row(struct UTUN_INSTANCE* inst, const char* channel, const s
|
|
|
|
|
int rc = sqlite3_step(st); sqlite3_finalize(st); |
|
|
|
|
if (rc != SQLITE_DONE) return fb_db_error(inst, channel, "put step"); |
|
|
|
|
if (merkle_sync_recompute_path(inst, MT_DATA_FEEDBACK, channel, fb_read64(record_key.bytes)) < 0) return -1; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT_SYNC, "feedback: applied ch=%s node=%016llx kind=%u ver=%llu previous=%llu bytes=%u", |
|
|
|
|
channel, (unsigned long long)r->node_id, r->kind, (unsigned long long)r->ver, |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT_SYNC, "feedback: applied ch=%s message=%s node=%016llx kind=%u ver=%llu previous=%llu bytes=%u", |
|
|
|
|
channel, message_hex, (unsigned long long)r->node_id, r->kind, (unsigned long long)r->ver, |
|
|
|
|
(unsigned long long)(found ? old.ver : 0), r->data_len); return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -313,7 +312,7 @@ static int fb_page(void* ctx, const char* channel, uint64_t prefix, int av, cons
|
|
|
|
|
sqlite3_finalize(st); data[0] = count >> 8; data[1] = count & 255; *len = off; return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Хеширует подписанные записи листа в том же порядке, что и страничный обход. */ |
|
|
|
|
/* Хеширует подписанные тела: разные валидные подписи одного тела не меняют корень. */ |
|
|
|
|
static int fb_hash(void* ctx, const char* channel, uint8_t level, uint64_t prefix, EVP_MD_CTX* hash) { |
|
|
|
|
struct UTUN_INSTANCE* inst = ctx; char table[40], sql[256]; |
|
|
|
|
if (fb_table(inst, channel, table, NULL) < 0 || level != MT_MAX_LEVEL) { |
|
|
|
|
@ -328,7 +327,7 @@ static int fb_hash(void* ctx, const char* channel, uint8_t level, uint64_t prefi
|
|
|
|
|
struct chat_feedback_record r; uint8_t wire[FB_WIRE_MAX]; |
|
|
|
|
if (fb_read_row(st, &r) < 0) { count = -1; break; } |
|
|
|
|
int bytes = fb_encode(&r, wire); |
|
|
|
|
if (bytes < 0 || EVP_DigestUpdate(hash, wire, bytes) != 1) { |
|
|
|
|
if (bytes < 0 || EVP_DigestUpdate(hash, wire, bytes - 64) != 1) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: leaf digest failed ch=%s", channel); count = -1; break; |
|
|
|
|
} |
|
|
|
|
count++; |
|
|
|
|
@ -397,7 +396,7 @@ int chat_feedback_init(struct UTUN_INSTANCE* inst) {
|
|
|
|
|
char channel[21]; snprintf(channel, sizeof(channel), "%s", sqlite3_column_text(channels, 0)); |
|
|
|
|
char table[40], sql[256]; |
|
|
|
|
if (chat_feedback_ensure_channel(inst, channel) < 0 || fb_table(inst, channel, table, NULL) < 0) { result = -1; break; } |
|
|
|
|
snprintf(sql, sizeof(sql), "SELECT message_id,node_id,kind,ver,data,signature FROM %s", table); |
|
|
|
|
snprintf(sql, sizeof(sql), "SELECT message_id,node_id,kind,ver,data,signature,record_key,bucket FROM %s", table); |
|
|
|
|
sqlite3_stmt* st = NULL; |
|
|
|
|
if (sqlite3_prepare_v2(inst->topo_sqlite_db, sql, -1, &st, NULL) != SQLITE_OK) { |
|
|
|
|
result = fb_db_error(inst, channel, "init records"); break; |
|
|
|
|
@ -413,6 +412,11 @@ int chat_feedback_init(struct UTUN_INSTANCE* inst) {
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: rebuild failed ch=%s node=%016llx", |
|
|
|
|
channel, (unsigned long long)r.node_id); result = -1; break; |
|
|
|
|
} |
|
|
|
|
if (sqlite3_column_bytes(st, 6) != 32 || memcmp(record_key.bytes, sqlite3_column_blob(st, 6), 32) |
|
|
|
|
|| (uint64_t)sqlite3_column_int64(st, 7) != (fb_read64(record_key.bytes) >> 39)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: corrupt local index ch=%s node=%016llx", |
|
|
|
|
channel, (unsigned long long)r.node_id); result = -1; break; |
|
|
|
|
} |
|
|
|
|
records++; |
|
|
|
|
} |
|
|
|
|
sqlite3_finalize(st); |
|
|
|
|
|