exchange

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

taler-exchange-httpd_kyc-wallet.c (9140B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2021, 2022, 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 taler-exchange-httpd_kyc-wallet.c
     18  * @brief Handle request for wallet for KYC check.
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h>
     26 #include <pthread.h>
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/taler_mhd_lib.h"
     29 #include "taler/taler_kyclogic_lib.h"
     30 #include "taler-exchange-httpd_common_kyc.h"
     31 #include "taler-exchange-httpd_kyc-wallet.h"
     32 #include "taler-exchange-httpd_responses.h"
     33 
     34 
     35 /**
     36  * Context for the request.
     37  */
     38 struct KycRequestContext
     39 {
     40 
     41   /**
     42    * Kept in a DLL.
     43    */
     44   struct KycRequestContext *next;
     45 
     46   /**
     47    * Kept in a DLL.
     48    */
     49   struct KycRequestContext *prev;
     50 
     51   /**
     52    * Handle for legitimization check.
     53    */
     54   struct TEH_LegitimizationCheckHandle *lch;
     55 
     56   /**
     57    * Payto URI of the reserve.
     58    */
     59   struct TALER_NormalizedPayto payto_uri;
     60 
     61   /**
     62    * Request context.
     63    */
     64   struct TEH_RequestContext *rc;
     65 
     66   /**
     67    * Response to return. Note that the response must
     68    * be queued or destroyed by the callee.  NULL
     69    * if the legitimization check was successful and the handler should return
     70    * a handler-specific result.
     71    */
     72   struct MHD_Response *response;
     73 
     74   /**
     75    * Public key of the reserve/wallet this is about.
     76    */
     77   struct TALER_NormalizedPaytoHashP h_payto;
     78 
     79   /**
     80    * The wallet's public key
     81    */
     82   union TALER_AccountPublicKeyP wallet_pub;
     83 
     84   /**
     85    * Balance threshold crossed by the wallet.
     86    */
     87   struct TALER_Amount balance;
     88 
     89   /**
     90    * KYC status, with row with the legitimization requirement.
     91    */
     92   struct TALER_EXCHANGEDB_KycStatus kyc;
     93 
     94   /**
     95    * Smallest amount (over any timeframe) that may
     96    * require additional KYC checks (if @a kyc.ok).
     97    */
     98   struct TALER_Amount next_threshold;
     99 
    100   /**
    101    * When do the current KYC rules possibly expire.
    102    * Only valid if @a kyc.ok.
    103    */
    104   struct GNUNET_TIME_Timestamp expiration_date;
    105 
    106   /**
    107    * HTTP status code for @a response, or 0
    108    */
    109   unsigned int http_status;
    110 
    111 };
    112 
    113 
    114 /**
    115  * Kept in a DLL.
    116  */
    117 static struct KycRequestContext *krc_head;
    118 
    119 /**
    120  * Kept in a DLL.
    121  */
    122 static struct KycRequestContext *krc_tail;
    123 
    124 
    125 void
    126 TEH_kyc_wallet_cleanup ()
    127 {
    128   struct KycRequestContext *krc;
    129 
    130   while (NULL != (krc = krc_head))
    131   {
    132     GNUNET_CONTAINER_DLL_remove (krc_head,
    133                                  krc_tail,
    134                                  krc);
    135     MHD_resume_connection (krc->rc->connection);
    136   }
    137 }
    138 
    139 
    140 /**
    141  * Function called to iterate over KYC-relevant
    142  * transaction amounts for a particular time range.
    143  * Returns the wallet balance.
    144  *
    145  * @param cls closure, a `struct KycRequestContext`
    146  * @param limit maximum time-range for which events
    147  *        should be fetched (timestamp in the past)
    148  * @param cb function to call on each event found,
    149  *        events must be returned in reverse chronological
    150  *        order
    151  * @param cb_cls closure for @a cb
    152  */
    153 static enum GNUNET_DB_QueryStatus
    154 balance_iterator (void *cls,
    155                   struct GNUNET_TIME_Absolute limit,
    156                   TALER_EXCHANGEDB_KycAmountCallback cb,
    157                   void *cb_cls)
    158 {
    159   struct KycRequestContext *krc = cls;
    160   enum GNUNET_GenericReturnValue ret;
    161 
    162   (void) limit;
    163   ret = cb (cb_cls,
    164             &krc->balance,
    165             GNUNET_TIME_absolute_get ());
    166   GNUNET_break (GNUNET_SYSERR != ret);
    167   if (GNUNET_OK != ret)
    168     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    169   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    170 }
    171 
    172 
    173 /**
    174  * Function called with the result of a legitimization
    175  * check.
    176  *
    177  * @param cls must be a `struct KycRequestContext *`
    178  * @param lcr legitimization check result
    179  */
    180 static void
    181 legi_result_cb (
    182   void *cls,
    183   const struct TEH_LegitimizationCheckResult *lcr)
    184 {
    185   struct KycRequestContext *krc = cls;
    186 
    187   TEH_plugin->preflight (TEH_plugin->cls);
    188   krc->lch = NULL;
    189   krc->http_status = lcr->http_status;
    190   krc->response = lcr->response;
    191   krc->kyc = lcr->kyc;
    192   krc->next_threshold = lcr->next_threshold;
    193   krc->expiration_date = lcr->expiration_date;
    194   GNUNET_CONTAINER_DLL_remove (krc_head,
    195                                krc_tail,
    196                                krc);
    197   MHD_resume_connection (krc->rc->connection);
    198   TALER_MHD_daemon_trigger ();
    199 }
    200 
    201 
    202 /**
    203  * Function to clean up our rh_ctx in @a rc
    204  *
    205  * @param[in,out] rc context to clean up
    206  */
    207 static void
    208 krc_cleaner (struct TEH_RequestContext *rc)
    209 {
    210   struct KycRequestContext *krc = rc->rh_ctx;
    211 
    212   if (NULL != krc->lch)
    213   {
    214     TEH_legitimization_check_cancel (krc->lch);
    215     krc->lch = NULL;
    216   }
    217   GNUNET_free (krc->payto_uri.normalized_payto);
    218   GNUNET_free (krc);
    219 }
    220 
    221 
    222 MHD_RESULT
    223 TEH_handler_kyc_wallet (
    224   struct TEH_RequestContext *rc,
    225   const json_t *root,
    226   const char *const args[])
    227 {
    228   struct KycRequestContext *krc = rc->rh_ctx;
    229 
    230   if (NULL == krc)
    231   {
    232     krc = GNUNET_new (struct KycRequestContext);
    233     krc->rc = rc;
    234     rc->rh_ctx = krc;
    235     rc->rh_cleaner = &krc_cleaner;
    236     {
    237       struct TALER_ReserveSignatureP reserve_sig;
    238       struct GNUNET_JSON_Specification spec[] = {
    239         GNUNET_JSON_spec_fixed_auto ("reserve_sig",
    240                                      &reserve_sig),
    241         GNUNET_JSON_spec_fixed_auto ("reserve_pub",
    242                                      &krc->wallet_pub.reserve_pub),
    243         TALER_JSON_spec_amount ("balance",
    244                                 TEH_currency,
    245                                 &krc->balance),
    246         GNUNET_JSON_spec_end ()
    247       };
    248       enum GNUNET_GenericReturnValue ret;
    249 
    250       (void) args;
    251       ret = TALER_MHD_parse_json_data (rc->connection,
    252                                        root,
    253                                        spec);
    254       if (GNUNET_SYSERR == ret)
    255         return MHD_NO; /* hard failure */
    256       if (GNUNET_NO == ret)
    257         return MHD_YES; /* failure */
    258 
    259       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
    260       if (GNUNET_OK !=
    261           TALER_wallet_account_setup_verify (
    262             &krc->wallet_pub.reserve_pub,
    263             &krc->balance,
    264             &reserve_sig))
    265       {
    266         GNUNET_break_op (0);
    267         return TALER_MHD_reply_with_error (
    268           rc->connection,
    269           MHD_HTTP_FORBIDDEN,
    270           TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID,
    271           NULL);
    272       }
    273     }
    274     krc->payto_uri
    275       = TALER_reserve_make_payto (TEH_base_url,
    276                                   &krc->wallet_pub.reserve_pub);
    277     TALER_normalized_payto_hash (krc->payto_uri,
    278                                  &krc->h_payto);
    279     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    280                 "h_payto of wallet %s is %s\n",
    281                 krc->payto_uri.normalized_payto,
    282                 TALER_B2S (&krc->h_payto));
    283     {
    284       struct TALER_FullPayto fake_full_payto;
    285 
    286       GNUNET_asprintf (&fake_full_payto.full_payto,
    287                        "%s?receiver-name=wallet",
    288                        krc->payto_uri.normalized_payto);
    289       krc->lch = TEH_legitimization_check (
    290         &rc->async_scope_id,
    291         TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
    292         fake_full_payto,
    293         &krc->h_payto,
    294         &krc->wallet_pub,
    295         &balance_iterator,
    296         krc,
    297         &legi_result_cb,
    298         krc);
    299       GNUNET_free (fake_full_payto.full_payto);
    300     }
    301     GNUNET_assert (NULL != krc->lch);
    302     MHD_suspend_connection (rc->connection);
    303     GNUNET_CONTAINER_DLL_insert (krc_head,
    304                                  krc_tail,
    305                                  krc);
    306     return MHD_YES;
    307   }
    308   if (NULL != krc->response)
    309     return MHD_queue_response (rc->connection,
    310                                krc->http_status,
    311                                krc->response);
    312   if (krc->kyc.ok)
    313   {
    314     bool have_ts
    315       = TALER_amount_is_valid (&krc->next_threshold);
    316 
    317     /* KYC not required or already satisfied */
    318     return TALER_MHD_REPLY_JSON_PACK (
    319       rc->connection,
    320       MHD_HTTP_OK,
    321       GNUNET_JSON_pack_timestamp ("expiration_time",
    322                                   krc->expiration_date),
    323       GNUNET_JSON_pack_allow_null (
    324         TALER_JSON_pack_amount ("next_threshold",
    325                                 have_ts
    326                                 ? &krc->next_threshold
    327                                 : NULL)));
    328   }
    329   return TEH_RESPONSE_reply_kyc_required (rc->connection,
    330                                           &krc->h_payto,
    331                                           &krc->kyc,
    332                                           false);
    333 }
    334 
    335 
    336 /* end of taler-exchange-httpd_kyc-wallet.c */