summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-12-18 19:25:26 +0100
committerFlorian Dold <florian@dold.me>2023-12-18 19:25:26 +0100
commit12a9b08c6f8c31f684239a30fc39acc9189c6571 (patch)
tree3755eb1a6b7659d44059ddb4cf85c832a8f88a8e /packages/taler-wallet-core/src/operations
parenta488ce70d6dbfe08845eccaeb2375b367f7c307a (diff)
downloadwallet-core-12a9b08c6f8c31f684239a30fc39acc9189c6571.tar.gz
wallet-core-12a9b08c6f8c31f684239a30fc39acc9189c6571.tar.bz2
wallet-core-12a9b08c6f8c31f684239a30fc39acc9189c6571.zip
wallet-core: towards properly handling peer-pull-debit expiry
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts168
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts23
3 files changed, 166 insertions, 27 deletions
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index da779a07d..c79aca1ad 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -365,7 +365,7 @@ async function processPeerPushDebitAbortingDeletePurse(
coinPubs,
RefreshReason.AbortPeerPushDebit,
);
- ppiRec.status = PeerPushDebitStatus.AbortingRefresh;
+ ppiRec.status = PeerPushDebitStatus.AbortingRefreshDeleted;
ppiRec.abortRefreshGroupId = refresh.refreshGroupId;
await tx.peerPushDebit.put(ppiRec);
const newTxState = computePeerPushDebitTransactionState(ppiRec);
@@ -415,7 +415,7 @@ async function transitionPeerPushDebitTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
-async function processPeerPushDebitAbortingRefresh(
+async function processPeerPushDebitAbortingRefreshDeleted(
ws: InternalWalletState,
peerPushInitiation: PeerPushDebitRecord,
): Promise<TaskRunResult> {
@@ -463,6 +463,54 @@ async function processPeerPushDebitAbortingRefresh(
return TaskRunResult.pending();
}
+async function processPeerPushDebitAbortingRefreshExpired(
+ ws: InternalWalletState,
+ peerPushInitiation: PeerPushDebitRecord,
+): Promise<TaskRunResult> {
+ const pursePub = peerPushInitiation.pursePub;
+ const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId;
+ checkLogicInvariant(!!abortRefreshGroupId);
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.PeerPushDebit,
+ pursePub: peerPushInitiation.pursePub,
+ });
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.refreshGroups, x.peerPushDebit])
+ .runReadWrite(async (tx) => {
+ const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId);
+ let newOpState: PeerPushDebitStatus | undefined;
+ if (!refreshGroup) {
+ // Maybe it got manually deleted? Means that we should
+ // just go into failed.
+ logger.warn("no aborting refresh group found for deposit group");
+ newOpState = PeerPushDebitStatus.Failed;
+ } else {
+ if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) {
+ newOpState = PeerPushDebitStatus.Expired;
+ } else if (
+ refreshGroup.operationStatus === RefreshOperationStatus.Failed
+ ) {
+ newOpState = PeerPushDebitStatus.Failed;
+ }
+ }
+ if (newOpState) {
+ const newDg = await tx.peerPushDebit.get(pursePub);
+ if (!newDg) {
+ return;
+ }
+ const oldTxState = computePeerPushDebitTransactionState(newDg);
+ newDg.status = newOpState;
+ const newTxState = computePeerPushDebitTransactionState(newDg);
+ await tx.peerPushDebit.put(newDg);
+ return { oldTxState, newTxState };
+ }
+ return undefined;
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
+ // FIXME: Shouldn't this be finished in some cases?!
+ return TaskRunResult.pending();
+}
+
/**
* Process the "pending(ready)" state of a peer-push-debit transaction.
*/
@@ -476,6 +524,10 @@ async function processPeerPushDebitReady(
tag: PendingTaskType.PeerPushDebit,
pursePub,
});
+ const transactionId = constructTaskIdentifier({
+ tag: PendingTaskType.PeerPushDebit,
+ pursePub,
+ });
runLongpollAsync(ws, retryTag, async (ct) => {
const mergeUrl = new URL(
`purses/${pursePub}/merge`,
@@ -510,14 +562,50 @@ async function processPeerPushDebitReady(
};
}
} else if (resp.status === HttpStatusCode.Gone) {
- await transitionPeerPushDebitTransaction(
- ws,
- peerPushInitiation.pursePub,
- {
- stFrom: PeerPushDebitStatus.PendingReady,
- stTo: PeerPushDebitStatus.Expired,
- },
- );
+ const transitionInfo = await ws.db
+ .mktx((x) => [
+ x.peerPushDebit,
+ x.refreshGroups,
+ x.denominations,
+ x.coinAvailability,
+ x.coins,
+ ])
+ .runReadWrite(async (tx) => {
+ const ppiRec = await tx.peerPushDebit.get(pursePub);
+ if (!ppiRec) {
+ return undefined;
+ }
+ if (ppiRec.status !== PeerPushDebitStatus.PendingReady) {
+ return undefined;
+ }
+ const currency = Amounts.currencyOf(ppiRec.amount);
+ const oldTxState = computePeerPushDebitTransactionState(ppiRec);
+ const coinPubs: CoinRefreshRequest[] = [];
+
+ for (let i = 0; i < ppiRec.coinSel.coinPubs.length; i++) {
+ coinPubs.push({
+ amount: ppiRec.coinSel.contributions[i],
+ coinPub: ppiRec.coinSel.coinPubs[i],
+ });
+ }
+
+ const refresh = await createRefreshGroup(
+ ws,
+ tx,
+ currency,
+ coinPubs,
+ RefreshReason.AbortPeerPushDebit,
+ );
+ ppiRec.status = PeerPushDebitStatus.AbortingRefreshExpired;
+ ppiRec.abortRefreshGroupId = refresh.refreshGroupId;
+ await tx.peerPushDebit.put(ppiRec);
+ const newTxState = computePeerPushDebitTransactionState(ppiRec);
+ return {
+ oldTxState,
+ newTxState,
+ };
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
return {
ready: true,
};
@@ -569,8 +657,10 @@ export async function processPeerPushDebit(
return processPeerPushDebitReady(ws, peerPushInitiation);
case PeerPushDebitStatus.AbortingDeletePurse:
return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation);
- case PeerPushDebitStatus.AbortingRefresh:
- return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation);
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ return processPeerPushDebitAbortingRefreshDeleted(ws, peerPushInitiation);
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ return processPeerPushDebitAbortingRefreshExpired(ws, peerPushInitiation);
default: {
const txState = computePeerPushDebitTransactionState(peerPushInitiation);
logger.warn(
@@ -722,11 +812,15 @@ export function computePeerPushDebitTransactionActions(
return [TransactionAction.Delete];
case PeerPushDebitStatus.AbortingDeletePurse:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
return [TransactionAction.Suspend, TransactionAction.Fail];
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ return [TransactionAction.Resume, TransactionAction.Fail];
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushDebitStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
@@ -773,9 +867,11 @@ export async function abortPeerPushDebitTransaction(
// Network request might already be in-flight!
newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ case PeerPushDebitStatus.AbortingRefreshExpired:
case PeerPushDebitStatus.Done:
case PeerPushDebitStatus.AbortingDeletePurse:
case PeerPushDebitStatus.Aborted:
@@ -824,13 +920,15 @@ export async function failPeerPushDebitTransaction(
}
let newStatus: PeerPushDebitStatus | undefined = undefined;
switch (pushDebitRec.status) {
- case PeerPushDebitStatus.AbortingRefresh:
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
// FIXME: What to do about the refresh group?
newStatus = PeerPushDebitStatus.Failed;
break;
case PeerPushDebitStatus.AbortingDeletePurse:
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
case PeerPushDebitStatus.PendingReady:
case PeerPushDebitStatus.SuspendedReady:
case PeerPushDebitStatus.SuspendedCreatePurse:
@@ -887,8 +985,11 @@ export async function suspendPeerPushDebitTransaction(
case PeerPushDebitStatus.PendingCreatePurse:
newStatus = PeerPushDebitStatus.SuspendedCreatePurse;
break;
- case PeerPushDebitStatus.AbortingRefresh:
- newStatus = PeerPushDebitStatus.SuspendedAbortingRefresh;
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ newStatus = PeerPushDebitStatus.SuspendedAbortingRefreshDeleted;
+ break;
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ newStatus = PeerPushDebitStatus.SuspendedAbortingRefreshExpired;
break;
case PeerPushDebitStatus.AbortingDeletePurse:
newStatus = PeerPushDebitStatus.SuspendedAbortingDeletePurse;
@@ -897,7 +998,8 @@ export async function suspendPeerPushDebitTransaction(
newStatus = PeerPushDebitStatus.SuspendedReady;
break;
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
case PeerPushDebitStatus.SuspendedReady:
case PeerPushDebitStatus.SuspendedCreatePurse:
case PeerPushDebitStatus.Done:
@@ -950,8 +1052,11 @@ export async function resumePeerPushDebitTransaction(
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
- newStatus = PeerPushDebitStatus.AbortingRefresh;
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
+ newStatus = PeerPushDebitStatus.AbortingRefreshDeleted;
+ break;
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ newStatus = PeerPushDebitStatus.AbortingRefreshExpired;
break;
case PeerPushDebitStatus.SuspendedReady:
newStatus = PeerPushDebitStatus.PendingReady;
@@ -960,7 +1065,8 @@ export async function resumePeerPushDebitTransaction(
newStatus = PeerPushDebitStatus.PendingCreatePurse;
break;
case PeerPushDebitStatus.PendingCreatePurse:
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
+ case PeerPushDebitStatus.AbortingRefreshExpired:
case PeerPushDebitStatus.AbortingDeletePurse:
case PeerPushDebitStatus.PendingReady:
case PeerPushDebitStatus.Done:
@@ -1011,17 +1117,27 @@ export function computePeerPushDebitTransactionState(
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefreshDeleted:
return {
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.Refresh,
};
+ case PeerPushDebitStatus.AbortingRefreshExpired:
+ return {
+ major: TransactionMajorState.Aborting,
+ minor: TransactionMinorState.RefreshExpired,
+ };
case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
return {
major: TransactionMajorState.SuspendedAborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefreshExpired:
+ return {
+ major: TransactionMajorState.SuspendedAborting,
+ minor: TransactionMinorState.RefreshExpired,
+ };
+ case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted:
return {
major: TransactionMajorState.SuspendedAborting,
minor: TransactionMinorState.Refresh,
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index a9d6c5595..233ca3fa4 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -640,7 +640,7 @@ export async function iterRecordsForPeerPushInitiation(
if (filter.onlyState === "nonfinal") {
const keyRange = GlobalIDB.KeyRange.bound(
PeerPushDebitStatus.PendingCreatePurse,
- PeerPushDebitStatus.AbortingRefresh,
+ PeerPushDebitStatus.AbortingRefreshDeleted,
);
await tx.peerPushDebit.indexes.byStatus.iter(keyRange).forEachAsync(f);
} else {
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index c58f03e05..3bbbc2a4b 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -528,6 +528,29 @@ async function refreshMelt(
derived.meltValueWithFee,
)} failed in refresh group ${refreshGroupId} due to conflict`,
);
+
+ const historySig = await ws.cryptoApi.signCoinHistoryRequest({
+ coinPriv: oldCoin.coinPriv,
+ coinPub: oldCoin.coinPub,
+ startOffset: 0,
+ });
+
+ const historyUrl = new URL(
+ `coins/${oldCoin.coinPub}/history`,
+ oldCoin.exchangeBaseUrl,
+ );
+
+ const historyResp = await ws.http.fetch(historyUrl.href, {
+ method: "GET",
+ headers: {
+ "Taler-Coin-History-Signature": historySig.sig,
+ },
+ });
+
+ const historyJson = await historyResp.json();
+ logger.info(`coin history: ${j2s(historyJson)}`);
+
+ // FIXME: Before failing and re-trying, analyse response and adjust amount
}
const meltResponse = await readSuccessResponseJsonOrThrow(