exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_deposit.c (28337B)


      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 do_retry (void *cls)
    228 {
    229   struct DepositState *ds = cls;
    230 
    231   ds->retry_task = NULL;
    232   TALER_TESTING_touch_cmd (ds->is);
    233   deposit_run (ds,
    234                NULL,
    235                ds->is);
    236 }
    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 deposit_cb (struct DepositState *ds,
    248             const struct TALER_EXCHANGE_PostBatchDepositResponse *dr)
    249 {
    250   ds->dh = NULL;
    251   if (ds->expected_response_code != dr->hr.http_status)
    252   {
    253     if (0 != ds->do_retry)
    254     {
    255       ds->do_retry--;
    256       if ( (0 == dr->hr.http_status) ||
    257            (TALER_EC_GENERIC_DB_SOFT_FAILURE == dr->hr.ec) ||
    258            (MHD_HTTP_INTERNAL_SERVER_ERROR == dr->hr.http_status) )
    259       {
    260         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         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == dr->hr.ec)
    266           ds->backoff = GNUNET_TIME_UNIT_ZERO;
    267         else
    268           ds->backoff = GNUNET_TIME_randomized_backoff (ds->backoff,
    269                                                         MAX_BACKOFF);
    270         TALER_TESTING_inc_tries (ds->is);
    271         GNUNET_assert (NULL == ds->retry_task);
    272         ds->retry_task
    273           = GNUNET_SCHEDULER_add_delayed (ds->backoff,
    274                                           &do_retry,
    275                                           ds);
    276         return;
    277       }
    278     }
    279     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     return;
    286   }
    287   if ( (MHD_HTTP_CONFLICT == dr->hr.http_status) &&
    288        (TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY ==
    289         dr->hr.ec) )
    290   {
    291     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     if ( (! cdc->verified) ||
    298          (0 == GNUNET_memcmp (&cdc->prev_h_denom_pub,
    299                               &ds->denom_pub->h_key)) )
    300     {
    301       GNUNET_break (0);
    302       TALER_TESTING_interpreter_fail (ds->is);
    303       return;
    304     }
    305   }
    306   if (MHD_HTTP_OK == dr->hr.http_status)
    307   {
    308     ds->deposit_succeeded = true;
    309     ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
    310     ds->exchange_pub = *dr->details.ok.exchange_pub;
    311     ds->exchange_sig = *dr->details.ok.exchange_sig;
    312     ds->cumulative_total = dr->details.ok.accumulated_total_without_fee;
    313   }
    314   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 deposit_run (void *cls,
    327              const struct TALER_TESTING_Command *cmd,
    328              struct TALER_TESTING_Interpreter *is)
    329 {
    330   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     TALER_JSON_spec_full_payto_uri ("payto_uri",
    343                                     &payto_uri),
    344     GNUNET_JSON_spec_fixed_auto ("salt",
    345                                  &wire_salt),
    346     GNUNET_JSON_spec_end ()
    347   };
    348   const char *exchange_url
    349     = TALER_TESTING_get_exchange_url (is);
    350 
    351   (void) cmd;
    352   if (NULL == exchange_url)
    353   {
    354     GNUNET_break (0);
    355     return;
    356   }
    357   ds->is = is;
    358   if (! GNUNET_TIME_absolute_is_zero (ds->refund_deadline.abs_time))
    359   {
    360     struct GNUNET_TIME_Relative refund_deadline;
    361 
    362     refund_deadline
    363       = GNUNET_TIME_absolute_get_remaining (ds->refund_deadline.abs_time);
    364     ds->wire_deadline
    365       = GNUNET_TIME_relative_to_timestamp (
    366           GNUNET_TIME_relative_multiply (refund_deadline,
    367                                          2));
    368   }
    369   else
    370   {
    371     ds->refund_deadline = ds->wallet_timestamp;
    372     ds->wire_deadline = GNUNET_TIME_timestamp_get ();
    373   }
    374   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     drcmd = TALER_TESTING_interpreter_lookup_command (is,
    381                                                       ds->deposit_reference);
    382     if (NULL == drcmd)
    383     {
    384       GNUNET_break (0);
    385       TALER_TESTING_interpreter_fail (is);
    386       return;
    387     }
    388     ods = drcmd->cls;
    389     ds->coin_reference = ods->coin_reference;
    390     ds->coin_index = ods->coin_index;
    391     ds->wire_details = json_incref (ods->wire_details);
    392     GNUNET_assert (NULL != ds->wire_details);
    393     ds->contract_terms = json_incref (ods->contract_terms);
    394     ds->wallet_timestamp = ods->wallet_timestamp;
    395     ds->refund_deadline = ods->refund_deadline;
    396     ds->wire_deadline = ods->wire_deadline;
    397     ds->amount = ods->amount;
    398     ds->account_priv = ods->account_priv;
    399     ds->account_pub = ods->account_pub;
    400     ds->command_initialized = true;
    401   }
    402   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     mpcmd = TALER_TESTING_interpreter_lookup_command (
    409       is,
    410       ds->merchant_priv_reference);
    411     if (NULL == mpcmd)
    412     {
    413       GNUNET_break (0);
    414       TALER_TESTING_interpreter_fail (is);
    415       return;
    416     }
    417     if ( (GNUNET_OK !=
    418           TALER_TESTING_get_trait_merchant_priv (mpcmd,
    419                                                  &merchant_priv)) )
    420     {
    421       GNUNET_break (0);
    422       TALER_TESTING_interpreter_fail (is);
    423       return;
    424     }
    425     ds->account_priv.merchant_priv = *merchant_priv;
    426     GNUNET_CRYPTO_eddsa_key_get_public (
    427       &ds->account_priv.merchant_priv.eddsa_priv,
    428       &ds->account_pub.merchant_pub.eddsa_pub);
    429   }
    430   else if (NULL != (acc_var
    431                       = TALER_TESTING_interpreter_get_command (
    432                           is,
    433                           "account-priv")))
    434   {
    435     const union TALER_AccountPrivateKeyP *account_priv;
    436 
    437     if ( (GNUNET_OK !=
    438           TALER_TESTING_get_trait_account_priv (acc_var,
    439                                                 &account_priv)) )
    440     {
    441       GNUNET_break (0);
    442       TALER_TESTING_interpreter_fail (is);
    443       return;
    444     }
    445     ds->account_priv = *account_priv;
    446     GNUNET_CRYPTO_eddsa_key_get_public (
    447       &ds->account_priv.merchant_priv.eddsa_priv,
    448       &ds->account_pub.merchant_pub.eddsa_pub);
    449   }
    450   else
    451   {
    452     GNUNET_CRYPTO_eddsa_key_create (
    453       &ds->account_priv.merchant_priv.eddsa_priv);
    454     GNUNET_CRYPTO_eddsa_key_get_public (
    455       &ds->account_priv.merchant_priv.eddsa_priv,
    456       &ds->account_pub.merchant_pub.eddsa_pub);
    457   }
    458   GNUNET_assert (NULL != ds->wire_details);
    459   if (GNUNET_OK !=
    460       GNUNET_JSON_parse (ds->wire_details,
    461                          spec,
    462                          NULL, NULL))
    463   {
    464     json_dumpf (ds->wire_details,
    465                 stderr,
    466                 JSON_INDENT (2));
    467     GNUNET_break (0);
    468     TALER_TESTING_interpreter_fail (is);
    469     return;
    470   }
    471   GNUNET_assert (ds->coin_reference);
    472   coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
    473                                                        ds->coin_reference);
    474   if (NULL == coin_cmd)
    475   {
    476     GNUNET_break (0);
    477     TALER_TESTING_interpreter_fail (is);
    478     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   if (GNUNET_OK !=
    488       TALER_TESTING_get_trait_coin_priv (coin_cmd,
    489                                          ds->coin_index,
    490                                          &coin_priv))
    491   {
    492     GNUNET_break (0);
    493     TALER_TESTING_interpreter_fail (is);
    494     return;
    495   }
    496   if (GNUNET_OK !=
    497       TALER_TESTING_get_trait_h_age_commitment (coin_cmd,
    498                                                 ds->coin_index,
    499                                                 &phac))
    500   {
    501     GNUNET_break (0);
    502     TALER_TESTING_interpreter_fail (is);
    503     return;
    504   }
    505   if (GNUNET_OK !=
    506       TALER_TESTING_get_trait_denom_pub (coin_cmd,
    507                                          ds->coin_index,
    508                                          &ds->denom_pub))
    509   {
    510     GNUNET_break (0);
    511     TALER_TESTING_interpreter_fail (is);
    512     return;
    513   }
    514   if (GNUNET_OK !=
    515       TALER_TESTING_get_trait_denom_sig (coin_cmd,
    516                                          ds->coin_index,
    517                                          &denom_pub_sig))
    518   {
    519     GNUNET_break (0);
    520     TALER_TESTING_interpreter_fail (is);
    521     return;
    522   }
    523   if (GNUNET_OK !=
    524       TALER_JSON_contract_hash (ds->contract_terms,
    525                                 &h_contract_terms))
    526   {
    527     GNUNET_break (0);
    528     TALER_TESTING_interpreter_fail (is);
    529     return;
    530   }
    531 
    532   ds->deposit_fee = ds->denom_pub->fees.deposit;
    533   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     GNUNET_assert (GNUNET_OK ==
    540                    TALER_JSON_merchant_wire_signature_hash (ds->wire_details,
    541                                                             &h_wire));
    542     TALER_wallet_deposit_sign (&ds->amount,
    543                                &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                                &ds->denom_pub->h_key,
    550                                ds->wallet_timestamp,
    551                                &ds->account_pub.merchant_pub,
    552                                ds->refund_deadline,
    553                                coin_priv,
    554                                &ds->coin_sig);
    555     ds->che.type = TALER_EXCHANGE_CTT_DEPOSIT;
    556     ds->che.amount = ds->amount;
    557     ds->che.details.deposit.h_wire = h_wire;
    558     ds->che.details.deposit.h_contract_terms = h_contract_terms;
    559     ds->che.details.deposit.no_h_policy = true;
    560     ds->che.details.deposit.no_wallet_data_hash = true;
    561     ds->che.details.deposit.wallet_timestamp = ds->wallet_timestamp;
    562     ds->che.details.deposit.merchant_pub = ds->account_pub.merchant_pub;
    563     ds->che.details.deposit.refund_deadline = ds->refund_deadline;
    564     ds->che.details.deposit.sig = ds->coin_sig;
    565     ds->che.details.deposit.no_hac = true;
    566     ds->che.details.deposit.deposit_fee = ds->denom_pub->fees.deposit;
    567   }
    568   GNUNET_assert (NULL == ds->dh);
    569   {
    570     struct TALER_EXCHANGE_CoinDepositDetail cdd = {
    571       .amount = ds->amount,
    572       .coin_pub = coin_pub,
    573       .coin_sig = ds->coin_sig,
    574       .denom_sig = *denom_pub_sig,
    575       .h_denom_pub = ds->denom_pub->h_key,
    576       .h_age_commitment = {{{0}}},
    577     };
    578     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     TALER_merchant_contract_sign (&h_contract_terms,
    589                                   &ds->account_priv.merchant_priv,
    590                                   &dcd.merchant_sig);
    591     if (NULL != phac)
    592       cdd.h_age_commitment = *phac;
    593 
    594     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   if (NULL == ds->dh)
    604   {
    605     GNUNET_break (0);
    606     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    607                 "Could not create deposit with EC %d\n",
    608                 (int) ec);
    609     TALER_TESTING_interpreter_fail (is);
    610     return;
    611   }
    612   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 deposit_cleanup (void *cls,
    627                  const struct TALER_TESTING_Command *cmd)
    628 {
    629   struct DepositState *ds = cls;
    630 
    631   if (NULL != ds->dh)
    632   {
    633     TALER_TESTING_command_incomplete (ds->is,
    634                                       cmd->label);
    635     TALER_EXCHANGE_post_batch_deposit_cancel (ds->dh);
    636     ds->dh = NULL;
    637   }
    638   if (NULL != ds->retry_task)
    639   {
    640     GNUNET_SCHEDULER_cancel (ds->retry_task);
    641     ds->retry_task = NULL;
    642   }
    643   json_decref (ds->wire_details);
    644   json_decref (ds->contract_terms);
    645   GNUNET_free (ds);
    646 }
    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 deposit_traits (void *cls,
    660                 const void **ret,
    661                 const char *trait,
    662                 unsigned int index)
    663 {
    664   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   const struct TALER_AgeCommitmentProof *age_commitment_proof=NULL;
    670   const struct TALER_AgeCommitmentHashP *h_age_commitment=NULL;
    671 
    672   if (! ds->command_initialized)
    673   {
    674     /* No access to traits yet. */
    675     GNUNET_break (0);
    676     return GNUNET_NO;
    677   }
    678 
    679   coin_cmd
    680     = TALER_TESTING_interpreter_lookup_command (ds->is,
    681                                                 ds->coin_reference);
    682   if (NULL == coin_cmd)
    683   {
    684     GNUNET_break (0);
    685     TALER_TESTING_interpreter_fail (ds->is);
    686     return GNUNET_NO;
    687   }
    688   if ( (GNUNET_OK !=
    689         TALER_TESTING_get_trait_coin_priv (coin_cmd,
    690                                            ds->coin_index,
    691                                            &coin_spent_priv)) ||
    692        (GNUNET_OK !=
    693         TALER_TESTING_get_trait_coin_pub (coin_cmd,
    694                                           ds->coin_index,
    695                                           &coin_spent_pub)) ||
    696        (GNUNET_OK !=
    697         TALER_TESTING_get_trait_age_commitment_proof (coin_cmd,
    698                                                       ds->coin_index,
    699                                                       &age_commitment_proof)) ||
    700        (GNUNET_OK !=
    701         TALER_TESTING_get_trait_h_age_commitment (coin_cmd,
    702                                                   ds->coin_index,
    703                                                   &h_age_commitment)) )
    704   {
    705     GNUNET_break (0);
    706     TALER_TESTING_interpreter_fail (ds->is);
    707     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       TALER_TESTING_make_trait_exchange_pub (0,
    715                                              &ds->exchange_pub),
    716       TALER_TESTING_make_trait_exchange_sig (0,
    717                                              &ds->exchange_sig),
    718       TALER_TESTING_make_trait_amount (&ds->cumulative_total),
    719       TALER_TESTING_make_trait_timestamp (0,
    720                                           &ds->exchange_timestamp),
    721       /* These traits are always available */
    722       TALER_TESTING_make_trait_coin_history (0,
    723                                              &ds->che),
    724       TALER_TESTING_make_trait_coin_priv (0,
    725                                           coin_spent_priv),
    726       TALER_TESTING_make_trait_coin_pub (0,
    727                                          coin_spent_pub),
    728       TALER_TESTING_make_trait_denom_pub (0,
    729                                           ds->denom_pub),
    730       TALER_TESTING_make_trait_coin_sig (0,
    731                                          &ds->coin_sig),
    732       TALER_TESTING_make_trait_age_commitment_proof (0,
    733                                                      age_commitment_proof),
    734       TALER_TESTING_make_trait_h_age_commitment (0,
    735                                                  h_age_commitment),
    736       TALER_TESTING_make_trait_wire_details (ds->wire_details),
    737       TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
    738       TALER_TESTING_make_trait_merchant_priv (&ds->account_priv.merchant_priv),
    739       TALER_TESTING_make_trait_merchant_pub (&ds->account_pub.merchant_pub),
    740       TALER_TESTING_make_trait_account_priv (&ds->account_priv),
    741       TALER_TESTING_make_trait_account_pub (&ds->account_pub),
    742       TALER_TESTING_make_trait_deposit_amount (0,
    743                                                &ds->amount),
    744       TALER_TESTING_make_trait_deposit_fee_amount (0,
    745                                                    &ds->deposit_fee),
    746       TALER_TESTING_make_trait_wire_deadline (0,
    747                                               &ds->wire_deadline),
    748       TALER_TESTING_make_trait_refund_deadline (0,
    749                                                 &ds->refund_deadline),
    750       TALER_TESTING_trait_end ()
    751     };
    752 
    753     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 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   ds = GNUNET_new (struct DepositState);
    777   ds->coin_reference = coin_reference;
    778   ds->coin_index = coin_index;
    779   ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto);
    780   GNUNET_assert (NULL != ds->wire_details);
    781   ds->contract_terms = json_loads (contract_terms,
    782                                    JSON_REJECT_DUPLICATES,
    783                                    NULL);
    784   if (NULL == ds->contract_terms)
    785   {
    786     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    787                 "Failed to parse contract terms `%s' for CMD `%s'\n",
    788                 contract_terms,
    789                 label);
    790     GNUNET_assert (0);
    791   }
    792   ds->wallet_timestamp = GNUNET_TIME_timestamp_get ();
    793   GNUNET_assert (0 ==
    794                  json_object_set_new (ds->contract_terms,
    795                                       "timestamp",
    796                                       GNUNET_JSON_from_timestamp (
    797                                         ds->wallet_timestamp)));
    798   if (! GNUNET_TIME_relative_is_zero (refund_deadline))
    799   {
    800     ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline);
    801     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   GNUNET_assert (GNUNET_OK ==
    808                  TALER_string_to_amount (amount,
    809                                          &ds->amount));
    810   ds->expected_response_code = expected_response_code;
    811   ds->command_initialized = true;
    812   {
    813     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     return cmd;
    822   }
    823 }
    824 
    825 
    826 struct TALER_TESTING_Command
    827 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   ds = GNUNET_new (struct DepositState);
    841   ds->merchant_priv_reference = merchant_priv_reference;
    842   ds->coin_reference = coin_reference;
    843   ds->coin_index = coin_index;
    844   ds->wire_details = TALER_TESTING_make_wire_details (target_account_payto);
    845   GNUNET_assert (NULL != ds->wire_details);
    846   ds->contract_terms = json_loads (contract_terms,
    847                                    JSON_REJECT_DUPLICATES,
    848                                    NULL);
    849   if (NULL == ds->contract_terms)
    850   {
    851     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    852                 "Failed to parse contract terms `%s' for CMD `%s'\n",
    853                 contract_terms,
    854                 label);
    855     GNUNET_assert (0);
    856   }
    857   ds->wallet_timestamp = GNUNET_TIME_timestamp_get ();
    858   GNUNET_assert (0 ==
    859                  json_object_set_new (ds->contract_terms,
    860                                       "timestamp",
    861                                       GNUNET_JSON_from_timestamp (
    862                                         ds->wallet_timestamp)));
    863   if (0 != refund_deadline.rel_value_us)
    864   {
    865     ds->refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_deadline);
    866     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   GNUNET_assert (GNUNET_OK ==
    873                  TALER_string_to_amount (amount,
    874                                          &ds->amount));
    875   ds->expected_response_code = expected_response_code;
    876   ds->command_initialized = true;
    877   {
    878     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     return cmd;
    887   }
    888 }
    889 
    890 
    891 struct TALER_TESTING_Command
    892 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   ds = GNUNET_new (struct DepositState);
    900   ds->deposit_reference = deposit_reference;
    901   ds->expected_response_code = expected_response_code;
    902   {
    903     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     return cmd;
    912   }
    913 }
    914 
    915 
    916 struct TALER_TESTING_Command
    917 TALER_TESTING_cmd_deposit_with_retry (struct TALER_TESTING_Command cmd)
    918 {
    919   struct DepositState *ds;
    920 
    921   GNUNET_assert (&deposit_run == cmd.run);
    922   ds = cmd.cls;
    923   ds->do_retry = NUM_RETRIES;
    924   return cmd;
    925 }
    926 
    927 
    928 /* end of testing_api_cmd_deposit.c */