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


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