aboutsummaryrefslogtreecommitdiff
path: root/extension/lib/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'extension/lib/wallet')
-rw-r--r--extension/lib/wallet/db.ts9
-rw-r--r--extension/lib/wallet/query.ts25
-rw-r--r--extension/lib/wallet/wallet.ts83
-rw-r--r--extension/lib/wallet/wxmessaging.js34
-rw-r--r--extension/lib/wallet/wxmessaging.ts62
5 files changed, 167 insertions, 46 deletions
diff --git a/extension/lib/wallet/db.ts b/extension/lib/wallet/db.ts
index a208f0923..8c331ef6f 100644
--- a/extension/lib/wallet/db.ts
+++ b/extension/lib/wallet/db.ts
@@ -33,7 +33,7 @@ const DB_VERSION = 1;
*/
export function openTalerDb(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
- let req = indexedDB.open(DB_NAME, DB_VERSION);
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
req.onerror = (e) => {
reject(e);
};
@@ -45,16 +45,17 @@ export function openTalerDb(): Promise<IDBDatabase> {
console.log("DB: upgrade needed: oldVersion = " + e.oldVersion);
switch (e.oldVersion) {
case 0: // DB does not exist yet
- let mints = db.createObjectStore("mints", {keyPath: "baseUrl"});
+ const mints = db.createObjectStore("mints", {keyPath: "baseUrl"});
mints.createIndex("pubKey", "keys.master_public_key");
db.createObjectStore("reserves", {keyPath: "reserve_pub"});
db.createObjectStore("denoms", {keyPath: "denomPub"});
- let coins = db.createObjectStore("coins", {keyPath: "coinPub"});
+ const coins = db.createObjectStore("coins", {keyPath: "coinPub"});
coins.createIndex("mintBaseUrl", "mintBaseUrl");
db.createObjectStore("transactions", {keyPath: "contractHash"});
db.createObjectStore("precoins",
{keyPath: "coinPub", autoIncrement: true});
- db.createObjectStore("history", {keyPath: "id", autoIncrement: true});
+ const history = db.createObjectStore("history", {keyPath: "id", autoIncrement: true});
+ history.createIndex("timestamp", "timestamp");
break;
}
};
diff --git a/extension/lib/wallet/query.ts b/extension/lib/wallet/query.ts
index dda716d07..375816193 100644
--- a/extension/lib/wallet/query.ts
+++ b/extension/lib/wallet/query.ts
@@ -39,7 +39,7 @@ export interface QueryStream<T> {
indexName: string,
keyFn: (obj: any) => any): QueryStream<[T,S]>;
filter(f: (any) => boolean): QueryStream<T>;
- reduce<S>(f: (S, T) => S, acc?: S): Promise<S>;
+ reduce<S>(f: (v: T, acc: S) => S, start?: S): Promise<S>;
}
@@ -166,7 +166,7 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
private storeName;
private options;
- constructor(qr, storeName, options?) {
+ constructor(qr, storeName, options) {
super(qr);
this.options = options;
this.storeName = storeName;
@@ -174,15 +174,16 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
subscribe(f) {
let doIt = (tx) => {
+ const {indexName = void 0, only = void 0} = this.options;
let s;
- if (this.options && this.options.indexName) {
+ if (indexName !== void 0) {
s = tx.objectStore(this.storeName)
.index(this.options.indexName);
} else {
s = tx.objectStore(this.storeName);
}
let kr = undefined;
- if (this.options && ("only" in this.options)) {
+ if (only !== void 0) {
kr = IDBKeyRange.only(this.options.only);
}
let req = s.openCursor(kr);
@@ -218,23 +219,11 @@ class QueryRoot {
this.db = db;
}
- iter<T>(storeName): QueryStream<T> {
+ iter<T>(storeName, {only = void 0, indexName = void 0} = {}): QueryStream<T> {
this.stores.add(storeName);
- return new IterQueryStream(this, storeName);
+ return new IterQueryStream(this, storeName, {only, indexName});
}
- iterOnly<T>(storeName, key): QueryStream<T> {
- this.stores.add(storeName);
- return new IterQueryStream(this, storeName, {only: key});
- }
-
-
- iterIndex<T>(storeName, indexName, key) {
- this.stores.add(storeName);
- return new IterQueryStream(this, storeName, {indexName: indexName});
- }
-
-
/**
* Put an object into the given object store.
* Overrides if an existing object with the same key exists
diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts
index 8dbcca044..2a931ae33 100644
--- a/extension/lib/wallet/wallet.ts
+++ b/extension/lib/wallet/wallet.ts
@@ -181,6 +181,18 @@ function canonicalizeBaseUrl(url) {
return x.href()
}
+function parsePrettyAmount(pretty: string): AmountJson_interface {
+ const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
+ if (!res) {
+ return null;
+ }
+ return {
+ value: parseInt(res[1], 10),
+ fraction: res[2] ? (parseFloat(`0.${res[2]}`) * 1e-6) : 0,
+ currency: res[3]
+ }
+}
+
interface HttpRequestLibrary {
req(method: string,
@@ -310,7 +322,7 @@ export class Wallet {
let ps = allowedMints.map((info) => {
return Query(this.db)
- .iterIndex("mints", "pubKey", info.master_pub)
+ .iter("mints", {indexName: "pubKey", only: info.master_pub})
.indexJoin("coins", "mintBaseUrl", (mint) => mint.baseUrl)
.reduce(storeMintCoin);
});
@@ -376,8 +388,20 @@ export class Wallet {
payReq: payReq
};
+
+ let historyEntry = {
+ type: "pay",
+ timestamp: (new Date).getTime(),
+ detail: {
+ merchantName: offer.contract.merchant.name,
+ amount: offer.contract.amount,
+ contractHash: offer.H_contract
+ }
+ };
+
return Query(this.db)
.put("transactions", t)
+ .put("history", historyEntry)
.putAll("coins", payCoinInfo.map((pci) => pci.updatedCoin))
.finish();
}
@@ -418,12 +442,17 @@ export class Wallet {
let reservePriv = EddsaPrivateKey.create();
let reservePub = reservePriv.getPublicKey();
let form = new FormData();
- let now = (new Date()).toString();
+ let now: number = (new Date).getTime();
form.append(req.field_amount, req.amount_str);
form.append(req.field_reserve_pub, reservePub.toCrock());
form.append(req.field_mint, req.mint);
// TODO: set bank-specified fields.
let mintBaseUrl = canonicalizeBaseUrl(req.mint);
+ let requestedAmount = parsePrettyAmount(req.amount_str);
+
+ if (!requestedAmount) {
+ throw Error(`unrecognized amount ${req.amount_str}.`);
+ }
return this.http.postForm(req.post_url, form)
.then((hresp) => {
@@ -441,7 +470,7 @@ export class Wallet {
last_query: null,
current_amount: null,
// XXX: set to actual amount
- initial_amount: null
+ requested_amount: null
};
if (hresp.status != 200) {
@@ -449,12 +478,22 @@ export class Wallet {
return resp;
}
+ let historyEntry = {
+ type: "create-reserve",
+ timestamp: now,
+ detail: {
+ requestedAmount,
+ reservePub: reserveRecord.reserve_pub,
+ }
+ };
+
resp.success = true;
// We can't show the page directly, so
// we show some generic page from the wallet.
resp.backlink = null;
return Query(this.db)
.put("reserves", reserveRecord)
+ .put("history", historyEntry)
.finish()
.then(() => {
// Do this in the background
@@ -574,10 +613,18 @@ export class Wallet {
.then(doBadge.bind(this));
}
- storeCoin(coin: Coin) {
- Query(this.db)
+ storeCoin(coin: Coin): Promise<void> {
+ let historyEntry = {
+ type: "withdraw",
+ timestamp: (new Date).getTime(),
+ detail: {
+ coinPub: coin.coinPub,
+ }
+ };
+ return Query(this.db)
.delete("precoins", coin.coinPub)
.add("coins", coin)
+ .add("history", historyEntry)
.finish()
.then(() => {
this.updateBadge();
@@ -624,7 +671,10 @@ export class Wallet {
}
let d = workList.pop();
this.withdraw(d, reserve)
- .then(() => next());
+ .then(() => next())
+ .catch((e) => {
+ console.log("Failed to withdraw coin", e.stack);
+ });
};
// Asynchronous recursion
@@ -647,7 +697,18 @@ export class Wallet {
if (!reserveInfo) {
throw Error();
}
+ let oldAmount = reserve.current_amount;
+ let newAmount = reserveInfo.balance;
reserve.current_amount = reserveInfo.balance;
+ let historyEntry = {
+ type: "reserve-update",
+ timestamp: (new Date).getTime(),
+ detail: {
+ reservePub,
+ oldAmount,
+ newAmount
+ }
+ };
return Query(this.db)
.put("reserves", reserve)
.finish()
@@ -696,4 +757,14 @@ export class Wallet {
.iter("coins")
.reduce(collectBalances, {});
}
+
+ getHistory() {
+ function collect(x, acc) {
+ acc.push(x);
+ return acc;
+ }
+ return Query(this.db)
+ .iter("history", {indexName: "timestamp"})
+ .reduce(collect, [])
+ }
}
diff --git a/extension/lib/wallet/wxmessaging.js b/extension/lib/wallet/wxmessaging.js
index d4df23a08..93ffd8bb7 100644
--- a/extension/lib/wallet/wxmessaging.js
+++ b/extension/lib/wallet/wxmessaging.js
@@ -20,7 +20,12 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
function makeHandlers(wallet) {
return (_a = {},
_a["balances"] = function (db, detail, sendResponse) {
- wallet.getBalances().then(sendResponse);
+ wallet.getBalances()
+ .then(sendResponse)
+ .catch(function (e) {
+ console.log("exception during 'balances'");
+ console.error(e.stack);
+ });
return true;
},
_a["dump-db"] = function (db, detail, sendResponse) {
@@ -54,6 +59,10 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
resp.backlink = chrome.extension.getURL("pages/reserve-success.html");
}
sendResponse(resp);
+ })
+ .catch(function (e) {
+ console.error("exception during 'confirm-reserve'");
+ console.error(e.stack);
});
return true;
},
@@ -63,6 +72,8 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
sendResponse({ success: true });
})
.catch(function (e) {
+ console.error("exception during 'confirm-pay'");
+ console.error(e.stack);
sendResponse({ error: e.message });
});
return true;
@@ -77,18 +88,33 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
});
})
.catch(function (e) {
+ console.error("exception during 'execute-payment'");
+ console.error(e.stack);
sendResponse({ success: false, error: e.message });
});
// async sendResponse
return true;
},
+ _a["get-history"] = function (db, detail, sendResponse) {
+ // TODO: limit history length
+ wallet.getHistory()
+ .then(function (h) {
+ sendResponse(h);
+ })
+ .catch(function (e) {
+ console.error("exception during 'get-history'");
+ console.error(e.stack);
+ });
+ return true;
+ },
_a
);
var _a;
}
function wxMain() {
chrome.browserAction.setBadgeText({ text: "" });
- db_3.openTalerDb().then(function (db) {
+ db_3.openTalerDb()
+ .then(function (db) {
var http = new http_1.BrowserHttpLib();
var badge = new ChromeBadge();
var wallet = new wallet_1.Wallet(db, http, badge);
@@ -101,6 +127,10 @@ System.register(["./wallet", "./db", "./http"], function(exports_1) {
console.error("Request type " + JSON.stringify(req) + " unknown, req " + req.type);
return false;
});
+ })
+ .catch(function (e) {
+ console.error("could not open database:");
+ console.error(e.stack);
});
}
exports_1("wxMain", wxMain);
diff --git a/extension/lib/wallet/wxmessaging.ts b/extension/lib/wallet/wxmessaging.ts
index 123746f4d..de9d5907c 100644
--- a/extension/lib/wallet/wxmessaging.ts
+++ b/extension/lib/wallet/wxmessaging.ts
@@ -36,7 +36,12 @@ import {Badge} from "./wallet";
function makeHandlers(wallet) {
return {
["balances"]: function(db, detail, sendResponse) {
- wallet.getBalances().then(sendResponse);
+ wallet.getBalances()
+ .then(sendResponse)
+ .catch((e) => {
+ console.log("exception during 'balances'");
+ console.error(e.stack);
+ });
return true;
},
["dump-db"]: function(db, detail, sendResponse) {
@@ -72,6 +77,10 @@ function makeHandlers(wallet) {
"pages/reserve-success.html");
}
sendResponse(resp);
+ })
+ .catch((e) => {
+ console.error("exception during 'confirm-reserve'");
+ console.error(e.stack);
});
return true;
},
@@ -81,6 +90,8 @@ function makeHandlers(wallet) {
sendResponse({success: true})
})
.catch((e) => {
+ console.error("exception during 'confirm-pay'");
+ console.error(e.stack);
sendResponse({error: e.message});
});
return true;
@@ -95,10 +106,24 @@ function makeHandlers(wallet) {
});
})
.catch((e) => {
+ console.error("exception during 'execute-payment'");
+ console.error(e.stack);
sendResponse({success: false, error: e.message});
});
// async sendResponse
return true;
+ },
+ ["get-history"]: function(db, detail, sendResponse) {
+ // TODO: limit history length
+ wallet.getHistory()
+ .then((h) => {
+ sendResponse(h);
+ })
+ .catch((e) => {
+ console.error("exception during 'get-history'");
+ console.error(e.stack);
+ });
+ return true;
}
};
}
@@ -117,19 +142,24 @@ class ChromeBadge implements Badge {
export function wxMain() {
chrome.browserAction.setBadgeText({text: ""});
- openTalerDb().then((db) => {
- let http = new BrowserHttpLib();
- let badge = new ChromeBadge();
- let wallet = new Wallet(db, http, badge);
- let handlers = makeHandlers(wallet);
- wallet.updateBadge();
- chrome.runtime.onMessage.addListener(
- function(req, sender, onresponse) {
- if (req.type in handlers) {
- return handlers[req.type](db, req.detail, onresponse);
- }
- console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`);
- return false;
- });
- });
+ openTalerDb()
+ .then((db) => {
+ let http = new BrowserHttpLib();
+ let badge = new ChromeBadge();
+ let wallet = new Wallet(db, http, badge);
+ let handlers = makeHandlers(wallet);
+ wallet.updateBadge();
+ chrome.runtime.onMessage.addListener(
+ function(req, sender, onresponse) {
+ if (req.type in handlers) {
+ return handlers[req.type](db, req.detail, onresponse);
+ }
+ console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`);
+ return false;
+ });
+ })
+ .catch((e) => {
+ console.error("could not open database:");
+ console.error(e.stack);
+ });
} \ No newline at end of file