commit cd439e8e793b5a49ba14cdc18119063731c18df8
parent 4dfd246f086a3cdde84a74184ba3d2deb436ce10
Author: Florian Dold <florian@dold.me>
Date: Wed, 9 Jul 2025 01:56:08 +0200
wallet-core,harness: fix DD64 issues, make tests pass
Diffstat:
9 files changed, 172 insertions(+), 56 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts b/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts
@@ -179,8 +179,11 @@ export async function runExchangeKycAuthTest(t: GlobalTestState) {
});
console.log(j2s(checkResp1));
-
- t.assertDeepEqual(checkResp1.case, HttpStatusCode.Accepted);
+ // Time-sensitive which status will be returned.
+ t.assertTrue(
+ checkResp1.case === HttpStatusCode.Accepted ||
+ checkResp1.case === HttpStatusCode.Ok,
+ );
}
const reservePair2 = createEddsaKeyPair();
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-form-withdrawal.ts b/packages/taler-harness/src/integrationtests/test-kyc-form-withdrawal.ts
@@ -142,6 +142,7 @@ export async function runKycFormWithdrawalTest(t: GlobalTestState) {
body: {
FULL_NAME: "Alice Abc",
DATE_OF_BIRTH: "2000-01-01",
+ FORM_ID: "test",
},
},
);
diff --git a/packages/taler-harness/src/integrationtests/test-tops-peer.ts b/packages/taler-harness/src/integrationtests/test-tops-peer.ts
@@ -185,11 +185,20 @@ export async function runTopsPeerTest(t: GlobalTestState) {
console.log(j2s(infoResp2));
if (
- infoResp2.case === "ok" &&
- infoResp2.body.requirements[0].form === "accept-tos"
+ !(
+ infoResp2.case === "ok" &&
+ infoResp2.body.requirements[0].form === "accept-tos"
+ )
) {
- t.fail("requirements include ToS, but client wants to do p2p");
+ t.fail("requirements need to include ToS (due to zero measure)");
}
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: prepareResp.transactionId,
+ txState: {
+ major: TransactionMajorState.Done,
+ },
+ });
}
runTopsPeerTest.suites = ["wallet"];
diff --git a/packages/taler-util/src/types-taler-wallet-transactions.ts b/packages/taler-util/src/types-taler-wallet-transactions.ts
@@ -193,6 +193,7 @@ export enum TransactionMinorState {
Unknown = "unknown",
Deposit = "deposit",
KycRequired = "kyc",
+ KycInit = "kyc-init",
MergeKycRequired = "merge-kyc",
BalanceKycRequired = "balance-kyc",
BalanceKycInit = "balance-kyc-init",
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
@@ -629,20 +629,34 @@ export function computeDepositTransactionStatus(
minor: TransactionMinorState.Deposit,
};
case DepositOperationStatus.PendingAggregateKyc:
- return {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.KycRequired,
- };
+ if (dg.kycInfo?.accessToken != null) {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case DepositOperationStatus.LegacyPendingTrack:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Track,
};
case DepositOperationStatus.SuspendedAggregateKyc:
- return {
- major: TransactionMajorState.Suspended,
- minor: TransactionMinorState.KycRequired,
- };
+ if (dg.kycInfo?.accessToken != null) {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case DepositOperationStatus.LegacySuspendedTrack:
return {
major: TransactionMajorState.Suspended,
@@ -675,17 +689,31 @@ export function computeDepositTransactionStatus(
major: TransactionMajorState.SuspendedAborting,
};
case DepositOperationStatus.PendingDepositKyc:
- return {
- major: TransactionMajorState.Pending,
- // We lie to the UI by hiding the specific KYC state.
- minor: TransactionMinorState.KycRequired,
- };
+ if (dg.kycInfo?.accessToken != null) {
+ return {
+ major: TransactionMajorState.Pending,
+ // We lie to the UI by hiding the specific KYC state.
+ minor: TransactionMinorState.KycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case DepositOperationStatus.SuspendedDepositKyc:
- return {
- major: TransactionMajorState.Suspended,
- // We lie to the UI by hiding the specific KYC state.
- minor: TransactionMinorState.KycRequired,
- };
+ if (dg.kycInfo?.accessToken != null) {
+ return {
+ major: TransactionMajorState.Suspended,
+ // We lie to the UI by hiding the specific KYC state.
+ minor: TransactionMinorState.KycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case DepositOperationStatus.PendingDepositKycAuth:
return {
major: TransactionMajorState.Pending,
@@ -1074,6 +1102,7 @@ async function processDepositGroupPendingKyc(
lastCheckStatus: maybeKycInfo.lastCheckStatus,
lastDeny: maybeKycInfo.lastDeny,
lastRuleGen: maybeKycInfo.lastRuleGen,
+ haveAccessToken: maybeKycInfo.accessToken != null,
};
}
@@ -1111,6 +1140,7 @@ async function processDepositGroupPendingKyc(
kycInfo.lastCheckCode = algoRes.updatedStatus.lastCheckCode;
kycInfo.lastDeny = algoRes.updatedStatus.lastDeny;
kycInfo.lastRuleGen = algoRes.updatedStatus.lastRuleGen;
+ kycInfo.accessToken = algoRes.updatedStatus.accessToken;
const requiresAuth = algoRes.requiresAuth;
diff --git a/packages/taler-wallet-core/src/kyc.ts b/packages/taler-wallet-core/src/kyc.ts
@@ -324,6 +324,7 @@ export interface GenericKycStatusReq {
readonly lastRuleGen?: number | undefined;
readonly lastAmlReview?: boolean | undefined;
readonly lastDeny?: DbPreciseTimestamp | undefined;
+ readonly haveAccessToken: boolean;
}
export interface GenericKycStatusResp {
@@ -331,6 +332,7 @@ export interface GenericKycStatusResp {
taskResult: TaskRunResult;
requiresAuth?: boolean;
updatedStatus?: {
+ accessToken?: string;
lastCheckStatus?: number | undefined;
lastCheckCode?: number | undefined;
lastRuleGen?: number | undefined;
@@ -349,7 +351,7 @@ export function isKycOperationDue(st: GenericKycStatusReq): boolean {
AbsoluteTime.isExpired(
AbsoluteTime.addDuration(
timestampAbsoluteFromDb(st.lastDeny),
- Duration.fromSpec({ minutes: 2 }),
+ Duration.fromSpec({ minutes: 30 }),
),
)
);
@@ -381,11 +383,11 @@ export async function runKycCheckAlgo(
let doLongpoll: boolean;
- if (st.lastCheckCode == null) {
+ if (st.lastCheckStatus == null || !st.haveAccessToken) {
doLongpoll = false;
} else if (
st.lastCheckStatus === HttpStatusCode.Forbidden ||
- st.lastCheckCode === HttpStatusCode.Conflict
+ st.lastCheckStatus === HttpStatusCode.Conflict
) {
doLongpoll = true;
url.searchParams.set("lpt", "1");
@@ -458,7 +460,13 @@ export async function runKycCheckAlgo(
updatedStatus.lastDeny = undefined;
break;
case HttpStatusCode.Ok: {
+ const resp = await readSuccessResponseJsonOrThrow(
+ kycStatusRes,
+ codecForAccountKycStatus(),
+ );
+ updatedStatus.lastRuleGen = resp.rule_gen;
updatedStatus.lastDeny = undefined;
+ updatedStatus.accessToken = resp.access_token;
break;
}
case HttpStatusCode.Accepted:
@@ -466,6 +474,8 @@ export async function runKycCheckAlgo(
kycStatusRes,
codecForAccountKycStatus(),
);
+ updatedStatus.accessToken = resp.access_token;
+ updatedStatus.lastRuleGen = resp.rule_gen;
exposedLimits = resp.limits;
rst.taskResult = TaskRunResult.longpollReturnedPending();
break;
@@ -481,7 +491,9 @@ export async function runKycCheckAlgo(
}
if (exposedLimits) {
- switch (await checkLimit(exposedLimits, "DEPOSIT", st.amount)) {
+ const checkRes = await checkLimit(exposedLimits, st.operation, st.amount);
+ logger.trace(`limit check result: ${LimitCheckResult[checkRes]}`);
+ switch (checkRes) {
case LimitCheckResult.Allowed:
updatedStatus.lastDeny = undefined;
break;
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
@@ -567,6 +567,7 @@ async function processPendingMergeKycRequired(
lastCheckStatus: pullIni.kycLastCheckStatus,
lastDeny: pullIni.kycLastDeny,
lastRuleGen: pullIni.kycLastRuleGen,
+ haveAccessToken: pullIni.kycAccessToken != null,
};
}
@@ -590,6 +591,7 @@ async function processPendingMergeKycRequired(
rec.kycLastCheckCode = updatedStatus.lastCheckCode;
rec.kycLastDeny = updatedStatus.lastDeny;
rec.kycLastRuleGen = updatedStatus.lastRuleGen;
+ rec.kycAccessToken = updatedStatus.accessToken;
return TransitionResultType.Transition;
});
return algoRes.taskResult;
@@ -1141,10 +1143,29 @@ export function computePeerPullCreditTransactionState(
minor: TransactionMinorState.CreatePurse,
};
case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
- return {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.MergeKycRequired,
- };
+ if (pullCreditRecord.kycAccessToken != null) {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.MergeKycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ if (pullCreditRecord.kycAccessToken != null) {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.MergeKycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case PeerPullPaymentCreditStatus.PendingReady:
return {
major: TransactionMajorState.Pending,
@@ -1174,11 +1195,6 @@ export function computePeerPullCreditTransactionState(
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Withdraw,
};
- case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
- return {
- major: TransactionMajorState.Suspended,
- minor: TransactionMinorState.MergeKycRequired,
- };
case PeerPullPaymentCreditStatus.Aborted:
return {
major: TransactionMajorState.Aborted,
diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
@@ -650,6 +650,7 @@ async function processPeerPushDebitMergeKyc(
lastCheckStatus: peerInc.kycLastCheckStatus,
lastDeny: peerInc.kycLastDeny,
lastRuleGen: peerInc.kycLastRuleGen,
+ haveAccessToken: peerInc.kycAccessToken != null,
};
}
@@ -673,6 +674,7 @@ async function processPeerPushDebitMergeKyc(
rec.kycLastCheckCode = updatedStatus.lastCheckCode;
rec.kycLastDeny = updatedStatus.lastDeny;
rec.kycLastRuleGen = updatedStatus.lastRuleGen;
+ rec.kycAccessToken = updatedStatus.accessToken;
return TransitionResultType.Transition;
});
return algoRes.taskResult;
@@ -1198,10 +1200,29 @@ export function computePeerPushCreditTransactionState(
major: TransactionMajorState.Done,
};
case PeerPushCreditStatus.PendingMergeKycRequired:
- return {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.MergeKycRequired,
- };
+ if (pushCreditRecord.kycAccessToken) {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.MergeKycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
+ case PeerPushCreditStatus.SuspendedMergeKycRequired:
+ if (pushCreditRecord.kycAccessToken) {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.MergeKycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case PeerPushCreditStatus.PendingWithdrawing:
return {
major: TransactionMajorState.Pending,
@@ -1212,11 +1233,6 @@ export function computePeerPushCreditTransactionState(
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Merge,
};
- case PeerPushCreditStatus.SuspendedMergeKycRequired:
- return {
- major: TransactionMajorState.Suspended,
- minor: TransactionMinorState.MergeKycRequired,
- };
case PeerPushCreditStatus.SuspendedWithdrawing:
return {
major: TransactionMajorState.Suspended,
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
@@ -857,15 +857,29 @@ export function computeWithdrawalTransactionStatus(
minor: TransactionMinorState.WithdrawCoins,
};
case WithdrawalGroupStatus.PendingKyc:
- return {
- major: TransactionMajorState.Pending,
- minor: TransactionMinorState.KycRequired,
- };
+ if (!!wgRecord.kycAccessToken) {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case WithdrawalGroupStatus.SuspendedKyc:
- return {
- major: TransactionMajorState.Suspended,
- minor: TransactionMinorState.KycRequired,
- };
+ if (!!wgRecord.kycAccessToken) {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycRequired,
+ };
+ } else {
+ return {
+ major: TransactionMajorState.Suspended,
+ minor: TransactionMinorState.KycInit,
+ };
+ }
case WithdrawalGroupStatus.FailedAbortingBank:
return {
major: TransactionMajorState.Failed,
@@ -1475,8 +1489,12 @@ async function transitionKycRequired(
planchet.planchetStatus = PlanchetStatus.KycRequired;
await tx.planchets.put(planchet);
}
- if (wg2.status !== WithdrawalGroupStatus.PendingReady) {
- return TransitionResult.stay();
+ switch (wg2.status) {
+ case WithdrawalGroupStatus.PendingReady:
+ case WithdrawalGroupStatus.PendingKyc:
+ break;
+ default:
+ return TransitionResult.stay();
}
wg2.kycPaytoHash = legiRequiredResp.h_payto;
wg2.kycLastDeny = timestampPreciseToDb(TalerPreciseTimestamp.now());
@@ -2317,6 +2335,7 @@ async function processWithdrawalGroupPendingKyc(
lastCheckStatus: withdrawalGroup.kycLastCheckStatus,
lastDeny: withdrawalGroup.kycLastDeny,
lastRuleGen: withdrawalGroup.kycLastRuleGen,
+ haveAccessToken: withdrawalGroup.kycAccessToken != null,
};
}
@@ -2343,6 +2362,7 @@ async function processWithdrawalGroupPendingKyc(
rec.kycLastCheckCode = updatedStatus.lastCheckCode;
rec.kycLastDeny = updatedStatus.lastDeny;
rec.kycLastRuleGen = updatedStatus.lastRuleGen;
+ rec.kycAccessToken = updatedStatus.accessToken;
return TransitionResult.transition(rec);
});
@@ -2503,6 +2523,12 @@ async function processWithdrawalGroupPendingReady(
const { withdrawalGroupId } = withdrawalGroup;
const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId);
+ logger.trace(
+ `processWithdrawalGroupPendingReady in DB state ${
+ WithdrawalGroupStatus[withdrawalGroup.status]
+ }`,
+ );
+
checkDbInvariant(
withdrawalGroup.denomsSel !== undefined,
"can't process uninitialized exchange",
@@ -2512,7 +2538,7 @@ async function processWithdrawalGroupPendingReady(
"can't get funding uri from uninitialized wg",
);
const exchangeBaseUrl = withdrawalGroup.exchangeBaseUrl;
- logger.trace(`updating exchange beofre processing wg`);
+ logger.trace(`updating exchange before processing wg`);
const exch = await fetchFreshExchange(wex, withdrawalGroup.exchangeBaseUrl);
if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) {
@@ -2600,6 +2626,8 @@ async function processWithdrawalGroupPendingReady(
);
}
+ logger.trace(`withdrawing ${numTotalCoins} coins`);
+
for (let i = 0; i < numTotalCoins; i += maxBatchSize) {
let resp: WithdrawalBatchResult;
if (exchangeVer.current >= 26) {