gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-typescript-core] branch master updated: harness: add test for exc


From: Admin
Subject: [taler-typescript-core] branch master updated: harness: add test for exchange kyc auth
Date: Tue, 03 Jun 2025 20:30:11 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository taler-typescript-core.

The following commit(s) were added to refs/heads/master by this push:
     new cca3196ed harness: add test for exchange kyc auth
cca3196ed is described below

commit cca3196ed9c61d3e3d92dcd18bea7a97d4526948
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Jun 3 20:29:55 2025 +0200

    harness: add test for exchange kyc auth
    
    Issue: https://bugs.taler.net/n/10044
---
 packages/taler-harness/src/harness/environments.ts |   6 +-
 .../src/integrationtests/test-exchange-kyc-auth.ts | 302 +++++++++++++++++++++
 .../src/integrationtests/testrunner.ts             |   2 +
 .../taler-util/src/http-client/exchange-client.ts  |  54 ++++
 4 files changed, 361 insertions(+), 3 deletions(-)

diff --git a/packages/taler-harness/src/harness/environments.ts 
b/packages/taler-harness/src/harness/environments.ts
index b4c598d63..ac56d857e 100644
--- a/packages/taler-harness/src/harness/environments.ts
+++ b/packages/taler-harness/src/harness/environments.ts
@@ -46,7 +46,7 @@ import {
   succeedOrThrow,
   TalerCorebankApiClient,
   TalerCoreBankHttpClient,
-  TalerExchangeHttpClient,
+  TalerExchangeHttpClient2,
   TalerMerchantApi,
   TalerMerchantInstanceHttpClient,
   TalerProtocolTimestamp,
@@ -1098,7 +1098,7 @@ export interface KycTestEnv {
   amlKeypair: EddsaKeyPairStrings;
   merchant: MerchantService;
   bankApi: TalerCoreBankHttpClient;
-  exchangeApi: TalerExchangeHttpClient;
+  exchangeApi: TalerExchangeHttpClient2;
   wireGatewayApi: TalerWireGatewayHttpClient;
   merchantApi: TalerMerchantInstanceHttpClient;
 }
@@ -1272,7 +1272,7 @@ export async function createKycTestkudosEnvironment(
     harnessHttpLib,
   );
 
-  const exchangeApi = new TalerExchangeHttpClient(exchange.baseUrl, {
+  const exchangeApi = new TalerExchangeHttpClient2(exchange.baseUrl, {
     httpClient: harnessHttpLib,
   });
 
diff --git 
a/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts 
b/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts
new file mode 100644
index 000000000..0bd71c27e
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts
@@ -0,0 +1,302 @@
+/*
+ This file is part of GNU Taler
+ (C) 2024 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,
+  createEddsaKeyPair,
+  encodeCrock,
+  hashNormalizedPaytoUri,
+  HttpStatusCode,
+  j2s,
+  TalerWireGatewayHttpClient,
+  TransactionMajorState,
+  TransactionMinorState,
+} from "@gnu-taler/taler-util";
+import {
+  createSyncCryptoApi,
+  WalletApiOperation,
+} from "@gnu-taler/taler-wallet-core";
+import {
+  configureCommonKyc,
+  createKycTestkudosEnvironment,
+  withdrawViaBankV3,
+} from "../harness/environments.js";
+import {
+  getTestHarnessPaytoForLabel,
+  GlobalTestState,
+} from "../harness/harness.js";
+
+const myAmlConfig = `
+# Fallback measure on errors.
+[kyc-measure-freeze-investigate]
+CHECK_NAME = skip
+PROGRAM = freeze-investigate
+VOLUNTARY = NO
+CONTEXT = {}
+
+[aml-program-freeze-investigate]
+DESCRIPTION = "Fallback measure on errors that freezes the account and asks 
AML staff to investigate the system failure."
+COMMAND = taler-exchange-helper-measure-freeze
+ENABLED = YES
+FALLBACK = freeze-investigate
+
+[aml-program-inform-investigate]
+DESCRIPTION = "Measure that asks AML staff to investigate an account and 
informs the account owner about it."
+COMMAND = taler-exchange-helper-measure-inform-investigate
+ENABLED = YES
+FALLBACK = freeze-investigate
+
+[kyc-check-form-gls-merchant-onboarding]
+TYPE = FORM
+FORM_NAME = gls-merchant-onboarding
+DESCRIPTION = "GLS Merchant Onboarding"
+DESCRIPTION_I18N = {}
+OUTPUTS =
+FALLBACK = freeze-investigate
+
+[kyc-measure-merchant-onboarding]
+CHECK_NAME = form-gls-merchant-onboarding
+PROGRAM = inform-investigate
+CONTEXT = {}
+VOLUNTARY = NO
+
+[kyc-rule-deposit-limit-zero]
+OPERATION_TYPE = DEPOSIT
+NEXT_MEASURES = merchant-onboarding
+EXPOSED = YES
+ENABLED = YES
+THRESHOLD = TESTKUDOS:1
+TIMEFRAME = "1 days"
+`;
+
+function adjustExchangeConfig(config: Configuration) {
+  configureCommonKyc(config);
+  config.loadFromString(myAmlConfig);
+}
+
+/**
+ * Test for KYC auth, based on withdrawals and/or
+ * KYC auth transfers.
+ */
+export async function runExchangeKycAuthTest(t: GlobalTestState) {
+  // Set up test environment
+
+  // FIXME: Reduced test environment without merchant suffices
+  const {
+    walletClient,
+    bankClient,
+    exchange,
+    amlKeypair,
+    exchangeBankAccount,
+    exchangeApi,
+  } = await createKycTestkudosEnvironment(t, { adjustExchangeConfig });
+
+  const merchantPayto = getTestHarnessPaytoForLabel("merchant-default");
+
+  const cryptoApi = createSyncCryptoApi();
+
+  const wireGatewayApiClient = new TalerWireGatewayHttpClient(
+    exchangeBankAccount.wireGatewayApiBaseUrl,
+  );
+
+  const merchantPair = await cryptoApi.createEddsaKeypair({});
+
+  const wres = await withdrawViaBankV3(t, {
+    walletClient,
+    exchange,
+    bankClient,
+    amount: "TESTKUDOS:20",
+  });
+
+  const kycPaytoHash = encodeCrock(hashNormalizedPaytoUri(merchantPayto));
+
+  await wres.withdrawalFinishedCond;
+
+  // Use the wallet to trigger KYC
+
+  const depositResp = await walletClient.call(
+    WalletApiOperation.CreateDepositGroup,
+    {
+      amount: "TESTKUDOS:5",
+      depositPaytoUri: merchantPayto,
+      testingFixedPriv: merchantPair.priv,
+    },
+  );
+
+  // Wait until KYC got triggered.
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+    transactionId: depositResp.transactionId,
+    txState: {
+      major: TransactionMajorState.Pending,
+      minor: TransactionMinorState.KycAuthRequired,
+    },
+  });
+
+  await wireGatewayApiClient.addKycAuth({
+    body: {
+      account_pub: merchantPair.pub,
+      amount: "TESTKUDOS:0.1",
+      debit_account: merchantPayto,
+    },
+    auth: exchangeBankAccount.wireGatewayAuth,
+  });
+
+  await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+    transactionId: depositResp.transactionId,
+    txState: {
+      major: TransactionMajorState.Pending,
+      minor: TransactionMinorState.KycRequired,
+    },
+  });
+
+  {
+    const sigResp = await cryptoApi.signWalletKycAuth({
+      accountPriv: merchantPair.priv,
+      accountPub: merchantPair.pub,
+    });
+    const checkResp1 = await exchangeApi.checkKycStatus({
+      accountPub: merchantPair.pub,
+      accountSig: sigResp.sig,
+      paytoHash: kycPaytoHash,
+    });
+
+    console.log(j2s(checkResp1));
+
+    t.assertDeepEqual(checkResp1.case, HttpStatusCode.Accepted);
+  }
+
+  const reservePair2 = createEddsaKeyPair();
+  const reservePair3 = createEddsaKeyPair();
+  const reservePair4 = createEddsaKeyPair();
+
+  {
+    const sigResp = await cryptoApi.signWalletKycAuth({
+      accountPriv: encodeCrock(reservePair2.eddsaPriv),
+      accountPub: encodeCrock(reservePair2.eddsaPub),
+    });
+    const checkResp = await exchangeApi.checkKycStatus({
+      accountPub: merchantPair.pub,
+      accountSig: sigResp.sig,
+      paytoHash: kycPaytoHash,
+    });
+
+    console.log(j2s(checkResp));
+
+    t.assertDeepEqual(checkResp.case, HttpStatusCode.Forbidden);
+  }
+
+  await wireGatewayApiClient.addIncoming({
+    body: {
+      reserve_pub: encodeCrock(reservePair2.eddsaPub),
+      amount: "TESTKUDOS:5",
+      debit_account: merchantPayto,
+    },
+    auth: exchangeBankAccount.wireGatewayAuth,
+  });
+
+  await wireGatewayApiClient.addIncoming({
+    body: {
+      reserve_pub: encodeCrock(reservePair3.eddsaPub),
+      amount: "TESTKUDOS:5",
+      debit_account: merchantPayto,
+    },
+    auth: exchangeBankAccount.wireGatewayAuth,
+  });
+
+  await wireGatewayApiClient.addIncoming({
+    body: {
+      reserve_pub: encodeCrock(reservePair4.eddsaPub),
+      amount: "TESTKUDOS:5",
+      debit_account: getTestHarnessPaytoForLabel("bob"),
+    },
+    auth: exchangeBankAccount.wireGatewayAuth,
+  });
+
+  await exchange.runWirewatchOnce();
+
+  // Even when no account pub is specified, the last reserve pub for
+  // the account must work.
+
+  {
+    const sigResp = await cryptoApi.signWalletKycAuth({
+      accountPriv: encodeCrock(reservePair3.eddsaPriv),
+      accountPub: encodeCrock(reservePair3.eddsaPub),
+    });
+    const checkResp = await exchangeApi.testingCheckKycStatusNoPub({
+      accountSig: sigResp.sig,
+      paytoHash: kycPaytoHash,
+    });
+
+    console.log(j2s(checkResp));
+
+    t.assertDeepEqual(checkResp.case, HttpStatusCode.Accepted);
+  }
+
+  // Now, kyc auth must work with explicit account key and both reserve pubs!
+
+  {
+    const sigResp = await cryptoApi.signWalletKycAuth({
+      accountPriv: merchantPair.priv,
+      accountPub: merchantPair.pub,
+    });
+    const checkResp = await exchangeApi.checkKycStatus({
+      accountPub: merchantPair.pub,
+      accountSig: sigResp.sig,
+      paytoHash: kycPaytoHash,
+    });
+
+    console.log(j2s(checkResp));
+
+    t.assertDeepEqual(checkResp.case, HttpStatusCode.Accepted);
+  }
+
+  {
+    const sigResp = await cryptoApi.signWalletKycAuth({
+      accountPriv: encodeCrock(reservePair3.eddsaPriv),
+      accountPub: encodeCrock(reservePair3.eddsaPub),
+    });
+    const checkResp = await exchangeApi.checkKycStatus({
+      accountPub: encodeCrock(reservePair3.eddsaPub),
+      accountSig: sigResp.sig,
+      paytoHash: kycPaytoHash,
+    });
+
+    console.log(j2s(checkResp));
+
+    t.assertDeepEqual(checkResp.case, HttpStatusCode.Accepted);
+  }
+
+  {
+    const sigResp = await cryptoApi.signWalletKycAuth({
+      accountPriv: encodeCrock(reservePair2.eddsaPriv),
+      accountPub: encodeCrock(reservePair2.eddsaPub),
+    });
+    const checkResp = await exchangeApi.checkKycStatus({
+      accountPub: encodeCrock(reservePair2.eddsaPub),
+      accountSig: sigResp.sig,
+      paytoHash: kycPaytoHash,
+    });
+
+    console.log(j2s(checkResp));
+
+    t.assertDeepEqual(checkResp.case, HttpStatusCode.Accepted);
+  }
+}
+
+runExchangeKycAuthTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts 
b/packages/taler-harness/src/integrationtests/testrunner.ts
index 453d80014..dad48dcdd 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -49,6 +49,7 @@ import { runDepositFaultTest } from "./test-deposit-fault.js";
 import { runDepositMergeTest } from "./test-deposit-merge.js";
 import { runDepositTest } from "./test-deposit.js";
 import { runExchangeDepositTest } from "./test-exchange-deposit.js";
+import { runExchangeKycAuthTest } from "./test-exchange-kyc-auth.js";
 import { runExchangeManagementFaultTest } from 
"./test-exchange-management-fault.js";
 import { runExchangeManagementTest } from "./test-exchange-management.js";
 import { runExchangeMasterPubChangeTest } from 
"./test-exchange-master-pub-change.js";
@@ -347,6 +348,7 @@ const allTests: TestMainFunction[] = [
   runUtilMerchantClientTest,
   runKycWalletDepositAbortTest,
   runKycMerchantDepositFormTest,
+  runExchangeKycAuthTest,
 ];
 
 export interface TestRunSpec {
diff --git a/packages/taler-util/src/http-client/exchange-client.ts 
b/packages/taler-util/src/http-client/exchange-client.ts
index 5a8919ae3..4ad168193 100644
--- a/packages/taler-util/src/http-client/exchange-client.ts
+++ b/packages/taler-util/src/http-client/exchange-client.ts
@@ -664,6 +664,60 @@ export class TalerExchangeHttpClient2 {
     }
   }
 
+  /**
+   * Do a /kyc-check request, but don't specify
+   * the account pub explicitly.
+   *
+   * Deprecated, but used in tests.
+   */
+  async testingCheckKycStatusNoPub(args: {
+    paytoHash: string;
+    accountSig: EddsaSignatureString;
+    longpoll?: boolean;
+    awaitAuth?: boolean;
+  }): Promise<
+    | OperationOk<void>
+    | OperationAlternative<HttpStatusCode.Ok, AccountKycStatus>
+    | OperationAlternative<HttpStatusCode.Accepted, AccountKycStatus>
+    | OperationFail<HttpStatusCode.Forbidden>
+    | OperationFail<HttpStatusCode.NotFound>
+    | OperationFail<HttpStatusCode.Conflict>
+  > {
+    const { paytoHash, accountSig, longpoll, awaitAuth } = args;
+    const url = new URL(`kyc-check/${paytoHash}`, this.baseUrl);
+    if (awaitAuth !== undefined) {
+      url.searchParams.set("await_auth", awaitAuth ? "YES" : "NO");
+    }
+
+    const resp = await this.fetch(
+      url,
+      {
+        headers: {
+          "Account-Owner-Signature": accountSig,
+        },
+      },
+      longpoll,
+    );
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+      case HttpStatusCode.Accepted:
+        return opKnownAlternativeHttpFailure(
+          resp,
+          resp.status,
+          codecForAccountKycStatus(),
+        );
+      case HttpStatusCode.NoContent:
+        return opEmptySuccess();
+      case HttpStatusCode.Forbidden:
+      case HttpStatusCode.NotFound:
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownHttpFailure(resp);
+    }
+  }
+
   /**
    * https://docs.taler.net/core/api-exchange.html#get--kyc-info-$ACCESS_TOKEN
    *

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]