commit d2883bf078f190067d7cfd8e5e91db0273c151eb
parent 80cdacbd4cb85846d332cf5f29d3278f18d9fa38
Author: Florian Dold <dold@taler.net>
Date: Thu, 20 Aug 2026 19:06:45 +0200
wallet-core: refresh value recovered by recoup-refresh
Diffstat:
2 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/packages/taler-wallet-core/src/recoup.test.ts b/packages/taler-wallet-core/src/recoup.test.ts
@@ -0,0 +1,32 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ 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/>
+ */
+import assert from "node:assert";
+import { test } from "node:test";
+import { WalletRecoupGroup } from "./db-common.js";
+import { scheduleRecoupRefresh } from "./recoup.js";
+
+test("recoup-refresh schedules and aggregates value on the old coin", () => {
+ const group = { scheduleRefreshCoins: [] } as unknown as WalletRecoupGroup;
+
+ scheduleRecoupRefresh(group, "old-coin", "TESTKUDOS:2");
+ scheduleRecoupRefresh(group, "old-coin", "TESTKUDOS:3");
+ scheduleRecoupRefresh(group, "another-old-coin", "TESTKUDOS:1");
+
+ assert.deepStrictEqual(group.scheduleRefreshCoins, [
+ { coinPub: "old-coin", amount: "TESTKUDOS:5" },
+ { coinPub: "another-old-coin", amount: "TESTKUDOS:1" },
+ ]);
+});
diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts
@@ -25,6 +25,7 @@
* Imports.
*/
import {
+ AmountLike,
Amounts,
CoinStatus,
Logger,
@@ -184,17 +185,41 @@ async function recoupRefreshCoin(
`no revoked denom for coin, hash ${revokedCoin.denomPubHash}`,
);
revokedCoin.status = CoinStatus.Dormant;
- // FIXME: Schedule recoup for the sum of refreshes, based on the coin event history.
- // recoupGroup.scheduleRefreshCoins.push({
- // coinPub: oldCoin.coinPub,
- // amount: Amounts.stringify(refreshAmount),
- // });
+ scheduleRecoupRefresh(
+ recoupGroup,
+ oldCoin.coinPub,
+ revokedCoinDenom.value,
+ );
await tx.upsertCoin(revokedCoin);
await tx.upsertCoin(oldCoin);
await putGroupAsFinished(wex, tx, recoupGroup, coinIdx);
});
}
+export function scheduleRecoupRefresh(
+ recoupGroup: WalletRecoupGroup,
+ oldCoinPub: string,
+ recoveredAmount: AmountLike,
+): void {
+ const existingIndex = recoupGroup.scheduleRefreshCoins.findIndex(
+ (x) => x.coinPub === oldCoinPub,
+ );
+ if (existingIndex >= 0) {
+ const existing = recoupGroup.scheduleRefreshCoins[existingIndex];
+ recoupGroup.scheduleRefreshCoins[existingIndex] = {
+ ...existing,
+ amount: Amounts.stringify(
+ Amounts.add(existing.amount, recoveredAmount).amount,
+ ),
+ };
+ return;
+ }
+ recoupGroup.scheduleRefreshCoins.push({
+ coinPub: oldCoinPub,
+ amount: Amounts.stringify(recoveredAmount),
+ });
+}
+
export async function recoupWithdrawCoin(
wex: WalletExecutionContext,
recoupGroupId: string,