gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] 04/04: implement new protocol / naming


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] 04/04: implement new protocol / naming
Date: Mon, 13 Feb 2017 00:44:51 +0100

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

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

commit 08d4a5b62532f867d3af67d8b8ad72921d02412a
Author: Florian Dold <address@hidden>
AuthorDate: Mon Feb 13 00:44:44 2017 +0100

    implement new protocol / naming
---
 src/content_scripts/notify.ts | 240 ++++++++++++++++++++++++------------------
 src/renderHtml.tsx            |   7 +-
 src/wallet.ts                 |  54 +++++-----
 src/wxBackend.ts              |  78 ++++++++------
 web-common                    |   2 +-
 5 files changed, 219 insertions(+), 162 deletions(-)

diff --git a/src/content_scripts/notify.ts b/src/content_scripts/notify.ts
index fda5d3f..10b988c 100644
--- a/src/content_scripts/notify.ts
+++ b/src/content_scripts/notify.ts
@@ -90,6 +90,19 @@ namespace TalerNotify {
     });
   }
 
+  function queryPayment(query: any): Promise<any> {
+    // current URL without fragment
+    const walletMsg = {
+      type: "query-payment",
+      detail: query,
+    };
+    return new Promise((resolve, reject) => {
+      chrome.runtime.sendMessage(walletMsg, (resp: any) => {
+        resolve(resp);
+      });
+    });
+  }
+
   function putHistory(historyEntry: any): Promise<void> {
     const walletMsg = {
       type: "put-history-entry",
@@ -109,16 +122,20 @@ namespace TalerNotify {
       type: "save-offer",
       detail: {
         offer: {
-          contract: offer.contract,
-          merchant_sig: offer.merchant_sig,
-          H_contract: offer.H_contract,
+          contract: offer.data,
+          merchant_sig: offer.sig,
+          H_contract: offer.hash,
           offer_time: new Date().getTime() / 1000
         },
       },
     };
     return new Promise((resolve, reject) => {
       chrome.runtime.sendMessage(walletMsg, (resp: any) => {
-        resolve(resp);
+        if (resp && resp.error) {
+          reject(resp);
+        } else {
+          resolve(resp);
+        }
       });
     });
   }
@@ -141,17 +158,10 @@ namespace TalerNotify {
         }
       });
 
-      if (resp && resp.type === "fetch") {
-        logVerbose && console.log("it's fetch");
-        taler.internalOfferContractFrom(resp.contractUrl);
+      if (resp && resp.type == "pay") {
+        logVerbose && console.log("doing taler.pay with", resp.payDetail);
+        taler.internalPay(resp.payDetail);
         document.documentElement.style.visibility = "hidden";
-
-      } else if (resp && resp.type === "execute") {
-        logVerbose && console.log("it's execute");
-        document.documentElement.style.visibility = "hidden";
-        taler.internalExecutePayment(resp.contractHash,
-                                     resp.payUrl,
-                                     resp.offerUrl);
       }
     });
   }
@@ -163,6 +173,104 @@ namespace TalerNotify {
     (detail: any, sendResponse: (msg: any) => void): void;
   }
 
+  function downloadContract(url: string): Promise<any> {
+    // FIXME: include and check nonce!
+    return new Promise((resolve, reject) => {
+      const contract_request = new XMLHttpRequest();
+      console.log("downloading contract from '" + url + "'")
+      contract_request.open("GET", url, true);
+      contract_request.onload = function (e) {
+        if (contract_request.readyState == 4) {
+          if (contract_request.status == 200) {
+            console.log("response text:",
+                        contract_request.responseText);
+            var contract_wrapper = JSON.parse(contract_request.responseText);
+            if (!contract_wrapper) {
+              console.error("response text was invalid json");
+              let detail = {hint: "invalid json", status: 
contract_request.status, body: contract_request.responseText};
+              reject(detail);
+              return;
+            }
+            resolve(contract_wrapper);
+          } else {
+            let detail = {hint: "contract download failed", status: 
contract_request.status, body: contract_request.responseText};
+            reject(detail);
+            return;
+          }
+        }
+      };
+      contract_request.onerror = function (e) {
+        let detail = {hint: "contract download failed", status: 
contract_request.status, body: contract_request.responseText};
+        reject(detail);
+        return;
+      };
+      contract_request.send();
+    });
+  }
+
+  async function processProposal(proposal: any) {
+    if (!proposal.data) {
+      console.error("field proposal.data field missing");
+      return;
+    }
+
+    if (!proposal.hash) {
+      console.error("proposal.hash field missing");
+      return;
+    }
+
+    let contractHash = await hashContract(proposal.data);
+
+    if (contractHash != proposal.hash) {
+      console.error("merchant-supplied contract hash is wrong");
+      return;
+    }
+
+    let resp = await checkRepurchase(proposal.data);
+
+    if (resp.error) {
+      console.error("wallet backend error", resp);
+      return;
+    }
+
+    if (resp.isRepurchase) {
+      logVerbose && console.log("doing repurchase");
+      console.assert(resp.existingFulfillmentUrl);
+      console.assert(resp.existingContractHash);
+      window.location.href = subst(resp.existingFulfillmentUrl,
+                                   resp.existingContractHash);
+
+    } else {
+
+      let merchantName = "(unknown)";
+      try {
+        merchantName = proposal.data.merchant.name;
+      } catch (e) {
+        // bad contract / name not included
+      }
+
+      let historyEntry = {
+        timestamp: (new Date).getTime(),
+        subjectId: `contract-${contractHash}`,
+        type: "offer-contract",
+        detail: {
+          contractHash,
+          merchantName,
+        }
+      };
+      await putHistory(historyEntry);
+      let offerId = await saveOffer(proposal);
+
+      const uri = URI(chrome.extension.getURL(
+        "/src/pages/confirm-contract.html"));
+      const params = {
+        offerId: offerId.toString(),
+      };
+      const target = uri.query(params).href();
+      document.location.replace(target);
+    }
+  }
+
   function registerHandlers() {
     /**
      * Add a handler for a DOM event, which automatically
@@ -237,70 +345,28 @@ namespace TalerNotify {
 
       const proposal = msg.contract_wrapper;
 
-      if (!proposal.data) {
-        console.error("field proposal.data field missing");
-        return;
-      }
+      processProposal(proposal);
+    });
 
-      if (!proposal.hash) {
-        console.error("proposal.hash field missing");
+    addHandler("taler-pay", async(msg: any, sendResponse: any) => {
+      let res = await queryPayment(msg.contract_query);
+      logVerbose && console.log("taler-pay: got response", res);
+      if (res && res.payReq) {
+        sendResponse(res);
         return;
       }
-
-      let contractHash = await hashContract(proposal.data);
-
-      if (contractHash != proposal.hash) {
-        console.error("merchant-supplied contract hash is wrong");
+      if (msg.contract_url) {
+        let proposal = await downloadContract(msg.contract_url);
+        await processProposal(proposal);
         return;
       }
 
-      let resp = await checkRepurchase(proposal.data);
-
-      if (resp.error) {
-        console.error("wallet backend error", resp);
+      if (msg.offer_url) {
+        document.location.href = msg.offer_url;
         return;
       }
 
-      if (resp.isRepurchase) {
-        logVerbose && console.log("doing repurchase");
-        console.assert(resp.existingFulfillmentUrl);
-        console.assert(resp.existingContractHash);
-        window.location.href = subst(resp.existingFulfillmentUrl,
-                                     resp.existingContractHash);
-
-      } else {
-
-        let merchantName = "(unknown)";
-        try {
-          merchantName = proposal.data.merchant.name;
-        } catch (e) {
-          // bad contract / name not included
-        }
-
-        let historyEntry = {
-          timestamp: (new Date).getTime(),
-          subjectId: `contract-${contractHash}`,
-          type: "offer-contract",
-          detail: {
-            contractHash,
-            merchantName,
-          }
-        };
-        await putHistory(historyEntry);
-        let offerId = await saveOffer(proposal);
-
-        const uri = URI(chrome.extension.getURL(
-          "/src/pages/confirm-contract.html"));
-        const params = {
-          offerId: offerId.toString(),
-        };
-        const target = uri.query(params).href();
-        if (msg.replace_navigation === true) {
-          document.location.replace(target);
-        } else {
-          document.location.href = target;
-        }
-      }
+      console.log("can't proceed with payment, no way to get contract 
specified");
     });
 
     addHandler("taler-payment-failed", (msg: any, sendResponse: any) => {
@@ -331,41 +397,5 @@ namespace TalerNotify {
         sendResponse();
       })
     });
-
-    addHandler("taler-get-payment", (msg: any, sendResponse: any) => {
-      const walletMsg = {
-        type: "execute-payment",
-        detail: {
-          H_contract: msg.H_contract,
-        },
-      };
-
-      chrome.runtime.sendMessage(walletMsg, (resp) => {
-        if (resp.rateLimitExceeded) {
-          console.error("rate limit exceeded, check for redirect loops");
-        }
-
-        if (!resp.success) {
-          if (msg.offering_url) {
-            window.location.href = msg.offering_url;
-          } else {
-            console.error("execute-payment failed", resp);
-          }
-          return;
-        }
-        let contract = resp.contract;
-        if (!contract) {
-          throw Error("contract missing");
-        }
-
-        // We have the details for then payment, the merchant page
-        // is responsible to give it to the merchant.
-        sendResponse({
-                       H_contract: msg.H_contract,
-                       contract: resp.contract,
-                       payment: resp.payReq,
-                     });
-      });
-    });
   }
 }
diff --git a/src/renderHtml.tsx b/src/renderHtml.tsx
index 40b4809..79e101b 100644
--- a/src/renderHtml.tsx
+++ b/src/renderHtml.tsx
@@ -30,7 +30,12 @@ export function prettyAmount(amount: AmountJson) {
 }
 
 export function renderContract(contract: Contract): JSX.Element {
-  let merchantName = <strong>{contract.merchant.name}</strong>;
+  let merchantName;
+  if (contract.merchant && contract.merchant.name) {
+    merchantName = <strong>{contract.merchant.name}</strong>;
+  } else {
+    merchantName = <strong>(pub: {contract.merchant_pub})</strong>;
+  }
   let amount = <strong>{prettyAmount(contract.amount)}</strong>;
 
   return (
diff --git a/src/wallet.ts b/src/wallet.ts
index 988ed32..1c9de01 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -164,20 +164,10 @@ export interface HistoryRecord {
 
 
 interface PayReq {
-  amount: AmountJson;
   coins: CoinPaySig[];
-  H_contract: string;
-  max_fee: AmountJson;
-  merchant_sig: string;
+  merchant_pub: string;
+  order_id: string;
   exchange: string;
-  refund_deadline: string;
-  timestamp: string;
-  pay_deadline: string;
-  /**
-   * Merchant instance identifier that should receive the
-   * payment, if applicable.
-   */
-  instance?: string;
 }
 
 interface TransactionRecord {
@@ -352,6 +342,8 @@ export namespace Stores {
       "contract.merchant_pub",
       "contract.repurchase_correlation_id"
     ]);
+    fulfillmentUrlIndex = new Index<string,TransactionRecord>(this, 
"fulfillment_url", "contract.fulfillment_url");
+    orderIdIndex = new Index<string,TransactionRecord>(this, "order_id", 
"contract.order_id");
   }
 
   class DenominationsStore extends Store<DenominationRecord> {
@@ -552,16 +544,10 @@ export class Wallet {
                                  payCoinInfo: PayCoinInfo,
                                  chosenExchange: string): Promise<void> {
     let payReq: PayReq = {
-      amount: offer.contract.amount,
       coins: payCoinInfo.map((x) => x.sig),
-      H_contract: offer.H_contract,
-      max_fee: offer.contract.max_fee,
-      merchant_sig: offer.merchant_sig,
-      exchange: URI(chosenExchange).href(),
-      refund_deadline: offer.contract.refund_deadline,
-      pay_deadline: offer.contract.pay_deadline,
-      timestamp: offer.contract.timestamp,
-      instance: offer.contract.merchant.instance
+      merchant_pub: offer.contract.merchant_pub,
+      order_id: offer.contract.order_id,
+      exchange: chosenExchange,
     };
     let t: TransactionRecord = {
       contractHash: offer.H_contract,
@@ -679,18 +665,36 @@ export class Wallet {
    * Retrieve all necessary information for looking up the contract
    * with the given hash.
    */
-  async executePayment(H_contract: string): Promise<any> {
-    let t = await this.q().get<TransactionRecord>(Stores.transactions,
-                                                  H_contract);
+  async queryPayment(query: any): Promise<any> {
+    let t: TransactionRecord | undefined;
+
+    console.log("query for payment", query);
+
+    switch (query.type) {
+      case "fulfillment_url":
+        t = await this.q().getIndexed(Stores.transactions.fulfillmentUrlIndex, 
query.value);
+        break;
+      case "order_id":
+        t = await this.q().getIndexed(Stores.transactions.orderIdIndex, 
query.value);
+        break;
+      case "hash":
+        t = await this.q().get<TransactionRecord>(Stores.transactions, 
query.value);
+        break;
+      default:
+        throw Error("invalid type");
+    }
+
     if (!t) {
+      console.log("query for payment failed");
       return {
         success: false,
-        contractFound: false,
       }
     }
+    console.log("query for payment succeeded:", t);
     let resp = {
       success: true,
       payReq: t.payReq,
+      H_contract: t.contractHash,
       contract: t.contract,
     };
     return resp;
diff --git a/src/wxBackend.ts b/src/wxBackend.ts
index 637ab5d..50e0689 100644
--- a/src/wxBackend.ts
+++ b/src/wxBackend.ts
@@ -139,20 +139,20 @@ function makeHandlers(db: IDBDatabase,
       }
       return wallet.checkPay(offer);
     },
-    ["execute-payment"]: function (detail: any, sender: MessageSender) {
+    ["query-payment"]: function (detail: any, sender: MessageSender) {
       if (sender.tab && sender.tab.id) {
         rateLimitCache[sender.tab.id]++;
         if (rateLimitCache[sender.tab.id] > 10) {
-          console.warn("rate limit for execute payment exceeded");
+          console.warn("rate limit for query-payment exceeded");
           let msg = {
-            error: "rate limit exceeded for execute-payment",
+            error: "rate limit exceeded for query-payment",
             rateLimitExceeded: true,
             hint: "Check for redirect loops",
           };
           return Promise.resolve(msg);
         }
       }
-      return wallet.executePayment(detail.H_contract);
+      return wallet.queryPayment(detail);
     },
     ["exchange-info"]: function (detail) {
       if (!detail.baseUrl) {
@@ -179,8 +179,10 @@ function makeHandlers(db: IDBDatabase,
       if (!offer) {
         return Promise.resolve({ error: "offer missing" });
       }
-      console.log("handling safe-offer");
-      return wallet.saveOffer(offer);
+      console.log("handling safe-offer", detail);
+      // FIXME:  fully migrate to new terminology
+      let checkedOffer = OfferRecord.checked(offer);
+      return wallet.saveOffer(checkedOffer);
     },
     ["reserve-creation-info"]: function (detail, sender) {
       if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
@@ -317,8 +319,7 @@ class ChromeNotifier implements Notifier {
  */
 let paymentRequestCookies: { [n: number]: any } = {};
 
-function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[],
-  url: string, tabId: number): any {
+function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[], url: 
string, tabId: number): any {
   const headers: { [s: string]: string } = {};
   for (let kv of headerList) {
     if (kv.value) {
@@ -326,35 +327,52 @@ function handleHttpPayment(headerList: 
chrome.webRequest.HttpHeader[],
     }
   }
 
-  const contractUrl = headers["x-taler-contract-url"];
-  if (contractUrl !== undefined) {
-    paymentRequestCookies[tabId] = { type: "fetch", contractUrl };
-    return;
+  let fields = {
+    contract_url: headers["x-taler-contract-url"],
+    contract_query: headers["x-taler-contract-query"],
+    offer_url: headers["x-taler-offer-url"],
+    pay_url: headers["x-taler-pay-url"],
   }
 
-  const contractHash = headers["x-taler-contract-hash"];
+  let n: number = 0;
 
-  if (contractHash !== undefined) {
-    const payUrl = headers["x-taler-pay-url"];
-    if (payUrl === undefined) {
-      console.log("malformed 402, X-Taler-Pay-Url missing");
-      return;
+  for (let key of Object.keys(fields)) {
+    if ((fields as any)[key]) {
+      n++;
     }
+  }
 
-    // Offer URL is optional
-    const offerUrl = headers["x-taler-offer-url"];
-    paymentRequestCookies[tabId] = {
-      type: "execute",
-      offerUrl,
-      payUrl,
-      contractHash
-    };
-    return;
+  if (n == 0) {
+    // looks like it's not a taler request, it might be
+    // for a different payment system (or the shop is buggy)
+    console.log("ignoring non-taler 402 response");
+  }
+
+  let contract_query = undefined;
+  // parse " type [ ':' value ] " format
+  if (fields.contract_query) {
+    let res = /[-a-zA-Z0-9_.,]+(:.*)?/.exec(fields.contract_query);
+    if (res) {
+      contract_query = {type: res[0], value: res[1]};
+      if (contract_query.type == "fulfillment_url" && !contract_query.value) {
+        contract_query.value = url;
+      }
+    }
   }
 
-  // looks like it's not a taler request, it might be
-  // for a different payment system (or the shop is buggy)
-  console.log("ignoring non-taler 402 response");
+  let payDetail = {
+    contract_query,
+    contract_url: fields.contract_url,
+    offer_url: fields.offer_url,
+    pay_url: fields.pay_url,
+  };
+
+  console.log("got pay detail", payDetail)
+
+  paymentRequestCookies[tabId] = {
+    type: "pay",
+    payDetail,
+  };
 }
 
 
diff --git a/web-common b/web-common
index d4de1c9..4831e66 160000
--- a/web-common
+++ b/web-common
@@ -1 +1 @@
-Subproject commit d4de1c912ecaac7991067027b352de61b237c0c9
+Subproject commit 4831e664d69759da288625911c053d145aa1b68c

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



reply via email to

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