LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_bank_transfer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 50 85 58.8 %
Date: 2025-06-05 21:03:14 Functions: 5 7 71.4 %

          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             :   struct TALER_FullPayto payto_debit_account;
      61             : 
      62             :   /**
      63             :    * Money receiver account URL.
      64             :    */
      65             :   struct TALER_FullPayto 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           0 : do_retry (void *cls)
     148             : {
     149           0 :   struct TransferState *fts = cls;
     150             : 
     151           0 :   fts->retry_task = NULL;
     152           0 :   TALER_TESTING_touch_cmd (fts->is);
     153           0 :   transfer_run (fts,
     154             :                 NULL,
     155             :                 fts->is);
     156           0 : }
     157             : 
     158             : 
     159             : /**
     160             :  * This callback will process the fakebank response to the wire
     161             :  * transfer.  It just checks whether the HTTP response code is
     162             :  * acceptable.
     163             :  *
     164             :  * @param cls closure with the interpreter state
     165             :  * @param tr response details
     166             :  */
     167             : static void
     168           2 : confirmation_cb (void *cls,
     169             :                  const struct TALER_BANK_TransferResponse *tr)
     170             : {
     171           2 :   struct TransferState *fts = cls;
     172           2 :   struct TALER_TESTING_Interpreter *is = fts->is;
     173             : 
     174           2 :   fts->weh = NULL;
     175           2 :   if (MHD_HTTP_OK != tr->http_status)
     176             :   {
     177           0 :     if (0 != fts->do_retry)
     178             :     {
     179           0 :       fts->do_retry--;
     180           0 :       if ( (0 == tr->http_status) ||
     181           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) ||
     182           0 :            (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) )
     183             :       {
     184           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     185             :                     "Retrying transfer failed with %u/%d\n",
     186             :                     tr->http_status,
     187             :                     (int) tr->ec);
     188             :         /* on DB conflicts, do not use backoff */
     189           0 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec)
     190           0 :           fts->backoff = GNUNET_TIME_UNIT_ZERO;
     191             :         else
     192           0 :           fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
     193           0 :         TALER_TESTING_inc_tries (fts->is);
     194             :         fts->retry_task
     195           0 :           = GNUNET_SCHEDULER_add_delayed (fts->backoff,
     196             :                                           &do_retry,
     197             :                                           fts);
     198           0 :         return;
     199             :       }
     200             :     }
     201           0 :     TALER_TESTING_unexpected_status (is,
     202             :                                      tr->http_status,
     203             :                                      MHD_HTTP_OK);
     204           0 :     return;
     205             :   }
     206             : 
     207           2 :   fts->serial_id = tr->details.ok.row_id;
     208           2 :   fts->timestamp = tr->details.ok.timestamp;
     209           2 :   TALER_TESTING_interpreter_next (is);
     210             : }
     211             : 
     212             : 
     213             : /**
     214             :  * Run the "transfer" CMD.
     215             :  *
     216             :  * @param cls closure.
     217             :  * @param cmd CMD being run.
     218             :  * @param is interpreter state.
     219             :  */
     220             : static void
     221           2 : transfer_run (void *cls,
     222             :               const struct TALER_TESTING_Command *cmd,
     223             :               struct TALER_TESTING_Interpreter *is)
     224             : {
     225           2 :   struct TransferState *fts = cls;
     226             :   void *buf;
     227             :   size_t buf_size;
     228             : 
     229             :   (void) cmd;
     230           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     231             :               "Transfer of %s from %s to %s\n",
     232             :               TALER_amount2s (&fts->amount),
     233             :               fts->account_debit_url,
     234             :               fts->payto_credit_account.full_payto);
     235           2 :   TALER_BANK_prepare_transfer (fts->payto_credit_account,
     236           2 :                                &fts->amount,
     237             :                                fts->exchange_base_url,
     238           2 :                                &fts->wtid,
     239             :                                &buf,
     240             :                                &buf_size);
     241           2 :   fts->is = is;
     242             :   fts->weh
     243           2 :     = TALER_BANK_transfer (
     244             :         TALER_TESTING_interpreter_get_context (is),
     245           2 :         &fts->auth,
     246             :         buf,
     247             :         buf_size,
     248             :         &confirmation_cb,
     249             :         fts);
     250           2 :   GNUNET_free (buf);
     251           2 :   if (NULL == fts->weh)
     252             :   {
     253           0 :     GNUNET_break (0);
     254           0 :     TALER_TESTING_interpreter_fail (is);
     255           0 :     return;
     256             :   }
     257             : }
     258             : 
     259             : 
     260             : /**
     261             :  * Free the state of a "fakebank transfer" CMD, and possibly
     262             :  * cancel a pending operation thereof.
     263             :  *
     264             :  * @param cls closure
     265             :  * @param cmd current CMD being cleaned up.
     266             :  */
     267             : static void
     268           2 : transfer_cleanup (void *cls,
     269             :                   const struct TALER_TESTING_Command *cmd)
     270             : {
     271           2 :   struct TransferState *fts = cls;
     272             : 
     273           2 :   if (NULL != fts->weh)
     274             :   {
     275           0 :     TALER_TESTING_command_incomplete (fts->is,
     276             :                                       cmd->label);
     277           0 :     TALER_BANK_transfer_cancel (fts->weh);
     278           0 :     fts->weh = NULL;
     279             :   }
     280           2 :   if (NULL != fts->retry_task)
     281             :   {
     282           0 :     GNUNET_SCHEDULER_cancel (fts->retry_task);
     283           0 :     fts->retry_task = NULL;
     284             :   }
     285           2 :   GNUNET_free (fts);
     286           2 : }
     287             : 
     288             : 
     289             : /**
     290             :  * Offer internal data from a "fakebank transfer" CMD to other
     291             :  * commands.
     292             :  *
     293             :  * @param cls closure.
     294             :  * @param[out] ret result
     295             :  * @param trait name of the trait.
     296             :  * @param index index number of the object to offer.
     297             :  * @return #GNUNET_OK on success.
     298             :  */
     299             : static enum GNUNET_GenericReturnValue
     300          12 : transfer_traits (void *cls,
     301             :                  const void **ret,
     302             :                  const char *trait,
     303             :                  unsigned int index)
     304             : {
     305          12 :   struct TransferState *fts = cls;
     306             :   struct TALER_TESTING_Trait traits[] = {
     307          12 :     TALER_TESTING_make_trait_exchange_url (
     308             :       fts->exchange_base_url),
     309          12 :     TALER_TESTING_make_trait_bank_row (&fts->serial_id),
     310          12 :     TALER_TESTING_make_trait_credit_payto_uri (
     311          12 :       &fts->payto_credit_account),
     312          12 :     TALER_TESTING_make_trait_debit_payto_uri (
     313          12 :       &fts->payto_debit_account),
     314          12 :     TALER_TESTING_make_trait_amount (&fts->amount),
     315          12 :     TALER_TESTING_make_trait_timestamp (0, &fts->timestamp),
     316          12 :     TALER_TESTING_make_trait_wtid (&fts->wtid),
     317          12 :     TALER_TESTING_trait_end ()
     318             :   };
     319             : 
     320          12 :   return TALER_TESTING_get_trait (traits,
     321             :                                   ret,
     322             :                                   trait,
     323             :                                   index);
     324             : }
     325             : 
     326             : 
     327             : struct TALER_TESTING_Command
     328           2 : TALER_TESTING_cmd_transfer (const char *label,
     329             :                             const char *amount,
     330             :                             const struct TALER_BANK_AuthenticationData *auth,
     331             :                             struct TALER_FullPayto payto_debit_account,
     332             :                             struct TALER_FullPayto payto_credit_account,
     333             :                             const struct TALER_WireTransferIdentifierRawP *wtid,
     334             :                             const char *exchange_base_url)
     335             : {
     336             :   struct TransferState *fts;
     337             : 
     338           2 :   fts = GNUNET_new (struct TransferState);
     339           2 :   fts->account_debit_url = auth->wire_gateway_url;
     340           2 :   fts->exchange_base_url = exchange_base_url;
     341           2 :   fts->payto_debit_account = payto_debit_account;
     342           2 :   fts->payto_credit_account = payto_credit_account;
     343           2 :   fts->auth = *auth;
     344           2 :   fts->wtid = *wtid;
     345           2 :   if (GNUNET_OK !=
     346           2 :       TALER_string_to_amount (amount,
     347             :                               &fts->amount))
     348             :   {
     349           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     350             :                 "Failed to parse amount `%s' at %s\n",
     351             :                 amount,
     352             :                 label);
     353           0 :     GNUNET_assert (0);
     354             :   }
     355             : 
     356             :   {
     357           2 :     struct TALER_TESTING_Command cmd = {
     358             :       .cls = fts,
     359             :       .label = label,
     360             :       .run = &transfer_run,
     361             :       .cleanup = &transfer_cleanup,
     362             :       .traits = &transfer_traits
     363             :     };
     364             : 
     365           2 :     return cmd;
     366             :   }
     367             : }
     368             : 
     369             : 
     370             : struct TALER_TESTING_Command
     371           0 : TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
     372             : {
     373             :   struct TransferState *fts;
     374             : 
     375           0 :   GNUNET_assert (&transfer_run == cmd.run);
     376           0 :   fts = cmd.cls;
     377           0 :   fts->do_retry = NUM_RETRIES;
     378           0 :   return cmd;
     379             : }
     380             : 
     381             : 
     382             : /* end of testing_api_cmd_bank_transfer.c */

Generated by: LCOV version 1.16