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-03-27 15:46:29 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              :   const struct TALER_Amount *cumulative_total;
     205            2 :   struct TALER_CoinSpendPublicKeyP coin_pubs[dcs->num_coins];
     206            2 :   const struct TALER_CoinSpendPublicKeyP *coin_pubps[dcs->num_coins];
     207            2 :   const struct TALER_CoinSpendSignatureP *coin_sigps[dcs->num_coins];
     208              :   const struct TALER_MerchantPrivateKeyP *merchant_priv;
     209              :   struct TALER_MerchantPublicKeyP merchant_pub;
     210              :   const struct TALER_ExchangePublicKeyP *exchange_pub;
     211              :   const struct TALER_ExchangeSignatureP *exchange_sig;
     212              :   const json_t *wire_details;
     213              :   const json_t *contract_terms;
     214              :   const struct TALER_EXCHANGE_Keys *keys;
     215              :   const struct TALER_EXCHANGE_SigningPublicKey *spk;
     216              :   const char *auditor_url;
     217              : 
     218              :   (void) cmd;
     219            2 :   dcs->is = is;
     220            2 :   GNUNET_assert (NULL != dcs->deposit_reference);
     221              :   {
     222              :     const struct TALER_TESTING_Command *auditor_cmd;
     223              : 
     224              :     auditor_cmd
     225            2 :       = TALER_TESTING_interpreter_get_command (is,
     226              :                                                "auditor");
     227            2 :     if (NULL == auditor_cmd)
     228              :     {
     229            0 :       GNUNET_break (0);
     230            0 :       TALER_TESTING_interpreter_fail (is);
     231            0 :       return;
     232              :     }
     233            2 :     if (GNUNET_OK !=
     234            2 :         TALER_TESTING_get_trait_auditor_url (auditor_cmd,
     235              :                                              &auditor_url))
     236              :     {
     237            0 :       GNUNET_break (0);
     238            0 :       TALER_TESTING_interpreter_fail (is);
     239            0 :       return;
     240              :     }
     241              :   }
     242              :   deposit_cmd
     243            2 :     = TALER_TESTING_interpreter_lookup_command (is,
     244              :                                                 dcs->deposit_reference);
     245            2 :   if (NULL == deposit_cmd)
     246              :   {
     247            0 :     GNUNET_break (0);
     248            0 :     TALER_TESTING_interpreter_fail (is);
     249            0 :     return;
     250              :   }
     251              : 
     252            2 :   GNUNET_assert (GNUNET_OK ==
     253              :                  TALER_TESTING_get_trait_exchange_pub (deposit_cmd,
     254              :                                                        0,
     255              :                                                        &exchange_pub));
     256            2 :   GNUNET_assert (GNUNET_OK ==
     257              :                  TALER_TESTING_get_trait_exchange_sig (deposit_cmd,
     258              :                                                        0,
     259              :                                                        &exchange_sig));
     260            2 :   GNUNET_assert (GNUNET_OK ==
     261              :                  TALER_TESTING_get_trait_amount (deposit_cmd,
     262              :                                                  &cumulative_total));
     263            2 :   GNUNET_assert (GNUNET_OK ==
     264              :                  TALER_TESTING_get_trait_timestamp (deposit_cmd,
     265              :                                                     0,
     266              :                                                     &exchange_timestamp));
     267            2 :   GNUNET_assert (GNUNET_OK ==
     268              :                  TALER_TESTING_get_trait_wire_deadline (deposit_cmd,
     269              :                                                         0,
     270              :                                                         &wire_deadline));
     271            2 :   GNUNET_assert (NULL != exchange_timestamp);
     272            2 :   keys = TALER_TESTING_get_keys (is);
     273            2 :   GNUNET_assert (NULL != keys);
     274            2 :   spk = TALER_EXCHANGE_get_signing_key_info (keys,
     275              :                                              exchange_pub);
     276              : 
     277            2 :   GNUNET_assert (GNUNET_OK ==
     278              :                  TALER_TESTING_get_trait_contract_terms (deposit_cmd,
     279              :                                                          &contract_terms));
     280              :   /* Very unlikely to fail */
     281            2 :   GNUNET_assert (NULL != contract_terms);
     282            2 :   GNUNET_assert (GNUNET_OK ==
     283              :                  TALER_JSON_contract_hash (contract_terms,
     284              :                                            &h_contract_terms));
     285            2 :   GNUNET_assert (GNUNET_OK ==
     286              :                  TALER_TESTING_get_trait_wire_details (deposit_cmd,
     287              :                                                        &wire_details));
     288            2 :   GNUNET_assert (GNUNET_OK ==
     289              :                  TALER_JSON_merchant_wire_signature_hash (wire_details,
     290              :                                                           &h_wire));
     291              : 
     292            4 :   for (unsigned int i = 0; i<dcs->num_coins; i++)
     293              :   {
     294              :     const struct TALER_CoinSpendPrivateKeyP *coin_priv;
     295              : 
     296            2 :     GNUNET_assert (GNUNET_OK ==
     297              :                    TALER_TESTING_get_trait_coin_priv (deposit_cmd,
     298              :                                                       i,
     299              :                                                       &coin_priv));
     300            2 :     GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
     301              :                                         &coin_pubs[i].eddsa_pub);
     302            2 :     coin_pubps[i] = &coin_pubs[i];
     303            2 :     GNUNET_assert (GNUNET_OK ==
     304              :                    TALER_TESTING_get_trait_coin_sig (deposit_cmd,
     305              :                                                      i,
     306              :                                                      &coin_sigps[i]));
     307              :   }
     308            2 :   GNUNET_assert (GNUNET_OK ==
     309              :                  TALER_TESTING_get_trait_merchant_priv (deposit_cmd,
     310              :                                                         &merchant_priv));
     311            2 :   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
     312              :                                       &merchant_pub.eddsa_pub);
     313            2 :   GNUNET_assert (GNUNET_OK ==
     314              :                  TALER_string_to_amount (dcs->amount_without_fee,
     315              :                                          &amount_without_fee));
     316              :   {
     317              :     struct GNUNET_JSON_Specification spec[] = {
     318              :       /* timestamp is mandatory */
     319            2 :       GNUNET_JSON_spec_timestamp ("timestamp",
     320              :                                   &timestamp),
     321            2 :       GNUNET_JSON_spec_mark_optional (
     322              :         GNUNET_JSON_spec_timestamp ("refund_deadline",
     323              :                                     &refund_deadline),
     324              :         NULL),
     325            2 :       GNUNET_JSON_spec_end ()
     326              :     };
     327              : 
     328            2 :     if (GNUNET_OK !=
     329            2 :         GNUNET_JSON_parse (contract_terms,
     330              :                            spec,
     331              :                            NULL, NULL))
     332              :     {
     333            0 :       GNUNET_break (0);
     334            0 :       TALER_TESTING_interpreter_fail (is);
     335            0 :       return;
     336              :     }
     337            2 :     if (GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time))
     338            2 :       refund_deadline = timestamp;
     339              :   }
     340            2 :   if (-1 ==
     341            2 :       TALER_amount_cmp (cumulative_total,
     342              :                         &amount_without_fee))
     343              :   {
     344              : 
     345              :     /* Cumulative must not below the amount we deposited this time */
     346            0 :     GNUNET_break (0);
     347            0 :     TALER_TESTING_interpreter_fail (is);
     348            0 :     return;
     349              :   }
     350            2 :   dcs->dc = TALER_AUDITOR_deposit_confirmation (
     351              :     TALER_TESTING_interpreter_get_context (is),
     352              :     auditor_url,
     353              :     &h_wire,
     354              :     &no_h_policy,
     355              :     &h_contract_terms,
     356              :     *exchange_timestamp,
     357              :     *wire_deadline,
     358              :     refund_deadline,
     359              :     cumulative_total,
     360              :     dcs->num_coins,
     361              :     coin_pubps,
     362              :     coin_sigps,
     363              :     &merchant_pub,
     364              :     exchange_pub,
     365              :     exchange_sig,
     366              :     &keys->master_pub,
     367              :     spk->valid_from,
     368              :     spk->valid_until,
     369              :     spk->valid_legal,
     370              :     &spk->master_sig,
     371              :     &deposit_confirmation_cb,
     372              :     dcs);
     373              : 
     374            2 :   if (NULL == dcs->dc)
     375              :   {
     376            0 :     GNUNET_break (0);
     377            0 :     TALER_TESTING_interpreter_fail (is);
     378            0 :     return;
     379              :   }
     380            2 :   return;
     381              : }
     382              : 
     383              : 
     384              : /**
     385              :  * Free the state of a "deposit_confirmation" CMD, and possibly cancel a
     386              :  * pending operation thereof.
     387              :  *
     388              :  * @param cls closure, a `struct DepositConfirmationState`
     389              :  * @param cmd the command which is being cleaned up.
     390              :  */
     391              : static void
     392            2 : deposit_confirmation_cleanup (void *cls,
     393              :                               const struct TALER_TESTING_Command *cmd)
     394              : {
     395            2 :   struct DepositConfirmationState *dcs = cls;
     396              : 
     397            2 :   if (NULL != dcs->dc)
     398              :   {
     399            0 :     TALER_TESTING_command_incomplete (dcs->is,
     400              :                                       cmd->label);
     401            0 :     TALER_AUDITOR_deposit_confirmation_cancel (dcs->dc);
     402            0 :     dcs->dc = NULL;
     403              :   }
     404            2 :   if (NULL != dcs->retry_task)
     405              :   {
     406            0 :     GNUNET_SCHEDULER_cancel (dcs->retry_task);
     407            0 :     dcs->retry_task = NULL;
     408              :   }
     409            2 :   GNUNET_free (dcs);
     410            2 : }
     411              : 
     412              : 
     413              : struct TALER_TESTING_Command
     414            2 : TALER_TESTING_cmd_deposit_confirmation (const char *label,
     415              :                                         const char *deposit_reference,
     416              :                                         unsigned int num_coins,
     417              :                                         const char *amount_without_fee,
     418              :                                         unsigned int expected_response_code)
     419              : {
     420              :   struct DepositConfirmationState *dcs;
     421              : 
     422            2 :   dcs = GNUNET_new (struct DepositConfirmationState);
     423            2 :   dcs->deposit_reference = deposit_reference;
     424            2 :   dcs->num_coins = num_coins;
     425            2 :   dcs->amount_without_fee = amount_without_fee;
     426            2 :   dcs->expected_response_code = expected_response_code;
     427              : 
     428              :   {
     429            2 :     struct TALER_TESTING_Command cmd = {
     430              :       .cls = dcs,
     431              :       .label = label,
     432              :       .run = &deposit_confirmation_run,
     433              :       .cleanup = &deposit_confirmation_cleanup
     434              :     };
     435              : 
     436            2 :     return cmd;
     437              :   }
     438              : }
     439              : 
     440              : 
     441              : struct TALER_TESTING_Command
     442            0 : TALER_TESTING_cmd_deposit_confirmation_with_retry (
     443              :   struct TALER_TESTING_Command cmd)
     444              : {
     445              :   struct DepositConfirmationState *dcs;
     446              : 
     447            0 :   GNUNET_assert (&deposit_confirmation_run == cmd.run);
     448            0 :   dcs = cmd.cls;
     449            0 :   dcs->do_retry = NUM_RETRIES;
     450            0 :   return cmd;
     451              : }
     452              : 
     453              : 
     454              : /* end of testing_auditor_api_cmd_deposit_confirmation.c */
        

Generated by: LCOV version 2.0-1