diff options
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 48 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/balance.ts | 6 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/deposits.ts | 178 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/pay-merchant.ts | 16 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/transactions.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/util/coinSelection.ts | 18 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet-api-types.ts | 24 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 8 |
8 files changed, 111 insertions, 189 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 25757ef25..abfb02445 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1042,52 +1042,6 @@ export enum RefundReason { AbortRefund = "abort-pay-refund", } -export interface AllowedAuditorInfo { - auditorBaseUrl: string; - auditorPub: string; -} - -export interface AllowedExchangeInfo { - exchangeBaseUrl: string; - exchangePub: string; -} - -/** - * Data extracted from the contract terms that is relevant for payment - * processing in the wallet. - */ -export interface WalletContractData { - /** - * Fulfillment URL, or the empty string if the order has no fulfillment URL. - * - * Stored as a non-nullable string as we use this field for IndexedDB indexing. - */ - fulfillmentUrl: string; - - contractTermsHash: string; - fulfillmentMessage?: string; - fulfillmentMessageI18n?: InternationalizedString; - merchantSig: string; - merchantPub: string; - merchant: MerchantInfo; - amount: AmountString; - orderId: string; - merchantBaseUrl: string; - summary: string; - summaryI18n: { [lang_tag: string]: string } | undefined; - autoRefund: TalerProtocolDuration | undefined; - maxWireFee: AmountString; - wireFeeAmortization: number; - payDeadline: TalerProtocolTimestamp; - refundDeadline: TalerProtocolTimestamp; - allowedExchanges: AllowedExchangeInfo[]; - timestamp: TalerProtocolTimestamp; - wireMethod: string; - wireInfoHash: string; - maxDepositFee: AmountString; - minimumAge?: number; -} - export enum PurchaseStatus { /** * Not downloaded yet. @@ -1710,6 +1664,8 @@ export interface DepositGroupRecord { /** * Verbatim contract terms. + * + * FIXME: Move this to the contract terms object store! */ contractTermsRaw: MerchantContractTerms; diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index a20ded2af..8034f78ea 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -50,6 +50,8 @@ * Imports. */ import { + AllowedAuditorInfo, + AllowedExchangeInfo, AmountJson, Amounts, BalancesResponse, @@ -60,17 +62,15 @@ import { ScopeType, } from "@gnu-taler/taler-util"; import { - AllowedAuditorInfo, - AllowedExchangeInfo, RefreshGroupRecord, WalletStoresV1, WithdrawalGroupStatus, } from "../db.js"; import { InternalWalletState } from "../internal-wallet-state.js"; +import { assertUnreachable } from "../util/assertUnreachable.js"; import { checkLogicInvariant } from "../util/invariants.js"; import { GetReadOnlyAccess } from "../util/query.js"; import { getExchangeDetails } from "./exchanges.js"; -import { assertUnreachable } from "../util/assertUnreachable.js"; /** * Logger. diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 8ea792d91..a3483a332 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -21,70 +21,69 @@ import { AbsoluteTime, AmountJson, Amounts, + BatchDepositRequestCoin, CancellationToken, - canonicalJson, - codecForDepositSuccess, - codecForTackTransactionAccepted, - codecForTackTransactionWired, CoinRefreshRequest, CreateDepositGroupRequest, CreateDepositGroupResponse, DepositGroupFees, - durationFromSpec, - encodeCrock, - ExchangeDepositRequest, + Duration, + ExchangeBatchDepositRequest, ExchangeRefundRequest, - getRandomBytes, - hashTruncate32, - hashWire, HttpStatusCode, - j2s, Logger, MerchantContractTerms, NotificationType, - parsePaytoUri, PayCoinSelection, PrepareDepositRequest, PrepareDepositResponse, RefreshReason, - stringToBytes, + TalerError, TalerErrorCode, - TalerProtocolTimestamp, TalerPreciseTimestamp, + TalerProtocolTimestamp, TrackTransaction, + TransactionAction, TransactionMajorState, TransactionMinorState, TransactionState, TransactionType, URL, WireFee, - TransactionAction, - Duration, + canonicalJson, + codecForBatchDepositSuccess, + codecForTackTransactionAccepted, + codecForTackTransactionWired, + durationFromSpec, + encodeCrock, + getRandomBytes, + hashTruncate32, + hashWire, + j2s, + parsePaytoUri, + stringToBytes, } from "@gnu-taler/taler-util"; +import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; +import { DepositElementStatus, DepositGroupRecord } from "../db.js"; import { - DenominationRecord, - DepositGroupRecord, - DepositElementStatus, -} from "../db.js"; -import { TalerError } from "@gnu-taler/taler-util"; -import { - createRefreshGroup, DepositOperationStatus, DepositTrackingInfo, - getTotalRefreshCost, KycPendingInfo, - KycUserType, PendingTaskType, RefreshOperationStatus, + createRefreshGroup, + getTotalRefreshCost, } from "../index.js"; import { InternalWalletState } from "../internal-wallet-state.js"; -import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; +import { assertUnreachable } from "../util/assertUnreachable.js"; +import { selectPayCoinsNew } from "../util/coinSelection.js"; +import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { - constructTaskIdentifier, TaskRunResult, + TombstoneTag, + constructTaskIdentifier, runLongpollAsync, spendCoins, - TombstoneTag, } from "./common.js"; import { getExchangeDetails } from "./exchanges.js"; import { @@ -92,15 +91,12 @@ import { generateDepositPermissions, getTotalPaymentCost, } from "./pay-merchant.js"; -import { selectPayCoinsNew } from "../util/coinSelection.js"; import { constructTransactionIdentifier, notifyTransition, parseTransactionIdentifier, stopLongpolling, } from "./transactions.js"; -import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; -import { assertUnreachable } from "../util/assertUnreachable.js"; /** * Logger. @@ -169,6 +165,10 @@ export function computeDepositTransactionStatus( } } +/** + * Compute the possible actions possible on a deposit transaction + * based on the current transaction state. + */ export function computeDepositTransactionActions( dg: DepositGroupRecord, ): TransactionAction[] { @@ -200,6 +200,11 @@ export function computeDepositTransactionActions( } } +/** + * Put a deposit group in a suspended state. + * While the deposit group is suspended, no network requests + * will be made to advance the transaction status. + */ export async function suspendDepositGroup( ws: InternalWalletState, depositGroupId: string, @@ -407,46 +412,6 @@ export async function deleteDepositGroup( } /** - * Check KYC status with the exchange, throw an appropriate exception when KYC - * is required. - * - * FIXME: Why does this throw an exception when KYC is required? - * Should we not return some proper result record here? - */ -async function checkDepositKycStatus( - ws: InternalWalletState, - exchangeUrl: string, - kycInfo: KycPendingInfo, - userType: KycUserType, -): Promise<void> { - const url = new URL( - `kyc-check/${kycInfo.requirementRow}/${kycInfo.paytoHash}/${userType}`, - exchangeUrl, - ); - logger.info(`kyc url ${url.href}`); - const kycStatusReq = await ws.http.fetch(url.href, { - method: "GET", - }); - if (kycStatusReq.status === HttpStatusCode.Ok) { - logger.warn("kyc requested, but already fulfilled"); - return; - } else if (kycStatusReq.status === HttpStatusCode.Accepted) { - const kycStatus = await kycStatusReq.json(); - logger.info(`kyc status: ${j2s(kycStatus)}`); - // FIXME: This error code is totally wrong - throw TalerError.fromDetail( - TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED, - { - kycUrl: kycStatus.kyc_url, - }, - `KYC check required for deposit`, - ); - } else { - throw Error(`unexpected response from kyc-check (${kycStatusReq.status})`); - } -} - -/** * Check whether the refresh associated with the * aborting deposit group is done. * @@ -940,38 +905,58 @@ async function processDepositGroupPendingDeposit( contractData, ); - for (let i = 0; i < depositPermissions.length; i++) { - const perm = depositPermissions[i]; + // Exchanges involved in the deposit + const exchanges: Set<string> = new Set(); - if (depositGroup.statusPerCoin[i] !== DepositElementStatus.DepositPending) { - continue; - } + for (const dp of depositPermissions) { + exchanges.add(dp.exchange_url); + } - const requestBody: ExchangeDepositRequest = { - contribution: Amounts.stringify(perm.contribution), - merchant_payto_uri: depositGroup.wire.payto_uri, - wire_salt: depositGroup.wire.salt, + // We need to do one batch per exchange. + for (const exchangeUrl of exchanges.values()) { + const coins: BatchDepositRequestCoin[] = []; + const batchIndexes: number[] = []; + + const batchReq: ExchangeBatchDepositRequest = { + coins, h_contract_terms: depositGroup.contractTermsHash, - ub_sig: perm.ub_sig, + merchant_payto_uri: depositGroup.wire.payto_uri, + merchant_pub: depositGroup.contractTermsRaw.merchant_pub, timestamp: depositGroup.contractTermsRaw.timestamp, + wire_salt: depositGroup.wire.salt, wire_transfer_deadline: depositGroup.contractTermsRaw.wire_transfer_deadline, refund_deadline: depositGroup.contractTermsRaw.refund_deadline, - coin_sig: perm.coin_sig, - denom_pub_hash: perm.h_denom, - merchant_pub: depositGroup.merchantPub, - h_age_commitment: perm.h_age_commitment, }; + + for (let i = 0; i < depositPermissions.length; i++) { + const perm = depositPermissions[i]; + if (perm.exchange_url != exchangeUrl) { + continue; + } + coins.push({ + coin_pub: perm.coin_pub, + coin_sig: perm.coin_sig, + contribution: Amounts.stringify(perm.contribution), + denom_pub_hash: perm.h_denom, + ub_sig: perm.ub_sig, + }); + batchIndexes.push(i); + } + // Check for cancellation before making network request. cancellationToken?.throwIfCancelled(); - const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url); + const url = new URL(`batch-deposit`, exchangeUrl); logger.info(`depositing to ${url}`); const httpResp = await ws.http.fetch(url.href, { method: "POST", - body: requestBody, + body: batchReq, cancellationToken: cancellationToken, }); - await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess()); + await readSuccessResponseJsonOrThrow( + httpResp, + codecForBatchDepositSuccess(), + ); await ws.db .mktx((x) => [x.depositGroups]) @@ -980,11 +965,13 @@ async function processDepositGroupPendingDeposit( if (!dg) { return; } - const coinStatus = dg.statusPerCoin[i]; - switch (coinStatus) { - case DepositElementStatus.DepositPending: - dg.statusPerCoin[i] = DepositElementStatus.Tracking; - await tx.depositGroups.put(dg); + for (const batchIndex of batchIndexes) { + const coinStatus = dg.statusPerCoin[batchIndex]; + switch (coinStatus) { + case DepositElementStatus.DepositPending: + dg.statusPerCoin[batchIndex] = DepositElementStatus.Tracking; + await tx.depositGroups.put(dg); + } } }); } @@ -1538,10 +1525,7 @@ async function getTotalFeesForDepositAmount( const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl .iter(coin.exchangeBaseUrl) .filter((x) => - Amounts.isSameCurrency( - x.value, - pcs.coinContributions[i], - ), + Amounts.isSameCurrency(x.value, pcs.coinContributions[i]), ); const amountLeft = Amounts.sub( denom.value, diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 57367bb20..fe0cbeda0 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -63,7 +63,6 @@ import { RefreshReason, SharePaymentResult, StartRefundQueryForUriResponse, - stringifyPaytoUri, stringifyPayUri, stringifyTalerUri, TalerError, @@ -78,6 +77,7 @@ import { TransactionState, TransactionType, URL, + WalletContractData, } from "@gnu-taler/taler-util"; import { getHttpResponseErrorDetails, @@ -95,7 +95,6 @@ import { PurchaseRecord, PurchaseStatus, RefundReason, - WalletContractData, WalletStoresV1, } from "../db.js"; import { @@ -115,15 +114,13 @@ import { checkDbInvariant } from "../util/invariants.js"; import { GetReadOnlyAccess } from "../util/query.js"; import { constructTaskIdentifier, - TaskRunResult, - TaskRunResultType, RetryInfo, - TaskIdentifiers, -} from "./common.js"; -import { runLongpollAsync, runTaskWithErrorReporting, spendCoins, + TaskIdentifiers, + TaskRunResult, + TaskRunResultType, } from "./common.js"; import { calculateRefreshOutput, @@ -173,10 +170,7 @@ export async function getTotalPaymentCost( const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl .iter(coin.exchangeBaseUrl) .filter((x) => - Amounts.isSameCurrency( - x.value, - pcs.coinContributions[i], - ), + Amounts.isSameCurrency(x.value, pcs.coinContributions[i]), ); const amountLeft = Amounts.sub( denom.value, diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 7bdb9af5b..31655ad71 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -41,6 +41,7 @@ import { TransactionsResponse, TransactionState, TransactionType, + WalletContractData, WithdrawalType, } from "@gnu-taler/taler-util"; import { @@ -60,7 +61,6 @@ import { RefreshOperationStatus, RefundGroupRecord, RewardRecord, - WalletContractData, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index ef2f85789..6fd0f1b86 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -28,21 +28,20 @@ import { AbsoluteTime, AgeCommitmentProof, AgeRestriction, + AllowedAuditorInfo, + AllowedExchangeInfo, AmountJson, AmountLike, - AmountResponse, Amounts, AmountString, CoinPublicKeyString, CoinStatus, - ConvertAmountRequest, DenominationInfo, DenominationPubKey, DenomSelectionState, Duration, ForcedCoinSel, ForcedDenomSel, - GetAmountRequest, j2s, Logger, parsePaytoUri, @@ -50,24 +49,13 @@ import { PayMerchantInsufficientBalanceDetails, PayPeerInsufficientBalanceDetails, strcmp, - TransactionAmountMode, - TransactionType, UnblindedSignature, } from "@gnu-taler/taler-util"; +import { DenominationRecord } from "../db.js"; import { - AllowedAuditorInfo, - AllowedExchangeInfo, - DenominationRecord, -} from "../db.js"; -import { - DbReadOnlyTransaction, getExchangeDetails, - GetReadOnlyAccess, - GetReadWriteAccess, isWithdrawableDenom, - StoreNames, WalletDbReadOnlyTransaction, - WalletStoresV1, } from "../index.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index d89ad257a..67c05a42f 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -38,7 +38,6 @@ import { ApplyDevExperimentRequest, BackupRecovery, BalancesResponse, - FailTransactionRequest, CheckPeerPullCreditRequest, CheckPeerPullCreditResponse, CheckPeerPushDebitRequest, @@ -51,14 +50,19 @@ import { ConvertAmountRequest, CreateDepositGroupRequest, CreateDepositGroupResponse, + CreateStoredBackupResponse, + DeleteStoredBackupRequest, DeleteTransactionRequest, ExchangeDetailedResponse, ExchangesListResponse, + FailTransactionRequest, ForceRefreshRequest, ForgetKnownBankAccountsRequest, GetAmountRequest, GetBalanceDetailRequest, GetContractTermsDetailsRequest, + GetCurrencyInfoRequest, + GetCurrencyInfoResponse, GetExchangeTosRequest, GetExchangeTosResult, GetPlanForOperationRequest, @@ -85,16 +89,21 @@ import { PreparePeerPushCreditRequest, PreparePeerPushCreditResponse, PrepareRefundRequest, - PrepareRewardRequest as PrepareRewardRequest, + PrepareRewardRequest, PrepareTipResult as PrepareRewardResult, + RecoverStoredBackupRequest, RecoveryLoadRequest, RetryTransactionRequest, SetCoinSuspendedRequest, SetWalletDeviceIdRequest, + SharePaymentRequest, + SharePaymentResult, StartRefundQueryForUriResponse, StartRefundQueryRequest, + StoredBackupList, TestPayArgs, TestPayResult, + TestingSetTimetravelRequest, Transaction, TransactionByIdRequest, TransactionsRequest, @@ -106,22 +115,13 @@ import { UserAttentionsResponse, ValidateIbanRequest, ValidateIbanResponse, + WalletContractData, WalletCoreVersion, WalletCurrencyInfo, WithdrawFakebankRequest, WithdrawTestBalanceRequest, WithdrawUriInfoResponse, - SharePaymentRequest, - SharePaymentResult, - GetCurrencyInfoRequest, - GetCurrencyInfoResponse, - StoredBackupList, - CreateStoredBackupResponse, - RecoverStoredBackupRequest, - DeleteStoredBackupRequest, - TestingSetTimetravelRequest, } from "@gnu-taler/taler-util"; -import { WalletContractData } from "./db.js"; import { AddBackupProviderRequest, AddBackupProviderResponse, diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 1a60b148c..2d0878afc 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -15,7 +15,7 @@ */ /** - * High-level wallet operations that should be indepentent from the underlying + * High-level wallet operations that should be independent from the underlying * browser extension interface. */ @@ -923,9 +923,9 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> { ageCommitmentProof: c.ageCommitmentProof, spend_allocation: c.spendAllocation ? { - amount: c.spendAllocation.amount, - id: c.spendAllocation.id, - } + amount: c.spendAllocation.amount, + id: c.spendAllocation.id, + } : undefined, }); } |