diff options
author | Sebastian <sebasjm@gmail.com> | 2024-08-26 09:33:48 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-08-26 09:34:03 -0300 |
commit | 1f3aac3b57af7f99de6d78fd43acea0b4cfdfcb1 (patch) | |
tree | a54e59ac94a04e3eb78761fbddb76b72844ba7a1 | |
parent | c0f4b0d0f206a82d4283735bb661a229a9bfffd3 (diff) |
exchange api
-rw-r--r-- | packages/kyc-ui/src/Routing.tsx | 2 | ||||
-rw-r--r-- | packages/kyc-ui/src/pages/Start.tsx | 2 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/exchange.ts | 693 | ||||
-rw-r--r-- | packages/taler-util/src/types-taler-exchange.ts | 554 |
4 files changed, 977 insertions, 274 deletions
diff --git a/packages/kyc-ui/src/Routing.tsx b/packages/kyc-ui/src/Routing.tsx index 193efe8e4..8dcfa24a4 100644 --- a/packages/kyc-ui/src/Routing.tsx +++ b/packages/kyc-ui/src/Routing.tsx @@ -27,8 +27,6 @@ import { useErrorBoundary } from "preact/hooks"; import { CallengeCompleted } from "./pages/CallengeCompleted.js"; import { Frame } from "./pages/Frame.js"; import { Start } from "./pages/Start.js"; -import { FillForm } from "./pages/FillForm.js"; -import { SaveToken } from "./pages/SaveToken.js"; export function Routing(): VNode { // check session and defined if this is diff --git a/packages/kyc-ui/src/pages/Start.tsx b/packages/kyc-ui/src/pages/Start.tsx index 9813d5ff9..7bdbbf80f 100644 --- a/packages/kyc-ui/src/pages/Start.tsx +++ b/packages/kyc-ui/src/pages/Start.tsx @@ -226,7 +226,7 @@ function RequirementRow({ ? undefined : withErrorHandler( async () => { - return lib.exchange.startKycProcess(reqId); + return lib.exchange.startExternalKycProcess(reqId); }, (res) => { window.open(res.body.redirect_url, "_blank"); diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts index 9b7635cb4..c9d2975d1 100644 --- a/packages/taler-util/src/http-client/exchange.ts +++ b/packages/taler-util/src/http-client/exchange.ts @@ -17,8 +17,9 @@ import { opKnownAlternativeFailure, opKnownHttpFailure, opSuccessFromHttp, - opUnknownFailure + opUnknownFailure, } from "../operation.js"; +import { Codec, codecForAny } from "../codec.js"; import { TalerSignaturePurpose, buildSigPS, @@ -26,7 +27,7 @@ import { eddsaSign, encodeCrock, stringToBytes, - timestampRoundedToBuffer + timestampRoundedToBuffer, } from "../taler-crypto.js"; import { AccessToken, @@ -35,10 +36,12 @@ import { PaginationParams, ReserveAccount, SigningKey, - codecForTalerCommonConfigResponse + codecForTalerCommonConfigResponse, } from "../types-taler-common.js"; import { AmlDecisionRequest, + BatchWithdrawResponse, + ExchangeBatchWithdrawRequest, ExchangeVersionResponse, KycRequirementInformationId, WalletKycRequest, @@ -52,9 +55,13 @@ import { codecForExchangeKeys, codecForKycProcessClientInformation, codecForKycProcessStartInformation, - codecForLegitimizationNeededResponse + codecForLegitimizationNeededResponse, } from "../types-taler-exchange.js"; -import { CacheEvictor, addMerchantPaginationParams, nullEvictor } from "./utils.js"; +import { + CacheEvictor, + addMerchantPaginationParams, + nullEvictor, +} from "./utils.js"; import { TalerError } from "../errors.js"; import { TalerErrorCode } from "../taler-error-codes.js"; @@ -71,6 +78,8 @@ export enum TalerExchangeCacheEviction { CREATE_DESCISION, } +declare const __pubId: unique symbol; +export type ReservePub = string & { [__pubId]: true }; /** */ export class TalerExchangeHttpClient { @@ -91,6 +100,20 @@ export class TalerExchangeHttpClient { const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); return compare?.compatible ?? false; } + + // TERMS + + /** + * https://docs.taler.net/core/api-exchange.html#get--seed + * + */ + /** + * https://docs.taler.net/core/api-exchange.html#get--seed + * + */ + + // EXCHANGE INFORMATION + /** * https://docs.taler.net/core/api-exchange.html#get--seed * @@ -163,6 +186,7 @@ export class TalerExchangeHttpClient { return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } } + /** * https://docs.taler.net/core/api-merchant.html#get--config * @@ -181,10 +205,390 @@ export class TalerExchangeHttpClient { } } - // TERMS + // + // MANAGEMENT + // + /** + * https://docs.taler.net/core/api-exchange.html#get--management-keys + * + */ + async getFutureKeys(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-keys + * + */ + async signFutureKeys(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-denominations-$H_DENOM_PUB-revoke + * + */ + async revokeFutureDenominationKeys(): Promise<never> { + throw Error("not yet implemented"); + } + /** + * https://docs.taler.net/core/api-exchange.html#post--management-signkeys-$EXCHANGE_PUB-revoke + * + */ + async revokeFutureSigningKeys(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-auditors + * + */ + async enableAuditor(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-auditors-$AUDITOR_PUB-disable + * + */ + async disableAuditor(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-wire-fee + * + */ + async configWireFee(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-global-fees + * + */ + async configGlobalFees(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-wire + * + */ + async enableWireMethod(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-wire-disable + * + */ + async disableWireMethod(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-drain + * + */ + async drainProfits(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-aml-officers + * + */ + async updateOfficer(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--management-partners + * + */ + async enablePartner(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // AUDITOR + // + + /** + * https://docs.taler.net/core/api-exchange.html#post--auditors-$AUDITOR_PUB-$H_DENOM_PUB + * + */ + async addAuditor(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // WITHDRAWAL + // + + /** + * https://docs.taler.net/core/api-exchange.html#get--reserves-$RESERVE_PUB + * + */ + async getReserveInfo(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--csr-withdraw + * + */ + async prepareCsrWithdawal(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--reserves-$RESERVE_PUB-batch-withdraw + * + */ + async withdraw(rid: ReservePub, body: ExchangeBatchWithdrawRequest) { + const url = new URL(`reserves/${rid}/batch-withdraw`, this.baseUrl); + + const resp = await this.httpLib.fetch(url.href, { + method: "POST", + body, + }); + + switch (resp.status) { + case HttpStatusCode.Ok: + return opSuccessFromHttp( + resp, + codecForAny() as Codec<BatchWithdrawResponse>, + ); + case HttpStatusCode.Forbidden: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Gone: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.UnavailableForLegalReasons: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForLegitimizationNeededResponse(), + ); + default: + return opUnknownFailure(resp, await readTalerErrorResponse(resp)); + } + } + + /** + * https://docs.taler.net/core/api-exchange.html#withdraw-with-age-restriction + * + */ + async withdrawWithAge(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--age-withdraw-$ACH-reveal + * + */ + async revealCoinsForAge(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // RESERVE HISTORY + // + + /** + * https://docs.taler.net/core/api-exchange.html#get--reserves-$RESERVE_PUB-history + * + */ + async getResverveHistory(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // COIN HISTORY // - // KYC operations + + /** + * https://docs.taler.net/core/api-exchange.html#get--coins-$COIN_PUB-history + * + */ + async getCoinHistory(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // DEPOSIT + // + + /** + * https://docs.taler.net/core/api-exchange.html#post--batch-deposit + * + */ + async deposit(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // REFRESH + // + + /** + * https://docs.taler.net/core/api-exchange.html#post--csr-melt + * + */ + async prepareCsrMelt(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--coins-$COIN_PUB-melt + * + */ + async meltCoin(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--refreshes-$RCH-reveal + * + */ + async releaveCoin(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#get--coins-$COIN_PUB-link + * + */ + async linkCoin(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // RECOUP + // + + /** + * https://docs.taler.net/core/api-exchange.html#post--coins-$COIN_PUB-recoup + * + */ + async recoupReserveCoin(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--coins-$COIN_PUB-recoup-refresh + * + */ + async recoupRefreshCoin(): Promise<never> { + throw Error("not yet implemented"); + } + + // WIRE TRANSFER + + /** + * https://docs.taler.net/core/api-exchange.html#get--transfers-$WTID + * + */ + async getWireTransferInfo(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#get--deposits-$H_WIRE-$MERCHANT_PUB-$H_CONTRACT_TERMS-$COIN_PUB + * + */ + async getWireTransferIdForDeposit(): Promise<never> { + throw Error("not yet implemented"); + } + + // REFUND + + /** + * https://docs.taler.net/core/api-exchange.html#post--coins-$COIN_PUB-refund + * + */ + async refund(): Promise<never> { + throw Error("not yet implemented"); + } + + // WALLET TO WALLET + + /** + * https://docs.taler.net/core/api-exchange.html#get--purses-$PURSE_PUB-merge + * + */ + async getPurseInfoAtMerge(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#get--purses-$PURSE_PUB-deposit + * + */ + async getPurseInfoAtDeposit(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-create + * + */ + async createPurseFromDeposit(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#delete--purses-$PURSE_PUB + * + */ + async deletePurse(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-merge + * + */ + async mergePurse(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--reserves-$RESERVE_PUB-purse + * + */ + async createPurseFromReserve(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-deposit + * + */ + async depositIntoPurse(): Promise<never> { + throw Error("not yet implemented"); + } + + // WADS + + /** + * https://docs.taler.net/core/api-exchange.html#get--wads-$WAD_ID + * + */ + async getWadInfo(): Promise<never> { + throw Error("not yet implemented"); + } + + // + // KYC // /** @@ -198,7 +602,7 @@ export class TalerExchangeHttpClient { balance, reserve_pub: account.id, reserve_sig: encodeCrock(account.signingKey), - } + }; const resp = await this.httpLib.fetch(url.href, { method: "POST", @@ -213,7 +617,11 @@ export class TalerExchangeHttpClient { case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.UnavailableForLegalReasons: - return opKnownAlternativeFailure(resp, resp.status, codecForLegitimizationNeededResponse()); + return opKnownAlternativeFailure( + resp, + resp.status, + codecForLegitimizationNeededResponse(), + ); default: return opUnknownFailure(resp, await readTalerErrorResponse(resp)); } @@ -223,9 +631,13 @@ export class TalerExchangeHttpClient { * https://docs.taler.net/core/api-exchange.html#post--kyc-wallet * */ - async checkKycStatus(account: ReserveAccount, requirementId: number, params: { - timeout?: number, - } = {}) { + async checkKycStatus( + account: ReserveAccount, + requirementId: number, + params: { + timeout?: number; + } = {}, + ) { const url = new URL(`kyc-check/${String(requirementId)}`, this.baseUrl); if (params.timeout !== undefined) { @@ -243,7 +655,11 @@ export class TalerExchangeHttpClient { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForAccountKycStatus()); case HttpStatusCode.Accepted: - return opKnownAlternativeFailure(resp, resp.status, codecForAccountKycStatus()); + return opKnownAlternativeFailure( + resp, + resp.status, + codecForAccountKycStatus(), + ); case HttpStatusCode.NoContent: return opEmptySuccess(resp); case HttpStatusCode.Forbidden: @@ -258,12 +674,16 @@ export class TalerExchangeHttpClient { } /** - * https://docs.taler.net/core/api-exchange.html#get--kyc-info-$ACCESS_TOKEN - * - */ - async checkKycInfo(token: AccessToken, known: KycRequirementInformationId[], params: { - timeout?: number, - } = {}) { + * https://docs.taler.net/core/api-exchange.html#get--kyc-info-$ACCESS_TOKEN + * + */ + async checkKycInfo( + token: AccessToken, + known: KycRequirementInformationId[], + params: { + timeout?: number; + } = {}, + ) { const url = new URL(`kyc-info/${token}`, this.baseUrl); if (params.timeout !== undefined) { @@ -273,15 +693,19 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "If-None-Match": known.length ? known.join(",") : undefined - } + "If-None-Match": known.length ? known.join(",") : undefined, + }, }); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForKycProcessClientInformation()); case HttpStatusCode.NoContent: - return opKnownAlternativeFailure(resp, HttpStatusCode.NoContent, codecForEmptyObject()); + return opKnownAlternativeFailure( + resp, + HttpStatusCode.NoContent, + codecForEmptyObject(), + ); case HttpStatusCode.NotModified: return opKnownHttpFailure(resp.status, resp); default: @@ -289,7 +713,6 @@ export class TalerExchangeHttpClient { } } - /** * https://docs.taler.net/core/api-exchange.html#post--kyc-upload-$ID * @@ -320,13 +743,15 @@ export class TalerExchangeHttpClient { * https://docs.taler.net/core/api-exchange.html#post--kyc-start-$ID * */ - async startKycProcess(requirement: KycRequirementInformationId, body: object = {}) { + async startExternalKycProcess( + requirement: KycRequirementInformationId, + body: object = {}, + ) { const url = new URL(`kyc-start/${requirement}`, this.baseUrl); - const resp = await this.httpLib.fetch(url.href, { method: "POST", - body + body, }); switch (resp.status) { @@ -343,119 +768,17 @@ export class TalerExchangeHttpClient { } } + /** + * https://docs.taler.net/core/api-exchange.html#get--kyc-proof-$PROVIDER_NAME?state=$H_PAYTO + * + */ + async completeExternalKycProcess(provider: string, state: string) {} + // // AML operations // /** - * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions-$STATE - * - */ - // async getDecisionsByState( - // auth: OfficerAccount, - // state: TalerExchangeApi.AmlState, - // pagination?: PaginationParams, - // ) { - // const url = new URL( - // `aml/${auth.id}/decisions/${TalerExchangeApi.AmlState[state]}`, - // this.baseUrl, - // ); - // addPaginationParams(url, pagination); - - // const resp = await this.httpLib.fetch(url.href, { - // method: "GET", - // headers: { - // "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey), - // }, - // }); - - // switch (resp.status) { - // case HttpStatusCode.Ok: - // return opSuccessFromHttp(resp, codecForAmlRecords()); - // case HttpStatusCode.NoContent: - // return opFixedSuccess({ records: [] }); - // //this should be unauthorized - // case HttpStatusCode.Forbidden: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.Unauthorized: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.NotFound: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.Conflict: - // return opKnownHttpFailure(resp.status, resp); - // default: - // return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - // } - // } - - // /** - // * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decision-$H_PAYTO - // * - // */ - // async getDecisionDetails(auth: OfficerAccount, account: string) { - // const url = new URL(`aml/${auth.id}/decision/${account}`, this.baseUrl); - - // const resp = await this.httpLib.fetch(url.href, { - // method: "GET", - // headers: { - // "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey), - // }, - // }); - - // switch (resp.status) { - // case HttpStatusCode.Ok: - // return opSuccessFromHttp(resp, codecForAmlDecisionDetails()); - // case HttpStatusCode.NoContent: - // return opFixedSuccess({ aml_history: [], kyc_attributes: [] }); - // //this should be unauthorized - // case HttpStatusCode.Forbidden: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.Unauthorized: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.NotFound: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.Conflict: - // return opKnownHttpFailure(resp.status, resp); - // default: - // return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - // } - // } - - // /** - // * https://docs.taler.net/core/api-exchange.html#post--aml-$OFFICER_PUB-decision - // * - // */ - // async addDecisionDetails( - // auth: OfficerAccount, - // decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">, - // ) { - // const url = new URL(`aml/${auth.id}/decision`, this.baseUrl); - - // const body = buildDecisionSignature(auth.signingKey, decision); - // const resp = await this.httpLib.fetch(url.href, { - // method: "POST", - // body, - // }); - - // switch (resp.status) { - // case HttpStatusCode.NoContent: - // return opEmptySuccess(resp); - // //FIXME: this should be unauthorized - // case HttpStatusCode.Forbidden: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.Unauthorized: - // return opKnownHttpFailure(resp.status, resp); - // //FIXME: this two need to be split by error code - // case HttpStatusCode.NotFound: - // return opKnownHttpFailure(resp.status, resp); - // case HttpStatusCode.Conflict: - // return opKnownHttpFailure(resp.status, resp); - // default: - // return opUnknownFailure(resp, await readTalerErrorResponse(resp)); - // } - // } - - /** * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-measures * */ @@ -481,26 +804,23 @@ export class TalerExchangeHttpClient { * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-measures * */ - async getAmlKycStatistics(auth: OfficerAccount, name: string, filter: { - since?: Date - until?: Date - } = {}) { + async getAmlKycStatistics( + auth: OfficerAccount, + name: string, + filter: { + since?: Date; + until?: Date; + } = {}, + ) { const url = new URL(`aml/${auth.id}/kyc-statistics/${name}`, this.baseUrl); if (filter.since !== undefined) { - url.searchParams.set( - "start_date", - String(filter.since.getTime()) - ); + url.searchParams.set("start_date", String(filter.since.getTime())); } if (filter.until !== undefined) { - url.searchParams.set( - "end_date", - String(filter.until.getTime()) - ); + url.searchParams.set("end_date", String(filter.until.getTime())); } - const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { @@ -519,11 +839,14 @@ export class TalerExchangeHttpClient { * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions * */ - async getAmlDecisions(auth: OfficerAccount, params: PaginationParams & { - account?: string, - active?: boolean, - investigation?: boolean, - } = {}) { + async getAmlDecisions( + auth: OfficerAccount, + params: PaginationParams & { + account?: string; + active?: boolean; + investigation?: boolean; + } = {}, + ) { const url = new URL(`aml/${auth.id}/decisions`, this.baseUrl); addMerchantPaginationParams(url, params); @@ -534,7 +857,10 @@ export class TalerExchangeHttpClient { url.searchParams.set("active", params.active ? "YES" : "NO"); } if (params.investigation !== undefined) { - url.searchParams.set("investigation", params.investigation ? "YES" : "NO"); + url.searchParams.set( + "investigation", + params.investigation ? "YES" : "NO", + ); } const resp = await this.httpLib.fetch(url.href, { @@ -564,7 +890,11 @@ export class TalerExchangeHttpClient { * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-attributes-$H_PAYTO * */ - async getAmlAttributesForAccount(auth: OfficerAccount, account: string, params: PaginationParams = {}) { + async getAmlAttributesForAccount( + auth: OfficerAccount, + account: string, + params: PaginationParams = {}, + ) { const url = new URL(`aml/${auth.id}/attributes/${account}`, this.baseUrl); addMerchantPaginationParams(url, params); @@ -591,12 +921,14 @@ export class TalerExchangeHttpClient { } } - /** * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-attributes-$H_PAYTO * */ - async makeAmlDesicion(auth: OfficerAccount, decision: Omit<AmlDecisionRequest, "officer_sig">) { + async makeAmlDesicion( + auth: OfficerAccount, + decision: Omit<AmlDecisionRequest, "officer_sig">, + ) { const url = new URL(`aml/${auth.id}/decision`, this.baseUrl); const body = buildAMLDecisionSignature(auth.signingKey, decision); @@ -622,20 +954,57 @@ export class TalerExchangeHttpClient { } } + // RESERVE control + + /** + * https://docs.taler.net/core/api-exchange.html#post--reserves-$RESERVE_PUB-open + * + */ + async reserveOpen(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#get--reserves-attest-$RESERVE_PUB + * + */ + async getReserveAttributes(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--reserves-attest-$RESERVE_PUB + * + */ + async signReserveAttributes(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#post--reserves-$RESERVE_PUB-close + * + */ + async closeReserve(): Promise<never> { + throw Error("not yet implemented"); + } + + /** + * https://docs.taler.net/core/api-exchange.html#delete--reserves-$RESERVE_PUB + * + */ + async deleteReserve(): Promise<never> { + throw Error("not yet implemented"); + } } function buildKYCQuerySignature(key: SigningKey): string { - const sigBlob = buildSigPS( - TalerSignaturePurpose.AML_QUERY, - ).build(); + const sigBlob = buildSigPS(TalerSignaturePurpose.AML_QUERY).build(); return encodeCrock(eddsaSign(sigBlob, key)); } function buildAMLQuerySignature(key: SigningKey): string { - const sigBlob = buildSigPS( - TalerSignaturePurpose.AML_QUERY, - ).build(); + const sigBlob = buildSigPS(TalerSignaturePurpose.AML_QUERY).build(); return encodeCrock(eddsaSign(sigBlob, key)); } diff --git a/packages/taler-util/src/types-taler-exchange.ts b/packages/taler-util/src/types-taler-exchange.ts index 0cd722239..e9c80e045 100644 --- a/packages/taler-util/src/types-taler-exchange.ts +++ b/packages/taler-util/src/types-taler-exchange.ts @@ -573,11 +573,6 @@ export interface DenomGroupCommon { // Fee charged by the exchange for refunding a coin of this denomination. fee_refund: AmountString; - - // XOR of all the SHA-512 hash values of the denominations' public keys - // in this group. Note that for hashing, the binary format of the - // public keys is used, and not their base32 encoding. - hash: HashCodeString; } export interface DenomCommon { @@ -933,18 +928,18 @@ export namespace DenomKeyType { } } -export interface RsaBlindedDenominationSignature { - cipher: DenomKeyType.Rsa; - blinded_rsa_signature: string; -} +// export interface RsaBlindedDenominationSignature { +// cipher: DenomKeyType.Rsa; +// blinded_rsa_signature: string; +// } -export interface CSBlindedDenominationSignature { - cipher: DenomKeyType.ClauseSchnorr; -} +// export interface CSBlindedDenominationSignature { +// cipher: DenomKeyType.ClauseSchnorr; +// } -export type BlindedDenominationSignature = - | RsaBlindedDenominationSignature - | CSBlindedDenominationSignature; +// export type BlindedDenominationSignature = +// | RsaBlindedDenominationSignature +// | CSBlindedDenominationSignature; export const codecForRsaBlindedDenominationSignature = () => buildCodecForObject<RsaBlindedDenominationSignature>() @@ -1952,6 +1947,8 @@ export enum AmlState { pending = 1, frozen = 2, } +type Float = number; + export interface ExchangeKeysResponse { // libtool-style representation of the Exchange protocol version, see // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning @@ -1969,100 +1966,95 @@ export interface ExchangeKeysResponse { */ // How wallets should render this currency. - // currency_specification: CurrencySpecification; - - // // Absolute cost offset for the STEFAN curve used - // // to (over) approximate fees payable by amount. - // stefan_abs: AmountString; - - // // Factor to multiply the logarithm of the amount - // // with to (over) approximate fees payable by amount. - // // Note that the total to be paid is first to be - // // divided by the smallest denomination to obtain - // // the value that the logarithm is to be taken of. - // stefan_log: AmountString; - - // // Linear cost factor for the STEFAN curve used - // // to (over) approximate fees payable by amount. - // // - // // Note that this is a scalar, as it is multiplied - // // with the actual amount. - // stefan_lin: Float; - - // // Type of the asset. "fiat", "crypto", "regional" - // // or "stock". Wallets should adjust their UI/UX - // // based on this value. - // asset_type: string; - - // // Array of wire accounts operated by the exchange for - // // incoming wire transfers. - // accounts: WireAccount[]; - - // // Object mapping names of wire methods (i.e. "iban" or "x-taler-bank") - // // to wire fees. - // wire_fees: { method: AggregateTransferFee[] }; - - // // List of exchanges that this exchange is partnering - // // with to enable wallet-to-wallet transfers. - // wads: ExchangePartner[]; - - // // Set to true if this exchange allows the use - // // of reserves for rewards. - // // @deprecated in protocol v18. - // rewards_allowed: false; - - // // EdDSA master public key of the exchange, used to sign entries - // // in denoms and signkeys. - // master_public_key: EddsaPublicKey; - - // // Relative duration until inactive reserves are closed; - // // not signed (!), can change without notice. - // reserve_closing_delay: RelativeTime; - - // // Threshold amounts beyond which wallet should - // // trigger the KYC process of the issuing - // // exchange. Optional option, if not given there is no limit. - // // Currency must match currency. - // wallet_balance_limit_without_kyc?: AmountString[]; - - // // Denominations offered by this exchange - // denominations: DenomGroup[]; - - // // Compact EdDSA signature (binary-only) over the - // // contatentation of all of the master_sigs (in reverse - // // chronological order by group) in the arrays under - // // "denominations". Signature of TALER_ExchangeKeySetPS - // exchange_sig: EddsaSignature; - - // // Public EdDSA key of the exchange that was used to generate the signature. - // // Should match one of the exchange's signing keys from signkeys. It is given - // // explicitly as the client might otherwise be confused by clock skew as to - // // which signing key was used for the exchange_sig. - // exchange_pub: EddsaPublicKey; - - // // Denominations for which the exchange currently offers/requests recoup. - // recoup: Recoup[]; - - // // Array of globally applicable fees by time range. - // global_fees: GlobalFees[]; - - // // The date when the denomination keys were last updated. - // list_issue_date: Timestamp; - - // // Auditors of the exchange. - // auditors: AuditorKeys[]; - - // // The exchange's signing keys. - // signkeys: SignKey[]; - - // // Optional field with a dictionary of (name, object) pairs defining the - // // supported and enabled extensions, such as age_restriction. - // extensions?: { name: ExtensionManifest }; - - // // Signature by the exchange master key of the SHA-256 hash of the - // // normalized JSON-object of field extensions, if it was set. - // // The signature has purpose TALER_SIGNATURE_MASTER_EXTENSIONS. - // extensions_sig?: EddsaSignature; + currency_specification: CurrencySpecification; + + // Absolute cost offset for the STEFAN curve used + // to (over) approximate fees payable by amount. + stefan_abs: AmountString; + + // Factor to multiply the logarithm of the amount + // with to (over) approximate fees payable by amount. + // Note that the total to be paid is first to be + // divided by the smallest denomination to obtain + // the value that the logarithm is to be taken of. + stefan_log: AmountString; + + // Linear cost factor for the STEFAN curve used + // to (over) approximate fees payable by amount. + // + // Note that this is a scalar, as it is multiplied + // with the actual amount. + stefan_lin: Float; + + // Type of the asset. "fiat", "crypto", "regional" + // or "stock". Wallets should adjust their UI/UX + // based on this value. + asset_type: string; + + // Array of wire accounts operated by the exchange for + // incoming wire transfers. + accounts: WireAccount[]; + + // Object mapping names of wire methods (i.e. "iban" or "x-taler-bank") + // to wire fees. + wire_fees: { method: AggregateTransferFee[] }; + + // List of exchanges that this exchange is partnering + // with to enable wallet-to-wallet transfers. + wads: ExchangePartner[]; + + // EdDSA master public key of the exchange, used to sign entries + // in denoms and signkeys. + master_public_key: EddsaPublicKey; + + // Relative duration until inactive reserves are closed; + // not signed (!), can change without notice. + reserve_closing_delay: RelativeTime; + + // Threshold amounts beyond which wallet should + // trigger the KYC process of the issuing + // exchange. Optional option, if not given there is no limit. + // Currency must match currency. + wallet_balance_limit_without_kyc?: AmountString[]; + + // Denominations offered by this exchange + denominations: DenomGroup[]; + + // Compact EdDSA signature (binary-only) over the + // contatentation of all of the master_sigs (in reverse + // chronological order by group) in the arrays under + // "denominations". Signature of TALER_ExchangeKeySetPS + exchange_sig: EddsaSignature; + + // Public EdDSA key of the exchange that was used to generate the signature. + // Should match one of the exchange's signing keys from signkeys. It is given + // explicitly as the client might otherwise be confused by clock skew as to + // which signing key was used for the exchange_sig. + exchange_pub: EddsaPublicKey; + + // Denominations for which the exchange currently offers/requests recoup. + recoup: Recoup[]; + + // Array of globally applicable fees by time range. + global_fees: GlobalFees[]; + + // The date when the denomination keys were last updated. + list_issue_date: Timestamp; + + // Auditors of the exchange. + auditors: AuditorKeys[]; + + // The exchange's signing keys. + signkeys: SignKey[]; + + // Optional field with a dictionary of (name, object) pairs defining the + // supported and enabled extensions, such as age_restriction. + extensions?: { name: ExtensionManifest }; + + // Signature by the exchange master key of the SHA-256 hash of the + // normalized JSON-object of field extensions, if it was set. + // The signature has purpose TALER_SIGNATURE_MASTER_EXTENSIONS. + extensions_sig?: EddsaSignature; } interface ExtensionManifest { @@ -2273,6 +2265,28 @@ export const codecForExchangeKeys = (): Codec<ExchangeKeysResponse> => .property("version", codecForString()) .property("base_url", codecForString()) .property("currency", codecForString()) + .property("accounts", codecForAny()) + .property("asset_type", codecForAny()) + .property("auditors", codecForAny()) + .property("currency_specification", codecForAny()) + .property("denominations", codecForAny()) + .property("exchange_pub", codecForAny()) + .property("exchange_sig", codecForAny()) + .property("extensions", codecForAny()) + .property("extensions_sig", codecForAny()) + .property("global_fees", codecForAny()) + .property("list_issue_date", codecForAny()) + .property("master_public_key", codecForAny()) + .property("recoup", codecForAny()) + .property("reserve_closing_delay", codecForAny()) + .property("signkeys", codecForAny()) + .property("stefan_abs", codecForAny()) + .property("stefan_lin", codecForAny()) + .property("stefan_log", codecForAny()) + .property("wads", codecForAny()) + .property("wallet_balance_limit_without_kyc", codecForAny()) + .property("wire_fees", codecForAny()) + .build("TalerExchangeApi.ExchangeKeysResponse"); export const codecForEventCounter = (): Codec<EventCounter> => @@ -2493,3 +2507,325 @@ export const codecForKycProcessStartInformation = buildCodecForObject<KycProcessStartInformation>() .property("redirect_url", codecForURLString()) .build("TalerExchangeApi.KycProcessStartInformation"); + +export interface BatchWithdrawResponse { + // Array of blinded signatures, in the same order as was + // given in the request. + ev_sigs: WithdrawResponse[]; +} +export interface WithdrawResponse { + // The blinded signature over the 'coin_ev', affirms the coin's + // validity after unblinding. + ev_sig: BlindedDenominationSignature; +} +export type BlindedDenominationSignature = + | RsaBlindedDenominationSignature + | CSBlindedDenominationSignature; + +export interface RsaBlindedDenominationSignature { + cipher: DenomKeyType.Rsa; + + // (blinded) RSA signature + blinded_rsa_signature: BlindedRsaSignature; +} + +export interface CSBlindedDenominationSignature { + cipher: DenomKeyType.ClauseSchnorr; + + // Signer chosen bit value, 0 or 1, used + // in Clause Blind Schnorr to make the + // ROS problem harder. + b: Integer; + + // Blinded scalar calculated from c_b. + s: Cs25519Scalar; +} + +type BlindedRsaSignature = string; +type Cs25519Scalar = string; +type HashCode = string; +type EddsaSignature = string; +type EddsaPublicKey = string; +type Amount = AmountString; +type Base32 = string; + +export interface WithdrawError { + // Text describing the error. + hint: string; + + // Detailed error code. + code: Integer; + + // Amount left in the reserve. + balance: AmountString; + + // History of the reserve's activity, in the same format + // as returned by /reserve/$RID/history. + history: TransactionHistoryItem[]; +} +export type TransactionHistoryItem = + | AccountSetupTransaction + | ReserveWithdrawTransaction + | ReserveAgeWithdrawTransaction + | ReserveCreditTransaction + | ReserveClosingTransaction + | ReserveOpenRequestTransaction + | ReserveCloseRequestTransaction + | PurseMergeTransaction; + +enum TransactionHistoryType { + setup = "SETUP", + withdraw = "WITHDRAW", + ageWithdraw = "AGEWITHDRAW", + credit = "CREDIT", + closing = "CLOSING", + open = "OPEN", + close = "CLOSE", + merge = "MERGE", +} +interface AccountSetupTransaction { + type: TransactionHistoryType.setup; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // KYC fee agreed to by the reserve owner. + kyc_fee: AmountString; + + // Time when the KYC was triggered. + kyc_timestamp: Timestamp; + + // Hash of the wire details of the account. + // Note that this hash is unsalted and potentially + // private (as it could be inverted), hence access + // to this endpoint must be authorized using the + // private key of the reserve. + h_wire: HashCode; + + // Signature created with the reserve's private key. + // Must be of purpose TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST over + // a TALER_AccountSetupRequestSignaturePS. + reserve_sig: EddsaSignature; +} +interface ReserveWithdrawTransaction { + type: "WITHDRAW"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // Amount withdrawn. + amount: Amount; + + // Hash of the denomination public key of the coin. + h_denom_pub: HashCode; + + // Hash of the blinded coin to be signed. + h_coin_envelope: HashCode; + + // Signature over a TALER_WithdrawRequestPS + // with purpose TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW + // created with the reserve's private key. + reserve_sig: EddsaSignature; + + // Fee that is charged for withdraw. + withdraw_fee: Amount; +} +interface ReserveAgeWithdrawTransaction { + type: "AGEWITHDRAW"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // Total Amount withdrawn. + amount: Amount; + + // Commitment of all n*kappa blinded coins. + h_commitment: HashCode; + + // Signature over a TALER_AgeWithdrawRequestPS + // with purpose TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW + // created with the reserve's private key. + reserve_sig: EddsaSignature; + + // Fee that is charged for withdraw. + withdraw_fee: Amount; +} +interface ReserveCreditTransaction { + type: "CREDIT"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // Amount deposited. + amount: Amount; + + // Sender account payto:// URL. + sender_account_url: string; + + // Opaque identifier internal to the exchange that + // uniquely identifies the wire transfer that credited the reserve. + wire_reference: Integer; + + // Timestamp of the incoming wire transfer. + timestamp: Timestamp; +} +interface ReserveClosingTransaction { + type: "CLOSING"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // Closing balance. + amount: Amount; + + // Closing fee charged by the exchange. + closing_fee: Amount; + + // Wire transfer subject. + wtid: Base32; + + // payto:// URI of the wire account into which the funds were returned to. + receiver_account_details: string; + + // This is a signature over a + // struct TALER_ReserveCloseConfirmationPS with purpose + // TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED. + exchange_sig: EddsaSignature; + + // Public key used to create 'exchange_sig'. + exchange_pub: EddsaPublicKey; + + // Time when the reserve was closed. + timestamp: Timestamp; +} +interface ReserveOpenRequestTransaction { + type: "OPEN"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // Open fee paid from the reserve. + open_fee: Amount; + + // This is a signature over + // a struct TALER_ReserveOpenPS with purpose + // TALER_SIGNATURE_WALLET_RESERVE_OPEN. + reserve_sig: EddsaSignature; + + // Timestamp of the open request. + request_timestamp: Timestamp; + + // Requested expiration. + requested_expiration: Timestamp; + + // Requested number of free open purses. + requested_min_purses: Integer; +} +interface ReserveCloseRequestTransaction { + type: "CLOSE"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // This is a signature over + // a struct TALER_ReserveClosePS with purpose + // TALER_SIGNATURE_WALLET_RESERVE_CLOSE. + reserve_sig: EddsaSignature; + + // Target account payto://, optional. + h_payto?: PaytoHash; + + // Timestamp of the close request. + request_timestamp: Timestamp; +} +interface PurseMergeTransaction { + type: "MERGE"; + + // Offset of this entry in the reserve history. + // Useful to request incremental histories via + // the "start" query parameter. + history_offset: Integer; + + // SHA-512 hash of the contact of the purse. + h_contract_terms: HashCode; + + // EdDSA public key used to approve merges of this purse. + merge_pub: EddsaPublicKey; + + // Minimum age required for all coins deposited into the purse. + min_age: Integer; + + // Number that identifies who created the purse + // and how it was paid for. + flags: Integer; + + // Purse public key. + purse_pub: EddsaPublicKey; + + // EdDSA signature of the account/reserve affirming the merge + // over a TALER_AccountMergeSignaturePS. + // Must be of purpose TALER_SIGNATURE_ACCOUNT_MERGE + reserve_sig: EddsaSignature; + + // Client-side timestamp of when the merge request was made. + merge_timestamp: Timestamp; + + // Indicative time by which the purse should expire + // if it has not been merged into an account. At this + // point, all of the deposits made should be + // auto-refunded. + purse_expiration: Timestamp; + + // Purse fee the reserve owner paid for the purse creation. + purse_fee: Amount; + + // Total amount merged into the reserve. + // (excludes fees). + amount: Amount; + + // True if the purse was actually merged. + // If false, only the purse_fee has an impact + // on the reserve balance! + merged: boolean; +} + +interface DenominationExpiredMessage { + // Taler error code. Note that beyond + // expiration this message format is also + // used if the key is not yet valid, or + // has been revoked. + code: number; + + // Signature by the exchange over a + // TALER_DenominationExpiredAffirmationPS. + // Must have purpose TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED. + exchange_sig: EddsaSignature; + + // Public key of the exchange used to create + // the 'exchange_sig. + exchange_pub: EddsaPublicKey; + + // Hash of the denomination public key that is unknown. + h_denom_pub: HashCode; + + // When was the signature created. + timestamp: Timestamp; + + // What kind of operation was requested that now + // failed? + oper: string; +} |