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 "platform.h"
26 : #include "taler_exchange_service.h"
27 : #include "taler_json_lib.h"
28 : #include <gnunet/gnunet_common.h>
29 : #include <microhttpd.h>
30 : #include <gnunet/gnunet_curl_lib.h>
31 : #include "taler_signatures.h"
32 : #include "taler_extensions.h"
33 : #include "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 4 : TALER_TESTING_make_trait_age_commitment_proof (
476 : idx,
477 4 : &details->age_commitment_proof),
478 4 : TALER_TESTING_make_trait_h_age_commitment (
479 : idx,
480 4 : &details->h_age_commitment),
481 4 : TALER_TESTING_trait_end ()
482 : };
483 :
484 4 : if (idx >= aws->num_coins)
485 0 : return GNUNET_NO;
486 :
487 4 : return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK)
488 : ? &traits[0] /* we have reserve history */
489 : : &traits[1], /* skip reserve history */
490 : ret,
491 : trait,
492 : idx);
493 : }
494 :
495 :
496 : struct TALER_TESTING_Command
497 5 : TALER_TESTING_cmd_withdraw_with_age_proof (const char *label,
498 : const char *reserve_reference,
499 : uint8_t max_age,
500 : unsigned int
501 : expected_response_code,
502 : const char *amount,
503 : ...)
504 : {
505 : struct AgeWithdrawState *aws;
506 : unsigned int cnt;
507 : va_list ap;
508 :
509 5 : aws = GNUNET_new (struct AgeWithdrawState);
510 5 : aws->reserve_reference = reserve_reference;
511 5 : aws->expected_response_code = expected_response_code;
512 5 : aws->mask = TALER_extensions_get_age_restriction_mask ();
513 5 : aws->max_age = TALER_get_lowest_age (&aws->mask,
514 : max_age);
515 5 : cnt = 1;
516 5 : va_start (ap, amount);
517 9 : while (NULL != (va_arg (ap, const char *)))
518 4 : cnt++;
519 5 : aws->num_coins = cnt;
520 5 : aws->coin_outputs = GNUNET_new_array (cnt,
521 : struct CoinOutputState);
522 5 : va_end (ap);
523 5 : va_start (ap, amount);
524 :
525 14 : for (unsigned int i = 0; i<aws->num_coins; i++)
526 : {
527 9 : struct CoinOutputState *out = &aws->coin_outputs[i];
528 9 : if (GNUNET_OK !=
529 9 : TALER_string_to_amount (amount,
530 : &out->amount))
531 : {
532 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533 : "Failed to parse amount `%s' at %s\n",
534 : amount,
535 : label);
536 0 : GNUNET_assert (0);
537 : }
538 : /* move on to next vararg! */
539 9 : amount = va_arg (ap, const char *);
540 : }
541 :
542 5 : GNUNET_assert (NULL == amount);
543 5 : va_end (ap);
544 :
545 : {
546 5 : struct TALER_TESTING_Command cmd = {
547 : .cls = aws,
548 : .label = label,
549 : .run = &age_withdraw_run,
550 : .cleanup = &age_withdraw_cleanup,
551 : .traits = &age_withdraw_traits,
552 : };
553 :
554 5 : return cmd;
555 : }
556 : }
557 :
558 :
559 : /**
560 : * The state for the age-withdraw-reveal operation
561 : */
562 : struct AgeRevealWithdrawState
563 : {
564 : /**
565 : * The reference to the CMD resembling the previous call to age-withdraw
566 : */
567 : const char *age_withdraw_reference;
568 :
569 : /**
570 : * The state to the previous age-withdraw command
571 : */
572 : const struct AgeWithdrawState *aws;
573 :
574 : /**
575 : * The expected response code from the call to the
576 : * age-withdraw-reveal operation
577 : */
578 : unsigned int expected_response_code;
579 :
580 : /**
581 : * Interpreter state (during command)
582 : */
583 : struct TALER_TESTING_Interpreter *is;
584 :
585 : /**
586 : * The handle to the reveal-operation
587 : */
588 : struct TALER_EXCHANGE_RevealWithdrawHandle *handle;
589 :
590 :
591 : /**
592 : * Number of coins, extracted form the age withdraw command
593 : */
594 : size_t num_coins;
595 :
596 : /**
597 : * The signatures of the @e num_coins coins returned
598 : */
599 : struct TALER_DenominationSignature *denom_sigs;
600 :
601 : };
602 :
603 :
604 : /**
605 : * Callback for the reveal response
606 : *
607 : * @param cls Closure of type `struct AgeRevealWithdrawState`
608 : * @param response The response
609 : */
610 : static void
611 3 : age_reveal_withdraw_cb (
612 : void *cls,
613 : const struct TALER_EXCHANGE_RevealWithdrawResponse *response)
614 : {
615 3 : struct AgeRevealWithdrawState *awrs = cls;
616 3 : struct TALER_TESTING_Interpreter *is = awrs->is;
617 :
618 3 : awrs->handle = NULL;
619 3 : if (awrs->expected_response_code != response->hr.http_status)
620 : {
621 0 : TALER_TESTING_unexpected_status_with_body (is,
622 : response->hr.http_status,
623 : awrs->expected_response_code,
624 : response->hr.reply);
625 0 : return;
626 : }
627 3 : switch (response->hr.http_status)
628 : {
629 3 : case MHD_HTTP_OK:
630 : {
631 3 : const struct AgeWithdrawState *aws = awrs->aws;
632 3 : GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs);
633 3 : awrs->denom_sigs = GNUNET_new_array (awrs->num_coins,
634 : struct TALER_DenominationSignature);
635 10 : for (size_t n = 0; n < awrs->num_coins; n++)
636 : {
637 7 : GNUNET_assert (GNUNET_OK ==
638 : TALER_denom_sig_unblind (
639 : &awrs->denom_sigs[n],
640 : &response->details.ok.blinded_denom_sigs[n],
641 : &aws->coin_outputs[n].details.blinding_key,
642 : &aws->coin_outputs[n].details.h_coin_pub,
643 : &aws->coin_outputs[n].details.blinding_values,
644 : &aws->denoms_pub[n].key));
645 7 : TALER_denom_sig_free (&awrs->denom_sigs[n]);
646 : }
647 :
648 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
649 : "age-withdraw reveal success!\n");
650 3 : GNUNET_free (awrs->denom_sigs);
651 : }
652 3 : break;
653 0 : case MHD_HTTP_NOT_FOUND:
654 : case MHD_HTTP_FORBIDDEN:
655 : /* nothing to check */
656 0 : break;
657 : /* FIXME[oec]: handle more cases !? */
658 0 : default:
659 : /* Unsupported status code (by test harness) */
660 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
661 : "Age withdraw reveal test command does not support status code %u\n",
662 : response->hr.http_status);
663 0 : GNUNET_break (0);
664 0 : break;
665 : }
666 :
667 : /* We are done with this command, pick the next one */
668 3 : TALER_TESTING_interpreter_next (is);
669 : }
670 :
671 :
672 : /**
673 : * Run the command for age-withdraw-reveal
674 : */
675 : static void
676 3 : age_reveal_withdraw_run (
677 : void *cls,
678 : const struct TALER_TESTING_Command *cmd,
679 : struct TALER_TESTING_Interpreter *is)
680 : {
681 3 : struct AgeRevealWithdrawState *awrs = cls;
682 : const struct TALER_TESTING_Command *age_withdraw_cmd;
683 : const struct AgeWithdrawState *aws;
684 :
685 : (void) cmd;
686 3 : awrs->is = is;
687 :
688 : /*
689 : * Get the command and state for the previous call to "age witdraw"
690 : */
691 : age_withdraw_cmd =
692 3 : TALER_TESTING_interpreter_lookup_command (is,
693 : awrs->age_withdraw_reference);
694 3 : if (NULL == age_withdraw_cmd)
695 : {
696 0 : GNUNET_break (0);
697 0 : TALER_TESTING_interpreter_fail (is);
698 0 : return;
699 : }
700 3 : GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run);
701 3 : aws = age_withdraw_cmd->cls;
702 3 : awrs->aws = aws;
703 3 : awrs->num_coins = aws->num_coins;
704 :
705 : {
706 : struct TALER_RevealWithdrawMasterSeedsP revealed_seeds;
707 3 : size_t j = 0;
708 12 : for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
709 : {
710 9 : if (aws->noreveal_index == k)
711 3 : continue;
712 :
713 6 : revealed_seeds.tuple[j] = aws->kappa_seed.tuple[k];
714 6 : j++;
715 : }
716 :
717 3 : awrs->handle =
718 3 : TALER_EXCHANGE_reveal_withdraw (
719 : TALER_TESTING_interpreter_get_context (is),
720 : TALER_TESTING_get_exchange_url (is),
721 3 : aws->num_coins,
722 : &aws->planchets_h,
723 : &revealed_seeds,
724 : age_reveal_withdraw_cb,
725 : awrs);
726 : }
727 : }
728 :
729 :
730 : /**
731 : * Free the state of a "age-withdraw-reveal" CMD, and possibly
732 : * cancel a pending operation thereof
733 : *
734 : * @param cls Closure of type `struct AgeRevealWithdrawState`
735 : * @param cmd The command being freed.
736 : */
737 : static void
738 3 : age_reveal_withdraw_cleanup (
739 : void *cls,
740 : const struct TALER_TESTING_Command *cmd)
741 : {
742 3 : struct AgeRevealWithdrawState *awrs = cls;
743 :
744 3 : if (NULL != awrs->handle)
745 : {
746 0 : TALER_TESTING_command_incomplete (awrs->is,
747 : cmd->label);
748 0 : TALER_EXCHANGE_reveal_withdraw_cancel (awrs->handle);
749 0 : awrs->handle = NULL;
750 : }
751 3 : GNUNET_free (awrs->denom_sigs);
752 3 : awrs->denom_sigs = NULL;
753 3 : GNUNET_free (awrs);
754 3 : }
755 :
756 :
757 : /**
758 : * Offer internal data of a "age withdraw reveal" CMD state to other commands.
759 : *
760 : * @param cls Closure of they `struct AgeRevealWithdrawState`
761 : * @param[out] ret result (could be anything)
762 : * @param trait name of the trait
763 : * @param idx index number of the object to offer.
764 : * @return #GNUNET_OK on success
765 : */
766 : static enum GNUNET_GenericReturnValue
767 4 : age_reveal_withdraw_traits (
768 : void *cls,
769 : const void **ret,
770 : const char *trait,
771 : unsigned int idx)
772 : {
773 4 : struct AgeRevealWithdrawState *awrs = cls;
774 : struct TALER_TESTING_Trait traits[] = {
775 4 : TALER_TESTING_make_trait_denom_sig (idx,
776 4 : &awrs->denom_sigs[idx]),
777 : /* FIXME: shall we provide the traits from the previous
778 : * call to "age withdraw" as well? */
779 4 : TALER_TESTING_trait_end ()
780 : };
781 :
782 4 : if (idx >= awrs->num_coins)
783 0 : return GNUNET_NO;
784 :
785 4 : return TALER_TESTING_get_trait (traits,
786 : ret,
787 : trait,
788 : idx);
789 : }
790 :
791 :
792 : struct TALER_TESTING_Command
793 3 : TALER_TESTING_cmd_withdraw_reveal_age_proof (
794 : const char *label,
795 : const char *age_withdraw_reference,
796 : unsigned int expected_response_code)
797 : {
798 : struct AgeRevealWithdrawState *awrs =
799 3 : GNUNET_new (struct AgeRevealWithdrawState);
800 :
801 3 : awrs->age_withdraw_reference = age_withdraw_reference;
802 3 : awrs->expected_response_code = expected_response_code;
803 : {
804 3 : struct TALER_TESTING_Command cmd = {
805 : .cls = awrs,
806 : .label = label,
807 : .run = age_reveal_withdraw_run,
808 : .cleanup = age_reveal_withdraw_cleanup,
809 : .traits = age_reveal_withdraw_traits,
810 : };
811 :
812 3 : return cmd;
813 : }
814 : }
815 :
816 :
817 : /* end of testing_api_cmd_age_withdraw.c */
|