aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/refresh.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-06-26 19:27:34 +0200
committerFlorian Dold <florian@dold.me>2023-06-26 19:27:42 +0200
commita844136489611525726c117cb28086b854bee5c0 (patch)
treebb3384fab39600c35675cd8e36caac9246ef9782 /packages/taler-wallet-core/src/operations/refresh.ts
parent2779086a32a62d6d16b7813c2ca4944dc02c4d93 (diff)
downloadwallet-core-a844136489611525726c117cb28086b854bee5c0.tar.xz
wallet-core: make changes to available amount atomic
W.r.t. transactions
Diffstat (limited to 'packages/taler-wallet-core/src/operations/refresh.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts76
1 files changed, 51 insertions, 25 deletions
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index 9c9ad8bbd..b91872735 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -46,16 +46,20 @@ import {
NotificationType,
RefreshGroupId,
RefreshReason,
+ TalerError,
TalerErrorCode,
TalerErrorDetail,
TalerPreciseTimestamp,
- TalerProtocolTimestamp,
TransactionAction,
TransactionMajorState,
TransactionState,
TransactionType,
URL,
} from "@gnu-taler/taler-util";
+import {
+ readSuccessResponseJsonOrThrow,
+ readUnexpectedResponseDetails,
+} from "@gnu-taler/taler-util/http";
import { TalerCryptoInterface } from "../crypto/cryptoImplementation.js";
import {
DerivedRefreshSession,
@@ -72,25 +76,23 @@ import {
RefreshReasonDetails,
WalletStoresV1,
} from "../db.js";
-import { TalerError } from "@gnu-taler/taler-util";
+import { isWithdrawableDenom, PendingTaskType } from "../index.js";
import {
EXCHANGE_COINS_LOCK,
InternalWalletState,
} from "../internal-wallet-state.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
-import {
- readSuccessResponseJsonOrThrow,
- readUnexpectedResponseDetails,
-} from "@gnu-taler/taler-util/http";
+import { selectWithdrawalDenominations } from "../util/coinSelection.js";
import { checkDbInvariant } from "../util/invariants.js";
import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js";
-import { constructTaskIdentifier, makeCoinAvailable, OperationAttemptResult, OperationAttemptResultType } from "./common.js";
-import { updateExchangeFromUrl } from "./exchanges.js";
-import { selectWithdrawalDenominations } from "../util/coinSelection.js";
import {
- isWithdrawableDenom,
- PendingTaskType,
-} from "../index.js";
+ constructTaskIdentifier,
+ makeCoinAvailable,
+ makeCoinsVisible,
+ OperationAttemptResult,
+ OperationAttemptResultType,
+} from "./common.js";
+import { updateExchangeFromUrl } from "./exchanges.js";
import {
constructTransactionIdentifier,
notifyTransition,
@@ -144,24 +146,26 @@ export function getTotalRefreshCost(
return totalCost;
}
-function updateGroupStatus(rg: RefreshGroupRecord): void {
- const allDone = fnutil.all(
+function updateGroupStatus(rg: RefreshGroupRecord): { final: boolean } {
+ const allFinal = fnutil.all(
rg.statusPerCoin,
- (x) => x === RefreshCoinStatus.Finished || x === RefreshCoinStatus.Frozen,
+ (x) => x === RefreshCoinStatus.Finished || x === RefreshCoinStatus.Failed,
);
- const anyFrozen = fnutil.any(
+ const anyFailed = fnutil.any(
rg.statusPerCoin,
- (x) => x === RefreshCoinStatus.Frozen,
+ (x) => x === RefreshCoinStatus.Failed,
);
- if (allDone) {
- if (anyFrozen) {
+ if (allFinal) {
+ if (anyFailed) {
rg.timestampFinished = TalerPreciseTimestamp.now();
rg.operationStatus = RefreshOperationStatus.Failed;
} else {
rg.timestampFinished = TalerPreciseTimestamp.now();
rg.operationStatus = RefreshOperationStatus.Finished;
}
+ return { final: true };
}
+ return { final: false };
}
/**
@@ -248,22 +252,30 @@ async function refreshCreateSession(
ws.config.testing.denomselAllowLate,
);
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Refresh,
+ refreshGroupId,
+ });
+
if (newCoinDenoms.selectedDenoms.length === 0) {
logger.trace(
`not refreshing, available amount ${amountToPretty(
availableAmount,
)} too small`,
);
+ // FIXME: State transition notification missing.
await ws.db
- .mktx((x) => [x.coins, x.refreshGroups])
+ .mktx((x) => [x.coins, x.coinAvailability, x.refreshGroups])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
return;
}
rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Finished;
- updateGroupStatus(rg);
-
+ const updateRes = updateGroupStatus(rg);
+ if (updateRes.final) {
+ await makeCoinsVisible(ws, tx, transactionId);
+ }
await tx.refreshGroups.put(rg);
});
return;
@@ -418,10 +430,15 @@ async function refreshMelt(
});
});
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Refresh,
+ refreshGroupId,
+ });
+
if (resp.status === HttpStatusCode.NotFound) {
const errDetails = await readUnexpectedResponseDetails(resp);
await ws.db
- .mktx((x) => [x.refreshGroups])
+ .mktx((x) => [x.refreshGroups, x.coins, x.coinAvailability])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@@ -433,9 +450,12 @@ async function refreshMelt(
if (rg.statusPerCoin[coinIndex] !== RefreshCoinStatus.Pending) {
return;
}
- rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Frozen;
+ rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed;
rg.lastErrorPerCoin[coinIndex] = errDetails;
- updateGroupStatus(rg);
+ const updateRes = updateGroupStatus(rg);
+ if (updateRes.final) {
+ await makeCoinsVisible(ws, tx, transactionId);
+ }
await tx.refreshGroups.put(rg);
});
return;
@@ -672,6 +692,11 @@ async function refreshReveal(
const coins: CoinRecord[] = [];
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Refresh,
+ refreshGroupId,
+ });
+
for (let i = 0; i < refreshSession.newDenoms.length; i++) {
const ncd = newCoinDenoms[i];
for (let j = 0; j < refreshSession.newDenoms[i].count; j++) {
@@ -701,6 +726,7 @@ async function refreshReveal(
refreshGroupId,
oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
},
+ sourceTransactionId: transactionId,
coinEvHash: pc.coinEvHash,
maxAge: pc.maxAge,
ageCommitmentProof: pc.ageCommitmentProof,