exchange

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

exchangedb_aml.c (20812B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2023, 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 Affero 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file exchangedb_aml.c
     18  * @brief helper function to handle AML programs
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/taler_exchangedb_plugin.h"
     22 #include "taler/taler_exchangedb_lib.h"
     23 #include "taler/taler_kyclogic_lib.h"
     24 #include "taler/taler_json_lib.h"
     25 #include "taler/taler_dbevents.h"
     26 #include <gnunet/gnunet_common.h>
     27 
     28 /**
     29  * Maximum recursion depth we allow for AML programs.
     30  * Basically, after this number of "skip" processes
     31  * we forcefully terminate the recursion and fail hard.
     32  */
     33 #define MAX_DEPTH 16
     34 
     35 enum GNUNET_DB_QueryStatus
     36 TALER_EXCHANGEDB_persist_aml_program_result (
     37   struct TALER_EXCHANGEDB_Plugin *plugin,
     38   uint64_t process_row,
     39   const struct TALER_NormalizedPaytoHashP *account_id,
     40   const struct TALER_KYCLOGIC_AmlProgramResult *apr,
     41   enum TALER_EXCHANGEDB_PersistProgramResultStatus *ret_pprs)
     42 {
     43   enum GNUNET_DB_QueryStatus qs;
     44   json_t *jmeasures = NULL;
     45   struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL;
     46 
     47   GNUNET_assert (NULL != ret_pprs);
     48 
     49   *ret_pprs = TALER_EXCHANGEDB_PPRS_OK;
     50 
     51   if ( (TALER_KYCLOGIC_AMLR_SUCCESS == apr->status) &&
     52        (NULL != apr->details.success.new_measures) )
     53   {
     54     lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules);
     55     if (NULL == lrs)
     56     {
     57       qs = plugin->insert_aml_program_failure (
     58         plugin->cls,
     59         process_row,
     60         account_id,
     61         "Failed to parse AML program output",
     62         TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT);
     63       GNUNET_break (qs > 0);
     64       return qs;
     65     }
     66     jmeasures = TALER_KYCLOGIC_get_jmeasures (
     67       lrs,
     68       apr->details.success.new_measures);
     69     if (NULL == jmeasures)
     70     {
     71       char *err;
     72 
     73       GNUNET_break (0);
     74       GNUNET_asprintf (&err,
     75                        "Failed to find measures `%s' specified in AML program output",
     76                        apr->details.success.new_measures);
     77       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     78                   "AML program specified invalid measures `%s'\n",
     79                   apr->details.success.new_measures);
     80       qs = plugin->insert_aml_program_failure (
     81         plugin->cls,
     82         process_row,
     83         account_id,
     84         err,
     85         TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT);
     86       *ret_pprs = TALER_EXCHANGEDB_PPRS_BAD_OUTCOME;
     87       TALER_KYCLOGIC_rules_free (lrs);
     88       GNUNET_free (err);
     89       GNUNET_break (qs > 0);
     90       return qs;
     91     }
     92   }
     93 
     94   qs = plugin->clear_aml_lock (
     95     plugin->cls,
     96     account_id);
     97   switch (apr->status)
     98   {
     99   case TALER_KYCLOGIC_AMLR_FAILURE:
    100     qs = plugin->insert_aml_program_failure (
    101       plugin->cls,
    102       process_row,
    103       account_id,
    104       apr->details.failure.error_message,
    105       apr->details.failure.ec);
    106     GNUNET_break (qs > 0);
    107     goto cleanup;
    108   case TALER_KYCLOGIC_AMLR_SUCCESS:
    109     {
    110       struct TALER_FullPayto null_payto_uri = { 0 };
    111       bool invalid_officer;
    112       bool unknown_account;
    113       struct GNUNET_TIME_Timestamp last_date;
    114       uint64_t legitimization_measure_serial_id;
    115       bool is_wallet;
    116 
    117       qs = plugin->insert_aml_decision (
    118         plugin->cls,
    119         null_payto_uri,
    120         account_id,
    121         GNUNET_TIME_timestamp_get (),
    122         apr->details.success.expiration_time,
    123         apr->details.success.account_properties,
    124         apr->details.success.new_rules,
    125         apr->details.success.to_investigate,
    126         apr->details.success.new_measures,
    127         jmeasures,
    128         NULL, /* justification */
    129         NULL, /* decider_pub */
    130         NULL, /* decider_sig */
    131         apr->details.success.num_events,
    132         apr->details.success.events,
    133         NULL, /* form ID */
    134         0, /* enc_attributes_size*/
    135         NULL, /* enc_attributes*/
    136         NULL, /* attributes_hash */
    137         GNUNET_TIME_UNIT_ZERO_TS, /* attributes_expiration_time */
    138         &invalid_officer,
    139         &unknown_account,
    140         &last_date,
    141         &legitimization_measure_serial_id,
    142         &is_wallet);
    143       GNUNET_break (qs > 0);
    144       goto cleanup;
    145     }
    146   }
    147   GNUNET_break (0);
    148   qs = GNUNET_DB_STATUS_HARD_ERROR;
    149 cleanup:
    150   TALER_KYCLOGIC_rules_free (lrs);
    151   json_decref (jmeasures);
    152   return qs;
    153 }
    154 
    155 
    156 struct TALER_EXCHANGEDB_RuleUpdater
    157 {
    158   /**
    159    * database plugin to use
    160    */
    161   struct TALER_EXCHANGEDB_Plugin *plugin;
    162 
    163   /**
    164    * key to use to decrypt attributes
    165    */
    166   struct TALER_AttributeEncryptionKeyP attribute_key;
    167 
    168   /**
    169    * account to get the rule set for
    170    */
    171   struct TALER_NormalizedPaytoHashP account;
    172 
    173   /**
    174    * function to call with the result
    175    */
    176   TALER_EXCHANGEDB_CurrentRulesCallback cb;
    177 
    178   /**
    179    * Closure for @e cb.
    180    */
    181   void *cb_cls;
    182 
    183   /**
    184    * Current rule set we are working on.
    185    */
    186   struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs;
    187 
    188   /**
    189    * Task for asynchronous continuations.
    190    */
    191   struct GNUNET_SCHEDULER_Task *t;
    192 
    193   /**
    194    * Handler waiting notification that (previous) AML program
    195    * finished.
    196    */
    197   struct GNUNET_DB_EventHandler *eh;
    198 
    199   /**
    200    * Handle to running AML program.
    201    */
    202   struct TALER_KYCLOGIC_AmlProgramRunnerHandle *amlh;
    203 
    204   /**
    205    * Name of the AML program we were running asynchronously,
    206    * for diagnostics.
    207    */
    208   char *aml_program_name;
    209 
    210   /**
    211    * Error hint to return with @e ec.
    212    */
    213   const char *hint;
    214 
    215   /**
    216    * Row the rule set in @a lrs is based on.
    217    */
    218   uint64_t legitimization_outcome_last_row;
    219 
    220   /**
    221    * Taler error code to return.
    222    */
    223   enum TALER_ErrorCode ec;
    224 
    225   /**
    226    * Counter used to limit recursion depth.
    227    */
    228   unsigned int depth;
    229 
    230   /**
    231    * True if @e account is for a wallet.
    232    */
    233   bool is_wallet;
    234 };
    235 
    236 
    237 /**
    238  * Function that finally returns the result to the application and cleans
    239  * up. Called with an open database transaction on success; on failure, the
    240  * transaction will have already been rolled back.
    241  *
    242  * @param[in,out] ru rule updater to return result for
    243  */
    244 static void
    245 return_result (struct TALER_EXCHANGEDB_RuleUpdater *ru)
    246 {
    247   struct TALER_EXCHANGEDB_RuleUpdaterResult rur = {
    248     .legitimization_outcome_last_row = ru->legitimization_outcome_last_row,
    249     .lrs = ru->lrs,
    250     .ec = ru->ec,
    251   };
    252 
    253   ru->cb (ru->cb_cls,
    254           &rur);
    255   ru->lrs = NULL;
    256   TALER_EXCHANGEDB_update_rules_cancel (ru);
    257 }
    258 
    259 
    260 /**
    261  * Fail the update with the given @a ec and @a hint.
    262  * Called with an open database transaction, which will
    263  * be rolled back (!).
    264  *
    265  * @param[in,out] ru account we are processing
    266  * @param ec error code to fail with
    267  * @param hint hint to return, can be NULL
    268  */
    269 static void
    270 fail_update (struct TALER_EXCHANGEDB_RuleUpdater *ru,
    271              enum TALER_ErrorCode ec,
    272              const char *hint)
    273 {
    274   GNUNET_assert (NULL == ru->t);
    275   ru->plugin->rollback (ru->plugin->cls);
    276   ru->ec = ec;
    277   ru->hint = hint;
    278   return_result (ru);
    279 }
    280 
    281 
    282 /**
    283  * Check the rules in @a ru to see if they are current, and
    284  * if not begin the updating process.  Called with an open
    285  * database transaction.
    286  *
    287  * @param[in] ru rule updater context
    288  */
    289 static void
    290 check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru);
    291 
    292 
    293 /**
    294  * Run the measure @a m in the context of the legitimisation rules
    295  * of @a ru.  Called with an open database transaction.
    296  *
    297  * @param ru updating context we are using
    298  * @param m measure we need to run next
    299  */
    300 static void
    301 run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
    302              const struct TALER_KYCLOGIC_Measure *m);
    303 
    304 
    305 /**
    306  * Function called after AML program was run.  Called
    307  * without an open database transaction, will start one!
    308  *
    309  * @param cls the `struct TALER_EXCHANGEDB_RuleUpdater *`
    310  * @param apr result of the AML program.
    311  */
    312 static void
    313 aml_result_callback (
    314   void *cls,
    315   const struct TALER_KYCLOGIC_AmlProgramResult *apr)
    316 {
    317   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
    318   enum GNUNET_DB_QueryStatus qs;
    319   enum GNUNET_GenericReturnValue res;
    320   enum TALER_EXCHANGEDB_PersistProgramResultStatus pprs;
    321 
    322   ru->amlh = NULL;
    323   res = ru->plugin->start (ru->plugin->cls,
    324                            "aml-persist-aml-program-result");
    325   if (GNUNET_OK != res)
    326   {
    327     GNUNET_break (0);
    328     fail_update (ru,
    329                  TALER_EC_GENERIC_DB_START_FAILED,
    330                  "aml-persist-aml-program-result");
    331     return;
    332   }
    333   /* Update database update based on result */
    334   qs = TALER_EXCHANGEDB_persist_aml_program_result (
    335     ru->plugin,
    336     0LLU, /* 0: no existing legitimization process, creates new row */
    337     &ru->account,
    338     apr,
    339     &pprs);
    340   switch (qs)
    341   {
    342   case GNUNET_DB_STATUS_HARD_ERROR:
    343     GNUNET_break (0);
    344     fail_update (ru,
    345                  TALER_EC_GENERIC_DB_STORE_FAILED,
    346                  "persist_aml_program_result");
    347     return;
    348   case GNUNET_DB_STATUS_SOFT_ERROR:
    349     /* Bad, couldn't persist AML result. Try again... */
    350     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    351                 "Serialization issue persisting result of AML program. Restarting.\n");
    352     fail_update (ru,
    353                  TALER_EC_GENERIC_DB_SOFT_FAILURE,
    354                  "persist_aml_program_result");
    355     return;
    356   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    357     /* Strange, but let's just continue */
    358     break;
    359   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    360     /* normal case */
    361     break;
    362   }
    363   switch (pprs)
    364   {
    365   case TALER_EXCHANGEDB_PPRS_OK:
    366     break;
    367   case TALER_EXCHANGEDB_PPRS_BAD_OUTCOME:
    368     fail_update (ru,
    369                  TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT,
    370                  "persist_aml_program_result");
    371     return;
    372   }
    373   switch (apr->status)
    374   {
    375   case TALER_KYCLOGIC_AMLR_SUCCESS:
    376     TALER_KYCLOGIC_rules_free (ru->lrs);
    377     ru->lrs = NULL;
    378     ru->lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules);
    379     /* Fall back to default rules on parse error! */
    380     GNUNET_break (NULL != ru->lrs);
    381     check_rules (ru);
    382     return;
    383   case TALER_KYCLOGIC_AMLR_FAILURE:
    384     {
    385       const char *fmn = apr->details.failure.fallback_measure;
    386       const struct TALER_KYCLOGIC_Measure *m;
    387 
    388       m = TALER_KYCLOGIC_get_measure (ru->lrs,
    389                                       fmn);
    390       if (NULL == m)
    391       {
    392         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    393                     "Fallback measure `%s' does not exist (anymore?).\n",
    394                     fmn);
    395         TALER_KYCLOGIC_rules_free (ru->lrs);
    396         ru->lrs = NULL;
    397         return_result (ru);
    398         return;
    399       }
    400       run_measure (ru,
    401                    m);
    402       return;
    403     }
    404   }
    405   /* This should be impossible */
    406   GNUNET_assert (0);
    407 }
    408 
    409 
    410 /**
    411  * Entrypoint that fetches the latest rules from the database
    412  * and starts processing them. Called without an open database
    413  * transaction, will start one.
    414  *
    415  * @param[in] cls the `struct TALER_EXCHANGEDB_RuleUpdater *` to run
    416  */
    417 static void
    418 fetch_latest_rules (void *cls);
    419 
    420 
    421 /**
    422  * Notification called when we either timeout on the AML program lock
    423  * or when the (previous) AML program finished and we can thus try again.
    424  *
    425  * @param cls  the `struct TALER_EXCHANGEDB_RuleUpdater *` to continue
    426  * @param extra additional event data provided (unused)
    427  * @param extra_size number of bytes in @a extra (unused)
    428  */
    429 static void
    430 trigger_fetch_latest_rules (void *cls,
    431                             const void *extra,
    432                             size_t extra_size)
    433 {
    434   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
    435 
    436   (void) extra;
    437   (void) extra_size;
    438   if (NULL != ru->t)
    439     return; /* multiple events triggered us, ignore */
    440   ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
    441                                     ru);
    442 }
    443 
    444 
    445 static void
    446 run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
    447              const struct TALER_KYCLOGIC_Measure *m)
    448 {
    449   if (NULL == m)
    450   {
    451     /* fall back to default rules */
    452     TALER_KYCLOGIC_rules_free (ru->lrs);
    453     ru->lrs = NULL;
    454     return_result (ru);
    455     return;
    456   }
    457   ru->depth++;
    458   if (ru->depth > MAX_DEPTH)
    459   {
    460     fail_update (ru,
    461                  TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
    462                  NULL);
    463     return;
    464   }
    465   if ( (NULL == m->check_name) ||
    466        (0 ==
    467         strcasecmp ("SKIP",
    468                     m->check_name)) )
    469   {
    470     struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = {
    471       .account = &ru->account,
    472       .is_wallet = ru->is_wallet,
    473       .db_plugin = ru->plugin,
    474       .attribute_key = &ru->attribute_key
    475     };
    476     enum GNUNET_DB_QueryStatus qs;
    477     struct GNUNET_TIME_Absolute xlock;
    478 
    479     /* Free previous one, in case we are iterating... */
    480     GNUNET_free (ru->aml_program_name);
    481     if (NULL != m->prog_name)
    482     {
    483       ru->aml_program_name = GNUNET_strdup (m->prog_name);
    484     }
    485     else
    486     {
    487       /* How do we get to run a measure if the check type
    488          is INFO (which is the only case where prog_name
    489          is allowed to be NULL?) */
    490       GNUNET_break (0);
    491       ru->aml_program_name = NULL;
    492     }
    493     qs = ru->plugin->set_aml_lock (
    494       ru->plugin->cls,
    495       &ru->account,
    496       GNUNET_TIME_relative_multiply (ru->plugin->max_aml_program_runtime,
    497                                      2),
    498       &xlock);
    499     if (GNUNET_TIME_absolute_is_future (xlock))
    500     {
    501       struct TALER_KycCompletedEventP eh = {
    502         .header.size = htons (sizeof (eh)),
    503         .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
    504         .h_payto = ru->account
    505       };
    506       /* Wait for either timeout or notification */
    507       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    508                   "AML program already running, waiting for it to finish\n");
    509       ru->plugin->rollback (ru->plugin->cls);
    510       ru->eh
    511         = ru->plugin->event_listen (
    512             ru->plugin->cls,
    513             GNUNET_TIME_absolute_get_remaining (xlock),
    514             &eh.header,
    515             &trigger_fetch_latest_rules,
    516             ru);
    517       return;
    518     }
    519     qs = ru->plugin->commit (ru->plugin->cls);
    520     if (qs < 0)
    521     {
    522       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    523       fail_update (ru,
    524                    GNUNET_DB_STATUS_SOFT_ERROR == qs
    525                    ? TALER_EC_GENERIC_DB_SOFT_FAILURE
    526                    : TALER_EC_GENERIC_DB_COMMIT_FAILED,
    527                    "current-aml-rule-fetch");
    528       return;
    529     }
    530     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    531                 "Check is of type 'SKIP', running AML program %s.\n",
    532                 m->prog_name);
    533     GNUNET_assert (NULL == ru->t);
    534     ru->amlh = TALER_KYCLOGIC_run_aml_program3 (
    535       ru->is_wallet,
    536       m,
    537       &TALER_EXCHANGEDB_current_attributes_builder,
    538       &hbc,
    539       &TALER_EXCHANGEDB_current_rule_builder,
    540       &hbc,
    541       &TALER_EXCHANGEDB_aml_history_builder,
    542       &hbc,
    543       &TALER_EXCHANGEDB_kyc_history_builder,
    544       &hbc,
    545       ru->plugin->max_aml_program_runtime,
    546       &aml_result_callback,
    547       ru);
    548     return;
    549   }
    550 
    551   /* User MUST pass interactive check */
    552   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    553               "Measure %s involves check %s\n",
    554               m->measure_name,
    555               m->check_name);
    556   {
    557     /* activate the measure/check */
    558     json_t *succ_jmeasures
    559       = TALER_KYCLOGIC_get_jmeasures (
    560           ru->lrs,
    561           m->measure_name);
    562     bool unknown_account;
    563     struct GNUNET_TIME_Timestamp last_date;
    564     enum GNUNET_DB_QueryStatus qs;
    565 
    566     qs = ru->plugin->insert_successor_measure (
    567       ru->plugin->cls,
    568       &ru->account,
    569       GNUNET_TIME_timestamp_get (),
    570       m->measure_name,
    571       succ_jmeasures,
    572       &unknown_account,
    573       &last_date);
    574     json_decref (succ_jmeasures);
    575     switch (qs)
    576     {
    577     case GNUNET_DB_STATUS_SOFT_ERROR:
    578       GNUNET_log (
    579         GNUNET_ERROR_TYPE_INFO,
    580         "Serialization issue!\n");
    581       fail_update (ru,
    582                    TALER_EC_GENERIC_DB_SOFT_FAILURE,
    583                    "insert_successor_measure");
    584       return;
    585     case GNUNET_DB_STATUS_HARD_ERROR:
    586       GNUNET_break (0);
    587       fail_update (ru,
    588                    TALER_EC_GENERIC_DB_STORE_FAILED,
    589                    "insert_successor_measure");
    590       return;
    591     default:
    592       break;
    593     }
    594     if (unknown_account)
    595     {
    596       fail_update (ru,
    597                    TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN,
    598                    NULL);
    599       return;
    600     }
    601   }
    602   /* The rules remain these rules until the user passes the check */
    603   return_result (ru);
    604 }
    605 
    606 
    607 /**
    608  * Update the expired legitimization rules in @a ru, checking for expiration
    609  * first.  Called with an open database transaction.
    610  *
    611  * @param[in,out] ru account we are processing
    612  */
    613 static void
    614 update_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
    615 {
    616   const struct TALER_KYCLOGIC_Measure *m;
    617 
    618   GNUNET_assert (NULL != ru->lrs);
    619   GNUNET_assert (GNUNET_TIME_absolute_is_past (
    620                    TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time));
    621   m = TALER_KYCLOGIC_rules_get_successor (ru->lrs);
    622   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    623               "Successor measure is %s.\n",
    624               (NULL != m) ? m->measure_name : "(null)");
    625   run_measure (ru,
    626                m);
    627 }
    628 
    629 
    630 static void
    631 check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
    632 {
    633   ru->depth++;
    634   if (ru->depth > MAX_DEPTH)
    635   {
    636     fail_update (ru,
    637                  TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
    638                  NULL);
    639     return;
    640   }
    641   if (NULL == ru->lrs)
    642   {
    643     /* return NULL, aka default rules */
    644     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    645                 "Default rules apply\n");
    646     return_result (ru);
    647     return;
    648   }
    649   if (! GNUNET_TIME_absolute_is_past
    650         (TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time) )
    651   {
    652     /* Rules did not expire, return them! */
    653     return_result (ru);
    654     return;
    655   }
    656   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    657               "Custom rules expired, updating...\n");
    658   update_rules (ru);
    659 }
    660 
    661 
    662 static void
    663 fetch_latest_rules (void *cls)
    664 {
    665   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
    666   enum GNUNET_DB_QueryStatus qs;
    667   json_t *jnew_rules;
    668   enum GNUNET_GenericReturnValue res;
    669 
    670   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    671               "Fetching latest rules.");
    672 
    673   ru->t = NULL;
    674   if (NULL != ru->eh)
    675   {
    676     /* cancel event listener, if we have one */
    677     ru->plugin->event_listen_cancel (ru->plugin->cls,
    678                                      ru->eh);
    679     ru->eh = NULL;
    680   }
    681   GNUNET_break (NULL == ru->lrs);
    682   res = ru->plugin->start (ru->plugin->cls,
    683                            "aml-begin-lookup-rules-by-access-token");
    684   if (GNUNET_OK != res)
    685   {
    686     GNUNET_break (0);
    687     fail_update (ru,
    688                  TALER_EC_GENERIC_DB_START_FAILED,
    689                  "aml-begin-lookup-rules-by-access-token");
    690     return;
    691   }
    692   qs = ru->plugin->lookup_rules_by_access_token (
    693     ru->plugin->cls,
    694     &ru->account,
    695     &jnew_rules,
    696     &ru->legitimization_outcome_last_row);
    697   if (qs < 0)
    698   {
    699     GNUNET_break (0);
    700     fail_update (ru,
    701                  TALER_EC_GENERIC_DB_FETCH_FAILED,
    702                  "lookup_rules_by_access_token");
    703     return;
    704   }
    705   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
    706        (NULL != jnew_rules) )
    707   {
    708     ru->lrs = TALER_KYCLOGIC_rules_parse (jnew_rules);
    709     GNUNET_break (NULL != ru->lrs);
    710     json_decref (jnew_rules);
    711   }
    712   check_rules (ru);
    713 }
    714 
    715 
    716 struct TALER_EXCHANGEDB_RuleUpdater *
    717 TALER_EXCHANGEDB_update_rules (
    718   struct TALER_EXCHANGEDB_Plugin *plugin,
    719   const struct TALER_AttributeEncryptionKeyP *attribute_key,
    720   const struct TALER_NormalizedPaytoHashP *account,
    721   bool is_wallet,
    722   TALER_EXCHANGEDB_CurrentRulesCallback cb,
    723   void *cb_cls)
    724 {
    725   struct TALER_EXCHANGEDB_RuleUpdater *ru;
    726 
    727   ru = GNUNET_new (struct TALER_EXCHANGEDB_RuleUpdater);
    728   ru->plugin = plugin;
    729   ru->attribute_key = *attribute_key;
    730   ru->account = *account;
    731   ru->is_wallet = is_wallet;
    732   ru->cb = cb;
    733   ru->cb_cls = cb_cls;
    734   ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
    735                                     ru);
    736   return ru;
    737 }
    738 
    739 
    740 void
    741 TALER_EXCHANGEDB_update_rules_cancel (
    742   struct TALER_EXCHANGEDB_RuleUpdater *ru)
    743 {
    744   if (NULL != ru->t)
    745   {
    746     GNUNET_SCHEDULER_cancel (ru->t);
    747     ru->t = NULL;
    748   }
    749   if (NULL != ru->amlh)
    750   {
    751     TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh);
    752     ru->amlh = NULL;
    753   }
    754   if (NULL != ru->lrs)
    755   {
    756     TALER_KYCLOGIC_rules_free (ru->lrs);
    757     ru->lrs = NULL;
    758   }
    759   if (NULL != ru->eh)
    760   {
    761     ru->plugin->event_listen_cancel (ru->plugin->cls,
    762                                      ru->eh);
    763     ru->eh = NULL;
    764   }
    765   GNUNET_free (ru->aml_program_name);
    766   GNUNET_free (ru);
    767 }