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