fix(chat): address adversarial review of the send-path change
Four confirmed findings from the review of ef247c9:
1. Persistence regression — the deferred-persist echo (in-memory Sending, written
only when the async callback resolved) meant a message broadcast on-chain but
whose callback hadn't fired yet was LOST from history if the app quit/crashed
in that window. Persist the echo immediately as Sending and UPSERT the final
status on resolve (new ChatDatabase::upsert with ON CONFLICT DO UPDATE, since
append is INSERT-OR-IGNORE). A stray persisted Sending still loads as Sent.
2. Fee ceiling — dragonxd REJECTS a 0-value tx whose fee exceeds the default
miners fee (0.0001), and max(getDefaultFee(), 0.0001) can only raise it, so a
default_fee > 0.0001 broke every chat send. Pin chat to exactly kChatMinFeeDrgx,
dropping getDefaultFee() from this path (chat always moves 0 value).
3. Lifetime — the resolve callback had no generation guard, so a wallet lock (which
doesn't disconnect) between submit and callback could resolve against a cleared
store. Capture chat_session_generation_ and bail on mismatch (both the full-node
callback and the lite optimistic resolve), matching the identity-fetch pattern.
4. Retry misdirect — Retry on a failed CONTACT REQUEST called sendChatMessage,
which (no peer key yet) just showed "waiting for reply". Route it to
sendContactRequestForCid() (refactored out of startChatConversation) so it
re-sends the request into the SAME conversation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -110,14 +110,17 @@ bool ChatService::recordOutgoing(const ChatMessage& message) {
|
||||
}
|
||||
|
||||
bool ChatService::recordOutgoingPending(const ChatMessage& message) {
|
||||
// In-memory only — deliberately NOT persisted yet, so "Sending" never survives to a restart; the
|
||||
// row is written by resolveOutgoing() once the broadcast resolves to Sent/Failed.
|
||||
return store_.append(message);
|
||||
// Persist immediately (as Sending) so a send survives an app quit before the broadcast resolves;
|
||||
// resolveOutgoing() then UPSERTS the row to the final status. A stray persisted Sending (crash mid-
|
||||
// broadcast) loads as Sent (see ChatDatabase::deserialize).
|
||||
const bool appended = store_.append(message);
|
||||
if (appended && db_) db_->upsert(message);
|
||||
return appended;
|
||||
}
|
||||
|
||||
void ChatService::resolveOutgoing(const std::string& txid, ChatDelivery delivery) {
|
||||
const ChatMessage* updated = store_.updateDelivery(txid, delivery);
|
||||
if (updated && db_) db_->append(*updated); // first persist, carrying the final status
|
||||
if (updated && db_) db_->upsert(*updated); // overwrite the Sending row with the final status
|
||||
}
|
||||
|
||||
} // namespace dragonx::chat
|
||||
|
||||
Reference in New Issue
Block a user