Commit graph

6 commits

Author SHA1 Message Date
Local Dev
7983e24f2e perf(aegis): history was 473 round trips in series, and persisted all of them
Second half of the Theseus-lag investigation. 0.27.1 removed the 7.4 MB
store; this removes the two things that produced it and the latency that
came with it.

Measured on this profile's own cache: 6,981 transactions, 7.38 MB, and one
consolidation with 400 inputs (median 2).

- loadHistory() awaited getTx() per displayed transaction and then again per
  INPUT, strictly one at a time. The 400-input row alone cost 400 serial
  round trips. Both waves are now prefetched with bounded parallelism
  (getTxMany, 12 at a time). Against a simulated 10 ms link the same 473
  fetches take 771 ms instead of ~4,730 ms; on a real 30-50 ms link the
  serial version was 15-25 seconds per refresh, per wallet.
- Parent transactions were persisted forever. They exist only to compute a
  delta for the 25 rows on screen, and keeping every one ever seen is what
  grew the file. Only the displayed window is written now — 25 entries,
  16.4 KB in the harness — while parents stay in a process-lifetime map
  bounded at 20,000, seeded from the window so a restart does not refetch
  what is already visible. A second refresh issues zero fetches.

TX_CACHE_VERSION 4 discards v3 caches. The v3 cap of 400 was also exactly
the wrong number for this data: a 400-input transaction needs 401 entries,
so it would have evicted and refetched on every single refresh.
2026-10-03 16:12:53 +02:00
Local Dev
40683c84cf perf(aegis): stop freezing Theseus — a 7.4 MB store parsed on every read
Reported as "opening Aegis makes Theseus get stuck / not responding", and it
was Aegis's fault.

The host's add-on store is ONE JSON file per add-on, and storage.get() does a
readFileSync plus a JSON.parse of the whole thing on every call —
synchronously, on the Electron main thread, the thread that drives the entire
browser. storage.set() additionally stringifies and writes all of it.

That store had grown to 7.4 MB, 99.9% of it one wallet's txCache: getTx()
kept every transaction it ever fetched, with full vin/vout arrays, and a busy
chipnet test wallet had thousands. Measured on the real file: parse 59 ms,
stringify 39 ms. So one storage.get blocked the UI for ~60 ms, one set for
~99 ms, and fullState() — which reads the store seven times over
selectedWalletId, walletRoles, walletEntries, snapshotForSelected and the
server list — cost ~420 ms. emitState() runs on every adapter change, across
nine wallets, so the main thread was never given back.

Three changes:

- txCache is capped at 400 entries, pruned newest-first by block time
  (unconfirmed entries sort as newest — they are the current ones). Worst
  case ~0.3 MB per wallet instead of unbounded.
- TX_CACHE_VERSION 3, so existing oversized caches are discarded on first
  load rather than needing a manual clear.
- loadHistory() only persists when something actually changed; an idle wallet
  was rewriting the whole file on every poll for nothing.
- api.storage gains a write-through read cache, so repeated gets cost one
  parse per process instead of one per call. Writes still go to the host
  unchanged. Safe because this process is the only writer — Aegis's panel
  talks over addon messages and never touches addon storage; if that changes,
  the cache has to go.
2026-10-03 10:56:59 +02:00
Local Dev
57177a3439 fix(aegis): refresh was a silent no-op, and reported success either way
Three separate reasons the Refresh button looked dead:

- wallet.js refresh() returned immediately when state.scanning was set, so a
  manual press during a background poll did nothing at all. A forced refresh
  now awaits the in-flight pass and then does real work; background polls
  still yield. scanning is only ever written inside doRefresh, which only
  refresh() calls, so scanning implies a pending inflight to wait on.
- The adapters catch their own fetch failures onto state.error instead of
  rejecting, so awaiting refresh() proved nothing and refreshChain reported
  ok:true for a wallet that had just failed against a dead server. It now
  reads the snapshot back.
- Success changed only a tooltip. Balances that were already current left the
  screen identical, which is indistinguishable from a broken button. It now
  flashes a result and says how many wallets were refreshed, or how many
  failed and why.

Version bumped once for this batch; not published yet.
2026-10-02 20:34:01 +02:00
Local Dev
3ce36a0184 fix(aegis): 0.20.0 — the UTXO scan was 28k requests that always found nothing
Went looking for a performance fix and found a correctness bug underneath it.

getTx() slims each transaction on the way into the cache, and the slim shape
kept only { value, scriptHex } — dropping vout.tokenData. That field is where
the server reports CashTokens. It is NOT in scriptPubKey.hex, which Fulcrum
returns with the token prefix already stripped. So the classify pass fetched
one transaction per UTXO, looked for a prefix that was never there, and
concluded "no token" every single time.

Measured against a real chipnet wallet (130 UTXOs, 91 of them token-bearing):
the old pass cost 54 requests for that address and found 0 tokens; sampling
12 of those transactions, exactly 0 had a scriptPubKey starting with the
token prefix. On the faucet-fed address with 28,289 UTXOs it was ~28k
requests, still finding nothing — which is what made the wallet look hung.

So HD BCH wallets have never shown CashTokens. 0.15.0 fixed the imported
adapter, which reads token_data off listunspent, and I took the HD path's
silence for an empty wallet.

Now: when the server negotiated protocol >= 1.5, listunspent carries
token_data and a UTXO WITHOUT it is definitively not token-bearing, so the
whole set is classified from the one call we already make — 1 request instead
of 28,289. Below 1.5 the fallback fetches parents as before but reads
vout.tokenData (present even at 1.4) and only decodes a prefix as a last
resort, so it is correct now too.

Both routes are reconciled onto one shape. They speak different dialects:
the decoder yields a numeric capability (0/1/2) labelled
immutable/mutable/minting, while Electrum sends a string and calls 0 "none".
Left alone, an identical UTXO would have described itself differently
depending on which server answered. The decoder's vocabulary wins, and the
Certificates pane treats immutable as the quiet default so only capabilities
that change what the holder can do get a tag.

txCache is versioned and dropped once: entries written by the old shape carry
no token information, and an absent field cannot be told apart from "no
token", so a warm cache on a pre-1.5 server would have reported a token
wallet as empty.

Verified against the live wallet: one request, 91 token UTXOs, 46 categories,
17 assets, 51 certificates — matching what the panel reports — with
capability labels normalised (5 immutable, 28 mutable, 18 minting).
2026-09-29 00:56:06 +02:00
Local Dev
cb7ca53b01 chore(aegis): 0.8.2 — sectioned Settings tab
Settings grew tall enough that the user had to scroll past a dozen
cards to reach Prices, Sites or About. Split it into six named
sections (Security, Session, Wallet, Prices, Sites, About) with a
chip nav row at the top; only one section is visible at a time and
the choice persists across restarts.

Adds an About card that names the wallet, the aegis.x front-door
site and the silentmode.st umbrella, so support triage has a
one-click way to reach either from within the panel.

Includes the accumulated 0.7.x-0.8.1 wallet work that was already
shipping on OTA (CashTokens/BCMR, imported-wallet spend, siascan
integration, consolidate, currency picker, footer update chip).
2026-09-22 20:37:28 +02:00
Local Dev
2e54bf5e5a Ship Theseus 0.3.28 5d15508b (Aegis update card + DevTools in tab sidebar + real favicons)
Setup    5d15508bba929f1f074c052ac933863eadf6eb8e56984ebd5a1af75e80626643
Portable a5d346b97f5a13d85fa3bd301a72075ddb82fe636d7b1a51840ffd5a16d879f4

Bundled since 0.3.27:

32d4b75 - Aegis (bchwallet) gains its own update card in Settings >
General beside Ariadne. Check for updates hits the same signed OTA
endpoint the boot timer uses; Restart to apply appears when a signed
newer version is staged. Uses the existing addons-check-updates + a
new app-restart IPC. New Aegis versions ship without a Theseus release.

32d4b75 (same commit) - DevTools (F12 / Ctrl+Shift+I) opens docked to
the right of the tab (mode: 'right') instead of a detached window.
Matches stock Chrome. Users who prefer detached can drag out via the
DevTools own toolbar.

b71c925 - Search-engine favicons in Settings > Search now use Google's
/s2/favicons service — DuckDuckGo's ip3 source returned 404 for enough
hosts (Brave, Bing, Yandex, etc.) that half the list was falling
through to the emoji placeholder.

Deployed. Verified LIVE 0.3.28.
2026-09-08 18:17:25 +02:00
Renamed from bundled-addons/bchwallet/lib/wallet.js (Browse further)