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 : * When did we send the /keys request?
80 : */
81 : struct GNUNET_TIME_Absolute request_time;
82 :
83 : /**
84 : * When did we receive the reply to the /keys request?
85 : */
86 : struct GNUNET_TIME_Absolute reply_time;
87 :
88 : /**
89 : * Are we waiting for /keys before continuing?
90 : */
91 : bool wait_for_keys;
92 : };
93 :
94 :
95 : /**
96 : * Function called with information about who is auditing
97 : * a particular exchange and what keys the exchange is using.
98 : *
99 : * @param ges our command state
100 : * @param kr response from /keys
101 : * @param[in] keys the keys of the exchange
102 : */
103 : static void
104 36 : cert_cb (struct GetExchangeState *ges,
105 : const struct TALER_EXCHANGE_KeysResponse *kr,
106 : struct TALER_EXCHANGE_Keys *keys)
107 : {
108 36 : const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr;
109 36 : struct TALER_TESTING_Interpreter *is = ges->is;
110 :
111 36 : ges->exchange = NULL;
112 36 : ges->reply_time = GNUNET_TIME_absolute_get ();
113 36 : if (NULL != ges->keys)
114 0 : TALER_EXCHANGE_keys_decref (ges->keys);
115 36 : ges->keys = keys;
116 36 : switch (hr->http_status)
117 : {
118 36 : case MHD_HTTP_OK:
119 36 : ges->my_denom_date = kr->details.ok.keys->last_denom_issue_date;
120 36 : if (ges->wait_for_keys)
121 : {
122 36 : ges->wait_for_keys = false;
123 36 : TALER_TESTING_interpreter_next (is);
124 36 : return;
125 : }
126 0 : return;
127 0 : default:
128 0 : GNUNET_break (0);
129 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
130 : "/keys responded with HTTP status %u\n",
131 : hr->http_status);
132 0 : if (ges->wait_for_keys)
133 : {
134 0 : ges->wait_for_keys = false;
135 0 : TALER_TESTING_interpreter_fail (is);
136 0 : return;
137 : }
138 0 : return;
139 : }
140 : }
141 :
142 :
143 : /**
144 : * Run the "get_exchange" command.
145 : *
146 : * @param cls closure.
147 : * @param cmd the command currently being executed.
148 : * @param is the interpreter state.
149 : */
150 : static void
151 36 : get_exchange_run (void *cls,
152 : const struct TALER_TESTING_Command *cmd,
153 : struct TALER_TESTING_Interpreter *is)
154 : {
155 36 : struct GetExchangeState *ges = cls;
156 36 : struct TALER_EXCHANGE_Keys *xkeys = NULL;
157 :
158 : (void) cmd;
159 36 : if (NULL == ges->exchange_url)
160 : {
161 0 : GNUNET_break (0);
162 0 : TALER_TESTING_interpreter_fail (is);
163 0 : return;
164 : }
165 36 : if (NULL != ges->last_keys_ref)
166 : {
167 : const struct TALER_TESTING_Command *state_cmd;
168 : struct TALER_EXCHANGE_Keys *old_keys;
169 : const char *exchange_url;
170 : json_t *s_keys;
171 :
172 : state_cmd
173 8 : = TALER_TESTING_interpreter_lookup_command (is,
174 : ges->last_keys_ref);
175 8 : if (NULL == state_cmd)
176 : {
177 : /* Command providing serialized keys not found. */
178 0 : GNUNET_break (0);
179 0 : TALER_TESTING_interpreter_fail (is);
180 0 : return;
181 : }
182 8 : if (GNUNET_OK !=
183 8 : TALER_TESTING_get_trait_keys (state_cmd,
184 : &old_keys))
185 : {
186 0 : GNUNET_break (0);
187 0 : TALER_TESTING_interpreter_fail (is);
188 0 : return;
189 : }
190 8 : if (NULL == old_keys)
191 : {
192 0 : GNUNET_break (0);
193 0 : TALER_TESTING_interpreter_fail (is);
194 0 : return;
195 : }
196 8 : if (GNUNET_OK !=
197 8 : TALER_TESTING_get_trait_exchange_url (state_cmd,
198 : &exchange_url))
199 : {
200 0 : GNUNET_break (0);
201 0 : TALER_TESTING_interpreter_fail (is);
202 0 : return;
203 : }
204 8 : if (0 != strcmp (exchange_url,
205 8 : ges->exchange_url))
206 : {
207 0 : GNUNET_break (0);
208 0 : TALER_TESTING_interpreter_fail (is);
209 0 : return;
210 : }
211 8 : s_keys = TALER_EXCHANGE_keys_to_json (old_keys);
212 8 : if (NULL == s_keys)
213 : {
214 0 : GNUNET_break (0);
215 0 : TALER_TESTING_interpreter_fail (is);
216 0 : return;
217 : }
218 8 : xkeys = TALER_EXCHANGE_keys_from_json (s_keys);
219 8 : if (NULL == xkeys)
220 : {
221 0 : GNUNET_break (0);
222 0 : json_dumpf (s_keys,
223 : stderr,
224 : JSON_INDENT (2));
225 0 : json_decref (s_keys);
226 0 : TALER_TESTING_interpreter_fail (is);
227 0 : return;
228 : }
229 8 : json_decref (s_keys);
230 : }
231 36 : if (NULL != ges->master_priv_file)
232 : {
233 36 : if (GNUNET_SYSERR ==
234 36 : GNUNET_CRYPTO_eddsa_key_from_file (ges->master_priv_file,
235 : GNUNET_YES,
236 : &ges->master_priv.eddsa_priv))
237 : {
238 0 : GNUNET_break (0);
239 0 : TALER_EXCHANGE_keys_decref (xkeys);
240 0 : TALER_TESTING_interpreter_fail (is);
241 0 : return;
242 : }
243 : }
244 36 : ges->is = is;
245 : ges->exchange
246 36 : = TALER_EXCHANGE_get_keys_create (
247 : TALER_TESTING_interpreter_get_context (is),
248 36 : ges->exchange_url);
249 36 : if (NULL == ges->exchange)
250 : {
251 0 : GNUNET_break (0);
252 0 : TALER_EXCHANGE_keys_decref (xkeys);
253 0 : TALER_TESTING_interpreter_fail (is);
254 0 : return;
255 : }
256 36 : if (NULL != xkeys)
257 : {
258 8 : TALER_EXCHANGE_get_keys_set_options (
259 : ges->exchange,
260 : TALER_EXCHANGE_get_keys_option_last_keys (xkeys));
261 : }
262 36 : TALER_EXCHANGE_keys_decref (xkeys);
263 36 : ges->request_time = GNUNET_TIME_absolute_get ();
264 36 : if (TALER_EC_NONE !=
265 36 : TALER_EXCHANGE_get_keys_start (ges->exchange,
266 : &cert_cb,
267 : ges))
268 : {
269 0 : GNUNET_break (0);
270 0 : TALER_EXCHANGE_get_keys_cancel (ges->exchange);
271 0 : ges->exchange = NULL;
272 0 : TALER_TESTING_interpreter_fail (is);
273 0 : return;
274 : }
275 36 : if (! ges->wait_for_keys)
276 0 : TALER_TESTING_interpreter_next (is);
277 : }
278 :
279 :
280 : /**
281 : * Cleanup the state.
282 : *
283 : * @param cls closure.
284 : * @param cmd the command which is being cleaned up.
285 : */
286 : static void
287 36 : get_exchange_cleanup (void *cls,
288 : const struct TALER_TESTING_Command *cmd)
289 : {
290 36 : struct GetExchangeState *ges = cls;
291 :
292 36 : if (NULL != ges->exchange)
293 : {
294 0 : TALER_EXCHANGE_get_keys_cancel (ges->exchange);
295 0 : ges->exchange = NULL;
296 : }
297 36 : TALER_EXCHANGE_keys_decref (ges->keys);
298 36 : ges->keys = NULL;
299 36 : GNUNET_free (ges->master_priv_file);
300 36 : GNUNET_free (ges->exchange_url);
301 36 : GNUNET_free (ges);
302 36 : }
303 :
304 :
305 : /**
306 : * Offer internal data to a "get_exchange" CMD state to other commands.
307 : *
308 : * @param cls closure
309 : * @param[out] ret result (could be anything)
310 : * @param trait name of the trait
311 : * @param index index number of the object to offer.
312 : * @return #GNUNET_OK on success
313 : */
314 : static enum GNUNET_GenericReturnValue
315 1556 : get_exchange_traits (void *cls,
316 : const void **ret,
317 : const char *trait,
318 : unsigned int index)
319 : {
320 1556 : struct GetExchangeState *ges = cls;
321 1556 : unsigned int off = (NULL == ges->master_priv_file) ? 1 : 0;
322 :
323 1556 : if (NULL != ges->keys)
324 : {
325 : struct TALER_TESTING_Trait traits[] = {
326 1556 : TALER_TESTING_make_trait_master_priv (&ges->master_priv),
327 1556 : TALER_TESTING_make_trait_master_pub (&ges->keys->master_pub),
328 1556 : TALER_TESTING_make_trait_keys (ges->keys),
329 1556 : TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
330 1556 : TALER_TESTING_make_trait_timestamp (0,
331 1556 : &ges->my_denom_date),
332 1556 : TALER_TESTING_make_trait_absolute_time (0,
333 1556 : &ges->request_time),
334 1556 : TALER_TESTING_make_trait_absolute_time (1,
335 1556 : &ges->reply_time),
336 1556 : TALER_TESTING_trait_end ()
337 : };
338 :
339 1556 : return TALER_TESTING_get_trait (&traits[off],
340 : ret,
341 : trait,
342 : index);
343 : }
344 : else
345 : {
346 : struct TALER_TESTING_Trait traits[] = {
347 0 : TALER_TESTING_make_trait_master_priv (&ges->master_priv),
348 0 : TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
349 0 : TALER_TESTING_make_trait_timestamp (0,
350 0 : &ges->my_denom_date),
351 0 : TALER_TESTING_make_trait_absolute_time (0,
352 0 : &ges->request_time),
353 0 : TALER_TESTING_make_trait_absolute_time (1,
354 0 : &ges->reply_time),
355 0 : TALER_TESTING_trait_end ()
356 : };
357 :
358 0 : return TALER_TESTING_get_trait (&traits[off],
359 : ret,
360 : trait,
361 : index);
362 : }
363 : }
364 :
365 :
366 : /**
367 : * Get the base URL of the exchange from @a cfg.
368 : *
369 : * @param cfg configuration to evaluate
370 : * @return base URL of the exchange according to @a cfg
371 : */
372 : static char *
373 36 : get_exchange_base_url (
374 : const struct GNUNET_CONFIGURATION_Handle *cfg)
375 : {
376 : char *exchange_url;
377 :
378 36 : if (GNUNET_OK !=
379 36 : GNUNET_CONFIGURATION_get_value_string (cfg,
380 : "exchange",
381 : "BASE_URL",
382 : &exchange_url))
383 : {
384 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
385 : "exchange",
386 : "BASE_URL");
387 0 : return NULL;
388 : }
389 36 : return exchange_url;
390 : }
391 :
392 :
393 : /**
394 : * Get the file name of the master private key file of the exchange from @a
395 : * cfg.
396 : *
397 : * @param cfg configuration to evaluate
398 : * @return base URL of the exchange according to @a cfg
399 : */
400 : static char *
401 36 : get_exchange_master_priv_file (
402 : const struct GNUNET_CONFIGURATION_Handle *cfg)
403 : {
404 : char *fn;
405 :
406 36 : if (GNUNET_OK !=
407 36 : GNUNET_CONFIGURATION_get_value_filename (cfg,
408 : "exchange-offline",
409 : "MASTER_PRIV_FILE",
410 : &fn))
411 : {
412 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
413 : "exchange-offline",
414 : "MASTER_PRIV_FILE");
415 0 : return NULL;
416 : }
417 36 : return fn;
418 : }
419 :
420 :
421 : struct TALER_TESTING_Command
422 36 : TALER_TESTING_cmd_get_exchange (
423 : const char *label,
424 : const struct GNUNET_CONFIGURATION_Handle *cfg,
425 : const char *last_keys_ref,
426 : bool wait_for_keys,
427 : bool load_private_key)
428 : {
429 : struct GetExchangeState *ges;
430 :
431 36 : ges = GNUNET_new (struct GetExchangeState);
432 36 : ges->exchange_url = get_exchange_base_url (cfg);
433 36 : ges->last_keys_ref = last_keys_ref;
434 36 : if (load_private_key)
435 36 : ges->master_priv_file = get_exchange_master_priv_file (cfg);
436 36 : ges->wait_for_keys = wait_for_keys;
437 : {
438 36 : struct TALER_TESTING_Command cmd = {
439 : .cls = ges,
440 : .label = label,
441 : .run = &get_exchange_run,
442 : .cleanup = &get_exchange_cleanup,
443 : .traits = &get_exchange_traits,
444 : .name = "exchange"
445 : };
446 :
447 36 : return cmd;
448 : }
449 : }
|