From 863e468df9d70c3d7268e96378c03b1cf7333f88 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sun, 31 Mar 2024 17:08:29 +0200 Subject: wallet-core: denom loss notifications --- packages/taler-wallet-core/src/exchanges.ts | 120 +++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 22 deletions(-) (limited to 'packages/taler-wallet-core/src') diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts index 2680ede78..48d8d4972 100644 --- a/packages/taler-wallet-core/src/exchanges.ts +++ b/packages/taler-wallet-core/src/exchanges.ts @@ -137,7 +137,10 @@ import { import { DbReadOnlyTransaction } from "./query.js"; import { createRecoupGroup } from "./recoup.js"; import { createRefreshGroup } from "./refresh.js"; -import { constructTransactionIdentifier } from "./transactions.js"; +import { + constructTransactionIdentifier, + notifyTransition, +} from "./transactions.js"; import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "./versions.js"; import { InternalWalletState, WalletExecutionContext } from "./wallet.js"; @@ -1419,8 +1422,6 @@ export async function updateExchangeFromUrlHandler( let noFees = checkNoFees(keysInfo); let peerPaymentsDisabled = checkPeerPaymentsDisabled(keysInfo); - let denomLossResult: boolean = false; - const updated = await wex.db.runReadWriteTx( [ "exchanges", @@ -1565,7 +1566,7 @@ export async function updateExchangeFromUrlHandler( logger.trace("done updating denominations in database"); - denomLossResult = await handleDenomLoss( + const denomLossResult = await handleDenomLoss( wex, tx, newDetails.currency, @@ -1581,6 +1582,7 @@ export async function updateExchangeFromUrlHandler( exchangeDetails: newDetails, oldExchangeState, newExchangeState, + denomLossResult, }; }, ); @@ -1589,11 +1591,10 @@ export async function updateExchangeFromUrlHandler( throw Error("something went wrong with updating the exchange"); } - if (denomLossResult) { - wex.ws.notify({ - type: NotificationType.BalanceChange, - hintTransactionId: "denom-loss:*", - }); + if (updated.denomLossResult) { + for (const notif of updated.denomLossResult.notifications) { + wex.ws.notify(notif); + } } logger.trace("done updating exchange info in database"); @@ -1689,6 +1690,10 @@ export async function updateExchangeFromUrlHandler( return TaskRunResult.progress(); } +interface DenomLossResult { + notifications: WalletNotification[]; +} + async function handleDenomLoss( wex: WalletExecutionContext, tx: WalletDbReadWriteTransaction< @@ -1696,7 +1701,7 @@ async function handleDenomLoss( >, currency: string, exchangeBaseUrl: string, -): Promise { +): Promise { const coinAvailabilityRecs = await tx.coinAvailability.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl); const denomsVanished: string[] = []; @@ -1706,6 +1711,10 @@ async function handleDenomLoss( let amountExpired = Amount.zeroOfCurrency(currency); let amountUnoffered = Amount.zeroOfCurrency(currency); + const result: DenomLossResult = { + notifications: [], + }; + for (const coinAv of coinAvailabilityRecs) { if (coinAv.freshCoinCount <= 0) { continue; @@ -1746,12 +1755,11 @@ async function handleDenomLoss( continue; } } - let hadLoss = false; if (denomsVanished.length > 0) { - hadLoss = true; + const denomLossEventId = encodeCrock(getRandomBytes(32)); await tx.denomLossEvents.add({ - denomLossEventId: encodeCrock(getRandomBytes(32)), + denomLossEventId, amount: amountVanished.toString(), currency, exchangeBaseUrl, @@ -1760,12 +1768,30 @@ async function handleDenomLoss( status: DenomLossStatus.Done, timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.DenomLoss, + denomLossEventId, + }); + result.notifications.push({ + type: NotificationType.TransactionStateTransition, + transactionId, + oldTxState: { + major: TransactionMajorState.None, + }, + newTxState: { + major: TransactionMajorState.Done, + }, + }); + result.notifications.push({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); } if (denomsUnoffered.length > 0) { - hadLoss = true; + const denomLossEventId = encodeCrock(getRandomBytes(32)); await tx.denomLossEvents.add({ - denomLossEventId: encodeCrock(getRandomBytes(32)), + denomLossEventId, amount: amountUnoffered.toString(), currency, exchangeBaseUrl, @@ -1774,12 +1800,30 @@ async function handleDenomLoss( status: DenomLossStatus.Done, timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.DenomLoss, + denomLossEventId, + }); + result.notifications.push({ + type: NotificationType.TransactionStateTransition, + transactionId, + oldTxState: { + major: TransactionMajorState.None, + }, + newTxState: { + major: TransactionMajorState.Done, + }, + }); + result.notifications.push({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); } if (denomsExpired.length > 0) { - hadLoss = true; + const denomLossEventId = encodeCrock(getRandomBytes(32)); await tx.denomLossEvents.add({ - denomLossEventId: encodeCrock(getRandomBytes(32)), + denomLossEventId, amount: amountExpired.toString(), currency, exchangeBaseUrl, @@ -1788,9 +1832,27 @@ async function handleDenomLoss( status: DenomLossStatus.Done, timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.DenomLoss, + denomLossEventId, + }); + result.notifications.push({ + type: NotificationType.TransactionStateTransition, + transactionId, + oldTxState: { + major: TransactionMajorState.None, + }, + newTxState: { + major: TransactionMajorState.Done, + }, + }); + result.notifications.push({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); } - return hadLoss; + return result; } export function computeDenomLossTransactionStatus( @@ -1827,10 +1889,24 @@ export class DenomLossTransactionContext implements TransactionContext { throw new Error("Method not implemented."); } async deleteTransaction(): Promise { - await this.wex.db.runReadWriteTx(["denomLossEvents"], async (tx) => { - await tx.denomLossEvents.delete(this.denomLossEventId); - }); - throw new Error("Method not implemented."); + const transitionInfo = await this.wex.db.runReadWriteTx( + ["denomLossEvents"], + async (tx) => { + const rec = await tx.denomLossEvents.get(this.denomLossEventId); + if (rec) { + const oldTxState = computeDenomLossTransactionStatus(rec); + await tx.denomLossEvents.delete(this.denomLossEventId); + return { + oldTxState, + newTxState: { + major: TransactionMajorState.Deleted, + }, + }; + } + return undefined; + }, + ); + notifyTransition(this.wex, this.transactionId, transitionInfo); } constructor( -- cgit v1.2.3