From 60891b502ad94438024e022ad7e30ab63692c163 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 23 Jul 2020 17:38:10 +0530 Subject: sunset history API (we have the transaction list now) --- src/operations/history.ts | 512 ---------------------------------------------- 1 file changed, 512 deletions(-) delete mode 100644 src/operations/history.ts (limited to 'src/operations') diff --git a/src/operations/history.ts b/src/operations/history.ts deleted file mode 100644 index 8fff4f888..000000000 --- a/src/operations/history.ts +++ /dev/null @@ -1,512 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see - */ - -/** - * Imports. - */ -import { InternalWalletState } from "./state"; -import { Stores, ProposalStatus, ProposalRecord } from "../types/dbTypes"; -import { Amounts } from "../util/amounts"; -import { AmountJson } from "../util/amounts"; -import { - HistoryQuery, - HistoryEvent, - HistoryEventType, - OrderShortInfo, - ReserveType, - ReserveCreationDetail, - VerbosePayCoinDetails, - VerboseRefreshDetails, -} from "../types/history"; -import { assertUnreachable } from "../util/assertUnreachable"; -import { TransactionHandle } from "../util/query"; -import { timestampCmp } from "../util/time"; -import { summarizeReserveHistory } from "../util/reserveHistoryUtil"; - -/** - * Create an event ID from the type and the primary key for the event. - */ -function makeEventId(type: HistoryEventType, ...args: string[]): string { - return type + ";" + args.map((x) => encodeURIComponent(x)).join(";"); -} - -function getOrderShortInfo( - proposal: ProposalRecord, -): OrderShortInfo | undefined { - const download = proposal.download; - if (!download) { - return undefined; - } - return { - amount: Amounts.stringify(download.contractData.amount), - fulfillmentUrl: download.contractData.fulfillmentUrl, - orderId: download.contractData.orderId, - merchantBaseUrl: download.contractData.merchantBaseUrl, - proposalId: proposal.proposalId, - summary: download.contractData.summary, - }; -} - -async function collectProposalHistory( - tx: TransactionHandle, - history: HistoryEvent[], - historyQuery?: HistoryQuery, -): Promise { - tx.iter(Stores.proposals).forEachAsync(async (proposal) => { - const status = proposal.proposalStatus; - switch (status) { - case ProposalStatus.ACCEPTED: - { - const shortInfo = getOrderShortInfo(proposal); - if (!shortInfo) { - break; - } - history.push({ - type: HistoryEventType.OrderAccepted, - eventId: makeEventId( - HistoryEventType.OrderAccepted, - proposal.proposalId, - ), - orderShortInfo: shortInfo, - timestamp: proposal.timestamp, - }); - } - break; - case ProposalStatus.DOWNLOADING: - case ProposalStatus.PROPOSED: - // no history event needed - break; - case ProposalStatus.REFUSED: - { - const shortInfo = getOrderShortInfo(proposal); - if (!shortInfo) { - break; - } - history.push({ - type: HistoryEventType.OrderRefused, - eventId: makeEventId( - HistoryEventType.OrderRefused, - proposal.proposalId, - ), - orderShortInfo: shortInfo, - timestamp: proposal.timestamp, - }); - } - break; - case ProposalStatus.REPURCHASE: - { - const alreadyPaidProposal = await tx.get( - Stores.proposals, - proposal.repurchaseProposalId, - ); - if (!alreadyPaidProposal) { - break; - } - const alreadyPaidOrderShortInfo = getOrderShortInfo( - alreadyPaidProposal, - ); - if (!alreadyPaidOrderShortInfo) { - break; - } - const newOrderShortInfo = getOrderShortInfo(proposal); - if (!newOrderShortInfo) { - break; - } - history.push({ - type: HistoryEventType.OrderRedirected, - eventId: makeEventId( - HistoryEventType.OrderRedirected, - proposal.proposalId, - ), - alreadyPaidOrderShortInfo, - newOrderShortInfo, - timestamp: proposal.timestamp, - }); - } - break; - default: - assertUnreachable(status); - } - }); -} - -/** - * Retrive the full event history for this wallet. - */ -export async function getHistory( - ws: InternalWalletState, - historyQuery?: HistoryQuery, -): Promise<{ history: HistoryEvent[] }> { - const history: HistoryEvent[] = []; - - // FIXME: do pagination instead of generating the full history - // We uniquely identify history rows via their timestamp. - // This works as timestamps are guaranteed to be monotonically - // increasing even - - await ws.db.runWithReadTransaction( - [ - Stores.currencies, - Stores.coins, - Stores.denominations, - Stores.exchanges, - Stores.exchangeUpdatedEvents, - Stores.proposals, - Stores.purchases, - Stores.refreshGroups, - Stores.reserves, - Stores.reserveHistory, - Stores.tips, - Stores.withdrawalGroups, - Stores.payEvents, - Stores.planchets, - Stores.refundEvents, - Stores.reserveUpdatedEvents, - Stores.recoupGroups, - ], - async (tx) => { - tx.iter(Stores.exchanges).forEach((exchange) => { - history.push({ - type: HistoryEventType.ExchangeAdded, - builtIn: false, - eventId: makeEventId( - HistoryEventType.ExchangeAdded, - exchange.baseUrl, - ), - exchangeBaseUrl: exchange.baseUrl, - timestamp: exchange.timestampAdded, - }); - }); - - tx.iter(Stores.exchangeUpdatedEvents).forEach((eu) => { - history.push({ - type: HistoryEventType.ExchangeUpdated, - eventId: makeEventId( - HistoryEventType.ExchangeUpdated, - eu.exchangeBaseUrl, - ), - exchangeBaseUrl: eu.exchangeBaseUrl, - timestamp: eu.timestamp, - }); - }); - - tx.iter(Stores.withdrawalGroups).forEach((wsr) => { - if (wsr.timestampFinish) { - history.push({ - type: HistoryEventType.Withdrawn, - withdrawalGroupId: wsr.withdrawalGroupId, - eventId: makeEventId( - HistoryEventType.Withdrawn, - wsr.withdrawalGroupId, - ), - amountWithdrawnEffective: Amounts.stringify( - wsr.denomsSel.totalCoinValue, - ), - amountWithdrawnRaw: Amounts.stringify(wsr.rawWithdrawalAmount), - exchangeBaseUrl: wsr.exchangeBaseUrl, - timestamp: wsr.timestampFinish, - withdrawalSource: wsr.source, - verboseDetails: undefined, - }); - } - }); - - await collectProposalHistory(tx, history, historyQuery); - - await tx.iter(Stores.payEvents).forEachAsync(async (pe) => { - const proposal = await tx.get(Stores.proposals, pe.proposalId); - if (!proposal) { - return; - } - const purchase = await tx.get(Stores.purchases, pe.proposalId); - if (!purchase) { - return; - } - const orderShortInfo = getOrderShortInfo(proposal); - if (!orderShortInfo) { - return; - } - let verboseDetails: VerbosePayCoinDetails | undefined = undefined; - if (historyQuery?.extraDebug) { - const coins: { - value: string; - contribution: string; - denomPub: string; - }[] = []; - for (const x of purchase.coinDepositPermissions) { - const c = await tx.get(Stores.coins, x.coin_pub); - if (!c) { - // FIXME: what to do here?? - continue; - } - const d = await tx.get(Stores.denominations, [ - c.exchangeBaseUrl, - c.denomPub, - ]); - if (!d) { - // FIXME: what to do here?? - continue; - } - coins.push({ - contribution: x.contribution, - denomPub: c.denomPub, - value: Amounts.stringify(d.value), - }); - } - verboseDetails = { coins }; - } - const amountPaidWithFees = Amounts.sum( - purchase.coinDepositPermissions.map((x) => - Amounts.parseOrThrow(x.contribution), - ), - ).amount; - history.push({ - type: HistoryEventType.PaymentSent, - eventId: makeEventId(HistoryEventType.PaymentSent, pe.proposalId), - orderShortInfo, - replay: pe.isReplay, - sessionId: pe.sessionId, - timestamp: pe.timestamp, - numCoins: purchase.coinDepositPermissions.length, - amountPaidWithFees: Amounts.stringify(amountPaidWithFees), - verboseDetails, - }); - }); - - await tx.iter(Stores.refreshGroups).forEachAsync(async (rg) => { - if (!rg.timestampFinished) { - return; - } - let numInputCoins = 0; - let numRefreshedInputCoins = 0; - let numOutputCoins = 0; - const amountsRaw: AmountJson[] = []; - const amountsEffective: AmountJson[] = []; - for (let i = 0; i < rg.refreshSessionPerCoin.length; i++) { - const session = rg.refreshSessionPerCoin[i]; - numInputCoins++; - const c = await tx.get(Stores.coins, rg.oldCoinPubs[i]); - if (!c) { - continue; - } - if (session) { - numRefreshedInputCoins++; - amountsRaw.push(session.amountRefreshInput); - amountsRaw.push(c.currentAmount); - amountsEffective.push(session.amountRefreshOutput); - numOutputCoins += session.newDenoms.length; - } else { - amountsRaw.push(c.currentAmount); - } - } - const amountRefreshedRaw = Amounts.sum(amountsRaw).amount; - let amountRefreshedEffective: AmountJson; - if (amountsEffective.length == 0) { - amountRefreshedEffective = Amounts.getZero( - amountRefreshedRaw.currency, - ); - } else { - amountRefreshedEffective = Amounts.sum(amountsEffective).amount; - } - let verboseDetails: VerboseRefreshDetails | undefined = undefined; - if (historyQuery?.extraDebug) { - const outputCoins: { - value: string; - denomPub: string; - }[] = []; - for (const rs of rg.refreshSessionPerCoin) { - if (!rs) { - continue; - } - for (const nd of rs.newDenoms) { - if (!nd) { - continue; - } - const d = await tx.get(Stores.denominations, [ - rs.exchangeBaseUrl, - nd, - ]); - if (!d) { - continue; - } - outputCoins.push({ - denomPub: d.denomPub, - value: Amounts.stringify(d.value), - }); - } - } - verboseDetails = { - outputCoins: outputCoins, - }; - } - history.push({ - type: HistoryEventType.Refreshed, - refreshGroupId: rg.refreshGroupId, - eventId: makeEventId(HistoryEventType.Refreshed, rg.refreshGroupId), - timestamp: rg.timestampFinished, - refreshReason: rg.reason, - amountRefreshedEffective: Amounts.stringify(amountRefreshedEffective), - amountRefreshedRaw: Amounts.stringify(amountRefreshedRaw), - numInputCoins, - numOutputCoins, - numRefreshedInputCoins, - verboseDetails, - }); - }); - - tx.iter(Stores.reserveUpdatedEvents).forEachAsync(async (ru) => { - const reserve = await tx.get(Stores.reserves, ru.reservePub); - if (!reserve) { - return; - } - let reserveCreationDetail: ReserveCreationDetail; - if (reserve.bankInfo) { - reserveCreationDetail = { - type: ReserveType.TalerBankWithdraw, - bankUrl: reserve.bankInfo.statusUrl, - }; - } else { - reserveCreationDetail = { - type: ReserveType.Manual, - }; - } - const hist = await tx.get(Stores.reserveHistory, reserve.reservePub); - if (!hist) { - throw Error("inconsistent database"); - } - const s = summarizeReserveHistory( - hist.reserveTransactions, - reserve.currency, - ); - history.push({ - type: HistoryEventType.ReserveBalanceUpdated, - eventId: makeEventId( - HistoryEventType.ReserveBalanceUpdated, - ru.reserveUpdateId, - ), - timestamp: ru.timestamp, - reserveShortInfo: { - exchangeBaseUrl: reserve.exchangeBaseUrl, - reserveCreationDetail, - reservePub: reserve.reservePub, - }, - reserveAwaitedAmount: Amounts.stringify(s.awaitedReserveAmount), - reserveBalance: Amounts.stringify(s.computedReserveBalance), - reserveUnclaimedAmount: Amounts.stringify(s.unclaimedReserveAmount), - }); - }); - - tx.iter(Stores.tips).forEach((tip) => { - if (tip.acceptedTimestamp) { - history.push({ - type: HistoryEventType.TipAccepted, - eventId: makeEventId(HistoryEventType.TipAccepted, tip.tipId), - timestamp: tip.acceptedTimestamp, - tipId: tip.tipId, - tipAmountRaw: Amounts.stringify(tip.amount), - }); - } - }); - - // tx.iter(Stores.refundEvents).forEachAsync(async (re) => { - // const proposal = await tx.get(Stores.proposals, re.proposalId); - // if (!proposal) { - // return; - // } - // const purchase = await tx.get(Stores.purchases, re.proposalId); - // if (!purchase) { - // return; - // } - // const orderShortInfo = getOrderShortInfo(proposal); - // if (!orderShortInfo) { - // return; - // } - // const purchaseAmount = purchase.contractData.amount; - // let amountRefundedRaw = Amounts.getZero(purchaseAmount.currency); - // let amountRefundedInvalid = Amounts.getZero(purchaseAmount.currency); - // let amountRefundedEffective = Amounts.getZero(purchaseAmount.currency); - // Object.keys(purchase.refundsDone).forEach((x, i) => { - // const r = purchase.refundsDone[x]; - // if (r.refundGroupId !== re.refundGroupId) { - // return; - // } - // const refundAmount = Amounts.parseOrThrow(r.perm.refund_amount); - // const refundFee = Amounts.parseOrThrow(r.perm.refund_fee); - // amountRefundedRaw = Amounts.add(amountRefundedRaw, refundAmount) - // .amount; - // amountRefundedEffective = Amounts.add( - // amountRefundedEffective, - // refundAmount, - // ).amount; - // amountRefundedEffective = Amounts.sub( - // amountRefundedEffective, - // refundFee, - // ).amount; - // }); - // Object.keys(purchase.refundsFailed).forEach((x, i) => { - // const r = purchase.refundsFailed[x]; - // if (r.refundGroupId !== re.refundGroupId) { - // return; - // } - // const ra = Amounts.parseOrThrow(r.perm.refund_amount); - // const refundFee = Amounts.parseOrThrow(r.perm.refund_fee); - // amountRefundedRaw = Amounts.add(amountRefundedRaw, ra).amount; - // amountRefundedInvalid = Amounts.add(amountRefundedInvalid, ra).amount; - // amountRefundedEffective = Amounts.sub( - // amountRefundedEffective, - // refundFee, - // ).amount; - // }); - // history.push({ - // type: HistoryEventType.Refund, - // eventId: makeEventId(HistoryEventType.Refund, re.refundGroupId), - // refundGroupId: re.refundGroupId, - // orderShortInfo, - // timestamp: re.timestamp, - // amountRefundedEffective: Amounts.stringify(amountRefundedEffective), - // amountRefundedRaw: Amounts.stringify(amountRefundedRaw), - // amountRefundedInvalid: Amounts.stringify(amountRefundedInvalid), - // }); - // }); - - tx.iter(Stores.recoupGroups).forEach((rg) => { - if (rg.timestampFinished) { - let verboseDetails: any = undefined; - if (historyQuery?.extraDebug) { - verboseDetails = { - oldAmountPerCoin: rg.oldAmountPerCoin.map(Amounts.stringify), - }; - } - - history.push({ - type: HistoryEventType.FundsRecouped, - timestamp: rg.timestampFinished, - eventId: makeEventId( - HistoryEventType.FundsRecouped, - rg.recoupGroupId, - ), - numCoinsRecouped: rg.coinPubs.length, - verboseDetails, - }); - } - }); - }, - ); - - history.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp)); - - return { history }; -} -- cgit v1.2.3