exchange

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

exchange_api_batch_deposit.c (24561B)


      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 finish_dh (struct TALER_EXCHANGE_BatchDepositHandle *dh)
    204 {
    205   dh->cb (dh->cb_cls,
    206           &dh->dr);
    207   TALER_EXCHANGE_batch_deposit_cancel (dh);
    208 }
    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 acc_confirmation_cb (
    220   void *cls,
    221   const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
    222 {
    223   struct TEAH_AuditorInteractionEntry *aie = cls;
    224   struct TALER_EXCHANGE_BatchDepositHandle *dh = aie->dh;
    225 
    226   if (MHD_HTTP_OK != dcr->hr.http_status)
    227   {
    228     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   GNUNET_CONTAINER_DLL_remove (dh->ai_head,
    235                                dh->ai_tail,
    236                                aie);
    237   GNUNET_free (aie);
    238   if (NULL == dh->ai_head)
    239     finish_dh (dh);
    240 }
    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 auditor_cb (void *cls,
    253             const char *auditor_url,
    254             const struct TALER_AuditorPublicKeyP *auditor_pub)
    255 {
    256   struct TALER_EXCHANGE_BatchDepositHandle *dh = cls;
    257   const struct TALER_EXCHANGE_SigningPublicKey *spk;
    258   struct TEAH_AuditorInteractionEntry *aie;
    259   const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (
    260                                                   dh->num_cdds)];
    261   const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL (
    262                                                   dh->num_cdds)];
    263 
    264   for (unsigned int i = 0; i<dh->num_cdds; i++)
    265   {
    266     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i];
    267 
    268     csigs[i] = &cdd->coin_sig;
    269     cpubs[i] = &cdd->coin_pub;
    270   }
    271 
    272   if (0 !=
    273       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
    274                                 dh->auditor_chance))
    275   {
    276     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    277                 "Not providing deposit confirmation to auditor\n");
    278     return;
    279   }
    280   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    281               "Will provide deposit confirmation to auditor `%s'\n",
    282               TALER_B2S (auditor_pub));
    283   spk = TALER_EXCHANGE_get_signing_key_info (dh->keys,
    284                                              &dh->exchange_pub);
    285   if (NULL == spk)
    286   {
    287     GNUNET_break_op (0);
    288     return;
    289   }
    290   aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
    291   aie->dh = dh;
    292   aie->auditor_url = auditor_url;
    293   aie->dch = TALER_AUDITOR_deposit_confirmation (
    294     dh->ctx,
    295     auditor_url,
    296     &dh->h_wire,
    297     &dh->h_policy,
    298     &dh->dcd.h_contract_terms,
    299     dh->exchange_timestamp,
    300     dh->dcd.wire_deadline,
    301     dh->dcd.refund_deadline,
    302     &dh->total_without_fee,
    303     dh->num_cdds,
    304     cpubs,
    305     csigs,
    306     &dh->dcd.merchant_pub,
    307     &dh->exchange_pub,
    308     &dh->exchange_sig,
    309     &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   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 handle_deposit_finished (void *cls,
    332                          long response_code,
    333                          const void *response)
    334 {
    335   struct TALER_EXCHANGE_BatchDepositHandle *dh = cls;
    336   const json_t *j = response;
    337   struct TALER_EXCHANGE_BatchDepositResult *dr = &dh->dr;
    338 
    339   dh->job = NULL;
    340   dh->response = json_incref ((json_t*) j);
    341   dr->hr.reply = dh->response;
    342   dr->hr.http_status = (unsigned int) response_code;
    343   switch (response_code)
    344   {
    345   case 0:
    346     dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    347     break;
    348   case MHD_HTTP_OK:
    349     {
    350       struct GNUNET_JSON_Specification spec[] = {
    351         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
    352                                      &dh->exchange_sig),
    353         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
    354                                      &dh->exchange_pub),
    355         GNUNET_JSON_spec_mark_optional (
    356           TALER_JSON_spec_web_url ("transaction_base_url",
    357                                    &dr->details.ok.transaction_base_url),
    358           NULL),
    359         GNUNET_JSON_spec_timestamp ("exchange_timestamp",
    360                                     &dh->exchange_timestamp),
    361         GNUNET_JSON_spec_end ()
    362       };
    363 
    364       if (GNUNET_OK !=
    365           GNUNET_JSON_parse (j,
    366                              spec,
    367                              NULL, NULL))
    368       {
    369         GNUNET_break_op (0);
    370         dr->hr.http_status = 0;
    371         dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    372         break;
    373       }
    374       if (GNUNET_OK !=
    375           TALER_EXCHANGE_test_signing_key (dh->keys,
    376                                            &dh->exchange_pub))
    377       {
    378         GNUNET_break_op (0);
    379         dr->hr.http_status = 0;
    380         dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
    381         break;
    382       }
    383       {
    384         const struct TALER_CoinSpendSignatureP *csigs[
    385           GNUNET_NZL (dh->num_cdds)];
    386 
    387         for (unsigned int i = 0; i<dh->num_cdds; i++)
    388           csigs[i] = &dh->cdds[i].coin_sig;
    389         if (GNUNET_OK !=
    390             TALER_exchange_online_deposit_confirmation_verify (
    391               &dh->dcd.h_contract_terms,
    392               &dh->h_wire,
    393               &dh->h_policy,
    394               dh->exchange_timestamp,
    395               dh->dcd.wire_deadline,
    396               dh->dcd.refund_deadline,
    397               &dh->total_without_fee,
    398               dh->num_cdds,
    399               csigs,
    400               &dh->dcd.merchant_pub,
    401               &dh->exchange_pub,
    402               &dh->exchange_sig))
    403         {
    404           GNUNET_break_op (0);
    405           dr->hr.http_status = 0;
    406           dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
    407           break;
    408         }
    409       }
    410       TEAH_get_auditors_for_dc (dh->keys,
    411                                 &auditor_cb,
    412                                 dh);
    413     }
    414     dr->details.ok.exchange_sig = &dh->exchange_sig;
    415     dr->details.ok.exchange_pub = &dh->exchange_pub;
    416     dr->details.ok.deposit_timestamp = dh->exchange_timestamp;
    417     break;
    418   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     dr->hr.ec = TALER_JSON_get_error_code (j);
    422     dr->hr.hint = TALER_JSON_get_error_hint (j);
    423     break;
    424   case MHD_HTTP_FORBIDDEN:
    425     dr->hr.ec = TALER_JSON_get_error_code (j);
    426     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     break;
    431   case MHD_HTTP_NOT_FOUND:
    432     dr->hr.ec = TALER_JSON_get_error_code (j);
    433     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     break;
    437   case MHD_HTTP_CONFLICT:
    438     {
    439       dr->hr.ec = TALER_JSON_get_error_code (j);
    440       dr->hr.hint = TALER_JSON_get_error_hint (j);
    441       switch (dr->hr.ec)
    442       {
    443       case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
    444         {
    445           struct GNUNET_JSON_Specification spec[] = {
    446             GNUNET_JSON_spec_fixed_auto (
    447               "coin_pub",
    448               &dr->details.conflict.details
    449               .insufficient_funds.coin_pub),
    450             GNUNET_JSON_spec_end ()
    451           };
    452 
    453           if (GNUNET_OK !=
    454               GNUNET_JSON_parse (j,
    455                                  spec,
    456                                  NULL, NULL))
    457           {
    458             GNUNET_break_op (0);
    459             dr->hr.http_status = 0;
    460             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    461             break;
    462           }
    463         }
    464         break;
    465       case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
    466         {
    467           struct GNUNET_JSON_Specification spec[] = {
    468             GNUNET_JSON_spec_fixed_auto (
    469               "coin_pub",
    470               &dr->details.conflict.details
    471               .coin_conflicting_age_hash.coin_pub),
    472             GNUNET_JSON_spec_end ()
    473           };
    474 
    475           if (GNUNET_OK !=
    476               GNUNET_JSON_parse (j,
    477                                  spec,
    478                                  NULL, NULL))
    479           {
    480             GNUNET_break_op (0);
    481             dr->hr.http_status = 0;
    482             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    483             break;
    484           }
    485         }
    486         break;
    487       case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
    488         {
    489           struct GNUNET_JSON_Specification spec[] = {
    490             GNUNET_JSON_spec_fixed_auto (
    491               "coin_pub",
    492               &dr->details.conflict.details
    493               .coin_conflicting_denomination_key.coin_pub),
    494             GNUNET_JSON_spec_end ()
    495           };
    496 
    497           if (GNUNET_OK !=
    498               GNUNET_JSON_parse (j,
    499                                  spec,
    500                                  NULL, NULL))
    501           {
    502             GNUNET_break_op (0);
    503             dr->hr.http_status = 0;
    504             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    505             break;
    506           }
    507         }
    508         break;
    509       case TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT:
    510         break;
    511       default:
    512         GNUNET_break_op (0);
    513         break;
    514       }
    515     }
    516     break;
    517   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     dr->hr.ec = TALER_JSON_get_error_code (j);
    523     dr->hr.hint = TALER_JSON_get_error_hint (j);
    524     break;
    525   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    526     {
    527       struct GNUNET_JSON_Specification spec[] = {
    528         GNUNET_JSON_spec_fixed_auto (
    529           "h_payto",
    530           &dr->details.unavailable_for_legal_reasons.h_payto),
    531         GNUNET_JSON_spec_uint64 (
    532           "requirement_row",
    533           &dr->details.unavailable_for_legal_reasons.requirement_row),
    534         GNUNET_JSON_spec_bool (
    535           "bad_kyc_auth",
    536           &dr->details.unavailable_for_legal_reasons.bad_kyc_auth),
    537         GNUNET_JSON_spec_end ()
    538       };
    539 
    540       if (GNUNET_OK !=
    541           GNUNET_JSON_parse (j,
    542                              spec,
    543                              NULL, NULL))
    544       {
    545         GNUNET_break_op (0);
    546         dr->hr.http_status = 0;
    547         dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    548         break;
    549       }
    550     }
    551     break;
    552   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    553     dr->hr.ec = TALER_JSON_get_error_code (j);
    554     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     break;
    558   default:
    559     /* unexpected response code */
    560     dr->hr.ec = TALER_JSON_get_error_code (j);
    561     dr->hr.hint = TALER_JSON_get_error_hint (j);
    562     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     GNUNET_break_op (0);
    567     break;
    568   }
    569   if (NULL != dh->ai_head)
    570     return;
    571   finish_dh (dh);
    572 }
    573 
    574 
    575 struct TALER_EXCHANGE_BatchDepositHandle *
    576 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 {
    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   if (0 == num_cdds)
    594   {
    595     GNUNET_break (0);
    596     return NULL;
    597   }
    598   if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline,
    599                                  >,
    600                                  dcd->wire_deadline))
    601   {
    602     GNUNET_break_op (0);
    603     *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE;
    604     return NULL;
    605   }
    606   dh = GNUNET_new (struct TALER_EXCHANGE_BatchDepositHandle);
    607   dh->auditor_chance = AUDITOR_CHANCE;
    608   dh->cb = cb;
    609   dh->cb_cls = cb_cls;
    610   dh->cdds = GNUNET_memdup (cdds,
    611                             num_cdds * sizeof (*cdds));
    612   dh->num_cdds = num_cdds;
    613   dh->dcd = *dcd;
    614   if (NULL != dcd->policy_details)
    615     TALER_deposit_policy_hash (dcd->policy_details,
    616                                &dh->h_policy);
    617   TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
    618                                       &dcd->wire_salt,
    619                                       &dh->h_wire);
    620   deposits = json_array ();
    621   GNUNET_assert (NULL != deposits);
    622   GNUNET_assert (GNUNET_OK ==
    623                  TALER_amount_set_zero (cdds[0].amount.currency,
    624                                         &dh->total_without_fee));
    625   for (unsigned int i = 0; i<num_cdds; i++)
    626   {
    627     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     dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
    633                                                        &cdd->h_denom_pub);
    634     if (NULL == dki)
    635     {
    636       *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
    637       GNUNET_break_op (0);
    638       json_decref (deposits);
    639       return NULL;
    640     }
    641     if (0 >
    642         TALER_amount_subtract (&amount_without_fee,
    643                                &cdd->amount,
    644                                &dki->fees.deposit))
    645     {
    646       *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
    647       GNUNET_break_op (0);
    648       GNUNET_free (dh->cdds);
    649       GNUNET_free (dh);
    650       json_decref (deposits);
    651       return NULL;
    652     }
    653     GNUNET_assert (0 <=
    654                    TALER_amount_add (&dh->total_without_fee,
    655                                      &dh->total_without_fee,
    656                                      &amount_without_fee));
    657     if (GNUNET_OK !=
    658         TALER_EXCHANGE_verify_deposit_signature_ (dcd,
    659                                                   &dh->h_policy,
    660                                                   &dh->h_wire,
    661                                                   cdd,
    662                                                   dki))
    663     {
    664       *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID;
    665       GNUNET_break_op (0);
    666       GNUNET_free (dh->cdds);
    667       GNUNET_free (dh);
    668       json_decref (deposits);
    669       return NULL;
    670     }
    671     if (! GNUNET_is_zero (&dcd->merchant_sig))
    672     {
    673       /* FIXME #9185: check merchant_sig!? */
    674     }
    675     if (GNUNET_is_zero (&cdd->h_age_commitment))
    676       h_age_commitmentp = NULL;
    677     else
    678       h_age_commitmentp = &cdd->h_age_commitment;
    679     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   dh->url = TALER_url_join (url,
    700                             "batch-deposit",
    701                             NULL);
    702   if (NULL == dh->url)
    703   {
    704     GNUNET_break (0);
    705     *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
    706     GNUNET_free (dh->url);
    707     GNUNET_free (dh->cdds);
    708     GNUNET_free (dh);
    709     json_decref (deposits);
    710     return NULL;
    711   }
    712 
    713   if (GNUNET_is_zero (&dcd->wallet_data_hash))
    714     wallet_data_hashp = NULL;
    715   else
    716     wallet_data_hashp = &dcd->wallet_data_hash;
    717 
    718   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   GNUNET_assert (NULL != deposit_obj);
    745   eh = TALER_EXCHANGE_curl_easy_get_ (dh->url);
    746   if ( (NULL == eh) ||
    747        (GNUNET_OK !=
    748         TALER_curl_easy_post (&dh->post_ctx,
    749                               eh,
    750                               deposit_obj)) )
    751   {
    752     *ec = TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
    753     GNUNET_break (0);
    754     if (NULL != eh)
    755       curl_easy_cleanup (eh);
    756     json_decref (deposit_obj);
    757     GNUNET_free (dh->cdds);
    758     GNUNET_free (dh->url);
    759     GNUNET_free (dh);
    760     return NULL;
    761   }
    762   json_decref (deposit_obj);
    763   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    764               "URL for deposit: `%s'\n",
    765               dh->url);
    766   dh->ctx = ctx;
    767   dh->keys = TALER_EXCHANGE_keys_incref (keys);
    768   dh->job = GNUNET_CURL_job_add2 (ctx,
    769                                   eh,
    770                                   dh->post_ctx.headers,
    771                                   &handle_deposit_finished,
    772                                   dh);
    773   return dh;
    774 }
    775 
    776 
    777 void
    778 TALER_EXCHANGE_batch_deposit_force_dc (
    779   struct TALER_EXCHANGE_BatchDepositHandle *deposit)
    780 {
    781   deposit->auditor_chance = 1;
    782 }
    783 
    784 
    785 void
    786 TALER_EXCHANGE_batch_deposit_cancel (
    787   struct TALER_EXCHANGE_BatchDepositHandle *deposit)
    788 {
    789   struct TEAH_AuditorInteractionEntry *aie;
    790 
    791   while (NULL != (aie = deposit->ai_head))
    792   {
    793     GNUNET_assert (aie->dh == deposit);
    794     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    795                 "Not sending deposit confirmation to auditor `%s' due to cancellation\n",
    796                 aie->auditor_url);
    797     TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
    798     GNUNET_CONTAINER_DLL_remove (deposit->ai_head,
    799                                  deposit->ai_tail,
    800                                  aie);
    801     GNUNET_free (aie);
    802   }
    803   if (NULL != deposit->job)
    804   {
    805     GNUNET_CURL_job_cancel (deposit->job);
    806     deposit->job = NULL;
    807   }
    808   TALER_EXCHANGE_keys_decref (deposit->keys);
    809   GNUNET_free (deposit->url);
    810   GNUNET_free (deposit->cdds);
    811   TALER_curl_easy_post_finished (&deposit->post_ctx);
    812   json_decref (deposit->response);
    813   GNUNET_free (deposit);
    814 }
    815 
    816 
    817 /* end of exchange_api_batch_deposit.c */