diff options
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
4 files changed, 122 insertions, 38 deletions
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 94ea2cb9c..b75bdfd74 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -743,6 +743,7 @@ async function updateExchangeFromUrlImpl( recoupGroupId = await ws.recoupOps.createRecoupGroup( ws, tx, + exchange.baseUrl, newlyRevokedCoinPubs, ); } diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index ae93711f9..38146f72e 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -126,7 +126,7 @@ async function gatherWithdrawalPending( resp.pendingOperations.push({ type: PendingTaskType.Withdraw, givesLifeness: true, - timestampDue: wsr.retryInfo.nextRetry, + timestampDue: wsr.retryInfo?.nextRetry ?? AbsoluteTime.now(), withdrawalGroupId: wsr.withdrawalGroupId, lastError: wsr.lastError, retryInfo: wsr.retryInfo, diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index 7c0f79daf..283707947 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -36,16 +36,17 @@ import { TalerErrorDetail, TalerProtocolTimestamp, URL, + codecForReserveStatus, } from "@gnu-taler/taler-util"; import { CoinRecord, CoinSourceType, CoinStatus, - OperationStatus, RecoupGroupRecord, RefreshCoinSource, ReserveRecordStatus, WalletStoresV1, + WithdrawalRecordType, WithdrawCoinSource, } from "../db.js"; import { InternalWalletState } from "../internal-wallet-state.js"; @@ -109,6 +110,10 @@ async function reportRecoupError( ws.notify({ type: NotificationType.RecoupOperationError, error: err }); } +/** + * Store a recoup group record in the database after marking + * a coin in the group as finished. + */ async function putGroupAsFinished( ws: InternalWalletState, tx: GetReadWriteAccess<{ @@ -127,29 +132,6 @@ async function putGroupAsFinished( return; } recoupGroup.recoupFinishedPerCoin[coinIdx] = true; - let allFinished = true; - for (const b of recoupGroup.recoupFinishedPerCoin) { - if (!b) { - allFinished = false; - } - } - if (allFinished) { - logger.info("all recoups of recoup group are finished"); - recoupGroup.timestampFinished = TalerProtocolTimestamp.now(); - recoupGroup.retryInfo = RetryInfo.reset(); - recoupGroup.lastError = undefined; - if (recoupGroup.scheduleRefreshCoins.length > 0) { - const refreshGroupId = await createRefreshGroup( - ws, - tx, - recoupGroup.scheduleRefreshCoins.map((x) => ({ coinPub: x })), - RefreshReason.Recoup, - ); - processRefreshGroup(ws, refreshGroupId.refreshGroupId).catch((e) => { - logger.error(`error while refreshing after recoup ${e}`); - }); - } - } await tx.recoupGroups.put(recoupGroup); } @@ -258,8 +240,6 @@ async function recoupWithdrawCoin( const currency = updatedCoin.currentAmount.currency; updatedCoin.currentAmount = Amounts.getZero(currency); await tx.coins.put(updatedCoin); - // FIXME: Actually withdraw here! - // await internalCreateWithdrawalGroup(ws, {...}); await putGroupAsFinished(ws, tx, recoupGroup, coinIdx); }); @@ -392,7 +372,7 @@ async function processRecoupGroupImpl( ): Promise<void> { const forceNow = options.forceNow ?? false; await setupRecoupRetry(ws, recoupGroupId, { reset: forceNow }); - const recoupGroup = await ws.db + let recoupGroup = await ws.db .mktx((x) => ({ recoupGroups: x.recoupGroups, })) @@ -416,23 +396,105 @@ async function processRecoupGroupImpl( }); await Promise.all(ps); + recoupGroup = await ws.db + .mktx((x) => ({ + recoupGroups: x.recoupGroups, + })) + .runReadOnly(async (tx) => { + return tx.recoupGroups.get(recoupGroupId); + }); + if (!recoupGroup) { + return; + } + + for (const b of recoupGroup.recoupFinishedPerCoin) { + if (!b) { + return; + } + } + + logger.info("all recoups of recoup group are finished"); + const reserveSet = new Set<string>(); + const reservePrivMap: Record<string, string> = {}; for (let i = 0; i < recoupGroup.coinPubs.length; i++) { const coinPub = recoupGroup.coinPubs[i]; - const coin = await ws.db + await ws.db .mktx((x) => ({ coins: x.coins, + reserves: x.reserves, })) .runReadOnly(async (tx) => { - return tx.coins.get(coinPub); + const coin = await tx.coins.get(coinPub); + if (!coin) { + throw Error(`Coin ${coinPub} not found, can't request recoup`); + } + if (coin.coinSource.type === CoinSourceType.Withdraw) { + const reserve = await tx.reserves.get(coin.coinSource.reservePub); + if (!reserve) { + return; + } + reserveSet.add(coin.coinSource.reservePub); + reservePrivMap[coin.coinSource.reservePub] = reserve.reservePriv; + } }); - if (!coin) { - throw Error(`Coin ${coinPub} not found, can't request recoup`); - } - if (coin.coinSource.type === CoinSourceType.Withdraw) { - reserveSet.add(coin.coinSource.reservePub); - } } + + for (const reservePub of reserveSet) { + const reserveUrl = new URL( + `reserves/${reservePub}`, + recoupGroup.exchangeBaseUrl, + ); + logger.info(`querying reserve status for recoup via ${reserveUrl}`); + + const resp = await ws.http.get(reserveUrl.href); + + const result = await readSuccessResponseJsonOrThrow( + resp, + codecForReserveStatus(), + ); + await internalCreateWithdrawalGroup(ws, { + amount: Amounts.parseOrThrow(result.balance), + exchangeBaseUrl: recoupGroup.exchangeBaseUrl, + reserveStatus: ReserveRecordStatus.QueryingStatus, + reserveKeyPair: { + pub: reservePub, + priv: reservePrivMap[reservePub], + }, + wgInfo: { + withdrawalType: WithdrawalRecordType.Recoup, + }, + }); + } + + await ws.db + .mktx((x) => ({ + recoupGroups: x.recoupGroups, + denominations: WalletStoresV1.denominations, + refreshGroups: WalletStoresV1.refreshGroups, + coins: WalletStoresV1.coins, + })) + .runReadWrite(async (tx) => { + const rg2 = await tx.recoupGroups.get(recoupGroupId); + if (!rg2) { + return; + } + rg2.timestampFinished = TalerProtocolTimestamp.now(); + rg2.retryInfo = RetryInfo.reset(); + rg2.lastError = undefined; + if (rg2.scheduleRefreshCoins.length > 0) { + const refreshGroupId = await createRefreshGroup( + ws, + tx, + rg2.scheduleRefreshCoins.map((x) => ({ coinPub: x })), + RefreshReason.Recoup, + ); + processRefreshGroup(ws, refreshGroupId.refreshGroupId).catch((e) => { + logger.error(`error while refreshing after recoup ${e}`); + }); + } + await tx.recoupGroups.put(rg2); + }); } export async function createRecoupGroup( @@ -443,12 +505,14 @@ export async function createRecoupGroup( refreshGroups: typeof WalletStoresV1.refreshGroups; coins: typeof WalletStoresV1.coins; }>, + exchangeBaseUrl: string, coinPubs: string[], ): Promise<string> { const recoupGroupId = encodeCrock(getRandomBytes(32)); const recoupGroup: RecoupGroupRecord = { recoupGroupId, + exchangeBaseUrl: exchangeBaseUrl, coinPubs: coinPubs, lastError: undefined, timestampFinished: undefined, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index a33f59162..84890a043 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -1135,6 +1135,22 @@ async function processWithdrawGroupImpl( withdrawalGroup.exchangeBaseUrl, ); + if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) { + await ws.db + .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups })) + .runReadWrite(async (tx) => { + const wg = await tx.withdrawalGroups.get(withdrawalGroupId); + if (!wg) { + return; + } + wg.operationStatus = OperationStatus.Finished; + delete wg.lastError; + delete wg.retryInfo; + await tx.withdrawalGroups.put(wg); + }); + return; + } + const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms .map((x) => x.count) .reduce((a, b) => a + b); @@ -1709,7 +1725,6 @@ export async function internalCreateWithdrawalGroup( args: { reserveStatus: ReserveRecordStatus; amount: AmountJson; - bankInfo?: ReserveBankInfo; exchangeBaseUrl: string; forcedDenomSel?: ForcedDenomSel; reserveKeyPair?: EddsaKeypair; @@ -1776,12 +1791,17 @@ export async function internalCreateWithdrawalGroup( await ws.db .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups, + reserves: x.reserves, exchanges: x.exchanges, exchangeDetails: x.exchangeDetails, exchangeTrust: x.exchangeTrust, })) .runReadWrite(async (tx) => { await tx.withdrawalGroups.add(withdrawalGroup); + await tx.reserves.put({ + reservePub: withdrawalGroup.reservePub, + reservePriv: withdrawalGroup.reservePriv, + }); if (!isAudited && !isTrusted) { await tx.exchangeTrust.put({ @@ -1906,7 +1926,6 @@ export async function createManualWithdrawal( withdrawalType: WithdrawalRecordType.BankManual, }, exchangeBaseUrl: req.exchangeBaseUrl, - bankInfo: undefined, forcedDenomSel: req.forcedDenomSel, restrictAge: req.restrictAge, reserveStatus: ReserveRecordStatus.QueryingStatus, |