aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-10-20 01:37:00 +0200
committerFlorian Dold <florian.dold@gmail.com>2016-10-20 01:37:00 +0200
commitde65019ed6db789a12ee4b654b1de5890daa2186 (patch)
tree9f8bee4bc6744b64a8f999d01a9c3932aa5ed776
parent9ee0823b7e4a97a2b1812847eaabdf6cf846655e (diff)
downloadwallet-core-de65019ed6db789a12ee4b654b1de5890daa2186.tar.gz
wallet-core-de65019ed6db789a12ee4b654b1de5890daa2186.tar.bz2
wallet-core-de65019ed6db789a12ee4b654b1de5890daa2186.zip
make queries then-able
-rw-r--r--lib/decl/lib.es6.d.ts76
-rw-r--r--lib/wallet/query.ts50
-rw-r--r--lib/wallet/types.ts9
-rw-r--r--lib/wallet/wallet.ts63
-rw-r--r--popup/popup.tsx2
-rw-r--r--tsconfig.json1
6 files changed, 132 insertions, 69 deletions
diff --git a/lib/decl/lib.es6.d.ts b/lib/decl/lib.es6.d.ts
index fc08339de..084ed4e8a 100644
--- a/lib/decl/lib.es6.d.ts
+++ b/lib/decl/lib.es6.d.ts
@@ -20288,44 +20288,44 @@ declare function addEventListener(type: "mouseenter", listener: (this: Window, e
declare function addEventListener(type: "mouseleave", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mousemove", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: "mouseout", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "mouseover", listener: (this: Window, MouseEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "mouseup", listener: (this: Window, MouseEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "mousewheel", listener: (this: Window, WheelEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "offline", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "online", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "orientationchange", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pagehide", listener: (this: Window, PageTransitionEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pageshow", listener: (this: Window, PageTransitionEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pause", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "play", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "playing", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointercancel", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointerdown", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointerenter", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointerleave", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointermove", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointerout", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointerover", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "pointerup", listener: (this: Window, PointerEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "popstate", listener: (this: Window, PopStateEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "progress", listener: (this: Window, ProgressEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "ratechange", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "readystatechange", listener: (this: Window, ProgressEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "reset", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "resize", listener: (this: Window, UIEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "scroll", listener: (this: Window, UIEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "seeked", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "seeking", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "select", listener: (this: Window, UIEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "stalled", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "storage", listener: (this: Window, StorageEvent) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "submit", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "suspend", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "timeupdate", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "unload", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "volumechange", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "waiting", listener: (this: Window, Event) => any, useCapture?: boolean): void;
-declare function addEventListener(type: "wheel", listener: (this: Window, WheelEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "mouseover", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "mouseup", listener: (this: Window, ev: MouseEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "mousewheel", listener: (this: Window, ev: WheelEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "offline", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "online", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "orientationchange", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pagehide", listener: (this: Window, ev: PageTransitionEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pageshow", listener: (this: Window, ev: PageTransitionEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pause", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "play", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "playing", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointercancel", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointerdown", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointerenter", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointerleave", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointermove", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointerout", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointerover", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "pointerup", listener: (this: Window, ev: PointerEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "popstate", listener: (this: Window, ev: PopStateEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "progress", listener: (this: Window, ev: ProgressEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "ratechange", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "readystatechange", listener: (this: Window, ev: ProgressEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "reset", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "resize", listener: (this: Window, ev: UIEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "scroll", listener: (this: Window, ev: UIEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "seeked", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "seeking", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "select", listener: (this: Window, ev: UIEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "stalled", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "storage", listener: (this: Window, ev: StorageEvent) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "submit", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "suspend", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "timeupdate", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "unload", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "volumechange", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "waiting", listener: (this: Window, ev: Event) => any, useCapture?: boolean): void;
+declare function addEventListener(type: "wheel", listener: (this: Window, ev: WheelEvent) => any, useCapture?: boolean): void;
declare function addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
type AAGUID = string;
type AlgorithmIdentifier = string | Algorithm;
diff --git a/lib/wallet/query.ts b/lib/wallet/query.ts
index acf9aa44d..c172bbeb7 100644
--- a/lib/wallet/query.ts
+++ b/lib/wallet/query.ts
@@ -69,6 +69,8 @@ export interface QueryStream<T> {
map<S>(f: (x:T) => S): QueryStream<S>;
flatMap<S>(f: (x: T) => S[]): QueryStream<S>;
toArray(): Promise<T[]>;
+
+ then(onfulfill: any, onreject: any): any;
}
export let AbortTransaction = Symbol("abort_transaction");
@@ -92,7 +94,7 @@ function openPromise<T>() {
}
-abstract class QueryStreamBase<T> implements QueryStream<T> {
+abstract class QueryStreamBase<T> implements QueryStream<T>, PromiseLike<void> {
abstract subscribe(f: (isDone: boolean,
value: any,
tx: IDBTransaction) => void): void;
@@ -103,11 +105,15 @@ abstract class QueryStreamBase<T> implements QueryStream<T> {
this.root = root;
}
- flatMap<S>(f: (x: T) => T[]): QueryStream<S> {
- return new QueryStreamFlatMap(this, f);
+ then<R>(onfulfilled: (value: void) => R | PromiseLike<R>, onrejected: (reason: any) => R | PromiseLike<R>): PromiseLike<R> {
+ return this.root.then(onfulfilled, onrejected);
+ }
+
+ flatMap<S>(f: (x: T) => S[]): QueryStream<S> {
+ return new QueryStreamFlatMap<T,S>(this, f);
}
- map<S>(f: (x: T) => S): QueryStream<T> {
+ map<S>(f: (x: T) => S): QueryStream<S> {
return new QueryStreamMap(this, f);
}
@@ -193,11 +199,11 @@ class QueryStreamFilter<T> extends QueryStreamBase<T> {
}
-class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
+class QueryStreamFlatMap<T,S> extends QueryStreamBase<S> {
s: QueryStreamBase<T>;
- flatMapFn: (v: T) => T[];
+ flatMapFn: (v: T) => S[];
- constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => T[]) {
+ constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => S[]) {
super(s.root);
this.s = s;
this.flatMapFn = flatMapFn;
@@ -218,11 +224,11 @@ class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
}
-class QueryStreamMap<T> extends QueryStreamBase<T> {
- s: QueryStreamBase<T>;
- mapFn: (v: T) => T[];
+class QueryStreamMap<S,T> extends QueryStreamBase<T> {
+ s: QueryStreamBase<S>;
+ mapFn: (v: S) => T;
- constructor(s: QueryStreamBase<T>, mapFn: (v: T) => T[]) {
+ constructor(s: QueryStreamBase<S>, mapFn: (v: S) => T) {
super(s.root);
this.s = s;
this.mapFn = mapFn;
@@ -364,7 +370,7 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
}
-export class QueryRoot {
+export class QueryRoot implements PromiseLike<void> {
private work: ((t: IDBTransaction) => void)[] = [];
private db: IDBDatabase;
private stores = new Set();
@@ -376,18 +382,26 @@ export class QueryRoot {
*/
private hasWrite: boolean;
+ private finishScheduled: boolean;
+
constructor(db: IDBDatabase) {
this.db = db;
}
+ then<R>(onfulfilled: (value: void) => R | PromiseLike<R>, onrejected: (reason: any) => R | PromiseLike<R>): PromiseLike<R> {
+ return this.finish().then(onfulfilled, onrejected);
+ }
+
iter<T>(store: Store<T>): QueryStream<T> {
this.stores.add(store.name);
+ this.scheduleFinish();
return new IterQueryStream(this, store.name, {});
}
iterIndex<S extends IDBValidKey,T>(index: Index<S,T>,
only?: S): QueryStream<T> {
this.stores.add(index.storeName);
+ this.scheduleFinish();
return new IterQueryStream(this, index.storeName, {
only,
indexName: index.indexName
@@ -403,6 +417,7 @@ export class QueryRoot {
let doPut = (tx: IDBTransaction) => {
tx.objectStore(store.name).put(val);
};
+ this.scheduleFinish();
this.addWork(doPut, store.name, true);
return this;
}
@@ -427,6 +442,7 @@ export class QueryRoot {
tx.objectStore(store.name).put(m);
}
};
+ this.scheduleFinish();
this.addWork(doPut, store.name, true);
return this;
}
@@ -443,6 +459,7 @@ export class QueryRoot {
tx.objectStore(store.name).put(obj);
}
};
+ this.scheduleFinish();
this.addWork(doPutAll, store.name, true);
return this;
}
@@ -456,6 +473,7 @@ export class QueryRoot {
const doAdd = (tx: IDBTransaction) => {
tx.objectStore(store.name).add(val);
};
+ this.scheduleFinish();
this.addWork(doAdd, store.name, true);
return this;
}
@@ -509,6 +527,13 @@ export class QueryRoot {
.then(() => promise);
}
+ private scheduleFinish() {
+ if (!this.finishScheduled) {
+ Promise.resolve().then(() => this.finish());
+ this.finishScheduled = true;
+ }
+ }
+
/**
* Finish the query, and start the query in the first place if necessary.
*/
@@ -543,6 +568,7 @@ export class QueryRoot {
const doDelete = (tx: IDBTransaction) => {
tx.objectStore(storeName).delete(key);
};
+ this.scheduleFinish();
this.addWork(doDelete, storeName, true);
return this;
}
diff --git a/lib/wallet/types.ts b/lib/wallet/types.ts
index 2df8094a7..3d21f8229 100644
--- a/lib/wallet/types.ts
+++ b/lib/wallet/types.ts
@@ -64,10 +64,15 @@ export interface ReserveRecord {
* be higher than the requested_amount
*/
requested_amount: AmountJson,
+
+
/**
- * Amount we've already withdrawn from the reserve.
+ * What's the current amount that sits
+ * in precoins?
*/
- withdrawn_amount: AmountJson;
+ precoin_amount: AmountJson;
+
+
confirmed: boolean,
}
diff --git a/lib/wallet/wallet.ts b/lib/wallet/wallet.ts
index 53508cf59..d4c95a6f0 100644
--- a/lib/wallet/wallet.ts
+++ b/lib/wallet/wallet.ts
@@ -33,7 +33,7 @@ import {
import {HttpResponse, RequestException} from "./http";
import {QueryRoot, Store, Index, JoinResult, AbortTransaction} from "./query";
import {Checkable} from "./checkable";
-import {canonicalizeBaseUrl} from "./helpers";
+import {canonicalizeBaseUrl, amountToPretty} from "./helpers";
import {ReserveCreationInfo, Amounts} from "./types";
import {PreCoin} from "./types";
import {CryptoApi} from "./cryptoApi";
@@ -403,10 +403,13 @@ export class Wallet {
}
}
- updateExchanges(): void {
+ async updateExchanges(): Promise<void> {
console.log("updating exchanges");
- let exchangesUrls = this.q().iter(Stores.exchanges).map((e) => e.baseUrl);
+ let exchangesUrls = await this.q()
+ .iter(Stores.exchanges)
+ .map((e) => e.baseUrl)
+ .toArray();
for (let url of exchangesUrls) {
this.updateExchangeFromUrl(url)
@@ -766,18 +769,18 @@ export class Wallet {
const coin = await this.withdrawExecute(preCoin);
const mutateReserve = (r: ReserveRecord) => {
- let currentAmount = r.current_amount;
- if (!currentAmount) {
- throw Error("can't withdraw from reserve when current amount is" +
- " unknown");
- }
- let x = Amounts.sub(currentAmount,
+
+ console.log(`before committing coin: current ${amountToPretty(r.current_amount!)}, precoin: ${amountToPretty(
+ r.precoin_amount)})}`);
+
+ let x = Amounts.sub(r.precoin_amount,
preCoin.coinValue,
denom!.fee_withdraw);
if (x.saturated) {
+ console.error("database inconsistent");
throw AbortTransaction;
}
- r.current_amount = x.amount;
+ r.precoin_amount = x.amount;
return r;
};
@@ -791,7 +794,6 @@ export class Wallet {
};
await this.q()
- .put(Stores.precoins, preCoin)
.mutate(Stores.reserves, preCoin.reservePub, mutateReserve)
.delete("precoins", coin.coinPub)
.add(Stores.coins, coin)
@@ -828,7 +830,7 @@ export class Wallet {
current_amount: null,
requested_amount: req.amount,
confirmed: false,
- withdrawn_amount: Amounts.getZero(req.amount.currency)
+ precoin_amount: Amounts.getZero(req.amount.currency),
};
const historyEntry = {
@@ -940,17 +942,47 @@ export class Wallet {
/**
* Withdraw coins from a reserve until it is empty.
*/
- private async depleteReserve(reserve: any,
+ private async depleteReserve(reserve: ReserveRecord,
exchange: IExchangeInfo): Promise<number> {
+ if (!reserve.current_amount) {
+ throw Error("can't withdraw when amount is unknown");
+ }
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
- let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
+ let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount!,
denomsAvailable);
let ps = denomsForWithdraw.map(async(denom) => {
+ function mutateReserve(r: ReserveRecord): ReserveRecord {
+ let currentAmount = r.current_amount;
+ if (!currentAmount) {
+ throw Error("can't withdraw when amount is unknown");
+ }
+ r.precoin_amount = Amounts.add(r.precoin_amount,
+ denom.value,
+ denom.fee_withdraw).amount;
+ let result = Amounts.sub(currentAmount,
+ denom.value,
+ denom.fee_withdraw);
+ if (result.saturated) {
+ console.error("can't create precoin, saturated");
+ throw AbortTransaction;
+ }
+ r.current_amount = result.amount;
+
+ console.log(`after creating precoin: current ${amountToPretty(r.current_amount)}, precoin: ${amountToPretty(
+ r.precoin_amount)})}`);
+
+ return r;
+ }
+
let preCoin = await this.cryptoApi
.createPreCoin(denom, reserve);
+ await this.q()
+ .put(Stores.precoins, preCoin)
+ .mutate(Stores.reserves, reserve.reserve_pub, mutateReserve);
await this.processPreCoin(preCoin);
});
+
await Promise.all(ps);
return ps.length;
}
@@ -1219,6 +1251,7 @@ export class Wallet {
if (!amount) {
amount = r.requested_amount;
}
+ amount = Amounts.add(amount, r.precoin_amount).amount;
if (Amounts.cmp(smallestWithdraw[r.exchange_base_url], amount) < 0) {
entry.pendingIncoming = Amounts.add(entry.pendingIncoming,
amount).amount;
@@ -1333,7 +1366,7 @@ export class Wallet {
oldDenom.fee_refresh));
function mutateCoin(c: Coin): Coin {
- let r = Amounts.sub(coin.currentAmount,
+ let r = Amounts.sub(c.currentAmount,
refreshSession.valueWithFee);
if (r.saturated) {
// Something else must have written the coin value
diff --git a/popup/popup.tsx b/popup/popup.tsx
index d58aa1dd3..5e439417a 100644
--- a/popup/popup.tsx
+++ b/popup/popup.tsx
@@ -270,7 +270,7 @@ class WalletBalanceView extends preact.Component<any, any> {
return i18n`Error: could not retrieve balance information.`;
}
if (!wallet) {
- return this.renderEmpty();
+ return <span></span>;
}
console.log(wallet);
let listing = Object.keys(wallet).map((key) => {
diff --git a/tsconfig.json b/tsconfig.json
index e9efcb50c..de73f283d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -17,7 +17,6 @@
"test/tests/taler.ts",
"lib/refs.d.ts",
"lib/i18n.ts",
- "lib/shopApi.ts",
"lib/taler-wallet-lib.ts",
"lib/wallet/checkable.ts",
"lib/wallet/chromeBadge.ts",