summaryrefslogtreecommitdiff
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
parent9e7ee06ad1870339d011a0be27867cc36f94490d (diff)
downloadwallet-core-332745862e728dc5e79a424698b2736c4f2683bf.tar.gz
wallet-core-332745862e728dc5e79a424698b2736c4f2683bf.tar.bz2
wallet-core-332745862e728dc5e79a424698b2736c4f2683bf.zip
wallet: towards db-less benchmarking, some refactoring
-rw-r--r--packages/taler-util/src/talerCrypto.ts16
-rw-r--r--packages/taler-util/src/talerTypes.ts46
-rw-r--r--packages/taler-util/src/time.ts3
-rw-r--r--packages/taler-util/src/walletTypes.ts2
-rw-r--r--packages/taler-wallet-cli/src/bench2.ts106
-rw-r--r--packages/taler-wallet-cli/src/harness/harness.ts187
-rw-r--r--packages/taler-wallet-cli/src/harness/helpers.ts5
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts12
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts8
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts16
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts23
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts11
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts54
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-tipping.ts17
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts358
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts8
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts29
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts2
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts24
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/testrunner.ts2
-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
-rw-r--r--packages/taler-wallet-webextension/src/i18n/taler-wallet-webextension.pot366
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts2
34 files changed, 1396 insertions, 362 deletions
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts
index 358da9dac..4d6e73671 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -785,6 +785,22 @@ export function setupRefreshTransferPub(
};
}
+/**
+ *
+ * @param paytoUri
+ * @param salt 16-byte salt
+ * @returns
+ */
+export function hashWire(paytoUri: string, salt: string): string {
+ const r = kdf(
+ 64,
+ stringToBytes(paytoUri + "\0"),
+ decodeCrock(salt),
+ stringToBytes("merchant-wire-signature"),
+ );
+ return encodeCrock(r);
+}
+
export enum TalerSignaturePurpose {
MERCHANT_TRACK_TRANSACTION = 1103,
WALLET_RESERVE_WITHDRAW = 1200,
diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts
index b38f788af..4ccfffce0 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -951,6 +951,15 @@ export interface MerchantPayResponse {
sig: string;
}
+export interface ExchangeMeltRequest {
+ coin_pub: CoinPublicKeyString;
+ confirm_sig: EddsaSignatureString;
+ denom_pub_hash: HashCodeString;
+ denom_sig: UnblindedSignature;
+ rc: string;
+ value_with_fee: AmountString;
+}
+
export interface ExchangeMeltResponse {
/**
* Which of the kappa indices does the client not have to reveal.
@@ -1710,3 +1719,40 @@ export interface ExchangeRefreshRevealRequest {
link_sigs: EddsaSignatureString[];
}
+
+export 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;
+}
+
+export 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");
diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts
index 5fef0bf47..3b80b4ee0 100644
--- a/packages/taler-util/src/time.ts
+++ b/packages/taler-util/src/time.ts
@@ -78,6 +78,9 @@ export namespace Duration {
return Math.ceil(d.d_ms / 1000 / 60 / 60 / 24 / 365);
}
export const fromSpec = durationFromSpec;
+ export function getForever(): Duration {
+ return { d_ms: "forever" };
+ }
}
export namespace Timestamp {
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index b8433e261..444fac154 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -458,7 +458,7 @@ export interface TalerErrorDetails {
details: unknown;
}
-export interface PlanchetCreationResult {
+export interface WithdrawalPlanchet {
coinPub: string;
coinPriv: string;
reservePub: string;
diff --git a/packages/taler-wallet-cli/src/bench2.ts b/packages/taler-wallet-cli/src/bench2.ts
new file mode 100644
index 000000000..884708207
--- /dev/null
+++ b/packages/taler-wallet-cli/src/bench2.ts
@@ -0,0 +1,106 @@
+/*
+ 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/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ buildCodecForObject,
+ codecForNumber,
+ codecForString,
+ codecOptional,
+ j2s,
+ Logger,
+} from "@gnu-taler/taler-util";
+import {
+ getDefaultNodeWallet2,
+ NodeHttpLib,
+ WalletApiOperation,
+ Wallet,
+ AccessStats,
+ downloadExchangeInfo,
+} from "@gnu-taler/taler-wallet-core";
+
+/**
+ * Entry point for the benchmark.
+ *
+ * The benchmark runs against an existing Taler deployment and does not
+ * set up its own services.
+ */
+export async function runBench2(configJson: any): Promise<void> {
+ const logger = new Logger("Bench1");
+
+ // Validate the configuration file for this benchmark.
+ const benchConf = codecForBench1Config().decode(configJson);
+
+ const myHttpLib = new NodeHttpLib();
+ myHttpLib.setThrottling(false);
+
+ const exchangeInfo = await downloadExchangeInfo(
+ benchConf.exchange,
+ myHttpLib,
+ );
+}
+
+/**
+ * Format of the configuration file passed to the benchmark
+ */
+interface Bench2Config {
+ /**
+ * Base URL of the bank.
+ */
+ bank: string;
+
+ /**
+ * Payto url for deposits.
+ */
+ payto: string;
+
+ /**
+ * Base URL of the exchange.
+ */
+ exchange: string;
+
+ /**
+ * How many withdraw/deposit iterations should be made?
+ * Defaults to 1.
+ */
+ iterations?: number;
+
+ currency: string;
+
+ deposits?: number;
+
+ /**
+ * How any iterations run until the wallet db gets purged
+ * Defaults to 20.
+ */
+ restartAfter?: number;
+}
+
+/**
+ * Schema validation codec for Bench1Config.
+ */
+const codecForBench1Config = () =>
+ buildCodecForObject<Bench2Config>()
+ .property("bank", codecForString())
+ .property("payto", codecForString())
+ .property("exchange", codecForString())
+ .property("iterations", codecOptional(codecForNumber()))
+ .property("deposits", codecOptional(codecForNumber()))
+ .property("currency", codecForString())
+ .property("restartAfter", codecOptional(codecForNumber()))
+ .build("Bench1Config");
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts b/packages/taler-wallet-cli/src/harness/harness.ts
index f4e422690..63bb17fcc 100644
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ b/packages/taler-wallet-cli/src/harness/harness.ts
@@ -45,6 +45,9 @@ import {
MerchantInstancesResponse,
} from "./merchantApiTypes";
import {
+ BankServiceHandle,
+ HarnessExchangeBankAccount,
+ NodeHttpLib,
openPromise,
OperationFailedError,
WalletCoreApiClient,
@@ -468,164 +471,6 @@ export async function pingProc(
}
}
-export interface HarnessExchangeBankAccount {
- accountName: string;
- accountPassword: string;
- accountPaytoUri: string;
- wireGatewayApiBaseUrl: string;
-}
-
-export interface BankServiceInterface {
- readonly baseUrl: string;
- readonly port: number;
-}
-
-export enum CreditDebitIndicator {
- Credit = "credit",
- Debit = "debit",
-}
-
-export interface BankAccountBalanceResponse {
- balance: {
- amount: AmountString;
- credit_debit_indicator: CreditDebitIndicator;
- };
-}
-
-export namespace BankAccessApi {
- export async function getAccountBalance(
- bank: BankServiceInterface,
- bankUser: BankUser,
- ): Promise<BankAccountBalanceResponse> {
- const url = new URL(`accounts/${bankUser.username}`, bank.baseUrl);
- const resp = await axios.get(url.href, {
- auth: bankUser,
- });
- return resp.data;
- }
-
- export async function createWithdrawalOperation(
- bank: BankServiceInterface,
- bankUser: BankUser,
- amount: string,
- ): Promise<WithdrawalOperationInfo> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals`,
- bank.baseUrl,
- );
- const resp = await axios.post(
- url.href,
- {
- amount,
- },
- {
- auth: bankUser,
- },
- );
- return codecForWithdrawalOperationInfo().decode(resp.data);
- }
-}
-
-export namespace BankApi {
- export async function registerAccount(
- bank: BankServiceInterface,
- username: string,
- password: string,
- ): Promise<BankUser> {
- const url = new URL("testing/register", bank.baseUrl);
- let resp = await axios.post(url.href, {
- username,
- password,
- });
- let paytoUri = `payto://x-taler-bank/localhost/${username}`;
- if (process.env.WALLET_HARNESS_WITH_EUFIN) {
- paytoUri = resp.data.paytoUri;
- }
- return {
- password,
- username,
- accountPaytoUri: paytoUri,
- };
- }
-
- export async function createRandomBankUser(
- bank: BankServiceInterface,
- ): 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: BankServiceInterface,
- params: {
- exchangeBankAccount: HarnessExchangeBankAccount;
- amount: string;
- reservePub: string;
- debitAccountPayto: string;
- },
- ) {
- let maybeBaseUrl = bank.baseUrl;
- if (process.env.WALLET_HARNESS_WITH_EUFIN) {
- maybeBaseUrl = (bank as EufinBankService).baseUrlDemobank;
- }
- let url = new URL(
- `taler-wire-gateway/${params.exchangeBankAccount.accountName}/admin/add-incoming`,
- maybeBaseUrl,
- );
- await axios.post(
- url.href,
- {
- amount: params.amount,
- reserve_pub: params.reservePub,
- debit_account: params.debitAccountPayto,
- },
- {
- auth: {
- username: params.exchangeBankAccount.accountName,
- password: params.exchangeBankAccount.accountPassword,
- },
- },
- );
- }
-
- export async function confirmWithdrawalOperation(
- bank: BankServiceInterface,
- bankUser: BankUser,
- wopi: WithdrawalOperationInfo,
- ): Promise<void> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/confirm`,
- bank.baseUrl,
- );
- await axios.post(
- url.href,
- {},
- {
- auth: bankUser,
- },
- );
- }
-
- export async function abortWithdrawalOperation(
- bank: BankServiceInterface,
- bankUser: BankUser,
- wopi: WithdrawalOperationInfo,
- ): Promise<void> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/abort`,
- bank.baseUrl,
- );
- await axios.post(
- url.href,
- {},
- {
- auth: bankUser,
- },
- );
- }
-}
-
class BankServiceBase {
proc: ProcessWrapper | undefined;
@@ -640,10 +485,12 @@ class BankServiceBase {
* Work in progress. The key point is that both Sandbox and Nexus
* will be configured and started by this class.
*/
-class EufinBankService extends BankServiceBase implements BankServiceInterface {
+class EufinBankService extends BankServiceBase implements BankServiceHandle {
sandboxProc: ProcessWrapper | undefined;
nexusProc: ProcessWrapper | undefined;
+ http = new NodeHttpLib();
+
static async create(
gc: GlobalTestState,
bc: BankConfig,
@@ -914,9 +761,11 @@ class EufinBankService extends BankServiceBase implements BankServiceInterface {
}
}
-class PybankService extends BankServiceBase implements BankServiceInterface {
+class PybankService extends BankServiceBase implements BankServiceHandle {
proc: ProcessWrapper | undefined;
+ http = new NodeHttpLib();
+
static async create(
gc: GlobalTestState,
bc: BankConfig,
@@ -955,6 +804,7 @@ class PybankService extends BankServiceBase implements BankServiceInterface {
const config = Configuration.load(this.configFile);
config.setString("bank", "suggested_exchange", e.baseUrl);
config.setString("bank", "suggested_exchange_payto", exchangePayto);
+ config.write(this.configFile);
}
get baseUrl(): string {
@@ -1087,23 +937,6 @@ export class FakeBankService {
}
}
-export interface BankUser {
- username: string;
- password: string;
- accountPaytoUri: string;
-}
-
-export interface WithdrawalOperationInfo {
- withdrawal_id: string;
- taler_withdraw_uri: string;
-}
-
-const codecForWithdrawalOperationInfo = (): Codec<WithdrawalOperationInfo> =>
- buildCodecForObject<WithdrawalOperationInfo>()
- .property("withdrawal_id", codecForString())
- .property("taler_withdraw_uri", codecForString())
- .build("WithdrawalOperationInfo");
-
export interface ExchangeConfig {
name: string;
currency: string;
diff --git a/packages/taler-wallet-cli/src/harness/helpers.ts b/packages/taler-wallet-cli/src/harness/helpers.ts
index f19c6a115..117bcdcf8 100644
--- a/packages/taler-wallet-cli/src/harness/helpers.ts
+++ b/packages/taler-wallet-cli/src/harness/helpers.ts
@@ -30,22 +30,19 @@ import {
Duration,
PreparePayResultType,
} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { BankAccessApi, BankApi, HarnessExchangeBankAccount, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "./denomStructures.js";
import {
FaultInjectedExchangeService,
FaultInjectedMerchantService,
} from "./faultInjection.js";
import {
- BankAccessApi,
- BankApi,
BankService,
DbInfo,
ExchangeService,
ExchangeServiceInterface,
getPayto,
GlobalTestState,
- HarnessExchangeBankAccount,
MerchantPrivateApi,
MerchantService,
MerchantServiceInterface,
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts b/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
index 2259dd8bb..8e4109752 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
@@ -24,13 +24,15 @@ import {
setupDb,
BankService,
MerchantService,
- BankApi,
- BankAccessApi,
- CreditDebitIndicator,
- getPayto
+ getPayto,
} from "../harness/harness.js";
import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util";
import { defaultCoinConfig } from "../harness/denomStructures";
+import {
+ BankApi,
+ BankAccessApi,
+ CreditDebitIndicator,
+} from "@gnu-taler/taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -97,8 +99,6 @@ export async function runBankApiTest(t: GlobalTestState) {
console.log("setup done!");
- const wallet = new WalletCli(t);
-
const bankUser = await BankApi.registerAccount(bank, "user1", "pw1");
// Make sure that registering twice results in a 409 Conflict
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts b/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts
index 91e9bdec5..f9c7c4b99 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts
@@ -24,11 +24,13 @@ import {
BankService,
ExchangeService,
MerchantService,
+ getPayto,
+} from "../harness/harness.js";
+import {
+ WalletApiOperation,
BankApi,
BankAccessApi,
- getPayto
-} from "../harness/harness.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+} from "@gnu-taler/taler-wallet-core";
import {
ExchangesListRespose,
URL,
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
index 3f7e1a9d1..33aad80d2 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
@@ -19,15 +19,16 @@
*/
import {
ContractTerms,
- CoreApiResponse,
getTimestampNow,
timestampTruncateToSecond,
} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ WalletApiOperation,
+ HarnessExchangeBankAccount,
+} from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures";
import {
DbInfo,
- HarnessExchangeBankAccount,
ExchangeService,
GlobalTestState,
MerchantService,
@@ -233,13 +234,8 @@ export async function createLibeufinTestEnvironment(
export async function runLibeufinBasicTest(t: GlobalTestState) {
// Set up test environment
- const {
- wallet,
- exchange,
- merchant,
- libeufinSandbox,
- libeufinNexus,
- } = await createLibeufinTestEnvironment(t);
+ const { wallet, exchange, merchant, libeufinSandbox, libeufinNexus } =
+ await createLibeufinTestEnvironment(t);
await wallet.client.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl,
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts
index 466b1efbd..a9dbeef9a 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts
@@ -20,25 +20,30 @@
import {
GlobalTestState,
MerchantPrivateApi,
- BankServiceInterface,
MerchantServiceInterface,
WalletCli,
ExchangeServiceInterface,
} from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
+import {
+ createSimpleTestkudosEnvironment,
+ withdrawViaBank,
+} from "../harness/helpers.js";
import {
URL,
durationFromSpec,
PreparePayResultType,
} from "@gnu-taler/taler-util";
import axios from "axios";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ WalletApiOperation,
+ BankServiceHandle,
+} from "@gnu-taler/taler-wallet-core";
async function testRefundApiWithFulfillmentUrl(
t: GlobalTestState,
env: {
merchant: MerchantServiceInterface;
- bank: BankServiceInterface;
+ bank: BankServiceHandle;
wallet: WalletCli;
exchange: ExchangeServiceInterface;
},
@@ -152,7 +157,7 @@ async function testRefundApiWithFulfillmentMessage(
t: GlobalTestState,
env: {
merchant: MerchantServiceInterface;
- bank: BankServiceInterface;
+ bank: BankServiceHandle;
wallet: WalletCli;
exchange: ExchangeServiceInterface;
},
@@ -267,12 +272,8 @@ async function testRefundApiWithFulfillmentMessage(
export async function runMerchantRefundApiTest(t: GlobalTestState) {
// Set up test environment
- const {
- wallet,
- bank,
- exchange,
- merchant,
- } = await createSimpleTestkudosEnvironment(t);
+ const { wallet, bank, exchange, merchant } =
+ await createSimpleTestkudosEnvironment(t);
// Withdraw digital cash into the wallet.
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts
index 7e421cc35..c78f030c8 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts
@@ -29,9 +29,7 @@ import {
BankService,
WalletCli,
MerchantPrivateApi,
- BankApi,
- BankAccessApi,
- getPayto
+ getPayto,
} from "../harness/harness.js";
import {
FaultInjectedExchangeService,
@@ -40,7 +38,11 @@ import {
} from "../harness/faultInjection";
import { CoreApiResponse } from "@gnu-taler/taler-util";
import { defaultCoinConfig } from "../harness/denomStructures";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ WalletApiOperation,
+ BankApi,
+ BankAccessApi,
+} from "@gnu-taler/taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -146,7 +148,6 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
await wallet.runUntilDone();
-
// Check balance
await wallet.client.call(WalletApiOperation.GetBalances, {});
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts
index 1d419fd9a..50a18944b 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts
@@ -17,31 +17,33 @@
/**
* Imports.
*/
+import { GlobalTestState, WalletCli } from "../harness/harness.js";
+import { makeTestPayment } from "../harness/helpers.js";
import {
- GlobalTestState,
+ WalletApiOperation,
BankApi,
- WalletCli,
- BankAccessApi
-} from "../harness/harness.js";
-import {
- makeTestPayment,
-} from "../harness/helpers.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+ BankAccessApi,
+ BankServiceHandle,
+ NodeHttpLib,
+} from "@gnu-taler/taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal and payment.
*/
export async function runPaymentDemoTest(t: GlobalTestState) {
-
// Withdraw digital cash into the wallet.
- let bankInterface = {
+ let bankInterface: BankServiceHandle = {
baseUrl: "https://bank.demo.taler.net/",
- port: 0 // unused.
+ http: new NodeHttpLib(),
};
let user = await BankApi.createRandomBankUser(bankInterface);
- let wop = await BankAccessApi.createWithdrawalOperation(bankInterface, user, "KUDOS:20");
+ let wop = await BankAccessApi.createWithdrawalOperation(
+ bankInterface,
+ user,
+ "KUDOS:20",
+ );
- let wallet = new WalletCli(t);
+ let wallet = new WalletCli(t);
await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
talerWithdrawUri: wop.taler_withdraw_uri,
});
@@ -60,7 +62,10 @@ export async function runPaymentDemoTest(t: GlobalTestState) {
});
await wallet.runUntilDone();
- let balanceBefore = await wallet.client.call(WalletApiOperation.GetBalances, {});
+ let balanceBefore = await wallet.client.call(
+ WalletApiOperation.GetBalances,
+ {},
+ );
t.assertTrue(balanceBefore["balances"].length == 1);
const order = {
@@ -70,7 +75,7 @@ export async function runPaymentDemoTest(t: GlobalTestState) {
};
let merchant = {
- makeInstanceBaseUrl: function(instanceName?: string) {
+ makeInstanceBaseUrl: function (instanceName?: string) {
return "https://backend.demo.taler.net/instances/donations/";
},
port: 0,
@@ -82,17 +87,26 @@ export async function runPaymentDemoTest(t: GlobalTestState) {
await makeTestPayment(
t,
{
- merchant, wallet, order
+ merchant,
+ wallet,
+ order,
},
{
- "Authorization": `Bearer ${process.env["TALER_ENV_FRONTENDS_APITOKEN"]}`,
- });
+ Authorization: `Bearer ${process.env["TALER_ENV_FRONTENDS_APITOKEN"]}`,
+ },
+ );
await wallet.runUntilDone();
- let balanceAfter = await wallet.client.call(WalletApiOperation.GetBalances, {});
+ let balanceAfter = await wallet.client.call(
+ WalletApiOperation.GetBalances,
+ {},
+ );
t.assertTrue(balanceAfter["balances"].length == 1);
- t.assertTrue(balanceBefore["balances"][0]["available"] > balanceAfter["balances"][0]["available"]);
+ t.assertTrue(
+ balanceBefore["balances"][0]["available"] >
+ balanceAfter["balances"][0]["available"],
+ );
}
runPaymentDemoTest.excludeByDefault = true;
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts b/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts
index f31220e24..f04293ed8 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts
@@ -17,8 +17,12 @@
/**
* Imports.
*/
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi, BankApi, getWireMethod } from "../harness/harness.js";
+import { WalletApiOperation, BankApi } from "@gnu-taler/taler-wallet-core";
+import {
+ GlobalTestState,
+ MerchantPrivateApi,
+ getWireMethod,
+} from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
/**
@@ -27,13 +31,8 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
export async function runTippingTest(t: GlobalTestState) {
// Set up test environment
- const {
- wallet,
- bank,
- exchange,
- merchant,
- exchangeBankAccount,
- } = await createSimpleTestkudosEnvironment(t);
+ const { wallet, bank, exchange, merchant, exchangeBankAccount } =
+ await createSimpleTestkudosEnvironment(t);
const mbu = await BankApi.createRandomBankUser(bank);
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
new file mode 100644
index 000000000..9ff605df5
--- /dev/null
+++ b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
@@ -0,0 +1,358 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 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/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ AmountJson,
+ AmountLike,
+ Amounts,
+ AmountString,
+ codecForBankWithdrawalOperationPostResponse,
+ codecForDepositSuccess,
+ codecForExchangeMeltResponse,
+ codecForWithdrawResponse,
+ DenominationPubKey,
+ eddsaGetPublic,
+ encodeCrock,
+ ExchangeMeltRequest,
+ ExchangeProtocolVersion,
+ ExchangeWithdrawRequest,
+ getRandomBytes,
+ getTimestampNow,
+ hashWire,
+ j2s,
+ Timestamp,
+ UnblindedSignature,
+} from "@gnu-taler/taler-util";
+import {
+ BankAccessApi,
+ BankApi,
+ BankServiceHandle,
+ CryptoApi,
+ DenominationRecord,
+ downloadExchangeInfo,
+ ExchangeInfo,
+ getBankWithdrawalInfo,
+ HttpRequestLibrary,
+ isWithdrawableDenom,
+ NodeHttpLib,
+ OperationFailedError,
+ readSuccessResponseJsonOrThrow,
+ SynchronousCryptoWorkerFactory,
+} from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState } from "../harness/harness.js";
+import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+
+const httpLib = new NodeHttpLib();
+
+export interface ReserveKeypair {
+ reservePub: string;
+ reservePriv: string;
+}
+
+/**
+ * Denormalized info about a coin.
+ */
+export interface CoinInfo {
+ coinPub: string;
+ coinPriv: string;
+ exchangeBaseUrl: string;
+ denomSig: UnblindedSignature;
+ denomPub: DenominationPubKey;
+ denomPubHash: string;
+ feeDeposit: string;
+ feeRefresh: string;
+}
+
+export function generateReserveKeypair(): ReserveKeypair {
+ const priv = getRandomBytes(32);
+ const pub = eddsaGetPublic(priv);
+ return {
+ reservePriv: encodeCrock(priv),
+ reservePub: encodeCrock(pub),
+ };
+}
+
+async function topupReserveWithDemobank(
+ reservePub: string,
+ bankBaseUrl: string,
+ exchangeInfo: ExchangeInfo,
+ amount: AmountString,
+) {
+ const bankHandle: BankServiceHandle = {
+ baseUrl: bankBaseUrl,
+ http: httpLib,
+ };
+ const bankUser = await BankApi.createRandomBankUser(bankHandle);
+ const wopi = await BankAccessApi.createWithdrawalOperation(
+ bankHandle,
+ bankUser,
+ amount,
+ );
+ const bankInfo = await getBankWithdrawalInfo(
+ httpLib,
+ wopi.taler_withdraw_uri,
+ );
+ const bankStatusUrl = bankInfo.extractedStatusUrl;
+ if (!bankInfo.suggestedExchange) {
+ throw Error("no suggested exchange");
+ }
+ const plainPaytoUris =
+ exchangeInfo.wire.accounts.map((x) => x.payto_uri) ?? [];
+ if (plainPaytoUris.length <= 0) {
+ throw new Error();
+ }
+ const httpResp = await httpLib.postJson(bankStatusUrl, {
+ reserve_pub: reservePub,
+ selected_exchange: plainPaytoUris[0],
+ });
+ await readSuccessResponseJsonOrThrow(
+ httpResp,
+ codecForBankWithdrawalOperationPostResponse(),
+ );
+ await BankApi.confirmWithdrawalOperation(bankHandle, bankUser, wopi);
+}
+
+async function withdrawCoin(args: {
+ http: HttpRequestLibrary;
+ cryptoApi: CryptoApi;
+ reserveKeyPair: ReserveKeypair;
+ denom: DenominationRecord;
+ exchangeBaseUrl: string;
+}): Promise<CoinInfo> {
+ const { http, cryptoApi, reserveKeyPair, denom, exchangeBaseUrl } = args;
+ const planchet = await cryptoApi.createPlanchet({
+ coinIndex: 0,
+ denomPub: denom.denomPub,
+ feeWithdraw: denom.feeWithdraw,
+ reservePriv: reserveKeyPair.reservePriv,
+ reservePub: reserveKeyPair.reservePub,
+ secretSeed: encodeCrock(getRandomBytes(32)),
+ value: denom.value,
+ });
+
+ const reqBody: ExchangeWithdrawRequest = {
+ denom_pub_hash: planchet.denomPubHash,
+ reserve_sig: planchet.withdrawSig,
+ coin_ev: planchet.coinEv,
+ };
+ const reqUrl = new URL(
+ `reserves/${planchet.reservePub}/withdraw`,
+ exchangeBaseUrl,
+ ).href;
+
+ const resp = await http.postJson(reqUrl, reqBody);
+ const r = await readSuccessResponseJsonOrThrow(
+ resp,
+ codecForWithdrawResponse(),
+ );
+
+ const ubSig = await cryptoApi.unblindDenominationSignature({
+ planchet,
+ evSig: r.ev_sig,
+ });
+
+ return {
+ coinPriv: planchet.coinPriv,
+ coinPub: planchet.coinPub,
+ denomSig: ubSig,
+ denomPub: denom.denomPub,
+ denomPubHash: denom.denomPubHash,
+ feeDeposit: Amounts.stringify(denom.feeDeposit),
+ feeRefresh: Amounts.stringify(denom.feeRefresh),
+ exchangeBaseUrl: args.exchangeBaseUrl,
+ };
+}
+
+function findDenomOrThrow(
+ exchangeInfo: ExchangeInfo,
+ amount: AmountString,
+): DenominationRecord {
+ for (const d of exchangeInfo.keys.currentDenominations) {
+ if (Amounts.cmp(d.value, amount) === 0 && isWithdrawableDenom(d)) {
+ return d;
+ }
+ }
+ throw new Error("no matching denomination found");
+}
+
+async function depositCoin(args: {
+ http: HttpRequestLibrary;
+ cryptoApi: CryptoApi;
+ exchangeBaseUrl: string;
+ coin: CoinInfo;
+ amount: AmountString;
+}) {
+ const { coin, http, cryptoApi } = args;
+ const depositPayto = "payto://x-taler-bank/localhost/foo";
+ const wireSalt = encodeCrock(getRandomBytes(16));
+ const contractTermsHash = encodeCrock(getRandomBytes(64));
+ const depositTimestamp = getTimestampNow();
+ const refundDeadline = getTimestampNow();
+ const merchantPub = encodeCrock(getRandomBytes(32));
+ const dp = await cryptoApi.signDepositPermission({
+ coinPriv: coin.coinPriv,
+ coinPub: coin.coinPub,
+ contractTermsHash,
+ denomKeyType: coin.denomPub.cipher,
+ denomPubHash: coin.denomPubHash,
+ denomSig: coin.denomSig,
+ exchangeBaseUrl: args.exchangeBaseUrl,
+ feeDeposit: Amounts.parseOrThrow(coin.feeDeposit),
+ merchantPub,
+ spendAmount: Amounts.parseOrThrow(args.amount),
+ timestamp: depositTimestamp,
+ refundDeadline: refundDeadline,
+ wireInfoHash: hashWire(depositPayto, wireSalt),
+ });
+ const requestBody = {
+ contribution: Amounts.stringify(dp.contribution),
+ merchant_payto_uri: depositPayto,
+ wire_salt: wireSalt,
+ h_contract_terms: contractTermsHash,
+ ub_sig: coin.denomSig,
+ timestamp: depositTimestamp,
+ wire_transfer_deadline: getTimestampNow(),
+ refund_deadline: refundDeadline,
+ coin_sig: dp.coin_sig,
+ denom_pub_hash: dp.h_denom,
+ merchant_pub: merchantPub,
+ };
+ const url = new URL(`coins/${dp.coin_pub}/deposit`, dp.exchange_url);
+ const httpResp = await http.postJson(url.href, requestBody);
+ await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
+}
+
+async function refreshCoin(req: {
+ http: HttpRequestLibrary;
+ cryptoApi: CryptoApi;
+ oldCoin: CoinInfo;
+ newDenoms: DenominationRecord[];
+}): Promise<void> {
+ const { cryptoApi, oldCoin, http } = req;
+ const refreshSessionSeed = encodeCrock(getRandomBytes(32));
+ const session = await cryptoApi.deriveRefreshSession({
+ exchangeProtocolVersion: ExchangeProtocolVersion.V12,
+ feeRefresh: Amounts.parseOrThrow(oldCoin.feeRefresh),
+ kappa: 3,
+ meltCoinDenomPubHash: oldCoin.denomPubHash,
+ meltCoinPriv: oldCoin.coinPriv,
+ meltCoinPub: oldCoin.coinPub,
+ sessionSecretSeed: refreshSessionSeed,
+ newCoinDenoms: req.newDenoms.map((x) => ({
+ count: 1,
+ denomPub: x.denomPub,
+ feeWithdraw: x.feeWithdraw,
+ value: x.value,
+ })),
+ });
+
+ const meltReqBody: ExchangeMeltRequest = {
+ coin_pub: oldCoin.coinPub,
+ confirm_sig: session.confirmSig,
+ denom_pub_hash: oldCoin.denomPubHash,
+ denom_sig: oldCoin.denomSig,
+ rc: session.hash,
+ value_with_fee: Amounts.stringify(session.meltValueWithFee),
+ };
+
+ const reqUrl = new URL(
+ `coins/${oldCoin.coinPub}/melt`,
+ oldCoin.exchangeBaseUrl,
+ );
+
+ const resp = await http.postJson(reqUrl.href, meltReqBody);
+
+ const meltResponse = await readSuccessResponseJsonOrThrow(
+ resp,
+ codecForExchangeMeltResponse(),
+ );
+
+ const norevealIndex = meltResponse.noreveal_index;
+
+
+}
+
+/**
+ * Run test for basic, bank-integrated withdrawal and payment.
+ */
+export async function runWalletDblessTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
+
+ const http = new NodeHttpLib();
+ const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
+
+ try {
+ // Withdraw digital cash into the wallet.
+
+ const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http);
+
+ const reserveKeyPair = generateReserveKeypair();
+
+ await topupReserveWithDemobank(
+ reserveKeyPair.reservePub,
+ bank.baseUrl,
+ exchangeInfo,
+ "TESTKUDOS:10",
+ );
+
+ await exchange.runWirewatchOnce();
+
+ const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8");
+
+ const coin = await withdrawCoin({
+ http,
+ cryptoApi,
+ reserveKeyPair,
+ denom: d1,
+ exchangeBaseUrl: exchange.baseUrl,
+ });
+
+ await depositCoin({
+ amount: "TESTKUDOS:4",
+ coin: coin,
+ cryptoApi,
+ exchangeBaseUrl: exchange.baseUrl,
+ http,
+ });
+
+ const refreshDenoms = [
+ findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"),
+ findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"),
+ ];
+
+ const freshCoins = await refreshCoin({
+ oldCoin: coin,
+ cryptoApi,
+ http,
+ newDenoms: refreshDenoms,
+ });
+ } catch (e) {
+ if (e instanceof OperationFailedError) {
+ console.log(e);
+ console.log(j2s(e.operationError));
+ } else {
+ console.log(e);
+ }
+ throw e;
+ }
+}
+
+runWalletDblessTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts
index 5ba1fa893..19668d760 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts
@@ -18,8 +18,12 @@
* Imports.
*/
import { TalerErrorCode } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, BankApi, BankAccessApi } from "../harness/harness.js";
+import {
+ WalletApiOperation,
+ BankApi,
+ BankAccessApi,
+} from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
/**
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
index 25df19e46..e8a8c5028 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -17,10 +17,13 @@
/**
* Imports.
*/
-import { GlobalTestState, BankApi, BankAccessApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-import { codecForBalancesResponse } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ WalletApiOperation,
+ BankApi,
+ BankAccessApi,
+} from "@gnu-taler/taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -41,18 +44,24 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Hand it to the wallet
- const r1 = await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
+ const r1 = await wallet.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ {
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ },
+ );
await wallet.runPending();
// Withdraw
- const r2 = await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
+ const r2 = await wallet.client.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ },
+ );
await wallet.runPending();
// Confirm it
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts
index abd25d282..5860aaf88 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts
@@ -19,13 +19,11 @@
*/
import {
GlobalTestState,
- BankApi,
WalletCli,
setupDb,
ExchangeService,
FakeBankService,
} from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import { URL } from "@gnu-taler/taler-util";
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts
index 2f88b3024..6ae0e65e7 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts
@@ -17,9 +17,9 @@
/**
* Imports.
*/
-import { GlobalTestState, BankApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation, BankApi } from "@gnu-taler/taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -27,12 +27,8 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runTestWithdrawalManualTest(t: GlobalTestState) {
// Set up test environment
- const {
- wallet,
- bank,
- exchange,
- exchangeBankAccount,
- } = await createSimpleTestkudosEnvironment(t);
+ const { wallet, bank, exchange, exchangeBankAccount } =
+ await createSimpleTestkudosEnvironment(t);
// Create a withdrawal operation
@@ -42,11 +38,13 @@ export async function runTestWithdrawalManualTest(t: GlobalTestState) {
exchangeBaseUrl: exchange.baseUrl,
});
-
- const wres = await wallet.client.call(WalletApiOperation.AcceptManualWithdrawal, {
- exchangeBaseUrl: exchange.baseUrl,
- amount: "TESTKUDOS:10",
- });
+ const wres = await wallet.client.call(
+ WalletApiOperation.AcceptManualWithdrawal,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ amount: "TESTKUDOS:10",
+ },
+ );
const reservePub: string = wres.reservePub;
diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
index 844904132..3839266c0 100644
--- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
@@ -87,6 +87,7 @@ import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
import { runDenomUnofferedTest } from "./test-denom-unoffered.js";
import { runWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js";
import { runClauseSchnorrTest } from "./test-clause-schnorr.js";
+import { runWalletDblessTest } from "./test-wallet-dbless.js";
/**
* Test runner.
@@ -162,6 +163,7 @@ const allTests: TestMainFunction[] = [
runWalletBackupBasicTest,
runWalletBackupDoublespendTest,
runWallettestingTest,
+ runWalletDblessTest,
runWithdrawalAbortBankTest,
runWithdrawalBankIntegratedTest,
];
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,
diff --git a/packages/taler-wallet-webextension/src/i18n/taler-wallet-webextension.pot b/packages/taler-wallet-webextension/src/i18n/taler-wallet-webextension.pot
new file mode 100644
index 000000000..4fd500d25
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/i18n/taler-wallet-webextension.pot
@@ -0,0 +1,366 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/NavigationBar.tsx:86
+#, c-format
+msgid "Balance"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/NavigationBar.tsx:87
+#, c-format
+msgid "Pending"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/NavigationBar.tsx:88
+#, c-format
+msgid "Backup"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/NavigationBar.tsx:89
+#, c-format
+msgid "Settings"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/NavigationBar.tsx:90
+#, c-format
+msgid "Dev"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx:127
+#, c-format
+msgid "Add provider"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx:137
+#, c-format
+msgid "Sync all backups"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx:139
+#, c-format
+msgid "Sync now"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/popup/BalancePage.tsx:79
+#, c-format
+msgid "You have no balance to show. Need some %1$s getting started?"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx:145
+#, c-format
+msgid "&lt; Back"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx:156
+#, c-format
+msgid "Next"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx:210
+#, c-format
+msgid "&lt; Back"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx:213
+#, c-format
+msgid "Add provider"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:57
+#, c-format
+msgid "Loading..."
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:64
+#, c-format
+msgid "There was an error loading the provider detail for "%1$s""
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:75
+#, c-format
+msgid "There is not known provider with url "%1$s". Redirecting back..."
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:131
+#, c-format
+msgid "Back up"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:142
+#, c-format
+msgid "Extend"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:148
+#, c-format
+msgid ""
+"terms has changed, extending the service will imply accepting the new terms of "
+"service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:158
+#, c-format
+msgid "old"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:162
+#, c-format
+msgid "new"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:169
+#, c-format
+msgid "fee"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:177
+#, c-format
+msgid "storage"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:190
+#, c-format
+msgid "&lt; back"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:194
+#, c-format
+msgid "remove provider"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:213
+#, c-format
+msgid "There is conflict with another backup from %1$s"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:228
+#, c-format
+msgid "Unknown backup problem: %1$s"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx:247
+#, c-format
+msgid "service paid"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/popup/Settings.tsx:46
+#, c-format
+msgid "Permissions"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:37
+#, c-format
+msgid "Exchange doesn't have terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:49
+#, c-format
+msgid "Exchange doesn't have terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:56
+#, c-format
+msgid "Review exchange terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:63
+#, c-format
+msgid "Review new version of terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:75
+#, c-format
+msgid "Show terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:83
+#, c-format
+msgid "I accept the exchange terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:127
+#, c-format
+msgid "Hide terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/TermsOfServiceSection.tsx:136
+#, c-format
+msgid "I accept the exchange terms of service"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx:110
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx:114
+#, c-format
+msgid "Loading terms.."
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx:121
+#, c-format
+msgid "Add exchange"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx:126
+#, c-format
+msgid "Add exchange"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.tsx:131
+#, c-format
+msgid "Add exchange anyway"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx:133
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx:149
+#, c-format
+msgid "Next"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx:83
+#, c-format
+msgid "You have no balance to show. Need some %1$s getting started?"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx:104
+#, c-format
+msgid "Add exchange"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx:144
+#, c-format
+msgid "Add exchange"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Settings.tsx:84
+#, c-format
+msgid "Permissions"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Settings.tsx:95
+#, c-format
+msgid "Known exchanges"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Transaction.tsx:154
+#, c-format
+msgid "&lt; Back"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Transaction.tsx:159
+#, c-format
+msgid "retry"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Transaction.tsx:163
+#, c-format
+msgid "Forget"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Transaction.tsx:194
+#, c-format
+msgid "Cancel"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/wallet/Transaction.tsx:198
+#, c-format
+msgid "Confirm"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Pay.tsx:211
+#, c-format
+msgid "Pay with a mobile phone"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Pay.tsx:211
+#, c-format
+msgid "Hide QR"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Pay.tsx:241
+#, c-format
+msgid "Pay"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Pay.tsx:265
+#, c-format
+msgid "Withdraw digital cash"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Pay.tsx:295
+#, c-format
+msgid "Digital cash payment"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:101
+#, c-format
+msgid "Digital cash withdrawal"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:149
+#, c-format
+msgid "Cancel exchange selection"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:150
+#, c-format
+msgid "Confirm exchange selection"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:155
+#, c-format
+msgid "Switch exchange"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:174
+#, c-format
+msgid "Confirm withdrawal"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:183
+#, c-format
+msgid "Withdraw anyway"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Withdraw.tsx:310
+#, c-format
+msgid "missing withdraw uri"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Deposit.tsx:119
+#, c-format
+msgid "Digital cash payment"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Deposit.tsx:133
+#, c-format
+msgid "Digital cash payment"
+msgstr ""
+
+#: /home/dold/repos/taler/wallet-core/packages/taler-wallet-webextension/src/cta/Deposit.tsx:186
+#, c-format
+msgid "Digital cash deposit"
+msgstr ""
+
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index c306b17a9..31b46d88d 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -38,7 +38,7 @@ import {
RemoveBackupProviderRequest
} from "@gnu-taler/taler-wallet-core";
import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits";
-import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw";
+import type { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw";
import { MessageFromBackend } from "./wxBackend";
/**