summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/util
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-02-19 12:49:17 +0100
committerFlorian Dold <florian@dold.me>2024-02-19 17:47:35 +0100
commit1ec521b9d214b286e747b3ccb3113730ac3a2509 (patch)
tree2f3d2b2906810dca45859b8cbfb8d18d53b27e80 /packages/taler-wallet-core/src/util
parent1034ecb5f20bd8c75e37e0b4b454ea6c1f4c1da6 (diff)
downloadwallet-core-1ec521b9d214b286e747b3ccb3113730ac3a2509.tar.gz
wallet-core-1ec521b9d214b286e747b3ccb3113730ac3a2509.tar.bz2
wallet-core-1ec521b9d214b286e747b3ccb3113730ac3a2509.zip
wallet-core: simplify/unify DB access
Diffstat (limited to 'packages/taler-wallet-core/src/util')
-rw-r--r--packages/taler-wallet-core/src/util/coinSelection.ts79
-rw-r--r--packages/taler-wallet-core/src/util/instructedAmountConversion.ts14
-rw-r--r--packages/taler-wallet-core/src/util/query.ts209
3 files changed, 75 insertions, 227 deletions
diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts
index be868867d..0f6316bce 100644
--- a/packages/taler-wallet-core/src/util/coinSelection.ts
+++ b/packages/taler-wallet-core/src/util/coinSelection.ts
@@ -58,7 +58,7 @@ import { DenominationRecord } from "../db.js";
import {
getExchangeWireDetailsInTx,
isWithdrawableDenom,
- WalletDbReadOnlyTransactionArr,
+ WalletDbReadOnlyTransaction,
} from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import {
@@ -359,33 +359,31 @@ export async function selectPayCoinsNew(
logger.trace(`coin selection request ${j2s(req)}`);
logger.trace(`selected coins (via denoms) for payment: ${j2s(finalSel)}`);
- await ws.db
- .mktx((x) => [x.coins, x.denominations])
- .runReadOnly(async (tx) => {
- for (const dph of Object.keys(finalSel)) {
- const selInfo = finalSel[dph];
- const numRequested = selInfo.contributions.length;
- const query = [
- selInfo.exchangeBaseUrl,
- selInfo.denomPubHash,
- selInfo.maxAge,
- CoinStatus.Fresh,
- ];
- logger.trace(`query: ${j2s(query)}`);
- const coins =
- await tx.coins.indexes.byExchangeDenomPubHashAndAgeAndStatus.getAll(
- query,
- numRequested,
- );
- if (coins.length != numRequested) {
- throw Error(
- `coin selection failed (not available anymore, got only ${coins.length}/${numRequested})`,
- );
- }
- coinPubs.push(...coins.map((x) => x.coinPub));
- coinContributions.push(...selInfo.contributions);
+ await ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => {
+ for (const dph of Object.keys(finalSel)) {
+ const selInfo = finalSel[dph];
+ const numRequested = selInfo.contributions.length;
+ const query = [
+ selInfo.exchangeBaseUrl,
+ selInfo.denomPubHash,
+ selInfo.maxAge,
+ CoinStatus.Fresh,
+ ];
+ logger.trace(`query: ${j2s(query)}`);
+ const coins =
+ await tx.coins.indexes.byExchangeDenomPubHashAndAgeAndStatus.getAll(
+ query,
+ numRequested,
+ );
+ if (coins.length != numRequested) {
+ throw Error(
+ `coin selection failed (not available anymore, got only ${coins.length}/${numRequested})`,
+ );
}
- });
+ coinPubs.push(...coins.map((x) => x.coinPub));
+ coinContributions.push(...selInfo.contributions);
+ }
+ });
return {
type: "success",
@@ -911,7 +909,7 @@ export interface PeerCoinSelectionRequest {
*/
async function selectPayPeerCandidatesForExchange(
ws: InternalWalletState,
- tx: WalletDbReadOnlyTransactionArr<["coinAvailability", "denominations"]>,
+ tx: WalletDbReadOnlyTransaction<["coinAvailability", "denominations"]>,
exchangeBaseUrl: string,
): Promise<AvailableDenom[]> {
const denoms: AvailableDenom[] = [];
@@ -1048,17 +1046,17 @@ export async function selectPeerCoins(
// one coin to spend.
throw new Error("amount of zero not allowed");
}
- return await ws.db
- .mktx((x) => [
- x.exchanges,
- x.contractTerms,
- x.coins,
- x.coinAvailability,
- x.denominations,
- x.refreshGroups,
- x.peerPushDebit,
- ])
- .runReadWrite(async (tx) => {
+ return await ws.db.runReadWriteTx(
+ [
+ "exchanges",
+ "contractTerms",
+ "coins",
+ "coinAvailability",
+ "denominations",
+ "refreshGroups",
+ "peerPushDebit",
+ ],
+ async (tx) => {
const exchanges = await tx.exchanges.iter().toArray();
const exchangeFeeGap: { [url: string]: AmountJson } = {};
const currency = Amounts.currencyOf(instructedAmount);
@@ -1232,5 +1230,6 @@ export async function selectPeerCoins(
};
return { type: "failure", insufficientBalanceDetails: errDetails };
- });
+ },
+ );
}
diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
index caa3fdca5..c4a2f2d5c 100644
--- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
+++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
@@ -139,14 +139,9 @@ async function getAvailableDenoms(
): Promise<AvailableCoins> {
const operationType = getOperationType(TransactionType.Deposit);
- return await ws.db
- .mktx((x) => [
- x.exchanges,
- x.exchangeDetails,
- x.denominations,
- x.coinAvailability,
- ])
- .runReadOnly(async (tx) => {
+ return await ws.db.runReadOnlyTx(
+ ["exchanges", "exchangeDetails", "denominations", "coinAvailability"],
+ async (tx) => {
const list: CoinInfo[] = [];
const exchanges: Record<string, ExchangeInfo> = {};
@@ -304,7 +299,8 @@ async function getAvailableDenoms(
}
return { list, exchanges };
- });
+ },
+ );
}
function buildCoinInfoFromDenom(
diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts
index 19fa0dbfd..90a3cac70 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -490,78 +490,14 @@ type ValidateKeyPath<T, P> = P extends `${infer PX extends keyof T &
// foo({x: [0,1,2]}, "x.0");
-export type GetReadOnlyAccess<BoundStores> = {
- [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
- infer StoreName,
- infer RecordType,
- infer IndexMap
- >
- ? StoreReadOnlyAccessor<RecordType, IndexMap>
- : unknown;
-};
-
export type StoreNames<StoreMap> = StoreMap extends {
[P in keyof StoreMap]: StoreWithIndexes<infer SN1, infer SD1, infer IM1>;
}
? keyof StoreMap
: unknown;
-export type DbReadOnlyTransaction<
- StoreMap,
- Stores extends StoreNames<StoreMap> & string,
-> = StoreMap extends {
- [P in Stores]: StoreWithIndexes<infer SN1, infer SD1, infer IM1>;
-}
- ? {
- [P in Stores]: StoreMap[P] extends StoreWithIndexes<
- infer StoreName,
- infer RecordType,
- infer IndexMap
- >
- ? StoreReadOnlyAccessor<RecordType, IndexMap>
- : unknown;
- }
- : unknown;
-
export type DbReadWriteTransaction<
StoreMap,
- Stores extends StoreNames<StoreMap> & string,
-> = StoreMap extends {
- [P in Stores]: StoreWithIndexes<infer SN1, infer SD1, infer IM1>;
-}
- ? {
- [P in Stores]: StoreMap[P] extends StoreWithIndexes<
- infer StoreName,
- infer RecordType,
- infer IndexMap
- >
- ? StoreReadWriteAccessor<RecordType, IndexMap>
- : unknown;
- }
- : unknown;
-
-export type GetReadWriteAccess<BoundStores> = {
- [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
- infer StoreName,
- infer RecordType,
- infer IndexMap
- >
- ? StoreReadWriteAccessor<RecordType, IndexMap>
- : unknown;
-};
-
-type ReadOnlyTransactionFunction<BoundStores, T> = (
- t: GetReadOnlyAccess<BoundStores>,
- rawTx: IDBTransaction,
-) => Promise<T>;
-
-type ReadWriteTransactionFunction<BoundStores, T> = (
- t: GetReadWriteAccess<BoundStores>,
- rawTx: IDBTransaction,
-) => Promise<T>;
-
-export type DbReadWriteTransactionArr<
- StoreMap,
StoresArr extends Array<StoreNames<StoreMap>>,
> = StoreMap extends {
[P in string]: StoreWithIndexes<infer _SN1, infer _SD1, infer _IM1>;
@@ -578,7 +514,7 @@ export type DbReadWriteTransactionArr<
}
: never;
-export type DbReadOnlyTransactionArr<
+export type DbReadOnlyTransaction<
StoreMap,
StoresArr extends Array<StoreNames<StoreMap>>,
> = StoreMap extends {
@@ -596,11 +532,6 @@ export type DbReadOnlyTransactionArr<
}
: never;
-export interface DbTransactionContext<BoundStores> {
- runReadWrite<T>(f: ReadWriteTransactionFunction<BoundStores, T>): Promise<T>;
- runReadOnly<T>(f: ReadOnlyTransactionFunction<BoundStores, T>): Promise<T>;
-}
-
/**
* Convert the type of an array to a union of the contents.
*
@@ -811,12 +742,6 @@ function makeWriteContext(
return ctx;
}
-type StoreNamesOf<X> = X extends { [x: number]: infer F }
- ? F extends { storeName: infer I }
- ? I
- : never
- : never;
-
/**
* Type-safe access to a database with a particular store map.
*
@@ -832,65 +757,46 @@ export class DbAccess<StoreMap> {
return this.db;
}
- /**
- * Run a transaction with all object stores.
- */
- mktxAll(): DbTransactionContext<StoreMap> {
- const storeNames: string[] = [];
+ runAllStoresReadWriteTx<T>(
+ txf: (
+ tx: DbReadWriteTransaction<StoreMap, Array<StoreNames<StoreMap>>>,
+ ) => Promise<T>,
+ ): Promise<T> {
const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
{};
- for (let i = 0; i < this.db.objectStoreNames.length; i++) {
- const sn = this.db.objectStoreNames[i];
+ const strStoreNames: string[] = [];
+ for (const sn of Object.keys(this.stores as any)) {
const swi = (this.stores as any)[sn] as StoreWithIndexes<any, any, any>;
- if (!swi) {
- logger.warn(`store metadata not available (${sn})`);
- continue;
- }
- storeNames.push(sn);
- accessibleStores[sn] = swi;
+ strStoreNames.push(swi.storeName);
+ accessibleStores[swi.storeName] = swi;
}
+ const tx = this.db.transaction(strStoreNames, "readwrite");
+ const writeContext = makeWriteContext(tx, accessibleStores);
+ return runTx(tx, writeContext, txf);
+ }
- const storeMapKeys = Object.keys(this.stores as any);
- for (const storeMapKey of storeMapKeys) {
- const swi = (this.stores as any)[storeMapKey] as StoreWithIndexes<
- any,
- any,
- any
- >;
- if (!accessibleStores[swi.storeName]) {
- const version = this.db.version;
- throw Error(
- `store '${swi.storeName}' required by schema but not in database (minver=${version})`,
- );
- }
+ runAllStoresReadOnlyTx<T>(
+ txf: (
+ tx: DbReadOnlyTransaction<StoreMap, Array<StoreNames<StoreMap>>>,
+ ) => Promise<T>,
+ ): Promise<T> {
+ const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
+ {};
+ const strStoreNames: string[] = [];
+ for (const sn of Object.keys(this.stores as any)) {
+ const swi = (this.stores as any)[sn] as StoreWithIndexes<any, any, any>;
+ strStoreNames.push(swi.storeName);
+ accessibleStores[swi.storeName] = swi;
}
-
- const runReadOnly = <T>(
- txf: ReadOnlyTransactionFunction<StoreMap, T>,
- ): Promise<T> => {
- const tx = this.db.transaction(storeNames, "readonly");
- const readContext = makeReadContext(tx, accessibleStores);
- return runTx(tx, readContext, txf);
- };
-
- const runReadWrite = <T>(
- txf: ReadWriteTransactionFunction<StoreMap, T>,
- ): Promise<T> => {
- const tx = this.db.transaction(storeNames, "readwrite");
- const writeContext = makeWriteContext(tx, accessibleStores);
- return runTx(tx, writeContext, txf);
- };
-
- return {
- runReadOnly,
- runReadWrite,
- };
+ const tx = this.db.transaction(strStoreNames, "readonly");
+ const writeContext = makeReadContext(tx, accessibleStores);
+ return runTx(tx, writeContext, txf);
}
runReadWriteTx<T, StoreNameArray extends Array<StoreNames<StoreMap>>>(
storeNames: StoreNameArray,
txf: (
- tx: DbReadWriteTransactionArr<StoreMap, StoreNameArray>,
+ tx: DbReadWriteTransaction<StoreMap, StoreNameArray>,
) => Promise<T>,
): Promise<T> {
const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
@@ -908,7 +814,7 @@ export class DbAccess<StoreMap> {
runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<StoreMap>>>(
storeNames: StoreNameArray,
- txf: (tx: DbReadOnlyTransactionArr<StoreMap, StoreNameArray>) => Promise<T>,
+ txf: (tx: DbReadOnlyTransaction<StoreMap, StoreNameArray>) => Promise<T>,
): Promise<T> {
const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
{};
@@ -922,57 +828,4 @@ export class DbAccess<StoreMap> {
const readContext = makeReadContext(tx, accessibleStores);
return runTx(tx, readContext, txf);
}
-
- /**
- * Run a transaction with selected object stores.
- *
- * The {@link namePicker} must be a function that selects a list of object
- * stores from all available object stores.
- */
- mktx<
- StoreNames extends keyof StoreMap,
- Stores extends StoreMap[StoreNames],
- StoreList extends Stores[],
- BoundStores extends {
- [X in StoreNamesOf<StoreList>]: StoreList[number] & { storeName: X };
- },
- >(namePicker: (x: StoreMap) => StoreList): DbTransactionContext<BoundStores> {
- const storeNames: string[] = [];
- const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
- {};
-
- const storePick = namePicker(this.stores) as any;
- if (typeof storePick !== "object" || storePick === null) {
- throw Error();
- }
- for (const swiPicked of storePick) {
- const swi = swiPicked as StoreWithIndexes<any, any, any>;
- if (swi.mark !== storeWithIndexesSymbol) {
- throw Error("invalid store descriptor returned from selector function");
- }
- storeNames.push(swi.storeName);
- accessibleStores[swi.storeName] = swi;
- }
-
- const runReadOnly = <T>(
- txf: ReadOnlyTransactionFunction<BoundStores, T>,
- ): Promise<T> => {
- const tx = this.db.transaction(storeNames, "readonly");
- const readContext = makeReadContext(tx, accessibleStores);
- return runTx(tx, readContext, txf);
- };
-
- const runReadWrite = <T>(
- txf: ReadWriteTransactionFunction<BoundStores, T>,
- ): Promise<T> => {
- const tx = this.db.transaction(storeNames, "readwrite");
- const writeContext = makeWriteContext(tx, accessibleStores);
- return runTx(tx, writeContext, txf);
- };
-
- return {
- runReadOnly,
- runReadWrite,
- };
- }
}