aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-12-07 22:02:11 +0100
committerFlorian Dold <florian.dold@gmail.com>2019-12-07 22:02:11 +0100
commit396bb61db70f654599256e512bfec4c008ee8269 (patch)
treecfaba65d5d0c4782c7c64c28b68a41689cdbc07a
parentb68b52e95c126e0d3b9788d6332c19144f9dc7e7 (diff)
reset retry counter when forcing operations
-rw-r--r--src/wallet-impl/exchanges.ts11
-rw-r--r--src/wallet-impl/pay.ts99
-rw-r--r--src/wallet-impl/refresh.ts30
-rw-r--r--src/wallet-impl/tip.ts19
-rw-r--r--src/wallet-impl/withdraw.ts22
-rw-r--r--src/wallet.ts16
6 files changed, 164 insertions, 33 deletions
diff --git a/src/wallet-impl/exchanges.ts b/src/wallet-impl/exchanges.ts
index 3814971a3..42d626a71 100644
--- a/src/wallet-impl/exchanges.ts
+++ b/src/wallet-impl/exchanges.ts
@@ -20,7 +20,6 @@ import {
KeysJson,
Denomination,
ExchangeWireJson,
- WireFeesJson,
} from "../talerTypes";
import { getTimestampNow, OperationError } from "../walletTypes";
import {
@@ -313,11 +312,11 @@ async function updateExchangeWithWireInfo(
export async function updateExchangeFromUrl(
ws: InternalWalletState,
baseUrl: string,
- force: boolean = false,
+ forceNow: boolean = false,
): Promise<ExchangeRecord> {
const onOpErr = (e: OperationError) => setExchangeError(ws, baseUrl, e);
return await guardOperationException(
- () => updateExchangeFromUrlImpl(ws, baseUrl, force),
+ () => updateExchangeFromUrlImpl(ws, baseUrl, forceNow),
onOpErr,
);
}
@@ -330,7 +329,7 @@ export async function updateExchangeFromUrl(
async function updateExchangeFromUrlImpl(
ws: InternalWalletState,
baseUrl: string,
- force: boolean = false,
+ forceNow: boolean = false,
): Promise<ExchangeRecord> {
const now = getTimestampNow();
baseUrl = canonicalizeBaseUrl(baseUrl);
@@ -353,10 +352,10 @@ async function updateExchangeFromUrlImpl(
if (!rec) {
return;
}
- if (rec.updateStatus != ExchangeUpdateStatus.FETCH_KEYS && !force) {
+ if (rec.updateStatus != ExchangeUpdateStatus.FETCH_KEYS && !forceNow) {
return;
}
- if (rec.updateStatus != ExchangeUpdateStatus.FETCH_KEYS && force) {
+ if (rec.updateStatus != ExchangeUpdateStatus.FETCH_KEYS && forceNow) {
rec.updateReason = "forced";
}
rec.updateStarted = now;
diff --git a/src/wallet-impl/pay.ts b/src/wallet-impl/pay.ts
index 7f9e90327..d100ad26c 100644
--- a/src/wallet-impl/pay.ts
+++ b/src/wallet-impl/pay.ts
@@ -67,7 +67,11 @@ import {
} from "../util/helpers";
import { Logger } from "../util/logging";
import { InternalWalletState } from "./state";
-import { parsePayUri, parseRefundUri, getOrderDownloadUrl } from "../util/taleruri";
+import {
+ parsePayUri,
+ parseRefundUri,
+ getOrderDownloadUrl,
+} from "../util/taleruri";
import { getTotalRefreshCost, refresh } from "./refresh";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
import { guardOperationException } from "./errors";
@@ -540,19 +544,37 @@ async function incrementPurchaseApplyRefundRetry(
export async function processDownloadProposal(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean = false,
): Promise<void> {
const onOpErr = (err: OperationError) =>
incrementProposalRetry(ws, proposalId, err);
await guardOperationException(
- () => processDownloadProposalImpl(ws, proposalId),
+ () => processDownloadProposalImpl(ws, proposalId, forceNow),
onOpErr,
);
}
+async function resetDownloadProposalRetry(
+ ws: InternalWalletState,
+ proposalId: string,
+) {
+ await oneShotMutate(ws.db, Stores.proposals, proposalId, (x) => {
+ if (x.retryInfo.active) {
+ x.retryInfo = initRetryInfo();
+ }
+ return x;
+ });
+
+}
+
async function processDownloadProposalImpl(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean,
): Promise<void> {
+ if (forceNow) {
+ await resetDownloadProposalRetry(ws, proposalId);
+ }
const proposal = await oneShotGet(ws.db, Stores.proposals, proposalId);
if (!proposal) {
return;
@@ -560,8 +582,10 @@ async function processDownloadProposalImpl(
if (proposal.proposalStatus != ProposalStatus.DOWNLOADING) {
return;
}
-
- const parsedUrl = new URL(getOrderDownloadUrl(proposal.merchantBaseUrl, proposal.orderId));
+
+ const parsedUrl = new URL(
+ getOrderDownloadUrl(proposal.merchantBaseUrl, proposal.orderId),
+ );
parsedUrl.searchParams.set("nonce", proposal.noncePub);
const urlWithNonce = parsedUrl.href;
console.log("downloading contract from '" + urlWithNonce + "'");
@@ -714,12 +738,16 @@ export async function submitPay(
if (isFirst) {
const ar = purchase.contractTerms.auto_refund;
if (ar) {
+ console.log("auto_refund present");
const autoRefundDelay = extractTalerDuration(ar);
+ console.log("auto_refund valid", autoRefundDelay);
if (autoRefundDelay) {
purchase.refundStatusRequested = true;
+ purchase.refundStatusRetryInfo = initRetryInfo();
+ purchase.lastRefundStatusError = undefined;
purchase.autoRefundDeadline = {
t_ms: getTimestampNow().t_ms + autoRefundDelay.d_ms,
- }
+ };
}
}
}
@@ -1091,7 +1119,10 @@ async function acceptRefundResponse(
let queryDone = true;
if (numNewRefunds === 0) {
- if (p.autoRefundDeadline && p.autoRefundDeadline.t_ms < getTimestampNow().t_ms) {
+ if (
+ p.autoRefundDeadline &&
+ p.autoRefundDeadline.t_ms > getTimestampNow().t_ms
+ ) {
queryDone = false;
}
}
@@ -1101,12 +1132,14 @@ async function acceptRefundResponse(
p.lastRefundStatusError = undefined;
p.refundStatusRetryInfo = initRetryInfo();
p.refundStatusRequested = false;
+ console.log("refund query done");
} else {
// No error, but we need to try again!
p.lastRefundStatusTimestamp = getTimestampNow();
p.refundStatusRetryInfo.retryCounter++;
updateRetryInfoTimeout(p.refundStatusRetryInfo);
p.lastRefundStatusError = undefined;
+ console.log("refund query not done");
}
if (numNewRefunds) {
@@ -1137,8 +1170,6 @@ async function startRefundQuery(
console.log("no purchase found for refund URL");
return false;
}
- if (p.refundStatusRequested) {
- }
p.refundStatusRequested = true;
p.lastRefundStatusError = undefined;
p.refundStatusRetryInfo = initRetryInfo();
@@ -1193,19 +1224,36 @@ export async function applyRefund(
export async function processPurchasePay(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean = false,
): Promise<void> {
const onOpErr = (e: OperationError) =>
incrementPurchasePayRetry(ws, proposalId, e);
await guardOperationException(
- () => processPurchasePayImpl(ws, proposalId),
+ () => processPurchasePayImpl(ws, proposalId, forceNow),
onOpErr,
);
}
+async function resetPurchasePayRetry(
+ ws: InternalWalletState,
+ proposalId: string,
+) {
+ await oneShotMutate(ws.db, Stores.purchases, proposalId, (x) => {
+ if (x.payRetryInfo.active) {
+ x.payRetryInfo = initRetryInfo();
+ }
+ return x;
+ });
+}
+
async function processPurchasePayImpl(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean,
): Promise<void> {
+ if (forceNow) {
+ await resetPurchasePayRetry(ws, proposalId);
+ }
const purchase = await oneShotGet(ws.db, Stores.purchases, proposalId);
if (!purchase) {
return;
@@ -1220,19 +1268,34 @@ async function processPurchasePayImpl(
export async function processPurchaseQueryRefund(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean = false,
): Promise<void> {
const onOpErr = (e: OperationError) =>
incrementPurchaseQueryRefundRetry(ws, proposalId, e);
await guardOperationException(
- () => processPurchaseQueryRefundImpl(ws, proposalId),
+ () => processPurchaseQueryRefundImpl(ws, proposalId, forceNow),
onOpErr,
);
}
+
+async function resetPurchaseQueryRefundRetry(ws: InternalWalletState, proposalId: string) {
+ await oneShotMutate(ws.db, Stores.purchases, proposalId, x => {
+ if (x.refundStatusRetryInfo.active) {
+ x.refundStatusRetryInfo = initRetryInfo();
+ }
+ return x;
+ });
+}
+
async function processPurchaseQueryRefundImpl(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean,
): Promise<void> {
+ if (forceNow) {
+ await resetPurchaseQueryRefundRetry(ws, proposalId);
+ }
const purchase = await oneShotGet(ws.db, Stores.purchases, proposalId);
if (!purchase) {
return;
@@ -1262,19 +1325,33 @@ async function processPurchaseQueryRefundImpl(
export async function processPurchaseApplyRefund(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean = false,
): Promise<void> {
const onOpErr = (e: OperationError) =>
incrementPurchaseApplyRefundRetry(ws, proposalId, e);
await guardOperationException(
- () => processPurchaseApplyRefundImpl(ws, proposalId),
+ () => processPurchaseApplyRefundImpl(ws, proposalId, forceNow),
onOpErr,
);
}
+async function resetPurchaseApplyRefundRetry(ws: InternalWalletState, proposalId: string) {
+ await oneShotMutate(ws.db, Stores.purchases, proposalId, x => {
+ if (x.refundApplyRetryInfo.active) {
+ x.refundApplyRetryInfo = initRetryInfo();
+ }
+ return x;
+ });
+}
+
async function processPurchaseApplyRefundImpl(
ws: InternalWalletState,
proposalId: string,
+ forceNow: boolean,
): Promise<void> {
+ if (forceNow) {
+ await resetPurchaseApplyRefundRetry(ws, proposalId);
+ }
const purchase = await oneShotGet(ws.db, Stores.purchases, proposalId);
if (!purchase) {
console.error("not submitting refunds, payment not found:");
diff --git a/src/wallet-impl/refresh.ts b/src/wallet-impl/refresh.ts
index 93be1435d..a23f34324 100644
--- a/src/wallet-impl/refresh.ts
+++ b/src/wallet-impl/refresh.ts
@@ -38,7 +38,11 @@ import { InternalWalletState } from "./state";
import { Logger } from "../util/logging";
import { getWithdrawDenomList } from "./withdraw";
import { updateExchangeFromUrl } from "./exchanges";
-import { getTimestampNow, OperationError, NotificationType } from "../walletTypes";
+import {
+ getTimestampNow,
+ OperationError,
+ NotificationType,
+} from "../walletTypes";
import { guardOperationException } from "./errors";
const logger = new Logger("refresh.ts");
@@ -315,25 +319,41 @@ async function incrementRefreshRetry(
ws.notify({ type: NotificationType.RefreshOperationError });
}
-
export async function processRefreshSession(
ws: InternalWalletState,
refreshSessionId: string,
+ forceNow: boolean = false,
) {
return ws.memoProcessRefresh.memo(refreshSessionId, async () => {
const onOpErr = (e: OperationError) =>
incrementRefreshRetry(ws, refreshSessionId, e);
return guardOperationException(
- () => processRefreshSessionImpl(ws, refreshSessionId),
+ () => processRefreshSessionImpl(ws, refreshSessionId, forceNow),
onOpErr,
);
});
}
+async function resetRefreshSessionRetry(
+ ws: InternalWalletState,
+ refreshSessionId: string,
+) {
+ await oneShotMutate(ws.db, Stores.refresh, refreshSessionId, (x) => {
+ if (x.retryInfo.active) {
+ x.retryInfo = initRetryInfo();
+ }
+ return x;
+ });
+}
+
async function processRefreshSessionImpl(
ws: InternalWalletState,
refreshSessionId: string,
+ forceNow: boolean,
) {
+ if (forceNow) {
+ await resetRefreshSessionRetry(ws, refreshSessionId);
+ }
const refreshSession = await oneShotGet(
ws.db,
Stores.refresh,
@@ -413,7 +433,7 @@ export async function refresh(
x.status = CoinStatus.Dormant;
return x;
});
- ws.notify( { type: NotificationType.RefreshRefused });
+ ws.notify({ type: NotificationType.RefreshRefused });
return;
}
@@ -450,7 +470,7 @@ export async function refresh(
},
);
logger.info(`created refresh session ${refreshSession.refreshSessionId}`);
- ws.notify( { type: NotificationType.RefreshStarted });
+ ws.notify({ type: NotificationType.RefreshStarted });
await processRefreshSession(ws, refreshSession.refreshSessionId);
}
diff --git a/src/wallet-impl/tip.ts b/src/wallet-impl/tip.ts
index 11e029fcd..e11eb3b42 100644
--- a/src/wallet-impl/tip.ts
+++ b/src/wallet-impl/tip.ts
@@ -128,15 +128,32 @@ async function incrementTipRetry(
export async function processTip(
ws: InternalWalletState,
tipId: string,
+ forceNow: boolean = false,
): Promise<void> {
const onOpErr = (e: OperationError) => incrementTipRetry(ws, tipId, e);
- await guardOperationException(() => processTipImpl(ws, tipId), onOpErr);
+ await guardOperationException(() => processTipImpl(ws, tipId, forceNow), onOpErr);
+}
+
+async function resetTipRetry(
+ ws: InternalWalletState,
+ tipId: string,
+): Promise<void> {
+ await oneShotMutate(ws.db, Stores.tips, tipId, (x) => {
+ if (x.retryInfo.active) {
+ x.retryInfo = initRetryInfo();
+ }
+ return x;
+ })
}
async function processTipImpl(
ws: InternalWalletState,
tipId: string,
+ forceNow: boolean,
) {
+ if (forceNow) {
+ await resetTipRetry(ws, tipId);
+ }
let tipRecord = await oneShotGet(ws.db, Stores.tips, tipId);
if (!tipRecord) {
return;
diff --git a/src/wallet-impl/withdraw.ts b/src/wallet-impl/withdraw.ts
index 5d89f64a9..96055c9c5 100644
--- a/src/wallet-impl/withdraw.ts
+++ b/src/wallet-impl/withdraw.ts
@@ -45,6 +45,7 @@ import {
oneShotIterIndex,
oneShotGetIndexed,
runWithWriteTransaction,
+ oneShotMutate,
} from "../util/query";
import {
updateExchangeFromUrl,
@@ -516,20 +517,37 @@ async function incrementWithdrawalRetry(
export async function processWithdrawSession(
ws: InternalWalletState,
withdrawalSessionId: string,
+ forceNow: boolean = false,
): Promise<void> {
const onOpErr = (e: OperationError) =>
incrementWithdrawalRetry(ws, withdrawalSessionId, e);
await guardOperationException(
- () => processWithdrawSessionImpl(ws, withdrawalSessionId),
+ () => processWithdrawSessionImpl(ws, withdrawalSessionId, forceNow),
onOpErr,
);
}
-export async function processWithdrawSessionImpl(
+async function resetWithdrawSessionRetry(
ws: InternalWalletState,
withdrawalSessionId: string,
+) {
+ await oneShotMutate(ws.db, Stores.withdrawalSession, withdrawalSessionId, (x) => {
+ if (x.retryInfo.active) {
+ x.retryInfo = initRetryInfo();
+ }
+ return x;
+ });
+}
+
+async function processWithdrawSessionImpl(
+ ws: InternalWalletState,
+ withdrawalSessionId: string,
+ forceNow: boolean,
): Promise<void> {
logger.trace("processing withdraw session", withdrawalSessionId);
+ if (forceNow) {
+ await resetWithdrawSessionRetry(ws, withdrawalSessionId);
+ }
const withdrawalSession = await oneShotGet(
ws.db,
Stores.withdrawalSession,
diff --git a/src/wallet.ts b/src/wallet.ts
index a4fc09f7d..328baf722 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -191,34 +191,34 @@ export class Wallet {
await refresh(this.ws, pending.coinPub);
break;
case "exchange-update":
- await updateExchangeFromUrl(this.ws, pending.exchangeBaseUrl);
+ await updateExchangeFromUrl(this.ws, pending.exchangeBaseUrl, forceNow);
break;
case "refresh":
- await processRefreshSession(this.ws, pending.refreshSessionId);
+ await processRefreshSession(this.ws, pending.refreshSessionId, forceNow);
break;
case "reserve":
await processReserve(this.ws, pending.reservePub, forceNow);
break;
case "withdraw":
- await processWithdrawSession(this.ws, pending.withdrawSessionId);
+ await processWithdrawSession(this.ws, pending.withdrawSessionId, forceNow);
break;
case "proposal-choice":
// Nothing to do, user needs to accept/reject
break;
case "proposal-download":
- await processDownloadProposal(this.ws, pending.proposalId);
+ await processDownloadProposal(this.ws, pending.proposalId, forceNow);
break;
case "tip":
- await processTip(this.ws, pending.tipId);
+ await processTip(this.ws, pending.tipId, forceNow);
break;
case "pay":
- await processPurchasePay(this.ws, pending.proposalId);
+ await processPurchasePay(this.ws, pending.proposalId, forceNow);
break;
case "refund-query":
- await processPurchaseQueryRefund(this.ws, pending.proposalId);
+ await processPurchaseQueryRefund(this.ws, pending.proposalId, forceNow);
break;
case "refund-apply":
- await processPurchaseApplyRefund(this.ws, pending.proposalId);
+ await processPurchaseApplyRefund(this.ws, pending.proposalId, forceNow);
break;
default:
assertUnreachable(pending);