summaryrefslogtreecommitdiff
path: root/src/operations/history.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-07-23 17:38:10 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-07-23 17:38:10 +0530
commit60891b502ad94438024e022ad7e30ab63692c163 (patch)
tree1792db806f716c6af348d797a3d9c15e5219c268 /src/operations/history.ts
parentd88829cfa8dc7bf2967fb494af0290e068466828 (diff)
downloadwallet-core-60891b502ad94438024e022ad7e30ab63692c163.tar.gz
wallet-core-60891b502ad94438024e022ad7e30ab63692c163.tar.bz2
wallet-core-60891b502ad94438024e022ad7e30ab63692c163.zip
sunset history API (we have the transaction list now)
Diffstat (limited to 'src/operations/history.ts')
-rw-r--r--src/operations/history.ts512
1 files changed, 0 insertions, 512 deletions
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 <http://www.gnu.org/licenses/>
- */
-
-/**
- * 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<void> {
- 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 };
-}