fix(chat): stop our own sent messages appearing as incoming duplicates
Two causes of the "every message I send shows up again as a reply from the peer": 1. Harvest bug — the full-refresh path fed a SENT tx's outgoing memos (from z_viewtransaction outgoing outputs) into the chat metadata extractor, and ChatService::ingest marks everything Incoming. So each send was re-ingested as a phantom "from peer" message. The recent-refresh path already omitted this, so it was accidental. Drop the outgoing chat-harvest (keep the tx-history harvest); genuine incoming still comes from z_listreceivedbyaddress, and our sends are recorded by the local echo. 2. Own-identity ingest filter — a memo whose sender public key equals our own identity is by definition something we sent (only we hold our key); it must never be ingested as incoming. Skip those in ingest. This also collapses same-seed self-chat (running the SAME wallet in the full node and Lite makes them one chat identity, so sends land on an address we also own and loop back). For a real two-party chat use two DIFFERENT wallets/seeds — same-seed wallets are one identity and can't be distinct peers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,8 +29,16 @@ int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata
|
|||||||
std::vector<std::string>* newIncomingCids) {
|
std::vector<std::string>* newIncomingCids) {
|
||||||
if (!has_identity_) return 0;
|
if (!has_identity_) return 0;
|
||||||
|
|
||||||
|
const std::string myPubKey = chatIdentityPublicKeyHex(identity_);
|
||||||
|
|
||||||
int added = 0;
|
int added = 0;
|
||||||
for (const auto& meta : metadata) {
|
for (const auto& meta : metadata) {
|
||||||
|
// A memo whose sender is our OWN identity is something we sent (only we hold our key). The local
|
||||||
|
// echo already records it as outgoing — ingesting it as incoming would duplicate it as a phantom
|
||||||
|
// "from peer" message. (This also collapses same-seed self-chat, where the "peer" wallet shares
|
||||||
|
// our identity, so every message would otherwise loop back.)
|
||||||
|
if (!myPubKey.empty() && meta.sender_public_key_hex == myPubKey) continue;
|
||||||
|
|
||||||
ChatMessage message;
|
ChatMessage message;
|
||||||
message.direction = ChatDirection::Incoming;
|
message.direction = ChatDirection::Incoming;
|
||||||
message.txid = meta.txid;
|
message.txid = meta.txid;
|
||||||
|
|||||||
@@ -168,20 +168,6 @@ void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetada
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetadata>& destination,
|
|
||||||
const std::string& txid,
|
|
||||||
const NetworkRefreshService::TransactionViewCacheEntry& entry)
|
|
||||||
{
|
|
||||||
if (!chat::hushChatFeatureEnabledAtBuild()) return;
|
|
||||||
|
|
||||||
std::vector<chat::HushChatMemoOutput> outputs;
|
|
||||||
outputs.reserve(entry.outgoing_outputs.size());
|
|
||||||
for (const auto& output : entry.outgoing_outputs) {
|
|
||||||
if (!output.memo.empty()) outputs.push_back(chat::HushChatMemoOutput{output.position, output.memo});
|
|
||||||
}
|
|
||||||
appendExtractedHushChatMetadata(destination, txid, outputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetadata>& destination,
|
void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetadata>& destination,
|
||||||
const HushChatMemoOutputMap& outputsByTxid)
|
const HushChatMemoOutputMap& outputsByTxid)
|
||||||
{
|
{
|
||||||
@@ -920,7 +906,10 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
|||||||
if (cached != snapshot.viewTxCache.end()) {
|
if (cached != snapshot.viewTxCache.end()) {
|
||||||
if (!trackedSend || !cached->second.outgoing_outputs.empty()) {
|
if (!trackedSend || !cached->second.outgoing_outputs.empty()) {
|
||||||
appendViewTransactionOutputs(result.transactions, txid, cached->second);
|
appendViewTransactionOutputs(result.transactions, txid, cached->second);
|
||||||
appendExtractedHushChatMetadata(result.hushChatMetadata, txid, cached->second);
|
// NB: do NOT extract chat metadata from our OWN outgoing memos here — ingest marks
|
||||||
|
// everything Incoming, so that duplicates every sent message as a phantom "from peer"
|
||||||
|
// entry. Genuine incoming comes from the z_listreceivedbyaddress path; our sends are
|
||||||
|
// recorded by the local echo. (The recent-refresh variant already omits this.)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -945,7 +934,8 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
|||||||
|
|
||||||
auto entry = parseViewTransactionCacheEntry(viewTransaction);
|
auto entry = parseViewTransactionCacheEntry(viewTransaction);
|
||||||
appendViewTransactionOutputs(result.transactions, txid, entry);
|
appendViewTransactionOutputs(result.transactions, txid, entry);
|
||||||
appendExtractedHushChatMetadata(result.hushChatMetadata, txid, entry);
|
// (Chat metadata is harvested only from RECEIVED notes, not our own outgoing memos — see
|
||||||
|
// the cached-view branch above.)
|
||||||
|
|
||||||
json rawTransaction;
|
json rawTransaction;
|
||||||
bool hasRawTransaction = false;
|
bool hasRawTransaction = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user