aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-01-12 16:54:38 +0100
committerFlorian Dold <florian@dold.me>2022-01-12 16:54:38 +0100
commit9f6e398884ee158cd22c685ce308043a25234095 (patch)
tree50afccf96d5b207c16f5280d31d8b906a9cf79d0
parentdd66e43b3ceb3e393f399ac5544941de8912c59d (diff)
downloadwallet-core-9f6e398884ee158cd22c685ce308043a25234095.tar.xz
do withdrawal with fewer DB accesses
-rw-r--r--packages/taler-wallet-core/src/db.ts28
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts208
-rw-r--r--packages/taler-wallet-core/src/pending-types.ts5
4 files changed, 124 insertions, 123 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 7f6b08e12..0b2d16ae7 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -158,7 +158,7 @@ export interface ReserveRecord {
*
* Only applies if bankWithdrawStatusUrl is defined.
*
- * Set to 0 if that hasn't happened yet.
+ * Set to undefined if that hasn't happened yet.
*/
timestampReserveInfoPosted: Timestamp | undefined;
@@ -210,7 +210,7 @@ export interface ReserveRecord {
/**
* Is there any work to be done for this reserve?
- *
+ *
* FIXME: Technically redundant, since the reserveStatus would indicate this.
*/
operationStatus: OperationStatus;
@@ -1341,6 +1341,9 @@ export interface DenomSelectionState {
* the coin selection we want to withdraw.
*/
export interface WithdrawalGroupRecord {
+ /**
+ * Unique identifier for the withdrawal group.
+ */
withdrawalGroupId: string;
/**
@@ -1348,8 +1351,15 @@ export interface WithdrawalGroupRecord {
*/
secretSeed: string;
+ /**
+ * Public key of the reserve that we're withdrawing from.
+ */
reservePub: string;
+ /**
+ * The exchange base URL that we're withdrawing from.
+ * (Redundantly stored, as the reserve record also has this info.)
+ */
exchangeBaseUrl: string;
/**
@@ -1363,6 +1373,10 @@ export interface WithdrawalGroupRecord {
*/
timestampFinish?: Timestamp;
+ /**
+ * Operation status of the withdrawal group.
+ * Used for indexing in the database.
+ */
operationStatus: OperationStatus;
/**
@@ -1371,8 +1385,18 @@ export interface WithdrawalGroupRecord {
*/
rawWithdrawalAmount: AmountJson;
+ /**
+ * Denominations selected for withdrawal.
+ */
denomsSel: DenomSelectionState;
+ /**
+ * UID of the denomination selection.
+ *
+ * Used for merging backups.
+ *
+ * FIXME: Should this not also include a timestamp for more logical merging?
+ */
denomSelUid: string;
/**
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index 07c29e874..99d275836 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -37,13 +37,9 @@ import {
} from "../pending-types.js";
import {
getTimestampNow,
- isTimestampExpired,
- j2s,
- Logger,
Timestamp,
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../common.js";
-import { getBalancesInsideTransaction } from "./balance.js";
import { GetReadOnlyAccess } from "../util/query.js";
async function gatherExchangePending(
@@ -353,9 +349,7 @@ export async function getPendingOperations(
recoupGroups: x.recoupGroups,
}))
.runReadWrite(async (tx) => {
- const walletBalance = await getBalancesInsideTransaction(ws, tx);
const resp: PendingOperationsResponse = {
- walletBalance,
pendingOperations: [],
};
await gatherExchangePending(tx, now, resp);
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index c44435e81..dd8a90ad9 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -55,6 +55,7 @@ import {
ExchangeRecord,
OperationStatus,
PlanchetRecord,
+ WithdrawalGroupRecord,
} from "../db.js";
import { walletCoreDebugFlags } from "../util/debugFlags.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
@@ -73,7 +74,7 @@ import {
/**
* Logger for this file.
*/
-const logger = new Logger("withdraw.ts");
+const logger = new Logger("operations/withdraw.ts");
/**
* FIXME: Eliminate this in favor of DenomSelectionState.
@@ -351,106 +352,95 @@ export async function getCandidateWithdrawalDenoms(
*/
async function processPlanchetGenerate(
ws: InternalWalletState,
- withdrawalGroupId: string,
+ withdrawalGroup: WithdrawalGroupRecord,
coinIdx: number,
): Promise<void> {
- const withdrawalGroup = await ws.db
- .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
- .runReadOnly(async (tx) => {
- return await tx.withdrawalGroups.get(withdrawalGroupId);
- });
- if (!withdrawalGroup) {
- return;
- }
let planchet = await ws.db
.mktx((x) => ({
planchets: x.planchets,
}))
.runReadOnly(async (tx) => {
return tx.planchets.indexes.byGroupAndIndex.get([
- withdrawalGroupId,
+ withdrawalGroup.withdrawalGroupId,
coinIdx,
]);
});
- if (!planchet) {
- let ci = 0;
- let denomPubHash: string | undefined;
- for (
- let di = 0;
- di < withdrawalGroup.denomsSel.selectedDenoms.length;
- di++
- ) {
- const d = withdrawalGroup.denomsSel.selectedDenoms[di];
- if (coinIdx >= ci && coinIdx < ci + d.count) {
- denomPubHash = d.denomPubHash;
- break;
- }
- ci += d.count;
- }
- if (!denomPubHash) {
- throw Error("invariant violated");
+ if (planchet) {
+ return;
+ }
+ let ci = 0;
+ let denomPubHash: string | undefined;
+ for (let di = 0; di < withdrawalGroup.denomsSel.selectedDenoms.length; di++) {
+ const d = withdrawalGroup.denomsSel.selectedDenoms[di];
+ if (coinIdx >= ci && coinIdx < ci + d.count) {
+ denomPubHash = d.denomPubHash;
+ break;
}
+ ci += d.count;
+ }
+ if (!denomPubHash) {
+ throw Error("invariant violated");
+ }
- const { denom, reserve } = await ws.db
- .mktx((x) => ({
- reserves: x.reserves,
- denominations: x.denominations,
- }))
- .runReadOnly(async (tx) => {
- const denom = await tx.denominations.get([
- withdrawalGroup.exchangeBaseUrl,
- denomPubHash!,
- ]);
- if (!denom) {
- throw Error("invariant violated");
- }
- const reserve = await tx.reserves.get(withdrawalGroup.reservePub);
- if (!reserve) {
- throw Error("invariant violated");
- }
- return { denom, reserve };
- });
- const r = await ws.cryptoApi.createPlanchet({
- denomPub: denom.denomPub,
- feeWithdraw: denom.feeWithdraw,
- reservePriv: reserve.reservePriv,
- reservePub: reserve.reservePub,
- value: denom.value,
- coinIndex: coinIdx,
- secretSeed: withdrawalGroup.secretSeed,
+ const { denom, reserve } = await ws.db
+ .mktx((x) => ({
+ reserves: x.reserves,
+ denominations: x.denominations,
+ }))
+ .runReadOnly(async (tx) => {
+ const denom = await tx.denominations.get([
+ withdrawalGroup.exchangeBaseUrl,
+ denomPubHash!,
+ ]);
+ if (!denom) {
+ throw Error("invariant violated");
+ }
+ const reserve = await tx.reserves.get(withdrawalGroup.reservePub);
+ if (!reserve) {
+ throw Error("invariant violated");
+ }
+ return { denom, reserve };
+ });
+ const r = await ws.cryptoApi.createPlanchet({
+ denomPub: denom.denomPub,
+ feeWithdraw: denom.feeWithdraw,
+ reservePriv: reserve.reservePriv,
+ reservePub: reserve.reservePub,
+ value: denom.value,
+ coinIndex: coinIdx,
+ secretSeed: withdrawalGroup.secretSeed,
+ });
+ const newPlanchet: PlanchetRecord = {
+ blindingKey: r.blindingKey,
+ coinEv: r.coinEv,
+ coinEvHash: r.coinEvHash,
+ coinIdx,
+ coinPriv: r.coinPriv,
+ coinPub: r.coinPub,
+ coinValue: r.coinValue,
+ denomPub: r.denomPub,
+ denomPubHash: r.denomPubHash,
+ isFromTip: false,
+ reservePub: r.reservePub,
+ withdrawalDone: false,
+ withdrawSig: r.withdrawSig,
+ withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
+ lastError: undefined,
+ };
+ await ws.db
+ .mktx((x) => ({ planchets: x.planchets }))
+ .runReadWrite(async (tx) => {
+ const p = await tx.planchets.indexes.byGroupAndIndex.get([
+ withdrawalGroup.withdrawalGroupId,
+ coinIdx,
+ ]);
+ if (p) {
+ planchet = p;
+ return;
+ }
+ await tx.planchets.put(newPlanchet);
+ planchet = newPlanchet;
});
- const newPlanchet: PlanchetRecord = {
- blindingKey: r.blindingKey,
- coinEv: r.coinEv,
- coinEvHash: r.coinEvHash,
- coinIdx,
- coinPriv: r.coinPriv,
- coinPub: r.coinPub,
- coinValue: r.coinValue,
- denomPub: r.denomPub,
- denomPubHash: r.denomPubHash,
- isFromTip: false,
- reservePub: r.reservePub,
- withdrawalDone: false,
- withdrawSig: r.withdrawSig,
- withdrawalGroupId: withdrawalGroupId,
- lastError: undefined,
- };
- await ws.db
- .mktx((x) => ({ planchets: x.planchets }))
- .runReadWrite(async (tx) => {
- const p = await tx.planchets.indexes.byGroupAndIndex.get([
- withdrawalGroupId,
- coinIdx,
- ]);
- if (p) {
- planchet = p;
- return;
- }
- await tx.planchets.put(newPlanchet);
- planchet = newPlanchet;
- });
- }
}
/**
@@ -460,7 +450,7 @@ async function processPlanchetGenerate(
*/
async function processPlanchetExchangeRequest(
ws: InternalWalletState,
- withdrawalGroupId: string,
+ withdrawalGroup: WithdrawalGroupRecord,
coinIdx: number,
): Promise<WithdrawResponse | undefined> {
const d = await ws.db
@@ -471,12 +461,8 @@ async function processPlanchetExchangeRequest(
denominations: x.denominations,
}))
.runReadOnly(async (tx) => {
- const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!withdrawalGroup) {
- return;
- }
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
- withdrawalGroupId,
+ withdrawalGroup.withdrawalGroupId,
coinIdx,
]);
if (!planchet) {
@@ -503,7 +489,7 @@ async function processPlanchetExchangeRequest(
}
logger.trace(
- `processing planchet #${coinIdx} in withdrawal ${withdrawalGroupId}`,
+ `processing planchet #${coinIdx} in withdrawal ${withdrawalGroup.withdrawalGroupId}`,
);
const reqBody: any = {
@@ -543,7 +529,7 @@ async function processPlanchetExchangeRequest(
.mktx((x) => ({ planchets: x.planchets }))
.runReadWrite(async (tx) => {
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
- withdrawalGroupId,
+ withdrawalGroup.withdrawalGroupId,
coinIdx,
]);
if (!planchet) {
@@ -558,7 +544,7 @@ async function processPlanchetExchangeRequest(
async function processPlanchetVerifyAndStoreCoin(
ws: InternalWalletState,
- withdrawalGroupId: string,
+ withdrawalGroup: WithdrawalGroupRecord,
coinIdx: number,
resp: WithdrawResponse,
): Promise<void> {
@@ -568,12 +554,8 @@ async function processPlanchetVerifyAndStoreCoin(
planchets: x.planchets,
}))
.runReadOnly(async (tx) => {
- const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!withdrawalGroup) {
- return;
- }
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
- withdrawalGroupId,
+ withdrawalGroup.withdrawalGroupId,
coinIdx,
]);
if (!planchet) {
@@ -635,7 +617,7 @@ async function processPlanchetVerifyAndStoreCoin(
.mktx((x) => ({ planchets: x.planchets }))
.runReadWrite(async (tx) => {
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
- withdrawalGroupId,
+ withdrawalGroup.withdrawalGroupId,
coinIdx,
]);
if (!planchet) {
@@ -679,7 +661,7 @@ async function processPlanchetVerifyAndStoreCoin(
type: CoinSourceType.Withdraw,
coinIndex: coinIdx,
reservePub: planchet.reservePub,
- withdrawalGroupId: withdrawalGroupId,
+ withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
},
suspended: false,
};
@@ -694,10 +676,6 @@ async function processPlanchetVerifyAndStoreCoin(
planchets: x.planchets,
}))
.runReadWrite(async (tx) => {
- const ws = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!ws) {
- return false;
- }
const p = await tx.planchets.get(planchetCoinPub);
if (!p || p.withdrawalDone) {
return false;
@@ -914,7 +892,7 @@ async function processWithdrawGroupImpl(
let work: Promise<void>[] = [];
for (let i = 0; i < numTotalCoins; i++) {
- work.push(processPlanchetGenerate(ws, withdrawalGroupId, i));
+ work.push(processPlanchetGenerate(ws, withdrawalGroup, i));
}
// Generate coins concurrently (parallelism only happens in the crypto API workers)
@@ -925,14 +903,14 @@ async function processWithdrawGroupImpl(
for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
const resp = await processPlanchetExchangeRequest(
ws,
- withdrawalGroupId,
+ withdrawalGroup,
coinIdx,
);
if (!resp) {
continue;
}
work.push(
- processPlanchetVerifyAndStoreCoin(ws, withdrawalGroupId, coinIdx, resp),
+ processPlanchetVerifyAndStoreCoin(ws, withdrawalGroup, coinIdx, resp),
);
}
@@ -1089,6 +1067,13 @@ export async function getExchangeWithdrawalInfo(
return ret;
}
+/**
+ * Get more information about a taler://withdraw URI.
+ *
+ * As side effects, the bank (via the bank integration API) is queried
+ * and the exchange suggested by the bank is permanently added
+ * to the wallet's list of known exchanges.
+ */
export async function getWithdrawalDetailsForUri(
ws: InternalWalletState,
talerWithdrawUri: string,
@@ -1110,6 +1095,9 @@ export async function getWithdrawalDetailsForUri(
}
}
+ // Extract information about possible exchanges for the withdrawal
+ // operation from the database.
+
const exchanges: ExchangeListItem[] = [];
await ws.db
diff --git a/packages/taler-wallet-core/src/pending-types.ts b/packages/taler-wallet-core/src/pending-types.ts
index 5033163a1..911d0d8bd 100644
--- a/packages/taler-wallet-core/src/pending-types.ts
+++ b/packages/taler-wallet-core/src/pending-types.ts
@@ -248,9 +248,4 @@ export interface PendingOperationsResponse {
* List of pending operations.
*/
pendingOperations: PendingTaskInfo[];
-
- /**
- * Current wallet balance, including pending balances.
- */
- walletBalance: BalancesResponse;
}