aboutsummaryrefslogtreecommitdiff
path: root/lib/wallet
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-09-22 15:09:18 +0200
committerChristian Grothoff <christian@grothoff.org>2016-09-22 15:09:18 +0200
commit4974dd19c02778f0c58fad6cd12e839c9164e00d (patch)
tree69198fc1d53cb595ed1cb393e86ac7ef0038a309 /lib/wallet
parent903bb780dea2a4c538d2ffde3ba5a0a7f4497050 (diff)
parentfca125a0da491e1753d2902d21a672559936922b (diff)
downloadwallet-core-4974dd19c02778f0c58fad6cd12e839c9164e00d.tar.xz
Merge branch 'master' of git+ssh://taler.net/var/git/wallet-webex
Diffstat (limited to 'lib/wallet')
-rw-r--r--lib/wallet/checkable.ts51
-rw-r--r--lib/wallet/cryptoApi.ts7
-rw-r--r--lib/wallet/cryptoLib.ts9
-rw-r--r--lib/wallet/db.ts15
-rw-r--r--lib/wallet/emscriptif.ts55
-rw-r--r--lib/wallet/helpers.ts8
-rw-r--r--lib/wallet/http.ts6
-rw-r--r--lib/wallet/query.ts140
-rw-r--r--lib/wallet/types.ts2
-rw-r--r--lib/wallet/wallet.ts69
-rw-r--r--lib/wallet/wxMessaging.ts92
11 files changed, 284 insertions, 170 deletions
diff --git a/lib/wallet/checkable.ts b/lib/wallet/checkable.ts
index a35634e81..9fd816578 100644
--- a/lib/wallet/checkable.ts
+++ b/lib/wallet/checkable.ts
@@ -25,18 +25,39 @@
*/
export namespace Checkable {
- export function SchemaError(message) {
+
+ type Path = (number|string)[];
+
+ interface SchemaErrorConstructor {
+ new (err: string): SchemaError;
+ }
+
+ interface SchemaError {
+ name: string;
+ message: string;
+ }
+
+ interface Prop {
+ propertyKey: any;
+ checker: any;
+ type: any;
+ elementChecker?: any;
+ elementProp?: any;
+ }
+
+ export let SchemaError = (function SchemaError(message: string) {
this.name = 'SchemaError';
this.message = message;
this.stack = (<any>new Error()).stack;
- }
+ }) as any as SchemaErrorConstructor;
+
SchemaError.prototype = new Error;
let chkSym = Symbol("checkable");
- function checkNumber(target, prop, path): any {
+ function checkNumber(target: any, prop: Prop, path: Path): any {
if ((typeof target) !== "number") {
throw new SchemaError(`expected number for ${path}`);
}
@@ -44,7 +65,7 @@ export namespace Checkable {
}
- function checkString(target, prop, path): any {
+ function checkString(target: any, prop: Prop, path: Path): any {
if (typeof target !== "string") {
throw new SchemaError(`expected string for ${path}, got ${typeof target} instead`);
}
@@ -52,7 +73,7 @@ export namespace Checkable {
}
- function checkAnyObject(target, prop, path): any {
+ function checkAnyObject(target: any, prop: Prop, path: Path): any {
if (typeof target !== "object") {
throw new SchemaError(`expected (any) object for ${path}, got ${typeof target} instead`);
}
@@ -60,12 +81,12 @@ export namespace Checkable {
}
- function checkAny(target, prop, path): any {
+ function checkAny(target: any, prop: Prop, path: Path): any {
return target;
}
- function checkList(target, prop, path): any {
+ function checkList(target: any, prop: Prop, path: Path): any {
if (!Array.isArray(target)) {
throw new SchemaError(`array expected for ${path}, got ${typeof target} instead`);
}
@@ -77,7 +98,7 @@ export namespace Checkable {
}
- function checkOptional(target, prop, path): any {
+ function checkOptional(target: any, prop: Prop, path: Path): any {
console.assert(prop.propertyKey);
prop.elementChecker(target,
prop.elementProp,
@@ -86,7 +107,7 @@ export namespace Checkable {
}
- function checkValue(target, prop, path): any {
+ function checkValue(target: any, prop: Prop, path: Path): any {
let type = prop.type;
if (!type) {
throw Error(`assertion failed (prop is ${JSON.stringify(prop)})`);
@@ -123,8 +144,8 @@ export namespace Checkable {
}
- export function Class(target) {
- target.checked = (v) => {
+ export function Class(target: any) {
+ target.checked = (v: any) => {
return checkValue(v, {
propertyKey: "(root)",
type: target,
@@ -135,7 +156,7 @@ export namespace Checkable {
}
- export function Value(type) {
+ export function Value(type: any) {
if (!type) {
throw Error("Type does not exist yet (wrong order of definitions?)");
}
@@ -152,7 +173,7 @@ export namespace Checkable {
}
- export function List(type) {
+ export function List(type: any) {
let stub = {};
type(stub, "(list-element)");
let elementProp = mkChk(stub).props[0];
@@ -174,7 +195,7 @@ export namespace Checkable {
}
- export function Optional(type) {
+ export function Optional(type: any) {
let stub = {};
type(stub, "(optional-element)");
let elementProp = mkChk(stub).props[0];
@@ -230,7 +251,7 @@ export namespace Checkable {
}
- function mkChk(target) {
+ function mkChk(target: any) {
let chk = target[chkSym];
if (!chk) {
chk = {props: []};
diff --git a/lib/wallet/cryptoApi.ts b/lib/wallet/cryptoApi.ts
index a0113b2ea..2a2a7d319 100644
--- a/lib/wallet/cryptoApi.ts
+++ b/lib/wallet/cryptoApi.ts
@@ -27,9 +27,10 @@ import {Denomination} from "./types";
import {Offer} from "./wallet";
import {CoinWithDenom} from "./wallet";
import {PayCoinInfo} from "./types";
+type RegistryEntry = {resolve: any; reject: any};
export class CryptoApi {
private nextRpcId: number = 1;
- private rpcRegistry = {};
+ private rpcRegistry: {[n: number]: RegistryEntry} = {};
private cryptoWorker: Worker;
@@ -52,14 +53,14 @@ export class CryptoApi {
}
- private registerRpcId(resolve, reject): number {
+ private registerRpcId(resolve: any, reject: any): number {
let id = this.nextRpcId++;
this.rpcRegistry[id] = {resolve, reject};
return id;
}
- private doRpc<T>(methodName: string, ...args): Promise<T> {
+ private doRpc<T>(methodName: string, ...args: any[]): Promise<T> {
return new Promise<T>((resolve, reject) => {
let msg = {
operation: methodName,
diff --git a/lib/wallet/cryptoLib.ts b/lib/wallet/cryptoLib.ts
index 1acbdaa16..1ca7b856f 100644
--- a/lib/wallet/cryptoLib.ts
+++ b/lib/wallet/cryptoLib.ts
@@ -28,6 +28,7 @@ import {Offer} from "./wallet";
import {CoinWithDenom} from "./wallet";
import {CoinPaySig} from "./types";
import {Denomination} from "./types";
+import {Amount} from "./emscriptif";
export function main(worker: Worker) {
@@ -43,7 +44,7 @@ export function main(worker: Worker) {
if (typeof msg.data.operation != "string") {
console.error("RPC operation must be string");
}
- let f = RpcFunctions[msg.data.operation];
+ let f = (RpcFunctions as any)[msg.data.operation];
if (!f) {
console.error(`unknown operation: '${msg.data.operation}'`);
return;
@@ -156,7 +157,7 @@ namespace RpcFunctions {
}
- export function rsaUnblind(sig, bk, pk): string {
+ export function rsaUnblind(sig: string, bk: string, pk: string): string {
let denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
native.RsaBlindingKeySecret.fromCrock(bk),
native.RsaPublicKey.fromCrock(pk));
@@ -170,11 +171,11 @@ namespace RpcFunctions {
*/
export function signDeposit(offer: Offer,
cds: CoinWithDenom[]): PayCoinInfo {
- let ret = [];
+ let ret: PayCoinInfo = [];
let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency);
let amountRemaining = new native.Amount(offer.contract.amount);
for (let cd of cds) {
- let coinSpend;
+ let coinSpend: Amount;
if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
break;
diff --git a/lib/wallet/db.ts b/lib/wallet/db.ts
index 6cd25ee27..5104f28fb 100644
--- a/lib/wallet/db.ts
+++ b/lib/wallet/db.ts
@@ -15,6 +15,7 @@
*/
"use strict";
+import Dictionary = _.Dictionary;
/**
* Declarations and helpers for
@@ -83,27 +84,27 @@ export function openTalerDb(): Promise<IDBDatabase> {
}
-export function exportDb(db): Promise<any> {
+export function exportDb(db: IDBDatabase): Promise<any> {
let dump = {
name: db.name,
version: db.version,
- stores: {}
+ stores: {} as Dictionary<any>,
};
return new Promise((resolve, reject) => {
- let tx = db.transaction(db.objectStoreNames);
- tx.addEventListener("complete", (e) => {
+ let tx = db.transaction(Array.from(db.objectStoreNames));
+ tx.addEventListener("complete", () => {
resolve(dump);
});
for (let i = 0; i < db.objectStoreNames.length; i++) {
let name = db.objectStoreNames[i];
- let storeDump = {};
+ let storeDump = {} as Dictionary<any>;
dump.stores[name] = storeDump;
let store = tx.objectStore(name)
.openCursor()
- .addEventListener("success", (e) => {
- let cursor = e.target.result;
+ .addEventListener("success", (e: Event) => {
+ let cursor = (e.target as any).result;
if (cursor) {
storeDump[cursor.key] = cursor.value;
cursor.continue();
diff --git a/lib/wallet/emscriptif.ts b/lib/wallet/emscriptif.ts
index 8540d15d0..1e5fb0283 100644
--- a/lib/wallet/emscriptif.ts
+++ b/lib/wallet/emscriptif.ts
@@ -36,11 +36,11 @@ const GNUNET_SYSERR = -1;
let Module = EmscWrapper.Module;
-let getEmsc: EmscWrapper.EmscFunGen = (...args) => Module.cwrap.apply(null,
+let getEmsc: EmscWrapper.EmscFunGen = (...args: any[]) => Module.cwrap.apply(null,
args);
var emsc = {
- free: (ptr) => Module._free(ptr),
+ free: (ptr: number) => Module._free(ptr),
get_value: getEmsc('TALER_WR_get_value',
'number',
['number']),
@@ -164,13 +164,12 @@ enum RandomQuality {
abstract class ArenaObject {
- private _nativePtr: number;
+ protected _nativePtr: number | undefined = undefined;
arena: Arena;
abstract destroy(): void;
constructor(arena?: Arena) {
- this.nativePtr = null;
if (!arena) {
if (arenaStack.length == 0) {
throw Error("No arena available")
@@ -192,14 +191,14 @@ abstract class ArenaObject {
}
free() {
- if (this.nativePtr !== undefined) {
+ if (this.nativePtr) {
emsc.free(this.nativePtr);
- this.nativePtr = undefined;
+ this._nativePtr = undefined;
}
}
alloc(size: number) {
- if (this.nativePtr !== undefined) {
+ if (this._nativePtr !== undefined) {
throw Error("Double allocation");
}
this.nativePtr = emscAlloc.malloc(size);
@@ -212,21 +211,22 @@ abstract class ArenaObject {
this._nativePtr = n;
}
- set nativePtr(v) {
+ set nativePtr(v: number) {
this.setNative(v);
}
get nativePtr() {
return this.getNative();
}
-
}
+
interface Arena {
put(obj: ArenaObject): void;
destroy(): void;
}
+
class DefaultArena implements Arena {
heap: Array<ArenaObject>;
@@ -234,7 +234,7 @@ class DefaultArena implements Arena {
this.heap = [];
}
- put(obj) {
+ put(obj: ArenaObject) {
this.heap.push(obj);
}
@@ -269,7 +269,7 @@ class SyncArena extends DefaultArena {
super();
}
- pub(obj) {
+ pub(obj: ArenaObject) {
super.put(obj);
if (!this.isScheduled) {
this.schedule();
@@ -308,14 +308,12 @@ export class Amount extends ArenaObject {
}
destroy() {
- if (this.nativePtr != 0) {
- emsc.free(this.nativePtr);
- }
+ super.free();
}
static getZero(currency: string, a?: Arena): Amount {
- let am = new Amount(null, a);
+ let am = new Amount(undefined, a);
let r = emsc.amount_get_zero(currency, am.getNative());
if (r != GNUNET_OK) {
throw Error("invalid currency");
@@ -442,7 +440,8 @@ abstract class PackedArenaObject extends ArenaObject {
}
alloc() {
- if (this.nativePtr === null) {
+ // FIXME: should the client be allowed to call alloc multiple times?
+ if (!this._nativePtr) {
this.nativePtr = emscAlloc.malloc(this.size());
}
}
@@ -466,7 +465,7 @@ abstract class PackedArenaObject extends ArenaObject {
b = (b + 256) % 256;
bytes.push("0".concat(b.toString(16)).slice(-2));
}
- let lines = [];
+ let lines: string[] = [];
for (let i = 0; i < bytes.length; i += 8) {
lines.push(bytes.slice(i, i + 8).join(","));
}
@@ -482,7 +481,7 @@ export class AmountNbo extends PackedArenaObject {
toJson(): any {
let a = new DefaultArena();
- let am = new Amount(null, a);
+ let am = new Amount(undefined, a);
am.fromNbo(this);
let json = am.toJson();
a.destroy();
@@ -508,7 +507,7 @@ export class EddsaPrivateKey extends PackedArenaObject {
return obj;
}
- static fromCrock: (string) => EddsaPrivateKey;
+ static fromCrock: (s: string) => EddsaPrivateKey;
}
mixinStatic(EddsaPrivateKey, fromCrock);
@@ -521,7 +520,7 @@ function fromCrock(s: string) {
}
-function mixin(obj, method, name?: string) {
+function mixin(obj: any, method: any, name?: string) {
if (!name) {
name = method.name;
}
@@ -532,7 +531,7 @@ function mixin(obj, method, name?: string) {
}
-function mixinStatic(obj, method, name?: string) {
+function mixinStatic(obj: any, method: any, name?: string) {
if (!name) {
name = method.name;
}
@@ -595,7 +594,7 @@ export class RsaBlindingKeySecret extends PackedArenaObject {
return o;
}
- static fromCrock: (string) => RsaBlindingKeySecret;
+ static fromCrock: (s: string) => RsaBlindingKeySecret;
}
mixinStatic(RsaBlindingKeySecret, fromCrock);
@@ -622,9 +621,9 @@ export class ByteArray extends PackedArenaObject {
return this.allocatedSize;
}
- constructor(desiredSize: number, init: number, a?: Arena) {
+ constructor(desiredSize: number, init?: number, a?: Arena) {
super(a);
- if (init === undefined || init === null) {
+ if (init === undefined) {
this.nativePtr = emscAlloc.malloc(desiredSize);
} else {
this.nativePtr = init;
@@ -642,7 +641,7 @@ export class ByteArray extends PackedArenaObject {
let hstr = emscAlloc.malloc(s.length + 1);
Module.writeStringToMemory(s, hstr);
let decodedLen = Math.floor((s.length * 5) / 8);
- let ba = new ByteArray(decodedLen, null, a);
+ let ba = new ByteArray(decodedLen, undefined, a);
let res = emsc.string_to_data(hstr, s.length, ba.nativePtr, decodedLen);
emsc.free(hstr);
if (res != GNUNET_OK) {
@@ -777,7 +776,7 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
x.alloc();
let r = /Date\(([0-9]+)\)/;
let m = r.exec(s);
- if (m.length != 2) {
+ if (!m || m.length != 2) {
throw Error();
}
let n = parseInt(m[1]) * 1000000;
@@ -899,11 +898,11 @@ interface Encodeable {
encode(arena?: Arena): ByteArray;
}
-function makeEncode(encodeFn) {
+function makeEncode(encodeFn: any) {
function encode(arena?: Arena) {
let ptr = emscAlloc.malloc(PTR_SIZE);
let len = encodeFn(this.getNative(), ptr);
- let res = new ByteArray(len, null, arena);
+ let res = new ByteArray(len, undefined, arena);
res.setNative(Module.getValue(ptr, '*'));
emsc.free(ptr);
return res;
diff --git a/lib/wallet/helpers.ts b/lib/wallet/helpers.ts
index fd1650758..5d231fe64 100644
--- a/lib/wallet/helpers.ts
+++ b/lib/wallet/helpers.ts
@@ -24,7 +24,7 @@
import {AmountJson} from "./types";
-export function substituteFulfillmentUrl(url: string, vars) {
+export function substituteFulfillmentUrl(url: string, vars: any) {
url = url.replace("${H_contract}", vars.H_contract);
url = url.replace("${$}", "$");
return url;
@@ -42,7 +42,7 @@ export function amountToPretty(amount: AmountJson): string {
*
* See http://api.taler.net/wallet.html#general
*/
-export function canonicalizeBaseUrl(url) {
+export function canonicalizeBaseUrl(url: string) {
let x = new URI(url);
if (!x.protocol()) {
x.protocol("https");
@@ -54,10 +54,10 @@ export function canonicalizeBaseUrl(url) {
}
-export function parsePrettyAmount(pretty: string): AmountJson {
+export function parsePrettyAmount(pretty: string): AmountJson|undefined {
const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
if (!res) {
- return null;
+ return undefined;
}
return {
value: parseInt(res[1], 10),
diff --git a/lib/wallet/http.ts b/lib/wallet/http.ts
index 60f388e4b..8f82ceaff 100644
--- a/lib/wallet/http.ts
+++ b/lib/wallet/http.ts
@@ -66,19 +66,19 @@ export class BrowserHttpLib {
}
- postJson(url: string|uri.URI, body) {
+ postJson(url: string|uri.URI, body: any) {
return this.req("post", url, {req: JSON.stringify(body)});
}
- postForm(url: string|uri.URI, form) {
+ postForm(url: string|uri.URI, form: any) {
return this.req("post", url, {req: form});
}
}
export class RequestException {
- constructor(detail) {
+ constructor(detail: any) {
}
}
diff --git a/lib/wallet/query.ts b/lib/wallet/query.ts
index 4eccb696b..c7420a3f7 100644
--- a/lib/wallet/query.ts
+++ b/lib/wallet/query.ts
@@ -24,7 +24,7 @@
"use strict";
-export function Query(db) {
+export function Query(db: IDBDatabase) {
return new QueryRoot(db);
}
@@ -36,24 +36,27 @@ export interface QueryStream<T> {
indexJoin<S>(storeName: string,
indexName: string,
keyFn: (obj: any) => any): QueryStream<[T,S]>;
- filter(f: (any) => boolean): QueryStream<T>;
+ filter(f: (x: any) => boolean): QueryStream<T>;
reduce<S>(f: (v: T, acc: S) => S, start?: S): Promise<S>;
- flatMap(f: (T) => T[]): QueryStream<T>;
+ flatMap(f: (x: T) => T[]): QueryStream<T>;
}
/**
* Get an unresolved promise together with its extracted resolve / reject
* function.
- *
- * @returns {{resolve: any, reject: any, promise: Promise<T>}}
*/
function openPromise<T>() {
- let resolve, reject;
+ let resolve: ((value?: T | PromiseLike<T>) => void) | null = null;
+ let reject: ((reason?: any) => void) | null = null;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
+ if (!(resolve && reject)) {
+ // Never happens, unless JS implementation is broken
+ throw Error();
+ }
return {resolve, reject, promise};
}
@@ -61,7 +64,7 @@ function openPromise<T>() {
abstract class QueryStreamBase<T> implements QueryStream<T> {
abstract subscribe(f: (isDone: boolean,
value: any,
- tx: IDBTransaction) => void);
+ tx: IDBTransaction) => void): void;
root: QueryRoot;
@@ -69,30 +72,28 @@ abstract class QueryStreamBase<T> implements QueryStream<T> {
this.root = root;
}
- flatMap(f: (T) => T[]): QueryStream<T> {
+ flatMap(f: (x: T) => T[]): QueryStream<T> {
return new QueryStreamFlatMap(this, f);
}
indexJoin<S>(storeName: string,
indexName: string,
key: any): QueryStream<[T,S]> {
- this.root.addWork(null, storeName, false);
+ this.root.addStoreAccess(storeName, false);
return new QueryStreamIndexJoin(this, storeName, indexName, key);
}
- filter(f: (any) => boolean): QueryStream<T> {
+ filter(f: (x: any) => boolean): QueryStream<T> {
return new QueryStreamFilter(this, f);
}
- reduce(f, acc?): Promise<any> {
- let leakedResolve;
- let p = new Promise((resolve, reject) => {
- leakedResolve = resolve;
- });
+ reduce<A>(f: (x: any, acc?: A) => A, init?: A): Promise<any> {
+ let {resolve, promise} = openPromise();
+ let acc = init;
this.subscribe((isDone, value) => {
if (isDone) {
- leakedResolve(acc);
+ resolve(acc);
return;
}
acc = f(value, acc);
@@ -100,22 +101,28 @@ abstract class QueryStreamBase<T> implements QueryStream<T> {
return Promise.resolve()
.then(() => this.root.finish())
- .then(() => p);
+ .then(() => promise);
}
}
+type FilterFn = (e: any) => boolean;
+type SubscribeFn = (done: boolean, value: any, tx: IDBTransaction) => void;
+
+interface FlatMapFn<T> {
+ (v: T): T[];
+}
class QueryStreamFilter<T> extends QueryStreamBase<T> {
s: QueryStreamBase<T>;
- filterFn;
+ filterFn: FilterFn;
- constructor(s: QueryStreamBase<T>, filterFn) {
+ constructor(s: QueryStreamBase<T>, filterFn: FilterFn) {
super(s.root);
this.s = s;
this.filterFn = filterFn;
}
- subscribe(f) {
+ subscribe(f: SubscribeFn) {
this.s.subscribe((isDone, value, tx) => {
if (isDone) {
f(true, undefined, tx);
@@ -131,15 +138,15 @@ class QueryStreamFilter<T> extends QueryStreamBase<T> {
class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
s: QueryStreamBase<T>;
- flatMapFn;
+ flatMapFn: (v: T) => T[];
- constructor(s: QueryStreamBase<T>, flatMapFn) {
+ constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => T[]) {
super(s.root);
this.s = s;
- this.flatMap = flatMapFn;
+ this.flatMapFn = flatMapFn;
}
- subscribe(f) {
+ subscribe(f: SubscribeFn) {
this.s.subscribe((isDone, value, tx) => {
if (isDone) {
f(true, undefined, tx);
@@ -154,13 +161,13 @@ class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
}
-class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
+class QueryStreamIndexJoin<T,S> extends QueryStreamBase<[T, S]> {
s: QueryStreamBase<T>;
- storeName;
- key;
- indexName;
+ storeName: string;
+ key: any;
+ indexName: string;
- constructor(s, storeName: string, indexName: string, key: any) {
+ constructor(s: QueryStreamBase<T>, storeName: string, indexName: string, key: any) {
super(s.root);
this.s = s;
this.storeName = storeName;
@@ -168,7 +175,7 @@ class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
this.indexName = indexName;
}
- subscribe(f) {
+ subscribe(f: SubscribeFn) {
this.s.subscribe((isDone, value, tx) => {
if (isDone) {
f(true, undefined, tx);
@@ -192,31 +199,31 @@ class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
class IterQueryStream<T> extends QueryStreamBase<T> {
- private storeName;
- private options;
- private subscribers;
+ private storeName: string;
+ private options: any;
+ private subscribers: SubscribeFn[];
- constructor(qr, storeName, options) {
+ constructor(qr: QueryRoot, storeName: string, options: any) {
super(qr);
this.options = options;
this.storeName = storeName;
this.subscribers = [];
- let doIt = (tx) => {
+ let doIt = (tx: IDBTransaction) => {
const {indexName = void 0, only = void 0} = this.options;
- let s;
+ let s: any;
if (indexName !== void 0) {
s = tx.objectStore(this.storeName)
.index(this.options.indexName);
} else {
s = tx.objectStore(this.storeName);
}
- let kr = undefined;
- if (only !== void 0) {
+ let kr: IDBKeyRange|undefined = undefined;
+ if (only !== undefined) {
kr = IDBKeyRange.only(this.options.only);
}
let req = s.openCursor(kr);
- req.onsuccess = (e) => {
+ req.onsuccess = () => {
let cursor: IDBCursorWithValue = req.result;
if (cursor) {
for (let f of this.subscribers) {
@@ -231,32 +238,33 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
}
};
- this.root.addWork(doIt, null, false);
+ this.root.addWork(doIt);
}
- subscribe(f) {
+ subscribe(f: SubscribeFn) {
this.subscribers.push(f);
}
}
class QueryRoot {
- private work = [];
+ private work: ((t: IDBTransaction) => void)[] = [];
private db: IDBDatabase;
private stores = new Set();
- private kickoffPromise;
+ private kickoffPromise: Promise<void>;
/**
* Some operations is a write operation,
* and we need to do a "readwrite" transaction/
*/
- private hasWrite;
+ private hasWrite: boolean;
- constructor(db) {
+ constructor(db: IDBDatabase) {
this.db = db;
}
- iter<T>(storeName, {only = void 0, indexName = void 0} = {}): QueryStream<T> {
+ iter<T>(storeName: string,
+ {only = <string|undefined>undefined, indexName = <string|undefined>undefined} = {}): QueryStream<T> {
this.stores.add(storeName);
return new IterQueryStream(this, storeName, {only, indexName});
}
@@ -266,7 +274,7 @@ class QueryRoot {
* Overrides if an existing object with the same key exists
* in the store.
*/
- put(storeName, val): QueryRoot {
+ put(storeName: string, val: any): QueryRoot {
let doPut = (tx: IDBTransaction) => {
tx.objectStore(storeName).put(val);
};
@@ -280,7 +288,7 @@ class QueryRoot {
* Fails if the object's key is already present
* in the object store.
*/
- putAll(storeName, iterable): QueryRoot {
+ putAll(storeName: string, iterable: any[]): QueryRoot {
const doPutAll = (tx: IDBTransaction) => {
for (const obj of iterable) {
tx.objectStore(storeName).put(obj);
@@ -295,7 +303,7 @@ class QueryRoot {
* Fails if the object's key is already present
* in the object store.
*/
- add(storeName, val): QueryRoot {
+ add(storeName: string, val: any): QueryRoot {
const doAdd = (tx: IDBTransaction) => {
tx.objectStore(storeName).add(val);
};
@@ -306,16 +314,16 @@ class QueryRoot {
/**
* Get one object from a store by its key.
*/
- get(storeName, key): Promise<any> {
+ get(storeName: any, key: any): Promise<any> {
if (key === void 0) {
throw Error("key must not be undefined");
}
const {resolve, promise} = openPromise();
- const doGet = (tx) => {
+ const doGet = (tx: IDBTransaction) => {
const req = tx.objectStore(storeName).get(key);
- req.onsuccess = (r) => {
+ req.onsuccess = () => {
resolve(req.result);
};
};
@@ -329,16 +337,16 @@ class QueryRoot {
/**
* Get one object from a store by its key.
*/
- getIndexed(storeName, indexName, key): Promise<any> {
+ getIndexed(storeName: string, indexName: string, key: any): Promise<any> {
if (key === void 0) {
throw Error("key must not be undefined");
}
const {resolve, promise} = openPromise();
- const doGetIndexed = (tx) => {
+ const doGetIndexed = (tx: IDBTransaction) => {
const req = tx.objectStore(storeName).index(indexName).get(key);
- req.onsuccess = (r) => {
+ req.onsuccess = () => {
resolve(req.result);
};
};
@@ -356,7 +364,7 @@ class QueryRoot {
if (this.kickoffPromise) {
return this.kickoffPromise;
}
- this.kickoffPromise = new Promise((resolve, reject) => {
+ this.kickoffPromise = new Promise<void>((resolve, reject) => {
if (this.work.length == 0) {
resolve();
return;
@@ -376,8 +384,8 @@ class QueryRoot {
/**
* Delete an object by from the given object store.
*/
- delete(storeName: string, key): QueryRoot {
- const doDelete = (tx) => {
+ delete(storeName: string, key: any): QueryRoot {
+ const doDelete = (tx: IDBTransaction) => {
tx.objectStore(storeName).delete(key);
};
this.addWork(doDelete, storeName, true);
@@ -387,17 +395,21 @@ class QueryRoot {
/**
* Low-level function to add a task to the internal work queue.
*/
- addWork(workFn: (IDBTransaction) => void,
- storeName: string,
- isWrite: boolean) {
+ addWork(workFn: (t: IDBTransaction) => void,
+ storeName?: string,
+ isWrite?: boolean) {
+ this.work.push(workFn);
+ if (storeName) {
+ this.addStoreAccess(storeName, isWrite);
+ }
+ }
+
+ addStoreAccess(storeName: string, isWrite?: boolean) {
if (storeName) {
this.stores.add(storeName);
}
if (isWrite) {
this.hasWrite = true;
}
- if (workFn) {
- this.work.push(workFn);
- }
}
} \ No newline at end of file
diff --git a/lib/wallet/types.ts b/lib/wallet/types.ts
index 2434dca32..e8b7a1e39 100644
--- a/lib/wallet/types.ts
+++ b/lib/wallet/types.ts
@@ -392,5 +392,5 @@ export interface CheckRepurchaseResult {
export interface Notifier {
- notify();
+ notify(): void;
}
diff --git a/lib/wallet/wallet.ts b/lib/wallet/wallet.ts
index 9edad2c5c..367c9cbcd 100644
--- a/lib/wallet/wallet.ts
+++ b/lib/wallet/wallet.ts
@@ -154,12 +154,12 @@ interface Transaction {
export interface Badge {
setText(s: string): void;
setColor(c: string): void;
- startBusy();
- stopBusy();
+ startBusy(): void;
+ stopBusy(): void;
}
-function deepEquals(x, y) {
+function deepEquals(x: any, y: any): boolean {
if (x === y) {
return true;
}
@@ -179,7 +179,7 @@ function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
}
-function getTalerStampSec(stamp: string): number {
+function getTalerStampSec(stamp: string): number|null {
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
if (!m) {
return null;
@@ -188,7 +188,7 @@ function getTalerStampSec(stamp: string): number {
}
-function setTimeout(f, t) {
+function setTimeout(f: any, t: number) {
return chrome.extension.getBackgroundPage().setTimeout(f, t);
}
@@ -211,13 +211,13 @@ interface HttpRequestLibrary {
get(url: string|uri.URI): Promise<HttpResponse>;
- postJson(url: string|uri.URI, body): Promise<HttpResponse>;
+ postJson(url: string|uri.URI, body: any): Promise<HttpResponse>;
- postForm(url: string|uri.URI, form): Promise<HttpResponse>;
+ postForm(url: string|uri.URI, form: any): Promise<HttpResponse>;
}
-function copy(o) {
+function copy(o: any) {
return JSON.parse(JSON.stringify(o));
}
@@ -240,11 +240,18 @@ interface KeyUpdateInfo {
function getWithdrawDenomList(amountAvailable: AmountJson,
denoms: Denomination[]): Denomination[] {
let remaining = Amounts.copy(amountAvailable);
- let ds: Denomination[] = [];
+ const ds: Denomination[] = [];
+
+ console.log("available denoms");
+ console.log(denoms);
denoms = denoms.filter(isWithdrawableDenom);
denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
+ console.log("withdrawable denoms");
+ console.log(denoms);
+
+
// This is an arbitrary number of coins
// we can withdraw in one go. It's not clear if this limit
// is useful ...
@@ -355,7 +362,7 @@ export class Wallet {
let x: number;
- function storeExchangeCoin(mc, url) {
+ function storeExchangeCoin(mc: any, url: string) {
let exchange: IExchangeInfo = mc[0];
console.log("got coin for exchange", url);
let coin: Coin = mc[1];
@@ -366,18 +373,16 @@ export class Wallet {
exchange.baseUrl);
return;
}
- let cd = {
- coin: coin,
- denom: exchange.active_denoms.find((e) => e.denom_pub === coin.denomPub)
- };
- if (!cd.denom) {
+ let denom = exchange.active_denoms.find((e) => e.denom_pub === coin.denomPub);
+ if (!denom) {
console.warn("denom not found (database inconsistent)");
return;
}
- if (cd.denom.value.currency !== paymentAmount.currency) {
+ if (denom.value.currency !== paymentAmount.currency) {
console.warn("same pubkey for different currencies");
return;
}
+ let cd = {coin, denom};
let x = m[url];
if (!x) {
m[url] = [cd];
@@ -464,7 +469,7 @@ export class Wallet {
private recordConfirmPay(offer: Offer,
payCoinInfo: PayCoinInfo,
chosenExchange: string): Promise<void> {
- let payReq = {};
+ let payReq: any = {};
payReq["amount"] = offer.contract.amount;
payReq["coins"] = payCoinInfo.map((x) => x.sig);
payReq["H_contract"] = offer.H_contract;
@@ -581,7 +586,7 @@ export class Wallet {
* Retrieve all necessary information for looking up the contract
* with the given hash.
*/
- executePayment(H_contract): Promise<any> {
+ executePayment(H_contract: string): Promise<any> {
return Promise.resolve().then(() => {
return Query(this.db)
.get("transactions", H_contract)
@@ -607,7 +612,7 @@ export class Wallet {
* First fetch information requred to withdraw from the reserve,
* then deplete the reserve, withdrawing coins until it is empty.
*/
- private processReserve(reserveRecord): void {
+ private processReserve(reserveRecord: any): void {
let retryDelayMs = 100;
const opId = "reserve-" + reserveRecord.reserve_pub;
this.startOperation(opId);
@@ -637,7 +642,7 @@ export class Wallet {
}
- private processPreCoin(preCoin, retryDelayMs = 100): void {
+ private processPreCoin(preCoin: any, retryDelayMs = 100): void {
this.withdrawExecute(preCoin)
.then((c) => this.storeCoin(c))
.catch((e) => {
@@ -803,7 +808,7 @@ export class Wallet {
/**
* Withdraw coins from a reserve until it is empty.
*/
- private depleteReserve(reserve, exchange: IExchangeInfo): Promise<void> {
+ private depleteReserve(reserve: any, exchange: IExchangeInfo): Promise<void> {
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
denomsAvailable);
@@ -912,7 +917,7 @@ export class Wallet {
* Optionally link the reserve entry to the new or existing
* exchange entry in then DB.
*/
- updateExchangeFromUrl(baseUrl): Promise<IExchangeInfo> {
+ updateExchangeFromUrl(baseUrl: string): Promise<IExchangeInfo> {
baseUrl = canonicalizeBaseUrl(baseUrl);
let reqUrl = URI("keys").absoluteTo(baseUrl);
return this.http.get(reqUrl).then((resp) => {
@@ -927,8 +932,8 @@ export class Wallet {
private updateExchangeFromJson(baseUrl: string,
exchangeKeysJson: KeysJson): Promise<IExchangeInfo> {
- let updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
- if (!updateTimeSec) {
+ const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
+ if (updateTimeSec === null) {
throw Error("invalid update time");
}
@@ -947,7 +952,7 @@ export class Wallet {
console.log("making fresh exchange");
} else {
if (updateTimeSec < r.last_update_time) {
- console.log("outdated /keys, not updating")
+ console.log("outdated /keys, not updating");
return Promise.resolve(r);
}
exchangeInfo = r;
@@ -966,9 +971,9 @@ export class Wallet {
{indexName: "exchangeBaseUrl", only: baseUrl})
.reduce((coin: Coin, suspendedCoins: Coin[]) => {
if (!updatedExchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) {
- return [].concat(suspendedCoins, [coin]);
+ return Array.prototype.concat(suspendedCoins, [coin]);
}
- return [].concat(suspendedCoins);
+ return Array.prototype.concat(suspendedCoins);
}, [])
.then((suspendedCoins: Coin[]) => {
let q = Query(this.db);
@@ -999,8 +1004,8 @@ export class Wallet {
let found = false;
for (let oldDenom of exchangeInfo.all_denoms) {
if (oldDenom.denom_pub === newDenom.denom_pub) {
- let a = Object.assign({}, oldDenom);
- let b = Object.assign({}, newDenom);
+ let a: any = Object.assign({}, oldDenom);
+ let b: any = Object.assign({}, newDenom);
// pub hash is only there for convenience in the wallet
delete a["pub_hash"];
delete b["pub_hash"];
@@ -1048,7 +1053,7 @@ export class Wallet {
* that is currenctly available for spending in the wallet.
*/
getBalances(): Promise<any> {
- function collectBalances(c: Coin, byCurrency) {
+ function collectBalances(c: Coin, byCurrency: any) {
if (c.suspended) {
return byCurrency;
}
@@ -1074,7 +1079,7 @@ export class Wallet {
* Retrive the full event history for this wallet.
*/
getHistory(): Promise<any> {
- function collect(x, acc) {
+ function collect(x: any, acc: any) {
acc.push(x);
return acc;
}
@@ -1099,7 +1104,7 @@ export class Wallet {
[contract.merchant_pub, contract.repurchase_correlation_id])
.then((result: Transaction) => {
console.log("db result", result);
- let isRepurchase;
+ let isRepurchase: boolean;
if (result) {
console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id);
return {
diff --git a/lib/wallet/wxMessaging.ts b/lib/wallet/wxMessaging.ts
index a8dc27524..9c08b20ca 100644
--- a/lib/wallet/wxMessaging.ts
+++ b/lib/wallet/wxMessaging.ts
@@ -15,7 +15,13 @@
*/
-import {Wallet, Offer, Badge, ConfirmReserveRequest, CreateReserveRequest} from "./wallet";
+import {
+ Wallet,
+ Offer,
+ Badge,
+ ConfirmReserveRequest,
+ CreateReserveRequest
+} from "./wallet";
import {deleteDb, exportDb, openTalerDb} from "./db";
import {BrowserHttpLib} from "./http";
import {Checkable} from "./checkable";
@@ -48,11 +54,17 @@ function makeHandlers(db: IDBDatabase,
return exportDb(db);
},
["ping"]: function(detail, sender) {
- return Promise.resolve({});
+ if (!sender || !sender.tab || !sender.tab.id) {
+ return Promise.resolve();
+ }
+ let id: number = sender.tab.id;
+ let info: any = <any>paymentRequestCookies[id];
+ delete paymentRequestCookies[id];
+ return Promise.resolve(info);
},
["reset"]: function(detail, sender) {
if (db) {
- let tx = db.transaction(db.objectStoreNames, 'readwrite');
+ let tx = db.transaction(Array.from(db.objectStoreNames), 'readwrite');
for (let i = 0; i < db.objectStoreNames.length; i++) {
tx.objectStore(db.objectStoreNames[i]).clear();
}
@@ -81,7 +93,7 @@ function makeHandlers(db: IDBDatabase,
return wallet.confirmReserve(req);
},
["confirm-pay"]: function(detail, sender) {
- let offer;
+ let offer: Offer;
try {
offer = Offer.checked(detail.offer);
} catch (e) {
@@ -100,7 +112,7 @@ function makeHandlers(db: IDBDatabase,
return wallet.confirmPay(offer);
},
["check-pay"]: function(detail, sender) {
- let offer;
+ let offer: Offer;
try {
offer = Offer.checked(detail.offer);
} catch (e) {
@@ -173,14 +185,14 @@ class ChromeBadge implements Badge {
}
-function dispatch(handlers, req, sender, sendResponse) {
+function dispatch(handlers: any, req: any, sender: any, sendResponse: any) {
if (req.type in handlers) {
Promise
.resolve()
.then(() => {
const p = handlers[req.type](req.detail, sender);
- return p.then((r) => {
+ return p.then((r: any) => {
sendResponse(r);
})
})
@@ -231,12 +243,58 @@ class ChromeNotifier implements Notifier {
}
+/**
+ * Mapping from tab ID to payment information (if any).
+ */
+let paymentRequestCookies: {[n: number]: any} = {};
+
+function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[],
+ url: string, tabId: number): any {
+ const headers: {[s: string]: string} = {};
+ for (let kv of headerList) {
+ if (kv.value) {
+ headers[kv.name.toLowerCase()] = kv.value;
+ }
+ }
+
+ const contractUrl = headers["x-taler-contract-url"];
+ if (contractUrl !== undefined) {
+ paymentRequestCookies[tabId] = {type: "fetch", contractUrl};
+ return;
+ }
+
+ const contractHash = headers["x-taler-contract-hash"];
+
+ if (contractHash !== undefined) {
+ const payUrl = headers["x-taler-pay-url"];
+ if (payUrl === undefined) {
+ console.log("malformed 402, X-Taler-Pay-Url missing");
+ return;
+ }
+
+ // Offer URL is optional
+ const offerUrl = headers["x-taler-offer-url"];
+ paymentRequestCookies[tabId] = {
+ type: "execute",
+ offerUrl,
+ payUrl,
+ contractHash
+ };
+ return;
+ }
+
+ // looks like it's not a taler request, it might be
+ // for a different payment system (or the shop is buggy)
+ console.log("ignoring non-taler 402 response");
+}
+
+
export function wxMain() {
chrome.browserAction.setBadgeText({text: ""});
chrome.tabs.query({}, function(tabs) {
for (let tab of tabs) {
- if (!tab.url) {
+ if (!tab.url || !tab.id) {
return;
}
let uri = URI(tab.url);
@@ -255,11 +313,14 @@ export function wxMain() {
console.error("could not open database");
console.error(e);
})
- .then((db) => {
+ .then((db: IDBDatabase) => {
let http = new BrowserHttpLib();
let badge = new ChromeBadge();
let notifier = new ChromeNotifier();
let wallet = new Wallet(db, http, badge, notifier);
+
+ // Handlers for messages coming directly from the content
+ // script on the page
let handlers = makeHandlers(db, wallet);
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
try {
@@ -276,6 +337,19 @@ export function wxMain() {
return false;
}
});
+
+ // Handlers for catching HTTP requests
+ chrome.webRequest.onHeadersReceived.addListener((details) => {
+ if (details.statusCode != 402) {
+ return;
+ }
+ console.log(`got 402 from ${details.url}`);
+ return handleHttpPayment(details.responseHeaders || [],
+ details.url,
+ details.tabId);
+ }, {urls: ["<all_urls>"]}, ["responseHeaders", "blocking"]);
+
+
})
.catch((e) => {
console.error("could not initialize wallet messaging");