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.8 % 112 67
Test Date: 2026-01-18 12:54: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/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            0 : do_retry (void *cls)
     119              : {
     120            0 :   struct DepositConfirmationState *dcs = cls;
     121              : 
     122            0 :   dcs->retry_task = NULL;
     123            0 :   TALER_TESTING_touch_cmd (dcs->is);
     124            0 :   deposit_confirmation_run (dcs,
     125              :                             NULL,
     126              :                             dcs->is);
     127            0 : }
     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            2 : deposit_confirmation_cb (
     139              :   void *cls,
     140              :   const struct TALER_AUDITOR_DepositConfirmationResponse *dcr)
     141              : {
     142            2 :   struct DepositConfirmationState *dcs = cls;
     143            2 :   const struct TALER_AUDITOR_HttpResponse *hr = &dcr->hr;
     144              : 
     145            2 :   dcs->dc = NULL;
     146            2 :   if (dcs->expected_response_code != hr->http_status)
     147              :   {
     148            0 :     if (0 != dcs->do_retry)
     149              :     {
     150            0 :       dcs->do_retry--;
     151            0 :       if ( (0 == hr->http_status) ||
     152            0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
     153            0 :            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
     154              :       {
     155            0 :         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            0 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
     161            0 :           dcs->backoff = GNUNET_TIME_UNIT_ZERO;
     162              :         else
     163            0 :           dcs->backoff = GNUNET_TIME_randomized_backoff (dcs->backoff,
     164              :                                                          MAX_BACKOFF);
     165            0 :         TALER_TESTING_inc_tries (dcs->is);
     166            0 :         dcs->retry_task = GNUNET_SCHEDULER_add_delayed (dcs->backoff,
     167              :                                                         &do_retry,
     168              :                                                         dcs);
     169            0 :         return;
     170              :       }
     171              :     }
     172            0 :     TALER_TESTING_unexpected_status (dcs->is,
     173              :                                      hr->http_status,
     174              :                                      dcs->expected_response_code);
     175            0 :     return;
     176              :   }
     177            2 :   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            2 : deposit_confirmation_run (void *cls,
     190              :                           const struct TALER_TESTING_Command *cmd,
     191              :                           struct TALER_TESTING_Interpreter *is)
     192            2 : {
     193              :   static struct TALER_ExtensionPolicyHashP no_h_policy;
     194            2 :   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            2 :   const struct GNUNET_TIME_Timestamp *exchange_timestamp = NULL;
     199              :   struct GNUNET_TIME_Timestamp timestamp;
     200              :   const struct GNUNET_TIME_Timestamp *wire_deadline;
     201            2 :   struct GNUNET_TIME_Timestamp refund_deadline
     202              :     = GNUNET_TIME_UNIT_ZERO_TS;
     203              :   struct TALER_Amount amount_without_fee;
     204            2 :   struct TALER_CoinSpendPublicKeyP coin_pubs[dcs->num_coins];
     205            2 :   const struct TALER_CoinSpendPublicKeyP *coin_pubps[dcs->num_coins];
     206            2 :   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            2 :   dcs->is = is;
     219            2 :   GNUNET_assert (NULL != dcs->deposit_reference);
     220              :   {
     221              :     const struct TALER_TESTING_Command *auditor_cmd;
     222              : 
     223              :     auditor_cmd
     224            2 :       = TALER_TESTING_interpreter_get_command (is,
     225              :                                                "auditor");
     226            2 :     if (NULL == auditor_cmd)
     227              :     {
     228            0 :       GNUNET_break (0);
     229            0 :       TALER_TESTING_interpreter_fail (is);
     230            0 :       return;
     231              :     }
     232            2 :     if (GNUNET_OK !=
     233            2 :         TALER_TESTING_get_trait_auditor_url (auditor_cmd,
     234              :                                              &auditor_url))
     235              :     {
     236            0 :       GNUNET_break (0);
     237            0 :       TALER_TESTING_interpreter_fail (is);
     238            0 :       return;
     239              :     }
     240              :   }
     241              :   deposit_cmd
     242            2 :     = TALER_TESTING_interpreter_lookup_command (is,
     243              :                                                 dcs->deposit_reference);
     244            2 :   if (NULL == deposit_cmd)
     245              :   {
     246            0 :     GNUNET_break (0);
     247            0 :     TALER_TESTING_interpreter_fail (is);
     248            0 :     return;
     249              :   }
     250              : 
     251            2 :   GNUNET_assert (GNUNET_OK ==
     252              :                  TALER_TESTING_get_trait_exchange_pub (deposit_cmd,
     253              :                                                        0,
     254              :                                                        &exchange_pub));
     255            2 :   GNUNET_assert (GNUNET_OK ==
     256              :                  TALER_TESTING_get_trait_exchange_sig (deposit_cmd,
     257              :                                                        0,
     258              :                                                        &exchange_sig));
     259            2 :   GNUNET_assert (GNUNET_OK ==
     260              :                  TALER_TESTING_get_trait_timestamp (deposit_cmd,
     261              :                                                     0,
     262              :                                                     &exchange_timestamp));
     263            2 :   GNUNET_assert (GNUNET_OK ==
     264              :                  TALER_TESTING_get_trait_wire_deadline (deposit_cmd,
     265              :                                                         0,
     266              :                                                         &wire_deadline));
     267            2 :   GNUNET_assert (NULL != exchange_timestamp);
     268            2 :   keys = TALER_TESTING_get_keys (is);
     269            2 :   GNUNET_assert (NULL != keys);
     270            2 :   spk = TALER_EXCHANGE_get_signing_key_info (keys,
     271              :                                              exchange_pub);
     272              : 
     273            2 :   GNUNET_assert (GNUNET_OK ==
     274              :                  TALER_TESTING_get_trait_contract_terms (deposit_cmd,
     275              :                                                          &contract_terms));
     276              :   /* Very unlikely to fail */
     277            2 :   GNUNET_assert (NULL != contract_terms);
     278            2 :   GNUNET_assert (GNUNET_OK ==
     279              :                  TALER_JSON_contract_hash (contract_terms,
     280              :                                            &h_contract_terms));
     281            2 :   GNUNET_assert (GNUNET_OK ==
     282              :                  TALER_TESTING_get_trait_wire_details (deposit_cmd,
     283              :                                                        &wire_details));
     284            2 :   GNUNET_assert (GNUNET_OK ==
     285              :                  TALER_JSON_merchant_wire_signature_hash (wire_details,
     286              :                                                           &h_wire));
     287              : 
     288            4 :   for (unsigned int i = 0; i<dcs->num_coins; i++)
     289              :   {
     290              :     const struct TALER_CoinSpendPrivateKeyP *coin_priv;
     291              : 
     292            2 :     GNUNET_assert (GNUNET_OK ==
     293              :                    TALER_TESTING_get_trait_coin_priv (deposit_cmd,
     294              :                                                       i,
     295              :                                                       &coin_priv));
     296            2 :     GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
     297              :                                         &coin_pubs[i].eddsa_pub);
     298            2 :     coin_pubps[i] = &coin_pubs[i];
     299            2 :     GNUNET_assert (GNUNET_OK ==
     300              :                    TALER_TESTING_get_trait_coin_sig (deposit_cmd,
     301              :                                                      i,
     302              :                                                      &coin_sigps[i]));
     303              :   }
     304            2 :   GNUNET_assert (GNUNET_OK ==
     305              :                  TALER_TESTING_get_trait_merchant_priv (deposit_cmd,
     306              :                                                         &merchant_priv));
     307            2 :   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
     308              :                                       &merchant_pub.eddsa_pub);
     309            2 :   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            2 :       GNUNET_JSON_spec_timestamp ("timestamp",
     316              :                                   &timestamp),
     317            2 :       GNUNET_JSON_spec_mark_optional (
     318              :         GNUNET_JSON_spec_timestamp ("refund_deadline",
     319              :                                     &refund_deadline),
     320              :         NULL),
     321            2 :       GNUNET_JSON_spec_end ()
     322              :     };
     323              : 
     324            2 :     if (GNUNET_OK !=
     325            2 :         GNUNET_JSON_parse (contract_terms,
     326              :                            spec,
     327              :                            NULL, NULL))
     328              :     {
     329            0 :       GNUNET_break (0);
     330            0 :       TALER_TESTING_interpreter_fail (is);
     331            0 :       return;
     332              :     }
     333            2 :     if (GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time))
     334            2 :       refund_deadline = timestamp;
     335              :   }
     336            2 :   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            2 :   if (NULL == dcs->dc)
     361              :   {
     362            0 :     GNUNET_break (0);
     363            0 :     TALER_TESTING_interpreter_fail (is);
     364            0 :     return;
     365              :   }
     366            2 :   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            2 : deposit_confirmation_cleanup (void *cls,
     379              :                               const struct TALER_TESTING_Command *cmd)
     380              : {
     381            2 :   struct DepositConfirmationState *dcs = cls;
     382              : 
     383            2 :   if (NULL != dcs->dc)
     384              :   {
     385            0 :     TALER_TESTING_command_incomplete (dcs->is,
     386              :                                       cmd->label);
     387            0 :     TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc);
     388            0 :     dcs->dc = NULL;
     389              :   }
     390            2 :   if (NULL != dcs->retry_task)
     391              :   {
     392            0 :     GNUNET_SCHEDULER_cancel (dcs->retry_task);
     393            0 :     dcs->retry_task = NULL;
     394              :   }
     395            2 :   GNUNET_free (dcs);
     396            2 : }
     397              : 
     398              : 
     399              : struct TALER_TESTING_Command
     400            2 : 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            2 :   dcs = GNUNET_new (struct DepositConfirmationState);
     409            2 :   dcs->deposit_reference = deposit_reference;
     410            2 :   dcs->num_coins = num_coins;
     411            2 :   dcs->amount_without_fee = amount_without_fee;
     412            2 :   dcs->expected_response_code = expected_response_code;
     413              : 
     414              :   {
     415            2 :     struct TALER_TESTING_Command cmd = {
     416              :       .cls = dcs,
     417              :       .label = label,
     418              :       .run = &deposit_confirmation_run,
     419              :       .cleanup = &deposit_confirmation_cleanup
     420              :     };
     421              : 
     422            2 :     return cmd;
     423              :   }
     424              : }
     425              : 
     426              : 
     427              : struct TALER_TESTING_Command
     428            0 : TALER_TESTING_cmd_deposit_confirmation_with_retry (
     429              :   struct TALER_TESTING_Command cmd)
     430              : {
     431              :   struct DepositConfirmationState *dcs;
     432              : 
     433            0 :   GNUNET_assert (&deposit_confirmation_run == cmd.run);
     434            0 :   dcs = cmd.cls;
     435            0 :   dcs->do_retry = NUM_RETRIES;
     436            0 :   return cmd;
     437              : }
     438              : 
     439              : 
     440              : /* end of testing_auditor_api_cmd_deposit_confirmation.c */
        

Generated by: LCOV version 2.0-1