diff options
Diffstat (limited to 'src/bank-lib')
| -rw-r--r-- | src/bank-lib/Makefile.am | 4 | ||||
| -rw-r--r-- | src/bank-lib/bank_api_transfer.c (renamed from src/bank-lib/bank_api_transaction.c) | 46 | ||||
| -rw-r--r-- | src/bank-lib/fakebank_history.c | 182 | ||||
| -rw-r--r-- | src/bank-lib/test_bank_api.c | 137 | ||||
| -rw-r--r-- | src/bank-lib/testing_api_cmd_admin_add_incoming.c | 625 | ||||
| -rw-r--r-- | src/bank-lib/testing_api_cmd_history_credit.c | 11 | ||||
| -rw-r--r-- | src/bank-lib/testing_api_cmd_history_debit.c | 12 | ||||
| -rw-r--r-- | src/bank-lib/testing_api_cmd_transfer.c | 394 | 
8 files changed, 1109 insertions, 302 deletions
| diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index 371f5e6d..d7493bde 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -41,7 +41,7 @@ libtalerbank_la_SOURCES = \    bank_api_common.c bank_api_common.h \    bank_api_credit.c \    bank_api_debit.c \ -  bank_api_transaction.c \ +  bank_api_transfer.c \    bank_api_parse.c  libtalerbank_la_LIBADD = \    $(top_builddir)/src/json/libtalerjson.la \ @@ -70,8 +70,10 @@ libtalerbanktesting_la_LDFLAGS = \    -version-info 0:0:0 \    -no-undefined  libtalerbanktesting_la_SOURCES = \ +  testing_api_cmd_admin_add_incoming.c \    testing_api_cmd_history_credit.c \    testing_api_cmd_history_debit.c \ +  testing_api_cmd_transfer.c \    testing_api_helpers.c  libtalerbanktesting_la_LIBADD = \    $(top_builddir)/src/json/libtalerjson.la \ diff --git a/src/bank-lib/bank_api_transaction.c b/src/bank-lib/bank_api_transfer.c index 17732848..93ff7bac 100644 --- a/src/bank-lib/bank_api_transaction.c +++ b/src/bank-lib/bank_api_transfer.c @@ -15,8 +15,8 @@    <http://www.gnu.org/licenses/>  */  /** - * @file bank-lib/bank_api_transaction.c - * @brief Implementation of the /transaction/ requests of the bank's HTTP API + * @file bank-lib/bank_api_transfer.c + * @brief Implementation of the /transfer/ requests of the bank's HTTP API   * @author Christian Grothoff   */  #include "platform.h" @@ -73,7 +73,7 @@ GNUNET_NETWORK_STRUCT_END   * @param exchange_base_url base URL of this exchange (included in subject   *        to facilitate use of tracking API by merchant backend)   * @param wtid wire transfer identifier to use - * @param buf[out] set to transaction data to persist, NULL on error + * @param buf[out] set to transfer data to persist, NULL on error   * @param buf_size[out] set to number of bytes in @a buf, 0 on error   */  void @@ -111,7 +111,7 @@ TALER_BANK_prepare_wire_transfer (const char *destination_account_url,  /** - * @brief An transaction Handle + * @brief An transfer Handle   */  struct TALER_BANK_WireExecuteHandle  { @@ -146,16 +146,16 @@ struct TALER_BANK_WireExecuteHandle  /**   * Function called when we're done processing the - * HTTP /transaction request. + * HTTP /transfer request.   *   * @param cls the `struct TALER_BANK_WireExecuteHandle`   * @param response_code HTTP response code, 0 on error   * @param response parsed JSON result, NULL on error   */  static void -handle_transaction_finished (void *cls, -                             long response_code, -                             const void *response) +handle_transfer_finished (void *cls, +                          long response_code, +                          const void *response)  {    struct TALER_BANK_WireExecuteHandle *weh = cls;    uint64_t row_id = UINT64_MAX; @@ -263,7 +263,7 @@ TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,                                    void *cc_cls)  {    struct TALER_BANK_WireExecuteHandle *weh; -  json_t *transaction_obj; +  json_t *transfer_obj;    CURL *eh;    const struct WirePackP *wp = buf;    uint32_t d_len; @@ -293,14 +293,14 @@ TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,    }    TALER_amount_ntoh (&amount,                       &wp->amount); -  transaction_obj = json_pack ("{s:o, s:o, s:s, s:o, s:o, s:s}", -                               "request_uid", GNUNET_JSON_from_data_auto ( -                                 &wp->request_uid), -                               "amount", TALER_JSON_from_amount (&amount), -                               "exchange_url", exchange_base_url, -                               "wtid", GNUNET_JSON_from_data_auto (&wp->wtid), -                               "credit_account", destination_account_url); -  if (NULL == transaction_obj) +  transfer_obj = json_pack ("{s:o, s:o, s:s, s:o, s:o, s:s}", +                            "request_uid", GNUNET_JSON_from_data_auto ( +                              &wp->request_uid), +                            "amount", TALER_JSON_from_amount (&amount), +                            "exchange_url", exchange_base_url, +                            "wtid", GNUNET_JSON_from_data_auto (&wp->wtid), +                            "credit_account", destination_account_url); +  if (NULL == transfer_obj)    {      GNUNET_break (0);      return NULL; @@ -309,7 +309,7 @@ TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,    weh->cb = cc;    weh->cb_cls = cc_cls;    weh->request_url = TALER_BANK_path_to_url_ (bank_base_url, -                                              "/transaction"); +                                              "/transfer");    weh->post_ctx.headers = curl_slist_append                              (weh->post_ctx.headers,                              "Content-Type: application/json"); @@ -325,20 +325,20 @@ TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,         (GNUNET_OK !=          TALER_curl_easy_post (&weh->post_ctx,                                eh, -                              transaction_obj)) ) +                              transfer_obj)) )    {      GNUNET_break (0);      TALER_BANK_execute_wire_transfer_cancel (weh);      curl_easy_cleanup (eh); -    json_decref (transaction_obj); +    json_decref (transfer_obj);      return NULL;    } -  json_decref (transaction_obj); +  json_decref (transfer_obj);    weh->job = GNUNET_CURL_job_add2 (ctx,                                     eh,                                     weh->post_ctx.headers, -                                   &handle_transaction_finished, +                                   &handle_transfer_finished,                                     weh);    return weh;  } @@ -365,4 +365,4 @@ TALER_BANK_execute_wire_transfer_cancel (struct  } -/* end of bank_api_transaction.c */ +/* end of bank_api_transfer.c */ diff --git a/src/bank-lib/fakebank_history.c b/src/bank-lib/fakebank_history.c deleted file mode 100644 index 2781cdca..00000000 --- a/src/bank-lib/fakebank_history.c +++ /dev/null @@ -1,182 +0,0 @@ -/* -  This file is part of TALER -  (C) 2016-2020 Taler Systems SA - -  TALER is free software; you can redistribute it and/or -  modify it under the terms of the GNU General Public License -  as published by the Free Software Foundation; either version 3, -  or (at your option) any later version. - -  TALER is distributed in the hope that it will be useful, -  but WITHOUT ANY WARRANTY; without even the implied warranty of -  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -  GNU General Public License for more details. - -  You should have received a copy of the GNU General Public -  License along with TALER; see the file COPYING.  If not, -  see <http://www.gnu.org/licenses/> -*/ - -/** - * @file bank-lib/fakebank_history.c - * @brief definitions for the "/history" layer. - * @author Marcello Stanisci <stanisci.m@gmail.com> - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include "taler_json_lib.h" -#include "fakebank.h" - - -int -TFH_build_history_response (struct MHD_Connection *connection, -                            struct Transaction *pos, -                            struct HistoryArgs *ha, -                            Skip skip, -                            Step step, -                            CheckAdvance advance) -{ - -  struct HistoryElement *history_results_head = NULL; -  struct HistoryElement *history_results_tail = NULL; -  struct HistoryElement *history_element = NULL; -  json_t *history; -  json_t *jresponse; -  int ret; - -  while ( (NULL != pos) && -          advance (ha, -                   pos) ) -  { -    json_t *trans; -    char *subject; -    const char *sign; - -    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -                "Found transaction over %s from %llu to %llu\n", -                TALER_amount2s (&pos->amount), -                (unsigned long long) pos->debit_account, -                (unsigned long long) pos->credit_account); - -    if ( (! ( ( (ha->account_number == pos->debit_account) && -                (0 != (ha->direction & TALER_BANK_DIRECTION_DEBIT)) ) || -              ( (ha->account_number == pos->credit_account) && -                (0 != (ha->direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) || -         ( (0 == (ha->direction & TALER_BANK_DIRECTION_CANCEL)) && -           (GNUNET_YES == pos->rejected) ) ) -    { -      pos = skip (ha, -                  pos); -      continue; -    } - -    GNUNET_asprintf (&subject, -                     "%s %s", -                     pos->subject, -                     pos->exchange_base_url); -    sign = -      (ha->account_number == pos->debit_account) -      ? (pos->rejected ? "cancel-" : "-") -      : (pos->rejected ? "cancel+" : "+"); -    trans = json_pack -              ("{s:I, s:o, s:o, s:s, s:I, s:s}", -              "row_id", (json_int_t) pos->row_id, -              "date", GNUNET_JSON_from_time_abs (pos->date), -              "amount", TALER_JSON_from_amount (&pos->amount), -              "sign", sign, -              "counterpart", (json_int_t) -              ( (ha->account_number == pos->debit_account) -                ? pos->credit_account -                : pos->debit_account), -              "wt_subject", subject); -    GNUNET_assert (NULL != trans); -    GNUNET_free (subject); - -    history_element = GNUNET_new (struct HistoryElement); -    history_element->element = trans; - - -    /* XXX: the ordering feature is missing.  */ - -    GNUNET_CONTAINER_DLL_insert_tail (history_results_head, -                                      history_results_tail, -                                      history_element); -    pos = step (ha, pos); -  } - -  history = json_array (); -  if (NULL != history_results_head) -    history_element = history_results_head; - -  while (NULL != history_element) -  { -    GNUNET_assert (0 == -                   json_array_append_new (history, -                                          history_element->element)); -    history_element = history_element->next; -    if (NULL != history_element) -      GNUNET_free_non_null (history_element->prev); -  } -  GNUNET_free_non_null (history_results_tail); - -  if (0 == json_array_size (history)) -  { -    struct MHD_Response *resp; - -    json_decref (history); -    GNUNET_log (GNUNET_ERROR_TYPE_INFO, -                "Returning empty transaction history\n"); -    resp = MHD_create_response_from_buffer -             (0, -             "", -             MHD_RESPMEM_PERSISTENT); -    ret = MHD_queue_response (connection, -                              MHD_HTTP_NO_CONTENT, -                              resp); -    MHD_destroy_response (resp); -    return ret; -  } - -  jresponse = json_pack ("{s:o}", -                         "data", -                         history); -  if (NULL == jresponse) -  { -    GNUNET_break (0); -    return MHD_NO; -  } - -  /* Finally build response object */ -  { -    struct MHD_Response *resp; -    void *json_str; -    size_t json_len; - -    json_str = json_dumps (jresponse, -                           JSON_INDENT (2)); -    json_decref (jresponse); -    if (NULL == json_str) -    { -      GNUNET_break (0); -      return MHD_NO; -    } -    json_len = strlen (json_str); -    resp = MHD_create_response_from_buffer (json_len, -                                            json_str, -                                            MHD_RESPMEM_MUST_FREE); -    if (NULL == resp) -    { -      GNUNET_break (0); -      free (json_str); -      return MHD_NO; -    } -    (void) MHD_add_response_header (resp, -                                    MHD_HTTP_HEADER_CONTENT_TYPE, -                                    "application/json"); -    ret = MHD_queue_response (connection, -                              MHD_HTTP_OK, -                              resp); -    MHD_destroy_response (resp); -  } -  return ret; -} diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c index 087e4484..5e7a3a33 100644 --- a/src/bank-lib/test_bank_api.c +++ b/src/bank-lib/test_bank_api.c @@ -16,7 +16,6 @@    License along with TALER; see the file COPYING.  If not,    see <http://www.gnu.org/licenses/>  */ -  /**   * @file bank/test_bank_api.c   * @brief testcase to test bank's HTTP API @@ -24,7 +23,6 @@   * @author Marcello Stanisci   * @author Christian Grothoff   */ -  #include "platform.h"  #include "taler_util.h"  #include "taler_signatures.h" @@ -45,6 +43,16 @@  static char *bank_url;  /** + * Account URL. + */ +static char *account_url; + +/** + * payto://-URL of another account. + */ +static char *payto_url; + +/**   * Handle to the Py-bank daemon.   */  static struct GNUNET_OS_Process *bankd; @@ -56,26 +64,6 @@ static struct GNUNET_OS_Process *bankd;  static int WITH_FAKEBANK;  /** - * Transfer @a amount from @a src account to @a dst using - * @a subject and the @a label for the command. - */ -#define TRANSFER(label,amount,src,dst,subject)          \ -  TALER_TESTING_cmd_fakebank_transfer_with_subject (label, \ -                                                    amount,   \ -                                                    bank_url, \ -                                                    src,                \ -                                                    dst, \ -                                                    AUTHS[src \ -                                                          - 1].details.basic. \ -                                                    username, \ -                                                    AUTHS[src \ -                                                          - 1].details.basic. \ -                                                    password, \ -                                                    subject, \ -                                                    "http://exchange.net/") - - -/**   * Main function that will tell the interpreter what commands to   * run.   * @@ -85,89 +73,54 @@ static void  run (void *cls,       struct TALER_TESTING_Interpreter *is)  { -  GNUNET_log (GNUNET_ERROR_TYPE_INFO, -              "Bank serves at `%s'\n", -              bank_url); -  extern struct TALER_BANK_AuthenticationData AUTHS[];    struct TALER_TESTING_Command commands[] = { -    TALER_TESTING_cmd_bank_history ("history-0", -                                    bank_url, -                                    TALER_TESTING_BANK_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_BOTH, -                                    GNUNET_YES, +    TALER_TESTING_cmd_bank_credits ("history-0", +                                    account_url,                                      NULL,                                      1), -    /* WARNING: old API has expected http response code among -     * the parameters, although it was always set as '200 OK' */ -    TRANSFER ("debit-1", -              "KUDOS:5.01", -              TALER_TESTING_EXCHANGE_ACCOUNT_NUMBER, -              TALER_TESTING_BANK_ACCOUNT_NUMBER, -              "subject 1"), -    TALER_TESTING_cmd_bank_history ("history-1c", -                                    bank_url, -                                    TALER_TESTING_BANK_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_CREDIT, -                                    GNUNET_YES, -                                    NULL, -                                    5), -    TALER_TESTING_cmd_bank_history ("history-1d", -                                    bank_url, -                                    TALER_TESTING_BANK_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_DEBIT, -                                    GNUNET_YES, +    TALER_TESTING_cmd_admin_add_incoming ("debit-1", +                                          "KUDOS:5.01", +                                          account_url, +                                          payto_url, +                                          NULL, +                                          NULL), +    TALER_TESTING_cmd_bank_credits ("history-1c", +                                    account_url,                                      NULL,                                      5), -    TRANSFER ("debit-2", -              "KUDOS:3.21", -              TALER_TESTING_EXCHANGE_ACCOUNT_NUMBER, -              TALER_TESTING_USER_ACCOUNT_NUMBER, -              "subject 2"), +    TALER_TESTING_cmd_bank_debits ("history-1d", +                                   account_url, +                                   NULL, +                                   5), +    TALER_TESTING_cmd_admin_add_incoming ("debit-2", +                                          "KUDOS:3.21", +                                          account_url, +                                          payto_url, +                                          NULL, +                                          NULL),      TRANSFER ("credit-2",                "KUDOS:3.22",                TALER_TESTING_USER_ACCOUNT_NUMBER,                TALER_TESTING_EXCHANGE_ACCOUNT_NUMBER,                "credit 2"), -    TALER_TESTING_cmd_bank_history ("history-2b", -                                    bank_url, -                                    TALER_TESTING_EXCHANGE_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_BOTH, -                                    GNUNET_YES, -                                    NULL, -                                    5), -    TALER_TESTING_cmd_bank_history ("history-2bi", -                                    bank_url, -                                    TALER_TESTING_EXCHANGE_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_BOTH, -                                    GNUNET_YES, -                                    "debit-1", -                                    5), -    TRANSFER ("credit-for-reject-1", -              "KUDOS:1.01", -              TALER_TESTING_BANK_ACCOUNT_NUMBER, -              TALER_TESTING_EXCHANGE_ACCOUNT_NUMBER, -              "subject 3"), -    TALER_TESTING_cmd_bank_reject ("reject-1", -                                   bank_url, -                                   "credit-for-reject-1"), -    TALER_TESTING_cmd_bank_history ("history-r1", -                                    bank_url, -                                    TALER_TESTING_BANK_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_BOTH, -                                    GNUNET_YES, -                                    NULL, -                                    5), -    TALER_TESTING_cmd_bank_history ("history-r1c", -                                    bank_url, -                                    TALER_TESTING_BANK_ACCOUNT_NUMBER, -                                    TALER_BANK_DIRECTION_BOTH -                                    | TALER_BANK_DIRECTION_CANCEL, -                                    GNUNET_YES, -                                    NULL, -                                    5), +    TALER_TESTING_cmd_bank_debits ("history-2b", +                                   account_url, +                                   NULL, +                                   5),      TALER_TESTING_cmd_end ()    }; +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "Bank serves at `%s'\n", +              bank_url); +  GNUNET_asprintf (&account_url, +                   "%s/%s", +                   base_url, +                   "alice"); +  GNUNET_asprintf (&payto_url, +                   "payto://x-taler-bank/%s/%s", +                   base_url, +                   "bob");    if (GNUNET_YES == WITH_FAKEBANK)      TALER_TESTING_run_with_fakebank (is,                                       commands, diff --git a/src/bank-lib/testing_api_cmd_admin_add_incoming.c b/src/bank-lib/testing_api_cmd_admin_add_incoming.c new file mode 100644 index 00000000..b7b18374 --- /dev/null +++ b/src/bank-lib/testing_api_cmd_admin_add_incoming.c @@ -0,0 +1,625 @@ +/* +  This file is part of TALER +  Copyright (C) 2018-2020 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify it +  under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 3, or (at your +  option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ +/** + * @file exchange-lib/testing_api_cmd_admin_add_incoming.c + * @brief implementation of a bank /admin/add-incoming command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "backoff.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" + + +/** + * State for a "fakebank transfer" CMD. + */ +struct AdminAddIncomingState +{ + +  /** +   * Label of any command that can trait-offer a reserve priv. +   */ +  const char *reserve_reference; + +  /** +   * Wire transfer amount. +   */ +  struct TALER_Amount amount; + +  /** +   * Base URL of the debit account. +   */ +  const char *debit_url; + +  /** +   * Money receiver account URL. +   */ +  const char *payto_credit_account; + +  /** +   * Username to use for authentication. +   */ +  struct TALER_BANK_AuthenticationData auth; + +  /** +   * Set (by the interpreter) to the reserve's private key +   * we used to make a wire transfer subject line with. +   */ +  struct TALER_ReservePrivateKeyP reserve_priv; + +  /** +   * Reserve public key matching @e reserve_priv. +   */ +  struct TALER_ReservePublicKeyP reserve_pub; + +  /** +   * Handle to the pending request at the fakebank. +   */ +  struct TALER_BANK_AdminAddIncomingHandle *aih; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Set to the wire transfer's unique ID. +   */ +  uint64_t serial_id; + +  /** +   * Timestamp of the transaction (as returned from the bank). +   */ +  struct GNUNET_TIME_Absolute timestamp; + +  /** +   * Merchant instance.  Sometimes used to get the tip reserve +   * private key by reading the appropriate config section. +   */ +  const char *instance; + +  /** +   * Configuration filename.  Used to get the tip reserve key +   * filename (used to obtain a public key to write in the +   * transfer subject). +   */ +  const char *config_filename; + +  /** +   * Task scheduled to try later. +   */ +  struct GNUNET_SCHEDULER_Task *retry_task; + +  /** +   * How long do we wait until we retry? +   */ +  struct GNUNET_TIME_Relative backoff; + +  /** +   * Was this command modified via +   * #TALER_TESTING_cmd_admin_add_incoming_with_retry to +   * enable retries? +   */ +  int do_retry; +}; + + +/** + * Run the "fakebank transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +fakebank_transfer_run (void *cls, +                       const struct TALER_TESTING_Command *cmd, +                       struct TALER_TESTING_Interpreter *is); + + +/** + * Task scheduled to re-try #fakebank_transfer_run. + * + * @param cls a `struct AdminAddIncomingState` + */ +static void +do_retry (void *cls) +{ +  struct AdminAddIncomingState *fts = cls; + +  fts->retry_task = NULL; +  fakebank_transfer_run (fts, +                         NULL, +                         fts->is); +} + + +/** + * This callback will process the fakebank response to the wire + * transfer.  It just checks whether the HTTP response code is + * acceptable. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for + *        successful status request; 0 if the exchange's reply is + *        bogus (fails to follow the protocol) + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param serial_id unique ID of the wire transfer + * @param timestamp time stamp of the transaction made. + * @param json raw response + */ +static void +confirmation_cb (void *cls, +                 unsigned int http_status, +                 enum TALER_ErrorCode ec, +                 uint64_t serial_id, +                 struct GNUNET_TIME_Absolute timestamp, +                 const json_t *json) +{ +  struct AdminAddIncomingState *fts = cls; +  struct TALER_TESTING_Interpreter *is = fts->is; + +  fts->aih = NULL; +  if (MHD_HTTP_OK != http_status) +  { +    if (GNUNET_YES == fts->do_retry) +    { +      if ( (0 == http_status) || +           (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || +           (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) +      { +        GNUNET_log +          (GNUNET_ERROR_TYPE_INFO, +          "Retrying fakebank transfer failed with %u/%d\n", +          http_status, +          (int) ec); +        /* on DB conflicts, do not use backoff */ +        if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) +          fts->backoff = GNUNET_TIME_UNIT_ZERO; +        else +          fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); +        fts->retry_task = GNUNET_SCHEDULER_add_delayed +                            (fts->backoff, +                            &do_retry, +                            fts); +        return; +      } +    } +    GNUNET_break (0); +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Fakebank returned HTTP status %u/%d\n", +                http_status, +                (int) ec); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  fts->serial_id = serial_id; +  fts->timestamp = timestamp; +  TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the "fakebank transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +fakebank_transfer_run (void *cls, +                       const struct TALER_TESTING_Command *cmd, +                       struct TALER_TESTING_Interpreter *is) +{ +  struct AdminAddIncomingState *fts = cls; + +  /* Use reserve public key as subject */ +  if (NULL != fts->reserve_reference) +  { +    const struct TALER_TESTING_Command *ref; +    const struct TALER_ReservePrivateKeyP *reserve_priv; + +    ref = TALER_TESTING_interpreter_lookup_command +            (is, fts->reserve_reference); +    if (NULL == ref) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } +    if (GNUNET_OK != +        TALER_TESTING_get_trait_reserve_priv (ref, +                                              0, +                                              &reserve_priv)) +    { +      GNUNET_break (0); +      TALER_TESTING_interpreter_fail (is); +      return; +    } +    fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv; +  } +  else +  { +    if (NULL != fts->instance) +    { +      char *section; +      char *keys; +      struct GNUNET_CRYPTO_EddsaPrivateKey *priv; +      struct GNUNET_CONFIGURATION_Handle *cfg; + +      GNUNET_assert (NULL != fts->config_filename); +      cfg = GNUNET_CONFIGURATION_create (); +      if (GNUNET_OK != +          GNUNET_CONFIGURATION_load (cfg, +                                     fts->config_filename)) +      { +        GNUNET_break (0); +        TALER_TESTING_interpreter_fail (is); +        return; +      } + +      GNUNET_asprintf (§ion, +                       "instance-%s", +                       fts->instance); +      if (GNUNET_OK != +          GNUNET_CONFIGURATION_get_value_filename +            (cfg, +            section, +            "TIP_RESERVE_PRIV_FILENAME", +            &keys)) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                    "Configuration fails to specify reserve" +                    " private key filename in section %s\n", +                    section); +        GNUNET_free (section); +        TALER_TESTING_interpreter_fail (is); +        return; +      } +      priv = GNUNET_CRYPTO_eddsa_key_create_from_file (keys); +      GNUNET_free (keys); +      if (NULL == priv) +      { +        GNUNET_log_config_invalid +          (GNUNET_ERROR_TYPE_ERROR, +          section, +          "TIP_RESERVE_PRIV_FILENAME", +          "Failed to read private key"); +        GNUNET_free (section); +        TALER_TESTING_interpreter_fail (is); +        return; +      } +      fts->reserve_priv.eddsa_priv = *priv; +      GNUNET_free (section); +      GNUNET_free (priv); +      GNUNET_CONFIGURATION_destroy (cfg); +    } +    else +    { +      /* No referenced reserve, no instance to take priv +       * from, no explicit subject given: create new key! */ +      struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + +      priv = GNUNET_CRYPTO_eddsa_key_create (); +      fts->reserve_priv.eddsa_priv = *priv; +      GNUNET_free (priv); +    } +  } +  GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv, +                                      &fts->reserve_pub.eddsa_pub); +  fts->is = is; +  fts->aih +    = TALER_BANK_admin_add_incoming +        (TALER_TESTING_interpreter_get_context (is), +        fts->debit_url, +        &fts->auth, +        &fts->reserve_pub, +        &fts->amount, +        fts->payto_credit_account, +        &confirmation_cb, +        fts); +  if (NULL == fts->aih) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +} + + +/** + * Free the state of a "fakebank transfer" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure + * @param cmd current CMD being cleaned up. + */ +static void +fakebank_transfer_cleanup (void *cls, +                           const struct TALER_TESTING_Command *cmd) +{ +  struct AdminAddIncomingState *fts = cls; + +  if (NULL != fts->aih) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %s did not complete\n", +                cmd->label); +    TALER_BANK_admin_add_incoming_cancel (fts->aih); +    fts->aih = NULL; +  } +  if (NULL != fts->retry_task) +  { +    GNUNET_SCHEDULER_cancel (fts->retry_task); +    fts->retry_task = NULL; +  } +  GNUNET_free (fts); +} + + +/** + * Offer internal data from a "fakebank transfer" CMD to other + * commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +fakebank_transfer_traits (void *cls, +                          const void **ret, +                          const char *trait, +                          unsigned int index) +{ +  struct AdminAddIncomingState *fts = cls; +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_url (1, fts->debit_url), +    TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), +    TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->payto_credit_account), +    TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT (fts->debit_url), +    TALER_TESTING_make_trait_amount_obj (0, &fts->amount), +    TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), +    TALER_TESTING_make_trait_reserve_priv (0, +                                           &fts->reserve_priv), +    TALER_TESTING_make_trait_reserve_pub (0, +                                          &fts->reserve_pub), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +/** + * Create fakebank_transfer command, the subject line will be + * derived from a randomly created reserve priv.  Note that that + * reserve priv will then be offered as trait. + * + * @param label command label. + * @param amount amount to transfer. + * @param account_base_url base URL of the account that implements this + *        wire transer (which account gives money). + * @param payto_credit_account which account receives money. + * @param auth_username username identifying the @a + *        debit_account_no at the bank. + * @param auth_password password for @a auth_username. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming +  (const char *label, +  const char *amount, +  const char *account_base_url, +  const struct TALER_BANK_AuthenticationData *auth, +  const char *payto_credit_account) +{ +  struct AdminAddIncomingState *fts; + +  fts = GNUNET_new (struct AdminAddIncomingState); +  fts->debit_url = account_base_url; +  fts->payto_credit_account = payto_credit_account; +  fts->auth = *auth; +  if (GNUNET_OK != +      TALER_string_to_amount (amount, +                              &fts->amount)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at %s\n", +                amount, +                label); +    GNUNET_assert (0); +  } + +  { +    struct TALER_TESTING_Command cmd = { +      .cls = fts, +      .label = label, +      .run = &fakebank_transfer_run, +      .cleanup = &fakebank_transfer_cleanup, +      .traits = &fakebank_transfer_traits +    }; + +    return cmd; +  } +} + + +/** + * Create "fakebank transfer" CMD, letting the caller specify + * a reference to a command that can offer a reserve private key. + * This private key will then be used to construct the subject line + * of the wire transfer. + * + * @param label command label. + * @param amount the amount to transfer. + * @param bank_url base URL of the bank running the transfer. + * @param debit_account_no which account (expressed as a number) + *        gives money. + * @param credit_account_no which account (expressed as a number) + *        receives money. + * @param auth_username username identifying the @a + *        debit_account_no at the bank. + * @param auth_password password for @a auth_username. + * @param ref reference to a command that can offer a reserve + *        private key. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming_with_ref +  (const char *label, +  const char *amount, +  const char *account_base_url, +  const struct TALER_BANK_AuthenticationData *auth, +  const char *payto_credit_account, +  const char *ref) +{ +  struct AdminAddIncomingState *fts; + +  fts = GNUNET_new (struct AdminAddIncomingState); +  fts->debit_url = account_base_url; +  fts->payto_credit_account = payto_credit_account; +  fts->auth = *auth; +  fts->reserve_reference = ref; +  if (GNUNET_OK != +      TALER_string_to_amount (amount, +                              &fts->amount)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at %s\n", +                amount, +                label); +    GNUNET_assert (0); +  } +  { +    struct TALER_TESTING_Command cmd = { +      .cls = fts, +      .label = label, +      .run = &fakebank_transfer_run, +      .cleanup = &fakebank_transfer_cleanup, +      .traits = &fakebank_transfer_traits +    }; + +    return cmd; +  } +} + + +/** + * Create "fakebank transfer" CMD, letting the caller specifying + * the merchant instance.  This version is useful when a tip + * reserve should be topped up, in fact the interpreter will need + * the "tipping instance" in order to get the instance public key + * and make a wire transfer subject out of it. + * + * @param label command label. + * @param amount amount to transfer. + * @param bank_url base URL of the bank that implements this + *        wire transer.  For simplicity, both credit and debit + *        bank account exist at the same bank. + * @param debit_account_no which account (expressed as a number) + *        gives money. + * @param credit_account_no which account (expressed as a number) + *        receives money. + * + * @param auth_username username identifying the @a + *        debit_account_no at the bank. + * @param auth_password password for @a auth_username. + * @param instance the instance that runs the tipping.  Under this + *        instance, the configuration file will provide the private + *        key of the tipping reserve.  This data will then used to + *        construct the wire transfer subject line. + * @param config_filename configuration file to use. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming_with_instance +  (const char *label, +  const char *amount, +  const char *account_base_url, +  const struct TALER_BANK_AuthenticationData *auth, +  const char *payto_credit_account, +  const char *instance, +  const char *config_filename) +{ +  struct AdminAddIncomingState *fts; + +  fts = GNUNET_new (struct AdminAddIncomingState); +  fts->debit_url = account_base_url; +  fts->payto_credit_account = payto_credit_account; +  fts->auth = *auth; +  fts->instance = instance; +  fts->config_filename = config_filename; +  if (GNUNET_OK != +      TALER_string_to_amount (amount, +                              &fts->amount)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at %s\n", +                amount, +                label); +    GNUNET_assert (0); +  } +  { +    struct TALER_TESTING_Command cmd = { +      .cls = fts, +      .label = label, +      .run = &fakebank_transfer_run, +      .cleanup = &fakebank_transfer_cleanup, +      .traits = &fakebank_transfer_traits +    }; + +    return cmd; +  } +} + + +/** + * Modify a fakebank transfer command to enable retries when the + * reserve is not yet full or we get other transient errors from the + * fakebank. + * + * @param cmd a fakebank transfer command + * @return the command with retries enabled + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_admin_add_incoming_retry (struct TALER_TESTING_Command cmd) +{ +  struct AdminAddIncomingState *fts; + +  GNUNET_assert (&fakebank_transfer_run == cmd.run); +  fts = cmd.cls; +  fts->do_retry = GNUNET_YES; +  return cmd; +} + + +/* end of testing_api_cmd_admin_add_incoming.c */ diff --git a/src/bank-lib/testing_api_cmd_history_credit.c b/src/bank-lib/testing_api_cmd_history_credit.c index 5c2b34d0..fefb2dda 100644 --- a/src/bank-lib/testing_api_cmd_history_credit.c +++ b/src/bank-lib/testing_api_cmd_history_credit.c @@ -60,6 +60,11 @@ struct HistoryState    struct TALER_BANK_CreditHistoryHandle *hh;    /** +   * Authentication data for the operation. +   */ +  struct TALER_BANK_AuthenticationData auth; + +  /**     * Expected number of results (= rows).     */    uint64_t results_obtained; @@ -683,7 +688,7 @@ history_run (void *cls,    hs->hh = TALER_BANK_credit_history (is->ctx,                                        hs->account_url, -                                      NULL, +                                      &hs->auth,                                        row_id,                                        hs->num_results,                                        &history_cb, @@ -731,6 +736,8 @@ history_cleanup (void *cls,  struct TALER_TESTING_Command  TALER_TESTING_cmd_bank_credits (const char *label,                                  const char *account_url, +                                const struct +                                TALER_BANK_AuthenticationData *auth,                                  const char *start_row_reference,                                  long long num_results)  { @@ -740,7 +747,7 @@ TALER_TESTING_cmd_bank_credits (const char *label,    hs->account_url = GNUNET_strdup (account_url);    hs->start_row_reference = start_row_reference;    hs->num_results = num_results; - +  hs->auth = *auth;    {      struct TALER_TESTING_Command cmd = {        .label = label, diff --git a/src/bank-lib/testing_api_cmd_history_debit.c b/src/bank-lib/testing_api_cmd_history_debit.c index 93f84da0..96f989c0 100644 --- a/src/bank-lib/testing_api_cmd_history_debit.c +++ b/src/bank-lib/testing_api_cmd_history_debit.c @@ -57,6 +57,11 @@ struct HistoryState    long long num_results;    /** +   * Login data to use to authenticate. +   */ +  struct TALER_BANK_AuthenticationData auth; + +  /**     * Handle to a pending "history" operation.     */    struct TALER_BANK_DebitHistoryHandle *hh; @@ -67,7 +72,7 @@ struct HistoryState    uint64_t results_obtained;    /** -   * Set to GNUNET_YES if the callback detects something +   * Set to #GNUNET_YES if the callback detects something     * unexpected.     */    int failed; @@ -684,7 +689,7 @@ history_run (void *cls,    hs->hh = TALER_BANK_debit_history (is->ctx,                                       hs->account_url, -                                     NULL, +                                     &hs->auth,                                       row_id,                                       hs->num_results,                                       &history_cb, @@ -722,6 +727,7 @@ history_cleanup (void *cls,   * @param label command label.   * @param account_url base URL of the account offering the "history"   *        operation. + * @param auth login data to use   * @param start_row_reference reference to a command that can   *        offer a row identifier, to be used as the starting row   *        to accept in the result. @@ -731,6 +737,7 @@ history_cleanup (void *cls,  struct TALER_TESTING_Command  TALER_TESTING_cmd_bank_debits (const char *label,                                 const char *account_url, +                               const struct TALER_BANK_AuthenticationData *auth,                                 const char *start_row_reference,                                 long long num_results)  { @@ -740,6 +747,7 @@ TALER_TESTING_cmd_bank_debits (const char *label,    hs->account_url = account_url;    hs->start_row_reference = start_row_reference;    hs->num_results = num_results; +  hs->auth = *auth;    {      struct TALER_TESTING_Command cmd = { diff --git a/src/bank-lib/testing_api_cmd_transfer.c b/src/bank-lib/testing_api_cmd_transfer.c new file mode 100644 index 00000000..d5a3872e --- /dev/null +++ b/src/bank-lib/testing_api_cmd_transfer.c @@ -0,0 +1,394 @@ +/* +  This file is part of TALER +  Copyright (C) 2018-2020 Taler Systems SA + +  TALER is free software; you can redistribute it and/or modify it +  under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 3, or (at your +  option) any later version. + +  TALER is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public +  License along with TALER; see the file COPYING.  If not, see +  <http://www.gnu.org/licenses/> +*/ +/** + * @file exchange-lib/testing_api_cmd_transfer.c + * @brief implementation of a bank /transfer command + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "backoff.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" + + +/** + * State for a "transfer" CMD. + */ +struct TransferState +{ + +  /** +   * Wire transfer amount. +   */ +  struct TALER_Amount amount; + +  /** +   * Base URL of the debit account. +   */ +  const char *account_debit_url; + +  /** +   * Money receiver account URL. +   */ +  const char *payto_credit_account; + +  /** +   * Username to use for authentication. +   */ +  struct TALER_BANK_AuthenticationData auth; + +  /** +   * Base URL of the exchange. +   */ +  const char *exchange_base_url; + +  /** +   * Wire transfer identifier to use. +   */ +  struct TALER_WireTransferIdentifierRawP wtid; + +  /** +   * Handle to the pending request at the fakebank. +   */ +  struct TALER_BANK_WireExecuteHandle *weh; + +  /** +   * Interpreter state. +   */ +  struct TALER_TESTING_Interpreter *is; + +  /** +   * Set to the wire transfer's unique ID. +   */ +  uint64_t serial_id; + +  /** +   * Timestamp of the transaction (as returned from the bank). +   */ +  struct GNUNET_TIME_Absolute timestamp; + +  /** +   * Configuration filename.  Used to get the tip reserve key +   * filename (used to obtain a public key to write in the +   * transfer subject). +   */ +  const char *config_filename; + +  /** +   * Task scheduled to try later. +   */ +  struct GNUNET_SCHEDULER_Task *retry_task; + +  /** +   * How long do we wait until we retry? +   */ +  struct GNUNET_TIME_Relative backoff; + +  /** +   * Was this command modified via +   * #TALER_TESTING_cmd_admin_add_incoming_with_retry to +   * enable retries? +   */ +  int do_retry; +}; + + +/** + * Run the "transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +transfer_run (void *cls, +              const struct TALER_TESTING_Command *cmd, +              struct TALER_TESTING_Interpreter *is); + + +/** + * Task scheduled to re-try #transfer_run. + * + * @param cls a `struct TransferState` + */ +static void +do_retry (void *cls) +{ +  struct TransferState *fts = cls; + +  fts->retry_task = NULL; +  transfer_run (fts, +                NULL, +                fts->is); +} + + +/** + * This callback will process the fakebank response to the wire + * transfer.  It just checks whether the HTTP response code is + * acceptable. + * + * @param cls closure with the interpreter state + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for + *        successful status request; 0 if the exchange's reply is + *        bogus (fails to follow the protocol) + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param serial_id unique ID of the wire transfer + * @param timestamp time stamp of the transaction made. + */ +static void +confirmation_cb (void *cls, +                 unsigned int http_status, +                 enum TALER_ErrorCode ec, +                 uint64_t serial_id, +                 struct GNUNET_TIME_Absolute timestamp) +{ +  struct TransferState *fts = cls; +  struct TALER_TESTING_Interpreter *is = fts->is; + +  fts->weh = NULL; +  if (MHD_HTTP_OK != http_status) +  { +    if (GNUNET_YES == fts->do_retry) +    { +      if ( (0 == http_status) || +           (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) || +           (MHD_HTTP_INTERNAL_SERVER_ERROR == http_status) ) +      { +        GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                    "Retrying transfer failed with %u/%d\n", +                    http_status, +                    (int) ec); +        /* on DB conflicts, do not use backoff */ +        if (TALER_EC_DB_COMMIT_FAILED_ON_RETRY == ec) +          fts->backoff = GNUNET_TIME_UNIT_ZERO; +        else +          fts->backoff = EXCHANGE_LIB_BACKOFF (fts->backoff); +        fts->retry_task = GNUNET_SCHEDULER_add_delayed +                            (fts->backoff, +                            &do_retry, +                            fts); +        return; +      } +    } +    GNUNET_break (0); +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Fakebank returned HTTP status %u/%d\n", +                http_status, +                (int) ec); +    TALER_TESTING_interpreter_fail (is); +    return; +  } + +  fts->serial_id = serial_id; +  fts->timestamp = timestamp; +  TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the "transfer" CMD. + * + * @param cls closure. + * @param cmd CMD being run. + * @param is interpreter state. + */ +static void +transfer_run (void *cls, +              const struct TALER_TESTING_Command *cmd, +              struct TALER_TESTING_Interpreter *is) +{ +  struct TransferState *fts = cls; +  void *buf; +  size_t buf_size; + +  TALER_BANK_prepare_wire_transfer (fts->payto_credit_account, +                                    &fts->amount, +                                    fts->exchange_base_url, +                                    &fts->wtid, +                                    &buf, +                                    &buf_size); +  fts->is = is; +  fts->weh +    = TALER_BANK_execute_wire_transfer +        (TALER_TESTING_interpreter_get_context (is), +        fts->account_debit_url, +        &fts->auth, +        buf, +        buf_size, +        &confirmation_cb, +        fts); +  GNUNET_free (buf); +  if (NULL == fts->weh) +  { +    GNUNET_break (0); +    TALER_TESTING_interpreter_fail (is); +    return; +  } +} + + +/** + * Free the state of a "fakebank transfer" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure + * @param cmd current CMD being cleaned up. + */ +static void +transfer_cleanup (void *cls, +                  const struct TALER_TESTING_Command *cmd) +{ +  struct TransferState *fts = cls; + +  if (NULL != fts->weh) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Command %s did not complete\n", +                cmd->label); +    TALER_BANK_execute_wire_transfer_cancel (fts->weh); +    fts->weh = NULL; +  } +  if (NULL != fts->retry_task) +  { +    GNUNET_SCHEDULER_cancel (fts->retry_task); +    fts->retry_task = NULL; +  } +  GNUNET_free (fts); +} + + +/** + * Offer internal data from a "fakebank transfer" CMD to other + * commands. + * + * @param cls closure. + * @param ret[out] result + * @param trait name of the trait. + * @param index index number of the object to offer. + * @return #GNUNET_OK on success. + */ +static int +transfer_traits (void *cls, +                 const void **ret, +                 const char *trait, +                 unsigned int index) +{ +  struct TransferState *fts = cls; +  struct TALER_TESTING_Trait traits[] = { +    TALER_TESTING_make_trait_url (1, fts->account_debit_url), +    TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), +    TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->payto_credit_account), +    TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT (fts->account_debit_url), +    TALER_TESTING_make_trait_amount_obj (0, &fts->amount), +    TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), +    TALER_TESTING_make_trait_wtid (0, +                                   &fts->wtid), +    TALER_TESTING_trait_end () +  }; + +  return TALER_TESTING_get_trait (traits, +                                  ret, +                                  trait, +                                  index); +} + + +/** + * Create transfer command. + * + * @param label command label. + * @param amount amount to transfer. + * @param account_base_url base URL of the account that implements this + *        wire transer (which account gives money). + * @param auth authentication data to use + * @param payto_credit_account which account receives money. + * @param wtid wire transfer identifier to use + * @param exchange_base_url exchange URL to use + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_transfer +  (const char *label, +  const char *amount, +  const char *account_base_url, +  const struct TALER_BANK_AuthenticationData *auth, +  const char *payto_credit_account, +  const struct TALER_WireTransferIdentifierRawP *wtid, +  const char *exchange_base_url) +{ +  struct TransferState *fts; + +  fts = GNUNET_new (struct TransferState); +  fts->account_debit_url = account_base_url; +  fts->exchange_base_url = exchange_base_url; +  fts->payto_credit_account = payto_credit_account; +  fts->auth = *auth; +  fts->wtid = *wtid; +  if (GNUNET_OK != +      TALER_string_to_amount (amount, +                              &fts->amount)) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                "Failed to parse amount `%s' at %s\n", +                amount, +                label); +    GNUNET_assert (0); +  } + +  { +    struct TALER_TESTING_Command cmd = { +      .cls = fts, +      .label = label, +      .run = &transfer_run, +      .cleanup = &transfer_cleanup, +      .traits = &transfer_traits +    }; + +    return cmd; +  } +} + + +/** + * Modify a transfer command to enable retries when the reserve is not yet + * full or we get other transient errors from the bank. + * + * @param cmd a fakebank transfer command + * @return the command with retries enabled + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_transfer_retry (struct TALER_TESTING_Command cmd) +{ +  struct TransferState *fts; + +  GNUNET_assert (&transfer_run == cmd.run); +  fts = cmd.cls; +  fts->do_retry = GNUNET_YES; +  return cmd; +} + + +/* end of testing_api_cmd_transfer.c */ | 
