gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: don't store reserve history a


From: gnunet
Subject: [taler-wallet-core] branch master updated: don't store reserve history anymore, adjust withdrawal implementation accordingly
Date: Wed, 16 Dec 2020 17:59:10 +0100

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 bafb52ed don't store reserve history anymore, adjust withdrawal 
implementation accordingly
bafb52ed is described below

commit bafb52edff4d56bcb9e3c3d0a260f507c517b08c
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Dec 16 17:59:04 2020 +0100

    don't store reserve history anymore, adjust withdrawal implementation 
accordingly
---
 .../src/crypto/workers/cryptoImplementation.ts     |   2 +
 packages/taler-wallet-core/src/index.ts            |   4 +-
 .../taler-wallet-core/src/operations/backup.ts     |  41 ++-
 .../taler-wallet-core/src/operations/exchanges.ts  |   1 -
 packages/taler-wallet-core/src/operations/pay.ts   |   2 +
 .../taler-wallet-core/src/operations/pending.ts    |   3 +-
 .../taler-wallet-core/src/operations/refresh.ts    |   7 +-
 .../taler-wallet-core/src/operations/reserves.ts   | 367 +++++++--------------
 packages/taler-wallet-core/src/operations/state.ts |   2 +-
 packages/taler-wallet-core/src/operations/tip.ts   |  16 +-
 .../src/operations/transactions.ts                 |   2 +-
 .../src/operations/withdraw-test.ts                |   4 +-
 .../taler-wallet-core/src/operations/withdraw.ts   |  67 ++--
 .../taler-wallet-core/src/types/backupTypes.ts     |  82 -----
 .../taler-wallet-core/src/types/cryptoTypes.ts     |   6 +
 packages/taler-wallet-core/src/types/dbTypes.ts    | 114 ++-----
 .../taler-wallet-core/src/types/notifications.ts   |   6 -
 packages/taler-wallet-core/src/types/pending.ts    | 276 ----------------
 .../taler-wallet-core/src/types/transactions.ts    | 337 -------------------
 .../taler-wallet-core/src/types/walletTypes.ts     |   2 +-
 packages/taler-wallet-core/src/util/query.ts       |   2 +-
 .../src/util/reserveHistoryUtil-test.ts            | 285 ----------------
 .../src/util/reserveHistoryUtil.ts                 | 363 --------------------
 packages/taler-wallet-core/src/wallet.ts           |   6 +-
 24 files changed, 248 insertions(+), 1749 deletions(-)

diff --git 
a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index deaad42b..4f553c50 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -206,6 +206,7 @@ export class CryptoImplementation {
     const tipPlanchet: DerivedTipPlanchet = {
       blindingKey: encodeCrock(blindingFactor),
       coinEv: encodeCrock(ev),
+      coinEvHash: encodeCrock(hash(ev)),
       coinPriv: encodeCrock(fc.coinPriv),
       coinPub: encodeCrock(fc.coinPub),
     };
@@ -463,6 +464,7 @@ export class CryptoImplementation {
             coinEv: encodeCrock(ev),
             privateKey: encodeCrock(coinPriv),
             publicKey: encodeCrock(coinPub),
+            coinEvHash: encodeCrock(hash(ev)),
           };
           planchets.push(planchet);
 
diff --git a/packages/taler-wallet-core/src/index.ts 
b/packages/taler-wallet-core/src/index.ts
index a94155b1..3d52ed76 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -63,5 +63,5 @@ export * from "./util/time";
 export * from "./types/talerTypes";
 export * from "./types/walletTypes";
 export * from "./types/notifications";
-export * from "./types/transactions";
-export * from "./types/pending";
+export * from "./types/transactionsTypes";
+export * from "./types/pendingTypes";
diff --git a/packages/taler-wallet-core/src/operations/backup.ts 
b/packages/taler-wallet-core/src/operations/backup.ts
index 7ab908c4..1e5aa542 100644
--- a/packages/taler-wallet-core/src/operations/backup.ts
+++ b/packages/taler-wallet-core/src/operations/backup.ts
@@ -44,6 +44,7 @@ import {
   BackupRefundState,
   BackupReserve,
   BackupTip,
+  BackupWithdrawalGroup,
   WalletBackupContentV1,
 } from "../types/backupTypes";
 import { TransactionHandle } from "../util/query";
@@ -172,6 +173,7 @@ export async function exportBackup(
       Stores.tips,
       Stores.recoupGroups,
       Stores.reserves,
+      Stores.withdrawalGroups,
     ],
     async (tx) => {
       const bs = await getWalletBackupState(ws, tx);
@@ -188,9 +190,46 @@ export async function exportBackup(
       const backupBackupProviders: BackupBackupProvider[] = [];
       const backupTips: BackupTip[] = [];
       const backupRecoupGroups: BackupRecoupGroup[] = [];
+      const withdrawalGroupsByReserve: {
+        [reservePub: string]: BackupWithdrawalGroup[];
+      } = {};
+
+      await tx.iter(Stores.withdrawalGroups).forEachAsync(async (wg) => {
+        const withdrawalGroups = (withdrawalGroupsByReserve[
+          wg.reservePub
+        ] ??= []);
+        // FIXME: finish!
+        // withdrawalGroups.push({
+        //   raw_withdrawal_amount: Amounts.stringify(wg.rawWithdrawalAmount),
+        //   selected_denoms: wg.denomsSel.selectedDenoms.map((x) => ({
+        //     count: x.count,
+        //     denom_pub_hash: x.denomPubHash,
+        //   })),
+        //   timestamp_start: wg.timestampStart,
+        //   timestamp_finish: wg.timestampFinish,
+        //   withdrawal_group_id: wg.withdrawalGroupId,
+        // });
+      });
 
       await tx.iter(Stores.reserves).forEach((reserve) => {
-        // FIXME: implement
+        const backupReserve: BackupReserve = {
+          initial_selected_denoms: reserve.initialDenomSel.selectedDenoms.map(
+            (x) => ({
+              count: x.count,
+              denom_pub_hash: x.denomPubHash,
+            }),
+          ),
+          initial_withdrawal_group_id: reserve.initialWithdrawalGroupId,
+          instructed_amount: Amounts.stringify(reserve.instructedAmount),
+          reserve_priv: reserve.reservePriv,
+          timestamp_created: reserve.timestampCreated,
+          withdrawal_groups:
+            withdrawalGroupsByReserve[reserve.reservePub] ?? [],
+        };
+        const backupReserves = (backupReservesByExchange[
+          reserve.exchangeBaseUrl
+        ] ??= []);
+        backupReserves.push(backupReserve);
       });
 
       await tx.iter(Stores.tips).forEach((tip) => {
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index b6865ccc..3e71634c 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -58,7 +58,6 @@ import {
 } from "../util/http";
 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";
 import { updateRetryInfoTimeout, initRetryInfo } from "../util/retries";
diff --git a/packages/taler-wallet-core/src/operations/pay.ts 
b/packages/taler-wallet-core/src/operations/pay.ts
index 52f0c451..c374cfe4 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -460,6 +460,8 @@ async function recordConfirmPay(
     paymentSubmitPending: true,
     refunds: {},
     merchantPaySig: undefined,
+    noncePriv: proposal.noncePriv,
+    noncePub: proposal.noncePub,
   };
 
   await ws.db.runWithWriteTransaction(
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index a42d89c9..cc693a49 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -29,7 +29,7 @@ import {
   PendingOperationType,
   ExchangeUpdateOperationStage,
   ReserveType,
-} from "../types/pending";
+} from "../types/pendingTypes";
 import {
   Duration,
   getTimestampNow,
@@ -189,7 +189,6 @@ async function gatherReservePending(
         // nothing to report as pending
         break;
       case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-      case ReserveRecordStatus.WITHDRAWING:
       case ReserveRecordStatus.QUERYING_STATUS:
       case ReserveRecordStatus.REGISTERING_BANK:
         resp.nextRetryDelay = updateRetryDelay(
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 71cc78fa..2d80f0a5 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -29,7 +29,7 @@ import { amountToPretty } from "../util/helpers";
 import { TransactionHandle } from "../util/query";
 import { InternalWalletState, EXCHANGE_COINS_LOCK } from "./state";
 import { Logger } from "../util/logging";
-import { getWithdrawDenomList, isWithdrawableDenom } from "./withdraw";
+import { selectWithdrawalDenominations, isWithdrawableDenom } from 
"./withdraw";
 import { updateExchangeFromUrl } from "./exchanges";
 import {
   TalerErrorDetails,
@@ -83,7 +83,7 @@ export function getTotalRefreshCost(
 ): AmountJson {
   const withdrawAmount = Amounts.sub(amountLeft, refreshedDenom.feeRefresh)
     .amount;
-  const withdrawDenoms = getWithdrawDenomList(withdrawAmount, denoms);
+  const withdrawDenoms = selectWithdrawalDenominations(withdrawAmount, denoms);
   const resultingAmount = Amounts.add(
     Amounts.getZero(withdrawAmount.currency),
     ...withdrawDenoms.selectedDenoms.map(
@@ -150,7 +150,7 @@ async function refreshCreateSession(
     oldDenom.feeRefresh,
   ).amount;
 
-  const newCoinDenoms = getWithdrawDenomList(availableAmount, availableDenoms);
+  const newCoinDenoms = selectWithdrawalDenominations(availableAmount, 
availableDenoms);
 
   if (newCoinDenoms.selectedDenoms.length === 0) {
     logger.trace(
@@ -478,6 +478,7 @@ async function refreshReveal(
           oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
         },
         suspended: false,
+        coinEvHash: pc.coinEv,
       };
 
       coins.push(coin);
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts 
b/packages/taler-wallet-core/src/operations/reserves.ts
index a2a1b301..95c38120 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -28,8 +28,6 @@ import {
   CurrencyRecord,
   Stores,
   WithdrawalGroupRecord,
-  WalletReserveHistoryItemType,
-  ReserveHistoryRecord,
   ReserveBankInfo,
 } from "../types/dbTypes";
 import { Logger } from "../util/logging";
@@ -47,10 +45,12 @@ import { assertUnreachable } from 
"../util/assertUnreachable";
 import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
 import { randomBytes } from "../crypto/primitives/nacl-fast";
 import {
-  selectWithdrawalDenoms,
   processWithdrawGroup,
   getBankWithdrawalInfo,
   denomSelectionInfoToState,
+  updateWithdrawalDenoms,
+  selectWithdrawalDenominations,
+  getPossibleWithdrawalDenoms,
 } from "./withdraw";
 import {
   guardOperationException,
@@ -66,11 +66,6 @@ import {
   durationMin,
   durationMax,
 } from "../util/time";
-import {
-  reconcileReserveHistory,
-  summarizeReserveHistory,
-  ReserveHistorySummary,
-} from "../util/reserveHistoryUtil";
 import { TransactionHandle } from "../util/query";
 import { addPaytoQueryParams } from "../util/payto";
 import { TalerErrorCode } from "../TalerErrorCode";
@@ -86,6 +81,7 @@ import {
   getRetryDuration,
   updateRetryInfoTimeout,
 } from "../util/retries";
+import { ReserveTransactionType } from "../types/ReserveTransaction";
 
 const logger = new Logger("reserves.ts");
 
@@ -138,11 +134,9 @@ export async function createReserve(
 
   const initialWithdrawalGroupId = encodeCrock(getRandomBytes(32));
 
-  const denomSelInfo = await selectWithdrawalDenoms(
-    ws,
-    canonExchange,
-    req.amount,
-  );
+  await updateWithdrawalDenoms(ws, canonExchange);
+  const denoms = await getPossibleWithdrawalDenoms(ws, canonExchange);
+  const denomSelInfo = selectWithdrawalDenominations(req.amount, denoms);
   const initialDenomSel = denomSelectionInfoToState(denomSelInfo);
 
   const reserveRecord: ReserveRecord = {
@@ -166,16 +160,6 @@ export async function createReserve(
     requestedQuery: false,
   };
 
-  const reserveHistoryRecord: ReserveHistoryRecord = {
-    reservePub: keypair.pub,
-    reserveTransactions: [],
-  };
-
-  reserveHistoryRecord.reserveTransactions.push({
-    type: WalletReserveHistoryItemType.Credit,
-    expectedAmount: req.amount,
-  });
-
   const exchangeInfo = await updateExchangeFromUrl(ws, req.exchange);
   const exchangeDetails = exchangeInfo.details;
   if (!exchangeDetails) {
@@ -206,12 +190,7 @@ export async function createReserve(
   const cr: CurrencyRecord = currencyRecord;
 
   const resp = await ws.db.runWithWriteTransaction(
-    [
-      Stores.currencies,
-      Stores.reserves,
-      Stores.reserveHistory,
-      Stores.bankWithdrawUris,
-    ],
+    [Stores.currencies, Stores.reserves, Stores.bankWithdrawUris],
     async (tx) => {
       // Check if we have already created a reserve for that 
bankWithdrawStatusUrl
       if (reserveRecord.bankInfo?.statusUrl) {
@@ -238,7 +217,6 @@ export async function createReserve(
       }
       await tx.put(Stores.currencies, cr);
       await tx.put(Stores.reserves, reserveRecord);
-      await tx.put(Stores.reserveHistory, reserveHistoryRecord);
       const r: CreateReserveResponse = {
         exchange: canonExchange,
         reservePub: keypair.pub,
@@ -499,6 +477,10 @@ async function incrementReserveRetry(
 /**
  * Update the information about a reserve that is stored in the wallet
  * by quering the reserve's exchange.
+ *
+ * If the reserve have funds that are not allocated in a withdrawal group yet
+ * and are big enough to withdraw with available denominations,
+ * create a new withdrawal group for the remaining amount.
  */
 async function updateReserve(
   ws: InternalWalletState,
@@ -542,78 +524,130 @@ async function updateReserve(
   }
 
   const reserveInfo = result.response;
-
   const balance = Amounts.parseOrThrow(reserveInfo.balance);
   const currency = balance.currency;
-  let updateSummary: ReserveHistorySummary | undefined;
-  await ws.db.runWithWriteTransaction(
-    [Stores.reserves, Stores.reserveHistory],
+
+  await updateWithdrawalDenoms(ws, reserve.exchangeBaseUrl);
+  const denoms = await getPossibleWithdrawalDenoms(ws, 
reserve.exchangeBaseUrl);
+
+  const newWithdrawalGroup = await ws.db.runWithWriteTransaction(
+    [Stores.coins, Stores.planchets, Stores.withdrawalGroups, Stores.reserves],
     async (tx) => {
-      const r = await tx.get(Stores.reserves, reservePub);
-      if (!r) {
-        return;
-      }
-      if (r.reserveStatus !== ReserveRecordStatus.QUERYING_STATUS) {
+      const newReserve = await tx.get(Stores.reserves, reserve.reservePub);
+      if (!newReserve) {
         return;
       }
+      let amountReservePlus = Amounts.getZero(currency);
+      let amountReserveMinus = Amounts.getZero(currency);
+
+      // Subtract withdrawal groups for this reserve from the available amount.
+      await tx
+        .iterIndexed(Stores.withdrawalGroups.byReservePub, reservePub)
+        .forEach((wg) => {
+          const cost = wg.denomsSel.totalWithdrawCost;
+          amountReserveMinus = Amounts.add(amountReserveMinus, cost).amount;
+        });
 
-      const hist = await tx.get(Stores.reserveHistory, reservePub);
-      if (!hist) {
-        throw Error("inconsistent database");
+      for (const entry of reserveInfo.history) {
+        switch (entry.type) {
+          case ReserveTransactionType.Credit:
+            amountReservePlus = Amounts.add(
+              amountReservePlus,
+              Amounts.parseOrThrow(entry.amount),
+            ).amount;
+            break;
+          case ReserveTransactionType.Recoup:
+            amountReservePlus = Amounts.add(
+              amountReservePlus,
+              Amounts.parseOrThrow(entry.amount),
+            ).amount;
+            break;
+          case ReserveTransactionType.Closing:
+            amountReserveMinus = Amounts.add(
+              amountReserveMinus,
+              Amounts.parseOrThrow(entry.amount),
+            ).amount;
+            break;
+          case ReserveTransactionType.Withdraw: {
+            // Now we check if the withdrawal transaction
+            // is part of any withdrawal known to this wallet.
+            const planchet = await tx.getIndexed(
+              Stores.planchets.coinEvHashIndex,
+              entry.h_coin_envelope,
+            );
+            if (planchet) {
+              // Amount is already accounted in some withdrawal session
+              break;
+            }
+            const coin = await tx.getIndexed(
+              Stores.coins.coinEvHashIndex,
+              entry.h_coin_envelope,
+            );
+            if (coin) {
+              // Amount is already accounted in some withdrawal session
+              break;
+            }
+            // Amount has been claimed by some withdrawal we don't know about
+            amountReserveMinus = Amounts.add(
+              amountReserveMinus,
+              Amounts.parseOrThrow(entry.amount),
+            ).amount;
+            break;
+          }
+        }
       }
 
-      const newHistoryTransactions = reserveInfo.history.slice(
-        hist.reserveTransactions.length,
+      const remainingAmount = Amounts.sub(amountReservePlus, 
amountReserveMinus)
+        .amount;
+      const denomSelInfo = selectWithdrawalDenominations(
+        remainingAmount,
+        denoms,
       );
 
-      const reserveUpdateId = encodeCrock(getRandomBytes(32));
+      if (denomSelInfo.selectedDenoms.length > 0) {
+        let withdrawalGroupId: string;
 
-      const reconciled = reconcileReserveHistory(
-        hist.reserveTransactions,
-        reserveInfo.history,
-      );
-
-      updateSummary = summarizeReserveHistory(
-        reconciled.updatedLocalHistory,
-        currency,
-      );
-
-      if (
-        reconciled.newAddedItems.length + reconciled.newMatchedItems.length !=
-        0
-      ) {
-        logger.trace("setting reserve status to 'withdrawing' after query");
-        r.reserveStatus = ReserveRecordStatus.WITHDRAWING;
-        r.retryInfo = initRetryInfo();
-        r.requestedQuery = false;
-      } else {
-        if (r.requestedQuery) {
-          logger.trace(
-            "setting reserve status to 'querying-status' (requested query) 
after query",
-          );
-          r.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
-          r.requestedQuery = false;
-          r.retryInfo = initRetryInfo();
+        if (!newReserve.initialWithdrawalStarted) {
+          withdrawalGroupId = newReserve.initialWithdrawalGroupId;
+          newReserve.initialWithdrawalStarted = true;
         } else {
-          logger.trace("setting reserve status to 'dormant' after query");
-          r.reserveStatus = ReserveRecordStatus.DORMANT;
-          r.retryInfo = initRetryInfo(false);
+          withdrawalGroupId = encodeCrock(randomBytes(32));
         }
+
+        const withdrawalRecord: WithdrawalGroupRecord = {
+          withdrawalGroupId: withdrawalGroupId,
+          exchangeBaseUrl: reserve.exchangeBaseUrl,
+          reservePub: reserve.reservePub,
+          rawWithdrawalAmount: remainingAmount,
+          timestampStart: getTimestampNow(),
+          retryInfo: initRetryInfo(),
+          lastError: undefined,
+          denomsSel: denomSelectionInfoToState(denomSelInfo),
+        };
+
+        newReserve.lastError = undefined;
+        newReserve.retryInfo = initRetryInfo(false);
+        newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
+
+        await tx.put(Stores.reserves, newReserve);
+        await tx.put(Stores.withdrawalGroups, withdrawalRecord);
+        return withdrawalRecord;
       }
-      r.lastSuccessfulStatusQuery = getTimestampNow();
-      hist.reserveTransactions = reconciled.updatedLocalHistory;
-      r.lastError = undefined;
-      await tx.put(Stores.reserves, r);
-      await tx.put(Stores.reserveHistory, hist);
+      return;
     },
   );
-  ws.notify({ type: NotificationType.ReserveUpdated, updateSummary });
-  const reserve2 = await ws.db.get(Stores.reserves, reservePub);
-  if (reserve2) {
-    logger.trace(
-      `after db transaction, reserve status is ${reserve2.reserveStatus}`,
-    );
+
+  if (newWithdrawalGroup) {
+    logger.trace("processing new withdraw group");
+    ws.notify({
+      type: NotificationType.WithdrawGroupCreated,
+      withdrawalGroupId: newWithdrawalGroup.withdrawalGroupId,
+    });
+    await processWithdrawGroup(ws, newWithdrawalGroup.withdrawalGroupId);
+  } else {
+    console.trace("withdraw session already existed");
   }
+
   return { ready: true };
 }
 
@@ -651,9 +685,6 @@ async function processReserveImpl(
         break;
       }
     }
-    case ReserveRecordStatus.WITHDRAWING:
-      await depleteReserve(ws, reservePub);
-      break;
     case ReserveRecordStatus.DORMANT:
       // nothing to do
       break;
@@ -669,166 +700,6 @@ async function processReserveImpl(
   }
 }
 
-/**
- * Withdraw coins from a reserve until it is empty.
- *
- * When finished, marks the reserve as depleted by setting
- * the depleted timestamp.
- */
-async function depleteReserve(
-  ws: InternalWalletState,
-  reservePub: string,
-): Promise<void> {
-  let reserve: ReserveRecord | undefined;
-  let hist: ReserveHistoryRecord | undefined;
-  await ws.db.runWithReadTransaction(
-    [Stores.reserves, Stores.reserveHistory],
-    async (tx) => {
-      reserve = await tx.get(Stores.reserves, reservePub);
-      hist = await tx.get(Stores.reserveHistory, reservePub);
-    },
-  );
-
-  if (!reserve) {
-    return;
-  }
-  if (!hist) {
-    throw Error("inconsistent database");
-  }
-  if (reserve.reserveStatus !== ReserveRecordStatus.WITHDRAWING) {
-    return;
-  }
-  logger.trace(`depleting reserve ${reservePub}`);
-
-  const summary = summarizeReserveHistory(
-    hist.reserveTransactions,
-    reserve.currency,
-  );
-
-  const withdrawAmount = summary.unclaimedReserveAmount;
-
-  const denomsForWithdraw = await selectWithdrawalDenoms(
-    ws,
-    reserve.exchangeBaseUrl,
-    withdrawAmount,
-  );
-  if (!denomsForWithdraw) {
-    // Only complain about inability to withdraw if we
-    // didn't withdraw before.
-    if (Amounts.isZero(summary.withdrawnAmount)) {
-      const opErr = makeErrorDetails(
-        TalerErrorCode.WALLET_EXCHANGE_DENOMINATIONS_INSUFFICIENT,
-        `Unable to withdraw from reserve, no denominations are available to 
withdraw.`,
-        {},
-      );
-      await incrementReserveRetry(ws, reserve.reservePub, opErr);
-      throw new OperationFailedAndReportedError(opErr);
-    }
-    return;
-  }
-
-  logger.trace(
-    `Selected coins total cost ${Amounts.stringify(
-      denomsForWithdraw.totalWithdrawCost,
-    )} for withdrawal of ${Amounts.stringify(withdrawAmount)}`,
-  );
-
-  logger.trace("selected denominations");
-
-  const newWithdrawalGroup = await ws.db.runWithWriteTransaction(
-    [
-      Stores.withdrawalGroups,
-      Stores.reserves,
-      Stores.reserveHistory,
-      Stores.planchets,
-    ],
-    async (tx) => {
-      const newReserve = await tx.get(Stores.reserves, reservePub);
-      if (!newReserve) {
-        return false;
-      }
-      if (newReserve.reserveStatus !== ReserveRecordStatus.WITHDRAWING) {
-        return false;
-      }
-      const newHist = await tx.get(Stores.reserveHistory, reservePub);
-      if (!newHist) {
-        throw Error("inconsistent database");
-      }
-      const newSummary = summarizeReserveHistory(
-        newHist.reserveTransactions,
-        newReserve.currency,
-      );
-      if (
-        Amounts.cmp(
-          newSummary.unclaimedReserveAmount,
-          denomsForWithdraw.totalWithdrawCost,
-        ) < 0
-      ) {
-        // Something must have happened concurrently!
-        logger.error(
-          "aborting withdrawal session, likely concurrent withdrawal happened",
-        );
-        logger.error(
-          `unclaimed reserve amount is ${newSummary.unclaimedReserveAmount}`,
-        );
-        logger.error(
-          `withdrawal cost is ${denomsForWithdraw.totalWithdrawCost}`,
-        );
-        return false;
-      }
-      for (let i = 0; i < denomsForWithdraw.selectedDenoms.length; i++) {
-        const sd = denomsForWithdraw.selectedDenoms[i];
-        for (let j = 0; j < sd.count; j++) {
-          const amt = Amounts.add(sd.denom.value, sd.denom.feeWithdraw).amount;
-          newHist.reserveTransactions.push({
-            type: WalletReserveHistoryItemType.Withdraw,
-            expectedAmount: amt,
-          });
-        }
-      }
-      logger.trace("setting reserve status to dormant after depletion");
-      newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
-      newReserve.retryInfo = initRetryInfo(false);
-
-      let withdrawalGroupId: string;
-
-      if (!newReserve.initialWithdrawalStarted) {
-        withdrawalGroupId = newReserve.initialWithdrawalGroupId;
-        newReserve.initialWithdrawalStarted = true;
-      } else {
-        withdrawalGroupId = encodeCrock(randomBytes(32));
-      }
-
-      const withdrawalRecord: WithdrawalGroupRecord = {
-        withdrawalGroupId: withdrawalGroupId,
-        exchangeBaseUrl: newReserve.exchangeBaseUrl,
-        reservePub: newReserve.reservePub,
-        rawWithdrawalAmount: withdrawAmount,
-        timestampStart: getTimestampNow(),
-        retryInfo: initRetryInfo(),
-        lastError: undefined,
-        denomsSel: denomSelectionInfoToState(denomsForWithdraw),
-      };
-
-      await tx.put(Stores.reserves, newReserve);
-      await tx.put(Stores.reserveHistory, newHist);
-      await tx.put(Stores.withdrawalGroups, withdrawalRecord);
-      return withdrawalRecord;
-    },
-  );
-
-  if (newWithdrawalGroup) {
-    logger.trace("processing new withdraw group");
-    ws.notify({
-      type: NotificationType.WithdrawGroupCreated,
-      withdrawalGroupId: newWithdrawalGroup.withdrawalGroupId,
-    });
-    await processWithdrawGroup(ws, newWithdrawalGroup.withdrawalGroupId);
-  } else {
-    console.trace("withdraw session already existed");
-  }
-}
-
 export async function createTalerWithdrawReserve(
   ws: InternalWalletState,
   talerWithdrawUri: string,
diff --git a/packages/taler-wallet-core/src/operations/state.ts 
b/packages/taler-wallet-core/src/operations/state.ts
index c4d5b38f..11695f6d 100644
--- a/packages/taler-wallet-core/src/operations/state.ts
+++ b/packages/taler-wallet-core/src/operations/state.ts
@@ -19,7 +19,7 @@ import { BalancesResponse } from "../types/walletTypes";
 import { CryptoApi, CryptoWorkerFactory } from "../crypto/workers/cryptoApi";
 import { AsyncOpMemoMap, AsyncOpMemoSingle } from "../util/asyncMemo";
 import { Logger } from "../util/logging";
-import { PendingOperationsResponse } from "../types/pending";
+import { PendingOperationsResponse } from "../types/pendingTypes";
 import { WalletNotification } from "../types/notifications";
 import { Database } from "../util/query";
 import { openPromise, OpenedPromise } from "../util/promiseUtils";
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index bc10e346..f47f7662 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -32,8 +32,10 @@ import {
 } from "../types/dbTypes";
 import {
   getExchangeWithdrawalInfo,
-  selectWithdrawalDenoms,
   denomSelectionInfoToState,
+  updateWithdrawalDenoms,
+  getPossibleWithdrawalDenoms,
+  selectWithdrawalDenominations,
 } from "./withdraw";
 import { updateExchangeFromUrl } from "./exchanges";
 import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
@@ -92,12 +94,15 @@ export async function prepareTip(
     );
 
     const walletTipId = encodeCrock(getRandomBytes(32));
-    const selectedDenoms = await selectWithdrawalDenoms(
-      ws,
-      tipPickupStatus.exchange_url,
+    await updateWithdrawalDenoms(ws, tipPickupStatus.exchange_url);
+    const denoms = await getPossibleWithdrawalDenoms(ws, 
tipPickupStatus.exchange_url);
+    const selectedDenoms = await selectWithdrawalDenominations(
       amount,
+      denoms
     );
 
+    const secretSeed = encodeCrock(getRandomBytes(64));
+
     tipRecord = {
       walletTipId: walletTipId,
       acceptedTimestamp: undefined,
@@ -105,7 +110,6 @@ export async function prepareTip(
       tipExpiration: tipPickupStatus.expiration,
       exchangeBaseUrl: tipPickupStatus.exchange_url,
       merchantBaseUrl: res.merchantBaseUrl,
-      planchets: undefined,
       createdTimestamp: getTimestampNow(),
       merchantTipId: res.merchantTipId,
       tipAmountEffective: Amounts.sub(
@@ -117,6 +121,7 @@ export async function prepareTip(
       lastError: undefined,
       denomsSel: denomSelectionInfoToState(selectedDenoms),
       pickedUpTimestamp: undefined,
+      secretSeed,
     };
     await ws.db.put(Stores.tips, tipRecord);
   }
@@ -316,6 +321,7 @@ async function processTipImpl(
       exchangeBaseUrl: tipRecord.exchangeBaseUrl,
       status: CoinStatus.Fresh,
       suspended: false,
+      coinEvHash: planchet.coinEvHash,
     });
   }
 
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 56e07a42..cf524db4 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -36,7 +36,7 @@ import {
   WithdrawalType,
   WithdrawalDetails,
   OrderShortInfo,
-} from "../types/transactions";
+} from "../types/transactionsTypes";
 import { getFundingPaytoUris } from "./reserves";
 import { TipResponse } from "../types/talerTypes";
 
diff --git a/packages/taler-wallet-core/src/operations/withdraw-test.ts 
b/packages/taler-wallet-core/src/operations/withdraw-test.ts
index 24cb6f4b..d21119c8 100644
--- a/packages/taler-wallet-core/src/operations/withdraw-test.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw-test.ts
@@ -15,7 +15,7 @@
  */
 
 import test from "ava";
-import { getWithdrawDenomList } from "./withdraw";
+import { selectWithdrawalDenominations } from "./withdraw";
 import { Amounts } from "../util/amounts";
 
 test("withdrawal selection bug repro", (t) => {
@@ -322,7 +322,7 @@ test("withdrawal selection bug repro", (t) => {
     },
   ];
 
-  const res = getWithdrawDenomList(amount, denoms);
+  const res = selectWithdrawalDenominations(amount, denoms);
 
   console.error("cost", Amounts.stringify(res.totalWithdrawCost));
   console.error("withdraw amount", Amounts.stringify(amount));
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index a3bb9724..758b8078 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -86,12 +86,13 @@ export function isWithdrawableDenom(d: DenominationRecord): 
boolean {
   return started && stillOkay && !d.isRevoked;
 }
 
+
 /**
  * Get a list of denominations (with repetitions possible)
  * whose total value is as close as possible to the available
  * amount, but never larger.
  */
-export function getWithdrawDenomList(
+export function selectWithdrawalDenominations(
   amountAvailable: AmountJson,
   denoms: DenominationRecord[],
 ): DenominationSelectionInfo {
@@ -207,7 +208,7 @@ export async function getBankWithdrawalInfo(
 /**
  * Return denominations that can potentially used for a withdrawal.
  */
-async function getPossibleDenoms(
+export async function getPossibleWithdrawalDenoms(
   ws: InternalWalletState,
   exchangeBaseUrl: string,
 ): Promise<DenominationRecord[]> {
@@ -470,6 +471,7 @@ async function processPlanchetVerifyAndStoreCoin(
     denomPub: planchet.denomPub,
     denomPubHash: planchet.denomPubHash,
     denomSig,
+    coinEvHash: planchet.coinEvHash,
     exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl,
     status: CoinStatus.Fresh,
     coinSource: {
@@ -524,17 +526,13 @@ export function denomSelectionInfoToState(
 }
 
 /**
- * Get a list of denominations to withdraw from the given exchange for the
- * given amount, making sure that all denominations' signatures are verified.
- *
- * Writes to the DB in order to record the result from verifying
- * denominations.
+ * Make sure that denominations that currently can be used for withdrawal
+ * are validated, and the result of validation is stored in the database.
  */
-export async function selectWithdrawalDenoms(
+export async function updateWithdrawalDenoms(
   ws: InternalWalletState,
   exchangeBaseUrl: string,
-  amount: AmountJson,
-): Promise<DenominationSelectionInfo> {
+): Promise<void> {
   const exchange = await ws.db.get(Stores.exchanges, exchangeBaseUrl);
   if (!exchange) {
     logger.error("exchange not found");
@@ -545,43 +543,24 @@ export async function selectWithdrawalDenoms(
     logger.error("exchange details not available");
     throw Error(`exchange ${exchangeBaseUrl} details not available`);
   }
-
-  let allValid = false;
-  let selectedDenoms: DenominationSelectionInfo;
-
-  // Find a denomination selection for the requested amount.
-  // If a selected denomination has not been validated yet
-  // and turns our to be invalid, we try again with the
-  // reduced set of denominations.
-  do {
-    allValid = true;
-    const nextPossibleDenoms = await getPossibleDenoms(ws, exchange.baseUrl);
-    selectedDenoms = getWithdrawDenomList(amount, nextPossibleDenoms);
-    for (const denomSel of selectedDenoms.selectedDenoms) {
-      const denom = denomSel.denom;
-      if (denom.status === DenominationStatus.Unverified) {
-        const valid = await ws.cryptoApi.isValidDenom(
-          denom,
-          exchangeDetails.masterPublicKey,
-        );
-        if (!valid) {
-          denom.status = DenominationStatus.VerifiedBad;
-          allValid = false;
-        } else {
-          denom.status = DenominationStatus.VerifiedGood;
-        }
-        await ws.db.put(Stores.denominations, denom);
+  const denominations = await getPossibleWithdrawalDenoms(ws, exchangeBaseUrl);
+  for (const denom of denominations) {
+    if (denom.status === DenominationStatus.Unverified) {
+      const valid = await ws.cryptoApi.isValidDenom(
+        denom,
+        exchangeDetails.masterPublicKey,
+      );
+      if (!valid) {
+        denom.status = DenominationStatus.VerifiedBad;
+      } else {
+        denom.status = DenominationStatus.VerifiedGood;
       }
+      await ws.db.put(Stores.denominations, denom);
     }
-  } while (selectedDenoms.selectedDenoms.length > 0 && !allValid);
-
-  if (Amounts.cmp(selectedDenoms.totalWithdrawCost, amount) > 0) {
-    throw Error("Bug: withdrawal coin selection is wrong");
   }
-
-  return selectedDenoms;
 }
 
+
 async function incrementWithdrawalRetry(
   ws: InternalWalletState,
   withdrawalGroupId: string,
@@ -745,7 +724,9 @@ export async function getExchangeWithdrawalInfo(
     throw Error(`exchange ${exchangeInfo.baseUrl} wire details not available`);
   }
 
-  const selectedDenoms = await selectWithdrawalDenoms(ws, baseUrl, amount);
+  await updateWithdrawalDenoms(ws, baseUrl);
+  const denoms = await getPossibleWithdrawalDenoms(ws, baseUrl);
+  const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
   const exchangeWireAccounts: string[] = [];
   for (const account of exchangeWireInfo.accounts) {
     exchangeWireAccounts.push(account.payto_uri);
diff --git a/packages/taler-wallet-core/src/types/backupTypes.ts 
b/packages/taler-wallet-core/src/types/backupTypes.ts
index 09bc4670..a3261ae3 100644
--- a/packages/taler-wallet-core/src/types/backupTypes.ts
+++ b/packages/taler-wallet-core/src/types/backupTypes.ts
@@ -53,12 +53,6 @@
  * Imports.
  */
 import { Timestamp } from "../util/time";
-import {
-  ReserveClosingTransaction,
-  ReserveCreditTransaction,
-  ReserveRecoupTransaction,
-  ReserveWithdrawTransaction,
-} from "./ReserveTransaction";
 
 /**
  * Type alias for strings that are to be treated like amounts.
@@ -1128,82 +1122,6 @@ export interface BackupExchange {
   tos_etag_accepted: string | undefined;
 }
 
-export enum WalletReserveHistoryItemType {
-  Credit = "credit",
-  Withdraw = "withdraw",
-  Closing = "closing",
-  Recoup = "recoup",
-}
-
-export interface BackupReserveHistoryCreditItem {
-  type: WalletReserveHistoryItemType.Credit;
-
-  /**
-   * Amount we expect to see credited.
-   */
-  expected_amount?: BackupAmountString;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matched_exchange_transaction?: ReserveCreditTransaction;
-}
-
-/**
- * Reserve history item for a withdrawal
- */
-export interface BackupReserveHistoryWithdrawItem {
-  type: WalletReserveHistoryItemType.Withdraw;
-
-  expected_amount?: BackupAmountString;
-
-  /**
-   * Hash of the blinded coin.
-   *
-   * When this value is set, it indicates that a withdrawal is active
-   * in the wallet for the reserve.
-   */
-  expected_coin_ev_hash?: string;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matched_exchange_transaction?: ReserveWithdrawTransaction;
-}
-
-export interface BackupReserveHistoryClosingItem {
-  type: WalletReserveHistoryItemType.Closing;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matched_exchange_transaction?: ReserveClosingTransaction;
-}
-
-export interface BackupReserveHistoryRecoupItem {
-  type: WalletReserveHistoryItemType.Recoup;
-
-  /**
-   * Amount we expect to see recouped.
-   */
-  expected_amount?: BackupAmountString;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matched_exchange_transaction?: ReserveRecoupTransaction;
-}
-
-export type BackupReserveHistoryItem =
-  | BackupReserveHistoryCreditItem
-  | BackupReserveHistoryWithdrawItem
-  | BackupReserveHistoryRecoupItem
-  | BackupReserveHistoryClosingItem;
-
 export enum BackupProposalStatus {
   /**
    * Proposed (and either downloaded or not,
diff --git a/packages/taler-wallet-core/src/types/cryptoTypes.ts 
b/packages/taler-wallet-core/src/types/cryptoTypes.ts
index 98bdf92e..eb18d83f 100644
--- a/packages/taler-wallet-core/src/types/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/types/cryptoTypes.ts
@@ -83,6 +83,11 @@ export interface DerivedRefreshSession {
      */
     coinEv: string;
 
+    /**
+     * Hash of the blinded public key.
+     */
+    coinEvHash: string;
+
     /**
      * Blinding key used.
      */
@@ -122,6 +127,7 @@ export interface DeriveTipRequest {
 export interface DerivedTipPlanchet {
   blindingKey: string;
   coinEv: string;
+  coinEvHash: string;
   coinPriv: string;
   coinPub: string;
 }
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts 
b/packages/taler-wallet-core/src/types/dbTypes.ts
index 3a42b8db..71a59131 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -65,12 +65,6 @@ export enum ReserveRecordStatus {
    */
   QUERYING_STATUS = "querying-status",
 
-  /**
-   * Status is queried, the wallet must now select coins
-   * and start withdrawing.
-   */
-  WITHDRAWING = "withdrawing",
-
   /**
    * The corresponding withdraw record has been created.
    * No further processing is done, unless explicitly requested
@@ -84,76 +78,6 @@ export enum ReserveRecordStatus {
   BANK_ABORTED = "bank-aborted",
 }
 
-export enum WalletReserveHistoryItemType {
-  Credit = "credit",
-  Withdraw = "withdraw",
-  Closing = "closing",
-  Recoup = "recoup",
-}
-
-export interface WalletReserveHistoryCreditItem {
-  type: WalletReserveHistoryItemType.Credit;
-
-  /**
-   * Amount we expect to see credited.
-   */
-  expectedAmount?: AmountJson;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matchedExchangeTransaction?: ReserveCreditTransaction;
-}
-
-export interface WalletReserveHistoryWithdrawItem {
-  expectedAmount?: AmountJson;
-
-  type: WalletReserveHistoryItemType.Withdraw;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matchedExchangeTransaction?: ReserveWithdrawTransaction;
-}
-
-export interface WalletReserveHistoryClosingItem {
-  type: WalletReserveHistoryItemType.Closing;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matchedExchangeTransaction?: ReserveClosingTransaction;
-}
-
-export interface WalletReserveHistoryRecoupItem {
-  type: WalletReserveHistoryItemType.Recoup;
-
-  /**
-   * Amount we expect to see recouped.
-   */
-  expectedAmount?: AmountJson;
-
-  /**
-   * Item from the reserve transaction history that this
-   * wallet reserve history item matches up with.
-   */
-  matchedExchangeTransaction?: ReserveRecoupTransaction;
-}
-
-export type WalletReserveHistoryItem =
-  | WalletReserveHistoryCreditItem
-  | WalletReserveHistoryWithdrawItem
-  | WalletReserveHistoryRecoupItem
-  | WalletReserveHistoryClosingItem;
-
-export interface ReserveHistoryRecord {
-  reservePub: string;
-  reserveTransactions: WalletReserveHistoryItem[];
-}
-
 export interface ReserveBankInfo {
   /**
    * Status URL that the wallet will use to query the status
@@ -667,6 +591,8 @@ export interface RefreshPlanchet {
    */
   coinEv: string;
 
+  coinEvHash: string;
+
   /**
    * Blinding key used.
    */
@@ -782,6 +708,14 @@ export interface CoinRecord {
    */
   blindingKey: string;
 
+  /**
+   * Hash of the coin envelope.
+   *
+   * Stored here for indexing purposes, so that when looking at a
+   * reserve history, we can quickly find the coin for a withdrawal 
transaction.
+   */
+  coinEvHash: string;
+
   /**
    * Status of the coin.
    */
@@ -1536,6 +1470,12 @@ class CoinsStore extends Store<"coins", CoinRecord> {
     string,
     CoinRecord
   >(this, "denomPubHashIndex", "denomPubHash");
+
+  coinEvHashIndex = new Index<"coins", "coinEvHashIndex", string, CoinRecord>(
+    this,
+    "coinEvHashIndex",
+    "coinEvHash",
+  );
 }
 
 class ProposalsStore extends Store<"proposals", ProposalRecord> {
@@ -1602,15 +1542,6 @@ class ReservesStore extends Store<"reserves", 
ReserveRecord> {
   }
 }
 
-class ReserveHistoryStore extends Store<
-  "reserveHistory",
-  ReserveHistoryRecord
-> {
-  constructor() {
-    super("reserveHistory", { keyPath: "reservePub" });
-  }
-}
-
 class TipsStore extends Store<"tips", TipRecord> {
   constructor() {
     super("tips", { keyPath: "walletTipId" });
@@ -1638,6 +1569,12 @@ class WithdrawalGroupsStore extends Store<
   constructor() {
     super("withdrawals", { keyPath: "withdrawalGroupId" });
   }
+  byReservePub = new Index<
+    "withdrawals",
+    "withdrawalsByReserveIndex",
+    string,
+    WithdrawalGroupRecord
+  >(this, "withdrawalsByReserveIndex", "reservePub");
 }
 
 class PlanchetsStore extends Store<"planchets", PlanchetRecord> {
@@ -1656,6 +1593,12 @@ class PlanchetsStore extends Store<"planchets", 
PlanchetRecord> {
     string,
     PlanchetRecord
   >(this, "withdrawalGroupIndex", "withdrawalGroupId");
+
+  coinEvHashIndex = new Index<"planchets", "coinEvHashIndex", string, 
PlanchetRecord>(
+    this,
+    "coinEvHashIndex",
+    "coinEvHash",
+  );
 }
 
 /**
@@ -1702,7 +1645,6 @@ export const Stores = {
     keyPath: "recoupGroupId",
   }),
   reserves: new ReservesStore(),
-  reserveHistory: new ReserveHistoryStore(),
   purchases: new PurchasesStore(),
   tips: new TipsStore(),
   withdrawalGroups: new WithdrawalGroupsStore(),
diff --git a/packages/taler-wallet-core/src/types/notifications.ts 
b/packages/taler-wallet-core/src/types/notifications.ts
index 533223cc..9ddcf4fa 100644
--- a/packages/taler-wallet-core/src/types/notifications.ts
+++ b/packages/taler-wallet-core/src/types/notifications.ts
@@ -23,7 +23,6 @@
  * Imports.
  */
 import { TalerErrorDetails } from "./walletTypes";
-import { ReserveHistorySummary } from "../util/reserveHistoryUtil";
 
 export enum NotificationType {
   CoinWithdrawn = "coin-withdrawn",
@@ -125,10 +124,6 @@ export interface RefreshRefusedNotification {
   type: NotificationType.RefreshUnwarranted;
 }
 
-export interface ReserveUpdatedNotification {
-  type: NotificationType.ReserveUpdated;
-  updateSummary?: ReserveHistorySummary;
-}
 
 export interface ReserveConfirmedNotification {
   type: NotificationType.ReserveConfirmed;
@@ -252,7 +247,6 @@ export type WalletNotification =
   | RefreshRevealedNotification
   | RefreshStartedNotification
   | RefreshRefusedNotification
-  | ReserveUpdatedNotification
   | ReserveCreatedNotification
   | ReserveConfirmedNotification
   | WithdrawalGroupFinishedNotification
diff --git a/packages/taler-wallet-core/src/types/pending.ts 
b/packages/taler-wallet-core/src/types/pending.ts
deleted file mode 100644
index 18d9a2fa..00000000
--- a/packages/taler-wallet-core/src/types/pending.ts
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Type and schema definitions for pending operations in the wallet.
- */
-
-/**
- * Imports.
- */
-import { TalerErrorDetails, BalancesResponse } from "./walletTypes";
-import { ReserveRecordStatus } from "./dbTypes";
-import { Timestamp, Duration } from "../util/time";
-import { RetryInfo } from "../util/retries";
-
-export enum PendingOperationType {
-  Bug = "bug",
-  ExchangeUpdate = "exchange-update",
-  ExchangeCheckRefresh = "exchange-check-refresh",
-  Pay = "pay",
-  ProposalChoice = "proposal-choice",
-  ProposalDownload = "proposal-download",
-  Refresh = "refresh",
-  Reserve = "reserve",
-  Recoup = "recoup",
-  RefundQuery = "refund-query",
-  TipChoice = "tip-choice",
-  TipPickup = "tip-pickup",
-  Withdraw = "withdraw",
-}
-
-/**
- * Information about a pending operation.
- */
-export type PendingOperationInfo = PendingOperationInfoCommon &
-  (
-    | PendingBugOperation
-    | PendingExchangeUpdateOperation
-    | PendingExchangeCheckRefreshOperation
-    | PendingPayOperation
-    | PendingProposalChoiceOperation
-    | PendingProposalDownloadOperation
-    | PendingRefreshOperation
-    | PendingRefundQueryOperation
-    | PendingReserveOperation
-    | PendingTipChoiceOperation
-    | PendingTipPickupOperation
-    | PendingWithdrawOperation
-    | PendingRecoupOperation
-  );
-
-/**
- * The wallet is currently updating information about an exchange.
- */
-export interface PendingExchangeUpdateOperation {
-  type: PendingOperationType.ExchangeUpdate;
-  stage: ExchangeUpdateOperationStage;
-  reason: string;
-  exchangeBaseUrl: string;
-  lastError: TalerErrorDetails | undefined;
-}
-
-/**
- * The wallet should check whether coins from this exchange
- * need to be auto-refreshed.
- */
-export interface PendingExchangeCheckRefreshOperation {
-  type: PendingOperationType.ExchangeCheckRefresh;
-  exchangeBaseUrl: string;
-}
-
-/**
- * Some interal error happened in the wallet.  This pending operation
- * should *only* be reported for problems in the wallet, not when
- * a problem with a merchant/exchange/etc. occurs.
- */
-export interface PendingBugOperation {
-  type: PendingOperationType.Bug;
-  message: string;
-  details: any;
-}
-
-/**
- * Current state of an exchange update operation.
- */
-export enum ExchangeUpdateOperationStage {
-  FetchKeys = "fetch-keys",
-  FetchWire = "fetch-wire",
-  FinalizeUpdate = "finalize-update",
-}
-
-export enum ReserveType {
-  /**
-   * Manually created.
-   */
-  Manual = "manual",
-  /**
-   * Withdrawn from a bank that has "tight" Taler integration
-   */
-  TalerBankWithdraw = "taler-bank-withdraw",
-}
-
-/**
- * Status of processing a reserve.
- *
- * Does *not* include the withdrawal operation that might result
- * from this.
- */
-export interface PendingReserveOperation {
-  type: PendingOperationType.Reserve;
-  retryInfo: RetryInfo | undefined;
-  stage: ReserveRecordStatus;
-  timestampCreated: Timestamp;
-  reserveType: ReserveType;
-  reservePub: string;
-  bankWithdrawConfirmUrl?: string;
-}
-
-/**
- * Status of an ongoing withdrawal operation.
- */
-export interface PendingRefreshOperation {
-  type: PendingOperationType.Refresh;
-  lastError?: TalerErrorDetails;
-  refreshGroupId: string;
-  finishedPerCoin: boolean[];
-  retryInfo: RetryInfo;
-}
-
-/**
- * Status of downloading signed contract terms from a merchant.
- */
-export interface PendingProposalDownloadOperation {
-  type: PendingOperationType.ProposalDownload;
-  merchantBaseUrl: string;
-  proposalTimestamp: Timestamp;
-  proposalId: string;
-  orderId: string;
-  lastError?: TalerErrorDetails;
-  retryInfo: RetryInfo;
-}
-
-/**
- * User must choose whether to accept or reject the merchant's
- * proposed contract terms.
- */
-export interface PendingProposalChoiceOperation {
-  type: PendingOperationType.ProposalChoice;
-  merchantBaseUrl: string;
-  proposalTimestamp: Timestamp;
-  proposalId: string;
-}
-
-/**
- * The wallet is picking up a tip that the user has accepted.
- */
-export interface PendingTipPickupOperation {
-  type: PendingOperationType.TipPickup;
-  tipId: string;
-  merchantBaseUrl: string;
-  merchantTipId: string;
-}
-
-/**
- * The wallet has been offered a tip, and the user now needs to
- * decide whether to accept or reject the tip.
- */
-export interface PendingTipChoiceOperation {
-  type: PendingOperationType.TipChoice;
-  tipId: string;
-  merchantBaseUrl: string;
-  merchantTipId: string;
-}
-
-/**
- * The wallet is signing coins and then sending them to
- * the merchant.
- */
-export interface PendingPayOperation {
-  type: PendingOperationType.Pay;
-  proposalId: string;
-  isReplay: boolean;
-  retryInfo: RetryInfo;
-  lastError: TalerErrorDetails | undefined;
-}
-
-/**
- * The wallet is querying the merchant about whether any refund
- * permissions are available for a purchase.
- */
-export interface PendingRefundQueryOperation {
-  type: PendingOperationType.RefundQuery;
-  proposalId: string;
-  retryInfo: RetryInfo;
-  lastError: TalerErrorDetails | undefined;
-}
-
-export interface PendingRecoupOperation {
-  type: PendingOperationType.Recoup;
-  recoupGroupId: string;
-  retryInfo: RetryInfo;
-  lastError: TalerErrorDetails | undefined;
-}
-
-/**
- * Status of an ongoing withdrawal operation.
- */
-export interface PendingWithdrawOperation {
-  type: PendingOperationType.Withdraw;
-  lastError: TalerErrorDetails | undefined;
-  retryInfo: RetryInfo;
-  withdrawalGroupId: string;
-  numCoinsWithdrawn: number;
-  numCoinsTotal: number;
-}
-
-/**
- * Fields that are present in every pending operation.
- */
-export interface PendingOperationInfoCommon {
-  /**
-   * Type of the pending operation.
-   */
-  type: PendingOperationType;
-
-  /**
-   * Set to true if the operation indicates that something is really in 
progress,
-   * as opposed to some regular scheduled operation or a permanent failure.
-   */
-  givesLifeness: boolean;
-
-  /**
-   * Retry info, not available on all pending operations.
-   * If it is available, it must have the same name.
-   */
-  retryInfo?: RetryInfo;
-}
-
-/**
- * Response returned from the pending operations API.
- */
-export interface PendingOperationsResponse {
-  /**
-   * List of pending operations.
-   */
-  pendingOperations: PendingOperationInfo[];
-
-  /**
-   * Current wallet balance, including pending balances.
-   */
-  walletBalance: BalancesResponse;
-
-  /**
-   * When is the next pending operation due to be re-tried?
-   */
-  nextRetryDelay: Duration;
-
-  /**
-   * Does this response only include pending operations that
-   * are due to be executed right now?
-   */
-  onlyDue: boolean;
-}
diff --git a/packages/taler-wallet-core/src/types/transactions.ts 
b/packages/taler-wallet-core/src/types/transactions.ts
deleted file mode 100644
index 0a683f29..00000000
--- a/packages/taler-wallet-core/src/types/transactions.ts
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Type and schema definitions for the wallet's transaction list.
- *
- * @author Florian Dold
- * @author Torsten Grote
- */
-
-/**
- * Imports.
- */
-import { Timestamp } from "../util/time";
-import {
-  AmountString,
-  Product,
-  InternationalizedString,
-  MerchantInfo,
-  codecForInternationalizedString,
-  codecForMerchantInfo,
-  codecForProduct,
-} from "./talerTypes";
-import {
-  Codec,
-  buildCodecForObject,
-  codecOptional,
-  codecForString,
-  codecForList,
-  codecForAny,
-} from "../util/codec";
-import { TalerErrorDetails } from "./walletTypes";
-
-export interface TransactionsRequest {
-  /**
-   * return only transactions in the given currency
-   */
-  currency?: string;
-
-  /**
-   * if present, results will be limited to transactions related to the given 
search string
-   */
-  search?: string;
-}
-
-export interface TransactionsResponse {
-  // a list of past and pending transactions sorted by pending, timestamp and 
transactionId.
-  // In case two events are both pending and have the same timestamp,
-  // they are sorted by the transactionId
-  // (lexically ascending and locale-independent comparison).
-  transactions: Transaction[];
-}
-
-export interface TransactionCommon {
-  // opaque unique ID for the transaction, used as a starting point for 
paginating queries
-  // and for invoking actions on the transaction (e.g. deleting/hiding it from 
the history)
-  transactionId: string;
-
-  // the type of the transaction; different types might provide additional 
information
-  type: TransactionType;
-
-  // main timestamp of the transaction
-  timestamp: Timestamp;
-
-  // true if the transaction is still pending, false otherwise
-  // If a transaction is not longer pending, its timestamp will be updated,
-  // but its transactionId will remain unchanged
-  pending: boolean;
-
-  // Raw amount of the transaction (exclusive of fees or other extra costs)
-  amountRaw: AmountString;
-
-  // Amount added or removed from the wallet's balance (including all fees and 
other costs)
-  amountEffective: AmountString;
-
-  error?: TalerErrorDetails;
-}
-
-export type Transaction =
-  | TransactionWithdrawal
-  | TransactionPayment
-  | TransactionRefund
-  | TransactionTip
-  | TransactionRefresh;
-
-export enum TransactionType {
-  Withdrawal = "withdrawal",
-  Payment = "payment",
-  Refund = "refund",
-  Refresh = "refresh",
-  Tip = "tip",
-}
-
-export enum WithdrawalType {
-  TalerBankIntegrationApi = "taler-bank-integration-api",
-  ManualTransfer = "manual-transfer",
-}
-
-export type WithdrawalDetails =
-  | WithdrawalDetailsForManualTransfer
-  | WithdrawalDetailsForTalerBankIntegrationApi;
-
-interface WithdrawalDetailsForManualTransfer {
-  type: WithdrawalType.ManualTransfer;
-
-  /**
-   * Payto URIs that the exchange supports.
-   *
-   * Already contains the amount and message.
-   */
-  exchangePaytoUris: string[];
-}
-
-interface WithdrawalDetailsForTalerBankIntegrationApi {
-  type: WithdrawalType.TalerBankIntegrationApi;
-
-  /**
-   * Set to true if the bank has confirmed the withdrawal, false if not.
-   * An unconfirmed withdrawal usually requires user-input and should be 
highlighted in the UI.
-   * See also bankConfirmationUrl below.
-   */
-  confirmed: boolean;
-
-  /**
-   * If the withdrawal is unconfirmed, this can include a URL for user
-   * initiated confirmation.
-   */
-  bankConfirmationUrl?: string;
-}
-
-// This should only be used for actual withdrawals
-// and not for tips that have their own transactions type.
-interface TransactionWithdrawal extends TransactionCommon {
-  type: TransactionType.Withdrawal;
-
-  /**
-   * Exchange of the withdrawal.
-   */
-  exchangeBaseUrl: string;
-
-  /**
-   * Amount that got subtracted from the reserve balance.
-   */
-  amountRaw: AmountString;
-
-  /**
-   * Amount that actually was (or will be) added to the wallet's balance.
-   */
-  amountEffective: AmountString;
-
-  withdrawalDetails: WithdrawalDetails;
-}
-
-export enum PaymentStatus {
-  /**
-   * Explicitly aborted after timeout / failure
-   */
-  Aborted = "aborted",
-
-  /**
-   * Payment failed, wallet will auto-retry.
-   * User should be given the option to retry now / abort.
-   */
-  Failed = "failed",
-
-  /**
-   * Paid successfully
-   */
-  Paid = "paid",
-
-  /**
-   * User accepted, payment is processing.
-   */
-  Accepted = "accepted",
-}
-
-export interface TransactionPayment extends TransactionCommon {
-  type: TransactionType.Payment;
-
-  /**
-   * Additional information about the payment.
-   */
-  info: OrderShortInfo;
-
-  /**
-   * Wallet-internal end-to-end identifier for the payment.
-   */
-  proposalId: string;
-
-  /**
-   * How far did the wallet get with processing the payment?
-   */
-  status: PaymentStatus;
-
-  /**
-   * Amount that must be paid for the contract
-   */
-  amountRaw: AmountString;
-
-  /**
-   * Amount that was paid, including deposit, wire and refresh fees.
-   */
-  amountEffective: AmountString;
-}
-
-export interface OrderShortInfo {
-  /**
-   * Order ID, uniquely identifies the order within a merchant instance
-   */
-  orderId: string;
-
-  /**
-   * Hash of the contract terms.
-   */
-  contractTermsHash: string;
-
-  /**
-   * More information about the merchant
-   */
-  merchant: MerchantInfo;
-
-  /**
-   * Summary of the order, given by the merchant
-   */
-  summary: string;
-
-  /**
-   * Map from IETF BCP 47 language tags to localized summaries
-   */
-  summary_i18n?: InternationalizedString;
-
-  /**
-   * List of products that are part of the order
-   */
-  products: Product[] | undefined;
-
-  /**
-   * URL of the fulfillment, given by the merchant
-   */
-  fulfillmentUrl?: string;
-
-  /**
-   * Plain text message that should be shown to the user
-   * when the payment is complete.
-   */
-  fulfillmentMessage?: string;
-
-  /**
-   * Translations of fulfillmentMessage.
-   */
-  fulfillmentMessage_i18n?: InternationalizedString;
-}
-
-interface TransactionRefund extends TransactionCommon {
-  type: TransactionType.Refund;
-
-  // ID for the transaction that is refunded
-  refundedTransactionId: string;
-
-  // Additional information about the refunded payment
-  info: OrderShortInfo;
-
-  // Amount that has been refunded by the merchant
-  amountRaw: AmountString;
-
-  // Amount will be added to the wallet's balance after fees and refreshing
-  amountEffective: AmountString;
-}
-
-interface TransactionTip extends TransactionCommon {
-  type: TransactionType.Tip;
-
-  // Raw amount of the tip, without extra fees that apply
-  amountRaw: AmountString;
-
-  // Amount will be (or was) added to the wallet's balance after fees and 
refreshing
-  amountEffective: AmountString;
-
-  merchantBaseUrl: string;
-}
-
-// A transaction shown for refreshes that are not associated to other 
transactions
-// such as a refresh necessary before coin expiration.
-// It should only be returned by the API if the effective amount is different 
from zero.
-interface TransactionRefresh extends TransactionCommon {
-  type: TransactionType.Refresh;
-
-  // Exchange that the coins are refreshed with
-  exchangeBaseUrl: string;
-
-  // Raw amount that is refreshed
-  amountRaw: AmountString;
-
-  // Amount that will be paid as fees for the refresh
-  amountEffective: AmountString;
-}
-
-export const codecForTransactionsRequest = (): Codec<TransactionsRequest> =>
-  buildCodecForObject<TransactionsRequest>()
-    .property("currency", codecOptional(codecForString()))
-    .property("search", codecOptional(codecForString()))
-    .build("TransactionsRequest");
-
-// FIXME: do full validation here!
-export const codecForTransactionsResponse = (): Codec<TransactionsResponse> =>
-  buildCodecForObject<TransactionsResponse>()
-    .property("transactions", codecForList(codecForAny()))
-    .build("TransactionsResponse");
-
-export const codecForOrderShortInfo = (): Codec<OrderShortInfo> =>
-  buildCodecForObject<OrderShortInfo>()
-    .property("contractTermsHash", codecForString())
-    .property("fulfillmentMessage", codecOptional(codecForString()))
-    .property(
-      "fulfillmentMessage_i18n",
-      codecOptional(codecForInternationalizedString()),
-    )
-    .property("fulfillmentUrl", codecOptional(codecForString()))
-    .property("merchant", codecForMerchantInfo())
-    .property("orderId", codecForString())
-    .property("products", codecOptional(codecForList(codecForProduct())))
-    .property("summary", codecForString())
-    .property("summary_i18n", codecOptional(codecForInternationalizedString()))
-    .build("OrderShortInfo");
diff --git a/packages/taler-wallet-core/src/types/walletTypes.ts 
b/packages/taler-wallet-core/src/types/walletTypes.ts
index d0e72b28..7dc675b3 100644
--- a/packages/taler-wallet-core/src/types/walletTypes.ts
+++ b/packages/taler-wallet-core/src/types/walletTypes.ts
@@ -55,7 +55,7 @@ import {
   codecForContractTerms,
   ContractTerms,
 } from "./talerTypes";
-import { OrderShortInfo, codecForOrderShortInfo } from "./transactions";
+import { OrderShortInfo, codecForOrderShortInfo } from "./transactionsTypes";
 
 /**
  * Response for the create reserve request to the wallet.
diff --git a/packages/taler-wallet-core/src/util/query.ts 
b/packages/taler-wallet-core/src/util/query.ts
index e1a23b16..35aab81e 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -583,7 +583,7 @@ export class Database {
   }
 
   async getIndexed<Ind extends Index<string, string, any, any>>(
-    index: InferIndex<Ind>,
+    index: Ind,
     key: IDBValidKey,
   ): Promise<IndexRecord<Ind> | undefined> {
     const tx = this.db.transaction([index.storeName], "readonly");
diff --git a/packages/taler-wallet-core/src/util/reserveHistoryUtil-test.ts 
b/packages/taler-wallet-core/src/util/reserveHistoryUtil-test.ts
deleted file mode 100644
index 79022de7..00000000
--- a/packages/taler-wallet-core/src/util/reserveHistoryUtil-test.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Imports.
- */
-import test from "ava";
-import {
-  reconcileReserveHistory,
-  summarizeReserveHistory,
-} from "./reserveHistoryUtil";
-import {
-  WalletReserveHistoryItem,
-  WalletReserveHistoryItemType,
-} from "../types/dbTypes";
-import {
-  ReserveTransaction,
-  ReserveTransactionType,
-} from "../types/ReserveTransaction";
-import { Amounts } from "./amounts";
-
-test("basics", (t) => {
-  const r = reconcileReserveHistory([], []);
-  t.deepEqual(r.updatedLocalHistory, []);
-});
-
-test("unmatched credit", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 1);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:100");
-  t.deepEqual(Amounts.stringify(s.awaitedReserveAmount), "TESTKUDOS:0");
-  t.deepEqual(Amounts.stringify(s.unclaimedReserveAmount), "TESTKUDOS:100");
-});
-
-test("unmatched credit #2", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:50",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC02",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:150");
-  t.deepEqual(Amounts.stringify(s.awaitedReserveAmount), "TESTKUDOS:0");
-  t.deepEqual(Amounts.stringify(s.unclaimedReserveAmount), "TESTKUDOS:150");
-});
-
-test("matched credit", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:100"),
-      matchedExchangeTransaction: {
-        type: ReserveTransactionType.Credit,
-        amount: "TESTKUDOS:100",
-        sender_account_url: "payto://void/",
-        timestamp: { t_ms: 42 },
-        wire_reference: "ABC01",
-      },
-    },
-  ];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:50",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC02",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:150");
-  t.deepEqual(Amounts.stringify(s.awaitedReserveAmount), "TESTKUDOS:0");
-  t.deepEqual(Amounts.stringify(s.unclaimedReserveAmount), "TESTKUDOS:150");
-});
-
-test("fulfilling credit", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:100"),
-    },
-  ];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:50",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC02",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:150");
-});
-
-test("unfulfilled credit", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:100"),
-    },
-  ];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:50",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC02",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:150");
-});
-
-test("awaited credit", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:50"),
-    },
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:100"),
-    },
-  ];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:100");
-  t.deepEqual(Amounts.stringify(s.awaitedReserveAmount), "TESTKUDOS:50");
-  t.deepEqual(Amounts.stringify(s.unclaimedReserveAmount), "TESTKUDOS:100");
-});
-
-test("withdrawal new match", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:100"),
-      matchedExchangeTransaction: {
-        type: ReserveTransactionType.Credit,
-        amount: "TESTKUDOS:100",
-        sender_account_url: "payto://void/",
-        timestamp: { t_ms: 42 },
-        wire_reference: "ABC01",
-      },
-    },
-    {
-      type: WalletReserveHistoryItemType.Withdraw,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:5"),
-    },
-  ];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-    {
-      type: ReserveTransactionType.Withdraw,
-      amount: "TESTKUDOS:5",
-      h_coin_envelope: "foobar",
-      h_denom_pub: "foobar",
-      reserve_sig: "foobar",
-      withdraw_fee: "TESTKUDOS:0.1",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:95");
-  t.deepEqual(Amounts.stringify(s.awaitedReserveAmount), "TESTKUDOS:0");
-  t.deepEqual(Amounts.stringify(s.unclaimedReserveAmount), "TESTKUDOS:95");
-});
-
-test("claimed but now arrived", (t) => {
-  const localHistory: WalletReserveHistoryItem[] = [
-    {
-      type: WalletReserveHistoryItemType.Credit,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:100"),
-      matchedExchangeTransaction: {
-        type: ReserveTransactionType.Credit,
-        amount: "TESTKUDOS:100",
-        sender_account_url: "payto://void/",
-        timestamp: { t_ms: 42 },
-        wire_reference: "ABC01",
-      },
-    },
-    {
-      type: WalletReserveHistoryItemType.Withdraw,
-      expectedAmount: Amounts.parseOrThrow("TESTKUDOS:5"),
-    },
-  ];
-  const remoteHistory: ReserveTransaction[] = [
-    {
-      type: ReserveTransactionType.Credit,
-      amount: "TESTKUDOS:100",
-      sender_account_url: "payto://void/",
-      timestamp: { t_ms: 42 },
-      wire_reference: "ABC01",
-    },
-  ];
-  const r = reconcileReserveHistory(localHistory, remoteHistory);
-  const s = summarizeReserveHistory(r.updatedLocalHistory, "TESTKUDOS");
-  t.deepEqual(r.updatedLocalHistory.length, 2);
-  t.deepEqual(Amounts.stringify(s.computedReserveBalance), "TESTKUDOS:100");
-  t.deepEqual(Amounts.stringify(s.awaitedReserveAmount), "TESTKUDOS:0");
-  t.deepEqual(Amounts.stringify(s.unclaimedReserveAmount), "TESTKUDOS:95");
-});
diff --git a/packages/taler-wallet-core/src/util/reserveHistoryUtil.ts 
b/packages/taler-wallet-core/src/util/reserveHistoryUtil.ts
deleted file mode 100644
index 60823e1e..00000000
--- a/packages/taler-wallet-core/src/util/reserveHistoryUtil.ts
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Helpers for dealing with reserve histories.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports.
- */
-import {
-  WalletReserveHistoryItem,
-  WalletReserveHistoryItemType,
-} from "../types/dbTypes";
-import {
-  ReserveTransaction,
-  ReserveTransactionType,
-} from "../types/ReserveTransaction";
-import * as Amounts from "../util/amounts";
-import { timestampCmp } from "./time";
-import { deepCopy } from "./helpers";
-import { AmountJson } from "../util/amounts";
-
-/**
- * Result of a reserve reconciliation.
- */
-export interface ReserveReconciliationResult {
-  /**
-   * The wallet's local history reconciled with the exchange's reserve history.
-   */
-  updatedLocalHistory: WalletReserveHistoryItem[];
-
-  /**
-   * History items that were newly created, subset of the
-   * updatedLocalHistory items.
-   */
-  newAddedItems: WalletReserveHistoryItem[];
-
-  /**
-   * History items that were newly matched, subset of the
-   * updatedLocalHistory items.
-   */
-  newMatchedItems: WalletReserveHistoryItem[];
-}
-
-/**
- * Various totals computed from the wallet's view
- * on the reserve history.
- */
-export interface ReserveHistorySummary {
-  /**
-   * Balance computed by the wallet, should match the balance
-   * computed by the reserve.
-   */
-  computedReserveBalance: Amounts.AmountJson;
-
-  /**
-   * Reserve balance that is still available for withdrawal.
-   */
-  unclaimedReserveAmount: Amounts.AmountJson;
-
-  /**
-   * Amount that we're still expecting to come into the reserve.
-   */
-  awaitedReserveAmount: Amounts.AmountJson;
-
-  /**
-   * Amount withdrawn from the reserve so far.  Only counts
-   * finished withdrawals, not withdrawals in progress.
-   */
-  withdrawnAmount: Amounts.AmountJson;
-}
-
-/**
- * Check if two reserve history items (exchange's version) match.
- */
-function isRemoteHistoryMatch(
-  t1: ReserveTransaction,
-  t2: ReserveTransaction,
-): boolean {
-  switch (t1.type) {
-    case ReserveTransactionType.Closing: {
-      return t1.type === t2.type && t1.wtid == t2.wtid;
-    }
-    case ReserveTransactionType.Credit: {
-      return t1.type === t2.type && t1.wire_reference === t2.wire_reference;
-    }
-    case ReserveTransactionType.Recoup: {
-      return (
-        t1.type === t2.type &&
-        t1.coin_pub === t2.coin_pub &&
-        timestampCmp(t1.timestamp, t2.timestamp) === 0
-      );
-    }
-    case ReserveTransactionType.Withdraw: {
-      return t1.type === t2.type && t1.h_coin_envelope === t2.h_coin_envelope;
-    }
-  }
-}
-
-/**
- * Check a local reserve history item and a remote history item are a match.
- */
-export function isLocalRemoteHistoryMatch(
-  t1: WalletReserveHistoryItem,
-  t2: ReserveTransaction,
-): boolean {
-  switch (t1.type) {
-    case WalletReserveHistoryItemType.Credit: {
-      return (
-        t2.type === ReserveTransactionType.Credit &&
-        !!t1.expectedAmount &&
-        Amounts.cmp(t1.expectedAmount, Amounts.parseOrThrow(t2.amount)) === 0
-      );
-    }
-    case WalletReserveHistoryItemType.Withdraw:
-      return (
-        t2.type === ReserveTransactionType.Withdraw &&
-        !!t1.expectedAmount &&
-        Amounts.cmp(t1.expectedAmount, Amounts.parseOrThrow(t2.amount)) === 0
-      );
-    case WalletReserveHistoryItemType.Recoup: {
-      return (
-        t2.type === ReserveTransactionType.Recoup &&
-        !!t1.expectedAmount &&
-        Amounts.cmp(t1.expectedAmount, Amounts.parseOrThrow(t2.amount)) === 0
-      );
-    }
-  }
-  return false;
-}
-
-/**
- * Compute totals for the wallet's view of the reserve history.
- */
-export function summarizeReserveHistory(
-  localHistory: WalletReserveHistoryItem[],
-  currency: string,
-): ReserveHistorySummary {
-  const posAmounts: AmountJson[] = [];
-  const negAmounts: AmountJson[] = [];
-  const expectedPosAmounts: AmountJson[] = [];
-  const expectedNegAmounts: AmountJson[] = [];
-  const withdrawnAmounts: AmountJson[] = [];
-
-  for (const item of localHistory) {
-    switch (item.type) {
-      case WalletReserveHistoryItemType.Credit:
-        if (item.matchedExchangeTransaction) {
-          posAmounts.push(
-            Amounts.parseOrThrow(item.matchedExchangeTransaction.amount),
-          );
-        } else if (item.expectedAmount) {
-          expectedPosAmounts.push(item.expectedAmount);
-        }
-        break;
-      case WalletReserveHistoryItemType.Recoup:
-        if (item.matchedExchangeTransaction) {
-          if (item.matchedExchangeTransaction) {
-            posAmounts.push(
-              Amounts.parseOrThrow(item.matchedExchangeTransaction.amount),
-            );
-          } else if (item.expectedAmount) {
-            expectedPosAmounts.push(item.expectedAmount);
-          } else {
-            throw Error("invariant failed");
-          }
-        }
-        break;
-      case WalletReserveHistoryItemType.Closing:
-        if (item.matchedExchangeTransaction) {
-          negAmounts.push(
-            Amounts.parseOrThrow(item.matchedExchangeTransaction.amount),
-          );
-        } else {
-          throw Error("invariant failed");
-        }
-        break;
-      case WalletReserveHistoryItemType.Withdraw:
-        if (item.matchedExchangeTransaction) {
-          negAmounts.push(
-            Amounts.parseOrThrow(item.matchedExchangeTransaction.amount),
-          );
-          withdrawnAmounts.push(
-            Amounts.parseOrThrow(item.matchedExchangeTransaction.amount),
-          );
-        } else if (item.expectedAmount) {
-          expectedNegAmounts.push(item.expectedAmount);
-        } else {
-          throw Error("invariant failed");
-        }
-        break;
-    }
-  }
-
-  const z = Amounts.getZero(currency);
-
-  const computedBalance = Amounts.sub(
-    Amounts.add(z, ...posAmounts).amount,
-    ...negAmounts,
-  ).amount;
-
-  const unclaimedReserveAmount = Amounts.sub(
-    Amounts.add(z, ...posAmounts).amount,
-    ...negAmounts,
-    ...expectedNegAmounts,
-  ).amount;
-
-  const awaitedReserveAmount = Amounts.sub(
-    Amounts.add(z, ...expectedPosAmounts).amount,
-    ...expectedNegAmounts,
-  ).amount;
-
-  const withdrawnAmount = Amounts.add(z, ...withdrawnAmounts).amount;
-
-  return {
-    computedReserveBalance: computedBalance,
-    unclaimedReserveAmount: unclaimedReserveAmount,
-    awaitedReserveAmount: awaitedReserveAmount,
-    withdrawnAmount,
-  };
-}
-
-/**
- * Reconcile the wallet's local model of the reserve history
- * with the reserve history of the exchange.
- */
-export function reconcileReserveHistory(
-  localHistory: WalletReserveHistoryItem[],
-  remoteHistory: ReserveTransaction[],
-): ReserveReconciliationResult {
-  const updatedLocalHistory: WalletReserveHistoryItem[] = deepCopy(
-    localHistory,
-  );
-  const newMatchedItems: WalletReserveHistoryItem[] = [];
-  const newAddedItems: WalletReserveHistoryItem[] = [];
-
-  const remoteMatched = remoteHistory.map(() => false);
-  const localMatched = localHistory.map(() => false);
-
-  // Take care of deposits
-
-  // First, see which pairs are already a definite match.
-  for (let remoteIndex = 0; remoteIndex < remoteHistory.length; remoteIndex++) 
{
-    const rhi = remoteHistory[remoteIndex];
-    for (let localIndex = 0; localIndex < localHistory.length; localIndex++) {
-      if (localMatched[localIndex]) {
-        continue;
-      }
-      const lhi = localHistory[localIndex];
-      if (!lhi.matchedExchangeTransaction) {
-        continue;
-      }
-      if (isRemoteHistoryMatch(rhi, lhi.matchedExchangeTransaction)) {
-        localMatched[localIndex] = true;
-        remoteMatched[remoteIndex] = true;
-        break;
-      }
-    }
-  }
-
-  // Check that all previously matched items are still matched
-  for (let localIndex = 0; localIndex < localHistory.length; localIndex++) {
-    if (localMatched[localIndex]) {
-      continue;
-    }
-    const lhi = localHistory[localIndex];
-    if (lhi.matchedExchangeTransaction) {
-      // Don't use for further matching
-      localMatched[localIndex] = true;
-      // FIXME: emit some error here!
-      throw Error("previously matched reserve history item now unmatched");
-    }
-  }
-
-  // Next, find out if there are any exact new matches between local and remote
-  // history items
-  for (let localIndex = 0; localIndex < localHistory.length; localIndex++) {
-    if (localMatched[localIndex]) {
-      continue;
-    }
-    const lhi = localHistory[localIndex];
-    for (
-      let remoteIndex = 0;
-      remoteIndex < remoteHistory.length;
-      remoteIndex++
-    ) {
-      const rhi = remoteHistory[remoteIndex];
-      if (remoteMatched[remoteIndex]) {
-        continue;
-      }
-      if (isLocalRemoteHistoryMatch(lhi, rhi)) {
-        localMatched[localIndex] = true;
-        remoteMatched[remoteIndex] = true;
-        updatedLocalHistory[localIndex].matchedExchangeTransaction = rhi as 
any;
-        newMatchedItems.push(lhi);
-        break;
-      }
-    }
-  }
-
-  // Finally we add new history items
-  for (let remoteIndex = 0; remoteIndex < remoteHistory.length; remoteIndex++) 
{
-    if (remoteMatched[remoteIndex]) {
-      continue;
-    }
-    const rhi = remoteHistory[remoteIndex];
-    let newItem: WalletReserveHistoryItem;
-    switch (rhi.type) {
-      case ReserveTransactionType.Closing: {
-        newItem = {
-          type: WalletReserveHistoryItemType.Closing,
-          matchedExchangeTransaction: rhi,
-        };
-        break;
-      }
-      case ReserveTransactionType.Credit: {
-        newItem = {
-          type: WalletReserveHistoryItemType.Credit,
-          matchedExchangeTransaction: rhi,
-        };
-        break;
-      }
-      case ReserveTransactionType.Recoup: {
-        newItem = {
-          type: WalletReserveHistoryItemType.Recoup,
-          matchedExchangeTransaction: rhi,
-        };
-        break;
-      }
-      case ReserveTransactionType.Withdraw: {
-        newItem = {
-          type: WalletReserveHistoryItemType.Withdraw,
-          matchedExchangeTransaction: rhi,
-        };
-        break;
-      }
-    }
-    updatedLocalHistory.push(newItem);
-    newAddedItems.push(newItem);
-  }
-
-  return {
-    updatedLocalHistory,
-    newAddedItems,
-    newMatchedItems,
-  };
-}
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 07af32bb..baafc63d 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -130,7 +130,7 @@ import {
   PendingOperationInfo,
   PendingOperationsResponse,
   PendingOperationType,
-} from "./types/pending";
+} from "./types/pendingTypes";
 import { WalletNotification, NotificationType } from "./types/notifications";
 import {
   processPurchaseQueryRefund,
@@ -148,7 +148,7 @@ import {
   TransactionsRequest,
   TransactionsResponse,
   codecForTransactionsRequest,
-} from "./types/transactions";
+} from "./types/transactionsTypes";
 import { getTransactions } from "./operations/transactions";
 import {
   withdrawTestBalance,
@@ -326,7 +326,7 @@ export class Wallet {
     } = {},
   ): Promise<void> {
     let done = false;
-    const p = new Promise((resolve, reject) => {
+    const p = new Promise<void>((resolve, reject) => {
       // Monitor for conditions that means we're done or we
       // should quit with an error (due to exceeded retries).
       this.addNotificationListener((n) => {

-- 
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]