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