aboutsummaryrefslogtreecommitdiff
path: root/extension/background/wallet.js
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 /extension/background/wallet.js
parentba4d272cc4a503ebccdf015a606bc488b33330a5 (diff)
Nicer DB interface, various refactoring.
Diffstat (limited to 'extension/background/wallet.js')
-rw-r--r--extension/background/wallet.js416
1 files changed, 176 insertions, 240 deletions
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();