LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_kyc-wallet.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 87.5 % 80 70
Test Date: 2026-03-18 14:32:00 Functions: 100.0 % 5 5

            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           18 : TEH_kyc_wallet_cleanup ()
     127              : {
     128              :   struct KycRequestContext *krc;
     129              : 
     130           18 :   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           18 : }
     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 2.0-1