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: 72 88 81.8 %
Date: 2021-08-30 06:43:37 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-2020 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_Absolute 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           2 : do_retry (void *cls)
     148             : {
     149           2 :   struct TransferState *fts = cls;
     150             : 
     151           2 :   fts->retry_task = NULL;
     152           2 :   fts->is->commands[fts->is->ip].last_req_time
     153           2 :     = GNUNET_TIME_absolute_get ();
     154           2 :   transfer_run (fts,
     155             :                 NULL,
     156             :                 fts->is);
     157           2 : }
     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           6 : confirmation_cb (void *cls,
     175             :                  unsigned int http_status,
     176             :                  enum TALER_ErrorCode ec,
     177             :                  uint64_t serial_id,
     178             :                  struct GNUNET_TIME_Absolute timestamp)
     179             : {
     180           6 :   struct TransferState *fts = cls;
     181           6 :   struct TALER_TESTING_Interpreter *is = fts->is;
     182             : 
     183           6 :   fts->weh = NULL;
     184           6 :   if (MHD_HTTP_OK != http_status)
     185             :   {
     186           2 :     if (0 != fts->do_retry)
     187             :     {
     188           2 :       fts->do_retry--;
     189           2 :       if ( (0 == http_status) ||
     190           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec) ||
     191             :            (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) )
     192             :       {
     193           2 :         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           2 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == ec)
     199           0 :           fts->backoff = GNUNET_TIME_UNIT_ZERO;
     200             :         else
     201           2 :           fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
     202           2 :         fts->is->commands[fts->is->ip].num_tries++;
     203             :         fts->retry_task
     204           2 :           = GNUNET_SCHEDULER_add_delayed (fts->backoff,
     205             :                                           &do_retry,
     206             :                                           fts);
     207           2 :         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           4 :   fts->serial_id = serial_id;
     220           4 :   fts->timestamp = timestamp;
     221           4 :   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           6 : transfer_run (void *cls,
     234             :               const struct TALER_TESTING_Command *cmd,
     235             :               struct TALER_TESTING_Interpreter *is)
     236             : {
     237           6 :   struct TransferState *fts = cls;
     238             :   void *buf;
     239             :   size_t buf_size;
     240             : 
     241             :   (void) cmd;
     242           6 :   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           6 :   TALER_BANK_prepare_transfer (fts->payto_credit_account,
     248           6 :                                &fts->amount,
     249             :                                fts->exchange_base_url,
     250           6 :                                &fts->wtid,
     251             :                                &buf,
     252             :                                &buf_size);
     253           6 :   fts->is = is;
     254             :   fts->weh
     255           6 :     = TALER_BANK_transfer (
     256             :         TALER_TESTING_interpreter_get_context (is),
     257           6 :         &fts->auth,
     258             :         buf,
     259             :         buf_size,
     260             :         &confirmation_cb,
     261             :         fts);
     262           6 :   GNUNET_free (buf);
     263           6 :   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           4 : transfer_cleanup (void *cls,
     281             :                   const struct TALER_TESTING_Command *cmd)
     282             : {
     283           4 :   struct TransferState *fts = cls;
     284             : 
     285           4 :   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           4 :   if (NULL != fts->retry_task)
     294             :   {
     295           0 :     GNUNET_SCHEDULER_cancel (fts->retry_task);
     296           0 :     fts->retry_task = NULL;
     297             :   }
     298           4 :   GNUNET_free (fts->payto_debit_account);
     299           4 :   GNUNET_free (fts);
     300           4 : }
     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 int
     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_url (TALER_TESTING_UT_EXCHANGE_BASE_URL,
     322             :                                   fts->exchange_base_url),
     323          12 :     TALER_TESTING_make_trait_bank_row (&fts->serial_id),
     324          12 :     TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT,
     325             :                                     fts->payto_credit_account),
     326          12 :     TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT,
     327          12 :                                     fts->payto_debit_account),
     328          12 :     TALER_TESTING_make_trait_amount_obj (0, &fts->amount),
     329          12 :     TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp),
     330          12 :     TALER_TESTING_make_trait_wtid (0,
     331          12 :                                    &fts->wtid),
     332          12 :     TALER_TESTING_trait_end ()
     333             :   };
     334             : 
     335          12 :   return TALER_TESTING_get_trait (traits,
     336             :                                   ret,
     337             :                                   trait,
     338             :                                   index);
     339             : }
     340             : 
     341             : 
     342             : /**
     343             :  * Create transfer command.
     344             :  *
     345             :  * @param label command label.
     346             :  * @param amount amount to transfer.
     347             :  * @param auth authentication data to use
     348             :  * @param payto_debit_account which account sends money.
     349             :  * @param payto_credit_account which account receives money.
     350             :  * @param wtid wire transfer identifier to use
     351             :  * @param exchange_base_url exchange URL to use
     352             :  * @return the command.
     353             :  */
     354             : struct TALER_TESTING_Command
     355           4 : TALER_TESTING_cmd_transfer (const char *label,
     356             :                             const char *amount,
     357             :                             const struct TALER_BANK_AuthenticationData *auth,
     358             :                             const char *payto_debit_account,
     359             :                             const char *payto_credit_account,
     360             :                             const struct TALER_WireTransferIdentifierRawP *wtid,
     361             :                             const char *exchange_base_url)
     362             : {
     363             :   struct TransferState *fts;
     364             : 
     365           4 :   fts = GNUNET_new (struct TransferState);
     366           4 :   fts->account_debit_url = auth->wire_gateway_url;
     367           4 :   fts->exchange_base_url = exchange_base_url;
     368           4 :   fts->payto_debit_account = GNUNET_strdup (payto_debit_account);
     369           4 :   fts->payto_credit_account = payto_credit_account;
     370           4 :   fts->auth = *auth;
     371           4 :   fts->wtid = *wtid;
     372           4 :   if (GNUNET_OK !=
     373           4 :       TALER_string_to_amount (amount,
     374             :                               &fts->amount))
     375             :   {
     376           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     377             :                 "Failed to parse amount `%s' at %s\n",
     378             :                 amount,
     379             :                 label);
     380           0 :     GNUNET_assert (0);
     381             :   }
     382             : 
     383             :   {
     384           4 :     struct TALER_TESTING_Command cmd = {
     385             :       .cls = fts,
     386             :       .label = label,
     387             :       .run = &transfer_run,
     388             :       .cleanup = &transfer_cleanup,
     389             :       .traits = &transfer_traits
     390             :     };
     391             : 
     392           4 :     return cmd;
     393             :   }
     394             : }
     395             : 
     396             : 
     397             : /**
     398             :  * Modify a transfer command to enable retries when the reserve is not yet
     399             :  * full or we get other transient errors from the bank.
     400             :  *
     401             :  * @param cmd a fakebank transfer command
     402             :  * @return the command with retries enabled
     403             :  */
     404             : struct TALER_TESTING_Command
     405           2 : TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
     406             : {
     407             :   struct TransferState *fts;
     408             : 
     409           2 :   GNUNET_assert (&transfer_run == cmd.run);
     410           2 :   fts = cmd.cls;
     411           2 :   fts->do_retry = NUM_RETRIES;
     412           2 :   return cmd;
     413             : }
     414             : 
     415             : 
     416             : /* end of testing_api_cmd_bank_transfer.c */

Generated by: LCOV version 1.14