LCOV - code coverage report
Current view: top level - lib - exchange_api_batch_deposit.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 174 289 60.2 %
Date: 2025-06-22 12:09:43 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :    This file is part of TALER
       3             :    Copyright (C) 2014-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
      15             :    <http://www.gnu.org/licenses/>
      16             :  */
      17             : /**
      18             :  * @file lib/exchange_api_batch_deposit.c
      19             :  * @brief Implementation of the /batch-deposit request of the exchange's HTTP API
      20             :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "taler/platform.h"
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_json_lib.h>
      28             : #include <gnunet/gnunet_curl_lib.h>
      29             : #include "taler/taler_json_lib.h"
      30             : #include "taler/taler_auditor_service.h"
      31             : #include "taler/taler_exchange_service.h"
      32             : #include "exchange_api_common.h"
      33             : #include "exchange_api_handle.h"
      34             : #include "taler/taler_signatures.h"
      35             : #include "exchange_api_curl_defaults.h"
      36             : 
      37             : 
      38             : /**
      39             :  * 1:#AUDITOR_CHANCE is the probability that we report deposits
      40             :  * to the auditor.
      41             :  *
      42             :  * 20==5% of going to auditor. This is possibly still too high, but set
      43             :  * deliberately this high for testing
      44             :  */
      45             : #define AUDITOR_CHANCE 20
      46             : 
      47             : 
      48             : /**
      49             :  * Entry in list of ongoing interactions with an auditor.
      50             :  */
      51             : struct TEAH_AuditorInteractionEntry
      52             : {
      53             :   /**
      54             :    * DLL entry.
      55             :    */
      56             :   struct TEAH_AuditorInteractionEntry *next;
      57             : 
      58             :   /**
      59             :    * DLL entry.
      60             :    */
      61             :   struct TEAH_AuditorInteractionEntry *prev;
      62             : 
      63             :   /**
      64             :    * URL of our auditor. For logging.
      65             :    */
      66             :   const char *auditor_url;
      67             : 
      68             :   /**
      69             :    * Interaction state.
      70             :    */
      71             :   struct TALER_AUDITOR_DepositConfirmationHandle *dch;
      72             : 
      73             :   /**
      74             :    * Batch deposit this is for.
      75             :    */
      76             :   struct TALER_EXCHANGE_BatchDepositHandle *dh;
      77             : };
      78             : 
      79             : 
      80             : /**
      81             :  * @brief A Deposit Handle
      82             :  */
      83             : struct TALER_EXCHANGE_BatchDepositHandle
      84             : {
      85             : 
      86             :   /**
      87             :    * The keys of the exchange.
      88             :    */
      89             :   struct TALER_EXCHANGE_Keys *keys;
      90             : 
      91             :   /**
      92             :    * Context for our curl request(s).
      93             :    */
      94             :   struct GNUNET_CURL_Context *ctx;
      95             : 
      96             :   /**
      97             :    * The url for this request.
      98             :    */
      99             :   char *url;
     100             : 
     101             :   /**
     102             :    * Context for #TEH_curl_easy_post(). Keeps the data that must
     103             :    * persist for Curl to make the upload.
     104             :    */
     105             :   struct TALER_CURL_PostContext post_ctx;
     106             : 
     107             :   /**
     108             :    * Handle for the request.
     109             :    */
     110             :   struct GNUNET_CURL_Job *job;
     111             : 
     112             :   /**
     113             :    * Function to call with the result.
     114             :    */
     115             :   TALER_EXCHANGE_BatchDepositResultCallback cb;
     116             : 
     117             :   /**
     118             :    * Closure for @a cb.
     119             :    */
     120             :   void *cb_cls;
     121             : 
     122             :   /**
     123             :    * Details about the contract.
     124             :    */
     125             :   struct TALER_EXCHANGE_DepositContractDetail dcd;
     126             : 
     127             :   /**
     128             :    * Array with details about the coins.
     129             :    */
     130             :   struct TALER_EXCHANGE_CoinDepositDetail *cdds;
     131             : 
     132             :   /**
     133             :    * Hash of the merchant's wire details.
     134             :    */
     135             :   struct TALER_MerchantWireHashP h_wire;
     136             : 
     137             :   /**
     138             :    * Hash over the extensions, or all zero.
     139             :    */
     140             :   struct TALER_ExtensionPolicyHashP h_policy;
     141             : 
     142             :   /**
     143             :    * Time when this confirmation was generated / when the exchange received
     144             :    * the deposit request.
     145             :    */
     146             :   struct GNUNET_TIME_Timestamp exchange_timestamp;
     147             : 
     148             :   /**
     149             :    * Exchange signature, set for #auditor_cb.
     150             :    */
     151             :   struct TALER_ExchangeSignatureP exchange_sig;
     152             : 
     153             :   /**
     154             :    * Head of DLL of interactions with this auditor.
     155             :    */
     156             :   struct TEAH_AuditorInteractionEntry *ai_head;
     157             : 
     158             :   /**
     159             :    * Tail of DLL of interactions with this auditor.
     160             :    */
     161             :   struct TEAH_AuditorInteractionEntry *ai_tail;
     162             : 
     163             :   /**
     164             :    * Result to return to the application once @e ai_head is empty.
     165             :    */
     166             :   struct TALER_EXCHANGE_BatchDepositResult dr;
     167             : 
     168             :   /**
     169             :    * Exchange signing public key, set for #auditor_cb.
     170             :    */
     171             :   struct TALER_ExchangePublicKeyP exchange_pub;
     172             : 
     173             :   /**
     174             :    * Total amount deposited without fees as calculated by us.
     175             :    */
     176             :   struct TALER_Amount total_without_fee;
     177             : 
     178             :   /**
     179             :    * Response object to free at the end.
     180             :    */
     181             :   json_t *response;
     182             : 
     183             :   /**
     184             :    * Chance that we will inform the auditor about the deposit
     185             :    * is 1:n, where the value of this field is "n".
     186             :    */
     187             :   unsigned int auditor_chance;
     188             : 
     189             :   /**
     190             :    * Length of the @e cdds array.
     191             :    */
     192             :   unsigned int num_cdds;
     193             : 
     194             : };
     195             : 
     196             : 
     197             : /**
     198             :  * Finish batch deposit operation by calling the callback.
     199             :  *
     200             :  * @param[in] dh handle to finished batch deposit operation
     201             :  */
     202             : static void
     203         104 : finish_dh (struct TALER_EXCHANGE_BatchDepositHandle *dh)
     204             : {
     205         104 :   dh->cb (dh->cb_cls,
     206         104 :           &dh->dr);
     207         104 :   TALER_EXCHANGE_batch_deposit_cancel (dh);
     208         104 : }
     209             : 
     210             : 
     211             : /**
     212             :  * Function called with the result from our call to the
     213             :  * auditor's /deposit-confirmation handler.
     214             :  *
     215             :  * @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
     216             :  * @param dcr response
     217             :  */
     218             : static void
     219           8 : acc_confirmation_cb (
     220             :   void *cls,
     221             :   const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
     222             : {
     223           8 :   struct TEAH_AuditorInteractionEntry *aie = cls;
     224           8 :   struct TALER_EXCHANGE_BatchDepositHandle *dh = aie->dh;
     225             : 
     226           8 :   if (MHD_HTTP_OK != dcr->hr.http_status)
     227             :   {
     228           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     229             :                 "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n",
     230             :                 aie->auditor_url,
     231             :                 dcr->hr.http_status,
     232             :                 dcr->hr.ec);
     233             :   }
     234           8 :   GNUNET_CONTAINER_DLL_remove (dh->ai_head,
     235             :                                dh->ai_tail,
     236             :                                aie);
     237           8 :   GNUNET_free (aie);
     238           8 :   if (NULL == dh->ai_head)
     239           8 :     finish_dh (dh);
     240           8 : }
     241             : 
     242             : 
     243             : /**
     244             :  * Function called for each auditor to give us a chance to possibly
     245             :  * launch a deposit confirmation interaction.
     246             :  *
     247             :  * @param cls closure
     248             :  * @param auditor_url base URL of the auditor
     249             :  * @param auditor_pub public key of the auditor
     250             :  */
     251             : static void
     252          38 : auditor_cb (void *cls,
     253             :             const char *auditor_url,
     254             :             const struct TALER_AuditorPublicKeyP *auditor_pub)
     255          38 : {
     256          38 :   struct TALER_EXCHANGE_BatchDepositHandle *dh = cls;
     257             :   const struct TALER_EXCHANGE_SigningPublicKey *spk;
     258             :   struct TEAH_AuditorInteractionEntry *aie;
     259          38 :   const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (
     260             :                                                   dh->num_cdds)];
     261          38 :   const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL (
     262             :                                                   dh->num_cdds)];
     263             : 
     264          76 :   for (unsigned int i = 0; i<dh->num_cdds; i++)
     265             :   {
     266          38 :     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i];
     267             : 
     268          38 :     csigs[i] = &cdd->coin_sig;
     269          38 :     cpubs[i] = &cdd->coin_pub;
     270             :   }
     271             : 
     272          38 :   if (0 !=
     273          38 :       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
     274             :                                 dh->auditor_chance))
     275             :   {
     276          30 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     277             :                 "Not providing deposit confirmation to auditor\n");
     278          30 :     return;
     279             :   }
     280           8 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     281             :               "Will provide deposit confirmation to auditor `%s'\n",
     282             :               TALER_B2S (auditor_pub));
     283           8 :   spk = TALER_EXCHANGE_get_signing_key_info (dh->keys,
     284           8 :                                              &dh->exchange_pub);
     285           8 :   if (NULL == spk)
     286             :   {
     287           0 :     GNUNET_break_op (0);
     288           0 :     return;
     289             :   }
     290           8 :   aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
     291           8 :   aie->dh = dh;
     292           8 :   aie->auditor_url = auditor_url;
     293          16 :   aie->dch = TALER_AUDITOR_deposit_confirmation (
     294             :     dh->ctx,
     295             :     auditor_url,
     296           8 :     &dh->h_wire,
     297           8 :     &dh->h_policy,
     298           8 :     &dh->dcd.h_contract_terms,
     299             :     dh->exchange_timestamp,
     300             :     dh->dcd.wire_deadline,
     301             :     dh->dcd.refund_deadline,
     302           8 :     &dh->total_without_fee,
     303             :     dh->num_cdds,
     304             :     cpubs,
     305             :     csigs,
     306           8 :     &dh->dcd.merchant_pub,
     307           8 :     &dh->exchange_pub,
     308           8 :     &dh->exchange_sig,
     309           8 :     &dh->keys->master_pub,
     310             :     spk->valid_from,
     311             :     spk->valid_until,
     312             :     spk->valid_legal,
     313             :     &spk->master_sig,
     314             :     &acc_confirmation_cb,
     315             :     aie);
     316           8 :   GNUNET_CONTAINER_DLL_insert (dh->ai_head,
     317             :                                dh->ai_tail,
     318             :                                aie);
     319             : }
     320             : 
     321             : 
     322             : /**
     323             :  * Function called when we're done processing the
     324             :  * HTTP /deposit request.
     325             :  *
     326             :  * @param cls the `struct TALER_EXCHANGE_BatchDepositHandle`
     327             :  * @param response_code HTTP response code, 0 on error
     328             :  * @param response parsed JSON result, NULL on error
     329             :  */
     330             : static void
     331         104 : handle_deposit_finished (void *cls,
     332             :                          long response_code,
     333             :                          const void *response)
     334             : {
     335         104 :   struct TALER_EXCHANGE_BatchDepositHandle *dh = cls;
     336         104 :   const json_t *j = response;
     337         104 :   struct TALER_EXCHANGE_BatchDepositResult *dr = &dh->dr;
     338             : 
     339         104 :   dh->job = NULL;
     340         104 :   dh->response = json_incref ((json_t*) j);
     341         104 :   dr->hr.reply = dh->response;
     342         104 :   dr->hr.http_status = (unsigned int) response_code;
     343         104 :   switch (response_code)
     344             :   {
     345           0 :   case 0:
     346           0 :     dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     347           0 :     break;
     348          90 :   case MHD_HTTP_OK:
     349             :     {
     350             :       struct GNUNET_JSON_Specification spec[] = {
     351          90 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     352             :                                      &dh->exchange_sig),
     353          90 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     354             :                                      &dh->exchange_pub),
     355          90 :         GNUNET_JSON_spec_mark_optional (
     356             :           TALER_JSON_spec_web_url ("transaction_base_url",
     357             :                                    &dr->details.ok.transaction_base_url),
     358             :           NULL),
     359          90 :         GNUNET_JSON_spec_timestamp ("exchange_timestamp",
     360             :                                     &dh->exchange_timestamp),
     361          90 :         GNUNET_JSON_spec_end ()
     362             :       };
     363             : 
     364          90 :       if (GNUNET_OK !=
     365          90 :           GNUNET_JSON_parse (j,
     366             :                              spec,
     367             :                              NULL, NULL))
     368             :       {
     369           0 :         GNUNET_break_op (0);
     370           0 :         dr->hr.http_status = 0;
     371           0 :         dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     372           0 :         break;
     373             :       }
     374          90 :       if (GNUNET_OK !=
     375          90 :           TALER_EXCHANGE_test_signing_key (dh->keys,
     376          90 :                                            &dh->exchange_pub))
     377             :       {
     378           0 :         GNUNET_break_op (0);
     379           0 :         dr->hr.http_status = 0;
     380           0 :         dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
     381           0 :         break;
     382             :       }
     383          90 :       {
     384          90 :         const struct TALER_CoinSpendSignatureP *csigs[
     385          90 :           GNUNET_NZL (dh->num_cdds)];
     386             : 
     387         182 :         for (unsigned int i = 0; i<dh->num_cdds; i++)
     388          92 :           csigs[i] = &dh->cdds[i].coin_sig;
     389          90 :         if (GNUNET_OK !=
     390          90 :             TALER_exchange_online_deposit_confirmation_verify (
     391          90 :               &dh->dcd.h_contract_terms,
     392          90 :               &dh->h_wire,
     393          90 :               &dh->h_policy,
     394             :               dh->exchange_timestamp,
     395             :               dh->dcd.wire_deadline,
     396             :               dh->dcd.refund_deadline,
     397          90 :               &dh->total_without_fee,
     398             :               dh->num_cdds,
     399             :               csigs,
     400          90 :               &dh->dcd.merchant_pub,
     401          90 :               &dh->exchange_pub,
     402          90 :               &dh->exchange_sig))
     403             :         {
     404           0 :           GNUNET_break_op (0);
     405           0 :           dr->hr.http_status = 0;
     406           0 :           dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
     407           0 :           break;
     408             :         }
     409             :       }
     410          90 :       TEAH_get_auditors_for_dc (dh->keys,
     411             :                                 &auditor_cb,
     412             :                                 dh);
     413             :     }
     414          90 :     dr->details.ok.exchange_sig = &dh->exchange_sig;
     415          90 :     dr->details.ok.exchange_pub = &dh->exchange_pub;
     416          90 :     dr->details.ok.deposit_timestamp = dh->exchange_timestamp;
     417          90 :     break;
     418           0 :   case MHD_HTTP_BAD_REQUEST:
     419             :     /* This should never happen, either us or the exchange is buggy
     420             :        (or API version conflict); just pass JSON reply to the application */
     421           0 :     dr->hr.ec = TALER_JSON_get_error_code (j);
     422           0 :     dr->hr.hint = TALER_JSON_get_error_hint (j);
     423           0 :     break;
     424           0 :   case MHD_HTTP_FORBIDDEN:
     425           0 :     dr->hr.ec = TALER_JSON_get_error_code (j);
     426           0 :     dr->hr.hint = TALER_JSON_get_error_hint (j);
     427             :     /* Nothing really to verify, exchange says one of the signatures is
     428             :        invalid; as we checked them, this should never happen, we
     429             :        should pass the JSON reply to the application */
     430           0 :     break;
     431           0 :   case MHD_HTTP_NOT_FOUND:
     432           0 :     dr->hr.ec = TALER_JSON_get_error_code (j);
     433           0 :     dr->hr.hint = TALER_JSON_get_error_hint (j);
     434             :     /* Nothing really to verify, this should never
     435             :        happen, we should pass the JSON reply to the application */
     436           0 :     break;
     437           9 :   case MHD_HTTP_CONFLICT:
     438             :     {
     439           9 :       dr->hr.ec = TALER_JSON_get_error_code (j);
     440           9 :       dr->hr.hint = TALER_JSON_get_error_hint (j);
     441           9 :       switch (dr->hr.ec)
     442             :       {
     443           4 :       case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
     444             :         {
     445             :           struct GNUNET_JSON_Specification spec[] = {
     446           4 :             GNUNET_JSON_spec_fixed_auto (
     447             :               "coin_pub",
     448             :               &dr->details.conflict.details
     449             :               .insufficient_funds.coin_pub),
     450           4 :             GNUNET_JSON_spec_end ()
     451             :           };
     452             : 
     453           4 :           if (GNUNET_OK !=
     454           4 :               GNUNET_JSON_parse (j,
     455             :                                  spec,
     456             :                                  NULL, NULL))
     457             :           {
     458           0 :             GNUNET_break_op (0);
     459           0 :             dr->hr.http_status = 0;
     460           0 :             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     461           0 :             break;
     462             :           }
     463             :         }
     464           4 :         break;
     465           0 :       case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
     466             :         {
     467             :           struct GNUNET_JSON_Specification spec[] = {
     468           0 :             GNUNET_JSON_spec_fixed_auto (
     469             :               "coin_pub",
     470             :               &dr->details.conflict.details
     471             :               .coin_conflicting_age_hash.coin_pub),
     472           0 :             GNUNET_JSON_spec_end ()
     473             :           };
     474             : 
     475           0 :           if (GNUNET_OK !=
     476           0 :               GNUNET_JSON_parse (j,
     477             :                                  spec,
     478             :                                  NULL, NULL))
     479             :           {
     480           0 :             GNUNET_break_op (0);
     481           0 :             dr->hr.http_status = 0;
     482           0 :             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     483           0 :             break;
     484             :           }
     485             :         }
     486           0 :         break;
     487           1 :       case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
     488             :         {
     489             :           struct GNUNET_JSON_Specification spec[] = {
     490           1 :             GNUNET_JSON_spec_fixed_auto (
     491             :               "coin_pub",
     492             :               &dr->details.conflict.details
     493             :               .coin_conflicting_denomination_key.coin_pub),
     494           1 :             GNUNET_JSON_spec_end ()
     495             :           };
     496             : 
     497           1 :           if (GNUNET_OK !=
     498           1 :               GNUNET_JSON_parse (j,
     499             :                                  spec,
     500             :                                  NULL, NULL))
     501             :           {
     502           0 :             GNUNET_break_op (0);
     503           0 :             dr->hr.http_status = 0;
     504           0 :             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     505           0 :             break;
     506             :           }
     507             :         }
     508           1 :         break;
     509           4 :       case TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT:
     510           4 :         break;
     511           0 :       default:
     512           0 :         GNUNET_break_op (0);
     513           0 :         break;
     514             :       }
     515             :     }
     516           9 :     break;
     517           0 :   case MHD_HTTP_GONE:
     518             :     /* could happen if denomination was revoked */
     519             :     /* Note: one might want to check /keys for revocation
     520             :        signature here, alas tricky in case our /keys
     521             :        is outdated => left to clients */
     522           0 :     dr->hr.ec = TALER_JSON_get_error_code (j);
     523           0 :     dr->hr.hint = TALER_JSON_get_error_hint (j);
     524           0 :     break;
     525           5 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     526             :     {
     527             :       struct GNUNET_JSON_Specification spec[] = {
     528           5 :         GNUNET_JSON_spec_fixed_auto (
     529             :           "h_payto",
     530             :           &dr->details.unavailable_for_legal_reasons.h_payto),
     531           5 :         GNUNET_JSON_spec_uint64 (
     532             :           "requirement_row",
     533             :           &dr->details.unavailable_for_legal_reasons.requirement_row),
     534           5 :         GNUNET_JSON_spec_bool (
     535             :           "bad_kyc_auth",
     536             :           &dr->details.unavailable_for_legal_reasons.bad_kyc_auth),
     537           5 :         GNUNET_JSON_spec_end ()
     538             :       };
     539             : 
     540           5 :       if (GNUNET_OK !=
     541           5 :           GNUNET_JSON_parse (j,
     542             :                              spec,
     543             :                              NULL, NULL))
     544             :       {
     545           0 :         GNUNET_break_op (0);
     546           0 :         dr->hr.http_status = 0;
     547           0 :         dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     548           0 :         break;
     549             :       }
     550             :     }
     551           5 :     break;
     552           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     553           0 :     dr->hr.ec = TALER_JSON_get_error_code (j);
     554           0 :     dr->hr.hint = TALER_JSON_get_error_hint (j);
     555             :     /* Server had an internal issue; we should retry, but this API
     556             :        leaves this to the application */
     557           0 :     break;
     558           0 :   default:
     559             :     /* unexpected response code */
     560           0 :     dr->hr.ec = TALER_JSON_get_error_code (j);
     561           0 :     dr->hr.hint = TALER_JSON_get_error_hint (j);
     562           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     563             :                 "Unexpected response code %u/%d for exchange deposit\n",
     564             :                 (unsigned int) response_code,
     565             :                 dr->hr.ec);
     566           0 :     GNUNET_break_op (0);
     567           0 :     break;
     568             :   }
     569         104 :   if (NULL != dh->ai_head)
     570           8 :     return;
     571          96 :   finish_dh (dh);
     572             : }
     573             : 
     574             : 
     575             : struct TALER_EXCHANGE_BatchDepositHandle *
     576         104 : TALER_EXCHANGE_batch_deposit (
     577             :   struct GNUNET_CURL_Context *ctx,
     578             :   const char *url,
     579             :   struct TALER_EXCHANGE_Keys *keys,
     580             :   const struct TALER_EXCHANGE_DepositContractDetail *dcd,
     581             :   unsigned int num_cdds,
     582             :   const struct TALER_EXCHANGE_CoinDepositDetail cdds[static num_cdds],
     583             :   TALER_EXCHANGE_BatchDepositResultCallback cb,
     584             :   void *cb_cls,
     585             :   enum TALER_ErrorCode *ec)
     586         104 : {
     587             :   struct TALER_EXCHANGE_BatchDepositHandle *dh;
     588             :   json_t *deposit_obj;
     589             :   json_t *deposits;
     590             :   CURL *eh;
     591             :   const struct GNUNET_HashCode *wallet_data_hashp;
     592             : 
     593         104 :   if (0 == num_cdds)
     594             :   {
     595           0 :     GNUNET_break (0);
     596           0 :     return NULL;
     597             :   }
     598         104 :   if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline,
     599             :                                  >,
     600             :                                  dcd->wire_deadline))
     601             :   {
     602           0 :     GNUNET_break_op (0);
     603           0 :     *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE;
     604           0 :     return NULL;
     605             :   }
     606         104 :   dh = GNUNET_new (struct TALER_EXCHANGE_BatchDepositHandle);
     607         104 :   dh->auditor_chance = AUDITOR_CHANCE;
     608         104 :   dh->cb = cb;
     609         104 :   dh->cb_cls = cb_cls;
     610         104 :   dh->cdds = GNUNET_memdup (cdds,
     611             :                             num_cdds * sizeof (*cdds));
     612         104 :   dh->num_cdds = num_cdds;
     613         104 :   dh->dcd = *dcd;
     614         104 :   if (NULL != dcd->policy_details)
     615           0 :     TALER_deposit_policy_hash (dcd->policy_details,
     616             :                                &dh->h_policy);
     617         104 :   TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
     618             :                                       &dcd->wire_salt,
     619             :                                       &dh->h_wire);
     620         104 :   deposits = json_array ();
     621         104 :   GNUNET_assert (NULL != deposits);
     622         104 :   GNUNET_assert (GNUNET_OK ==
     623             :                  TALER_amount_set_zero (cdds[0].amount.currency,
     624             :                                         &dh->total_without_fee));
     625         210 :   for (unsigned int i = 0; i<num_cdds; i++)
     626             :   {
     627         106 :     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i];
     628             :     const struct TALER_EXCHANGE_DenomPublicKey *dki;
     629             :     const struct TALER_AgeCommitmentHashP *h_age_commitmentp;
     630             :     struct TALER_Amount amount_without_fee;
     631             : 
     632         106 :     dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
     633             :                                                        &cdd->h_denom_pub);
     634         106 :     if (NULL == dki)
     635             :     {
     636           0 :       *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
     637           0 :       GNUNET_break_op (0);
     638           0 :       json_decref (deposits);
     639           0 :       return NULL;
     640             :     }
     641         106 :     if (0 >
     642         106 :         TALER_amount_subtract (&amount_without_fee,
     643             :                                &cdd->amount,
     644             :                                &dki->fees.deposit))
     645             :     {
     646           0 :       *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
     647           0 :       GNUNET_break_op (0);
     648           0 :       GNUNET_free (dh->cdds);
     649           0 :       GNUNET_free (dh);
     650           0 :       json_decref (deposits);
     651           0 :       return NULL;
     652             :     }
     653         106 :     GNUNET_assert (0 <=
     654             :                    TALER_amount_add (&dh->total_without_fee,
     655             :                                      &dh->total_without_fee,
     656             :                                      &amount_without_fee));
     657         106 :     if (GNUNET_OK !=
     658         106 :         TALER_EXCHANGE_verify_deposit_signature_ (dcd,
     659         106 :                                                   &dh->h_policy,
     660         106 :                                                   &dh->h_wire,
     661             :                                                   cdd,
     662             :                                                   dki))
     663             :     {
     664           0 :       *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID;
     665           0 :       GNUNET_break_op (0);
     666           0 :       GNUNET_free (dh->cdds);
     667           0 :       GNUNET_free (dh);
     668           0 :       json_decref (deposits);
     669           0 :       return NULL;
     670             :     }
     671         106 :     if (! GNUNET_is_zero (&dcd->merchant_sig))
     672             :     {
     673             :       /* FIXME #9185: check merchant_sig!? */
     674             :     }
     675         106 :     if (GNUNET_is_zero (&cdd->h_age_commitment))
     676          80 :       h_age_commitmentp = NULL;
     677             :     else
     678          26 :       h_age_commitmentp = &cdd->h_age_commitment;
     679         106 :     GNUNET_assert (
     680             :       0 ==
     681             :       json_array_append_new (
     682             :         deposits,
     683             :         GNUNET_JSON_PACK (
     684             :           TALER_JSON_pack_amount ("contribution",
     685             :                                   &cdd->amount),
     686             :           GNUNET_JSON_pack_data_auto ("denom_pub_hash",
     687             :                                       &cdd->h_denom_pub),
     688             :           TALER_JSON_pack_denom_sig ("ub_sig",
     689             :                                      &cdd->denom_sig),
     690             :           GNUNET_JSON_pack_data_auto ("coin_pub",
     691             :                                       &cdd->coin_pub),
     692             :           GNUNET_JSON_pack_allow_null (
     693             :             GNUNET_JSON_pack_data_auto ("h_age_commitment",
     694             :                                         h_age_commitmentp)),
     695             :           GNUNET_JSON_pack_data_auto ("coin_sig",
     696             :                                       &cdd->coin_sig)
     697             :           )));
     698             :   }
     699         104 :   dh->url = TALER_url_join (url,
     700             :                             "batch-deposit",
     701             :                             NULL);
     702         104 :   if (NULL == dh->url)
     703             :   {
     704           0 :     GNUNET_break (0);
     705           0 :     *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
     706           0 :     GNUNET_free (dh->url);
     707           0 :     GNUNET_free (dh->cdds);
     708           0 :     GNUNET_free (dh);
     709           0 :     json_decref (deposits);
     710           0 :     return NULL;
     711             :   }
     712             : 
     713         104 :   if (GNUNET_is_zero (&dcd->wallet_data_hash))
     714         104 :     wallet_data_hashp = NULL;
     715             :   else
     716           0 :     wallet_data_hashp = &dcd->wallet_data_hash;
     717             : 
     718         104 :   deposit_obj = GNUNET_JSON_PACK (
     719             :     TALER_JSON_pack_full_payto ("merchant_payto_uri",
     720             :                                 dcd->merchant_payto_uri),
     721             :     GNUNET_JSON_pack_data_auto ("wire_salt",
     722             :                                 &dcd->wire_salt),
     723             :     GNUNET_JSON_pack_data_auto ("h_contract_terms",
     724             :                                 &dcd->h_contract_terms),
     725             :     GNUNET_JSON_pack_array_steal ("coins",
     726             :                                   deposits),
     727             :     GNUNET_JSON_pack_allow_null (
     728             :       GNUNET_JSON_pack_data_auto ("wallet_data_hash",
     729             :                                   wallet_data_hashp)),
     730             :     GNUNET_JSON_pack_allow_null (
     731             :       GNUNET_JSON_pack_object_steal ("policy_details",
     732             :                                      (json_t *) dcd->policy_details)),
     733             :     GNUNET_JSON_pack_timestamp ("timestamp",
     734             :                                 dcd->wallet_timestamp),
     735             :     GNUNET_JSON_pack_data_auto ("merchant_pub",
     736             :                                 &dcd->merchant_pub),
     737             :     GNUNET_JSON_pack_data_auto ("merchant_sig",
     738             :                                 &dcd->merchant_sig),
     739             :     GNUNET_JSON_pack_allow_null (
     740             :       GNUNET_JSON_pack_timestamp ("refund_deadline",
     741             :                                   dcd->refund_deadline)),
     742             :     GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
     743             :                                 dcd->wire_deadline));
     744         104 :   GNUNET_assert (NULL != deposit_obj);
     745         104 :   eh = TALER_EXCHANGE_curl_easy_get_ (dh->url);
     746         208 :   if ( (NULL == eh) ||
     747             :        (GNUNET_OK !=
     748         104 :         TALER_curl_easy_post (&dh->post_ctx,
     749             :                               eh,
     750             :                               deposit_obj)) )
     751             :   {
     752           0 :     *ec = TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
     753           0 :     GNUNET_break (0);
     754           0 :     if (NULL != eh)
     755           0 :       curl_easy_cleanup (eh);
     756           0 :     json_decref (deposit_obj);
     757           0 :     GNUNET_free (dh->cdds);
     758           0 :     GNUNET_free (dh->url);
     759           0 :     GNUNET_free (dh);
     760           0 :     return NULL;
     761             :   }
     762         104 :   json_decref (deposit_obj);
     763         104 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     764             :               "URL for deposit: `%s'\n",
     765             :               dh->url);
     766         104 :   dh->ctx = ctx;
     767         104 :   dh->keys = TALER_EXCHANGE_keys_incref (keys);
     768         208 :   dh->job = GNUNET_CURL_job_add2 (ctx,
     769             :                                   eh,
     770         104 :                                   dh->post_ctx.headers,
     771             :                                   &handle_deposit_finished,
     772             :                                   dh);
     773         104 :   return dh;
     774             : }
     775             : 
     776             : 
     777             : void
     778           6 : TALER_EXCHANGE_batch_deposit_force_dc (
     779             :   struct TALER_EXCHANGE_BatchDepositHandle *deposit)
     780             : {
     781           6 :   deposit->auditor_chance = 1;
     782           6 : }
     783             : 
     784             : 
     785             : void
     786         104 : TALER_EXCHANGE_batch_deposit_cancel (
     787             :   struct TALER_EXCHANGE_BatchDepositHandle *deposit)
     788             : {
     789             :   struct TEAH_AuditorInteractionEntry *aie;
     790             : 
     791         104 :   while (NULL != (aie = deposit->ai_head))
     792             :   {
     793           0 :     GNUNET_assert (aie->dh == deposit);
     794           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     795             :                 "Not sending deposit confirmation to auditor `%s' due to cancellation\n",
     796             :                 aie->auditor_url);
     797           0 :     TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
     798           0 :     GNUNET_CONTAINER_DLL_remove (deposit->ai_head,
     799             :                                  deposit->ai_tail,
     800             :                                  aie);
     801           0 :     GNUNET_free (aie);
     802             :   }
     803         104 :   if (NULL != deposit->job)
     804             :   {
     805           0 :     GNUNET_CURL_job_cancel (deposit->job);
     806           0 :     deposit->job = NULL;
     807             :   }
     808         104 :   TALER_EXCHANGE_keys_decref (deposit->keys);
     809         104 :   GNUNET_free (deposit->url);
     810         104 :   GNUNET_free (deposit->cdds);
     811         104 :   TALER_curl_easy_post_finished (&deposit->post_ctx);
     812         104 :   json_decref (deposit->response);
     813         104 :   GNUNET_free (deposit);
     814         104 : }
     815             : 
     816             : 
     817             : /* end of exchange_api_batch_deposit.c */

Generated by: LCOV version 1.16