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 (10766B)


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