exchange

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

exchange_api_post-batch-deposit.c (27387B)


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