diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-01-05 01:10:31 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-01-05 01:10:31 +0100 |
commit | c9fc0c31efa36ed6095971be693148d774667bb8 (patch) | |
tree | c6f2a410aeea81f0ba347391dc5b274a29a84900 /extension/background/wallet.js | |
parent | ba4d272cc4a503ebccdf015a606bc488b33330a5 (diff) |
Nicer DB interface, various refactoring.
Diffstat (limited to 'extension/background/wallet.js')
-rw-r--r-- | extension/background/wallet.js | 416 |
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(); |