LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_auditor_deposit_confirmation.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 59.3 % 118 70
Test Date: 2026-04-14 15:39:31 Functions: 66.7 % 6 4

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

Generated by: LCOV version 2.0-1