commit 357f9d6eef887a5616c67c58dffaea78f1e5eb41
parent 5765db5ba77cd5ab8ad72f2ce8d117ee79bec809
Author: Antoine A <>
Date: Thu, 24 Apr 2025 09:39:01 +0200
wallet-core: improve peer-push-credit
Diffstat:
4 files changed, 82 insertions(+), 63 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-peer-push.ts b/packages/taler-harness/src/integrationtests/test-peer-push.ts
@@ -166,15 +166,16 @@ export async function runPeerPushTest(t: GlobalTestState) {
t.assertTrue(prepare2.transactionId === idempotent.transactionId);
}
- await wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, {
- transactionId: prepare2.transactionId,
- });
- await wallet3.call(WalletApiOperation.ConfirmPeerPushCredit, {
- transactionId: prepare3.transactionId,
- });
+ await Promise.all([
+ wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, {
+ transactionId: prepare2.transactionId,
+ }),
+ wallet3.call(WalletApiOperation.ConfirmPeerPushCredit, {
+ transactionId: prepare3.transactionId,
+ }),
+ ]);
// Idempotent
- // FIXME support conflict during POST /merge
await Promise.all([
wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, {
transactionId: prepare2.transactionId,
@@ -189,35 +190,43 @@ export async function runPeerPushTest(t: GlobalTestState) {
transactionId: tx.transactionId,
txState: {
major: TransactionMajorState.Done,
- },
- logId: `confirm-w1`,
- timeout: { seconds: 20 },
- }),
- wallet2.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: prepare2.transactionId,
- txState: {
- major: TransactionMajorState.Done,
- },
- logId: `confirm-w2`,
- timeout: { seconds: 20 },
- }),
- // FIXME should be aborted
- wallet3.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: prepare3.transactionId,
- txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.Merge,
- },
- logId: `confirm-w3`,
- timeout: { seconds: 20 },
+ }
}),
+ Promise.race([
+ Promise.all([
+ wallet2.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: prepare2.transactionId,
+ txState: {
+ major: TransactionMajorState.Done
+ }
+ }),
+ wallet3.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: prepare3.transactionId,
+ txState: {
+ major: TransactionMajorState.Aborted
+ }
+ }),
+ ]),
+ Promise.all([
+ wallet2.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: prepare2.transactionId,
+ txState: {
+ major: TransactionMajorState.Aborted,
+ }
+ }),
+ wallet3.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: prepare3.transactionId,
+ txState: {
+ major: TransactionMajorState.Done
+ }
+ }),
+ ])
+ ]),
wallet4.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: prepare4.transactionId,
txState: {
major: TransactionMajorState.Aborted,
- },
- logId: `confirm-w4`,
- timeout: { seconds: 20 },
+ }
}),
]);
@@ -253,13 +262,6 @@ export async function runPeerPushTest(t: GlobalTestState) {
transactionId: tx.transactionId,
});
- await wallet1.call(WalletApiOperation.TestingWaitTransactionState, {
- transactionId: tx.transactionId,
- txState: {
- major: TransactionMajorState.Aborted,
- },
- });
-
await wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, {
transactionId: prepare2.transactionId,
});
@@ -271,12 +273,10 @@ export async function runPeerPushTest(t: GlobalTestState) {
major: TransactionMajorState.Aborted,
},
}),
- // FIXME should be aborted
wallet2.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: prepare2.transactionId,
txState: {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.Merge,
+ major: TransactionMajorState.Aborted,
},
}),
wallet3.call(WalletApiOperation.TestingWaitTransactionState, {
@@ -332,26 +332,19 @@ export async function runPeerPushTest(t: GlobalTestState) {
transactionId: tx.transactionId,
txState: {
major: TransactionMajorState.Aborted,
- },
- logId: `exp-wallet1`,
- timeout: { seconds: 10 },
+ }
}),
- // FIXME should be aborted
wallet2.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: prepare2.transactionId,
txState: {
major: TransactionMajorState.Aborted,
- },
- logId: `exp-wallet2`,
- timeout: { seconds: 10 },
+ }
}),
wallet3.call(WalletApiOperation.TestingWaitTransactionState, {
transactionId: prepare3.transactionId,
txState: {
major: TransactionMajorState.Aborted,
- },
- logId: `exp-wallet3`,
- timeout: { seconds: 10 },
+ }
}),
]);
}
diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2022-2024 Taler Systems S.A.
+ (C) 2022-2025 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
@@ -32,9 +32,11 @@ import {
opEmptySuccess,
opFixedSuccess,
opKnownAlternativeFailure,
+ opKnownFailure,
opKnownHttpFailure,
opSuccessFromHttp,
opUnknownFailure,
+ opUnknownHttpFailure,
} from "../operation.js";
import { EddsaPrivP, encodeCrock } from "../taler-crypto.js";
import {
@@ -582,17 +584,19 @@ export class TalerExchangeHttpClient {
async postPurseMerge(args: {
pursePub: string;
body: ExchangePurseMergeRequest;
- cancellationToken?: CancellationToken;
+ cancellationToken: CancellationToken;
}): Promise<
| OperationOk<ExchangeMergeSuccessResponse>
| OperationAlternative<
- HttpStatusCode.UnavailableForLegalReasons,
- LegitimizationNeededResponse
- >
+ HttpStatusCode.UnavailableForLegalReasons,
+ LegitimizationNeededResponse
+ >
| OperationAlternative<
- HttpStatusCode.Conflict,
- ExchangeMergeConflictResponse
- >
+ HttpStatusCode.Conflict,
+ ExchangeMergeConflictResponse
+ >
+ | OperationFail<HttpStatusCode.NotFound>
+ | OperationFail<HttpStatusCode.Gone>
> {
const mergePurseUrl = new URL(
`purses/${args.pursePub}/merge`,
@@ -601,6 +605,7 @@ export class TalerExchangeHttpClient {
const resp = await this.httpLib.fetch(mergePurseUrl.href, {
method: "POST",
body: args.body,
+ cancellationToken: args.cancellationToken
});
switch (resp.status) {
case HttpStatusCode.Ok:
@@ -617,8 +622,11 @@ export class TalerExchangeHttpClient {
resp.status,
codecForExchangeMergeConflictResponse(),
);
+ case HttpStatusCode.Gone:
+ case HttpStatusCode.NotFound:
+ return opKnownHttpFailure(resp.status, resp);
default:
- return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+ return opUnknownHttpFailure(resp);
}
}
diff --git a/packages/taler-util/src/operation.ts b/packages/taler-util/src/operation.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2023-2024 Taler Systems S.A.
+ (C) 2023-2025 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
@@ -139,6 +139,13 @@ export async function opKnownHttpFailure<T extends HttpStatusCode>(
return { type: "fail", case: s, detail };
}
+export async function opUnknownHttpFailure(
+ resp: HttpResponse,
+): Promise<never> {
+ const detail = await readTalerErrorResponse(resp);
+ return opUnknownFailure(resp, detail)
+}
+
export function opKnownTalerFailure<T extends TalerErrorCode>(
s: T,
detail: TalerErrorDetail,
diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
@@ -317,7 +317,7 @@ export class PeerPushCreditTransactionContext implements TransactionContext {
amountEffective: isUnsuccessfulTransaction(txState)
? Amounts.stringify(Amounts.zeroOfAmount(peerContractTerms.amount))
: // FIXME: This is wrong, needs to consider fees!
- Amounts.stringify(peerContractTerms.amount),
+ Amounts.stringify(peerContractTerms.amount),
amountRaw: Amounts.stringify(peerContractTerms.amount),
exchangeBaseUrl: pushInc.exchangeBaseUrl,
info: {
@@ -922,11 +922,22 @@ async function handlePendingMerge(
}
case HttpStatusCode.Conflict: {
// FIXME: Check signature.
+ // FIXME: status completed by other
await ctx.transitionStatus(
PeerPushCreditStatus.PendingMerge,
PeerPushCreditStatus.Aborted,
);
- return TaskRunResult.progress();
+ return TaskRunResult.finished();
+ }
+ case HttpStatusCode.NotFound: {
+ await ctx.failTransaction(mergeResp.detail);
+ return TaskRunResult.finished();
+ }
+ case HttpStatusCode.Gone: {
+ // FIXME: status expired
+ ctx.abortTransaction()
+ await ctx.failTransaction(mergeResp.detail);
+ return TaskRunResult.finished();
}
default:
assertUnreachable(mergeResp);