LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_auditor_deposit_confirmation.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 67 112 59.8 %
Date: 2025-06-22 12:09:43 Functions: 4 6 66.7 %

          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 1.16