exchange

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

taler-exchange-kyc-trigger.c (11040B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2020-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 taler-exchange-kyc-trigger.c
     18  * @brief Support for manually triggering KYC/AML processes for testing
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include <gnunet/gnunet_json_lib.h>
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <microhttpd.h>
     25 #include "taler/taler_json_lib.h"
     26 #include "taler/taler-exchange/get-kyc-check-H_NORMALIZED_PAYTO.h"
     27 #include "taler/taler_exchange_service.h"
     28 
     29 
     30 /**
     31  * Our private key.
     32  */
     33 static struct TALER_ReservePrivateKeyP reserve_priv;
     34 
     35 /**
     36  * Our public key.
     37  */
     38 static struct TALER_ReservePublicKeyP reserve_pub;
     39 
     40 /**
     41  * Our context for making HTTP requests.
     42  */
     43 static struct GNUNET_CURL_Context *ctx;
     44 
     45 /**
     46  * Reschedule context for #ctx.
     47  */
     48 static struct GNUNET_CURL_RescheduleContext *rc;
     49 
     50 /**
     51  * Handle to the exchange's configuration
     52  */
     53 static const struct GNUNET_CONFIGURATION_Handle *kcfg;
     54 
     55 /**
     56  * Handle for exchange interaction.
     57  */
     58 static struct TALER_EXCHANGE_PostKycWalletHandle *kwh;
     59 
     60 /**
     61  * Handle for exchange kyc-check interaction.
     62  */
     63 static struct TALER_EXCHANGE_GetKycCheckHandle *kc;
     64 
     65 /**
     66  * Balance threshold to report to the exchange.
     67  */
     68 static struct TALER_Amount balance;
     69 
     70 /**
     71  * Return value from main().
     72  */
     73 static int global_ret;
     74 
     75 /**
     76  * Currency we have configured.
     77  */
     78 static char *currency;
     79 
     80 /**
     81  * URL of the exchange we are interacting with
     82  * as per our configuration.
     83  */
     84 static char *CFG_exchange_url;
     85 
     86 
     87 /**
     88  * Function called with the result of a KYC check.
     89  *
     90  * @param cls closure
     91  * @param ks the account's KYC status details
     92  */
     93 static void
     94 kyc_status_cb (
     95   void *cls,
     96   const struct TALER_EXCHANGE_GetKycCheckResponse *ks)
     97 {
     98   kc = NULL;
     99   switch (ks->hr.http_status)
    100   {
    101   case MHD_HTTP_OK:
    102     {
    103       char *buf;
    104 
    105       buf = GNUNET_STRINGS_data_to_string_alloc (
    106         &ks->details.ok.access_token,
    107         sizeof (ks->details.ok.access_token));
    108       fprintf (stdout,
    109                "KYC SPA at %skyc-spa/%s\n",
    110                CFG_exchange_url,
    111                buf);
    112       GNUNET_free (buf);
    113     }
    114     break;
    115   case MHD_HTTP_ACCEPTED:
    116     {
    117       char *buf;
    118 
    119       buf = GNUNET_STRINGS_data_to_string_alloc (
    120         &ks->details.ok.access_token,
    121         sizeof (ks->details.ok.access_token));
    122       fprintf (stdout,
    123                "KYC SPA at %skyc-spa/%s\n",
    124                CFG_exchange_url,
    125                buf);
    126       GNUNET_free (buf);
    127     }
    128     break;
    129   case MHD_HTTP_FORBIDDEN:
    130     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    131                 "KYC auth now required?\n");
    132     break;
    133   }
    134   GNUNET_SCHEDULER_shutdown ();
    135 }
    136 
    137 
    138 /**
    139  * Function called with the result for a wallet looking
    140  * up its KYC payment target.
    141  *
    142  * @param cls closure
    143  * @param ks the wallets KYC payment target details
    144  */
    145 static void
    146 kyc_wallet_cb (
    147   void *cls,
    148   const struct TALER_EXCHANGE_PostKycWalletResponse *ks)
    149 {
    150   kwh = NULL;
    151   switch (ks->hr.http_status)
    152   {
    153   case MHD_HTTP_OK:
    154     fprintf (stdout,
    155              "OK, next threshold at %s\n",
    156              TALER_amount2s (&ks->details.ok.next_threshold));
    157     break;
    158   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    159     {
    160       const struct TALER_EXCHANGE_KycNeededRedirect *knr
    161         = &ks->details.unavailable_for_legal_reasons;
    162       char *ps;
    163 
    164       ps = GNUNET_STRINGS_data_to_string_alloc (&knr->h_payto,
    165                                                 sizeof (knr->h_payto));
    166       fprintf (stderr,
    167                "KYC needed (%llu, %s) for %s\n",
    168                (unsigned long long) knr->requirement_row,
    169                knr->bad_kyc_auth
    170                ? "KYC auth needed"
    171                : "KYC auth OK",
    172                ps);
    173       GNUNET_free (ps);
    174       if (! knr->bad_kyc_auth)
    175       {
    176         struct TALER_NormalizedPaytoHashP h_payto;
    177         union TALER_AccountPrivateKeyP pk;
    178         struct TALER_NormalizedPayto np;
    179 
    180         np = TALER_reserve_make_payto (CFG_exchange_url,
    181                                        &reserve_pub);
    182         TALER_normalized_payto_hash (np,
    183                                      &h_payto);
    184         GNUNET_free (np.normalized_payto);
    185         pk.reserve_priv = reserve_priv;
    186         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    187                     "Requesting /kyc-check to determine KYC entrypoint\n");
    188         kc = TALER_EXCHANGE_get_kyc_check_create (ctx,
    189                                                   CFG_exchange_url,
    190                                                   &h_payto,
    191                                                   &pk);
    192         GNUNET_break (NULL != kc);
    193         if (NULL == kc)
    194           break;
    195         GNUNET_assert (TALER_EC_NONE ==
    196                        TALER_EXCHANGE_get_kyc_check_start (
    197                          kc,
    198                          &kyc_status_cb,
    199                          NULL));
    200         return;
    201       }
    202     }
    203     break;
    204   default:
    205     fprintf (stdout,
    206              "Unexpected HTTP status %u\n",
    207              ks->hr.http_status);
    208     break;
    209   }
    210   GNUNET_SCHEDULER_shutdown ();
    211 }
    212 
    213 
    214 /**
    215  * Shutdown task. Invoked when the application is being terminated.
    216  *
    217  * @param cls NULL
    218  */
    219 static void
    220 do_shutdown (void *cls)
    221 {
    222   (void) cls;
    223   if (NULL != kwh)
    224   {
    225     TALER_EXCHANGE_post_kyc_wallet_cancel (kwh);
    226     kwh = NULL;
    227   }
    228   if (NULL != kc)
    229   {
    230     TALER_EXCHANGE_get_kyc_check_cancel (kc);
    231     kc = NULL;
    232   }
    233   if (NULL != ctx)
    234   {
    235     GNUNET_CURL_fini (ctx);
    236     ctx = NULL;
    237   }
    238   if (NULL != rc)
    239   {
    240     GNUNET_CURL_gnunet_rc_destroy (rc);
    241     rc = NULL;
    242   }
    243 }
    244 
    245 
    246 /**
    247  * Load the reserve key.
    248  *
    249  * @param do_create #GNUNET_YES if the key may be created
    250  * @return #GNUNET_OK on success
    251  */
    252 static enum GNUNET_GenericReturnValue
    253 load_reserve_key (int do_create)
    254 {
    255   char *fn;
    256 
    257   if (GNUNET_OK ==
    258       GNUNET_CONFIGURATION_get_value_filename (kcfg,
    259                                                "exchange-testing",
    260                                                "RESERVE_PRIV_FILE",
    261                                                &fn))
    262   {
    263     enum GNUNET_GenericReturnValue ret;
    264 
    265     if (GNUNET_YES !=
    266         GNUNET_DISK_file_test (fn))
    267       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    268                   "Account private key `%s' does not exist yet, creating it!\n",
    269                   fn);
    270     ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
    271                                              do_create,
    272                                              &reserve_priv.eddsa_priv);
    273     if (GNUNET_SYSERR == ret)
    274     {
    275       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    276                   "Failed to initialize master key from file `%s': %s\n",
    277                   fn,
    278                   "could not create file");
    279       GNUNET_free (fn);
    280       return GNUNET_SYSERR;
    281     }
    282     GNUNET_free (fn);
    283   }
    284   else
    285   {
    286     GNUNET_CRYPTO_eddsa_key_create (&reserve_priv.eddsa_priv);
    287   }
    288   GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv,
    289                                       &reserve_pub.eddsa_pub);
    290   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    291               "Using reserve public key %s\n",
    292               TALER_B2S (&reserve_pub));
    293   return GNUNET_OK;
    294 }
    295 
    296 
    297 /**
    298  * Main function that will be run.
    299  *
    300  * @param cls closure
    301  * @param args remaining command-line arguments
    302  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    303  * @param cfg configuration
    304  */
    305 static void
    306 run (void *cls,
    307      char *const *args,
    308      const char *cfgfile,
    309      const struct GNUNET_CONFIGURATION_Handle *cfg)
    310 {
    311   (void) cls;
    312   (void) cfgfile;
    313   kcfg = cfg;
    314 
    315   if (GNUNET_OK !=
    316       load_reserve_key (GNUNET_YES))
    317   {
    318     GNUNET_break (0);
    319     global_ret = EXIT_FAILURE;
    320     return;
    321   }
    322   if (GNUNET_OK !=
    323       TALER_config_get_currency (kcfg,
    324                                  "exchange",
    325                                  &currency))
    326   {
    327     global_ret = EXIT_NOTCONFIGURED;
    328     return;
    329   }
    330   if ( (GNUNET_OK !=
    331         TALER_amount_is_valid (&balance)) ||
    332        (0 != strcmp (balance.currency,
    333                      currency)) )
    334   {
    335     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    336                 "Invalid balance threshold `%s'\n",
    337                 TALER_amount2s (&balance));
    338     global_ret = EXIT_FAILURE;
    339     return;
    340   }
    341   if ( (NULL == CFG_exchange_url) &&
    342        (GNUNET_OK !=
    343         GNUNET_CONFIGURATION_get_value_string (kcfg,
    344                                                "exchange",
    345                                                "BASE_URL",
    346                                                &CFG_exchange_url)) )
    347   {
    348     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    349                                "exchange",
    350                                "BASE_URL");
    351     global_ret = EXIT_NOTCONFIGURED;
    352     GNUNET_SCHEDULER_shutdown ();
    353     return;
    354   }
    355   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    356                           &rc);
    357   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    358   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    359                                  NULL);
    360   kwh = TALER_EXCHANGE_post_kyc_wallet_create (ctx,
    361                                                CFG_exchange_url,
    362                                                &reserve_priv,
    363                                                &balance);
    364   if (NULL == kwh)
    365   {
    366     GNUNET_break (0);
    367     GNUNET_SCHEDULER_shutdown ();
    368   }
    369   GNUNET_assert (TALER_EC_NONE ==
    370                  TALER_EXCHANGE_post_kyc_wallet_start (kwh,
    371                                                        &kyc_wallet_cb,
    372                                                        NULL));
    373 }
    374 
    375 
    376 /**
    377  * The main function of the taler-exchange-kyc-trigger tool.
    378  *
    379  * @param argc number of arguments from the command line
    380  * @param argv command line arguments
    381  * @return 0 ok, 1 on error
    382  */
    383 int
    384 main (int argc,
    385       char *const *argv)
    386 {
    387   struct GNUNET_GETOPT_CommandLineOption options[] = {
    388     TALER_getopt_get_amount ('b',
    389                              "balance",
    390                              "AMOUNT",
    391                              "balance threshold to report to the exchange",
    392                              &balance),
    393     GNUNET_GETOPT_OPTION_END
    394   };
    395   enum GNUNET_GenericReturnValue ret;
    396 
    397   ret = GNUNET_PROGRAM_run (
    398     TALER_EXCHANGE_project_data (),
    399     argc, argv,
    400     "taler-exchange-kyc-trigger",
    401     gettext_noop (
    402       "Trigger KYC/AML measures based on high wallet balance for testing"),
    403     options,
    404     &run, NULL);
    405   if (GNUNET_SYSERR == ret)
    406     return EXIT_INVALIDARGUMENT;
    407   if (GNUNET_NO == ret)
    408     return EXIT_SUCCESS;
    409   return global_ret;
    410 }
    411 
    412 
    413 /* end of taler-exchange-kyc-trigger.c */