gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated: implement pay-u


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: implement pay-url and disable broken wallet GC
Date: Tue, 20 Aug 2019 17:58:07 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 106bc6ad implement pay-url and disable broken wallet GC
106bc6ad is described below

commit 106bc6ad9a78c199e6b0e89bef854174d9014c28
Author: Florian Dold <address@hidden>
AuthorDate: Tue Aug 20 17:58:01 2019 +0200

    implement pay-url and disable broken wallet GC
---
 src/headless/taler-wallet-cli.ts | 104 ++++++++++++++++++++++++++++---
 src/wallet.ts                    | 131 ++++++++++++++++++---------------------
 src/walletTypes.ts               |   7 +++
 3 files changed, 162 insertions(+), 80 deletions(-)

diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 0a7529e4..4a1f5d91 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -21,13 +21,27 @@ import { MerchantBackendConnection } from "./merchant";
 import { runIntegrationTest } from "./integrationtest";
 import { Wallet } from "../wallet";
 import querystring = require("querystring");
-import qrcodeGenerator = require("qrcode-generator")
+import qrcodeGenerator = require("qrcode-generator");
+import readline = require("readline");
 
 const program = new commander.Command();
 program.version("0.0.1").option("--verbose", "enable verbose output", false);
 
 const walletDbPath = os.homedir + "/" + ".talerwalletdb.json";
 
+function prompt(question: string): Promise<string> {
+  const stdinReadline = readline.createInterface({
+    input: process.stdin,
+    output: process.stdout,
+  });
+  return new Promise<string>((resolve, reject) => {
+    stdinReadline.question(question, res => {
+      resolve(res);
+      stdinReadline.close();
+    });
+  });
+}
+
 function applyVerbose(verbose: boolean) {
   if (verbose) {
     console.log("enabled verbose logging");
@@ -64,8 +78,8 @@ program
   });
 
 async function asyncSleep(milliSeconds: number): Promise<void> {
-  return new Promise<void>((resolve, reject) =>{
-    setTimeout(() => resolve(), milliSeconds)
+  return new Promise<void>((resolve, reject) => {
+    setTimeout(() => resolve(), milliSeconds);
   });
 }
 
@@ -76,8 +90,16 @@ program
   .action(async cmdObj => {
     applyVerbose(program.verbose);
     console.log("creating order");
-    const merchantBackend = new 
MerchantBackendConnection("https://backend.test.taler.net";, "default", 
"sandbox");
-    const orderResp = await merchantBackend.createOrder(cmdObj.amount, 
cmdObj.summary, "");
+    const merchantBackend = new MerchantBackendConnection(
+      "https://backend.test.taler.net";,
+      "default",
+      "sandbox",
+    );
+    const orderResp = await merchantBackend.createOrder(
+      cmdObj.amount,
+      cmdObj.summary,
+      "",
+    );
     console.log("created new order with order ID", orderResp.orderId);
     const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId);
     const qrcode = qrcodeGenerator(0, "M");
@@ -87,21 +109,85 @@ program
       process.exit(1);
       return;
     }
-    qrcode.addData("talerpay:" + querystring.escape(contractUrl));
+    const url = "talerpay:" + querystring.escape(contractUrl);
+    console.log("contract url:", url);
+    qrcode.addData(url);
     qrcode.make();
     console.log(qrcode.createASCII());
-    console.log("waiting for payment ...")
+    console.log("waiting for payment ...");
     while (1) {
       await asyncSleep(500);
-      const checkPayResp2 = await 
merchantBackend.checkPayment(orderResp.orderId);
+      const checkPayResp2 = await merchantBackend.checkPayment(
+        orderResp.orderId,
+      );
       if (checkPayResp2.paid) {
-        console.log("payment successfully received!")
+        console.log("payment successfully received!");
         break;
       }
     }
   });
 
 program
+  .command("pay-url <pay-url>")
+  .option("-y, --yes", "automatically answer yes to prompts")
+  .action(async (payUrl, cmdObj) => {
+    applyVerbose(program.verbose);
+    console.log("paying for", payUrl);
+    const wallet = await getDefaultNodeWallet({
+      persistentStoragePath: walletDbPath,
+    });
+    const result = await wallet.preparePay(payUrl);
+    if (result.status === "error") {
+      console.error("Could not pay:", result.error);
+      process.exit(1);
+      return;
+    }
+    if (result.status === "insufficient-balance") {
+      console.log("contract", result.contractTerms!);
+      console.error("insufficient balance");
+      process.exit(1);
+      return;
+    }
+    if (result.status === "paid") {
+      console.log("already paid!");
+      process.exit(0);
+      return;
+    }
+    if (result.status === "payment-possible") {
+      console.log("paying ...");
+    } else {
+      throw Error("not reached");
+    }
+    console.log("contract", result.contractTerms!);
+    let pay;
+    if (cmdObj.yes) {
+      pay = true;
+    } else {
+      while (true) {
+        const yesNoResp = (await prompt("Pay? [Y/n]")).toLowerCase();
+        if (yesNoResp === "" || yesNoResp === "y" || yesNoResp === "yes") {
+          pay = true;
+          break;
+        } else if (yesNoResp === "n" || yesNoResp === "no") {
+          pay = false;
+          break;
+        } else {
+          console.log("please answer y/n");
+        }
+      }
+    }
+
+    if (pay) {
+      const payRes = await wallet.confirmPay(result.proposalId!, undefined);
+      console.log("paid!");      
+    } else {
+      console.log("not paying");
+    }
+
+    wallet.stop();
+  });
+
+program
   .command("integrationtest")
   .option(
     "-e, --exchange <exchange-url>",
diff --git a/src/wallet.ts b/src/wallet.ts
index f7df6658..eda7bfea 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -104,6 +104,7 @@ import {
   TipStatus,
   WalletBalance,
   WalletBalanceEntry,
+  PreparePayResult,
 } from "./walletTypes";
 import { openPromise } from "./promiseUtils";
 
@@ -382,7 +383,6 @@ export class Wallet {
 
   private async fillDefaults() {
     const onTrue = (r: QueryRoot) => {
-      console.log("defaults already applied");
     };
     const onFalse = (r: QueryRoot) => {
       Wallet.enableTracing && console.log("applying defaults");
@@ -593,6 +593,8 @@ export class Wallet {
         .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
         .toArray();
 
+      console.log("considering coins", coins);
+
       const denoms = await this.q()
         .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
         .toArray();
@@ -718,6 +720,53 @@ export class Wallet {
     return t;
   }
 
+  async preparePay(url: string): Promise<PreparePayResult> {
+    const talerpayPrefix = "talerpay:"
+    if (url.startsWith(talerpayPrefix)) {
+      url = decodeURIComponent(url.substring(talerpayPrefix.length));
+    }
+    let proposalId: number;
+    let checkResult: CheckPayResult;
+    try {
+      console.log("downloading proposal");
+      proposalId = await this.downloadProposal(url);
+      console.log("calling checkPay");
+      checkResult = await this.checkPay(proposalId);
+      console.log("checkPay result", checkResult);
+    } catch (e) {
+      return {
+        status: "error",
+        error: e.toString(),
+      }
+    }
+    const proposal = await this.getProposal(proposalId);
+    if (!proposal) {
+      throw Error("could not get proposal");
+    }
+    if (checkResult.status === "paid") {
+      return {
+        status: "paid",
+        contractTerms: proposal.contractTerms,
+        proposalId: proposal.id!,
+      };
+    }
+    if (checkResult.status === "insufficient-balance") {
+      return {
+        status: "insufficient-balance",
+        contractTerms: proposal.contractTerms,
+        proposalId: proposal.id!,
+      };
+    }
+    if (checkResult.status === "payment-possible") {
+      return {
+        status: "payment-possible",
+        contractTerms: proposal.contractTerms,
+        proposalId: proposal.id!,
+      };
+    }
+    throw Error("not reached");
+  }
+
   /**
    * Download a proposal and store it in the database.
    * Returns an id for it to retrieve it later.
@@ -1001,6 +1050,8 @@ export class Wallet {
 
     const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount);
 
+    Wallet.enableTracing && console.log(`checking if payment of 
${JSON.stringify(paymentAmount)} is possible`);
+
     let wireFeeLimit;
     if (proposal.contractTerms.max_wire_fee) {
       wireFeeLimit = Amounts.parseOrThrow(proposal.contractTerms.max_wire_fee);
@@ -1146,7 +1197,10 @@ export class Wallet {
         this.processPreCoinThrottle[preCoin.exchangeBaseUrl]
       ) {
         const timeout = Math.min(retryDelayMs * 2, 5 * 60 * 1000);
-        Wallet.enableTracing && console.log(`throttling processPreCoin of 
${preCoinPub} for ${timeout}ms`);
+        Wallet.enableTracing &&
+          console.log(
+            `throttling processPreCoin of ${preCoinPub} for ${timeout}ms`,
+          );
         this.timerGroup.after(retryDelayMs, () => processPreCoinInternal());
         return op.promise;
       }
@@ -3370,76 +3424,11 @@ export class Wallet {
    * based on the current system time.
    */
   async collectGarbage() {
-    const nowMilli = new Date().getTime();
-    const nowSec = Math.floor(nowMilli / 1000);
-
-    const gcReserve = (r: ReserveRecord, n: number) => {
-      // This rule to purge reserves is a bit over-eager, since we still might
-      // receive an emergency payback from the exchange.  In this case we need
-      // to wait for the exchange to wire the money back or change this rule to
-      // wait until all coins from the reserve were spent.
-      if (r.timestamp_depleted) {
-        return true;
-      }
-      return false;
-    };
-    await this.q()
-      .deleteIf(Stores.reserves, gcReserve)
-      .finish();
-
-    const gcProposal = (d: ProposalDownloadRecord, n: number) => {
-      // Delete proposal after 60 minutes or 5 minutes before pay deadline,
-      // whatever comes first.
-      const deadlinePayMilli =
-        getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
-      const deadlineExpireMilli = nowMilli + 1000 * 60 * 60;
-      return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
-    };
-    await this.q()
-      .deleteIf(Stores.proposals, gcProposal)
-      .finish();
-
-    const activeExchanges: string[] = [];
-    const gcExchange = (d: ExchangeRecord, n: number) => {
-      // Delete if if unused and last update more than 20 minutes ago
-      if (!d.lastUsedTime && nowMilli > d.lastUpdateTime + 1000 * 60 * 20) {
-        return true;
-      }
-      activeExchanges.push(d.baseUrl);
-      return false;
-    };
-
-    await this.q()
-      .deleteIf(Stores.exchanges, gcExchange)
-      .finish();
-
-    // FIXME: check if this is correct!
-    const gcDenominations = (d: DenominationRecord, n: number) => {
-      if (nowSec > getTalerStampSec(d.stampExpireDeposit)!) {
-        console.log("garbage-collecting denomination due to expiration");
-        return true;
-      }
-      if (activeExchanges.indexOf(d.exchangeBaseUrl) < 0) {
-        console.log("garbage-collecting denomination due to missing exchange");
-        return true;
-      }
-      return false;
-    };
-    await this.q()
-      .deleteIf(Stores.denominations, gcDenominations)
-      .finish();
-
-    const gcWireFees = (r: ExchangeWireFeesRecord, n: number) => {
-      if (activeExchanges.indexOf(r.exchangeBaseUrl) < 0) {
-        return true;
-      }
-      return false;
-    };
-    await this.q()
-      .deleteIf(Stores.exchangeWireFees, gcWireFees)
-      .finish();
+    // FIXME(#5845)
 
-    // FIXME(#5210) also GC coins
+    // We currently do not garbage-collect the wallet database.  This might 
change
+    // after the feature has been properly re-designed, and we have come up 
with a
+    // strategy to test it.
   }
 
   clearNotification(): void {
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
index f9d753f2..e6564b91 100644
--- a/src/walletTypes.ts
+++ b/src/walletTypes.ts
@@ -472,3 +472,10 @@ export interface NextUrlResult {
   nextUrl: string;
   lastSessionId: string | undefined;
 }
+
+export interface PreparePayResult {
+  status: "paid" | "insufficient-balance" | "payment-possible" | "error";
+  contractTerms?: ContractTerms;
+  error?: string;
+  proposalId?: number;
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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