Aegis: a BCH payment re-reads permissions after waiting for the PIN
signAndSend took a permissions snapshot, waited up to two minutes for the PIN, then wrote the snapshot back. Revoking the site meanwhile still let the allowance payment go out and restored the revoked allowance, and any other permission change made during the wait was lost. After the wait it now re-reads permissions, refuses an allowance payment whose allowance was revoked, replaced or no longer covers it, and changes only this origin's sendTx.
This commit is contained in:
parent
4511a933f4
commit
7447d57e96
1 changed files with 21 additions and 10 deletions
|
|
@ -3351,15 +3351,31 @@ function registerPageMessages(api) {
|
|||
const perms = permissions(api);
|
||||
const budget = perms[origin] && perms[origin].sendTx;
|
||||
const remaining = budget ? Math.max(0, (budget.capSats | 0) - (budget.usedSats | 0)) : 0;
|
||||
// The PIN wait below can last two minutes, during which the user may
|
||||
// revoke the site or other permissions may change. Everything after it
|
||||
// re-reads permissions and changes only this origin's sendTx; writing
|
||||
// back the snapshot taken here used to restore a revoked allowance and
|
||||
// wipe any other change made meanwhile.
|
||||
const patchSendTx = (fn) => {
|
||||
const now = permissions(api);
|
||||
const cur = { ...(now[origin] || {}) };
|
||||
const next = fn(cur.sendTx);
|
||||
if (!next && !now[origin]) return; // revoked meanwhile: nothing to change
|
||||
if (next) cur.sendTx = next; else delete cur.sendTx;
|
||||
now[origin] = cur;
|
||||
api.storage.set("permissions", now);
|
||||
};
|
||||
if (budget && d.total <= remaining) {
|
||||
// An allowance skips the overlay, not the PIN the user asked for on
|
||||
// every transaction.
|
||||
await requireDappTxPin(origin, "Bitcoin Cash payment");
|
||||
const fresh = (permissions(api)[origin] || {}).sendTx;
|
||||
const left = fresh ? Math.max(0, (fresh.capSats | 0) - (fresh.usedSats | 0)) : 0;
|
||||
if (!fresh || fresh.grantedAt !== budget.grantedAt || d.total > left) throw new Error("this site's allowance changed while waiting for the PIN; ask again");
|
||||
const r = await rt.adapter.signAndBroadcast(plan);
|
||||
budget.usedSats = (budget.usedSats | 0) + d.total;
|
||||
api.storage.set("permissions", perms);
|
||||
patchSendTx((t) => (t ? { ...t, usedSats: (t.usedSats | 0) + d.total } : t));
|
||||
emitState();
|
||||
api.log(`silent send ${d.total} sat for ${origin}, ${remaining - d.total} sat of allowance left`);
|
||||
api.log(`silent send ${d.total} sat for ${origin}, ${left - d.total} sat of allowance left`);
|
||||
return { txid: r.txid };
|
||||
}
|
||||
const rows = d.recipients.map((r, i) => ({ label: d.recipients.length > 1 ? `To #${i + 1}` : "To", value: r.to, mono: true }));
|
||||
|
|
@ -3384,13 +3400,8 @@ function registerPageMessages(api) {
|
|||
await requireDappTxPin(origin, "Bitcoin Cash payment");
|
||||
const cap = flags.find((f) => f.startsWith("cap="));
|
||||
const capSats = cap ? Number(cap.slice(4)) : 0;
|
||||
if (BCH_ALLOWANCES.includes(capSats)) {
|
||||
perms[origin] = { ...(perms[origin] || {}), sendTx: { capSats, usedSats: 0, grantedAt: Date.now() } };
|
||||
api.storage.set("permissions", perms);
|
||||
} else if (budget) {
|
||||
delete perms[origin].sendTx;
|
||||
api.storage.set("permissions", perms);
|
||||
}
|
||||
if (BCH_ALLOWANCES.includes(capSats)) patchSendTx(() => ({ capSats, usedSats: 0, grantedAt: Date.now() }));
|
||||
else if (budget) patchSendTx(() => null);
|
||||
emitState();
|
||||
const r = await rt.adapter.signAndBroadcast(plan);
|
||||
return { txid: r.txid };
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue