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