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


      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 "policy extensions" which were removed as a feature.
    142    * For now always all zeros!
    143    */
    144   struct TALER_ExtensionPolicyHashP h_policy;
    145 
    146   /**
    147    * Time when this confirmation was generated / when the exchange received
    148    * the deposit request.
    149    */
    150   struct GNUNET_TIME_Timestamp exchange_timestamp;
    151 
    152   /**
    153    * Exchange signature, set for #auditor_cb.
    154    */
    155   struct TALER_ExchangeSignatureP exchange_sig;
    156 
    157   /**
    158    * Head of DLL of interactions with this auditor.
    159    */
    160   struct TEAH_AuditorInteractionEntry *ai_head;
    161 
    162   /**
    163    * Tail of DLL of interactions with this auditor.
    164    */
    165   struct TEAH_AuditorInteractionEntry *ai_tail;
    166 
    167   /**
    168    * Result to return to the application once @e ai_head is empty.
    169    */
    170   struct TALER_EXCHANGE_PostBatchDepositResponse dr;
    171 
    172   /**
    173    * Exchange signing public key, set for #auditor_cb.
    174    */
    175   struct TALER_ExchangePublicKeyP exchange_pub;
    176 
    177   /**
    178    * Total amount deposited without fees as calculated by us.
    179    */
    180   struct TALER_Amount total_without_fee;
    181 
    182   /**
    183    * Response object to free at the end.
    184    */
    185   json_t *response;
    186 
    187   /**
    188    * The JSON body to POST (built during _create, used during _start).
    189    */
    190   json_t *deposit_obj;
    191 
    192   /**
    193    * Chance that we will inform the auditor about the deposit
    194    * is 1:n, where the value of this field is "n".
    195    */
    196   unsigned int auditor_chance;
    197 
    198   /**
    199    * Length of the @e cdds array.
    200    */
    201   unsigned int num_cdds;
    202 
    203   /**
    204    * Whether to verify the merchant signature on the contract terms.
    205    */
    206   bool verify_merchant_sig;
    207 
    208 };
    209 
    210 
    211 /**
    212  * Finish batch deposit operation by calling the callback.
    213  *
    214  * @param[in] dh handle to finished batch deposit operation
    215  */
    216 static void
    217 finish_dh (struct TALER_EXCHANGE_PostBatchDepositHandle *dh)
    218 {
    219   dh->cb (dh->cb_cls,
    220           &dh->dr);
    221   TALER_EXCHANGE_post_batch_deposit_cancel (dh);
    222 }
    223 
    224 
    225 /**
    226  * Function called with the result from our call to the
    227  * auditor's /deposit-confirmation handler.
    228  *
    229  * @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
    230  * @param dcr response
    231  */
    232 static void
    233 acc_confirmation_cb (
    234   void *cls,
    235   const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
    236 {
    237   struct TEAH_AuditorInteractionEntry *aie = cls;
    238   struct TALER_EXCHANGE_PostBatchDepositHandle *dh = aie->dh;
    239 
    240   if (MHD_HTTP_OK != dcr->hr.http_status)
    241   {
    242     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    243                 "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n",
    244                 aie->auditor_url,
    245                 dcr->hr.http_status,
    246                 dcr->hr.ec);
    247   }
    248   GNUNET_CONTAINER_DLL_remove (dh->ai_head,
    249                                dh->ai_tail,
    250                                aie);
    251   GNUNET_free (aie);
    252   if (NULL == dh->ai_head)
    253     finish_dh (dh);
    254 }
    255 
    256 
    257 /**
    258  * Function called for each auditor to give us a chance to possibly
    259  * launch a deposit confirmation interaction.
    260  *
    261  * @param cls closure
    262  * @param auditor_url base URL of the auditor
    263  * @param auditor_pub public key of the auditor
    264  */
    265 static void
    266 auditor_cb (void *cls,
    267             const char *auditor_url,
    268             const struct TALER_AuditorPublicKeyP *auditor_pub)
    269 {
    270   struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls;
    271   const struct TALER_EXCHANGE_SigningPublicKey *spk;
    272   struct TEAH_AuditorInteractionEntry *aie;
    273   const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (
    274                                                   dh->num_cdds)];
    275   const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL (
    276                                                   dh->num_cdds)];
    277 
    278   for (unsigned int i = 0; i<dh->num_cdds; i++)
    279   {
    280     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i];
    281 
    282     csigs[i] = &cdd->coin_sig;
    283     cpubs[i] = &cdd->coin_pub;
    284   }
    285 
    286   if (0 !=
    287       GNUNET_CRYPTO_random_u32 (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   TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri,
    644                                       &dcd->wire_salt,
    645                                       &dh->h_wire);
    646   deposits = json_array ();
    647   GNUNET_assert (NULL != deposits);
    648   GNUNET_assert (GNUNET_OK ==
    649                  TALER_amount_set_zero (cdds[0].amount.currency,
    650                                         &dh->total_without_fee));
    651   for (unsigned int i = 0; i<num_cdds; i++)
    652   {
    653     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i];
    654     const struct TALER_EXCHANGE_DenomPublicKey *dki;
    655     const struct TALER_AgeCommitmentHashP *h_age_commitmentp;
    656     struct TALER_Amount amount_without_fee;
    657 
    658     dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
    659                                                        &cdd->h_denom_pub);
    660     if (NULL == dki)
    661     {
    662       *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
    663       GNUNET_break_op (0);
    664       json_decref (deposits);
    665       GNUNET_free (dh->base_url);
    666       GNUNET_free (dh->cdds);
    667       GNUNET_free (dh);
    668       return NULL;
    669     }
    670     if (0 >
    671         TALER_amount_subtract (&amount_without_fee,
    672                                &cdd->amount,
    673                                &dki->fees.deposit))
    674     {
    675       *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
    676       GNUNET_break_op (0);
    677       json_decref (deposits);
    678       GNUNET_free (dh->base_url);
    679       GNUNET_free (dh->cdds);
    680       GNUNET_free (dh);
    681       return NULL;
    682     }
    683     GNUNET_assert (0 <=
    684                    TALER_amount_add (&dh->total_without_fee,
    685                                      &dh->total_without_fee,
    686                                      &amount_without_fee));
    687     if (GNUNET_OK !=
    688         TALER_EXCHANGE_verify_deposit_signature_ (dcd,
    689                                                   &dh->h_policy,
    690                                                   &dh->h_wire,
    691                                                   cdd,
    692                                                   dki))
    693     {
    694       *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID;
    695       GNUNET_break_op (0);
    696       json_decref (deposits);
    697       GNUNET_free (dh->base_url);
    698       GNUNET_free (dh->cdds);
    699       GNUNET_free (dh);
    700       return NULL;
    701     }
    702     if (GNUNET_is_zero (&cdd->h_age_commitment))
    703       h_age_commitmentp = NULL;
    704     else
    705       h_age_commitmentp = &cdd->h_age_commitment;
    706     GNUNET_assert (
    707       0 ==
    708       json_array_append_new (
    709         deposits,
    710         GNUNET_JSON_PACK (
    711           TALER_JSON_pack_amount ("contribution",
    712                                   &cdd->amount),
    713           GNUNET_JSON_pack_data_auto ("denom_pub_hash",
    714                                       &cdd->h_denom_pub),
    715           TALER_JSON_pack_denom_sig ("ub_sig",
    716                                      &cdd->denom_sig),
    717           GNUNET_JSON_pack_data_auto ("coin_pub",
    718                                       &cdd->coin_pub),
    719           GNUNET_JSON_pack_allow_null (
    720             GNUNET_JSON_pack_data_auto ("h_age_commitment",
    721                                         h_age_commitmentp)),
    722           GNUNET_JSON_pack_data_auto ("coin_sig",
    723                                       &cdd->coin_sig)
    724           )));
    725   }
    726 
    727   if (GNUNET_is_zero (&dcd->wallet_data_hash))
    728     wallet_data_hashp = NULL;
    729   else
    730     wallet_data_hashp = &dcd->wallet_data_hash;
    731   dh->deposit_obj = GNUNET_JSON_PACK (
    732     TALER_JSON_pack_full_payto ("merchant_payto_uri",
    733                                 dcd->merchant_payto_uri),
    734     GNUNET_JSON_pack_allow_null (
    735       GNUNET_JSON_pack_string ("extra_wire_subject_metadata",
    736                                dcd->extra_wire_subject_metadata)),
    737     GNUNET_JSON_pack_data_auto ("wire_salt",
    738                                 &dcd->wire_salt),
    739     GNUNET_JSON_pack_data_auto ("h_contract_terms",
    740                                 &dcd->h_contract_terms),
    741     GNUNET_JSON_pack_array_steal ("coins",
    742                                   deposits),
    743     GNUNET_JSON_pack_allow_null (
    744       GNUNET_JSON_pack_data_auto ("wallet_data_hash",
    745                                   wallet_data_hashp)),
    746     GNUNET_JSON_pack_timestamp ("timestamp",
    747                                 dcd->wallet_timestamp),
    748     GNUNET_JSON_pack_data_auto ("merchant_pub",
    749                                 &dcd->merchant_pub),
    750     GNUNET_JSON_pack_data_auto ("merchant_sig",
    751                                 &dcd->merchant_sig),
    752     GNUNET_JSON_pack_allow_null (
    753       GNUNET_JSON_pack_timestamp ("refund_deadline",
    754                                   dcd->refund_deadline)),
    755     GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
    756                                 dcd->wire_deadline));
    757   if (NULL == dh->deposit_obj)
    758   {
    759     GNUNET_break (0);
    760     *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
    761     GNUNET_free (dh->base_url);
    762     GNUNET_free (dh->cdds);
    763     GNUNET_free (dh);
    764     return NULL;
    765   }
    766   dh->keys = TALER_EXCHANGE_keys_incref (keys);
    767   *ec = TALER_EC_NONE;
    768   return dh;
    769 }
    770 
    771 
    772 enum GNUNET_GenericReturnValue
    773 TALER_EXCHANGE_post_batch_deposit_set_options_ (
    774   struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh,
    775   unsigned int num_options,
    776   const struct TALER_EXCHANGE_PostBatchDepositOptionValue options[])
    777 {
    778   for (unsigned int i = 0; i < num_options; i++)
    779   {
    780     const struct TALER_EXCHANGE_PostBatchDepositOptionValue *opt = &options[i];
    781     switch (opt->option)
    782     {
    783     case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_END:
    784       return GNUNET_OK;
    785     case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_FORCE_DC:
    786       pbdh->auditor_chance = 1;
    787       break;
    788     case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_VERIFY_MERCHANT_SIG:
    789       pbdh->verify_merchant_sig = true;
    790       break;
    791     }
    792   }
    793   return GNUNET_OK;
    794 }
    795 
    796 
    797 enum TALER_ErrorCode
    798 TALER_EXCHANGE_post_batch_deposit_start (
    799   struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh,
    800   TALER_EXCHANGE_PostBatchDepositCallback cb,
    801   TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE *cb_cls)
    802 {
    803   CURL *eh;
    804 
    805   if (pbdh->verify_merchant_sig &&
    806       (GNUNET_OK !=
    807        TALER_merchant_contract_verify (
    808          &pbdh->dcd.h_contract_terms,
    809          &pbdh->dcd.merchant_pub,
    810          &pbdh->dcd.merchant_sig)) )
    811   {
    812     GNUNET_break_op (0);
    813     return TALER_EC_GENERIC_PARAMETER_MALFORMED;
    814   }
    815   pbdh->cb = cb;
    816   pbdh->cb_cls = cb_cls;
    817   pbdh->url = TALER_url_join (pbdh->base_url,
    818                               "batch-deposit",
    819                               NULL);
    820   if (NULL == pbdh->url)
    821   {
    822     GNUNET_break (0);
    823     return TALER_EC_GENERIC_ALLOCATION_FAILURE;
    824   }
    825   eh = TALER_EXCHANGE_curl_easy_get_ (pbdh->url);
    826   if ( (NULL == eh) ||
    827        (GNUNET_OK !=
    828         TALER_curl_easy_post (&pbdh->post_ctx,
    829                               eh,
    830                               pbdh->deposit_obj)) )
    831   {
    832     GNUNET_break (0);
    833     if (NULL != eh)
    834       curl_easy_cleanup (eh);
    835     return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE;
    836   }
    837   /* Help debug #11305 */
    838   GNUNET_assert (CURLE_OK ==
    839                  curl_easy_setopt (eh,
    840                                    CURLOPT_VERBOSE,
    841                                    1));
    842   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    843               "URL for batch-deposit: `%s'\n",
    844               pbdh->url);
    845   pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx,
    846                                     eh,
    847                                     pbdh->post_ctx.headers,
    848                                     &handle_deposit_finished,
    849                                     pbdh);
    850   if (NULL == pbdh->job)
    851     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    852   return TALER_EC_NONE;
    853 }
    854 
    855 
    856 void
    857 TALER_EXCHANGE_post_batch_deposit_cancel (
    858   struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh)
    859 {
    860   struct TEAH_AuditorInteractionEntry *aie;
    861 
    862   while (NULL != (aie = pbdh->ai_head))
    863   {
    864     GNUNET_assert (aie->dh == pbdh);
    865     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    866                 "Not sending deposit confirmation to auditor `%s' due to cancellation\n",
    867                 aie->auditor_url);
    868     TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
    869     GNUNET_CONTAINER_DLL_remove (pbdh->ai_head,
    870                                  pbdh->ai_tail,
    871                                  aie);
    872     GNUNET_free (aie);
    873   }
    874   if (NULL != pbdh->job)
    875   {
    876     GNUNET_CURL_job_cancel (pbdh->job);
    877     pbdh->job = NULL;
    878   }
    879   TALER_EXCHANGE_keys_decref (pbdh->keys);
    880   GNUNET_free (pbdh->base_url);
    881   GNUNET_free (pbdh->url);
    882   GNUNET_free (pbdh->cdds);
    883   TALER_curl_easy_post_finished (&pbdh->post_ctx);
    884   json_decref (pbdh->deposit_obj);
    885   json_decref (pbdh->response);
    886   GNUNET_free (pbdh);
    887 }
    888 
    889 
    890 /* end of exchange_api_post-batch-deposit.c */