exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

iterate_aml_decisions.c (11091B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2024 Taler Systems SA
      4 
      5    TALER is free software; you can redistribute it and/or modify it under the
      6    terms of the GNU General Public License as published by the Free Software
      7    Foundation; either version 3, or (at your option) any later version.
      8 
      9    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13    You should have received a copy of the GNU General Public License along with
     14    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 /**
     17  * @file exchangedb/iterate_aml_decisions.c
     18  * @brief Implementation of the iterate_aml_decisions function for Postgres
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/taler_pq_lib.h"
     22 #include "taler/taler_kyclogic_lib.h"
     23 #include "exchange-database/iterate_aml_decisions.h"
     24 #include "helper.h"
     25 
     26 
     27 /**
     28  * Hard upper bound on the number of records returned by a single
     29  * call, regardless of the limit requested by the client.
     30  */
     31 #define MAX_RECORDS 50000
     32 
     33 
     34 /**
     35  * Closure for #handle_aml_result.
     36  */
     37 struct AmlProcessResultContext
     38 {
     39   /**
     40    * Function to call on each result.
     41    */
     42   TALER_EXCHANGEDB_AmlDecisionCallback cb;
     43 
     44   /**
     45    * Closure for @e cb.
     46    */
     47   void *cb_cls;
     48 
     49   /**
     50    * Plugin context.
     51    */
     52   struct TALER_EXCHANGEDB_PostgresContext *pg;
     53 
     54   /**
     55    * Set to #GNUNET_SYSERR on serious errors.
     56    */
     57   enum GNUNET_GenericReturnValue status;
     58 };
     59 
     60 
     61 /**
     62  * Function to be called with the results of a SELECT statement
     63  * that has returned @a num_results results.  Helper function
     64  * for #TALER_EXCHANGEDB_iterate_aml_decisions().
     65  *
     66  * @param cls closure of type `struct AmlProcessResultContext *`
     67  * @param result the postgres result
     68  * @param num_results the number of results in @a result
     69  */
     70 static void
     71 handle_aml_result (void *cls,
     72                    PGresult *result,
     73                    unsigned int num_results)
     74 {
     75   struct AmlProcessResultContext *ctx = cls;
     76 
     77   for (unsigned int i = 0; i<num_results; i++)
     78   {
     79     struct TALER_NormalizedPaytoHashP h_payto;
     80     uint64_t rowid;
     81     uint64_t kyc_attributes_rowid;
     82     char *justification = NULL;
     83     char *new_measures = NULL;
     84     struct TALER_AmlOfficerPublicKeyP decider_pub;
     85     char *decider_name = NULL;
     86     bool no_decider_pub;
     87     bool no_kyc_attributes_rowid;
     88     struct GNUNET_TIME_Timestamp decision_time;
     89     struct GNUNET_TIME_Absolute expiration_time;
     90     json_t *jproperties = NULL;
     91     bool is_wallet;
     92     bool to_investigate;
     93     bool is_active;
     94     json_t *account_rules = NULL;
     95     json_t *default_rules = NULL;
     96     struct TALER_FullPayto payto;
     97     struct GNUNET_PQ_ResultSpec rs[] = {
     98       GNUNET_PQ_result_spec_uint64 ("outcome_serial_id",
     99                                     &rowid),
    100       GNUNET_PQ_result_spec_auto_from_type ("h_payto",
    101                                             &h_payto),
    102       GNUNET_PQ_result_spec_bool ("is_wallet",
    103                                   &is_wallet),
    104       GNUNET_PQ_result_spec_allow_null (
    105         GNUNET_PQ_result_spec_string ("justification",
    106                                       &justification),
    107         NULL),
    108       GNUNET_PQ_result_spec_allow_null (
    109         GNUNET_PQ_result_spec_string ("new_measure_name",
    110                                       &new_measures),
    111         NULL),
    112       GNUNET_PQ_result_spec_allow_null (
    113         GNUNET_PQ_result_spec_auto_from_type ("decider_pub",
    114                                               &decider_pub),
    115         &no_decider_pub),
    116       GNUNET_PQ_result_spec_allow_null (
    117         GNUNET_PQ_result_spec_string ("decider_name",
    118                                       &decider_name),
    119         NULL),
    120       GNUNET_PQ_result_spec_allow_null (
    121         GNUNET_PQ_result_spec_uint64 ("kyc_attributes_serial_id",
    122                                       &kyc_attributes_rowid),
    123         &no_kyc_attributes_rowid),
    124       GNUNET_PQ_result_spec_timestamp ("decision_time",
    125                                        &decision_time),
    126       GNUNET_PQ_result_spec_absolute_time ("expiration_time",
    127                                            &expiration_time),
    128       GNUNET_PQ_result_spec_allow_null (
    129         TALER_PQ_result_spec_json ("jproperties",
    130                                    &jproperties),
    131         NULL),
    132       GNUNET_PQ_result_spec_allow_null (
    133         TALER_PQ_result_spec_json ("jnew_rules",
    134                                    &account_rules),
    135         NULL),
    136       GNUNET_PQ_result_spec_bool ("to_investigate",
    137                                   &to_investigate),
    138       GNUNET_PQ_result_spec_bool ("is_active",
    139                                   &is_active),
    140       GNUNET_PQ_result_spec_string ("payto_uri",
    141                                     &payto.full_payto),
    142       GNUNET_PQ_result_spec_end
    143     };
    144 
    145     if (GNUNET_OK !=
    146         GNUNET_PQ_extract_result (result,
    147                                   rs,
    148                                   i))
    149     {
    150       GNUNET_break (0);
    151       ctx->status = GNUNET_SYSERR;
    152       return;
    153     }
    154     if (GNUNET_TIME_absolute_is_past (expiration_time))
    155       is_active = false;
    156     if (NULL == account_rules)
    157     {
    158       /* A NULL rule set means the account is on the exchange's default
    159          rules (see exchange_do_insert_successor_measure), not that it has
    160          no limits.  Report the defaults so the caller always gets a valid
    161          rule set. */
    162       default_rules = TALER_KYCLOGIC_get_default_legi_rules (is_wallet);
    163       account_rules = default_rules;
    164     }
    165     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    166                 "Returning AML decisions for `%s' (%s)\n",
    167                 TALER_B2S (&h_payto),
    168                 is_wallet
    169                 ? "wallet"
    170                 : "account");
    171     ctx->cb (ctx->cb_cls,
    172              rowid,
    173              justification,
    174              new_measures,
    175              no_decider_pub ? NULL : &decider_pub,
    176              decider_name,
    177              no_kyc_attributes_rowid ? NULL : &kyc_attributes_rowid,
    178              &h_payto,
    179              decision_time,
    180              expiration_time,
    181              jproperties,
    182              to_investigate,
    183              is_active,
    184              is_wallet,
    185              payto,
    186              account_rules);
    187     json_decref (default_rules);
    188     GNUNET_PQ_cleanup_result (rs);
    189   }
    190 }
    191 
    192 
    193 enum GNUNET_DB_QueryStatus
    194 TALER_EXCHANGEDB_iterate_aml_decisions (
    195   struct TALER_EXCHANGEDB_PostgresContext *pg,
    196   const struct TALER_NormalizedPaytoHashP *h_payto,
    197   enum TALER_EXCHANGE_YesNoAll investigation_only,
    198   enum TALER_EXCHANGE_YesNoAll active_only,
    199   uint64_t offset,
    200   int64_t limit,
    201   TALER_EXCHANGEDB_AmlDecisionCallback cb,
    202   void *cb_cls)
    203 {
    204   uint64_t ulimit = GNUNET_MIN ((uint64_t) MAX_RECORDS,
    205                                 TALER_EXCHANGEDB_abs_limit (limit));
    206   struct GNUNET_PQ_QueryParam params[] = {
    207     GNUNET_PQ_query_param_bool (NULL == h_payto),
    208     NULL == h_payto
    209     ? GNUNET_PQ_query_param_null ()
    210     : GNUNET_PQ_query_param_auto_from_type (h_payto),
    211     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL ==
    212                                  investigation_only)),
    213     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES ==
    214                                  investigation_only)),
    215     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL ==
    216                                  active_only)),
    217     GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES ==
    218                                  active_only)),
    219     GNUNET_PQ_query_param_uint64 (&offset),
    220     GNUNET_PQ_query_param_uint64 (&ulimit),
    221     GNUNET_PQ_query_param_end
    222   };
    223   struct AmlProcessResultContext ctx = {
    224     .cb = cb,
    225     .cb_cls = cb_cls,
    226     .pg = pg,
    227     .status = GNUNET_OK
    228   };
    229   enum GNUNET_DB_QueryStatus qs;
    230   const char *stmt = (limit > 0)
    231     ? "iterate_aml_decisions_inc"
    232     : "iterate_aml_decisions_dec";
    233 
    234   PREPARE (pg,
    235            "iterate_aml_decisions_inc",
    236            "SELECT"
    237            " lo.outcome_serial_id"
    238            ",lo.h_payto"
    239            ",ah.justification"
    240            ",lo.new_measure_name"
    241            ",ah.decider_pub"
    242            ",astaff.decider_name"
    243            ",ah.kyc_attributes_serial_id"
    244            ",lo.decision_time"
    245            ",lo.expiration_time"
    246            ",lo.jproperties::TEXT"
    247            ",lo.to_investigate"
    248            ",lo.is_active"
    249            ",lo.jnew_rules::TEXT"
    250            ",kt.is_wallet"
    251            ",wt.payto_uri"
    252            " FROM legitimization_outcomes lo"
    253            " JOIN kyc_targets kt"
    254            "   ON (lo.h_payto = kt.h_normalized_payto)"
    255            " JOIN wire_targets wt"
    256            "   ON (lo.h_payto = wt.h_normalized_payto)"
    257            " LEFT JOIN aml_history ah"
    258            "   USING (outcome_serial_id)"
    259            " LEFT JOIN LATERAL"
    260            "   (SELECT s.decider_name"
    261            "      FROM aml_staff s"
    262            "     WHERE (s.decider_pub = ah.decider_pub)"
    263            "       AND (s.last_change <= lo.decision_time)"
    264            "     ORDER BY s.last_change DESC"
    265            "     LIMIT 1) astaff ON TRUE"
    266            " WHERE (outcome_serial_id > $7)"
    267            "   AND ($1 OR (lo.h_payto = $2))"
    268            "   AND ($3 OR (lo.to_investigate = $4))"
    269            "   AND ($5 OR (lo.is_active = $6))"
    270            " ORDER BY lo.outcome_serial_id ASC"
    271            " LIMIT $8");
    272   PREPARE (pg,
    273            "iterate_aml_decisions_dec",
    274            "SELECT"
    275            " lo.outcome_serial_id"
    276            ",lo.h_payto"
    277            ",ah.justification"
    278            ",lo.new_measure_name"
    279            ",ah.decider_pub"
    280            ",astaff.decider_name"
    281            ",ah.kyc_attributes_serial_id"
    282            ",lo.decision_time"
    283            ",lo.expiration_time"
    284            ",lo.jproperties::TEXT"
    285            ",lo.to_investigate"
    286            ",lo.is_active"
    287            ",lo.jnew_rules::TEXT"
    288            ",kt.is_wallet"
    289            ",wt.payto_uri"
    290            " FROM legitimization_outcomes lo"
    291            " JOIN kyc_targets kt"
    292            "   ON (lo.h_payto = kt.h_normalized_payto)"
    293            " JOIN wire_targets wt"
    294            "   ON (lo.h_payto = wt.h_normalized_payto)"
    295            " LEFT JOIN aml_history ah"
    296            "   USING (outcome_serial_id)"
    297            " LEFT JOIN LATERAL"
    298            "   (SELECT s.decider_name"
    299            "      FROM aml_staff s"
    300            "     WHERE (s.decider_pub = ah.decider_pub)"
    301            "       AND (s.last_change <= lo.decision_time)"
    302            "     ORDER BY s.last_change DESC"
    303            "     LIMIT 1) astaff ON TRUE"
    304            " WHERE lo.outcome_serial_id < $7"
    305            "  AND ($1 OR (lo.h_payto = $2))"
    306            "  AND ($3 OR (lo.to_investigate = $4))"
    307            "  AND ($5 OR (lo.is_active = $6))"
    308            " ORDER BY lo.outcome_serial_id DESC"
    309            " LIMIT $8");
    310   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    311                                              stmt,
    312                                              params,
    313                                              &handle_aml_result,
    314                                              &ctx);
    315   if (GNUNET_OK != ctx.status)
    316     return GNUNET_DB_STATUS_HARD_ERROR;
    317   return qs;
    318 }