From 8e5c74ecaeb093f223bd549a5b7db6264202f125 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Mon, 27 Nov 2023 18:08:32 -0600 Subject: [PATCH] fix(smartWallet): handle upgrade disconnects from purse notifiers observeNotifier() would handle upgrade disconnects if the notifiers were durable. But in @agoric/ertp, they're ephemeral, so we open-code the loop. --- packages/smart-wallet/src/smartWallet.js | 53 +++++++++++++++--------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/packages/smart-wallet/src/smartWallet.js b/packages/smart-wallet/src/smartWallet.js index c7f57f969f0e..d67e62ed47b7 100644 --- a/packages/smart-wallet/src/smartWallet.js +++ b/packages/smart-wallet/src/smartWallet.js @@ -27,6 +27,8 @@ import { prepareRecorderKit, } from '@agoric/zoe/src/contractSupport/index.js'; import { E } from '@endo/far'; +import { isUpgradeDisconnection } from '@agoric/internal/src/upgrade-api.js'; + import { makeInvitationsHelper } from './invitations.js'; import { makeOfferExecutor } from './offers.js'; import { shape } from './typeGuards.js'; @@ -418,29 +420,40 @@ export const prepareSmartWallet = (baggage, shared) => { /** @type {(purse: ERef) => Promise} */ async watchPurse(purseRef) { const { address } = this.state; + const { helper } = this.facets; const purse = await purseRef; // promises don't fit in durable storage - const { helper } = this.facets; - // publish purse's balance and changes - void E.when( - E(purse).getCurrentAmount(), - balance => helper.updateBalance(purse, balance), - err => - console.error( - address, - 'initial purse balance publish failed', - err, - ), - ); - void observeNotifier(E(purse).getCurrentAmountNotifier(), { - updateState(balance) { - helper.updateBalance(purse, balance); - }, - fail(reason) { - console.error(address, `failed updateState observer`, reason); - }, - }); + // This would seem to fit the observeNotifier() pattern, + // but purse notifiers are not (always) durable. + // outer loop: for each upgrade disconnection... + for (;;) { + const notifier = E(purse).getCurrentAmountNotifier(); + let count; + // for each balance update + for (;;) { + try { + const { value: balance, updateCount } = + // eslint-disable-next-line no-await-in-loop + await E(notifier).getUpdateSince(count); + count = updateCount; + helper.updateBalance(purse, balance); + // final update + if (updateCount === undefined) { + return; + } + } catch (err) { + if (isUpgradeDisconnection(err)) { + break; + } + console.error( + `*** ${address} failed amount observer, ${err} ***`, + ); + // TODO: think about API change. + throw err; + } + } + } }, /**