taler-typescript-core

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

commit 4d75a0c5f43bf68ed1b7bb3b7847b38fc5329abb
parent b7ebb1e567c59da6dc9a5baa74e48a8b52abb39b
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 13 Oct 2026 00:26:38 +0200

add starting point for POST /withdraw-reveal (age-restricted withdrawal)

Diffstat:
Mpackages/taler-util/src/http-client/exchange-client.ts | 45+++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-util/src/types-taler-exchange.ts | 35+++++++++++++++++++++++++++++++++++
2 files changed, 80 insertions(+), 0 deletions(-)

diff --git a/packages/taler-util/src/http-client/exchange-client.ts b/packages/taler-util/src/http-client/exchange-client.ts @@ -76,6 +76,8 @@ import { ExchangeRefundSuccessResponse, ExchangeReservePurseRequest, ExchangeRevealMeltResponseV2, + ExchangeRevealWithdrawRequest, + ExchangeRevealWithdrawResponse, ExchangeTransferList, ExchangeWithdrawRequest, ExchangeWithdrawResponse, @@ -115,6 +117,7 @@ import { codecForExchangeMergeSuccessResponse, codecForExchangePurseStatus, codecForExchangeRevealMeltResponseV2, + codecForExchangeRevealWithdrawResponse, codecForExchangeRefundSuccessResponse, codecForExchangeTransferList, codecForExchangeWithdrawResponse, @@ -2178,6 +2181,48 @@ export class TalerExchangeHttpClient { } } + /** + * Request: POST /reveal-withdraw + * + * https://docs.taler.net/core/api-exchange.html#post--reveal-withdraw + * + * Second step of an age-restricted withdrawal: discloses the kappa-1 + * batches that the exchange did not select. The request identifies the + * withdraw operation by ``reserve_pub`` and ``planchets_h``; both are + * required since protocol v42. + */ + async postRevealWithdraw(args: { + body: ExchangeRevealWithdrawRequest; + }): Promise< + | OperationOk<ExchangeRevealWithdrawResponse> + | OperationFail<HttpStatusCode.BadRequest> + | OperationFail<HttpStatusCode.NotFound> + | OperationFail<HttpStatusCode.PayloadTooLarge> + | OperationFail<HttpStatusCode.InternalServerError> + | OperationFail<HttpStatusCode.ServiceUnavailable> + > { + const url = new URL(`reveal-withdraw`, this.baseUrl); + const resp = await this.fetch(url, { + method: "POST", + body: args.body, + }); + switch (resp.status) { + case HttpStatusCode.Ok: + return opSuccessFromHttp( + resp, + codecForExchangeRevealWithdrawResponse(), + ); + case HttpStatusCode.BadRequest: + case HttpStatusCode.NotFound: + case HttpStatusCode.PayloadTooLarge: + case HttpStatusCode.InternalServerError: + case HttpStatusCode.ServiceUnavailable: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownHttpFailure(resp); + } + } + async postBlindingPrepare(args: { body: BlindingPrepareRequest; }): Promise< diff --git a/packages/taler-util/src/types-taler-exchange.ts b/packages/taler-util/src/types-taler-exchange.ts @@ -1009,6 +1009,29 @@ export interface ExchangeRefreshRevealRequestV2 { age_commitment?: Edx25519PublicKeyEnc[]; } +/** + * Docs name: RevealWithdrawRequest + */ +export interface ExchangeRevealWithdrawRequest { + // Public key of the reserve that the original call to /withdraw was + // made from. Together with planchets_h, this identifies the withdraw + // operation. + // + // @since protocol **v42**. Required: without it the exchange cannot + // look the operation up by its primary key. + reserve_pub: EddsaPublicKey; + + // Running hash of all blinded planchets from the previous + // call to /withdraw. + planchets_h: HashCode; + + // Array of (kappa - 1) disclosed batch secrets, from which for each + // of the n coins in a batch their coin master secret is derived, and + // from that in turn their private key, blinding, nonce (for + // Clause-Schnorr) and age restriction. + disclosed_batch_seeds: HashCode[]; +} + export const codecForRecoup = (): Codec<Recoup> => buildCodecForObject<Recoup>() .property("h_denom_pub", codecForString()) @@ -1226,6 +1249,18 @@ export const codecForExchangeRevealMeltResponseV2 = .property("ev_sigs", codecForList(codecForBlindedDenominationSignature())) .build("ExchangeRevealMeltResponseV2"); +/** + * Response to a POST /reveal-withdraw request. Structurally identical to + * {@link ExchangeWithdrawResponse} (an array of blinded signatures). + */ +export type ExchangeRevealWithdrawResponse = ExchangeWithdrawResponse; + +export const codecForExchangeRevealWithdrawResponse = + (): Codec<ExchangeRevealWithdrawResponse> => + buildCodecForObject<ExchangeRevealWithdrawResponse>() + .property("ev_sigs", codecForList(codecForBlindedDenominationSignature())) + .build("ExchangeRevealWithdrawResponse"); + export const codecForExchangeMeltResponse = (): Codec<ExchangeMeltResponse> => buildCodecForObject<ExchangeMeltResponse>() .property("exchange_pub", codecForEddsaPublicKey())