get rid of cyclic imports

This commit is contained in:
Florian Dold 2021-06-22 12:18:12 +02:00
parent c4f46cb9d2
commit 7383b89cab
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 68 additions and 30 deletions

View File

@ -14,34 +14,35 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/**
* Common interface of the internal wallet state. This object is passed
* to the various operations (exchange management, withdrawal, refresh, reserve
* management, etc.).
*
* Some operations can be accessed via this state object. This allows mutual
* recursion between operations, without having cycling dependencies between
* the respective TypeScript files.
*
* (You can think of this as a "header file" for the wallet implementation.)
*/
/** /**
* Imports. * Imports.
*/ */
import { import { WalletNotification, BalancesResponse } from "@gnu-taler/taler-util";
WalletNotification, import { CryptoApi } from "./crypto/workers/cryptoApi.js";
BalancesResponse,
Logger,
} from "@gnu-taler/taler-util";
import { CryptoApi, CryptoWorkerFactory } from "./crypto/workers/cryptoApi.js";
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js"; import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
import {
getExchangeDetails,
getExchangeTrust,
updateExchangeFromUrl,
} from "./operations/exchanges.js";
import { PendingOperationsResponse } from "./pending-types.js"; import { PendingOperationsResponse } from "./pending-types.js";
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js"; import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
import { HttpRequestLibrary } from "./util/http.js"; import { HttpRequestLibrary } from "./util/http.js";
import { AsyncCondition } from "./util/promiseUtils.js";
import { import {
AsyncCondition, DbAccess,
OpenedPromise, GetReadOnlyAccess,
openPromise, GetReadWriteAccess,
} from "./util/promiseUtils.js"; } from "./util/query.js";
import { DbAccess, GetReadOnlyAccess } from "./util/query.js";
import { TimerGroup } from "./util/timer.js"; import { TimerGroup } from "./util/timer.js";
const logger = new Logger("state.ts");
export const EXCHANGE_COINS_LOCK = "exchange-coins-lock"; export const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock"; export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
@ -77,12 +78,30 @@ export interface ExchangeOperations {
}>; }>;
} }
export interface RecoupOperations {
createRecoupGroup(
ws: InternalWalletState,
tx: GetReadWriteAccess<{
recoupGroups: typeof WalletStoresV1.recoupGroups;
denominations: typeof WalletStoresV1.denominations;
refreshGroups: typeof WalletStoresV1.refreshGroups;
coins: typeof WalletStoresV1.coins;
}>,
coinPubs: string[],
): Promise<string>;
processRecoupGroup(
ws: InternalWalletState,
recoupGroupId: string,
forceNow?: boolean,
): Promise<void>;
}
export type NotificationListener = (n: WalletNotification) => void; export type NotificationListener = (n: WalletNotification) => void;
/** /**
* Internal, shard wallet state that is used by the implementation * Internal, shard wallet state that is used by the implementation
* of wallet operations. * of wallet operations.
* *
* FIXME: This should not be exported anywhere from the taler-wallet-core package, * FIXME: This should not be exported anywhere from the taler-wallet-core package,
* as it's an opaque implementation detail. * as it's an opaque implementation detail.
*/ */
@ -106,6 +125,7 @@ export interface InternalWalletState {
initCalled: boolean; initCalled: boolean;
exchangeOps: ExchangeOperations; exchangeOps: ExchangeOperations;
recoupOps: RecoupOperations;
db: DbAccess<typeof WalletStoresV1>; db: DbAccess<typeof WalletStoresV1>;
http: HttpRequestLibrary; http: HttpRequestLibrary;
@ -124,4 +144,6 @@ export interface InternalWalletState {
* by string tokens. * by string tokens.
*/ */
runSequentialized<T>(tokens: string[], f: () => Promise<T>): Promise<T>; runSequentialized<T>(tokens: string[], f: () => Promise<T>): Promise<T>;
runUntilDone(req?: { maxRetries?: number }): Promise<void>;
} }

View File

@ -65,7 +65,6 @@ import {
makeErrorDetails, makeErrorDetails,
OperationFailedError, OperationFailedError,
} from "../errors.js"; } from "../errors.js";
import { createRecoupGroup, processRecoupGroup } from "./recoup.js";
import { InternalWalletState, TrustInfo } from "../common.js"; import { InternalWalletState, TrustInfo } from "../common.js";
import { import {
WALLET_CACHE_BREAKER_CLIENT_VERSION, WALLET_CACHE_BREAKER_CLIENT_VERSION,
@ -556,7 +555,11 @@ async function updateExchangeFromUrlImpl(
} }
if (newlyRevokedCoinPubs.length != 0) { if (newlyRevokedCoinPubs.length != 0) {
logger.trace("recouping coins", newlyRevokedCoinPubs); logger.trace("recouping coins", newlyRevokedCoinPubs);
recoupGroupId = await createRecoupGroup(ws, tx, newlyRevokedCoinPubs); recoupGroupId = await ws.recoupOps.createRecoupGroup(
ws,
tx,
newlyRevokedCoinPubs,
);
} }
return { return {
exchange: r, exchange: r,
@ -567,7 +570,7 @@ async function updateExchangeFromUrlImpl(
if (recoupGroupId) { if (recoupGroupId) {
// Asynchronously start recoup. This doesn't need to finish // Asynchronously start recoup. This doesn't need to finish
// for the exchange update to be considered finished. // for the exchange update to be considered finished.
processRecoupGroup(ws, recoupGroupId).catch((e) => { ws.recoupOps.processRecoupGroup(ws, recoupGroupId).catch((e) => {
logger.error("error while recouping coins:", e); logger.error("error while recouping coins:", e);
}); });
} }

View File

@ -38,7 +38,6 @@ import { createTalerWithdrawReserve } from "./reserves.js";
import { InternalWalletState } from "../common.js"; import { InternalWalletState } from "../common.js";
import { confirmPay, preparePayForUri } from "./pay.js"; import { confirmPay, preparePayForUri } from "./pay.js";
import { getBalances } from "./balance.js"; import { getBalances } from "./balance.js";
import { runUntilDone } from "../wallet.js";
import { applyRefund } from "./refund.js"; import { applyRefund } from "./refund.js";
const logger = new Logger("operations/testing.ts"); const logger = new Logger("operations/testing.ts");
@ -333,7 +332,7 @@ export async function runIntegrationTest(
args.bankBaseUrl, args.bankBaseUrl,
args.exchangeBaseUrl, args.exchangeBaseUrl,
); );
await runUntilDone(ws); await ws.runUntilDone();
logger.info("done withdrawing test balance"); logger.info("done withdrawing test balance");
const balance = await getBalances(ws); const balance = await getBalances(ws);
@ -348,7 +347,7 @@ export async function runIntegrationTest(
await makePayment(ws, myMerchant, args.amountToSpend, "hello world"); await makePayment(ws, myMerchant, args.amountToSpend, "hello world");
// Wait until the refresh is done // Wait until the refresh is done
await runUntilDone(ws); await ws.runUntilDone();
logger.trace("withdrawing test balance for refund"); logger.trace("withdrawing test balance for refund");
const withdrawAmountTwo = Amounts.parseOrThrow(`${currency}:18`); const withdrawAmountTwo = Amounts.parseOrThrow(`${currency}:18`);
@ -364,7 +363,7 @@ export async function runIntegrationTest(
); );
// Wait until the withdraw is done // Wait until the withdraw is done
await runUntilDone(ws); await ws.runUntilDone();
const { orderId: refundOrderId } = await makePayment( const { orderId: refundOrderId } = await makePayment(
ws, ws,
@ -388,7 +387,7 @@ export async function runIntegrationTest(
logger.trace("integration test: applied refund"); logger.trace("integration test: applied refund");
// Wait until the refund is done // Wait until the refund is done
await runUntilDone(ws); await ws.runUntilDone();
logger.trace("integration test: making payment after refund"); logger.trace("integration test: making payment after refund");
@ -401,7 +400,7 @@ export async function runIntegrationTest(
logger.trace("integration test: make payment done"); logger.trace("integration test: make payment done");
await runUntilDone(ws); await ws.runUntilDone();
logger.trace("integration test: all done!"); logger.trace("integration test: all done!");
} }

View File

@ -33,7 +33,6 @@ import {
getDurationRemaining, getDurationRemaining,
isTimestampExpired, isTimestampExpired,
j2s, j2s,
PreparePayResultType,
TalerErrorCode, TalerErrorCode,
Timestamp, Timestamp,
timestampMin, timestampMin,
@ -72,7 +71,7 @@ import {
processPurchasePay, processPurchasePay,
} from "./operations/pay.js"; } from "./operations/pay.js";
import { getPendingOperations } from "./operations/pending.js"; import { getPendingOperations } from "./operations/pending.js";
import { processRecoupGroup } from "./operations/recoup.js"; import { createRecoupGroup, processRecoupGroup } from "./operations/recoup.js";
import { import {
autoRefresh, autoRefresh,
createRefreshGroup, createRefreshGroup,
@ -93,6 +92,7 @@ import {
ExchangeOperations, ExchangeOperations,
InternalWalletState, InternalWalletState,
NotificationListener, NotificationListener,
RecoupOperations,
} from "./common.js"; } from "./common.js";
import { import {
runIntegrationTest, runIntegrationTest,
@ -673,6 +673,7 @@ async function dispatchRequestInternal(
switch (operation) { switch (operation) {
case "initWallet": { case "initWallet": {
ws.initCalled = true; ws.initCalled = true;
await fillDefaults(ws);
return {}; return {};
} }
case "withdrawTestkudos": { case "withdrawTestkudos": {
@ -1046,6 +1047,11 @@ class InternalWalletStateImpl implements InternalWalletState {
updateExchangeFromUrl, updateExchangeFromUrl,
}; };
recoupOps: RecoupOperations = {
createRecoupGroup: createRecoupGroup,
processRecoupGroup: processRecoupGroup,
};
/** /**
* Promises that are waiting for a particular resource. * Promises that are waiting for a particular resource.
*/ */
@ -1091,6 +1097,14 @@ class InternalWalletStateImpl implements InternalWalletState {
this.cryptoApi.stop(); this.cryptoApi.stop();
} }
async runUntilDone(
req: {
maxRetries?: number;
} = {},
): Promise<void> {
runUntilDone(this, req);
}
/** /**
* Run an async function after acquiring a list of locks, identified * Run an async function after acquiring a list of locks, identified
* by string tokens. * by string tokens.