Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020, 2021, 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_management_global_fees.c
18 : * @brief Handle request to add global fee details
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h>
26 : #include <pthread.h>
27 : #include "taler_json_lib.h"
28 : #include "taler_mhd_lib.h"
29 : #include "taler_signatures.h"
30 : #include "taler-exchange-httpd_keys.h"
31 : #include "taler-exchange-httpd_management.h"
32 : #include "taler-exchange-httpd_responses.h"
33 :
34 :
35 : /**
36 : * Closure for the #add_fee transaction.
37 : */
38 : struct AddFeeContext
39 : {
40 : /**
41 : * Fee's signature affirming the #TALER_SIGNATURE_MASTER_GLOBAL_FEES operation.
42 : */
43 : struct TALER_MasterSignatureP master_sig;
44 :
45 : /**
46 : * Starting period.
47 : */
48 : struct GNUNET_TIME_Timestamp start_time;
49 :
50 : /**
51 : * End of period.
52 : */
53 : struct GNUNET_TIME_Timestamp end_time;
54 :
55 : /**
56 : * Global fee amounts.
57 : */
58 : struct TALER_GlobalFeeSet fees;
59 :
60 : /**
61 : * When does an unmerged purse expire?
62 : */
63 : struct GNUNET_TIME_Relative purse_timeout;
64 :
65 : /**
66 : * When does an account without KYC expire?
67 : */
68 : struct GNUNET_TIME_Relative kyc_timeout;
69 :
70 : /**
71 : * When does an account history expire?
72 : */
73 : struct GNUNET_TIME_Relative history_expiration;
74 :
75 : /**
76 : * Number of free purses per account.
77 : */
78 : uint32_t purse_account_limit;
79 :
80 : };
81 :
82 :
83 : /**
84 : * Function implementing database transaction to add a fee. Runs the
85 : * transaction logic; IF it returns a non-error code, the transaction logic
86 : * MUST NOT queue a MHD response. IF it returns an hard error, the
87 : * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
88 : * returns the soft error code, the function MAY be called again to retry and
89 : * MUST not queue a MHD response.
90 : *
91 : * @param cls closure with a `struct AddFeeContext`
92 : * @param connection MHD request which triggered the transaction
93 : * @param[out] mhd_ret set to MHD response status for @a connection,
94 : * if transaction failed (!)
95 : * @return transaction status
96 : */
97 : static enum GNUNET_DB_QueryStatus
98 0 : add_fee (void *cls,
99 : struct MHD_Connection *connection,
100 : MHD_RESULT *mhd_ret)
101 : {
102 0 : struct AddFeeContext *afc = cls;
103 : enum GNUNET_DB_QueryStatus qs;
104 : struct TALER_GlobalFeeSet fees;
105 : struct GNUNET_TIME_Relative purse_timeout;
106 : struct GNUNET_TIME_Relative kyc_timeout;
107 : struct GNUNET_TIME_Relative history_expiration;
108 : uint32_t purse_account_limit;
109 :
110 0 : qs = TEH_plugin->lookup_global_fee_by_time (
111 0 : TEH_plugin->cls,
112 : afc->start_time,
113 : afc->end_time,
114 : &fees,
115 : &purse_timeout,
116 : &kyc_timeout,
117 : &history_expiration,
118 : &purse_account_limit);
119 0 : if (qs < 0)
120 : {
121 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
122 0 : return qs;
123 0 : GNUNET_break (0);
124 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
125 : MHD_HTTP_INTERNAL_SERVER_ERROR,
126 : TALER_EC_GENERIC_DB_FETCH_FAILED,
127 : "lookup global fee");
128 0 : return qs;
129 : }
130 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
131 : {
132 0 : if ( (GNUNET_OK ==
133 0 : TALER_amount_is_valid (&fees.history)) &&
134 : (0 ==
135 0 : TALER_global_fee_set_cmp (&fees,
136 0 : &afc->fees)) )
137 : {
138 : /* this will trigger the 'success' response */
139 0 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
140 : }
141 : else
142 : {
143 0 : *mhd_ret = TALER_MHD_reply_with_error (
144 : connection,
145 : MHD_HTTP_CONFLICT,
146 : TALER_EC_EXCHANGE_MANAGEMENT_GLOBAL_FEE_MISMATCH,
147 : NULL);
148 : }
149 0 : return GNUNET_DB_STATUS_HARD_ERROR;
150 : }
151 :
152 0 : qs = TEH_plugin->insert_global_fee (
153 0 : TEH_plugin->cls,
154 : afc->start_time,
155 : afc->end_time,
156 0 : &afc->fees,
157 : afc->purse_timeout,
158 : afc->kyc_timeout,
159 : afc->history_expiration,
160 : afc->purse_account_limit,
161 0 : &afc->master_sig);
162 0 : if (qs < 0)
163 : {
164 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
165 0 : return qs;
166 0 : GNUNET_break (0);
167 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
168 : MHD_HTTP_INTERNAL_SERVER_ERROR,
169 : TALER_EC_GENERIC_DB_STORE_FAILED,
170 : "insert fee");
171 0 : return qs;
172 : }
173 0 : return qs;
174 : }
175 :
176 :
177 : MHD_RESULT
178 0 : TEH_handler_management_post_global_fees (
179 : struct MHD_Connection *connection,
180 : const json_t *root)
181 : {
182 : struct AddFeeContext afc;
183 : struct GNUNET_JSON_Specification spec[] = {
184 0 : GNUNET_JSON_spec_fixed_auto ("master_sig",
185 : &afc.master_sig),
186 0 : GNUNET_JSON_spec_timestamp ("fee_start",
187 : &afc.start_time),
188 0 : GNUNET_JSON_spec_timestamp ("fee_end",
189 : &afc.end_time),
190 0 : TALER_JSON_spec_amount ("history_fee",
191 : TEH_currency,
192 : &afc.fees.history),
193 0 : TALER_JSON_spec_amount ("kyc_fee",
194 : TEH_currency,
195 : &afc.fees.kyc),
196 0 : TALER_JSON_spec_amount ("account_fee",
197 : TEH_currency,
198 : &afc.fees.account),
199 0 : TALER_JSON_spec_amount ("purse_fee",
200 : TEH_currency,
201 : &afc.fees.purse),
202 0 : GNUNET_JSON_spec_relative_time ("purse_timeout",
203 : &afc.purse_timeout),
204 0 : GNUNET_JSON_spec_relative_time ("kyc_timeout",
205 : &afc.kyc_timeout),
206 0 : GNUNET_JSON_spec_relative_time ("history_expiration",
207 : &afc.history_expiration),
208 0 : GNUNET_JSON_spec_uint32 ("purse_account_limit",
209 : &afc.purse_account_limit),
210 0 : GNUNET_JSON_spec_end ()
211 : };
212 :
213 : {
214 : enum GNUNET_GenericReturnValue res;
215 :
216 0 : res = TALER_MHD_parse_json_data (connection,
217 : root,
218 : spec);
219 0 : if (GNUNET_SYSERR == res)
220 0 : return MHD_NO; /* hard failure */
221 0 : if (GNUNET_NO == res)
222 0 : return MHD_YES; /* failure */
223 : }
224 :
225 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
226 0 : if (GNUNET_OK !=
227 0 : TALER_exchange_offline_global_fee_verify (
228 : afc.start_time,
229 : afc.end_time,
230 : &afc.fees,
231 : afc.purse_timeout,
232 : afc.kyc_timeout,
233 : afc.history_expiration,
234 : afc.purse_account_limit,
235 : &TEH_master_public_key,
236 : &afc.master_sig))
237 : {
238 : /* signature invalid */
239 0 : GNUNET_break_op (0);
240 0 : return TALER_MHD_reply_with_error (
241 : connection,
242 : MHD_HTTP_FORBIDDEN,
243 : TALER_EC_EXCHANGE_MANAGEMENT_GLOBAL_FEE_SIGNATURE_INVALID,
244 : NULL);
245 : }
246 :
247 : {
248 : enum GNUNET_GenericReturnValue res;
249 : MHD_RESULT ret;
250 :
251 0 : res = TEH_DB_run_transaction (connection,
252 : "add global fee",
253 : TEH_MT_REQUEST_OTHER,
254 : &ret,
255 : &add_fee,
256 : &afc);
257 0 : if (GNUNET_SYSERR == res)
258 0 : return ret;
259 : }
260 0 : TEH_keys_update_states ();
261 0 : return TALER_MHD_reply_static (
262 : connection,
263 : MHD_HTTP_NO_CONTENT,
264 : NULL,
265 : NULL,
266 : 0);
267 : }
268 :
269 :
270 : /* end of taler-exchange-httpd_management_global_fees.c */
|