gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: fix un-offered denom situatio


From: gnunet
Subject: [taler-wallet-core] branch master updated: fix un-offered denom situation, test case almost works
Date: Mon, 23 Aug 2021 22:28:53 +0200

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 828e65b0 fix un-offered denom situation, test case almost works
828e65b0 is described below

commit 828e65b0eba66b7c999d9867396797a239449a6d
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Aug 23 22:28:36 2021 +0200

    fix un-offered denom situation, test case almost works
---
 .../src/integrationtests/harness.ts                |  25 ++--
 .../src/integrationtests/test-denom-unoffered.ts   | 138 +++++++++++++++++++++
 .../src/integrationtests/testrunner.ts             |   2 +
 packages/taler-wallet-core/src/db.ts               |   3 -
 packages/taler-wallet-core/src/index.ts            |   1 +
 .../taler-wallet-core/src/operations/exchanges.ts  |  43 ++++++-
 packages/taler-wallet-core/src/operations/pay.ts   |   6 +
 .../src/operations/transactions.ts                 |   2 +-
 .../taler-wallet-core/src/operations/withdraw.ts   |  17 ++-
 9 files changed, 217 insertions(+), 20 deletions(-)

diff --git a/packages/taler-wallet-cli/src/integrationtests/harness.ts 
b/packages/taler-wallet-cli/src/integrationtests/harness.ts
index 10b93b33..d47dfe09 100644
--- a/packages/taler-wallet-cli/src/integrationtests/harness.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/harness.ts
@@ -838,12 +838,6 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       e.roundUnit ?? `${e.currency}:0.01`,
     );
     setTalerPaths(config, gc.testDir + "/talerhome");
-
-    config.setString(
-      "exchange",
-      "keydir",
-      "${TALER_DATA_HOME}/exchange/live-keys/",
-    );
     config.setString(
       "exchange",
       "revocation_dir",
@@ -1078,6 +1072,23 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     );
   }
 
+  async purgeSecmodKeys(): Promise<void> {
+    const cfg = Configuration.load(this.configFilename);
+    const rsaKeydir = cfg.getPath("taler-exchange-secmod-rsa", 
"KEY_DIR").required();
+    const eddsaKeydir = cfg.getPath("taler-exchange-secmod-eddsa", 
"KEY_DIR").required();
+    // Be *VERY* careful when changing this, or you will accidentally delete 
user data.
+    await sh(this.globalState, "rm-secmod-keys", `rm -rf ${rsaKeydir}/COIN_*`);
+    await sh(this.globalState, "rm-secmod-keys", `rm ${eddsaKeydir}/*`);
+  }
+
+  async purgeDatabase(): Promise<void> {
+    await sh(
+      this.globalState,
+      "exchange-dbinit",
+      `taler-exchange-dbinit -r -c "${this.configFilename}"`,
+    );
+  }
+
   async start(): Promise<void> {
     if (this.isRunning()) {
       throw Error("exchange is already running");
@@ -1111,8 +1122,6 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       [
         "-c",
         this.configFilename,
-        "--num-threads",
-        "1",
         ...this.timetravelArgArr,
       ],
       `exchange-httpd-${this.name}`,
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
new file mode 100644
index 00000000..7a1ff669
--- /dev/null
+++ b/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
@@ -0,0 +1,138 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 {
+  PreparePayResultType,
+  TalerErrorCode,
+  TalerErrorDetails,
+  TransactionType,
+} from "@gnu-taler/taler-util";
+import {
+  WalletApiOperation,
+} from "@gnu-taler/taler-wallet-core";
+import { makeEventId } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState, MerchantPrivateApi } from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+
+export async function runDenomUnofferedTest(t: GlobalTestState) {
+  // Set up test environment
+
+  const {
+    wallet,
+    bank,
+    exchange,
+    merchant,
+  } = await createSimpleTestkudosEnvironment(t);
+
+  // Withdraw digital cash into the wallet.
+
+  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+
+  // Make the exchange forget the denomination.
+  // Effectively we completely reset the exchange,
+  // but keep the exchange master public key.
+
+  await exchange.stop();
+  await exchange.purgeDatabase();
+  await exchange.purgeSecmodKeys();
+  await exchange.start();
+  await exchange.pingUntilAvailable();
+
+  await merchant.stop();
+  await merchant.start();
+  await merchant.pingUntilAvailable();
+
+  const order = {
+    summary: "Buy me!",
+    amount: "TESTKUDOS:5",
+    fulfillment_url: "taler://fulfillment-success/thx",
+  };
+
+  {
+    const orderResp = await MerchantPrivateApi.createOrder(
+      merchant,
+      "default",
+      {
+        order: order,
+      },
+    );
+
+    let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
+      merchant,
+      {
+        orderId: orderResp.order_id,
+      },
+    );
+
+    t.assertTrue(orderStatus.order_status === "unpaid");
+
+    // Make wallet pay for the order
+
+    const preparePayResult = await wallet.client.call(
+      WalletApiOperation.PreparePayForUri,
+      {
+        talerPayUri: orderStatus.taler_pay_uri,
+      },
+    );
+
+    t.assertTrue(
+      preparePayResult.status === PreparePayResultType.PaymentPossible,
+    );
+
+    const exc = await t.assertThrowsAsync(async () => {
+      await wallet.client.call(WalletApiOperation.ConfirmPay, {
+        proposalId: preparePayResult.proposalId,
+      });
+    });
+
+    const errorDetails: TalerErrorDetails = exc.operationError;
+    // FIXME: We might want a more specific error code here!
+    t.assertDeepEqual(
+      errorDetails.code,
+      TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
+    );
+    const merchantErrorCode = (errorDetails.details as any).errorResponse.code;
+    t.assertDeepEqual(
+      merchantErrorCode,
+      TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND,
+    );
+
+    const purchId = makeEventId(TransactionType.Payment, 
preparePayResult.proposalId);
+    await wallet.client.call(WalletApiOperation.DeleteTransaction, {
+      transactionId: purchId,
+    });
+
+    // Now, delete the purchase and refresh operation.
+  }
+
+  await wallet.client.call(WalletApiOperation.AddExchange, {
+    exchangeBaseUrl: exchange.baseUrl,
+    forceUpdate: true,
+  });
+
+  // Now withdrawal should work again.
+  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+
+  await wallet.runUntilDone();
+
+  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+  console.log(JSON.stringify(txs, undefined, 2));
+}
+
+runDenomUnofferedTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts 
b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
index ab699e8b..25067fbb 100644
--- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
@@ -82,6 +82,7 @@ import { runPaymentForgettableTest } from 
"./test-payment-forgettable.js";
 import { runPaymentZeroTest } from "./test-payment-zero.js";
 import { runMerchantSpecPublicOrdersTest } from 
"./test-merchant-spec-public-orders.js";
 import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
+import { runDenomUnofferedTest } from "./test-denom-unoffered.js";
 
 /**
  * Test runner.
@@ -101,6 +102,7 @@ const allTests: TestMainFunction[] = [
   runBankApiTest,
   runClaimLoopTest,
   runDepositTest,
+  runDenomUnofferedTest,
   runExchangeManagementTest,
   runExchangeTimetravelTest,
   runFeeRegressionTest,
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 65a874ea..093332e8 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -963,9 +963,6 @@ export interface RefreshSessionRecord {
   /**
    * 512-bit secret that can be used to derive
    * the other cryptographic material for the refresh session.
-   *
-   * FIXME:  We currently store the derived material, but
-   * should always derive it.
    */
   sessionSecretSeed: string;
 
diff --git a/packages/taler-wallet-core/src/index.ts 
b/packages/taler-wallet-core/src/index.ts
index ccd3488d..035edc76 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -46,3 +46,4 @@ export * from "./wallet-api-types.js";
 export * from "./wallet.js";
 
 export * from "./operations/backup/index.js";
+export { makeEventId } from "./operations/transactions.js";
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 0670c8a6..23459de9 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -153,6 +153,9 @@ async function downloadExchangeWithTermsOfService(
   return { tosText, tosEtag };
 }
 
+/**
+ * Get exchange details from the database.
+ */
 export async function getExchangeDetails(
   tx: GetReadOnlyAccess<{
     exchanges: typeof WalletStoresV1.exchanges;
@@ -320,6 +323,7 @@ interface ExchangeKeysDownloadResult {
   reserveClosingDelay: Duration;
   expiry: Timestamp;
   recoup: Recoup[];
+  listIssueDate: Timestamp;
 }
 
 /**
@@ -392,6 +396,7 @@ async function downloadKeysInfo(
       minDuration: durationFromSpec({ hours: 1 }),
     }),
     recoup: exchangeKeysJson.recoup ?? [],
+    listIssueDate: exchangeKeysJson.list_issue_date,
   };
 }
 
@@ -508,9 +513,9 @@ async function updateExchangeFromUrlImpl(
       r.lastError = undefined;
       r.retryInfo = initRetryInfo();
       r.lastUpdate = getTimestampNow();
-      (r.nextUpdate = keysInfo.expiry),
-        // New denominations might be available.
-        (r.nextRefreshCheck = getTimestampNow());
+      r.nextUpdate = keysInfo.expiry;
+      // New denominations might be available.
+      r.nextRefreshCheck = getTimestampNow();
       r.detailsPointer = {
         currency: details.currency,
         masterPublicKey: details.masterPublicKey,
@@ -521,17 +526,47 @@ async function updateExchangeFromUrlImpl(
       await tx.exchangeDetails.put(details);
 
       logger.trace("updating denominations in database");
+      const currentDenomSet = new Set<string>(
+        keysInfo.currentDenominations.map((x) => x.denomPubHash),
+      );
       for (const currentDenom of keysInfo.currentDenominations) {
         const oldDenom = await tx.denominations.get([
           baseUrl,
           currentDenom.denomPubHash,
         ]);
         if (oldDenom) {
-          // FIXME: Do consistency check
+          // FIXME: Do consistency check, report to auditor if necessary.
         } else {
           await tx.denominations.put(currentDenom);
         }
       }
+
+      // Update list issue date for all denominations,
+      // and mark non-offered denominations as such.
+      await tx.denominations.indexes.byExchangeBaseUrl
+        .iter(r.baseUrl)
+        .forEachAsync(async (x) => {
+          if (!currentDenomSet.has(x.denomPubHash)) {
+            // FIXME: Here, an auditor report should be created, unless
+            // the denomination is really legally expired.
+            if (x.isOffered) {
+              x.isOffered = false;
+              logger.info(
+                `setting denomination ${x.denomPubHash} to offered=false`,
+              );
+            }
+          } else {
+            x.listIssueDate = keysInfo.listIssueDate;
+            if (!x.isOffered) {
+              x.isOffered = true;
+              logger.info(
+                `setting denomination ${x.denomPubHash} to offered=true`,
+              );
+            }
+          }
+          await tx.denominations.put(x);
+        });
+
       logger.trace("done updating denominations in database");
 
       // Handle recoup
diff --git a/packages/taler-wallet-core/src/operations/pay.ts 
b/packages/taler-wallet-core/src/operations/pay.ts
index 54049feb..a201e6f8 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -211,6 +211,12 @@ function isSpendableCoin(coin: CoinRecord, denom: 
DenominationRecord): boolean {
   if (coin.suspended) {
     return false;
   }
+  if (denom.isRevoked) {
+    return false;
+  }
+  if (!denom.isOffered) {
+    return false;
+  }
   if (coin.status !== CoinStatus.Fresh) {
     return false;
   }
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index a07551e8..a21dbe8b 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -505,7 +505,7 @@ export async function deleteTransaction(
         const purchase = await tx.purchases.get(proposalId);
         if (purchase) {
           found = true;
-          await tx.proposals.delete(proposalId);
+          await tx.purchases.delete(proposalId);
         }
         if (found) {
           await tx.tombstones.put({
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 81c35c17..521cfa11 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -158,8 +158,8 @@ interface ExchangeWithdrawDetails {
 }
 
 /**
- * Check if a denom is withdrawable based on the expiration time
- * and revocation state.
+ * Check if a denom is withdrawable based on the expiration time,
+ * revocation and offered state.
  */
 export function isWithdrawableDenom(d: DenominationRecord): boolean {
   const now = getTimestampNow();
@@ -175,7 +175,7 @@ export function isWithdrawableDenom(d: DenominationRecord): 
boolean {
   }
   const remaining = getDurationRemaining(lastPossibleWithdraw, now);
   const stillOkay = remaining.d_ms !== 0;
-  return started && stillOkay && !d.isRevoked;
+  return started && stillOkay && !d.isRevoked && d.isOffered;
 }
 
 /**
@@ -230,6 +230,14 @@ export function selectWithdrawalDenominations(
     }
   }
 
+  if (logger.shouldLogTrace()) {
+    logger.trace(`selected withdrawal denoms for 
${Amounts.stringify(totalCoinValue)}`);
+    for (const sd of selectedDenoms) {
+      logger.trace(`denom_pub_hash=${sd.denom.denomPubHash}, 
count=${sd.count}`);
+    }
+    logger.trace("(end of withdrawal denom list)");
+  }
+
   return {
     selectedDenoms,
     totalCoinValue,
@@ -306,7 +314,8 @@ export async function getCandidateWithdrawalDenoms(
   return await ws.db
     .mktx((x) => ({ denominations: x.denominations }))
     .runReadOnly(async (tx) => {
-      return 
tx.denominations.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl);
+      const allDenoms = await 
tx.denominations.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl);
+      return allDenoms.filter(isWithdrawableDenom);
     });
 }
 

-- 
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]