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