From fe011321a4b65bc0736634ee2a4d9c7bf0618351 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 2 Nov 2022 13:40:03 +0100 Subject: wallet-core: compute residual amount for recoup properly --- packages/taler-util/src/backup-types.ts | 2 ++ .../src/integrationtests/test-revocation.ts | 1 + packages/taler-wallet-core/src/db.ts | 1 + .../src/operations/backup/export.ts | 1 + .../src/operations/backup/import.ts | 1 + packages/taler-wallet-core/src/operations/recoup.ts | 20 +++++++++++++++----- packages/taler-wallet-core/src/operations/refresh.ts | 13 +++++++++---- 7 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/taler-util/src/backup-types.ts b/packages/taler-util/src/backup-types.ts index 71f5bd04e..b3c6b5515 100644 --- a/packages/taler-util/src/backup-types.ts +++ b/packages/taler-util/src/backup-types.ts @@ -534,6 +534,8 @@ export interface BackupRefreshCoinSource { * Public key of the coin that was refreshed into this coin. */ old_coin_pub: string; + + refresh_group_id: string; } /** diff --git a/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts b/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts index 3ed071a18..0fbb4960e 100644 --- a/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts +++ b/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts @@ -181,6 +181,7 @@ export async function runRevocationTest(t: GlobalTestState) { await makeTestPayment(t, { wallet, merchant, order }); wallet.deleteDatabase(); + await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 307f76f21..dcc9fbe99 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -640,6 +640,7 @@ export interface WithdrawCoinSource { export interface RefreshCoinSource { type: CoinSourceType.Refresh; + refreshGroupId: string; oldCoinPub: string; } diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index b33d2f11a..965d51776 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -240,6 +240,7 @@ export async function exportBackup( bcs = { type: BackupCoinSourceType.Refresh, old_coin_pub: coin.coinSource.oldCoinPub, + refresh_group_id: coin.coinSource.refreshGroupId, }; break; case CoinSourceType.Tip: diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 73ad1ecc6..133699647 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -250,6 +250,7 @@ export async function importCoin( coinSource = { type: CoinSourceType.Refresh, oldCoinPub: backupCoin.coin_source.old_coin_pub, + refreshGroupId: backupCoin.coin_source.refresh_group_id, }; break; case BackupCoinSourceType.Tip: diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index f16225295..c2df6115b 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -279,11 +279,21 @@ async function recoupRefreshCoin( checkDbInvariant(!!oldCoinDenom); checkDbInvariant(!!revokedCoinDenom); revokedCoin.status = CoinStatus.Dormant; - recoupGroup.scheduleRefreshCoins.push({ - coinPub: oldCoin.coinPub, - //amount: Amounts.sub(oldCoinDenom.value, revokedCoinDenom.value).amount, - amount: revokedCoinDenom.value, - }); + if (!revokedCoin.spendAllocation) { + // We don't know what happened to this coin + logger.error( + `can't refresh-recoup coin ${revokedCoin.coinPub}, no spendAllocation known`, + ); + } else { + let residualAmount = Amounts.sub( + revokedCoinDenom.value, + revokedCoin.spendAllocation.amount, + ).amount; + recoupGroup.scheduleRefreshCoins.push({ + coinPub: oldCoin.coinPub, + amount: residualAmount, + }); + } await tx.coins.put(revokedCoin); await tx.coins.put(oldCoin); await putGroupAsFinished(ws, tx, recoupGroup, coinIdx); diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index e18faea32..ea0fae8bb 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -687,6 +687,7 @@ async function refreshReveal( status: CoinStatus.Fresh, coinSource: { type: CoinSourceType.Refresh, + refreshGroupId, oldCoinPub: refreshGroup.oldCoinPubs[coinIndex], }, coinEvHash: pc.coinEvHash, @@ -838,10 +839,6 @@ async function processRefreshSession( * Refreshes the remaining amount on the coin, effectively capturing the remaining * value in the refresh group. * - * The caller must ensure that - * the remaining amount was updated correctly before the coin was deposited or - * credited. - * * The caller must also ensure that the coins that should be refreshed exist * in the current database transaction. */ @@ -893,6 +890,10 @@ export async function createRefreshGroup( ); switch (coin.status) { case CoinStatus.Dormant: + coin.spendAllocation = { + amount: Amounts.stringify(ocp.amount), + id: `txn:refresh:${refreshGroupId}`, + }; break; case CoinStatus.Fresh: { coin.status = CoinStatus.Dormant; @@ -911,6 +912,10 @@ export async function createRefreshGroup( // For suspended coins, we don't have to adjust coin // availability, as they are not counted as available. coin.status = CoinStatus.Dormant; + coin.spendAllocation = { + amount: Amounts.stringify(ocp.amount), + id: `txn:refresh:${refreshGroupId}`, + }; break; } default: -- cgit v1.2.3