Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018-2024 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_batch_deposit.c
21 : * @brief command for testing /batch-deposit.
22 : * @author Marcello Stanisci
23 : * @author Christian Grothoff
24 : */
25 : #include "taler/taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler/taler_testing_lib.h"
28 : #include "taler/taler_signatures.h"
29 : #include "backoff.h"
30 :
31 :
32 : /**
33 : * How often do we retry before giving up?
34 : */
35 : #define NUM_RETRIES 5
36 :
37 : /**
38 : * How long do we wait AT MOST when retrying?
39 : */
40 : #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
41 : GNUNET_TIME_UNIT_MILLISECONDS, 100)
42 :
43 :
44 : /**
45 : * Information per coin in the batch.
46 : */
47 : struct Coin
48 : {
49 :
50 : /**
51 : * Amount to deposit.
52 : */
53 : struct TALER_Amount amount;
54 :
55 : /**
56 : * Deposit fee.
57 : */
58 : struct TALER_Amount deposit_fee;
59 :
60 : /**
61 : * Our coin signature.
62 : */
63 : struct TALER_CoinSpendSignatureP coin_sig;
64 :
65 : /**
66 : * Reference to any command that is able to provide a coin,
67 : * possibly using $LABEL#$INDEX notation.
68 : */
69 : char *coin_reference;
70 :
71 : /**
72 : * Denomination public key of the coin.
73 : */
74 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
75 :
76 : /**
77 : * The command being referenced.
78 : */
79 : const struct TALER_TESTING_Command *coin_cmd;
80 :
81 : /**
82 : * Expected entry in the coin history created by this
83 : * coin.
84 : */
85 : struct TALER_EXCHANGE_CoinHistoryEntry che;
86 :
87 : /**
88 : * Index of the coin at @e coin_cmd.
89 : */
90 : unsigned int coin_idx;
91 : };
92 :
93 :
94 : /**
95 : * State for a "batch deposit" CMD.
96 : */
97 : struct BatchDepositState
98 : {
99 :
100 : /**
101 : * Refund deadline. Zero for no refunds.
102 : */
103 : struct GNUNET_TIME_Timestamp refund_deadline;
104 :
105 : /**
106 : * Wire deadline.
107 : */
108 : struct GNUNET_TIME_Timestamp wire_deadline;
109 :
110 : /**
111 : * Timestamp of the /deposit operation in the wallet (contract signing time).
112 : */
113 : struct GNUNET_TIME_Timestamp wallet_timestamp;
114 :
115 : /**
116 : * How long do we wait until we retry?
117 : */
118 : struct GNUNET_TIME_Relative backoff;
119 :
120 : /**
121 : * When did the exchange receive the deposit?
122 : */
123 : struct GNUNET_TIME_Timestamp exchange_timestamp;
124 :
125 : /**
126 : * Signing key used by the exchange to sign the
127 : * deposit confirmation.
128 : */
129 : struct TALER_ExchangePublicKeyP exchange_pub;
130 :
131 : /**
132 : * Set (by the interpreter) to a fresh private key. This
133 : * key will be used to sign the deposit request.
134 : */
135 : union TALER_AccountPrivateKeyP account_priv;
136 :
137 : /**
138 : * Set (by the interpreter) to the public key
139 : * corresponding to @e account_priv.
140 : */
141 : union TALER_AccountPublicKeyP account_pub;
142 :
143 : /**
144 : * Deposit handle while operation is running.
145 : */
146 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh;
147 :
148 : /**
149 : * Array of coins to batch-deposit.
150 : */
151 : struct Coin *coins;
152 :
153 : /**
154 : * Wire details of who is depositing -- this would be merchant
155 : * wire details in a normal scenario.
156 : */
157 : json_t *wire_details;
158 :
159 : /**
160 : * JSON string describing what a proposal is about.
161 : */
162 : json_t *contract_terms;
163 :
164 : /**
165 : * Interpreter state.
166 : */
167 : struct TALER_TESTING_Interpreter *is;
168 :
169 : /**
170 : * Task scheduled to try later.
171 : */
172 : struct GNUNET_SCHEDULER_Task *retry_task;
173 :
174 : /**
175 : * Deposit confirmation signature from the exchange.
176 : */
177 : struct TALER_ExchangeSignatureP exchange_sig;
178 :
179 : /**
180 : * Set to the KYC requirement payto hash *if* the exchange replied with a
181 : * request for KYC.
182 : */
183 : struct TALER_NormalizedPaytoHashP h_payto;
184 :
185 : /**
186 : * Set to the KYC requirement row *if* the exchange replied with
187 : * a request for KYC.
188 : */
189 : uint64_t requirement_row;
190 :
191 : /**
192 : * Reference to previous deposit operation.
193 : * Only present if we're supposed to replay the previous deposit.
194 : */
195 : const char *deposit_reference;
196 :
197 : /**
198 : * If @e coin_reference refers to an operation that generated
199 : * an array of coins, this value determines which coin to pick.
200 : */
201 : unsigned int num_coins;
202 :
203 : /**
204 : * Expected HTTP response code.
205 : */
206 : unsigned int expected_response_code;
207 :
208 : /**
209 : * Set to true if the /deposit succeeded
210 : * and we now can provide the resulting traits.
211 : */
212 : bool deposit_succeeded;
213 :
214 : };
215 :
216 :
217 : /**
218 : * Callback to analyze the /batch-deposit response, just used to check if the
219 : * response code is acceptable.
220 : *
221 : * @param cls closure.
222 : * @param dr deposit response details
223 : */
224 : static void
225 2 : batch_deposit_cb (void *cls,
226 : const struct TALER_EXCHANGE_PostBatchDepositResponse *dr)
227 : {
228 2 : struct BatchDepositState *ds = cls;
229 :
230 2 : ds->dh = NULL;
231 2 : if (ds->expected_response_code != dr->hr.http_status)
232 : {
233 0 : TALER_TESTING_unexpected_status (ds->is,
234 : dr->hr.http_status,
235 : ds->expected_response_code);
236 0 : return;
237 : }
238 2 : switch (dr->hr.http_status)
239 : {
240 2 : case MHD_HTTP_OK:
241 2 : ds->deposit_succeeded = GNUNET_YES;
242 2 : ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
243 2 : ds->exchange_pub = *dr->details.ok.exchange_pub;
244 2 : ds->exchange_sig = *dr->details.ok.exchange_sig;
245 2 : break;
246 0 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
247 : /* nothing to check */
248 : ds->requirement_row
249 0 : = dr->details.unavailable_for_legal_reasons.requirement_row;
250 : ds->h_payto
251 0 : = dr->details.unavailable_for_legal_reasons.h_payto;
252 0 : break;
253 : }
254 2 : TALER_TESTING_interpreter_next (ds->is);
255 : }
256 :
257 :
258 : /**
259 : * Run the command.
260 : *
261 : * @param cls closure.
262 : * @param cmd the command to execute.
263 : * @param is the interpreter state.
264 : */
265 : static void
266 2 : batch_deposit_run (void *cls,
267 : const struct TALER_TESTING_Command *cmd,
268 : struct TALER_TESTING_Interpreter *is)
269 2 : {
270 2 : struct BatchDepositState *ds = cls;
271 : const struct TALER_DenominationSignature *denom_pub_sig;
272 : struct TALER_PrivateContractHashP h_contract_terms;
273 : enum TALER_ErrorCode ec;
274 : struct TALER_WireSaltP wire_salt;
275 : struct TALER_MerchantWireHashP h_wire;
276 : struct TALER_FullPayto payto_uri;
277 2 : struct TALER_EXCHANGE_CoinDepositDetail cdds[ds->num_coins];
278 : struct GNUNET_JSON_Specification spec[] = {
279 2 : TALER_JSON_spec_full_payto_uri ("payto_uri",
280 : &payto_uri),
281 2 : GNUNET_JSON_spec_fixed_auto ("salt",
282 : &wire_salt),
283 2 : GNUNET_JSON_spec_end ()
284 : };
285 : const char *exchange_url
286 2 : = TALER_TESTING_get_exchange_url (is);
287 :
288 : (void) cmd;
289 2 : if (NULL == exchange_url)
290 : {
291 0 : GNUNET_break (0);
292 0 : return;
293 : }
294 2 : memset (cdds,
295 : 0,
296 : sizeof (cdds));
297 2 : ds->is = is;
298 2 : GNUNET_assert (NULL != ds->wire_details);
299 2 : if (GNUNET_OK !=
300 2 : GNUNET_JSON_parse (ds->wire_details,
301 : spec,
302 : NULL, NULL))
303 : {
304 0 : json_dumpf (ds->wire_details,
305 : stderr,
306 : JSON_INDENT (2));
307 0 : GNUNET_break (0);
308 0 : TALER_TESTING_interpreter_fail (is);
309 0 : return;
310 : }
311 : #if DUMP_CONTRACT
312 : fprintf (stderr,
313 : "Using contract:\n");
314 : json_dumpf (ds->contract_terms,
315 : stderr,
316 : JSON_INDENT (2));
317 : #endif
318 2 : if (GNUNET_OK !=
319 2 : TALER_JSON_contract_hash (ds->contract_terms,
320 : &h_contract_terms))
321 : {
322 0 : GNUNET_break (0);
323 0 : TALER_TESTING_interpreter_fail (is);
324 0 : return;
325 : }
326 2 : GNUNET_assert (GNUNET_OK ==
327 : TALER_JSON_merchant_wire_signature_hash (ds->wire_details,
328 : &h_wire));
329 2 : if (! GNUNET_TIME_absolute_is_zero (ds->refund_deadline.abs_time))
330 : {
331 : struct GNUNET_TIME_Relative refund_deadline;
332 :
333 : refund_deadline
334 0 : = GNUNET_TIME_absolute_get_remaining (ds->refund_deadline.abs_time);
335 : ds->wire_deadline
336 : =
337 0 : GNUNET_TIME_relative_to_timestamp (
338 : GNUNET_TIME_relative_multiply (refund_deadline,
339 : 2));
340 : }
341 : else
342 : {
343 2 : ds->refund_deadline = ds->wallet_timestamp;
344 2 : ds->wire_deadline = GNUNET_TIME_timestamp_get ();
345 : }
346 :
347 : {
348 : const struct TALER_TESTING_Command *acc_var;
349 2 : if (NULL != (acc_var
350 2 : = TALER_TESTING_interpreter_get_command (
351 : is,
352 : "account-priv")))
353 : {
354 : const union TALER_AccountPrivateKeyP *account_priv;
355 :
356 2 : if ( (GNUNET_OK !=
357 2 : TALER_TESTING_get_trait_account_priv (acc_var,
358 : &account_priv)) )
359 : {
360 0 : GNUNET_break (0);
361 0 : TALER_TESTING_interpreter_fail (is);
362 0 : return;
363 : }
364 2 : ds->account_priv = *account_priv;
365 2 : GNUNET_CRYPTO_eddsa_key_get_public (
366 2 : &ds->account_priv.merchant_priv.eddsa_priv,
367 : &ds->account_pub.merchant_pub.eddsa_pub);
368 : }
369 : else
370 : {
371 0 : GNUNET_CRYPTO_eddsa_key_create (
372 : &ds->account_priv.merchant_priv.eddsa_priv);
373 0 : GNUNET_CRYPTO_eddsa_key_get_public (
374 0 : &ds->account_priv.merchant_priv.eddsa_priv,
375 : &ds->account_pub.merchant_pub.eddsa_pub);
376 : }
377 : }
378 6 : for (unsigned int i = 0; i<ds->num_coins; i++)
379 : {
380 4 : struct Coin *coin = &ds->coins[i];
381 4 : struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i];
382 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
383 4 : const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL;
384 :
385 4 : GNUNET_assert (NULL != coin->coin_reference);
386 4 : cdd->amount = coin->amount;
387 8 : coin->coin_cmd = TALER_TESTING_interpreter_lookup_command (
388 : is,
389 4 : coin->coin_reference);
390 4 : if (NULL == coin->coin_cmd)
391 : {
392 0 : GNUNET_break (0);
393 0 : TALER_TESTING_interpreter_fail (is);
394 0 : return;
395 : }
396 :
397 4 : if ( (GNUNET_OK !=
398 4 : TALER_TESTING_get_trait_coin_priv (coin->coin_cmd,
399 : coin->coin_idx,
400 4 : &coin_priv)) ||
401 : (GNUNET_OK !=
402 4 : TALER_TESTING_get_trait_age_commitment_proof (coin->coin_cmd,
403 : coin->coin_idx,
404 : &age_commitment_proof))
405 4 : ||
406 : (GNUNET_OK !=
407 4 : TALER_TESTING_get_trait_denom_pub (coin->coin_cmd,
408 : coin->coin_idx,
409 4 : &coin->denom_pub)) ||
410 : (GNUNET_OK !=
411 4 : TALER_TESTING_get_trait_denom_sig (coin->coin_cmd,
412 : coin->coin_idx,
413 : &denom_pub_sig)) )
414 : {
415 0 : GNUNET_break (0);
416 0 : TALER_TESTING_interpreter_fail (is);
417 0 : return;
418 : }
419 4 : if (NULL != age_commitment_proof)
420 : {
421 0 : TALER_age_commitment_hash (&age_commitment_proof->commitment,
422 : &cdd->h_age_commitment);
423 : }
424 4 : coin->deposit_fee = coin->denom_pub->fees.deposit;
425 4 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
426 : &cdd->coin_pub.eddsa_pub);
427 4 : cdd->denom_sig = *denom_pub_sig;
428 4 : cdd->h_denom_pub = coin->denom_pub->h_key;
429 4 : TALER_wallet_deposit_sign (&coin->amount,
430 4 : &coin->denom_pub->fees.deposit,
431 : &h_wire,
432 : &h_contract_terms,
433 : NULL, /* wallet_data_hash */
434 4 : &cdd->h_age_commitment,
435 : NULL, /* hash of extensions */
436 4 : &coin->denom_pub->h_key,
437 : ds->wallet_timestamp,
438 4 : &ds->account_pub.merchant_pub,
439 : ds->refund_deadline,
440 : coin_priv,
441 : &cdd->coin_sig);
442 4 : coin->coin_sig = cdd->coin_sig;
443 4 : coin->che.type = TALER_EXCHANGE_CTT_DEPOSIT;
444 4 : coin->che.amount = coin->amount;
445 4 : coin->che.details.deposit.h_wire = h_wire;
446 4 : coin->che.details.deposit.h_contract_terms = h_contract_terms;
447 4 : coin->che.details.deposit.no_h_policy = true;
448 4 : coin->che.details.deposit.no_wallet_data_hash = true;
449 4 : coin->che.details.deposit.wallet_timestamp = ds->wallet_timestamp;
450 4 : coin->che.details.deposit.merchant_pub = ds->account_pub.merchant_pub;
451 4 : coin->che.details.deposit.refund_deadline = ds->refund_deadline;
452 4 : coin->che.details.deposit.sig = cdd->coin_sig;
453 4 : coin->che.details.deposit.no_hac = GNUNET_is_zero (&cdd->h_age_commitment);
454 4 : coin->che.details.deposit.hac = cdd->h_age_commitment;
455 4 : coin->che.details.deposit.deposit_fee = coin->denom_pub->fees.deposit;
456 : }
457 :
458 2 : GNUNET_assert (NULL == ds->dh);
459 : {
460 2 : struct TALER_EXCHANGE_DepositContractDetail dcd = {
461 : .wire_deadline = ds->wire_deadline,
462 : .merchant_payto_uri = payto_uri,
463 : .wire_salt = wire_salt,
464 : .h_contract_terms = h_contract_terms,
465 : .policy_details = NULL /* FIXME #7270-OEC */,
466 : .wallet_timestamp = ds->wallet_timestamp,
467 : .merchant_pub = ds->account_pub.merchant_pub,
468 : .refund_deadline = ds->refund_deadline
469 : };
470 :
471 2 : TALER_merchant_contract_sign (&h_contract_terms,
472 2 : &ds->account_priv.merchant_priv,
473 : &dcd.merchant_sig);
474 2 : ds->dh = TALER_EXCHANGE_post_batch_deposit_create (
475 : TALER_TESTING_interpreter_get_context (is),
476 : exchange_url,
477 : TALER_TESTING_get_keys (is),
478 : &dcd,
479 : ds->num_coins,
480 : cdds,
481 : &ec);
482 : }
483 2 : if (NULL == ds->dh)
484 : {
485 0 : GNUNET_break (0);
486 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
487 : "Could not create deposit with EC %d\n",
488 : (int) ec);
489 0 : TALER_TESTING_interpreter_fail (is);
490 0 : return;
491 : }
492 2 : TALER_EXCHANGE_post_batch_deposit_start (ds->dh,
493 : &batch_deposit_cb,
494 : ds);
495 : }
496 :
497 :
498 : /**
499 : * Free the state of a "batch-deposit" CMD, and possibly cancel a
500 : * pending operation thereof.
501 : *
502 : * @param cls closure, must be a `struct BatchDepositState`.
503 : * @param cmd the command which is being cleaned up.
504 : */
505 : static void
506 2 : batch_deposit_cleanup (void *cls,
507 : const struct TALER_TESTING_Command *cmd)
508 : {
509 2 : struct BatchDepositState *ds = cls;
510 :
511 2 : if (NULL != ds->dh)
512 : {
513 0 : TALER_TESTING_command_incomplete (ds->is,
514 : cmd->label);
515 0 : TALER_EXCHANGE_post_batch_deposit_cancel (ds->dh);
516 0 : ds->dh = NULL;
517 : }
518 2 : if (NULL != ds->retry_task)
519 : {
520 0 : GNUNET_SCHEDULER_cancel (ds->retry_task);
521 0 : ds->retry_task = NULL;
522 : }
523 6 : for (unsigned int i = 0; i<ds->num_coins; i++)
524 4 : GNUNET_free (ds->coins[i].coin_reference);
525 2 : GNUNET_free (ds->coins);
526 2 : json_decref (ds->wire_details);
527 2 : json_decref (ds->contract_terms);
528 2 : GNUNET_free (ds);
529 2 : }
530 :
531 :
532 : /**
533 : * Offer internal data from a "batch-deposit" CMD, to other commands.
534 : *
535 : * @param cls closure.
536 : * @param[out] ret result.
537 : * @param trait name of the trait.
538 : * @param index index number of the object to offer.
539 : * @return #GNUNET_OK on success.
540 : */
541 : static enum GNUNET_GenericReturnValue
542 8 : batch_deposit_traits (void *cls,
543 : const void **ret,
544 : const char *trait,
545 : unsigned int index)
546 : {
547 8 : struct BatchDepositState *ds = cls;
548 8 : const struct Coin *coin = &ds->coins[index];
549 : /* Will point to coin cmd internals. */
550 : const struct TALER_CoinSpendPrivateKeyP *coin_spent_priv;
551 : const struct TALER_CoinSpendPublicKeyP *coin_spent_pub;
552 : const struct TALER_AgeCommitmentProof *age_commitment_proof;
553 :
554 8 : if (index >= ds->num_coins)
555 : {
556 2 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
557 : "[batch_deposit_traits] asked for index #%u while num_coins is #%u\n",
558 : index,
559 : ds->num_coins);
560 2 : return GNUNET_NO;
561 : }
562 6 : if (NULL == coin->coin_cmd)
563 : {
564 0 : GNUNET_break (0);
565 0 : TALER_TESTING_interpreter_fail (ds->is);
566 0 : return GNUNET_NO;
567 : }
568 6 : if ( (GNUNET_OK !=
569 6 : TALER_TESTING_get_trait_coin_priv (coin->coin_cmd,
570 6 : coin->coin_idx,
571 6 : &coin_spent_priv)) ||
572 : (GNUNET_OK !=
573 6 : TALER_TESTING_get_trait_coin_pub (coin->coin_cmd,
574 6 : coin->coin_idx,
575 6 : &coin_spent_pub)) ||
576 : (GNUNET_OK !=
577 6 : TALER_TESTING_get_trait_age_commitment_proof (coin->coin_cmd,
578 6 : coin->coin_idx,
579 : &age_commitment_proof)) )
580 : {
581 0 : GNUNET_break (0);
582 0 : TALER_TESTING_interpreter_fail (ds->is);
583 0 : return GNUNET_NO;
584 : }
585 :
586 : {
587 : struct TALER_TESTING_Trait traits[] = {
588 : /* First two traits are only available if
589 : ds->traits is #GNUNET_YES */
590 6 : TALER_TESTING_make_trait_exchange_pub (0,
591 6 : &ds->exchange_pub),
592 6 : TALER_TESTING_make_trait_exchange_sig (0,
593 6 : &ds->exchange_sig),
594 : /* These traits are always available */
595 6 : TALER_TESTING_make_trait_wire_details (ds->wire_details),
596 6 : TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
597 6 : TALER_TESTING_make_trait_merchant_priv (&ds->account_priv.merchant_priv),
598 6 : TALER_TESTING_make_trait_merchant_pub (&ds->account_pub.merchant_pub),
599 6 : TALER_TESTING_make_trait_account_priv (&ds->account_priv),
600 6 : TALER_TESTING_make_trait_account_pub (&ds->account_pub),
601 6 : TALER_TESTING_make_trait_age_commitment_proof (index,
602 : age_commitment_proof),
603 6 : TALER_TESTING_make_trait_coin_history (index,
604 : &coin->che),
605 6 : TALER_TESTING_make_trait_coin_pub (index,
606 : coin_spent_pub),
607 6 : TALER_TESTING_make_trait_denom_pub (index,
608 6 : coin->denom_pub),
609 6 : TALER_TESTING_make_trait_coin_priv (index,
610 : coin_spent_priv),
611 6 : TALER_TESTING_make_trait_coin_sig (index,
612 : &coin->coin_sig),
613 6 : TALER_TESTING_make_trait_deposit_amount (index,
614 : &coin->amount),
615 6 : TALER_TESTING_make_trait_deposit_fee_amount (index,
616 : &coin->deposit_fee),
617 6 : TALER_TESTING_make_trait_timestamp (index,
618 6 : &ds->exchange_timestamp),
619 6 : TALER_TESTING_make_trait_wire_deadline (index,
620 6 : &ds->wire_deadline),
621 6 : TALER_TESTING_make_trait_refund_deadline (index,
622 6 : &ds->refund_deadline),
623 6 : TALER_TESTING_make_trait_legi_requirement_row (&ds->requirement_row),
624 6 : TALER_TESTING_make_trait_h_normalized_payto (&ds->h_payto),
625 6 : TALER_TESTING_trait_end ()
626 : };
627 :
628 6 : return TALER_TESTING_get_trait ((ds->deposit_succeeded)
629 : ? traits
630 : : &traits[2],
631 : ret,
632 : trait,
633 : index);
634 : }
635 : }
636 :
637 :
638 : struct TALER_TESTING_Command
639 2 : TALER_TESTING_cmd_batch_deposit (
640 : const char *label,
641 : const struct TALER_FullPayto target_account_payto,
642 : const char *contract_terms,
643 : struct GNUNET_TIME_Relative refund_deadline,
644 : unsigned int expected_response_code,
645 : ...)
646 : {
647 : struct BatchDepositState *ds;
648 : va_list ap;
649 2 : unsigned int num_coins = 0;
650 : const char *ref;
651 :
652 2 : va_start (ap,
653 : expected_response_code);
654 6 : while (NULL != (ref = va_arg (ap,
655 : const char *)))
656 : {
657 4 : GNUNET_assert (NULL != va_arg (ap,
658 : const char *));
659 4 : num_coins++;
660 : }
661 2 : va_end (ap);
662 :
663 2 : ds = GNUNET_new (struct BatchDepositState);
664 2 : ds->num_coins = num_coins;
665 2 : ds->coins = GNUNET_new_array (num_coins,
666 : struct Coin);
667 2 : num_coins = 0;
668 2 : va_start (ap,
669 : expected_response_code);
670 6 : while (NULL != (ref = va_arg (ap,
671 : const char *)))
672 : {
673 4 : struct Coin *coin = &ds->coins[num_coins++];
674 4 : const char *amount = va_arg (ap,
675 : const char *);
676 :
677 4 : GNUNET_assert (GNUNET_OK ==
678 : TALER_TESTING_parse_coin_reference (ref,
679 : &coin->coin_reference,
680 : &coin->coin_idx));
681 4 : GNUNET_assert (GNUNET_OK ==
682 : TALER_string_to_amount (amount,
683 : &coin->amount));
684 : }
685 2 : va_end (ap);
686 :
687 2 : ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto);
688 2 : GNUNET_assert (NULL != ds->wire_details);
689 2 : ds->contract_terms = json_loads (contract_terms,
690 : JSON_REJECT_DUPLICATES,
691 : NULL);
692 2 : if (NULL == ds->contract_terms)
693 : {
694 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
695 : "Failed to parse contract terms `%s' for CMD `%s'\n",
696 : contract_terms,
697 : label);
698 0 : GNUNET_assert (0);
699 : }
700 2 : ds->wallet_timestamp = GNUNET_TIME_timestamp_get ();
701 2 : GNUNET_assert (0 ==
702 : json_object_set_new (ds->contract_terms,
703 : "timestamp",
704 : GNUNET_JSON_from_timestamp (
705 : ds->wallet_timestamp)));
706 2 : if (! GNUNET_TIME_relative_is_zero (refund_deadline))
707 : {
708 0 : ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline);
709 0 : GNUNET_assert (0 ==
710 : json_object_set_new (ds->contract_terms,
711 : "refund_deadline",
712 : GNUNET_JSON_from_timestamp (
713 : ds->refund_deadline)));
714 : }
715 2 : ds->expected_response_code = expected_response_code;
716 : {
717 2 : struct TALER_TESTING_Command cmd = {
718 : .cls = ds,
719 : .label = label,
720 : .run = &batch_deposit_run,
721 : .cleanup = &batch_deposit_cleanup,
722 : .traits = &batch_deposit_traits
723 : };
724 :
725 2 : return cmd;
726 : }
727 : }
728 :
729 :
730 : /* end of testing_api_cmd_batch_deposit.c */
|