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 (9686B)


      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 "backoff.h"
     26 #include "taler/taler_json_lib.h"
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include "taler/taler_bank_service.h"
     29 #include "taler/taler_fakebank_lib.h"
     30 #include "taler/taler_signatures.h"
     31 #include "taler/taler_testing_lib.h"
     32 
     33 
     34 /**
     35  * How often do we retry before giving up?
     36  */
     37 #define NUM_RETRIES 5
     38 
     39 
     40 /**
     41  * State for a "transfer" CMD.
     42  */
     43 struct TransferState
     44 {
     45 
     46   /**
     47    * Wire transfer amount.
     48    */
     49   struct TALER_Amount amount;
     50 
     51   /**
     52    * Base URL of the debit account.
     53    */
     54   const char *account_debit_url;
     55 
     56   /**
     57    * Money receiver payto URL.
     58    */
     59   struct TALER_FullPayto payto_debit_account;
     60 
     61   /**
     62    * Money receiver account URL.
     63    */
     64   struct TALER_FullPayto payto_credit_account;
     65 
     66   /**
     67    * Username to use for authentication.
     68    */
     69   struct TALER_BANK_AuthenticationData auth;
     70 
     71   /**
     72    * Base URL of the exchange.
     73    */
     74   const char *exchange_base_url;
     75 
     76   /**
     77    * Wire transfer identifier to use.
     78    */
     79   struct TALER_WireTransferIdentifierRawP wtid;
     80 
     81   /**
     82    * Handle to the pending request at the fakebank.
     83    */
     84   struct TALER_BANK_TransferHandle *weh;
     85 
     86   /**
     87    * Interpreter state.
     88    */
     89   struct TALER_TESTING_Interpreter *is;
     90 
     91   /**
     92    * Set to the wire transfer's unique ID.
     93    */
     94   uint64_t serial_id;
     95 
     96   /**
     97    * Timestamp of the transaction (as returned from the bank).
     98    */
     99   struct GNUNET_TIME_Timestamp timestamp;
    100 
    101   /**
    102    * Configuration filename.  Used to get the tip reserve key
    103    * filename (used to obtain a public key to write in the
    104    * transfer subject).
    105    */
    106   const char *config_filename;
    107 
    108   /**
    109    * Task scheduled to try later.
    110    */
    111   struct GNUNET_SCHEDULER_Task *retry_task;
    112 
    113   /**
    114    * How long do we wait until we retry?
    115    */
    116   struct GNUNET_TIME_Relative backoff;
    117 
    118   /**
    119    * Was this command modified via
    120    * #TALER_TESTING_cmd_admin_add_incoming_with_retry to
    121    * enable retries? If so, how often should we still retry?
    122    */
    123   unsigned int do_retry;
    124 };
    125 
    126 
    127 /**
    128  * Run the "transfer" CMD.
    129  *
    130  * @param cls closure.
    131  * @param cmd CMD being run.
    132  * @param is interpreter state.
    133  */
    134 static void
    135 transfer_run (void *cls,
    136               const struct TALER_TESTING_Command *cmd,
    137               struct TALER_TESTING_Interpreter *is);
    138 
    139 
    140 /**
    141  * Task scheduled to re-try #transfer_run.
    142  *
    143  * @param cls a `struct TransferState`
    144  */
    145 static void
    146 do_retry (void *cls)
    147 {
    148   struct TransferState *fts = cls;
    149 
    150   fts->retry_task = NULL;
    151   TALER_TESTING_touch_cmd (fts->is);
    152   transfer_run (fts,
    153                 NULL,
    154                 fts->is);
    155 }
    156 
    157 
    158 /**
    159  * This callback will process the fakebank response to the wire
    160  * transfer.  It just checks whether the HTTP response code is
    161  * acceptable.
    162  *
    163  * @param cls closure with the interpreter state
    164  * @param tr response details
    165  */
    166 static void
    167 confirmation_cb (void *cls,
    168                  const struct TALER_BANK_TransferResponse *tr)
    169 {
    170   struct TransferState *fts = cls;
    171   struct TALER_TESTING_Interpreter *is = fts->is;
    172 
    173   fts->weh = NULL;
    174   if (MHD_HTTP_OK != tr->http_status)
    175   {
    176     if (0 != fts->do_retry)
    177     {
    178       fts->do_retry--;
    179       if ( (0 == tr->http_status) ||
    180            (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec) ||
    181            (MHD_HTTP_INTERNAL_SERVER_ERROR == tr->http_status) )
    182       {
    183         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    184                     "Retrying transfer failed with %u/%d\n",
    185                     tr->http_status,
    186                     (int) tr->ec);
    187         /* on DB conflicts, do not use backoff */
    188         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == tr->ec)
    189           fts->backoff = GNUNET_TIME_UNIT_ZERO;
    190         else
    191           fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff);
    192         TALER_TESTING_inc_tries (fts->is);
    193         fts->retry_task
    194           = GNUNET_SCHEDULER_add_delayed (fts->backoff,
    195                                           &do_retry,
    196                                           fts);
    197         return;
    198       }
    199     }
    200     TALER_TESTING_unexpected_status (is,
    201                                      tr->http_status,
    202                                      MHD_HTTP_OK);
    203     return;
    204   }
    205 
    206   fts->serial_id = tr->details.ok.row_id;
    207   fts->timestamp = tr->details.ok.timestamp;
    208   TALER_TESTING_interpreter_next (is);
    209 }
    210 
    211 
    212 /**
    213  * Run the "transfer" CMD.
    214  *
    215  * @param cls closure.
    216  * @param cmd CMD being run.
    217  * @param is interpreter state.
    218  */
    219 static void
    220 transfer_run (void *cls,
    221               const struct TALER_TESTING_Command *cmd,
    222               struct TALER_TESTING_Interpreter *is)
    223 {
    224   struct TransferState *fts = cls;
    225   void *buf;
    226   size_t buf_size;
    227 
    228   (void) cmd;
    229   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    230               "Transfer of %s from %s to %s\n",
    231               TALER_amount2s (&fts->amount),
    232               fts->account_debit_url,
    233               fts->payto_credit_account.full_payto);
    234   TALER_BANK_prepare_transfer (fts->payto_credit_account,
    235                                &fts->amount,
    236                                fts->exchange_base_url,
    237                                &fts->wtid,
    238                                NULL, /* no additional meta data */
    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 */