aboutsummaryrefslogtreecommitdiff
path: root/lib/wallet/wallet.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-10-12 02:55:53 +0200
committerFlorian Dold <florian.dold@gmail.com>2016-10-12 02:55:53 +0200
commitd4be3906e32ac7d9933c6030d6493f2f2152bdd9 (patch)
tree3341586381b6975a3c1fa99ca69c63fba5ac9c35 /lib/wallet/wallet.ts
parentdbcd85451edfc33f2e0a42c431f0cf3ab3b12876 (diff)
downloadwallet-core-d4be3906e32ac7d9933c6030d6493f2f2152bdd9.tar.xz
tree view of wallet db
Diffstat (limited to 'lib/wallet/wallet.ts')
-rw-r--r--lib/wallet/wallet.ts253
1 files changed, 141 insertions, 112 deletions
diff --git a/lib/wallet/wallet.ts b/lib/wallet/wallet.ts
index 67288f666..337ed8255 100644
--- a/lib/wallet/wallet.ts
+++ b/lib/wallet/wallet.ts
@@ -29,19 +29,19 @@ import {
Notifier,
WireInfo
} from "./types";
-import {HttpResponse, RequestException} from "./http";
-import {Query} from "./query";
-import {Checkable} from "./checkable";
-import {canonicalizeBaseUrl} from "./helpers";
-import {ReserveCreationInfo, Amounts} from "./types";
-import {PreCoin} from "./types";
-import {Reserve} from "./types";
-import {CryptoApi} from "./cryptoApi";
-import {Coin} from "./types";
-import {PayCoinInfo} from "./types";
-import {CheckRepurchaseResult} from "./types";
-import {Contract} from "./types";
-import {ExchangeHandle} from "./types";
+import { HttpResponse, RequestException } from "./http";
+import { Query } from "./query";
+import { Checkable } from "./checkable";
+import { canonicalizeBaseUrl } from "./helpers";
+import { ReserveCreationInfo, Amounts } from "./types";
+import { PreCoin } from "./types";
+import { Reserve } from "./types";
+import { CryptoApi } from "./cryptoApi";
+import { Coin } from "./types";
+import { PayCoinInfo } from "./types";
+import { CheckRepurchaseResult } from "./types";
+import { Contract } from "./types";
+import { ExchangeHandle } from "./types";
"use strict";
@@ -55,11 +55,11 @@ interface ReserveRecord {
reserve_priv: string,
exchange_base_url: string,
created: number,
- last_query: number|null,
+ last_query: number | null,
/**
* Current amount left in the reserve
*/
- current_amount: AmountJson|null,
+ current_amount: AmountJson | null,
/**
* Amount requested when the reserve was created.
* When a reserve is re-used (rare!) the current_amount can
@@ -229,7 +229,7 @@ function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
}
-function getTalerStampSec(stamp: string): number|null {
+function getTalerStampSec(stamp: string): number | null {
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
if (!m) {
return null;
@@ -256,14 +256,14 @@ function isWithdrawableDenom(d: Denomination) {
interface HttpRequestLibrary {
req(method: string,
- url: string|uri.URI,
- options?: any): Promise<HttpResponse>;
+ url: string | uri.URI,
+ options?: any): Promise<HttpResponse>;
- get(url: string|uri.URI): Promise<HttpResponse>;
+ get(url: string | uri.URI): Promise<HttpResponse>;
- postJson(url: string|uri.URI, body: any): Promise<HttpResponse>;
+ postJson(url: string | uri.URI, body: any): Promise<HttpResponse>;
- postForm(url: string|uri.URI, form: any): Promise<HttpResponse>;
+ postForm(url: string | uri.URI, form: any): Promise<HttpResponse>;
}
@@ -288,7 +288,7 @@ interface KeyUpdateInfo {
* amount, but never larger.
*/
function getWithdrawDenomList(amountAvailable: AmountJson,
- denoms: Denomination[]): Denomination[] {
+ denoms: Denomination[]): Denomination[] {
let remaining = Amounts.copy(amountAvailable);
const ds: Denomination[] = [];
@@ -331,9 +331,9 @@ export class Wallet {
private runningOperations: Set<string> = new Set();
constructor(db: IDBDatabase,
- http: HttpRequestLibrary,
- badge: Badge,
- notifier: Notifier) {
+ http: HttpRequestLibrary,
+ badge: Badge,
+ notifier: Notifier) {
this.db = db;
this.http = http;
this.badge = badge;
@@ -363,9 +363,9 @@ export class Wallet {
.iter("exchanges")
.reduce((exchange: IExchangeInfo) => {
this.updateExchangeFromUrl(exchange.baseUrl)
- .catch((e) => {
- console.error("updating exchange failed", e);
- });
+ .catch((e) => {
+ console.error("updating exchange failed", e);
+ });
});
}
@@ -397,8 +397,8 @@ export class Wallet {
* but only if the sum the coins' remaining value exceeds the payment amount.
*/
private async getPossibleExchangeCoins(paymentAmount: AmountJson,
- depositFeeLimit: AmountJson,
- allowedExchanges: ExchangeHandle[]): Promise<ExchangeCoins> {
+ depositFeeLimit: AmountJson,
+ allowedExchanges: ExchangeHandle[]): Promise<ExchangeCoins> {
// Mapping from exchange base URL to list of coins together with their
// denomination
let m: ExchangeCoins = {};
@@ -411,9 +411,9 @@ export class Wallet {
let coin: Coin = mc[1];
if (coin.suspended) {
console.log("skipping suspended coin",
- coin.denomPub,
- "from exchange",
- exchange.baseUrl);
+ coin.denomPub,
+ "from exchange",
+ exchange.baseUrl);
return;
}
let denom = exchange.active_denoms.find((e) => e.denom_pub === coin.denomPub);
@@ -425,7 +425,7 @@ export class Wallet {
console.warn("same pubkey for different currencies");
return;
}
- let cd = {coin, denom};
+ let cd = { coin, denom };
let x = m[url];
if (!x) {
m[url] = [cd];
@@ -446,7 +446,7 @@ export class Wallet {
console.log("Checking for merchant's exchange", JSON.stringify(info));
return [
Query(this.db)
- .iter("exchanges", {indexName: "pubKey", only: info.master_pub})
+ .iter("exchanges", { indexName: "pubKey", only: info.master_pub })
.indexJoin("coins", "exchangeBaseUrl", (exchange) => exchange.baseUrl)
.reduce((x) => storeExchangeCoin(x, info.url))
];
@@ -467,38 +467,38 @@ export class Wallet {
// under depositFeeLimit
nextExchange:
- for (let key in m) {
- let coins = m[key];
- // Sort by ascending deposit fee
- coins.sort((o1, o2) => Amounts.cmp(o1.denom.fee_deposit,
- o2.denom.fee_deposit));
- let maxFee = Amounts.copy(depositFeeLimit);
- let minAmount = Amounts.copy(paymentAmount);
- let accFee = Amounts.copy(coins[0].denom.fee_deposit);
- let accAmount = Amounts.getZero(coins[0].coin.currentAmount.currency);
- let usableCoins: CoinWithDenom[] = [];
- nextCoin:
- for (let i = 0; i < coins.length; i++) {
- let coinAmount = Amounts.copy(coins[i].coin.currentAmount);
- let coinFee = coins[i].denom.fee_deposit;
- if (Amounts.cmp(coinAmount, coinFee) <= 0) {
- continue nextCoin;
- }
- accFee = Amounts.add(accFee, coinFee).amount;
- accAmount = Amounts.add(accAmount, coinAmount).amount;
- if (Amounts.cmp(accFee, maxFee) >= 0) {
- // FIXME: if the fees are too high, we have
- // to cover them ourselves ....
- console.log("too much fees");
- continue nextExchange;
- }
- usableCoins.push(coins[i]);
- if (Amounts.cmp(accAmount, minAmount) >= 0) {
- ret[key] = usableCoins;
- continue nextExchange;
- }
- }
+ for (let key in m) {
+ let coins = m[key];
+ // Sort by ascending deposit fee
+ coins.sort((o1, o2) => Amounts.cmp(o1.denom.fee_deposit,
+ o2.denom.fee_deposit));
+ let maxFee = Amounts.copy(depositFeeLimit);
+ let minAmount = Amounts.copy(paymentAmount);
+ let accFee = Amounts.copy(coins[0].denom.fee_deposit);
+ let accAmount = Amounts.getZero(coins[0].coin.currentAmount.currency);
+ let usableCoins: CoinWithDenom[] = [];
+ nextCoin:
+ for (let i = 0; i < coins.length; i++) {
+ let coinAmount = Amounts.copy(coins[i].coin.currentAmount);
+ let coinFee = coins[i].denom.fee_deposit;
+ if (Amounts.cmp(coinAmount, coinFee) <= 0) {
+ continue nextCoin;
+ }
+ accFee = Amounts.add(accFee, coinFee).amount;
+ accAmount = Amounts.add(accAmount, coinAmount).amount;
+ if (Amounts.cmp(accFee, maxFee) >= 0) {
+ // FIXME: if the fees are too high, we have
+ // to cover them ourselves ....
+ console.log("too much fees");
+ continue nextExchange;
+ }
+ usableCoins.push(coins[i]);
+ if (Amounts.cmp(accAmount, minAmount) >= 0) {
+ ret[key] = usableCoins;
+ continue nextExchange;
+ }
}
+ }
return ret;
}
@@ -508,8 +508,8 @@ export class Wallet {
* pay for a contract in the wallet's database.
*/
private async recordConfirmPay(offer: Offer,
- payCoinInfo: PayCoinInfo,
- chosenExchange: string): Promise<void> {
+ payCoinInfo: PayCoinInfo,
+ chosenExchange: string): Promise<void> {
let payReq: any = {};
payReq["amount"] = offer.contract.amount;
payReq["coins"] = payCoinInfo.map((x) => x.sig);
@@ -571,8 +571,8 @@ export class Wallet {
}
let mcs = await this.getPossibleExchangeCoins(offer.contract.amount,
- offer.contract.max_fee,
- offer.contract.exchanges);
+ offer.contract.max_fee,
+ offer.contract.exchanges);
if (Object.keys(mcs).length == 0) {
console.log("not confirming payment, insufficient coins");
@@ -584,8 +584,8 @@ export class Wallet {
let ds = await this.cryptoApi.signDeposit(offer, mcs[exchangeUrl]);
await this.recordConfirmPay(offer,
- ds,
- exchangeUrl);
+ ds,
+ exchangeUrl);
return {};
}
@@ -600,13 +600,13 @@ export class Wallet {
Query(this.db)
.get("transactions", offer.H_contract);
if (transaction) {
- return {isPayed: true};
+ return { isPayed: true };
}
// If not already payed, check if we could pay for it.
let mcs = await this.getPossibleExchangeCoins(offer.contract.amount,
- offer.contract.max_fee,
- offer.contract.exchanges);
+ offer.contract.max_fee,
+ offer.contract.exchanges);
if (Object.keys(mcs).length == 0) {
console.log("not confirming payment, insufficient coins");
@@ -614,7 +614,7 @@ export class Wallet {
error: "coins-insufficient",
};
}
- return {isPayed: false};
+ return { isPayed: false };
}
@@ -645,14 +645,14 @@ export class Wallet {
* then deplete the reserve, withdrawing coins until it is empty.
*/
private async processReserve(reserveRecord: ReserveRecord,
- retryDelayMs: number = 250): Promise<void> {
+ retryDelayMs: number = 250): Promise<void> {
const opId = "reserve-" + reserveRecord.reserve_pub;
this.startOperation(opId);
try {
let exchange = await this.updateExchangeFromUrl(reserveRecord.exchange_base_url);
let reserve = await this.updateReserve(reserveRecord.reserve_pub,
- exchange);
+ exchange);
let n = await this.depleteReserve(reserve, exchange);
if (n != 0) {
@@ -672,10 +672,10 @@ export class Wallet {
} catch (e) {
// random, exponential backoff truncated at 3 minutes
let nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * Math.random(),
- 3000 * 60);
+ 3000 * 60);
console.warn(`Failed to deplete reserve, trying again in ${retryDelayMs} ms`);
setTimeout(() => this.processReserve(reserveRecord, nextDelay),
- retryDelayMs);
+ retryDelayMs);
} finally {
this.stopOperation(opId);
}
@@ -683,18 +683,18 @@ export class Wallet {
private async processPreCoin(preCoin: PreCoin,
- retryDelayMs = 100): Promise<void> {
+ retryDelayMs = 100): Promise<void> {
try {
const coin = await this.withdrawExecute(preCoin);
this.storeCoin(coin);
} catch (e) {
console.error("Failed to withdraw coin from precoin, retrying in",
- retryDelayMs,
- "ms", e);
+ retryDelayMs,
+ "ms", e);
// exponential backoff truncated at one minute
let nextRetryDelayMs = Math.min(retryDelayMs * 2, 1000 * 60);
setTimeout(() => this.processPreCoin(preCoin, nextRetryDelayMs),
- retryDelayMs);
+ retryDelayMs);
}
}
@@ -801,8 +801,8 @@ export class Wallet {
}
let r = JSON.parse(resp.responseText);
let denomSig = await this.cryptoApi.rsaUnblind(r.ev_sig,
- pc.blindingKey,
- pc.denomPub);
+ pc.blindingKey,
+ pc.denomPub);
let coin: Coin = {
coinPub: pc.coinPub,
coinPriv: pc.coinPriv,
@@ -840,7 +840,7 @@ export class Wallet {
private async withdraw(denom: Denomination, reserve: Reserve): Promise<void> {
console.log("creating pre coin at", new Date());
let preCoin = await this.cryptoApi
- .createPreCoin(denom, reserve);
+ .createPreCoin(denom, reserve);
await Query(this.db)
.put("precoins", preCoin)
.finish();
@@ -852,10 +852,10 @@ export class Wallet {
* Withdraw coins from a reserve until it is empty.
*/
private async depleteReserve(reserve: any,
- exchange: IExchangeInfo): Promise<number> {
+ exchange: IExchangeInfo): Promise<number> {
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
- denomsAvailable);
+ denomsAvailable);
let ps = denomsForWithdraw.map((denom) => this.withdraw(denom, reserve));
await Promise.all(ps);
@@ -868,11 +868,11 @@ export class Wallet {
* by quering the reserve's exchange.
*/
private async updateReserve(reservePub: string,
- exchange: IExchangeInfo): Promise<Reserve> {
+ exchange: IExchangeInfo): Promise<Reserve> {
let reserve = await Query(this.db)
.get("reserves", reservePub);
let reqUrl = URI("reserve/status").absoluteTo(exchange.baseUrl);
- reqUrl.query({'reserve_pub': reservePub});
+ reqUrl.query({ 'reserve_pub': reservePub });
let resp = await this.http.get(reqUrl);
if (resp.status != 200) {
throw Error();
@@ -922,18 +922,18 @@ export class Wallet {
}
async getReserveCreationInfo(baseUrl: string,
- amount: AmountJson): Promise<ReserveCreationInfo> {
+ amount: AmountJson): Promise<ReserveCreationInfo> {
let exchangeInfo = await this.updateExchangeFromUrl(baseUrl);
let selectedDenoms = getWithdrawDenomList(amount,
- exchangeInfo.active_denoms);
+ exchangeInfo.active_denoms);
let acc = Amounts.getZero(amount.currency);
for (let d of selectedDenoms) {
acc = Amounts.add(acc, d.fee_withdraw).amount;
}
let actualCoinCost = selectedDenoms
.map((d: Denomination) => Amounts.add(d.value,
- d.fee_withdraw).amount)
+ d.fee_withdraw).amount)
.reduce((a, b) => Amounts.add(a, b).amount);
let wireInfo = await this.getWireInfo(baseUrl);
@@ -968,7 +968,7 @@ export class Wallet {
private async suspendCoins(exchangeInfo: IExchangeInfo): Promise<void> {
let suspendedCoins = await Query(this.db)
.iter("coins",
- {indexName: "exchangeBaseUrl", only: exchangeInfo.baseUrl})
+ { indexName: "exchangeBaseUrl", only: exchangeInfo.baseUrl })
.reduce((coin: Coin, suspendedCoins: Coin[]) => {
if (!exchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) {
return Array.prototype.concat(suspendedCoins, [coin]);
@@ -987,7 +987,7 @@ export class Wallet {
private async updateExchangeFromJson(baseUrl: string,
- exchangeKeysJson: KeysJson): Promise<IExchangeInfo> {
+ exchangeKeysJson: KeysJson): Promise<IExchangeInfo> {
const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
if (updateTimeSec === null) {
throw Error("invalid update time");
@@ -1016,7 +1016,7 @@ export class Wallet {
}
let updatedExchangeInfo = await this.updateExchangeInfo(exchangeInfo,
- exchangeKeysJson);
+ exchangeKeysJson);
await this.suspendCoins(updatedExchangeInfo);
await Query(this.db)
@@ -1028,7 +1028,7 @@ export class Wallet {
private async updateExchangeInfo(exchangeInfo: IExchangeInfo,
- newKeys: KeysJson): Promise<IExchangeInfo> {
+ newKeys: KeysJson): Promise<IExchangeInfo> {
if (exchangeInfo.masterPublicKey != newKeys.master_public_key) {
throw Error("public keys do not match");
}
@@ -1064,15 +1064,15 @@ export class Wallet {
return true;
});
- let ps = denomsToCheck.map(async(denom) => {
+ let ps = denomsToCheck.map(async (denom) => {
let valid = await this.cryptoApi
- .isValidDenom(denom,
- exchangeInfo.masterPublicKey);
+ .isValidDenom(denom,
+ exchangeInfo.masterPublicKey);
if (!valid) {
console.error("invalid denomination",
- denom,
- "with key",
- exchangeInfo.masterPublicKey);
+ denom,
+ "with key",
+ exchangeInfo.masterPublicKey);
// FIXME: report to auditors
}
exchangeInfo.active_denoms.push(denom);
@@ -1099,7 +1099,7 @@ export class Wallet {
acc = Amounts.getZero(c.currentAmount.currency);
}
byCurrency[c.currentAmount.currency] = Amounts.add(c.currentAmount,
- acc).amount;
+ acc).amount;
return byCurrency;
}
@@ -1107,7 +1107,7 @@ export class Wallet {
.iter("coins")
.reduce(collectBalances, {});
- return {balances: byCurrency};
+ return { balances: byCurrency };
}
@@ -1122,12 +1122,41 @@ export class Wallet {
let history = await
Query(this.db)
- .iter("history", {indexName: "timestamp"})
+ .iter("history", { indexName: "timestamp" })
.reduce(collect, []);
- return {history};
+ return { history };
+ }
+
+ async getExchanges(): Promise<IExchangeInfo[]> {
+ return Query(this.db)
+ .iter<IExchangeInfo>("exchanges")
+ .flatMap((e) => [e])
+ .toArray();
}
+ async getReserves(exchangeBaseUrl: string): Promise<Reserve[]> {
+ return Query(this.db)
+ .iter<Reserve>("reserves")
+ .filter((r: Reserve) => r.exchange_base_url === exchangeBaseUrl)
+ .toArray();
+ }
+
+ async getCoins(exchangeBaseUrl: string): Promise<Coin[]> {
+ return Query(this.db)
+ .iter<Coin>("coins")
+ .filter((c: Coin) => c.exchangeBaseUrl === exchangeBaseUrl)
+ .toArray();
+ }
+
+ async getPreCoins(exchangeBaseUrl: string): Promise<PreCoin[]> {
+ return Query(this.db)
+ .iter<PreCoin>("precoins")
+ .filter((c: PreCoin) => c.exchangeBaseUrl === exchangeBaseUrl)
+ .toArray();
+ }
+
+
async hashContract(contract: any): Promise<string> {
return this.cryptoApi.hashString(canonicalJson(contract));
}
@@ -1138,12 +1167,12 @@ export class Wallet {
async checkRepurchase(contract: Contract): Promise<CheckRepurchaseResult> {
if (!contract.repurchase_correlation_id) {
console.log("no repurchase: no correlation id");
- return {isRepurchase: false};
+ return { isRepurchase: false };
}
let result: Transaction = await Query(this.db)
.getIndexed("transactions",
- "repurchase",
- [contract.merchant_pub, contract.repurchase_correlation_id]);
+ "repurchase",
+ [contract.merchant_pub, contract.repurchase_correlation_id]);
if (result) {
console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id);
@@ -1153,7 +1182,7 @@ export class Wallet {
existingFulfillmentUrl: result.contract.fulfillment_url,
};
} else {
- return {isRepurchase: false};
+ return { isRepurchase: false };
}
}
}