exchange

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

update_rules.c (18408B)


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