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_auditor_deposit_confirmation.c (13421B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2018-2023 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_auditor_deposit_confirmation.c
     21  * @brief command for testing /deposit_confirmation.
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/platform.h"
     25 #include "taler/taler_json_lib.h"
     26 #include <gnunet/gnunet_curl_lib.h>
     27 #include "taler/taler_auditor_service.h"
     28 #include "taler/taler_testing_lib.h"
     29 #include "taler/taler_signatures.h"
     30 #include "taler/backoff.h"
     31 
     32 /**
     33  * How long do we wait AT MOST when retrying?
     34  */
     35 #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
     36           GNUNET_TIME_UNIT_MILLISECONDS, 100)
     37 
     38 /**
     39  * How often do we retry before giving up?
     40  */
     41 #define NUM_RETRIES 5
     42 
     43 
     44 /**
     45  * State for a "deposit confirmation" CMD.
     46  */
     47 struct DepositConfirmationState
     48 {
     49 
     50   /**
     51    * Reference to any command that is able to provide a deposit.
     52    */
     53   const char *deposit_reference;
     54 
     55   /**
     56    * What is the deposited amount without the fee (i.e. the
     57    * amount we expect in the deposit confirmation)?
     58    */
     59   const char *amount_without_fee;
     60 
     61   /**
     62    * How many coins were there in the @e deposit_reference?
     63    */
     64   unsigned int num_coins;
     65 
     66   /**
     67    * DepositConfirmation handle while operation is running.
     68    */
     69   struct TALER_AUDITOR_DepositConfirmationHandle *dc;
     70 
     71   /**
     72    * Interpreter state.
     73    */
     74   struct TALER_TESTING_Interpreter *is;
     75 
     76   /**
     77    * Task scheduled to try later.
     78    */
     79   struct GNUNET_SCHEDULER_Task *retry_task;
     80 
     81   /**
     82    * How long do we wait until we retry?
     83    */
     84   struct GNUNET_TIME_Relative backoff;
     85 
     86   /**
     87    * Expected HTTP response code.
     88    */
     89   unsigned int expected_response_code;
     90 
     91   /**
     92    * How often should we retry on (transient) failures?
     93    */
     94   unsigned int do_retry;
     95 
     96 };
     97 
     98 
     99 /**
    100  * Run the command.
    101  *
    102  * @param cls closure.
    103  * @param cmd the command to execute.
    104  * @param is the interpreter state.
    105  */
    106 static void
    107 deposit_confirmation_run (void *cls,
    108                           const struct TALER_TESTING_Command *cmd,
    109                           struct TALER_TESTING_Interpreter *is);
    110 
    111 
    112 /**
    113  * Task scheduled to re-try #deposit_confirmation_run.
    114  *
    115  * @param cls a `struct DepositConfirmationState`
    116  */
    117 static void
    118 do_retry (void *cls)
    119 {
    120   struct DepositConfirmationState *dcs = cls;
    121 
    122   dcs->retry_task = NULL;
    123   TALER_TESTING_touch_cmd (dcs->is);
    124   deposit_confirmation_run (dcs,
    125                             NULL,
    126                             dcs->is);
    127 }
    128 
    129 
    130 /**
    131  * Callback to analyze the /deposit-confirmation response, just used
    132  * to check if the response code is acceptable.
    133  *
    134  * @param cls closure.
    135  * @param dcr response details
    136  */
    137 static void
    138 deposit_confirmation_cb (
    139   void *cls,
    140   const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
    141 {
    142   struct DepositConfirmationState *dcs = cls;
    143   const struct TALER_AUDITOR_HttpResponse *hr = &dcr->hr;
    144 
    145   dcs->dc = NULL;
    146   if (dcs->expected_response_code != hr->http_status)
    147   {
    148     if (0 != dcs->do_retry)
    149     {
    150       dcs->do_retry--;
    151       if ( (0 == hr->http_status) ||
    152            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
    153            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
    154       {
    155         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    156                     "Retrying deposit confirmation failed with %u/%d\n",
    157                     hr->http_status,
    158                     (int) hr->ec);
    159         /* on DB conflicts, do not use backoff */
    160         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
    161           dcs->backoff = GNUNET_TIME_UNIT_ZERO;
    162         else
    163           dcs->backoff = GNUNET_TIME_randomized_backoff (dcs->backoff,
    164                                                          MAX_BACKOFF);
    165         TALER_TESTING_inc_tries (dcs->is);
    166         dcs->retry_task = GNUNET_SCHEDULER_add_delayed (dcs->backoff,
    167                                                         &do_retry,
    168                                                         dcs);
    169         return;
    170       }
    171     }
    172     TALER_TESTING_unexpected_status (dcs->is,
    173                                      hr->http_status,
    174                                      dcs->expected_response_code);
    175     return;
    176   }
    177   TALER_TESTING_interpreter_next (dcs->is);
    178 }
    179 
    180 
    181 /**
    182  * Run the command.
    183  *
    184  * @param cls closure.
    185  * @param cmd the command to execute.
    186  * @param is the interpreter state.
    187  */
    188 static void
    189 deposit_confirmation_run (void *cls,
    190                           const struct TALER_TESTING_Command *cmd,
    191                           struct TALER_TESTING_Interpreter *is)
    192 {
    193   static struct TALER_ExtensionPolicyHashP no_h_policy;
    194   struct DepositConfirmationState *dcs = cls;
    195   const struct TALER_TESTING_Command *deposit_cmd;
    196   struct TALER_MerchantWireHashP h_wire;
    197   struct TALER_PrivateContractHashP h_contract_terms;
    198   const struct GNUNET_TIME_Timestamp *exchange_timestamp = NULL;
    199   struct GNUNET_TIME_Timestamp timestamp;
    200   const struct GNUNET_TIME_Timestamp *wire_deadline;
    201   struct GNUNET_TIME_Timestamp refund_deadline
    202     = GNUNET_TIME_UNIT_ZERO_TS;
    203   struct TALER_Amount amount_without_fee;
    204   struct TALER_CoinSpendPublicKeyP coin_pubs[dcs->num_coins];
    205   const struct TALER_CoinSpendPublicKeyP *coin_pubps[dcs->num_coins];
    206   const struct TALER_CoinSpendSignatureP *coin_sigps[dcs->num_coins];
    207   const struct TALER_MerchantPrivateKeyP *merchant_priv;
    208   struct TALER_MerchantPublicKeyP merchant_pub;
    209   const struct TALER_ExchangePublicKeyP *exchange_pub;
    210   const struct TALER_ExchangeSignatureP *exchange_sig;
    211   const json_t *wire_details;
    212   const json_t *contract_terms;
    213   const struct TALER_EXCHANGE_Keys *keys;
    214   const struct TALER_EXCHANGE_SigningPublicKey *spk;
    215   const char *auditor_url;
    216 
    217   (void) cmd;
    218   dcs->is = is;
    219   GNUNET_assert (NULL != dcs->deposit_reference);
    220   {
    221     const struct TALER_TESTING_Command *auditor_cmd;
    222 
    223     auditor_cmd
    224       = TALER_TESTING_interpreter_get_command (is,
    225                                                "auditor");
    226     if (NULL == auditor_cmd)
    227     {
    228       GNUNET_break (0);
    229       TALER_TESTING_interpreter_fail (is);
    230       return;
    231     }
    232     if (GNUNET_OK !=
    233         TALER_TESTING_get_trait_auditor_url (auditor_cmd,
    234                                              &auditor_url))
    235     {
    236       GNUNET_break (0);
    237       TALER_TESTING_interpreter_fail (is);
    238       return;
    239     }
    240   }
    241   deposit_cmd
    242     = TALER_TESTING_interpreter_lookup_command (is,
    243                                                 dcs->deposit_reference);
    244   if (NULL == deposit_cmd)
    245   {
    246     GNUNET_break (0);
    247     TALER_TESTING_interpreter_fail (is);
    248     return;
    249   }
    250 
    251   GNUNET_assert (GNUNET_OK ==
    252                  TALER_TESTING_get_trait_exchange_pub (deposit_cmd,
    253                                                        0,
    254                                                        &exchange_pub));
    255   GNUNET_assert (GNUNET_OK ==
    256                  TALER_TESTING_get_trait_exchange_sig (deposit_cmd,
    257                                                        0,
    258                                                        &exchange_sig));
    259   GNUNET_assert (GNUNET_OK ==
    260                  TALER_TESTING_get_trait_timestamp (deposit_cmd,
    261                                                     0,
    262                                                     &exchange_timestamp));
    263   GNUNET_assert (GNUNET_OK ==
    264                  TALER_TESTING_get_trait_wire_deadline (deposit_cmd,
    265                                                         0,
    266                                                         &wire_deadline));
    267   GNUNET_assert (NULL != exchange_timestamp);
    268   keys = TALER_TESTING_get_keys (is);
    269   GNUNET_assert (NULL != keys);
    270   spk = TALER_EXCHANGE_get_signing_key_info (keys,
    271                                              exchange_pub);
    272 
    273   GNUNET_assert (GNUNET_OK ==
    274                  TALER_TESTING_get_trait_contract_terms (deposit_cmd,
    275                                                          &contract_terms));
    276   /* Very unlikely to fail */
    277   GNUNET_assert (NULL != contract_terms);
    278   GNUNET_assert (GNUNET_OK ==
    279                  TALER_JSON_contract_hash (contract_terms,
    280                                            &h_contract_terms));
    281   GNUNET_assert (GNUNET_OK ==
    282                  TALER_TESTING_get_trait_wire_details (deposit_cmd,
    283                                                        &wire_details));
    284   GNUNET_assert (GNUNET_OK ==
    285                  TALER_JSON_merchant_wire_signature_hash (wire_details,
    286                                                           &h_wire));
    287 
    288   for (unsigned int i = 0; i<dcs->num_coins; i++)
    289   {
    290     const struct TALER_CoinSpendPrivateKeyP *coin_priv;
    291 
    292     GNUNET_assert (GNUNET_OK ==
    293                    TALER_TESTING_get_trait_coin_priv (deposit_cmd,
    294                                                       i,
    295                                                       &coin_priv));
    296     GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
    297                                         &coin_pubs[i].eddsa_pub);
    298     coin_pubps[i] = &coin_pubs[i];
    299     GNUNET_assert (GNUNET_OK ==
    300                    TALER_TESTING_get_trait_coin_sig (deposit_cmd,
    301                                                      i,
    302                                                      &coin_sigps[i]));
    303   }
    304   GNUNET_assert (GNUNET_OK ==
    305                  TALER_TESTING_get_trait_merchant_priv (deposit_cmd,
    306                                                         &merchant_priv));
    307   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
    308                                       &merchant_pub.eddsa_pub);
    309   GNUNET_assert (GNUNET_OK ==
    310                  TALER_string_to_amount (dcs->amount_without_fee,
    311                                          &amount_without_fee));
    312   {
    313     struct GNUNET_JSON_Specification spec[] = {
    314       /* timestamp is mandatory */
    315       GNUNET_JSON_spec_timestamp ("timestamp",
    316                                   &timestamp),
    317       GNUNET_JSON_spec_mark_optional (
    318         GNUNET_JSON_spec_timestamp ("refund_deadline",
    319                                     &refund_deadline),
    320         NULL),
    321       GNUNET_JSON_spec_end ()
    322     };
    323 
    324     if (GNUNET_OK !=
    325         GNUNET_JSON_parse (contract_terms,
    326                            spec,
    327                            NULL, NULL))
    328     {
    329       GNUNET_break (0);
    330       TALER_TESTING_interpreter_fail (is);
    331       return;
    332     }
    333     if (GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time))
    334       refund_deadline = timestamp;
    335   }
    336   dcs->dc = TALER_AUDITOR_deposit_confirmation (
    337     TALER_TESTING_interpreter_get_context (is),
    338     auditor_url,
    339     &h_wire,
    340     &no_h_policy,
    341     &h_contract_terms,
    342     *exchange_timestamp,
    343     *wire_deadline,
    344     refund_deadline,
    345     &amount_without_fee,
    346     dcs->num_coins,
    347     coin_pubps,
    348     coin_sigps,
    349     &merchant_pub,
    350     exchange_pub,
    351     exchange_sig,
    352     &keys->master_pub,
    353     spk->valid_from,
    354     spk->valid_until,
    355     spk->valid_legal,
    356     &spk->master_sig,
    357     &deposit_confirmation_cb,
    358     dcs);
    359 
    360   if (NULL == dcs->dc)
    361   {
    362     GNUNET_break (0);
    363     TALER_TESTING_interpreter_fail (is);
    364     return;
    365   }
    366   return;
    367 }
    368 
    369 
    370 /**
    371  * Free the state of a "deposit_confirmation" CMD, and possibly cancel a
    372  * pending operation thereof.
    373  *
    374  * @param cls closure, a `struct DepositConfirmationState`
    375  * @param cmd the command which is being cleaned up.
    376  */
    377 static void
    378 deposit_confirmation_cleanup (void *cls,
    379                               const struct TALER_TESTING_Command *cmd)
    380 {
    381   struct DepositConfirmationState *dcs = cls;
    382 
    383   if (NULL != dcs->dc)
    384   {
    385     TALER_TESTING_command_incomplete (dcs->is,
    386                                       cmd->label);
    387     TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc);
    388     dcs->dc = NULL;
    389   }
    390   if (NULL != dcs->retry_task)
    391   {
    392     GNUNET_SCHEDULER_cancel (dcs->retry_task);
    393     dcs->retry_task = NULL;
    394   }
    395   GNUNET_free (dcs);
    396 }
    397 
    398 
    399 struct TALER_TESTING_Command
    400 TALER_TESTING_cmd_deposit_confirmation (const char *label,
    401                                         const char *deposit_reference,
    402                                         unsigned int num_coins,
    403                                         const char *amount_without_fee,
    404                                         unsigned int expected_response_code)
    405 {
    406   struct DepositConfirmationState *dcs;
    407 
    408   dcs = GNUNET_new (struct DepositConfirmationState);
    409   dcs->deposit_reference = deposit_reference;
    410   dcs->num_coins = num_coins;
    411   dcs->amount_without_fee = amount_without_fee;
    412   dcs->expected_response_code = expected_response_code;
    413 
    414   {
    415     struct TALER_TESTING_Command cmd = {
    416       .cls = dcs,
    417       .label = label,
    418       .run = &deposit_confirmation_run,
    419       .cleanup = &deposit_confirmation_cleanup
    420     };
    421 
    422     return cmd;
    423   }
    424 }
    425 
    426 
    427 struct TALER_TESTING_Command
    428 TALER_TESTING_cmd_deposit_confirmation_with_retry (
    429   struct TALER_TESTING_Command cmd)
    430 {
    431   struct DepositConfirmationState *dcs;
    432 
    433   GNUNET_assert (&deposit_confirmation_run == cmd.run);
    434   dcs = cmd.cls;
    435   dcs->do_retry = NUM_RETRIES;
    436   return cmd;
    437 }
    438 
    439 
    440 /* end of testing_auditor_api_cmd_deposit_confirmation.c */