From 106bc6ad9a78c199e6b0e89bef854174d9014c28 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 20 Aug 2019 17:58:01 +0200 Subject: implement pay-url and disable broken wallet GC --- src/wallet.ts | 131 +++++++++++++++++++++++++++------------------------------- 1 file changed, 60 insertions(+), 71 deletions(-) (limited to 'src/wallet.ts') diff --git a/src/wallet.ts b/src/wallet.ts index f7df6658f..eda7bfeaf 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -104,6 +104,7 @@ import { TipStatus, WalletBalance, WalletBalanceEntry, + PreparePayResult, } from "./walletTypes"; import { openPromise } from "./promiseUtils"; @@ -382,7 +383,6 @@ export class Wallet { private async fillDefaults() { const onTrue = (r: QueryRoot) => { - console.log("defaults already applied"); }; const onFalse = (r: QueryRoot) => { Wallet.enableTracing && console.log("applying defaults"); @@ -593,6 +593,8 @@ export class Wallet { .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl) .toArray(); + console.log("considering coins", coins); + const denoms = await this.q() .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl) .toArray(); @@ -718,6 +720,53 @@ export class Wallet { return t; } + async preparePay(url: string): Promise { + const talerpayPrefix = "talerpay:" + if (url.startsWith(talerpayPrefix)) { + url = decodeURIComponent(url.substring(talerpayPrefix.length)); + } + let proposalId: number; + let checkResult: CheckPayResult; + try { + console.log("downloading proposal"); + proposalId = await this.downloadProposal(url); + console.log("calling checkPay"); + checkResult = await this.checkPay(proposalId); + console.log("checkPay result", checkResult); + } catch (e) { + return { + status: "error", + error: e.toString(), + } + } + const proposal = await this.getProposal(proposalId); + if (!proposal) { + throw Error("could not get proposal"); + } + if (checkResult.status === "paid") { + return { + status: "paid", + contractTerms: proposal.contractTerms, + proposalId: proposal.id!, + }; + } + if (checkResult.status === "insufficient-balance") { + return { + status: "insufficient-balance", + contractTerms: proposal.contractTerms, + proposalId: proposal.id!, + }; + } + if (checkResult.status === "payment-possible") { + return { + status: "payment-possible", + contractTerms: proposal.contractTerms, + proposalId: proposal.id!, + }; + } + throw Error("not reached"); + } + /** * Download a proposal and store it in the database. * Returns an id for it to retrieve it later. @@ -1001,6 +1050,8 @@ export class Wallet { const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount); + Wallet.enableTracing && console.log(`checking if payment of ${JSON.stringify(paymentAmount)} is possible`); + let wireFeeLimit; if (proposal.contractTerms.max_wire_fee) { wireFeeLimit = Amounts.parseOrThrow(proposal.contractTerms.max_wire_fee); @@ -1146,7 +1197,10 @@ export class Wallet { this.processPreCoinThrottle[preCoin.exchangeBaseUrl] ) { const timeout = Math.min(retryDelayMs * 2, 5 * 60 * 1000); - Wallet.enableTracing && console.log(`throttling processPreCoin of ${preCoinPub} for ${timeout}ms`); + Wallet.enableTracing && + console.log( + `throttling processPreCoin of ${preCoinPub} for ${timeout}ms`, + ); this.timerGroup.after(retryDelayMs, () => processPreCoinInternal()); return op.promise; } @@ -3370,76 +3424,11 @@ export class Wallet { * based on the current system time. */ async collectGarbage() { - const nowMilli = new Date().getTime(); - const nowSec = Math.floor(nowMilli / 1000); - - const gcReserve = (r: ReserveRecord, n: number) => { - // This rule to purge reserves is a bit over-eager, since we still might - // receive an emergency payback from the exchange. In this case we need - // to wait for the exchange to wire the money back or change this rule to - // wait until all coins from the reserve were spent. - if (r.timestamp_depleted) { - return true; - } - return false; - }; - await this.q() - .deleteIf(Stores.reserves, gcReserve) - .finish(); - - const gcProposal = (d: ProposalDownloadRecord, n: number) => { - // Delete proposal after 60 minutes or 5 minutes before pay deadline, - // whatever comes first. - const deadlinePayMilli = - getTalerStampSec(d.contractTerms.pay_deadline)! * 1000; - const deadlineExpireMilli = nowMilli + 1000 * 60 * 60; - return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli); - }; - await this.q() - .deleteIf(Stores.proposals, gcProposal) - .finish(); - - const activeExchanges: string[] = []; - const gcExchange = (d: ExchangeRecord, n: number) => { - // Delete if if unused and last update more than 20 minutes ago - if (!d.lastUsedTime && nowMilli > d.lastUpdateTime + 1000 * 60 * 20) { - return true; - } - activeExchanges.push(d.baseUrl); - return false; - }; - - await this.q() - .deleteIf(Stores.exchanges, gcExchange) - .finish(); - - // FIXME: check if this is correct! - const gcDenominations = (d: DenominationRecord, n: number) => { - if (nowSec > getTalerStampSec(d.stampExpireDeposit)!) { - console.log("garbage-collecting denomination due to expiration"); - return true; - } - if (activeExchanges.indexOf(d.exchangeBaseUrl) < 0) { - console.log("garbage-collecting denomination due to missing exchange"); - return true; - } - return false; - }; - await this.q() - .deleteIf(Stores.denominations, gcDenominations) - .finish(); - - const gcWireFees = (r: ExchangeWireFeesRecord, n: number) => { - if (activeExchanges.indexOf(r.exchangeBaseUrl) < 0) { - return true; - } - return false; - }; - await this.q() - .deleteIf(Stores.exchangeWireFees, gcWireFees) - .finish(); + // FIXME(#5845) - // FIXME(#5210) also GC coins + // We currently do not garbage-collect the wallet database. This might change + // after the feature has been properly re-designed, and we have come up with a + // strategy to test it. } clearNotification(): void { -- cgit v1.2.3