cache denomination lookups
This commit is contained in:
parent
cea0ac02b6
commit
cd2473e1ad
@ -20,7 +20,7 @@
|
|||||||
* management, etc.).
|
* management, etc.).
|
||||||
*
|
*
|
||||||
* Some operations can be accessed via this state object. This allows mutual
|
* 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.
|
* the respective TypeScript files.
|
||||||
*
|
*
|
||||||
* (You can think of this as a "header file" for the wallet implementation.)
|
* (You can think of this as a "header file" for the wallet implementation.)
|
||||||
@ -29,7 +29,13 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* 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 { CryptoApi } from "./crypto/workers/cryptoApi.js";
|
||||||
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
||||||
import { PendingOperationsResponse } from "./pending-types.js";
|
import { PendingOperationsResponse } from "./pending-types.js";
|
||||||
@ -119,6 +125,64 @@ export interface RecoupOperations {
|
|||||||
): Promise<void>;
|
): 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;
|
export type NotificationListener = (n: WalletNotification) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,6 +226,15 @@ export interface InternalWalletState {
|
|||||||
merchantOps: MerchantOperations;
|
merchantOps: MerchantOperations;
|
||||||
reserveOps: ReserveOperations;
|
reserveOps: ReserveOperations;
|
||||||
|
|
||||||
|
getDenomInfo(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
tx: GetReadWriteAccess<{
|
||||||
|
denominations: typeof WalletStoresV1.denominations;
|
||||||
|
}>,
|
||||||
|
exchangeBaseUrl: string,
|
||||||
|
denomPubHash: string,
|
||||||
|
): Promise<DenomInfo | undefined>;
|
||||||
|
|
||||||
db: DbAccess<typeof WalletStoresV1>;
|
db: DbAccess<typeof WalletStoresV1>;
|
||||||
http: HttpRequestLibrary;
|
http: HttpRequestLibrary;
|
||||||
|
|
||||||
|
@ -35,10 +35,7 @@ import {
|
|||||||
PendingTaskType,
|
PendingTaskType,
|
||||||
ReserveType,
|
ReserveType,
|
||||||
} from "../pending-types.js";
|
} from "../pending-types.js";
|
||||||
import {
|
import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util";
|
||||||
getTimestampNow,
|
|
||||||
Timestamp,
|
|
||||||
} from "@gnu-taler/taler-util";
|
|
||||||
import { InternalWalletState } from "../common.js";
|
import { InternalWalletState } from "../common.js";
|
||||||
import { GetReadOnlyAccess } from "../util/query.js";
|
import { GetReadOnlyAccess } from "../util/query.js";
|
||||||
|
|
||||||
@ -74,35 +71,36 @@ async function gatherReservePending(
|
|||||||
now: Timestamp,
|
now: Timestamp,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.reserves.indexes.byStatus
|
const reserves = await tx.reserves.indexes.byStatus.getAll(
|
||||||
.iter(OperationStatus.Pending)
|
OperationStatus.Pending,
|
||||||
.forEach((reserve) => {
|
);
|
||||||
const reserveType = reserve.bankInfo
|
for (const reserve of reserves) {
|
||||||
? ReserveType.TalerBankWithdraw
|
const reserveType = reserve.bankInfo
|
||||||
: ReserveType.Manual;
|
? ReserveType.TalerBankWithdraw
|
||||||
switch (reserve.reserveStatus) {
|
: ReserveType.Manual;
|
||||||
case ReserveRecordStatus.DORMANT:
|
switch (reserve.reserveStatus) {
|
||||||
// nothing to report as pending
|
case ReserveRecordStatus.DORMANT:
|
||||||
break;
|
// nothing to report as pending
|
||||||
case ReserveRecordStatus.WAIT_CONFIRM_BANK:
|
break;
|
||||||
case ReserveRecordStatus.QUERYING_STATUS:
|
case ReserveRecordStatus.WAIT_CONFIRM_BANK:
|
||||||
case ReserveRecordStatus.REGISTERING_BANK:
|
case ReserveRecordStatus.QUERYING_STATUS:
|
||||||
resp.pendingOperations.push({
|
case ReserveRecordStatus.REGISTERING_BANK:
|
||||||
type: PendingTaskType.Reserve,
|
resp.pendingOperations.push({
|
||||||
givesLifeness: true,
|
type: PendingTaskType.Reserve,
|
||||||
timestampDue: reserve.retryInfo.nextRetry,
|
givesLifeness: true,
|
||||||
stage: reserve.reserveStatus,
|
timestampDue: reserve.retryInfo.nextRetry,
|
||||||
timestampCreated: reserve.timestampCreated,
|
stage: reserve.reserveStatus,
|
||||||
reserveType,
|
timestampCreated: reserve.timestampCreated,
|
||||||
reservePub: reserve.reservePub,
|
reserveType,
|
||||||
retryInfo: reserve.retryInfo,
|
reservePub: reserve.reservePub,
|
||||||
});
|
retryInfo: reserve.retryInfo,
|
||||||
break;
|
});
|
||||||
default:
|
break;
|
||||||
// FIXME: report problem!
|
default:
|
||||||
break;
|
// FIXME: report problem!
|
||||||
}
|
break;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gatherRefreshPending(
|
async function gatherRefreshPending(
|
||||||
@ -110,26 +108,27 @@ async function gatherRefreshPending(
|
|||||||
now: Timestamp,
|
now: Timestamp,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.refreshGroups.indexes.byStatus
|
const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(
|
||||||
.iter(OperationStatus.Pending)
|
OperationStatus.Pending,
|
||||||
.forEach((r) => {
|
);
|
||||||
if (r.timestampFinished) {
|
for (const r of refreshGroups) {
|
||||||
return;
|
if (r.timestampFinished) {
|
||||||
}
|
return;
|
||||||
if (r.frozen) {
|
}
|
||||||
return;
|
if (r.frozen) {
|
||||||
}
|
return;
|
||||||
resp.pendingOperations.push({
|
}
|
||||||
type: PendingTaskType.Refresh,
|
resp.pendingOperations.push({
|
||||||
givesLifeness: true,
|
type: PendingTaskType.Refresh,
|
||||||
timestampDue: r.retryInfo.nextRetry,
|
givesLifeness: true,
|
||||||
refreshGroupId: r.refreshGroupId,
|
timestampDue: r.retryInfo.nextRetry,
|
||||||
finishedPerCoin: r.statusPerCoin.map(
|
refreshGroupId: r.refreshGroupId,
|
||||||
(x) => x === RefreshCoinStatus.Finished,
|
finishedPerCoin: r.statusPerCoin.map(
|
||||||
),
|
(x) => x === RefreshCoinStatus.Finished,
|
||||||
retryInfo: r.retryInfo,
|
),
|
||||||
});
|
retryInfo: r.retryInfo,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gatherWithdrawalPending(
|
async function gatherWithdrawalPending(
|
||||||
@ -140,31 +139,32 @@ async function gatherWithdrawalPending(
|
|||||||
now: Timestamp,
|
now: Timestamp,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.withdrawalGroups.indexes.byStatus
|
const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
|
||||||
.iter(OperationStatus.Pending)
|
OperationStatus.Pending,
|
||||||
.forEachAsync(async (wsr) => {
|
);
|
||||||
if (wsr.timestampFinish) {
|
for (const wsr of wsrs) {
|
||||||
return;
|
if (wsr.timestampFinish) {
|
||||||
}
|
return;
|
||||||
let numCoinsWithdrawn = 0;
|
}
|
||||||
let numCoinsTotal = 0;
|
let numCoinsWithdrawn = 0;
|
||||||
await tx.planchets.indexes.byGroup
|
let numCoinsTotal = 0;
|
||||||
.iter(wsr.withdrawalGroupId)
|
await tx.planchets.indexes.byGroup
|
||||||
.forEach((x) => {
|
.iter(wsr.withdrawalGroupId)
|
||||||
numCoinsTotal++;
|
.forEach((x) => {
|
||||||
if (x.withdrawalDone) {
|
numCoinsTotal++;
|
||||||
numCoinsWithdrawn++;
|
if (x.withdrawalDone) {
|
||||||
}
|
numCoinsWithdrawn++;
|
||||||
});
|
}
|
||||||
resp.pendingOperations.push({
|
|
||||||
type: PendingTaskType.Withdraw,
|
|
||||||
givesLifeness: true,
|
|
||||||
timestampDue: wsr.retryInfo.nextRetry,
|
|
||||||
withdrawalGroupId: wsr.withdrawalGroupId,
|
|
||||||
lastError: wsr.lastError,
|
|
||||||
retryInfo: wsr.retryInfo,
|
|
||||||
});
|
});
|
||||||
|
resp.pendingOperations.push({
|
||||||
|
type: PendingTaskType.Withdraw,
|
||||||
|
givesLifeness: true,
|
||||||
|
timestampDue: wsr.retryInfo.nextRetry,
|
||||||
|
withdrawalGroupId: wsr.withdrawalGroupId,
|
||||||
|
lastError: wsr.lastError,
|
||||||
|
retryInfo: wsr.retryInfo,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gatherProposalPending(
|
async function gatherProposalPending(
|
||||||
@ -197,22 +197,23 @@ async function gatherDepositPending(
|
|||||||
now: Timestamp,
|
now: Timestamp,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await tx.depositGroups.indexes.byStatus
|
const dgs = await tx.depositGroups.indexes.byStatus.getAll(
|
||||||
.iter(OperationStatus.Pending)
|
OperationStatus.Pending,
|
||||||
.forEach((dg) => {
|
);
|
||||||
if (dg.timestampFinished) {
|
for (const dg of dgs) {
|
||||||
return;
|
if (dg.timestampFinished) {
|
||||||
}
|
return;
|
||||||
const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow();
|
}
|
||||||
resp.pendingOperations.push({
|
const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow();
|
||||||
type: PendingTaskType.Deposit,
|
resp.pendingOperations.push({
|
||||||
givesLifeness: true,
|
type: PendingTaskType.Deposit,
|
||||||
timestampDue,
|
givesLifeness: true,
|
||||||
depositGroupId: dg.depositGroupId,
|
timestampDue,
|
||||||
lastError: dg.lastError,
|
depositGroupId: dg.depositGroupId,
|
||||||
retryInfo: dg.retryInfo,
|
lastError: dg.lastError,
|
||||||
});
|
retryInfo: dg.retryInfo,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gatherTipPending(
|
async function gatherTipPending(
|
||||||
|
@ -975,13 +975,13 @@ async function processWithdrawGroupImpl(
|
|||||||
|
|
||||||
export async function getExchangeWithdrawalInfo(
|
export async function getExchangeWithdrawalInfo(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
baseUrl: string,
|
exchangeBaseUrl: string,
|
||||||
amount: AmountJson,
|
amount: AmountJson,
|
||||||
): Promise<ExchangeWithdrawDetails> {
|
): Promise<ExchangeWithdrawDetails> {
|
||||||
const { exchange, exchangeDetails } =
|
const { exchange, exchangeDetails } =
|
||||||
await ws.exchangeOps.updateExchangeFromUrl(ws, baseUrl);
|
await ws.exchangeOps.updateExchangeFromUrl(ws, exchangeBaseUrl);
|
||||||
await updateWithdrawalDenoms(ws, baseUrl);
|
await updateWithdrawalDenoms(ws, exchangeBaseUrl);
|
||||||
const denoms = await getCandidateWithdrawalDenoms(ws, baseUrl);
|
const denoms = await getCandidateWithdrawalDenoms(ws, exchangeBaseUrl);
|
||||||
const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
|
const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
|
||||||
const exchangeWireAccounts: string[] = [];
|
const exchangeWireAccounts: string[] = [];
|
||||||
for (const account of exchangeDetails.wireInfo.accounts) {
|
for (const account of exchangeDetails.wireInfo.accounts) {
|
||||||
@ -1006,9 +1006,10 @@ export async function getExchangeWithdrawalInfo(
|
|||||||
const possibleDenoms = await ws.db
|
const possibleDenoms = await ws.db
|
||||||
.mktx((x) => ({ denominations: x.denominations }))
|
.mktx((x) => ({ denominations: x.denominations }))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
return tx.denominations.indexes.byExchangeBaseUrl
|
const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
|
||||||
.iter()
|
exchangeBaseUrl,
|
||||||
.filter((d) => d.isOffered);
|
);
|
||||||
|
return ds.filter((x) => x.isOffered);
|
||||||
});
|
});
|
||||||
|
|
||||||
let versionMatch;
|
let versionMatch;
|
||||||
|
@ -103,6 +103,7 @@ import {
|
|||||||
processReserve,
|
processReserve,
|
||||||
} from "./operations/reserves.js";
|
} from "./operations/reserves.js";
|
||||||
import {
|
import {
|
||||||
|
DenomInfo,
|
||||||
ExchangeOperations,
|
ExchangeOperations,
|
||||||
InternalWalletState,
|
InternalWalletState,
|
||||||
MerchantInfo,
|
MerchantInfo,
|
||||||
@ -186,13 +187,12 @@ import {
|
|||||||
OpenedPromise,
|
OpenedPromise,
|
||||||
openPromise,
|
openPromise,
|
||||||
} from "./util/promiseUtils.js";
|
} from "./util/promiseUtils.js";
|
||||||
import { DbAccess } from "./util/query.js";
|
import { DbAccess, GetReadWriteAccess } from "./util/query.js";
|
||||||
import {
|
import {
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
readSuccessResponseJsonOrThrow,
|
readSuccessResponseJsonOrThrow,
|
||||||
} from "./util/http.js";
|
} from "./util/http.js";
|
||||||
import { getMerchantInfo } from "./operations/merchants.js";
|
import { getMerchantInfo } from "./operations/merchants.js";
|
||||||
import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
|
|
||||||
|
|
||||||
const builtinAuditors: AuditorTrustRecord[] = [
|
const builtinAuditors: AuditorTrustRecord[] = [
|
||||||
{
|
{
|
||||||
@ -506,24 +506,24 @@ async function listKnownBankAccounts(
|
|||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
currency?: string,
|
currency?: string,
|
||||||
): Promise<KnownBankAccounts> {
|
): Promise<KnownBankAccounts> {
|
||||||
const accounts: PaytoUri[] = []
|
const accounts: PaytoUri[] = [];
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
reserves: x.reserves,
|
reserves: x.reserves,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
const reservesRecords = await tx.reserves.iter().toArray()
|
const reservesRecords = await tx.reserves.iter().toArray();
|
||||||
for (const r of reservesRecords) {
|
for (const r of reservesRecords) {
|
||||||
if (currency && currency !== r.currency) {
|
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) {
|
if (payto) {
|
||||||
accounts.push(payto)
|
accounts.push(payto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
return { accounts }
|
return { accounts };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getExchanges(
|
async function getExchanges(
|
||||||
@ -785,9 +785,8 @@ async function dispatchRequestInternal(
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
case "getWithdrawalDetailsForAmount": {
|
case "getWithdrawalDetailsForAmount": {
|
||||||
const req = codecForGetWithdrawalDetailsForAmountRequest().decode(
|
const req =
|
||||||
payload,
|
codecForGetWithdrawalDetailsForAmountRequest().decode(payload);
|
||||||
);
|
|
||||||
return await getWithdrawalDetailsForAmount(
|
return await getWithdrawalDetailsForAmount(
|
||||||
ws,
|
ws,
|
||||||
req.exchangeBaseUrl,
|
req.exchangeBaseUrl,
|
||||||
@ -810,9 +809,8 @@ async function dispatchRequestInternal(
|
|||||||
return await applyRefund(ws, req.talerRefundUri);
|
return await applyRefund(ws, req.talerRefundUri);
|
||||||
}
|
}
|
||||||
case "acceptBankIntegratedWithdrawal": {
|
case "acceptBankIntegratedWithdrawal": {
|
||||||
const req = codecForAcceptBankIntegratedWithdrawalRequest().decode(
|
const req =
|
||||||
payload,
|
codecForAcceptBankIntegratedWithdrawalRequest().decode(payload);
|
||||||
);
|
|
||||||
return await acceptWithdrawal(
|
return await acceptWithdrawal(
|
||||||
ws,
|
ws,
|
||||||
req.talerWithdrawUri,
|
req.talerWithdrawUri,
|
||||||
@ -1048,7 +1046,7 @@ export async function handleCoreApiRequest(
|
|||||||
try {
|
try {
|
||||||
logger.error("Caught unexpected exception:");
|
logger.error("Caught unexpected exception:");
|
||||||
logger.error(e.stack);
|
logger.error(e.stack);
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
return {
|
return {
|
||||||
type: "error",
|
type: "error",
|
||||||
operation,
|
operation,
|
||||||
@ -1133,7 +1131,8 @@ export class Wallet {
|
|||||||
class InternalWalletStateImpl implements InternalWalletState {
|
class InternalWalletStateImpl implements InternalWalletState {
|
||||||
memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> = new AsyncOpMemoSingle();
|
memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> =
|
||||||
|
new AsyncOpMemoSingle();
|
||||||
memoGetBalance: AsyncOpMemoSingle<BalancesResponse> = new AsyncOpMemoSingle();
|
memoGetBalance: AsyncOpMemoSingle<BalancesResponse> = new AsyncOpMemoSingle();
|
||||||
memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
@ -1169,7 +1168,10 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
|
|
||||||
reserveOps: ReserveOperations = {
|
reserveOps: ReserveOperations = {
|
||||||
processReserve: processReserve,
|
processReserve: processReserve,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// FIXME: Use an LRU cache here.
|
||||||
|
private denomCache: Record<string, DenomInfo> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promises that are waiting for a particular resource.
|
* Promises that are waiting for a particular resource.
|
||||||
@ -1193,6 +1195,22 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
|
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 {
|
notify(n: WalletNotification): void {
|
||||||
logger.trace("Notification", n);
|
logger.trace("Notification", n);
|
||||||
for (const l of this.listeners) {
|
for (const l of this.listeners) {
|
||||||
|
Loading…
Reference in New Issue
Block a user