gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 22/151: basic refactoring of httpd for new AML, incompl


From: gnunet
Subject: [taler-exchange] 22/151: basic refactoring of httpd for new AML, incomplete
Date: Tue, 30 Jul 2024 23:36:32 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

commit e362663b75830c89f620be750d36e343b0d8ca3d
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue May 7 14:56:08 2024 +0200

    basic refactoring of httpd for new AML, incomplete
---
 src/exchange/Makefile.am                           |  22 +-
 src/exchange/taler-exchange-aggregator.c           |  17 +-
 src/exchange/taler-exchange-httpd_age-withdraw.c   | 274 +++-----
 src/exchange/taler-exchange-httpd_batch-withdraw.c | 265 +------
 src/exchange/taler-exchange-httpd_deposits_get.c   |   8 +-
 src/exchange/taler-exchange-httpd_responses.c      |  25 -
 src/exchange/taler-exchange-httpd_responses.h      |  13 -
 src/exchange/taler-exchange-httpd_withdraw.c       | 211 ++++++
 src/exchange/taler-exchange-httpd_withdraw.h       |  51 ++
 src/exchangedb/pg_get_kyc_rules.c                  |   9 +-
 src/exchangedb/pg_get_kyc_rules.h                  |   2 -
 src/include/taler_exchangedb_plugin.h              |   2 -
 src/include/taler_kyclogic_lib.h                   |  41 +-
 src/kyclogic/kyclogic_api.c                        | 758 +++++++++++----------
 src/kyclogic/taler-exchange-kyc-tester.c           |  24 +-
 15 files changed, 835 insertions(+), 887 deletions(-)

diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 1c0c2c684..2cf514f0c 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -127,9 +127,6 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \
   taler-exchange-httpd_age-withdraw_reveal.c 
taler-exchange-httpd_age-withdraw_reveal.h \
   taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \
-  taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \
-  taler-exchange-httpd_aml-decision-get.c \
-  taler-exchange-httpd_aml-decisions-get.c \
   taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \
   taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \
   taler-exchange-httpd_coins_get.c taler-exchange-httpd_coins_get.h \
@@ -139,12 +136,9 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \
   taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \
   taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
-  taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
   taler-exchange-httpd_extensions.c taler-exchange-httpd_extensions.h \
   taler-exchange-httpd_keys.c taler-exchange-httpd_keys.h \
-  taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.h \
   taler-exchange-httpd_kyc-proof.c taler-exchange-httpd_kyc-proof.h \
-  taler-exchange-httpd_kyc-wallet.c taler-exchange-httpd_kyc-wallet.h \
   taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \
   taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
   taler-exchange-httpd_management.h \
@@ -168,22 +162,30 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_purses_deposit.c taler-exchange-httpd_purses_deposit.h \
   taler-exchange-httpd_purses_delete.c taler-exchange-httpd_purses_delete.h \
   taler-exchange-httpd_purses_get.c taler-exchange-httpd_purses_get.h \
-  taler-exchange-httpd_purses_merge.c taler-exchange-httpd_purses_merge.h \
   taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
   taler-exchange-httpd_recoup-refresh.c taler-exchange-httpd_recoup-refresh.h \
   taler-exchange-httpd_refreshes_reveal.c 
taler-exchange-httpd_refreshes_reveal.h \
   taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
   taler-exchange-httpd_reserves_attest.c 
taler-exchange-httpd_reserves_attest.h \
-  taler-exchange-httpd_reserves_close.c taler-exchange-httpd_reserves_close.h \
   taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \
   taler-exchange-httpd_reserves_get_attest.c 
taler-exchange-httpd_reserves_get_attest.h \
   taler-exchange-httpd_reserves_history.c 
taler-exchange-httpd_reserves_history.h \
   taler-exchange-httpd_reserves_open.c taler-exchange-httpd_reserves_open.h \
-  taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h \
   taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
   taler-exchange-httpd_spa.c taler-exchange-httpd_spa.h \
   taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
-  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h
+  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
+  taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
+
+#  taler-exchange-httpd_purses_merge.c taler-exchange-httpd_purses_merge.h \
+#  taler-exchange-httpd_reserves_close.c taler-exchange-httpd_reserves_close.h 
\
+#  taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h 
\
+#  taler-exchange-httpd_kyc-check.c taler-exchange-httpd_kyc-check.h \
+#  taler-exchange-httpd_kyc-wallet.c taler-exchange-httpd_kyc-wallet.h \
+#  taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
+#  taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \
+#  taler-exchange-httpd_aml-decision-get.c \
+#  taler-exchange-httpd_aml-decisions-get.c
 
 taler_exchange_httpd_LDADD = \
   $(LIBGCRYPT_LIBS) \
diff --git a/src/exchange/taler-exchange-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index 9a4b36a71..0e485ae9c 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -500,7 +500,6 @@ legitimization_satisfied (struct AggregationUnit *au_active)
 {
   struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL;
   struct TALER_KYCLOGIC_KycRule *requirement;
-  struct GNUNET_TIME_Timestamp expiration_time;
   enum GNUNET_DB_QueryStatus qs;
   json_t *jrule;
 
@@ -512,29 +511,17 @@ legitimization_satisfied (struct AggregationUnit 
*au_active)
 
     qs = db_plugin->get_kyc_rules (db_plugin->cls,
                                    &au_active->h_payto,
-                                   &expiration_time,
                                    &jrules);
     if (qs < 0)
     {
       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
       return false;
     }
-    if ( (qs > 0) &&
-         (GNUNET_TIME_absolute_is_past (expiration_time.abs_time)) )
-    {
-      json_decref (jrules);
-      jrules = NULL;
-      qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
-    }
     if (qs > 0)
     {
       lrs = TALER_KYCLOGIC_rules_parse (jrules);
-      if (NULL == lrs)
-      {
-        GNUNET_break (0);
-        json_decref (jrules);
-        return false;
-      }
+      GNUNET_break (NULL != lrs);
+      /* Fall back to default rules on parse error! */
       json_decref (jrules);
     }
   }
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c 
b/src/exchange/taler-exchange-httpd_age-withdraw.c
index 9276fb191..4bb1db5b9 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw.c
@@ -33,6 +33,7 @@
 #include "taler_kyclogic_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_age-withdraw.h"
+#include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keys.h"
 #include "taler_util.h"
@@ -53,11 +54,6 @@ struct AgeWithdrawContext
    */
   struct GNUNET_TIME_Timestamp now;
 
-  /**
-   * Hash of the wire source URL, needed when kyc is needed.
-   */
-  struct TALER_PaytoHashP h_payto;
-
   /**
    * The data from the age-withdraw request, as we persist it
    */
@@ -549,19 +545,18 @@ reply_age_withdraw_success (
 {
   struct TALER_ExchangePublicKeyP pub;
   struct TALER_ExchangeSignatureP sig;
-  enum TALER_ErrorCode ec =
-    TALER_exchange_online_age_withdraw_confirmation_sign (
-      &TEH_keys_exchange_sign_,
-      ach,
-      noreveal_index,
-      &pub,
-      &sig);
-
+  enum TALER_ErrorCode ec;
+
+  ec = TALER_exchange_online_age_withdraw_confirmation_sign (
+    &TEH_keys_exchange_sign_,
+    ach,
+    noreveal_index,
+    &pub,
+    &sig);
   if (TALER_EC_NONE != ec)
     return TALER_MHD_reply_with_ec (connection,
                                     ec,
                                     NULL);
-
   return TALER_MHD_REPLY_JSON_PACK (connection,
                                     MHD_HTTP_OK,
                                     GNUNET_JSON_pack_uint64 ("noreveal_index",
@@ -618,49 +613,6 @@ request_is_idempotent (struct MHD_Connection *con,
 }
 
 
-/**
- * Function called to iterate over KYC-relevant transaction amounts for a
- * particular time range. Called within a database transaction, so must
- * not start a new one.
- *
- * @param cls closure, identifies the event type and account to iterate
- *        over events for
- * @param limit maximum time-range for which events should be fetched
- *        (timestamp in the past)
- * @param cb function to call on each event found, events must be returned
- *        in reverse chronological order
- * @param cb_cls closure for @a cb, of type struct AgeWithdrawContext
- */
-static void
-age_withdraw_amount_cb (void *cls,
-                        struct GNUNET_TIME_Absolute limit,
-                        TALER_EXCHANGEDB_KycAmountCallback cb,
-                        void *cb_cls)
-{
-  struct AgeWithdrawContext *awc = cls;
-  enum GNUNET_DB_QueryStatus qs;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Signaling amount %s for KYC check during age-withdrawal\n",
-              TALER_amount2s (&awc->commitment.amount_with_fee));
-  if (GNUNET_OK !=
-      cb (cb_cls,
-          &awc->commitment.amount_with_fee,
-          awc->now.abs_time))
-    return;
-  qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (TEH_plugin->cls,
-                                                          &awc->h_payto,
-                                                          limit,
-                                                          cb,
-                                                          cb_cls);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Got %d additional transactions for this age-withdrawal and 
limit %llu\n",
-              qs,
-              (unsigned long long) limit.abs_value_us);
-  GNUNET_break (qs >= 0);
-}
-
-
 /**
  * Function implementing age withdraw transaction.  Runs the
  * transaction logic; IF it returns a non-error code, the transaction
@@ -682,135 +634,89 @@ age_withdraw_transaction (void *cls,
 {
   struct AgeWithdrawContext *awc = cls;
   enum GNUNET_DB_QueryStatus qs;
-
-  qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
-                                        &awc->commitment.reserve_pub,
-                                        &awc->h_payto);
-  if (qs < 0)
+  bool found = false;
+  bool balance_ok = false;
+  bool age_ok = false;
+  bool conflict = false;
+  uint16_t allowed_maximum_age = 0;
+  uint32_t reserve_birthday = 0;
+  struct TALER_Amount reserve_balance;
+
+  qs = TEH_withdraw_kyc_check (&awc->kyc,
+                               connection,
+                               mhd_ret,
+                               &awc->commitment.reserve_pub,
+                               &awc->commitment.amount_with_fee,
+                               awc->now);
+  if ( (qs < 0) ||
+       (! awc->kyc.ok) )
     return qs;
-
-  /* If _no_ results, reserve was created by merge,
-     in which case no KYC check is required as the
-     merge already did that. */
-
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls,
+                                    &awc->commitment,
+                                    awc->now,
+                                    &found,
+                                    &balance_ok,
+                                    &reserve_balance,
+                                    &age_ok,
+                                    &allowed_maximum_age,
+                                    &reserve_birthday,
+                                    &conflict);
+  if (0 > qs)
   {
-    char *kyc_required;
-
-    qs = TALER_KYCLOGIC_kyc_test_required (
-      TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW,
-      &awc->h_payto,
-      TEH_plugin->select_satisfied_kyc_processes,
-      TEH_plugin->cls,
-      &age_withdraw_amount_cb,
-      awc,
-      &kyc_required);
-
-    if (qs < 0)
-    {
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      {
-        GNUNET_break (0);
-        *mhd_ret = TALER_MHD_reply_with_ec (connection,
-                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                            "kyc_test_required");
-      }
-      return qs;
-    }
-
-    if (NULL != kyc_required)
-    {
-      /* Mark result and return by inserting KYC requirement into DB! */
-      awc->kyc.ok = false;
-      return TEH_plugin->insert_kyc_requirement_for_account (
-        TEH_plugin->cls,
-        kyc_required,
-        &awc->h_payto,
-        &awc->commitment.reserve_pub,
-        &awc->kyc.requirement_row);
-    }
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TALER_MHD_reply_with_ec (connection,
+                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                          "do_age_withdraw");
+    return qs;
   }
-
-  awc->kyc.ok = true;
-
-  /* KYC requirement fulfilled, do the age-withdraw transaction */
+  if (! found)
   {
-    bool found = false;
-    bool balance_ok = false;
-    bool age_ok = false;
-    bool conflict = false;
-    uint16_t allowed_maximum_age = 0;
-    uint32_t reserve_birthday = 0;
-    struct TALER_Amount reserve_balance;
-
-    qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls,
-                                      &awc->commitment,
-                                      awc->now,
-                                      &found,
-                                      &balance_ok,
-                                      &reserve_balance,
-                                      &age_ok,
-                                      &allowed_maximum_age,
-                                      &reserve_birthday,
-                                      &conflict);
-    if (0 > qs)
-    {
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_ec (connection,
-                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                            "do_age_withdraw");
-      return qs;
-    }
-    if (! found)
-    {
-      *mhd_ret = TALER_MHD_reply_with_ec (connection,
-                                          
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
-                                          NULL);
-      return GNUNET_DB_STATUS_HARD_ERROR;
-    }
-    if (! age_ok)
-    {
-      enum TALER_ErrorCode ec =
-        TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE;
-
-      *mhd_ret =
-        TALER_MHD_REPLY_JSON_PACK (
-          connection,
-          MHD_HTTP_CONFLICT,
-          TALER_MHD_PACK_EC (ec),
-          GNUNET_JSON_pack_uint64 ("allowed_maximum_age",
-                                   allowed_maximum_age),
-          GNUNET_JSON_pack_uint64 ("reserve_birthday",
-                                   reserve_birthday));
-
-      return GNUNET_DB_STATUS_HARD_ERROR;
-    }
-    if (! balance_ok)
-    {
-      TEH_plugin->rollback (TEH_plugin->cls);
+    *mhd_ret = TALER_MHD_reply_with_ec (connection,
+                                        
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+                                        NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (! age_ok)
+  {
+    enum TALER_ErrorCode ec =
+      TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE;
 
-      *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
+    *mhd_ret =
+      TALER_MHD_REPLY_JSON_PACK (
         connection,
-        TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
-        &reserve_balance,
-        &awc->commitment.amount_with_fee,
-        &awc->commitment.reserve_pub);
-
-      return GNUNET_DB_STATUS_HARD_ERROR;
-    }
-    if (conflict)
-    {
-      /* do_age_withdraw signaled a conflict, so there MUST be an entry
-       * in the DB.  Put that into the response */
-      bool ok = request_is_idempotent (connection,
-                                       awc,
-                                       mhd_ret);
-      GNUNET_assert (ok);
-      return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-    }
-    *mhd_ret = -1;
+        MHD_HTTP_CONFLICT,
+        TALER_MHD_PACK_EC (ec),
+        GNUNET_JSON_pack_uint64 ("allowed_maximum_age",
+                                 allowed_maximum_age),
+        GNUNET_JSON_pack_uint64 ("reserve_birthday",
+                                 reserve_birthday));
+
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
+  if (! balance_ok)
+  {
+    TEH_plugin->rollback (TEH_plugin->cls);
+
+    *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
+      connection,
+      TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
+      &reserve_balance,
+      &awc->commitment.amount_with_fee,
+      &awc->commitment.reserve_pub);
 
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (conflict)
+  {
+    /* do_age_withdraw signaled a conflict, so there MUST be an entry
+     * in the DB.  Put that into the response */
+    bool ok = request_is_idempotent (connection,
+                                     awc,
+                                     mhd_ret);
+    GNUNET_assert (ok);
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  }
+  *mhd_ret = -1;
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW]++;
   return qs;
@@ -846,8 +752,6 @@ sign_and_do_age_withdraw (
   struct TALER_BlindedDenominationSignature denom_sigs[awc->num_coins];
   uint8_t noreveal_index;
 
-  awc->now = GNUNET_TIME_timestamp_get ();
-
   /* Pick the challenge */
   noreveal_index =
     GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
@@ -918,7 +822,10 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
   MHD_RESULT mhd_ret;
   const json_t *j_denom_hs;
   const json_t *j_blinded_coin_evs;
-  struct AgeWithdrawContext awc = {0};
+  struct AgeWithdrawContext awc = {
+    .commitment.reserve_pub = *reserve_pub,
+    .now = GNUNET_TIME_timestamp_get ()
+  };
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_array_const ("denom_hs",
                                   &j_denom_hs),
@@ -931,9 +838,6 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
     GNUNET_JSON_spec_end ()
   };
 
-  awc.commitment.reserve_pub = *reserve_pub;
-
-
   /* Parse the JSON body */
   {
     enum GNUNET_GenericReturnValue res;
@@ -1000,7 +904,7 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
      * the DB-transaction */
     if (! awc.kyc.ok)
       mhd_ret = TEH_RESPONSE_reply_kyc_required (rc->connection,
-                                                 &awc.h_payto,
+                                                 NULL, /* FIXME! */
                                                  &awc.kyc);
     else
       mhd_ret = reply_age_withdraw_success (rc->connection,
diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c 
b/src/exchange/taler-exchange-httpd_batch-withdraw.c
index 2212897a0..d9f5489d8 100644
--- a/src/exchange/taler-exchange-httpd_batch-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2023 Taler Systems SA
+  Copyright (C) 2014-2024 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as
@@ -31,6 +31,7 @@
 #include "taler_kyclogic_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_batch-withdraw.h"
+#include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keys.h"
 #include "taler_util.h"
@@ -89,12 +90,6 @@ struct BatchWithdrawContext
    */
   struct PlanchetContext *planchets;
 
-  /**
-   * Hash of the payto-URI representing the reserve
-   * from which we are withdrawing.
-   */
-  struct TALER_PaytoHashP h_payto;
-
   /**
    * Current time for the DB transaction.
    */
@@ -110,81 +105,9 @@ struct BatchWithdrawContext
    */
   unsigned int planchets_length;
 
-  /**
-   * AML decision, #TALER_AML_NORMAL if we may proceed.
-   */
-  enum TALER_AmlDecisionState aml_decision;
-
 };
 
 
-/**
- * Function called to iterate over KYC-relevant
- * transaction amounts for a particular time range.
- * Called within a database transaction, so must
- * not start a new one.
- *
- * @param cls closure, identifies the event type and
- *        account to iterate over events for
- * @param limit maximum time-range for which events
- *        should be fetched (timestamp in the past)
- * @param cb function to call on each event found,
- *        events must be returned in reverse chronological
- *        order
- * @param cb_cls closure for @a cb
- */
-static void
-batch_withdraw_amount_cb (void *cls,
-                          struct GNUNET_TIME_Absolute limit,
-                          TALER_EXCHANGEDB_KycAmountCallback cb,
-                          void *cb_cls)
-{
-  struct BatchWithdrawContext *wc = cls;
-  enum GNUNET_DB_QueryStatus qs;
-
-  if (GNUNET_OK !=
-      cb (cb_cls,
-          &wc->batch_total,
-          wc->now.abs_time))
-    return;
-  qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
-    TEH_plugin->cls,
-    &wc->h_payto,
-    limit,
-    cb,
-    cb_cls);
-  GNUNET_break (qs >= 0);
-}
-
-
-/**
- * Function called on each @a amount that was found to
- * be relevant for the AML check as it was merged into
- * the reserve.
- *
- * @param cls `struct TALER_Amount *` to total up the amounts
- * @param amount encountered transaction amount
- * @param date when was the amount encountered
- * @return #GNUNET_OK to continue to iterate,
- *         #GNUNET_NO to abort iteration
- *         #GNUNET_SYSERR on internal error (also abort itaration)
- */
-static enum GNUNET_GenericReturnValue
-aml_amount_cb (
-  void *cls,
-  const struct TALER_Amount *amount,
-  struct GNUNET_TIME_Absolute date)
-{
-  struct TALER_Amount *total = cls;
-
-  GNUNET_assert (0 <=
-                 TALER_amount_add (total,
-                                   total,
-                                   amount));
-  return GNUNET_OK;
-}
-
-
 /**
  * Generates our final (successful) response.
  *
@@ -198,17 +121,6 @@ generate_reply_success (const struct TEH_RequestContext 
*rc,
 {
   json_t *sigs;
 
-  if (! wc->kyc.ok)
-  {
-    /* KYC required */
-    return TEH_RESPONSE_reply_kyc_required (rc->connection,
-                                            &wc->h_payto,
-                                            &wc->kyc);
-  }
-  if (TALER_AML_NORMAL != wc->aml_decision)
-    return TEH_RESPONSE_reply_aml_blocked (rc->connection,
-                                           wc->aml_decision);
-
   sigs = json_array ();
   GNUNET_assert (NULL != sigs);
   for (unsigned int i = 0; i<wc->planchets_length; i++)
@@ -309,163 +221,16 @@ batch_withdraw_transaction (void *cls,
   bool age_ok = false;
   uint16_t allowed_maximum_age = 0;
   struct TALER_Amount reserve_balance;
-  char *kyc_required;
-  struct TALER_PaytoHashP reserve_h_payto;
-
-  wc->now = GNUNET_TIME_timestamp_get ();
-  /* Do AML check: compute total merged amount and check
-     against applicable AML threshold */
-  {
-    char *reserve_payto;
-
-    reserve_payto = TALER_reserve_make_payto (TEH_base_url,
-                                              wc->reserve_pub);
-    TALER_payto_hash (reserve_payto,
-                      &reserve_h_payto);
-    GNUNET_free (reserve_payto);
-  }
-  {
-    struct TALER_Amount merge_amount;
-    struct TALER_Amount threshold;
-    struct GNUNET_TIME_Absolute now_minus_one_month;
-
-    now_minus_one_month
-      = GNUNET_TIME_absolute_subtract (wc->now.abs_time,
-                                       GNUNET_TIME_UNIT_MONTHS);
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_set_zero (TEH_currency,
-                                          &merge_amount));
-    qs = TEH_plugin->select_merge_amounts_for_kyc_check (TEH_plugin->cls,
-                                                         &reserve_h_payto,
-                                                         now_minus_one_month,
-                                                         &aml_amount_cb,
-                                                         &merge_amount);
-    if (qs < 0)
-    {
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               
"select_merge_amounts_for_kyc_check");
-      return qs;
-    }
-    qs = TEH_plugin->select_aml_threshold (TEH_plugin->cls,
-                                           &reserve_h_payto,
-                                           &wc->aml_decision,
-                                           &wc->kyc,
-                                           &threshold);
-    if (qs < 0)
-    {
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               "select_aml_threshold");
-      return qs;
-    }
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      threshold = TEH_aml_threshold; /* use default */
-      wc->aml_decision = TALER_AML_NORMAL;
-    }
-
-    switch (wc->aml_decision)
-    {
-    case TALER_AML_NORMAL:
-      if (0 >= TALER_amount_cmp (&merge_amount,
-                                 &threshold))
-      {
-        /* merge_amount <= threshold, continue withdraw below */
-        break;
-      }
-      wc->aml_decision = TALER_AML_PENDING;
-      qs = TEH_plugin->trigger_aml_process (TEH_plugin->cls,
-                                            &reserve_h_payto,
-                                            &merge_amount);
-      if (qs <= 0)
-      {
-        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-        if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-          *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                                 
MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                                 
TALER_EC_GENERIC_DB_STORE_FAILED,
-                                                 "trigger_aml_process");
-        return qs;
-      }
-      return qs;
-    case TALER_AML_PENDING:
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "AML already pending, doing nothing\n");
-      return qs;
-    case TALER_AML_FROZEN:
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Account frozen, doing nothing\n");
-      return qs;
-    }
-  }
 
-  /* Check if the money came from a wire transfer */
-  qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
-                                        wc->reserve_pub,
-                                        &wc->h_payto);
-  if (qs < 0)
-  {
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                             "reserves_get_origin");
+  qs = TEH_withdraw_kyc_check (&wc->kyc,
+                               connection,
+                               mhd_ret,
+                               wc->reserve_pub,
+                               &wc->batch_total,
+                               wc->now);
+  if ( (qs < 0) ||
+       (! wc->kyc.ok) )
     return qs;
-  }
-  /* If no results, reserve was created by merge, in which case no KYC check
-     is required as the merge already did that. */
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-  {
-    qs = TALER_KYCLOGIC_kyc_test_required (
-      TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
-      &wc->h_payto,
-      TEH_plugin->select_satisfied_kyc_processes,
-      TEH_plugin->cls,
-      &batch_withdraw_amount_cb,
-      wc,
-      &kyc_required);
-    if (qs < 0)
-    {
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               "kyc_test_required");
-      return qs;
-    }
-    if (NULL != kyc_required)
-    {
-      /* insert KYC requirement into DB! */
-      wc->kyc.ok = false;
-      qs = TEH_plugin->insert_kyc_requirement_for_account (
-        TEH_plugin->cls,
-        kyc_required,
-        &wc->h_payto,
-        wc->reserve_pub,
-        &wc->kyc.requirement_row);
-      GNUNET_free (kyc_required);
-      if (qs < 0)
-      {
-        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-        if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-          *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                                 
MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                                 
TALER_EC_GENERIC_DB_STORE_FAILED,
-                                                 
"insert_kyc_requirement_for_account");
-      }
-      return qs;
-    }
-  }
-  wc->kyc.ok = true;
-
   qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,
                                       wc->now,
                                       wc->reserve_pub,
@@ -669,6 +434,13 @@ prepare_transaction (const struct TEH_RequestContext *rc,
     }
   }
   /* return final positive response */
+  if (! wc->kyc.ok)
+  {
+    /* KYC required */
+    return TEH_RESPONSE_reply_kyc_required (rc->connection,
+                                            NULL, /* FIXME! */
+                                            &wc->kyc);
+  }
   return generate_reply_success (rc,
                                  wc);
 }
@@ -877,7 +649,8 @@ TEH_handler_batch_withdraw (struct TEH_RequestContext *rc,
 {
   struct BatchWithdrawContext wc = {
     .reserve_pub = reserve_pub,
-    .rc = rc
+    .rc = rc,
+    .now = GNUNET_TIME_timestamp_get ()
   };
   const json_t *planchets;
   struct GNUNET_JSON_Specification spec[] = {
diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c 
b/src/exchange/taler-exchange-httpd_deposits_get.c
index 0850d19eb..934c638b7 100644
--- a/src/exchange/taler-exchange-httpd_deposits_get.c
+++ b/src/exchange/taler-exchange-httpd_deposits_get.c
@@ -122,11 +122,6 @@ struct DepositWtidContext
    */
   struct TALER_EXCHANGEDB_KycStatus kyc;
 
-  /**
-   * AML status information for the receiving account.
-   */
-  enum TALER_AmlDecisionState aml_decision;
-
   /**
    * Set to #GNUNET_YES by #handle_wtid if the wire transfer is still pending
    * (and the above were not set).
@@ -377,7 +372,8 @@ handle_track_transaction_request (
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                                        TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
-                                       "wire fees exceed aggregate in 
database");
+                                       "wire fees exceed aggregate in 
database")
+    ;
   if (GNUNET_YES == ctx->pending)
   {
     if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) &&
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 8993ea50f..b3825c9c2 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -353,31 +353,6 @@ TEH_RESPONSE_reply_kyc_required (struct MHD_Connection 
*connection,
 }
 
 
-MHD_RESULT
-TEH_RESPONSE_reply_aml_blocked (struct MHD_Connection *connection,
-                                enum TALER_AmlDecisionState status)
-{
-  enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
-
-  switch (status)
-  {
-  case TALER_AML_NORMAL:
-    GNUNET_break (0);
-    return MHD_NO;
-  case TALER_AML_PENDING:
-    ec = TALER_EC_EXCHANGE_GENERIC_AML_PENDING;
-    break;
-  case TALER_AML_FROZEN:
-    ec = TALER_EC_EXCHANGE_GENERIC_AML_FROZEN;
-    break;
-  }
-  return TALER_MHD_REPLY_JSON_PACK (
-    connection,
-    MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
-    TALER_JSON_pack_ec (ec));
-}
-
-
 MHD_RESULT
 TEH_RESPONSE_reply_not_modified (
   struct MHD_Connection *connection,
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index 24b24621f..77ca9d557 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -97,19 +97,6 @@ TEH_RESPONSE_reply_kyc_required (struct MHD_Connection 
*connection,
                                  const struct TALER_EXCHANGEDB_KycStatus *kyc);
 
 
-/**
- * Send information that an AML process is blocking
- * the operation right now.
- *
- * @param connection connection to the client
- * @param status current AML status
- * @return MHD result code
- */
-MHD_RESULT
-TEH_RESPONSE_reply_aml_blocked (struct MHD_Connection *connection,
-                                enum TALER_AmlDecisionState status);
-
-
 /**
  * Send assertion that the given denomination key hash
  * is not usable (typically expired) at this time.
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
new file mode 100644
index 000000000..f54235bde
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -0,0 +1,211 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2024 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  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 Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General
+  Public License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_batch-withdraw.c
+ * @brief Handle /reserves/$RESERVE_PUB/batch-withdraw requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler-exchange-httpd.h"
+#include "taler_json_lib.h"
+#include "taler_kyclogic_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_withdraw.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler_util.h"
+
+
+/**
+ * Closure for #withdraw_amount_cb().
+ */
+struct WithdrawContext
+{
+  /**
+   * Total amount being withdrawn now.
+   */
+  const struct TALER_Amount *withdraw_total;
+
+  /**
+   * Current time.
+   */
+  struct GNUNET_TIME_Timestamp now;
+
+  /**
+   * Account we are checking against.
+   */
+  struct TALER_PaytoHashP h_payto;
+};
+
+
+/**
+ * Function called to iterate over KYC-relevant transaction amounts for a
+ * particular time range. Called within a database transaction, so must
+ * not start a new one.
+ *
+ * @param cls closure, identifies the event type and account to iterate
+ *        over events for
+ * @param limit maximum time-range for which events should be fetched
+ *        (timestamp in the past)
+ * @param cb function to call on each event found, events must be returned
+ *        in reverse chronological order
+ * @param cb_cls closure for @a cb, of type struct AgeWithdrawContext
+ */
+static void
+withdraw_amount_cb (
+  void *cls,
+  struct GNUNET_TIME_Absolute limit,
+  TALER_EXCHANGEDB_KycAmountCallback cb,
+  void *cb_cls)
+{
+  struct WithdrawContext *wc = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Signaling amount %s for KYC check during age-withdrawal\n",
+              TALER_amount2s (wc->withdraw_total));
+  if (GNUNET_OK !=
+      cb (cb_cls,
+          wc->withdraw_total,
+          wc->now.abs_time))
+    return;
+  qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (TEH_plugin->cls,
+                                                          &wc->h_payto,
+                                                          limit,
+                                                          cb,
+                                                          cb_cls);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Got %d additional transactions for this age-withdrawal and 
limit %llu\n",
+              qs,
+              (unsigned long long) limit.abs_value_us);
+  GNUNET_break (qs >= 0);
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_withdraw_kyc_check (
+  struct TALER_EXCHANGEDB_KycStatus *kyc,
+  struct MHD_Connection *connection,
+  MHD_RESULT *mhd_ret,
+  const struct TALER_ReservePublicKeyP *reserve_pub,
+  const struct TALER_Amount *withdraw_total,
+  struct GNUNET_TIME_Timestamp now
+  )
+{
+  enum GNUNET_DB_QueryStatus qs;
+  struct WithdrawContext wc = {
+    .withdraw_total = withdraw_total,
+    .now = now
+  };
+
+  /* Check if the money came from a wire transfer */
+  qs = TEH_plugin->reserves_get_origin (
+    TEH_plugin->cls,
+    reserve_pub,
+    &wc.h_payto);
+  if (qs < 0)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                             "reserves_get_origin");
+    return qs;
+  }
+
+  /* If _no_ results, reserve was created by merge,
+     in which case no KYC check is required as the
+     merge already did that. */
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  {
+    json_t *jrules;
+    struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL;
+    struct TALER_KYCLOGIC_KycRule *requirement;
+
+    qs = TEH_plugin->get_kyc_rules (TEH_plugin->cls,
+                                    &wc.h_payto,
+                                    &jrules);
+    if (qs < 0)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      return qs;
+    }
+    if (qs > 0)
+    {
+      lrs = TALER_KYCLOGIC_rules_parse (jrules);
+      GNUNET_break (NULL != lrs);
+      /* Fall back to default rules on parse error! */
+      json_decref (jrules);
+    }
+    qs = TALER_KYCLOGIC_kyc_test_required (
+      TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW,
+      &wc.h_payto,
+      lrs,
+      &withdraw_amount_cb,
+      &wc,
+      &requirement);
+    if (qs < 0)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      TALER_KYCLOGIC_rules_free (lrs);
+      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+        *mhd_ret = TALER_MHD_reply_with_ec (connection,
+                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                            "kyc_test_required");
+      return qs;
+    }
+
+    if (NULL != requirement)
+    {
+      json_t *jrule;
+      union TALER_AccountPublicKeyP account_pub;
+
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "KYC requirement is %s\n",
+                  TALER_KYCLOGIC_rule2s (requirement));
+      jrule = TALER_KYCLOGIC_rule2j (requirement);
+      account_pub.reserve_pub
+        = *reserve_pub;
+      kyc->ok = false;
+      qs = TEH_plugin->trigger_kyc_rule_for_account (
+        TEH_plugin->cls,
+        &wc.h_payto,
+        &account_pub,
+        jrule,
+        TALER_KYCLOGIC_rule2priority (requirement),
+        &kyc->requirement_row);
+      TALER_KYCLOGIC_rules_free (lrs);
+      json_decref (jrule);
+      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      {
+        GNUNET_break (0);
+        *mhd_ret = TALER_MHD_reply_with_ec (connection,
+                                            TALER_EC_GENERIC_DB_STORE_FAILED,
+                                            "trigger_kyc_rule_for_account");
+      }
+      return qs;
+    }
+    TALER_KYCLOGIC_rules_free (lrs);
+  }
+  kyc->ok = true;
+  return qs;
+}
diff --git a/src/exchange/taler-exchange-httpd_withdraw.h 
b/src/exchange/taler-exchange-httpd_withdraw.h
new file mode 100644
index 000000000..1391735c5
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_withdraw.h
@@ -0,0 +1,51 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  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 Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_withdraw.h
+ * @brief common code for withdraw requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_WITHDRAW_H
+#define TALER_EXCHANGE_HTTPD_WITHDRAW_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Do legitimization check for withdrawing @a withdraw_total
+ * from @a reserve_pub at time @a now.
+ *
+ * @param[out] kyc set to kyc status
+ * @param[in,out] connection used to return hard errors
+ * @param[out] mhd_ret set if errors were returned
+ *     (only on hard error)
+ * @param reserve_pub reserve from which we withdraw
+ * @param withdraw_total how much are being withdrawn
+ * @param now current time
+ * @return transaction status, error will have been
+ *   queued if transaction status is set to hard error
+ */
+enum GNUNET_DB_QueryStatus
+TEH_withdraw_kyc_check (
+  struct TALER_EXCHANGEDB_KycStatus *kyc,
+  struct MHD_Connection *connection,
+  MHD_RESULT *mhd_ret,
+  const struct TALER_ReservePublicKeyP *reserve_pub,
+  const struct TALER_Amount *withdraw_total,
+  struct GNUNET_TIME_Timestamp now);
+
+#endif
diff --git a/src/exchangedb/pg_get_kyc_rules.c 
b/src/exchangedb/pg_get_kyc_rules.c
index 0364470b7..52d69b6b3 100644
--- a/src/exchangedb/pg_get_kyc_rules.c
+++ b/src/exchangedb/pg_get_kyc_rules.c
@@ -30,19 +30,20 @@ enum GNUNET_DB_QueryStatus
 TEH_PG_get_kyc_rules (
   void *cls,
   const struct TALER_PaytoHashP *h_payto,
-  struct GNUNET_TIME_Timestamp *expiration_time,
   json_t **jrules)
 {
   struct PostgresClosure *pg = cls;
+  struct GNUNET_TIME_Timestamp now
+    = GNUNET_TIME_timestamp_get ();
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (h_payto),
+    GNUNET_PQ_query_param_timestamp ("now",
+                                     &now),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_json ("jnew_rules",
                                 jrules),
-    GNUNET_PQ_result_spec_timestamp ("expiration_time",
-                                     expiration_time),
     GNUNET_PQ_result_spec_end
   };
 
@@ -50,9 +51,9 @@ TEH_PG_get_kyc_rules (
            "get_kyc_rules",
            "SELECT"
            "  jnew_rules"
-           " ,expiration_time"
            "  FROM legitimization_outcomes"
            " WHERE h_payto=$1"
+           "   AND expiration_time >= $2"
            "   AND is_active;");
   return GNUNET_PQ_eval_prepared_singleton_select (
     pg->conn,
diff --git a/src/exchangedb/pg_get_kyc_rules.h 
b/src/exchangedb/pg_get_kyc_rules.h
index 8d43b9546..9b0e2d68d 100644
--- a/src/exchangedb/pg_get_kyc_rules.h
+++ b/src/exchangedb/pg_get_kyc_rules.h
@@ -31,7 +31,6 @@
  *
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param h_payto account identifier
- * @param[out] expiration_time when do the @a jrules expire
  * @param[out] jrules set to the active KYC rules for the
  *    given account, set to NULL if no custom rules are active
  * @return transaction status code
@@ -40,7 +39,6 @@ enum GNUNET_DB_QueryStatus
 TEH_PG_get_kyc_rules (
   void *cls,
   const struct TALER_PaytoHashP *h_payto,
-  struct GNUNET_TIME_Timestamp *expiration_time,
   json_t **jrules);
 
 #endif
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 3e82e3bb5..cc1c39d5f 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -6866,7 +6866,6 @@ struct TALER_EXCHANGEDB_Plugin
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param h_payto account identifier
-   * @param[out] expiration_time when do the @a jrules expire
    * @param[out] jrules set to the active KYC rules for the
    *    given account, set to NULL if no custom rules are active
    * @return transaction status code
@@ -6875,7 +6874,6 @@ struct TALER_EXCHANGEDB_Plugin
     (*get_kyc_rules)(
     void *cls,
     const struct TALER_PaytoHashP *h_payto,
-    struct GNUNET_TIME_Timestamp *expiration_time,
     json_t **jrules);
 
 
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
index 08ba424cf..168415b64 100644
--- a/src/include/taler_kyclogic_lib.h
+++ b/src/include/taler_kyclogic_lib.h
@@ -222,6 +222,7 @@ TALER_KYCLOGIC_rule2j (struct TALER_KYCLOGIC_KycRule *r);
 uint32_t
 TALER_KYCLOGIC_rule2priority (struct TALER_KYCLOGIC_KycRule *r);
 
+
 /**
  * Iterate over all thresholds that are applicable to a particular type of @a
  * event under exposed global rules.
@@ -258,8 +259,9 @@ TALER_KYCLOGIC_is_satisfiable (
  * FIXME: we probably want to instead set up the logic
  * with the context instead of just returning it here!
  *
- * @param requirements space-separated list of required checks
- * @param ut type of the entity performing the check
+ * @param lrs rule set
+ * @param kyc_rule rule that was triggered
+ * @param measure_name selected measure
  * @param[out] plugin set to the KYC logic API
  * @param[out] pd set to the specific operation context
  * @param[out] configuration_section set to the name of the KYC logic 
configuration section * @return #GNUNET_OK on success
@@ -311,4 +313,39 @@ TALER_KYCLOGIC_lookup_checks (
   char ***provided_checks);
 
 
+/**
+ * Function called with the provider details and
+ * associated plugin closures for matching logics.
+ *
+ * @param cls closure
+ * @param pd provider details of a matching logic
+ * @param plugin_cls closure of the plugin
+ * @return #GNUNET_OK to continue to iterate
+ */
+typedef enum GNUNET_GenericReturnValue
+(*TALER_KYCLOGIC_DetailsCallback)(
+  void *cls,
+  const struct TALER_KYCLOGIC_ProviderDetails *pd,
+  void *plugin_cls);
+
+
+/**
+ * Call @a cb for all logics with name @a logic_name,
+ * providing the plugin closure and the @a pd configurations.
+ * Obtain the provider logic for a given set of @a lrs
+ * and a specific @a kyc_rule from @a lrs that was
+ * triggered and the choosen @a measure_name from the
+ * list of measures of that @a kyc_rule.
+  *
+ * @param logic_name name of the logic to match
+ * @param cb function to call on matching results
+ * @param cb_cls closure for @a cb
+ */
+void
+TALER_KYCLOGIC_kyc_get_details (
+  const char *logic_name,
+  TALER_KYCLOGIC_DetailsCallback cb,
+  void *cb_cls);
+
+
 #endif
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
index 9fb024a57..7c1984f28 100644
--- a/src/kyclogic/kyclogic_api.c
+++ b/src/kyclogic/kyclogic_api.c
@@ -215,6 +215,11 @@ struct TALER_KYCLOGIC_KycRule
    */
   unsigned int num_measures;
 
+  /**
+   * Display priority for this rule.
+   */
+  uint32_t display_priority;
+
   /**
    * What operation type is this rule for?
    */
@@ -279,27 +284,24 @@ struct TALER_KYCLOGIC_LegitimizationRuleSet
    */
   unsigned int num_custom_measures;
 
-  /**
-   * Display priority for this rule.
-   */
-  uint32_t display_priority;
 };
 
 
-enum GNUNET_GenericReturnValue
-TALER_KYCLOGIC_rules_parse (
-  const json_t *jrules,
-  struct TALER_KYCLOGIC_KycRuleSet *rules)
+struct TALER_KYCLOGIC_LegitimizationRuleSet *
+TALER_KYCLOGIC_rules_parse (const json_t *jrules)
 {
   // FIXME!
-  return GNUNET_SYSERR;
+  GNUNET_break (0);
+  return NULL;
 }
 
 
 void
-TALER_KYCLOGIC_rules_free (struct TALER_KYCLOGIC_KycRuleSet *krs)
+TALER_KYCLOGIC_rules_free (struct TALER_KYCLOGIC_LegitimizationRuleSet *krs)
 {
-  // fIXME
+  // FIXME
+  GNUNET_break (0);
+  GNUNET_free (krs);
 }
 
 
@@ -310,6 +312,15 @@ TALER_KYCLOGIC_rule2s (struct TALER_KYCLOGIC_KycRule *r)
 }
 
 
+json_t *
+TALER_KYCLOGIC_rule2j (struct TALER_KYCLOGIC_KycRule *r)
+{
+  // FIXME!
+  GNUNET_break (0);
+  return NULL;
+}
+
+
 uint32_t
 TALER_KYCLOGIC_rule2priority (struct TALER_KYCLOGIC_KycRule *r)
 {
@@ -401,7 +412,7 @@ static unsigned int num_kyc_checks;
 /**
  * Rules that apply if we do not have an AMLA record.
  */
-static struct TALER_KYCLOGIC_KycRuleSet default_rules;
+static struct TALER_KYCLOGIC_LegitimizationRuleSet default_rules;
 
 /**
  * Array of available AML programs.
@@ -1148,8 +1159,8 @@ add_rule (const struct GNUNET_CONFIGURATION_Handle *cfg,
                 &kt->next_measures,
                 &kt->num_measures);
     GNUNET_free (measures);
-    GNUNET_array_append (kyc_rules,
-                         num_kyc_rules,
+    GNUNET_array_append (default_rules.kyc_rules,
+                         default_rules.num_kyc_rules,
                          kt);
   }
   return GNUNET_OK;
@@ -1370,9 +1381,9 @@ TALER_KYCLOGIC_kyc_init (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
     return GNUNET_SYSERR;
   }
 
-  if (0 != num_kyc_rules)
-    qsort (kyc_rules,
-           num_kyc_rules,
+  if (0 != default_rules.num_kyc_rules)
+    qsort (default_rules.kyc_rules,
+           default_rules.num_kyc_rules,
            sizeof (struct TALER_KYCLOGIC_KycRule *),
            &sort_by_timeframe);
   // FIXME: add configuration sanity checking!
@@ -1383,9 +1394,10 @@ TALER_KYCLOGIC_kyc_init (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
 void
 TALER_KYCLOGIC_kyc_done (void)
 {
-  for (unsigned int i = 0; i<num_kyc_rules; i++)
+  for (unsigned int i = 0; i<default_rules.num_kyc_rules; i++)
   {
-    struct TALER_KYCLOGIC_KycRule *kt = kyc_rules[i];
+    struct TALER_KYCLOGIC_KycRule *kt
+      = default_rules.kyc_rules[i];
 
     for (unsigned int j = 0; j<kt->num_measures; j++)
       GNUNET_free (kt->next_measures[j]);
@@ -1395,8 +1407,8 @@ TALER_KYCLOGIC_kyc_done (void)
     GNUNET_free (kt->rule_name);
     GNUNET_free (kt);
   }
-  GNUNET_array_grow (kyc_rules,
-                     num_kyc_rules,
+  GNUNET_array_grow (default_rules.kyc_rules,
+                     default_rules.num_kyc_rules,
                      0);
   for (unsigned int i = 0; i<num_kyc_providers; i++)
   {
@@ -1482,240 +1494,161 @@ TALER_KYCLOGIC_kyc_done (void)
 }
 
 
-/* end of kyclogic_api.c */
-
-#if 0
-// FIXME from here...
-
-
-/**
- * Closure for the #eval_trigger().
- */
-struct ThresholdTestContext
+enum GNUNET_GenericReturnValue
+TALER_KYCLOGIC_requirements_to_logic (
+  const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs,
+  const struct TALER_KYCLOGIC_KycRule *kyc_rule,
+  const char *measure_name,
+  struct TALER_KYCLOGIC_Plugin **plugin,
+  struct TALER_KYCLOGIC_ProviderDetails **pd,
+  const char **configuration_section)
 {
-  /**
-   * Total amount so far.
-   */
-  struct TALER_Amount total;
-
-  /**
-   * Trigger event to evaluate triggers of.
-   */
-  enum TALER_KYCLOGIC_KycTriggerEvent event;
-
-  /**
-   * Offset in the triggers array where we need to start
-   * checking for triggers. All trigges below this
-   * offset were already hit.
-   */
-  unsigned int start;
-
-  /**
-   * Array of checks needed so far.
-   */
-  struct TALER_KYCLOGIC_KycCheck **needed;
-
-  /**
-   * Pointer to number of entries used in @a needed.
-   */
-  unsigned int *needed_cnt;
-
-  /**
-   * Has @e total been initialized yet?
-   */
-  bool have_total;
-};
+#if FIXME
+  struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
+  unsigned int needed_cnt = 0;
+  unsigned long long min_cost = ULLONG_MAX;
+  unsigned int max_checks = 0;
+  const struct TALER_KYCLOGIC_KycProvider *kp_best = NULL;
 
+  if (NULL == requirements)
+    return GNUNET_NO;
+  {
+    char *req = GNUNET_strdup (requirements);
 
-/**
- * Function called on each @a amount that was found to
- * be relevant for a KYC check.
- *
- * @param cls closure to allow the KYC module to
- *        total up amounts and evaluate rules
- * @param amount encountered transaction amount
- * @param date when was the amount encountered
- * @return #GNUNET_OK to continue to iterate,
- *         #GNUNET_NO to abort iteration
- *         #GNUNET_SYSERR on internal error (also abort itaration)
- */
-static enum GNUNET_GenericReturnValue
-eval_trigger (void *cls,
-              const struct TALER_Amount *amount,
-              struct GNUNET_TIME_Absolute date)
-{
-  struct ThresholdTestContext *ttc = cls;
-  struct GNUNET_TIME_Relative duration;
-  bool bump = true;
+    for (const char *tok = strtok (req, " ");
+         NULL != tok;
+         tok = strtok (NULL, " "))
+      needed[needed_cnt++] = add_check (tok);
+    GNUNET_free (req);
+  }
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "KYC check with new amount %s\n",
-              TALER_amount2s (amount));
-  duration = GNUNET_TIME_absolute_get_duration (date);
-  if (ttc->have_total)
+  /* Count maximum number of remaining checks covered by any
+     provider */
+  for (unsigned int i = 0; i<num_kyc_providers; i++)
   {
-    if (0 >
-        TALER_amount_add (&ttc->total,
-                          &ttc->total,
-                          amount))
+    const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
+    unsigned int matched = 0;
+
+    if (kp->user_type != ut)
+      continue;
+    for (unsigned int j = 0; j<kp->num_checks; j++)
     {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
+      const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
+
+      for (unsigned int k = 0; k<needed_cnt; k++)
+        if (kc == needed[k])
+        {
+          matched++;
+          break;
+        }
     }
+    max_checks = GNUNET_MAX (max_checks,
+                             matched);
   }
-  else
-  {
-    ttc->total = *amount;
-    ttc->have_total = true;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "KYC check: new total is %s\n",
-              TALER_amount2s (&ttc->total));
-  for (unsigned int i = ttc->start; i<num_kyc_triggers; i++)
+  if (0 == max_checks)
+    return GNUNET_SYSERR;
+
+  /* Find min-cost provider covering max_checks. */
+  for (unsigned int i = 0; i<num_kyc_providers; i++)
   {
-    const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
+    const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
+    unsigned int matched = 0;
 
-    if (ttc->event != kt->trigger)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "KYC check #%u: trigger type does not match\n",
-                  i);
+    if (kp->user_type != ut)
       continue;
-    }
-    duration = GNUNET_TIME_relative_max (duration,
-                                         kt->timeframe);
-    if (GNUNET_TIME_relative_cmp (kt->timeframe,
-                                  >,
-                                  duration))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "KYC check #%u: amount is beyond time limit\n",
-                  i);
-      if (bump)
-        ttc->start = i;
-      return GNUNET_OK;
-    }
-    if (-1 ==
-        TALER_amount_cmp (&ttc->total,
-                          &kt->threshold))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "KYC check #%u: amount is below threshold\n",
-                  i);
-      if (bump)
-        ttc->start = i;
-      bump = false;
-      continue; /* amount too low to trigger */
-    }
-    /* add check to list of required checks, unless
-       already present... */
-    for (unsigned int j = 0; j<kt->num_checks; j++)
+    for (unsigned int j = 0; j<kp->num_checks; j++)
     {
-      struct TALER_KYCLOGIC_KycCheck *rc = kt->required_checks[j];
-      bool found = false;
+      const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
 
-      for (unsigned int k = 0; k<*ttc->needed_cnt; k++)
-        if (ttc->needed[k] == rc)
+      for (unsigned int k = 0; k<needed_cnt; k++)
+        if (kc == needed[k])
         {
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "KYC rule #%u already listed\n",
-                      j);
-          found = true;
+          matched++;
           break;
         }
-      if (! found)
-      {
-        ttc->needed[*ttc->needed_cnt] = rc;
-        (*ttc->needed_cnt)++;
-      }
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "KYC check #%u (%s) is applicable, %u checks needed so far\n",
-                i,
-                ttc->needed[(*ttc->needed_cnt) - 1]->name,
-                *ttc->needed_cnt);
+    if ( (max_checks == matched) &&
+         (kp->cost < min_cost) )
+    {
+      min_cost = kp->cost;
+      kp_best = kp;
+    }
   }
-  if (bump)
-    return GNUNET_NO; /* we hit all possible triggers! */
+  GNUNET_assert (NULL != kp_best);
+  *plugin = kp_best->logic;
+  *pd = kp_best->pd;
+  *configuration_section = kp_best->provider_section_name;
   return GNUNET_OK;
+#else
+  GNUNET_break (0);
+  return GNUNET_SYSERR;
+#endif
 }
 
 
-/**
- * Closure for the #remove_satisfied().
- */
-struct RemoveContext
+enum GNUNET_GenericReturnValue
+TALER_KYCLOGIC_lookup_logic (
+  const char *name,
+  struct TALER_KYCLOGIC_Plugin **plugin,
+  struct TALER_KYCLOGIC_ProviderDetails **pd,
+  const char **provider_section)
 {
+#if FIXME
+  for (unsigned int i = 0; i<num_kyc_providers; i++)
+  {
+    struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
 
-  /**
-   * Array of checks needed so far.
-   */
-  struct TALER_KYCLOGIC_KycCheck **needed;
+    if (0 !=
+        strcasecmp (name,
+                    kp->provider_section_name))
+      continue;
+    *plugin = kp->logic;
+    *pd = kp->pd;
+    *provider_section = kp->provider_section_name;
+    return GNUNET_OK;
+  }
+  for (unsigned int i = 0; i<num_kyc_logics; i++)
+  {
+    struct TALER_KYCLOGIC_Plugin *logic = kyc_logics[i];
 
-  /**
-   * Pointer to number of entries used in @a needed.
-   */
-  unsigned int *needed_cnt;
-
-  /**
-   * Object with information about collected KYC data.
-   */
-  json_t *kyc_details;
-};
+    if (0 !=
+        strcasecmp (logic->name,
+                    name))
+      continue;
+    *plugin = logic;
+    *pd = NULL;
+    *provider_section = NULL;
+    return GNUNET_OK;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+              "Provider `%s' unknown\n",
+              name);
+#else
+  GNUNET_break (0);
+#endif
+  return GNUNET_SYSERR;
+}
 
 
-/**
- * Remove all checks satisfied by @a provider_name from
- * our list of checks.
- *
- * @param cls a `struct RemoveContext`
- * @param provider_name section name of provider that was already run 
previously
- */
-static void
-remove_satisfied (void *cls,
-                  const char *provider_name)
+void
+TALER_KYCLOGIC_kyc_get_details (
+  const char *logic_name,
+  TALER_KYCLOGIC_DetailsCallback cb,
+  void *cb_cls)
 {
-  struct RemoveContext *rc = cls;
-
   for (unsigned int i = 0; i<num_kyc_providers; i++)
   {
-    const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
+    struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
 
-    if (0 != strcasecmp (provider_name,
-                         kp->provider_section_name))
+    if (0 !=
+        strcmp (kp->logic->name,
+                logic_name))
       continue;
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Provider `%s' satisfied\n",
-                provider_name);
-    for (unsigned int j = 0; j<kp->num_checks; j++)
-    {
-      const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
-
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Provider satisfies check `%s'\n",
-                  kc->name);
-      if (NULL != rc->kyc_details)
-      {
-        GNUNET_assert (0 ==
-                       json_object_set_new (
-                         rc->kyc_details,
-                         kc->name,
-                         json_object ()));
-      }
-      for (unsigned int k = 0; k<*rc->needed_cnt; k++)
-        if (kc == rc->needed[k])
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Removing check `%s' from list\n",
-                      kc->name);
-          rc->needed[k] = rc->needed[*rc->needed_cnt - 1];
-          (*rc->needed_cnt)--;
-          if (0 == *rc->needed_cnt)
-            return; /* for sure finished */
-          break;
-        }
-    }
-    break;
+    if (GNUNET_OK !=
+        cb (cb_cls,
+            kp->pd,
+            kp->logic->cls))
+      return;
   }
 }
 
@@ -1724,12 +1657,12 @@ enum GNUNET_DB_QueryStatus
 TALER_KYCLOGIC_kyc_test_required (
   enum TALER_KYCLOGIC_KycTriggerEvent event,
   const struct TALER_PaytoHashP *h_payto,
-  TALER_KYCLOGIC_KycSatisfiedIterator ki,
-  void *ki_cls,
+  const struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs,
   TALER_KYCLOGIC_KycAmountIterator ai,
   void *ai_cls,
-  char **required)
+  struct TALER_KYCLOGIC_KycRule **triggered_rule)
 {
+#if FIXME
   struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
   unsigned int needed_cnt = 0;
   char *ret;
@@ -1865,28 +1798,247 @@ TALER_KYCLOGIC_kyc_test_required (
   }
   *required = ret;
   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+#else
+  GNUNET_break (0);
+  return GNUNET_DB_STATUS_HARD_ERROR;
+#endif
 }
 
 
-void
-TALER_KYCLOGIC_kyc_get_details (
-  const char *logic_name,
-  TALER_KYCLOGIC_DetailsCallback cb,
-  void *cb_cls)
+/* end of kyclogic_api.c */
+
+#if 0
+// FIXME from here...
+
+
+/**
+ * Closure for the #eval_trigger().
+ */
+struct ThresholdTestContext
+{
+  /**
+   * Total amount so far.
+   */
+  struct TALER_Amount total;
+
+  /**
+   * Trigger event to evaluate triggers of.
+   */
+  enum TALER_KYCLOGIC_KycTriggerEvent event;
+
+  /**
+   * Offset in the triggers array where we need to start
+   * checking for triggers. All trigges below this
+   * offset were already hit.
+   */
+  unsigned int start;
+
+  /**
+   * Array of checks needed so far.
+   */
+  struct TALER_KYCLOGIC_KycCheck **needed;
+
+  /**
+   * Pointer to number of entries used in @a needed.
+   */
+  unsigned int *needed_cnt;
+
+  /**
+   * Has @e total been initialized yet?
+   */
+  bool have_total;
+};
+
+
+/**
+ * Function called on each @a amount that was found to
+ * be relevant for a KYC check.
+ *
+ * @param cls closure to allow the KYC module to
+ *        total up amounts and evaluate rules
+ * @param amount encountered transaction amount
+ * @param date when was the amount encountered
+ * @return #GNUNET_OK to continue to iterate,
+ *         #GNUNET_NO to abort iteration
+ *         #GNUNET_SYSERR on internal error (also abort itaration)
+ */
+static enum GNUNET_GenericReturnValue
+eval_trigger (void *cls,
+              const struct TALER_Amount *amount,
+              struct GNUNET_TIME_Absolute date)
 {
+  struct ThresholdTestContext *ttc = cls;
+  struct GNUNET_TIME_Relative duration;
+  bool bump = true;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "KYC check with new amount %s\n",
+              TALER_amount2s (amount));
+  duration = GNUNET_TIME_absolute_get_duration (date);
+  if (ttc->have_total)
+  {
+    if (0 >
+        TALER_amount_add (&ttc->total,
+                          &ttc->total,
+                          amount))
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+  }
+  else
+  {
+    ttc->total = *amount;
+    ttc->have_total = true;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "KYC check: new total is %s\n",
+              TALER_amount2s (&ttc->total));
+  for (unsigned int i = ttc->start; i<num_kyc_triggers; i++)
+  {
+    const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
+
+    if (ttc->event != kt->trigger)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "KYC check #%u: trigger type does not match\n",
+                  i);
+      continue;
+    }
+    duration = GNUNET_TIME_relative_max (duration,
+                                         kt->timeframe);
+    if (GNUNET_TIME_relative_cmp (kt->timeframe,
+                                  >,
+                                  duration))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "KYC check #%u: amount is beyond time limit\n",
+                  i);
+      if (bump)
+        ttc->start = i;
+      return GNUNET_OK;
+    }
+    if (-1 ==
+        TALER_amount_cmp (&ttc->total,
+                          &kt->threshold))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "KYC check #%u: amount is below threshold\n",
+                  i);
+      if (bump)
+        ttc->start = i;
+      bump = false;
+      continue; /* amount too low to trigger */
+    }
+    /* add check to list of required checks, unless
+       already present... */
+    for (unsigned int j = 0; j<kt->num_checks; j++)
+    {
+      struct TALER_KYCLOGIC_KycCheck *rc = kt->required_checks[j];
+      bool found = false;
+
+      for (unsigned int k = 0; k<*ttc->needed_cnt; k++)
+        if (ttc->needed[k] == rc)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "KYC rule #%u already listed\n",
+                      j);
+          found = true;
+          break;
+        }
+      if (! found)
+      {
+        ttc->needed[*ttc->needed_cnt] = rc;
+        (*ttc->needed_cnt)++;
+      }
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "KYC check #%u (%s) is applicable, %u checks needed so far\n",
+                i,
+                ttc->needed[(*ttc->needed_cnt) - 1]->name,
+                *ttc->needed_cnt);
+  }
+  if (bump)
+    return GNUNET_NO; /* we hit all possible triggers! */
+  return GNUNET_OK;
+}
+
+
+/**
+ * Closure for the #remove_satisfied().
+ */
+struct RemoveContext
+{
+
+  /**
+   * Array of checks needed so far.
+   */
+  struct TALER_KYCLOGIC_KycCheck **needed;
+
+  /**
+   * Pointer to number of entries used in @a needed.
+   */
+  unsigned int *needed_cnt;
+
+  /**
+   * Object with information about collected KYC data.
+   */
+  json_t *kyc_details;
+};
+
+
+/**
+ * Remove all checks satisfied by @a provider_name from
+ * our list of checks.
+ *
+ * @param cls a `struct RemoveContext`
+ * @param provider_name section name of provider that was already run 
previously
+ */
+static void
+remove_satisfied (void *cls,
+                  const char *provider_name)
+{
+  struct RemoveContext *rc = cls;
+
   for (unsigned int i = 0; i<num_kyc_providers; i++)
   {
-    struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
+    const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
 
-    if (0 !=
-        strcmp (kp->logic->name,
-                logic_name))
+    if (0 != strcasecmp (provider_name,
+                         kp->provider_section_name))
       continue;
-    if (GNUNET_OK !=
-        cb (cb_cls,
-            kp->pd,
-            kp->logic->cls))
-      return;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Provider `%s' satisfied\n",
+                provider_name);
+    for (unsigned int j = 0; j<kp->num_checks; j++)
+    {
+      const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
+
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Provider satisfies check `%s'\n",
+                  kc->name);
+      if (NULL != rc->kyc_details)
+      {
+        GNUNET_assert (0 ==
+                       json_object_set_new (
+                         rc->kyc_details,
+                         kc->name,
+                         json_object ()));
+      }
+      for (unsigned int k = 0; k<*rc->needed_cnt; k++)
+        if (kc == rc->needed[k])
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "Removing check `%s' from list\n",
+                      kc->name);
+          rc->needed[k] = rc->needed[*rc->needed_cnt - 1];
+          (*rc->needed_cnt)--;
+          if (0 == *rc->needed_cnt)
+            return; /* for sure finished */
+          break;
+        }
+    }
+    break;
   }
 }
 
@@ -1982,132 +2134,6 @@ TALER_KYCLOGIC_check_satisfied (
 }
 
 
-enum GNUNET_GenericReturnValue
-TALER_KYCLOGIC_requirements_to_logic (
-  const char *requirements,
-  enum TALER_KYCLOGIC_KycUserType ut,
-  struct TALER_KYCLOGIC_Plugin **plugin,
-  struct TALER_KYCLOGIC_ProviderDetails **pd,
-  const char **configuration_section)
-{
-  struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
-  unsigned int needed_cnt = 0;
-  unsigned long long min_cost = ULLONG_MAX;
-  unsigned int max_checks = 0;
-  const struct TALER_KYCLOGIC_KycProvider *kp_best = NULL;
-
-  if (NULL == requirements)
-    return GNUNET_NO;
-  {
-    char *req = GNUNET_strdup (requirements);
-
-    for (const char *tok = strtok (req, " ");
-         NULL != tok;
-         tok = strtok (NULL, " "))
-      needed[needed_cnt++] = add_check (tok);
-    GNUNET_free (req);
-  }
-
-  /* Count maximum number of remaining checks covered by any
-     provider */
-  for (unsigned int i = 0; i<num_kyc_providers; i++)
-  {
-    const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
-    unsigned int matched = 0;
-
-    if (kp->user_type != ut)
-      continue;
-    for (unsigned int j = 0; j<kp->num_checks; j++)
-    {
-      const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
-
-      for (unsigned int k = 0; k<needed_cnt; k++)
-        if (kc == needed[k])
-        {
-          matched++;
-          break;
-        }
-    }
-    max_checks = GNUNET_MAX (max_checks,
-                             matched);
-  }
-  if (0 == max_checks)
-    return GNUNET_SYSERR;
-
-  /* Find min-cost provider covering max_checks. */
-  for (unsigned int i = 0; i<num_kyc_providers; i++)
-  {
-    const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
-    unsigned int matched = 0;
-
-    if (kp->user_type != ut)
-      continue;
-    for (unsigned int j = 0; j<kp->num_checks; j++)
-    {
-      const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
-
-      for (unsigned int k = 0; k<needed_cnt; k++)
-        if (kc == needed[k])
-        {
-          matched++;
-          break;
-        }
-    }
-    if ( (max_checks == matched) &&
-         (kp->cost < min_cost) )
-    {
-      min_cost = kp->cost;
-      kp_best = kp;
-    }
-  }
-  GNUNET_assert (NULL != kp_best);
-  *plugin = kp_best->logic;
-  *pd = kp_best->pd;
-  *configuration_section = kp_best->provider_section_name;
-  return GNUNET_OK;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_KYCLOGIC_lookup_logic (
-  const char *name,
-  struct TALER_KYCLOGIC_Plugin **plugin,
-  struct TALER_KYCLOGIC_ProviderDetails **pd,
-  const char **provider_section)
-{
-  for (unsigned int i = 0; i<num_kyc_providers; i++)
-  {
-    struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
-
-    if (0 !=
-        strcasecmp (name,
-                    kp->provider_section_name))
-      continue;
-    *plugin = kp->logic;
-    *pd = kp->pd;
-    *provider_section = kp->provider_section_name;
-    return GNUNET_OK;
-  }
-  for (unsigned int i = 0; i<num_kyc_logics; i++)
-  {
-    struct TALER_KYCLOGIC_Plugin *logic = kyc_logics[i];
-
-    if (0 !=
-        strcasecmp (logic->name,
-                    name))
-      continue;
-    *plugin = logic;
-    *pd = NULL;
-    *provider_section = NULL;
-    return GNUNET_OK;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-              "Provider `%s' unknown\n",
-              name);
-  return GNUNET_SYSERR;
-}
-
-
 void
 TALER_KYCLOGIC_kyc_iterate_thresholds (
   enum TALER_KYCLOGIC_KycTriggerEvent event,
diff --git a/src/kyclogic/taler-exchange-kyc-tester.c 
b/src/kyclogic/taler-exchange-kyc-tester.c
index e942ff7a8..629de5995 100644
--- a/src/kyclogic/taler-exchange-kyc-tester.c
+++ b/src/kyclogic/taler-exchange-kyc-tester.c
@@ -278,9 +278,9 @@ static int run_webservice;
 static int global_ret;
 
 /**
- * -r command-line flag.
+ * -m command-line flag.
  */
-static char *requirements;
+static char *measure;
 
 /**
  * Handle for ongoing initiation operation.
@@ -1466,19 +1466,21 @@ run (void *cls,
     return;
   }
   global_ret = EXIT_SUCCESS;
-  if (NULL != requirements)
+  if (NULL != measure)
   {
     struct TALER_KYCLOGIC_ProviderDetails *pd;
 
     if (GNUNET_OK !=
-        TALER_KYCLOGIC_requirements_to_logic (requirements,
+        TALER_KYCLOGIC_requirements_to_logic (NULL, /* FIXME! */
+                                              NULL, /* FIXME! */
+                                              measure,
                                               &ih_logic,
                                               &pd,
                                               &provider_section_name))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Could not initiate KYC for requirements `%s' (configuration 
error?)\n",
-                  requirements);
+                  "Could not initiate KYC for measure `%s' (configuration 
error?)\n",
+                  measure);
       global_ret = EXIT_NOTCONFIGURED;
       GNUNET_SCHEDULER_shutdown ();
       return;
@@ -1575,11 +1577,11 @@ main (int argc,
       "run the integrated HTTP service",
       &run_webservice),
     GNUNET_GETOPT_option_string (
-      'R',
-      "requirements",
-      "CHECKS",
-      "initiate KYC check for the given list of (space-separated) checks",
-      &requirements),
+      'm',
+      "measure",
+      "MEASURE_NAME",
+      "initiate KYC check for the selected measure",
+      &measure),
     GNUNET_GETOPT_option_string (
       'u',
       "user",

-- 
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]