From 9f6e398884ee158cd22c685ce308043a25234095 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 12 Jan 2022 16:54:38 +0100 Subject: do withdrawal with fewer DB accesses --- packages/taler-wallet-core/src/db.ts | 28 ++- .../taler-wallet-core/src/operations/pending.ts | 6 - .../taler-wallet-core/src/operations/withdraw.ts | 208 ++++++++++----------- packages/taler-wallet-core/src/pending-types.ts | 5 - 4 files changed, 124 insertions(+), 123 deletions(-) (limited to 'packages/taler-wallet-core') 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 { - 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 { 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 { @@ -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[] = []; 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; } -- cgit v1.2.3