cache denomination lookups
This commit is contained in:
parent
cea0ac02b6
commit
cd2473e1ad
@ -20,7 +20,7 @@
|
||||
* management, etc.).
|
||||
*
|
||||
* Some operations can be accessed via this state object. This allows mutual
|
||||
* recursion between operations, without having cycling dependencies between
|
||||
* recursion between operations, without having cyclic dependencies between
|
||||
* the respective TypeScript files.
|
||||
*
|
||||
* (You can think of this as a "header file" for the wallet implementation.)
|
||||
@ -29,7 +29,13 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { WalletNotification, BalancesResponse } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
WalletNotification,
|
||||
BalancesResponse,
|
||||
AmountJson,
|
||||
DenominationPubKey,
|
||||
Timestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { CryptoApi } from "./crypto/workers/cryptoApi.js";
|
||||
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
||||
import { PendingOperationsResponse } from "./pending-types.js";
|
||||
@ -119,6 +125,64 @@ export interface RecoupOperations {
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface DenomInfo {
|
||||
/**
|
||||
* Value of one coin of the denomination.
|
||||
*/
|
||||
value: AmountJson;
|
||||
|
||||
/**
|
||||
* The denomination public key.
|
||||
*/
|
||||
denomPub: DenominationPubKey;
|
||||
|
||||
/**
|
||||
* Hash of the denomination public key.
|
||||
* Stored in the database for faster lookups.
|
||||
*/
|
||||
denomPubHash: string;
|
||||
|
||||
/**
|
||||
* Fee for withdrawing.
|
||||
*/
|
||||
feeWithdraw: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for depositing.
|
||||
*/
|
||||
feeDeposit: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refreshing.
|
||||
*/
|
||||
feeRefresh: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refunding.
|
||||
*/
|
||||
feeRefund: AmountJson;
|
||||
|
||||
/**
|
||||
* Validity start date of the denomination.
|
||||
*/
|
||||
stampStart: Timestamp;
|
||||
|
||||
/**
|
||||
* Date after which the currency can't be withdrawn anymore.
|
||||
*/
|
||||
stampExpireWithdraw: Timestamp;
|
||||
|
||||
/**
|
||||
* Date after the denomination officially doesn't exist anymore.
|
||||
*/
|
||||
stampExpireLegal: Timestamp;
|
||||
|
||||
/**
|
||||
* Data after which coins of this denomination can't be deposited anymore.
|
||||
*/
|
||||
stampExpireDeposit: Timestamp;
|
||||
}
|
||||
|
||||
export type NotificationListener = (n: WalletNotification) => void;
|
||||
|
||||
/**
|
||||
@ -162,6 +226,15 @@ export interface InternalWalletState {
|
||||
merchantOps: MerchantOperations;
|
||||
reserveOps: ReserveOperations;
|
||||
|
||||
getDenomInfo(
|
||||
ws: InternalWalletState,
|
||||
tx: GetReadWriteAccess<{
|
||||
denominations: typeof WalletStoresV1.denominations;
|
||||
}>,
|
||||
exchangeBaseUrl: string,
|
||||
denomPubHash: string,
|
||||
): Promise<DenomInfo | undefined>;
|
||||
|
||||
db: DbAccess<typeof WalletStoresV1>;
|
||||
http: HttpRequestLibrary;
|
||||
|
||||
|
@ -35,10 +35,7 @@ import {
|
||||
PendingTaskType,
|
||||
ReserveType,
|
||||
} from "../pending-types.js";
|
||||
import {
|
||||
getTimestampNow,
|
||||
Timestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util";
|
||||
import { InternalWalletState } from "../common.js";
|
||||
import { GetReadOnlyAccess } from "../util/query.js";
|
||||
|
||||
@ -74,9 +71,10 @@ async function gatherReservePending(
|
||||
now: Timestamp,
|
||||
resp: PendingOperationsResponse,
|
||||
): Promise<void> {
|
||||
await tx.reserves.indexes.byStatus
|
||||
.iter(OperationStatus.Pending)
|
||||
.forEach((reserve) => {
|
||||
const reserves = await tx.reserves.indexes.byStatus.getAll(
|
||||
OperationStatus.Pending,
|
||||
);
|
||||
for (const reserve of reserves) {
|
||||
const reserveType = reserve.bankInfo
|
||||
? ReserveType.TalerBankWithdraw
|
||||
: ReserveType.Manual;
|
||||
@ -102,7 +100,7 @@ async function gatherReservePending(
|
||||
// FIXME: report problem!
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function gatherRefreshPending(
|
||||
@ -110,9 +108,10 @@ async function gatherRefreshPending(
|
||||
now: Timestamp,
|
||||
resp: PendingOperationsResponse,
|
||||
): Promise<void> {
|
||||
await tx.refreshGroups.indexes.byStatus
|
||||
.iter(OperationStatus.Pending)
|
||||
.forEach((r) => {
|
||||
const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(
|
||||
OperationStatus.Pending,
|
||||
);
|
||||
for (const r of refreshGroups) {
|
||||
if (r.timestampFinished) {
|
||||
return;
|
||||
}
|
||||
@ -129,7 +128,7 @@ async function gatherRefreshPending(
|
||||
),
|
||||
retryInfo: r.retryInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function gatherWithdrawalPending(
|
||||
@ -140,9 +139,10 @@ async function gatherWithdrawalPending(
|
||||
now: Timestamp,
|
||||
resp: PendingOperationsResponse,
|
||||
): Promise<void> {
|
||||
await tx.withdrawalGroups.indexes.byStatus
|
||||
.iter(OperationStatus.Pending)
|
||||
.forEachAsync(async (wsr) => {
|
||||
const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
|
||||
OperationStatus.Pending,
|
||||
);
|
||||
for (const wsr of wsrs) {
|
||||
if (wsr.timestampFinish) {
|
||||
return;
|
||||
}
|
||||
@ -164,7 +164,7 @@ async function gatherWithdrawalPending(
|
||||
lastError: wsr.lastError,
|
||||
retryInfo: wsr.retryInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function gatherProposalPending(
|
||||
@ -197,9 +197,10 @@ async function gatherDepositPending(
|
||||
now: Timestamp,
|
||||
resp: PendingOperationsResponse,
|
||||
): Promise<void> {
|
||||
await tx.depositGroups.indexes.byStatus
|
||||
.iter(OperationStatus.Pending)
|
||||
.forEach((dg) => {
|
||||
const dgs = await tx.depositGroups.indexes.byStatus.getAll(
|
||||
OperationStatus.Pending,
|
||||
);
|
||||
for (const dg of dgs) {
|
||||
if (dg.timestampFinished) {
|
||||
return;
|
||||
}
|
||||
@ -212,7 +213,7 @@ async function gatherDepositPending(
|
||||
lastError: dg.lastError,
|
||||
retryInfo: dg.retryInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function gatherTipPending(
|
||||
|
@ -975,13 +975,13 @@ async function processWithdrawGroupImpl(
|
||||
|
||||
export async function getExchangeWithdrawalInfo(
|
||||
ws: InternalWalletState,
|
||||
baseUrl: string,
|
||||
exchangeBaseUrl: string,
|
||||
amount: AmountJson,
|
||||
): Promise<ExchangeWithdrawDetails> {
|
||||
const { exchange, exchangeDetails } =
|
||||
await ws.exchangeOps.updateExchangeFromUrl(ws, baseUrl);
|
||||
await updateWithdrawalDenoms(ws, baseUrl);
|
||||
const denoms = await getCandidateWithdrawalDenoms(ws, baseUrl);
|
||||
await ws.exchangeOps.updateExchangeFromUrl(ws, exchangeBaseUrl);
|
||||
await updateWithdrawalDenoms(ws, exchangeBaseUrl);
|
||||
const denoms = await getCandidateWithdrawalDenoms(ws, exchangeBaseUrl);
|
||||
const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
|
||||
const exchangeWireAccounts: string[] = [];
|
||||
for (const account of exchangeDetails.wireInfo.accounts) {
|
||||
@ -1006,9 +1006,10 @@ export async function getExchangeWithdrawalInfo(
|
||||
const possibleDenoms = await ws.db
|
||||
.mktx((x) => ({ denominations: x.denominations }))
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.denominations.indexes.byExchangeBaseUrl
|
||||
.iter()
|
||||
.filter((d) => d.isOffered);
|
||||
const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
|
||||
exchangeBaseUrl,
|
||||
);
|
||||
return ds.filter((x) => x.isOffered);
|
||||
});
|
||||
|
||||
let versionMatch;
|
||||
|
@ -103,6 +103,7 @@ import {
|
||||
processReserve,
|
||||
} from "./operations/reserves.js";
|
||||
import {
|
||||
DenomInfo,
|
||||
ExchangeOperations,
|
||||
InternalWalletState,
|
||||
MerchantInfo,
|
||||
@ -186,13 +187,12 @@ import {
|
||||
OpenedPromise,
|
||||
openPromise,
|
||||
} from "./util/promiseUtils.js";
|
||||
import { DbAccess } from "./util/query.js";
|
||||
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
|
||||
import {
|
||||
HttpRequestLibrary,
|
||||
readSuccessResponseJsonOrThrow,
|
||||
} from "./util/http.js";
|
||||
import { getMerchantInfo } from "./operations/merchants.js";
|
||||
import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
|
||||
|
||||
const builtinAuditors: AuditorTrustRecord[] = [
|
||||
{
|
||||
@ -506,24 +506,24 @@ async function listKnownBankAccounts(
|
||||
ws: InternalWalletState,
|
||||
currency?: string,
|
||||
): Promise<KnownBankAccounts> {
|
||||
const accounts: PaytoUri[] = []
|
||||
const accounts: PaytoUri[] = [];
|
||||
await ws.db
|
||||
.mktx((x) => ({
|
||||
reserves: x.reserves,
|
||||
}))
|
||||
.runReadOnly(async (tx) => {
|
||||
const reservesRecords = await tx.reserves.iter().toArray()
|
||||
const reservesRecords = await tx.reserves.iter().toArray();
|
||||
for (const r of reservesRecords) {
|
||||
if (currency && currency !== r.currency) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
const payto = r.senderWire ? parsePaytoUri(r.senderWire) : undefined
|
||||
const payto = r.senderWire ? parsePaytoUri(r.senderWire) : undefined;
|
||||
if (payto) {
|
||||
accounts.push(payto)
|
||||
accounts.push(payto);
|
||||
}
|
||||
}
|
||||
})
|
||||
return { accounts }
|
||||
});
|
||||
return { accounts };
|
||||
}
|
||||
|
||||
async function getExchanges(
|
||||
@ -785,9 +785,8 @@ async function dispatchRequestInternal(
|
||||
return res;
|
||||
}
|
||||
case "getWithdrawalDetailsForAmount": {
|
||||
const req = codecForGetWithdrawalDetailsForAmountRequest().decode(
|
||||
payload,
|
||||
);
|
||||
const req =
|
||||
codecForGetWithdrawalDetailsForAmountRequest().decode(payload);
|
||||
return await getWithdrawalDetailsForAmount(
|
||||
ws,
|
||||
req.exchangeBaseUrl,
|
||||
@ -810,9 +809,8 @@ async function dispatchRequestInternal(
|
||||
return await applyRefund(ws, req.talerRefundUri);
|
||||
}
|
||||
case "acceptBankIntegratedWithdrawal": {
|
||||
const req = codecForAcceptBankIntegratedWithdrawalRequest().decode(
|
||||
payload,
|
||||
);
|
||||
const req =
|
||||
codecForAcceptBankIntegratedWithdrawalRequest().decode(payload);
|
||||
return await acceptWithdrawal(
|
||||
ws,
|
||||
req.talerWithdrawUri,
|
||||
@ -1048,7 +1046,7 @@ export async function handleCoreApiRequest(
|
||||
try {
|
||||
logger.error("Caught unexpected exception:");
|
||||
logger.error(e.stack);
|
||||
} catch (e) { }
|
||||
} catch (e) {}
|
||||
return {
|
||||
type: "error",
|
||||
operation,
|
||||
@ -1133,7 +1131,8 @@ export class Wallet {
|
||||
class InternalWalletStateImpl implements InternalWalletState {
|
||||
memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||
memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||
memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> = new AsyncOpMemoSingle();
|
||||
memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> =
|
||||
new AsyncOpMemoSingle();
|
||||
memoGetBalance: AsyncOpMemoSingle<BalancesResponse> = new AsyncOpMemoSingle();
|
||||
memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||
memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||
@ -1169,7 +1168,10 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
|
||||
reserveOps: ReserveOperations = {
|
||||
processReserve: processReserve,
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: Use an LRU cache here.
|
||||
private denomCache: Record<string, DenomInfo> = {};
|
||||
|
||||
/**
|
||||
* Promises that are waiting for a particular resource.
|
||||
@ -1193,6 +1195,22 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
|
||||
}
|
||||
|
||||
async getDenomInfo(
|
||||
ws: InternalWalletState,
|
||||
tx: GetReadWriteAccess<{
|
||||
denominations: typeof WalletStoresV1.denominations;
|
||||
}>,
|
||||
exchangeBaseUrl: string,
|
||||
denomPubHash: string,
|
||||
): Promise<DenomInfo | undefined> {
|
||||
const key = `${exchangeBaseUrl}:${denomPubHash}`;
|
||||
const cached = this.denomCache[key];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
return await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
|
||||
}
|
||||
|
||||
notify(n: WalletNotification): void {
|
||||
logger.trace("Notification", n);
|
||||
for (const l of this.listeners) {
|
||||
|
Loading…
Reference in New Issue
Block a user