commit 29dfafaa1282d6b1e03bd7211ce598a09c22bae7
parent 5a12edcd5d49029d85c47dc1169a5cc26cc6d10b
Author: Florian Dold <florian.dold@gmail.com>
Date: Fri, 19 Feb 2016 04:23:00 +0100
error handling
Diffstat:
9 files changed, 345 insertions(+), 317 deletions(-)
diff --git a/extension/background/main.ts b/extension/background/main.ts
@@ -23,8 +23,8 @@
// TypeScript does not allow ".js" extensions in the
// module name, so SystemJS must add it.
System.config({
- defaultJSExtensions: true,
-});
+ defaultJSExtensions: true,
+ });
// We expect that in the manifest, the emscripten js is loaded
// becore the background page.
@@ -36,14 +36,16 @@ if ("object" !== typeof Module) {
// Manually register the emscripten js as a SystemJS, so that
// we can use it from TypeScript by importing it.
-let mod = System.newModule({Module: Module});
-let modName = System.normalizeSync("../lib/emscripten/emsc");
-console.log("registering", modName);
-System.set(modName, mod);
-
-System.import("../lib/wallet/wxmessaging")
- .then((wxmessaging) => {
- wxmessaging.wxMain();
+{
+ let mod = System.newModule({Module: Module});
+ let modName = System.normalizeSync("../lib/emscripten/emsc");
+ console.log("registering", modName);
+ System.set(modName, mod);
+}
+
+System.import("../lib/wallet/wxMessaging")
+ .then((wxMessaging) => {
+ wxMessaging.wxMain();
})
.catch((e) => {
console.log("wallet failed");
diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts
@@ -108,60 +108,55 @@ class MintInfo implements IMintInfo {
* the first error.
*/
mergeKeys(newKeys: KeysJson, wallet: Wallet): Promise<void> {
- return Promise.resolve().then(() => {
- if (!this.masterPublicKey) {
- this.masterPublicKey = newKeys.master_public_key;
- }
+ if (!this.masterPublicKey) {
+ this.masterPublicKey = newKeys.master_public_key;
+ }
- if (this.masterPublicKey != newKeys.master_public_key) {
- throw Error("public keys do not match");
- }
+ if (this.masterPublicKey != newKeys.master_public_key) {
+ throw Error("public keys do not match");
+ }
- for (let newDenom of newKeys.denoms) {
- let found = false;
- for (let oldDenom of this.denoms) {
- if (oldDenom.denom_pub === newDenom.denom_pub) {
- let a = Object.assign({}, oldDenom);
- let b = Object.assign({}, newDenom);
- // pub hash is only there for convenience in the wallet
- delete a["pub_hash"];
- delete b["pub_hash"];
- if (!_.isEqual(a, b)) {
- console.log("old/new:");
- console.dir(a);
- console.dir(b);
- throw Error("denomination modified");
- }
- // TODO: check if info still matches
- found = true;
- break;
+ let ps = newKeys.denoms.map((newDenom) => {
+ let found = false;
+ for (let oldDenom of this.denoms) {
+ if (oldDenom.denom_pub === newDenom.denom_pub) {
+ let a = Object.assign({}, oldDenom);
+ let b = Object.assign({}, newDenom);
+ // pub hash is only there for convenience in the wallet
+ delete a["pub_hash"];
+ delete b["pub_hash"];
+ if (!deepEquals(a, b)) {
+ console.log("old/new:");
+ console.dir(a);
+ console.dir(b);
+ throw Error("denomination modified");
}
+ found = true;
+ break;
}
+ }
- if (found) {
- continue;
- }
-
- console.log("validating denomination");
-
- return wallet.isValidDenom(newDenom, this.masterPublicKey)
- .then((valid) => {
- if (!valid) {
- throw Error("signature on denomination invalid");
- }
+ if (found) {
+ return Promise.resolve();
+ }
- let d: Denomination = Object.assign({}, newDenom);
- d.pub_hash = native.RsaPublicKey.fromCrock(d.denom_pub)
- .encode()
- .hash()
- .toCrock();
- this.denoms.push(d);
+ return wallet.isValidDenom(newDenom, this.masterPublicKey)
+ .then((valid) => {
+ if (!valid) {
+ throw Error("signature on denomination invalid");
+ }
- });
+ let d: Denomination = Object.assign({}, newDenom);
+ d.pub_hash = native.RsaPublicKey.fromCrock(d.denom_pub)
+ .encode()
+ .hash()
+ .toCrock();
+ this.denoms.push(d);
- }
- return;
+ });
});
+
+ return Promise.all(ps).then(() => void 0);
}
}
@@ -308,6 +303,21 @@ export interface Badge {
type PayCoinInfo = Array<{ updatedCoin: Coin, sig: CoinPaySig }>;
+function deepEquals(x, y) {
+ if (x === y) {
+ return true;
+ }
+
+ if (Array.isArray(x) && x.length !== y.length) {
+ return false;
+ }
+
+ var p = Object.keys(x);
+ return Object.keys(y).every((i) => p.indexOf(i) !== -1) &&
+ p.every((i) => deepEquals(x[i], y[i]));
+}
+
+
function getTalerStampSec(stamp: string) {
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
if (!m) {
@@ -603,6 +613,9 @@ export class Wallet {
payReq: payReq,
};
+ console.log("pay request");
+ console.dir(payReq);
+
let historyEntry = {
type: "pay",
timestamp: (new Date).getTime(),
diff --git a/extension/lib/wallet/wxApi.ts b/extension/lib/wallet/wxApi.ts
@@ -0,0 +1,40 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {AmountJson} from "./types";
+import {ReserveCreationInfo} from "./types";
+
+/**
+ * Interface to the wallet through WebExtension messaging.
+ */
+
+
+export function getReserveCreationInfo(baseUrl: string,
+ amount: AmountJson): Promise<ReserveCreationInfo> {
+ let m = {type: "reserve-creation-info", detail: {baseUrl, amount}};
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage(m, (resp) => {
+ if (resp.error) {
+ console.error("error response", resp);
+ let e = Error("call to reserve-creation-info failed");
+ (e as any).errorResponse = resp;
+ reject(e);
+ return;
+ }
+ resolve(resp);
+ });
+ });
+}
+\ No newline at end of file
diff --git a/extension/lib/wallet/wxMessaging.ts b/extension/lib/wallet/wxMessaging.ts
@@ -0,0 +1,223 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+ */
+
+
+import {Wallet, Offer, Badge, ConfirmReserveRequest, CreateReserveRequest} from "./wallet";
+import {deleteDb, exportDb, openTalerDb} from "./db";
+import {BrowserHttpLib} from "./http";
+import {Checkable} from "./checkable";
+import {AmountJson} from "./types";
+import Port = chrome.runtime.Port;
+import {Notifier} from "./types";
+
+"use strict";
+
+/**
+ * Messaging for the WebExtensions wallet. Should contain
+ * parts that are specific for WebExtensions, but as little business
+ * logic as possible.
+ *
+ * @author Florian Dold
+ */
+
+
+type Handler = (detail: any) => Promise<any>;
+
+function makeHandlers(db: IDBDatabase,
+ wallet: Wallet): {[msg: string]: Handler} {
+ return {
+ ["balances"]: function(detail) {
+ return wallet.getBalances();
+ },
+ ["dump-db"]: function(detail) {
+ return exportDb(db);
+ },
+ ["reset"]: function(detail) {
+ let tx = db.transaction(db.objectStoreNames, 'readwrite');
+ for (let i = 0; i < db.objectStoreNames.length; i++) {
+ tx.objectStore(db.objectStoreNames[i]).clear();
+ }
+ deleteDb();
+
+ chrome.browserAction.setBadgeText({text: ""});
+ console.log("reset done");
+ // Response is synchronous
+ return Promise.resolve({});
+ },
+ ["create-reserve"]: function(detail) {
+ const d = {
+ mint: detail.mint,
+ amount: detail.amount,
+ };
+ const req = CreateReserveRequest.checked(d);
+ return wallet.createReserve(req);
+ },
+ ["confirm-reserve"]: function(detail) {
+ // TODO: make it a checkable
+ const d = {
+ reservePub: detail.reservePub
+ };
+ const req = ConfirmReserveRequest.checked(d);
+ return wallet.confirmReserve(req);
+ },
+ ["confirm-pay"]: function(detail) {
+ let offer;
+ try {
+ offer = Offer.checked(detail.offer);
+ } catch (e) {
+ if (e instanceof Checkable.SchemaError) {
+ console.error("schema error:", e.message);
+ return Promise.resolve({
+ error: "invalid contract",
+ hint: e.message,
+ detail: detail
+ });
+ } else {
+ throw e;
+ }
+ }
+
+ return wallet.confirmPay(offer);
+ },
+ ["execute-payment"]: function(detail) {
+ return wallet.executePayment(detail.H_contract);
+ },
+ ["mint-info"]: function(detail) {
+ if (!detail.baseUrl) {
+ return Promise.resolve({error: "bad url"});
+ }
+ return wallet.updateMintFromUrl(detail.baseUrl);
+ },
+ ["reserve-creation-info"]: function(detail) {
+ if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
+ return Promise.resolve({error: "bad url"});
+ }
+ let amount = AmountJson.checked(detail.amount);
+ return wallet.getReserveCreationInfo(detail.baseUrl, amount);
+ },
+ ["get-history"]: function(detail) {
+ // TODO: limit history length
+ return wallet.getHistory();
+ },
+ };
+}
+
+
+class ChromeBadge implements Badge {
+ setText(s: string) {
+ chrome.browserAction.setBadgeText({text: s});
+ }
+
+ setColor(c: string) {
+ chrome.browserAction.setBadgeBackgroundColor({color: c});
+ }
+}
+
+
+function dispatch(handlers, req, sendResponse) {
+ if (req.type in handlers) {
+ Promise
+ .resolve()
+ .then(() => {
+ const p = handlers[req.type](req.detail);
+
+ return p.then((r) => {
+ sendResponse(r);
+ })
+ })
+ .catch((e) => {
+ console.log("exception during wallet handler");
+ console.error(e);
+ sendResponse({
+ error: "exception",
+ hint: e.message,
+ stack: e.stack.toString()
+ });
+ });
+ // The sendResponse call is async
+ return true;
+ } else {
+ console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`);
+ sendResponse({error: "request unknown"});
+ // The sendResponse call is sync
+ return false;
+ }
+}
+
+class ChromeNotifier implements Notifier {
+ ports: Port[] = [];
+
+ constructor() {
+ chrome.runtime.onConnect.addListener((port) => {
+ console.log("got connect!");
+ this.ports.push(port);
+ port.onDisconnect.addListener(() => {
+ let i = this.ports.indexOf(port);
+ if (i >= 0) {
+ this.ports.splice(i, 1);
+ } else {
+ console.error("port already removed");
+ }
+ });
+ });
+ }
+
+ notify() {
+ console.log("notifying all ports");
+ for (let p of this.ports) {
+ p.postMessage({notify: true});
+ }
+ }
+}
+
+
+export function wxMain() {
+ chrome.browserAction.setBadgeText({text: ""});
+
+ Promise.resolve()
+ .then(() => {
+ return openTalerDb();
+ })
+ .catch((e) => {
+ console.error("could not open database");
+ console.error(e);
+ })
+ .then((db) => {
+ let http = new BrowserHttpLib();
+ let badge = new ChromeBadge();
+ let notifier = new ChromeNotifier();
+ let wallet = new Wallet(db, http, badge, notifier);
+ let handlers = makeHandlers(db, wallet);
+ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+ try {
+ return dispatch(handlers, req, sendResponse)
+ } catch (e) {
+ console.log("exception during wallet handler (dispatch)");
+ console.error(e);
+ sendResponse({
+ error: "exception",
+ hint: e.message,
+ stack: e.stack.toString()
+ });
+ return false;
+ }
+ });
+ })
+ .catch((e) => {
+ console.error("could not initialize wallet messaging");
+ console.error(e);
+ });
+}
+\ No newline at end of file
diff --git a/extension/lib/wallet/wxmessaging.ts b/extension/lib/wallet/wxmessaging.ts
@@ -1,223 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
-
- 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.
-
- 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
- TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
- */
-
-
-import {Wallet, Offer, Badge, ConfirmReserveRequest, CreateReserveRequest} from "./wallet";
-import {deleteDb, exportDb, openTalerDb} from "./db";
-import {BrowserHttpLib} from "./http";
-import {Checkable} from "./checkable";
-import {AmountJson} from "./types";
-import Port = chrome.runtime.Port;
-import {Notifier} from "./types";
-
-"use strict";
-
-/**
- * Messaging for the WebExtensions wallet. Should contain
- * parts that are specific for WebExtensions, but as little business
- * logic as possible.
- *
- * @author Florian Dold
- */
-
-
-type Handler = (detail: any) => Promise<any>;
-
-function makeHandlers(db: IDBDatabase,
- wallet: Wallet): {[msg: string]: Handler} {
- return {
- ["balances"]: function(detail) {
- return wallet.getBalances();
- },
- ["dump-db"]: function(detail) {
- return exportDb(db);
- },
- ["reset"]: function(detail) {
- let tx = db.transaction(db.objectStoreNames, 'readwrite');
- for (let i = 0; i < db.objectStoreNames.length; i++) {
- tx.objectStore(db.objectStoreNames[i]).clear();
- }
- deleteDb();
-
- chrome.browserAction.setBadgeText({text: ""});
- console.log("reset done");
- // Response is synchronous
- return Promise.resolve({});
- },
- ["create-reserve"]: function(detail) {
- const d = {
- mint: detail.mint,
- amount: detail.amount,
- };
- const req = CreateReserveRequest.checked(d);
- return wallet.createReserve(req);
- },
- ["confirm-reserve"]: function(detail) {
- // TODO: make it a checkable
- const d = {
- reservePub: detail.reservePub
- };
- const req = ConfirmReserveRequest.checked(d);
- return wallet.confirmReserve(req);
- },
- ["confirm-pay"]: function(detail) {
- let offer;
- try {
- offer = Offer.checked(detail.offer);
- } catch (e) {
- if (e instanceof Checkable.SchemaError) {
- console.error("schema error:", e.message);
- return Promise.resolve({
- error: "invalid contract",
- hint: e.message,
- detail: detail
- });
- } else {
- throw e;
- }
- }
-
- return wallet.confirmPay(offer);
- },
- ["execute-payment"]: function(detail) {
- return wallet.executePayment(detail.H_contract);
- },
- ["mint-info"]: function(detail) {
- if (!detail.baseUrl) {
- return Promise.resolve({error: "bad url"});
- }
- return wallet.updateMintFromUrl(detail.baseUrl);
- },
- ["reserve-creation-info"]: function(detail) {
- if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
- return Promise.resolve({error: "bad url"});
- }
- let amount = AmountJson.checked(detail.amount);
- return wallet.getReserveCreationInfo(detail.baseUrl, amount);
- },
- ["get-history"]: function(detail) {
- // TODO: limit history length
- return wallet.getHistory();
- },
- };
-}
-
-
-class ChromeBadge implements Badge {
- setText(s: string) {
- chrome.browserAction.setBadgeText({text: s});
- }
-
- setColor(c: string) {
- chrome.browserAction.setBadgeBackgroundColor({color: c});
- }
-}
-
-
-function dispatch(handlers, req, sendResponse) {
- if (req.type in handlers) {
- Promise
- .resolve()
- .then(() => {
- const p = handlers[req.type](req.detail);
-
- return p.then((r) => {
- sendResponse(r);
- })
- })
- .catch((e) => {
- console.log("exception during wallet handler");
- console.error(e.stack);
- sendResponse({
- error: "exception",
- hint: e.message,
- stack: e.stack.toString()
- });
- });
- // The sendResponse call is async
- return true;
- } else {
- console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`);
- sendResponse({error: "request unknown"});
- // The sendResponse call is sync
- return false;
- }
-}
-
-class ChromeNotifier implements Notifier {
- ports: Port[] = [];
-
- constructor() {
- chrome.runtime.onConnect.addListener((port) => {
- console.log("got connect!");
- this.ports.push(port);
- port.onDisconnect.addListener(() => {
- let i = this.ports.indexOf(port);
- if (i >= 0) {
- this.ports.splice(i, 1);
- } else {
- console.error("port already removed");
- }
- });
- });
- }
-
- notify() {
- console.log("notifying all ports");
- for (let p of this.ports) {
- p.postMessage({notify: true});
- }
- }
-}
-
-
-export function wxMain() {
- chrome.browserAction.setBadgeText({text: ""});
-
- Promise.resolve()
- .then(() => {
- return openTalerDb();
- })
- .catch((e) => {
- console.error("could not open database");
- console.error(e);
- })
- .then((db) => {
- let http = new BrowserHttpLib();
- let badge = new ChromeBadge();
- let notifier = new ChromeNotifier();
- let wallet = new Wallet(db, http, badge, notifier);
- let handlers = makeHandlers(db, wallet);
- chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
- try {
- return dispatch(handlers, req, sendResponse)
- } catch (e) {
- console.log("exception during wallet handler (dispatch)");
- console.error(e.stack);
- sendResponse({
- error: "exception",
- hint: e.message,
- stack: e.stack.toString()
- });
- return false;
- }
- });
- })
- .catch((e) => {
- console.error("could not initialize wallet messaging");
- console.error(e);
- });
-}
-\ No newline at end of file
diff --git a/extension/manifest.json b/extension/manifest.json
@@ -2,7 +2,7 @@
"description": "Privacy preserving and transparent payments",
"manifest_version": 2,
"name": "GNU Taler Wallet (git)",
- "version": "0.5.6",
+ "version": "0.5.9",
"applications": {
"gecko": {
diff --git a/extension/pages/confirm-create-reserve.js b/extension/pages/confirm-create-reserve.js
@@ -13,10 +13,10 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-System.register(["../lib/wallet/helpers", "../lib/wallet/types", "mithril"], function(exports_1, context_1) {
+System.register(["../lib/wallet/helpers", "../lib/wallet/types", "mithril", "../lib/wallet/wxApi"], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
- var helpers_1, types_1, mithril_1;
+ var helpers_1, types_1, mithril_1, wxApi_1;
var DelayTimer, Controller;
function view(ctrl) {
var controls = [];
@@ -104,21 +104,6 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types", "mithril"], fun
}
return Promise.resolve(mint);
}
- function getReserveCreationInfo(baseUrl, amount) {
- var m = { type: "reserve-creation-info", detail: { baseUrl: baseUrl, amount: amount } };
- return new Promise(function (resolve, reject) {
- chrome.runtime.sendMessage(m, function (resp) {
- if (resp.error) {
- console.error("error response", resp);
- var e = Error("call to reserve-creation-info failed");
- e.errorResponse = resp;
- reject(e);
- return;
- }
- resolve(resp);
- });
- });
- }
function main() {
var url = URI(document.location.href);
var query = URI.parseQuery(url.query());
@@ -149,6 +134,9 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types", "mithril"], fun
},
function (mithril_1_1) {
mithril_1 = mithril_1_1;
+ },
+ function (wxApi_1_1) {
+ wxApi_1 = wxApi_1_1;
}],
execute: function() {
"use strict";
@@ -208,7 +196,7 @@ System.register(["../lib/wallet/helpers", "../lib/wallet/types", "mithril"], fun
}
mithril_1.default.redraw(true);
console.log("doing get mint info");
- getReserveCreationInfo(_this.url(), _this.amount)
+ wxApi_1.getReserveCreationInfo(_this.url(), _this.amount)
.then(function (r) {
console.log("get mint info resolved");
_this.isValidMint = true;
diff --git a/extension/pages/confirm-create-reserve.tsx b/extension/pages/confirm-create-reserve.tsx
@@ -23,6 +23,7 @@ import {IMintInfo} from "../lib/wallet/types";
import {ReserveCreationInfo} from "../lib/wallet/types";
import MithrilComponent = _mithril.MithrilComponent;
import {Denomination} from "../lib/wallet/types";
+import {getReserveCreationInfo} from "../lib/wallet/wxApi";
"use strict";
@@ -273,24 +274,6 @@ function getSuggestedMint(currency: string): Promise<string> {
}
-function getReserveCreationInfo(baseUrl: string,
- amount: AmountJson): Promise<ReserveCreationInfo> {
- let m = {type: "reserve-creation-info", detail: {baseUrl, amount}};
- return new Promise((resolve, reject) => {
- chrome.runtime.sendMessage(m, (resp) => {
- if (resp.error) {
- console.error("error response", resp);
- let e = Error("call to reserve-creation-info failed");
- (e as any).errorResponse = resp;
- reject(e);
- return;
- }
- resolve(resp);
- });
- });
-}
-
-
export function main() {
const url = URI(document.location.href);
const query: any = URI.parseQuery(url.query());
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
@@ -22,7 +22,8 @@
"lib/wallet/query.ts",
"lib/wallet/types.ts",
"lib/wallet/wallet.ts",
- "lib/wallet/wxmessaging.ts",
+ "lib/wallet/wxApi.ts",
+ "lib/wallet/wxMessaging.ts",
"background/main.ts",
"content_scripts/notify.ts",
"popup/popup.tsx",