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: precompute spec


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: precompute speculative signature for payment
Date: Tue, 12 Dec 2017 21:54:17 +0100

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 ca2a46a8 precompute speculative signature for payment
ca2a46a8 is described below

commit ca2a46a8575d66d529accb1ce3aaf97be8f37e2f
Author: Florian Dold <address@hidden>
AuthorDate: Tue Dec 12 21:54:14 2017 +0100

    precompute speculative signature for payment
---
 src/crypto/cryptoApi.ts    |  4 +--
 src/crypto/cryptoWorker.ts | 11 +++++--
 src/query.ts               | 32 +++++++++++++++++++++
 src/types.ts               |  8 ++++--
 src/wallet.ts              | 71 +++++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index 94083d62..d0ba6ada 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -275,8 +275,8 @@ export class CryptoApi {
     return this.doRpc<boolean>("isValidWireFee", 2, type, wf, masterPub);
   }
 
-  isValidPaymentSignature(sig: string, contractHash: string, merchantPub: 
string) {
-    return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, 
contractHash, merchantPub);
+  isValidPaymentSignature(sig: string, contractHash: string, merchantPub: 
string): Promise<boolean> {
+    return this.doRpc<boolean>("isValidPaymentSignature", 1, sig, 
contractHash, merchantPub);
   }
 
   signDeposit(contractTerms: ContractTerms,
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 92947d03..28634b23 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -261,7 +261,11 @@ namespace RpcFunctions {
    */
   export function signDeposit(contractTerms: ContractTerms,
                               cds: CoinWithDenom[]): PayCoinInfo {
-    const ret: PayCoinInfo = [];
+    const ret: PayCoinInfo = {
+      originalCoins: [],
+      updatedCoins: [],
+      sigs: [],
+    };
 
     const contractTermsHash = hashString(canonicalJson(contractTerms));
 
@@ -275,6 +279,7 @@ namespace RpcFunctions {
     const amountRemaining = new native.Amount(total);
     for (const cd of cds) {
       let coinSpend: Amount;
+      const originalCoin = { ...(cd.coin) };
 
       if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
         break;
@@ -324,7 +329,9 @@ namespace RpcFunctions {
         f: coinSpend.toJson(),
         ub_sig: cd.coin.denomSig,
       };
-      ret.push({sig: s, updatedCoin: cd.coin});
+      ret.sigs.push(s);
+      ret.updatedCoins.push(cd.coin);
+      ret.originalCoins.push(originalCoin);
     }
     return ret;
   }
diff --git a/src/query.ts b/src/query.ts
index 9889ed13..b199e0e9 100644
--- a/src/query.ts
+++ b/src/query.ts
@@ -807,6 +807,38 @@ export class QueryRoot {
   }
 
   /**
+   * Get get objects from a store by their keys.
+   * If no object for a key exists, the resulting position in the array
+   * contains 'undefined'.
+   */
+  getMany<T>(store: Store<T>, keys: any[]): Promise<T[]> {
+    this.checkFinished();
+
+    const { resolve, promise } = openPromise();
+    const results: T[] = [];
+
+    const doGetMany = (tx: IDBTransaction) => {
+      for (const key of keys) {
+        if (key === void 0) {
+          throw Error("key must not be undefined");
+        }
+        const req = tx.objectStore(store.name).get(key);
+        req.onsuccess = () => {
+          results.push(req.result);
+          if (results.length == keys.length) {
+            resolve(results);
+          }
+        };
+      }
+    };
+
+    this.addWork(doGetMany, store.name, false);
+    return Promise.resolve()
+                  .then(() => this.finish())
+                  .then(() => promise);
+  }
+
+  /**
    * Get one object from a store by its key.
    */
   getIndexed<I extends IDBValidKey, T>(index: Index<I, T>,
diff --git a/src/types.ts b/src/types.ts
index ca01203d..c0f36fc9 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1297,7 +1297,11 @@ export interface ExchangeWireFeesRecord {
  * Coins used for a payment, with signatures authorizing the payment and the
  * coins with remaining value updated to accomodate for a payment.
  */
-export type PayCoinInfo = Array<{ updatedCoin: CoinRecord, sig: CoinPaySig }>;
+export interface PayCoinInfo {
+  originalCoins: CoinRecord[];
+  updatedCoins: CoinRecord[];
+  sigs: CoinPaySig[];
+}
 
 
 /**
@@ -1787,8 +1791,6 @@ export interface PurchaseRecord {
    * Set to 0 if no refund was made on the purchase.
    */
   timestamp_refund: number;
-
-  userAccepted: boolean;
 }
 
 
diff --git a/src/wallet.ts b/src/wallet.ts
index 2ab24571..08e3049c 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -324,6 +324,13 @@ export interface CoinsReturnRecord {
   wire: any;
 }
 
+interface SpeculativePayData {
+  payCoinInfo: PayCoinInfo;
+  exchangeUrl: string;
+  proposalId: number;
+  proposal: ProposalRecord;
+}
+
 
 /**
  * Wallet protocol version spoken with the exchange
@@ -651,6 +658,7 @@ export class Wallet {
   private processPreCoinConcurrent = 0;
   private processPreCoinThrottle: {[url: string]: number} = {};
   private timerGroup: TimerGroup;
+  private speculativePayData: SpeculativePayData | undefined;
 
   /**
    * Set of identifiers for running operations.
@@ -971,7 +979,7 @@ export class Wallet {
                                  payCoinInfo: PayCoinInfo,
                                  chosenExchange: string): Promise<void> {
     const payReq: PayReq = {
-      coins: payCoinInfo.map((x) => x.sig),
+      coins: payCoinInfo.sigs,
       exchange: chosenExchange,
       merchant_pub: proposal.contractTerms.merchant_pub,
       order_id: proposal.contractTerms.order_id,
@@ -990,7 +998,7 @@ export class Wallet {
 
     await this.q()
               .put(Stores.purchases, t)
-              .putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
+              .putAll(Stores.coins, payCoinInfo.updatedCoins)
               .finish();
     this.badge.showNotification();
     this.notifier.notify();
@@ -1048,17 +1056,53 @@ export class Wallet {
       console.log("not confirming payment, insufficient coins");
       return "insufficient-balance";
     }
-    const {exchangeUrl, cds} = res;
 
-    const ds = await this.cryptoApi.signDeposit(proposal.contractTerms, cds);
-    await this.recordConfirmPay(proposal, ds, exchangeUrl);
+    const sd = await this.getSpeculativePayData(proposalId);
+    if (!sd) {
+      const { exchangeUrl, cds } = res;
+      const payCoinInfo = await 
this.cryptoApi.signDeposit(proposal.contractTerms, cds);
+      await this.recordConfirmPay(proposal, payCoinInfo, exchangeUrl);
+    } else {
+      await this.recordConfirmPay(sd.proposal, sd.payCoinInfo, sd.exchangeUrl);
+    }
+
     return "paid";
   }
 
+  /**
+   * Get the speculative pay data, but only if coins have not changed in 
between.
+   */
+  async getSpeculativePayData(proposalId: number): Promise<SpeculativePayData 
| undefined> {
+    const sp = this.speculativePayData;
+    if (!sp) {
+      return;
+    }
+    if (sp.proposalId != proposalId) {
+      return;
+    }
+    const coinKeys = sp.payCoinInfo.updatedCoins.map(x => x.coinPub);
+    const coins = await this.q().getMany(Stores.coins, coinKeys);
+    for (let i = 0; i < coins.length; i++) {
+      const specCoin = sp.payCoinInfo.originalCoins[i];
+      const currentCoin = coins[i];
+
+      // Coin does not exist anymore!
+      if (!currentCoin) {
+        return;
+      }
+      if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) != 0) 
{
+        return
+      }
+    }
+    return sp;
+  }
 
   /**
    * Check if payment for an offer is possible, or if the offer has already
    * been payed for.
+   *
+   * Also speculatively computes the signature for the payment to make the 
payment
+   * look faster to the user.
    */
   async checkPay(proposalId: number): Promise<CheckPayResult> {
     const proposal = await this.q().get(Stores.proposals, proposalId);
@@ -1089,6 +1133,19 @@ export class Wallet {
       console.log("not confirming payment, insufficient coins");
       return { status: "insufficient-balance" };
     }
+
+    // Only create speculative signature if we don't already have one for this 
proposal
+    if ((!this.speculativePayData) || (this.speculativePayData && 
this.speculativePayData.proposalId != proposalId)) {
+      const { exchangeUrl, cds } = res;
+      const payCoinInfo = await 
this.cryptoApi.signDeposit(proposal.contractTerms, cds);
+      this.speculativePayData = {
+        exchangeUrl,
+        payCoinInfo,
+        proposal,
+        proposalId,
+      };
+    }
+
     return { status: "payment-possible", coinSelection: res };
   }
 
@@ -2673,7 +2730,7 @@ export class Wallet {
 
     console.log("pci", payCoinInfo);
 
-    const coins = payCoinInfo.map((pci) => ({ coinPaySig: pci.sig }));
+    const coins = payCoinInfo.sigs.map((s) => ({ coinPaySig: s }));
 
     const coinsReturnRecord: CoinsReturnRecord = {
       coins,
@@ -2686,7 +2743,7 @@ export class Wallet {
 
     await this.q()
               .put(Stores.coinsReturns, coinsReturnRecord)
-              .putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
+              .putAll(Stores.coins, payCoinInfo.updatedCoins)
               .finish();
     this.badge.showNotification();
     this.notifier.notify();

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



reply via email to

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