taler-typescript-core

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

commit 3d4d25ed08ba4d50ff3de1f3bf6c8ae727fbdd6c
parent a2b1a5cdd17dc58b136494162b3f0179008b1893
Author: Florian Dold <florian@dold.me>
Date:   Wed, 11 Dec 2024 16:49:32 +0100

test

Diffstat:
Apackages/taler-harness/src/integrationtests/test-kyc-deposit-aggregate-implicit-auth.ts | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 4+++-
Mpackages/taler-util/src/types-taler-exchange.ts | 22++++++++++++++++++++--
Mpackages/taler-wallet-core/src/deposits.ts | 4++++
4 files changed, 174 insertions(+), 3 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-kyc-deposit-aggregate-implicit-auth.ts b/packages/taler-harness/src/integrationtests/test-kyc-deposit-aggregate-implicit-auth.ts @@ -0,0 +1,147 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import { + Configuration, + TransactionMajorState, + TransactionMinorState, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { + createKycTestkudosEnvironment, + postAmlDecisionNoRules, + withdrawViaBankV3, +} from "../harness/environments.js"; +import { GlobalTestState } from "../harness/harness.js"; + +function adjustExchangeConfig(config: Configuration): void { + config.setString("exchange", "enable_kyc", "yes"); + + config.setString("KYC-RULE-R1", "operation_type", "aggregate"); + 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:5"); + config.setString("KYC-RULE-R1", "timeframe", "1d"); + 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", "P1"); + + config.setString("AML-PROGRAM-P1", "command", "/bin/true"); + config.setString("AML-PROGRAM-P1", "enabled", "true"); + config.setString("AML-PROGRAM-P1", "description", "this does nothing"); + config.setString("AML-PROGRAM-P1", "fallback", "M1"); + + config.setString("KYC-CHECK-C1", "type", "INFO"); + config.setString("KYC-CHECK-C1", "description", "my check!"); + config.setString("KYC-CHECK-C1", "fallback", "M1"); +} + +export async function runKycDepositAggregateImplicitAuthTest(t: GlobalTestState) { + // Set up test environment + + const { + walletClient, + bankClient, + exchange, + amlKeypair, + } = await createKycTestkudosEnvironment(t, { + adjustExchangeConfig, + }); + + // Withdraw digital cash into the wallet. + + const wres = await withdrawViaBankV3(t, { + bankClient, + amount: "TESTKUDOS:50", + exchange: exchange, + walletClient: walletClient, + }); + + await wres.withdrawalFinishedCond; + + const depositResp = await walletClient.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:10", + depositPaytoUri: wres.accountPaytoUri, + }, + ); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: depositResp.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Track, + }, + }); + + await exchange.runAggregatorOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + console.log("waiting for kyc-required"); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: depositResp.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.KycRequired, + }, + }); + + console.log("waiting done"); + + const txDetails = await walletClient.call( + WalletApiOperation.GetTransactionById, + { + transactionId: depositResp.transactionId, + }, + ); + + const kycPaytoHash = txDetails.kycPaytoHash; + + t.assertTrue(!!kycPaytoHash); + + await postAmlDecisionNoRules(t, { + amlPriv: amlKeypair.priv, + amlPub: amlKeypair.pub, + exchangeBaseUrl: exchange.baseUrl, + paytoHash: kycPaytoHash, + }); + + await exchange.runAggregatorOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + await exchange.runTransferOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: depositResp.transactionId, + txState: { + major: TransactionMajorState.Done, + }, + }); +} + +runKycDepositAggregateImplicitAuthTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -51,6 +51,7 @@ import { runForcedSelectionTest } from "./test-forced-selection.js"; import { runKnownAccountsTest } from "./test-known-accounts.js"; import { runKycBalanceWithdrawalTest } from "./test-kyc-balance-withdrawal.js"; import { runKycDecisionsTest } from "./test-kyc-decisions.js"; +import { runKycDepositAggregateImplicitAuthTest } from "./test-kyc-deposit-aggregate-implicit-auth.js"; import { runKycDepositAggregateTest } from "./test-kyc-deposit-aggregate.js"; import { runKycDepositDepositKyctransferTest } from "./test-kyc-deposit-deposit-kyctransfer.js"; import { runKycDepositDepositTest } from "./test-kyc-deposit-deposit.js"; @@ -64,6 +65,7 @@ import { runKycPeerPullTest } from "./test-kyc-peer-pull.js"; import { runKycPeerPushTest } from "./test-kyc-peer-push.js"; import { runKycSkipExpirationTest } from "./test-kyc-skip-expiration.js"; import { runKycThresholdWithdrawalTest } from "./test-kyc-threshold-withdrawal.js"; +import { runKycTwoFormsTest } from "./test-kyc-two-forms.js"; import { runKycWithdrawalVerbotenTest } from "./test-kyc-withdrawal-verboten.js"; import { runKycTest } from "./test-kyc.js"; import { runLibeufinBankTest } from "./test-libeufin-bank.js"; @@ -148,7 +150,6 @@ import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js"; import { runWithdrawalIdempotentTest } from "./test-withdrawal-idempotent.js"; import { runWithdrawalManualTest } from "./test-withdrawal-manual.js"; import { runWithdrawalPrepareTest } from "./test-withdrawal-prepare.js"; -import { runKycTwoFormsTest } from "./test-kyc-two-forms.js"; /** * Test runner. @@ -288,6 +289,7 @@ const allTests: TestMainFunction[] = [ runKycMerchantActivateBankAccountTest, runKycDecisionsTest, runKnownAccountsTest, + runKycDepositAggregateImplicitAuthTest, ]; export interface TestRunSpec { diff --git a/packages/taler-util/src/types-taler-exchange.ts b/packages/taler-util/src/types-taler-exchange.ts @@ -1379,6 +1379,17 @@ export interface TrackTransactionAccepted { // Time by which the exchange currently thinks the deposit will be executed. // Actual execution may be later if the KYC check is not satisfied by then. execution_time: TalerProtocolTimestamp; + + // Public key associated with the account. The client must sign + // the initial request for the KYC status using the corresponding + // private key. Will be the merchant (instance) public key. + // + // Absent if no public key is currently associated + // with the account and the client MUST thus first + // credit the exchange via an inbound wire transfer + // to associate a public key with the debited account. + // @since protocol **v20**. + account_pub: EddsaPublicKeyString | undefined; } export const codecForTackTransactionAccepted = @@ -1387,6 +1398,7 @@ export const codecForTackTransactionAccepted = .property("requirement_row", codecOptional(codecForNumber())) .property("kyc_ok", codecForBoolean()) .property("execution_time", codecForTimestamp) + .property("account_pub", codecOptional(codecForString())) .build("TackTransactionAccepted"); export const codecForPeerContractTerms = (): Codec<PeerContractTerms> => @@ -2460,7 +2472,10 @@ export const codecForAmlProgramRequirement = (): Codec<AmlProgramRequirement> => export const codecForKycCheckInformation = (): Codec<KycCheckInformation> => buildCodecForObject<KycCheckInformation>() .property("description", codecForString()) - .property("description_i18n", codecOptional(codecForInternationalizedString())) + .property( + "description_i18n", + codecOptional(codecForInternationalizedString()), + ) .property("fallback", codecForString()) .property("outputs", codecForList(codecForString())) .property("requires", codecForList(codecForString())) @@ -2607,7 +2622,10 @@ export const codecForKycCheckPublicInformation = (): Codec<KycCheckPublicInformation> => buildCodecForObject<KycCheckPublicInformation>() .property("description", codecForString()) - .property("description_i18n", codecOptional(codecForInternationalizedString())) + .property( + "description_i18n", + codecOptional(codecForInternationalizedString()), + ) .build("TalerExchangeApi.KycCheckPublicInformation"); export const codecForKycRequirementInformationId = diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -1322,6 +1322,10 @@ async function processDepositGroupPendingTrack( logger.trace(`track response: ${j2s(track)}`); if (track.type === "accepted") { if (!track.kyc_ok && track.requirement_row !== undefined) { + if (!track.account_pub) { + // FIXME: Handle this properly. + throw Error("Deposit accepted but KYC auth missing"); + } // FIXME: Take this from the response. If not present, require KYC transfer. // But: Why did exchange accept deposit in the first place if missing? const paytoHash = encodeCrock(