LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_refreshes_reveal.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 0 364 0.0 %
Date: 2022-08-25 06:15:09 Functions: 0 5 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2022 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero 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 Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_refreshes_reveal.c
      18             :  * @brief Handle /refreshes/$RCH/reveal requests
      19             :  * @author Florian Dold
      20             :  * @author Benedikt Mueller
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <jansson.h>
      26             : #include <microhttpd.h>
      27             : #include "taler_mhd_lib.h"
      28             : #include "taler-exchange-httpd_mhd.h"
      29             : #include "taler-exchange-httpd_refreshes_reveal.h"
      30             : #include "taler-exchange-httpd_responses.h"
      31             : #include "taler-exchange-httpd_keys.h"
      32             : 
      33             : 
      34             : /**
      35             :  * Send a response for "/refreshes/$RCH/reveal".
      36             :  *
      37             :  * @param connection the connection to send the response to
      38             :  * @param num_freshcoins number of new coins for which we reveal data
      39             :  * @param rrcs array of @a num_freshcoins signatures revealed
      40             :  * @return a MHD result code
      41             :  */
      42             : static MHD_RESULT
      43           0 : reply_refreshes_reveal_success (
      44             :   struct MHD_Connection *connection,
      45             :   unsigned int num_freshcoins,
      46             :   const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs)
      47             : {
      48             :   json_t *list;
      49             : 
      50           0 :   list = json_array ();
      51           0 :   GNUNET_assert (NULL != list);
      52           0 :   for (unsigned int freshcoin_index = 0;
      53             :        freshcoin_index < num_freshcoins;
      54           0 :        freshcoin_index++)
      55             :   {
      56             :     json_t *obj;
      57             : 
      58           0 :     obj = GNUNET_JSON_PACK (
      59             :       TALER_JSON_pack_blinded_denom_sig ("ev_sig",
      60             :                                          &rrcs[freshcoin_index].coin_sig));
      61           0 :     GNUNET_assert (0 ==
      62             :                    json_array_append_new (list,
      63             :                                           obj));
      64             :   }
      65             : 
      66           0 :   return TALER_MHD_REPLY_JSON_PACK (
      67             :     connection,
      68             :     MHD_HTTP_OK,
      69             :     GNUNET_JSON_pack_array_steal ("ev_sigs",
      70             :                                   list));
      71             : }
      72             : 
      73             : 
      74             : /**
      75             :  * State for a /refreshes/$RCH/reveal operation.
      76             :  */
      77             : struct RevealContext
      78             : {
      79             : 
      80             :   /**
      81             :    * Commitment of the refresh operation.
      82             :    */
      83             :   struct TALER_RefreshCommitmentP rc;
      84             : 
      85             :   /**
      86             :    * Transfer public key at gamma.
      87             :    */
      88             :   struct TALER_TransferPublicKeyP gamma_tp;
      89             : 
      90             :   /**
      91             :    * Transfer private keys revealed to us.
      92             :    */
      93             :   struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
      94             : 
      95             :   /**
      96             :    * Melt data for our session we got from the database for @e rc.
      97             :    */
      98             :   struct TALER_EXCHANGEDB_Melt melt;
      99             : 
     100             :   /**
     101             :    * Denominations being requested.
     102             :    */
     103             :   const struct TEH_DenominationKey **dks;
     104             : 
     105             :   /**
     106             :    * Age commitment that was used for the original coin.  If not NULL, its hash
     107             :    * should be the same as melt.session.h_age_commitment.
     108             :    */
     109             :   struct TALER_AgeCommitment *old_age_commitment;
     110             : 
     111             :   /**
     112             :    * Array of information about fresh coins being revealed.
     113             :    */
     114             :   /* FIXME: const would be nicer here, but we initialize
     115             :      the 'alg_values' in the verification
     116             :      routine; suboptimal to be fixed... */
     117             :   struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs;
     118             : 
     119             :   /**
     120             :    * Envelopes to be signed.
     121             :    */
     122             :   struct TALER_RefreshCoinData *rcds;
     123             : 
     124             :   /**
     125             :    * Refresh master secret.
     126             :    */
     127             :   struct TALER_RefreshMasterSecretP rms;
     128             : 
     129             :   /**
     130             :    * Size of the @e dks, @e rcds and @e ev_sigs arrays (if non-NULL).
     131             :    */
     132             :   unsigned int num_fresh_coins;
     133             : 
     134             :   /**
     135             :    * True if @e rms was not provided.
     136             :    */
     137             :   bool no_rms;
     138             : };
     139             : 
     140             : 
     141             : /**
     142             :  * Check client's revelation against the original commitment.
     143             :  * The client is revealing to us the
     144             :  * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
     145             :  * revealed transfer keys would allow linkage to the blinded coins.
     146             :  *
     147             :  * IF it returns #GNUNET_OK, the transaction logic MUST
     148             :  * NOT queue a MHD response.  IF it returns an error, the
     149             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.
     150             :  *
     151             :  * @param rctx our operation context
     152             :  * @param connection MHD request which triggered the transaction
     153             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     154             :  *             if transaction failed (!)
     155             :  * @return #GNUNET_OK if commitment was OK
     156             :  */
     157             : static enum GNUNET_GenericReturnValue
     158           0 : check_commitment (struct RevealContext *rctx,
     159             :                   struct MHD_Connection *connection,
     160             :                   MHD_RESULT *mhd_ret)
     161           0 : {
     162           0 :   struct TALER_CsNonce nonces[rctx->num_fresh_coins];
     163           0 :   unsigned int aoff = 0;
     164             : 
     165           0 :   for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
     166             :   {
     167           0 :     const struct TALER_DenominationPublicKey *dk = &rctx->dks[j]->denom_pub;
     168             : 
     169           0 :     if (dk->cipher != rctx->rcds[j].blinded_planchet.cipher)
     170             :     {
     171           0 :       GNUNET_break (0);
     172           0 :       *mhd_ret = TALER_MHD_reply_with_error (
     173             :         connection,
     174             :         MHD_HTTP_BAD_REQUEST,
     175             :         TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
     176             :         NULL);
     177           0 :       return GNUNET_SYSERR;
     178             :     }
     179           0 :     switch (dk->cipher)
     180             :     {
     181           0 :     case TALER_DENOMINATION_INVALID:
     182           0 :       GNUNET_break (0);
     183           0 :       *mhd_ret = TALER_MHD_reply_with_error (
     184             :         connection,
     185             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     186             :         TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     187             :         NULL);
     188           0 :       return GNUNET_SYSERR;
     189           0 :     case TALER_DENOMINATION_RSA:
     190           0 :       continue;
     191           0 :     case TALER_DENOMINATION_CS:
     192             :       nonces[aoff]
     193           0 :         = rctx->rcds[j].blinded_planchet.details.cs_blinded_planchet.nonce;
     194           0 :       aoff++;
     195           0 :       break;
     196             :     }
     197           0 :   }
     198             : 
     199             :   // OPTIMIZE: do this in batch later!
     200           0 :   aoff = 0;
     201           0 :   for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
     202             :   {
     203           0 :     const struct TALER_DenominationPublicKey *dk = &rctx->dks[j]->denom_pub;
     204           0 :     struct TALER_ExchangeWithdrawValues *alg_values
     205           0 :       = &rctx->rrcs[j].exchange_vals;
     206             : 
     207           0 :     alg_values->cipher = dk->cipher;
     208           0 :     switch (dk->cipher)
     209             :     {
     210           0 :     case TALER_DENOMINATION_INVALID:
     211           0 :       GNUNET_assert (0);
     212             :       return GNUNET_SYSERR;
     213           0 :     case TALER_DENOMINATION_RSA:
     214           0 :       continue;
     215           0 :     case TALER_DENOMINATION_CS:
     216             :       {
     217             :         enum TALER_ErrorCode ec;
     218             : 
     219           0 :         ec = TEH_keys_denomination_cs_r_pub_melt (
     220           0 :           &rctx->rrcs[j].h_denom_pub,
     221           0 :           &nonces[aoff],
     222             :           &alg_values->details.cs_values);
     223           0 :         if (TALER_EC_NONE != ec)
     224             :         {
     225           0 :           *mhd_ret = TALER_MHD_reply_with_error (connection,
     226             :                                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
     227             :                                                  ec,
     228             :                                                  NULL);
     229           0 :           return GNUNET_SYSERR;
     230             :         }
     231           0 :         aoff++;
     232             :       }
     233             :     }
     234             :   }
     235             :   /* Verify commitment */
     236             :   {
     237             :     /* Note that the contents of rcs[melt.session.noreveal_index]
     238             :        will be aliased and are *not* allocated (or deallocated) in
     239             :        this function -- in contrast to the other offsets! */
     240             :     struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
     241             :     struct TALER_RefreshCommitmentP rc_expected;
     242             :     unsigned int off;
     243             : 
     244           0 :     off = 0; /* did we pass session.noreveal_index yet? */
     245           0 :     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     246             :     {
     247           0 :       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
     248             : 
     249           0 :       if (i == rctx->melt.session.noreveal_index)
     250             :       {
     251             :         /* Take these coin envelopes from the client */
     252           0 :         rce->transfer_pub = rctx->gamma_tp;
     253           0 :         rce->new_coins = rctx->rcds;
     254           0 :         off = 1;
     255             :       }
     256             :       else
     257             :       {
     258             :         /* Reconstruct coin envelopes from transfer private key */
     259           0 :         const struct TALER_TransferPrivateKeyP *tpriv
     260           0 :           = &rctx->transfer_privs[i - off];
     261             :         struct TALER_TransferSecretP ts;
     262           0 :         struct TALER_AgeCommitmentHash h = {0};
     263           0 :         struct TALER_AgeCommitmentHash *hac = NULL;
     264             : 
     265           0 :         GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
     266             :                                             &rce->transfer_pub.ecdhe_pub);
     267           0 :         TEH_METRICS_num_keyexchanges[TEH_MT_KEYX_ECDH]++;
     268           0 :         TALER_link_reveal_transfer_secret (tpriv,
     269           0 :                                            &rctx->melt.session.coin.coin_pub,
     270             :                                            &ts);
     271           0 :         rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
     272             :                                            struct TALER_RefreshCoinData);
     273           0 :         aoff = 0;
     274           0 :         for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
     275             :         {
     276           0 :           const struct TALER_DenominationPublicKey *dk
     277           0 :             = &rctx->dks[j]->denom_pub;
     278           0 :           struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
     279             :           struct TALER_CoinSpendPrivateKeyP coin_priv;
     280             :           union TALER_DenominationBlindingKeyP bks;
     281           0 :           const struct TALER_ExchangeWithdrawValues *alg_value
     282           0 :             = &rctx->rrcs[j].exchange_vals;
     283           0 :           struct TALER_PlanchetDetail pd = {0};
     284             :           struct TALER_CoinPubHashP c_hash;
     285             :           struct TALER_PlanchetMasterSecretP ps;
     286             : 
     287           0 :           rcd->dk = &rctx->dks[j]->denom_pub;
     288           0 :           TALER_transfer_secret_to_planchet_secret (&ts,
     289             :                                                     j,
     290             :                                                     &ps);
     291           0 :           TALER_planchet_setup_coin_priv (&ps,
     292             :                                           alg_value,
     293             :                                           &coin_priv);
     294           0 :           TALER_planchet_blinding_secret_create (&ps,
     295             :                                                  alg_value,
     296             :                                                  &bks);
     297             :           /* Calculate, if applicable, the age commitment and its hash, from
     298             :            * the transfer_secret and the old age commitment. */
     299           0 :           if (NULL != rctx->old_age_commitment)
     300             :           {
     301           0 :             struct TALER_AgeCommitmentProof acp = {
     302             :               /* we only need the commitment, not the proof, for the call to
     303             :                * TALER_age_commitment_derive. */
     304           0 :               .commitment = *(rctx->old_age_commitment)
     305             :             };
     306           0 :             struct TALER_AgeCommitmentProof nacp = {0};
     307             : 
     308           0 :             GNUNET_assert (GNUNET_OK ==
     309             :                            TALER_age_commitment_derive (
     310             :                              &acp,
     311             :                              &ts.key,
     312             :                              &nacp));
     313             : 
     314           0 :             TALER_age_commitment_hash (&nacp.commitment, &h);
     315           0 :             hac = &h;
     316             :           }
     317             : 
     318           0 :           GNUNET_assert (GNUNET_OK ==
     319             :                          TALER_planchet_prepare (rcd->dk,
     320             :                                                  alg_value,
     321             :                                                  &bks,
     322             :                                                  &coin_priv,
     323             :                                                  hac,
     324             :                                                  &c_hash,
     325             :                                                  &pd));
     326           0 :           if (TALER_DENOMINATION_CS == dk->cipher)
     327             :           {
     328           0 :             pd.blinded_planchet.details.cs_blinded_planchet.nonce =
     329             :               nonces[aoff];
     330           0 :             aoff++;
     331             :           }
     332           0 :           rcd->blinded_planchet = pd.blinded_planchet;
     333             :         }
     334             :       }
     335             :     }
     336           0 :     TALER_refresh_get_commitment (&rc_expected,
     337             :                                   TALER_CNC_KAPPA,
     338           0 :                                   rctx->no_rms
     339             :                                   ? NULL
     340             :                                   : &rctx->rms,
     341             :                                   rctx->num_fresh_coins,
     342             :                                   rcs,
     343           0 :                                   &rctx->melt.session.coin.coin_pub,
     344           0 :                                   &rctx->melt.session.amount_with_fee);
     345             : 
     346             :     /* Free resources allocated above */
     347           0 :     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     348             :     {
     349           0 :       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
     350             : 
     351           0 :       if (i == rctx->melt.session.noreveal_index)
     352           0 :         continue; /* This offset is special: not allocated! */
     353           0 :       for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
     354             :       {
     355           0 :         struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
     356             : 
     357           0 :         TALER_blinded_planchet_free (&rcd->blinded_planchet);
     358             :       }
     359           0 :       GNUNET_free (rce->new_coins);
     360             :     }
     361             : 
     362             :     /* Verify rc_expected matches rc */
     363           0 :     if (0 != GNUNET_memcmp (&rctx->rc,
     364             :                             &rc_expected))
     365             :     {
     366           0 :       GNUNET_break_op (0);
     367           0 :       *mhd_ret = TALER_MHD_REPLY_JSON_PACK (
     368             :         connection,
     369             :         MHD_HTTP_CONFLICT,
     370             :         TALER_JSON_pack_ec (
     371             :           TALER_EC_EXCHANGE_REFRESHES_REVEAL_COMMITMENT_VIOLATION),
     372             :         GNUNET_JSON_pack_data_auto ("rc_expected",
     373             :                                     &rc_expected));
     374           0 :       return GNUNET_SYSERR;
     375             :     }
     376             :   } /* end of checking "rc_expected" */
     377             : 
     378             :   /* check amounts add up! */
     379             :   {
     380             :     struct TALER_Amount refresh_cost;
     381             : 
     382           0 :     refresh_cost = rctx->melt.melt_fee;
     383           0 :     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     384             :     {
     385             :       struct TALER_Amount total;
     386             : 
     387           0 :       if ( (0 >
     388           0 :             TALER_amount_add (&total,
     389           0 :                               &rctx->dks[i]->meta.fees.withdraw,
     390           0 :                               &rctx->dks[i]->meta.value)) ||
     391             :            (0 >
     392           0 :             TALER_amount_add (&refresh_cost,
     393             :                               &refresh_cost,
     394             :                               &total)) )
     395             :       {
     396           0 :         GNUNET_break_op (0);
     397           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     398             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     399             :                                                TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
     400             :                                                NULL);
     401           0 :         return GNUNET_SYSERR;
     402             :       }
     403             :     }
     404           0 :     if (0 < TALER_amount_cmp (&refresh_cost,
     405           0 :                               &rctx->melt.session.amount_with_fee))
     406             :     {
     407           0 :       GNUNET_break_op (0);
     408           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     409             :                                              MHD_HTTP_BAD_REQUEST,
     410             :                                              TALER_EC_EXCHANGE_REFRESHES_REVEAL_AMOUNT_INSUFFICIENT,
     411             :                                              NULL);
     412           0 :       return GNUNET_SYSERR;
     413             :     }
     414             :   }
     415           0 :   return GNUNET_OK;
     416             : }
     417             : 
     418             : 
     419             : /**
     420             :  * Resolve denomination hashes.
     421             :  *
     422             :  * @param connection the MHD connection to handle
     423             :  * @param rctx context for the operation, partially built at this time
     424             :  * @param link_sigs_json link signatures in JSON format
     425             :  * @param new_denoms_h_json requests for fresh coins to be created
     426             :  * @param old_age_commitment_json age commitment that went into the withdrawal, maybe NULL
     427             :  * @param coin_evs envelopes of gamma-selected coins to be signed
     428             :  * @return MHD result code
     429             :  */
     430             : static MHD_RESULT
     431           0 : resolve_refreshes_reveal_denominations (
     432             :   struct MHD_Connection *connection,
     433             :   struct RevealContext *rctx,
     434             :   const json_t *link_sigs_json,
     435             :   const json_t *new_denoms_h_json,
     436             :   const json_t *old_age_commitment_json,
     437             :   const json_t *coin_evs)
     438           0 : {
     439           0 :   unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
     440             :   /* We know num_fresh_coins is bounded by #TALER_MAX_FRESH_COINS, so this is safe */
     441           0 :   const struct TEH_DenominationKey *dks[num_fresh_coins];
     442             :   const struct TEH_DenominationKey *old_dk;
     443           0 :   struct TALER_RefreshCoinData rcds[num_fresh_coins];
     444           0 :   struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[num_fresh_coins];
     445             :   MHD_RESULT ret;
     446             :   struct TEH_KeyStateHandle *ksh;
     447             :   uint64_t melt_serial_id;
     448             :   enum GNUNET_DB_QueryStatus qs;
     449             : 
     450           0 :   memset (dks, 0, sizeof (dks));
     451           0 :   memset (rrcs, 0, sizeof (rrcs));
     452           0 :   memset (rcds, 0, sizeof (rcds));
     453           0 :   rctx->num_fresh_coins = num_fresh_coins;
     454             : 
     455           0 :   ksh = TEH_keys_get_state ();
     456           0 :   if (NULL == ksh)
     457             :   {
     458           0 :     return TALER_MHD_reply_with_error (connection,
     459             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     460             :                                        TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
     461             :                                        NULL);
     462             :   }
     463             : 
     464             :   /* lookup old_coin_pub in database */
     465             :   {
     466             :     enum GNUNET_DB_QueryStatus qs;
     467             : 
     468           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     469           0 :         (qs = TEH_plugin->get_melt (TEH_plugin->cls,
     470           0 :                                     &rctx->rc,
     471             :                                     &rctx->melt,
     472             :                                     &melt_serial_id)))
     473             :     {
     474           0 :       switch (qs)
     475             :       {
     476           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     477           0 :         ret = TALER_MHD_reply_with_error (connection,
     478             :                                           MHD_HTTP_NOT_FOUND,
     479             :                                           TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
     480             :                                           NULL);
     481           0 :         break;
     482           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
     483           0 :         ret = TALER_MHD_reply_with_error (connection,
     484             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
     485             :                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
     486             :                                           "melt");
     487           0 :         break;
     488           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
     489             :       default:
     490           0 :         GNUNET_break (0);   /* should be impossible */
     491           0 :         ret = TALER_MHD_reply_with_error (connection,
     492             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
     493             :                                           TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     494             :                                           NULL);
     495           0 :         break;
     496             :       }
     497           0 :       goto cleanup;
     498             :     }
     499           0 :     if (rctx->melt.session.noreveal_index >= TALER_CNC_KAPPA)
     500             :     {
     501           0 :       GNUNET_break (0);
     502           0 :       ret = TALER_MHD_reply_with_error (connection,
     503             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     504             :                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
     505             :                                         "melt");
     506           0 :       goto cleanup;
     507             :     }
     508             :   }
     509             : 
     510           0 :   old_dk = TEH_keys_denomination_by_hash2 (
     511             :     ksh,
     512           0 :     &rctx->melt.session.coin.denom_pub_hash,
     513             :     connection,
     514             :     &ret);
     515           0 :   if (NULL == old_dk)
     516           0 :     return ret;
     517             : 
     518             :   /* Parse denomination key hashes */
     519           0 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     520             :   {
     521             :     struct GNUNET_JSON_Specification spec[] = {
     522           0 :       GNUNET_JSON_spec_fixed_auto (NULL,
     523             :                                    &rrcs[i].h_denom_pub),
     524           0 :       GNUNET_JSON_spec_end ()
     525             :     };
     526             :     enum GNUNET_GenericReturnValue res;
     527             : 
     528           0 :     res = TALER_MHD_parse_json_array (connection,
     529             :                                       new_denoms_h_json,
     530             :                                       spec,
     531             :                                       i,
     532             :                                       -1);
     533           0 :     if (GNUNET_OK != res)
     534           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     535           0 :     dks[i] = TEH_keys_denomination_by_hash2 (ksh,
     536           0 :                                              &rrcs[i].h_denom_pub,
     537             :                                              connection,
     538             :                                              &ret);
     539           0 :     if (NULL == dks[i])
     540           0 :       return ret;
     541           0 :     if ( (TALER_DENOMINATION_CS == dks[i]->denom_pub.cipher) &&
     542           0 :          (rctx->no_rms) )
     543             :     {
     544           0 :       return TALER_MHD_reply_with_error (
     545             :         connection,
     546             :         MHD_HTTP_BAD_REQUEST,
     547             :         TALER_EC_GENERIC_PARAMETER_MISSING,
     548             :         "rms");
     549             :     }
     550           0 :     if (GNUNET_TIME_absolute_is_past (dks[i]->meta.expire_withdraw.abs_time))
     551             :     {
     552             :       /* This denomination is past the expiration time for withdraws */
     553           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     554             :         connection,
     555           0 :         &rrcs[i].h_denom_pub,
     556             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     557             :         "REVEAL");
     558             :     }
     559           0 :     if (GNUNET_TIME_absolute_is_future (dks[i]->meta.start.abs_time))
     560             :     {
     561             :       /* This denomination is not yet valid */
     562           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     563             :         connection,
     564           0 :         &rrcs[i].h_denom_pub,
     565             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     566             :         "REVEAL");
     567             :     }
     568           0 :     if (dks[i]->recoup_possible)
     569             :     {
     570             :       /* This denomination has been revoked */
     571           0 :       return TALER_MHD_reply_with_error (
     572             :         connection,
     573             :         MHD_HTTP_GONE,
     574             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
     575             :         NULL);
     576             :     }
     577             :   }
     578             : 
     579             :   /* Parse coin envelopes */
     580           0 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     581             :   {
     582           0 :     struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
     583             :     struct GNUNET_JSON_Specification spec[] = {
     584           0 :       TALER_JSON_spec_blinded_planchet (NULL,
     585             :                                         &rrc->blinded_planchet),
     586           0 :       GNUNET_JSON_spec_end ()
     587             :     };
     588             :     enum GNUNET_GenericReturnValue res;
     589             : 
     590           0 :     res = TALER_MHD_parse_json_array (connection,
     591             :                                       coin_evs,
     592             :                                       spec,
     593             :                                       i,
     594             :                                       -1);
     595           0 :     if (GNUNET_OK != res)
     596             :     {
     597           0 :       for (unsigned int j = 0; j<i; j++)
     598           0 :         TALER_blinded_planchet_free (&rrcs[j].blinded_planchet);
     599           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     600             :     }
     601           0 :     TALER_coin_ev_hash (&rrc->blinded_planchet,
     602           0 :                         &rrcs[i].h_denom_pub,
     603             :                         &rrc->coin_envelope_hash);
     604             :   }
     605             : 
     606           0 :   if (TEH_age_restriction_enabled &&
     607           0 :       ((NULL == old_age_commitment_json) !=
     608           0 :        TALER_AgeCommitmentHash_isNullOrZero (
     609             :          &rctx->melt.session.coin.h_age_commitment)))
     610             :   {
     611           0 :     GNUNET_break (0);
     612           0 :     return MHD_NO;
     613             :   }
     614             : 
     615             :   /* Reconstruct the old age commitment and verify its hash matches the one
     616             :    * from the melt request */
     617           0 :   if (TEH_age_restriction_enabled &&
     618             :       (NULL != old_age_commitment_json))
     619             :   {
     620             :     enum GNUNET_GenericReturnValue res;
     621             :     struct TALER_AgeCommitment *oac;
     622           0 :     size_t ng = json_array_size (old_age_commitment_json);
     623           0 :     bool failed = true;
     624             : 
     625             :     /* Has been checked in handle_refreshes_reveal_json() */
     626           0 :     GNUNET_assert (ng ==
     627             :                    TALER_extensions_age_restriction_num_groups ());
     628             : 
     629           0 :     rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment);
     630           0 :     oac = rctx->old_age_commitment;
     631           0 :     oac->mask = old_dk->meta.age_mask;
     632           0 :     oac->num = ng;
     633           0 :     oac->keys = GNUNET_new_array (ng, struct TALER_AgeCommitmentPublicKeyP);
     634             : 
     635             :     /* Extract old age commitment */
     636           0 :     for (unsigned int i = 0; i< ng; i++)
     637             :     {
     638             :       struct GNUNET_JSON_Specification ac_spec[] = {
     639           0 :         GNUNET_JSON_spec_fixed_auto (NULL,
     640             :                                      &oac->keys[i]),
     641           0 :         GNUNET_JSON_spec_end ()
     642             :       };
     643             : 
     644           0 :       res = TALER_MHD_parse_json_array (connection,
     645             :                                         old_age_commitment_json,
     646             :                                         ac_spec,
     647             :                                         i,
     648             :                                         -1);
     649             : 
     650           0 :       GNUNET_break_op (GNUNET_OK == res);
     651           0 :       if (GNUNET_OK != res)
     652           0 :         goto clean_age;
     653             :     }
     654             : 
     655             :     /* Sanity check: Compare hash from melting with hash of this age commitment */
     656             :     {
     657           0 :       struct TALER_AgeCommitmentHash hac = {0};
     658           0 :       TALER_age_commitment_hash (oac, &hac);
     659           0 :       if (0 != memcmp (&hac,
     660           0 :                        &rctx->melt.session.coin.h_age_commitment,
     661             :                        sizeof(struct TALER_AgeCommitmentHash)))
     662           0 :         goto clean_age;
     663             :     }
     664             : 
     665           0 :     failed = false;
     666             : 
     667           0 : clean_age:
     668           0 :     if (failed)
     669             :     {
     670           0 :       TALER_age_commitment_free (oac);
     671           0 :       return TALER_MHD_reply_with_error (connection,
     672             :                                          MHD_HTTP_BAD_REQUEST,
     673             :                                          TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
     674             :                                          "old_age_commitment");
     675             :     }
     676             :   }
     677             : 
     678             :   /* Parse link signatures array */
     679           0 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     680             :   {
     681             :     struct GNUNET_JSON_Specification link_spec[] = {
     682           0 :       GNUNET_JSON_spec_fixed_auto (NULL,
     683             :                                    &rrcs[i].orig_coin_link_sig),
     684           0 :       GNUNET_JSON_spec_end ()
     685             :     };
     686             :     enum GNUNET_GenericReturnValue res;
     687             : 
     688           0 :     res = TALER_MHD_parse_json_array (connection,
     689             :                                       link_sigs_json,
     690             :                                       link_spec,
     691             :                                       i,
     692             :                                       -1);
     693           0 :     if (GNUNET_OK != res)
     694           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     695             : 
     696             :     /* Check signature */
     697           0 :     TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     698           0 :     if (GNUNET_OK !=
     699           0 :         TALER_wallet_link_verify (
     700           0 :           &rrcs[i].h_denom_pub,
     701           0 :           &rctx->gamma_tp,
     702           0 :           &rrcs[i].coin_envelope_hash,
     703           0 :           &rctx->melt.session.coin.coin_pub,
     704           0 :           &rrcs[i].orig_coin_link_sig))
     705             :     {
     706           0 :       GNUNET_break_op (0);
     707           0 :       ret = TALER_MHD_reply_with_error (
     708             :         connection,
     709             :         MHD_HTTP_FORBIDDEN,
     710             :         TALER_EC_EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID,
     711             :         NULL);
     712           0 :       goto cleanup;
     713             :     }
     714             :   }
     715             : 
     716             :   /* prepare for check_commitment */
     717           0 :   for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     718             :   {
     719           0 :     const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
     720           0 :     struct TALER_RefreshCoinData *rcd = &rcds[i];
     721             : 
     722           0 :     rcd->blinded_planchet = rrc->blinded_planchet;
     723           0 :     rcd->dk = &dks[i]->denom_pub;
     724           0 :     if (rcd->blinded_planchet.cipher != rcd->dk->cipher)
     725             :     {
     726           0 :       GNUNET_break_op (0);
     727           0 :       ret = TALER_MHD_REPLY_JSON_PACK (
     728             :         connection,
     729             :         MHD_HTTP_BAD_REQUEST,
     730             :         TALER_JSON_pack_ec (
     731             :           TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH));
     732           0 :       goto cleanup;
     733             :     }
     734             :   }
     735             : 
     736           0 :   rctx->dks = dks;
     737           0 :   rctx->rcds = rcds;
     738           0 :   rctx->rrcs = rrcs;
     739           0 :   if (GNUNET_OK !=
     740           0 :       check_commitment (rctx,
     741             :                         connection,
     742             :                         &ret))
     743           0 :     goto cleanup;
     744             : 
     745           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     746             :               "Creating %u signatures\n",
     747             :               (unsigned int) rctx->num_fresh_coins);
     748             : 
     749             :   /* create fresh coin signatures */
     750           0 :   for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     751             :   {
     752             :     enum TALER_ErrorCode ec;
     753             : 
     754             :     // FIXME #7272: replace with a batch call that
     755             :     // passes all coins in once go!
     756           0 :     ec = TEH_keys_denomination_sign_melt (
     757           0 :       &rrcs[i].h_denom_pub,
     758           0 :       &rcds[i].blinded_planchet,
     759             :       &rrcs[i].coin_sig);
     760           0 :     if (TALER_EC_NONE != ec)
     761             :     {
     762           0 :       GNUNET_break (0);
     763           0 :       ret = TALER_MHD_reply_with_ec (connection,
     764             :                                      ec,
     765             :                                      NULL);
     766           0 :       goto cleanup;
     767             :     }
     768             :   }
     769             : 
     770           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     771             :               "Signatures ready, starting DB interaction\n");
     772             : 
     773           0 :   for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++)
     774             :   {
     775             :     bool changed;
     776             : 
     777             :     /* Persist operation result in DB */
     778           0 :     if (GNUNET_OK !=
     779           0 :         TEH_plugin->start (TEH_plugin->cls,
     780             :                            "insert_refresh_reveal batch"))
     781             :     {
     782           0 :       GNUNET_break (0);
     783           0 :       ret = TALER_MHD_reply_with_error (connection,
     784             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     785             :                                         TALER_EC_GENERIC_DB_START_FAILED,
     786             :                                         NULL);
     787           0 :       goto cleanup;
     788             :     }
     789           0 :     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     790             :     {
     791           0 :       struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
     792             : 
     793           0 :       rrc->blinded_planchet = rcds[i].blinded_planchet;
     794             :     }
     795           0 :     qs = TEH_plugin->insert_refresh_reveal (
     796           0 :       TEH_plugin->cls,
     797             :       melt_serial_id,
     798             :       num_fresh_coins,
     799             :       rrcs,
     800             :       TALER_CNC_KAPPA - 1,
     801           0 :       rctx->transfer_privs,
     802           0 :       &rctx->gamma_tp);
     803           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     804             :     {
     805           0 :       TEH_plugin->rollback (TEH_plugin->cls);
     806           0 :       continue;
     807             :     }
     808             :     /* 0 == qs is ok, as we did not check for repeated requests */
     809           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     810             :     {
     811           0 :       GNUNET_break (0);
     812           0 :       TEH_plugin->rollback (TEH_plugin->cls);
     813           0 :       ret = TALER_MHD_reply_with_error (connection,
     814             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     815             :                                         TALER_EC_GENERIC_DB_STORE_FAILED,
     816             :                                         "insert_refresh_reveal");
     817           0 :       goto cleanup;
     818             :     }
     819           0 :     changed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
     820           0 :     qs = TEH_plugin->commit (TEH_plugin->cls);
     821           0 :     if (qs >= 0)
     822             :     {
     823           0 :       if (changed)
     824           0 :         TEH_METRICS_num_success[TEH_MT_SUCCESS_REFRESH_REVEAL]++;
     825           0 :       break;   /* success */
     826             :     }
     827           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     828             :     {
     829           0 :       GNUNET_break (0);
     830           0 :       TEH_plugin->rollback (TEH_plugin->cls);
     831           0 :       ret = TALER_MHD_reply_with_error (connection,
     832             :                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     833             :                                         TALER_EC_GENERIC_DB_COMMIT_FAILED,
     834             :                                         NULL);
     835           0 :       goto cleanup;
     836             :     }
     837           0 :     TEH_plugin->rollback (TEH_plugin->cls);
     838             :   }
     839           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     840             :   {
     841           0 :     GNUNET_break (0);
     842           0 :     TEH_plugin->rollback (TEH_plugin->cls);
     843           0 :     ret = TALER_MHD_reply_with_error (connection,
     844             :                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
     845             :                                       TALER_EC_GENERIC_DB_SOFT_FAILURE,
     846             :                                       NULL);
     847           0 :     goto cleanup;
     848             :   }
     849             :   /* Generate final (positive) response */
     850           0 :   ret = reply_refreshes_reveal_success (connection,
     851             :                                         num_fresh_coins,
     852             :                                         rrcs);
     853           0 : cleanup:
     854           0 :   GNUNET_break (MHD_NO != ret);
     855             :   /* free resources */
     856           0 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     857             :   {
     858           0 :     struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
     859             : 
     860           0 :     TALER_blinded_denom_sig_free (&rrc->coin_sig);
     861           0 :     TALER_blinded_planchet_free (&rrc->blinded_planchet);
     862             :   }
     863           0 :   return ret;
     864             : }
     865             : 
     866             : 
     867             : /**
     868             :  * Handle a "/refreshes/$RCH/reveal" request.   Parses the given JSON
     869             :  * transfer private keys and if successful, passes everything to
     870             :  * #resolve_refreshes_reveal_denominations() which will verify that the
     871             :  * revealed information is valid then returns the signed refreshed
     872             :  * coins.
     873             :  *
     874             :  * If the denomination has age restriction support, the array of EDDSA public
     875             :  * keys, one for each age group that was activated during the withdrawal
     876             :  * by the parent/ward, must be provided in old_age_commitment.  The hash of
     877             :  * this array must be the same as the h_age_commitment of the persisted reveal
     878             :  * request.
     879             :  *
     880             :  * @param connection the MHD connection to handle
     881             :  * @param rctx context for the operation, partially built at this time
     882             :  * @param tp_json private transfer keys in JSON format
     883             :  * @param link_sigs_json link signatures in JSON format
     884             :  * @param new_denoms_h_json requests for fresh coins to be created
     885             :  * @param old_age_commitment_json array of EDDSA public keys in JSON, used for age restriction, maybe NULL
     886             :  * @param coin_evs envelopes of gamma-selected coins to be signed
     887             :  * @return MHD result code
     888             :  */
     889             : static MHD_RESULT
     890           0 : handle_refreshes_reveal_json (struct MHD_Connection *connection,
     891             :                               struct RevealContext *rctx,
     892             :                               const json_t *tp_json,
     893             :                               const json_t *link_sigs_json,
     894             :                               const json_t *new_denoms_h_json,
     895             :                               const json_t *old_age_commitment_json,
     896             :                               const json_t *coin_evs)
     897             : {
     898           0 :   unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
     899           0 :   unsigned int num_tprivs = json_array_size (tp_json);
     900             : 
     901           0 :   GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1); /* checked just earlier */
     902           0 :   if ( (num_fresh_coins >= TALER_MAX_FRESH_COINS) ||
     903             :        (0 == num_fresh_coins) )
     904             :   {
     905           0 :     GNUNET_break_op (0);
     906           0 :     return TALER_MHD_reply_with_error (connection,
     907             :                                        MHD_HTTP_BAD_REQUEST,
     908             :                                        TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
     909             :                                        NULL);
     910             : 
     911             :   }
     912           0 :   if (json_array_size (new_denoms_h_json) !=
     913           0 :       json_array_size (coin_evs))
     914             :   {
     915           0 :     GNUNET_break_op (0);
     916           0 :     return TALER_MHD_reply_with_error (connection,
     917             :                                        MHD_HTTP_BAD_REQUEST,
     918             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH,
     919             :                                        "new_denoms/coin_evs");
     920             :   }
     921           0 :   if (json_array_size (new_denoms_h_json) !=
     922           0 :       json_array_size (link_sigs_json))
     923             :   {
     924           0 :     GNUNET_break_op (0);
     925           0 :     return TALER_MHD_reply_with_error (connection,
     926             :                                        MHD_HTTP_BAD_REQUEST,
     927             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH,
     928             :                                        "new_denoms/link_sigs");
     929             :   }
     930             : 
     931             :   /* Sanity check of age commitment: If it was provided, it _must_ be an array
     932             :    * of the size the # of age groups */
     933           0 :   if (NULL != old_age_commitment_json
     934           0 :       && TALER_extensions_age_restriction_num_groups () !=
     935           0 :       json_array_size (old_age_commitment_json))
     936             :   {
     937           0 :     GNUNET_break_op (0);
     938           0 :     return TALER_MHD_reply_with_error (connection,
     939             :                                        MHD_HTTP_BAD_REQUEST,
     940             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
     941             :                                        "old_age_commitment");
     942             :   }
     943             : 
     944             :   /* Parse transfer private keys array */
     945           0 :   for (unsigned int i = 0; i<num_tprivs; i++)
     946             :   {
     947             :     struct GNUNET_JSON_Specification trans_spec[] = {
     948           0 :       GNUNET_JSON_spec_fixed_auto (NULL,
     949             :                                    &rctx->transfer_privs[i]),
     950           0 :       GNUNET_JSON_spec_end ()
     951             :     };
     952             :     enum GNUNET_GenericReturnValue res;
     953             : 
     954           0 :     res = TALER_MHD_parse_json_array (connection,
     955             :                                       tp_json,
     956             :                                       trans_spec,
     957             :                                       i,
     958             :                                       -1);
     959           0 :     if (GNUNET_OK != res)
     960           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     961             :   }
     962             : 
     963           0 :   return resolve_refreshes_reveal_denominations (connection,
     964             :                                                  rctx,
     965             :                                                  link_sigs_json,
     966             :                                                  new_denoms_h_json,
     967             :                                                  old_age_commitment_json,
     968             :                                                  coin_evs);
     969             : }
     970             : 
     971             : 
     972             : MHD_RESULT
     973           0 : TEH_handler_reveal (struct TEH_RequestContext *rc,
     974             :                     const json_t *root,
     975             :                     const char *const args[2])
     976             : {
     977             :   json_t *coin_evs;
     978             :   json_t *transfer_privs;
     979             :   json_t *link_sigs;
     980             :   json_t *new_denoms_h;
     981             :   json_t *old_age_commitment;
     982             :   struct RevealContext rctx;
     983             :   struct GNUNET_JSON_Specification spec[] = {
     984           0 :     GNUNET_JSON_spec_fixed_auto ("transfer_pub",
     985             :                                  &rctx.gamma_tp),
     986           0 :     GNUNET_JSON_spec_json ("transfer_privs",
     987             :                            &transfer_privs),
     988           0 :     GNUNET_JSON_spec_json ("link_sigs",
     989             :                            &link_sigs),
     990           0 :     GNUNET_JSON_spec_json ("coin_evs",
     991             :                            &coin_evs),
     992           0 :     GNUNET_JSON_spec_json ("new_denoms_h",
     993             :                            &new_denoms_h),
     994           0 :     GNUNET_JSON_spec_mark_optional (
     995             :       GNUNET_JSON_spec_json ("old_age_commitment",
     996             :                              &old_age_commitment),
     997             :       NULL),
     998           0 :     GNUNET_JSON_spec_mark_optional (
     999             :       GNUNET_JSON_spec_fixed_auto ("rms",
    1000             :                                    &rctx.rms),
    1001             :       &rctx.no_rms),
    1002           0 :     GNUNET_JSON_spec_end ()
    1003             :   };
    1004             : 
    1005           0 :   memset (&rctx,
    1006             :           0,
    1007             :           sizeof (rctx));
    1008           0 :   if (GNUNET_OK !=
    1009           0 :       GNUNET_STRINGS_string_to_data (args[0],
    1010             :                                      strlen (args[0]),
    1011             :                                      &rctx.rc,
    1012             :                                      sizeof (rctx.rc)))
    1013             :   {
    1014           0 :     GNUNET_break_op (0);
    1015           0 :     return TALER_MHD_reply_with_error (rc->connection,
    1016             :                                        MHD_HTTP_BAD_REQUEST,
    1017             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_INVALID_RCH,
    1018             :                                        args[0]);
    1019             :   }
    1020           0 :   if (0 != strcmp (args[1],
    1021             :                    "reveal"))
    1022             :   {
    1023           0 :     GNUNET_break_op (0);
    1024           0 :     return TALER_MHD_reply_with_error (rc->connection,
    1025             :                                        MHD_HTTP_BAD_REQUEST,
    1026             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_OPERATION_INVALID,
    1027           0 :                                        args[1]);
    1028             :   }
    1029             : 
    1030             :   {
    1031             :     enum GNUNET_GenericReturnValue res;
    1032             : 
    1033           0 :     res = TALER_MHD_parse_json_data (rc->connection,
    1034             :                                      root,
    1035             :                                      spec);
    1036           0 :     if (GNUNET_OK != res)
    1037             :     {
    1038           0 :       GNUNET_break_op (0);
    1039           0 :       return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
    1040             :     }
    1041             :   }
    1042             : 
    1043             :   /* Check we got enough transfer private keys */
    1044             :   /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
    1045           0 :   if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
    1046             :   {
    1047           0 :     GNUNET_JSON_parse_free (spec);
    1048           0 :     GNUNET_break_op (0);
    1049           0 :     return TALER_MHD_reply_with_error (rc->connection,
    1050             :                                        MHD_HTTP_BAD_REQUEST,
    1051             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
    1052             :                                        NULL);
    1053             :   }
    1054             : 
    1055             :   {
    1056             :     MHD_RESULT res;
    1057             : 
    1058           0 :     res = handle_refreshes_reveal_json (rc->connection,
    1059             :                                         &rctx,
    1060             :                                         transfer_privs,
    1061             :                                         link_sigs,
    1062             :                                         new_denoms_h,
    1063             :                                         old_age_commitment,
    1064             :                                         coin_evs);
    1065           0 :     GNUNET_JSON_parse_free (spec);
    1066           0 :     return res;
    1067             :   }
    1068             : }
    1069             : 
    1070             : 
    1071             : /* end of taler-exchange-httpd_refreshes_reveal.c */

Generated by: LCOV version 1.14