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