exchange

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

taler-exchange-httpd_db.c (7625B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2017, 2021 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 <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-exchange-httpd_db.c
     18  * @brief Generic database operations for the exchange.
     19  * @author Christian Grothoff
     20  */
     21 #include <gnunet/gnunet_db_lib.h>
     22 #include <pthread.h>
     23 #include <jansson.h>
     24 #include <gnunet/gnunet_json_lib.h>
     25 #include "taler/taler_error_codes.h"
     26 #include "taler/taler_json_lib.h"
     27 #include "taler/taler_mhd_lib.h"
     28 #include "exchangedb_lib.h"
     29 #include "taler-exchange-httpd_db.h"
     30 #include "taler-exchange-httpd_responses.h"
     31 #include "exchange-database/start.h"
     32 #include "exchange-database/commit.h"
     33 #include "exchange-database/do_insert_known_coin.h"
     34 #include "exchange-database/get_signature_for_known_coin.h"
     35 #include "exchange-database/preflight.h"
     36 #include "exchange-database/rollback.h"
     37 
     38 
     39 enum GNUNET_DB_QueryStatus
     40 TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
     41                      struct MHD_Connection *connection,
     42                      uint64_t *known_coin_id,
     43                      enum MHD_Result *mhd_ret)
     44 {
     45   enum TALER_EXCHANGEDB_CoinKnownStatus cks;
     46   struct TALER_DenominationHashP h_denom_pub;
     47   struct TALER_AgeCommitmentHashP h_age_commitment = {{{0}}};
     48 
     49   /* make sure coin is 'known' in database */
     50   cks = TALER_EXCHANGEDB_do_insert_known_coin (TEH_pg,
     51                                                coin,
     52                                                known_coin_id,
     53                                                &h_denom_pub,
     54                                                &h_age_commitment);
     55   switch (cks)
     56   {
     57   case TALER_EXCHANGEDB_CKS_ADDED:
     58     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     59   case TALER_EXCHANGEDB_CKS_PRESENT:
     60     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     61   case TALER_EXCHANGEDB_CKS_SOFT_FAIL:
     62     return GNUNET_DB_STATUS_SOFT_ERROR;
     63   case TALER_EXCHANGEDB_CKS_HARD_FAIL:
     64     *mhd_ret
     65       = TALER_MHD_reply_with_error (connection,
     66                                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     67                                     TALER_EC_GENERIC_DB_STORE_FAILED,
     68                                     NULL);
     69     return GNUNET_DB_STATUS_HARD_ERROR;
     70   case TALER_EXCHANGEDB_CKS_DENOM_CONFLICT:
     71     /* The exchange has a seen this coin before, but with a different denomination.
     72      * Get the corresponding signature and sent it to the client as proof */
     73     {
     74       struct
     75       {
     76         struct TALER_DenominationPublicKey pub;
     77         struct TALER_DenominationSignature sig;
     78       } prev_denom = {0};
     79 
     80       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     81           TALER_EXCHANGEDB_get_signature_for_known_coin (TEH_pg,
     82                                                          &coin->coin_pub,
     83                                                          &prev_denom.pub,
     84                                                          &prev_denom.sig))
     85       {
     86         /* There _should_ have been a result, because
     87          * we ended here due to a conflict! */
     88         GNUNET_break (0);
     89         *mhd_ret = TALER_MHD_reply_with_error (connection,
     90                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     91                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     92                                                NULL);
     93         return GNUNET_DB_STATUS_HARD_ERROR;
     94       }
     95 
     96       *mhd_ret = TEH_RESPONSE_reply_coin_denomination_conflict (
     97         connection,
     98         TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
     99         &coin->coin_pub,
    100         &prev_denom.pub,
    101         &prev_denom.sig);
    102       TALER_denom_pub_free (&prev_denom.pub);
    103       TALER_denom_sig_free (&prev_denom.sig);
    104       return GNUNET_DB_STATUS_HARD_ERROR;
    105     }
    106   case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL:
    107   case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL:
    108   case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS:
    109     *mhd_ret = TEH_RESPONSE_reply_coin_age_commitment_conflict (
    110       connection,
    111       TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH,
    112       cks,
    113       &h_denom_pub,
    114       &coin->coin_pub,
    115       &h_age_commitment);
    116     return GNUNET_DB_STATUS_HARD_ERROR;
    117   }
    118   GNUNET_assert (0);
    119   return GNUNET_DB_STATUS_HARD_ERROR;
    120 }
    121 
    122 
    123 enum GNUNET_GenericReturnValue
    124 TEH_DB_run_transaction (struct MHD_Connection *connection,
    125                         const char *name,
    126                         enum TEH_MetricTypeRequest mt,
    127                         enum MHD_Result *mhd_ret,
    128                         TEH_DB_TransactionCallback cb,
    129                         void *cb_cls)
    130 {
    131   if (NULL != mhd_ret)
    132     *mhd_ret = -1; /* set to invalid value, to help detect bugs */
    133   if (GNUNET_OK !=
    134       TALER_EXCHANGEDB_preflight (TEH_pg))
    135   {
    136     GNUNET_break (0);
    137     if (NULL != mhd_ret)
    138       *mhd_ret = TALER_MHD_reply_with_error (connection,
    139                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    140                                              TALER_EC_GENERIC_DB_SETUP_FAILED,
    141                                              NULL);
    142     return GNUNET_SYSERR;
    143   }
    144   GNUNET_assert (mt < TEH_MT_REQUEST_COUNT);
    145   TEH_METRICS_num_requests[mt]++;
    146   for (unsigned int retries = 0;
    147        retries < MAX_TRANSACTION_COMMIT_RETRIES;
    148        retries++)
    149   {
    150     enum GNUNET_DB_QueryStatus qs;
    151 
    152     if (GNUNET_OK !=
    153         TALER_EXCHANGEDB_start (TEH_pg,
    154                                 name))
    155     {
    156       GNUNET_break (0);
    157       if (NULL != mhd_ret)
    158         *mhd_ret = TALER_MHD_reply_with_error (
    159           connection,
    160           MHD_HTTP_INTERNAL_SERVER_ERROR,
    161           TALER_EC_GENERIC_DB_START_FAILED,
    162           NULL);
    163       return GNUNET_SYSERR;
    164     }
    165     qs = cb (cb_cls,
    166              connection,
    167              mhd_ret);
    168     if (0 > qs)
    169     {
    170       TALER_EXCHANGEDB_rollback (TEH_pg);
    171       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    172         return GNUNET_SYSERR;
    173     }
    174     else
    175     {
    176       qs = TALER_EXCHANGEDB_commit (TEH_pg);
    177       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    178       {
    179         TALER_EXCHANGEDB_rollback (TEH_pg);
    180         if (NULL != mhd_ret)
    181           *mhd_ret = TALER_MHD_reply_with_error (
    182             connection,
    183             MHD_HTTP_INTERNAL_SERVER_ERROR,
    184             TALER_EC_GENERIC_DB_COMMIT_FAILED,
    185             NULL);
    186         return GNUNET_SYSERR;
    187       }
    188       if (0 > qs)
    189         TALER_EXCHANGEDB_rollback (TEH_pg);
    190     }
    191     /* make sure callback did not violate invariants! */
    192     GNUNET_assert ( (NULL == mhd_ret) ||
    193                     (-1 == (int) *mhd_ret) );
    194     if (0 <= qs)
    195       return GNUNET_OK;
    196     TEH_METRICS_num_conflict[mt]++;
    197   }
    198   TALER_EXCHANGEDB_rollback (TEH_pg);
    199   TALER_LOG_ERROR ("Transaction `%s' commit failed %u times\n",
    200                    name,
    201                    MAX_TRANSACTION_COMMIT_RETRIES);
    202   if (NULL != mhd_ret)
    203     *mhd_ret = TALER_MHD_reply_with_error (connection,
    204                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    205                                            TALER_EC_GENERIC_DB_SOFT_FAILURE,
    206                                            NULL);
    207   return GNUNET_SYSERR;
    208 }
    209 
    210 
    211 /* end of taler-exchange-httpd_db.c */