perf(chat): sends re-read by origin, chat scans under the scan budget

A z_sendmany submit set off a burst of wallet scans before anything had
changed. Ops now re-read the wallet when they finish, by who started them:
the user's sends and dialog ops get a re-read plus one forced balance once
none of their ops is pending; client auto-shield re-reads unforced; chat
messages and splits refresh only the chat note view. A pending send no
longer forces scans, and chat txids no longer enter the tracked-send set.

Chat ingest skips stored messages before decrypting and caches
undecryptable ones per identity. The inbox scan runs when getwalletinfo's
txcount changed (a cheap probe, at most 2% duty) or a cost-scaled safety
net is due; the note-buffer scan runs on need. Both are admitted by the
scan budget, on a steady clock. The chat payer comes from a per-address
view of verified notes, with debits re-applied across in-flight scans;
splits are sized to one payer and only when they can refill the buffer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 11:06:34 -05:00
co-authored by Claude Opus 5.5
parent f86cc4d6b7
commit b29a09eaa0
15 changed files with 1037 additions and 177 deletions
+7
View File
@@ -14,6 +14,7 @@
#include <functional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace dragonx::chat {
@@ -99,9 +100,15 @@ public:
const ChatStore& store() const { return store_; }
ChatStore& store() { return store_; }
std::uint64_t decryptAttempts() const { return decrypt_attempts_; } // tests / diagnostics
private:
ChatKeyPair identity_{};
bool has_identity_ = false;
// Decrypt failures, keyed by what the decrypt reads (bounded). A rescan re-sends every message ever
// received, so without it each poll re-ran the crypto for every undecryptable memo.
std::unordered_set<std::string> undecryptable_;
std::uint64_t decrypt_attempts_ = 0;
ChatStore store_;
ChatDatabase* db_ = nullptr; // optional; not owned
std::function<bool(const std::string&)> blocked_pred_; // true => cid is blocked (drop its messages)