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 "taler/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/taler_json_lib.h"
28 : #include "taler/taler_mhd_lib.h"
29 : #include "taler/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 19 : add_fee (void *cls,
99 : struct MHD_Connection *connection,
100 : MHD_RESULT *mhd_ret)
101 : {
102 19 : 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 history_expiration;
107 : uint32_t purse_account_limit;
108 :
109 19 : qs = TEH_plugin->lookup_global_fee_by_time (
110 19 : TEH_plugin->cls,
111 : afc->start_time,
112 : afc->end_time,
113 : &fees,
114 : &purse_timeout,
115 : &history_expiration,
116 : &purse_account_limit);
117 19 : if (qs < 0)
118 : {
119 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
120 0 : return qs;
121 0 : GNUNET_break (0);
122 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
123 : MHD_HTTP_INTERNAL_SERVER_ERROR,
124 : TALER_EC_GENERIC_DB_FETCH_FAILED,
125 : "lookup global fee");
126 0 : return qs;
127 : }
128 19 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
129 : {
130 0 : if ( (GNUNET_OK ==
131 0 : TALER_amount_is_valid (&fees.history)) &&
132 : (0 ==
133 0 : TALER_global_fee_set_cmp (&fees,
134 0 : &afc->fees)) )
135 : {
136 : /* this will trigger the 'success' response */
137 0 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
138 : }
139 : else
140 : {
141 0 : *mhd_ret = TALER_MHD_reply_with_error (
142 : connection,
143 : MHD_HTTP_CONFLICT,
144 : TALER_EC_EXCHANGE_MANAGEMENT_GLOBAL_FEE_MISMATCH,
145 : NULL);
146 : }
147 0 : return GNUNET_DB_STATUS_HARD_ERROR;
148 : }
149 :
150 19 : qs = TEH_plugin->insert_global_fee (
151 19 : TEH_plugin->cls,
152 : afc->start_time,
153 : afc->end_time,
154 19 : &afc->fees,
155 : afc->purse_timeout,
156 : afc->history_expiration,
157 : afc->purse_account_limit,
158 19 : &afc->master_sig);
159 19 : if (qs < 0)
160 : {
161 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
162 0 : return qs;
163 0 : GNUNET_break (0);
164 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
165 : MHD_HTTP_INTERNAL_SERVER_ERROR,
166 : TALER_EC_GENERIC_DB_STORE_FAILED,
167 : "insert fee");
168 0 : return qs;
169 : }
170 19 : return qs;
171 : }
172 :
173 :
174 : MHD_RESULT
175 19 : TEH_handler_management_post_global_fees (
176 : struct MHD_Connection *connection,
177 : const json_t *root)
178 : {
179 : struct AddFeeContext afc;
180 : struct GNUNET_JSON_Specification spec[] = {
181 19 : GNUNET_JSON_spec_fixed_auto ("master_sig",
182 : &afc.master_sig),
183 19 : GNUNET_JSON_spec_timestamp ("fee_start",
184 : &afc.start_time),
185 19 : GNUNET_JSON_spec_timestamp ("fee_end",
186 : &afc.end_time),
187 19 : TALER_JSON_spec_amount ("history_fee",
188 : TEH_currency,
189 : &afc.fees.history),
190 19 : TALER_JSON_spec_amount ("account_fee",
191 : TEH_currency,
192 : &afc.fees.account),
193 19 : TALER_JSON_spec_amount ("purse_fee",
194 : TEH_currency,
195 : &afc.fees.purse),
196 19 : GNUNET_JSON_spec_relative_time ("purse_timeout",
197 : &afc.purse_timeout),
198 19 : GNUNET_JSON_spec_relative_time ("history_expiration",
199 : &afc.history_expiration),
200 19 : GNUNET_JSON_spec_uint32 ("purse_account_limit",
201 : &afc.purse_account_limit),
202 19 : GNUNET_JSON_spec_end ()
203 : };
204 :
205 : {
206 : enum GNUNET_GenericReturnValue res;
207 :
208 19 : res = TALER_MHD_parse_json_data (connection,
209 : root,
210 : spec);
211 19 : if (GNUNET_SYSERR == res)
212 0 : return MHD_NO; /* hard failure */
213 19 : if (GNUNET_NO == res)
214 0 : return MHD_YES; /* failure */
215 : }
216 :
217 19 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
218 19 : if (GNUNET_OK !=
219 19 : TALER_exchange_offline_global_fee_verify (
220 : afc.start_time,
221 : afc.end_time,
222 : &afc.fees,
223 : afc.purse_timeout,
224 : afc.history_expiration,
225 : afc.purse_account_limit,
226 : &TEH_master_public_key,
227 : &afc.master_sig))
228 : {
229 : /* signature invalid */
230 0 : GNUNET_break_op (0);
231 0 : return TALER_MHD_reply_with_error (
232 : connection,
233 : MHD_HTTP_FORBIDDEN,
234 : TALER_EC_EXCHANGE_MANAGEMENT_GLOBAL_FEE_SIGNATURE_INVALID,
235 : NULL);
236 : }
237 :
238 : {
239 : enum GNUNET_GenericReturnValue res;
240 : MHD_RESULT ret;
241 :
242 19 : res = TEH_DB_run_transaction (connection,
243 : "add global fee",
244 : TEH_MT_REQUEST_OTHER,
245 : &ret,
246 : &add_fee,
247 : &afc);
248 19 : if (GNUNET_SYSERR == res)
249 0 : return ret;
250 : }
251 19 : TEH_keys_update_states ();
252 19 : return TALER_MHD_reply_static (
253 : connection,
254 : MHD_HTTP_NO_CONTENT,
255 : NULL,
256 : NULL,
257 : 0);
258 : }
259 :
260 :
261 : /* end of taler-exchange-httpd_management_global_fees.c */
|