aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-08-26 01:18:01 +0200
committerFlorian Dold <florian@dold.me>2022-08-26 01:18:01 +0200
commit30e8fd83c256826fc995edae499bf8bb6b60b7f2 (patch)
tree994752f4eced988458cf2b9ae3f1c143ca73cdf5 /packages/taler-wallet-core/src/operations
parent70d0199572ee6a95c68dd0b960d80e4ae93c4b0a (diff)
downloadwallet-core-30e8fd83c256826fc995edae499bf8bb6b60b7f2.tar.xz
wallet-core: fix revocation, re-introduce reserves object store
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts1
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/recoup.ts134
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts23
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,