Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2023-2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it
6 : under the terms of the GNU General Public License as published by
7 : the Free Software Foundation; either version 3, or (at your
8 : 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 GNU
13 : 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_age_withdraw.c
21 : * @brief implements the withdraw command for age-restricted coins
22 : * @author Özgür Kesim
23 : */
24 :
25 : #include "taler/platform.h"
26 : #include "taler/taler_exchange_service.h"
27 : #include "taler/taler_json_lib.h"
28 : #include <gnunet/gnunet_common.h>
29 : #include <microhttpd.h>
30 : #include <gnunet/gnunet_curl_lib.h>
31 : #include "taler/taler_signatures.h"
32 : #include "taler/taler_extensions.h"
33 : #include "taler/taler_testing_lib.h"
34 :
35 : /*
36 : * The output state of coin
37 : */
38 : struct CoinOutputState
39 : {
40 :
41 : /**
42 : * The calculated details during "withdraw", for the selected coin.
43 : */
44 : struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details;
45 :
46 : /**
47 : * The (wanted) value of the coin, MUST be the same as input.denom_pub.value;
48 : */
49 : struct TALER_Amount amount;
50 :
51 : };
52 :
53 : /**
54 : * State for a "age withdraw" CMD:
55 : */
56 :
57 : struct AgeWithdrawState
58 : {
59 :
60 : /**
61 : * Interpreter state (during command)
62 : */
63 : struct TALER_TESTING_Interpreter *is;
64 :
65 : /**
66 : * The age-withdraw handle
67 : */
68 : struct TALER_EXCHANGE_PostWithdrawHandle *handle;
69 :
70 : /**
71 : * Exchange base URL. Only used as offered trait.
72 : */
73 : char *exchange_url;
74 :
75 : /**
76 : * URI of the reserve we are withdrawing from.
77 : */
78 : struct TALER_NormalizedPayto reserve_payto_uri;
79 :
80 : /**
81 : * Private key of the reserve we are withdrawing from.
82 : */
83 : struct TALER_ReservePrivateKeyP reserve_priv;
84 :
85 : /**
86 : * Public key of the reserve we are withdrawing from.
87 : */
88 : struct TALER_ReservePublicKeyP reserve_pub;
89 :
90 : /**
91 : * Which reserve should we withdraw from?
92 : */
93 : const char *reserve_reference;
94 :
95 : /**
96 : * Expected HTTP response code to the request.
97 : */
98 : unsigned int expected_response_code;
99 :
100 : /**
101 : * Age mask
102 : */
103 : struct TALER_AgeMask mask;
104 :
105 : /**
106 : * The maximum age we commit to
107 : */
108 : uint8_t max_age;
109 :
110 : /**
111 : * Number of coins to withdraw
112 : */
113 : size_t num_coins;
114 :
115 : /**
116 : * The @e num_coins denomination public keys that are provided
117 : * to the `TALER_EXCHANGE_post_withdraw()` API.
118 : */
119 : struct TALER_EXCHANGE_DenomPublicKey *denoms_pub;
120 :
121 : /**
122 : * The master seed from which all the other seeds are derived from
123 : */
124 : struct TALER_WithdrawMasterSeedP seed;
125 :
126 : /**
127 : * The #TALER_CNC_KAPPA seeds derived from @e seed
128 : */
129 : struct TALER_KappaWithdrawMasterSeedP kappa_seed;
130 :
131 : /**
132 : * The master seed from which all the other seeds are derived from
133 : */
134 : struct TALER_BlindingMasterSeedP blinding_seed;
135 :
136 : /**
137 : * The output state of @e num_coins coins, calculated during the
138 : * "age-withdraw" operation.
139 : */
140 : struct CoinOutputState *coin_outputs;
141 :
142 : /**
143 : * The index returned by the exchange for the "age-withdraw" operation,
144 : * of the kappa coin candidates that we do not disclose and keep.
145 : */
146 : uint8_t noreveal_index;
147 :
148 : /**
149 : * The hash of the commitment, needed for the reveal step.
150 : */
151 : struct TALER_HashBlindedPlanchetsP planchets_h;
152 :
153 : /**
154 : * The hash of the selected blinded planchets
155 : */
156 : struct TALER_HashBlindedPlanchetsP selected_h;
157 :
158 : /**
159 : * Set to the KYC requirement payto hash *if* the exchange replied with a
160 : * request for KYC.
161 : */
162 : struct TALER_NormalizedPaytoHashP h_payto;
163 :
164 : /**
165 : * Set to the KYC requirement row *if* the exchange replied with
166 : * a request for KYC.
167 : */
168 : uint64_t requirement_row;
169 :
170 : /**
171 : * Reserve history entry that corresponds to this withdraw.
172 : * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
173 : */
174 : struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
175 : };
176 :
177 : /**
178 : * Callback for the "age-withdraw" operation; It checks that the response
179 : * code is expected and store the exchange signature in the state.
180 : *
181 : * @param cls Closure of type `struct AgeWithdrawState *`
182 : * @param response Response details
183 : */
184 : static void
185 5 : age_withdraw_cb (
186 : void *cls,
187 : const struct TALER_EXCHANGE_PostWithdrawResponse *response)
188 : {
189 5 : struct AgeWithdrawState *aws = cls;
190 5 : struct TALER_TESTING_Interpreter *is = aws->is;
191 :
192 5 : aws->handle = NULL;
193 5 : if (aws->expected_response_code != response->hr.http_status)
194 : {
195 0 : TALER_TESTING_unexpected_status_with_body (is,
196 : response->hr.http_status,
197 : aws->expected_response_code,
198 : response->hr.reply);
199 0 : return;
200 : }
201 :
202 5 : switch (response->hr.http_status)
203 : {
204 3 : case MHD_HTTP_CREATED:
205 3 : aws->noreveal_index = response->details.created.noreveal_index;
206 3 : aws->planchets_h = response->details.created.planchets_h;
207 3 : aws->selected_h = response->details.created.selected_h;
208 3 : aws->reserve_history.details.withdraw.planchets_h = aws->planchets_h;
209 3 : aws->reserve_history.details.withdraw.selected_h = aws->selected_h;
210 3 : aws->reserve_history.details.withdraw.noreveal_index = aws->noreveal_index;
211 3 : aws->kappa_seed = response->details.created.kappa_seed;
212 :
213 3 : GNUNET_assert (aws->num_coins == response->details.created.num_coins);
214 10 : for (size_t n = 0; n < aws->num_coins; n++)
215 : {
216 7 : aws->coin_outputs[n].details = response->details.created.coin_details[n];
217 7 : TALER_age_commitment_proof_deep_copy (
218 7 : &aws->coin_outputs[n].details.age_commitment_proof,
219 7 : &response->details.created.coin_details[n].age_commitment_proof);
220 7 : TALER_denom_ewv_copy (
221 7 : &aws->coin_outputs[n].details.blinding_values,
222 7 : &response->details.created.coin_details[n].blinding_values);
223 : }
224 3 : break;
225 0 : case MHD_HTTP_FORBIDDEN:
226 : case MHD_HTTP_NOT_FOUND:
227 : case MHD_HTTP_GONE:
228 : /* nothing to check */
229 0 : break;
230 2 : case MHD_HTTP_CONFLICT:
231 : /* FIXME[oec]: Add this to the response-type and handle it here */
232 2 : break;
233 0 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
234 : default:
235 : /* Unsupported status code (by test harness) */
236 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
237 : "test command for age-withdraw not support status code %u, body:\n"
238 : ">>%s<<\n",
239 : response->hr.http_status,
240 : json_dumps (response->hr.reply, JSON_INDENT (2)));
241 0 : GNUNET_break (0);
242 0 : break;
243 : }
244 :
245 : /* We are done with this command, pick the next one */
246 5 : TALER_TESTING_interpreter_next (is);
247 : }
248 :
249 :
250 : /**
251 : * Run the command for age-withdraw.
252 : */
253 : static void
254 5 : age_withdraw_run (
255 : void *cls,
256 : const struct TALER_TESTING_Command *cmd,
257 : struct TALER_TESTING_Interpreter *is)
258 : {
259 5 : struct AgeWithdrawState *aws = cls;
260 5 : struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is);
261 : const struct TALER_ReservePrivateKeyP *rp;
262 : const struct TALER_TESTING_Command *create_reserve;
263 : const struct TALER_EXCHANGE_DenomPublicKey *dpk;
264 :
265 5 : aws->is = is;
266 :
267 : /* Prepare the reserve related data */
268 : create_reserve
269 5 : = TALER_TESTING_interpreter_lookup_command (
270 : is,
271 : aws->reserve_reference);
272 :
273 5 : if (NULL == create_reserve)
274 : {
275 0 : GNUNET_break (0);
276 0 : TALER_TESTING_interpreter_fail (is);
277 0 : return;
278 : }
279 5 : if (GNUNET_OK !=
280 5 : TALER_TESTING_get_trait_reserve_priv (create_reserve,
281 : &rp))
282 : {
283 0 : GNUNET_break (0);
284 0 : TALER_TESTING_interpreter_fail (is);
285 0 : return;
286 : }
287 5 : if (NULL == aws->exchange_url)
288 : aws->exchange_url
289 5 : = GNUNET_strdup (TALER_TESTING_get_exchange_url (is));
290 5 : aws->reserve_priv = *rp;
291 5 : GNUNET_CRYPTO_eddsa_key_get_public (&aws->reserve_priv.eddsa_priv,
292 : &aws->reserve_pub.eddsa_pub);
293 : aws->reserve_payto_uri
294 5 : = TALER_reserve_make_payto (aws->exchange_url,
295 5 : &aws->reserve_pub);
296 :
297 5 : aws->denoms_pub = GNUNET_new_array (aws->num_coins,
298 : struct TALER_EXCHANGE_DenomPublicKey);
299 :
300 5 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
301 5 : &aws->seed,
302 : sizeof(aws->seed));
303 :
304 14 : for (unsigned int i = 0; i<aws->num_coins; i++)
305 : {
306 9 : struct TALER_EXCHANGE_DenomPublicKey *denom_pub = &aws->denoms_pub[i];
307 9 : struct CoinOutputState *cos = &aws->coin_outputs[i];
308 :
309 : /* Find denomination */
310 9 : dpk = TALER_TESTING_find_pk (keys,
311 9 : &cos->amount,
312 : true); /* _always_ use denominations with age-striction */
313 9 : if (NULL == dpk)
314 : {
315 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
316 : "Failed to determine denomination key for amount at %s\n",
317 : (NULL != cmd) ? cmd->label : "<retried command>");
318 0 : GNUNET_break (0);
319 0 : TALER_TESTING_interpreter_fail (is);
320 0 : return;
321 : }
322 :
323 : /* We copy the denomination key, as re-querying /keys
324 : * would free the old one. */
325 9 : *denom_pub = *dpk;
326 9 : TALER_denom_pub_copy (&denom_pub->key,
327 : &dpk->key);
328 :
329 : /* Accumulate the expected total amount and fee for the history */
330 9 : GNUNET_assert (0 <=
331 : TALER_amount_add (&aws->reserve_history.amount,
332 : &cos->amount,
333 : &denom_pub->fees.withdraw));
334 9 : if (i == 0)
335 5 : GNUNET_assert (GNUNET_OK ==
336 : TALER_amount_set_zero (
337 : denom_pub->fees.withdraw.currency,
338 : &aws->reserve_history.details.withdraw.fee));
339 :
340 9 : GNUNET_assert (0 <=
341 : TALER_amount_add (&aws->reserve_history.details.withdraw.fee,
342 : &aws->reserve_history.details.withdraw.fee,
343 : &denom_pub->fees.withdraw));
344 :
345 : }
346 : /* Save the expected history entry */
347 5 : aws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
348 5 : aws->reserve_history.details.withdraw.age_restricted = true;
349 5 : aws->reserve_history.details.withdraw.max_age = aws->max_age;
350 :
351 :
352 : /* Execute the age-restricted variant of withdraw protocol */
353 5 : aws->handle =
354 5 : TALER_EXCHANGE_post_withdraw_create (
355 : TALER_TESTING_interpreter_get_context (is),
356 : TALER_TESTING_get_exchange_url (is),
357 : keys,
358 : rp,
359 : aws->num_coins,
360 5 : aws->denoms_pub,
361 5 : &aws->seed,
362 5 : aws->max_age);
363 5 : if (NULL == aws->handle)
364 : {
365 0 : GNUNET_break (0);
366 0 : TALER_TESTING_interpreter_fail (is);
367 0 : return;
368 : }
369 5 : GNUNET_assert (GNUNET_OK ==
370 : TALER_EXCHANGE_post_withdraw_set_options (
371 : aws->handle,
372 : TALER_EXCHANGE_post_withdraw_option_with_age_proof (
373 : aws->max_age)));
374 5 : GNUNET_assert (TALER_EC_NONE ==
375 : TALER_EXCHANGE_post_withdraw_start (aws->handle,
376 : &age_withdraw_cb,
377 : aws));
378 : }
379 :
380 :
381 : /**
382 : * Free the state of a "age withdraw" CMD, and possibly cancel a
383 : * pending operation thereof
384 : *
385 : * @param cls Closure of type `struct AgeWithdrawState`
386 : * @param cmd The command being freed.
387 : */
388 : static void
389 5 : age_withdraw_cleanup (
390 : void *cls,
391 : const struct TALER_TESTING_Command *cmd)
392 : {
393 5 : struct AgeWithdrawState *aws = cls;
394 :
395 5 : if (NULL != aws->handle)
396 : {
397 0 : TALER_TESTING_command_incomplete (aws->is,
398 : cmd->label);
399 0 : TALER_EXCHANGE_post_withdraw_cancel (aws->handle);
400 0 : aws->handle = NULL;
401 : }
402 :
403 5 : if (NULL != aws->denoms_pub)
404 : {
405 14 : for (size_t n = 0; n < aws->num_coins; n++)
406 9 : TALER_denom_pub_free (&aws->denoms_pub[n].key);
407 :
408 5 : GNUNET_free (aws->denoms_pub);
409 5 : aws->denoms_pub = NULL;
410 : }
411 :
412 5 : if (NULL != aws->coin_outputs)
413 : {
414 14 : for (size_t n = 0; n < aws->num_coins; n++)
415 : {
416 9 : struct CoinOutputState *out = &aws->coin_outputs[n];
417 9 : TALER_age_commitment_proof_free (&out->details.age_commitment_proof);
418 9 : TALER_denom_ewv_free (&out->details.blinding_values);
419 : }
420 5 : GNUNET_free (aws->coin_outputs);
421 5 : aws->coin_outputs = NULL;
422 : }
423 :
424 5 : GNUNET_free (aws->exchange_url);
425 5 : aws->exchange_url = NULL;
426 5 : GNUNET_free (aws->reserve_payto_uri.normalized_payto);
427 5 : aws->reserve_payto_uri.normalized_payto = NULL;
428 5 : GNUNET_free (aws);
429 5 : }
430 :
431 :
432 : /**
433 : * Offer internal data of a "age withdraw" CMD state to other commands.
434 : *
435 : * @param cls Closure of type `struct AgeWithdrawState`
436 : * @param[out] ret result (could be anything)
437 : * @param trait name of the trait
438 : * @param idx index number of the object to offer.
439 : * @return #GNUNET_OK on success
440 : */
441 : static enum GNUNET_GenericReturnValue
442 4 : age_withdraw_traits (
443 : void *cls,
444 : const void **ret,
445 : const char *trait,
446 : unsigned int idx)
447 : {
448 4 : struct AgeWithdrawState *aws = cls;
449 4 : struct CoinOutputState *out = &aws->coin_outputs[idx];
450 4 : struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *details =
451 4 : &aws->coin_outputs[idx].details;
452 : struct TALER_TESTING_Trait traits[] = {
453 : /* history entry MUST be first due to response code logic below! */
454 4 : TALER_TESTING_make_trait_reserve_history (idx,
455 4 : &aws->reserve_history),
456 4 : TALER_TESTING_make_trait_denom_pub (idx,
457 4 : &aws->denoms_pub[idx]),
458 4 : TALER_TESTING_make_trait_reserve_priv (&aws->reserve_priv),
459 4 : TALER_TESTING_make_trait_reserve_pub (&aws->reserve_pub),
460 4 : TALER_TESTING_make_trait_withdraw_commitment (&aws->planchets_h),
461 4 : TALER_TESTING_make_trait_amounts (idx,
462 4 : &out->amount),
463 : /* FIXME[oec]: add legal requirement to response and handle it here, as well
464 : TALER_TESTING_make_trait_legi_requirement_row (&aws->requirement_row),
465 : TALER_TESTING_make_trait_h_payto (&aws->h_payto),
466 : */
467 4 : TALER_TESTING_make_trait_normalized_payto_uri (&aws->reserve_payto_uri),
468 4 : TALER_TESTING_make_trait_exchange_url (aws->exchange_url),
469 4 : TALER_TESTING_make_trait_coin_priv (idx,
470 4 : &details->coin_priv),
471 4 : TALER_TESTING_make_trait_withdraw_seed (&aws->seed),
472 : /* FIXME[oec]: needed!?
473 : TALER_TESTING_make_trait_planchet_secrets (idx,
474 : &aws->secrets[k][idx]),
475 : */
476 4 : TALER_TESTING_make_trait_blinding_key (idx,
477 4 : &details->blinding_key),
478 4 : TALER_TESTING_make_trait_exchange_blinding_values (idx,
479 4 : &details->blinding_values
480 : ),
481 4 : TALER_TESTING_make_trait_age_commitment_proof (
482 : idx,
483 4 : &details->age_commitment_proof),
484 4 : TALER_TESTING_make_trait_h_age_commitment (
485 : idx,
486 4 : &details->h_age_commitment),
487 4 : TALER_TESTING_trait_end ()
488 : };
489 :
490 4 : if (idx >= aws->num_coins)
491 0 : return GNUNET_NO;
492 :
493 4 : return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK)
494 : ? &traits[0] /* we have reserve history */
495 : : &traits[1], /* skip reserve history */
496 : ret,
497 : trait,
498 : idx);
499 : }
500 :
501 :
502 : struct TALER_TESTING_Command
503 5 : TALER_TESTING_cmd_withdraw_with_age_proof (const char *label,
504 : const char *reserve_reference,
505 : uint8_t max_age,
506 : unsigned int
507 : expected_response_code,
508 : const char *amount,
509 : ...)
510 : {
511 : struct AgeWithdrawState *aws;
512 : unsigned int cnt;
513 : va_list ap;
514 :
515 5 : aws = GNUNET_new (struct AgeWithdrawState);
516 5 : aws->reserve_reference = reserve_reference;
517 5 : aws->expected_response_code = expected_response_code;
518 5 : aws->mask = TALER_extensions_get_age_restriction_mask ();
519 5 : aws->max_age = TALER_get_lowest_age (&aws->mask,
520 : max_age);
521 5 : cnt = 1;
522 5 : va_start (ap, amount);
523 9 : while (NULL != (va_arg (ap, const char *)))
524 4 : cnt++;
525 5 : aws->num_coins = cnt;
526 5 : aws->coin_outputs = GNUNET_new_array (cnt,
527 : struct CoinOutputState);
528 5 : va_end (ap);
529 5 : va_start (ap, amount);
530 :
531 14 : for (unsigned int i = 0; i<aws->num_coins; i++)
532 : {
533 9 : struct CoinOutputState *out = &aws->coin_outputs[i];
534 9 : if (GNUNET_OK !=
535 9 : TALER_string_to_amount (amount,
536 : &out->amount))
537 : {
538 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
539 : "Failed to parse amount `%s' at %s\n",
540 : amount,
541 : label);
542 0 : GNUNET_assert (0);
543 : }
544 : /* move on to next vararg! */
545 9 : amount = va_arg (ap, const char *);
546 : }
547 :
548 5 : GNUNET_assert (NULL == amount);
549 5 : va_end (ap);
550 :
551 : {
552 5 : struct TALER_TESTING_Command cmd = {
553 : .cls = aws,
554 : .label = label,
555 : .run = &age_withdraw_run,
556 : .cleanup = &age_withdraw_cleanup,
557 : .traits = &age_withdraw_traits,
558 : };
559 :
560 5 : return cmd;
561 : }
562 : }
563 :
564 :
565 : /**
566 : * The state for the age-withdraw-reveal operation
567 : */
568 : struct AgeRevealWithdrawState
569 : {
570 : /**
571 : * The reference to the CMD resembling the previous call to age-withdraw
572 : */
573 : const char *age_withdraw_reference;
574 :
575 : /**
576 : * The state to the previous age-withdraw command
577 : */
578 : const struct AgeWithdrawState *aws;
579 :
580 : /**
581 : * The expected response code from the call to the
582 : * age-withdraw-reveal operation
583 : */
584 : unsigned int expected_response_code;
585 :
586 : /**
587 : * Interpreter state (during command)
588 : */
589 : struct TALER_TESTING_Interpreter *is;
590 :
591 : /**
592 : * The handle to the reveal-operation
593 : */
594 : struct TALER_EXCHANGE_PostRevealWithdrawHandle *handle;
595 :
596 :
597 : /**
598 : * Number of coins, extracted form the age withdraw command
599 : */
600 : size_t num_coins;
601 :
602 : /**
603 : * The signatures of the @e num_coins coins returned
604 : */
605 : struct TALER_DenominationSignature *denom_sigs;
606 :
607 : };
608 :
609 :
610 : /**
611 : * Callback for the reveal response
612 : *
613 : * @param cls Closure of type `struct AgeRevealWithdrawState`
614 : * @param response The response
615 : */
616 : static void
617 3 : age_reveal_withdraw_cb (
618 : void *cls,
619 : const struct TALER_EXCHANGE_PostRevealWithdrawResponse *response)
620 : {
621 3 : struct AgeRevealWithdrawState *awrs = cls;
622 3 : struct TALER_TESTING_Interpreter *is = awrs->is;
623 :
624 3 : awrs->handle = NULL;
625 3 : if (awrs->expected_response_code != response->hr.http_status)
626 : {
627 0 : TALER_TESTING_unexpected_status_with_body (is,
628 : response->hr.http_status,
629 : awrs->expected_response_code,
630 : response->hr.reply);
631 0 : return;
632 : }
633 3 : switch (response->hr.http_status)
634 : {
635 3 : case MHD_HTTP_OK:
636 : {
637 3 : const struct AgeWithdrawState *aws = awrs->aws;
638 3 : GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs);
639 3 : awrs->denom_sigs = GNUNET_new_array (awrs->num_coins,
640 : struct TALER_DenominationSignature);
641 10 : for (size_t n = 0; n < awrs->num_coins; n++)
642 : {
643 7 : GNUNET_assert (GNUNET_OK ==
644 : TALER_denom_sig_unblind (
645 : &awrs->denom_sigs[n],
646 : &response->details.ok.blinded_denom_sigs[n],
647 : &aws->coin_outputs[n].details.blinding_key,
648 : &aws->coin_outputs[n].details.h_coin_pub,
649 : &aws->coin_outputs[n].details.blinding_values,
650 : &aws->denoms_pub[n].key));
651 7 : TALER_denom_sig_free (&awrs->denom_sigs[n]);
652 : }
653 :
654 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
655 : "age-withdraw reveal success!\n");
656 3 : GNUNET_free (awrs->denom_sigs);
657 : }
658 3 : break;
659 0 : case MHD_HTTP_NOT_FOUND:
660 : case MHD_HTTP_FORBIDDEN:
661 : /* nothing to check */
662 0 : break;
663 : /* FIXME[oec]: handle more cases !? */
664 0 : default:
665 : /* Unsupported status code (by test harness) */
666 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
667 : "Age withdraw reveal test command does not support status code %u\n",
668 : response->hr.http_status);
669 0 : GNUNET_break (0);
670 0 : break;
671 : }
672 :
673 : /* We are done with this command, pick the next one */
674 3 : TALER_TESTING_interpreter_next (is);
675 : }
676 :
677 :
678 : /**
679 : * Run the command for age-withdraw-reveal
680 : */
681 : static void
682 3 : age_reveal_withdraw_run (
683 : void *cls,
684 : const struct TALER_TESTING_Command *cmd,
685 : struct TALER_TESTING_Interpreter *is)
686 : {
687 3 : struct AgeRevealWithdrawState *awrs = cls;
688 : const struct TALER_TESTING_Command *age_withdraw_cmd;
689 : const struct AgeWithdrawState *aws;
690 :
691 : (void) cmd;
692 3 : awrs->is = is;
693 :
694 : /*
695 : * Get the command and state for the previous call to "age witdraw"
696 : */
697 : age_withdraw_cmd =
698 3 : TALER_TESTING_interpreter_lookup_command (is,
699 : awrs->age_withdraw_reference);
700 3 : if (NULL == age_withdraw_cmd)
701 : {
702 0 : GNUNET_break (0);
703 0 : TALER_TESTING_interpreter_fail (is);
704 0 : return;
705 : }
706 3 : GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run);
707 3 : aws = age_withdraw_cmd->cls;
708 3 : awrs->aws = aws;
709 3 : awrs->num_coins = aws->num_coins;
710 :
711 : {
712 : struct TALER_RevealWithdrawMasterSeedsP revealed_seeds;
713 3 : size_t j = 0;
714 12 : for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
715 : {
716 9 : if (aws->noreveal_index == k)
717 3 : continue;
718 :
719 6 : revealed_seeds.tuple[j] = aws->kappa_seed.tuple[k];
720 6 : j++;
721 : }
722 :
723 3 : awrs->handle =
724 3 : TALER_EXCHANGE_post_reveal_withdraw_create (
725 : TALER_TESTING_interpreter_get_context (is),
726 : TALER_TESTING_get_exchange_url (is),
727 3 : aws->num_coins,
728 : &aws->planchets_h,
729 : &revealed_seeds);
730 3 : GNUNET_assert (NULL != awrs->handle);
731 3 : GNUNET_assert (TALER_EC_NONE ==
732 : TALER_EXCHANGE_post_reveal_withdraw_start (
733 : awrs->handle,
734 : &age_reveal_withdraw_cb,
735 : awrs));
736 : }
737 : }
738 :
739 :
740 : /**
741 : * Free the state of a "age-withdraw-reveal" CMD, and possibly
742 : * cancel a pending operation thereof
743 : *
744 : * @param cls Closure of type `struct AgeRevealWithdrawState`
745 : * @param cmd The command being freed.
746 : */
747 : static void
748 3 : age_reveal_withdraw_cleanup (
749 : void *cls,
750 : const struct TALER_TESTING_Command *cmd)
751 : {
752 3 : struct AgeRevealWithdrawState *awrs = cls;
753 :
754 3 : if (NULL != awrs->handle)
755 : {
756 0 : TALER_TESTING_command_incomplete (awrs->is,
757 : cmd->label);
758 0 : TALER_EXCHANGE_post_reveal_withdraw_cancel (awrs->handle);
759 0 : awrs->handle = NULL;
760 : }
761 3 : GNUNET_free (awrs->denom_sigs);
762 3 : awrs->denom_sigs = NULL;
763 3 : GNUNET_free (awrs);
764 3 : }
765 :
766 :
767 : /**
768 : * Offer internal data of a "age withdraw reveal" CMD state to other commands.
769 : *
770 : * @param cls Closure of they `struct AgeRevealWithdrawState`
771 : * @param[out] ret result (could be anything)
772 : * @param trait name of the trait
773 : * @param idx index number of the object to offer.
774 : * @return #GNUNET_OK on success
775 : */
776 : static enum GNUNET_GenericReturnValue
777 4 : age_reveal_withdraw_traits (
778 : void *cls,
779 : const void **ret,
780 : const char *trait,
781 : unsigned int idx)
782 : {
783 4 : struct AgeRevealWithdrawState *awrs = cls;
784 : struct TALER_TESTING_Trait traits[] = {
785 4 : TALER_TESTING_make_trait_denom_sig (idx,
786 4 : &awrs->denom_sigs[idx]),
787 : /* FIXME: shall we provide the traits from the previous
788 : * call to "age withdraw" as well? */
789 4 : TALER_TESTING_trait_end ()
790 : };
791 :
792 4 : if (idx >= awrs->num_coins)
793 0 : return GNUNET_NO;
794 :
795 4 : return TALER_TESTING_get_trait (traits,
796 : ret,
797 : trait,
798 : idx);
799 : }
800 :
801 :
802 : struct TALER_TESTING_Command
803 3 : TALER_TESTING_cmd_withdraw_reveal_age_proof (
804 : const char *label,
805 : const char *age_withdraw_reference,
806 : unsigned int expected_response_code)
807 : {
808 : struct AgeRevealWithdrawState *awrs =
809 3 : GNUNET_new (struct AgeRevealWithdrawState);
810 :
811 3 : awrs->age_withdraw_reference = age_withdraw_reference;
812 3 : awrs->expected_response_code = expected_response_code;
813 : {
814 3 : struct TALER_TESTING_Command cmd = {
815 : .cls = awrs,
816 : .label = label,
817 : .run = age_reveal_withdraw_run,
818 : .cleanup = age_reveal_withdraw_cleanup,
819 : .traits = age_reveal_withdraw_traits,
820 : };
821 :
822 3 : return cmd;
823 : }
824 : }
825 :
826 :
827 : /* end of testing_api_cmd_age_withdraw.c */
|