aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-01-05 01:10:31 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-01-05 01:10:31 +0100
commitc9fc0c31efa36ed6095971be693148d774667bb8 (patch)
treec6f2a410aeea81f0ba347391dc5b274a29a84900
parentba4d272cc4a503ebccdf015a606bc488b33330a5 (diff)
downloadwallet-core-c9fc0c31efa36ed6095971be693148d774667bb8.tar.xz
Nicer DB interface, various refactoring.
-rw-r--r--extension/background/emscriptif.js1
-rw-r--r--extension/background/emscriptif.ts3
-rw-r--r--extension/background/query.ts157
-rw-r--r--extension/background/wallet.js416
-rw-r--r--extension/background/wallet.ts580
-rw-r--r--extension/manifest.json1
-rw-r--r--extension/tsconfig.json1
7 files changed, 605 insertions, 554 deletions
diff --git a/extension/background/emscriptif.js b/extension/background/emscriptif.js
index 914cc4a48..83bf8b5bb 100644
--- a/extension/background/emscriptif.js
+++ b/extension/background/emscriptif.js
@@ -401,7 +401,6 @@ class HashCode extends PackedArenaObject {
case "nonce":
qual = RandomQuality.NONCE;
break;
- break;
default:
throw Error(format("unknown crypto quality: {0}", qual));
}
diff --git a/extension/background/emscriptif.ts b/extension/background/emscriptif.ts
index b5b040dc5..46937d088 100644
--- a/extension/background/emscriptif.ts
+++ b/extension/background/emscriptif.ts
@@ -302,7 +302,7 @@ class Amount extends ArenaObject {
}
- static getZero(currency: string, a?: Arena) {
+ static getZero(currency: string, a?: Arena): Amount {
let am = new Amount(null, a);
let r = emsc.amount_get_zero(currency, am.getNative());
if (r != GNUNET_OK) {
@@ -590,7 +590,6 @@ class HashCode extends PackedArenaObject {
case "nonce":
qual = RandomQuality.NONCE;
break;
- break;
default:
throw Error(format("unknown crypto quality: {0}", qual));
}
diff --git a/extension/background/query.ts b/extension/background/query.ts
new file mode 100644
index 000000000..bfe3102f3
--- /dev/null
+++ b/extension/background/query.ts
@@ -0,0 +1,157 @@
+/*
+ This file is part of TALER
+ (C) 2015 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/>
+ */
+
+/// <reference path="../decl/chrome/chrome.d.ts" />
+
+"use strict";
+
+
+function Query(db) {
+ return new QueryRoot(db);
+}
+
+class QueryStream {
+ qr: QueryRoot;
+ storeName;
+ constructor(qr, storeName) {
+ this.qr = qr;
+ this.storeName = storeName;
+ }
+ join(indexName: string, key: any) {
+ // join on the source relation's key, which may be
+ // a path or a transformer function
+ throw Error("Not implemented");
+ }
+ reduce(f, acc): Promise<any> {
+ let leakedResolve;
+ let p = new Promise((resolve, reject) => {
+ leakedResolve = resolve;
+ });
+ let qr = this.qr;
+ let storeName = this.storeName;
+
+ function doReduce() {
+ let req = qr.tx.objectStore(storeName).openCursor();
+ req.onsuccess = (e) => {
+ let cursor: IDBCursorWithValue = req.result;
+ if (cursor) {
+ acc = f(acc, cursor.value);
+ cursor.continue();
+ } else {
+ leakedResolve(acc);
+ }
+ }
+ }
+
+ this.qr.work.push(doReduce);
+ // We need this one level of indirection so that the kickoff
+ // is run asynchronously.
+ return Promise.resolve().then(() => this.qr.finish().then(() => p));
+ }
+}
+
+
+class QueryRoot {
+ work = [];
+ db: IDBDatabase;
+ tx: IDBTransaction;
+ stores = new Set();
+ kickoffPromise;
+
+ constructor(db) {
+ this.db = db;
+ }
+
+ iter(storeName): QueryStream {
+ this.stores.add(storeName);
+ return new QueryStream(this, storeName);
+ }
+
+ put(storeName, val): QueryRoot {
+ this.stores.add(storeName);
+ function doPut() {
+ this.tx.objectStore(storeName).put(val);
+ }
+ this.work.push(doPut);
+ return this;
+ }
+
+ putAll(storeName, iterable): QueryRoot {
+ this.stores.add(storeName);
+ function doPutAll() {
+ for (let obj of iterable) {
+ this.tx.objectStore(storeName).put(obj);
+ }
+ }
+ this.work.push(doPutAll);
+ return this;
+ }
+
+ add(storeName, val): QueryRoot {
+ this.stores.add(storeName);
+ function doAdd() {
+ this.tx.objectStore(storeName).add(val);
+ }
+ this.work.push(doAdd);
+ return this;
+ }
+
+ get(storeName, key): Promise<any> {
+ this.stores.add(storeName);
+ let leakedResolve;
+ let p = new Promise((resolve, reject) => {
+ leakedResolve = resolve;
+ });
+ if (!leakedResolve) {
+ // According to ES6 spec (paragraph 25.4.3.1), this can't happen.
+ throw Error("assertion failed");
+ }
+ function doGet() {
+ let req = this.tx.objectStore(storeName).get(key);
+ req.onsuccess = (r) => {
+ leakedResolve(r);
+ };
+ }
+ this.work.push(doGet);
+ return p;
+ }
+
+ finish(): Promise<void> {
+ if (this.kickoffPromise) {
+ return this.kickoffPromise;
+ }
+ this.kickoffPromise = new Promise((resolve, reject) => {
+
+ this.tx = this.db.transaction(Array.from(this.stores), "readwrite");
+ this.tx.oncomplete = () => {
+ resolve();
+ };
+ for (let w of this.work) {
+ w();
+ }
+ });
+ return this.kickoffPromise;
+ }
+
+ delete(storeName: string, key): QueryRoot {
+ this.stores.add(storeName);
+ function doDelete() {
+ this.tx.objectStore(storeName).delete(key);
+ }
+ this.work.push(doDelete);
+ return this;
+ }
+} \ No newline at end of file
diff --git a/extension/background/wallet.js b/extension/background/wallet.js
index fc84fdfb0..268ca32ed 100644
--- a/extension/background/wallet.js
+++ b/extension/background/wallet.js
@@ -13,8 +13,6 @@
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/>
*/
-/// <reference path="../decl/urijs/URIjs.d.ts" />
-/// <reference path="../decl/chrome/chrome.d.ts" />
'use strict';
/**
* See http://api.taler.net/wallet.html#general
@@ -36,9 +34,7 @@ function signDeposit(db, offer, cds) {
cds = copy(cds);
for (let cd of cds) {
let coinSpend;
- console.log("amount remaining:", amountRemaining.toJson());
if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
- console.log("full amount spent");
break;
}
if (amountRemaining.cmp(new Amount(cd.coin.currentAmount)) < 0) {
@@ -64,9 +60,6 @@ function signDeposit(db, offer, cds) {
transaction_id: UInt64.fromNumber(offer.contract.transaction_id),
};
let d = new DepositRequestPS(args);
- console.log("Deposit request #" + ret.length);
- console.log("DepositRequestPS: \n", d.toJson());
- console.log("DepositRequestPS sig: \n", d.toPurpose().hexdump());
let coinSig = eddsaSign(d.toPurpose(), EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
.toCrock();
let s = {
@@ -163,7 +156,6 @@ function getPossibleMintCoins(db, paymentAmount, depositFeeLimit, allowedMints)
continue nextMint;
}
}
- console.log(format("mint {0}: acc {1} is not enough for {2}", key, JSON.stringify(accAmount.toJson()), JSON.stringify(minAmount.toJson())));
}
resolve(ret);
};
@@ -173,32 +165,25 @@ function getPossibleMintCoins(db, paymentAmount, depositFeeLimit, allowedMints)
});
}
function executePay(db, offer, payCoinInfo, merchantBaseUrl, chosenMint) {
- return new Promise((resolve, reject) => {
- let payReq = {};
- payReq["H_wire"] = offer.contract.H_wire;
- payReq["H_contract"] = offer.H_contract;
- payReq["transaction_id"] = offer.contract.transaction_id;
- payReq["refund_deadline"] = offer.contract.refund_deadline;
- payReq["mint"] = URI(chosenMint).href();
- payReq["coins"] = payCoinInfo.map((x) => x.sig);
- payReq["timestamp"] = offer.contract.timestamp;
- let payUrl = URI(offer.pay_url).absoluteTo(merchantBaseUrl);
- let t = {
- contractHash: offer.H_contract,
- contract: offer.contract,
- payUrl: payUrl.href(),
- payReq: payReq
- };
- let tx = db.transaction(["transactions", "coins"], "readwrite");
- tx.objectStore('transactions').put(t);
- for (let c of payCoinInfo) {
- tx.objectStore("coins").put(c.updatedCoin);
- }
- tx.oncomplete = (e) => {
- updateBadge(db);
- resolve();
- };
- });
+ let payReq = {};
+ payReq["H_wire"] = offer.contract.H_wire;
+ payReq["H_contract"] = offer.H_contract;
+ payReq["transaction_id"] = offer.contract.transaction_id;
+ payReq["refund_deadline"] = offer.contract.refund_deadline;
+ payReq["mint"] = URI(chosenMint).href();
+ payReq["coins"] = payCoinInfo.map((x) => x.sig);
+ payReq["timestamp"] = offer.contract.timestamp;
+ let payUrl = URI(offer.pay_url).absoluteTo(merchantBaseUrl);
+ let t = {
+ contractHash: offer.H_contract,
+ contract: offer.contract,
+ payUrl: payUrl.href(),
+ payReq: payReq
+ };
+ return Query(db)
+ .put("transactions", t)
+ .putAll("coins", payCoinInfo.map((pci) => pci.updatedCoin))
+ .finish();
}
function confirmPay(db, detail, sendResponse) {
let offer = detail.offer;
@@ -206,37 +191,36 @@ function confirmPay(db, detail, sendResponse) {
.then((mcs) => {
if (Object.keys(mcs).length == 0) {
sendResponse({ error: "Not enough coins." });
+ // FIXME: does not work like expected here ...
return;
}
let mintUrl = Object.keys(mcs)[0];
let ds = signDeposit(db, offer, mcs[mintUrl]);
- return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl);
- })
- .then(() => {
- sendResponse({
- success: true,
+ return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl)
+ .then(() => {
+ sendResponse({
+ success: true,
+ });
});
});
return true;
}
function doPayment(db, detail, sendResponse) {
let H_contract = detail.H_contract;
- let req = db.transaction(['transactions'])
- .objectStore("transactions")
- .get(H_contract);
- console.log("executing contract", H_contract);
- req.onsuccess = (e) => {
- console.log("got db response for existing contract");
- if (!req.result) {
+ Query(db)
+ .get("transactions", H_contract)
+ .then((r) => {
+ if (!r) {
sendResponse({ success: false, error: "contract not found" });
return;
}
sendResponse({
success: true,
- payUrl: req.result.payUrl,
- payReq: req.result.payReq
+ payUrl: r.payUrl,
+ payReq: r.payReq
});
- };
+ });
+ // async sendResponse
return true;
}
function confirmReserve(db, detail, sendResponse) {
@@ -249,7 +233,6 @@ function confirmReserve(db, detail, sendResponse) {
form.append(detail.field_mint, detail.mint);
// XXX: set bank-specified fields.
let myRequest = new XMLHttpRequest();
- console.log("making request to " + detail.post_url);
myRequest.open('post', detail.post_url);
myRequest.send(form);
let mintBaseUrl = canonicalizeBaseUrl(detail.mint);
@@ -336,10 +319,7 @@ function withdrawPrepare(db, denom, reserve) {
h_denomination_pub: denomPub.encode().hash(),
h_coin_envelope: ev.hash()
});
- console.log("about to sign");
var sig = eddsaSign(withdrawRequest.toPurpose(), reservePriv);
- console.log("signed");
- console.log("crypto done, doing request");
let preCoin = {
reservePub: reservePub.toCrock(),
blindingKey: blindingFactor.toCrock(),
@@ -351,93 +331,63 @@ function withdrawPrepare(db, denom, reserve) {
coinEv: ev.toCrock(),
coinValue: denom.value
};
- console.log("storing precoin", JSON.stringify(preCoin));
- let tx = db.transaction(['precoins'], 'readwrite');
- tx.objectStore('precoins').add(preCoin);
- return new Promise((resolve, reject) => {
- tx.oncomplete = (e) => {
- resolve(preCoin);
- };
- });
-}
-function dbGet(db, store, key) {
- let tx = db.transaction([store]);
- let req = tx.objectStore(store).get(key);
- return new Promise((resolve, reject) => {
- req.onsuccess = (e) => resolve(req.result);
- });
+ return Query(db).put("precoins", preCoin).finish().then(() => preCoin);
}
function withdrawExecute(db, pc) {
- return dbGet(db, 'reserves', pc.reservePub)
- .then((r) => new Promise((resolve, reject) => {
- console.log("loading precoin", JSON.stringify(pc));
+ return Query(db)
+ .get("reserves", pc.reservePub)
+ .then((r) => {
let wd = {};
wd.denom_pub = pc.denomPub;
wd.reserve_pub = pc.reservePub;
wd.reserve_sig = pc.withdrawSig;
wd.coin_ev = pc.coinEv;
let reqUrl = URI("reserve/withdraw").absoluteTo(r.mint_base_url);
- let myRequest = new XMLHttpRequest();
- console.log("making request to " + reqUrl.href());
- myRequest.open('post', reqUrl.href());
- myRequest.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
- myRequest.send(JSON.stringify(wd));
- myRequest.addEventListener('readystatechange', (e) => {
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- if (myRequest.status != 200) {
- console.log("Withdrawal failed, status ", myRequest.status);
- reject();
- return;
- }
- console.log("Withdrawal successful");
- console.log(myRequest.responseText);
- let resp = JSON.parse(myRequest.responseText);
- let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.ev_sig), RsaBlindingKey.fromCrock(pc.blindingKey), RsaPublicKey.fromCrock(pc.denomPub));
- let coin = {
- coinPub: pc.coinPub,
- coinPriv: pc.coinPriv,
- denomPub: pc.denomPub,
- denomSig: denomSig.encode().toCrock(),
- currentAmount: pc.coinValue,
- mintBaseUrl: pc.mintBaseUrl,
- };
- console.log("unblinded coin");
- resolve(coin);
- }
- else {
- console.log("ready state change to", myRequest.status);
- }
- });
- }));
+ return httpPost(reqUrl, wd);
+ })
+ .then(resp => {
+ if (resp.status != 200) {
+ throw new RequestException({
+ hint: "Withdrawal failed",
+ status: resp.status
+ });
+ }
+ let r = JSON.parse(resp.responseText);
+ let denomSig = rsaUnblind(RsaSignature.fromCrock(r.ev_sig), RsaBlindingKey.fromCrock(pc.blindingKey), RsaPublicKey.fromCrock(pc.denomPub));
+ let coin = {
+ coinPub: pc.coinPub,
+ coinPriv: pc.coinPriv,
+ denomPub: pc.denomPub,
+ denomSig: denomSig.encode().toCrock(),
+ currentAmount: pc.coinValue,
+ mintBaseUrl: pc.mintBaseUrl,
+ };
+ return coin;
+ });
}
function updateBadge(db) {
- let tx = db.transaction(['coins'], 'readwrite');
- let req = tx.objectStore('coins').openCursor();
- let n = 0;
- req.onsuccess = (e) => {
- let cursor = req.result;
- if (cursor) {
- let c = cursor.value;
- if (c.currentAmount.fraction != 0 || c.currentAmount.value != 0) {
- n++;
- }
- cursor.continue();
+ function countNonEmpty(n, c) {
+ if (c.currentAmount.fraction != 0 || c.currentAmount.value != 0) {
+ return n + 1;
}
- else {
- chrome.browserAction.setBadgeText({ text: "" + n });
- chrome.browserAction.setBadgeBackgroundColor({ color: "#0F0" });
- }
- };
+ return n;
+ }
+ function doBadge(n) {
+ chrome.browserAction.setBadgeText({ text: "" + n });
+ chrome.browserAction.setBadgeBackgroundColor({ color: "#0F0" });
+ }
+ Query(db)
+ .iter("coins")
+ .reduce(countNonEmpty, 0)
+ .then(doBadge);
}
function storeCoin(db, coin) {
- let tx = db.transaction(['coins', 'precoins'], 'readwrite');
- tx.objectStore('precoins').delete(coin.coinPub);
- tx.objectStore('coins').add(coin);
- return new Promise((resolve, reject) => {
- tx.oncomplete = (e) => {
- resolve();
- updateBadge(db);
- };
+ Query(db)
+ .delete("precoins", coin.coinPub)
+ .add("coins", coin)
+ .finish()
+ .then(() => {
+ updateBadge(db);
});
}
function withdraw(db, denom, reserve) {
@@ -482,86 +432,81 @@ function depleteReserve(db, reserve, mint) {
next();
}
function updateReserve(db, reservePub, mint) {
- let reserve;
+ let reservePubStr = reservePub.toCrock();
+ return Query(db)
+ .get("reserves", reservePubStr)
+ .then((reserve) => {
+ let reqUrl = URI("reserve/status").absoluteTo(mint.baseUrl);
+ reqUrl.query({ 'reserve_pub': reservePubStr });
+ return httpGet(reqUrl).then(resp => {
+ if (resp.status != 200) {
+ throw Error();
+ }
+ let reserveInfo = JSON.parse(resp.responseText);
+ if (!reserveInfo) {
+ throw Error();
+ }
+ reserve.current_amount = reserveInfo.balance;
+ let q = Query(db);
+ return q.put("reserves", reserve).finish().then(() => reserve);
+ });
+ });
+}
+function httpReq(method, url, options) {
+ let urlString;
+ if (url instanceof URI) {
+ urlString = url.href();
+ }
+ else if (typeof url === "string") {
+ urlString = url;
+ }
return new Promise((resolve, reject) => {
- let tx = db.transaction(['reserves']);
- tx.objectStore('reserves').get(reservePub.toCrock()).onsuccess = (e) => {
- let reserve = e.target.result;
- let reqUrl = URI("reserve/status").absoluteTo(mint.baseUrl);
- reqUrl.query({ 'reserve_pub': reservePub.toCrock() });
- let myRequest = new XMLHttpRequest();
- console.log("making request to " + reqUrl.href());
- myRequest.open('get', reqUrl.href());
- myRequest.send();
- myRequest.addEventListener('readystatechange', (e) => {
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- if (myRequest.status != 200) {
- reject();
- return;
- }
- let reserveInfo = JSON.parse(myRequest.responseText);
- console.log("got response " + JSON.stringify(reserveInfo));
- reserve.current_amount = reserveInfo.balance;
- let tx = db.transaction(['reserves'], 'readwrite');
- console.log("putting updated reserve " + JSON.stringify(reserve));
- tx.objectStore('reserves').put(reserve);
- tx.oncomplete = (e) => {
- resolve(reserve);
- };
- }
- });
- };
+ let myRequest = new XMLHttpRequest();
+ myRequest.open(method, urlString);
+ if (options && options.req) {
+ myRequest.send(options.req);
+ }
+ myRequest.addEventListener("readystatechange", (e) => {
+ if (myRequest.readyState == XMLHttpRequest.DONE) {
+ let resp = {
+ status: myRequest.status,
+ responseText: myRequest.responseText
+ };
+ resolve(resp);
+ }
+ });
});
}
+function httpGet(url) {
+ return httpReq("get", url);
+}
+function httpPost(url, body) {
+ return httpReq("put", url, { req: JSON.stringify(body) });
+}
+class RequestException {
+ constructor(detail) {
+ }
+}
/**
* Update or add mint DB entry by fetching the /keys information.
* Optionally link the reserve entry to the new or existing
* mint entry in then DB.
*/
function updateMintFromUrl(db, baseUrl) {
- console.log("base url is " + baseUrl);
let reqUrl = URI("keys").absoluteTo(baseUrl);
- let myRequest = new XMLHttpRequest();
- myRequest.open('get', reqUrl.href());
- myRequest.send();
- return new Promise((resolve, reject) => {
- myRequest.addEventListener('readystatechange', (e) => {
- console.log("state change to " + myRequest.readyState);
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- if (myRequest.status == 200) {
- console.log("got /keys");
- let mintKeysJson = JSON.parse(myRequest.responseText);
- if (!mintKeysJson) {
- console.log("keys invalid");
- reject();
- }
- else {
- let mint = {
- baseUrl: baseUrl,
- keys: mintKeysJson
- };
- let tx = db.transaction(['mints', 'denoms'], 'readwrite');
- tx.objectStore('mints').put(mint);
- for (let d of mintKeysJson.denoms) {
- // TODO: verify and complete
- let di = {
- denomPub: d.denom_pub,
- value: d.value
- };
- tx.objectStore('denoms').put(di);
- }
- tx.oncomplete = (e) => {
- resolve(mint);
- };
- }
- }
- else {
- console.log("/keys request failed with status " + myRequest.status);
- // XXX: also write last error to DB to show in the UI
- reject();
- }
- }
- });
+ return httpGet(reqUrl).then((resp) => {
+ if (resp.status != 200) {
+ throw Error("/keys request failed");
+ }
+ let mintKeysJson = JSON.parse(resp.responseText);
+ if (!mintKeysJson) {
+ throw new RequestException({ url: reqUrl, hint: "keys invalid" });
+ }
+ let mint = {
+ baseUrl: baseUrl,
+ keys: mintKeysJson
+ };
+ return Query(db).put("mints", mint).finish().then(() => mint);
});
}
function dumpDb(db, detail, sendResponse) {
@@ -570,7 +515,6 @@ function dumpDb(db, detail, sendResponse) {
version: db.version,
stores: {}
};
- console.log("stores: " + JSON.stringify(db.objectStoreNames));
let tx = db.transaction(db.objectStoreNames);
tx.addEventListener('complete', (e) => {
sendResponse(dump);
@@ -600,52 +544,44 @@ function reset(db, detail, sendResponse) {
indexedDB.deleteDatabase(DB_NAME);
chrome.browserAction.setBadgeText({ text: "" });
console.log("reset done");
+ // Response is synchronous
return false;
}
function balances(db, detail, sendResponse) {
- let byCurrency = {};
- let tx = db.transaction(['coins', 'denoms']);
- let req = tx.objectStore('coins').openCursor();
- req.onsuccess = (e) => {
- let cursor = req.result;
- if (cursor) {
- let c = cursor.value;
- tx.objectStore('denoms').get(c.denomPub).onsuccess = (e2) => {
- let d = e2.target.result;
- let acc = byCurrency[d.value.currency];
- if (!acc) {
- acc = Amount.getZero(c.currentAmount.currency);
- }
- let am = new Amount(c.currentAmount);
- am.add(new Amount(acc));
- byCurrency[d.value.currency] = am.toJson();
- console.log("counting", byCurrency[d.value.currency]);
- };
- cursor.continue();
+ function collectBalances(c, byCurrency) {
+ let acc = byCurrency[c.currentAmount.currency];
+ if (!acc) {
+ acc = Amount.getZero(c.currentAmount.currency).toJson();
}
- else {
- sendResponse(byCurrency);
- }
- };
+ let am = new Amount(c.currentAmount);
+ am.add(new Amount(acc));
+ byCurrency[c.currentAmount.currency] = am.toJson();
+ }
+ Query(db)
+ .iter("coins")
+ .reduce(collectBalances, {})
+ .then(sendResponse);
return true;
}
-chrome.browserAction.setBadgeText({ text: "" });
-openTalerDb().then((db) => {
- console.log("db loaded");
- updateBadge(db);
- chrome.runtime.onMessage.addListener(function (req, sender, onresponse) {
- let dispatch = {
- "confirm-reserve": confirmReserve,
- "confirm-pay": confirmPay,
- "dump-db": dumpDb,
- "balances": balances,
- "execute-payment": doPayment,
- "reset": reset
- };
- if (req.type in dispatch) {
- return dispatch[req.type](db, req.detail, onresponse);
- }
- console.error(format("Request type {1} unknown, req {0}", JSON.stringify(req), req.type));
- return false;
+function wxMain() {
+ chrome.browserAction.setBadgeText({ text: "" });
+ openTalerDb().then((db) => {
+ updateBadge(db);
+ chrome.runtime.onMessage.addListener(function (req, sender, onresponse) {
+ let dispatch = {
+ "confirm-reserve": confirmReserve,
+ "confirm-pay": confirmPay,
+ "dump-db": dumpDb,
+ "balances": balances,
+ "execute-payment": doPayment,
+ "reset": reset
+ };
+ if (req.type in dispatch) {
+ return dispatch[req.type](db, req.detail, onresponse);
+ }
+ console.error(format("Request type {1} unknown, req {0}", JSON.stringify(req), req.type));
+ return false;
+ });
});
-});
+}
+wxMain();
diff --git a/extension/background/wallet.ts b/extension/background/wallet.ts
index 1abd09fd8..c6152253e 100644
--- a/extension/background/wallet.ts
+++ b/extension/background/wallet.ts
@@ -16,6 +16,8 @@
/// <reference path="../decl/urijs/URIjs.d.ts" />
/// <reference path="../decl/chrome/chrome.d.ts" />
+import URIStatic = uri.URIStatic;
+import Request = chrome.devtools.network.Request;
'use strict';
@@ -25,22 +27,6 @@ interface AmountJson {
currency: string;
}
-
-/**
- * See http://api.taler.net/wallet.html#general
- */
-function canonicalizeBaseUrl(url) {
- let x = new URI(url);
- if (!x.protocol()) {
- x.protocol("https");
- }
- x.path(x.path() + "/").normalizePath();
- x.fragment();
- x.query();
- return x.href()
-}
-
-
interface ConfirmPayRequest {
merchantPageUrl: string;
offer: Offer;
@@ -50,6 +36,12 @@ interface MintCoins {
[mintUrl: string]: Db.CoinWithDenom[];
}
+
+interface MintInfo {
+ master_pub: string;
+ url: string;
+}
+
interface Offer {
contract: Contract;
sig: string;
@@ -74,7 +66,6 @@ interface Contract {
transaction_id: number;
}
-
interface CoinPaySig {
coin_sig: string;
coin_pub: string;
@@ -84,9 +75,39 @@ interface CoinPaySig {
}
+interface Transaction {
+ contractHash: string;
+ contract: any;
+ payUrl: string;
+ payReq: any;
+}
+
+
+interface Reserve {
+ mint_base_url: string
+ reserve_priv: string;
+ reserve_pub: string;
+}
+
+
type PayCoinInfo = Array<{ updatedCoin: Db.Coin, sig: CoinPaySig }>;
+/**
+ * See http://api.taler.net/wallet.html#general
+ */
+function canonicalizeBaseUrl(url) {
+ let x = new URI(url);
+ if (!x.protocol()) {
+ x.protocol("https");
+ }
+ x.path(x.path() + "/").normalizePath();
+ x.fragment();
+ x.query();
+ return x.href()
+}
+
+
function signDeposit(db: IDBDatabase,
offer: Offer,
cds: Db.CoinWithDenom[]): PayCoinInfo {
@@ -97,10 +118,7 @@ function signDeposit(db: IDBDatabase,
for (let cd of cds) {
let coinSpend;
- console.log("amount remaining:", amountRemaining.toJson());
-
if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
- console.log("full amount spent");
break;
}
@@ -131,10 +149,6 @@ function signDeposit(db: IDBDatabase,
let d = new DepositRequestPS(args);
- console.log("Deposit request #" + ret.length);
- console.log("DepositRequestPS: \n", d.toJson());
- console.log("DepositRequestPS sig: \n", d.toPurpose().hexdump());
-
let coinSig = eddsaSign(d.toPurpose(),
EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
.toCrock();
@@ -151,11 +165,6 @@ function signDeposit(db: IDBDatabase,
return ret;
}
-interface MintInfo {
- master_pub: string;
- url: string;
-}
-
/**
* Get mints and associated coins that are still spendable,
@@ -228,28 +237,24 @@ function getPossibleMintCoins(db: IDBDatabase,
let accAmount = Amount.getZero(coins[0].c.coin.currentAmount.currency);
let usableCoins: Db.CoinWithDenom[] = [];
nextCoin:
- for (let i = 0; i < coins.length; i++) {
- let coinAmount = new Amount(coins[i].c.coin.currentAmount);
- let coinFee = coins[i].a;
- if (coinAmount.cmp(coinFee) <= 0) {
- continue nextCoin;
+ for (let i = 0; i < coins.length; i++) {
+ let coinAmount = new Amount(coins[i].c.coin.currentAmount);
+ let coinFee = coins[i].a;
+ if (coinAmount.cmp(coinFee) <= 0) {
+ continue nextCoin;
+ }
+ accFee.add(coinFee);
+ accAmount.add(coinAmount);
+ if (accFee.cmp(maxFee) >= 0) {
+ console.log("too much fees");
+ continue nextMint;
+ }
+ usableCoins.push(coins[i].c);
+ if (accAmount.cmp(minAmount) >= 0) {
+ ret[key] = usableCoins;
+ continue nextMint;
+ }
}
- accFee.add(coinFee);
- accAmount.add(coinAmount);
- if (accFee.cmp(maxFee) >= 0) {
- console.log("too much fees");
- continue nextMint;
- }
- usableCoins.push(coins[i].c);
- if (accAmount.cmp(minAmount) >= 0) {
- ret[key] = usableCoins;
- continue nextMint;
- }
- }
- console.log(format("mint {0}: acc {1} is not enough for {2}",
- key,
- JSON.stringify(accAmount.toJson()),
- JSON.stringify(minAmount.toJson())));
}
resolve(ret);
};
@@ -261,45 +266,31 @@ function getPossibleMintCoins(db: IDBDatabase,
}
-interface Transaction {
- contractHash: string;
- contract: any;
- payUrl: string;
- payReq: any;
-}
-
-
function executePay(db,
offer: Offer,
payCoinInfo: PayCoinInfo,
merchantBaseUrl: string,
- chosenMint: string) {
- return new Promise((resolve, reject) => {
- let payReq = {};
- payReq["H_wire"] = offer.contract.H_wire;
- payReq["H_contract"] = offer.H_contract;
- payReq["transaction_id"] = offer.contract.transaction_id;
- payReq["refund_deadline"] = offer.contract.refund_deadline;
- payReq["mint"] = URI(chosenMint).href();
- payReq["coins"] = payCoinInfo.map((x) => x.sig);
- payReq["timestamp"] = offer.contract.timestamp;
- let payUrl = URI(offer.pay_url).absoluteTo(merchantBaseUrl);
- let t: Transaction = {
- contractHash: offer.H_contract,
- contract: offer.contract,
- payUrl: payUrl.href(),
- payReq: payReq
- };
- let tx = db.transaction(["transactions", "coins"], "readwrite");
- tx.objectStore('transactions').put(t);
- for (let c of payCoinInfo) {
- tx.objectStore("coins").put(c.updatedCoin);
- }
- tx.oncomplete = (e) => {
- updateBadge(db);
- resolve();
- };
- });
+ chosenMint: string): Promise<void> {
+ let payReq = {};
+ payReq["H_wire"] = offer.contract.H_wire;
+ payReq["H_contract"] = offer.H_contract;
+ payReq["transaction_id"] = offer.contract.transaction_id;
+ payReq["refund_deadline"] = offer.contract.refund_deadline;
+ payReq["mint"] = URI(chosenMint).href();
+ payReq["coins"] = payCoinInfo.map((x) => x.sig);
+ payReq["timestamp"] = offer.contract.timestamp;
+ let payUrl = URI(offer.pay_url).absoluteTo(merchantBaseUrl);
+ let t: Transaction = {
+ contractHash: offer.H_contract,
+ contract: offer.contract,
+ payUrl: payUrl.href(),
+ payReq: payReq
+ };
+
+ return Query(db)
+ .put("transactions", t)
+ .putAll("coins", payCoinInfo.map((pci) => pci.updatedCoin))
+ .finish();
}
@@ -312,16 +303,17 @@ function confirmPay(db, detail: ConfirmPayRequest, sendResponse) {
.then((mcs) => {
if (Object.keys(mcs).length == 0) {
sendResponse({error: "Not enough coins."});
+ // FIXME: does not work like expected here ...
return;
}
let mintUrl = Object.keys(mcs)[0];
let ds = signDeposit(db, offer, mcs[mintUrl]);
- return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl);
- })
- .then(() => {
- sendResponse({
- success: true,
- });
+ return executePay(db, offer, ds, detail.merchantPageUrl, mintUrl)
+ .then(() => {
+ sendResponse({
+ success: true,
+ });
+ });
});
return true;
}
@@ -329,22 +321,20 @@ function confirmPay(db, detail: ConfirmPayRequest, sendResponse) {
function doPayment(db, detail, sendResponse) {
let H_contract = detail.H_contract;
- let req = db.transaction(['transactions'])
- .objectStore("transactions")
- .get(H_contract);
- console.log("executing contract", H_contract);
- req.onsuccess = (e) => {
- console.log("got db response for existing contract");
- if (!req.result) {
- sendResponse({success: false, error: "contract not found"});
- return;
- }
- sendResponse({
- success: true,
- payUrl: req.result.payUrl,
- payReq: req.result.payReq
- });
- };
+ Query(db)
+ .get("transactions", H_contract)
+ .then((r) => {
+ if (!r) {
+ sendResponse({success: false, error: "contract not found"});
+ return;
+ }
+ sendResponse({
+ success: true,
+ payUrl: r.payUrl,
+ payReq: r.payReq
+ });
+ });
+ // async sendResponse
return true;
}
@@ -359,7 +349,6 @@ function confirmReserve(db, detail, sendResponse) {
form.append(detail.field_mint, detail.mint);
// XXX: set bank-specified fields.
let myRequest = new XMLHttpRequest();
- console.log("making request to " + detail.post_url);
myRequest.open('post', detail.post_url);
myRequest.send(form);
let mintBaseUrl = canonicalizeBaseUrl(detail.mint);
@@ -427,13 +416,6 @@ function rankDenom(denom1: any, denom2: any) {
}
-interface Reserve {
- mint_base_url: string
- reserve_priv: string;
- reserve_pub: string;
-}
-
-
function withdrawPrepare(db: IDBDatabase,
denom: Db.Denomination,
reserve: Reserve): Promise<Db.PreCoin> {
@@ -465,11 +447,7 @@ function withdrawPrepare(db: IDBDatabase,
h_coin_envelope: ev.hash()
});
- console.log("about to sign");
var sig = eddsaSign(withdrawRequest.toPurpose(), reservePriv);
- console.log("signed");
-
- console.log("crypto done, doing request");
let preCoin: Db.PreCoin = {
reservePub: reservePub.toCrock(),
@@ -483,104 +461,74 @@ function withdrawPrepare(db: IDBDatabase,
coinValue: denom.value
};
- console.log("storing precoin", JSON.stringify(preCoin));
-
- let tx = db.transaction(['precoins'], 'readwrite');
- tx.objectStore('precoins').add(preCoin);
- return new Promise((resolve, reject) => {
- tx.oncomplete = (e) => {
- resolve(preCoin);
- }
- });
-}
-
-
-function dbGet(db, store: string, key: any): Promise<any> {
- let tx = db.transaction([store]);
- let req = tx.objectStore(store).get(key);
- return new Promise((resolve, reject) => {
- req.onsuccess = (e) => resolve(req.result);
- });
+ return Query(db).put("precoins", preCoin).finish().then(() => preCoin);
}
function withdrawExecute(db, pc: Db.PreCoin): Promise<Db.Coin> {
- return dbGet(db, 'reserves', pc.reservePub)
- .then((r) => new Promise((resolve, reject) => {
- console.log("loading precoin", JSON.stringify(pc));
+ return Query(db)
+ .get("reserves", pc.reservePub)
+ .then((r) => {
let wd: any = {};
wd.denom_pub = pc.denomPub;
wd.reserve_pub = pc.reservePub;
wd.reserve_sig = pc.withdrawSig;
wd.coin_ev = pc.coinEv;
let reqUrl = URI("reserve/withdraw").absoluteTo(r.mint_base_url);
- let myRequest = new XMLHttpRequest();
- console.log("making request to " + reqUrl.href());
- myRequest.open('post', reqUrl.href());
- myRequest.setRequestHeader("Content-Type",
- "application/json;charset=UTF-8");
- myRequest.send(JSON.stringify(wd));
- myRequest.addEventListener('readystatechange', (e) => {
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- if (myRequest.status != 200) {
- console.log("Withdrawal failed, status ", myRequest.status);
- reject();
- return;
- }
- console.log("Withdrawal successful");
- console.log(myRequest.responseText);
- let resp = JSON.parse(myRequest.responseText);
- let denomSig = rsaUnblind(RsaSignature.fromCrock(resp.ev_sig),
- RsaBlindingKey.fromCrock(pc.blindingKey),
- RsaPublicKey.fromCrock(pc.denomPub));
- let coin: Db.Coin = {
- coinPub: pc.coinPub,
- coinPriv: pc.coinPriv,
- denomPub: pc.denomPub,
- denomSig: denomSig.encode().toCrock(),
- currentAmount: pc.coinValue,
- mintBaseUrl: pc.mintBaseUrl,
- };
- console.log("unblinded coin");
- resolve(coin);
- } else {
- console.log("ready state change to", myRequest.status);
- }
- });
- }));
+ return httpPost(reqUrl, wd);
+ })
+ .then(resp => {
+ if (resp.status != 200) {
+ throw new RequestException({
+ hint: "Withdrawal failed",
+ status: resp.status
+ });
+ }
+ let r = JSON.parse(resp.responseText);
+ let denomSig = rsaUnblind(RsaSignature.fromCrock(r.ev_sig),
+ RsaBlindingKey.fromCrock(pc.blindingKey),
+ RsaPublicKey.fromCrock(pc.denomPub));
+ let coin: Db.Coin = {
+ coinPub: pc.coinPub,
+ coinPriv: pc.coinPriv,
+ denomPub: pc.denomPub,
+ denomSig: denomSig.encode().toCrock(),
+ currentAmount: pc.coinValue,
+ mintBaseUrl: pc.mintBaseUrl,
+ };
+ return coin;
+ });
}
function updateBadge(db) {
- let tx = db.transaction(['coins'], 'readwrite');
- let req = tx.objectStore('coins').openCursor();
- let n = 0;
- req.onsuccess = (e) => {
- let cursor = req.result;
- if (cursor) {
- let c: Db.Coin = cursor.value;
- if (c.currentAmount.fraction != 0 || c.currentAmount.value != 0) {
- n++;
- }
- cursor.continue();
- } else {
- chrome.browserAction.setBadgeText({text: "" + n});
- chrome.browserAction.setBadgeBackgroundColor({color: "#0F0"});
+ function countNonEmpty(n, c) {
+ if (c.currentAmount.fraction != 0 || c.currentAmount.value != 0) {
+ return n + 1;
}
+ return n;
}
+
+ function doBadge(n) {
+ chrome.browserAction.setBadgeText({text: "" + n});
+ chrome.browserAction.setBadgeBackgroundColor({color: "#0F0"});
+ }
+
+ Query(db)
+ .iter("coins")
+ .reduce(countNonEmpty, 0)
+ .then(doBadge);
}
function storeCoin(db, coin: Db.Coin) {
- let tx = db.transaction(['coins', 'precoins'], 'readwrite');
- tx.objectStore('precoins').delete(coin.coinPub);
- tx.objectStore('coins').add(coin);
- return new Promise<void>((resolve, reject) => {
- tx.oncomplete = (e) => {
- resolve();
+ Query(db)
+ .delete("precoins", coin.coinPub)
+ .add("coins", coin)
+ .finish()
+ .then(() => {
updateBadge(db);
- }
- });
+ });
}
@@ -631,88 +579,102 @@ function depleteReserve(db, reserve, mint) {
}
-function updateReserve(db, reservePub: EddsaPublicKey, mint) {
- let reserve;
- return new Promise((resolve, reject) => {
- let tx = db.transaction(['reserves']);
- tx.objectStore('reserves').get(reservePub.toCrock()).onsuccess = (e) => {
- let reserve = e.target.result;
+function updateReserve(db: IDBDatabase,
+ reservePub: EddsaPublicKey,
+ mint): Promise<Reserve> {
+ let reservePubStr = reservePub.toCrock();
+ return Query(db)
+ .get("reserves", reservePubStr)
+ .then((reserve) => {
let reqUrl = URI("reserve/status").absoluteTo(mint.baseUrl);
- reqUrl.query({'reserve_pub': reservePub.toCrock()});
- let myRequest = new XMLHttpRequest();
- console.log("making request to " + reqUrl.href());
- myRequest.open('get', reqUrl.href());
- myRequest.send();
- myRequest.addEventListener('readystatechange', (e) => {
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- if (myRequest.status != 200) {
- reject();
- return;
- }
- let reserveInfo = JSON.parse(myRequest.responseText);
- console.log("got response " + JSON.stringify(reserveInfo));
- reserve.current_amount = reserveInfo.balance;
- let tx = db.transaction(['reserves'], 'readwrite');
- console.log("putting updated reserve " + JSON.stringify(reserve));
- tx.objectStore('reserves').put(reserve);
- tx.oncomplete = (e) => {
- resolve(reserve);
- };
+ reqUrl.query({'reserve_pub': reservePubStr});
+ return httpGet(reqUrl).then(resp => {
+ if (resp.status != 200) {
+ throw Error();
+ }
+ let reserveInfo = JSON.parse(resp.responseText);
+ if (!reserveInfo) {
+ throw Error();
}
+ reserve.current_amount = reserveInfo.balance;
+ let q = Query(db);
+ return q.put("reserves", reserve).finish().then(() => reserve);
});
- };
+ });
+}
+
+
+interface HttpResponse {
+ status: number;
+ responseText: string;
+}
+
+
+function httpReq(method: string,
+ url: string|uri.URI,
+ options?: any): Promise<HttpResponse> {
+ let urlString: string;
+ if (url instanceof URI) {
+ urlString = url.href();
+ } else if (typeof url === "string") {
+ urlString = url;
+ }
+
+ return new Promise((resolve, reject) => {
+ let myRequest = new XMLHttpRequest();
+ myRequest.open(method, urlString);
+ if (options && options.req) {
+ myRequest.send(options.req);
+ }
+ myRequest.addEventListener("readystatechange", (e) => {
+ if (myRequest.readyState == XMLHttpRequest.DONE) {
+ let resp = {
+ status: myRequest.status,
+ responseText: myRequest.responseText
+ };
+ resolve(resp);
+ }
+ });
});
+}
+
+
+function httpGet(url: string|uri.URI) {
+ return httpReq("get", url);
+}
+
+function httpPost(url: string|uri.URI, body) {
+ return httpReq("put", url, {req: JSON.stringify(body)});
}
+class RequestException {
+ constructor(detail) {
+
+ }
+}
+
/**
* Update or add mint DB entry by fetching the /keys information.
* Optionally link the reserve entry to the new or existing
* mint entry in then DB.
*/
function updateMintFromUrl(db, baseUrl) {
- console.log("base url is " + baseUrl);
let reqUrl = URI("keys").absoluteTo(baseUrl);
- let myRequest = new XMLHttpRequest();
- myRequest.open('get', reqUrl.href());
- myRequest.send();
- return new Promise((resolve, reject) => {
- myRequest.addEventListener('readystatechange', (e) => {
- console.log("state change to " + myRequest.readyState);
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- if (myRequest.status == 200) {
- console.log("got /keys");
- let mintKeysJson = JSON.parse(myRequest.responseText);
- if (!mintKeysJson) {
- console.log("keys invalid");
- reject();
- } else {
- let mint: Db.Mint = {
- baseUrl: baseUrl,
- keys: mintKeysJson
- };
- let tx = db.transaction(['mints', 'denoms'], 'readwrite');
- tx.objectStore('mints').put(mint);
- for (let d of mintKeysJson.denoms) {
- // TODO: verify and complete
- let di = {
- denomPub: d.denom_pub,
- value: d.value
- };
- tx.objectStore('denoms').put(di);
- }
- tx.oncomplete = (e) => {
- resolve(mint);
- };
- }
- } else {
- console.log("/keys request failed with status " + myRequest.status);
- // XXX: also write last error to DB to show in the UI
- reject();
- }
- }
- });
+ return httpGet(reqUrl).then((resp) => {
+ if (resp.status != 200) {
+ throw Error("/keys request failed");
+ }
+ let mintKeysJson = JSON.parse(resp.responseText);
+ if (!mintKeysJson) {
+ throw new RequestException({url: reqUrl, hint: "keys invalid"});
+ }
+ let mint: Db.Mint = {
+ baseUrl: baseUrl,
+ keys: mintKeysJson
+ };
+ return Query(db).put("mints", mint).finish().then(() => mint);
});
}
@@ -723,7 +685,6 @@ function dumpDb(db, detail, sendResponse) {
version: db.version,
stores: {}
};
- console.log("stores: " + JSON.stringify(db.objectStoreNames));
let tx = db.transaction(db.objectStoreNames);
tx.addEventListener('complete', (e) => {
sendResponse(dump);
@@ -755,58 +716,55 @@ function reset(db, detail, sendResponse) {
indexedDB.deleteDatabase(DB_NAME);
chrome.browserAction.setBadgeText({text: ""});
console.log("reset done");
+ // Response is synchronous
return false;
}
-function balances(db, detail, sendResponse) {
- let byCurrency = {};
- let tx = db.transaction(['coins', 'denoms']);
- let req = tx.objectStore('coins').openCursor();
- req.onsuccess = (e) => {
- let cursor = req.result;
- if (cursor) {
- let c: Db.Coin = cursor.value;
- tx.objectStore('denoms').get(c.denomPub).onsuccess = (e2) => {
- let d = e2.target.result;
- let acc = byCurrency[d.value.currency];
- if (!acc) {
- acc = Amount.getZero(c.currentAmount.currency);
- }
- let am = new Amount(c.currentAmount);
- am.add(new Amount(acc));
- byCurrency[d.value.currency] = am.toJson();
- console.log("counting", byCurrency[d.value.currency]);
- };
- cursor.continue();
- } else {
- sendResponse(byCurrency);
+function balances(db, detail, sendResponse): boolean {
+ function collectBalances(c: Db.Coin, byCurrency) {
+ let acc: AmountJson = byCurrency[c.currentAmount.currency];
+ if (!acc) {
+ acc = Amount.getZero(c.currentAmount.currency).toJson();
}
- };
+ let am = new Amount(c.currentAmount);
+ am.add(new Amount(acc));
+ byCurrency[c.currentAmount.currency] = am.toJson();
+ }
+
+ Query(db)
+ .iter("coins")
+ .reduce(collectBalances, {})
+ .then(sendResponse);
return true;
}
-chrome.browserAction.setBadgeText({text: ""});
-
-openTalerDb().then((db) => {
- console.log("db loaded");
- updateBadge(db);
- chrome.runtime.onMessage.addListener(
- function(req, sender, onresponse) {
- let dispatch = {
- "confirm-reserve": confirmReserve,
- "confirm-pay": confirmPay,
- "dump-db": dumpDb,
- "balances": balances,
- "execute-payment": doPayment,
- "reset": reset
- };
- if (req.type in dispatch) {
- return dispatch[req.type](db, req.detail, onresponse);
- }
- console.error(format("Request type {1} unknown, req {0}",
- JSON.stringify(req),
- req.type));
- return false;
- });
-});
+
+function wxMain() {
+ chrome.browserAction.setBadgeText({text: ""});
+
+ openTalerDb().then((db) => {
+ updateBadge(db);
+ chrome.runtime.onMessage.addListener(
+ function(req, sender, onresponse) {
+ let dispatch = {
+ "confirm-reserve": confirmReserve,
+ "confirm-pay": confirmPay,
+ "dump-db": dumpDb,
+ "balances": balances,
+ "execute-payment": doPayment,
+ "reset": reset
+ };
+ if (req.type in dispatch) {
+ return dispatch[req.type](db, req.detail, onresponse);
+ }
+ console.error(format("Request type {1} unknown, req {0}",
+ JSON.stringify(req),
+ req.type));
+ return false;
+ });
+ });
+}
+
+
+wxMain(); \ No newline at end of file
diff --git a/extension/manifest.json b/extension/manifest.json
index 9a3b28b37..90745e577 100644
--- a/extension/manifest.json
+++ b/extension/manifest.json
@@ -46,6 +46,7 @@
"background/libwrapper.js",
"background/emscriptif.js",
"background/db.js",
+ "background/query.js",
"background/wallet.js"
]
},
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
index 669c264e6..43b814f9d 100644
--- a/extension/tsconfig.json
+++ b/extension/tsconfig.json
@@ -7,6 +7,7 @@
"background/wallet.ts",
"background/emscriptif.ts",
"background/db.ts",
+ "background/query.ts",
"lib/util.ts",
"lib/polyfill-react.ts",
"content_scripts/notify.ts",