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 (27447B)


      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 (GNUNET_CRYPTO_QUALITY_WEAK,
    287                                 dh->auditor_chance))
    288   {
    289     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    290                 "Not providing deposit confirmation to auditor\n");
    291     return;
    292   }
    293   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    294               "Will provide deposit confirmation to auditor `%s'\n",
    295               TALER_B2S (auditor_pub));
    296   spk = TALER_EXCHANGE_get_signing_key_info (dh->keys,
    297                                              &dh->exchange_pub);
    298   if (NULL == spk)
    299   {
    300     GNUNET_break_op (0);
    301     return;
    302   }
    303   aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
    304   aie->dh = dh;
    305   aie->auditor_url = auditor_url;
    306   aie->dch = TALER_AUDITOR_deposit_confirmation (
    307     dh->ctx,
    308     auditor_url,
    309     &dh->h_wire,
    310     &dh->h_policy,
    311     &dh->dcd.h_contract_terms,
    312     dh->exchange_timestamp,
    313     dh->dcd.wire_deadline,
    314     dh->dcd.refund_deadline,
    315     &dh->total_without_fee,
    316     dh->num_cdds,
    317     cpubs,
    318     csigs,
    319     &dh->dcd.merchant_pub,
    320     &dh->exchange_pub,
    321     &dh->exchange_sig,
    322     &dh->keys->master_pub,
    323     spk->valid_from,
    324     spk->valid_until,
    325     spk->valid_legal,
    326     &spk->master_sig,
    327     &acc_confirmation_cb,
    328     aie);
    329   if (NULL == aie->dch)
    330   {
    331     GNUNET_break (0);
    332     GNUNET_free (aie);
    333     return;
    334   }
    335   GNUNET_CONTAINER_DLL_insert (dh->ai_head,
    336                                dh->ai_tail,
    337                                aie);
    338 }
    339 
    340 
    341 /**
    342  * Function called when we're done processing the
    343  * HTTP /batch-deposit request.
    344  *
    345  * @param cls the `struct TALER_EXCHANGE_PostBatchDepositHandle`
    346  * @param response_code HTTP response code, 0 on error
    347  * @param response parsed JSON result, NULL on error
    348  */
    349 static void
    350 handle_deposit_finished (void *cls,
    351                          long response_code,
    352                          const void *response)
    353 {
    354   struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls;
    355   const json_t *j = response;
    356   struct TALER_EXCHANGE_PostBatchDepositResponse *dr = &dh->dr;
    357 
    358   dh->job = NULL;
    359   dh->response = json_incref ((json_t*) j);
    360   dr->hr.reply = dh->response;
    361   dr->hr.http_status = (unsigned int) response_code;
    362   switch (response_code)
    363   {
    364   case 0:
    365     dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    366     break;
    367   case MHD_HTTP_OK:
    368     {
    369       bool prev33;
    370       struct GNUNET_JSON_Specification spec[] = {
    371         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
    372                                      &dh->exchange_sig),
    373         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
    374                                      &dh->exchange_pub),
    375         GNUNET_JSON_spec_mark_optional (
    376           TALER_JSON_spec_amount (
    377             "accumulated_total_without_fee",
    378             dh->total_without_fee.currency,
    379             &dr->details.ok.accumulated_total_without_fee),
    380           &prev33),
    381         GNUNET_JSON_spec_mark_optional (
    382           TALER_JSON_spec_web_url ("transaction_base_url",
    383                                    &dr->details.ok.transaction_base_url),
    384           NULL),
    385         GNUNET_JSON_spec_timestamp ("exchange_timestamp",
    386                                     &dh->exchange_timestamp),
    387         GNUNET_JSON_spec_end ()
    388       };
    389 
    390       if (GNUNET_OK !=
    391           GNUNET_JSON_parse (j,
    392                              spec,
    393                              NULL, NULL))
    394       {
    395         GNUNET_break_op (0);
    396         dr->hr.http_status = 0;
    397         dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    398         break;
    399       }
    400       if (GNUNET_OK !=
    401           TALER_EXCHANGE_test_signing_key (dh->keys,
    402                                            &dh->exchange_pub))
    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       if (prev33)
    410         dr->details.ok.accumulated_total_without_fee
    411           = dh->total_without_fee;
    412       if (1 ==
    413           TALER_amount_cmp (&dh->total_without_fee,
    414                             &dr->details.ok.accumulated_total_without_fee))
    415       {
    416         /* Amount signed by exchange is SMALLER than what we deposited */
    417         GNUNET_break_op (0);
    418         dr->hr.http_status = 0;
    419         dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
    420         break;
    421       }
    422       {
    423         const struct TALER_CoinSpendSignatureP *csigs[
    424           GNUNET_NZL (dh->num_cdds)];
    425 
    426         for (unsigned int i = 0; i<dh->num_cdds; i++)
    427           csigs[i] = &dh->cdds[i].coin_sig;
    428         if (GNUNET_OK !=
    429             TALER_exchange_online_deposit_confirmation_verify (
    430               &dh->dcd.h_contract_terms,
    431               &dh->h_wire,
    432               &dh->h_policy,
    433               dh->exchange_timestamp,
    434               dh->dcd.wire_deadline,
    435               dh->dcd.refund_deadline,
    436               &dr->details.ok.accumulated_total_without_fee,
    437               dh->num_cdds,
    438               csigs,
    439               &dh->dcd.merchant_pub,
    440               &dh->exchange_pub,
    441               &dh->exchange_sig))
    442         {
    443           GNUNET_break_op (0);
    444           dr->hr.http_status = 0;
    445           dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
    446           break;
    447         }
    448       }
    449       dh->total_without_fee = dr->details.ok.accumulated_total_without_fee;
    450       TALER_EXCHANGE_get_auditors_for_dc_ (dh->keys,
    451                                            &auditor_cb,
    452                                            dh);
    453     }
    454     dr->details.ok.exchange_sig = &dh->exchange_sig;
    455     dr->details.ok.exchange_pub = &dh->exchange_pub;
    456     dr->details.ok.deposit_timestamp = dh->exchange_timestamp;
    457     break;
    458   case MHD_HTTP_BAD_REQUEST:
    459     /* This should never happen, either us or the exchange is buggy
    460        (or API version conflict); just pass JSON reply to the application */
    461     dr->hr.ec = TALER_JSON_get_error_code (j);
    462     dr->hr.hint = TALER_JSON_get_error_hint (j);
    463     break;
    464   case MHD_HTTP_FORBIDDEN:
    465     dr->hr.ec = TALER_JSON_get_error_code (j);
    466     dr->hr.hint = TALER_JSON_get_error_hint (j);
    467     /* Nothing really to verify, exchange says one of the signatures is
    468        invalid; as we checked them, this should never happen, we
    469        should pass the JSON reply to the application */
    470     break;
    471   case MHD_HTTP_NOT_FOUND:
    472     dr->hr.ec = TALER_JSON_get_error_code (j);
    473     dr->hr.hint = TALER_JSON_get_error_hint (j);
    474     /* Nothing really to verify, this should never
    475        happen, we should pass the JSON reply to the application */
    476     break;
    477   case MHD_HTTP_CONFLICT:
    478     {
    479       dr->hr.ec = TALER_JSON_get_error_code (j);
    480       dr->hr.hint = TALER_JSON_get_error_hint (j);
    481       switch (dr->hr.ec)
    482       {
    483       case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
    484         {
    485           struct GNUNET_JSON_Specification spec[] = {
    486             GNUNET_JSON_spec_fixed_auto (
    487               "coin_pub",
    488               &dr->details.conflict.details.insufficient_funds.coin_pub),
    489             GNUNET_JSON_spec_fixed_auto (
    490               "h_denom_pub",
    491               &dr->details.conflict.details.insufficient_funds.h_denom_pub),
    492             GNUNET_JSON_spec_end ()
    493           };
    494 
    495           if (GNUNET_OK !=
    496               GNUNET_JSON_parse (j,
    497                                  spec,
    498                                  NULL, NULL))
    499           {
    500             GNUNET_break_op (0);
    501             dr->hr.http_status = 0;
    502             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    503             break;
    504           }
    505         }
    506         break;
    507       case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
    508         {
    509           struct GNUNET_JSON_Specification spec[] = {
    510             GNUNET_JSON_spec_fixed_auto (
    511               "coin_pub",
    512               &dr->details.conflict.details
    513               .coin_conflicting_age_hash.coin_pub),
    514             GNUNET_JSON_spec_fixed_auto (
    515               "h_denom_pub",
    516               &dr->details.conflict.details
    517               .coin_conflicting_age_hash.h_denom_pub),
    518             GNUNET_JSON_spec_end ()
    519           };
    520 
    521           if (GNUNET_OK !=
    522               GNUNET_JSON_parse (j,
    523                                  spec,
    524                                  NULL, NULL))
    525           {
    526             GNUNET_break_op (0);
    527             dr->hr.http_status = 0;
    528             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    529             break;
    530           }
    531         }
    532         break;
    533       case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
    534         {
    535           struct GNUNET_JSON_Specification spec[] = {
    536             GNUNET_JSON_spec_fixed_auto (
    537               "coin_pub",
    538               &dr->details.conflict.details
    539               .coin_conflicting_denomination_key.coin_pub),
    540             GNUNET_JSON_spec_end ()
    541           };
    542 
    543           if (GNUNET_OK !=
    544               GNUNET_JSON_parse (j,
    545                                  spec,
    546                                  NULL, NULL))
    547           {
    548             GNUNET_break_op (0);
    549             dr->hr.http_status = 0;
    550             dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    551             break;
    552           }
    553         }
    554         break;
    555       case TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT:
    556         break;
    557       default:
    558         GNUNET_break_op (0);
    559         break;
    560       }
    561     }
    562     break;
    563   case MHD_HTTP_GONE:
    564     /* could happen if denomination was revoked */
    565     /* Note: one might want to check /keys for revocation
    566        signature here, alas tricky in case our /keys
    567        is outdated => left to clients */
    568     dr->hr.ec = TALER_JSON_get_error_code (j);
    569     dr->hr.hint = TALER_JSON_get_error_hint (j);
    570     break;
    571   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    572     dr->hr.ec = TALER_JSON_get_error_code (j);
    573     dr->hr.hint = TALER_JSON_get_error_hint (j);
    574     if (GNUNET_OK !=
    575         TALER_EXCHANGE_parse_451 (&dr->details.unavailable_for_legal_reasons,
    576                                   j))
    577     {
    578       GNUNET_break_op (0);
    579       dr->hr.http_status = 0;
    580       dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    581       break;
    582     }
    583     break;
    584   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    585     dr->hr.ec = TALER_JSON_get_error_code (j);
    586     dr->hr.hint = TALER_JSON_get_error_hint (j);
    587     /* Server had an internal issue; we should retry, but this API
    588        leaves this to the application */
    589     break;
    590   default:
    591     /* unexpected response code */
    592     dr->hr.ec = TALER_JSON_get_error_code (j);
    593     dr->hr.hint = TALER_JSON_get_error_hint (j);
    594     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    595                 "Unexpected response code %u/%d for exchange deposit\n",
    596                 (unsigned int) response_code,
    597                 dr->hr.ec);
    598     GNUNET_break_op (0);
    599     break;
    600   }
    601   if (NULL != dh->ai_head)
    602     return;
    603   finish_dh (dh);
    604 }
    605 
    606 
    607 struct TALER_EXCHANGE_PostBatchDepositHandle *
    608 TALER_EXCHANGE_post_batch_deposit_create (
    609   struct GNUNET_CURL_Context *ctx,
    610   const char *url,
    611   struct TALER_EXCHANGE_Keys *keys,
    612   const struct TALER_EXCHANGE_DepositContractDetail *dcd,
    613   unsigned int num_cdds,
    614   const struct TALER_EXCHANGE_CoinDepositDetail cdds[static num_cdds],
    615   enum TALER_ErrorCode *ec)
    616 {
    617   struct TALER_EXCHANGE_PostBatchDepositHandle *dh;
    618   json_t *deposits;
    619   const struct GNUNET_HashCode *wallet_data_hashp;
    620 
    621   if (0 == num_cdds)
    622   {
    623     GNUNET_break (0);
    624     *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    625     return NULL;
    626   }
    627   if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline,
    628                                  >,
    629                                  dcd->wire_deadline))
    630   {
    631     GNUNET_break_op (0);
    632     *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE;
    633     return NULL;
    634   }
    635   dh = GNUNET_new (struct TALER_EXCHANGE_PostBatchDepositHandle);
    636   dh->auditor_chance = AUDITOR_CHANCE;
    637   dh->ctx = ctx;
    638   dh->base_url = GNUNET_strdup (url);
    639   dh->cdds = GNUNET_memdup (cdds,
    640                             num_cdds * sizeof (*cdds));
    641   dh->num_cdds = num_cdds;
    642   dh->dcd = *dcd;
    643   if (NULL != dcd->policy_details)
    644     TALER_deposit_policy_hash (dcd->policy_details,
    645                                &dh->h_policy);
    646   TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
    647                                       &dcd->wire_salt,
    648                                       &dh->h_wire);
    649   deposits = json_array ();
    650   GNUNET_assert (NULL != deposits);
    651   GNUNET_assert (GNUNET_OK ==
    652                  TALER_amount_set_zero (cdds[0].amount.currency,
    653                                         &dh->total_without_fee));
    654   for (unsigned int i = 0; i<num_cdds; i++)
    655   {
    656     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i];
    657     const struct TALER_EXCHANGE_DenomPublicKey *dki;
    658     const struct TALER_AgeCommitmentHashP *h_age_commitmentp;
    659     struct TALER_Amount amount_without_fee;
    660 
    661     dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
    662                                                        &cdd->h_denom_pub);
    663     if (NULL == dki)
    664     {
    665       *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
    666       GNUNET_break_op (0);
    667       json_decref (deposits);
    668       GNUNET_free (dh->base_url);
    669       GNUNET_free (dh->cdds);
    670       GNUNET_free (dh);
    671       return NULL;
    672     }
    673     if (0 >
    674         TALER_amount_subtract (&amount_without_fee,
    675                                &cdd->amount,
    676                                &dki->fees.deposit))
    677     {
    678       *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
    679       GNUNET_break_op (0);
    680       json_decref (deposits);
    681       GNUNET_free (dh->base_url);
    682       GNUNET_free (dh->cdds);
    683       GNUNET_free (dh);
    684       return NULL;
    685     }
    686     GNUNET_assert (0 <=
    687                    TALER_amount_add (&dh->total_without_fee,
    688                                      &dh->total_without_fee,
    689                                      &amount_without_fee));
    690     if (GNUNET_OK !=
    691         TALER_EXCHANGE_verify_deposit_signature_ (dcd,
    692                                                   &dh->h_policy,
    693                                                   &dh->h_wire,
    694                                                   cdd,
    695                                                   dki))
    696     {
    697       *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID;
    698       GNUNET_break_op (0);
    699       json_decref (deposits);
    700       GNUNET_free (dh->base_url);
    701       GNUNET_free (dh->cdds);
    702       GNUNET_free (dh);
    703       return NULL;
    704     }
    705     if (GNUNET_is_zero (&cdd->h_age_commitment))
    706       h_age_commitmentp = NULL;
    707     else
    708       h_age_commitmentp = &cdd->h_age_commitment;
    709     GNUNET_assert (
    710       0 ==
    711       json_array_append_new (
    712         deposits,
    713         GNUNET_JSON_PACK (
    714           TALER_JSON_pack_amount ("contribution",
    715                                   &cdd->amount),
    716           GNUNET_JSON_pack_data_auto ("denom_pub_hash",
    717                                       &cdd->h_denom_pub),
    718           TALER_JSON_pack_denom_sig ("ub_sig",
    719                                      &cdd->denom_sig),
    720           GNUNET_JSON_pack_data_auto ("coin_pub",
    721                                       &cdd->coin_pub),
    722           GNUNET_JSON_pack_allow_null (
    723             GNUNET_JSON_pack_data_auto ("h_age_commitment",
    724                                         h_age_commitmentp)),
    725           GNUNET_JSON_pack_data_auto ("coin_sig",
    726                                       &cdd->coin_sig)
    727           )));
    728   }
    729 
    730   if (GNUNET_is_zero (&dcd->wallet_data_hash))
    731     wallet_data_hashp = NULL;
    732   else
    733     wallet_data_hashp = &dcd->wallet_data_hash;
    734   dh->deposit_obj = GNUNET_JSON_PACK (
    735     TALER_JSON_pack_full_payto ("merchant_payto_uri",
    736                                 dcd->merchant_payto_uri),
    737     GNUNET_JSON_pack_allow_null (
    738       GNUNET_JSON_pack_string ("extra_wire_subject_metadata",
    739                                dcd->extra_wire_subject_metadata)),
    740     GNUNET_JSON_pack_data_auto ("wire_salt",
    741                                 &dcd->wire_salt),
    742     GNUNET_JSON_pack_data_auto ("h_contract_terms",
    743                                 &dcd->h_contract_terms),
    744     GNUNET_JSON_pack_array_steal ("coins",
    745                                   deposits),
    746     GNUNET_JSON_pack_allow_null (
    747       GNUNET_JSON_pack_data_auto ("wallet_data_hash",
    748                                   wallet_data_hashp)),
    749     GNUNET_JSON_pack_allow_null (
    750       GNUNET_JSON_pack_object_steal ("policy",
    751                                      (json_t *) dcd->policy_details)),
    752     GNUNET_JSON_pack_timestamp ("timestamp",
    753                                 dcd->wallet_timestamp),
    754     GNUNET_JSON_pack_data_auto ("merchant_pub",
    755                                 &dcd->merchant_pub),
    756     GNUNET_JSON_pack_data_auto ("merchant_sig",
    757                                 &dcd->merchant_sig),
    758     GNUNET_JSON_pack_allow_null (
    759       GNUNET_JSON_pack_timestamp ("refund_deadline",
    760                                   dcd->refund_deadline)),
    761     GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
    762                                 dcd->wire_deadline));
    763   if (NULL == dh->deposit_obj)
    764   {
    765     GNUNET_break (0);
    766     *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
    767     GNUNET_free (dh->base_url);
    768     GNUNET_free (dh->cdds);
    769     GNUNET_free (dh);
    770     return NULL;
    771   }
    772   dh->keys = TALER_EXCHANGE_keys_incref (keys);
    773   *ec = TALER_EC_NONE;
    774   return dh;
    775 }
    776 
    777 
    778 enum GNUNET_GenericReturnValue
    779 TALER_EXCHANGE_post_batch_deposit_set_options_ (
    780   struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh,
    781   unsigned int num_options,
    782   const struct TALER_EXCHANGE_PostBatchDepositOptionValue options[])
    783 {
    784   for (unsigned int i = 0; i < num_options; i++)
    785   {
    786     const struct TALER_EXCHANGE_PostBatchDepositOptionValue *opt = &options[i];
    787     switch (opt->option)
    788     {
    789     case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_END:
    790       return GNUNET_OK;
    791     case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_FORCE_DC:
    792       pbdh->auditor_chance = 1;
    793       break;
    794     case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_VERIFY_MERCHANT_SIG:
    795       pbdh->verify_merchant_sig = true;
    796       break;
    797     }
    798   }
    799   return GNUNET_OK;
    800 }
    801 
    802 
    803 enum TALER_ErrorCode
    804 TALER_EXCHANGE_post_batch_deposit_start (
    805   struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh,
    806   TALER_EXCHANGE_PostBatchDepositCallback cb,
    807   TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE *cb_cls)
    808 {
    809   CURL *eh;
    810 
    811   if (pbdh->verify_merchant_sig &&
    812       (GNUNET_OK !=
    813        TALER_merchant_contract_verify (
    814          &pbdh->dcd.h_contract_terms,
    815          &pbdh->dcd.merchant_pub,
    816          &pbdh->dcd.merchant_sig)) )
    817   {
    818     GNUNET_break_op (0);
    819     return TALER_EC_GENERIC_PARAMETER_MALFORMED;
    820   }
    821   pbdh->cb = cb;
    822   pbdh->cb_cls = cb_cls;
    823   pbdh->url = TALER_url_join (pbdh->base_url,
    824                               "batch-deposit",
    825                               NULL);
    826   if (NULL == pbdh->url)
    827   {
    828     GNUNET_break (0);
    829     return TALER_EC_GENERIC_ALLOCATION_FAILURE;
    830   }
    831   eh = TALER_EXCHANGE_curl_easy_get_ (pbdh->url);
    832   if ( (NULL == eh) ||
    833        (GNUNET_OK !=
    834         TALER_curl_easy_post (&pbdh->post_ctx,
    835                               eh,
    836                               pbdh->deposit_obj)) )
    837   {
    838     GNUNET_break (0);
    839     if (NULL != eh)
    840       curl_easy_cleanup (eh);
    841     return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
    842   }
    843   /* Help debug #11305 */
    844   GNUNET_assert (CURLE_OK ==
    845                  curl_easy_setopt (eh,
    846                                    CURLOPT_VERBOSE,
    847                                    1));
    848   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    849               "URL for batch-deposit: `%s'\n",
    850               pbdh->url);
    851   pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx,
    852                                     eh,
    853                                     pbdh->post_ctx.headers,
    854                                     &handle_deposit_finished,
    855                                     pbdh);
    856   if (NULL == pbdh->job)
    857     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    858   return TALER_EC_NONE;
    859 }
    860 
    861 
    862 void
    863 TALER_EXCHANGE_post_batch_deposit_cancel (
    864   struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh)
    865 {
    866   struct TEAH_AuditorInteractionEntry *aie;
    867 
    868   while (NULL != (aie = pbdh->ai_head))
    869   {
    870     GNUNET_assert (aie->dh == pbdh);
    871     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    872                 "Not sending deposit confirmation to auditor `%s' due to cancellation\n",
    873                 aie->auditor_url);
    874     TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
    875     GNUNET_CONTAINER_DLL_remove (pbdh->ai_head,
    876                                  pbdh->ai_tail,
    877                                  aie);
    878     GNUNET_free (aie);
    879   }
    880   if (NULL != pbdh->job)
    881   {
    882     GNUNET_CURL_job_cancel (pbdh->job);
    883     pbdh->job = NULL;
    884   }
    885   TALER_EXCHANGE_keys_decref (pbdh->keys);
    886   GNUNET_free (pbdh->base_url);
    887   GNUNET_free (pbdh->url);
    888   GNUNET_free (pbdh->cdds);
    889   TALER_curl_easy_post_finished (&pbdh->post_ctx);
    890   json_decref (pbdh->deposit_obj);
    891   json_decref (pbdh->response);
    892   GNUNET_free (pbdh);
    893 }
    894 
    895 
    896 /* end of exchange_api_post-batch-deposit.c */