LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_kyc-wallet.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 80 87.5 %
Date: 2025-06-22 12:09:43 Functions: 5 5 100.0 %

          Line data    Source code
       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          21 : TEH_kyc_wallet_cleanup ()
     127             : {
     128             :   struct KycRequestContext *krc;
     129             : 
     130          21 :   while (NULL != (krc = krc_head))
     131             :   {
     132           0 :     GNUNET_CONTAINER_DLL_remove (krc_head,
     133             :                                  krc_tail,
     134             :                                  krc);
     135           0 :     MHD_resume_connection (krc->rc->connection);
     136             :   }
     137          21 : }
     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           7 : balance_iterator (void *cls,
     155             :                   struct GNUNET_TIME_Absolute limit,
     156             :                   TALER_EXCHANGEDB_KycAmountCallback cb,
     157             :                   void *cb_cls)
     158             : {
     159           7 :   struct KycRequestContext *krc = cls;
     160             :   enum GNUNET_GenericReturnValue ret;
     161             : 
     162             :   (void) limit;
     163           7 :   ret = cb (cb_cls,
     164           7 :             &krc->balance,
     165             :             GNUNET_TIME_absolute_get ());
     166           7 :   GNUNET_break (GNUNET_SYSERR != ret);
     167           7 :   if (GNUNET_OK != ret)
     168           0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     169           7 :   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           7 : legi_result_cb (
     182             :   void *cls,
     183             :   const struct TEH_LegitimizationCheckResult *lcr)
     184             : {
     185           7 :   struct KycRequestContext *krc = cls;
     186             : 
     187           7 :   TEH_plugin->preflight (TEH_plugin->cls);
     188           7 :   krc->lch = NULL;
     189           7 :   krc->http_status = lcr->http_status;
     190           7 :   krc->response = lcr->response;
     191           7 :   krc->kyc = lcr->kyc;
     192           7 :   krc->next_threshold = lcr->next_threshold;
     193           7 :   krc->expiration_date = lcr->expiration_date;
     194           7 :   GNUNET_CONTAINER_DLL_remove (krc_head,
     195             :                                krc_tail,
     196             :                                krc);
     197           7 :   MHD_resume_connection (krc->rc->connection);
     198           7 :   TALER_MHD_daemon_trigger ();
     199           7 : }
     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           7 : krc_cleaner (struct TEH_RequestContext *rc)
     209             : {
     210           7 :   struct KycRequestContext *krc = rc->rh_ctx;
     211             : 
     212           7 :   if (NULL != krc->lch)
     213             :   {
     214           0 :     TEH_legitimization_check_cancel (krc->lch);
     215           0 :     krc->lch = NULL;
     216             :   }
     217           7 :   GNUNET_free (krc->payto_uri.normalized_payto);
     218           7 :   GNUNET_free (krc);
     219           7 : }
     220             : 
     221             : 
     222             : MHD_RESULT
     223          14 : TEH_handler_kyc_wallet (
     224             :   struct TEH_RequestContext *rc,
     225             :   const json_t *root,
     226             :   const char *const args[])
     227             : {
     228          14 :   struct KycRequestContext *krc = rc->rh_ctx;
     229             : 
     230          14 :   if (NULL == krc)
     231             :   {
     232           7 :     krc = GNUNET_new (struct KycRequestContext);
     233           7 :     krc->rc = rc;
     234           7 :     rc->rh_ctx = krc;
     235           7 :     rc->rh_cleaner = &krc_cleaner;
     236             :     {
     237             :       struct TALER_ReserveSignatureP reserve_sig;
     238             :       struct GNUNET_JSON_Specification spec[] = {
     239           7 :         GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     240             :                                      &reserve_sig),
     241           7 :         GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     242             :                                      &krc->wallet_pub.reserve_pub),
     243           7 :         TALER_JSON_spec_amount ("balance",
     244             :                                 TEH_currency,
     245             :                                 &krc->balance),
     246           7 :         GNUNET_JSON_spec_end ()
     247             :       };
     248             :       enum GNUNET_GenericReturnValue ret;
     249             : 
     250             :       (void) args;
     251           7 :       ret = TALER_MHD_parse_json_data (rc->connection,
     252             :                                        root,
     253             :                                        spec);
     254           7 :       if (GNUNET_SYSERR == ret)
     255           0 :         return MHD_NO; /* hard failure */
     256           7 :       if (GNUNET_NO == ret)
     257           0 :         return MHD_YES; /* failure */
     258             : 
     259           7 :       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     260           7 :       if (GNUNET_OK !=
     261           7 :           TALER_wallet_account_setup_verify (
     262           7 :             &krc->wallet_pub.reserve_pub,
     263           7 :             &krc->balance,
     264             :             &reserve_sig))
     265             :       {
     266           0 :         GNUNET_break_op (0);
     267           0 :         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           7 :       = TALER_reserve_make_payto (TEH_base_url,
     276           7 :                                   &krc->wallet_pub.reserve_pub);
     277           7 :     TALER_normalized_payto_hash (krc->payto_uri,
     278             :                                  &krc->h_payto);
     279           7 :     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           7 :       GNUNET_asprintf (&fake_full_payto.full_payto,
     287             :                        "%s?receiver-name=wallet",
     288             :                        krc->payto_uri.normalized_payto);
     289          14 :       krc->lch = TEH_legitimization_check (
     290           7 :         &rc->async_scope_id,
     291             :         TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
     292             :         fake_full_payto,
     293           7 :         &krc->h_payto,
     294           7 :         &krc->wallet_pub,
     295             :         &balance_iterator,
     296             :         krc,
     297             :         &legi_result_cb,
     298             :         krc);
     299           7 :       GNUNET_free (fake_full_payto.full_payto);
     300             :     }
     301           7 :     GNUNET_assert (NULL != krc->lch);
     302           7 :     MHD_suspend_connection (rc->connection);
     303           7 :     GNUNET_CONTAINER_DLL_insert (krc_head,
     304             :                                  krc_tail,
     305             :                                  krc);
     306           7 :     return MHD_YES;
     307             :   }
     308           7 :   if (NULL != krc->response)
     309           0 :     return MHD_queue_response (rc->connection,
     310             :                                krc->http_status,
     311             :                                krc->response);
     312           7 :   if (krc->kyc.ok)
     313             :   {
     314           2 :     bool have_ts
     315           2 :       = TALER_amount_is_valid (&krc->next_threshold);
     316             : 
     317             :     /* KYC not required or already satisfied */
     318           2 :     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           5 :   return TEH_RESPONSE_reply_kyc_required (rc->connection,
     330           5 :                                           &krc->h_payto,
     331           5 :                                           &krc->kyc,
     332             :                                           false);
     333             : }
     334             : 
     335             : 
     336             : /* end of taler-exchange-httpd_kyc-wallet.c */

Generated by: LCOV version 1.16