aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-harness
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-08-08 16:49:37 +0200
committerFlorian Dold <florian@dold.me>2024-08-14 13:22:41 +0200
commit3c86bfb1435deba091771ca4e6135fbfd29b70ec (patch)
tree1f7833e9a903deb90f040f8e73663e5f104e945a /packages/taler-harness
parent947aa424ca0bc214c3e175221636fe6193c939c2 (diff)
wallet-core: implement basic wallet KYC for balance thresholds
Diffstat (limited to 'packages/taler-harness')
-rw-r--r--packages/taler-harness/src/harness/harness.ts20
-rw-r--r--packages/taler-harness/src/integrationtests/test-kyc-exchange-wallet.ts284
-rw-r--r--packages/taler-harness/src/integrationtests/test-kyc.ts15
-rw-r--r--packages/taler-harness/src/integrationtests/testrunner.ts2
4 files changed, 315 insertions, 6 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
index 2bbaafaf2..dc331a6cc 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -1618,6 +1618,26 @@ export class ExchangeService implements ExchangeServiceInterface {
}
}
+ async enableAmlAccount(
+ amlStaffPub: string,
+ legalName: string,
+ ): Promise<void> {
+ await runCommand(
+ this.globalState,
+ "exchange-offline",
+ "taler-exchange-offline",
+ [
+ "-c",
+ this.configFilename,
+ "aml-enable",
+ amlStaffPub,
+ legalName,
+ "rw",
+ "upload",
+ ],
+ );
+ }
+
async pingUntilAvailable(): Promise<void> {
// We request /management/keys, since /keys can block
// when we didn't do the key setup yet.
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-exchange-wallet.ts b/packages/taler-harness/src/integrationtests/test-kyc-exchange-wallet.ts
new file mode 100644
index 000000000..8b2b3ad30
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-kyc-exchange-wallet.ts
@@ -0,0 +1,284 @@
+/*
+ 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 {
+ AmlDecisionRequest,
+ AmlDecisionRequestWithoutSignature,
+ decodeCrock,
+ encodeCrock,
+ ExchangeWalletKycStatus,
+ hashPaytoUri,
+ HttpStatusCode,
+ j2s,
+ signAmlDecision,
+ TalerCorebankApiClient,
+ TalerProtocolTimestamp,
+} from "@gnu-taler/taler-util";
+import {
+ createSyncCryptoApi,
+ EddsaKeypair,
+ WalletApiOperation,
+} from "@gnu-taler/taler-wallet-core";
+import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
+import {
+ BankService,
+ DbInfo,
+ ExchangeService,
+ generateRandomPayto,
+ GlobalTestState,
+ HarnessExchangeBankAccount,
+ harnessHttpLib,
+ setupDb,
+ WalletClient,
+ WalletService,
+} from "../harness/harness.js";
+import { EnvOptions } from "../harness/helpers.js";
+
+interface KycTestEnv {
+ commonDb: DbInfo;
+ bankClient: TalerCorebankApiClient;
+ exchange: ExchangeService;
+ exchangeBankAccount: HarnessExchangeBankAccount;
+ walletClient: WalletClient;
+ walletService: WalletService;
+ amlKeypair: EddsaKeypair;
+}
+
+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";
+ let exchangePaytoUri = generateRandomPayto(exchangeBankUsername);
+
+ await exchange.addBankAccount("1", {
+ accountName: exchangeBankUsername,
+ accountPassword: exchangeBankPassword,
+ wireGatewayApiBaseUrl: new URL(
+ "accounts/exchange/taler-wire-gateway/",
+ bank.baseUrl,
+ ).href,
+ accountPaytoUri: exchangePaytoUri,
+ });
+
+ bank.setSuggestedExchange(exchange, exchangePaytoUri);
+
+ await bank.start();
+
+ await bank.pingUntilAvailable();
+
+ const bankClient = new TalerCorebankApiClient(bank.corebankApiBaseUrl, {
+ auth: {
+ username: "admin",
+ password: "adminpw",
+ },
+ });
+
+ await bankClient.registerAccountExtended({
+ name: receiverName,
+ password: exchangeBankPassword,
+ username: exchangeBankUsername,
+ is_taler_exchange: true,
+ payto_uri: exchangePaytoUri,
+ });
+
+ exchange.addCoinConfigList(coinConfig);
+
+ await exchange.modifyConfig(async (config) => {
+ config.setString("exchange", "enable_kyc", "yes");
+
+ 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:5");
+ 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", "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");
+ });
+
+ 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,
+ walletClient,
+ walletService,
+ bankClient,
+ exchangeBankAccount: {
+ accountName: "",
+ accountPassword: "",
+ accountPaytoUri: "",
+ wireGatewayApiBaseUrl: "",
+ },
+ amlKeypair,
+ };
+}
+
+export async function runKycExchangeWalletTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const { walletClient, exchange, amlKeypair } =
+ await createKycTestkudosEnvironment(t);
+
+ await walletClient.call(WalletApiOperation.AddExchange, {
+ exchangeBaseUrl: exchange.baseUrl,
+ });
+
+ await walletClient.call(WalletApiOperation.StartExchangeWalletKyc, {
+ amount: "TESTKUDOS:20",
+ exchangeBaseUrl: exchange.baseUrl,
+ });
+
+ await walletClient.call(WalletApiOperation.TestingWaitExchangeWalletKyc, {
+ amount: "TESTKUDOS:20",
+ exchangeBaseUrl: exchange.baseUrl,
+ passed: false,
+ });
+
+ const exchangeEntry = await walletClient.call(
+ WalletApiOperation.GetExchangeEntryByUrl,
+ {
+ exchangeBaseUrl: exchange.baseUrl,
+ },
+ );
+
+ console.log(j2s(exchangeEntry));
+
+ t.assertDeepEqual(
+ exchangeEntry.walletKycStatus,
+ ExchangeWalletKycStatus.Legi,
+ );
+
+ const kycReservePub = exchangeEntry.walletKycReservePub;
+
+ t.assertTrue(!!kycReservePub);
+
+ // FIXME: Create/user helper function for this!
+ const hPayto = hashPaytoUri(
+ `payto://taler-reserve-http/localhost:${exchange.port}/${kycReservePub}`,
+ );
+
+ console.log(`hPayto: ${hPayto}`);
+
+ {
+ const sigData: AmlDecisionRequestWithoutSignature = {
+ decision_time: TalerProtocolTimestamp.now(),
+ h_payto: encodeCrock(hPayto),
+ justification: "Bla",
+ keep_investigating: false,
+ new_rules: {
+ custom_measures: {},
+ expiration_time: TalerProtocolTimestamp.never(),
+ rules: [],
+ successor_measure: undefined,
+ },
+ properties: {
+ foo: "42",
+ },
+ };
+
+ const sig = signAmlDecision(decodeCrock(amlKeypair.priv), sigData);
+
+ const reqBody: AmlDecisionRequest = {
+ ...sigData,
+ officer_sig: encodeCrock(sig),
+ };
+
+ const reqUrl = new URL(`aml/${amlKeypair.pub}/decision`, exchange.baseUrl);
+
+ const resp = await harnessHttpLib.fetch(reqUrl.href, {
+ method: "POST",
+ body: reqBody,
+ });
+
+ console.log(`aml decision status: ${resp.status}`);
+
+ t.assertDeepEqual(resp.status, HttpStatusCode.NoContent);
+ }
+
+ await walletClient.call(WalletApiOperation.TestingWaitExchangeWalletKyc, {
+ amount: "TESTKUDOS:20",
+ exchangeBaseUrl: exchange.baseUrl,
+ passed: true,
+ });
+}
+
+runKycExchangeWalletTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-kyc.ts b/packages/taler-harness/src/integrationtests/test-kyc.ts
index 78e5d426c..74ca2457c 100644
--- a/packages/taler-harness/src/integrationtests/test-kyc.ts
+++ b/packages/taler-harness/src/integrationtests/test-kyc.ts
@@ -32,7 +32,7 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import * as http from "node:http";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import {
- BankService,
+ BankService,
ExchangeService,
GlobalTestState,
MerchantService,
@@ -81,7 +81,10 @@ async function createKycTestkudosEnvironment(
await exchange.addBankAccount("1", {
accountName: exchangeBankUsername,
accountPassword: exchangeBankPassword,
- wireGatewayApiBaseUrl: new URL("accounts/exchange/taler-wire-gateway/", bank.baseUrl).href,
+ wireGatewayApiBaseUrl: new URL(
+ "accounts/exchange/taler-wire-gateway/",
+ bank.baseUrl,
+ ).href,
accountPaytoUri: exchangePaytoUri,
});
@@ -236,10 +239,10 @@ async function createKycTestkudosEnvironment(
walletService,
bankClient,
exchangeBankAccount: {
- accountName: '',
- accountPassword: '',
- accountPaytoUri: '',
- wireGatewayApiBaseUrl: '',
+ accountName: "",
+ accountPassword: "",
+ accountPaytoUri: "",
+ wireGatewayApiBaseUrl: "",
},
};
}
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
index 0be555034..629a9029a 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -47,6 +47,7 @@ import { runExchangePurseTest } from "./test-exchange-purse.js";
import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
import { runFeeRegressionTest } from "./test-fee-regression.js";
import { runForcedSelectionTest } from "./test-forced-selection.js";
+import { runKycExchangeWalletTest } from "./test-kyc-exchange-wallet.js";
import { runKycThresholdWithdrawalTest } from "./test-kyc-threshold-withdrawal.js";
import { runKycTest } from "./test-kyc.js";
import { runLibeufinBankTest } from "./test-libeufin-bank.js";
@@ -246,6 +247,7 @@ const allTests: TestMainFunction[] = [
runWithdrawalExternalTest,
runWithdrawalIdempotentTest,
runKycThresholdWithdrawalTest,
+ runKycExchangeWalletTest,
];
export interface TestRunSpec {