LCOV - code coverage report
Current view: top level - lib - exchange_api_reveal_withdraw.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 69 115 60.0 %
Date: 2025-06-05 21:03:14 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2023-2025 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
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file lib/exchange_api_reveal_withdraw.c
      19             :  * @brief Implementation of /reveal-withdraw requests
      20             :  * @author Özgür Kesim
      21             :  */
      22             : 
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_common.h>
      25             : #include <jansson.h>
      26             : #include <microhttpd.h> /* just for HTTP status codes */
      27             : #include <gnunet/gnunet_util_lib.h>
      28             : #include <gnunet/gnunet_json_lib.h>
      29             : #include <gnunet/gnunet_curl_lib.h>
      30             : #include "taler_curl_lib.h"
      31             : #include "taler_json_lib.h"
      32             : #include "taler_exchange_service.h"
      33             : #include "exchange_api_common.h"
      34             : #include "exchange_api_handle.h"
      35             : #include "taler_signatures.h"
      36             : #include "exchange_api_curl_defaults.h"
      37             : 
      38             : /**
      39             :  * Handler for a running reveal-withdraw  request
      40             :  */
      41             : struct TALER_EXCHANGE_RevealWithdrawHandle
      42             : {
      43             :   /**
      44             :    * The commitment from the previous call withdraw
      45             :    */
      46             :   const struct TALER_HashBlindedPlanchetsP *planchets_h;
      47             : 
      48             :   /**
      49             :    * Number of coins for which to reveal tuples of seeds
      50             :    */
      51             :   size_t num_coins;
      52             : 
      53             :   /**
      54             :    * The TALER_CNC_KAPPA-1 tuple of seeds to reveal
      55             :    */
      56             :   struct TALER_RevealWithdrawMasterSeedsP seeds;
      57             : 
      58             :   /**
      59             :    * The url for the reveal request
      60             :    */
      61             :   char *request_url;
      62             : 
      63             :   /**
      64             :    * CURL handle for the request job.
      65             :    */
      66             :   struct GNUNET_CURL_Job *job;
      67             : 
      68             :   /**
      69             :    * Post Context
      70             :    */
      71             :   struct TALER_CURL_PostContext post_ctx;
      72             : 
      73             :   /**
      74             :    * Callback
      75             :    */
      76             :   TALER_EXCHANGE_RevealWithdrawCallback callback;
      77             : 
      78             :   /**
      79             :    * Reveal
      80             :    */
      81             :   void *callback_cls;
      82             : };
      83             : 
      84             : 
      85             : /**
      86             :  * We got a 200 OK response for the /reveal-withdraw operation.
      87             :  * Extract the signed blindedcoins and return it to the caller.
      88             :  *
      89             :  * @param wrh operation handle
      90             :  * @param j_response reply from the exchange
      91             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
      92             :  */
      93             : static enum GNUNET_GenericReturnValue
      94           3 : reveal_withdraw_ok (
      95             :   struct TALER_EXCHANGE_RevealWithdrawHandle *wrh,
      96             :   const json_t *j_response)
      97             : {
      98           3 :   struct TALER_EXCHANGE_RevealWithdrawResponse response = {
      99             :     .hr.reply = j_response,
     100             :     .hr.http_status = MHD_HTTP_OK,
     101             :   };
     102             :   const json_t *j_sigs;
     103             :   struct GNUNET_JSON_Specification spec[] = {
     104           3 :     GNUNET_JSON_spec_array_const ("ev_sigs",
     105             :                                   &j_sigs),
     106           3 :     GNUNET_JSON_spec_end ()
     107             :   };
     108             : 
     109           3 :   if (GNUNET_OK !=
     110           3 :       GNUNET_JSON_parse (j_response,
     111             :                          spec,
     112             :                          NULL, NULL))
     113             :   {
     114           0 :     GNUNET_break_op (0);
     115           0 :     return GNUNET_SYSERR;
     116             :   }
     117             : 
     118           3 :   if (wrh->num_coins != json_array_size (j_sigs))
     119             :   {
     120             :     /* Number of coins generated does not match our expectation */
     121           0 :     GNUNET_break_op (0);
     122           0 :     return GNUNET_SYSERR;
     123             :   }
     124             : 
     125           3 :   {
     126           3 :     struct TALER_BlindedDenominationSignature denom_sigs[wrh->num_coins];
     127             :     json_t *j_sig;
     128             :     size_t n;
     129             : 
     130             :     /* Reconstruct the coins and unblind the signatures */
     131          10 :     json_array_foreach (j_sigs, n, j_sig)
     132             :     {
     133             :       struct GNUNET_JSON_Specification ispec[] = {
     134           7 :         TALER_JSON_spec_blinded_denom_sig (NULL,
     135             :                                            &denom_sigs[n]),
     136           7 :         GNUNET_JSON_spec_end ()
     137             :       };
     138             : 
     139           7 :       if (GNUNET_OK !=
     140           7 :           GNUNET_JSON_parse (j_sig,
     141             :                              ispec,
     142             :                              NULL, NULL))
     143             :       {
     144           0 :         GNUNET_break_op (0);
     145           0 :         return GNUNET_SYSERR;
     146             :       }
     147             :     }
     148             : 
     149           3 :     response.details.ok.num_sigs = wrh->num_coins;
     150           3 :     response.details.ok.blinded_denom_sigs = denom_sigs;
     151           3 :     wrh->callback (wrh->callback_cls,
     152             :                    &response);
     153             :     /* Make sure the callback isn't called again */
     154           3 :     wrh->callback = NULL;
     155             :     /* Free resources */
     156          10 :     for (size_t i = 0; i < wrh->num_coins; i++)
     157           7 :       TALER_blinded_denom_sig_free (&denom_sigs[i]);
     158             :   }
     159             : 
     160           3 :   return GNUNET_OK;
     161             : }
     162             : 
     163             : 
     164             : /**
     165             :  * Function called when we're done processing the
     166             :  * HTTP /reveal-withdraw request.
     167             :  *
     168             :  * @param cls the `struct TALER_EXCHANGE_RevealWithdrawHandle`
     169             :  * @param response_code The HTTP response code
     170             :  * @param response response data
     171             :  */
     172             : static void
     173           3 : handle_reveal_withdraw_finished (
     174             :   void *cls,
     175             :   long response_code,
     176             :   const void *response)
     177             : {
     178           3 :   struct TALER_EXCHANGE_RevealWithdrawHandle *wrh = cls;
     179           3 :   const json_t *j_response = response;
     180           3 :   struct TALER_EXCHANGE_RevealWithdrawResponse awr = {
     181             :     .hr.reply = j_response,
     182           3 :     .hr.http_status = (unsigned int) response_code
     183             :   };
     184             : 
     185           3 :   wrh->job = NULL;
     186           3 :   switch (response_code)
     187             :   {
     188           0 :   case 0:
     189           0 :     awr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     190           0 :     break;
     191           3 :   case MHD_HTTP_OK:
     192             :     {
     193             :       enum GNUNET_GenericReturnValue ret;
     194             : 
     195           3 :       ret = reveal_withdraw_ok (wrh,
     196             :                                 j_response);
     197           3 :       if (GNUNET_OK != ret)
     198             :       {
     199           0 :         GNUNET_break_op (0);
     200           0 :         awr.hr.http_status = 0;
     201           0 :         awr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     202           0 :         break;
     203             :       }
     204           3 :       GNUNET_assert (NULL == wrh->callback);
     205           3 :       TALER_EXCHANGE_reveal_withdraw_cancel (wrh);
     206           3 :       return;
     207             :     }
     208           0 :   case MHD_HTTP_BAD_REQUEST:
     209             :     /* This should never happen, either us or the exchange is buggy
     210             :        (or API version conflict); just pass JSON reply to the application */
     211           0 :     awr.hr.ec = TALER_JSON_get_error_code (j_response);
     212           0 :     awr.hr.hint = TALER_JSON_get_error_hint (j_response);
     213           0 :     break;
     214           0 :   case MHD_HTTP_NOT_FOUND:
     215             :     /* Nothing really to verify, the exchange basically just says
     216             :        that it doesn't know this age-withdraw commitment. */
     217           0 :     awr.hr.ec = TALER_JSON_get_error_code (j_response);
     218           0 :     awr.hr.hint = TALER_JSON_get_error_hint (j_response);
     219           0 :     break;
     220           0 :   case MHD_HTTP_CONFLICT:
     221             :     /* An age commitment for one of the coins did not fulfill
     222             :      * the required maximum age requirement of the corresponding
     223             :      * reserve.
     224             :      * Error code: TALER_EC_EXCHANGE_GENERIC_COIN_AGE_REQUIREMENT_FAILURE
     225             :      * or TALER_EC_EXCHANGE_AGE_WITHDRAW_REVEAL_INVALID_HASH.
     226             :      */
     227           0 :     awr.hr.ec = TALER_JSON_get_error_code (j_response);
     228           0 :     awr.hr.hint = TALER_JSON_get_error_hint (j_response);
     229           0 :     break;
     230           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     231             :     /* Server had an internal issue; we should retry, but this API
     232             :        leaves this to the application */
     233           0 :     awr.hr.ec = TALER_JSON_get_error_code (j_response);
     234           0 :     awr.hr.hint = TALER_JSON_get_error_hint (j_response);
     235           0 :     break;
     236           0 :   default:
     237             :     /* unexpected response code */
     238           0 :     GNUNET_break_op (0);
     239           0 :     awr.hr.ec = TALER_JSON_get_error_code (j_response);
     240           0 :     awr.hr.hint = TALER_JSON_get_error_hint (j_response);
     241           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     242             :                 "Unexpected response code %u/%d for exchange age-withdraw\n",
     243             :                 (unsigned int) response_code,
     244             :                 (int) awr.hr.ec);
     245           0 :     break;
     246             :   }
     247           0 :   wrh->callback (wrh->callback_cls,
     248             :                  &awr);
     249           0 :   TALER_EXCHANGE_reveal_withdraw_cancel (wrh);
     250             : }
     251             : 
     252             : 
     253             : /**
     254             :  * Call /reveal-withdraw
     255             :  *
     256             :  * @param curl_ctx The context for CURL
     257             :  * @param wrh The handler
     258             :  */
     259             : static void
     260           3 : perform_protocol (
     261             :   struct GNUNET_CURL_Context *curl_ctx,
     262             :   struct TALER_EXCHANGE_RevealWithdrawHandle *wrh)
     263             : {
     264             :   CURL *curlh;
     265             :   json_t *j_array_of_secrets;
     266             : 
     267           3 :   j_array_of_secrets = json_array ();
     268           3 :   GNUNET_assert (NULL != j_array_of_secrets);
     269             : 
     270           9 :   for (uint8_t k = 0; k < TALER_CNC_KAPPA - 1; k++)
     271             :   {
     272           6 :     json_t *j_sec = GNUNET_JSON_from_data_auto (&wrh->seeds.tuple[k]);
     273           6 :     GNUNET_assert (NULL != j_sec);
     274           6 :     GNUNET_assert (0 == json_array_append_new (j_array_of_secrets,
     275             :                                                j_sec));
     276             :   }
     277             :   {
     278             :     json_t *j_request_body;
     279             : 
     280           3 :     j_request_body = GNUNET_JSON_PACK (
     281             :       GNUNET_JSON_pack_data_auto ("planchets_h",
     282             :                                   wrh->planchets_h),
     283             :       GNUNET_JSON_pack_array_steal ("disclosed_batch_seeds",
     284             :                                     j_array_of_secrets));
     285           3 :     GNUNET_assert (NULL != j_request_body);
     286             : 
     287           3 :     curlh = TALER_EXCHANGE_curl_easy_get_ (wrh->request_url);
     288           3 :     GNUNET_assert (NULL != curlh);
     289           3 :     GNUNET_assert (GNUNET_OK ==
     290             :                    TALER_curl_easy_post (&wrh->post_ctx,
     291             :                                          curlh,
     292             :                                          j_request_body));
     293             : 
     294           3 :     json_decref (j_request_body);
     295             :   }
     296             : 
     297           6 :   wrh->job = GNUNET_CURL_job_add2 (
     298             :     curl_ctx,
     299             :     curlh,
     300           3 :     wrh->post_ctx.headers,
     301             :     &handle_reveal_withdraw_finished,
     302             :     wrh);
     303           3 :   if (NULL == wrh->job)
     304             :   {
     305           0 :     GNUNET_break (0);
     306           0 :     if (NULL != curlh)
     307           0 :       curl_easy_cleanup (curlh);
     308           0 :     TALER_EXCHANGE_reveal_withdraw_cancel (wrh);
     309             :   }
     310             : 
     311           3 :   return;
     312             : }
     313             : 
     314             : 
     315             : struct TALER_EXCHANGE_RevealWithdrawHandle *
     316           3 : TALER_EXCHANGE_reveal_withdraw (
     317             :   struct GNUNET_CURL_Context *curl_ctx,
     318             :   const char *exchange_url,
     319             :   size_t num_coins,
     320             :   const struct TALER_HashBlindedPlanchetsP *planchets_h,
     321             :   const struct TALER_RevealWithdrawMasterSeedsP *seeds,
     322             :   TALER_EXCHANGE_RevealWithdrawCallback reveal_cb,
     323             :   void *reveal_cb_cls)
     324             : {
     325             :   struct TALER_EXCHANGE_RevealWithdrawHandle *wrh =
     326           3 :     GNUNET_new (struct TALER_EXCHANGE_RevealWithdrawHandle);
     327           3 :   wrh->planchets_h = planchets_h;
     328           3 :   wrh->num_coins = num_coins;
     329           3 :   wrh->seeds = *seeds;
     330           3 :   wrh->callback = reveal_cb;
     331           3 :   wrh->callback_cls = reveal_cb_cls;
     332           3 :   wrh->request_url = TALER_url_join (exchange_url,
     333             :                                      "reveal-withdraw",
     334             :                                      NULL);
     335           3 :   if (NULL == wrh->request_url)
     336             :   {
     337           0 :     GNUNET_break (0);
     338           0 :     GNUNET_free (wrh);
     339           0 :     return NULL;
     340             :   }
     341             : 
     342           3 :   perform_protocol (curl_ctx, wrh);
     343             : 
     344           3 :   return wrh;
     345             : }
     346             : 
     347             : 
     348             : void
     349           3 : TALER_EXCHANGE_reveal_withdraw_cancel (
     350             :   struct TALER_EXCHANGE_RevealWithdrawHandle *wrh)
     351             : {
     352           3 :   if (NULL != wrh->job)
     353             :   {
     354           0 :     GNUNET_CURL_job_cancel (wrh->job);
     355           0 :     wrh->job = NULL;
     356             :   }
     357           3 :   TALER_curl_easy_post_finished (&wrh->post_ctx);
     358             : 
     359           3 :   if (NULL != wrh->request_url)
     360           3 :     GNUNET_free (wrh->request_url);
     361             : 
     362           3 :   GNUNET_free (wrh);
     363           3 : }
     364             : 
     365             : 
     366             : /* exchange_api_reveal_withdraw.c */

Generated by: LCOV version 1.16