LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_bank_transfer.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 71 87 81.6 %
Date: 2022-08-25 06:15:09 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2018-2021 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_bank_transfer.c
      21             :  * @brief implementation of a bank /transfer command
      22             :  * @author Christian Grothoff
      23             :  * @author Marcello Stanisci
      24             :  */
      25             : #include "platform.h"
      26             : #include "backoff.h"
      27             : #include "taler_json_lib.h"
      28             : #include <gnunet/gnunet_curl_lib.h>
      29             : #include "taler_bank_service.h"
      30             : #include "taler_fakebank_lib.h"
      31             : #include "taler_signatures.h"
      32             : #include "taler_testing_lib.h"
      33             : 
      34             : 
      35             : /**
      36             :  * How often do we retry before giving up?
      37             :  */
      38             : #define NUM_RETRIES 5
      39             : 
      40             : 
      41             : /**
      42             :  * State for a "transfer" CMD.
      43             :  */
      44             : struct TransferState
      45             : {
      46             : 
      47             :   /**
      48             :    * Wire transfer amount.
      49             :    */
      50             :   struct TALER_Amount amount;
      51             : 
      52             :   /**
      53             :    * Base URL of the debit account.
      54             :    */
      55             :   const char *account_debit_url;
      56             : 
      57             :   /**
      58             :    * Money receiver payto URL.
      59             :    */
      60             :   char *payto_debit_account;
      61             : 
      62             :   /**
      63             :    * Money receiver account URL.
      64             :    */
      65             :   const char *payto_credit_account;
      66             : 
      67             :   /**
      68             :    * Username to use for authentication.
      69             :    */
      70             :   struct TALER_BANK_AuthenticationData auth;
      71             : 
      72             :   /**
      73             :    * Base URL of the exchange.
      74             :    */
      75             :   const char *exchange_base_url;
      76             : 
      77             :   /**
      78             :    * Wire transfer identifier to use.
      79             :    */
      80             :   struct TALER_WireTransferIdentifierRawP wtid;
      81             : 
      82             :   /**
      83             :    * Handle to the pending request at the fakebank.
      84             :    */
      85             :   struct TALER_BANK_TransferHandle *weh;
      86             : 
      87             :   /**
      88             :    * Interpreter state.
      89             :    */
      90             :   struct TALER_TESTING_Interpreter *is;
      91             : 
      92             :   /**
      93             :    * Set to the wire transfer's unique ID.
      94             :    */
      95             :   uint64_t serial_id;
      96             : 
      97             :   /**
      98             :    * Timestamp of the transaction (as returned from the bank).
      99             :    */
     100             :   struct GNUNET_TIME_Timestamp timestamp;
     101             : 
     102             :   /**
     103             :    * Configuration filename.  Used to get the tip reserve key
     104             :    * filename (used to obtain a public key to write in the
     105             :    * transfer subject).
     106             :    */
     107             :   const char *config_filename;
     108             : 
     109             :   /**
     110             :    * Task scheduled to try later.
     111             :    */
     112             :   struct GNUNET_SCHEDULER_Task *retry_task;
     113             : 
     114             :   /**
     115             :    * How long do we wait until we retry?
     116             :    */
     117             :   struct GNUNET_TIME_Relative backoff;
     118             : 
     119             :   /**
     120             :    * Was this command modified via
     121             :    * #TALER_TESTING_cmd_admin_add_incoming_with_retry to
     122             :    * enable retries? If so, how often should we still retry?
     123             :    */
     124             :   unsigned int do_retry;
     125             : };
     126             : 
     127             : 
     128             : /**
     129             :  * Run the "transfer" CMD.
     130             :  *
     131             :  * @param cls closure.
     132             :  * @param cmd CMD being run.
     133             :  * @param is interpreter state.
     134             :  */
     135             : static void
     136             : transfer_run (void *cls,
     137             :               const struct TALER_TESTING_Command *cmd,
     138             :               struct TALER_TESTING_Interpreter *is);
     139             : 
     140             : 
     141             : /**
     142             :  * Task scheduled to re-try #transfer_run.
     143             :  *
     144             :  * @param cls a `struct TransferState`
     145             :  */
     146             : static void
     147           1 : do_retry (void *cls)
     148             : {
     149           1 :   struct TransferState *fts = cls;
     150             : 
     151           1 :   fts->retry_task = NULL;
     152           1 :   fts->is->commands[fts->is->ip].last_req_time
     153           1 :     = GNUNET_TIME_absolute_get ();
     154           1 :   transfer_run (fts,
     155             :                 NULL,
     156             :                 fts->is);
     157           1 : }
     158             : 
     159             : 
     160             : /**
     161             :  * This callback will process the fakebank response to the wire
     162             :  * transfer.  It just checks whether the HTTP response code is
     163             :  * acceptable.
     164             :  *
     165             :  * @param cls closure with the interpreter state
     166             :  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
     167             :  *        successful status request; 0 if the exchange's reply is
     168             :  *        bogus (fails to follow the protocol)
     169             :  * @param ec taler-specific error code, #TALER_EC_NONE on success
     170             :  * @param serial_id unique ID of the wire transfer
     171             :  * @param timestamp time stamp of the transaction made.
     172             :  */
     173             : static void
     174           4 : confirmation_cb (void *cls,
     175             :                  unsigned int http_status,
     176             :                  enum TALER_ErrorCode ec,
     177             :                  uint64_t serial_id,
     178             :                  struct GNUNET_TIME_Timestamp timestamp)
     179             : {
     180           4 :   struct TransferState *fts = cls;
     181           4 :   struct TALER_TESTING_Interpreter *is = fts->is;
     182             : 
     183           4 :   fts->weh = NULL;
     184           4 :   if (MHD_HTTP_OK != http_status)
     185             :   {
     186           1 :     if (0 != fts->do_retry)
     187             :     {
     188           1 :       fts->do_retry--;
     189           1 :       if ( (0 == http_status) ||
     190           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) ||
     191             :            (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) )
     192             :       {
     193           1 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     194             :                     "Retrying transfer failed with %u/%d\n",
     195             :                     http_status,
     196             :                     (int) ec);
     197             :         /* on DB conflicts, do not use backoff */
     198           1 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec)
     199           0 :           fts->backoff = GNUNET_TIME_UNIT_ZERO;
     200             :         else
     201           1 :           fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
     202           1 :         fts->is->commands[fts->is->ip].num_tries++;
     203             :         fts->retry_task
     204           1 :           = GNUNET_SCHEDULER_add_delayed (fts->backoff,
     205             :                                           &do_retry,
     206             :                                           fts);
     207           1 :         return;
     208             :       }
     209             :     }
     210           0 :     GNUNET_break (0);
     211           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     212             :                 "Bank returned HTTP status %u/%d\n",
     213             :                 http_status,
     214             :                 (int) ec);
     215           0 :     TALER_TESTING_interpreter_fail (is);
     216           0 :     return;
     217             :   }
     218             : 
     219           3 :   fts->serial_id = serial_id;
     220           3 :   fts->timestamp = timestamp;
     221           3 :   TALER_TESTING_interpreter_next (is);
     222             : }
     223             : 
     224             : 
     225             : /**
     226             :  * Run the "transfer" CMD.
     227             :  *
     228             :  * @param cls closure.
     229             :  * @param cmd CMD being run.
     230             :  * @param is interpreter state.
     231             :  */
     232             : static void
     233           4 : transfer_run (void *cls,
     234             :               const struct TALER_TESTING_Command *cmd,
     235             :               struct TALER_TESTING_Interpreter *is)
     236             : {
     237           4 :   struct TransferState *fts = cls;
     238             :   void *buf;
     239             :   size_t buf_size;
     240             : 
     241             :   (void) cmd;
     242           4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     243             :               "Transfer of %s from %s to %s\n",
     244             :               TALER_amount2s (&fts->amount),
     245             :               fts->account_debit_url,
     246             :               fts->payto_credit_account);
     247           4 :   TALER_BANK_prepare_transfer (fts->payto_credit_account,
     248           4 :                                &fts->amount,
     249             :                                fts->exchange_base_url,
     250           4 :                                &fts->wtid,
     251             :                                &buf,
     252             :                                &buf_size);
     253           4 :   fts->is = is;
     254             :   fts->weh
     255           4 :     = TALER_BANK_transfer (
     256             :         TALER_TESTING_interpreter_get_context (is),
     257           4 :         &fts->auth,
     258             :         buf,
     259             :         buf_size,
     260             :         &confirmation_cb,
     261             :         fts);
     262           4 :   GNUNET_free (buf);
     263           4 :   if (NULL == fts->weh)
     264             :   {
     265           0 :     GNUNET_break (0);
     266           0 :     TALER_TESTING_interpreter_fail (is);
     267           0 :     return;
     268             :   }
     269             : }
     270             : 
     271             : 
     272             : /**
     273             :  * Free the state of a "fakebank transfer" CMD, and possibly
     274             :  * cancel a pending operation thereof.
     275             :  *
     276             :  * @param cls closure
     277             :  * @param cmd current CMD being cleaned up.
     278             :  */
     279             : static void
     280           3 : transfer_cleanup (void *cls,
     281             :                   const struct TALER_TESTING_Command *cmd)
     282             : {
     283           3 :   struct TransferState *fts = cls;
     284             : 
     285           3 :   if (NULL != fts->weh)
     286             :   {
     287           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     288             :                 "Command %s did not complete\n",
     289             :                 cmd->label);
     290           0 :     TALER_BANK_transfer_cancel (fts->weh);
     291           0 :     fts->weh = NULL;
     292             :   }
     293           3 :   if (NULL != fts->retry_task)
     294             :   {
     295           0 :     GNUNET_SCHEDULER_cancel (fts->retry_task);
     296           0 :     fts->retry_task = NULL;
     297             :   }
     298           3 :   GNUNET_free (fts->payto_debit_account);
     299           3 :   GNUNET_free (fts);
     300           3 : }
     301             : 
     302             : 
     303             : /**
     304             :  * Offer internal data from a "fakebank transfer" CMD to other
     305             :  * commands.
     306             :  *
     307             :  * @param cls closure.
     308             :  * @param[out] ret result
     309             :  * @param trait name of the trait.
     310             :  * @param index index number of the object to offer.
     311             :  * @return #GNUNET_OK on success.
     312             :  */
     313             : static enum GNUNET_GenericReturnValue
     314          12 : transfer_traits (void *cls,
     315             :                  const void **ret,
     316             :                  const char *trait,
     317             :                  unsigned int index)
     318             : {
     319          12 :   struct TransferState *fts = cls;
     320             :   struct TALER_TESTING_Trait traits[] = {
     321          12 :     TALER_TESTING_make_trait_exchange_url (
     322             :       (const char **) &fts->exchange_base_url),
     323          12 :     TALER_TESTING_make_trait_bank_row (&fts->serial_id),
     324          12 :     TALER_TESTING_make_trait_credit_payto_uri (
     325             :       (const char **) &fts->payto_credit_account),
     326          12 :     TALER_TESTING_make_trait_debit_payto_uri (
     327          12 :       (const char **) &fts->payto_debit_account),
     328          12 :     TALER_TESTING_make_trait_amount (&fts->amount),
     329          12 :     TALER_TESTING_make_trait_timestamp (0, &fts->timestamp),
     330          12 :     TALER_TESTING_make_trait_wtid (&fts->wtid),
     331          12 :     TALER_TESTING_trait_end ()
     332             :   };
     333             : 
     334          12 :   return TALER_TESTING_get_trait (traits,
     335             :                                   ret,
     336             :                                   trait,
     337             :                                   index);
     338             : }
     339             : 
     340             : 
     341             : struct TALER_TESTING_Command
     342           3 : TALER_TESTING_cmd_transfer (const char *label,
     343             :                             const char *amount,
     344             :                             const struct TALER_BANK_AuthenticationData *auth,
     345             :                             const char *payto_debit_account,
     346             :                             const char *payto_credit_account,
     347             :                             const struct TALER_WireTransferIdentifierRawP *wtid,
     348             :                             const char *exchange_base_url)
     349             : {
     350             :   struct TransferState *fts;
     351             : 
     352           3 :   fts = GNUNET_new (struct TransferState);
     353           3 :   fts->account_debit_url = auth->wire_gateway_url;
     354           3 :   fts->exchange_base_url = exchange_base_url;
     355           3 :   fts->payto_debit_account = GNUNET_strdup (payto_debit_account);
     356           3 :   fts->payto_credit_account = payto_credit_account;
     357           3 :   fts->auth = *auth;
     358           3 :   fts->wtid = *wtid;
     359           3 :   if (GNUNET_OK !=
     360           3 :       TALER_string_to_amount (amount,
     361             :                               &fts->amount))
     362             :   {
     363           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     364             :                 "Failed to parse amount `%s' at %s\n",
     365             :                 amount,
     366             :                 label);
     367           0 :     GNUNET_assert (0);
     368             :   }
     369             : 
     370             :   {
     371           3 :     struct TALER_TESTING_Command cmd = {
     372             :       .cls = fts,
     373             :       .label = label,
     374             :       .run = &transfer_run,
     375             :       .cleanup = &transfer_cleanup,
     376             :       .traits = &transfer_traits
     377             :     };
     378             : 
     379           3 :     return cmd;
     380             :   }
     381             : }
     382             : 
     383             : 
     384             : struct TALER_TESTING_Command
     385           1 : TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
     386             : {
     387             :   struct TransferState *fts;
     388             : 
     389           1 :   GNUNET_assert (&transfer_run == cmd.run);
     390           1 :   fts = cmd.cls;
     391           1 :   fts->do_retry = NUM_RETRIES;
     392           1 :   return cmd;
     393             : }
     394             : 
     395             : 
     396             : /* end of testing_api_cmd_bank_transfer.c */

Generated by: LCOV version 1.14