diff options
-rw-r--r-- | packages/taler-util/src/walletTypes.ts | 124 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 16 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/backup/import.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/withdraw.ts | 107 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 55 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/cta/Withdraw/state.ts | 11 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/cta/Withdraw/test.ts | 41 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/wxApi.ts | 16 |
8 files changed, 158 insertions, 214 deletions
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index d0765ae31..0891f5cf9 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -68,6 +68,7 @@ import { BackupRecovery } from "./backupTypes.js"; import { PaytoUri } from "./payto.js"; import { TalerErrorCode } from "./taler-error-codes.js"; import { AgeCommitmentProof } from "./talerCrypto.js"; +import { VersionMatchResult } from "./libtool-version.js"; /** * Response for the create reserve request to the wallet. @@ -692,6 +693,7 @@ export interface ExchangeGlobalFees { signature: string; } + const codecForExchangeAccount = (): Codec<ExchangeAccount> => buildCodecForObject<ExchangeAccount>() .property("payto_uri", codecForString()) @@ -929,6 +931,110 @@ export interface ManualWithdrawalDetails { * Ways to pay the exchange. */ paytoUris: string[]; + + /** + * If the exchange supports age-restricted coins it will return + * the array of ages. + */ + ageRestrictionOptions?: number[]; +} + +/** + * Selected denominations withn some extra info. + */ + export interface DenomSelectionState { + totalCoinValue: AmountJson; + totalWithdrawCost: AmountJson; + selectedDenoms: { + denomPubHash: string; + count: number; + }[]; +} + +/** + * Information about what will happen doing a withdrawal. + * + * Sent to the wallet frontend to be rendered and shown to the user. + */ + export interface ExchangeWithdrawalDetails { + exchangePaytoUris: string[]; + + /** + * Filtered wire info to send to the bank. + */ + exchangeWireAccounts: string[]; + + /** + * Selected denominations for withdraw. + */ + selectedDenoms: DenomSelectionState; + + /** + * Does the wallet know about an auditor for + * the exchange that the reserve. + */ + isAudited: boolean; + + /** + * Did the user already accept the current terms of service for the exchange? + */ + termsOfServiceAccepted: boolean; + + /** + * The exchange is trusted directly. + */ + isTrusted: boolean; + + /** + * The earliest deposit expiration of the selected coins. + */ + earliestDepositExpiration: TalerProtocolTimestamp; + + /** + * Number of currently offered denominations. + */ + numOfferedDenoms: number; + + /** + * Public keys of trusted auditors for the currency we're withdrawing. + */ + trustedAuditorPubs: string[]; + + /** + * Result of checking the wallet's version + * against the exchange's version. + * + * Older exchanges don't return version information. + */ + versionMatch: VersionMatchResult | undefined; + + /** + * Libtool-style version string for the exchange or "unknown" + * for older exchanges. + */ + exchangeVersion: string; + + /** + * Libtool-style version string for the wallet. + */ + walletVersion: string; + + /** + * Amount that will be subtracted from the reserve's balance. + */ + withdrawalAmountRaw: AmountString; + + /** + * Amount that will actually be added to the wallet's balance. + */ + withdrawalAmountEffective: AmountString; + + /** + * If the exchange supports age-restricted coins it will return + * the array of ages. + * + */ + ageRestrictionOptions?: number[]; } export interface GetExchangeTosResult { @@ -1142,24 +1248,6 @@ export const codecForForgetKnownBankAccounts = .property("payto", codecForString()) .build("ForgetKnownBankAccountsRequest"); -export interface GetExchangeWithdrawalInfo { - exchangeBaseUrl: string; - amount: AmountJson; - tosAcceptedFormat?: string[]; - ageRestricted?: number; -} - -export const codecForGetExchangeWithdrawalInfo = - (): Codec<GetExchangeWithdrawalInfo> => - buildCodecForObject<GetExchangeWithdrawalInfo>() - .property("exchangeBaseUrl", codecForString()) - .property("amount", codecForAmountJson()) - .property( - "tosAcceptedFormat", - codecOptional(codecForList(codecForString())), - ) - .build("GetExchangeWithdrawalInfo"); - export interface AbortProposalRequest { proposalId: string; } diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index f4cdb68c1..8f7f22292 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -47,6 +47,7 @@ import { DenominationInfo, GlobalFees, ExchangeGlobalFees, + DenomSelectionState, } from "@gnu-taler/taler-util"; import { RetryInfo, RetryTags } from "./util/retries.js"; import { Event, IDBDatabase } from "@gnu-taler/idb-bridge"; @@ -430,8 +431,11 @@ export interface ExchangeDetailsRecord { /** * Fees for exchange services + * + * FIXME: Put in separate object store! */ globalFees: ExchangeGlobalFees[]; + /** * Signing keys we got from the exchange, can also contain * older signing keys that are not returned by /keys anymore. @@ -1280,18 +1284,6 @@ export interface WalletBackupConfState { lastBackupNonce?: string; } -/** - * Selected denominations withn some extra info. - */ -export interface DenomSelectionState { - totalCoinValue: AmountJson; - totalWithdrawCost: AmountJson; - selectedDenoms: { - denomPubHash: string; - count: number; - }[]; -} - export const enum WithdrawalRecordType { BankManual = "bank-manual", BankIntegrated = "bank-integrated", diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index e631845f6..f4e6ab5eb 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -28,6 +28,7 @@ import { BackupWgType, codecForContractTerms, DenomKeyType, + DenomSelectionState, j2s, Logger, PayCoinSelection, @@ -43,7 +44,6 @@ import { CoinStatus, DenominationRecord, DenominationVerificationStatus, - DenomSelectionState, OperationStatus, ProposalDownload, PurchaseStatus, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 3c2541e9a..e4bf6cd11 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -37,10 +37,12 @@ import { codecForWithdrawOperationStatusResponse, codecForWithdrawResponse, DenomKeyType, + DenomSelectionState, Duration, durationFromSpec, encodeCrock, ExchangeListItem, + ExchangeWithdrawalDetails, ExchangeWithdrawRequest, ForcedDenomSel, getRandomBytes, @@ -67,9 +69,6 @@ import { CoinStatus, DenominationRecord, DenominationVerificationStatus, - DenomSelectionState, - ExchangeDetailsRecord, - ExchangeRecord, PlanchetRecord, WalletStoresV1, WgInfo, @@ -127,96 +126,6 @@ import { const logger = new Logger("operations/withdraw.ts"); /** - * Information about what will happen when creating a reserve. - * - * Sent to the wallet frontend to be rendered and shown to the user. - */ -export interface ExchangeWithdrawDetails { - /** - * Exchange that the reserve will be created at. - * - * FIXME: Should be its own record. - */ - exchangeInfo: ExchangeRecord; - - exchangeDetails: ExchangeDetailsRecord; - - /** - * Filtered wire info to send to the bank. - */ - exchangeWireAccounts: string[]; - - /** - * Selected denominations for withdraw. - */ - selectedDenoms: DenomSelectionState; - - /** - * Does the wallet know about an auditor for - * the exchange that the reserve. - */ - isAudited: boolean; - - /** - * Did the user already accept the current terms of service for the exchange? - */ - termsOfServiceAccepted: boolean; - - /** - * The exchange is trusted directly. - */ - isTrusted: boolean; - - /** - * The earliest deposit expiration of the selected coins. - */ - earliestDepositExpiration: TalerProtocolTimestamp; - - /** - * Number of currently offered denominations. - */ - numOfferedDenoms: number; - - /** - * Public keys of trusted auditors for the currency we're withdrawing. - */ - trustedAuditorPubs: string[]; - - /** - * Result of checking the wallet's version - * against the exchange's version. - * - * Older exchanges don't return version information. - */ - versionMatch: VersionMatchResult | undefined; - - /** - * Libtool-style version string for the exchange or "unknown" - * for older exchanges. - */ - exchangeVersion: string; - - /** - * Libtool-style version string for the wallet. - */ - walletVersion: string; - - withdrawalAmountRaw: AmountString; - - /** - * Amount that will actually be added to the wallet's balance. - */ - withdrawalAmountEffective: AmountString; - - /** - * If the exchange supports age-restricted coins it will return - * the array of ages. - * - */ - ageRestrictionOptions?: number[]; -} - -/** * Check if a denom is withdrawable based on the expiration time, * revocation and offered state. */ @@ -1280,7 +1189,7 @@ export async function getExchangeWithdrawalInfo( exchangeBaseUrl: string, instructedAmount: AmountJson, ageRestricted: number | undefined, -): Promise<ExchangeWithdrawDetails> { +): Promise<ExchangeWithdrawalDetails> { const { exchange, exchangeDetails } = await ws.exchangeOps.updateExchangeFromUrl(ws, exchangeBaseUrl); await updateWithdrawalDenoms(ws, exchangeBaseUrl); @@ -1378,10 +1287,14 @@ export async function getExchangeWithdrawalInfo( } } - const ret: ExchangeWithdrawDetails = { + const paytoUris = exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri); + if (!paytoUris) { + throw Error("exchange is in invalid state"); + } + + const ret: ExchangeWithdrawalDetails = { earliestDepositExpiration, - exchangeInfo: exchange, - exchangeDetails, + exchangePaytoUris: paytoUris, exchangeWireAccounts, exchangeVersion: exchangeDetails.protocolVersion || "unknown", isAudited, diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 254648064..22cbeb4b8 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -47,7 +47,6 @@ import { codecForForgetKnownBankAccounts, codecForGetContractTermsDetails, codecForGetExchangeTosRequest, - codecForGetExchangeWithdrawalInfo, codecForGetFeeForDeposit, codecForGetWithdrawalDetailsForAmountRequest, codecForGetWithdrawalDetailsForUri, @@ -112,7 +111,11 @@ import { importDb, WalletStoresV1, } from "./db.js"; -import { applyDevExperiment, maybeInitDevMode, setDevMode } from "./dev-experiments.js"; +import { + applyDevExperiment, + maybeInitDevMode, + setDevMode, +} from "./dev-experiments.js"; import { getErrorDetailFromException, TalerError } from "./errors.js"; import { ActiveLongpollInfo, @@ -248,32 +251,6 @@ const builtinExchanges: string[] = ["https://exchange.demo.taler.net/"]; const logger = new Logger("wallet.ts"); -async function getWithdrawalDetailsForAmount( - ws: InternalWalletState, - exchangeBaseUrl: string, - amount: AmountJson, - restrictAge: number | undefined, -): Promise<ManualWithdrawalDetails> { - const wi = await getExchangeWithdrawalInfo( - ws, - exchangeBaseUrl, - amount, - restrictAge, - ); - const paytoUris = wi.exchangeDetails.wireInfo.accounts.map( - (x) => x.payto_uri, - ); - if (!paytoUris) { - throw Error("exchange is in invalid state"); - } - return { - amountRaw: Amounts.stringify(amount), - amountEffective: Amounts.stringify(wi.selectedDenoms.totalCoinValue), - paytoUris, - tosAccepted: wi.termsOfServiceAccepted, - }; -} - /** * Call the right handler for a pending operation without doing * any special error handling. @@ -1038,16 +1015,6 @@ async function dispatchRequestInternal( const req = codecForGetWithdrawalDetailsForUri().decode(payload); return await getWithdrawalDetailsForUri(ws, req.talerWithdrawUri); } - - case "getExchangeWithdrawalInfo": { - const req = codecForGetExchangeWithdrawalInfo().decode(payload); - return await getExchangeWithdrawalInfo( - ws, - req.exchangeBaseUrl, - req.amount, - req.ageRestricted, - ); - } case "acceptManualWithdrawal": { const req = codecForAcceptManualWithdrawalRequet().decode(payload); const res = await createManualWithdrawal(ws, { @@ -1060,12 +1027,18 @@ async function dispatchRequestInternal( case "getWithdrawalDetailsForAmount": { const req = codecForGetWithdrawalDetailsForAmountRequest().decode(payload); - return await getWithdrawalDetailsForAmount( + const wi = await getExchangeWithdrawalInfo( ws, req.exchangeBaseUrl, Amounts.parseOrThrow(req.amount), req.restrictAge, ); + return { + amountRaw: req.amount, + amountEffective: Amounts.stringify(wi.selectedDenoms.totalCoinValue), + paytoUris: wi.exchangePaytoUris, + tosAccepted: wi.termsOfServiceAccepted, + }; } case "getBalances": { return await getBalances(ws); @@ -1255,7 +1228,7 @@ async function dispatchRequestInternal( case "withdrawFakebank": { const req = codecForWithdrawFakebankRequest().decode(payload); const amount = Amounts.parseOrThrow(req.amount); - const details = await getWithdrawalDetailsForAmount( + const details = await getExchangeWithdrawalInfo( ws, req.exchange, amount, @@ -1265,7 +1238,7 @@ async function dispatchRequestInternal( amount: amount, exchangeBaseUrl: req.exchange, }); - const paytoUri = details.paytoUris[0]; + const paytoUri = details.exchangePaytoUris[0]; const pt = parsePaytoUri(paytoUri); if (!pt) { throw Error("failed to parse payto URI"); diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index c2b9e375f..53bac579e 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -182,16 +182,15 @@ function exchangeSelectionState( * about the withdrawal */ const amountHook = useAsyncAsHook(async () => { - const info = await api.getExchangeWithdrawalInfo({ + const info = await api.getWithdrawalDetailsForAmount({ exchangeBaseUrl: currentExchange.exchangeBaseUrl, - amount: chosenAmount, - tosAcceptedFormat: ["text/xml"], - ageRestricted, + amount: Amounts.stringify(chosenAmount), + restrictAge: ageRestricted, }); const withdrawAmount = { - raw: Amounts.parseOrThrow(info.withdrawalAmountRaw), - effective: Amounts.parseOrThrow(info.withdrawalAmountEffective), + raw: Amounts.parseOrThrow(info.amountRaw), + effective: Amounts.parseOrThrow(info.amountEffective), }; return { diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts index f3598b557..d86771208 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts @@ -22,14 +22,11 @@ import { Amounts, ExchangeFullDetails, - ExchangeListItem, GetExchangeTosResult, } from "@gnu-taler/taler-util"; -import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { mountHook } from "../../test-utils.js"; import { useComponentStateFromURI } from "./state.js"; -import * as wxApi from "../../wxApi.js"; const exchanges: ExchangeFullDetails[] = [ { @@ -162,20 +159,11 @@ describe("Withdraw CTA states", () => { }, { listExchanges: async () => ({ exchanges }), - getWithdrawalDetailsForUri: async ({ - talerWithdrawUri, - }: any): Promise<ExchangeWithdrawDetails> => - ({ - amount: "ARS:2", - possibleExchanges: exchanges, - defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, - } as Partial<ExchangeWithdrawDetails> as ExchangeWithdrawDetails), - getExchangeWithdrawalInfo: - async (): Promise<ExchangeWithdrawDetails> => - ({ - withdrawalAmountRaw: "ARS:2", - withdrawalAmountEffective: "ARS:2", - } as any), + getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({ + amount: "ARS:2", + possibleExchanges: exchanges, + defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, + }), getExchangeTos: async (): Promise<GetExchangeTosResult> => ({ contentType: "text", content: "just accept", @@ -255,19 +243,12 @@ describe("Withdraw CTA states", () => { }, }, { - listExchanges: async () => listExchangesResponse, - getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => - ({ - amount: "ARS:2", - possibleExchanges: exchanges, - defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, - } as Partial<ExchangeWithdrawDetails> as ExchangeWithdrawDetails), - getExchangeWithdrawalInfo: - async (): Promise<ExchangeWithdrawDetails> => - ({ - withdrawalAmountRaw: "ARS:2", - withdrawalAmountEffective: "ARS:2", - } as any), + listExchanges: async () => ({ exchanges }), + getWithdrawalDetailsForUri: async ({ talerWithdrawUri }: any) => ({ + amount: "ARS:2", + possibleExchanges: exchanges, + defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl, + }), getExchangeTos: async (): Promise<GetExchangeTosResult> => ({ contentType: "text", content: "just accept", diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index e0a1ee238..17083cb5b 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -51,8 +51,8 @@ import { ExchangesListResponse, ForgetKnownBankAccountsRequest, GetExchangeTosResult, - GetExchangeWithdrawalInfo, GetFeeForDepositRequest, + GetWithdrawalDetailsForAmountRequest, GetWithdrawalDetailsForUriRequest, InitiatePeerPullPaymentRequest, InitiatePeerPullPaymentResponse, @@ -60,6 +60,7 @@ import { InitiatePeerPushPaymentResponse, KnownBankAccounts, Logger, + ManualWithdrawalDetails, NotificationType, PaytoUri, PrepareDepositRequest, @@ -81,7 +82,6 @@ import { import { AddBackupProviderRequest, BackupInfo, - ExchangeWithdrawDetails, PendingOperationsResponse, RemoveBackupProviderRequest, TalerError, @@ -459,14 +459,12 @@ export function getWithdrawalDetailsForUri( return callBackend("getWithdrawalDetailsForUri", req); } -/** - * Get diagnostics information - */ -export function getExchangeWithdrawalInfo( - req: GetExchangeWithdrawalInfo, -): Promise<ExchangeWithdrawDetails> { - return callBackend("getExchangeWithdrawalInfo", req); +export function getWithdrawalDetailsForAmount( + req: GetWithdrawalDetailsForAmountRequest, +): Promise<ManualWithdrawalDetails> { + return callBackend("getWithdrawalDetailsForAmount", req); } + export function getExchangeTos( exchangeBaseUrl: string, acceptedFormat: string[], |