gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: schedule exchange updating


From: gnunet
Subject: [taler-wallet-core] branch master updated: schedule exchange updating
Date: Wed, 02 Sep 2020 11:14:48 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 8a3ac7f0 schedule exchange updating
8a3ac7f0 is described below

commit 8a3ac7f08b114360118bf58a38983401107a62cf
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Wed Sep 2 14:44:36 2020 +0530

    schedule exchange updating
---
 packages/taler-wallet-core/src/db.ts               |  2 +-
 .../taler-wallet-core/src/operations/exchanges.ts  | 66 +++++++++++++---------
 .../taler-wallet-core/src/operations/pending.ts    | 28 +++++++--
 packages/taler-wallet-core/src/types/dbTypes.ts    |  6 ++
 packages/taler-wallet-core/src/util/http.ts        | 19 +++++--
 packages/taler-wallet-core/src/util/time.ts        | 32 +++++++++++
 6 files changed, 113 insertions(+), 40 deletions(-)

diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index a55d9bb1..d5ebdb6c 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -8,7 +8,7 @@ import { IDBFactory, IDBDatabase } from "idb-bridge";
  * with each major change.  When incrementing the major version,
  * the wallet should import data from the previous version.
  */
-const TALER_DB_NAME = "taler-walletdb-v8";
+const TALER_DB_NAME = "taler-walletdb-v9";
 
 /**
  * Current database minor version, should be incremented
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 618b1cf4..d162ca3b 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -30,6 +30,8 @@ import {
   WireFee,
   ExchangeUpdateReason,
   ExchangeUpdatedEventRecord,
+  initRetryInfo,
+  updateRetryInfoTimeout,
 } from "../types/dbTypes";
 import { canonicalizeBaseUrl } from "../util/helpers";
 import * as Amounts from "../util/amounts";
@@ -43,7 +45,12 @@ import {
   WALLET_CACHE_BREAKER_CLIENT_VERSION,
   WALLET_EXCHANGE_PROTOCOL_VERSION,
 } from "./versions";
-import { getTimestampNow, Duration, isTimestampExpired } from "../util/time";
+import {
+  getTimestampNow,
+  Duration,
+  isTimestampExpired,
+  durationFromSpec,
+} from "../util/time";
 import { compare } from "../util/libtoolVersion";
 import { createRecoupGroup, processRecoupGroup } from "./recoup";
 import { TalerErrorCode } from "../TalerErrorCode";
@@ -56,6 +63,7 @@ import { Logger } from "../util/logging";
 import { URL } from "../util/url";
 import { reconcileReserveHistory } from "../util/reserveHistoryUtil";
 import { checkDbInvariant } from "../util/invariants";
+import { NotificationType } from "../types/notifications";
 
 const logger = new Logger("exchanges.ts");
 
@@ -86,17 +94,23 @@ async function denominationRecordFromKeys(
   return d;
 }
 
-async function setExchangeError(
+async function handleExchangeUpdateError(
   ws: InternalWalletState,
   baseUrl: string,
   err: TalerErrorDetails,
 ): Promise<void> {
-  logger.warn(`last error for exchange ${baseUrl}:`, err);
-  const mut = (exchange: ExchangeRecord): ExchangeRecord => {
+  await ws.db.runWithWriteTransaction([Stores.exchanges], async (tx) => {
+    const exchange = await tx.get(Stores.exchanges, baseUrl);
+    if (!exchange) {
+      return;
+    }
+    exchange.retryInfo.retryCounter++;
+    updateRetryInfoTimeout(exchange.retryInfo);
     exchange.lastError = err;
-    return exchange;
-  };
-  await ws.db.mutate(Stores.exchanges, baseUrl, mut);
+  });
+  if (err) {
+    ws.notify({ type: NotificationType.ExchangeOperationError, error: err });
+  }
 }
 
 function getExchangeRequestTimeout(e: ExchangeRecord): Duration {
@@ -142,7 +156,7 @@ async function updateExchangeWithKeys(
         exchangeBaseUrl: baseUrl,
       },
     );
-    await setExchangeError(ws, baseUrl, opErr);
+    await handleExchangeUpdateError(ws, baseUrl, opErr);
     throw new OperationFailedAndReportedError(opErr);
   }
 
@@ -158,7 +172,7 @@ async function updateExchangeWithKeys(
         walletProtocolVersion: WALLET_EXCHANGE_PROTOCOL_VERSION,
       },
     );
-    await setExchangeError(ws, baseUrl, opErr);
+    await handleExchangeUpdateError(ws, baseUrl, opErr);
     throw new OperationFailedAndReportedError(opErr);
   }
 
@@ -198,10 +212,13 @@ async function updateExchangeWithKeys(
         masterPublicKey: exchangeKeysJson.master_public_key,
         protocolVersion: protocolVersion,
         signingKeys: exchangeKeysJson.signkeys,
-        nextUpdateTime: getExpiryTimestamp(resp),
+        nextUpdateTime: getExpiryTimestamp(resp, {
+          minDuration: durationFromSpec({ hours: 1 }),
+        }),
       };
       r.updateStatus = ExchangeUpdateStatus.FetchWire;
       r.lastError = undefined;
+      r.retryInfo = initRetryInfo(false);
       await tx.put(Stores.exchanges, r);
 
       for (const newDenom of newDenominations) {
@@ -433,6 +450,7 @@ async function updateExchangeWithWireInfo(
     };
     r.updateStatus = ExchangeUpdateStatus.FetchTerms;
     r.lastError = undefined;
+    r.retryInfo = initRetryInfo(false);
     await tx.put(Stores.exchanges, r);
   });
 }
@@ -443,7 +461,7 @@ export async function updateExchangeFromUrl(
   forceNow = false,
 ): Promise<ExchangeRecord> {
   const onOpErr = (e: TalerErrorDetails): Promise<void> =>
-    setExchangeError(ws, baseUrl, e);
+    handleExchangeUpdateError(ws, baseUrl, e);
   return await guardOperationException(
     () => updateExchangeFromUrlImpl(ws, baseUrl, forceNow),
     onOpErr,
@@ -460,6 +478,7 @@ async function updateExchangeFromUrlImpl(
   baseUrl: string,
   forceNow = false,
 ): Promise<ExchangeRecord> {
+  logger.trace(`updating exchange info for ${baseUrl}`);
   const now = getTimestampNow();
   baseUrl = canonicalizeBaseUrl(baseUrl);
 
@@ -480,6 +499,7 @@ async function updateExchangeFromUrlImpl(
       termsOfServiceAcceptedTimestamp: undefined,
       termsOfServiceLastEtag: undefined,
       termsOfServiceText: undefined,
+      retryInfo: initRetryInfo(false),
     };
     await ws.db.put(Stores.exchanges, newExchangeRecord);
   } else {
@@ -488,8 +508,11 @@ async function updateExchangeFromUrlImpl(
       if (!rec) {
         return;
       }
-      if (rec.updateStatus != ExchangeUpdateStatus.FetchKeys && !forceNow) {
-        return;
+      if (rec.updateStatus != ExchangeUpdateStatus.FetchKeys) {
+        const t = rec.details?.nextUpdateTime;
+        if (!forceNow && t && !isTimestampExpired(t)) {
+          return;
+        }
       }
       if (rec.updateStatus != ExchangeUpdateStatus.FetchKeys && forceNow) {
         rec.updateReason = ExchangeUpdateReason.Forced;
@@ -497,31 +520,18 @@ async function updateExchangeFromUrlImpl(
       rec.updateStarted = now;
       rec.updateStatus = ExchangeUpdateStatus.FetchKeys;
       rec.lastError = undefined;
+      rec.retryInfo = initRetryInfo(false);
       t.put(Stores.exchanges, rec);
     });
   }
 
-  r = await ws.db.get(Stores.exchanges, baseUrl);
-  checkDbInvariant(!!r);
-
-
-  const t = r.details?.nextUpdateTime;
-  if (!forceNow && t && !isTimestampExpired(t)) {
-    logger.trace("using cached exchange info");
-    return r;
-  }
-
   await updateExchangeWithKeys(ws, baseUrl);
   await updateExchangeWithWireInfo(ws, baseUrl);
   await updateExchangeWithTermsOfService(ws, baseUrl);
   await updateExchangeFinalize(ws, baseUrl);
 
   const updatedExchange = await ws.db.get(Stores.exchanges, baseUrl);
-
-  if (!updatedExchange) {
-    // This should practically never happen
-    throw Error("exchange not found");
-  }
+  checkDbInvariant(!!updatedExchange);
   return updatedExchange;
 }
 
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index 88196162..8cbc5e56 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -56,10 +56,6 @@ async function gatherExchangePending(
   resp: PendingOperationsResponse,
   onlyDue = false,
 ): Promise<void> {
-  if (onlyDue) {
-    // FIXME: exchanges should also be updated regularly
-    return;
-  }
   await tx.iter(Stores.exchanges).forEach((e) => {
     switch (e.updateStatus) {
       case ExchangeUpdateStatus.Finished:
@@ -79,7 +75,7 @@ async function gatherExchangePending(
             type: PendingOperationType.Bug,
             givesLifeness: false,
             message:
-              "Exchange record does not have details, but no update in 
progress.",
+              "Exchange record does not have details, but no update finished.",
             details: {
               exchangeBaseUrl: e.baseUrl,
             },
@@ -90,14 +86,28 @@ async function gatherExchangePending(
             type: PendingOperationType.Bug,
             givesLifeness: false,
             message:
-              "Exchange record does not have wire info, but no update in 
progress.",
+              "Exchange record does not have wire info, but no update 
finished.",
             details: {
               exchangeBaseUrl: e.baseUrl,
             },
           });
         }
+        if (e.details && e.details.nextUpdateTime.t_ms < now.t_ms) {
+          resp.pendingOperations.push({
+            type: PendingOperationType.ExchangeUpdate,
+            givesLifeness: false,
+            stage: ExchangeUpdateOperationStage.FetchKeys,
+            exchangeBaseUrl: e.baseUrl,
+            lastError: e.lastError,
+            reason: "scheduled",
+          });
+          break;
+        }
         break;
       case ExchangeUpdateStatus.FetchKeys:
+        if (onlyDue && e.retryInfo.nextRetry.t_ms > now.t_ms) {
+          return;
+        }
         resp.pendingOperations.push({
           type: PendingOperationType.ExchangeUpdate,
           givesLifeness: false,
@@ -108,6 +118,9 @@ async function gatherExchangePending(
         });
         break;
       case ExchangeUpdateStatus.FetchWire:
+        if (onlyDue && e.retryInfo.nextRetry.t_ms > now.t_ms) {
+          return;
+        }
         resp.pendingOperations.push({
           type: PendingOperationType.ExchangeUpdate,
           givesLifeness: false,
@@ -118,6 +131,9 @@ async function gatherExchangePending(
         });
         break;
       case ExchangeUpdateStatus.FinalizeUpdate:
+        if (onlyDue && e.retryInfo.nextRetry.t_ms > now.t_ms) {
+          return;
+        }
         resp.pendingOperations.push({
           type: PendingOperationType.ExchangeUpdate,
           givesLifeness: false,
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts 
b/packages/taler-wallet-core/src/types/dbTypes.ts
index b74a9ce3..801bb449 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -113,6 +113,7 @@ export function updateRetryInfoTimeout(
     r.nextRetry = { t_ms: "never" };
     return;
   }
+  r.active = true;
   const t =
     now.t_ms + p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
   r.nextRetry = { t_ms: t };
@@ -642,6 +643,11 @@ export interface ExchangeRecord {
   updateReason?: ExchangeUpdateReason;
 
   lastError?: TalerErrorDetails;
+
+  /**
+   * Retry status for fetching updated information about the exchange.
+   */
+  retryInfo: RetryInfo;
 }
 
 
diff --git a/packages/taler-wallet-core/src/util/http.ts 
b/packages/taler-wallet-core/src/util/http.ts
index 0977b429..e050efe6 100644
--- a/packages/taler-wallet-core/src/util/http.ts
+++ b/packages/taler-wallet-core/src/util/http.ts
@@ -26,7 +26,7 @@ import { Codec } from "./codec";
 import { OperationFailedError, makeErrorDetails } from "../operations/errors";
 import { TalerErrorCode } from "../TalerErrorCode";
 import { Logger } from "./logging";
-import { Duration, Timestamp, getTimestampNow } from "./time";
+import { Duration, Timestamp, getTimestampNow, timestampAddDuration, 
timestampMin, timestampMax } from "./time";
 
 const logger = new Logger("http.ts");
 
@@ -257,15 +257,24 @@ export async function readSuccessResponseTextOrThrow<T>(
 /**
  * Get the timestamp at which the response's content is considered expired.
  */
-export function getExpiryTimestamp(httpResponse: HttpResponse): Timestamp {
+export function getExpiryTimestamp(
+  httpResponse: HttpResponse,
+  opt: { minDuration?: Duration },
+): Timestamp {
   const expiryDateMs = new Date(
     httpResponse.headers.get("expiry") ?? "",
   ).getTime();
+  let t: Timestamp;
   if (Number.isNaN(expiryDateMs)) {
-    return getTimestampNow();
+    t = getTimestampNow();
   } else {
-    return {
+    t = {
       t_ms: expiryDateMs,
-    }
+    };
+  }
+  if (opt.minDuration) {
+    const t2 = timestampAddDuration(getTimestampNow(), opt.minDuration);
+    return timestampMax(t, t2);
   }
+  return t;
 }
diff --git a/packages/taler-wallet-core/src/util/time.ts 
b/packages/taler-wallet-core/src/util/time.ts
index ff4c1885..1641924a 100644
--- a/packages/taler-wallet-core/src/util/time.ts
+++ b/packages/taler-wallet-core/src/util/time.ts
@@ -76,6 +76,38 @@ export function timestampMin(t1: Timestamp, t2: Timestamp): 
Timestamp {
   return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
 }
 
+export function timestampMax(t1: Timestamp, t2: Timestamp): Timestamp {
+  if (t1.t_ms === "never") {
+    return { t_ms: "never" };
+  }
+  if (t2.t_ms === "never") {
+    return { t_ms: "never" };
+  }
+  return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
+}
+
+const SECONDS = 1000
+const MINUTES = SECONDS * 60;
+const HOURS = MINUTES * 60;
+
+export function durationFromSpec(spec: {
+  seconds?: number,
+  hours?: number,
+  minutes?: number,
+}): Duration {
+  let d_ms = 0;
+  if (spec.seconds) {
+    d_ms += spec.seconds * SECONDS;
+  }
+  if (spec.minutes) {
+    d_ms += spec.minutes * MINUTES;
+  }
+  if (spec.hours) {
+    d_ms += spec.hours * HOURS;
+  }
+  return { d_ms };
+}
+
 /**
  * Truncate a timestamp so that that it represents a multiple
  * of seconds.  The timestamp is always rounded down.

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]