more async

This commit is contained in:
Florian Dold 2016-09-28 18:54:48 +02:00
parent c5dc5fd935
commit b4815b2b06

View File

@ -125,24 +125,11 @@ export class Offer {
} }
interface ConfirmPayRequest {
offer: Offer;
}
interface ExchangeCoins { interface ExchangeCoins {
[exchangeUrl: string]: CoinWithDenom[]; [exchangeUrl: string]: CoinWithDenom[];
} }
interface CoinPaySig {
coin_sig: string;
coin_pub: string;
ub_sig: string;
denom_pub: string;
f: AmountJson;
}
interface Transaction { interface Transaction {
contractHash: string; contractHash: string;
contract: Contract; contract: Contract;
@ -505,21 +492,21 @@ export class Wallet {
* Add a contract to the wallet and sign coins, * Add a contract to the wallet and sign coins,
* but do not send them yet. * but do not send them yet.
*/ */
confirmPay(offer: Offer): Promise<any> { async confirmPay(offer: Offer): Promise<any> {
console.log("executing confirmPay"); console.log("executing confirmPay");
return Query(this.db) let transaction = await Query(this.db)
.get("transactions", offer.H_contract) .get("transactions", offer.H_contract);
.then((transaction) => {
if (transaction) { if (transaction) {
// Already payed ... // Already payed ...
return {}; return {};
} }
return Promise.resolve().then(() => {
return this.getPossibleExchangeCoins(offer.contract.amount, let mcs = await this.getPossibleExchangeCoins(offer.contract.amount,
offer.contract.max_fee, offer.contract.max_fee,
offer.contract.exchanges) offer.contract.exchanges);
}).then((mcs) => {
if (Object.keys(mcs).length == 0) { if (Object.keys(mcs).length == 0) {
console.log("not confirming payment, insufficient coins"); console.log("not confirming payment, insufficient coins");
return { return {
@ -528,13 +515,11 @@ export class Wallet {
} }
let exchangeUrl = Object.keys(mcs)[0]; let exchangeUrl = Object.keys(mcs)[0];
return this.cryptoApi.signDeposit(offer, mcs[exchangeUrl]) let ds = await this.cryptoApi.signDeposit(offer, mcs[exchangeUrl]);
.then((ds) => this.recordConfirmPay(offer, await this.recordConfirmPay(offer,
ds, ds,
exchangeUrl)) exchangeUrl);
.then(() => ({})); return {};
});
});
} }
@ -542,23 +527,20 @@ export class Wallet {
* Add a contract to the wallet and sign coins, * Add a contract to the wallet and sign coins,
* but do not send them yet. * but do not send them yet.
*/ */
checkPay(offer: Offer): Promise<any> { async checkPay(offer: Offer): Promise<any> {
console.log("executing checkPay");
// First check if we already payed for it. // First check if we already payed for it.
return Query(this.db) let transaction = await
.get("transactions", offer.H_contract) Query(this.db)
.then((transaction) => { .get("transactions", offer.H_contract);
if (transaction) { if (transaction) {
return {isPayed: true}; return {isPayed: true};
} }
// If not already payed, check if we could pay for it. // If not already payed, check if we could pay for it.
return Promise.resolve().then(() => { let mcs = await this.getPossibleExchangeCoins(offer.contract.amount,
return this.getPossibleExchangeCoins(offer.contract.amount,
offer.contract.max_fee, offer.contract.max_fee,
offer.contract.exchanges) offer.contract.exchanges);
}).then((mcs) => {
if (Object.keys(mcs).length == 0) { if (Object.keys(mcs).length == 0) {
console.log("not confirming payment, insufficient coins"); console.log("not confirming payment, insufficient coins");
return { return {
@ -566,9 +548,6 @@ export class Wallet {
}; };
} }
return {isPayed: false}; return {isPayed: false};
});
});
} }
@ -576,11 +555,9 @@ export class Wallet {
* Retrieve all necessary information for looking up the contract * Retrieve all necessary information for looking up the contract
* with the given hash. * with the given hash.
*/ */
executePayment(H_contract: string): Promise<any> { async executePayment(H_contract: string): Promise<any> {
return Promise.resolve().then(() => { let t = await Query(this.db)
return Query(this.db) .get("transactions", H_contract);
.get("transactions", H_contract)
.then((t) => {
if (!t) { if (!t) {
return { return {
success: false, success: false,
@ -593,8 +570,6 @@ export class Wallet {
contract: t.contract, contract: t.contract,
}; };
return resp; return resp;
});
});
} }
@ -602,16 +577,16 @@ export class Wallet {
* First fetch information requred to withdraw from the reserve, * First fetch information requred to withdraw from the reserve,
* then deplete the reserve, withdrawing coins until it is empty. * then deplete the reserve, withdrawing coins until it is empty.
*/ */
private processReserve(reserveRecord: any, retryDelayMs: number = 250): void { private async processReserve(reserveRecord: any,
retryDelayMs: number = 250): Promise<void> {
const opId = "reserve-" + reserveRecord.reserve_pub; const opId = "reserve-" + reserveRecord.reserve_pub;
this.startOperation(opId); this.startOperation(opId);
this.updateExchangeFromUrl(reserveRecord.exchange_base_url)
.then((exchange) => try {
this.updateReserve(reserveRecord.reserve_pub, exchange) let exchange = await this.updateExchangeFromUrl(reserveRecord.exchange_base_url);
.then((reserve) => this.depleteReserve(reserve, let reserve = await this.updateReserve(reserveRecord.reserve_pub,
exchange))) exchange);
.then(() => { await this.depleteReserve(reserve, exchange);
this.stopOperation(opId);
let depleted = { let depleted = {
type: "depleted-reserve", type: "depleted-reserve",
timestamp: (new Date).getTime(), timestamp: (new Date).getTime(),
@ -619,42 +594,42 @@ export class Wallet {
reservePub: reserveRecord.reserve_pub, reservePub: reserveRecord.reserve_pub,
} }
}; };
return Query(this.db).put("history", depleted).finish(); await Query(this.db).put("history", depleted).finish();
}) } catch (e) {
.catch((e) => {
// Don't show progress while we're sleeping
this.stopOperation(opId);
// random, exponential backoff truncated at 3 minutes // random, exponential backoff truncated at 3 minutes
let nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * Math.random(), let nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * Math.random(),
3000 * 60); 3000 * 60);
console.warn(`Failed to deplete reserve, trying again in ${retryDelayMs} ms`); console.warn(`Failed to deplete reserve, trying again in ${retryDelayMs} ms`);
setTimeout(() => this.processReserve(reserveRecord, nextDelay), setTimeout(() => this.processReserve(reserveRecord, nextDelay),
retryDelayMs); retryDelayMs);
}); } finally {
this.stopOperation(opId);
}
} }
private processPreCoin(preCoin: any, retryDelayMs = 100): void { private async processPreCoin(preCoin: any,
this.withdrawExecute(preCoin) retryDelayMs = 100): Promise<void> {
.then((c) => this.storeCoin(c)) try {
.catch((e) => { const coin = await this.withdrawExecute(preCoin);
this.storeCoin(coin);
} catch (e) {
console.error("Failed to withdraw coin from precoin, retrying in", console.error("Failed to withdraw coin from precoin, retrying in",
retryDelayMs, retryDelayMs,
"ms"); "ms", e);
console.error(e);
// exponential backoff truncated at one minute // exponential backoff truncated at one minute
let nextRetryDelayMs = Math.min(retryDelayMs * 2, 1000 * 60); let nextRetryDelayMs = Math.min(retryDelayMs * 2, 1000 * 60);
setTimeout(() => this.processPreCoin(preCoin, nextRetryDelayMs), setTimeout(() => this.processPreCoin(preCoin, nextRetryDelayMs),
retryDelayMs); retryDelayMs);
}); }
} }
/** /**
* Create a reserve, but do not flag it as confirmed yet. * Create a reserve, but do not flag it as confirmed yet.
*/ */
createReserve(req: CreateReserveRequest): Promise<CreateReserveResponse> { async createReserve(req: CreateReserveRequest): Promise<CreateReserveResponse> {
return this.cryptoApi.createEddsaKeypair().then((keypair) => { let keypair = await this.cryptoApi.createEddsaKeypair();
const now = (new Date).getTime(); const now = (new Date).getTime();
const canonExchange = canonicalizeBaseUrl(req.exchange); const canonExchange = canonicalizeBaseUrl(req.exchange);
@ -669,7 +644,6 @@ export class Wallet {
confirmed: false, confirmed: false,
}; };
const historyEntry = { const historyEntry = {
type: "create-reserve", type: "create-reserve",
timestamp: now, timestamp: now,
@ -679,18 +653,16 @@ export class Wallet {
} }
}; };
return Query(this.db) await Query(this.db)
.put("reserves", reserveRecord) .put("reserves", reserveRecord)
.put("history", historyEntry) .put("history", historyEntry)
.finish() .finish();
.then(() => {
let r: CreateReserveResponse = { let r: CreateReserveResponse = {
exchange: canonExchange, exchange: canonExchange,
reservePub: keypair.pub, reservePub: keypair.pub,
}; };
return r; return r;
});
});
} }
@ -703,7 +675,7 @@ export class Wallet {
* A confirmed reserve should be shown to the user in the UI, while * A confirmed reserve should be shown to the user in the UI, while
* an unconfirmed reserve should be hidden. * an unconfirmed reserve should be hidden.
*/ */
confirmReserve(req: ConfirmReserveRequest): Promise<void> { async confirmReserve(req: ConfirmReserveRequest): Promise<void> {
const now = (new Date).getTime(); const now = (new Date).getTime();
const historyEntry = { const historyEntry = {
type: "confirm-reserve", type: "confirm-reserve",
@ -712,9 +684,8 @@ export class Wallet {
reservePub: req.reservePub, reservePub: req.reservePub,
} }
}; };
return Query(this.db) let r = await Query(this.db)
.get("reserves", req.reservePub) .get("reserves", req.reservePub);
.then((r) => {
if (!r) { if (!r) {
console.error("Unable to confirm reserve, not found in DB"); console.error("Unable to confirm reserve, not found in DB");
return; return;
@ -728,23 +699,22 @@ export class Wallet {
// Do this in the background // Do this in the background
this.processReserve(r); this.processReserve(r);
}); });
});
} }
private withdrawExecute(pc: PreCoin): Promise<Coin> { private async withdrawExecute(pc: PreCoin): Promise<Coin> {
return Query(this.db) let reserve = await Query(this.db)
.get("reserves", pc.reservePub) .get("reserves", pc.reservePub);
.then((r) => {
let wd: any = {}; let wd: any = {};
wd.denom_pub = pc.denomPub; wd.denom_pub = pc.denomPub;
wd.reserve_pub = pc.reservePub; wd.reserve_pub = pc.reservePub;
wd.reserve_sig = pc.withdrawSig; wd.reserve_sig = pc.withdrawSig;
wd.coin_ev = pc.coinEv; wd.coin_ev = pc.coinEv;
let reqUrl = URI("reserve/withdraw").absoluteTo(r.exchange_base_url); let reqUrl = URI("reserve/withdraw").absoluteTo(reserve.exchange_base_url);
return this.http.postJson(reqUrl, wd); let resp = await this.http.postJson(reqUrl, wd);
})
.then(resp => {
if (resp.status != 200) { if (resp.status != 200) {
throw new RequestException({ throw new RequestException({
hint: "Withdrawal failed", hint: "Withdrawal failed",
@ -752,8 +722,9 @@ export class Wallet {
}); });
} }
let r = JSON.parse(resp.responseText); let r = JSON.parse(resp.responseText);
return this.cryptoApi.rsaUnblind(r.ev_sig, pc.blindingKey, pc.denomPub) let denomSig = await this.cryptoApi.rsaUnblind(r.ev_sig,
.then((denomSig) => { pc.blindingKey,
pc.denomPub);
let coin: Coin = { let coin: Coin = {
coinPub: pc.coinPub, coinPub: pc.coinPub,
coinPriv: pc.coinPriv, coinPriv: pc.coinPriv,
@ -763,12 +734,9 @@ export class Wallet {
exchangeBaseUrl: pc.exchangeBaseUrl, exchangeBaseUrl: pc.exchangeBaseUrl,
}; };
return coin; return coin;
});
});
} }
storeCoin(coin: Coin): Promise<void> { async storeCoin(coin: Coin): Promise<void> {
console.log("storing coin", new Date()); console.log("storing coin", new Date());
let historyEntry = { let historyEntry = {
type: "withdraw", type: "withdraw",
@ -777,14 +745,12 @@ export class Wallet {
coinPub: coin.coinPub, coinPub: coin.coinPub,
} }
}; };
return Query(this.db) await Query(this.db)
.delete("precoins", coin.coinPub) .delete("precoins", coin.coinPub)
.add("coins", coin) .add("coins", coin)
.add("history", historyEntry) .add("history", historyEntry)
.finish() .finish();
.then(() => {
this.notifier.notify(); this.notifier.notify();
});
} }
@ -823,7 +789,7 @@ export class Wallet {
* Update the information about a reserve that is stored in the wallet * Update the information about a reserve that is stored in the wallet
* by quering the reserve's exchange. * by quering the reserve's exchange.
*/ */
private updateReserve(reservePub: string, private async updateReserve(reservePub: string,
exchange: IExchangeInfo): Promise<Reserve> { exchange: IExchangeInfo): Promise<Reserve> {
return Query(this.db) return Query(this.db)
.get("reserves", reservePub) .get("reserves", reservePub)
@ -862,10 +828,11 @@ export class Wallet {
/** /**
* Get the wire information for the exchange with the given base URL. * Get the wire information for the exchange with the given base URL.
*/ */
getWireInfo(exchangeBaseUrl: string): Promise<WireInfo> { async getWireInfo(exchangeBaseUrl: string): Promise<WireInfo> {
exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl); exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl);
let reqUrl = URI("wire").absoluteTo(exchangeBaseUrl); let reqUrl = URI("wire").absoluteTo(exchangeBaseUrl);
return this.http.get(reqUrl).then((resp: HttpResponse) => { let resp = await this.http.get(reqUrl);
if (resp.status != 200) { if (resp.status != 200) {
throw Error("/wire request failed"); throw Error("/wire request failed");
} }
@ -875,13 +842,12 @@ export class Wallet {
throw Error("/wire response malformed") throw Error("/wire response malformed")
} }
return wiJson; return wiJson;
});
} }
getReserveCreationInfo(baseUrl: string, async getReserveCreationInfo(baseUrl: string,
amount: AmountJson): Promise<ReserveCreationInfo> { amount: AmountJson): Promise<ReserveCreationInfo> {
let p = this.updateExchangeFromUrl(baseUrl); let exchangeInfo = await this.updateExchangeFromUrl(baseUrl);
return p.then((exchangeInfo: IExchangeInfo) => {
let selectedDenoms = getWithdrawDenomList(amount, let selectedDenoms = getWithdrawDenomList(amount,
exchangeInfo.active_denoms); exchangeInfo.active_denoms);
let acc = Amounts.getZero(amount.currency); let acc = Amounts.getZero(amount.currency);
@ -892,7 +858,9 @@ export class Wallet {
.map((d: Denomination) => Amounts.add(d.value, .map((d: Denomination) => Amounts.add(d.value,
d.fee_withdraw).amount) d.fee_withdraw).amount)
.reduce((a, b) => Amounts.add(a, b).amount); .reduce((a, b) => Amounts.add(a, b).amount);
return this.getWireInfo(baseUrl).then((wireInfo) => {
let wireInfo = await this.getWireInfo(baseUrl);
let ret: ReserveCreationInfo = { let ret: ReserveCreationInfo = {
exchangeInfo, exchangeInfo,
selectedDenoms, selectedDenoms,
@ -901,9 +869,6 @@ export class Wallet {
overhead: Amounts.sub(amount, actualCoinCost).amount, overhead: Amounts.sub(amount, actualCoinCost).amount,
}; };
return ret; return ret;
});
});
} }
@ -912,16 +877,15 @@ export class Wallet {
* Optionally link the reserve entry to the new or existing * Optionally link the reserve entry to the new or existing
* exchange entry in then DB. * exchange entry in then DB.
*/ */
updateExchangeFromUrl(baseUrl: string): Promise<IExchangeInfo> { async updateExchangeFromUrl(baseUrl: string): Promise<IExchangeInfo> {
baseUrl = canonicalizeBaseUrl(baseUrl); baseUrl = canonicalizeBaseUrl(baseUrl);
let reqUrl = URI("keys").absoluteTo(baseUrl); let reqUrl = URI("keys").absoluteTo(baseUrl);
return this.http.get(reqUrl).then((resp) => { let resp = await this.http.get(reqUrl);
if (resp.status != 200) { if (resp.status != 200) {
throw Error("/keys request failed"); throw Error("/keys request failed");
} }
let exchangeKeysJson = KeysJson.checked(JSON.parse(resp.responseText)); let exchangeKeysJson = KeysJson.checked(JSON.parse(resp.responseText));
return this.updateExchangeFromJson(baseUrl, exchangeKeysJson); return this.updateExchangeFromJson(baseUrl, exchangeKeysJson);
});
} }
private async suspendCoins(exchangeInfo: IExchangeInfo): Promise<void> { private async suspendCoins(exchangeInfo: IExchangeInfo): Promise<void> {