summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-09-04 02:20:20 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-09-04 02:20:20 +0530
commit9ec6018efef9b45ee42ccda33ed7093881534141 (patch)
tree3b7b0a8bb78cb959531f965152c8307607a05d92 /packages/taler-wallet-core/src
parent54c0d1c2589951be26a83575b77dbb80f3a08b79 (diff)
downloadwallet-core-9ec6018efef9b45ee42ccda33ed7093881534141.tar.gz
wallet-core-9ec6018efef9b45ee42ccda33ed7093881534141.tar.bz2
wallet-core-9ec6018efef9b45ee42ccda33ed7093881534141.zip
test recoup, fix bug in reserve state machine, fix bug in recoup-refresh
Diffstat (limited to 'packages/taler-wallet-core/src')
-rw-r--r--packages/taler-wallet-core/src/db.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/recoup.ts9
-rw-r--r--packages/taler-wallet-core/src/operations/reserves.ts31
-rw-r--r--packages/taler-wallet-core/src/types/dbTypes.ts6
-rw-r--r--packages/taler-wallet-core/src/types/notifications.ts2
-rw-r--r--packages/taler-wallet-core/src/types/walletTypes.ts33
-rw-r--r--packages/taler-wallet-core/src/wallet.ts54
7 files changed, 121 insertions, 16 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index d5ebdb6c5..b3203935e 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-v9";
+const TALER_DB_NAME = "taler-walletdb-v10";
/**
* Current database minor version, should be incremented
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts
index 0e4ce18d3..91579f602 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -201,6 +201,7 @@ async function recoupWithdrawCoin(
const currency = updatedCoin.currentAmount.currency;
updatedCoin.currentAmount = Amounts.getZero(currency);
updatedReserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
+ updatedReserve.retryInfo = initRetryInfo();
await tx.put(Stores.coins, updatedCoin);
await tx.put(Stores.reserves, updatedReserve);
await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
@@ -253,7 +254,13 @@ async function recoupRefreshCoin(
}
await ws.db.runWithWriteTransaction(
- [Stores.coins, Stores.reserves, Stores.recoupGroups, Stores.refreshGroups],
+ [
+ Stores.coins,
+ Stores.denominations,
+ Stores.reserves,
+ Stores.recoupGroups,
+ Stores.refreshGroups,
+ ],
async (tx) => {
const recoupGroup = await tx.get(Stores.recoupGroups, recoupGroupId);
if (!recoupGroup) {
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts
index 439eb34a6..a28c2e0cf 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -74,6 +74,7 @@ import {
import {
reconcileReserveHistory,
summarizeReserveHistory,
+ ReserveHistorySummary,
} from "../util/reserveHistoryUtil";
import { TransactionHandle } from "../util/query";
import { addPaytoQueryParams } from "../util/payto";
@@ -162,6 +163,7 @@ export async function createReserve(
retryInfo: initRetryInfo(),
lastError: undefined,
currency: req.amount.currency,
+ requestedQuery: false,
};
const reserveHistoryRecord: ReserveHistoryRecord = {
@@ -285,13 +287,12 @@ export async function forceQueryReserve(
// Only force status query where it makes sense
switch (reserve.reserveStatus) {
case ReserveRecordStatus.DORMANT:
- case ReserveRecordStatus.WITHDRAWING:
- case ReserveRecordStatus.QUERYING_STATUS:
+ reserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
break;
default:
+ reserve.requestedQuery = true;
return;
}
- reserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
reserve.retryInfo = initRetryInfo();
await tx.put(Stores.reserves, reserve);
});
@@ -551,6 +552,7 @@ async function updateReserve(
const balance = Amounts.parseOrThrow(reserveInfo.balance);
const currency = balance.currency;
+ let updateSummary: ReserveHistorySummary | undefined;
await ws.db.runWithWriteTransaction(
[Stores.reserves, Stores.reserveUpdatedEvents, Stores.reserveHistory],
async (tx) => {
@@ -578,7 +580,7 @@ async function updateReserve(
reserveInfo.history,
);
- const summary = summarizeReserveHistory(
+ updateSummary = summarizeReserveHistory(
reconciled.updatedLocalHistory,
currency,
);
@@ -591,16 +593,24 @@ async function updateReserve(
reservePub: r.reservePub,
timestamp: getTimestampNow(),
amountReserveBalance: Amounts.stringify(balance),
- amountExpected: Amounts.stringify(summary.awaitedReserveAmount),
+ amountExpected: Amounts.stringify(updateSummary.awaitedReserveAmount),
newHistoryTransactions,
reserveUpdateId,
};
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate);
+ logger.trace("setting reserve status to 'withdrawing' after query");
r.reserveStatus = ReserveRecordStatus.WITHDRAWING;
r.retryInfo = initRetryInfo();
} else {
- r.reserveStatus = ReserveRecordStatus.DORMANT;
- r.retryInfo = initRetryInfo(false);
+ logger.trace("setting reserve status to 'dormant' after query");
+ if (r.requestedQuery) {
+ r.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
+ r.requestedQuery = false;
+ r.retryInfo = initRetryInfo();
+ } else {
+ r.reserveStatus = ReserveRecordStatus.DORMANT;
+ r.retryInfo = initRetryInfo(false);
+ }
}
r.lastSuccessfulStatusQuery = getTimestampNow();
hist.reserveTransactions = reconciled.updatedLocalHistory;
@@ -609,7 +619,11 @@ async function updateReserve(
await tx.put(Stores.reserveHistory, hist);
},
);
- ws.notify({ type: NotificationType.ReserveUpdated });
+ 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}`);
+ }
return { ready: true };
}
@@ -782,6 +796,7 @@ async function depleteReserve(
});
}
}
+ logger.trace("setting reserve status to dormant after depletion");
newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
newReserve.retryInfo = initRetryInfo(false);
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts
index 30a562822..45c19cbd0 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -321,6 +321,12 @@ export interface ReserveRecord {
reserveStatus: ReserveRecordStatus;
/**
+ * Was a reserve query requested? If so, query again instead
+ * of going into dormant status.
+ */
+ requestedQuery: boolean;
+
+ /**
* Time of the last successful status query.
*/
lastSuccessfulStatusQuery: Timestamp | undefined;
diff --git a/packages/taler-wallet-core/src/types/notifications.ts b/packages/taler-wallet-core/src/types/notifications.ts
index 7d3795a6d..7a51f0d83 100644
--- a/packages/taler-wallet-core/src/types/notifications.ts
+++ b/packages/taler-wallet-core/src/types/notifications.ts
@@ -24,6 +24,7 @@
*/
import { TalerErrorDetails } from "./walletTypes";
import { WithdrawalSource } from "./dbTypes";
+import { ReserveHistorySummary } from "../util/reserveHistoryUtil";
export enum NotificationType {
CoinWithdrawn = "coin-withdrawn",
@@ -126,6 +127,7 @@ export interface RefreshRefusedNotification {
export interface ReserveUpdatedNotification {
type: NotificationType.ReserveUpdated;
+ updateSummary?: ReserveHistorySummary;
}
export interface ReserveConfirmedNotification {
diff --git a/packages/taler-wallet-core/src/types/walletTypes.ts b/packages/taler-wallet-core/src/types/walletTypes.ts
index 5686ee61c..82f29c39d 100644
--- a/packages/taler-wallet-core/src/types/walletTypes.ts
+++ b/packages/taler-wallet-core/src/types/walletTypes.ts
@@ -691,6 +691,17 @@ export const codecForAddExchangeRequest = (): Codec<AddExchangeRequest> =>
.property("exchangeBaseUrl", codecForString())
.build("AddExchangeRequest");
+export interface ForceExchangeUpdateRequest {
+ exchangeBaseUrl: string;
+}
+
+export const codecForForceExchangeUpdateRequest = (): Codec<
+ AddExchangeRequest
+> =>
+ buildCodecForObject<AddExchangeRequest>()
+ .property("exchangeBaseUrl", codecForString())
+ .build("AddExchangeRequest");
+
export interface GetExchangeTosRequest {
exchangeBaseUrl: string;
}
@@ -870,3 +881,25 @@ export const codecForApplyRefundResponse = (): Codec<ApplyRefundResponse> =>
.property("pendingAtExchange", codecForBoolean())
.property("proposalId", codecForString())
.build("ApplyRefundResponse");
+
+export interface SetCoinSuspendedRequest {
+ coinPub: string;
+ suspended: boolean;
+}
+
+export const codecForSetCoinSuspendedRequest = (): Codec<
+ SetCoinSuspendedRequest
+> =>
+ buildCodecForObject<SetCoinSuspendedRequest>()
+ .property("coinPub", codecForString())
+ .property("suspended", codecForBoolean())
+ .build("SetCoinSuspendedRequest");
+
+export interface ForceRefreshRequest {
+ coinPubList: string[];
+}
+
+export const codecForForceRefreshRequest = (): Codec<ForceRefreshRequest> =>
+ buildCodecForObject<ForceRefreshRequest>()
+ .property("coinPubList", codecForList(codecForString()))
+ .build("ForceRefreshRequest");
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 21de541e5..9666665a4 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -90,6 +90,9 @@ import {
withdrawTestBalanceDefaults,
codecForWithdrawTestBalance,
codecForTestPayArgs,
+ codecForSetCoinSuspendedRequest,
+ codecForForceExchangeUpdateRequest,
+ codecForForceRefreshRequest,
} from "./types/walletTypes";
import { Logger } from "./util/logging";
@@ -110,7 +113,11 @@ import {
import { InternalWalletState } from "./operations/state";
import { createReserve } from "./operations/reserves";
-import { processRefreshGroup, createRefreshGroup, autoRefresh } from "./operations/refresh";
+import {
+ processRefreshGroup,
+ createRefreshGroup,
+ autoRefresh,
+} from "./operations/refresh";
import { processWithdrawGroup } from "./operations/withdraw";
import { getPendingOperations } from "./operations/pending";
import { getBalances } from "./operations/balance";
@@ -268,7 +275,7 @@ export class Wallet {
await processRecoupGroup(this.ws, pending.recoupGroupId, forceNow);
break;
case PendingOperationType.ExchangeCheckRefresh:
- await autoRefresh(this.ws, pending.exchangeBaseUrl)
+ await autoRefresh(this.ws, pending.exchangeBaseUrl);
break;
default:
assertUnreachable(pending);
@@ -371,7 +378,8 @@ export class Wallet {
}
private async runRetryLoopImpl(): Promise<void> {
- while (!this.stopped) {
+ let iteration = 0;
+ for (; !this.stopped; iteration++) {
const pending = await this.getPendingOperations({ onlyDue: true });
let numDueAndLive = 0;
for (const p of pending.pendingOperations) {
@@ -379,7 +387,9 @@ export class Wallet {
numDueAndLive++;
}
}
- if (numDueAndLive === 0) {
+ // Make sure that we run tasks that don't give lifeness at least
+ // one time.
+ if (iteration !== 0 && numDueAndLive === 0) {
const allPending = await this.getPendingOperations({ onlyDue: false });
let numPending = 0;
let numGivingLiveness = 0;
@@ -406,11 +416,12 @@ export class Wallet {
numPending,
});
await Promise.race([timeout, this.latch.wait()]);
- logger.trace("timeout done");
} else {
// FIXME: maybe be a bit smarter about executing these
// operations in parallel?
- logger.trace(`running ${pending.pendingOperations.length} pending operations`);
+ logger.trace(
+ `running ${pending.pendingOperations.length} pending operations`,
+ );
for (const p of pending.pendingOperations) {
try {
await this.processOnePendingOperation(p);
@@ -985,6 +996,11 @@ export class Wallet {
await this.updateExchangeFromUrl(req.exchangeBaseUrl);
return {};
}
+ case "forceUpdateExchange": {
+ const req = codecForForceExchangeUpdateRequest().decode(payload);
+ await this.updateExchangeFromUrl(req.exchangeBaseUrl, true);
+ return {};
+ }
case "listExchanges": {
return await this.getExchanges();
}
@@ -1054,6 +1070,32 @@ export class Wallet {
const req = codecForConfirmPayRequest().decode(payload);
return await this.confirmPay(req.proposalId, req.sessionId);
}
+ case "dumpCoins": {
+ return await this.dumpCoins();
+ }
+ case "setCoinSuspended": {
+ const req = codecForSetCoinSuspendedRequest().decode(payload);
+ await this.setCoinSuspended(req.coinPub, req.suspended);
+ return {};
+ }
+ case "forceRefresh": {
+ const req = codecForForceRefreshRequest().decode(payload);
+ const coinPubs = req.coinPubList.map((x) => ({ coinPub: x }));
+ const refreshGroupId = await this.db.runWithWriteTransaction(
+ [Stores.refreshGroups, Stores.denominations, Stores.coins],
+ async (tx) => {
+ return await createRefreshGroup(
+ this.ws,
+ tx,
+ coinPubs,
+ RefreshReason.Manual,
+ );
+ },
+ );
+ return {
+ refreshGroupId,
+ };
+ }
}
throw OperationFailedError.fromCode(
TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN,