Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2023 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_get_exchange.c
21 : * @brief Command to get an exchange handle
22 : * @author Christian Grothoff
23 : */
24 : #include "taler/taler_json_lib.h"
25 : #include <gnunet/gnunet_curl_lib.h>
26 : struct GetExchangeState;
27 : #define TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE struct GetExchangeState
28 : #include "taler/taler_testing_lib.h"
29 :
30 :
31 : /**
32 : * State for a "get exchange" CMD.
33 : */
34 : struct GetExchangeState
35 : {
36 :
37 : /**
38 : * Master private key of the exchange.
39 : */
40 : struct TALER_MasterPrivateKeyP master_priv;
41 :
42 : /**
43 : * Our interpreter state.
44 : */
45 : struct TALER_TESTING_Interpreter *is;
46 :
47 : /**
48 : * Exchange handle we produced.
49 : */
50 : struct TALER_EXCHANGE_GetKeysHandle *exchange;
51 :
52 : /**
53 : * Keys of the exchange.
54 : */
55 : struct TALER_EXCHANGE_Keys *keys;
56 :
57 : /**
58 : * URL of the exchange.
59 : */
60 : char *exchange_url;
61 :
62 : /**
63 : * Filename of the master private key of the exchange.
64 : */
65 : char *master_priv_file;
66 :
67 : /**
68 : * Label of a command to use to obtain existing
69 : * keys.
70 : */
71 : const char *last_keys_ref;
72 :
73 : /**
74 : * Last denomination date we received when doing this request.
75 : */
76 : struct GNUNET_TIME_Timestamp my_denom_date;
77 :
78 : /**
79 : * Are we waiting for /keys before continuing?
80 : */
81 : bool wait_for_keys;
82 : };
83 :
84 :
85 : /**
86 : * Function called with information about who is auditing
87 : * a particular exchange and what keys the exchange is using.
88 : *
89 : * @param ges our command state
90 : * @param kr response from /keys
91 : * @param[in] keys the keys of the exchange
92 : */
93 : static void
94 28 : cert_cb (struct GetExchangeState *ges,
95 : const struct TALER_EXCHANGE_KeysResponse *kr,
96 : struct TALER_EXCHANGE_Keys *keys)
97 : {
98 28 : const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr;
99 28 : struct TALER_TESTING_Interpreter *is = ges->is;
100 :
101 28 : ges->exchange = NULL;
102 28 : if (NULL != ges->keys)
103 0 : TALER_EXCHANGE_keys_decref (ges->keys);
104 28 : ges->keys = keys;
105 28 : switch (hr->http_status)
106 : {
107 28 : case MHD_HTTP_OK:
108 28 : if (ges->wait_for_keys)
109 : {
110 28 : ges->wait_for_keys = false;
111 28 : TALER_TESTING_interpreter_next (is);
112 28 : return;
113 : }
114 0 : ges->my_denom_date = kr->details.ok.keys->last_denom_issue_date;
115 0 : return;
116 0 : default:
117 0 : GNUNET_break (0);
118 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
119 : "/keys responded with HTTP status %u\n",
120 : hr->http_status);
121 0 : if (ges->wait_for_keys)
122 : {
123 0 : ges->wait_for_keys = false;
124 0 : TALER_TESTING_interpreter_fail (is);
125 0 : return;
126 : }
127 0 : return;
128 : }
129 : }
130 :
131 :
132 : /**
133 : * Run the "get_exchange" command.
134 : *
135 : * @param cls closure.
136 : * @param cmd the command currently being executed.
137 : * @param is the interpreter state.
138 : */
139 : static void
140 28 : get_exchange_run (void *cls,
141 : const struct TALER_TESTING_Command *cmd,
142 : struct TALER_TESTING_Interpreter *is)
143 : {
144 28 : struct GetExchangeState *ges = cls;
145 28 : struct TALER_EXCHANGE_Keys *xkeys = NULL;
146 :
147 : (void) cmd;
148 28 : if (NULL == ges->exchange_url)
149 : {
150 0 : GNUNET_break (0);
151 0 : TALER_TESTING_interpreter_fail (is);
152 0 : return;
153 : }
154 28 : if (NULL != ges->last_keys_ref)
155 : {
156 : const struct TALER_TESTING_Command *state_cmd;
157 : struct TALER_EXCHANGE_Keys *old_keys;
158 : const char *exchange_url;
159 : json_t *s_keys;
160 :
161 : state_cmd
162 8 : = TALER_TESTING_interpreter_lookup_command (is,
163 : ges->last_keys_ref);
164 8 : if (NULL == state_cmd)
165 : {
166 : /* Command providing serialized keys not found. */
167 0 : GNUNET_break (0);
168 0 : TALER_TESTING_interpreter_fail (is);
169 0 : return;
170 : }
171 8 : if (GNUNET_OK !=
172 8 : TALER_TESTING_get_trait_keys (state_cmd,
173 : &old_keys))
174 : {
175 0 : GNUNET_break (0);
176 0 : TALER_TESTING_interpreter_fail (is);
177 0 : return;
178 : }
179 8 : if (NULL == old_keys)
180 : {
181 0 : GNUNET_break (0);
182 0 : TALER_TESTING_interpreter_fail (is);
183 0 : return;
184 : }
185 8 : if (GNUNET_OK !=
186 8 : TALER_TESTING_get_trait_exchange_url (state_cmd,
187 : &exchange_url))
188 : {
189 0 : GNUNET_break (0);
190 0 : TALER_TESTING_interpreter_fail (is);
191 0 : return;
192 : }
193 8 : if (0 != strcmp (exchange_url,
194 8 : ges->exchange_url))
195 : {
196 0 : GNUNET_break (0);
197 0 : TALER_TESTING_interpreter_fail (is);
198 0 : return;
199 : }
200 8 : s_keys = TALER_EXCHANGE_keys_to_json (old_keys);
201 8 : if (NULL == s_keys)
202 : {
203 0 : GNUNET_break (0);
204 0 : TALER_TESTING_interpreter_fail (is);
205 0 : return;
206 : }
207 8 : xkeys = TALER_EXCHANGE_keys_from_json (s_keys);
208 8 : if (NULL == xkeys)
209 : {
210 0 : GNUNET_break (0);
211 0 : json_dumpf (s_keys,
212 : stderr,
213 : JSON_INDENT (2));
214 0 : json_decref (s_keys);
215 0 : TALER_TESTING_interpreter_fail (is);
216 0 : return;
217 : }
218 8 : json_decref (s_keys);
219 : }
220 28 : if (NULL != ges->master_priv_file)
221 : {
222 28 : if (GNUNET_SYSERR ==
223 28 : GNUNET_CRYPTO_eddsa_key_from_file (ges->master_priv_file,
224 : GNUNET_YES,
225 : &ges->master_priv.eddsa_priv))
226 : {
227 0 : GNUNET_break (0);
228 0 : TALER_EXCHANGE_keys_decref (xkeys);
229 0 : TALER_TESTING_interpreter_fail (is);
230 0 : return;
231 : }
232 : }
233 28 : ges->is = is;
234 : ges->exchange
235 28 : = TALER_EXCHANGE_get_keys_create (
236 : TALER_TESTING_interpreter_get_context (is),
237 28 : ges->exchange_url);
238 28 : if (NULL == ges->exchange)
239 : {
240 0 : GNUNET_break (0);
241 0 : TALER_EXCHANGE_keys_decref (xkeys);
242 0 : TALER_TESTING_interpreter_fail (is);
243 0 : return;
244 : }
245 28 : if (NULL != xkeys)
246 : {
247 8 : TALER_EXCHANGE_get_keys_set_options (
248 : ges->exchange,
249 : TALER_EXCHANGE_get_keys_option_last_keys (xkeys));
250 : }
251 28 : TALER_EXCHANGE_keys_decref (xkeys);
252 28 : if (TALER_EC_NONE !=
253 28 : TALER_EXCHANGE_get_keys_start (ges->exchange,
254 : &cert_cb,
255 : ges))
256 : {
257 0 : GNUNET_break (0);
258 0 : TALER_EXCHANGE_get_keys_cancel (ges->exchange);
259 0 : ges->exchange = NULL;
260 0 : TALER_TESTING_interpreter_fail (is);
261 0 : return;
262 : }
263 28 : if (! ges->wait_for_keys)
264 0 : TALER_TESTING_interpreter_next (is);
265 : }
266 :
267 :
268 : /**
269 : * Cleanup the state.
270 : *
271 : * @param cls closure.
272 : * @param cmd the command which is being cleaned up.
273 : */
274 : static void
275 28 : get_exchange_cleanup (void *cls,
276 : const struct TALER_TESTING_Command *cmd)
277 : {
278 28 : struct GetExchangeState *ges = cls;
279 :
280 28 : if (NULL != ges->exchange)
281 : {
282 0 : TALER_EXCHANGE_get_keys_cancel (ges->exchange);
283 0 : ges->exchange = NULL;
284 : }
285 28 : TALER_EXCHANGE_keys_decref (ges->keys);
286 28 : ges->keys = NULL;
287 28 : GNUNET_free (ges->master_priv_file);
288 28 : GNUNET_free (ges->exchange_url);
289 28 : GNUNET_free (ges);
290 28 : }
291 :
292 :
293 : /**
294 : * Offer internal data to a "get_exchange" CMD state to other commands.
295 : *
296 : * @param cls closure
297 : * @param[out] ret result (could be anything)
298 : * @param trait name of the trait
299 : * @param index index number of the object to offer.
300 : * @return #GNUNET_OK on success
301 : */
302 : static enum GNUNET_GenericReturnValue
303 1105 : get_exchange_traits (void *cls,
304 : const void **ret,
305 : const char *trait,
306 : unsigned int index)
307 : {
308 1105 : struct GetExchangeState *ges = cls;
309 1105 : unsigned int off = (NULL == ges->master_priv_file) ? 1 : 0;
310 :
311 1105 : if (NULL != ges->keys)
312 : {
313 : struct TALER_TESTING_Trait traits[] = {
314 1105 : TALER_TESTING_make_trait_master_priv (&ges->master_priv),
315 1105 : TALER_TESTING_make_trait_master_pub (&ges->keys->master_pub),
316 1105 : TALER_TESTING_make_trait_keys (ges->keys),
317 1105 : TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
318 1105 : TALER_TESTING_make_trait_timestamp (0,
319 1105 : &ges->my_denom_date),
320 1105 : TALER_TESTING_trait_end ()
321 : };
322 :
323 1105 : return TALER_TESTING_get_trait (&traits[off],
324 : ret,
325 : trait,
326 : index);
327 : }
328 : else
329 : {
330 : struct TALER_TESTING_Trait traits[] = {
331 0 : TALER_TESTING_make_trait_master_priv (&ges->master_priv),
332 0 : TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
333 0 : TALER_TESTING_make_trait_timestamp (0,
334 0 : &ges->my_denom_date),
335 0 : TALER_TESTING_trait_end ()
336 : };
337 :
338 0 : return TALER_TESTING_get_trait (&traits[off],
339 : ret,
340 : trait,
341 : index);
342 : }
343 : }
344 :
345 :
346 : /**
347 : * Get the base URL of the exchange from @a cfg.
348 : *
349 : * @param cfg configuration to evaluate
350 : * @return base URL of the exchange according to @a cfg
351 : */
352 : static char *
353 28 : get_exchange_base_url (
354 : const struct GNUNET_CONFIGURATION_Handle *cfg)
355 : {
356 : char *exchange_url;
357 :
358 28 : if (GNUNET_OK !=
359 28 : GNUNET_CONFIGURATION_get_value_string (cfg,
360 : "exchange",
361 : "BASE_URL",
362 : &exchange_url))
363 : {
364 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
365 : "exchange",
366 : "BASE_URL");
367 0 : return NULL;
368 : }
369 28 : return exchange_url;
370 : }
371 :
372 :
373 : /**
374 : * Get the file name of the master private key file of the exchange from @a
375 : * cfg.
376 : *
377 : * @param cfg configuration to evaluate
378 : * @return base URL of the exchange according to @a cfg
379 : */
380 : static char *
381 28 : get_exchange_master_priv_file (
382 : const struct GNUNET_CONFIGURATION_Handle *cfg)
383 : {
384 : char *fn;
385 :
386 28 : if (GNUNET_OK !=
387 28 : GNUNET_CONFIGURATION_get_value_filename (cfg,
388 : "exchange-offline",
389 : "MASTER_PRIV_FILE",
390 : &fn))
391 : {
392 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
393 : "exchange-offline",
394 : "MASTER_PRIV_FILE");
395 0 : return NULL;
396 : }
397 28 : return fn;
398 : }
399 :
400 :
401 : struct TALER_TESTING_Command
402 28 : TALER_TESTING_cmd_get_exchange (
403 : const char *label,
404 : const struct GNUNET_CONFIGURATION_Handle *cfg,
405 : const char *last_keys_ref,
406 : bool wait_for_keys,
407 : bool load_private_key)
408 : {
409 : struct GetExchangeState *ges;
410 :
411 28 : ges = GNUNET_new (struct GetExchangeState);
412 28 : ges->exchange_url = get_exchange_base_url (cfg);
413 28 : ges->last_keys_ref = last_keys_ref;
414 28 : if (load_private_key)
415 28 : ges->master_priv_file = get_exchange_master_priv_file (cfg);
416 28 : ges->wait_for_keys = wait_for_keys;
417 : {
418 28 : struct TALER_TESTING_Command cmd = {
419 : .cls = ges,
420 : .label = label,
421 : .run = &get_exchange_run,
422 : .cleanup = &get_exchange_cleanup,
423 : .traits = &get_exchange_traits,
424 : .name = "exchange"
425 : };
426 :
427 28 : return cmd;
428 : }
429 : }
|