summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-03-14 18:31:30 +0100
committerFlorian Dold <florian@dold.me>2022-03-14 18:31:36 +0100
commit332745862e728dc5e79a424698b2736c4f2683bf (patch)
tree6617d10c145868741f751853261c9c126b6f580e /packages/taler-wallet-core
parent9e7ee06ad1870339d011a0be27867cc36f94490d (diff)
downloadwallet-core-332745862e728dc5e79a424698b2736c4f2683bf.tar.gz
wallet-core-332745862e728dc5e79a424698b2736c4f2683bf.tar.bz2
wallet-core-332745862e728dc5e79a424698b2736c4f2683bf.zip
wallet: towards db-less benchmarking, some refactoring
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r--packages/taler-wallet-core/src/bank-api-client.ts249
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts23
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts33
-rw-r--r--packages/taler-wallet-core/src/index.ts11
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts41
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts34
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts13
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts20
-rw-r--r--packages/taler-wallet-core/src/operations/reserves.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/testing.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts15
-rw-r--r--packages/taler-wallet-core/src/util/http.ts6
12 files changed, 373 insertions, 88 deletions
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts b/packages/taler-wallet-core/src/bank-api-client.ts
new file mode 100644
index 000000000..744c3b833
--- /dev/null
+++ b/packages/taler-wallet-core/src/bank-api-client.ts
@@ -0,0 +1,249 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Client for the Taler (demo-)bank.
+ */
+
+/**
+ * Imports.
+ */
+import {
+ AmountString,
+ buildCodecForObject,
+ Codec,
+ codecForString,
+ encodeCrock,
+ getRandomBytes,
+} from "@gnu-taler/taler-util";
+import {
+ HttpRequestLibrary,
+ readSuccessResponseJsonOrErrorCode,
+ readSuccessResponseJsonOrThrow,
+} from "./index.browser.js";
+
+export enum CreditDebitIndicator {
+ Credit = "credit",
+ Debit = "debit",
+}
+
+export interface BankAccountBalanceResponse {
+ balance: {
+ amount: AmountString;
+ credit_debit_indicator: CreditDebitIndicator;
+ };
+}
+
+export interface BankServiceHandle {
+ readonly baseUrl: string;
+ readonly http: HttpRequestLibrary;
+}
+
+export interface BankUser {
+ username: string;
+ password: string;
+ accountPaytoUri: string;
+}
+
+export interface WithdrawalOperationInfo {
+ withdrawal_id: string;
+ taler_withdraw_uri: string;
+}
+
+/**
+ * FIXME: Rename, this is not part of the integration test harness anymore.
+ */
+export interface HarnessExchangeBankAccount {
+ accountName: string;
+ accountPassword: string;
+ accountPaytoUri: string;
+ wireGatewayApiBaseUrl: string;
+}
+
+/**
+ * Helper function to generate the "Authorization" HTTP header.
+ */
+function makeBasicAuthHeader(username: string, password: string): string {
+ const auth = `${username}:${password}`;
+ const authEncoded: string = Buffer.from(auth).toString("base64");
+ return `Basic ${authEncoded}`;
+}
+
+const codecForWithdrawalOperationInfo = (): Codec<WithdrawalOperationInfo> =>
+ buildCodecForObject<WithdrawalOperationInfo>()
+ .property("withdrawal_id", codecForString())
+ .property("taler_withdraw_uri", codecForString())
+ .build("WithdrawalOperationInfo");
+
+export namespace BankApi {
+ export async function registerAccount(
+ bank: BankServiceHandle,
+ username: string,
+ password: string,
+ ): Promise<BankUser> {
+ const url = new URL("testing/register", bank.baseUrl);
+ const resp = await bank.http.postJson(url.href, { username, password });
+ let paytoUri = `payto://x-taler-bank/localhost/${username}`;
+ if (resp.status !== 200 && resp.status !== 202) {
+ throw new Error();
+ }
+ try {
+ const respJson = await resp.json();
+ // LibEuFin demobank returns payto URI in response
+ if (respJson.paytoUri) {
+ paytoUri = respJson.paytoUri;
+ }
+ } catch (e) {}
+ return {
+ password,
+ username,
+ accountPaytoUri: paytoUri,
+ };
+ }
+
+ export async function createRandomBankUser(
+ bank: BankServiceHandle,
+ ): Promise<BankUser> {
+ const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+ const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+ return await registerAccount(bank, username, password);
+ }
+
+ export async function adminAddIncoming(
+ bank: BankServiceHandle,
+ params: {
+ exchangeBankAccount: HarnessExchangeBankAccount;
+ amount: string;
+ reservePub: string;
+ debitAccountPayto: string;
+ },
+ ) {
+ let maybeBaseUrl = bank.baseUrl;
+ let url = new URL(
+ `taler-wire-gateway/${params.exchangeBankAccount.accountName}/admin/add-incoming`,
+ maybeBaseUrl,
+ );
+ await bank.http.postJson(
+ url.href,
+ {
+ amount: params.amount,
+ reserve_pub: params.reservePub,
+ debit_account: params.debitAccountPayto,
+ },
+ {
+ headers: {
+ Authorization: makeBasicAuthHeader(
+ params.exchangeBankAccount.accountName,
+ params.exchangeBankAccount.accountPassword,
+ ),
+ },
+ },
+ );
+ }
+
+ export async function confirmWithdrawalOperation(
+ bank: BankServiceHandle,
+ bankUser: BankUser,
+ wopi: WithdrawalOperationInfo,
+ ): Promise<void> {
+ const url = new URL(
+ `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/confirm`,
+ bank.baseUrl,
+ );
+ await bank.http.postJson(
+ url.href,
+ {},
+ {
+ headers: {
+ Authorization: makeBasicAuthHeader(
+ bankUser.username,
+ bankUser.password,
+ ),
+ },
+ },
+ );
+ }
+
+ export async function abortWithdrawalOperation(
+ bank: BankServiceHandle,
+ bankUser: BankUser,
+ wopi: WithdrawalOperationInfo,
+ ): Promise<void> {
+ const url = new URL(
+ `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/abort`,
+ bank.baseUrl,
+ );
+ await bank.http.postJson(
+ url.href,
+ {},
+ {
+ headers: {
+ Authorization: makeBasicAuthHeader(
+ bankUser.username,
+ bankUser.password,
+ ),
+ },
+ },
+ );
+ }
+}
+
+export namespace BankAccessApi {
+ export async function getAccountBalance(
+ bank: BankServiceHandle,
+ bankUser: BankUser,
+ ): Promise<BankAccountBalanceResponse> {
+ const url = new URL(`accounts/${bankUser.username}`, bank.baseUrl);
+ const resp = await bank.http.get(url.href, {
+ headers: {
+ Authorization: makeBasicAuthHeader(
+ bankUser.username,
+ bankUser.password,
+ ),
+ },
+ });
+ return await resp.json();
+ }
+
+ export async function createWithdrawalOperation(
+ bank: BankServiceHandle,
+ bankUser: BankUser,
+ amount: string,
+ ): Promise<WithdrawalOperationInfo> {
+ const url = new URL(
+ `accounts/${bankUser.username}/withdrawals`,
+ bank.baseUrl,
+ );
+ const resp = await bank.http.postJson(
+ url.href,
+ {
+ amount,
+ },
+ {
+ headers: {
+ Authorization: makeBasicAuthHeader(
+ bankUser.username,
+ bankUser.password,
+ ),
+ },
+ },
+ );
+ return readSuccessResponseJsonOrThrow(
+ resp,
+ codecForWithdrawalOperationInfo(),
+ );
+ }
+}
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index 16446bb9e..b5a5950b1 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -22,20 +22,22 @@
/**
* Imports.
*/
-import { CoinRecord, DenominationRecord, WireFee } from "../../db.js";
+import { DenominationRecord, WireFee } from "../../db.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
import {
+ BlindedDenominationSignature,
CoinDepositPermission,
CoinEnvelope,
RecoupRefreshRequest,
RecoupRequest,
+ UnblindedSignature,
} from "@gnu-taler/taler-util";
import {
BenchmarkResult,
- PlanchetCreationResult,
+ WithdrawalPlanchet,
PlanchetCreationRequest,
DepositInfo,
MakeSyncSignatureRequest,
@@ -324,10 +326,19 @@ export class CryptoApi {
return p;
}
- createPlanchet(
- req: PlanchetCreationRequest,
- ): Promise<PlanchetCreationResult> {
- return this.doRpc<PlanchetCreationResult>("createPlanchet", 1, req);
+ createPlanchet(req: PlanchetCreationRequest): Promise<WithdrawalPlanchet> {
+ return this.doRpc<WithdrawalPlanchet>("createPlanchet", 1, req);
+ }
+
+ unblindDenominationSignature(req: {
+ planchet: WithdrawalPlanchet;
+ evSig: BlindedDenominationSignature;
+ }): Promise<UnblindedSignature> {
+ return this.doRpc<UnblindedSignature>(
+ "unblindDenominationSignature",
+ 1,
+ req,
+ );
}
createTipPlanchet(req: DeriveTipRequest): Promise<DerivedTipPlanchet> {
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index af77e2be4..15a086ae1 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -53,7 +53,7 @@ import {
Logger,
MakeSyncSignatureRequest,
PlanchetCreationRequest,
- PlanchetCreationResult,
+ WithdrawalPlanchet,
randomBytes,
RecoupRefreshRequest,
RecoupRequest,
@@ -70,6 +70,9 @@ import {
Timestamp,
timestampTruncateToSecond,
typedArrayConcat,
+ BlindedDenominationSignature,
+ RsaUnblindedSignature,
+ UnblindedSignature,
} from "@gnu-taler/taler-util";
import bigint from "big-integer";
import { DenominationRecord, WireFee } from "../../db.js";
@@ -169,7 +172,7 @@ export class CryptoImplementation {
*/
async createPlanchet(
req: PlanchetCreationRequest,
- ): Promise<PlanchetCreationResult> {
+ ): Promise<WithdrawalPlanchet> {
const denomPub = req.denomPub;
if (denomPub.cipher === DenomKeyType.Rsa) {
const reservePub = decodeCrock(req.reservePub);
@@ -200,7 +203,7 @@ export class CryptoImplementation {
priv: req.reservePriv,
});
- const planchet: PlanchetCreationResult = {
+ const planchet: WithdrawalPlanchet = {
blindingKey: encodeCrock(derivedPlanchet.bks),
coinEv,
coinPriv: encodeCrock(derivedPlanchet.coinPriv),
@@ -428,6 +431,30 @@ export class CryptoImplementation {
};
}
+ unblindDenominationSignature(req: {
+ planchet: WithdrawalPlanchet;
+ evSig: BlindedDenominationSignature;
+ }): UnblindedSignature {
+ if (req.evSig.cipher === DenomKeyType.Rsa) {
+ if (req.planchet.denomPub.cipher !== DenomKeyType.Rsa) {
+ throw new Error(
+ "planchet cipher does not match blind signature cipher",
+ );
+ }
+ const denomSig = rsaUnblind(
+ decodeCrock(req.evSig.blinded_rsa_signature),
+ decodeCrock(req.planchet.denomPub.rsa_public_key),
+ decodeCrock(req.planchet.blindingKey),
+ );
+ return {
+ cipher: DenomKeyType.Rsa,
+ rsa_signature: encodeCrock(denomSig),
+ };
+ } else {
+ throw Error(`unblinding for cipher ${req.evSig.cipher} not implemented`);
+ }
+ }
+
/**
* Unblind a blindly signed value.
*/
diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts
index 179ba6b8f..c657290f1 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -36,7 +36,7 @@ export * from "./db-utils.js";
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation.js";
export type { CryptoWorker } from "./crypto/workers/cryptoWorkerInterface.js";
export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi.js";
-export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorker.js"
+export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorker.js";
export * from "./pending-types.js";
@@ -47,3 +47,12 @@ export * from "./wallet.js";
export * from "./operations/backup/index.js";
export { makeEventId } from "./operations/transactions.js";
+
+export * from "./operations/exchanges.js";
+
+export * from "./bank-api-client.js";
+
+export * from "./operations/reserves.js";
+export * from "./operations/withdraw.js";
+
+export * from "./crypto/workers/synchronousWorkerFactory.js";
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index e45da7b4c..a5d6c93cf 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -20,6 +20,7 @@ import {
buildCodecForObject,
canonicalJson,
Codec,
+ codecForDepositSuccess,
codecForString,
codecForTimestamp,
codecOptional,
@@ -32,6 +33,7 @@ import {
GetFeeForDepositRequest,
getRandomBytes,
getTimestampNow,
+ hashWire,
Logger,
NotificationType,
parsePaytoUri,
@@ -57,7 +59,6 @@ import {
generateDepositPermissions,
getCandidatePayCoins,
getTotalPaymentCost,
- hashWire,
} from "./pay.js";
import { getTotalRefreshCost } from "./refresh.js";
@@ -66,43 +67,6 @@ import { getTotalRefreshCost } from "./refresh.js";
*/
const logger = new Logger("deposits.ts");
-interface DepositSuccess {
- // Optional base URL of the exchange for looking up wire transfers
- // associated with this transaction. If not given,
- // the base URL is the same as the one used for this request.
- // Can be used if the base URL for /transactions/ differs from that
- // for /coins/, i.e. for load balancing. Clients SHOULD
- // respect the transaction_base_url if provided. Any HTTP server
- // belonging to an exchange MUST generate a 307 or 308 redirection
- // to the correct base URL should a client uses the wrong base
- // URL, or if the base URL has changed since the deposit.
- transaction_base_url?: string;
-
- // timestamp when the deposit was received by the exchange.
- exchange_timestamp: Timestamp;
-
- // the EdDSA signature of TALER_DepositConfirmationPS using a current
- // signing key of the exchange affirming the successful
- // deposit and that the exchange will transfer the funds after the refund
- // deadline, or as soon as possible if the refund deadline is zero.
- exchange_sig: string;
-
- // public EdDSA key of the exchange that was used to
- // generate the signature.
- // Should match one of the exchange's signing keys from /keys. It is given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- exchange_pub: string;
-}
-
-const codecForDepositSuccess = (): Codec<DepositSuccess> =>
- buildCodecForObject<DepositSuccess>()
- .property("exchange_pub", codecForString())
- .property("exchange_sig", codecForString())
- .property("exchange_timestamp", codecForTimestamp)
- .property("transaction_base_url", codecOptional(codecForString()))
- .build("DepositSuccess");
-
async function resetDepositGroupRetry(
ws: InternalWalletState,
depositGroupId: string,
@@ -202,7 +166,6 @@ async function processDepositGroupImpl(
}
const perm = depositPermissions[i];
let requestBody: any;
- logger.info("creating v10 deposit request");
requestBody = {
contribution: Amounts.stringify(perm.contribution),
merchant_payto_uri: depositGroup.wire.payto_uri,
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index 9d4a56fff..2006b792f 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -43,6 +43,7 @@ import {
codecForAny,
DenominationPubKey,
DenomKeyType,
+ ExchangeKeysJson,
} from "@gnu-taler/taler-util";
import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
import { CryptoApi } from "../crypto/workers/cryptoApi.js";
@@ -292,12 +293,37 @@ async function validateWireInfo(
};
}
+export interface ExchangeInfo {
+ wire: ExchangeWireJson;
+ keys: ExchangeKeysDownloadResult;
+}
+
+export async function downloadExchangeInfo(
+ exchangeBaseUrl: string,
+ http: HttpRequestLibrary,
+): Promise<ExchangeInfo> {
+ const wireInfo = await downloadExchangeWireInfo(
+ exchangeBaseUrl,
+ http,
+ Duration.getForever(),
+ );
+ const keysInfo = await downloadExchangeKeysInfo(
+ exchangeBaseUrl,
+ http,
+ Duration.getForever(),
+ );
+ return {
+ keys: keysInfo,
+ wire: wireInfo,
+ };
+}
+
/**
* Fetch wire information for an exchange.
*
* @param exchangeBaseUrl Exchange base URL, assumed to be already normalized.
*/
-async function downloadExchangeWithWireInfo(
+async function downloadExchangeWireInfo(
exchangeBaseUrl: string,
http: HttpRequestLibrary,
timeout: Duration,
@@ -374,7 +400,7 @@ interface ExchangeKeysDownloadResult {
/**
* Download and validate an exchange's /keys data.
*/
-async function downloadKeysInfo(
+async function downloadExchangeKeysInfo(
baseUrl: string,
http: HttpRequestLibrary,
timeout: Duration,
@@ -526,10 +552,10 @@ async function updateExchangeFromUrlImpl(
const timeout = getExchangeRequestTimeout();
- const keysInfo = await downloadKeysInfo(baseUrl, ws.http, timeout);
+ const keysInfo = await downloadExchangeKeysInfo(baseUrl, ws.http, timeout);
logger.info("updating exchange /wire info");
- const wireInfoDownload = await downloadExchangeWithWireInfo(
+ const wireInfoDownload = await downloadExchangeWireInfo(
baseUrl,
ws.http,
timeout,
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 6001cac4f..9844dc52e 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -113,19 +113,6 @@ import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
const logger = new Logger("pay.ts");
/**
- * FIXME: Move this to crypto worker or at least talerCrypto.ts
- */
-export function hashWire(paytoUri: string, salt: string): string {
- const r = kdf(
- 64,
- stringToBytes(paytoUri + "\0"),
- decodeCrock(salt),
- stringToBytes("merchant-wire-signature"),
- );
- return encodeCrock(r);
-}
-
-/**
* Compute the total cost of a payment to the customer.
*
* This includes the amount taken by the merchant, fees (wire/deposit) contributed
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index 550119de1..cc2a1c566 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -17,6 +17,7 @@
import {
DenomKeyType,
encodeCrock,
+ ExchangeMeltRequest,
ExchangeProtocolVersion,
ExchangeRefreshRevealRequest,
getRandomBytes,
@@ -394,17 +395,14 @@ async function refreshMelt(
`coins/${oldCoin.coinPub}/melt`,
oldCoin.exchangeBaseUrl,
);
- let meltReqBody: any;
- if (oldDenom.denomPub.cipher === DenomKeyType.Rsa) {
- meltReqBody = {
- coin_pub: oldCoin.coinPub,
- confirm_sig: derived.confirmSig,
- denom_pub_hash: oldCoin.denomPubHash,
- denom_sig: oldCoin.denomSig,
- rc: derived.hash,
- value_with_fee: Amounts.stringify(derived.meltValueWithFee),
- };
- }
+ const meltReqBody: ExchangeMeltRequest = {
+ coin_pub: oldCoin.coinPub,
+ confirm_sig: derived.confirmSig,
+ denom_pub_hash: oldCoin.denomPubHash,
+ denom_sig: oldCoin.denomSig,
+ rc: derived.hash,
+ value_with_fee: Amounts.stringify(derived.meltValueWithFee),
+ };
const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => {
return await ws.http.postJson(reqUrl.href, meltReqBody, {
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts
index a16d3ec31..d91ce89f1 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -780,7 +780,7 @@ export async function createTalerWithdrawReserve(
selectedExchange: string,
): Promise<AcceptWithdrawalResponse> {
await updateExchangeFromUrl(ws, selectedExchange);
- const withdrawInfo = await getBankWithdrawalInfo(ws, talerWithdrawUri);
+ const withdrawInfo = await getBankWithdrawalInfo(ws.http, talerWithdrawUri);
const exchangePaytoUri = await getExchangePaytoUri(
ws,
selectedExchange,
diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts
index d6f0626dd..93f48fb83 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -74,7 +74,7 @@ function makeId(length: number): string {
/**
* Helper function to generate the "Authorization" HTTP header.
*/
-function makeAuth(username: string, password: string): string {
+function makeBasicAuthHeader(username: string, password: string): string {
const auth = `${username}:${password}`;
const authEncoded: string = Buffer.from(auth).toString("base64");
return `Basic ${authEncoded}`;
@@ -89,7 +89,7 @@ export async function withdrawTestBalance(
const bankUser = await registerRandomBankUser(ws.http, bankBaseUrl);
logger.trace(`Registered bank user ${JSON.stringify(bankUser)}`);
- const wresp = await createBankWithdrawalUri(
+ const wresp = await createDemoBankWithdrawalUri(
ws.http,
bankBaseUrl,
bankUser,
@@ -119,7 +119,11 @@ function getMerchantAuthHeader(m: MerchantBackendInfo): Record<string, string> {
return {};
}
-async function createBankWithdrawalUri(
+/**
+ * Use the testing API of a demobank to create a taler://withdraw URI
+ * that the wallet can then use to make a withdrawal.
+ */
+export async function createDemoBankWithdrawalUri(
http: HttpRequestLibrary,
bankBaseUrl: string,
bankUser: BankUser,
@@ -136,7 +140,7 @@ async function createBankWithdrawalUri(
},
{
headers: {
- Authorization: makeAuth(bankUser.username, bankUser.password),
+ Authorization: makeBasicAuthHeader(bankUser.username, bankUser.password),
},
},
);
@@ -159,7 +163,7 @@ async function confirmBankWithdrawalUri(
{},
{
headers: {
- Authorization: makeAuth(bankUser.username, bankUser.password),
+ Authorization: makeBasicAuthHeader(bankUser.username, bankUser.password),
},
},
);
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index ae3763a02..392cecf0b 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -59,7 +59,10 @@ import {
WithdrawalGroupRecord,
} from "../db.js";
import { walletCoreDebugFlags } from "../util/debugFlags.js";
-import { readSuccessResponseJsonOrThrow } from "../util/http.js";
+import {
+ HttpRequestLibrary,
+ readSuccessResponseJsonOrThrow,
+} from "../util/http.js";
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js";
import {
guardOperationException,
@@ -271,9 +274,11 @@ export function selectWithdrawalDenominations(
/**
* Get information about a withdrawal from
* a taler://withdraw URI by asking the bank.
+ *
+ * FIXME: Move into bank client.
*/
export async function getBankWithdrawalInfo(
- ws: InternalWalletState,
+ http: HttpRequestLibrary,
talerWithdrawUri: string,
): Promise<BankWithdrawDetails> {
const uriResult = parseWithdrawUri(talerWithdrawUri);
@@ -283,7 +288,7 @@ export async function getBankWithdrawalInfo(
const configReqUrl = new URL("config", uriResult.bankIntegrationApiBaseUrl);
- const configResp = await ws.http.get(configReqUrl.href);
+ const configResp = await http.get(configReqUrl.href);
const config = await readSuccessResponseJsonOrThrow(
configResp,
codecForTalerConfigResponse(),
@@ -309,7 +314,7 @@ export async function getBankWithdrawalInfo(
`withdrawal-operation/${uriResult.withdrawalOperationId}`,
uriResult.bankIntegrationApiBaseUrl,
);
- const resp = await ws.http.get(reqUrl.href);
+ const resp = await http.get(reqUrl.href);
const status = await readSuccessResponseJsonOrThrow(
resp,
codecForWithdrawOperationStatusResponse(),
@@ -1076,7 +1081,7 @@ export async function getWithdrawalDetailsForUri(
talerWithdrawUri: string,
): Promise<WithdrawUriInfoResponse> {
logger.trace(`getting withdrawal details for URI ${talerWithdrawUri}`);
- const info = await getBankWithdrawalInfo(ws, talerWithdrawUri);
+ const info = await getBankWithdrawalInfo(ws.http, talerWithdrawUri);
logger.trace(`got bank info`);
if (info.suggestedExchange) {
// FIXME: right now the exchange gets permanently added,
diff --git a/packages/taler-wallet-core/src/util/http.ts b/packages/taler-wallet-core/src/util/http.ts
index 3a7062c99..43fe29bba 100644
--- a/packages/taler-wallet-core/src/util/http.ts
+++ b/packages/taler-wallet-core/src/util/http.ts
@@ -34,6 +34,7 @@ import {
timestampMax,
TalerErrorDetails,
Codec,
+ j2s,
} from "@gnu-taler/taler-util";
import { TalerErrorCode } from "@gnu-taler/taler-util";
@@ -131,6 +132,11 @@ export async function readTalerErrorResponse(
const errJson = await httpResponse.json();
const talerErrorCode = errJson.code;
if (typeof talerErrorCode !== "number") {
+ logger.warn(
+ `malformed error response (status ${httpResponse.status}): ${j2s(
+ errJson,
+ )}`,
+ );
throw new OperationFailedError(
makeErrorDetails(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,