gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: towards a currency conversion


From: gnunet
Subject: [taler-wallet-core] branch master updated: towards a currency conversion integration test
Date: Tue, 21 Nov 2023 15:00:26 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 6000a55d5 towards a currency conversion integration test
6000a55d5 is described below

commit 6000a55d583832a71335310514688f1f6faed722
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Nov 21 15:00:14 2023 +0100

    towards a currency conversion integration test
---
 packages/taler-harness/src/harness/harness.ts      |   6 +
 .../integrationtests/test-withdrawal-conversion.ts | 247 +++++++++++++++++++++
 .../src/integrationtests/testrunner.ts             |   2 +
 packages/taler-util/src/merchant-api-types.ts      |   8 +-
 packages/taler-util/src/taler-types.ts             |  48 ++--
 packages/taler-util/src/wallet-types.ts            |  20 +-
 .../taler-wallet-core/src/operations/exchanges.ts  |  16 +-
 7 files changed, 282 insertions(+), 65 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts 
b/packages/taler-harness/src/harness/harness.ts
index 1253e3dd5..37e0b02a7 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -45,6 +45,7 @@ import {
   j2s,
   parsePaytoUri,
   stringToBytes,
+  AccountRestriction,
 } from "@gnu-taler/taler-util";
 import {
   HttpRequestLibrary,
@@ -589,6 +590,11 @@ export interface HarnessExchangeBankAccount {
   accountPassword: string;
   accountPaytoUri: string;
   wireGatewayApiBaseUrl: string;
+
+  conversionUrl?: string;
+
+  debitRestrictions?: AccountRestriction[];
+  creditRestrictions?: AccountRestriction[];
 }
 
 /**
diff --git 
a/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts 
b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts
new file mode 100644
index 000000000..2a9dd5800
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts
@@ -0,0 +1,247 @@
+/*
+ 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 {
+  AbsoluteTime,
+  AmountString,
+  Duration,
+  Logger,
+  TalerCorebankApiClient,
+  WireGatewayApiClient,
+  j2s,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+  BankService,
+  ExchangeService,
+  GlobalTestState,
+  MerchantService,
+  generateRandomPayto,
+  setupDb,
+} from "../harness/harness.js";
+import { createWalletDaemonWithClient } from "../harness/helpers.js";
+import { defaultCoinConfig } from "../harness/denomStructures.js";
+import * as http from "node:http";
+
+const logger = new Logger("test-withdrawal-conversion.ts");
+
+interface TestfakeConversionService {
+  stop: () => void;
+}
+
+function splitInTwoAt(s: string, separator: string): [string, string] {
+  const idx = s.indexOf(separator);
+  if (idx === -1) {
+    return [s, ""];
+  }
+  return [s.slice(0, idx), s.slice(idx + 1)];
+}
+
+/**
+ * Testfake for the kyc service that the exchange talks to.
+ */
+async function runTestfakeConversionService(): 
Promise<TestfakeConversionService> {
+  const server = http.createServer((req, res) => {
+    const requestUrl = req.url!;
+    logger.info(`kyc: got ${req.method} request, ${requestUrl}`);
+
+    const [path, query] = splitInTwoAt(requestUrl, "?");
+
+    const qp = new URLSearchParams(query);
+
+    if (path === "/config") {
+      res.writeHead(200, { "Content-Type": "application/json" });
+      res.end(
+        JSON.stringify({
+          version: "0:0:0",
+          name: "taler-conversion-info",
+          regional_currency: {},
+          fiat_currency: {},
+        }),
+      );
+    } else if (path === "/cashin-rate") {
+      res.writeHead(200, { "Content-Type": "application/json" });
+      res.end(
+        JSON.stringify({
+          amount_debit: "FOO:123",
+          amount_credit: "BAR:123",
+        }),
+      );
+    } else {
+      res.writeHead(400, { "Content-Type": "application/json" });
+      res.end(JSON.stringify({ code: 1, message: "bad request" }));
+    }
+  });
+  await new Promise<void>((resolve, reject) => {
+    server.listen(8071, () => resolve());
+  });
+  return {
+    stop() {
+      server.close();
+    },
+  };
+}
+
+/**
+ * Test for currency conversion during manual withdrawal.
+ */
+export async function runWithdrawalConversionTest(t: GlobalTestState) {
+  // Set up test environment
+
+  const db = await setupDb(t);
+
+  const bank = await BankService.create(t, {
+    allowRegistrations: true,
+    currency: "TESTKUDOS",
+    database: db.connStr,
+    httpPort: 8082,
+  });
+
+  const exchange = ExchangeService.create(t, {
+    name: "testexchange-1",
+    currency: "TESTKUDOS",
+    httpPort: 8081,
+    database: db.connStr,
+  });
+
+  const merchant = await MerchantService.create(t, {
+    name: "testmerchant-1",
+    currency: "TESTKUDOS",
+    httpPort: 8083,
+    database: db.connStr,
+  });
+
+  const exchangeBankAccount = await bank.createExchangeAccount(
+    "myexchange",
+    "x",
+  );
+  exchangeBankAccount.conversionUrl = "http://localhost:8071/";;
+  await exchange.addBankAccount("1", exchangeBankAccount);
+
+  await bank.start();
+
+  await bank.pingUntilAvailable();
+
+  exchange.addOfferedCoins(defaultCoinConfig);
+
+  await exchange.start();
+  await exchange.pingUntilAvailable();
+
+  merchant.addExchange(exchange);
+  await merchant.start();
+  await merchant.pingUntilAvailable();
+
+  await merchant.addInstanceWithWireAccount({
+    id: "default",
+    name: "Default Instance",
+    paytoUris: [generateRandomPayto("merchant-default")],
+    defaultWireTransferDelay: Duration.toTalerProtocolDuration(
+      Duration.fromSpec({ minutes: 1 }),
+    ),
+  });
+
+  await merchant.addInstanceWithWireAccount({
+    id: "minst1",
+    name: "minst1",
+    paytoUris: [generateRandomPayto("minst1")],
+    defaultWireTransferDelay: Duration.toTalerProtocolDuration(
+      Duration.fromSpec({ minutes: 1 }),
+    ),
+  });
+
+  const { walletClient, walletService } = await createWalletDaemonWithClient(
+    t,
+    { name: "wallet" },
+  );
+
+  await runTestfakeConversionService();
+
+  // Create a withdrawal operation
+
+  const bankAccessApiClient = new TalerCorebankApiClient(
+    bank.corebankApiBaseUrl,
+  );
+
+  const user = await bankAccessApiClient.createRandomBankUser();
+
+  await walletClient.call(WalletApiOperation.AddExchange, {
+    exchangeBaseUrl: exchange.baseUrl,
+  });
+
+  const infoRes = walletClient.call(
+    WalletApiOperation.GetWithdrawalDetailsForAmount,
+    {
+      exchangeBaseUrl: exchange.baseUrl,
+      amount: "EXTCOIN:20" as AmountString,
+    },
+  );
+
+  console.log(`withdrawal details: ${j2s(infoRes)}`);
+
+  const tStart = AbsoluteTime.now();
+
+  logger.info("starting AcceptManualWithdrawal request");
+  // We expect this to return immediately.
+
+  const wres = await walletClient.call(
+    WalletApiOperation.AcceptManualWithdrawal,
+    {
+      exchangeBaseUrl: exchange.baseUrl,
+      amount: "TESTKUDOS:10" as AmountString,
+    },
+  );
+
+  logger.info("AcceptManualWithdrawal finished");
+  logger.info(`result: ${j2s(wres)}`);
+
+  // Check that the request did not go into long-polling.
+  const duration = AbsoluteTime.difference(tStart, AbsoluteTime.now());
+  if (typeof duration.d_ms !== "number" || duration.d_ms > 5 * 1000) {
+    throw Error("withdrawal took too long (longpolling issue)");
+  }
+
+  const reservePub: string = wres.reservePub;
+
+  const wireGatewayApiClient = new WireGatewayApiClient(
+    exchangeBankAccount.wireGatewayApiBaseUrl,
+    {
+      auth: {
+        username: exchangeBankAccount.accountName,
+        password: exchangeBankAccount.accountPassword,
+      },
+    },
+  );
+
+  await wireGatewayApiClient.adminAddIncoming({
+    amount: "TESTKUDOS:10",
+    debitAccountPayto: user.accountPaytoUri,
+    reservePub: reservePub,
+  });
+
+  await exchange.runWirewatchOnce();
+
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+
+  // Check balance
+
+  const balResp = await walletClient.call(WalletApiOperation.GetBalances, {});
+  t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
+}
+
+runWithdrawalConversionTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts 
b/packages/taler-harness/src/integrationtests/testrunner.ts
index 25bce5712..b363e58a9 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -95,6 +95,7 @@ import { runWalletGenDbTest } from "./test-wallet-gendb.js";
 import { runLibeufinBankTest } from "./test-libeufin-bank.js";
 import { runMultiExchangeTest } from "./test-multiexchange.js";
 import { runAgeRestrictionsDepositTest } from 
"./test-age-restrictions-deposit.js";
+import { runWithdrawalConversionTest } from "./test-withdrawal-conversion.js";
 
 /**
  * Test runner.
@@ -173,6 +174,7 @@ const allTests: TestMainFunction[] = [
   runWithdrawalBankIntegratedTest,
   runWithdrawalFakebankTest,
   runWithdrawalFeesTest,
+  runWithdrawalConversionTest,
   runWithdrawalHugeTest,
   runTermOfServiceFormatTest,
   runStoredBackupsTest,
diff --git a/packages/taler-util/src/merchant-api-types.ts 
b/packages/taler-util/src/merchant-api-types.ts
index 9a7740088..999246597 100644
--- a/packages/taler-util/src/merchant-api-types.ts
+++ b/packages/taler-util/src/merchant-api-types.ts
@@ -44,8 +44,8 @@ import {
   TalerProtocolDuration,
   codecForTimestamp,
   TalerProtocolTimestamp,
-  WireAccount,
-  codecForWireAccount,
+  ExchangeWireAccount,
+  codecForExchangeWireAccount,
   codecForList,
   FacadeCredentials,
 } from "@gnu-taler/taler-util";
@@ -376,13 +376,13 @@ export interface MerchantReserveCreateConfirmation {
   reserve_pub: EddsaPublicKeyString;
 
   // Wire accounts of the exchange where to transfer the funds.
-  accounts: WireAccount[];
+  accounts: ExchangeWireAccount[];
 }
 
 export const codecForMerchantReserveCreateConfirmation =
   (): Codec<MerchantReserveCreateConfirmation> =>
     buildCodecForObject<MerchantReserveCreateConfirmation>()
-      .property("accounts", codecForList(codecForWireAccount()))
+      .property("accounts", codecForList(codecForExchangeWireAccount()))
       .property("reserve_pub", codecForString())
       .build("MerchantReserveCreateConfirmation");
 
diff --git a/packages/taler-util/src/taler-types.ts 
b/packages/taler-util/src/taler-types.ts
index c0c8cc17d..5774f09f7 100644
--- a/packages/taler-util/src/taler-types.ts
+++ b/packages/taler-util/src/taler-types.ts
@@ -761,7 +761,7 @@ export class ExchangeKeysJson {
 
   global_fees: GlobalFees[];
 
-  accounts: AccountInfo[];
+  accounts: ExchangeWireAccount[];
 
   wire_fees: { [methodName: string]: WireFeesJson[] };
 
@@ -939,20 +939,6 @@ export class WireFeesJson {
   end_date: TalerProtocolTimestamp;
 }
 
-export interface AccountInfo {
-  payto_uri: string;
-  master_sig: string;
-  // Will become mandatory in later protocol versions
-  conversion_url?: string;
-  credit_restrictions?: any;
-  debit_restrictions?: any;
-}
-
-/**
- * @deprecated
- */
-export interface ExchangeWireJson { }
-
 /**
  * Proposal returned from the contract URL.
  */
@@ -1516,7 +1502,7 @@ export const codecForExchangeKeysJson = (): 
Codec<ExchangeKeysJson> =>
     .property("version", codecForString())
     .property("reserve_closing_delay", codecForDuration)
     .property("global_fees", codecForList(codecForGlobalFees()))
-    .property("accounts", codecForList(codecForAccountInfo()))
+    .property("accounts", codecForList(codecForExchangeWireAccount()))
     .property("wire_fees", codecForMap(codecForList(codecForWireFeesJson())))
     .property("denominations", codecForList(codecForNgDenominations))
     .build("ExchangeKeysJson");
@@ -1530,15 +1516,6 @@ export const codecForWireFeesJson = (): 
Codec<WireFeesJson> =>
     .property("end_date", codecForTimestamp)
     .build("WireFeesJson");
 
-export const codecForAccountInfo = (): Codec<AccountInfo> =>
-  buildCodecForObject<AccountInfo>()
-    .property("payto_uri", codecForString())
-    .property("master_sig", codecForString())
-    .property("conversion_url", codecOptional(codecForString()))
-    .property("credit_restrictions", codecForAny())
-    .property("debit_restrictions", codecForAny())
-    .build("AccountInfo");
-
 export const codecForProposal = (): Codec<Proposal> =>
   buildCodecForObject<Proposal>()
     .property("contract_terms", codecForAny())
@@ -1568,13 +1545,14 @@ export const codecForWithdrawOperationStatusResponse =
       .property("wire_types", codecForList(codecForString()))
       .build("WithdrawOperationStatusResponse");
 
-export const codecForRewardPickupGetResponse = (): 
Codec<RewardPickupGetResponse> =>
-  buildCodecForObject<RewardPickupGetResponse>()
-    .property("reward_amount", codecForString())
-    .property("exchange_url", codecForString())
-    .property("next_url", codecOptional(codecForString()))
-    .property("expiration", codecForTimestamp)
-    .build("TipPickupGetResponse");
+export const codecForRewardPickupGetResponse =
+  (): Codec<RewardPickupGetResponse> =>
+    buildCodecForObject<RewardPickupGetResponse>()
+      .property("reward_amount", codecForString())
+      .property("exchange_url", codecForString())
+      .property("next_url", codecOptional(codecForString()))
+      .property("expiration", codecForTimestamp)
+      .build("TipPickupGetResponse");
 
 export const codecForRecoupConfirmation = (): Codec<RecoupConfirmation> =>
   buildCodecForObject<RecoupConfirmation>()
@@ -2376,7 +2354,7 @@ export interface RegexAccountRestriction {
   human_hint_i18n?: InternationalizedString;
 }
 
-export interface WireAccount {
+export interface ExchangeWireAccount {
   // payto:// URI identifying the account and wire method
   payto_uri: string;
 
@@ -2401,8 +2379,8 @@ export interface WireAccount {
   master_sig: EddsaSignatureString;
 }
 
-export const codecForWireAccount = (): Codec<WireAccount> =>
-  buildCodecForObject<WireAccount>()
+export const codecForExchangeWireAccount = (): Codec<ExchangeWireAccount> =>
+  buildCodecForObject<ExchangeWireAccount>()
     .property("conversion_url", codecOptional(codecForString()))
     .property("credit_restrictions", codecForList(codecForAny()))
     .property("debit_restrictions", codecForList(codecForAny()))
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 7a4ad91e8..148117673 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -58,11 +58,13 @@ import {
   DenomKeyType,
   DenominationPubKey,
   ExchangeAuditor,
+  ExchangeWireAccount,
   InternationalizedString,
   MerchantContractTerms,
   MerchantInfo,
   PeerContractTerms,
   UnblindedSignature,
+  codecForExchangeWireAccount,
   codecForMerchantContractTerms,
   codecForPeerContractTerms,
 } from "./taler-types.js";
@@ -1123,19 +1125,11 @@ export interface WireFee {
   sig: string;
 }
 
-/**
- * Information about one of the exchange's bank accounts.
- */
-export interface ExchangeAccount {
-  payto_uri: string;
-  master_sig: string;
-}
-
 export type WireFeeMap = { [wireMethod: string]: WireFee[] };
 
 export interface WireInfo {
   feesForType: WireFeeMap;
-  accounts: ExchangeAccount[];
+  accounts: ExchangeWireAccount[];
 }
 
 export interface ExchangeGlobalFees {
@@ -1154,12 +1148,6 @@ export interface ExchangeGlobalFees {
   signature: string;
 }
 
-const codecForExchangeAccount = (): Codec<ExchangeAccount> =>
-  buildCodecForObject<ExchangeAccount>()
-    .property("payto_uri", codecForString())
-    .property("master_sig", codecForString())
-    .build("codecForExchangeAccount");
-
 const codecForWireFee = (): Codec<WireFee> =>
   buildCodecForObject<WireFee>()
     .property("sig", codecForString())
@@ -1172,7 +1160,7 @@ const codecForWireFee = (): Codec<WireFee> =>
 const codecForWireInfo = (): Codec<WireInfo> =>
   buildCodecForObject<WireInfo>()
     .property("feesForType", codecForMap(codecForList(codecForWireFee())))
-    .property("accounts", codecForList(codecForExchangeAccount()))
+    .property("accounts", codecForList(codecForExchangeWireAccount()))
     .build("codecForWireInfo");
 
 export interface DenominationInfo {
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 82d7b42bf..622f04bd3 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -19,23 +19,19 @@
  */
 import {
   AbsoluteTime,
-  AccountInfo,
   Amounts,
   CancellationToken,
   canonicalizeBaseUrl,
   codecForExchangeKeysJson,
-  DenomGroup,
   DenominationPubKey,
   DenomKeyType,
   Duration,
   durationFromSpec,
   encodeCrock,
   ExchangeAuditor,
-  ExchangeDenomination,
-  ExchangeEntryStatus,
   ExchangeGlobalFees,
   ExchangeSignKeyJson,
-  ExchangeWireJson,
+  ExchangeWireAccount,
   GlobalFees,
   hashDenomPub,
   j2s,
@@ -58,10 +54,10 @@ import {
   WireInfo,
 } from "@gnu-taler/taler-util";
 import {
+  getExpiry,
   HttpRequestLibrary,
-  readSuccessResponseTextOrThrow,
   readSuccessResponseJsonOrThrow,
-  getExpiry,
+  readSuccessResponseTextOrThrow,
 } from "@gnu-taler/taler-util/http";
 import {
   DenominationRecord,
@@ -79,7 +75,7 @@ import {
   timestampProtocolToDb,
   WalletDbReadWriteTransaction,
 } from "../index.js";
-import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js";
+import { InternalWalletState } from "../internal-wallet-state.js";
 import { checkDbInvariant } from "../util/invariants.js";
 import {
   DbAccess,
@@ -88,10 +84,10 @@ import {
 } from "../util/query.js";
 import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js";
 import {
-  TaskRunResultType,
   runTaskWithErrorReporting,
   TaskIdentifiers,
   TaskRunResult,
+  TaskRunResultType,
 } from "./common.js";
 
 const logger = new Logger("exchanges.ts");
@@ -380,7 +376,7 @@ interface ExchangeKeysDownloadResult {
   recoup: Recoup[];
   listIssueDate: TalerProtocolTimestamp;
   globalFees: GlobalFees[];
-  accounts: AccountInfo[];
+  accounts: ExchangeWireAccount[];
   wireFees: { [methodName: string]: WireFeesJson[] };
 }
 

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]