summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/common.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-core/src/common.ts')
-rw-r--r--packages/taler-wallet-core/src/common.ts102
1 files changed, 84 insertions, 18 deletions
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
index eb06b8eb0..755c46188 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -21,6 +21,7 @@ import {
AbsoluteTime,
AmountJson,
Amounts,
+ AsyncFlag,
CoinRefreshRequest,
CoinStatus,
Duration,
@@ -35,6 +36,7 @@ import {
TalerProtocolTimestamp,
TombstoneIdStr,
TransactionIdStr,
+ WalletNotification,
assertUnreachable,
checkDbInvariant,
checkLogicInvariant,
@@ -145,7 +147,13 @@ export async function makeCoinAvailable(
export async function spendCoins(
wex: WalletExecutionContext,
tx: WalletDbReadWriteTransaction<
- ["coins", "coinAvailability", "refreshGroups", "denominations"]
+ [
+ "coins",
+ "coinAvailability",
+ "refreshGroups",
+ "refreshSessions",
+ "denominations",
+ ]
>,
csi: CoinsSpendInfo,
): Promise<void> {
@@ -275,6 +283,8 @@ export function getExchangeUpdateStatusFromRecord(
return ExchangeUpdateStatus.ReadyUpdate;
case ExchangeEntryDbUpdateStatus.Suspended:
return ExchangeUpdateStatus.Suspended;
+ default:
+ assertUnreachable(r.updateStatus);
}
}
@@ -288,6 +298,8 @@ export function getExchangeEntryStatusFromRecord(
return ExchangeEntryStatus.Preset;
case ExchangeEntryDbRecordStatus.Used:
return ExchangeEntryStatus.Used;
+ default:
+ assertUnreachable(r.entryStatus);
}
}
@@ -480,25 +492,28 @@ function updateTimeout(
r.nextRetry = timestampPreciseToDb(TalerPreciseTimestamp.fromMilliseconds(t));
}
-export namespace DbRetryInfo {
- export function getDuration(
- r: DbRetryInfo | undefined,
- p: RetryPolicy = defaultRetryPolicy,
- ): Duration {
- if (!r) {
- // If we don't have any retry info, run immediately.
- return { d_ms: 0 };
- }
- if (p.backoffDelta.d_ms === "forever") {
- return { d_ms: "forever" };
- }
- const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
- return {
- d_ms:
- p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t),
- };
+export function computeDbBackoff(retryCounter: number): DbPreciseTimestamp {
+ const now = AbsoluteTime.now();
+ if (now.t_ms === "never") {
+ throw Error("assertion failed");
+ }
+ const p = defaultRetryPolicy;
+ if (p.backoffDelta.d_ms === "forever") {
+ throw Error("assertion failed");
}
+ const nextIncrement =
+ p.backoffDelta.d_ms * Math.pow(p.backoffBase, retryCounter);
+
+ const t =
+ now.t_ms +
+ (p.maxTimeout.d_ms === "forever"
+ ? nextIncrement
+ : Math.min(p.maxTimeout.d_ms, nextIncrement));
+ return timestampPreciseToDb(TalerPreciseTimestamp.fromMilliseconds(t));
+}
+
+export namespace DbRetryInfo {
export function reset(p: RetryPolicy = defaultRetryPolicy): DbRetryInfo {
const now = TalerPreciseTimestamp.now();
const info: DbRetryInfo = {
@@ -756,3 +771,54 @@ export enum PendingTaskType {
declare const __taskIdStr: unique symbol;
export type TaskIdStr = string & { [__taskIdStr]: true };
+
+/**
+ * Wait until the wallet is in a particular state.
+ *
+ * Two functions must be provided:
+ * 1. checkState, which checks if the wallet is in the
+ * desired state.
+ * 2. filterNotification, which checks whether a notification
+ * might have lead to a state change.
+ */
+export async function genericWaitForState(
+ wex: WalletExecutionContext,
+ args: {
+ checkState: () => Promise<boolean>;
+ filterNotification: (notif: WalletNotification) => boolean;
+ },
+): Promise<void> {
+ await wex.taskScheduler.ensureRunning();
+
+ // FIXME: Clean up using the new JS "using" / Symbol.dispose syntax.
+ const flag = new AsyncFlag();
+ // Raise purchaseNotifFlag whenever we get a notification
+ // about our refresh.
+ const cancelNotif = wex.ws.addNotificationListener((notif) => {
+ if (args.filterNotification(notif)) {
+ flag.raise();
+ }
+ });
+ const unregisterOnCancelled = wex.cancellationToken.onCancelled(() => {
+ cancelNotif();
+ flag.raise();
+ });
+
+ try {
+ while (true) {
+ if (wex.cancellationToken.isCancelled) {
+ throw Error("cancelled");
+ }
+ if (await args.checkState()) {
+ return;
+ }
+ // Wait for the next transition
+ await flag.wait();
+ flag.reset();
+ }
+ } catch (e) {
+ unregisterOnCancelled();
+ cancelNotif();
+ throw e;
+ }
+}