more async
This commit is contained in:
parent
c5dc5fd935
commit
b4815b2b06
@ -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> {
|
||||||
|
Loading…
Reference in New Issue
Block a user