diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ark/store.js | 20 | ||||
| -rw-r--r-- | src/features/ark.js | 23 |
2 files changed, 41 insertions, 2 deletions
diff --git a/src/ark/store.js b/src/ark/store.js index b1caa94..24d8f55 100644 --- a/src/ark/store.js +++ b/src/ark/store.js @@ -139,6 +139,7 @@ export class ArkStore { } } this.ready = true; + this._parsed = null; // peeks now read the opened store if (migrated.length) { this.flush().then(() => { for (const k of migrated) { try { ls.removeItem(k); } catch {} } }).catch(() => {}); } @@ -153,13 +154,32 @@ export class ArkStore { const v = this.mem.get(key); return v ? clone(v) : null; } + // The stored object itself, not a copy — for readers that only look (the + // render path). get() deep-copies, and a big wallet's state is ~1 MB: on a + // Pixel 9 each copy took ~220 ms and the first render asked five to seven + // times before the manager connected, 1.4 s of every launch. Never mutate + // what this returns. Before the store opens (or on localStorage) the parse + // is kept until the key is next written. + peek(key) { + if (!this.ready || this.lsMode) { + const hit = this._parsed && this._parsed.get(key); + if (hit !== undefined) return hit; + let v = null; + try { const raw = this.ls().getItem(key); v = raw ? JSON.parse(raw) : null; } catch { v = null; } + (this._parsed ||= new Map()).set(key, v); + return v; + } + return this.mem.get(key) || null; + } set(key, obj) { + if (this._parsed) this._parsed.delete(key); if (!this.ready || this.lsMode) { try { this.ls().setItem(key, JSON.stringify(obj)); } catch {} return; } this.mem.set(key, obj); this.dirty.add(key); this._schedule(); } remove(key) { + if (this._parsed) this._parsed.delete(key); if (!this.ready || this.lsMode) { try { this.ls().removeItem(key); } catch {} return; } this.mem.delete(key); this.dirty.add(key); diff --git a/src/features/ark.js b/src/features/ark.js index 5d3b293..f5ba9d8 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -77,11 +77,15 @@ if (typeof document !== 'undefined') { document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') flushAll(); }); } +// A read-only look at the stored state (stub wallets in tests have only loadArkState). +const peekArk = (wallet, url) => (wallet.peekArkState ? wallet.peekArkState(url) : wallet.loadArkState(url)); + // Wallet storage/key helpers for this feature, installed onto the core // wallet instance so a build without the feature ships none of it. export function installArkWallet(wallet) { if (wallet.loadArkState) return; // already installed const store = arkStore(); // IndexedDB-backed, memory-fronted; opens now + const peekOwnerOk = new WeakSet(); // stored states whose owner check passed Object.assign(wallet, { _arkStore: store, // ---- Ark support ------------------------------------------------------- @@ -133,6 +137,21 @@ export function installArkWallet(wallet) { return st; } catch { return null; } }, + // loadArkState for readers that only look (see ArkStore.peek): the stored + // object, no copy — never mutate it. Misfiled state goes through + // loadArkState's repair; each object's owner is decoded once. + peekArkState(url) { + try { + const st = store.peek(this._arkKey(url)); + if (!st) return null; + if (!peekOwnerOk.has(st)) { + const owner = this._arkVtxoOwner(st); + if (owner && st.serverPubkey && owner !== st.serverPubkey) return this.loadArkState(url); + peekOwnerOk.add(st); + } + return st; + } catch { return null; } + }, // Offer up pre-namespacing state, but only to the ASP that actually // cosigned it. Its owner is read from the state's own stamp, or failing // that decoded straight out of a vtxo — the vtxo wire format carries the @@ -554,7 +573,7 @@ export function arkFeature(ctx) { function arkWanted() { const cfg = getArkConfig(); - const st = cfg && wallet.loadArkState(cfg.ark); + const st = cfg && peekArk(wallet, cfg.ark); return !!(st && ((st.vtxos || []).length || (st.movements || []).length || (st.actions || []).some((a) => !['done', 'failed'].includes(a.step)))); } @@ -797,7 +816,7 @@ export function arkFeature(ctx) { // (send/exit) still requires the seed and is gated separately. const cfg = getArkConfig(); if (!cfg) return null; - return wallet.loadArkState(cfg.ark); + return peekArk(wallet, cfg.ark); } // Which receives actually arrived over Lightning? A payment to the user's |
