aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-cli
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-cli
parent9e7ee06ad1870339d011a0be27867cc36f94490d (diff)
wallet: towards db-less benchmarking, some refactoring
Diffstat (limited to 'packages/taler-wallet-cli')
-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
16 files changed, 590 insertions, 272 deletions
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,
];