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