aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-05-20 16:24:41 +0200
committerFlorian Dold <florian@dold.me>2021-05-20 16:24:41 +0200
commit6fc9a052b7f8e3ef0bd1b26279b11dc6bc12f5e4 (patch)
treedb7e785017f30ccff5371592695ae10d92612d16
parent1fb182700252675eb89bfef5c27637e8396f1258 (diff)
implement deletion of withdrawal transactions
-rw-r--r--packages/taler-util/src/walletTypes.ts4
-rw-r--r--packages/taler-wallet-cli/src/index.ts19
-rw-r--r--packages/taler-wallet-core/src/db.ts11
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts55
-rw-r--r--packages/taler-wallet-core/src/util/query.ts1
-rw-r--r--packages/taler-wallet-core/src/wallet.ts7
6 files changed, 90 insertions, 7 deletions
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index 657e6568c..006140070 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -945,4 +945,8 @@ export interface WalletCurrencyInfo {
exchangeMasterPub: string;
exchangeBaseUrl: string;
}[];
+}
+
+export interface DeleteTransactionRequest {
+ transactionId: string;
} \ No newline at end of file
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 8a9e28d32..59b8d5c60 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -285,6 +285,21 @@ walletCli
});
walletCli
+ .subcommand("deleteTransaction", "delete-transaction", {
+ help: "Permanently delete a transaction from the transaction list.",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to delete",
+ })
+ .action(async (args) => {
+ await withWallet(args, async (wallet) => {
+ await wallet.deleteTransaction({
+ transactionId: args.deleteTransaction.transactionId,
+ });
+ });
+ });
+
+walletCli
.subcommand("handleUri", "handle-uri", {
help: "Handle a taler:// URI.",
})
@@ -609,13 +624,13 @@ const currenciesCli = walletCli.subcommand("currencies", "currencies", {
});
currenciesCli
- .subcommand("show", "show", { help: "Show currencies."})
+ .subcommand("show", "show", { help: "Show currencies." })
.action(async (args) => {
await withWallet(args, async (wallet) => {
const currencies = await wallet.getCurrencies();
console.log(JSON.stringify(currencies, undefined, 2));
});
- })
+ });
const reservesCli = advancedCli.subcommand("reserves", "reserves", {
help: "Manage reserves.",
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 52fe5c3dc..609f43ea1 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -25,7 +25,6 @@ import {
MerchantInfo,
Product,
RefreshReason,
- ReserveTransaction,
TalerErrorDetails,
Timestamp,
} from "@gnu-taler/taler-util";
@@ -1783,7 +1782,7 @@ class AuditorTrustStore extends Store<"auditorTrust", AuditorTrustRecord> {
class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
constructor() {
super("exchangeTrust", {
- keyPath: ["currency", "exchangeBaseUrl", "exchangePub"],
+ keyPath: ["currency", "exchangeBaseUrl", "exchangeMasterPub"],
});
}
exchangeMasterPubIndex = new Index<
@@ -1791,7 +1790,7 @@ class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
"exchangeMasterPubIndex",
string,
ExchangeTrustRecord
- >(this, "exchangeMasterPubIndex", "exchangePub");
+ >(this, "exchangeMasterPubIndex", "exchangeMasterPub");
uidIndex = new Index<
"exchangeTrust",
"uidIndex",
@@ -1810,6 +1809,12 @@ class ReservesStore extends Store<"reserves", ReserveRecord> {
constructor() {
super("reserves", { keyPath: "reservePub" });
}
+ byInitialWithdrawalGroupId = new Index<
+ "reserves",
+ "initialWithdrawalGroupIdIndex",
+ string,
+ ReserveRecord
+ >(this, "initialWithdrawalGroupIdIndex", "initialWithdrawalGroupId");
}
class TipsStore extends Store<"tips", TipRecord> {
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index dcd3ddbd9..48d0ffec5 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -24,6 +24,7 @@ import {
RefundState,
ReserveRecordStatus,
AbortStatus,
+ ReserveRecord,
} from "../db.js";
import { AmountJson, Amounts, timestampCmp } from "@gnu-taler/taler-util";
import {
@@ -42,7 +43,7 @@ import { getFundingPaytoUris } from "./reserves";
* Create an event ID from the type and the primary key for the event.
*/
function makeEventId(type: TransactionType, ...args: string[]): string {
- return type + ";" + args.map((x) => encodeURIComponent(x)).join(";");
+ return type + ":" + args.map((x) => encodeURIComponent(x)).join(":");
}
function shouldSkipCurrency(
@@ -365,3 +366,55 @@ export async function getTransactions(
return { transactions: [...txNotPending, ...txPending] };
}
+
+export enum TombstoneTag {
+ WithdrawalGroup = "withdrawal-group",
+ Reserve = "reserve",
+}
+
+/**
+ * Permanentely delete a transaction based on the transaction ID.
+ */
+export async function deleteTransaction(
+ ws: InternalWalletState,
+ transactionId: string,
+): Promise<void> {
+ const [type, ...rest] = transactionId.split(":");
+
+ if (type === TransactionType.Withdrawal) {
+ const withdrawalGroupId = rest[0];
+ ws.db.runWithWriteTransaction(
+ [Stores.withdrawalGroups, Stores.reserves, Stores.tombstones],
+ async (tx) => {
+ const withdrawalGroupRecord = await tx.get(
+ Stores.withdrawalGroups,
+ withdrawalGroupId,
+ );
+ if (withdrawalGroupRecord) {
+ await tx.delete(Stores.withdrawalGroups, withdrawalGroupId);
+ await tx.put(Stores.tombstones, {
+ id: TombstoneTag.WithdrawalGroup + ":" + withdrawalGroupId,
+ });
+ return;
+ }
+ const reserveRecord: ReserveRecord | undefined = await tx.getIndexed(
+ Stores.reserves.byInitialWithdrawalGroupId,
+ withdrawalGroupId,
+ );
+ if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {
+ const reservePub = reserveRecord.reservePub;
+ await tx.delete(Stores.reserves, reservePub);
+ await tx.put(Stores.tombstones, {
+ id: TombstoneTag.Reserve + ":" + reservePub,
+ });
+ }
+ },
+ );
+ } else if (type === TransactionType.Refund) {
+ // To delete refund transactions, the whole
+ // purchase should be deleted.
+ throw Error("refunds cannot be deleted");
+ } else {
+ throw Error(`can't delete a '${type}' transaction`);
+ }
+}
diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts
index 4c37a7254..1ef75a420 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -33,6 +33,7 @@ import {
IDBVersionChangeEvent,
Event,
IDBCursor,
+ IDBKeyPath,
} from "@gnu-taler/idb-bridge";
import { Logger } from "./logging";
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 192b54926..c380786ab 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -25,6 +25,7 @@
import {
BackupRecovery,
codecForAny,
+ DeleteTransactionRequest,
TalerErrorCode,
WalletCurrencyInfo,
} from "@gnu-taler/taler-util";
@@ -92,7 +93,7 @@ import {
withdrawTestBalance,
} from "./operations/testing";
import { acceptTip, prepareTip, processTip } from "./operations/tip";
-import { getTransactions } from "./operations/transactions";
+import { deleteTransaction, getTransactions } from "./operations/transactions";
import {
getExchangeWithdrawalInfo,
getWithdrawalDetailsForUri,
@@ -578,6 +579,10 @@ export class Wallet {
return getWithdrawalDetailsForUri(this.ws, talerWithdrawUri);
}
+ async deleteTransaction(req: DeleteTransactionRequest): Promise<void> {
+ return deleteTransaction(this.ws, req.transactionId);
+ }
+
/**
* Update or add exchange DB entry by fetching the /keys and /wire information.
* Optionally link the reserve entry to the new or existing