aboutsummaryrefslogtreecommitdiffstats
path: root/src/nwc-bg.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/nwc-bg.js')
-rw-r--r--src/nwc-bg.js46
1 files changed, 42 insertions, 4 deletions
diff --git a/src/nwc-bg.js b/src/nwc-bg.js
index 92786c0..2c73206 100644
--- a/src/nwc-bg.js
+++ b/src/nwc-bg.js
@@ -57,6 +57,30 @@ export async function saveBg(walletKey, rec) {
export async function clearBg(walletKey) {
return tx('readwrite', (s) => { s.delete(walletKey); });
}
+
+// The worker's diary: what it did while the app was closed — each push, the
+// steps of a payment, every notification shown or refused. A phone stops a
+// worker without a trace, and the diary's last line says where it got to.
+// Settings → Notifications → Copy hands it to whoever is looking into it.
+// Appended line by line (a worker that dies mid-push keeps what it wrote),
+// one writer at a time, a few hundred lines kept.
+const LOG_KEY = '__log';
+const LOG_MAX = 400;
+let logChain = Promise.resolve();
+export function appendBgLog(line) {
+ logChain = logChain.then(() => tx('readwrite', async (s) => {
+ const cur = (await get(s, LOG_KEY)) || [];
+ cur.push(line);
+ s.put(cur.length > LOG_MAX ? cur.slice(-LOG_MAX) : cur, LOG_KEY);
+ })).catch(() => {});
+ return logChain;
+}
+export async function readBgLog() {
+ try { return (await tx('readonly', (s) => get(s, LOG_KEY))) || []; } catch { return []; }
+}
+export async function clearBgLog() {
+ return tx('readwrite', (s) => { s.delete(LOG_KEY); });
+}
// The service worker doesn't know wallet cache keys — it finds the right
// record by the connection's service pubkey across all of them.
export async function allBgs() {
@@ -67,7 +91,8 @@ export async function allBgs() {
cur.onsuccess = () => {
const c = cur.result;
if (!c) return res(out);
- out.push({ walletKey: c.key, rec: c.value });
+ // the worker's diary shares the store under a reserved key
+ if (!(typeof c.key === 'string' && c.key.startsWith('__'))) out.push({ walletKey: c.key, rec: c.value });
c.continue();
};
cur.onerror = () => rej(cur.error);
@@ -84,10 +109,15 @@ export const BG_KEY_LOOKAHEAD = 25;
// module). Chain 3 covers coins + change, chain 4/0 the mailbox, chain 5 a
// window of receive preimages — everything the worker needs to pay AND to
// mint invoices while the app is closed.
-export function buildBg({ ark, mgrState, keyFor, connections, offer, autowithdraw }) {
+export function buildBg({ ark, mgrState, keyFor, connections, offer, autowithdraw, custody }) {
const keys = {};
const idx = new Set((mgrState.vtxos || []).filter((v) => v.state !== 'spent').map((v) => v.keyIndex));
for (let i = 0; i < BG_KEY_LOOKAHEAD; i++) idx.add((mgrState.nextKeyIndex || 1) + i);
+ // A standing renewal order (and a renewal in flight) burned its output key
+ // when it was registered, below the counter by now: the worker needs that
+ // key to claim the renewed coin while the app is closed.
+ for (const e of mgrState.scheduled || []) if (e.outKeyIndex != null) idx.add(e.outKeyIndex);
+ for (const a of mgrState.actions || []) if (a.type === 'refresh' && a.step !== 'done' && a.outKeyIndex != null) idx.add(a.outKeyIndex);
idx.add(0); // the receive-address key: mailbox/refund defaults land on it
for (const i of idx) keys[String(i)] = keyFor(3, i);
const keys5 = {};
@@ -107,9 +137,17 @@ export function buildBg({ ark, mgrState, keyFor, connections, offer, autowithdra
// { on, threshold, keep, target: { kind, address, url?, spkHex? } } — the
// destination is resolved by the app, so the worker never needs a relay
autowithdraw: autowithdraw || null,
+ // the custody tier ('custodial' | 'self' | 'early'): whether the worker
+ // registers renewal orders for bare coins, and how far ahead
+ custody: custody || null,
+ // The budget's spent counter rides along (stamped with its window) so the
+ // worker counts what the app spent; a revoked one rides along flagged, to
+ // be refused rather than ignored.
connections: (connections || []).map((c) => ({
- id: c.id, servicePk: c.servicePk, serviceSk: c.serviceSk, clientPk: c.clientPk,
- maxSat: c.maxSat, dailySat: c.dailySat,
+ id: c.id, name: c.name || '', servicePk: c.servicePk, serviceSk: c.serviceSk, clientPk: c.clientPk,
+ maxSat: c.maxSat, dailySat: c.dailySat, period: c.period || 'day',
+ spentDate: c.spentDate || null, spentToday: c.spentToday || 0,
+ revoked: !!c.revoked,
})),
};
}