diff options
Diffstat (limited to 'packages/taler-wallet-core/src/wallet.ts')
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 1243 |
1 files changed, 696 insertions, 547 deletions
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 1f2686cc9..9c870531e 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -57,6 +57,8 @@ import { FailTransactionRequest, ForgetKnownBankAccountsRequest, GetActiveTasksResponse, + GetBankingChoicesForPaytoRequest, + GetBankingChoicesForPaytoResponse, GetContractTermsDetailsRequest, GetCurrencySpecificationRequest, GetCurrencySpecificationResponse, @@ -76,6 +78,7 @@ import { ListExchangesForScopedCurrencyRequest, ListGlobalCurrencyAuditorsResponse, ListGlobalCurrencyExchangesResponse, + ListKnownBankAccountsRequest, Logger, NotificationType, ObservabilityContext, @@ -102,9 +105,9 @@ import { TestingGetDenomStatsResponse, TestingGetReserveHistoryRequest, TestingSetTimetravelRequest, - TestingWaitTransactionRequest, TimerAPI, TimerGroup, + TransactionIdStr, TransactionType, TransactionsResponse, UpdateExchangeEntryRequest, @@ -114,6 +117,7 @@ import { WalletCoreVersion, WalletNotification, WalletRunConfig, + WireTypeDetails, WithdrawTestBalanceRequest, canonicalizeBaseUrl, checkDbInvariant, @@ -129,6 +133,7 @@ import { codecForAny, codecForApplyDevExperiment, codecForCanonicalizeBaseUrlRequest, + codecForCheckDepositRequest, codecForCheckPayTemplateRequest, codecForCheckPeerPullPaymentRequest, codecForCheckPeerPushDebitRequest, @@ -144,15 +149,18 @@ import { codecForFailTransactionRequest, codecForForceRefreshRequest, codecForForgetKnownBankAccounts, - codecForGetAmountRequest, codecForGetBalanceDetailRequest, + codecForGetBankingChoicesForPaytoRequest, codecForGetContractTermsDetails, codecForGetCurrencyInfoRequest, codecForGetDepositWireTypesForCurrencyRequest, codecForGetExchangeEntryByUrlRequest, codecForGetExchangeResourcesRequest, codecForGetExchangeTosRequest, + codecForGetMaxDepositAmountRequest, + codecForGetMaxPeerPushDebitAmountRequest, codecForGetQrCodesForPaytoRequest, + codecForGetTransactionsV2Request, codecForGetWithdrawalDetailsForAmountRequest, codecForGetWithdrawalDetailsForUri, codecForHintNetworkAvailabilityRequest, @@ -165,7 +173,6 @@ import { codecForListExchangesForScopedCurrencyRequest, codecForListKnownBankAccounts, codecForPrepareBankIntegratedWithdrawalRequest, - codecForPrepareDepositRequest, codecForPreparePayRequest, codecForPreparePayTemplateRequest, codecForPreparePeerPullPaymentRequest, @@ -180,12 +187,14 @@ import { codecForSetCoinSuspendedRequest, codecForSetWalletDeviceIdRequest, codecForSharePaymentRequest, + codecForStartExchangeWalletKycRequest, codecForStartRefundQueryRequest, codecForSuspendTransaction, codecForTestPayArgs, codecForTestingGetDenomStatsRequest, codecForTestingGetReserveHistoryRequest, codecForTestingSetTimetravelRequest, + codecForTestingWaitWalletKycRequest, codecForTransactionByIdRequest, codecForTransactionsRequest, codecForUpdateExchangeEntryRequest, @@ -214,7 +223,6 @@ import { markAttentionRequestAsRead, } from "./attention.js"; import { - RunBackupCycleRequest, addBackupProvider, codecForAddBackupProviderRequest, codecForRemoveBackupProvider, @@ -227,6 +235,10 @@ import { setWalletDeviceId, } from "./backup/index.js"; import { getBalanceDetail, getBalances } from "./balance.js"; +import { + getMaxDepositAmount, + getMaxPeerPushDebitAmount, +} from "./coinSelection.js"; import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; import { CryptoDispatcher, @@ -264,16 +276,13 @@ import { getExchangeResources, getExchangeTos, getExchangeWireDetailsInTx, + handleStartExchangeWalletKyc, + handleTestingWaitExchangeState, + handleTestingWaitExchangeWalletKyc, listExchanges, lookupExchangeByUri, } from "./exchanges.js"; -import { - convertDepositAmount, - convertPeerPushAmount, - convertWithdrawalAmount, - getMaxDepositAmount, - getMaxPeerPushAmount, -} from "./instructedAmountConversion.js"; +import { convertDepositAmount } from "./instructedAmountConversion.js"; import { ObservableDbAccess, ObservableTaskScheduler, @@ -290,7 +299,7 @@ import { startRefundQueryForUri, } from "./pay-merchant.js"; import { - checkPeerPullPaymentInitiation, + checkPeerPullCredit, initiatePeerPullPayment, } from "./pay-peer-pull-credit.js"; import { @@ -335,8 +344,9 @@ import { failTransaction, getTransactionById, getTransactions, - getWithdrawalTransactionByUri, + getTransactionsV2, parseTransactionIdentifier, + rematerializeTransactions, restartAll as restartAllRunningTasks, resumeTransaction, retryAll, @@ -380,6 +390,7 @@ export interface WalletExecutionContext { readonly http: HttpRequestLibrary; readonly db: DbAccess<typeof WalletStoresV1>; readonly oc: ObservabilityContext; + readonly cts: CancellationToken.Source | undefined; readonly taskScheduler: TaskScheduler; } @@ -427,6 +438,41 @@ async function fillDefaults(wex: WalletExecutionContext): Promise<void> { } } +/** + * Incremented each time we want to re-materialize transactions. + */ +const MATERIALIZED_TRANSACTIONS_VERSION = 1; + +async function migrateMaterializedTransactions( + wex: WalletExecutionContext, +): Promise<void> { + await wex.db.runAllStoresReadWriteTx({}, async (tx) => { + const ver = await tx.config.get("materializedTransactionsVersion"); + if (ver) { + if (ver.key !== ConfigRecordKey.MaterializedTransactionsVersion) { + logger.error("invalid configuration (materializedTransactionsVersion)"); + return; + } + if (ver.value == MATERIALIZED_TRANSACTIONS_VERSION) { + return; + } + if (ver.value > MATERIALIZED_TRANSACTIONS_VERSION) { + logger.error( + "database is newer than code (materializedTransactionsVersion)", + ); + return; + } + } + + await rematerializeTransactions(wex, tx); + + await tx.config.put({ + key: ConfigRecordKey.MaterializedTransactionsVersion, + value: MATERIALIZED_TRANSACTIONS_VERSION, + }); + }); +} + export async function getDenomInfo( wex: WalletExecutionContext, tx: WalletDbReadOnlyTransaction<["denominations"]>, @@ -451,11 +497,12 @@ export async function getDenomInfo( * List bank accounts known to the wallet from * previous withdrawals. */ -async function listKnownBankAccounts( +async function handleListKnownBankAccounts( wex: WalletExecutionContext, - currency?: string, + req: ListKnownBankAccountsRequest, ): Promise<KnownBankAccounts> { const accounts: KnownBankAccountsInfo[] = []; + const currency = req.currency; await wex.db.runReadOnlyTx({ storeNames: ["bankAccounts"] }, async (tx) => { const knownAccounts = await tx.bankAccounts.iter().toArray(); for (const r of knownAccounts) { @@ -624,7 +671,12 @@ async function getClientFromWalletState( const client: WalletCoreApiClient = { async call(op, payload): Promise<any> { id = (id + 1) % (Number.MAX_SAFE_INTEGER - 100); - const res = await handleCoreApiRequest(ws, op, String(id), payload); + const res = await dispatchWalletCoreApiRequest( + ws, + op, + String(id), + payload, + ); switch (res.type) { case "error": throw TalerError.fromUncheckedDetail(res.error); @@ -701,6 +753,9 @@ async function recoverStoredBackup( }); logger.info(`backup found, now importing`); await importDb(wex.db.idbHandle(), bd); + await wex.db.runAllStoresReadWriteTx({}, async (tx) => { + await rematerializeTransactions(wex, tx); + }); logger.info(`import done`); } @@ -790,8 +845,11 @@ async function handleSetWalletRunConfig( logger.trace("filling defaults"); await fillDefaults(wex); } + + await migrateMaterializedTransactions(wex); + const resp: InitResponse = { - versionInfo: handleGetVersion(wex), + versionInfo: await handleGetVersion(wex), }; if (req.config?.lazyTaskLoop) { @@ -1008,6 +1066,50 @@ async function handleGetQrCodesForPayto( }; } +async function handleGetBankingChoicesForPayto( + wex: WalletExecutionContext, + req: GetBankingChoicesForPaytoRequest, +): Promise<GetBankingChoicesForPaytoResponse> { + const parsedPayto = parsePaytoUri(req.paytoUri); + if (!parsedPayto) { + throw Error("invalid payto URI"); + } + const amount = parsedPayto.params["amount"]; + if (!amount) { + logger.warn("payto URI has no amount"); + return { + choices: [], + }; + } + const currency = Amounts.currencyOf(amount); + switch (currency) { + case "KUDOS": + return { + choices: [ + { + label: "Demobank Website", + type: "link", + uri: `https://bank.demo.taler.net/webui/#/transfer/${encodeURIComponent( + req.paytoUri, + )}`, + }, + { + label: "Demobank App", + type: "link", + uri: `https://bank.demo.taler.net/app/transfer/${encodeURIComponent( + req.paytoUri, + )}`, + }, + ], + }; + break; + default: + return { + choices: [], + }; + } +} + async function handleConfirmPay( wex: WalletExecutionContext, req: ConfirmPayRequest, @@ -1118,18 +1220,14 @@ async function handleStartRefundQuery( return {}; } -async function handleAddBackupProvider( - wex: WalletExecutionContext, - req: RunBackupCycleRequest, -): Promise<EmptyObject> { - await runBackupCycle(wex, req); - return {}; -} - async function handleHintNetworkAvailability( wex: WalletExecutionContext, req: HintNetworkAvailabilityRequest, ): Promise<EmptyObject> { + // If network was already available, don't do anything + if (wex.ws.networkAvailable === req.isNetworkAvailable) { + return {}; + } wex.ws.networkAvailable = req.isNetworkAvailable; // When network becomes available, restart tasks as they're blocked // waiting for the network. @@ -1144,6 +1242,8 @@ async function handleGetDepositWireTypesForCurrency( req: GetDepositWireTypesForCurrencyRequest, ): Promise<GetDepositWireTypesForCurrencyResponse> { const wtSet: Set<string> = new Set(); + const wireTypeDetails: WireTypeDetails[] = []; + const talerBankHostnames: string[] = []; await wex.db.runReadOnlyTx( { storeNames: ["exchanges", "exchangeDetails"] }, async (tx) => { @@ -1165,25 +1265,42 @@ async function handleGetDepositWireTypesForCurrency( } } if (!usable) { - break; + continue; } const parsedPayto = parsePaytoUri(acc.payto_uri); if (!parsedPayto) { continue; } - wtSet.add(parsedPayto.targetType); + if ( + parsedPayto.isKnown && + parsedPayto.targetType === "x-taler-bank" + ) { + if (!talerBankHostnames.includes(parsedPayto.host)) { + talerBankHostnames.push(parsedPayto.host); + } + } + if (!wtSet.has(parsedPayto.targetType)) { + wtSet.add(parsedPayto.targetType); + wireTypeDetails.push({ + paymentTargetType: parsedPayto.targetType, + // Will possibly extended later by other exchanges + // with the same wire type. + talerBankHostnames, + }); + } } } }, ); return { wireTypes: [...wtSet], + wireTypeDetails, }; } async function handleListGlobalCurrencyExchanges( wex: WalletExecutionContext, - req: EmptyObject, + _req: EmptyObject, ): Promise<ListGlobalCurrencyExchangesResponse> { const resp: ListGlobalCurrencyExchangesResponse = { exchanges: [], @@ -1206,7 +1323,7 @@ async function handleListGlobalCurrencyExchanges( async function handleListGlobalCurrencyAuditors( wex: WalletExecutionContext, - req: EmptyObject, + _req: EmptyObject, ): Promise<ListGlobalCurrencyAuditorsResponse> { const resp: ListGlobalCurrencyAuditorsResponse = { auditors: [], @@ -1323,7 +1440,7 @@ async function handleAddGlobalCurrencyAuditor( async function handleShutdown( wex: WalletExecutionContext, - req: EmptyObject, + _req: EmptyObject, ): Promise<EmptyObject> { wex.ws.stop(); return {}; @@ -1339,7 +1456,7 @@ async function handleTestingSetTimetravel( } async function handleCanonicalizeBaseUrl( - wex: WalletExecutionContext, + _wex: WalletExecutionContext, req: CanonicalizeBaseUrlRequest, ): Promise<CanonicalizeBaseUrlResponse> { return { @@ -1357,7 +1474,7 @@ async function handleDeleteExchange( async function handleCreateStoredBackup( wex: WalletExecutionContext, - req: EmptyObject, + _req: EmptyObject, ): Promise<CreateStoredBackupResponse> { return await createStoredBackup(wex); } @@ -1375,6 +1492,93 @@ async function handleAcceptBankIntegratedWithdrawal( }); } +async function handleGetCurrencySpecification( + wex: WalletExecutionContext, + req: GetCurrencySpecificationRequest, +): Promise<GetCurrencySpecificationResponse> { + const spec = await wex.db.runReadOnlyTx( + { + storeNames: ["currencyInfo"], + }, + async (tx) => { + return WalletDbHelpers.getCurrencyInfo(tx, req.scope); + }, + ); + if (spec) { + return { + currencySpecification: spec.currencySpec, + }; + } + // Hard-coded mock for KUDOS and TESTKUDOS + if (req.scope.currency === "KUDOS") { + const kudosResp: GetCurrencySpecificationResponse = { + currencySpecification: { + name: "Kudos (Taler Demonstrator)", + num_fractional_input_digits: 2, + num_fractional_normal_digits: 2, + num_fractional_trailing_zero_digits: 2, + alt_unit_names: { + "0": "ク", + }, + }, + }; + return kudosResp; + } else if (req.scope.currency === "TESTKUDOS") { + const testkudosResp: GetCurrencySpecificationResponse = { + currencySpecification: { + name: "Test (Taler Unstable Demonstrator)", + num_fractional_input_digits: 0, + num_fractional_normal_digits: 0, + num_fractional_trailing_zero_digits: 0, + alt_unit_names: { + "0": "テ", + }, + }, + }; + return testkudosResp; + } + const defaultResp: GetCurrencySpecificationResponse = { + currencySpecification: { + name: req.scope.currency, + num_fractional_input_digits: 2, + num_fractional_normal_digits: 2, + num_fractional_trailing_zero_digits: 2, + alt_unit_names: { + "0": req.scope.currency, + }, + }, + }; + return defaultResp; +} + +export async function handleHintApplicationResumed( + wex: WalletExecutionContext, + req: EmptyObject, +): Promise<EmptyObject> { + logger.info("handling hintApplicationResumed"); + await restartAllRunningTasks(wex); + return {}; +} + +async function handleGetVersion( + wex: WalletExecutionContext, +): Promise<WalletCoreVersion> { + const result: WalletCoreVersion = { + implementationSemver: walletCoreBuildInfo.implementationSemver, + implementationGitHash: walletCoreBuildInfo.implementationGitHash, + hash: undefined, + version: WALLET_CORE_API_PROTOCOL_VERSION, + exchange: WALLET_EXCHANGE_PROTOCOL_VERSION, + merchant: WALLET_MERCHANT_PROTOCOL_VERSION, + bankConversionApiRange: WALLET_BANK_CONVERSION_API_PROTOCOL_VERSION, + bankIntegrationApiRange: TalerBankIntegrationHttpClient.PROTOCOL_VERSION, + corebankApiRange: WALLET_COREBANK_API_PROTOCOL_VERSION, + bank: TalerBankIntegrationHttpClient.PROTOCOL_VERSION, + devMode: wex.ws.config.testing.devModeActive, + }; + return result; +} + interface HandlerWithValidator<Tag extends WalletApiOperation> { codec: Codec<WalletCoreRequestType<Tag>>; handler: ( @@ -1383,8 +1587,15 @@ interface HandlerWithValidator<Tag extends WalletApiOperation> { ) => Promise<WalletCoreResponseType<Tag>>; } -// @ts-ignore const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { + [WalletApiOperation.TestingWaitExchangeState]: { + codec: codecForAny(), + handler: handleTestingWaitExchangeState, + }, + [WalletApiOperation.HintApplicationResumed]: { + codec: codecForEmptyObject(), + handler: handleHintApplicationResumed, + }, [WalletApiOperation.AbortTransaction]: { codec: codecForAbortTransaction(), handler: handleAbortTransaction, @@ -1442,14 +1653,14 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { codec: codecForTransactionsRequest(), handler: getTransactions, }, + [WalletApiOperation.GetTransactionsV2]: { + codec: codecForGetTransactionsV2Request(), + handler: getTransactionsV2, + }, [WalletApiOperation.GetTransactionById]: { codec: codecForTransactionByIdRequest(), handler: getTransactionById, }, - [WalletApiOperation.GetWithdrawalTransactionByUri]: { - codec: codecForGetWithdrawalDetailsForUri(), - handler: getWithdrawalTransactionByUri, - }, [WalletApiOperation.AddExchange]: { codec: codecForAddExchangeRequest(), handler: handleAddExchange, @@ -1482,564 +1693,503 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { codec: codecForAddExchangeRequest(), handler: (wex, req) => getExchangeDetailedInfo(wex, req.exchangeBaseUrl), }, -}; - -/** - * Implementation of the "wallet-core" API. - */ -async function dispatchRequestInternal( - wex: WalletExecutionContext, - cts: CancellationToken.Source, - operation: WalletApiOperation, - payload: unknown, -): Promise<WalletCoreResponseType<typeof operation>> { - if (!wex.ws.initCalled && operation !== WalletApiOperation.InitWallet) { - throw Error( - `wallet must be initialized before running operation ${operation}`, - ); - } - // FIXME: Can we make this more type-safe by using the request/response type - // definitions we already have? - switch (operation) { - case WalletApiOperation.CreateStoredBackup: - return await handleCreateStoredBackup(wex, {}); - case WalletApiOperation.DeleteStoredBackup: { - const req = codecForDeleteStoredBackupRequest().decode(payload); - return await handleDeleteStoredBackup(wex, req); - } - case WalletApiOperation.ListStoredBackups: - return listStoredBackups(wex); - case WalletApiOperation.RecoverStoredBackup: { - const req = codecForRecoverStoredBackupRequest().decode(payload); - return await handleRecoverStoredBackup(wex, req); - } - case WalletApiOperation.SetWalletRunConfig: - case WalletApiOperation.InitWallet: { - const req = codecForInitRequest().decode(payload); - return await handleSetWalletRunConfig(wex, req); - } - case WalletApiOperation.WithdrawTestkudos: { - return await handleWithdrawTestkudos(wex); - } - case WalletApiOperation.WithdrawTestBalance: { - const req = codecForWithdrawTestBalance().decode(payload); - return await handleWithdrawTestBalance(wex, req); - } - case WalletApiOperation.RunIntegrationTest: { - const req = codecForIntegrationTestArgs().decode(payload); - return await handleRunIntegrationTest(wex, req); - } - case WalletApiOperation.RunIntegrationTestV2: { - const req = codecForIntegrationTestV2Args().decode(payload); - return await handleRunIntegrationTestV2(wex, req); - } - case WalletApiOperation.ValidateIban: { - const req = codecForValidateIbanRequest().decode(payload); - return handleValidateIban(wex, req); - } - case WalletApiOperation.TestPay: { - const req = codecForTestPayArgs().decode(payload); - return await testPay(wex, req); - } - case WalletApiOperation.GetTransactions: { - const req = codecForTransactionsRequest().decode(payload); - return await getTransactions(wex, req); - } - case WalletApiOperation.GetTransactionById: { - const req = codecForTransactionByIdRequest().decode(payload); - return await getTransactionById(wex, req); - } - case WalletApiOperation.GetWithdrawalTransactionByUri: { - const req = codecForGetWithdrawalDetailsForUri().decode(payload); - return await getWithdrawalTransactionByUri(wex, req); - } - case WalletApiOperation.AddExchange: { - const req = codecForAddExchangeRequest().decode(payload); - return await handleAddExchange(wex, req); - } - case WalletApiOperation.TestingPing: { + [WalletApiOperation.ListKnownBankAccounts]: { + codec: codecForListKnownBankAccounts(), + handler: handleListKnownBankAccounts, + }, + [WalletApiOperation.AddKnownBankAccounts]: { + codec: codecForAddKnownBankAccounts(), + handler: handleAddKnownBankAccount, + }, + [WalletApiOperation.ForgetKnownBankAccounts]: { + codec: codecForForgetKnownBankAccounts(), + handler: handleForgetKnownBankAccounts, + }, + [WalletApiOperation.GetWithdrawalDetailsForUri]: { + codec: codecForGetWithdrawalDetailsForUri(), + handler: (wex, req) => + getWithdrawalDetailsForUri(wex, req.talerWithdrawUri), + }, + [WalletApiOperation.TestingGetReserveHistory]: { + codec: codecForTestingGetReserveHistoryRequest(), + handler: handleTestingGetReserveHistory, + }, + [WalletApiOperation.AcceptManualWithdrawal]: { + codec: codecForAcceptManualWithdrawalRequest(), + handler: handleAcceptManualWithdrawal, + }, + [WalletApiOperation.GetWithdrawalDetailsForAmount]: { + codec: codecForGetWithdrawalDetailsForAmountRequest(), + handler: getWithdrawalDetailsForAmount, + }, + [WalletApiOperation.GetBalances]: { + codec: codecForEmptyObject(), + handler: getBalances, + }, + [WalletApiOperation.GetBalanceDetail]: { + codec: codecForGetBalanceDetailRequest(), + handler: getBalanceDetail, + }, + [WalletApiOperation.GetUserAttentionRequests]: { + codec: codecForUserAttentionsRequest(), + handler: getUserAttentions, + }, + [WalletApiOperation.MarkAttentionRequestAsRead]: { + codec: codecForUserAttentionByIdRequest(), + handler: async (wex, req) => { + await markAttentionRequestAsRead(wex, req); return {}; - } - case WalletApiOperation.UpdateExchangeEntry: { - const req = codecForUpdateExchangeEntryRequest().decode(payload); - return await handleUpdateExchangeEntry(wex, req); - } - case WalletApiOperation.TestingGetDenomStats: { - const req = codecForTestingGetDenomStatsRequest().decode(payload); - return handleTestingGetDenomStats(wex, req); - } - case WalletApiOperation.ListExchanges: { - return await listExchanges(wex); - } - case WalletApiOperation.GetExchangeEntryByUrl: { - const req = codecForGetExchangeEntryByUrlRequest().decode(payload); - return lookupExchangeByUri(wex, req); - } - case WalletApiOperation.ListExchangesForScopedCurrency: { - const req = - codecForListExchangesForScopedCurrencyRequest().decode(payload); - return await handleListExchangesForScopedCurrency(wex, req); - } - case WalletApiOperation.GetExchangeDetailedInfo: { - const req = codecForAddExchangeRequest().decode(payload); - return await getExchangeDetailedInfo(wex, req.exchangeBaseUrl); - } - case WalletApiOperation.ListKnownBankAccounts: { - const req = codecForListKnownBankAccounts().decode(payload); - return await listKnownBankAccounts(wex, req.currency); - } - case WalletApiOperation.AddKnownBankAccounts: { - const req = codecForAddKnownBankAccounts().decode(payload); - return await handleAddKnownBankAccount(wex, req); - } - case WalletApiOperation.ForgetKnownBankAccounts: { - const req = codecForForgetKnownBankAccounts().decode(payload); - return await handleForgetKnownBankAccounts(wex, req); - } - case WalletApiOperation.GetWithdrawalDetailsForUri: { - const req = codecForGetWithdrawalDetailsForUri().decode(payload); - return await getWithdrawalDetailsForUri(wex, req.talerWithdrawUri); - } - case WalletApiOperation.TestingGetReserveHistory: { - const req = codecForTestingGetReserveHistoryRequest().decode(payload); - return await handleTestingGetReserveHistory(wex, req); - } - case WalletApiOperation.AcceptManualWithdrawal: { - const req = codecForAcceptManualWithdrawalRequest().decode(payload); - return await handleAcceptManualWithdrawal(wex, req); - } - case WalletApiOperation.GetWithdrawalDetailsForAmount: { - const req = - codecForGetWithdrawalDetailsForAmountRequest().decode(payload); - return await getWithdrawalDetailsForAmount(wex, cts, req); - } - case WalletApiOperation.GetBalances: { - return await getBalances(wex); - } - case WalletApiOperation.GetBalanceDetail: { - const req = codecForGetBalanceDetailRequest().decode(payload); - return await getBalanceDetail(wex, req); - } - case WalletApiOperation.GetUserAttentionRequests: { - const req = codecForUserAttentionsRequest().decode(payload); - return await getUserAttentions(wex, req); - } - case WalletApiOperation.MarkAttentionRequestAsRead: { - const req = codecForUserAttentionByIdRequest().decode(payload); - return await markAttentionRequestAsRead(wex, req); - } - case WalletApiOperation.GetUserAttentionUnreadCount: { - const req = codecForUserAttentionsRequest().decode(payload); - return await getUserAttentionsUnreadCount(wex, req); - } - case WalletApiOperation.SetExchangeTosAccepted: { - const req = codecForAcceptExchangeTosRequest().decode(payload); + }, + }, + [WalletApiOperation.GetUserAttentionUnreadCount]: { + codec: codecForUserAttentionsRequest(), + handler: getUserAttentionsUnreadCount, + }, + [WalletApiOperation.SetExchangeTosAccepted]: { + codec: codecForAcceptExchangeTosRequest(), + handler: async (wex, req) => { await acceptExchangeTermsOfService(wex, req.exchangeBaseUrl); return {}; - } - case WalletApiOperation.SetExchangeTosForgotten: { - const req = codecForAcceptExchangeTosRequest().decode(payload); + }, + }, + [WalletApiOperation.SetExchangeTosForgotten]: { + codec: codecForAcceptExchangeTosRequest(), + handler: async (wex, req) => { await forgetExchangeTermsOfService(wex, req.exchangeBaseUrl); return {}; - } - case WalletApiOperation.AcceptBankIntegratedWithdrawal: { - const req = - codecForAcceptBankIntegratedWithdrawalRequest().decode(payload); - return handleAcceptBankIntegratedWithdrawal(wex, req); - } - case WalletApiOperation.ConfirmWithdrawal: { - const req = codecForConfirmWithdrawalRequestRequest().decode(payload); - return confirmWithdrawal(wex, req); - } - case WalletApiOperation.PrepareBankIntegratedWithdrawal: { - const req = - codecForPrepareBankIntegratedWithdrawalRequest().decode(payload); - return prepareBankIntegratedWithdrawal(wex, req); - } - case WalletApiOperation.GetExchangeTos: { - const req = codecForGetExchangeTosRequest().decode(payload); - return await handleGetExchangeTos(wex, req); - } - case WalletApiOperation.GetContractTermsDetails: { - const req = codecForGetContractTermsDetails().decode(payload); - return handleGetContractTermsDetails(wex, req); - } - case WalletApiOperation.RetryPendingNow: { - return handleRetryPendingNow(wex); - } - case WalletApiOperation.SharePayment: { - const req = codecForSharePaymentRequest().decode(payload); - return await handleSharePayment(wex, req); - } - case WalletApiOperation.PrepareWithdrawExchange: { - const req = codecForPrepareWithdrawExchangeRequest().decode(payload); - return await handlePrepareWithdrawExchange(wex, req); - } - case WalletApiOperation.CheckPayForTemplate: { - const req = codecForCheckPayTemplateRequest().decode(payload); - return await checkPayForTemplate(wex, req); - } - case WalletApiOperation.PreparePayForUri: { - const req = codecForPreparePayRequest().decode(payload); - return await preparePayForUri(wex, req.talerPayUri); - } - case WalletApiOperation.PreparePayForTemplate: { - const req = codecForPreparePayTemplateRequest().decode(payload); - return preparePayForTemplate(wex, req); - } - case WalletApiOperation.GetQrCodesForPayto: { - const req = codecForGetQrCodesForPaytoRequest().decode(payload); - return handleGetQrCodesForPayto(wex, req); - } - case WalletApiOperation.ConfirmPay: { - const req = codecForConfirmPayRequest().decode(payload); - return handleConfirmPay(wex, req); - } - case WalletApiOperation.AbortTransaction: { - const req = codecForAbortTransaction().decode(payload); - return handleAbortTransaction(wex, req); - } - case WalletApiOperation.SuspendTransaction: { - const req = codecForSuspendTransaction().decode(payload); - return handleSuspendTransaction(wex, req); - } - case WalletApiOperation.GetActiveTasks: { - return await handleGetActiveTasks(wex, {}); - } - case WalletApiOperation.FailTransaction: { - const req = codecForFailTransactionRequest().decode(payload); - return await handleFailTransaction(wex, req); - } - case WalletApiOperation.ResumeTransaction: { - const req = codecForResumeTransaction().decode(payload); + }, + }, + [WalletApiOperation.AcceptBankIntegratedWithdrawal]: { + codec: codecForAcceptBankIntegratedWithdrawalRequest(), + handler: handleAcceptBankIntegratedWithdrawal, + }, + [WalletApiOperation.ConfirmWithdrawal]: { + codec: codecForConfirmWithdrawalRequestRequest(), + handler: confirmWithdrawal, + }, + [WalletApiOperation.PrepareBankIntegratedWithdrawal]: { + codec: codecForPrepareBankIntegratedWithdrawalRequest(), + handler: prepareBankIntegratedWithdrawal, + }, + [WalletApiOperation.GetExchangeTos]: { + codec: codecForGetExchangeTosRequest(), + handler: handleGetExchangeTos, + }, + [WalletApiOperation.GetContractTermsDetails]: { + codec: codecForGetContractTermsDetails(), + handler: handleGetContractTermsDetails, + }, + [WalletApiOperation.RetryPendingNow]: { + codec: codecForEmptyObject(), + handler: handleRetryPendingNow, + }, + [WalletApiOperation.SharePayment]: { + codec: codecForSharePaymentRequest(), + handler: handleSharePayment, + }, + [WalletApiOperation.PrepareWithdrawExchange]: { + codec: codecForPrepareWithdrawExchangeRequest(), + handler: handlePrepareWithdrawExchange, + }, + [WalletApiOperation.CheckPayForTemplate]: { + codec: codecForCheckPayTemplateRequest(), + handler: checkPayForTemplate, + }, + [WalletApiOperation.PreparePayForUri]: { + codec: codecForPreparePayRequest(), + handler: (wex, req) => preparePayForUri(wex, req.talerPayUri), + }, + [WalletApiOperation.PreparePayForTemplate]: { + codec: codecForPreparePayTemplateRequest(), + handler: preparePayForTemplate, + }, + [WalletApiOperation.GetQrCodesForPayto]: { + codec: codecForGetQrCodesForPaytoRequest(), + handler: handleGetQrCodesForPayto, + }, + [WalletApiOperation.ConfirmPay]: { + codec: codecForConfirmPayRequest(), + handler: handleConfirmPay, + }, + [WalletApiOperation.SuspendTransaction]: { + codec: codecForSuspendTransaction(), + handler: handleSuspendTransaction, + }, + [WalletApiOperation.GetActiveTasks]: { + codec: codecForEmptyObject(), + handler: handleGetActiveTasks, + }, + [WalletApiOperation.FailTransaction]: { + codec: codecForFailTransactionRequest(), + handler: handleFailTransaction, + }, + [WalletApiOperation.ResumeTransaction]: { + codec: codecForResumeTransaction(), + handler: async (wex, req) => { await resumeTransaction(wex, req.transactionId); return {}; - } - case WalletApiOperation.DumpCoins: { - return await dumpCoins(wex); - } - case WalletApiOperation.SetCoinSuspended: { - const req = codecForSetCoinSuspendedRequest().decode(payload); + }, + }, + [WalletApiOperation.DumpCoins]: { + codec: codecForEmptyObject(), + handler: dumpCoins, + }, + [WalletApiOperation.SetCoinSuspended]: { + codec: codecForSetCoinSuspendedRequest(), + handler: async (wex, req) => { await setCoinSuspended(wex, req.coinPub, req.suspended); return {}; - } - case WalletApiOperation.TestingGetSampleTransactions: - const req = codecForEmptyObject().decode(payload); - return handleTestingGetSampleTransactions(wex, req); - case WalletApiOperation.ForceRefresh: { - const req = codecForForceRefreshRequest().decode(payload); - return await forceRefresh(wex, req); - } - case WalletApiOperation.StartRefundQueryForUri: { - const req = codecForPrepareRefundRequest().decode(payload); - return await startRefundQueryForUri(wex, req.talerRefundUri); - } - case WalletApiOperation.StartRefundQuery: { - const req = codecForStartRefundQueryRequest().decode(payload); - return handleStartRefundQuery(wex, req); - } - case WalletApiOperation.AddBackupProvider: { - const req = codecForAddBackupProviderRequest().decode(payload); - return await addBackupProvider(wex, req); - } - case WalletApiOperation.RunBackupCycle: { - const req = codecForRunBackupCycle().decode(payload); - return handleAddBackupProvider(wex, req); - } - case WalletApiOperation.RemoveBackupProvider: { - const req = codecForRemoveBackupProvider().decode(payload); + }, + }, + [WalletApiOperation.TestingGetSampleTransactions]: { + codec: codecForEmptyObject(), + handler: handleTestingGetSampleTransactions, + }, + [WalletApiOperation.StartRefundQueryForUri]: { + codec: codecForPrepareRefundRequest(), + handler: (wex, req) => startRefundQueryForUri(wex, req.talerRefundUri), + }, + [WalletApiOperation.StartRefundQuery]: { + codec: codecForStartRefundQueryRequest(), + handler: handleStartRefundQuery, + }, + [WalletApiOperation.AddBackupProvider]: { + codec: codecForAddBackupProviderRequest(), + handler: addBackupProvider, + }, + [WalletApiOperation.RunBackupCycle]: { + codec: codecForRunBackupCycle(), + handler: async (wex, req) => { + await runBackupCycle(wex, req); + return {}; + }, + }, + [WalletApiOperation.RemoveBackupProvider]: { + codec: codecForRemoveBackupProvider(), + handler: async (wex, req) => { await removeBackupProvider(wex, req); return {}; - } - case WalletApiOperation.ExportBackupRecovery: { - const resp = await getBackupRecovery(wex); - return resp; - } - case WalletApiOperation.TestingWaitTransactionState: { - const req = payload as TestingWaitTransactionRequest; + }, + }, + [WalletApiOperation.ExportBackupRecovery]: { + codec: codecForEmptyObject(), + handler: getBackupRecovery, + }, + [WalletApiOperation.TestingWaitTransactionState]: { + codec: codecForAny(), + handler: async (wex, req) => { await waitTransactionState(wex, req.transactionId, req.txState); return {}; - } - case WalletApiOperation.GetCurrencySpecification: { - const req = codecForGetCurrencyInfoRequest().decode(payload); - return handleGetCurrencySpecification(wex, req); - } - case WalletApiOperation.ImportBackupRecovery: { - const req = codecForAny().decode(payload); + }, + }, + [WalletApiOperation.GetCurrencySpecification]: { + codec: codecForGetCurrencyInfoRequest(), + handler: handleGetCurrencySpecification, + }, + [WalletApiOperation.ImportBackupRecovery]: { + codec: codecForAny(), + handler: async (wex, req) => { await loadBackupRecovery(wex, req); return {}; - } - case WalletApiOperation.HintNetworkAvailability: { - const req = codecForHintNetworkAvailabilityRequest().decode(payload); - return await handleHintNetworkAvailability(wex, req); - } - case WalletApiOperation.ConvertDepositAmount: { - const req = codecForConvertAmountRequest.decode(payload); - return await convertDepositAmount(wex, req); - } - case WalletApiOperation.GetMaxDepositAmount: { - const req = codecForGetAmountRequest.decode(payload); - return await getMaxDepositAmount(wex, req); - } - case WalletApiOperation.ConvertPeerPushAmount: { - const req = codecForConvertAmountRequest.decode(payload); - return await convertPeerPushAmount(wex, req); - } - case WalletApiOperation.GetMaxPeerPushAmount: { - const req = codecForGetAmountRequest.decode(payload); - return await getMaxPeerPushAmount(wex, req); - } - case WalletApiOperation.ConvertWithdrawalAmount: { - const req = codecForConvertAmountRequest.decode(payload); - return await convertWithdrawalAmount(wex, req); - } - case WalletApiOperation.GetBackupInfo: { - const resp = await getBackupInfo(wex); - return resp; - } - case WalletApiOperation.PrepareDeposit: { - const req = codecForPrepareDepositRequest().decode(payload); - return await checkDepositGroup(wex, req); - } - case WalletApiOperation.GenerateDepositGroupTxId: + }, + }, + [WalletApiOperation.HintNetworkAvailability]: { + codec: codecForHintNetworkAvailabilityRequest(), + handler: handleHintNetworkAvailability, + }, + [WalletApiOperation.ConvertDepositAmount]: { + codec: codecForConvertAmountRequest, + handler: convertDepositAmount, + }, + [WalletApiOperation.GetMaxDepositAmount]: { + codec: codecForGetMaxDepositAmountRequest, + handler: getMaxDepositAmount, + }, + [WalletApiOperation.GetMaxPeerPushDebitAmount]: { + codec: codecForGetMaxPeerPushDebitAmountRequest(), + handler: getMaxPeerPushDebitAmount, + }, + [WalletApiOperation.GetBackupInfo]: { + codec: codecForEmptyObject(), + handler: getBackupInfo, + }, + [WalletApiOperation.PrepareDeposit]: { + codec: codecForCheckDepositRequest(), + handler: checkDepositGroup, + }, + [WalletApiOperation.CheckDeposit]: { + codec: codecForCheckDepositRequest(), + handler: checkDepositGroup, + }, + [WalletApiOperation.GenerateDepositGroupTxId]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { return { - transactionId: generateDepositGroupTxId(), + transactionId: generateDepositGroupTxId() as TransactionIdStr, }; - case WalletApiOperation.CreateDepositGroup: { - const req = codecForCreateDepositGroupRequest().decode(payload); - return await createDepositGroup(wex, req); - } - case WalletApiOperation.DeleteTransaction: { - const req = codecForDeleteTransactionRequest().decode(payload); + }, + }, + [WalletApiOperation.CreateDepositGroup]: { + codec: codecForCreateDepositGroupRequest(), + handler: createDepositGroup, + }, + [WalletApiOperation.DeleteTransaction]: { + codec: codecForDeleteTransactionRequest(), + handler: async (wex, req) => { await deleteTransaction(wex, req.transactionId); return {}; - } - case WalletApiOperation.RetryTransaction: { - const req = codecForRetryTransactionRequest().decode(payload); + }, + }, + [WalletApiOperation.RetryTransaction]: { + codec: codecForRetryTransactionRequest(), + handler: async (wex, req) => { await retryTransaction(wex, req.transactionId); return {}; - } - case WalletApiOperation.SetWalletDeviceId: { - const req = codecForSetWalletDeviceIdRequest().decode(payload); + }, + }, + [WalletApiOperation.SetWalletDeviceId]: { + codec: codecForSetWalletDeviceIdRequest(), + handler: async (wex, req) => { await setWalletDeviceId(wex, req.walletDeviceId); return {}; - } - case WalletApiOperation.TestCrypto: { + }, + }, + [WalletApiOperation.TestCrypto]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { return await wex.cryptoApi.hashString({ str: "hello world" }); - } - case WalletApiOperation.ClearDb: { + }, + }, + [WalletApiOperation.ClearDb]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { await clearDatabase(wex.db.idbHandle()); wex.ws.clearAllCaches(); return {}; - } - case WalletApiOperation.Recycle: { + }, + }, + [WalletApiOperation.Recycle]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { throw Error("not implemented"); - return {}; - } - case WalletApiOperation.ExportDb: { + }, + }, + [WalletApiOperation.ExportDb]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { const dbDump = await exportDb(wex.ws.idb); return dbDump; - } - case WalletApiOperation.GetDepositWireTypesForCurrency: { - const req = - codecForGetDepositWireTypesForCurrencyRequest().decode(payload); - return handleGetDepositWireTypesForCurrency(wex, req); - } - case WalletApiOperation.ListGlobalCurrencyExchanges: { - const req = codecForEmptyObject().decode(payload); - return await handleListGlobalCurrencyExchanges(wex, req); - } - case WalletApiOperation.ListGlobalCurrencyAuditors: { - const req = codecForEmptyObject().decode(payload); - return await handleListGlobalCurrencyAuditors(wex, req); - } - case WalletApiOperation.AddGlobalCurrencyExchange: { - const req = codecForAddGlobalCurrencyExchangeRequest().decode(payload); - return handleAddGlobalCurrencyExchange(wex, req); - } - case WalletApiOperation.RemoveGlobalCurrencyExchange: { - const req = codecForRemoveGlobalCurrencyExchangeRequest().decode(payload); - return handleRemoveGlobalCurrencyExchange(wex, req); - } - case WalletApiOperation.AddGlobalCurrencyAuditor: { - const req = codecForAddGlobalCurrencyAuditorRequest().decode(payload); - return handleAddGlobalCurrencyAuditor(wex, req); - } - case WalletApiOperation.TestingWaitTasksDone: { + }, + }, + [WalletApiOperation.GetDepositWireTypesForCurrency]: { + codec: codecForGetDepositWireTypesForCurrencyRequest(), + handler: handleGetDepositWireTypesForCurrency, + }, + [WalletApiOperation.ListGlobalCurrencyExchanges]: { + codec: codecForEmptyObject(), + handler: handleListGlobalCurrencyExchanges, + }, + [WalletApiOperation.ListGlobalCurrencyAuditors]: { + codec: codecForEmptyObject(), + handler: handleListGlobalCurrencyAuditors, + }, + [WalletApiOperation.AddGlobalCurrencyExchange]: { + codec: codecForAddGlobalCurrencyExchangeRequest(), + handler: handleAddGlobalCurrencyExchange, + }, + [WalletApiOperation.RemoveGlobalCurrencyExchange]: { + codec: codecForRemoveGlobalCurrencyExchangeRequest(), + handler: handleRemoveGlobalCurrencyExchange, + }, + [WalletApiOperation.AddGlobalCurrencyAuditor]: { + codec: codecForAddGlobalCurrencyAuditorRequest(), + handler: handleAddGlobalCurrencyAuditor, + }, + [WalletApiOperation.TestingWaitTasksDone]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { await waitTasksDone(wex); return {}; - } - case WalletApiOperation.TestingResetAllRetries: + }, + }, + [WalletApiOperation.TestingResetAllRetries]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { await retryAll(wex); return {}; - case WalletApiOperation.RemoveGlobalCurrencyAuditor: { - const req = codecForRemoveGlobalCurrencyAuditorRequest().decode(payload); - return await handleRemoveGlobalCurrencyAuditor(wex, req); - } - case WalletApiOperation.ImportDb: { - const req = codecForImportDbRequest().decode(payload); + }, + }, + [WalletApiOperation.RemoveGlobalCurrencyAuditor]: { + codec: codecForRemoveGlobalCurrencyAuditorRequest(), + handler: handleRemoveGlobalCurrencyAuditor, + }, + [WalletApiOperation.ImportDb]: { + codec: codecForImportDbRequest(), + handler: async (wex, req) => { + // FIXME: This should atomically re-materialize transactions! await importDb(wex.db.idbHandle(), req.dump); - return []; - } - case WalletApiOperation.CheckPeerPushDebit: { - const req = codecForCheckPeerPushDebitRequest().decode(payload); - return await checkPeerPushDebit(wex, req); - } - case WalletApiOperation.InitiatePeerPushDebit: { - const req = codecForInitiatePeerPushDebitRequest().decode(payload); - return await initiatePeerPushDebit(wex, req); - } - case WalletApiOperation.PreparePeerPushCredit: { - const req = codecForPreparePeerPushCreditRequest().decode(payload); - return await preparePeerPushCredit(wex, req); - } - case WalletApiOperation.ConfirmPeerPushCredit: { - const req = codecForConfirmPeerPushPaymentRequest().decode(payload); - return await confirmPeerPushCredit(wex, req); - } - case WalletApiOperation.CheckPeerPullCredit: { - const req = codecForPreparePeerPullPaymentRequest().decode(payload); - return await checkPeerPullPaymentInitiation(wex, req); - } - case WalletApiOperation.InitiatePeerPullCredit: { - const req = codecForInitiatePeerPullPaymentRequest().decode(payload); - return await initiatePeerPullPayment(wex, req); - } - case WalletApiOperation.PreparePeerPullDebit: { - const req = codecForCheckPeerPullPaymentRequest().decode(payload); - return await preparePeerPullDebit(wex, req); - } - case WalletApiOperation.ConfirmPeerPullDebit: { - const req = codecForAcceptPeerPullPaymentRequest().decode(payload); - return await confirmPeerPullDebit(wex, req); - } - case WalletApiOperation.ApplyDevExperiment: { - const req = codecForApplyDevExperiment().decode(payload); + await wex.db.runAllStoresReadWriteTx({}, async (tx) => { + await rematerializeTransactions(wex, tx); + }); + return {}; + }, + }, + [WalletApiOperation.CheckPeerPushDebit]: { + codec: codecForCheckPeerPushDebitRequest(), + handler: checkPeerPushDebit, + }, + [WalletApiOperation.InitiatePeerPushDebit]: { + codec: codecForInitiatePeerPushDebitRequest(), + handler: initiatePeerPushDebit, + }, + [WalletApiOperation.PreparePeerPushCredit]: { + codec: codecForPreparePeerPushCreditRequest(), + handler: preparePeerPushCredit, + }, + [WalletApiOperation.ConfirmPeerPushCredit]: { + codec: codecForConfirmPeerPushPaymentRequest(), + handler: confirmPeerPushCredit, + }, + [WalletApiOperation.CheckPeerPullCredit]: { + codec: codecForPreparePeerPullPaymentRequest(), + handler: checkPeerPullCredit, + }, + [WalletApiOperation.InitiatePeerPullCredit]: { + codec: codecForInitiatePeerPullPaymentRequest(), + handler: initiatePeerPullPayment, + }, + [WalletApiOperation.PreparePeerPullDebit]: { + codec: codecForCheckPeerPullPaymentRequest(), + handler: preparePeerPullDebit, + }, + [WalletApiOperation.ConfirmPeerPullDebit]: { + codec: codecForAcceptPeerPullPaymentRequest(), + handler: confirmPeerPullDebit, + }, + [WalletApiOperation.ApplyDevExperiment]: { + codec: codecForApplyDevExperiment(), + handler: async (wex, req) => { await applyDevExperiment(wex, req.devExperimentUri); return {}; - } - case WalletApiOperation.Shutdown: { - const req = codecForEmptyObject().decode(payload); - return await handleShutdown(wex, req); - } - case WalletApiOperation.GetVersion: { - return handleGetVersion(wex); - } - case WalletApiOperation.TestingWaitTransactionsFinal: - return await waitUntilAllTransactionsFinal(wex); - case WalletApiOperation.TestingWaitRefreshesFinal: - return await waitUntilRefreshesDone(wex); - case WalletApiOperation.TestingSetTimetravel: { - const req = codecForTestingSetTimetravelRequest().decode(payload); - return await handleTestingSetTimetravel(wex, req); - } - case WalletApiOperation.DeleteExchange: { - const req = codecForDeleteExchangeRequest().decode(payload); - return await handleDeleteExchange(wex, req); - } - case WalletApiOperation.GetExchangeResources: { - const req = codecForGetExchangeResourcesRequest().decode(payload); + }, + }, + [WalletApiOperation.Shutdown]: { + codec: codecForEmptyObject(), + handler: handleShutdown, + }, + [WalletApiOperation.GetVersion]: { + codec: codecForEmptyObject(), + handler: handleGetVersion, + }, + [WalletApiOperation.TestingWaitTransactionsFinal]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { + await waitUntilAllTransactionsFinal(wex); + return {}; + }, + }, + [WalletApiOperation.TestingWaitRefreshesFinal]: { + codec: codecForEmptyObject(), + handler: async (wex, req) => { + await waitUntilRefreshesDone(wex); + return {}; + }, + }, + [WalletApiOperation.TestingSetTimetravel]: { + codec: codecForTestingSetTimetravelRequest(), + handler: async (wex, req) => { + await handleTestingSetTimetravel(wex, req); + return {}; + }, + }, + [WalletApiOperation.DeleteExchange]: { + codec: codecForDeleteExchangeRequest(), + handler: handleDeleteExchange, + }, + [WalletApiOperation.GetExchangeResources]: { + codec: codecForGetExchangeResourcesRequest(), + handler: async (wex, req) => { return await getExchangeResources(wex, req.exchangeBaseUrl); - } - case WalletApiOperation.CanonicalizeBaseUrl: { - const req = codecForCanonicalizeBaseUrlRequest().decode(payload); - return handleCanonicalizeBaseUrl(wex, req); - } - } - throw TalerError.fromDetail( - TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN, - { - operation, }, - "unknown operation", - ); -} - -export async function handleGetCurrencySpecification( - wex: WalletExecutionContext, - req: GetCurrencySpecificationRequest, -): Promise<GetCurrencySpecificationResponse> { - const spec = await wex.db.runReadOnlyTx( - { - storeNames: ["currencyInfo"], + }, + [WalletApiOperation.CanonicalizeBaseUrl]: { + codec: codecForCanonicalizeBaseUrlRequest(), + handler: handleCanonicalizeBaseUrl, + }, + [WalletApiOperation.ForceRefresh]: { + codec: codecForForceRefreshRequest(), + handler: async (wex, req) => { + await forceRefresh(wex, req); + return {}; }, - async (tx) => { - return WalletDbHelpers.getCurrencyInfo(tx, req.scope); + }, + [WalletApiOperation.ExportBackup]: { + codec: codecForAny(), + handler: async (wex, req) => { + throw Error("not implemented"); }, - ); - if (spec) { - return { - currencySpecification: spec.currencySpec, - }; + }, + [WalletApiOperation.ListAssociatedRefreshes]: { + codec: codecForAny(), + handler: async (wex, req) => { + throw Error("not implemented"); + }, + }, + [WalletApiOperation.GetBankingChoicesForPayto]: { + codec: codecForGetBankingChoicesForPaytoRequest(), + handler: handleGetBankingChoicesForPayto, + }, + [WalletApiOperation.StartExchangeWalletKyc]: { + codec: codecForStartExchangeWalletKycRequest(), + handler: handleStartExchangeWalletKyc, + }, + [WalletApiOperation.TestingWaitExchangeWalletKyc]: { + codec: codecForTestingWaitWalletKycRequest(), + handler: handleTestingWaitExchangeWalletKyc, + }, +}; + +/** + * Implementation of the "wallet-core" API. + */ +async function dispatchRequestInternal( + wex: WalletExecutionContext, + operation: WalletApiOperation, + payload: unknown, +): Promise<WalletCoreResponseType<typeof operation>> { + if (!wex.ws.initCalled && operation !== WalletApiOperation.InitWallet) { + throw Error( + `wallet must be initialized before running operation ${operation}`, + ); } - // Hard-coded mock for KUDOS and TESTKUDOS - if (req.scope.currency === "KUDOS") { - const kudosResp: GetCurrencySpecificationResponse = { - currencySpecification: { - name: "Kudos (Taler Demonstrator)", - num_fractional_input_digits: 2, - num_fractional_normal_digits: 2, - num_fractional_trailing_zero_digits: 2, - alt_unit_names: { - "0": "ク", - }, - }, - }; - return kudosResp; - } else if (req.scope.currency === "TESTKUDOS") { - const testkudosResp: GetCurrencySpecificationResponse = { - currencySpecification: { - name: "Test (Taler Unstable Demonstrator)", - num_fractional_input_digits: 0, - num_fractional_normal_digits: 0, - num_fractional_trailing_zero_digits: 0, - alt_unit_names: { - "0": "テ", - }, + + const h: HandlerWithValidator<any> = handlers[operation]; + + if (!h) { + throw TalerError.fromDetail( + TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN, + { + operation, }, - }; - return testkudosResp; + "unknown operation", + ); } - const defaultResp: GetCurrencySpecificationResponse = { - currencySpecification: { - name: req.scope.currency, - num_fractional_input_digits: 2, - num_fractional_normal_digits: 2, - num_fractional_trailing_zero_digits: 2, - alt_unit_names: { - "0": req.scope.currency, - }, - }, - }; - return defaultResp; -} -function handleGetVersion(wex: WalletExecutionContext): WalletCoreVersion { - const result: WalletCoreVersion = { - implementationSemver: walletCoreBuildInfo.implementationSemver, - implementationGitHash: walletCoreBuildInfo.implementationGitHash, - hash: undefined, - version: WALLET_CORE_API_PROTOCOL_VERSION, - exchange: WALLET_EXCHANGE_PROTOCOL_VERSION, - merchant: WALLET_MERCHANT_PROTOCOL_VERSION, - bankConversionApiRange: WALLET_BANK_CONVERSION_API_PROTOCOL_VERSION, - bankIntegrationApiRange: TalerBankIntegrationHttpClient.PROTOCOL_VERSION, - corebankApiRange: WALLET_COREBANK_API_PROTOCOL_VERSION, - bank: TalerBankIntegrationHttpClient.PROTOCOL_VERSION, - devMode: wex.ws.config.testing.devModeActive, - }; - return result; + const req = h.codec.decode(payload); + return await h.handler(wex, req); } export function getObservedWalletExecutionContext( ws: InternalWalletState, cancellationToken: CancellationToken, + cts: CancellationToken.Source | undefined, oc: ObservabilityContext, ): WalletExecutionContext { const db = ws.createDbAccessHandle(cancellationToken); const wex: WalletExecutionContext = { ws, cancellationToken, + cts, cryptoApi: observeTalerCrypto(ws.cryptoApi, oc), db: new ObservableDbAccess(db, oc), http: new ObservableHttpClientLibrary(ws.http, oc), @@ -2052,12 +2202,14 @@ export function getObservedWalletExecutionContext( export function getNormalWalletExecutionContext( ws: InternalWalletState, cancellationToken: CancellationToken, + cts: CancellationToken.Source | undefined, oc: ObservabilityContext, ): WalletExecutionContext { const db = ws.createDbAccessHandle(cancellationToken); const wex: WalletExecutionContext = { ws, cancellationToken, + cts, cryptoApi: ws.cryptoApi, db, get http() { @@ -2075,7 +2227,7 @@ export function getNormalWalletExecutionContext( /** * Handle a request to the wallet-core API. */ -async function handleCoreApiRequest( +async function dispatchWalletCoreApiRequest( ws: InternalWalletState, operation: string, id: string, @@ -2106,12 +2258,12 @@ async function handleCoreApiRequest( }, }; - wex = getObservedWalletExecutionContext(ws, cts.token, oc); + wex = getObservedWalletExecutionContext(ws, cts.token, cts, oc); } else { oc = { observe(evt) {}, }; - wex = getNormalWalletExecutionContext(ws, cts.token, oc); + wex = getNormalWalletExecutionContext(ws, cts.token, cts, oc); } try { @@ -2122,7 +2274,6 @@ async function handleCoreApiRequest( }); const result = await dispatchRequestInternal( wex, - cts, operation as any, payload, ); @@ -2154,9 +2305,7 @@ async function handleCoreApiRequest( } } -export function applyRunConfigDefaults( - wcp?: PartialWalletRunConfig, -): WalletRunConfig { +function applyRunConfigDefaults(wcp?: PartialWalletRunConfig): WalletRunConfig { return { builtin: { exchanges: wcp?.builtin?.exchanges ?? [ @@ -2232,7 +2381,7 @@ export class Wallet { payload: unknown, ): Promise<CoreApiResponse> { await this.ws.ensureWalletDbOpen(); - return handleCoreApiRequest(this.ws, operation, id, payload); + return dispatchWalletCoreApiRequest(this.ws, operation, id, payload); } } |