aboutsummaryrefslogtreecommitdiff
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
parent70d0199572ee6a95c68dd0b960d80e4ae93c4b0a (diff)
wallet-core: fix revocation, re-introduce reserves object store
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts2
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts15
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/testrunner.ts106
-rw-r--r--packages/taler-wallet-core/src/db.ts29
-rw-r--r--packages/taler-wallet-core/src/internal-wallet-state.ts2
-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
-rw-r--r--packages/taler-wallet-core/src/pending-types.ts2
10 files changed, 211 insertions, 105 deletions
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
index ca7dc33d8..83231b358 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
@@ -243,7 +243,7 @@ export async function runLibeufinBasicTest(t: GlobalTestState) {
WalletApiOperation.AcceptManualWithdrawal,
{
exchangeBaseUrl: exchange.baseUrl,
- amount: "EUR:10",
+ amount: "EUR:15",
},
);
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts
index 23d76081f..ff7a50ae6 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts
@@ -17,12 +17,11 @@
/**
* Imports.
*/
-import { GlobalTestState, delayMs } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
SandboxUserBundle,
NexusUserBundle,
launchLibeufinServices,
- LibeufinSandboxApi,
LibeufinNexusApi,
} from "../harness/libeufin";
@@ -73,7 +72,7 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
user02sandbox.ebicsBankAccount.label, // debit
user01sandbox.ebicsBankAccount.label, // credit
"EUR:10",
- "first payment",
+ "second payment",
);
await LibeufinNexusApi.fetchTransactions(
@@ -82,13 +81,13 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
"all", // range
"report", // level
);
-
+
// Check that user 01 has 20, via Nexus.
let accountInfo = await LibeufinNexusApi.getBankAccount(
libeufinServices.libeufinNexus,
- user01nexus.localAccountName
+ user01nexus.localAccountName,
);
- t.assertTrue(accountInfo.data.lastSeenBalance == "EUR:20");
+ t.assertAmountEquals(accountInfo.data.lastSeenBalance, "EUR:20");
// user 01 gives 30
await libeufinServices.libeufinSandbox.makeTransaction(
@@ -107,8 +106,8 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
let accountInfoDebit = await LibeufinNexusApi.getBankAccount(
libeufinServices.libeufinNexus,
- user01nexus.localAccountName
+ user01nexus.localAccountName,
);
- t.assertTrue(accountInfoDebit.data.lastSeenBalance == "-EUR:10");
+ t.assertDeepEqual(accountInfoDebit.data.lastSeenBalance, "-EUR:10");
}
runLibeufinNexusBalanceTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
index 88e67a8bb..74aa66005 100644
--- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
@@ -26,71 +26,71 @@ import {
TestRunResult,
} from "../harness/harness.js";
import { runAgeRestrictionsTest } from "./test-age-restrictions.js";
-import { runBankApiTest } from "./test-bank-api";
-import { runClaimLoopTest } from "./test-claim-loop";
+import { runBankApiTest } from "./test-bank-api.js";
+import { runClaimLoopTest } from "./test-claim-loop.js";
import { runClauseSchnorrTest } from "./test-clause-schnorr.js";
import { runDenomUnofferedTest } from "./test-denom-unoffered.js";
-import { runDepositTest } from "./test-deposit";
-import { runExchangeManagementTest } from "./test-exchange-management";
+import { runDepositTest } from "./test-deposit.js";
+import { runExchangeManagementTest } from "./test-exchange-management.js";
import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
-import { runFeeRegressionTest } from "./test-fee-regression";
+import { runFeeRegressionTest } from "./test-fee-regression.js";
import { runForcedSelectionTest } from "./test-forced-selection.js";
-import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount";
-import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection";
-import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade";
-import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request";
-import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions";
-import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt";
-import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions";
-import { runLibeufinApiSchedulingTest } from "./test-libeufin-api-scheduling";
-import { runLibeufinApiUsersTest } from "./test-libeufin-api-users";
-import { runLibeufinBadGatewayTest } from "./test-libeufin-bad-gateway";
-import { runLibeufinBasicTest } from "./test-libeufin-basic";
-import { runLibeufinC5xTest } from "./test-libeufin-c5x";
-import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis";
-import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation";
-import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance";
-import { runLibeufinRefundTest } from "./test-libeufin-refund";
-import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users";
-import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli";
-import { runLibeufinTutorialTest } from "./test-libeufin-tutorial";
-import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion";
-import { runMerchantInstancesTest } from "./test-merchant-instances";
+import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount.js";
+import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection.js";
+import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade.js";
+import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request.js";
+import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions.js";
+import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt.js";
+import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions.js";
+import { runLibeufinApiSchedulingTest } from "./test-libeufin-api-scheduling.js";
+import { runLibeufinApiUsersTest } from "./test-libeufin-api-users.js";
+import { runLibeufinBadGatewayTest } from "./test-libeufin-bad-gateway.js";
+import { runLibeufinBasicTest } from "./test-libeufin-basic.js";
+import { runLibeufinC5xTest } from "./test-libeufin-c5x.js";
+import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis.js";
+import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation.js";
+import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance.js";
+import { runLibeufinRefundTest } from "./test-libeufin-refund.js";
+import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users.js";
+import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli.js";
+import { runLibeufinTutorialTest } from "./test-libeufin-tutorial.js";
+import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion.js";
+import { runMerchantInstancesTest } from "./test-merchant-instances.js";
import { runMerchantInstancesDeleteTest } from "./test-merchant-instances-delete";
-import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls";
-import { runMerchantLongpollingTest } from "./test-merchant-longpolling";
-import { runMerchantRefundApiTest } from "./test-merchant-refund-api";
+import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls.js";
+import { runMerchantLongpollingTest } from "./test-merchant-longpolling.js";
+import { runMerchantRefundApiTest } from "./test-merchant-refund-api.js";
import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js";
-import { runPayAbortTest } from "./test-pay-abort";
-import { runPayPaidTest } from "./test-pay-paid";
-import { runPaymentTest } from "./test-payment";
-import { runPaymentClaimTest } from "./test-payment-claim";
-import { runPaymentFaultTest } from "./test-payment-fault";
+import { runPayAbortTest } from "./test-pay-abort.js";
+import { runPayPaidTest } from "./test-pay-paid.js";
+import { runPaymentTest } from "./test-payment.js";
+import { runPaymentClaimTest } from "./test-payment-claim.js";
+import { runPaymentFaultTest } from "./test-payment-fault.js";
import { runPaymentForgettableTest } from "./test-payment-forgettable.js";
-import { runPaymentIdempotencyTest } from "./test-payment-idempotency";
-import { runPaymentMultipleTest } from "./test-payment-multiple";
-import { runPaymentDemoTest } from "./test-payment-on-demo";
-import { runPaymentTransientTest } from "./test-payment-transient";
+import { runPaymentIdempotencyTest } from "./test-payment-idempotency.js";
+import { runPaymentMultipleTest } from "./test-payment-multiple.js";
+import { runPaymentDemoTest } from "./test-payment-on-demo.js";
+import { runPaymentTransientTest } from "./test-payment-transient.js";
import { runPaymentZeroTest } from "./test-payment-zero.js";
-import { runPaywallFlowTest } from "./test-paywall-flow";
+import { runPaywallFlowTest } from "./test-paywall-flow.js";
import { runPeerToPeerPullTest } from "./test-peer-to-peer-pull.js";
import { runPeerToPeerPushTest } from "./test-peer-to-peer-push.js";
-import { runRefundTest } from "./test-refund";
-import { runRefundAutoTest } from "./test-refund-auto";
-import { runRefundGoneTest } from "./test-refund-gone";
-import { runRefundIncrementalTest } from "./test-refund-incremental";
-import { runRevocationTest } from "./test-revocation";
-import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh";
-import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw";
-import { runTippingTest } from "./test-tipping";
-import { runWalletBackupBasicTest } from "./test-wallet-backup-basic";
-import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend";
+import { runRefundTest } from "./test-refund.js";
+import { runRefundAutoTest } from "./test-refund-auto.js";
+import { runRefundGoneTest } from "./test-refund-gone.js";
+import { runRefundIncrementalTest } from "./test-refund-incremental.js";
+import { runRevocationTest } from "./test-revocation.js";
+import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh.js";
+import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw.js";
+import { runTippingTest } from "./test-tipping.js";
+import { runWalletBackupBasicTest } from "./test-wallet-backup-basic.js";
+import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend.js";
import { runWalletDblessTest } from "./test-wallet-dbless.js";
-import { runWallettestingTest } from "./test-wallettesting";
-import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank";
-import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated";
+import { runWallettestingTest } from "./test-wallettesting.js";
+import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank.js";
+import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated.js";
import { runWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js";
-import { runTestWithdrawalManualTest } from "./test-withdrawal-manual";
+import { runTestWithdrawalManualTest } from "./test-withdrawal-manual.js";
/**
* Test runner.
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 7ba0fadc6..3f97be045 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1224,6 +1224,7 @@ export const enum WithdrawalRecordType {
BankIntegrated = "bank-integrated",
PeerPullCredit = "peer-pull-credit",
PeerPushCredit = "peer-push-credit",
+ Recoup = "recoup",
}
export interface WgInfoBankIntegrated {
@@ -1253,11 +1254,16 @@ export interface WgInfoBankPeerPush {
withdrawalType: WithdrawalRecordType.PeerPushCredit;
}
+export interface WgInfoBankRecoup {
+ withdrawalType: WithdrawalRecordType.Recoup;
+}
+
export type WgInfo =
| WgInfoBankIntegrated
| WgInfoBankManual
| WgInfoBankPeerPull
- | WgInfoBankPeerPush;
+ | WgInfoBankPeerPush
+ | WgInfoBankRecoup;
/**
* Group of withdrawal operations that need to be executed.
@@ -1287,6 +1293,8 @@ export interface WithdrawalGroupRecord {
/**
* The reserve private key.
+ *
+ * FIXME: Already in the reserves object store, redundant!
*/
reservePriv: string;
@@ -1355,9 +1363,9 @@ export interface WithdrawalGroupRecord {
denomSelUid: string;
/**
- * Retry info, always present even on completed operations so that indexing works.
+ * Retry info.
*/
- retryInfo: RetryInfo;
+ retryInfo?: RetryInfo;
lastError: TalerErrorDetail | undefined;
}
@@ -1386,6 +1394,8 @@ export interface RecoupGroupRecord {
*/
recoupGroupId: string;
+ exchangeBaseUrl: string;
+
timestampStarted: TalerProtocolTimestamp;
timestampFinished: TalerProtocolTimestamp | undefined;
@@ -1724,6 +1734,13 @@ export interface PeerPullPaymentIncomingRecord {
contractPriv: string;
}
+// FIXME: give this some smaller "row ID" to
+// reference in other records?
+export interface ReserveRecord {
+ reservePub: string;
+ reservePriv: string;
+}
+
export const WalletStoresV1 = {
coins: describeStore(
describeContents<CoinRecord>("coins", {
@@ -1735,6 +1752,12 @@ export const WalletStoresV1 = {
byCoinEvHash: describeIndex("byCoinEvHash", "coinEvHash"),
},
),
+ reserves: describeStore(
+ describeContents<ReserveRecord>("reserves", {
+ keyPath: "reservePub",
+ }),
+ {},
+ ),
config: describeStore(
describeContents<ConfigRecord>("config", { keyPath: "key" }),
{},
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts
index 0650ed040..e82bc139b 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -73,7 +73,6 @@ export interface MerchantOperations {
): Promise<MerchantInfo>;
}
-
/**
* Interface for exchange-related operations.
*/
@@ -113,6 +112,7 @@ export interface RecoupOperations {
refreshGroups: typeof WalletStoresV1.refreshGroups;
coins: typeof WalletStoresV1.coins;
}>,
+ exchangeBaseUrl: string,
coinPubs: string[],
): Promise<string>;
processRecoupGroup(
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,
diff --git a/packages/taler-wallet-core/src/pending-types.ts b/packages/taler-wallet-core/src/pending-types.ts
index e372a593d..39df9d0cb 100644
--- a/packages/taler-wallet-core/src/pending-types.ts
+++ b/packages/taler-wallet-core/src/pending-types.ts
@@ -182,7 +182,7 @@ export interface PendingRecoupTask {
export interface PendingWithdrawTask {
type: PendingTaskType.Withdraw;
lastError: TalerErrorDetail | undefined;
- retryInfo: RetryInfo;
+ retryInfo?: RetryInfo;
withdrawalGroupId: string;
}