From 5591077fe9241def5fa33fc90a24681c8b7b0976 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 23 Feb 2016 14:07:53 +0100 Subject: [PATCH] repurchase detection --- extension/content_scripts/notify.js | 34 ++++++++++++++++++++++---- extension/content_scripts/notify.ts | 38 +++++++++++++++++++++++++---- extension/lib/wallet/db.ts | 15 ++++++++++-- extension/lib/wallet/query.ts | 23 +++++++++++++++++ extension/lib/wallet/types.ts | 7 ++++++ extension/lib/wallet/wallet.ts | 30 ++++++++++++++++++++++- extension/lib/wallet/wxMessaging.ts | 5 ++++ 7 files changed, 139 insertions(+), 13 deletions(-) diff --git a/extension/content_scripts/notify.js b/extension/content_scripts/notify.js index 4749fe9bf..d81732f10 100644 --- a/extension/content_scripts/notify.js +++ b/extension/content_scripts/notify.js @@ -65,12 +65,36 @@ var TalerNotify; document.addEventListener("taler-contract", function (e) { // XXX: the merchant should just give us the parsed data ... var offer = JSON.parse(e.detail); - var uri = URI(chrome.extension.getURL("pages/confirm-contract.html")); - var params = { - offer: JSON.stringify(offer), - merchantPageUrl: document.location.href, + if (!offer.contract) { + console.error("contract field missing"); + return; + } + var msg = { + type: "check-repurchase", + detail: { + contract: offer.contract + }, }; - document.location.href = uri.query(params).href(); + chrome.runtime.sendMessage(msg, function (resp) { + if (resp.error) { + console.error("wallet backend error", resp); + return; + } + if (resp.isRepurchase) { + console.log("doing repurchase"); + console.assert(resp.existingFulfillmentUrl); + console.assert(resp.existingContractHash); + window.location.href = subst(resp.existingFulfillmentUrl, resp.existingContractHash); + } + else { + var uri = URI(chrome.extension.getURL("pages/confirm-contract.html")); + var params = { + offer: JSON.stringify(offer), + merchantPageUrl: document.location.href, + }; + document.location.href = uri.query(params).href(); + } + }); }); document.addEventListener('taler-execute-payment', function (e) { console.log("got taler-execute-payment in content page"); diff --git a/extension/content_scripts/notify.ts b/extension/content_scripts/notify.ts index 9a64bcf0c..d300dbc03 100644 --- a/extension/content_scripts/notify.ts +++ b/extension/content_scripts/notify.ts @@ -77,12 +77,40 @@ namespace TalerNotify { document.addEventListener("taler-contract", function(e: CustomEvent) { // XXX: the merchant should just give us the parsed data ... let offer = JSON.parse(e.detail); - let uri = URI(chrome.extension.getURL("pages/confirm-contract.html")); - let params = { - offer: JSON.stringify(offer), - merchantPageUrl: document.location.href, + + if (!offer.contract) { + console.error("contract field missing"); + return; + } + + let msg = { + type: "check-repurchase", + detail: { + contract: offer.contract + }, }; - document.location.href = uri.query(params).href(); + + chrome.runtime.sendMessage(msg, (resp) => { + if (resp.error) { + console.error("wallet backend error", resp); + return; + } + if (resp.isRepurchase) { + console.log("doing repurchase"); + console.assert(resp.existingFulfillmentUrl); + console.assert(resp.existingContractHash); + window.location.href = subst(resp.existingFulfillmentUrl, + resp.existingContractHash); + + } else { + let uri = URI(chrome.extension.getURL("pages/confirm-contract.html")); + let params = { + offer: JSON.stringify(offer), + merchantPageUrl: document.location.href, + }; + document.location.href = uri.query(params).href(); + } + }); }); diff --git a/extension/lib/wallet/db.ts b/extension/lib/wallet/db.ts index 106f1f34c..c7621c5ff 100644 --- a/extension/lib/wallet/db.ts +++ b/extension/lib/wallet/db.ts @@ -51,10 +51,21 @@ export function openTalerDb(): Promise { db.createObjectStore("denoms", {keyPath: "denomPub"}); const coins = db.createObjectStore("coins", {keyPath: "coinPub"}); coins.createIndex("mintBaseUrl", "mintBaseUrl"); - db.createObjectStore("transactions", {keyPath: "contractHash"}); + const transactions = db.createObjectStore("transactions", + {keyPath: "contractHash"}); + transactions.createIndex("repurchase", + [ + "contract.merchant_pub", + "contract.repurchase_correlation_id" + ]); + db.createObjectStore("precoins", {keyPath: "coinPub", autoIncrement: true}); - const history = db.createObjectStore("history", {keyPath: "id", autoIncrement: true}); + const history = db.createObjectStore("history", + { + keyPath: "id", + autoIncrement: true + }); history.createIndex("timestamp", "timestamp"); break; } diff --git a/extension/lib/wallet/query.ts b/extension/lib/wallet/query.ts index b82c85189..62411dab3 100644 --- a/extension/lib/wallet/query.ts +++ b/extension/lib/wallet/query.ts @@ -287,6 +287,29 @@ class QueryRoot { .then(() => promise); } + /** + * Get one object from a store by its key. + */ + getIndexed(storeName, indexName, key): Promise { + if (key === void 0) { + throw Error("key must not be undefined"); + } + + const {resolve, promise} = openPromise(); + + const doGetIndexed = (tx) => { + const req = tx.objectStore(storeName).index(indexName).get(key); + req.onsuccess = (r) => { + resolve(req.result); + }; + }; + + this.addWork(doGetIndexed, storeName, false); + return Promise.resolve() + .then(() => this.finish()) + .then(() => promise); + } + /** * Finish the query, and start the query in the first place if necessary. */ diff --git a/extension/lib/wallet/types.ts b/extension/lib/wallet/types.ts index 88b55f918..9c7b21b7c 100644 --- a/extension/lib/wallet/types.ts +++ b/extension/lib/wallet/types.ts @@ -257,6 +257,13 @@ export namespace Amounts { } +export interface CheckRepurchaseResult { + isRepurchase: boolean; + existingContractHash?: string; + existingFulfillmentUrl?: string; +} + + export interface Notifier { notify(); } \ No newline at end of file diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts index 67e35bd11..92fb92a4a 100644 --- a/extension/lib/wallet/wallet.ts +++ b/extension/lib/wallet/wallet.ts @@ -32,6 +32,7 @@ import {Reserve} from "./types"; import {CryptoApi} from "./cryptoApi"; import {Coin} from "./types"; import {PayCoinInfo} from "./types"; +import {CheckRepurchaseResult} from "./types"; "use strict"; @@ -279,8 +280,9 @@ interface CoinPaySig { interface Transaction { contractHash: string; - contract: any; + contract: Contract; payReq: any; + merchantSig: string; } @@ -512,6 +514,7 @@ export class Wallet { contractHash: offer.H_contract, contract: offer.contract, payReq: payReq, + merchantSig: offer.merchant_sig, }; console.log("pay request"); @@ -926,4 +929,29 @@ export class Wallet { .iter("history", {indexName: "timestamp"}) .reduce(collect, []) } + + checkRepurchase(contract: Contract): Promise { + if (!contract.repurchase_correlation_id) { + console.log("no repurchase: no correlation id"); + return Promise.resolve({isRepurchase: false}); + } + return Query(this.db) + .getIndexed("transactions", + "repurchase", + [contract.merchant_pub, contract.repurchase_correlation_id]) + .then((result: Transaction) => { + console.log("db result", result); + let isRepurchase; + if (result) { + console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id); + return { + isRepurchase: true, + existingContractHash: result.contractHash, + existingFulfillmentUrl: result.contract.fulfillment_url, + }; + } else { + return {isRepurchase: false}; + } + }); + } } \ No newline at end of file diff --git a/extension/lib/wallet/wxMessaging.ts b/extension/lib/wallet/wxMessaging.ts index d497e5246..740873d88 100644 --- a/extension/lib/wallet/wxMessaging.ts +++ b/extension/lib/wallet/wxMessaging.ts @@ -22,6 +22,7 @@ import {Checkable} from "./checkable"; import {AmountJson} from "./types"; import Port = chrome.runtime.Port; import {Notifier} from "./types"; +import {Contract} from "./wallet"; "use strict"; @@ -108,6 +109,10 @@ function makeHandlers(db: IDBDatabase, let amount = AmountJson.checked(detail.amount); return wallet.getReserveCreationInfo(detail.baseUrl, amount); }, + ["check-repurchase"]: function(detail) { + let contract = Contract.checked(detail.contract); + return wallet.checkRepurchase(contract); + }, ["get-history"]: function(detail) { // TODO: limit history length return wallet.getHistory();