diff options
author | Florian Dold <florian@dold.me> | 2024-06-30 22:10:39 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-06-30 22:10:39 +0200 |
commit | aff3c5c19d1c08f96037faa80446a27c641e0219 (patch) | |
tree | 74a31e39d541cfa3819ac4e2e02731e30cb84b06 | |
parent | c297b5130cb7e9ceba4602548d9ccb68735d55e5 (diff) | |
download | wallet-core-aff3c5c19d1c08f96037faa80446a27c641e0219.tar.xz |
wallet-core: request handler refactoring WIP
-rw-r--r-- | packages/taler-wallet-core/src/wallet-api-types.ts | 5 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 432 |
2 files changed, 272 insertions, 165 deletions
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index c1943daa1..12abb6469 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -95,6 +95,7 @@ import { InitiatePeerPushDebitRequest, InitiatePeerPushDebitResponse, IntegrationTestArgs, + IntegrationTestV2Args, KnownBankAccounts, ListAssociatedRefreshesRequest, ListAssociatedRefreshesResponse, @@ -275,7 +276,6 @@ export enum WalletApiOperation { TestingWaitTransactionState = "testingWaitTransactionState", TestingWaitTasksDone = "testingWaitTasksDone", TestingSetTimetravel = "testingSetTimetravel", - TestingInfiniteTransactionLoop = "testingInfiniteTransactionLoop", TestingGetDenomStats = "testingGetDenomStats", TestingPing = "testingPing", TestingGetReserveHistory = "testingGetReserveHistory", @@ -1068,7 +1068,7 @@ export type RunIntegrationTestOp = { */ export type RunIntegrationTestV2Op = { op: WalletApiOperation.RunIntegrationTestV2; - request: IntegrationTestArgs; + request: IntegrationTestV2Args; response: EmptyObject; }; @@ -1344,7 +1344,6 @@ export type WalletOperations = { [WalletApiOperation.RecoverStoredBackup]: RecoverStoredBackupsOp; [WalletApiOperation.UpdateExchangeEntry]: UpdateExchangeEntryOp; [WalletApiOperation.PrepareWithdrawExchange]: PrepareWithdrawExchangeOp; - [WalletApiOperation.TestingInfiniteTransactionLoop]: any; [WalletApiOperation.DeleteExchange]: DeleteExchangeOp; [WalletApiOperation.GetExchangeResources]: GetExchangeResourcesOp; [WalletApiOperation.ListGlobalCurrencyAuditors]: ListGlobalCurrencyAuditorsOp; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 9e3aea137..5b3b4da29 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -26,21 +26,29 @@ import { IDBDatabase, IDBFactory } from "@gnu-taler/idb-bridge"; import { AbortTransactionRequest, AbsoluteTime, + AcceptBankIntegratedWithdrawalRequest, AcceptManualWithdrawalRequest, AcceptManualWithdrawalResult, + AcceptWithdrawalResponse, ActiveTask, AddExchangeRequest, + AddGlobalCurrencyAuditorRequest, + AddGlobalCurrencyExchangeRequest, AddKnownBankAccountsRequest, AmountJson, AmountString, Amounts, CancellationToken, + CanonicalizeBaseUrlRequest, + CanonicalizeBaseUrlResponse, + Codec, CoinDumpJson, CoinStatus, ConfirmPayRequest, ConfirmPayResult, CoreApiResponse, CreateStoredBackupResponse, + DeleteExchangeRequest, DeleteStoredBackupRequest, DenominationInfo, Duration, @@ -78,6 +86,8 @@ import { PrepareWithdrawExchangeRequest, PrepareWithdrawExchangeResponse, RecoverStoredBackupRequest, + RemoveGlobalCurrencyAuditorRequest, + RemoveGlobalCurrencyExchangeRequest, SharePaymentRequest, SharePaymentResult, StartRefundQueryRequest, @@ -91,6 +101,7 @@ import { TestingGetDenomStatsRequest, TestingGetDenomStatsResponse, TestingGetReserveHistoryRequest, + TestingSetTimetravelRequest, TestingWaitTransactionRequest, TimerAPI, TimerGroup, @@ -174,7 +185,6 @@ import { codecForTestPayArgs, codecForTestingGetDenomStatsRequest, codecForTestingGetReserveHistoryRequest, - codecForTestingListTasksForTransactionRequest, codecForTestingSetTimetravelRequest, codecForTransactionByIdRequest, codecForTransactionsRequest, @@ -343,6 +353,7 @@ import { import { WalletApiOperation, WalletCoreApiClient, + WalletCoreRequestType, WalletCoreResponseType, } from "./wallet-api-types.js"; import { @@ -800,9 +811,7 @@ async function handleWithdrawTestkudos(wex: WalletExecutionContext) { exchangeBaseUrl: "https://exchange.test.taler.net/", }); // FIXME: Is this correct? - return { - versionInfo: handleGetVersion(wex), - }; + return {}; } async function handleWithdrawTestBalance( @@ -1195,6 +1204,250 @@ async function handleListGlobalCurrencyExchanges( return resp; } +async function handleListGlobalCurrencyAuditors( + wex: WalletExecutionContext, + req: EmptyObject, +): Promise<ListGlobalCurrencyAuditorsResponse> { + const resp: ListGlobalCurrencyAuditorsResponse = { + auditors: [], + }; + await wex.db.runReadOnlyTx( + { storeNames: ["globalCurrencyAuditors"] }, + async (tx) => { + const gcaList = await tx.globalCurrencyAuditors.iter().toArray(); + for (const gca of gcaList) { + resp.auditors.push({ + currency: gca.currency, + auditorBaseUrl: gca.auditorBaseUrl, + auditorPub: gca.auditorPub, + }); + } + }, + ); + return resp; +} + +async function handleAddGlobalCurrencyExchange( + wex: WalletExecutionContext, + req: AddGlobalCurrencyExchangeRequest, +): Promise<EmptyObject> { + await wex.db.runReadWriteTx( + { storeNames: ["globalCurrencyExchanges"] }, + async (tx) => { + const key = [req.currency, req.exchangeBaseUrl, req.exchangeMasterPub]; + const existingRec = + await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get( + key, + ); + if (existingRec) { + return; + } + wex.ws.exchangeCache.clear(); + await tx.globalCurrencyExchanges.add({ + currency: req.currency, + exchangeBaseUrl: req.exchangeBaseUrl, + exchangeMasterPub: req.exchangeMasterPub, + }); + }, + ); + return {}; +} + +async function handleRemoveGlobalCurrencyAuditor( + wex: WalletExecutionContext, + req: RemoveGlobalCurrencyAuditorRequest, +): Promise<EmptyObject> { + await wex.db.runReadWriteTx( + { storeNames: ["globalCurrencyAuditors"] }, + async (tx) => { + const key = [req.currency, req.auditorBaseUrl, req.auditorPub]; + const existingRec = + await tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get(key); + if (!existingRec) { + return; + } + checkDbInvariant(!!existingRec.id, `no global currency for ${j2s(key)}`); + await tx.globalCurrencyAuditors.delete(existingRec.id); + wex.ws.exchangeCache.clear(); + }, + ); + return {}; +} + +async function handleRemoveGlobalCurrencyExchange( + wex: WalletExecutionContext, + req: RemoveGlobalCurrencyExchangeRequest, +): Promise<EmptyObject> { + await wex.db.runReadWriteTx( + { storeNames: ["globalCurrencyExchanges"] }, + async (tx) => { + const key = [req.currency, req.exchangeBaseUrl, req.exchangeMasterPub]; + const existingRec = + await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get( + key, + ); + if (!existingRec) { + return; + } + wex.ws.exchangeCache.clear(); + checkDbInvariant(!!existingRec.id, `no global exchange for ${j2s(key)}`); + await tx.globalCurrencyExchanges.delete(existingRec.id); + }, + ); + return {}; +} + +async function handleAddGlobalCurrencyAuditor( + wex: WalletExecutionContext, + req: AddGlobalCurrencyAuditorRequest, +): Promise<EmptyObject> { + await wex.db.runReadWriteTx( + { storeNames: ["globalCurrencyAuditors"] }, + async (tx) => { + const key = [req.currency, req.auditorBaseUrl, req.auditorPub]; + const existingRec = + await tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get(key); + if (existingRec) { + return; + } + await tx.globalCurrencyAuditors.add({ + currency: req.currency, + auditorBaseUrl: req.auditorBaseUrl, + auditorPub: req.auditorPub, + }); + wex.ws.exchangeCache.clear(); + }, + ); + return {}; +} + +async function handleShutdown( + wex: WalletExecutionContext, + req: EmptyObject, +): Promise<EmptyObject> { + wex.ws.stop(); + return {}; +} + +async function handleTestingSetTimetravel( + wex: WalletExecutionContext, + req: TestingSetTimetravelRequest, +): Promise<EmptyObject> { + setDangerousTimetravel(req.offsetMs); + await wex.taskScheduler.reload(); + return {}; +} + +async function handleCanonicalizeBaseUrl( + wex: WalletExecutionContext, + req: CanonicalizeBaseUrlRequest, +): Promise<CanonicalizeBaseUrlResponse> { + return { + url: canonicalizeBaseUrl(req.url), + }; +} + +async function handleDeleteExchange( + wex: WalletExecutionContext, + req: DeleteExchangeRequest, +): Promise<EmptyObject> { + await deleteExchange(wex, req); + return {}; +} + +async function handleCreateStoredBackup( + wex: WalletExecutionContext, + req: EmptyObject, +): Promise<CreateStoredBackupResponse> { + return await createStoredBackup(wex); +} + +async function handleAcceptBankIntegratedWithdrawal( + wex: WalletExecutionContext, + req: AcceptBankIntegratedWithdrawalRequest, +): Promise<AcceptWithdrawalResponse> { + return await acceptWithdrawalFromUri(wex, { + selectedExchange: req.exchangeBaseUrl, + talerWithdrawUri: req.talerWithdrawUri, + forcedDenomSel: req.forcedDenomSel, + restrictAge: req.restrictAge, + amount: req.amount, + }); +} + +interface HandlerWithValidator<Tag extends WalletApiOperation> { + codec: Codec<WalletCoreRequestType<Tag>>; + handler: ( + wex: WalletExecutionContext, + req: WalletCoreRequestType<Tag>, + ) => Promise<WalletCoreResponseType<Tag>>; +} + +// @ts-ignore +const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { + [WalletApiOperation.AbortTransaction]: { + codec: codecForAbortTransaction(), + handler: handleAbortTransaction, + }, + [WalletApiOperation.CreateStoredBackup]: { + codec: codecForEmptyObject(), + handler: handleCreateStoredBackup, + }, + [WalletApiOperation.DeleteStoredBackup]: { + codec: codecForDeleteStoredBackupRequest(), + handler: handleDeleteStoredBackup, + }, + [WalletApiOperation.ListStoredBackups]: { + codec: codecForEmptyObject(), + handler: listStoredBackups, + }, + [WalletApiOperation.SetWalletRunConfig]: { + codec: codecForInitRequest(), + handler: handleSetWalletRunConfig, + }, + // Alias for SetWalletRunConfig + [WalletApiOperation.InitWallet]: { + codec: codecForInitRequest(), + handler: handleSetWalletRunConfig, + }, + [WalletApiOperation.RecoverStoredBackup]: { + codec: codecForRecoverStoredBackupRequest(), + handler: handleRecoverStoredBackup, + }, + [WalletApiOperation.WithdrawTestkudos]: { + codec: codecForEmptyObject(), + handler: handleWithdrawTestkudos, + }, + [WalletApiOperation.WithdrawTestBalance]: { + codec: codecForWithdrawTestBalance(), + handler: handleWithdrawTestBalance, + }, + [WalletApiOperation.RunIntegrationTest]: { + codec: codecForIntegrationTestArgs(), + handler: handleRunIntegrationTest, + }, + [WalletApiOperation.RunIntegrationTestV2]: { + codec: codecForIntegrationTestV2Args(), + handler: handleRunIntegrationTestV2, + }, + [WalletApiOperation.ValidateIban]: { + codec: codecForValidateIbanRequest(), + handler: handleValidateIban, + }, + [WalletApiOperation.TestPay]: { + codec: codecForTestPayArgs(), + handler: testPay, + }, + [WalletApiOperation.GetTransactions]: { + codec: codecForTransactionsRequest(), + handler: getTransactions, + }, + [WalletApiOperation.GetTransactionById]: { + codec: codecForTransactionByIdRequest(), + handler: getTransactionById, + }, +}; + /** * Implementation of the "wallet-core" API. */ @@ -1213,7 +1466,7 @@ async function dispatchRequestInternal( // definitions we already have? switch (operation) { case WalletApiOperation.CreateStoredBackup: - return await createStoredBackup(wex); + return await handleCreateStoredBackup(wex, {}); case WalletApiOperation.DeleteStoredBackup: { const req = codecForDeleteStoredBackupRequest().decode(payload); return await handleDeleteStoredBackup(wex, req); @@ -1356,13 +1609,7 @@ async function dispatchRequestInternal( case WalletApiOperation.AcceptBankIntegratedWithdrawal: { const req = codecForAcceptBankIntegratedWithdrawalRequest().decode(payload); - return await acceptWithdrawalFromUri(wex, { - selectedExchange: req.exchangeBaseUrl, - talerWithdrawUri: req.talerWithdrawUri, - forcedDenomSel: req.forcedDenomSel, - restrictAge: req.restrictAge, - amount: req.amount, - }); + return handleAcceptBankIntegratedWithdrawal(wex, req); } case WalletApiOperation.ConfirmWithdrawal: { const req = codecForConfirmWithdrawalRequestRequest().decode(payload); @@ -1567,100 +1814,20 @@ async function dispatchRequestInternal( return await handleListGlobalCurrencyExchanges(wex, req); } case WalletApiOperation.ListGlobalCurrencyAuditors: { - const resp: ListGlobalCurrencyAuditorsResponse = { - auditors: [], - }; - await wex.db.runReadOnlyTx( - { storeNames: ["globalCurrencyAuditors"] }, - async (tx) => { - const gcaList = await tx.globalCurrencyAuditors.iter().toArray(); - for (const gca of gcaList) { - resp.auditors.push({ - currency: gca.currency, - auditorBaseUrl: gca.auditorBaseUrl, - auditorPub: gca.auditorPub, - }); - } - }, - ); - return resp; + const req = codecForEmptyObject().decode(payload); + return await handleListGlobalCurrencyAuditors(wex, req); } case WalletApiOperation.AddGlobalCurrencyExchange: { const req = codecForAddGlobalCurrencyExchangeRequest().decode(payload); - await wex.db.runReadWriteTx( - { storeNames: ["globalCurrencyExchanges"] }, - async (tx) => { - const key = [ - req.currency, - req.exchangeBaseUrl, - req.exchangeMasterPub, - ]; - const existingRec = - await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get( - key, - ); - if (existingRec) { - return; - } - wex.ws.exchangeCache.clear(); - await tx.globalCurrencyExchanges.add({ - currency: req.currency, - exchangeBaseUrl: req.exchangeBaseUrl, - exchangeMasterPub: req.exchangeMasterPub, - }); - }, - ); - return {}; + return handleAddGlobalCurrencyExchange(wex, req); } case WalletApiOperation.RemoveGlobalCurrencyExchange: { const req = codecForRemoveGlobalCurrencyExchangeRequest().decode(payload); - await wex.db.runReadWriteTx( - { storeNames: ["globalCurrencyExchanges"] }, - async (tx) => { - const key = [ - req.currency, - req.exchangeBaseUrl, - req.exchangeMasterPub, - ]; - const existingRec = - await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get( - key, - ); - if (!existingRec) { - return; - } - wex.ws.exchangeCache.clear(); - checkDbInvariant( - !!existingRec.id, - `no global exchange for ${j2s(key)}`, - ); - await tx.globalCurrencyExchanges.delete(existingRec.id); - }, - ); - return {}; + return handleRemoveGlobalCurrencyExchange(wex, req); } case WalletApiOperation.AddGlobalCurrencyAuditor: { const req = codecForAddGlobalCurrencyAuditorRequest().decode(payload); - await wex.db.runReadWriteTx( - { storeNames: ["globalCurrencyAuditors"] }, - async (tx) => { - const key = [req.currency, req.auditorBaseUrl, req.auditorPub]; - const existingRec = - await tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get( - key, - ); - if (existingRec) { - return; - } - await tx.globalCurrencyAuditors.add({ - currency: req.currency, - auditorBaseUrl: req.auditorBaseUrl, - auditorPub: req.auditorPub, - }); - wex.ws.exchangeCache.clear(); - }, - ); - return {}; + return handleAddGlobalCurrencyAuditor(wex, req); } case WalletApiOperation.TestingWaitTasksDone: { await waitTasksDone(wex); @@ -1671,26 +1838,7 @@ async function dispatchRequestInternal( return {}; case WalletApiOperation.RemoveGlobalCurrencyAuditor: { const req = codecForRemoveGlobalCurrencyAuditorRequest().decode(payload); - await wex.db.runReadWriteTx( - { storeNames: ["globalCurrencyAuditors"] }, - async (tx) => { - const key = [req.currency, req.auditorBaseUrl, req.auditorPub]; - const existingRec = - await tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get( - key, - ); - if (!existingRec) { - return; - } - checkDbInvariant( - !!existingRec.id, - `no global currency for ${j2s(key)}`, - ); - await tx.globalCurrencyAuditors.delete(existingRec.id); - wex.ws.exchangeCache.clear(); - }, - ); - return {}; + return await handleRemoveGlobalCurrencyAuditor(wex, req); } case WalletApiOperation.ImportDb: { const req = codecForImportDbRequest().decode(payload); @@ -1735,8 +1883,8 @@ async function dispatchRequestInternal( return {}; } case WalletApiOperation.Shutdown: { - wex.ws.stop(); - return {}; + const req = codecForEmptyObject().decode(payload); + return await handleShutdown(wex, req); } case WalletApiOperation.GetVersion: { return handleGetVersion(wex); @@ -1747,14 +1895,11 @@ async function dispatchRequestInternal( return await waitUntilRefreshesDone(wex); case WalletApiOperation.TestingSetTimetravel: { const req = codecForTestingSetTimetravelRequest().decode(payload); - setDangerousTimetravel(req.offsetMs); - await wex.taskScheduler.reload(); - return {}; + return await handleTestingSetTimetravel(wex, req); } case WalletApiOperation.DeleteExchange: { const req = codecForDeleteExchangeRequest().decode(payload); - await deleteExchange(wex, req); - return {}; + return await handleDeleteExchange(wex, req); } case WalletApiOperation.GetExchangeResources: { const req = codecForGetExchangeResourcesRequest().decode(payload); @@ -1762,45 +1907,8 @@ async function dispatchRequestInternal( } case WalletApiOperation.CanonicalizeBaseUrl: { const req = codecForCanonicalizeBaseUrlRequest().decode(payload); - return { - url: canonicalizeBaseUrl(req.url), - }; - } - case WalletApiOperation.TestingInfiniteTransactionLoop: { - const myDelayMs = (payload as any).delayMs ?? 5; - const shouldFetch = !!(payload as any).shouldFetch; - const doFetch = async () => { - while (1) { - const url = - "https://exchange.demo.taler.net/reserves/01PMMB9PJN0QBWAFBXV6R0KNJJMAKXCV4D6FDG0GJFDJQXGYP32G?timeout_ms=30000"; - logger.info(`fetching ${url}`); - const res = await wex.http.fetch(url); - logger.info(`fetch result ${res.status}`); - } - }; - if (shouldFetch) { - // In the background! - doFetch(); - } - let loopCount = 0; - while (true) { - logger.info(`looping test write tx, iteration ${loopCount}`); - await wex.db.runReadWriteTx({ storeNames: ["config"] }, async (tx) => { - await tx.config.put({ - key: ConfigRecordKey.TestLoopTx, - value: loopCount, - }); - }); - if (myDelayMs != 0) { - await new Promise<void>((resolve, reject) => { - setTimeout(() => resolve(), myDelayMs); - }); - } - loopCount = (loopCount + 1) % (Number.MAX_SAFE_INTEGER - 1); - } + return handleCanonicalizeBaseUrl(wex, req); } - // default: - // assertUnreachable(operation); } throw TalerError.fromDetail( TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN, |