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_deposit.c
21 : * @brief command for testing /deposit.
22 : * @author Marcello Stanisci
23 : */
24 : #include "taler/taler_json_lib.h"
25 : #include <gnunet/gnunet_curl_lib.h>
26 : struct DepositState;
27 : #define TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE \
28 : struct DepositState
29 : #include "taler/exchange/post-batch-deposit.h"
30 : #include "taler/taler_testing_lib.h"
31 : #include "taler/taler_signatures.h"
32 : #include "backoff.h"
33 :
34 :
35 : /**
36 : * How often do we retry before giving up?
37 : */
38 : #define NUM_RETRIES 5
39 :
40 : /**
41 : * How long do we wait AT MOST when retrying?
42 : */
43 : #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
44 : GNUNET_TIME_UNIT_MILLISECONDS, 100)
45 :
46 :
47 : /**
48 : * State for a "deposit" CMD.
49 : */
50 : struct DepositState
51 : {
52 :
53 : /**
54 : * Amount to deposit.
55 : */
56 : struct TALER_Amount amount;
57 :
58 : /**
59 : * Deposit fee.
60 : */
61 : struct TALER_Amount deposit_fee;
62 :
63 : /**
64 : * Reference to any command that is able to provide a coin.
65 : */
66 : const char *coin_reference;
67 :
68 : /**
69 : * If @e coin_reference refers to an operation that generated
70 : * an array of coins, this value determines which coin to pick.
71 : */
72 : unsigned int coin_index;
73 :
74 : /**
75 : * Our coin signature.
76 : */
77 : struct TALER_CoinSpendSignatureP coin_sig;
78 :
79 : /**
80 : * Wire details of who is depositing -- this would be merchant
81 : * wire details in a normal scenario.
82 : */
83 : json_t *wire_details;
84 :
85 : /**
86 : * JSON string describing what a proposal is about.
87 : */
88 : json_t *contract_terms;
89 :
90 : /**
91 : * Refund deadline. Zero for no refunds.
92 : */
93 : struct GNUNET_TIME_Timestamp refund_deadline;
94 :
95 : /**
96 : * Wire deadline.
97 : */
98 : struct GNUNET_TIME_Timestamp wire_deadline;
99 :
100 : /**
101 : * Set (by the interpreter) to a fresh private key. This
102 : * key will be used to sign the deposit request.
103 : */
104 : union TALER_AccountPrivateKeyP account_priv;
105 :
106 : /**
107 : * Set (by the interpreter) to the public key
108 : * corresponding to @e account_priv.
109 : */
110 : union TALER_AccountPublicKeyP account_pub;
111 :
112 : /**
113 : * Deposit handle while operation is running.
114 : */
115 : struct TALER_EXCHANGE_PostBatchDepositHandle *dh;
116 :
117 : /**
118 : * Denomination public key of the deposited coin.
119 : */
120 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
121 :
122 : /**
123 : * Timestamp of the /deposit operation in the wallet (contract signing time).
124 : */
125 : struct GNUNET_TIME_Timestamp wallet_timestamp;
126 :
127 : /**
128 : * Interpreter state.
129 : */
130 : struct TALER_TESTING_Interpreter *is;
131 :
132 : /**
133 : * Task scheduled to try later.
134 : */
135 : struct GNUNET_SCHEDULER_Task *retry_task;
136 :
137 : /**
138 : * How long do we wait until we retry?
139 : */
140 : struct GNUNET_TIME_Relative backoff;
141 :
142 : /**
143 : * Expected HTTP response code.
144 : */
145 : unsigned int expected_response_code;
146 :
147 : /**
148 : * How often should we retry on (transient) failures?
149 : */
150 : unsigned int do_retry;
151 :
152 : /**
153 : * Set to true if the /deposit succeeded
154 : * and we now can provide the resulting traits.
155 : */
156 : bool deposit_succeeded;
157 :
158 : /**
159 : * Expected entry in the coin history created by this
160 : * operation.
161 : */
162 : struct TALER_EXCHANGE_CoinHistoryEntry che;
163 :
164 : /**
165 : * When did the exchange receive the deposit?
166 : */
167 : struct GNUNET_TIME_Timestamp exchange_timestamp;
168 :
169 : /**
170 : * Signing key used by the exchange to sign the
171 : * deposit confirmation.
172 : */
173 : struct TALER_ExchangePublicKeyP exchange_pub;
174 :
175 : /**
176 : * Signature from the exchange on the
177 : * deposit confirmation.
178 : */
179 : struct TALER_ExchangeSignatureP exchange_sig;
180 :
181 : /**
182 : * Cumulative total the @e exchange_sig signed over.
183 : */
184 : struct TALER_Amount cumulative_total;
185 :
186 : /**
187 : * Reference to previous deposit operation.
188 : * Only present if we're supposed to replay the previous deposit.
189 : */
190 : const char *deposit_reference;
191 :
192 : /**
193 : * Did we set the parameters for this deposit command?
194 : *
195 : * When we're referencing another deposit operation,
196 : * this will only be set after the command has been started.
197 : */
198 : bool command_initialized;
199 :
200 : /**
201 : * Reference to fetch the merchant private key from.
202 : * If NULL, we generate our own, fresh merchant key.
203 : */
204 : const char *merchant_priv_reference;
205 : };
206 :
207 :
208 : /**
209 : * Run the command.
210 : *
211 : * @param cls closure.
212 : * @param cmd the command to execute.
213 : * @param is the interpreter state.
214 : */
215 : static void
216 : deposit_run (void *cls,
217 : const struct TALER_TESTING_Command *cmd,
218 : struct TALER_TESTING_Interpreter *is);
219 :
220 :
221 : /**
222 : * Task scheduled to re-try #deposit_run.
223 : *
224 : * @param cls a `struct DepositState`
225 : */
226 : static void
227 0 : do_retry (void *cls)
228 : {
229 0 : struct DepositState *ds = cls;
230 :
231 0 : ds->retry_task = NULL;
232 0 : TALER_TESTING_touch_cmd (ds->is);
233 0 : deposit_run (ds,
234 : NULL,
235 : ds->is);
236 0 : }
237 :
238 :
239 : /**
240 : * Callback to analyze the /deposit response, just used to
241 : * check if the response code is acceptable.
242 : *
243 : * @param ds closure.
244 : * @param dr deposit response details
245 : */
246 : static void
247 112 : deposit_cb (struct DepositState *ds,
248 : const struct TALER_EXCHANGE_PostBatchDepositResponse *dr)
249 : {
250 112 : ds->dh = NULL;
251 112 : if (ds->expected_response_code != dr->hr.http_status)
252 : {
253 0 : if (0 != ds->do_retry)
254 : {
255 0 : ds->do_retry--;
256 0 : if ( (0 == dr->hr.http_status) ||
257 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == dr->hr.ec) ||
258 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == dr->hr.http_status) )
259 : {
260 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
261 : "Retrying deposit failed with %u/%d\n",
262 : dr->hr.http_status,
263 : (int) dr->hr.ec);
264 : /* on DB conflicts, do not use backoff */
265 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == dr->hr.ec)
266 0 : ds->backoff = GNUNET_TIME_UNIT_ZERO;
267 : else
268 0 : ds->backoff = GNUNET_TIME_randomized_backoff (ds->backoff,
269 : MAX_BACKOFF);
270 0 : TALER_TESTING_inc_tries (ds->is);
271 0 : GNUNET_assert (NULL == ds->retry_task);
272 : ds->retry_task
273 0 : = GNUNET_SCHEDULER_add_delayed (ds->backoff,
274 : &do_retry,
275 : ds);
276 0 : return;
277 : }
278 : }
279 0 : TALER_TESTING_unexpected_status_with_body (
280 : ds->is,
281 : dr->hr.http_status,
282 : ds->expected_response_code,
283 : dr->hr.reply);
284 :
285 0 : return;
286 : }
287 112 : if ( (MHD_HTTP_CONFLICT == dr->hr.http_status) &&
288 : (TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY ==
289 9 : dr->hr.ec) )
290 : {
291 1 : const struct TALER_EXCHANGE_CoinDenominationConflict *cdc
292 : = &dr->details.conflict.details.coin_conflict.details.
293 : denomination_conflict;
294 :
295 : /* the library verified the exchange's proof; the denomination it
296 : names must be one we know, and differ from the one we used */
297 1 : if ( (! cdc->verified) ||
298 1 : (0 == GNUNET_memcmp (&cdc->prev_h_denom_pub,
299 : &ds->denom_pub->h_key)) )
300 : {
301 0 : GNUNET_break (0);
302 0 : TALER_TESTING_interpreter_fail (ds->is);
303 0 : return;
304 : }
305 : }
306 112 : if (MHD_HTTP_OK == dr->hr.http_status)
307 : {
308 92 : ds->deposit_succeeded = true;
309 92 : ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
310 92 : ds->exchange_pub = *dr->details.ok.exchange_pub;
311 92 : ds->exchange_sig = *dr->details.ok.exchange_sig;
312 92 : ds->cumulative_total = dr->details.ok.accumulated_total_without_fee;
313 : }
314 112 : TALER_TESTING_interpreter_next (ds->is);
315 : }
316 :
317 :
318 : /**
319 : * Run the command.
320 : *
321 : * @param cls closure.
322 : * @param cmd the command to execute.
323 : * @param is the interpreter state.
324 : */
325 : static void
326 112 : deposit_run (void *cls,
327 : const struct TALER_TESTING_Command *cmd,
328 : struct TALER_TESTING_Interpreter *is)
329 : {
330 112 : struct DepositState *ds = cls;
331 : const struct TALER_TESTING_Command *coin_cmd;
332 : const struct TALER_TESTING_Command *acc_var;
333 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
334 : struct TALER_CoinSpendPublicKeyP coin_pub;
335 : const struct TALER_AgeCommitmentHashP *phac;
336 : const struct TALER_DenominationSignature *denom_pub_sig;
337 : struct TALER_PrivateContractHashP h_contract_terms;
338 : enum TALER_ErrorCode ec;
339 : struct TALER_WireSaltP wire_salt;
340 : struct TALER_FullPayto payto_uri;
341 : struct GNUNET_JSON_Specification spec[] = {
342 112 : TALER_JSON_spec_full_payto_uri ("payto_uri",
343 : &payto_uri),
344 112 : GNUNET_JSON_spec_fixed_auto ("salt",
345 : &wire_salt),
346 112 : GNUNET_JSON_spec_end ()
347 : };
348 : const char *exchange_url
349 112 : = TALER_TESTING_get_exchange_url (is);
350 :
351 : (void) cmd;
352 112 : if (NULL == exchange_url)
353 : {
354 0 : GNUNET_break (0);
355 0 : return;
356 : }
357 112 : ds->is = is;
358 112 : if (! GNUNET_TIME_absolute_is_zero (ds->refund_deadline.abs_time))
359 : {
360 : struct GNUNET_TIME_Relative refund_deadline;
361 :
362 : refund_deadline
363 10 : = GNUNET_TIME_absolute_get_remaining (ds->refund_deadline.abs_time);
364 : ds->wire_deadline
365 10 : = GNUNET_TIME_relative_to_timestamp (
366 : GNUNET_TIME_relative_multiply (refund_deadline,
367 : 2));
368 : }
369 : else
370 : {
371 102 : ds->refund_deadline = ds->wallet_timestamp;
372 102 : ds->wire_deadline = GNUNET_TIME_timestamp_get ();
373 : }
374 112 : if (NULL != ds->deposit_reference)
375 : {
376 : /* We're copying another deposit operation, initialize here. */
377 : const struct TALER_TESTING_Command *drcmd;
378 : struct DepositState *ods;
379 :
380 14 : drcmd = TALER_TESTING_interpreter_lookup_command (is,
381 : ds->deposit_reference);
382 14 : if (NULL == drcmd)
383 : {
384 0 : GNUNET_break (0);
385 0 : TALER_TESTING_interpreter_fail (is);
386 0 : return;
387 : }
388 14 : ods = drcmd->cls;
389 14 : ds->coin_reference = ods->coin_reference;
390 14 : ds->coin_index = ods->coin_index;
391 14 : ds->wire_details = json_incref (ods->wire_details);
392 14 : GNUNET_assert (NULL != ds->wire_details);
393 14 : ds->contract_terms = json_incref (ods->contract_terms);
394 14 : ds->wallet_timestamp = ods->wallet_timestamp;
395 14 : ds->refund_deadline = ods->refund_deadline;
396 14 : ds->wire_deadline = ods->wire_deadline;
397 14 : ds->amount = ods->amount;
398 14 : ds->account_priv = ods->account_priv;
399 14 : ds->account_pub = ods->account_pub;
400 14 : ds->command_initialized = true;
401 : }
402 98 : else if (NULL != ds->merchant_priv_reference)
403 : {
404 : /* We're copying the merchant key from another deposit operation */
405 : const struct TALER_MerchantPrivateKeyP *merchant_priv;
406 : const struct TALER_TESTING_Command *mpcmd;
407 :
408 2 : mpcmd = TALER_TESTING_interpreter_lookup_command (
409 : is,
410 : ds->merchant_priv_reference);
411 2 : if (NULL == mpcmd)
412 : {
413 0 : GNUNET_break (0);
414 0 : TALER_TESTING_interpreter_fail (is);
415 0 : return;
416 : }
417 2 : if ( (GNUNET_OK !=
418 2 : TALER_TESTING_get_trait_merchant_priv (mpcmd,
419 : &merchant_priv)) )
420 : {
421 0 : GNUNET_break (0);
422 0 : TALER_TESTING_interpreter_fail (is);
423 0 : return;
424 : }
425 2 : ds->account_priv.merchant_priv = *merchant_priv;
426 2 : GNUNET_CRYPTO_eddsa_key_get_public (
427 2 : &ds->account_priv.merchant_priv.eddsa_priv,
428 : &ds->account_pub.merchant_pub.eddsa_pub);
429 : }
430 96 : else if (NULL != (acc_var
431 96 : = TALER_TESTING_interpreter_get_command (
432 : is,
433 : "account-priv")))
434 : {
435 : const union TALER_AccountPrivateKeyP *account_priv;
436 :
437 62 : if ( (GNUNET_OK !=
438 62 : TALER_TESTING_get_trait_account_priv (acc_var,
439 : &account_priv)) )
440 : {
441 0 : GNUNET_break (0);
442 0 : TALER_TESTING_interpreter_fail (is);
443 0 : return;
444 : }
445 62 : ds->account_priv = *account_priv;
446 62 : GNUNET_CRYPTO_eddsa_key_get_public (
447 62 : &ds->account_priv.merchant_priv.eddsa_priv,
448 : &ds->account_pub.merchant_pub.eddsa_pub);
449 : }
450 : else
451 : {
452 34 : GNUNET_CRYPTO_eddsa_key_create (
453 : &ds->account_priv.merchant_priv.eddsa_priv);
454 34 : GNUNET_CRYPTO_eddsa_key_get_public (
455 34 : &ds->account_priv.merchant_priv.eddsa_priv,
456 : &ds->account_pub.merchant_pub.eddsa_pub);
457 : }
458 112 : GNUNET_assert (NULL != ds->wire_details);
459 112 : if (GNUNET_OK !=
460 112 : GNUNET_JSON_parse (ds->wire_details,
461 : spec,
462 : NULL, NULL))
463 : {
464 0 : json_dumpf (ds->wire_details,
465 : stderr,
466 : JSON_INDENT (2));
467 0 : GNUNET_break (0);
468 0 : TALER_TESTING_interpreter_fail (is);
469 0 : return;
470 : }
471 112 : GNUNET_assert (ds->coin_reference);
472 112 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
473 : ds->coin_reference);
474 112 : if (NULL == coin_cmd)
475 : {
476 0 : GNUNET_break (0);
477 0 : TALER_TESTING_interpreter_fail (is);
478 0 : return;
479 : }
480 : #if DUMP_CONTRACT
481 : fprintf (stderr,
482 : "Using contract:\n");
483 : json_dumpf (ds->contract_terms,
484 : stderr,
485 : JSON_INDENT (2));
486 : #endif
487 112 : if (GNUNET_OK !=
488 112 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
489 : ds->coin_index,
490 : &coin_priv))
491 : {
492 0 : GNUNET_break (0);
493 0 : TALER_TESTING_interpreter_fail (is);
494 0 : return;
495 : }
496 112 : if (GNUNET_OK !=
497 112 : TALER_TESTING_get_trait_h_age_commitment (coin_cmd,
498 : ds->coin_index,
499 : &phac))
500 : {
501 0 : GNUNET_break (0);
502 0 : TALER_TESTING_interpreter_fail (is);
503 0 : return;
504 : }
505 112 : if (GNUNET_OK !=
506 112 : TALER_TESTING_get_trait_denom_pub (coin_cmd,
507 : ds->coin_index,
508 : &ds->denom_pub))
509 : {
510 0 : GNUNET_break (0);
511 0 : TALER_TESTING_interpreter_fail (is);
512 0 : return;
513 : }
514 112 : if (GNUNET_OK !=
515 112 : TALER_TESTING_get_trait_denom_sig (coin_cmd,
516 : ds->coin_index,
517 : &denom_pub_sig))
518 : {
519 0 : GNUNET_break (0);
520 0 : TALER_TESTING_interpreter_fail (is);
521 0 : return;
522 : }
523 112 : if (GNUNET_OK !=
524 112 : TALER_JSON_contract_hash (ds->contract_terms,
525 : &h_contract_terms))
526 : {
527 0 : GNUNET_break (0);
528 0 : TALER_TESTING_interpreter_fail (is);
529 0 : return;
530 : }
531 :
532 112 : ds->deposit_fee = ds->denom_pub->fees.deposit;
533 112 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
534 : &coin_pub.eddsa_pub);
535 :
536 : {
537 : struct TALER_MerchantWireHashP h_wire;
538 :
539 112 : GNUNET_assert (GNUNET_OK ==
540 : TALER_JSON_merchant_wire_signature_hash (ds->wire_details,
541 : &h_wire));
542 112 : TALER_wallet_deposit_sign (&ds->amount,
543 112 : &ds->denom_pub->fees.deposit,
544 : &h_wire,
545 : &h_contract_terms,
546 : NULL, /* wallet data hash */
547 : phac,
548 : NULL, /* hash of extensions */
549 112 : &ds->denom_pub->h_key,
550 : ds->wallet_timestamp,
551 112 : &ds->account_pub.merchant_pub,
552 : ds->refund_deadline,
553 : coin_priv,
554 : &ds->coin_sig);
555 112 : ds->che.type = TALER_EXCHANGE_CTT_DEPOSIT;
556 112 : ds->che.amount = ds->amount;
557 112 : ds->che.details.deposit.h_wire = h_wire;
558 112 : ds->che.details.deposit.h_contract_terms = h_contract_terms;
559 112 : ds->che.details.deposit.no_h_policy = true;
560 112 : ds->che.details.deposit.no_wallet_data_hash = true;
561 112 : ds->che.details.deposit.wallet_timestamp = ds->wallet_timestamp;
562 112 : ds->che.details.deposit.merchant_pub = ds->account_pub.merchant_pub;
563 112 : ds->che.details.deposit.refund_deadline = ds->refund_deadline;
564 112 : ds->che.details.deposit.sig = ds->coin_sig;
565 112 : ds->che.details.deposit.no_hac = true;
566 112 : ds->che.details.deposit.deposit_fee = ds->denom_pub->fees.deposit;
567 : }
568 112 : GNUNET_assert (NULL == ds->dh);
569 : {
570 112 : struct TALER_EXCHANGE_CoinDepositDetail cdd = {
571 : .amount = ds->amount,
572 : .coin_pub = coin_pub,
573 : .coin_sig = ds->coin_sig,
574 112 : .denom_sig = *denom_pub_sig,
575 112 : .h_denom_pub = ds->denom_pub->h_key,
576 : .h_age_commitment = {{{0}}},
577 : };
578 112 : struct TALER_EXCHANGE_DepositContractDetail dcd = {
579 : .wire_deadline = ds->wire_deadline,
580 : .merchant_payto_uri = payto_uri,
581 : .wire_salt = wire_salt,
582 : .h_contract_terms = h_contract_terms,
583 : .wallet_timestamp = ds->wallet_timestamp,
584 : .merchant_pub = ds->account_pub.merchant_pub,
585 : .refund_deadline = ds->refund_deadline
586 : };
587 :
588 112 : TALER_merchant_contract_sign (&h_contract_terms,
589 112 : &ds->account_priv.merchant_priv,
590 : &dcd.merchant_sig);
591 112 : if (NULL != phac)
592 34 : cdd.h_age_commitment = *phac;
593 :
594 112 : ds->dh = TALER_EXCHANGE_post_batch_deposit_create (
595 : TALER_TESTING_interpreter_get_context (is),
596 : exchange_url,
597 : TALER_TESTING_get_keys (is),
598 : &dcd,
599 : 1,
600 : &cdd,
601 : &ec);
602 : }
603 112 : if (NULL == ds->dh)
604 : {
605 0 : GNUNET_break (0);
606 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
607 : "Could not create deposit with EC %d\n",
608 : (int) ec);
609 0 : TALER_TESTING_interpreter_fail (is);
610 0 : return;
611 : }
612 112 : TALER_EXCHANGE_post_batch_deposit_start (ds->dh,
613 : &deposit_cb,
614 : ds);
615 : }
616 :
617 :
618 : /**
619 : * Free the state of a "deposit" CMD, and possibly cancel a
620 : * pending operation thereof.
621 : *
622 : * @param cls closure, must be a `struct DepositState`.
623 : * @param cmd the command which is being cleaned up.
624 : */
625 : static void
626 112 : deposit_cleanup (void *cls,
627 : const struct TALER_TESTING_Command *cmd)
628 : {
629 112 : struct DepositState *ds = cls;
630 :
631 112 : if (NULL != ds->dh)
632 : {
633 0 : TALER_TESTING_command_incomplete (ds->is,
634 : cmd->label);
635 0 : TALER_EXCHANGE_post_batch_deposit_cancel (ds->dh);
636 0 : ds->dh = NULL;
637 : }
638 112 : if (NULL != ds->retry_task)
639 : {
640 0 : GNUNET_SCHEDULER_cancel (ds->retry_task);
641 0 : ds->retry_task = NULL;
642 : }
643 112 : json_decref (ds->wire_details);
644 112 : json_decref (ds->contract_terms);
645 112 : GNUNET_free (ds);
646 112 : }
647 :
648 :
649 : /**
650 : * Offer internal data from a "deposit" CMD, to other commands.
651 : *
652 : * @param cls closure.
653 : * @param[out] ret result.
654 : * @param trait name of the trait.
655 : * @param index index number of the object to offer.
656 : * @return #GNUNET_OK on success.
657 : */
658 : static enum GNUNET_GenericReturnValue
659 398 : deposit_traits (void *cls,
660 : const void **ret,
661 : const char *trait,
662 : unsigned int index)
663 : {
664 398 : struct DepositState *ds = cls;
665 : const struct TALER_TESTING_Command *coin_cmd;
666 : /* Will point to coin cmd internals. */
667 : const struct TALER_CoinSpendPrivateKeyP *coin_spent_priv;
668 : const struct TALER_CoinSpendPublicKeyP *coin_spent_pub;
669 398 : const struct TALER_AgeCommitmentProof *age_commitment_proof=NULL;
670 398 : const struct TALER_AgeCommitmentHashP *h_age_commitment=NULL;
671 :
672 398 : if (! ds->command_initialized)
673 : {
674 : /* No access to traits yet. */
675 0 : GNUNET_break (0);
676 0 : return GNUNET_NO;
677 : }
678 :
679 : coin_cmd
680 398 : = TALER_TESTING_interpreter_lookup_command (ds->is,
681 : ds->coin_reference);
682 398 : if (NULL == coin_cmd)
683 : {
684 0 : GNUNET_break (0);
685 0 : TALER_TESTING_interpreter_fail (ds->is);
686 0 : return GNUNET_NO;
687 : }
688 398 : if ( (GNUNET_OK !=
689 398 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
690 : ds->coin_index,
691 398 : &coin_spent_priv)) ||
692 : (GNUNET_OK !=
693 398 : TALER_TESTING_get_trait_coin_pub (coin_cmd,
694 : ds->coin_index,
695 398 : &coin_spent_pub)) ||
696 : (GNUNET_OK !=
697 398 : TALER_TESTING_get_trait_age_commitment_proof (coin_cmd,
698 : ds->coin_index,
699 398 : &age_commitment_proof)) ||
700 : (GNUNET_OK !=
701 398 : TALER_TESTING_get_trait_h_age_commitment (coin_cmd,
702 : ds->coin_index,
703 : &h_age_commitment)) )
704 : {
705 0 : GNUNET_break (0);
706 0 : TALER_TESTING_interpreter_fail (ds->is);
707 0 : return GNUNET_NO;
708 : }
709 :
710 : {
711 : struct TALER_TESTING_Trait traits[] = {
712 : /* First four traits are only available if
713 : ds->traits is true */
714 398 : TALER_TESTING_make_trait_exchange_pub (0,
715 398 : &ds->exchange_pub),
716 398 : TALER_TESTING_make_trait_exchange_sig (0,
717 398 : &ds->exchange_sig),
718 398 : TALER_TESTING_make_trait_amount (&ds->cumulative_total),
719 398 : TALER_TESTING_make_trait_timestamp (0,
720 398 : &ds->exchange_timestamp),
721 : /* These traits are always available */
722 398 : TALER_TESTING_make_trait_coin_history (0,
723 398 : &ds->che),
724 398 : TALER_TESTING_make_trait_coin_priv (0,
725 : coin_spent_priv),
726 398 : TALER_TESTING_make_trait_coin_pub (0,
727 : coin_spent_pub),
728 398 : TALER_TESTING_make_trait_denom_pub (0,
729 : ds->denom_pub),
730 398 : TALER_TESTING_make_trait_coin_sig (0,
731 398 : &ds->coin_sig),
732 398 : TALER_TESTING_make_trait_age_commitment_proof (0,
733 : age_commitment_proof),
734 398 : TALER_TESTING_make_trait_h_age_commitment (0,
735 : h_age_commitment),
736 398 : TALER_TESTING_make_trait_wire_details (ds->wire_details),
737 398 : TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
738 398 : TALER_TESTING_make_trait_merchant_priv (&ds->account_priv.merchant_priv),
739 398 : TALER_TESTING_make_trait_merchant_pub (&ds->account_pub.merchant_pub),
740 398 : TALER_TESTING_make_trait_account_priv (&ds->account_priv),
741 398 : TALER_TESTING_make_trait_account_pub (&ds->account_pub),
742 398 : TALER_TESTING_make_trait_deposit_amount (0,
743 398 : &ds->amount),
744 398 : TALER_TESTING_make_trait_deposit_fee_amount (0,
745 398 : &ds->deposit_fee),
746 398 : TALER_TESTING_make_trait_wire_deadline (0,
747 398 : &ds->wire_deadline),
748 398 : TALER_TESTING_make_trait_refund_deadline (0,
749 398 : &ds->refund_deadline),
750 398 : TALER_TESTING_trait_end ()
751 : };
752 :
753 398 : return TALER_TESTING_get_trait ((ds->deposit_succeeded)
754 : ? traits
755 : : &traits[4],
756 : ret,
757 : trait,
758 : index);
759 : }
760 : }
761 :
762 :
763 : struct TALER_TESTING_Command
764 96 : TALER_TESTING_cmd_deposit (
765 : const char *label,
766 : const char *coin_reference,
767 : unsigned int coin_index,
768 : struct TALER_FullPayto target_account_payto,
769 : const char *contract_terms,
770 : struct GNUNET_TIME_Relative refund_deadline,
771 : const char *amount,
772 : unsigned int expected_response_code)
773 : {
774 : struct DepositState *ds;
775 :
776 96 : ds = GNUNET_new (struct DepositState);
777 96 : ds->coin_reference = coin_reference;
778 96 : ds->coin_index = coin_index;
779 96 : ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto);
780 96 : GNUNET_assert (NULL != ds->wire_details);
781 96 : ds->contract_terms = json_loads (contract_terms,
782 : JSON_REJECT_DUPLICATES,
783 : NULL);
784 96 : if (NULL == ds->contract_terms)
785 : {
786 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
787 : "Failed to parse contract terms `%s' for CMD `%s'\n",
788 : contract_terms,
789 : label);
790 0 : GNUNET_assert (0);
791 : }
792 96 : ds->wallet_timestamp = GNUNET_TIME_timestamp_get ();
793 96 : GNUNET_assert (0 ==
794 : json_object_set_new (ds->contract_terms,
795 : "timestamp",
796 : GNUNET_JSON_from_timestamp (
797 : ds->wallet_timestamp)));
798 96 : if (! GNUNET_TIME_relative_is_zero (refund_deadline))
799 : {
800 10 : ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline);
801 10 : GNUNET_assert (0 ==
802 : json_object_set_new (ds->contract_terms,
803 : "refund_deadline",
804 : GNUNET_JSON_from_timestamp (
805 : ds->refund_deadline)));
806 : }
807 96 : GNUNET_assert (GNUNET_OK ==
808 : TALER_string_to_amount (amount,
809 : &ds->amount));
810 96 : ds->expected_response_code = expected_response_code;
811 96 : ds->command_initialized = true;
812 : {
813 96 : struct TALER_TESTING_Command cmd = {
814 : .cls = ds,
815 : .label = label,
816 : .run = &deposit_run,
817 : .cleanup = &deposit_cleanup,
818 : .traits = &deposit_traits
819 : };
820 :
821 96 : return cmd;
822 : }
823 : }
824 :
825 :
826 : struct TALER_TESTING_Command
827 2 : TALER_TESTING_cmd_deposit_with_ref (
828 : const char *label,
829 : const char *coin_reference,
830 : unsigned int coin_index,
831 : struct TALER_FullPayto target_account_payto,
832 : const char *contract_terms,
833 : struct GNUNET_TIME_Relative refund_deadline,
834 : const char *amount,
835 : unsigned int expected_response_code,
836 : const char *merchant_priv_reference)
837 : {
838 : struct DepositState *ds;
839 :
840 2 : ds = GNUNET_new (struct DepositState);
841 2 : ds->merchant_priv_reference = merchant_priv_reference;
842 2 : ds->coin_reference = coin_reference;
843 2 : ds->coin_index = coin_index;
844 2 : ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto);
845 2 : GNUNET_assert (NULL != ds->wire_details);
846 2 : ds->contract_terms = json_loads (contract_terms,
847 : JSON_REJECT_DUPLICATES,
848 : NULL);
849 2 : if (NULL == ds->contract_terms)
850 : {
851 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
852 : "Failed to parse contract terms `%s' for CMD `%s'\n",
853 : contract_terms,
854 : label);
855 0 : GNUNET_assert (0);
856 : }
857 2 : ds->wallet_timestamp = GNUNET_TIME_timestamp_get ();
858 2 : GNUNET_assert (0 ==
859 : json_object_set_new (ds->contract_terms,
860 : "timestamp",
861 : GNUNET_JSON_from_timestamp (
862 : ds->wallet_timestamp)));
863 2 : if (0 != refund_deadline.rel_value_us)
864 : {
865 0 : ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline);
866 0 : GNUNET_assert (0 ==
867 : json_object_set_new (ds->contract_terms,
868 : "refund_deadline",
869 : GNUNET_JSON_from_timestamp (
870 : ds->refund_deadline)));
871 : }
872 2 : GNUNET_assert (GNUNET_OK ==
873 : TALER_string_to_amount (amount,
874 : &ds->amount));
875 2 : ds->expected_response_code = expected_response_code;
876 2 : ds->command_initialized = true;
877 : {
878 2 : struct TALER_TESTING_Command cmd = {
879 : .cls = ds,
880 : .label = label,
881 : .run = &deposit_run,
882 : .cleanup = &deposit_cleanup,
883 : .traits = &deposit_traits
884 : };
885 :
886 2 : return cmd;
887 : }
888 : }
889 :
890 :
891 : struct TALER_TESTING_Command
892 14 : TALER_TESTING_cmd_deposit_replay (
893 : const char *label,
894 : const char *deposit_reference,
895 : unsigned int expected_response_code)
896 : {
897 : struct DepositState *ds;
898 :
899 14 : ds = GNUNET_new (struct DepositState);
900 14 : ds->deposit_reference = deposit_reference;
901 14 : ds->expected_response_code = expected_response_code;
902 : {
903 14 : struct TALER_TESTING_Command cmd = {
904 : .cls = ds,
905 : .label = label,
906 : .run = &deposit_run,
907 : .cleanup = &deposit_cleanup,
908 : .traits = &deposit_traits
909 : };
910 :
911 14 : return cmd;
912 : }
913 : }
914 :
915 :
916 : struct TALER_TESTING_Command
917 0 : TALER_TESTING_cmd_deposit_with_retry (struct TALER_TESTING_Command cmd)
918 : {
919 : struct DepositState *ds;
920 :
921 0 : GNUNET_assert (&deposit_run == cmd.run);
922 0 : ds = cmd.cls;
923 0 : ds->do_retry = NUM_RETRIES;
924 0 : return cmd;
925 : }
926 :
927 :
928 : /* end of testing_api_cmd_deposit.c */
|