diff options
Diffstat (limited to 'src/wallet.ts')
-rw-r--r-- | src/wallet.ts | 842 |
1 files changed, 428 insertions, 414 deletions
diff --git a/src/wallet.ts b/src/wallet.ts index a2a9e83f5..b21cdbd96 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -22,57 +22,58 @@ /** * Imports. */ +import {Checkable} from "./checkable"; +import {CryptoApi} from "./crypto/cryptoApi"; +import { + amountToPretty, + canonicalJson, + canonicalizeBaseUrl, + deepEquals, + flatMap, + getTalerStampSec, +} from "./helpers"; +import { + HttpRequestLibrary, + HttpResponse, + RequestException, +} from "./http"; +import { + AbortTransaction, + Index, + JoinLeftResult, + JoinResult, + QueryRoot, + Store, +} from "./query"; import { AmountJson, Amounts, - CoinRecord, + Auditor, + AuditorRecord, CoinPaySig, + CoinRecord, + CoinStatus, Contract, CreateReserveResponse, + CurrencyRecord, Denomination, + DenominationRecord, + DenominationStatus, ExchangeHandle, ExchangeRecord, + ExchangeWireFeesRecord, Notifier, PayCoinInfo, + PaybackConfirmation, PreCoinRecord, RefreshSessionRecord, ReserveCreationInfo, ReserveRecord, - CurrencyRecord, - Auditor, - AuditorRecord, WalletBalance, WalletBalanceEntry, WireFee, - ExchangeWireFeesRecord, WireInfo, - DenominationRecord, - DenominationStatus, - CoinStatus, - PaybackConfirmation, } from "./types"; -import { - HttpRequestLibrary, - HttpResponse, - RequestException, -} from "./http"; -import { - AbortTransaction, - Index, - JoinResult, - QueryRoot, - Store, JoinLeftResult, -} from "./query"; -import {Checkable} from "./checkable"; -import { - amountToPretty, - canonicalizeBaseUrl, - canonicalJson, - deepEquals, - flatMap, - getTalerStampSec, -} from "./helpers"; -import {CryptoApi} from "./crypto/cryptoApi"; import URI = require("urijs"); @@ -272,33 +273,31 @@ export interface ConfigRecord { } - const builtinCurrencies: CurrencyRecord[] = [ { - name: "KUDOS", - fractionalDigits: 2, auditors: [ { + auditorPub: "XN9KMN5G2KGPCAN0E89MM5HE8FV4WBWA9KDTMTDR817MWBCYA7H0", baseUrl: "https://auditor.demo.taler.net/", expirationStamp: (new Date(2027, 1)).getTime(), - auditorPub: "XN9KMN5G2KGPCAN0E89MM5HE8FV4WBWA9KDTMTDR817MWBCYA7H0", }, ], exchanges: [], + fractionalDigits: 2, + name: "KUDOS", }, { - name: "PUDOS", - fractionalDigits: 2, auditors: [ ], exchanges: [ { baseUrl: "https://exchange.test.taler.net/", priority: 0 }, ], + fractionalDigits: 2, + name: "PUDOS", }, ]; - // FIXME: these functions should be dependency-injected // into the wallet, as this is chrome specific => bad @@ -312,17 +311,17 @@ function setInterval(f: any, t: number) { function isWithdrawableDenom(d: DenominationRecord) { - const now_sec = (new Date).getTime() / 1000; - const stamp_withdraw_sec = getTalerStampSec(d.stampExpireWithdraw); - if (stamp_withdraw_sec == null) { + const nowSec = (new Date()).getTime() / 1000; + const stampWithdrawSec = getTalerStampSec(d.stampExpireWithdraw); + if (stampWithdrawSec === null) { return false; } - const stamp_start_sec = getTalerStampSec(d.stampStart); - if (stamp_start_sec == null) { + const stampStartSec = getTalerStampSec(d.stampStart); + if (stampStartSec === null) { return false; } // Withdraw if still possible to withdraw within a minute - if ((stamp_withdraw_sec + 60 > now_sec) && (now_sec >= stamp_start_sec)) { + if ((stampWithdrawSec + 60 > nowSec) && (nowSec >= stampStartSec)) { return true; } return false; @@ -333,31 +332,30 @@ export type CoinSelectionResult = {exchangeUrl: string, cds: CoinWithDenom[]}|un export function selectCoins(cds: CoinWithDenom[], paymentAmount: AmountJson, depositFeeLimit: AmountJson): CoinWithDenom[]|undefined { - if (cds.length == 0) { + if (cds.length === 0) { return undefined; } // Sort by ascending deposit fee cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit, o2.denom.feeDeposit)); - let currency = cds[0].denom.value.currency; - let cdsResult: CoinWithDenom[] = []; + const currency = cds[0].denom.value.currency; + const cdsResult: CoinWithDenom[] = []; let accFee: AmountJson = Amounts.getZero(currency); let accAmount: AmountJson = Amounts.getZero(currency); let isBelowFee = false; let coversAmount = false; let coversAmountWithFee = false; - for (let i = 0; i < cds.length; i++) { - let {coin, denom} = cds[i]; + for (const {coin, denom} of cds) { if (coin.suspended) { continue; } - if (coin.status != CoinStatus.Fresh) { + if (coin.status !== CoinStatus.Fresh) { continue; } if (Amounts.cmp(denom.feeDeposit, coin.currentAmount) >= 0) { continue; } - cdsResult.push(cds[i]); + cdsResult.push({coin, denom}); accFee = Amounts.add(denom.feeDeposit, accFee).amount; accAmount = Amounts.add(coin.currentAmount, accAmount).amount; coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0; @@ -391,8 +389,8 @@ function getWithdrawDenomList(amountAvailable: AmountJson, // is useful ... for (let i = 0; i < 1000; i++) { let found = false; - for (let d of denoms) { - let cost = Amounts.add(d.value, d.feeWithdraw).amount; + for (const d of denoms) { + const cost = Amounts.add(d.value, d.feeWithdraw).amount; if (Amounts.cmp(remaining, cost) < 0) { continue; } @@ -415,7 +413,7 @@ export namespace Stores { super("exchanges", {keyPath: "baseUrl"}); } - pubKeyIndex = new Index<string,ExchangeRecord>(this, "pubKey", "masterPublicKey"); + pubKeyIndex = new Index<string, ExchangeRecord>(this, "pubKey", "masterPublicKey"); } class NonceStore extends Store<NonceRecord> { @@ -429,26 +427,26 @@ export namespace Stores { super("coins", {keyPath: "coinPub"}); } - exchangeBaseUrlIndex = new Index<string,CoinRecord>(this, "exchangeBaseUrl", "exchangeBaseUrl"); - denomPubIndex = new Index<string,CoinRecord>(this, "denomPub", "denomPub"); + exchangeBaseUrlIndex = new Index<string, CoinRecord>(this, "exchangeBaseUrl", "exchangeBaseUrl"); + denomPubIndex = new Index<string, CoinRecord>(this, "denomPub", "denomPub"); } class HistoryStore extends Store<HistoryRecord> { constructor() { super("history", { + autoIncrement: true, keyPath: "id", - autoIncrement: true }); } - timestampIndex = new Index<number,HistoryRecord>(this, "timestamp", "timestamp"); + timestampIndex = new Index<number, HistoryRecord>(this, "timestamp", "timestamp"); } class OffersStore extends Store<OfferRecord> { constructor() { super("offers", { + autoIncrement: true, keyPath: "id", - autoIncrement: true }); } } @@ -458,8 +456,8 @@ export namespace Stores { super("transactions", {keyPath: "contractHash"}); } - fulfillmentUrlIndex = new Index<string,TransactionRecord>(this, "fulfillment_url", "contract.fulfillment_url"); - orderIdIndex = new Index<string,TransactionRecord>(this, "order_id", "contract.order_id"); + fulfillmentUrlIndex = new Index<string, TransactionRecord>(this, "fulfillment_url", "contract.fulfillment_url"); + orderIdIndex = new Index<string, TransactionRecord>(this, "order_id", "contract.order_id"); } class DenominationsStore extends Store<DenominationRecord> { @@ -469,7 +467,7 @@ export namespace Stores { {keyPath: ["exchangeBaseUrl", "denomPub"] as any as IDBKeyPath}); } - denomPubHashIndex = new Index<string,DenominationRecord>(this, "denomPubHash", "denomPubHash"); + denomPubHashIndex = new Index<string, DenominationRecord>(this, "denomPubHash", "denomPubHash"); exchangeBaseUrlIndex = new Index<string, DenominationRecord>(this, "exchangeBaseUrl", "exchangeBaseUrl"); denomPubIndex = new Index<string, DenominationRecord>(this, "denomPub", "denomPub"); } @@ -491,19 +489,31 @@ export namespace Stores { super("exchangeWireFees", {keyPath: "exchangeBaseUrl"}); } } - export const exchanges: ExchangeStore = new ExchangeStore(); - export const exchangeWireFees: ExchangeWireFeesStore = new ExchangeWireFeesStore(); - export const nonces: NonceStore = new NonceStore(); - export const transactions: TransactionsStore = new TransactionsStore(); - export const reserves: Store<ReserveRecord> = new Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"}); - export const coins: CoinsStore = new CoinsStore(); - export const refresh: Store<RefreshSessionRecord> = new Store<RefreshSessionRecord>("refresh", {keyPath: "meltCoinPub"}); - export const history: HistoryStore = new HistoryStore(); - export const offers: OffersStore = new OffersStore(); - export const precoins: Store<PreCoinRecord> = new Store<PreCoinRecord>("precoins", {keyPath: "coinPub"}); - export const denominations: DenominationsStore = new DenominationsStore(); - export const currencies: CurrenciesStore = new CurrenciesStore(); - export const config: ConfigStore = new ConfigStore(); + export const exchanges = new ExchangeStore(); + export const exchangeWireFees = new ExchangeWireFeesStore(); + export const nonces = new NonceStore(); + export const transactions = new TransactionsStore(); + export const reserves = new Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"}); + export const coins = new CoinsStore(); + export const refresh = new Store<RefreshSessionRecord>("refresh", {keyPath: "meltCoinPub"}); + export const history = new HistoryStore(); + export const offers = new OffersStore(); + export const precoins = new Store<PreCoinRecord>("precoins", {keyPath: "coinPub"}); + export const denominations = new DenominationsStore(); + export const currencies = new CurrenciesStore(); + export const config = new ConfigStore(); +} + + +interface CoinsForPaymentArgs { + allowedAuditors: Auditor[]; + allowedExchanges: ExchangeHandle[]; + depositFeeLimit: AmountJson; + paymentAmount: AmountJson; + wireFeeAmortization: number; + wireFeeLimit: AmountJson; + wireFeeTime: number; + wireMethod: string; } @@ -543,10 +553,10 @@ export class Wallet { } private async fillDefaults() { - let onTrue = (r: QueryRoot) => { + const onTrue = (r: QueryRoot) => { console.log("defaults already applied"); }; - let onFalse = (r: QueryRoot) => { + const onFalse = (r: QueryRoot) => { console.log("applying defaults"); r.put(Stores.config, {key: "currencyDefaultsApplied", value: true}) .putAll(Stores.currencies, builtinCurrencies) @@ -555,7 +565,7 @@ export class Wallet { await ( this.q() .iter(Stores.config) - .filter(x => x.key == "currencyDefaultsApplied") + .filter((x) => x.key === "currencyDefaultsApplied") .first() .cond((x) => x && x.value, onTrue, onFalse) ); @@ -569,7 +579,7 @@ export class Wallet { private stopOperation(operationId: string) { this.runningOperations.delete(operationId); - if (this.runningOperations.size == 0) { + if (this.runningOperations.size === 0) { this.badge.stopBusy(); } } @@ -577,12 +587,12 @@ export class Wallet { async updateExchanges(): Promise<void> { console.log("updating exchanges"); - let exchangesUrls = await this.q() - .iter(Stores.exchanges) - .map((e) => e.baseUrl) - .toArray(); + const exchangesUrls = await this.q() + .iter(Stores.exchanges) + .map((e) => e.baseUrl) + .toArray(); - for (let url of exchangesUrls) { + for (const url of exchangesUrls) { this.updateExchangeFromUrl(url) .catch((e) => { console.error("updating exchange failed", e); @@ -621,7 +631,7 @@ export class Wallet { this.q() .iter(Stores.coins) .reduce((c: CoinRecord) => { - if (c.status == CoinStatus.Dirty) { + if (c.status === CoinStatus.Dirty) { console.log("resuming pending refresh for coin", c); this.refresh(c.coinPub); } @@ -633,23 +643,28 @@ export class Wallet { * Get exchanges and associated coins that are still spendable, * but only if the sum the coins' remaining value exceeds the payment amount. */ - private async getCoinsForPayment(paymentAmount: AmountJson, - wireMethod: string, - wireFeeTime: number, - depositFeeLimit: AmountJson, - wireFeeLimit: AmountJson, - wireFeeAmortization: number, - allowedExchanges: ExchangeHandle[], - allowedAuditors: Auditor[]): Promise<CoinSelectionResult> { - - let exchanges = await this.q().iter(Stores.exchanges).toArray(); - - for (let exchange of exchanges) { + private async getCoinsForPayment(args: CoinsForPaymentArgs): Promise<CoinSelectionResult> { + const { + allowedAuditors, + allowedExchanges, + depositFeeLimit, + paymentAmount, + wireFeeAmortization, + wireFeeLimit, + wireFeeTime, + wireMethod, + } = args; + + let remainingAmount = paymentAmount; + + const exchanges = await this.q().iter(Stores.exchanges).toArray(); + + for (const exchange of exchanges) { let isOkay: boolean = false; // is the exchange explicitly allowed? - for (let allowedExchange of allowedExchanges) { - if (allowedExchange.master_pub == exchange.masterPublicKey) { + for (const allowedExchange of allowedExchanges) { + if (allowedExchange.master_pub === exchange.masterPublicKey) { isOkay = true; break; } @@ -657,9 +672,9 @@ export class Wallet { // is the exchange allowed because of one of its auditors? if (!isOkay) { - for (let allowedAuditor of allowedAuditors) { - for (let auditor of exchange.auditors) { - if (auditor.auditor_pub == allowedAuditor.auditor_pub) { + for (const allowedAuditor of allowedAuditors) { + for (const auditor of exchange.auditors) { + if (auditor.auditor_pub === allowedAuditor.auditor_pub) { isOkay = true; break; } @@ -674,53 +689,52 @@ export class Wallet { continue; } - let coins: CoinRecord[] = await this.q() + const coins: CoinRecord[] = await this.q() .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl) .toArray(); - if (!coins || coins.length == 0) { + if (!coins || coins.length === 0) { continue; } // Denomination of the first coin, we assume that all other // coins have the same currency - let firstDenom = await this.q().get(Stores.denominations, - [ - exchange.baseUrl, - coins[0].denomPub - ]); + const firstDenom = await this.q().get(Stores.denominations, + [ + exchange.baseUrl, + coins[0].denomPub, + ]); if (!firstDenom) { throw Error("db inconsistent"); } - let currency = firstDenom.value.currency; - let cds: CoinWithDenom[] = []; - for (let i = 0; i < coins.length; i++) { - let coin = coins[i]; - let denom = await this.q().get(Stores.denominations, + const currency = firstDenom.value.currency; + const cds: CoinWithDenom[] = []; + for (const coin of coins) { + const denom = await this.q().get(Stores.denominations, [exchange.baseUrl, coin.denomPub]); if (!denom) { throw Error("db inconsistent"); } - if (denom.value.currency != currency) { + if (denom.value.currency !== currency) { console.warn(`same pubkey for different currencies at exchange ${exchange.baseUrl}`); continue; } if (coin.suspended) { continue; } - if (coin.status != CoinStatus.Fresh) { + if (coin.status !== CoinStatus.Fresh) { continue; } cds.push({coin, denom}); } - let fees = await this.q().get(Stores.exchangeWireFees, exchange.baseUrl); + const fees = await this.q().get(Stores.exchangeWireFees, exchange.baseUrl); if (!fees) { console.error("no fees found for exchange", exchange); continue; } - let wireFee: AmountJson|undefined = undefined; - for (let fee of (fees.feesForType[wireMethod] || [])) { + let wireFee: AmountJson|undefined; + for (const fee of (fees.feesForType[wireMethod] || [])) { if (fee.startStamp >= wireFeeTime && fee.endStamp <= wireFeeTime) { wireFee = fee.wireFee; break; @@ -728,18 +742,18 @@ export class Wallet { } if (wireFee) { - let amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization); + const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization); if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) { - paymentAmount = Amounts.add(amortizedWireFee, paymentAmount).amount; + remainingAmount = Amounts.add(amortizedWireFee, remainingAmount).amount; } } - let res = selectCoins(cds, paymentAmount, depositFeeLimit); + const res = selectCoins(cds, remainingAmount, depositFeeLimit); if (res) { return { - exchangeUrl: exchange.baseUrl, cds: res, - } + exchangeUrl: exchange.baseUrl, + }; } } return undefined; @@ -753,31 +767,31 @@ export class Wallet { private async recordConfirmPay(offer: OfferRecord, payCoinInfo: PayCoinInfo, chosenExchange: string): Promise<void> { - let payReq: PayReq = { + const payReq: PayReq = { coins: payCoinInfo.map((x) => x.sig), + exchange: chosenExchange, merchant_pub: offer.contract.merchant_pub, order_id: offer.contract.order_id, - exchange: chosenExchange, }; - let t: TransactionRecord = { - contractHash: offer.H_contract, + const t: TransactionRecord = { contract: offer.contract, - payReq: payReq, - merchantSig: offer.merchant_sig, + contractHash: offer.H_contract, finished: false, + merchantSig: offer.merchant_sig, + payReq, }; - let historyEntry: HistoryRecord = { - type: "pay", - timestamp: (new Date).getTime(), - subjectId: `contract-${offer.H_contract}`, + const historyEntry: HistoryRecord = { detail: { - merchantName: offer.contract.merchant.name, amount: offer.contract.amount, contractHash: offer.H_contract, fulfillmentUrl: offer.contract.fulfillment_url, + merchantName: offer.contract.merchant.name, }, - level: HistoryLevel.User + level: HistoryLevel.User, + subjectId: `contract-${offer.H_contract}`, + timestamp: (new Date()).getTime(), + type: "pay", }; await this.q() @@ -798,7 +812,7 @@ export class Wallet { async saveOffer(offer: OfferRecord): Promise<number> { console.log(`saving offer in wallet.ts`); - let id = await this.q().putWithResult(Stores.offers, offer); + const id = await this.q().putWithResult(Stores.offers, offer); this.notifier.notify(); console.log(`saved offer with id ${id}`); if (typeof id !== "number") { @@ -815,21 +829,23 @@ export class Wallet { async confirmPay(offer: OfferRecord): Promise<any> { console.log("executing confirmPay"); - let transaction = await this.q().get(Stores.transactions, offer.H_contract); + const transaction = await this.q().get(Stores.transactions, offer.H_contract); if (transaction) { // Already payed ... return {}; } - let res = await this.getCoinsForPayment(offer.contract.amount, - offer.contract.wire_method, - getTalerStampSec(offer.contract.timestamp) || 0, - offer.contract.max_fee, - offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), - offer.contract.wire_fee_amortization || 1, - offer.contract.exchanges, - offer.contract.auditors); + const res = await this.getCoinsForPayment({ + allowedAuditors: offer.contract.auditors, + allowedExchanges: offer.contract.exchanges, + depositFeeLimit: offer.contract.max_fee, + paymentAmount: offer.contract.amount, + wireFeeAmortization: offer.contract.wire_fee_amortization || 1, + wireFeeLimit: offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), + wireFeeTime: getTalerStampSec(offer.contract.timestamp) || 0, + wireMethod: offer.contract.wire_method, + }); console.log("max_fee", offer.contract.max_fee); console.log("coin selection result", res); @@ -840,9 +856,9 @@ export class Wallet { error: "coins-insufficient", }; } - let {exchangeUrl, cds} = res; + const {exchangeUrl, cds} = res; - let ds = await this.cryptoApi.signDeposit(offer, cds); + const ds = await this.cryptoApi.signDeposit(offer, cds); await this.recordConfirmPay(offer, ds, exchangeUrl); @@ -856,20 +872,22 @@ export class Wallet { */ async checkPay(offer: OfferRecord): Promise<any> { // First check if we already payed for it. - let transaction = await this.q().get(Stores.transactions, offer.H_contract); + const transaction = await this.q().get(Stores.transactions, offer.H_contract); if (transaction) { return {isPayed: true}; } // If not already payed, check if we could pay for it. - let res = await this.getCoinsForPayment(offer.contract.amount, - offer.contract.wire_method, - getTalerStampSec(offer.contract.timestamp) || 0, - offer.contract.max_fee, - offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), - offer.contract.wire_fee_amortization || 1, - offer.contract.exchanges, - offer.contract.auditors); + const res = await this.getCoinsForPayment({ + allowedAuditors: offer.contract.auditors, + allowedExchanges: offer.contract.exchanges, + depositFeeLimit: offer.contract.max_fee, + paymentAmount: offer.contract.amount, + wireFeeAmortization: offer.contract.wire_fee_amortization || 1, + wireFeeLimit: offer.contract.max_wire_fee || Amounts.getZero(offer.contract.amount.currency), + wireFeeTime: getTalerStampSec(offer.contract.timestamp) || 0, + wireMethod: offer.contract.wire_method, + }); if (!res) { console.log("not confirming payment, insufficient coins"); @@ -894,14 +912,14 @@ export class Wallet { console.log("query for payment failed"); return { success: false, - } + }; } console.log("query for payment succeeded:", t); - let resp = { - success: true, - payReq: t.payReq, + const resp = { H_contract: t.contractHash, contract: t.contract, + payReq: t.payReq, + success: true, }; return resp; } @@ -917,29 +935,28 @@ export class Wallet { this.startOperation(opId); try { - let exchange = await this.updateExchangeFromUrl(reserveRecord.exchange_base_url); - let reserve = await this.updateReserve(reserveRecord.reserve_pub); - let n = await this.depleteReserve(reserve); + const exchange = await this.updateExchangeFromUrl(reserveRecord.exchange_base_url); + const reserve = await this.updateReserve(reserveRecord.reserve_pub); + const n = await this.depleteReserve(reserve); - if (n != 0) { - let depleted: HistoryRecord = { - type: "depleted-reserve", - subjectId: `reserve-progress-${reserveRecord.reserve_pub}`, - timestamp: (new Date).getTime(), + if (n !== 0) { + const depleted: HistoryRecord = { detail: { + currentAmount: reserveRecord.current_amount, exchangeBaseUrl: reserveRecord.exchange_base_url, - reservePub: reserveRecord.reserve_pub, requestedAmount: reserveRecord.requested_amount, - currentAmount: reserveRecord.current_amount, + reservePub: reserveRecord.reserve_pub, }, - level: HistoryLevel.User + level: HistoryLevel.User, + subjectId: `reserve-progress-${reserveRecord.reserve_pub}`, + timestamp: (new Date()).getTime(), + type: "depleted-reserve", }; await this.q().put(Stores.history, depleted).finish(); } } catch (e) { // random, exponential backoff truncated at 3 minutes - let nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * Math.random(), - 3000 * 60); + const nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * Math.random(), 3000 * 60); console.warn(`Failed to deplete reserve, trying again in ${retryDelayMs} ms`); setTimeout(() => this.processReserve(reserveRecord, nextDelay), retryDelayMs); @@ -980,9 +997,7 @@ export class Wallet { 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.feeWithdraw); + const x = Amounts.sub(r.precoin_amount, preCoin.coinValue, denom.feeWithdraw); if (x.saturated) { console.error("database inconsistent"); throw AbortTransaction; @@ -992,12 +1007,12 @@ export class Wallet { }; const historyEntry: HistoryRecord = { - type: "withdraw", - timestamp: (new Date).getTime(), - level: HistoryLevel.Expert, detail: { coinPub: coin.coinPub, - } + }, + level: HistoryLevel.Expert, + timestamp: (new Date()).getTime(), + type: "withdraw", }; await this.q() @@ -1013,10 +1028,12 @@ export class Wallet { retryDelayMs, "ms", e); // exponential backoff truncated at one minute - let nextRetryDelayMs = Math.min(retryDelayMs * 2, 5 * 60 * 1000); + const nextRetryDelayMs = Math.min(retryDelayMs * 2, 5 * 60 * 1000); setTimeout(() => this.processPreCoin(preCoin, nextRetryDelayMs), retryDelayMs); - this.processPreCoinThrottle[preCoin.exchangeBaseUrl] = (this.processPreCoinThrottle[preCoin.exchangeBaseUrl] || 0) + 1; + + const currentThrottle = this.processPreCoinThrottle[preCoin.exchangeBaseUrl] || 0; + this.processPreCoinThrottle[preCoin.exchangeBaseUrl] = currentThrottle + 1; setTimeout(() => {this.processPreCoinThrottle[preCoin.exchangeBaseUrl]--; }, retryDelayMs); } finally { this.processPreCoinConcurrent--; @@ -1031,44 +1048,44 @@ export class Wallet { * audited nor trusted already. */ async createReserve(req: CreateReserveRequest): Promise<CreateReserveResponse> { - let keypair = await this.cryptoApi.createEddsaKeypair(); - const now = (new Date).getTime(); + const keypair = await this.cryptoApi.createEddsaKeypair(); + const now = (new Date()).getTime(); const canonExchange = canonicalizeBaseUrl(req.exchange); const reserveRecord: ReserveRecord = { - hasPayback: false, - reserve_pub: keypair.pub, - reserve_priv: keypair.priv, - exchange_base_url: canonExchange, + confirmed: false, created: now, - last_query: null, current_amount: null, - requested_amount: req.amount, - confirmed: false, + exchange_base_url: canonExchange, + hasPayback: false, + last_query: null, precoin_amount: Amounts.getZero(req.amount.currency), + requested_amount: req.amount, + reserve_priv: keypair.priv, + reserve_pub: keypair.pub, }; const historyEntry = { - type: "create-reserve", - level: HistoryLevel.Expert, - timestamp: now, - subjectId: `reserve-progress-${reserveRecord.reserve_pub}`, detail: { requestedAmount: req.amount, reservePub: reserveRecord.reserve_pub, - } + }, + level: HistoryLevel.Expert, + subjectId: `reserve-progress-${reserveRecord.reserve_pub}`, + timestamp: now, + type: "create-reserve", }; - let exchangeInfo = await this.updateExchangeFromUrl(req.exchange); - let {isAudited, isTrusted} = await this.getExchangeTrust(exchangeInfo); + const exchangeInfo = await this.updateExchangeFromUrl(req.exchange); + const {isAudited, isTrusted} = await this.getExchangeTrust(exchangeInfo); let currencyRecord = await this.q().get(Stores.currencies, exchangeInfo.currency); if (!currencyRecord) { currencyRecord = { - name: exchangeInfo.currency, - fractionalDigits: 2, - exchanges: [], auditors: [], - } + exchanges: [], + fractionalDigits: 2, + name: exchangeInfo.currency, + }; } if (!isAudited && !isTrusted) { @@ -1081,7 +1098,7 @@ export class Wallet { .put(Stores.history, historyEntry) .finish(); - let r: CreateReserveResponse = { + const r: CreateReserveResponse = { exchange: canonExchange, reservePub: keypair.pub, }; @@ -1099,8 +1116,8 @@ export class Wallet { * an unconfirmed reserve should be hidden. */ async confirmReserve(req: ConfirmReserveRequest): Promise<void> { - const now = (new Date).getTime(); - let reserve: ReserveRecord|undefined = await ( + const now = (new Date()).getTime(); + const reserve: ReserveRecord|undefined = await ( this.q().get<ReserveRecord>(Stores.reserves, req.reservePub)); if (!reserve) { @@ -1109,15 +1126,15 @@ export class Wallet { } console.log("reserve confirmed"); const historyEntry: HistoryRecord = { - type: "confirm-reserve", - timestamp: now, - subjectId: `reserve-progress-${reserve.reserve_pub}`, detail: { exchangeBaseUrl: reserve.exchange_base_url, - reservePub: req.reservePub, requestedAmount: reserve.requested_amount, + reservePub: req.reservePub, }, level: HistoryLevel.User, + subjectId: `reserve-progress-${reserve.reserve_pub}`, + timestamp: now, + type: "confirm-reserve", }; reserve.confirmed = true; await this.q() @@ -1131,40 +1148,40 @@ export class Wallet { private async withdrawExecute(pc: PreCoinRecord): Promise<CoinRecord> { - let reserve = await this.q().get<ReserveRecord>(Stores.reserves, + const reserve = await this.q().get<ReserveRecord>(Stores.reserves, pc.reservePub); if (!reserve) { throw Error("db inconsistent"); } - let wd: any = {}; + const wd: any = {}; wd.denom_pub = pc.denomPub; wd.reserve_pub = pc.reservePub; wd.reserve_sig = pc.withdrawSig; wd.coin_ev = pc.coinEv; - let reqUrl = (new URI("reserve/withdraw")).absoluteTo(reserve.exchange_base_url); - let resp = await this.http.postJson(reqUrl.href(), wd); + const reqUrl = (new URI("reserve/withdraw")).absoluteTo(reserve.exchange_base_url); + const resp = await this.http.postJson(reqUrl.href(), wd); - if (resp.status != 200) { + if (resp.status !== 200) { throw new RequestException({ hint: "Withdrawal failed", - status: resp.status + status: resp.status, }); } - let r = JSON.parse(resp.responseText); - let denomSig = await this.cryptoApi.rsaUnblind(r.ev_sig, + const r = JSON.parse(resp.responseText); + const denomSig = await this.cryptoApi.rsaUnblind(r.ev_sig, pc.blindingKey, pc.denomPub); - let coin: CoinRecord = { - reservePub: pc.reservePub, - coinPub: pc.coinPub, - coinPriv: pc.coinPriv, - denomPub: pc.denomPub, - denomSig: denomSig, + const coin: CoinRecord = { blindingKey: pc.blindingKey, + coinPriv: pc.coinPriv, + coinPub: pc.coinPub, currentAmount: pc.coinValue, + denomPub: pc.denomPub, + denomSig, exchangeBaseUrl: pc.exchangeBaseUrl, + reservePub: pc.reservePub, status: CoinStatus.Fresh, }; return coin; @@ -1179,25 +1196,24 @@ export class Wallet { if (!reserve.current_amount) { throw Error("can't withdraw when amount is unknown"); } - let currentAmount = reserve.current_amount; - if (!currentAmount) { + const withdrawAmount = reserve.current_amount; + if (!withdrawAmount) { throw Error("can't withdraw when amount is unknown"); } - let denomsForWithdraw = await this.getVerifiedWithdrawDenomList(reserve.exchange_base_url, - currentAmount); + const denomsForWithdraw = await this.getVerifiedWithdrawDenomList(reserve.exchange_base_url, withdrawAmount); console.log(`withdrawing ${denomsForWithdraw.length} coins`); - let ps = denomsForWithdraw.map(async(denom) => { + const ps = denomsForWithdraw.map(async(denom) => { function mutateReserve(r: ReserveRecord): ReserveRecord { - let currentAmount = r.current_amount; + const 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.feeWithdraw).amount; - let result = Amounts.sub(currentAmount, + const result = Amounts.sub(currentAmount, denom.value, denom.feeWithdraw); if (result.saturated) { @@ -1212,7 +1228,7 @@ export class Wallet { return r; } - let preCoin = await this.cryptoApi + const preCoin = await this.cryptoApi .createPreCoin(denom, reserve); await this.q() .put(Stores.precoins, preCoin) @@ -1230,34 +1246,34 @@ export class Wallet { * by quering the reserve's exchange. */ private async updateReserve(reservePub: string): Promise<ReserveRecord> { - let reserve = await this.q() + const reserve = await this.q() .get<ReserveRecord>(Stores.reserves, reservePub); if (!reserve) { throw Error("reserve not in db"); } - let reqUrl = new URI("reserve/status").absoluteTo(reserve.exchange_base_url); - reqUrl.query({'reserve_pub': reservePub}); - let resp = await this.http.get(reqUrl.href()); - if (resp.status != 200) { + const reqUrl = new URI("reserve/status").absoluteTo(reserve.exchange_base_url); + reqUrl.query({reserve_pub: reservePub}); + const resp = await this.http.get(reqUrl.href()); + if (resp.status !== 200) { throw Error(); } - let reserveInfo = JSON.parse(resp.responseText); + const reserveInfo = JSON.parse(resp.responseText); if (!reserveInfo) { throw Error(); } - let oldAmount = reserve.current_amount; - let newAmount = reserveInfo.balance; + const oldAmount = reserve.current_amount; + const newAmount = reserveInfo.balance; reserve.current_amount = reserveInfo.balance; - let historyEntry = { - type: "reserve-update", - timestamp: (new Date).getTime(), - subjectId: `reserve-progress-${reserve.reserve_pub}`, + const historyEntry = { detail: { - reservePub, - requestedAmount: reserve.requested_amount, + newAmount, oldAmount, - newAmount - } + requestedAmount: reserve.requested_amount, + reservePub, + }, + subjectId: `reserve-progress-${reserve.reserve_pub}`, + timestamp: (new Date()).getTime(), + type: "reserve-update", }; await this.q() .put(Stores.reserves, reserve) @@ -1272,16 +1288,16 @@ export class Wallet { */ async getWireInfo(exchangeBaseUrl: string): Promise<WireInfo> { exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl); - let reqUrl = new URI("wire").absoluteTo(exchangeBaseUrl); - let resp = await this.http.get(reqUrl.href()); + const reqUrl = new URI("wire").absoluteTo(exchangeBaseUrl); + const resp = await this.http.get(reqUrl.href()); - if (resp.status != 200) { + if (resp.status !== 200) { throw Error("/wire request failed"); } - let wiJson = JSON.parse(resp.responseText); + const wiJson = JSON.parse(resp.responseText); if (!wiJson) { - throw Error("/wire response malformed") + throw Error("/wire response malformed"); } return wiJson; } @@ -1290,7 +1306,7 @@ export class Wallet { return ( this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl) - .filter((d) => d.status == DenominationStatus.Unverified || d.status == DenominationStatus.VerifiedGood) + .filter((d) => d.status === DenominationStatus.Unverified || d.status === DenominationStatus.VerifiedGood) .toArray() ); } @@ -1312,7 +1328,7 @@ export class Wallet { const possibleDenoms = await ( this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl) - .filter((d) => d.status == DenominationStatus.Unverified || d.status == DenominationStatus.VerifiedGood) + .filter((d) => d.status === DenominationStatus.Unverified || d.status === DenominationStatus.VerifiedGood) .toArray() ); @@ -1323,12 +1339,12 @@ export class Wallet { do { allValid = true; - let nextPossibleDenoms = []; + const nextPossibleDenoms = []; selectedDenoms = getWithdrawDenomList(amount, possibleDenoms); - for (let denom of selectedDenoms || []) { - if (denom.status == DenominationStatus.Unverified) { + for (const denom of selectedDenoms || []) { + if (denom.status === DenominationStatus.Unverified) { console.log(`verifying denom ${denom.denomPub.substr(0, 15)}`); - let valid = await this.cryptoApi.isValidDenom(denom, + const valid = await this.cryptoApi.isValidDenom(denom, exchange.masterPublicKey); if (!valid) { denom.status = DenominationStatus.VerifiedBad; @@ -1349,24 +1365,23 @@ export class Wallet { } - /** * Check if and how an exchange is trusted and/or audited. */ async getExchangeTrust(exchangeInfo: ExchangeRecord): Promise<{isTrusted: boolean, isAudited: boolean}> { let isTrusted = false; let isAudited = false; - let currencyRecord = await this.q().get(Stores.currencies, exchangeInfo.currency); + const currencyRecord = await this.q().get(Stores.currencies, exchangeInfo.currency); if (currencyRecord) { - for (let trustedExchange of currencyRecord.exchanges) { - if (trustedExchange.baseUrl == exchangeInfo.baseUrl) { + for (const trustedExchange of currencyRecord.exchanges) { + if (trustedExchange.baseUrl === exchangeInfo.baseUrl) { isTrusted = true; break; } } - for (let trustedAuditor of currencyRecord.auditors) { - for (let exchangeAuditor of exchangeInfo.auditors) { - if (trustedAuditor.baseUrl == exchangeAuditor.url) { + for (const trustedAuditor of currencyRecord.auditors) { + for (const exchangeAuditor of exchangeInfo.auditors) { + if (trustedAuditor.baseUrl === exchangeAuditor.url) { isAudited = true; break; } @@ -1378,47 +1393,47 @@ export class Wallet { async getReserveCreationInfo(baseUrl: string, amount: AmountJson): Promise<ReserveCreationInfo> { - let exchangeInfo = await this.updateExchangeFromUrl(baseUrl); + const exchangeInfo = await this.updateExchangeFromUrl(baseUrl); - let selectedDenoms = await this.getVerifiedWithdrawDenomList(baseUrl, + const selectedDenoms = await this.getVerifiedWithdrawDenomList(baseUrl, amount); let acc = Amounts.getZero(amount.currency); - for (let d of selectedDenoms) { + for (const d of selectedDenoms) { acc = Amounts.add(acc, d.feeWithdraw).amount; } - let actualCoinCost = selectedDenoms + const actualCoinCost = selectedDenoms .map((d: DenominationRecord) => Amounts.add(d.value, d.feeWithdraw).amount) .reduce((a, b) => Amounts.add(a, b).amount); - let wireInfo = await this.getWireInfo(baseUrl); + const wireInfo = await this.getWireInfo(baseUrl); - let wireFees = await this.q().get(Stores.exchangeWireFees, baseUrl); + const wireFees = await this.q().get(Stores.exchangeWireFees, baseUrl); if (!wireFees) { // should never happen unless DB is inconsistent throw Error(`no wire fees found for exchange ${baseUrl}`); } - let {isTrusted, isAudited} = await this.getExchangeTrust(exchangeInfo); + const {isTrusted, isAudited} = await this.getExchangeTrust(exchangeInfo); - let earliestDepositExpiration = Infinity;; - for (let denom of selectedDenoms) { - let expireDeposit = getTalerStampSec(denom.stampExpireDeposit)!; + let earliestDepositExpiration = Infinity; + for (const denom of selectedDenoms) { + const expireDeposit = getTalerStampSec(denom.stampExpireDeposit)!; if (expireDeposit < earliestDepositExpiration) { earliestDepositExpiration = expireDeposit; } } - let ret: ReserveCreationInfo = { + const ret: ReserveCreationInfo = { + earliestDepositExpiration, exchangeInfo, - selectedDenoms, - wireInfo, - wireFees, isAudited, isTrusted, - withdrawFee: acc, - earliestDepositExpiration, overhead: Amounts.sub(amount, actualCoinCost).amount, + selectedDenoms, + wireFees, + wireInfo, + withdrawFee: acc, }; return ret; } @@ -1431,24 +1446,24 @@ export class Wallet { */ async updateExchangeFromUrl(baseUrl: string): Promise<ExchangeRecord> { baseUrl = canonicalizeBaseUrl(baseUrl); - let keysUrl = new URI("keys").absoluteTo(baseUrl); - let wireUrl = new URI("wire").absoluteTo(baseUrl); - let keysResp = await this.http.get(keysUrl.href()); - if (keysResp.status != 200) { + const keysUrl = new URI("keys").absoluteTo(baseUrl); + const wireUrl = new URI("wire").absoluteTo(baseUrl); + const keysResp = await this.http.get(keysUrl.href()); + if (keysResp.status !== 200) { throw Error("/keys request failed"); } - let wireResp = await this.http.get(wireUrl.href()); - if (wireResp.status != 200) { + const wireResp = await this.http.get(wireUrl.href()); + if (wireResp.status !== 200) { throw Error("/wire request failed"); } - let exchangeKeysJson = KeysJson.checked(JSON.parse(keysResp.responseText)); - let wireRespJson = JSON.parse(wireResp.responseText); + const exchangeKeysJson = KeysJson.checked(JSON.parse(keysResp.responseText)); + const wireRespJson = JSON.parse(wireResp.responseText); if (typeof wireRespJson !== "object") { throw Error("/wire response is not an object"); } console.log("exchange wire", wireRespJson); - let wireMethodDetails: WireDetailJson[] = []; - for (let methodName in wireRespJson) { + const wireMethodDetails: WireDetailJson[] = []; + for (const methodName in wireRespJson) { wireMethodDetails.push(WireDetailJson.checked(wireRespJson[methodName])); } return this.updateExchangeFromJson(baseUrl, exchangeKeysJson, wireMethodDetails); @@ -1456,12 +1471,12 @@ export class Wallet { private async suspendCoins(exchangeInfo: ExchangeRecord): Promise<void> { - let suspendedCoins = await ( + const resultSuspendedCoins = await ( this.q() .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeInfo.baseUrl) .indexJoinLeft(Stores.denominations.exchangeBaseUrlIndex, (e) => e.exchangeBaseUrl) - .reduce((cd: JoinLeftResult<CoinRecord,DenominationRecord>, + .reduce((cd: JoinLeftResult<CoinRecord, DenominationRecord>, suspendedCoins: CoinRecord[]) => { if ((!cd.right) || (!cd.right.isOffered)) { return Array.prototype.concat(suspendedCoins, [cd.left]); @@ -1469,8 +1484,8 @@ export class Wallet { return Array.prototype.concat(suspendedCoins); }, [])); - let q = this.q(); - suspendedCoins.map((c) => { + const q = this.q(); + resultSuspendedCoins.map((c) => { console.log("suspending coin", c); c.suspended = true; q.put(Stores.coins, c); @@ -1489,7 +1504,7 @@ export class Wallet { throw Error("invalid update time"); } - if (exchangeKeysJson.denoms.length == 0) { + if (exchangeKeysJson.denoms.length === 0) { throw Error("exchange doesn't offer any denominations"); } @@ -1499,24 +1514,24 @@ export class Wallet { if (!r) { exchangeInfo = { + auditors: exchangeKeysJson.auditors, baseUrl, + currency: exchangeKeysJson.denoms[0].value.currency, lastUpdateTime: updateTimeSec, masterPublicKey: exchangeKeysJson.master_public_key, - auditors: exchangeKeysJson.auditors, - currency: exchangeKeysJson.denoms[0].value.currency, }; console.log("making fresh exchange"); } else { if (updateTimeSec < r.lastUpdateTime) { console.log("outdated /keys, not updating"); - return r + return r; } exchangeInfo = r; exchangeInfo.lastUpdateTime = updateTimeSec; console.log("updating old exchange"); } - let updatedExchangeInfo = await this.updateExchangeInfo(exchangeInfo, + const updatedExchangeInfo = await this.updateExchangeInfo(exchangeInfo, exchangeKeysJson); await this.suspendCoins(updatedExchangeInfo); @@ -1532,37 +1547,37 @@ export class Wallet { }; } - for (let detail of wireMethodDetails) { + for (const detail of wireMethodDetails) { let latestFeeStamp = 0; - let fees = oldWireFees.feesForType[detail.type] || []; + const fees = oldWireFees.feesForType[detail.type] || []; oldWireFees.feesForType[detail.type] = fees; - for (let oldFee of fees) { + for (const oldFee of fees) { if (oldFee.endStamp > latestFeeStamp) { latestFeeStamp = oldFee.endStamp; } } - for (let fee of detail.fees) { - let start = getTalerStampSec(fee.start_date); - if (start == null) { + for (const fee of detail.fees) { + const start = getTalerStampSec(fee.start_date); + if (start === null) { console.error("invalid start stamp in fee", fee); continue; } if (start < latestFeeStamp) { continue; } - let end = getTalerStampSec(fee.end_date); - if (end == null) { + const end = getTalerStampSec(fee.end_date); + if (end === null) { console.error("invalid end stamp in fee", fee); continue; } - let wf: WireFee = { - wireFee: fee.wire_fee, + const wf: WireFee = { closingFee: fee.closing_fee, + endStamp: end, sig: fee.sig, startStamp: start, - endStamp: end, - } - let valid: boolean = await this.cryptoApi.isValidWireFee(detail.type, wf, exchangeInfo.masterPublicKey); + wireFee: fee.wire_fee, + }; + const valid: boolean = await this.cryptoApi.isValidWireFee(detail.type, wf, exchangeInfo.masterPublicKey); if (!valid) { console.error("fee signature invalid", fee); throw Error("fee signature invalid"); @@ -1574,14 +1589,14 @@ export class Wallet { await this.q().put(Stores.exchangeWireFees, oldWireFees); if (exchangeKeysJson.payback) { - for (let payback of exchangeKeysJson.payback) { - let denom = await this.q().getIndexed(Stores.denominations.denomPubHashIndex, payback.h_denom_pub); + for (const payback of exchangeKeysJson.payback) { + const denom = await this.q().getIndexed(Stores.denominations.denomPubHashIndex, payback.h_denom_pub); if (!denom) { continue; } console.log(`cashing back denom`, denom); - let coins = await this.q().iterIndex(Stores.coins.denomPubIndex, denom.denomPub).toArray(); - for (let coin of coins) { + const coins = await this.q().iterIndex(Stores.coins.denomPubIndex, denom.denomPub).toArray(); + for (const coin of coins) { this.payback(coin.coinPub); } } @@ -1593,7 +1608,7 @@ export class Wallet { private async updateExchangeInfo(exchangeInfo: ExchangeRecord, newKeys: KeysJson): Promise<ExchangeRecord> { - if (exchangeInfo.masterPublicKey != newKeys.master_public_key) { + if (exchangeInfo.masterPublicKey !== newKeys.master_public_key) { throw Error("public keys do not match"); } @@ -1608,17 +1623,17 @@ export class Wallet { const newDenoms: typeof existingDenoms = {}; const newAndUnseenDenoms: typeof existingDenoms = {}; - for (let d of newKeys.denoms) { - let dr = await this.denominationRecordFromKeys(exchangeInfo.baseUrl, d); + for (const d of newKeys.denoms) { + const dr = await this.denominationRecordFromKeys(exchangeInfo.baseUrl, d); if (!(d.denom_pub in existingDenoms)) { newAndUnseenDenoms[dr.denomPub] = dr; } newDenoms[dr.denomPub] = dr; } - for (let oldDenomPub in existingDenoms) { + for (const oldDenomPub in existingDenoms) { if (!(oldDenomPub in newDenoms)) { - let d = existingDenoms[oldDenomPub]; + const d = existingDenoms[oldDenomPub]; d.isOffered = false; } } @@ -1640,13 +1655,13 @@ export class Wallet { async getBalances(): Promise<WalletBalance> { function ensureEntry(balance: WalletBalance, currency: string) { let entry: WalletBalanceEntry|undefined = balance[currency]; - let z = Amounts.getZero(currency); + const z = Amounts.getZero(currency); if (!entry) { balance[currency] = entry = { available: z, + paybackAmount: z, pendingIncoming: z, pendingPayment: z, - paybackAmount: z, }; } return entry; @@ -1656,11 +1671,11 @@ export class Wallet { if (c.suspended) { return balance; } - if (!(c.status == CoinStatus.Dirty || c.status == CoinStatus.Fresh)) { + if (!(c.status === CoinStatus.Dirty || c.status === CoinStatus.Fresh)) { return balance; } - let currency = c.currentAmount.currency; - let entry = ensureEntry(balance, currency); + const currency = c.currentAmount.currency; + const entry = ensureEntry(balance, currency); entry.available = Amounts.add(entry.available, c.currentAmount).amount; return balance; } @@ -1669,7 +1684,7 @@ export class Wallet { if (!r.confirmed) { return balance; } - let entry = ensureEntry(balance, r.requested_amount.currency); + const entry = ensureEntry(balance, r.requested_amount.currency); let amount = r.current_amount; if (!amount) { amount = r.requested_amount; @@ -1686,7 +1701,7 @@ export class Wallet { if (!r.hasPayback) { return balance; } - let entry = ensureEntry(balance, r.requested_amount.currency); + const entry = ensureEntry(balance, r.requested_amount.currency); if (Amounts.cmp(smallestWithdraw[r.exchange_base_url], r.current_amount!) < 0) { entry.paybackAmount = Amounts.add(entry.paybackAmount, r.current_amount!).amount; } @@ -1700,7 +1715,7 @@ export class Wallet { if (r.finished) { return balance; } - let entry = ensureEntry(balance, r.valueWithFee.currency); + const entry = ensureEntry(balance, r.valueWithFee.currency); entry.pendingIncoming = Amounts.add(entry.pendingIncoming, r.valueOutput).amount; @@ -1711,7 +1726,7 @@ export class Wallet { if (t.finished) { return balance; } - let entry = ensureEntry(balance, t.contract.amount.currency); + const entry = ensureEntry(balance, t.contract.amount.currency); entry.pendingPayment = Amounts.add(entry.pendingPayment, t.contract.amount).amount; @@ -1721,7 +1736,7 @@ export class Wallet { function collectSmallestWithdraw(e: JoinResult<ExchangeRecord, DenominationRecord>, sw: any) { let min = sw[e.left.baseUrl]; - let v = Amounts.add(e.right.value, e.right.feeWithdraw).amount; + const v = Amounts.add(e.right.value, e.right.feeWithdraw).amount; if (!min) { min = v; } else if (Amounts.cmp(v, min) < 0) { @@ -1731,7 +1746,7 @@ export class Wallet { return sw; } - let balance = {}; + const balance = {}; // Mapping from exchange pub to smallest // possible amount we can withdraw let smallestWithdraw: {[baseUrl: string]: AmountJson} = {}; @@ -1742,7 +1757,7 @@ export class Wallet { (x) => x.baseUrl) .reduce(collectSmallestWithdraw, {})); - let tx = this.q(); + const tx = this.q(); tx.iter(Stores.coins) .reduce(collectBalances, balance); tx.iter(Stores.refresh) @@ -1760,52 +1775,52 @@ export class Wallet { async createRefreshSession(oldCoinPub: string): Promise<RefreshSessionRecord|undefined> { - let coin = await this.q().get<CoinRecord>(Stores.coins, oldCoinPub); + const coin = await this.q().get<CoinRecord>(Stores.coins, oldCoinPub); if (!coin) { throw Error("coin not found"); } - if (coin.currentAmount.value == 0 && coin.currentAmount.fraction == 0) { + if (coin.currentAmount.value === 0 && coin.currentAmount.fraction === 0) { return undefined; } - let exchange = await this.updateExchangeFromUrl(coin.exchangeBaseUrl); + const exchange = await this.updateExchangeFromUrl(coin.exchangeBaseUrl); if (!exchange) { throw Error("db inconsistent"); } - let oldDenom = await this.q().get(Stores.denominations, + const oldDenom = await this.q().get(Stores.denominations, [exchange.baseUrl, coin.denomPub]); if (!oldDenom) { throw Error("db inconsistent"); } - let availableDenoms: DenominationRecord[] = await ( + const availableDenoms: DenominationRecord[] = await ( this.q() .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl) .toArray() ); - let availableAmount = Amounts.sub(coin.currentAmount, + const availableAmount = Amounts.sub(coin.currentAmount, oldDenom.feeRefresh).amount; - let newCoinDenoms = getWithdrawDenomList(availableAmount, + const newCoinDenoms = getWithdrawDenomList(availableAmount, availableDenoms); console.log("refreshing coin", coin); console.log("refreshing into", newCoinDenoms); - if (newCoinDenoms.length == 0) { + if (newCoinDenoms.length === 0) { console.log(`not refreshing, available amount ${amountToPretty(availableAmount)} too small`); return undefined; } - let refreshSession: RefreshSessionRecord = await ( + const refreshSession: RefreshSessionRecord = await ( this.cryptoApi.createRefreshSession(exchange.baseUrl, 3, coin, @@ -1813,7 +1828,7 @@ export class Wallet { oldDenom.feeRefresh)); function mutateCoin(c: CoinRecord): CoinRecord { - let r = Amounts.sub(c.currentAmount, + const r = Amounts.sub(c.currentAmount, refreshSession.valueWithFee); if (r.saturated) { // Something else must have written the coin value @@ -1837,7 +1852,7 @@ export class Wallet { async refresh(oldCoinPub: string): Promise<void> { let refreshSession: RefreshSessionRecord|undefined; - let oldSession = await this.q().get(Stores.refresh, oldCoinPub); + const oldSession = await this.q().get(Stores.refresh, oldCoinPub); if (oldSession) { console.log("got old session for", oldCoinPub); console.log(oldSession); @@ -1858,9 +1873,9 @@ export class Wallet { return; } if (typeof refreshSession.norevealIndex !== "number") { - let coinPub = refreshSession.meltCoinPub; + const coinPub = refreshSession.meltCoinPub; await this.refreshMelt(refreshSession); - let r = await this.q().get<RefreshSessionRecord>(Stores.refresh, coinPub); + const r = await this.q().get<RefreshSessionRecord>(Stores.refresh, coinPub); if (!r) { throw Error("refresh session does not exist anymore"); } @@ -1872,53 +1887,53 @@ export class Wallet { async refreshMelt(refreshSession: RefreshSessionRecord): Promise<void> { - if (refreshSession.norevealIndex != undefined) { + if (refreshSession.norevealIndex !== undefined) { console.error("won't melt again"); return; } - let coin = await this.q().get<CoinRecord>(Stores.coins, + const coin = await this.q().get<CoinRecord>(Stores.coins, refreshSession.meltCoinPub); if (!coin) { console.error("can't melt coin, it does not exist"); return; } - let reqUrl = new URI("refresh/melt").absoluteTo(refreshSession.exchangeBaseUrl); - let meltCoin = { + const reqUrl = new URI("refresh/melt").absoluteTo(refreshSession.exchangeBaseUrl); + const meltCoin = { coin_pub: coin.coinPub, + confirm_sig: refreshSession.confirmSig, denom_pub: coin.denomPub, denom_sig: coin.denomSig, - confirm_sig: refreshSession.confirmSig, value_with_fee: refreshSession.valueWithFee, }; - let coinEvs = refreshSession.preCoinsForGammas.map((x) => x.map((y) => y.coinEv)); - let req = { - "new_denoms": refreshSession.newDenoms, - "melt_coin": meltCoin, - "transfer_pubs": refreshSession.transferPubs, - "coin_evs": coinEvs, + const coinEvs = refreshSession.preCoinsForGammas.map((x) => x.map((y) => y.coinEv)); + const req = { + coin_evs: coinEvs, + melt_coin: meltCoin, + new_denoms: refreshSession.newDenoms, + transfer_pubs: refreshSession.transferPubs, }; console.log("melt request:", req); - let resp = await this.http.postJson(reqUrl.href(), req); + const resp = await this.http.postJson(reqUrl.href(), req); console.log("melt request:", req); console.log("melt response:", resp.responseText); - if (resp.status != 200) { + if (resp.status !== 200) { console.error(resp.responseText); throw Error("refresh failed"); } - let respJson = JSON.parse(resp.responseText); + const respJson = JSON.parse(resp.responseText); if (!respJson) { throw Error("exchange responded with garbage"); } - let norevealIndex = respJson.noreveal_index; + const norevealIndex = respJson.noreveal_index; - if (typeof norevealIndex != "number") { + if (typeof norevealIndex !== "number") { throw Error("invalid response"); } @@ -1929,71 +1944,70 @@ export class Wallet { async refreshReveal(refreshSession: RefreshSessionRecord): Promise<void> { - let norevealIndex = refreshSession.norevealIndex; - if (norevealIndex == undefined) { + const norevealIndex = refreshSession.norevealIndex; + if (norevealIndex === undefined) { throw Error("can't reveal without melting first"); } - let privs = Array.from(refreshSession.transferPrivs); + const privs = Array.from(refreshSession.transferPrivs); privs.splice(norevealIndex, 1); - let req = { - "session_hash": refreshSession.hash, - "transfer_privs": privs, + const req = { + session_hash: refreshSession.hash, + transfer_privs: privs, }; - let reqUrl = new URI("refresh/reveal") - .absoluteTo(refreshSession.exchangeBaseUrl); + const reqUrl = new URI("refresh/reveal") .absoluteTo(refreshSession.exchangeBaseUrl); console.log("reveal request:", req); - let resp = await this.http.postJson(reqUrl.href(), req); + const resp = await this.http.postJson(reqUrl.href(), req); console.log("session:", refreshSession); console.log("reveal response:", resp); - if (resp.status != 200) { + if (resp.status !== 200) { console.log("error: /refresh/reveal returned status " + resp.status); return; } - let respJson = JSON.parse(resp.responseText); + const respJson = JSON.parse(resp.responseText); if (!respJson.ev_sigs || !Array.isArray(respJson.ev_sigs)) { console.log("/refresh/reveal did not contain ev_sigs"); } - let exchange = await this.q().get<ExchangeRecord>(Stores.exchanges, + const exchange = await this.q().get<ExchangeRecord>(Stores.exchanges, refreshSession.exchangeBaseUrl); if (!exchange) { console.error(`exchange ${refreshSession.exchangeBaseUrl} not found`); return; } - let coins: CoinRecord[] = []; + const coins: CoinRecord[] = []; for (let i = 0; i < respJson.ev_sigs.length; i++) { - let denom = await ( + const denom = await ( this.q() .get(Stores.denominations, [ refreshSession.exchangeBaseUrl, - refreshSession.newDenoms[i] + refreshSession.newDenoms[i], ])); if (!denom) { console.error("denom not found"); continue; } - let pc = refreshSession.preCoinsForGammas[refreshSession.norevealIndex!][i]; - let denomSig = await this.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig, + const pc = refreshSession.preCoinsForGammas[refreshSession.norevealIndex!][i]; + const denomSig = await this.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig, pc.blindingKey, denom.denomPub); - let coin: CoinRecord = { - reservePub: undefined, + const coin: CoinRecord = { blindingKey: pc.blindingKey, - coinPub: pc.publicKey, coinPriv: pc.privateKey, - denomPub: denom.denomPub, - denomSig: denomSig, + coinPub: pc.publicKey, currentAmount: denom.value, + denomPub: denom.denomPub, + denomSig, exchangeBaseUrl: refreshSession.exchangeBaseUrl, + reservePub: undefined, status: CoinStatus.Fresh, }; @@ -2018,7 +2032,7 @@ export class Wallet { return acc; } - let history = await ( + const history = await ( this.q() .iterIndex(Stores.history.timestampIndex) .reduce(collect, [])); @@ -2027,12 +2041,12 @@ export class Wallet { } async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> { - let denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeUrl).toArray(); + const denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeUrl).toArray(); return denoms; } async getOffer(offerId: number): Promise<any> { - let offer = await this.q() .get(Stores.offers, offerId); + const offer = await this.q() .get(Stores.offers, offerId); return offer; } @@ -2089,7 +2103,7 @@ export class Wallet { * Store the private key in our DB, so we can prove ownership. */ async generateNonce(): Promise<string> { - let {priv, pub} = await this.cryptoApi.createEddsaKeypair(); + const {priv, pub} = await this.cryptoApi.createEddsaKeypair(); await this.q() .put(Stores.nonces, {priv, pub}) .finish(); @@ -2103,23 +2117,23 @@ export class Wallet { async paymentSucceeded(contractHash: string, merchantSig: string): Promise<any> { const doPaymentSucceeded = async() => { - let t = await this.q().get<TransactionRecord>(Stores.transactions, + const t = await this.q().get<TransactionRecord>(Stores.transactions, contractHash); if (!t) { console.error("contract not found"); return; } - let merchantPub = t.contract.merchant_pub; - let valid = this.cryptoApi.isValidPaymentSignature(merchantSig, contractHash, merchantPub); + const merchantPub = t.contract.merchant_pub; + const valid = this.cryptoApi.isValidPaymentSignature(merchantSig, contractHash, merchantPub); if (!valid) { console.error("merchant payment signature invalid"); // FIXME: properly display error return; } t.finished = true; - let modifiedCoins: CoinRecord[] = []; - for (let pc of t.payReq.coins) { - let c = await this.q().get<CoinRecord>(Stores.coins, pc.coin_pub); + const modifiedCoins: CoinRecord[] = []; + for (const pc of t.payReq.coins) { + const c = await this.q().get<CoinRecord>(Stores.coins, pc.coin_pub); if (!c) { console.error("coin not found"); return; @@ -2132,7 +2146,7 @@ export class Wallet { .putAll(Stores.coins, modifiedCoins) .put(Stores.transactions, t) .finish(); - for (let c of t.payReq.coins) { + for (const c of t.payReq.coins) { this.refresh(c.coin_pub); } }; @@ -2145,11 +2159,11 @@ export class Wallet { if (!coin) { throw Error(`Coin ${coinPub} not found, can't request payback`); } - let reservePub = coin.reservePub; + const reservePub = coin.reservePub; if (!reservePub) { throw Error(`Can't request payback for a refreshed coin`); } - let reserve = await this.q().get(Stores.reserves, reservePub); + const reserve = await this.q().get(Stores.reserves, reservePub); if (!reserve) { throw Error(`Reserve of coin ${coinPub} not found`); } @@ -2167,14 +2181,14 @@ export class Wallet { reserve.hasPayback = true; await this.q().put(Stores.coins, coin).put(Stores.reserves, reserve); - let paybackRequest = await this.cryptoApi.createPaybackRequest(coin); - let reqUrl = new URI("payback").absoluteTo(coin.exchangeBaseUrl); - let resp = await this.http.postJson(reqUrl.href(), paybackRequest); - if (resp.status != 200) { + const paybackRequest = await this.cryptoApi.createPaybackRequest(coin); + const reqUrl = new URI("payback").absoluteTo(coin.exchangeBaseUrl); + const resp = await this.http.postJson(reqUrl.href(), paybackRequest); + if (resp.status !== 200) { throw Error(); } - let paybackConfirmation = PaybackConfirmation.checked(JSON.parse(resp.responseText)); - if (paybackConfirmation.reserve_pub != coin.reservePub) { + const paybackConfirmation = PaybackConfirmation.checked(JSON.parse(resp.responseText)); + if (paybackConfirmation.reserve_pub !== coin.reservePub) { throw Error(`Coin's reserve doesn't match reserve on payback`); } coin = await this.q().get(Stores.coins, coinPub); @@ -2188,29 +2202,29 @@ export class Wallet { async denominationRecordFromKeys(exchangeBaseUrl: string, denomIn: Denomination): Promise<DenominationRecord> { - let denomPubHash = await this.cryptoApi.hashDenomPub(denomIn.denom_pub); - let d: DenominationRecord = { - denomPubHash, + const denomPubHash = await this.cryptoApi.hashDenomPub(denomIn.denom_pub); + const d: DenominationRecord = { denomPub: denomIn.denom_pub, - exchangeBaseUrl: exchangeBaseUrl, + denomPubHash, + exchangeBaseUrl, feeDeposit: denomIn.fee_deposit, - masterSig: denomIn.master_sig, - feeRefund: denomIn.fee_refund, feeRefresh: denomIn.fee_refresh, + feeRefund: denomIn.fee_refund, feeWithdraw: denomIn.fee_withdraw, + isOffered: true, + masterSig: denomIn.master_sig, stampExpireDeposit: denomIn.stamp_expire_deposit, stampExpireLegal: denomIn.stamp_expire_legal, stampExpireWithdraw: denomIn.stamp_expire_withdraw, stampStart: denomIn.stamp_start, status: DenominationStatus.Unverified, - isOffered: true, value: denomIn.value, }; return d; } async withdrawPaybackReserve(reservePub: string): Promise<void> { - let reserve = await this.q().get(Stores.reserves, reservePub); + const reserve = await this.q().get(Stores.reserves, reservePub); if (!reserve) { throw Error(`Reserve ${reservePub} does not exist`); } @@ -2220,7 +2234,7 @@ export class Wallet { } async getPaybackReserves(): Promise<ReserveRecord[]> { - return await this.q().iter(Stores.reserves).filter(r => r.hasPayback).toArray() + return await this.q().iter(Stores.reserves).filter((r) => r.hasPayback).toArray(); } } |