From c93d53d6473934f1d6082a25e2fe26b90c55bb91 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 4 Dec 2023 21:33:11 +0100 Subject: wallet-core: work around a libeufin-bank bug We now ignore the result of POSTing to the bank integration API, since the implementation is outdated and doesn't match the API spec and wallet-core type validators. --- packages/taler-util/src/codec.ts | 19 +++- packages/taler-util/src/http-client/types.ts | 102 ++++++++++++--------- .../taler-wallet-core/src/operations/withdraw.ts | 22 ++--- 3 files changed, 85 insertions(+), 58 deletions(-) diff --git a/packages/taler-util/src/codec.ts b/packages/taler-util/src/codec.ts index 670111b75..701fc8835 100644 --- a/packages/taler-util/src/codec.ts +++ b/packages/taler-util/src/codec.ts @@ -14,12 +14,17 @@ GNU Taler; see the file COPYING. If not, see */ +import { j2s } from "./helpers.js"; +import { Logger } from "./logging.js"; + /** * Type-safe codecs for converting from/to JSON. */ /* eslint-disable @typescript-eslint/ban-types */ +const logger = new Logger("codec.ts"); + /** * Error thrown when decoding fails. */ @@ -334,21 +339,22 @@ export function codecForStringURL(shouldEndWithSlash?: boolean): Codec { } if (shouldEndWithSlash && !x.endsWith("/")) { throw new DecodingError( - `expected URL string that ends with slash at ${renderContext(c)} but got ${x}`, + `expected URL string that ends with slash at ${renderContext( + c, + )} but got ${x}`, ); } try { - const url = new URL(x) + const url = new URL(x); return x; - } catch(e) { + } catch (e) { if (e instanceof Error) { - throw new DecodingError(e.message) + throw new DecodingError(e.message); } else { throw new DecodingError( `expected an URL string at ${renderContext(c)} but got "${x}"`, ); } - } }, }; @@ -465,6 +471,9 @@ export function codecForEither>>( continue; } } + if (logger.shouldLogTrace()) { + logger.trace(`offending value: ${j2s(x)}`); + } throw new DecodingError( `No alternative matched at at ${renderContext(c)}`, ); diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index e25bd6ebd..436a06874 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -358,16 +358,22 @@ export const codecForChallengeContactData = export const codecForWithdrawalPublicInfo = (): Codec => buildCodecForObject() - .property("username", codecForString(),) - .property("amount", codecForAmountString(),) - .property("selected_exchange_account", codecOptional(codecForPaytoString())) + .property("username", codecForString()) + .property("amount", codecForAmountString()) + .property( + "selected_exchange_account", + codecOptional(codecForPaytoString()), + ) .property("selected_reserve_pub", codecOptional(codecForString())) - .property("status", codecForEither( - codecForConstString("pending"), - codecForConstString("selected"), - codecForConstString("aborted"), - codecForConstString("confirmed"), - ),) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("selected"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) .build("TalerCorebankApi.WithdrawalPublicInfo"); export const codecForBankAccountTransactionsResponse = @@ -397,7 +403,6 @@ export const codecForBankAccountTransactionInfo = .property("date", codecForTimestamp) .build("TalerCorebankApi.BankAccountTransactionInfo"); - export const codecForCreateTransactionResponse = (): Codec => buildCodecForObject() @@ -410,7 +415,6 @@ export const codecForRegisterAccountResponse = .property("internal_payto_uri", codecForPaytoString()) .build("TalerCorebankApi.RegisterAccountResponse"); - export const codecForBankAccountCreateWithdrawalResponse = (): Codec => buildCodecForObject() @@ -559,12 +563,15 @@ export const codecForBankVersion = export const codecForBankWithdrawalOperationStatus = (): Codec => buildCodecForObject() - .property("status", codecForEither( - codecForConstString("pending"), - codecForConstString("selected"), - codecForConstString("aborted"), - codecForConstString("confirmed") - )) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("selected"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) .property("amount", codecForAmountString()) .property("sender_wire", codecOptional(codecForPaytoString())) .property("suggested_exchange", codecOptional(codecForString())) @@ -577,11 +584,15 @@ export const codecForBankWithdrawalOperationStatus = export const codecForBankWithdrawalOperationPostResponse = (): Codec => buildCodecForObject() - .property("status", codecForEither( - codecForConstString("selected"), - codecForConstString("aborted"), - codecForConstString("confirmed") - )) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("selected"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) .property("confirm_transfer_url", codecOptional(codecForURL())) .build("TalerBankIntegrationApi.BankWithdrawalOperationPostResponse"); @@ -789,20 +800,26 @@ export const codecForConversionInfo = .property("cashin_fee", codecForAmountString()) .property("cashin_min_amount", codecForAmountString()) .property("cashin_ratio", codecForDecimalNumber()) - .property("cashin_rounding_mode", codecForEither( - codecForConstString("zero"), - codecForConstString("up"), - codecForConstString("nearest") - )) + .property( + "cashin_rounding_mode", + codecForEither( + codecForConstString("zero"), + codecForConstString("up"), + codecForConstString("nearest"), + ), + ) .property("cashin_tiny_amount", codecForAmountString()) .property("cashout_fee", codecForAmountString()) .property("cashout_min_amount", codecForAmountString()) .property("cashout_ratio", codecForDecimalNumber()) - .property("cashout_rounding_mode", codecForEither( - codecForConstString("zero"), - codecForConstString("up"), - codecForConstString("nearest") - )) + .property( + "cashout_rounding_mode", + codecForEither( + codecForConstString("zero"), + codecForConstString("up"), + codecForConstString("nearest"), + ), + ) .property("cashout_tiny_amount", codecForAmountString()) .build("ConversionBankConfig.ConversionInfo"); @@ -820,7 +837,7 @@ export const codecForConversionBankConfig = .property("fiat_currency_specification", codecForCurrencySpecificiation()) .property("conversion_rate", codecOptional(codecForConversionInfo())) - .build("ConversionBankConfig.IntegrationConfig") + .build("ConversionBankConfig.IntegrationConfig"); // export const codecFor = // (): Codec => @@ -861,17 +878,20 @@ interface CSCoinEnvelope { // a 256-bit nonce, converted to Crockford Base32. type DenominationBlindingKeyP = string; -const codecForURL = codecForString -const codecForLibtoolVersion = codecForString -const codecForCurrencyName = codecForString -const codecForDecimalNumber = codecForString +const codecForURL = codecForString; +const codecForLibtoolVersion = codecForString; +const codecForCurrencyName = codecForString; +const codecForDecimalNumber = codecForString; enum TanChannel { SMS = "sms", EMAIL = "email", } -export type WithdrawalOperationStatus = "pending" | "selected" | "aborted" | "confirmed" - +export type WithdrawalOperationStatus = + | "pending" + | "selected" + | "aborted" + | "confirmed"; export namespace TalerWireGatewayApi { export interface TransferResponse { @@ -1125,7 +1145,7 @@ export namespace TalerBankConversionApi { // Extra conversion rate information. // Only present if server opts in to report the static conversion rate. - conversion_rate?: ConversionInfo + conversion_rate?: ConversionInfo; } export interface CashinConversionResponse { @@ -1205,7 +1225,6 @@ export namespace TalerBankIntegrationApi { // confirmed: the transfer has been confirmed and registered by the bank status: WithdrawalOperationStatus; - // Amount that will be withdrawn with this operation // (raw amount without fee considerations). amount: AmountString; @@ -1338,7 +1357,6 @@ export namespace TalerCorebankApi { selected_exchange_account?: PaytoString; } - export interface BankAccountTransactionsResponse { transactions: BankAccountTransactionInfo[]; } diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 4a1fdbfae..63c811e60 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -60,6 +60,7 @@ import { WithdrawalExchangeAccountDetails, addPaytoQueryParams, canonicalizeBaseUrl, + codecForAny, codecForBankWithdrawalOperationPostResponse, codecForCashinConversionResponse, codecForConversionBankConfig, @@ -868,10 +869,10 @@ async function handleKycRequired( amlStatus === AmlStatus.normal || amlStatus === undefined ? WithdrawalGroupStatus.PendingKyc : amlStatus === AmlStatus.pending - ? WithdrawalGroupStatus.PendingAml - : amlStatus === AmlStatus.fronzen - ? WithdrawalGroupStatus.SuspendedAml - : assertUnreachable(amlStatus); + ? WithdrawalGroupStatus.PendingAml + : amlStatus === AmlStatus.fronzen + ? WithdrawalGroupStatus.SuspendedAml + : assertUnreachable(amlStatus); notificationKycUrl = kycUrl; @@ -1207,7 +1208,8 @@ export async function updateWithdrawalDenoms( denom.verificationStatus === DenominationVerificationStatus.Unverified ) { logger.trace( - `Validating denomination (${current + 1}/${denominations.length + `Validating denomination (${current + 1}/${ + denominations.length }) signature of ${denom.denomPubHash}`, ); let valid = false; @@ -1853,7 +1855,7 @@ export async function getExchangeWithdrawalInfo( ) { logger.warn( `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + - `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`, + `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`, ); } } @@ -2097,10 +2099,8 @@ async function registerReserveWithBank( body: reqBody, timeout: getReserveRequestTimeout(withdrawalGroup), }); - await readSuccessResponseJsonOrThrow( - httpResp, - codecForBankWithdrawalOperationPostResponse(), - ); + // FIXME: libeufin-bank currently doesn't return a response in the right format, so we don't validate at all. + await readSuccessResponseJsonOrThrow(httpResp, codecForAny()); const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { @@ -2622,7 +2622,7 @@ async function fetchWithdrawalAccountInfo( configResp, codecForConversionBankConfig(), ); - currencySpecification = configParsed.fiat_currency_specification + currencySpecification = configParsed.fiat_currency_specification; if (req.reservePub) { } } else { -- cgit v1.2.3