taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit b9755dd70ae0f0cffe41197fca05ee3794ad3e17
parent fae7e033790d09aca85aeb1de53b2bdc73cb2d0b
Author: Florian Dold <florian@dold.me>
Date:   Thu, 19 Mar 2026 22:08:48 +0100

wallet-core: fix pub/priv confusion in deposit heuristic, modernize deposit tests

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-age-restrictions-deposit.ts | 10+---------
Mpackages/taler-harness/src/integrationtests/test-deposit-merge.ts | 202++++++++++++++++++++++++++++++++++---------------------------------------------
Mpackages/taler-harness/src/integrationtests/test-deposit.ts | 10++--------
Mpackages/taler-harness/src/integrationtests/test-kyc-deposit-kycauth.ts | 8++------
Mpackages/taler-util/src/types-taler-wallet.ts | 6++++++
Mpackages/taler-wallet-core/src/deposits.ts | 15++++-----------
Mpackages/taler-wallet-core/src/wallet-api-types.ts | 15---------------
Mpackages/taler-wallet-core/src/wallet.ts | 15+--------------
8 files changed, 102 insertions(+), 179 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-age-restrictions-deposit.ts b/packages/taler-harness/src/integrationtests/test-age-restrictions-deposit.ts @@ -59,23 +59,15 @@ export async function runAgeRestrictionsDepositTest(t: GlobalTestState) { await withdrawalResult.withdrawalFinishedCond; - const dgIdResp = await walletClient.client.call( - WalletApiOperation.GenerateDepositGroupTxId, - {}, - ); - - const depositTxId = dgIdResp.transactionId; - const depositGroupResult = await walletClient.client.call( WalletApiOperation.CreateDepositGroup, { amount: "TESTKUDOS:10" as AmountString, depositPaytoUri: getTestHarnessPaytoForLabel("foo"), - transactionId: depositTxId, }, ); - t.assertDeepEqual(depositGroupResult.transactionId, depositTxId); + const depositTxId = depositGroupResult.transactionId; await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { transactionId: depositTxId, diff --git a/packages/taler-harness/src/integrationtests/test-deposit-merge.ts b/packages/taler-harness/src/integrationtests/test-deposit-merge.ts @@ -18,10 +18,9 @@ * Imports. */ import { + AbsoluteTime, AmountString, Duration, - NotificationType, - TransactionIdStr, TransactionMajorState, TransactionMinorState, j2s, @@ -62,46 +61,31 @@ export async function runDepositMergeTest(t: GlobalTestState) { /** * first deposit */ - let d1Id: TransactionIdStr; - let d1Track: Promise<boolean>; - let d1Done: Promise<boolean>; - { - const { transactionId: depositTxId } = await walletClient.client.call( - WalletApiOperation.GenerateDepositGroupTxId, - {}, - ); - - d1Track = walletClient.waitForNotificationCond( - (n) => - n.type == NotificationType.TransactionStateTransition && - n.transactionId == depositTxId && - n.newTxState.major == TransactionMajorState.Finalizing && - n.newTxState.minor == TransactionMinorState.Track, - ); - - d1Done = walletClient.waitForNotificationCond( - (n) => - n.type == NotificationType.TransactionStateTransition && - n.transactionId == depositTxId && - n.newTxState.major == TransactionMajorState.Done, - ); - - const depositGroupResult = await walletClient.client.call( - WalletApiOperation.CreateDepositGroup, - { - amount: "TESTKUDOS:3" as AmountString, - depositPaytoUri, - transactionId: depositTxId, - }, - ); - - t.assertDeepEqual(depositGroupResult.transactionId, depositTxId); - d1Id = depositGroupResult.transactionId; - } + const depositGroupResult1 = await walletClient.client.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:3" as AmountString, + depositPaytoUri, + wireDeadline: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ minutes: 5 }), + ), + ), + }, + ); + const d1Id = depositGroupResult1.transactionId; t.logStep("after first deposit"); - await d1Track; + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: d1Id, + txState: { + major: TransactionMajorState.Finalizing, + minor: TransactionMinorState.Track, + }, + }); + await exchange.stop(); // @ts-ignore duration is not forever exchange.setTimetravel(Duration.fromSpec({ minutes: 1 }).d_ms); @@ -114,46 +98,32 @@ export async function runDepositMergeTest(t: GlobalTestState) { /** * second deposit after 1 minute */ - let d2Id: TransactionIdStr; - let d2Track: Promise<boolean>; - let d2Done: Promise<boolean>; - { - const { transactionId: depositTxId } = await walletClient.client.call( - WalletApiOperation.GenerateDepositGroupTxId, - {}, - ); - - d2Track = walletClient.waitForNotificationCond( - (n) => - n.type == NotificationType.TransactionStateTransition && - n.transactionId == depositTxId && - n.newTxState.major == TransactionMajorState.Finalizing && - n.newTxState.minor == TransactionMinorState.Track, - ); - d2Done = walletClient.waitForNotificationCond( - (n) => - n.type == NotificationType.TransactionStateTransition && - n.transactionId == depositTxId && - n.newTxState.major == TransactionMajorState.Done, - ); - - const depositGroupResult = await walletClient.client.call( - WalletApiOperation.CreateDepositGroup, - { - amount: "TESTKUDOS:3" as AmountString, - depositPaytoUri, - transactionId: depositTxId, - }, - ); - - t.assertDeepEqual(depositGroupResult.transactionId, depositTxId); - d2Id = depositGroupResult.transactionId; - } + const depositGroupResult2 = await walletClient.client.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:3" as AmountString, + depositPaytoUri, + wireDeadline: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ minutes: 5 }), + ), + ), + }, + ); + + const d2Id = depositGroupResult2.transactionId; t.logStep("done with second deposit"); - await d2Track; + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: d2Id, + txState: { + major: TransactionMajorState.Finalizing, + minor: TransactionMinorState.Track, + }, + }); t.logStep("done with second deposit (track)"); await exchange.stop(); @@ -166,44 +136,29 @@ export async function runDepositMergeTest(t: GlobalTestState) { /** * third deposit after 2 minute */ - let d3Id: TransactionIdStr; - let d3Track: Promise<boolean>; - let d3Done: Promise<boolean>; - { - const { transactionId: depositTxId } = await walletClient.client.call( - WalletApiOperation.GenerateDepositGroupTxId, - {}, - ); - - d3Track = walletClient.waitForNotificationCond( - (n) => - n.type == NotificationType.TransactionStateTransition && - n.transactionId == depositTxId && - n.newTxState.major == TransactionMajorState.Finalizing && - n.newTxState.minor == TransactionMinorState.Track, - ); - - d3Done = walletClient.waitForNotificationCond( - (n) => - n.type == NotificationType.TransactionStateTransition && - n.transactionId == depositTxId && - n.newTxState.major == TransactionMajorState.Done, - ); - - const depositGroupResult = await walletClient.client.call( - WalletApiOperation.CreateDepositGroup, - { - amount: "TESTKUDOS:3" as AmountString, - depositPaytoUri, - transactionId: depositTxId, - }, - ); - - t.assertDeepEqual(depositGroupResult.transactionId, depositTxId); - d3Id = depositGroupResult.transactionId; - } - - await d3Track; + const depositGroupResult3 = await walletClient.client.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:3" as AmountString, + depositPaytoUri, + wireDeadline: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration( + AbsoluteTime.now(), + Duration.fromSpec({ minutes: 5 }), + ), + ), + }, + ); + + const d3Id = depositGroupResult3.transactionId; + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: d3Id, + txState: { + major: TransactionMajorState.Finalizing, + minor: TransactionMinorState.Track, + }, + }); await exchange.stop(); // @ts-ignore duration is not forever exchange.setTimetravel(Duration.fromSpec({ minutes: 3 }).d_ms); @@ -271,9 +226,24 @@ export async function runDepositMergeTest(t: GlobalTestState) { transactionId: d3Id, }); - await d1Done; - await d2Done; - await d3Done; + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: d1Id, + txState: { + major: TransactionMajorState.Done, + }, + }); + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: d2Id, + txState: { + major: TransactionMajorState.Done, + }, + }); + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: d3Id, + txState: { + major: TransactionMajorState.Done, + }, + }); /* check deposit tx after 6 minute, first one should already be wired since default * wire deadline is 5 minutes * diff --git a/packages/taler-harness/src/integrationtests/test-deposit.ts b/packages/taler-harness/src/integrationtests/test-deposit.ts @@ -71,22 +71,16 @@ export async function runDepositTest(t: GlobalTestState) { t.assertAmountEquals(maxDepositResp.rawAmount, "TESTKUDOS:19.72"); t.assertAmountEquals(maxDepositResp.effectiveAmount, "TESTKUDOS:19.84"); - const dgIdResp = await walletClient.client.call( - WalletApiOperation.GenerateDepositGroupTxId, - {}, - ); - - const depositTxId = dgIdResp.transactionId; - const depositGroupResult = await walletClient.client.call( WalletApiOperation.CreateDepositGroup, { amount: "TESTKUDOS:10" as AmountString, depositPaytoUri, - transactionId: depositTxId, }, ); + const depositTxId = depositGroupResult.transactionId; + t.assertDeepEqual(depositGroupResult.transactionId, depositTxId); const balDuring = await walletClient.call(WalletApiOperation.GetBalances, {}); diff --git a/packages/taler-harness/src/integrationtests/test-kyc-deposit-kycauth.ts b/packages/taler-harness/src/integrationtests/test-kyc-deposit-kycauth.ts @@ -124,20 +124,16 @@ export async function runKycDepositKycauthTest(t: GlobalTestState) { }, }); - const { transactionId: depositTxId } = await w1.walletClient.call( - WalletApiOperation.GenerateDepositGroupTxId, - {}, - ); - const deposit = await w1.walletClient.call( WalletApiOperation.CreateDepositGroup, { amount: "TESTKUDOS:10" as AmountString, depositPaytoUri: getTestHarnessPaytoForLabel("foo"), - transactionId: depositTxId, }, ); + const depositTxId = deposit.transactionId; + await w1.walletClient.call(WalletApiOperation.TestingWaitTransactionState, { transactionId: depositTxId, txState: { diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -3116,6 +3116,11 @@ export interface CreateDepositGroupRequest { testingFixedPriv?: string; /** + * Optional wire deadline for the deposits. + */ + wireDeadline?: TalerProtocolTimestamp; + + /** * Pre-allocated transaction ID. * Allows clients to easily handle notifications * that occur while the operation has been created but @@ -3187,6 +3192,7 @@ export const codecForCreateDepositGroupRequest = .property("depositPaytoUri", codecForString()) .property("transactionId", codecOptional(codecForTransactionIdStr())) .property("testingFixedPriv", codecOptional(codecForString())) + .property("wireDeadline", codecOptional(codecForTimestamp)) .build("CreateDepositGroupRequest"); /** diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -1236,10 +1236,10 @@ async function provideDepositAccountKeypair( if (!exchange) { throw Error("exchange for deposit not found anymore"); } - if (exchange.currentAccountPriv && exchange.currentAccountPriv) { + if (exchange.currentAccountPriv && exchange.currentAccountPub) { return { priv: exchange.currentAccountPriv, - pub: exchange.currentAccountPriv, + pub: exchange.currentAccountPub, }; } const res = await tryFindAccountKeypair(tx, exchangeBaseUrl); @@ -2016,14 +2016,6 @@ export async function internalCheckDepositGroup( }; } -export function generateDepositGroupTxId(): string { - const depositGroupId = encodeCrock(getRandomBytes(32)); - return constructTransactionIdentifier({ - tag: TransactionType.Deposit, - depositGroupId: depositGroupId, - }); -} - export async function createDepositGroup( wex: WalletExecutionContext, req: CreateDepositGroupRequest, @@ -2046,7 +2038,8 @@ export async function createDepositGroup( } const now = AbsoluteTime.now(); - const wireDeadline = AbsoluteTime.toProtocolTimestamp(now); + const wireDeadline = + req.wireDeadline ?? AbsoluteTime.toProtocolTimestamp(now); const nowRounded = AbsoluteTime.toProtocolTimestamp(now); const payCoinSel = await selectPayCoins(wex, { diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -267,7 +267,6 @@ export enum WalletApiOperation { ForceRefresh = "forceRefresh", CheckDeposit = "checkDeposit", GetVersion = "getVersion", - GenerateDepositGroupTxId = "generateDepositGroupTxId", CreateDepositGroup = "createDepositGroup", ImportDb = "importDb", ExportDb = "exportDb", @@ -1087,19 +1086,6 @@ export type GetCurrencySpecificationOp = { // group: Deposits /** - * Generate a fresh transaction ID for a deposit group. - * - * The resulting transaction ID can be specified when creating - * a deposit group, so that the client can already start waiting for notifications - * on that specific deposit group before the GreateDepositGroup request returns. - */ -export type GenerateDepositGroupTxIdOp = { - op: WalletApiOperation.GenerateDepositGroupTxId; - request: EmptyObject; - response: TxIdResponse; -}; - -/** * Create a new deposit group. * * Deposit groups are used to deposit multiple coins to a bank @@ -1613,7 +1599,6 @@ export type WalletOperations = { [WalletApiOperation.GetExchangeDetailedInfo]: GetExchangeDetailedInfoOp; [WalletApiOperation.GetExchangeEntryByUrl]: GetExchangeEntryByUrlOp; [WalletApiOperation.CheckDeposit]: CheckDepositOp; - [WalletApiOperation.GenerateDepositGroupTxId]: GenerateDepositGroupTxIdOp; [WalletApiOperation.CreateDepositGroup]: CreateDepositGroupOp; [WalletApiOperation.ExportDbToFile]: ExportDbToFileOp; [WalletApiOperation.ImportDbFromFile]: ImportDbFromFileOp; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -150,7 +150,6 @@ import { TestingSetTimetravelRequest, TimerAPI, TimerGroup, - TransactionIdStr, TransactionType, TransactionsResponse, UpdateExchangeEntryRequest, @@ -323,11 +322,7 @@ import { isWithdrawableDenom, } from "./denominations.js"; import { UnverifiedDenomError } from "./denomSelection.js"; -import { - checkDepositGroup, - createDepositGroup, - generateDepositGroupTxId, -} from "./deposits.js"; +import { checkDepositGroup, createDepositGroup } from "./deposits.js"; import { DevExperimentHttpLib, applyDevExperiment } from "./dev-experiments.js"; import { handleGetDonau, @@ -2513,14 +2508,6 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { codec: codecForCheckDepositRequest(), handler: checkDepositGroup, }, - [WalletApiOperation.GenerateDepositGroupTxId]: { - codec: codecForEmptyObject(), - handler: async (wex, req) => { - return { - transactionId: generateDepositGroupTxId() as TransactionIdStr, - }; - }, - }, [WalletApiOperation.CreateDepositGroup]: { codec: codecForCreateDepositGroupRequest(), handler: createDepositGroup,