Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 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 : /**
21 : * @file testing_api_cmd_tip_pickup.c
22 : * @brief command to test the tipping.
23 : * @author Marcello Stanisci
24 : */
25 : #include "platform.h"
26 : #include <taler/taler_exchange_service.h>
27 : #include <taler/taler_testing_lib.h>
28 : #include "taler_merchant_service.h"
29 : #include "taler_merchant_testing_lib.h"
30 :
31 : /**
32 : * State for a /tip-pickup CMD.
33 : */
34 : struct TipPickupState
35 : {
36 : /**
37 : * Merchant base URL.
38 : */
39 : const char *merchant_url;
40 :
41 : /**
42 : * Exchange base URL.
43 : */
44 : const char *exchange_url;
45 :
46 : /**
47 : * Expected HTTP response code.
48 : */
49 : unsigned int http_status;
50 :
51 : /**
52 : * Reference to a /tip/authorize CMD. This will be used to
53 : * get the tip id to make the request with.
54 : */
55 : const char *authorize_reference;
56 :
57 : /**
58 : * If set to non NULL, it references another pickup CMD
59 : * that will provide all the data which is needed to issue
60 : * the request (like planchet secrets, denomination keys..).
61 : */
62 : const char *replay_reference;
63 :
64 : /**
65 : * Handle to a on-going /tip/pickup request.
66 : */
67 : struct TALER_MERCHANT_TipPickupHandle *tpo;
68 :
69 : /**
70 : * The interpreter state.
71 : */
72 : struct TALER_TESTING_Interpreter *is;
73 :
74 : /**
75 : * An array of string-defined amounts that indicates
76 : * which denominations are going to be used to receive
77 : * tips.
78 : */
79 : const char **amounts;
80 :
81 : /**
82 : * The object version of the above @a amounts.
83 : */
84 : struct TALER_Amount *amounts_obj;
85 :
86 : /**
87 : * The sum of the the amounts above.
88 : */
89 : struct TALER_Amount total_amount;
90 :
91 : /**
92 : * The array of denomination keys, in the same order of @a
93 : * amounts.
94 : */
95 : const struct TALER_EXCHANGE_DenomPublicKey **dks;
96 :
97 : /**
98 : * The array of planchet secrets, in the same order of @a
99 : * amounts.
100 : */
101 : struct TALER_PlanchetMasterSecretP *psa;
102 :
103 : /**
104 : * Set (by the interpreter) to an array of @a num_coins
105 : * details on coins created from the (successful) tip operation.
106 : */
107 : struct TALER_EXCHANGE_PrivateCoinDetails *pcds;
108 :
109 : /**
110 : * How many coins are involved in the tipping operation.
111 : */
112 : uint32_t num_coins;
113 :
114 : /**
115 : * Expected Taler error code (NOTE: this is NOT the HTTP
116 : * response code).
117 : */
118 : enum TALER_ErrorCode expected_ec;
119 : };
120 :
121 :
122 : /**
123 : * Callback for a /tip-pickup request, it mainly checks if
124 : * values returned from the backend are as expected, and if so
125 : * (and if the status was 200 OK) proceede with the withdrawal.
126 : *
127 : * @param cls closure
128 : * @param pd details about the result of the operation
129 : */
130 : static void
131 0 : pickup_cb (void *cls,
132 : const struct TALER_MERCHANT_PickupDetails *pd)
133 : {
134 0 : struct TipPickupState *tps = cls;
135 :
136 0 : tps->tpo = NULL;
137 0 : if (pd->hr.http_status != tps->http_status)
138 : {
139 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
140 : "Unexpected response code %u (%d) to command %s\n",
141 : pd->hr.http_status,
142 : (int) pd->hr.ec,
143 : TALER_TESTING_interpreter_get_current_label (tps->is));
144 0 : TALER_TESTING_FAIL (tps->is);
145 : }
146 :
147 0 : if (pd->hr.ec != tps->expected_ec)
148 0 : TALER_TESTING_FAIL (tps->is);
149 :
150 : /* Safe to go ahead: http status was expected. */
151 0 : if ( (MHD_HTTP_OK != pd->hr.http_status) ||
152 0 : (TALER_EC_NONE != pd->hr.ec) )
153 : {
154 0 : TALER_TESTING_interpreter_next (tps->is);
155 0 : return;
156 : }
157 0 : if (pd->details.success.num_sigs != tps->num_coins)
158 0 : TALER_TESTING_FAIL (tps->is);
159 0 : tps->pcds = GNUNET_new_array (tps->num_coins,
160 : struct TALER_EXCHANGE_PrivateCoinDetails);
161 0 : for (unsigned int i = 0; i<tps->num_coins; i++)
162 : {
163 0 : struct TALER_EXCHANGE_PrivateCoinDetails *pcd =
164 0 : &pd->details.success.pcds[i];
165 :
166 0 : tps->pcds[i] = *pcd;
167 0 : TALER_denom_sig_deep_copy (&tps->pcds[i].sig,
168 0 : &pcd->sig);
169 : }
170 0 : TALER_TESTING_interpreter_next (tps->is);
171 : }
172 :
173 :
174 : /**
175 : * Run a /tip-pickup CMD.
176 : *
177 : * @param cls closure
178 : * @param cmd the current /tip-pickup CMD.
179 : * @param is interpreter state.
180 : */
181 : static void
182 0 : tip_pickup_run (void *cls,
183 : const struct TALER_TESTING_Command *cmd,
184 : struct TALER_TESTING_Interpreter *is)
185 : {
186 0 : struct TipPickupState *tps = cls;
187 : unsigned int num_planchets;
188 : const struct TALER_TESTING_Command *replay_cmd;
189 : const struct TALER_TESTING_Command *authorize_cmd;
190 : const struct TALER_TipIdentifierP *tip_id;
191 :
192 0 : tps->is = is;
193 0 : tps->exchange_url = TALER_EXCHANGE_get_base_url (is->exchange);
194 0 : if (NULL == tps->replay_reference)
195 : {
196 0 : replay_cmd = NULL;
197 :
198 : /* Count planchets. */
199 0 : for (num_planchets = 0;
200 0 : NULL != tps->amounts[num_planchets];
201 0 : num_planchets++)
202 : ;
203 : }
204 : else
205 : {
206 : const uint32_t *np;
207 :
208 0 : if (NULL == /* looking for "parent" tip-pickup command */
209 : (replay_cmd
210 0 : = TALER_TESTING_interpreter_lookup_command (is,
211 : tps->replay_reference)) )
212 0 : TALER_TESTING_FAIL (is);
213 :
214 0 : if (GNUNET_OK !=
215 0 : TALER_TESTING_get_trait_num_planchets (replay_cmd,
216 : &np))
217 0 : TALER_TESTING_FAIL (is);
218 0 : num_planchets = *np;
219 : }
220 :
221 0 : if (NULL ==
222 : (authorize_cmd
223 0 : = TALER_TESTING_interpreter_lookup_command (is,
224 : tps->authorize_reference)) )
225 0 : TALER_TESTING_FAIL (is);
226 :
227 0 : tps->num_coins = num_planchets;
228 0 : {
229 0 : struct TALER_MERCHANT_PlanchetData planchets[num_planchets];
230 :
231 0 : tps->psa = GNUNET_new_array (num_planchets,
232 : struct TALER_PlanchetMasterSecretP);
233 0 : tps->dks = GNUNET_new_array (num_planchets,
234 : const struct TALER_EXCHANGE_DenomPublicKey *);
235 0 : tps->amounts_obj = GNUNET_new_array (num_planchets,
236 : struct TALER_Amount);
237 0 : for (unsigned int i = 0; i<num_planchets; i++)
238 : {
239 0 : if (NULL == replay_cmd)
240 : {
241 0 : GNUNET_assert (GNUNET_OK ==
242 : TALER_string_to_amount (tps->amounts[i],
243 : &tps->amounts_obj[i]));
244 0 : if (0 == i)
245 0 : GNUNET_assert (GNUNET_OK ==
246 : TALER_amount_set_zero (tps->amounts_obj[i].currency,
247 : &tps->total_amount));
248 :
249 0 : GNUNET_assert (0 <
250 : TALER_amount_add (&tps->total_amount,
251 : &tps->total_amount,
252 : &tps->amounts_obj[i]));
253 0 : tps->dks[i] = TALER_TESTING_find_pk (is->keys,
254 0 : &tps->amounts_obj[i],
255 : false);
256 0 : if (NULL == tps->dks[i])
257 0 : TALER_TESTING_FAIL (is);
258 0 : TALER_planchet_master_setup_random (&tps->psa[i]);
259 : }
260 : else
261 : {
262 : const struct TALER_PlanchetMasterSecretP *ps;
263 :
264 0 : if (GNUNET_OK !=
265 0 : TALER_TESTING_get_trait_denom_pub (replay_cmd,
266 : i,
267 0 : &tps->dks[i]))
268 0 : TALER_TESTING_FAIL (is);
269 0 : if (GNUNET_OK !=
270 0 : TALER_TESTING_get_trait_planchet_secrets (replay_cmd,
271 : i,
272 : &ps))
273 0 : TALER_TESTING_FAIL (is);
274 0 : tps->psa[i] = *ps;
275 : }
276 0 : planchets[i].pk = tps->dks[i];
277 0 : planchets[i].ps = tps->psa[i];
278 : }
279 0 : if (GNUNET_OK !=
280 0 : TALER_TESTING_get_trait_tip_id (authorize_cmd,
281 : &tip_id))
282 0 : TALER_TESTING_FAIL (is);
283 0 : tps->tpo = TALER_MERCHANT_tip_pickup (is->ctx,
284 : is->exchange,
285 : tps->merchant_url,
286 : tip_id,
287 : num_planchets,
288 : planchets,
289 : &pickup_cb,
290 : tps);
291 0 : GNUNET_assert (NULL != tps->tpo);
292 : }
293 : }
294 :
295 :
296 : /**
297 : * Free a /tip-pickup CMD state, and possibly cancel a
298 : * pending /tip-pickup request.
299 : *
300 : * @param cls closure.
301 : * @param cmd current CMD to be freed.
302 : */
303 : static void
304 0 : tip_pickup_cleanup (void *cls,
305 : const struct TALER_TESTING_Command *cmd)
306 : {
307 0 : struct TipPickupState *tps = cls;
308 :
309 0 : GNUNET_free (tps->amounts_obj);
310 0 : GNUNET_free (tps->dks);
311 0 : GNUNET_free (tps->psa);
312 0 : if (NULL != tps->pcds)
313 : {
314 0 : for (unsigned int i = 0; i<tps->num_coins; i++)
315 0 : TALER_denom_sig_free (&tps->pcds[i].sig);
316 0 : GNUNET_free (tps->pcds);
317 : }
318 0 : if (NULL != tps->tpo)
319 : {
320 0 : TALER_LOG_WARNING ("Tip-pickup operation did not complete\n");
321 0 : TALER_MERCHANT_tip_pickup_cancel (tps->tpo);
322 : }
323 0 : GNUNET_free (tps);
324 0 : }
325 :
326 :
327 : static enum GNUNET_GenericReturnValue
328 0 : tip_pickup_traits (void *cls,
329 : const void **ret,
330 : const char *trait,
331 : unsigned int index)
332 : {
333 0 : struct TipPickupState *tps = cls;
334 :
335 0 : if (index >= tps->num_coins)
336 0 : return GNUNET_SYSERR;
337 : {
338 : struct TALER_TESTING_Trait traits[] = {
339 0 : TALER_TESTING_make_trait_planchet_secrets (index,
340 0 : &tps->psa[index]),
341 0 : TALER_TESTING_make_trait_coin_priv (index,
342 0 : &tps->pcds[index].coin_priv),
343 0 : TALER_TESTING_make_trait_denom_pub (index,
344 0 : tps->dks[index]),
345 0 : TALER_TESTING_make_trait_denom_sig (index,
346 0 : &tps->pcds[index].sig),
347 0 : TALER_TESTING_make_trait_amounts (index,
348 0 : &tps->amounts_obj[index]),
349 0 : TALER_TESTING_make_trait_amount (&tps->total_amount),
350 0 : TALER_TESTING_make_trait_num_planchets (&tps->num_coins),
351 0 : TALER_TESTING_make_trait_exchange_url (&tps->exchange_url),
352 0 : TALER_TESTING_trait_end ()
353 : };
354 :
355 0 : return TALER_TESTING_get_trait (traits,
356 : ret,
357 : trait,
358 : index);
359 : }
360 : }
361 :
362 :
363 : struct TALER_TESTING_Command
364 0 : TALER_TESTING_cmd_tip_pickup (const char *label,
365 : const char *merchant_url,
366 : unsigned int http_status,
367 : const char *authorize_reference,
368 : const char **amounts)
369 : {
370 : struct TipPickupState *tps;
371 :
372 0 : tps = GNUNET_new (struct TipPickupState);
373 0 : tps->merchant_url = merchant_url;
374 0 : tps->authorize_reference = authorize_reference;
375 0 : tps->amounts = amounts;
376 0 : tps->http_status = http_status;
377 : {
378 0 : struct TALER_TESTING_Command cmd = {
379 : .cls = tps,
380 : .label = label,
381 : .run = &tip_pickup_run,
382 : .cleanup = &tip_pickup_cleanup,
383 : .traits = &tip_pickup_traits
384 : };
385 :
386 0 : return cmd;
387 : }
388 : }
389 :
390 :
391 : struct TALER_TESTING_Command
392 0 : TALER_TESTING_cmd_tip_pickup_with_ec (const char *label,
393 : const char *merchant_url,
394 : unsigned int http_status,
395 : const char *authorize_reference,
396 : const char **amounts,
397 : enum TALER_ErrorCode ec)
398 : {
399 : struct TALER_TESTING_Command cmd;
400 : struct TipPickupState *tps;
401 :
402 0 : cmd = TALER_TESTING_cmd_tip_pickup (label,
403 : merchant_url,
404 : http_status,
405 : authorize_reference,
406 : amounts);
407 0 : tps = cmd.cls;
408 0 : tps->expected_ec = ec;
409 0 : return cmd;
410 : }
411 :
412 :
413 : /* end of testing_api_cmd_tip_pickup.c */
|