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 */