taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 97f0842cdd5072cf5eaa5284d333541dbf2e034a
parent f77c4e71874a2f4493079f796976354759b3f95a
Author: Florian Dold <florian@dold.me>
Date:   Sat,  3 May 2025 22:16:00 +0200

wallet-core: request KYC for current balance threshold, not next

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts | 194++++++++++++++++---------------------------------------------------------------
Mpackages/taler-util/src/types-taler-wallet.ts | 2++
Mpackages/taler-wallet-core/src/exchanges.ts | 16++++++++++------
3 files changed, 51 insertions(+), 161 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts b/packages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts @@ -18,180 +18,59 @@ * Imports. */ import { + Configuration, encodeCrock, ExchangeWalletKycStatus, hashFullPaytoUri, j2s, - TalerCorebankApiClient, TransactionIdStr, TransactionMajorState, TransactionMinorState, } from "@gnu-taler/taler-util"; -import { - createSyncCryptoApi, - EddsaKeyPairStrings, - WalletApiOperation, -} from "@gnu-taler/taler-wallet-core"; -import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; -import { - BankService, - DbInfo, - ExchangeService, - getTestHarnessPaytoForLabel, - GlobalTestState, - HarnessExchangeBankAccount, - setupDb, - WalletClient, - WalletService, -} from "../harness/harness.js"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { configureCommonKyc, - EnvOptions, + createKycTestkudosEnvironment, postAmlDecisionNoRules, withdrawViaBankV3, } from "../harness/environments.js"; - -interface KycTestEnv { - commonDb: DbInfo; - bankClient: TalerCorebankApiClient; - exchange: ExchangeService; - exchangeBankAccount: HarnessExchangeBankAccount; - walletClient: WalletClient; - walletService: WalletService; - amlKeypair: EddsaKeyPairStrings; -} - -async function createKycTestkudosEnvironment( - t: GlobalTestState, - coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")), - opts: EnvOptions = {}, -): Promise<KycTestEnv> { - const db = await setupDb(t); - - const bank = await BankService.create(t, { - allowRegistrations: true, - currency: "TESTKUDOS", - database: db.connStr, - httpPort: 8082, - }); - - const exchange = ExchangeService.create(t, { - name: "testexchange-1", - currency: "TESTKUDOS", - httpPort: 8081, - database: db.connStr, - }); - - let receiverName = "Exchange"; - let exchangeBankUsername = "exchange"; - let exchangeBankPassword = "mypw-password"; - let exchangePaytoUri = getTestHarnessPaytoForLabel(exchangeBankUsername); - - const exchangeBankAccount: HarnessExchangeBankAccount = { - wireGatewayAuth: { - username: exchangeBankUsername, - password: exchangeBankPassword, - }, - wireGatewayApiBaseUrl: new URL( - "accounts/exchange/taler-wire-gateway/", - bank.baseUrl, - ).href, - accountPaytoUri: exchangePaytoUri, - } - - await exchange.addBankAccount("1", exchangeBankAccount); - - bank.setSuggestedExchange(exchange, exchangePaytoUri); - - await bank.start(); - - await bank.pingUntilAvailable(); - - const bankClient = new TalerCorebankApiClient(bank.corebankApiBaseUrl, { - auth: { - username: "admin", - password: "admin-password", - }, - }); - - await bankClient.registerAccountExtended({ - name: receiverName, - password: exchangeBankPassword, - username: exchangeBankUsername, - is_taler_exchange: true, - payto_uri: exchangePaytoUri, - }); - - exchange.addCoinConfigList(coinConfig); - - await exchange.modifyConfig(async (config) => { - configureCommonKyc(config); - - config.setString("KYC-RULE-R1", "operation_type", "balance"); - config.setString("KYC-RULE-R1", "enabled", "yes"); - config.setString("KYC-RULE-R1", "exposed", "yes"); - config.setString("KYC-RULE-R1", "is_and_combinator", "yes"); - config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:10"); - config.setString("KYC-RULE-R1", "timeframe", "forever"); - config.setString("KYC-RULE-R1", "next_measures", "M1"); - - config.setString("KYC-MEASURE-M1", "check_name", "C1"); - config.setString("KYC-MEASURE-M1", "context", "{}"); - config.setString("KYC-MEASURE-M1", "program", "NONE"); - - config.setString("KYC-CHECK-C1", "type", "INFO"); - config.setString("KYC-CHECK-C1", "description", "my check!"); - config.setString("KYC-CHECK-C1", "fallback", "FREEZE"); - }); - - await exchange.start(); - - const cryptoApi = createSyncCryptoApi(); - const amlKeypair = await cryptoApi.createEddsaKeypair({}); - - await exchange.enableAmlAccount(amlKeypair.pub, "Alice"); - - const walletService = new WalletService(t, { - name: "wallet", - useInMemoryDb: true, - }); - await walletService.start(); - await walletService.pingUntilAvailable(); - - const walletClient = new WalletClient({ - name: "wallet", - unixPath: walletService.socketPath, - onNotification(n) { - console.log("got notification", n); - }, - }); - await walletClient.connect(); - await walletClient.client.call(WalletApiOperation.InitWallet, { - config: { - testing: { - skipDefaults: true, - }, - }, - }); - - console.log("setup done!"); - - return { - commonDb: db, - exchange, - amlKeypair, - walletClient, - walletService, - bankClient, - exchangeBankAccount, - }; +import { GlobalTestState } from "../harness/harness.js"; + +function adjustExchangeConfig(config: Configuration): void { + configureCommonKyc(config); + + config.setString("KYC-RULE-R1", "operation_type", "balance"); + config.setString("KYC-RULE-R1", "enabled", "yes"); + config.setString("KYC-RULE-R1", "exposed", "yes"); + config.setString("KYC-RULE-R1", "is_and_combinator", "yes"); + config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:10"); + config.setString("KYC-RULE-R1", "timeframe", "forever"); + config.setString("KYC-RULE-R1", "next_measures", "M1"); + + config.setString("KYC-RULE-R2", "operation_type", "balance"); + config.setString("KYC-RULE-R2", "enabled", "yes"); + config.setString("KYC-RULE-R2", "exposed", "yes"); + config.setString("KYC-RULE-R2", "is_and_combinator", "yes"); + config.setString("KYC-RULE-R2", "threshold", "TESTKUDOS:30"); + config.setString("KYC-RULE-R2", "timeframe", "forever"); + config.setString("KYC-RULE-R2", "next_measures", "M1"); + + config.setString("KYC-MEASURE-M1", "check_name", "C1"); + config.setString("KYC-MEASURE-M1", "context", "{}"); + config.setString("KYC-MEASURE-M1", "program", "NONE"); + + config.setString("KYC-CHECK-C1", "type", "INFO"); + config.setString("KYC-CHECK-C1", "description", "my check!"); + config.setString("KYC-CHECK-C1", "fallback", "FREEZE"); } export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { // Set up test environment const { walletClient, bankClient, exchange, amlKeypair } = - await createKycTestkudosEnvironment(t); + await createKycTestkudosEnvironment(t, { + adjustExchangeConfig, + }); // Withdraw digital cash into the wallet. @@ -238,6 +117,11 @@ export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { ExchangeWalletKycStatus.Legi, ); + t.assertDeepEqual( + exchangeEntry.walletKycRequestedThreshold, + "TESTKUDOS:10", + ); + const kycReservePub = exchangeEntry.walletKycReservePub; t.assertTrue(!!kycReservePub); diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -1595,6 +1595,8 @@ export interface ExchangeListItem { walletKycReservePub?: string; walletKycAccessToken?: string; walletKycUrl?: string; + /** Threshold that we've requested to satisfy. */ + walletKycRequestedThreshold?: string; /** * P2P payments are disabled with this exchange diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -504,6 +504,7 @@ async function makeExchangeListItem( ? new URL(`kyc-spa/${reserveRec.kycAccessToken}`, r.baseUrl).href : undefined, walletKycAccessToken: reserveRec?.kycAccessToken, + walletKycRequestedThreshold: reserveRec?.thresholdRequested, tosStatus: getExchangeTosStatusFromRecord(r), ageRestrictionOptions: exchangeDetails?.ageMask ? AgeRestriction.getAgeGroupsFromMask(exchangeDetails.ageMask) @@ -3234,6 +3235,7 @@ export type BalanceThresholdCheckResult = } | { result: "violation"; + /** Threshold that needs to be requested to proceed. */ nextThreshold: AmountString; walletKycStatus: ExchangeWalletKycStatus | undefined; walletKycAccessToken: string | undefined; @@ -3312,11 +3314,10 @@ export async function checkIncomingAmountLegalUnderKycBalanceThreshold( limits.sort((a, b) => Amounts.cmp(a, b)); logger.trace(`applicable limits: ${j2s(limits)}`); let limViolated: AmountString | undefined = undefined; - let limNext: AmountString | undefined = undefined; for (let i = 0; i < limits.length; i++) { if (Amounts.cmp(limits[i], balExpected) <= 0) { limViolated = limits[i]; - limNext = limits[i + 1]; + const limNext = limits[i + 1]; if (limNext == null || Amounts.cmp(limNext, balExpected) > 0) { break; } @@ -3328,12 +3329,10 @@ export async function checkIncomingAmountLegalUnderKycBalanceThreshold( result: "ok", }; } else { - logger.info( - `balance limit ${limViolated} would be violated, next is ${limNext}`, - ); + logger.info(`balance limit ${limViolated} would be violated`); return { result: "violation", - nextThreshold: limNext ?? limViolated, + nextThreshold: limViolated, walletKycStatus: reserveRec?.status ? getKycStatusFromReserveStatus(reserveRec.status) : undefined, @@ -3442,6 +3441,11 @@ export async function handleTestingWaitExchangeWalletKyc( return {}; } +/** + * Start a wallet KYC process. + * + * Does nothing if the KYC process has already been started. + */ export async function handleStartExchangeWalletKyc( wex: WalletExecutionContext, req: StartExchangeWalletKycRequest,