From 5fc621690a1610a15bd1c4f45083667e051ee0d8 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 14 Jul 2026 19:23:02 +0300 Subject: [PATCH] fix: wall-clock timestamps + merkle_sync timeout + build script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace get_time_us() with utun_gettimeofday() for PUSH message timestamps (db_sync.c, chat_core.c) — monotonic clocks differ across Linux/Windows - Fix merkle_sync: restart session timer in _handle_request to prevent double-sided timeout when both nodes start member_sync simultaneously - Fix INIT_RESP short-format: don't mark synced when my_count > peer_count - Add db_sync_get_last_timestamp() to avoid duplicate timestamp generation between db_sync and chat_core on_db_sync_insert - Fix db_sync_stub to generate timestamps consistently - build.sh: auto-rebuild chatgui via CMake after utun build --- build.sh | 9 +++++++++ src/db_sync.c | 6 +++++- src/db_sync.h | 1 + tools/chatgui/transport/chat_core.c | 2 +- tools/chatgui/transport/chat_sync.c | 7 +++++-- tools/chatgui/transport/db_sync_stub.c | 8 ++++++++ tools/chatgui/transport/merkle_sync.c | 9 ++++++++- 7 files changed, 37 insertions(+), 5 deletions(-) diff --git a/build.sh b/build.sh index 2638c0d8..e1bb0b52 100755 --- a/build.sh +++ b/build.sh @@ -180,6 +180,15 @@ if [ $BUILD_SUCCESS -eq 1 ]; then echo "" echo "Build completed successfully!" echo "Binary: utun" + + # Rebuild chatgui (CMake project) if transport sources changed + if [ -f tools/chatgui/build/CMakeCache.txt ] && command -v cmake &> /dev/null; then + echo "" + echo "======================================" + echo "Rebuilding chatgui (CMake)" + echo "======================================" + (cd tools/chatgui/build && cmake --build . -j4 2>&1) || echo "WARNING: chatgui build failed" + fi else echo "" echo "ERROR: Build failed!" diff --git a/src/db_sync.c b/src/db_sync.c index 20348d6b..0db2e845 100644 --- a/src/db_sync.c +++ b/src/db_sync.c @@ -351,7 +351,9 @@ int db_sync_insert_len(struct DB_SYNC_INSTANCE* si, const char* json_data, size_ int db_sync_insert_signed(struct DB_SYNC_INSTANCE* si, const char* json_data, size_t len, const uint8_t* sig, size_t sig_len) { if (!si||!si->enabled||!json_data||len==0) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC,"insert invalid args"); return -1; } - uint64_t dh=db_datahash((const uint8_t*)json_data,len), nu=get_time_us(); if (nu<=si->last_timestamp_us) nu=si->last_timestamp_us+1; si->last_timestamp_us=nu; uint64_t ts=nu, id=si->next_id; + struct timeval tv; utun_gettimeofday(&tv, NULL); uint64_t nu=(uint64_t)tv.tv_sec*1000000ULL+(uint64_t)tv.tv_usec; + if (nu<=si->last_timestamp_us) nu=si->last_timestamp_us+1; si->last_timestamp_us=nu; + uint64_t dh=db_datahash((const uint8_t*)json_data,len), ts=nu, id=si->next_id; int ret=db_record_insert(si,id,ts,dh,json_data,len,sig,sig_len); if (ret!=0) return ret; if (si->on_insert) si->on_insert(si,json_data,len,si->db_sync->inst->node_id,si->on_insert_arg); // Build PUSH: [DB_MSG_PUSH][id:8][ts:8][dh:8][dlen:4][data][sig_len:1][sig] @@ -370,6 +372,8 @@ uint32_t db_sync_count(struct DB_SYNC_INSTANCE* si) { if (!si||!si->enabled) ret void db_sync_set_insert_cb(struct DB_SYNC_INSTANCE* si, db_sync_insert_cb cb, void* arg) { if (!si) return; si->on_insert=cb; si->on_insert_arg=arg; } +uint64_t db_sync_get_last_timestamp(struct DB_SYNC_INSTANCE* si) { return si ? si->last_timestamp_us : 0; } + int db_sync_select(struct DB_SYNC_INSTANCE* si, uint32_t offset, uint32_t limit, db_sync_select_cb cb, void* arg) { if (!si||!si->enabled||!cb) return 0; sqlite3_stmt* stmt; if (si_prep(si,&stmt,"SELECT id,timestamp,author,data,author_signature,delivered_peers,delivery_chain FROM \"%s\" ORDER BY timestamp,datahash LIMIT ? OFFSET ?")!=SQLITE_OK) return 0; diff --git a/src/db_sync.h b/src/db_sync.h index aff01f8d..43a12e0b 100644 --- a/src/db_sync.h +++ b/src/db_sync.h @@ -85,6 +85,7 @@ int db_sync_insert(struct DB_SYNC_INSTANCE* si, const char* json_data); int db_sync_insert_signed(struct DB_SYNC_INSTANCE* si, const char* json_data, size_t len, const uint8_t* sig, size_t sig_len); uint32_t db_sync_count(struct DB_SYNC_INSTANCE* si); +uint64_t db_sync_get_last_timestamp(struct DB_SYNC_INSTANCE* si); // Select: iterate records ordered by (timestamp, datahash), starting at offset, max limit (0=unlimited). // Returns number of records passed to callback. diff --git a/tools/chatgui/transport/chat_core.c b/tools/chatgui/transport/chat_core.c index a71adec5..3b7bf8e8 100644 --- a/tools/chatgui/transport/chat_core.c +++ b/tools/chatgui/transport/chat_core.c @@ -1025,7 +1025,7 @@ static void on_db_sync_insert(struct DB_SYNC_INSTANCE* si, const char* data, siz if (!jd_start) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: on_db_sync_insert JSON parse fail: no '\"d\"' field ch=%s", CC_ID, ch_id); return; } uint64_t dh; compute_datahash((const uint8_t*)jd_start, jd_len, &dh); - uint64_t ts = get_time_us(); + uint64_t ts = db_sync_get_last_timestamp(si); char tbl[80]; msg_table_name(ch_id, tbl, sizeof(tbl)); uint8_t prev_ch[32]; get_last_chain_hash(ch_id, prev_ch); uint8_t chain_h[32]; compute_chain_hash(prev_ch, (int64_t)ts, dh, chain_h); diff --git a/tools/chatgui/transport/chat_sync.c b/tools/chatgui/transport/chat_sync.c index edbc91e6..239b1a35 100644 --- a/tools/chatgui/transport/chat_sync.c +++ b/tools/chatgui/transport/chat_sync.c @@ -189,9 +189,12 @@ static void cs_handle_init_resp(struct chat_sync* cs, uint64_t peer, cs_send(cs, ch_id, peer, snd, 7); DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: INIT_RESP peer ahead (peer=%u my=%u), request from=%u", CS_ID, peer_count, my_count, from); - } else { + } else if (peer_count == my_count) { if (ch) ch->synced = CS_SYNC_DONE; - DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: INIT_RESP synced (peer=%u <= my=%u)", + DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: INIT_RESP synced (peer=%u == my=%u)", + CS_ID, peer_count, my_count); + } else { + DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: INIT_RESP peer behind (peer=%u < my=%u), waiting for peer SEND_DATA", CS_ID, peer_count, my_count); } } diff --git a/tools/chatgui/transport/db_sync_stub.c b/tools/chatgui/transport/db_sync_stub.c index 91d48a4d..33e095a1 100644 --- a/tools/chatgui/transport/db_sync_stub.c +++ b/tools/chatgui/transport/db_sync_stub.c @@ -21,6 +21,7 @@ struct DB_SYNC_INSTANCE { struct DB_SYNC* db_sync; uint8_t enabled; + uint64_t last_timestamp_us; db_sync_insert_cb on_insert; void* on_insert_arg; }; @@ -117,6 +118,8 @@ int db_sync_insert_signed(struct DB_SYNC_INSTANCE* si, const char* json_data, size_t len, const uint8_t* sig, size_t sig_len) { (void)sig; (void)sig_len; if (!si || !si->enabled || !json_data || len == 0) return -1; + struct timeval tv; utun_gettimeofday(&tv, NULL); + si->last_timestamp_us = (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec; if (si->on_insert) si->on_insert(si, json_data, len, si->db_sync->inst->node_id, si->on_insert_arg); return 0; @@ -152,3 +155,8 @@ void db_sync_set_insert_cb(struct DB_SYNC_INSTANCE* si, db_sync_insert_cb cb, vo if (si->on_insert_arg) u_free(si->on_insert_arg); si->on_insert_arg = arg; } + +uint64_t db_sync_get_last_timestamp(struct DB_SYNC_INSTANCE* si) { + if (!si) return 0; + return si->last_timestamp_us; +} diff --git a/tools/chatgui/transport/merkle_sync.c b/tools/chatgui/transport/merkle_sync.c index 30f00af2..db3e0f5a 100644 --- a/tools/chatgui/transport/merkle_sync.c +++ b/tools/chatgui/transport/merkle_sync.c @@ -417,7 +417,14 @@ static void _handle_request(struct merkle_sync* ms, uint64_t peer, const char* n buckets[bc].level = lvl; buckets[bc].prefix_bytes = pb_i; buckets[bc].prefix = pr; bc++; off++; /* skip is_terminal byte */ } - if (bc > 0) _send_batch(ms, peer, ns, buckets, bc); + if (bc > 0) { + _send_batch(ms, peer, ns, buckets, bc); + struct ms_session* s = _session_find(ms, peer, ns); + if (s && s->active) { + if (s->timer) { uasync_cancel_timeout(ms->inst->ua, s->timer); s->timer = NULL; } + _session_start_timer(s); + } + } } static void _handle_batch(struct merkle_sync* ms, uint64_t peer, const char* ns,