exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_bank_transfer.c (9653B)


      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 "taler/platform.h"
     26 #include "taler/backoff.h"
     27 #include "taler/taler_json_lib.h"
     28 #include <gnunet/gnunet_curl_lib.h>
     29 #include "taler/taler_bank_service.h"
     30 #include "taler/taler_fakebank_lib.h"
     31 #include "taler/taler_signatures.h"
     32 #include "taler/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 do_retry (void *cls)
    148 {
    149   struct TransferState *fts = cls;
    150 
    151   fts->retry_task = NULL;
    152   TALER_TESTING_touch_cmd (fts->is);
    153   transfer_run (fts,
    154                 NULL,
    155                 fts->is);
    156 }
    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 confirmation_cb (void *cls,
    169                  const struct TALER_BANK_TransferResponse *tr)
    170 {
    171   struct TransferState *fts = cls;
    172   struct TALER_TESTING_Interpreter *is = fts->is;
    173 
    174   fts->weh = NULL;
    175   if (MHD_HTTP_OK != tr->http_status)
    176   {
    177     if (0 != fts->do_retry)
    178     {
    179       fts->do_retry--;
    180       if ( (0 == tr->http_status) ||
    181            (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) ||
    182            (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) )
    183       {
    184         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         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec)
    190           fts->backoff = GNUNET_TIME_UNIT_ZERO;
    191         else
    192           fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
    193         TALER_TESTING_inc_tries (fts->is);
    194         fts->retry_task
    195           = GNUNET_SCHEDULER_add_delayed (fts->backoff,
    196                                           &do_retry,
    197                                           fts);
    198         return;
    199       }
    200     }
    201     TALER_TESTING_unexpected_status (is,
    202                                      tr->http_status,
    203                                      MHD_HTTP_OK);
    204     return;
    205   }
    206 
    207   fts->serial_id = tr->details.ok.row_id;
    208   fts->timestamp = tr->details.ok.timestamp;
    209   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 transfer_run (void *cls,
    222               const struct TALER_TESTING_Command *cmd,
    223               struct TALER_TESTING_Interpreter *is)
    224 {
    225   struct TransferState *fts = cls;
    226   void *buf;
    227   size_t buf_size;
    228 
    229   (void) cmd;
    230   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   TALER_BANK_prepare_transfer (fts->payto_credit_account,
    236                                &fts->amount,
    237                                fts->exchange_base_url,
    238                                &fts->wtid,
    239                                &buf,
    240                                &buf_size);
    241   fts->is = is;
    242   fts->weh
    243     = TALER_BANK_transfer (
    244         TALER_TESTING_interpreter_get_context (is),
    245         &fts->auth,
    246         buf,
    247         buf_size,
    248         &confirmation_cb,
    249         fts);
    250   GNUNET_free (buf);
    251   if (NULL == fts->weh)
    252   {
    253     GNUNET_break (0);
    254     TALER_TESTING_interpreter_fail (is);
    255     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 transfer_cleanup (void *cls,
    269                   const struct TALER_TESTING_Command *cmd)
    270 {
    271   struct TransferState *fts = cls;
    272 
    273   if (NULL != fts->weh)
    274   {
    275     TALER_TESTING_command_incomplete (fts->is,
    276                                       cmd->label);
    277     TALER_BANK_transfer_cancel (fts->weh);
    278     fts->weh = NULL;
    279   }
    280   if (NULL != fts->retry_task)
    281   {
    282     GNUNET_SCHEDULER_cancel (fts->retry_task);
    283     fts->retry_task = NULL;
    284   }
    285   GNUNET_free (fts);
    286 }
    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 transfer_traits (void *cls,
    301                  const void **ret,
    302                  const char *trait,
    303                  unsigned int index)
    304 {
    305   struct TransferState *fts = cls;
    306   struct TALER_TESTING_Trait traits[] = {
    307     TALER_TESTING_make_trait_exchange_url (
    308       fts->exchange_base_url),
    309     TALER_TESTING_make_trait_bank_row (&fts->serial_id),
    310     TALER_TESTING_make_trait_credit_payto_uri (
    311       &fts->payto_credit_account),
    312     TALER_TESTING_make_trait_debit_payto_uri (
    313       &fts->payto_debit_account),
    314     TALER_TESTING_make_trait_amount (&fts->amount),
    315     TALER_TESTING_make_trait_timestamp (0, &fts->timestamp),
    316     TALER_TESTING_make_trait_wtid (&fts->wtid),
    317     TALER_TESTING_trait_end ()
    318   };
    319 
    320   return TALER_TESTING_get_trait (traits,
    321                                   ret,
    322                                   trait,
    323                                   index);
    324 }
    325 
    326 
    327 struct TALER_TESTING_Command
    328 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   fts = GNUNET_new (struct TransferState);
    339   fts->account_debit_url = auth->wire_gateway_url;
    340   fts->exchange_base_url = exchange_base_url;
    341   fts->payto_debit_account = payto_debit_account;
    342   fts->payto_credit_account = payto_credit_account;
    343   fts->auth = *auth;
    344   fts->wtid = *wtid;
    345   if (GNUNET_OK !=
    346       TALER_string_to_amount (amount,
    347                               &fts->amount))
    348   {
    349     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    350                 "Failed to parse amount `%s' at %s\n",
    351                 amount,
    352                 label);
    353     GNUNET_assert (0);
    354   }
    355 
    356   {
    357     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     return cmd;
    366   }
    367 }
    368 
    369 
    370 struct TALER_TESTING_Command
    371 TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd)
    372 {
    373   struct TransferState *fts;
    374 
    375   GNUNET_assert (&transfer_run == cmd.run);
    376   fts = cmd.cls;
    377   fts->do_retry = NUM_RETRIES;
    378   return cmd;
    379 }
    380 
    381 
    382 /* end of testing_api_cmd_bank_transfer.c */