taler-typescript-core

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

commit 5ad8afc229a748d1428633035ce63b47354519ce
parent e3c6913eb9c899f197f48e55b39c8e01b8afbbbe
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Tue, 20 Oct 2026 12:54:26 +0200

taldir/maildir: implement progress tokens in API

Diffstat:
Mpackages/taler-util/src/http-client/mailbox.ts | 4++--
Mpackages/taler-util/src/types-taler-mailbox.ts | 2+-
Mpackages/taler-util/src/types-taler-wallet.ts | 49++++++++++++++++++++++++++++++++++++++++++++++++-
Mpackages/taler-wallet-core/src/mailbox.ts | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mpackages/taler-wallet-core/src/requests.ts | 11++++++-----
Mpackages/taler-wallet-core/src/taldir.ts | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/wallet-api-types.ts | 6++++--
7 files changed, 181 insertions(+), 19 deletions(-)

diff --git a/packages/taler-util/src/http-client/mailbox.ts b/packages/taler-util/src/http-client/mailbox.ts @@ -21,7 +21,7 @@ import { LibtoolVersion, MailboxConfiguration, MailboxMetadata, - MailboxRegisterRequest, + MailboxRegisterRequestHttp, MailboxRegisterResult, OperationAlternative, OperationFail, @@ -295,7 +295,7 @@ export class TalerMailboxInstanceHttpClient { * https://docs.taler.net/core/api-mailbox.html#post--register */ async registerMailbox( - req: MailboxRegisterRequest, + req: MailboxRegisterRequestHttp, ): Promise< | OperationOk<MailboxRegisterResult> | OperationFail<HttpStatusCode.Forbidden> diff --git a/packages/taler-util/src/types-taler-mailbox.ts b/packages/taler-util/src/types-taler-mailbox.ts @@ -91,7 +91,7 @@ export interface MailboxRegisterPaymentRequired { talerUri?: string; } -export interface MailboxRegisterRequest { +export interface MailboxRegisterRequestHttp { // Mailbox metadata. mailbox_metadata: MailboxMetadata; diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -1645,6 +1645,7 @@ export interface TaldirRegistrationCompletionRequest { challenge: string; taldirBaseUrl: string; targetUri: string; + progressToken?: string; } export const codecForTaldirRegistrationCompletionRequest = @@ -1655,6 +1656,7 @@ export const codecForTaldirRegistrationCompletionRequest = .property("alias", codecForString()) .property("aliasType", codecForString()) .property("challenge", codecForString()) + .property("progressToken", codecOptional(codecForString())) .build("TaldirRegistrationCompletionRequest"); export interface TaldirRegistrationRequest { @@ -1663,6 +1665,7 @@ export interface TaldirRegistrationRequest { targetUri: string; taldirBaseUrl: string; duration: RelativeTime; + progressToken?: string; } export const codecForTaldirRegistrationRequest = @@ -1673,12 +1676,14 @@ export const codecForTaldirRegistrationRequest = .property("alias", codecForString()) .property("aliasType", codecForString()) .property("duration", codecForDuration) + .property("progressToken", codecOptional(codecForString())) .build("TaldirRegistrationRequest"); export interface TaldirLookupRequest { alias: string; aliasType: string; taldirBaseUrl: string; + progressToken?: string; } export const codecForTaldirLookupRequest = (): Codec<TaldirLookupRequest> => @@ -1686,6 +1691,7 @@ export const codecForTaldirLookupRequest = (): Codec<TaldirLookupRequest> => .property("taldirBaseUrl", codecForString()) .property("alias", codecForString()) .property("aliasType", codecForString()) + .property("progressToken", codecOptional(codecForString())) .build("TaldirLookupRequest"); export interface TaldirLookupResponse { @@ -1698,11 +1704,13 @@ export interface TaldirSupportedAliasesResponse { export interface TaldirGetSupportedAliasesRequest { taldirBaseUrl: string; + progressToken?: string; } export const codecForTaldirGetSupportedAliasesRequest = (): Codec<TaldirGetSupportedAliasesRequest> => buildCodecForObject<TaldirGetSupportedAliasesRequest>() .property("taldirBaseUrl", codecForString()) + .property("progressToken", codecOptional(codecForString())) .build("TaldirGetSupportedAliasesRequest"); export interface GetMailboxResponse { @@ -1718,7 +1726,7 @@ export interface MailboxBaseUrl { } export const codecForMailboxBaseUrl = (): Codec<MailboxBaseUrl> => - buildCodecForObject<MailboxBaseUrl>() +buildCodecForObject<MailboxBaseUrl>() .property("mailboxBaseUrl", codecForString()) .build("MailboxBaseUrl"); @@ -1743,8 +1751,46 @@ export const codecForMailboxConfiguration = (): Codec<MailboxConfiguration> => export interface SendTalerUriMailboxMessageRequest { contact: ContactEntry; talerUri: string; + progressToken?: string; +} + +export interface MailboxRefreshRequest { + mailboxConf: MailboxConfiguration; + progressToken?: string; +} + +export const codecForMailboxRefreshRequest = + (): Codec<MailboxRefreshRequest> => + buildCodecForObject<MailboxRefreshRequest>() + .property("mailboxConf", codecForMailboxConfiguration()) + .property("progressToken", codecOptional(codecForString())) + .build("MailboxRefreshRequest"); + +export interface MailboxRegistrationRequest { + mailboxConf: MailboxConfiguration; + progressToken?: string; } +export const codecForMailboxRegistrationRequest = + (): Codec<MailboxRegistrationRequest> => + buildCodecForObject<MailboxRegistrationRequest>() + .property("mailboxConf", codecForMailboxConfiguration()) + .property("progressToken", codecOptional(codecForString())) + .build("MailboxRegistrationRequest"); + +export interface MailboxInitializationRequest { + mailboxBaseUrl: MailboxBaseUrl; + progressToken?: string; +} + +export const codecForMailboxInitializationRequest = + (): Codec<MailboxInitializationRequest> => + buildCodecForObject<MailboxInitializationRequest>() + .property("mailboxBaseUrl", codecForMailboxBaseUrl()) + .property("progressToken", codecOptional(codecForString())) + .build("MailboxInitializationRequest"); + + export interface AddMailboxMessageRequest { message: MailboxMessageRecord; } @@ -1794,6 +1840,7 @@ export const codecForSendTalerUriMailboxMessageRequest = buildCodecForObject<SendTalerUriMailboxMessageRequest>() .property("contact", codecForContactEntry()) .property("talerUri", codecForString()) + .property("progressToken", codecOptional(codecForString())) .build("SendTalerUriMailboxMessageRequest"); export interface WalletCoreVersion { diff --git a/packages/taler-wallet-core/src/mailbox.ts b/packages/taler-wallet-core/src/mailbox.ts @@ -35,7 +35,8 @@ import { MailboxMessageRecordsResponse, MailboxMessagesResponse, MailboxMetadata, - MailboxRegisterRequest, + MailboxRefreshRequest, + MailboxRegistrationRequest, MailboxRegisterResult, NotificationType, Result, @@ -58,8 +59,14 @@ import { sha512, stringToBytes, succeedOrThrow, + MailboxRegisterRequestHttp, + MailboxInitializationRequest, } from "@gnu-taler/taler-util"; import { WalletExecutionContext } from "./wallet.js"; +import { + runWithMaybeProgressContext, +} from "./progress.js"; + const logger = new Logger("mailbox.ts"); export const MAX_MAILBOX_REFRESH_BATCHES = 100; @@ -135,8 +142,21 @@ export async function listMailboxMessages( */ export async function registerMailbox( wex: WalletExecutionContext, - mailboxConf: MailboxConfiguration, + req: MailboxRegistrationRequest, +): Promise<MailboxRegisterResult> { + return runWithMaybeProgressContext( + wex, + "mailboxRegistrationRequest", + req.progressToken, + () => internalRegisterMailbox(wex, req), + ); +} + +async function internalRegisterMailbox( + wex: WalletExecutionContext, + req: MailboxRegistrationRequest, ): Promise<MailboxRegisterResult> { + const mailboxConf = req.mailboxConf; const privateSigningKey = decodeCrock(mailboxConf.privateKey); const signingKey = eddsaGetPublic(privateSigningKey); const encryptionKey = hpkeSecretKeyGetPublic( @@ -176,7 +196,7 @@ export async function registerMailbox( encryption_key_type: "X25519", // FIXME only supported encryption key type ATM expiration: mailboxConf.expiration, }; - const req: MailboxRegisterRequest = { + const httpReq: MailboxRegisterRequestHttp = { mailbox_metadata: info, signature: encodeCrock(signature), }; @@ -185,7 +205,7 @@ export async function registerMailbox( wex.http, ); - const resp = await mailboxClient.registerMailbox(req); + const resp = await mailboxClient.registerMailbox(httpReq); switch (resp.case) { case "ok": return resp.body; @@ -220,10 +240,23 @@ export async function getMailbox( * Create new mailbox configuration locally and * try to register it. */ -export async function createNewMailbox( +export async function initializeMailbox( wex: WalletExecutionContext, - mailboxBaseUrl: MailboxBaseUrl, + req: MailboxInitializationRequest, +): Promise<MailboxConfiguration> { + return runWithMaybeProgressContext( + wex, + "mailboxInitializationRequest", + req.progressToken, + () => internalInitializeMailbox(wex, req), + ); +} + +async function internalInitializeMailbox( + wex: WalletExecutionContext, + req: MailboxInitializationRequest, ): Promise<MailboxConfiguration> { + const mailboxBaseUrl = req.mailboxBaseUrl; const keys = createEddsaKeyPair(); const hpkeKey = encodeCrock(hpkeCreateSecretKey()); const privKey = encodeCrock(keys.eddsaPriv); @@ -242,7 +275,10 @@ export async function createNewMailbox( expiration: AbsoluteTime.toProtocolTimestamp(nowInAYear), }; - const resp = await registerMailbox(wex, mailboxConf); + const resp = await registerMailbox(wex, { + mailboxConf: mailboxConf, + progressToken: req.progressToken, + }); if (resp.status == "payment-required") { if (!resp.talerUri) { throw TalerError.fromDetail( @@ -294,8 +330,21 @@ function decryptTalerUriMessage( */ export async function refreshMailbox( wex: WalletExecutionContext, - mailboxConf: MailboxConfiguration, + req: MailboxRefreshRequest, +): Promise<MailboxMessageRecordsResponse> { + return runWithMaybeProgressContext( + wex, + "mailboxRefreshRequest", + req.progressToken, + () => internalRefreshMailbox(wex, req), + ); +} + +async function internalRefreshMailbox( + wex: WalletExecutionContext, + req: MailboxRefreshRequest, ): Promise<MailboxMessageRecordsResponse> { + const mailboxConf = req.mailboxConf; const mailboxClient = new TalerMailboxInstanceHttpClient( mailboxConf.mailboxBaseUrl, wex.http, @@ -452,6 +501,18 @@ export async function sendTalerUriMessage( wex: WalletExecutionContext, req: SendTalerUriMailboxMessageRequest, ): Promise<EmptyObject> { + return runWithMaybeProgressContext( + wex, + "sendTalerUriMessage", + req.progressToken, + () => internalSendTalerUriMessage(wex, req), + ); +} + +async function internalSendTalerUriMessage( + wex: WalletExecutionContext, + req: SendTalerUriMailboxMessageRequest, +): Promise<EmptyObject> { const mailboxClient = new TalerMailboxInstanceHttpClient( req.contact.mailboxBaseUri, wex.http, diff --git a/packages/taler-wallet-core/src/requests.ts b/packages/taler-wallet-core/src/requests.ts @@ -231,7 +231,7 @@ import { codecForListSubscriptionsRequest, codecForMigrateDatabaseRequest, codecForMailboxBaseUrl, - codecForMailboxConfiguration, + codecForMailboxRefreshRequest, codecForPrepareBankIntegratedWithdrawalRequest, codecForPreparePayForPaivanaRequest, codecForPreparePayRequest, @@ -289,6 +289,7 @@ import { setDangerousTimetravel, setGlobalLogLevelFromString, validateIban, + codecForMailboxInitializationRequest, } from "@gnu-taler/taler-util"; import { HttpResponse } from "@gnu-taler/taler-util/http"; import { getBalanceDetail, getBalances } from "./balance.js"; @@ -349,9 +350,9 @@ import { import { convertDepositAmount } from "./instructedAmountConversion.js"; import { addMailboxMessage, - createNewMailbox, deleteMailboxMessage, getMailbox, + initializeMailbox, listMailboxMessages, refreshMailbox, sendTalerUriMessage, @@ -2590,8 +2591,8 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { handler: getSupportedAliasTypes, }, [WalletApiOperation.InitializeMailbox]: { - codec: codecForMailboxBaseUrl(), - handler: createNewMailbox, + codec: codecForMailboxInitializationRequest(), + handler: initializeMailbox, }, [WalletApiOperation.GetMailbox]: { codec: codecForMailboxBaseUrl(), @@ -2614,7 +2615,7 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { handler: listMailboxMessages, }, [WalletApiOperation.RefreshMailbox]: { - codec: codecForMailboxConfiguration(), + codec: codecForMailboxRefreshRequest(), handler: refreshMailbox, }, [WalletApiOperation.TestingGetDbStats]: { diff --git a/packages/taler-wallet-core/src/taldir.ts b/packages/taler-wallet-core/src/taldir.ts @@ -37,6 +37,9 @@ import { TalerErrorCode, } from "@gnu-taler/taler-util"; import { WalletExecutionContext } from "./wallet.js"; +import { + runWithMaybeProgressContext, +} from "./progress.js"; const logger = new Logger("taldir.ts"); @@ -58,6 +61,18 @@ function createHAliasBuffer(aliasType: string, alias: string): Uint8Array { */ export async function getSupportedAliases( wex: WalletExecutionContext, + req: TaldirGetSupportedAliasesRequest, +): Promise<TaldirSupportedAliasesResponse> { + return runWithMaybeProgressContext( + wex, + "getSupportedAliasesRequest", + req.progressToken, + () => internalGetSupportedAliases(wex, req), + ); +} + +async function internalGetSupportedAliases( + wex: WalletExecutionContext, lookupRequest: TaldirGetSupportedAliasesRequest, ): Promise<TaldirSupportedAliasesResponse> { const taldirClient = new TalerDirectoryInstanceHttpClient( @@ -86,6 +101,18 @@ export async function getSupportedAliases( */ export async function lookupAlias( wex: WalletExecutionContext, + req: TaldirLookupRequest, +): Promise<TaldirLookupResponse> { + return runWithMaybeProgressContext( + wex, + "lookupAlias", + req.progressToken, + () => internalLookupAlias(wex, req), + ); +} + +async function internalLookupAlias( + wex: WalletExecutionContext, lookupRequest: TaldirLookupRequest, ): Promise<TaldirLookupResponse> { const taldirClient = new TalerDirectoryInstanceHttpClient( @@ -145,6 +172,18 @@ export async function lookupAlias( */ export async function registerAlias( wex: WalletExecutionContext, + req: TaldirRegistrationRequest, +): Promise<TaldirRegistrationResponse> { + return runWithMaybeProgressContext( + wex, + "registerAlias", + req.progressToken, + () => internalRegisterAlias(wex, req), + ); +} + +async function internalRegisterAlias( + wex: WalletExecutionContext, registerRequest: TaldirRegistrationRequest, ): Promise<TaldirRegistrationResponse> { const taldirClient = new TalerDirectoryInstanceHttpClient( @@ -180,6 +219,18 @@ export async function registerAlias( */ export async function completeAliasRegistration( wex: WalletExecutionContext, + req: TaldirRegistrationCompletionRequest, +): Promise<EmptyObject> { + return runWithMaybeProgressContext( + wex, + "completeAliasRegistration", + req.progressToken, + () => internalCompleteAliasRegistration(wex, req), + ); +} + +async function internalCompleteAliasRegistration( + wex: WalletExecutionContext, completionRequest: TaldirRegistrationCompletionRequest, ): Promise<EmptyObject> { const taldirClient = new TalerDirectoryInstanceHttpClient( diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -232,6 +232,8 @@ import { WithdrawTestBalanceRequest, WithdrawUriInfoResponse, WithdrawalDetailsForAmount, + MailboxRefreshRequest, + MailboxInitializationRequest, } from "@gnu-taler/taler-util"; import { PaymentBalanceDetails } from "./balance.js"; import { WithdrawTestBalanceResult } from "./testing.js"; @@ -647,7 +649,7 @@ export type GetSupportedAliasesOp = { */ export type RefreshMailboxOp = { op: WalletApiOperation.RefreshMailbox; - request: MailboxConfiguration; + request: MailboxRefreshRequest; response: MailboxMessageRecordsResponse; }; @@ -656,7 +658,7 @@ export type RefreshMailboxOp = { */ export type InitializeMailboxOp = { op: WalletApiOperation.InitializeMailbox; - request: MailboxBaseUrl; + request: MailboxInitializationRequest; response: MailboxConfiguration; };