summaryrefslogtreecommitdiff
path: root/src/bank-lib
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-01-11 15:19:56 +0100
committerChristian Grothoff <christian@grothoff.org>2020-01-11 15:20:17 +0100
commit9443c10d7feb0d91323869dd08ec61ca781564f4 (patch)
treefd617ea56cc1d2ea370ce7e5467574a536b52d28 /src/bank-lib
parent554da10133eb491b352a106b98ebeaed797133bb (diff)
downloadexchange-9443c10d7feb0d91323869dd08ec61ca781564f4.tar.gz
exchange-9443c10d7feb0d91323869dd08ec61ca781564f4.tar.bz2
exchange-9443c10d7feb0d91323869dd08ec61ca781564f4.zip
major refactoring, eliminating wire-plugins and moving towards new bank API. main code compiles, testcases known to fail, code sure not to fully work yet
Diffstat (limited to 'src/bank-lib')
-rw-r--r--src/bank-lib/Makefile.am13
-rw-r--r--src/bank-lib/bank_api_admin.c71
-rw-r--r--src/bank-lib/bank_api_common.c86
-rw-r--r--src/bank-lib/bank_api_common.h12
-rw-r--r--src/bank-lib/bank_api_credit.c315
-rw-r--r--src/bank-lib/bank_api_debit.c (renamed from src/bank-lib/bank_api_history.c)256
-rw-r--r--src/bank-lib/bank_api_parse.c54
-rw-r--r--src/bank-lib/bank_api_reject.c242
-rw-r--r--src/bank-lib/bank_api_transaction.c368
-rw-r--r--src/bank-lib/fakebank.c728
-rw-r--r--src/bank-lib/fakebank.h283
-rw-r--r--src/bank-lib/fakebank_history.c204
-rw-r--r--src/bank-lib/taler-bank-transfer.c55
-rw-r--r--src/bank-lib/test_bank_api.c2
-rw-r--r--src/bank-lib/testing_api_cmd_history_credit.c (renamed from src/bank-lib/testing_api_cmd_history.c)484
-rw-r--r--src/bank-lib/testing_api_cmd_history_debit.c758
-rw-r--r--src/bank-lib/testing_api_cmd_reject.c223
17 files changed, 2268 insertions, 1886 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 3bb863232..371f5e6d6 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -39,8 +39,9 @@ libtalerbank_la_LDFLAGS = \
libtalerbank_la_SOURCES = \
bank_api_admin.c \
bank_api_common.c bank_api_common.h \
- bank_api_history.c \
- bank_api_reject.c \
+ bank_api_credit.c \
+ bank_api_debit.c \
+ bank_api_transaction.c \
bank_api_parse.c
libtalerbank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@@ -55,10 +56,10 @@ libtalerfakebank_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalerfakebank_la_SOURCES = \
- fakebank_history.c \
- fakebank.c fakebank.h
+ fakebank.c
libtalerfakebank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/mhd/libtalermhd.la \
-lgnunetjson \
-lgnunetutil \
-ljansson \
@@ -69,8 +70,8 @@ libtalerbanktesting_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalerbanktesting_la_SOURCES = \
- testing_api_cmd_history.c \
- testing_api_cmd_reject.c \
+ testing_api_cmd_history_credit.c \
+ testing_api_cmd_history_debit.c \
testing_api_helpers.c
libtalerbanktesting_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c
index 5240a3724..068fd0e0e 100644
--- a/src/bank-lib/bank_api_admin.c
+++ b/src/bank-lib/bank_api_admin.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015, 2016, 2017 Taler Systems SA
+ Copyright (C) 2015--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
@@ -166,13 +166,11 @@ handle_admin_add_incoming_finished (void *cls,
* to the operators of the bank.
*
* @param ctx curl context for the event loop
- * @param bank_base_url URL of the bank (used to execute this request)
+ * @param account_base_url URL of the bank (used to execute this request)
* @param auth authentication data to send to the bank
- * @param exchange_base_url base URL of the exchange (for tracking)
- * @param subject wire transfer subject for the transfer
+ * @param reserve_pub wire transfer subject for the transfer
* @param amount amount that was deposited
- * @param debit_account_no account number to withdraw from (53 bits at most)
- * @param credit_account_no account number to deposit into (53 bits at most)
+ * @param credit_account account to deposit into (payto)
* @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for the above callback
* @return NULL
@@ -181,13 +179,12 @@ handle_admin_add_incoming_finished (void *cls,
*/
struct TALER_BANK_AdminAddIncomingHandle *
TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
- const char *bank_base_url,
+ const char *account_base_url,
const struct TALER_BANK_AuthenticationData *auth,
- const char *exchange_base_url,
- const char *subject,
+ const struct
+ TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount,
- uint64_t debit_account_no,
- uint64_t credit_account_no,
+ const char *credit_account,
TALER_BANK_AdminAddIncomingResultCallback res_cb,
void *res_cb_cls)
{
@@ -195,18 +192,10 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
json_t *admin_obj;
CURL *eh;
- if (NULL == exchange_base_url)
- {
- GNUNET_break (0);
- return NULL;
- }
- admin_obj = json_pack ("{s:{s:s}, s:s, s:s, s:o, s:I, s:I}",
- "auth", "type", "basic",
- "exchange_url", exchange_base_url,
- "subject", subject,
+ admin_obj = json_pack ("{s:o, s:o, s:s}",
+ "subject", GNUNET_JSON_from_data_auto (reserve_pub),
"amount", TALER_JSON_from_amount (amount),
- "debit_account", (json_int_t) debit_account_no,
- "credit_account", (json_int_t) credit_account_no);
+ "credit_account", credit_account);
if (NULL == admin_obj)
{
GNUNET_break (0);
@@ -215,27 +204,33 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle);
aai->cb = res_cb;
aai->cb_cls = res_cb_cls;
- aai->request_url = TALER_BANK_path_to_url_ (bank_base_url,
+ aai->request_url = TALER_BANK_path_to_url_ (account_base_url,
"/admin/add/incoming");
- aai->post_ctx.headers = TALER_BANK_make_auth_header_ (auth);
-
- GNUNET_assert
- (NULL != (aai->post_ctx.headers = curl_slist_append
- (aai->post_ctx.headers,
- "Content-Type: application/json")));
+ aai->post_ctx.headers = curl_slist_append
+ (aai->post_ctx.headers,
+ "Content-Type: application/json");
eh = curl_easy_init ();
-
- GNUNET_assert (GNUNET_OK ==
- TALER_curl_easy_post (&aai->post_ctx, eh, admin_obj));
-
+ if ( (GNUNET_OK !=
+ TALER_BANK_setup_auth_ (eh,
+ auth)) ||
+ (CURLE_OK !=
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ aai->request_url)) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&aai->post_ctx,
+ eh,
+ admin_obj)) )
+ {
+ GNUNET_break (0);
+ TALER_BANK_admin_add_incoming_cancel (aai);
+ curl_easy_cleanup (eh);
+ json_decref (admin_obj);
+ return NULL;
+ }
json_decref (admin_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_URL,
- aai->request_url));
-
aai->job = GNUNET_CURL_job_add2 (ctx,
eh,
aai->post_ctx.headers,
diff --git a/src/bank-lib/bank_api_common.c b/src/bank-lib/bank_api_common.c
index 50769dae2..8a8f4289f 100644
--- a/src/bank-lib/bank_api_common.c
+++ b/src/bank-lib/bank_api_common.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015, 2016, 2017 GNUnet e.V.
+ Copyright (C) 2015-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
@@ -24,67 +24,47 @@
/**
- * Append HTTP key-value pair to curl header list.
+ * Set authentication data in @a easy from @a auth.
+ * The API currently specifies the use of HTTP basic
+ * authentication.
*
- * @param hdr list to append to, can be NULL
- * @param key key to append
- * @param value value to append
- * @return new list, NULL on error
+ * @param easy curl handle to setup for authentication
+ * @param auth authentication data to use
+ * @return #GNUNET_OK in success
*/
-static struct curl_slist *
-append (struct curl_slist *hdr,
- const char *key,
- const char *value)
+int
+TALER_BANK_setup_auth_ (CURL *easy,
+ const struct TALER_BANK_AuthenticationData *auth)
{
- char *str;
- struct curl_slist *ret;
-
- GNUNET_asprintf (&str,
- "%s: %s",
- key,
- value);
- ret = curl_slist_append (hdr,
- str);
- GNUNET_free (str);
- if (NULL == ret)
- {
- GNUNET_break (0);
- curl_slist_free_all (hdr);
- return NULL;
- }
- return ret;
-}
-
-
-/**
- * Build authentication header from @a auth.
- *
- * @param auth authentication data to use.
- *
- * @return NULL on error, otherwise curl headers to use.
- */
-struct curl_slist *
-TALER_BANK_make_auth_header_
- (const struct TALER_BANK_AuthenticationData *auth)
-{
- struct curl_slist *authh;
+ int ret;
+ ret = GNUNET_OK;
switch (auth->method)
{
case TALER_BANK_AUTH_NONE:
- return NULL;
+ return GNUNET_OK;
case TALER_BANK_AUTH_BASIC:
- authh = append (NULL,
- "X-Taler-Bank-Username",
- auth->details.basic.username);
- if (NULL == authh)
- return NULL;
- authh = append (authh,
- "X-Taler-Bank-Password",
- auth->details.basic.password);
- return authh;
+ {
+ char *up;
+
+ GNUNET_asprintf (&up,
+ "%s:%s",
+ auth->details.basic.username,
+ auth->details.basic.password);
+ if ( (CURLE_OK !=
+ curl_easy_setopt (easy,
+ CURLOPT_HTTPAUTH,
+ CURLAUTH_BASIC)) ||
+ (CURLE_OK !=
+ curl_easy_setopt (easy,
+ CURLOPT_USERPWD,
+ up)) )
+ ret = GNUNET_SYSERR;
+ GNUNET_free (up);
+ break;
+ }
}
- return NULL;
+ return ret;
}
diff --git a/src/bank-lib/bank_api_common.h b/src/bank-lib/bank_api_common.h
index fcf2029d4..1120ed94e 100644
--- a/src/bank-lib/bank_api_common.h
+++ b/src/bank-lib/bank_api_common.h
@@ -40,6 +40,18 @@ TALER_BANK_make_auth_header_ (const struct TALER_BANK_AuthenticationData *auth);
/**
+ * Set authentication data in @a easy from @a auth.
+ *
+ * @param easy curl handle to setup for authentication
+ * @param auth authentication data to use
+ * @return #GNUNET_OK in success
+ */
+int
+TALER_BANK_setup_auth_ (CURL *easy,
+ const struct TALER_BANK_AuthenticationData *auth);
+
+
+/**
* Obtain the URL to use for an API request.
*
* @param u base URL of the bank
diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c
new file mode 100644
index 000000000..ed0a1e2a7
--- /dev/null
+++ b/src/bank-lib/bank_api_credit.c
@@ -0,0 +1,315 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2017--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/bank_api_history.c
+ * @brief Implementation of the /history[-range]
+ * requests of the bank's HTTP API.
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "bank_api_common.h"
+#include <microhttpd.h> /* just for HTTP status codes */
+#include "taler_signatures.h"
+
+
+/**
+ * @brief A /history Handle
+ */
+struct TALER_BANK_CreditHistoryHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *request_url;
+
+ /**
+ * The base URL of the bank.
+ */
+ char *bank_base_url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_BANK_CreditResultCallback hcb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *hcb_cls;
+};
+
+
+/**
+ * Parse history given in JSON format and invoke the callback on each item.
+ *
+ * @param hh handle to the account history request
+ * @param history JSON array with the history
+ * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
+ * were set,
+ * #GNUNET_SYSERR if there was a protocol violation in @a history
+ */
+static int
+parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
+ const json_t *history)
+{
+ json_t *history_array;
+
+ if (NULL == (history_array = json_object_get (history,
+ "data")))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! json_is_array (history_array))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ for (unsigned int i = 0; i<json_array_size (history_array); i++)
+ {
+ struct TALER_BANK_CreditDetails td;
+ uint64_t row_id;
+ struct GNUNET_JSON_Specification hist_spec[] = {
+ TALER_JSON_spec_amount ("amount",
+ &td.amount),
+ GNUNET_JSON_spec_absolute_time ("date",
+ &td.execution_date),
+ GNUNET_JSON_spec_uint64 ("row_id",
+ &row_id),
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &td.reserve_pub),
+ GNUNET_JSON_spec_string ("counterpart",
+ &td.account_url),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *transaction = json_array_get (history_array,
+ i);
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (transaction,
+ hist_spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ hh->hcb (hh->hcb_cls,
+ MHD_HTTP_OK,
+ TALER_EC_NONE,
+ row_id,
+ &td,
+ transaction);
+ GNUNET_JSON_parse_free (hist_spec);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /history request.
+ *
+ * @param cls the `struct TALER_BANK_CreditHistoryHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_history_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_BANK_CreditHistoryHandle *hh = cls;
+ enum TALER_ErrorCode ec;
+ const json_t *j = response;
+
+ hh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ ec = TALER_EC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ parse_account_history (hh,
+ j))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_INVALID_RESPONSE;
+ break;
+ }
+ response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
+ ec = TALER_EC_NONE;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ ec = TALER_EC_NONE;
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the bank is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ /* Access denied */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ /* Nothing really to verify, bank says one of the signatures is
+ invalid; as we checked them, this should never happen, we
+ should pass the JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ (unsigned int) response_code);
+ GNUNET_break (0);
+ ec = TALER_BANK_parse_ec_ (j);
+ response_code = 0;
+ break;
+ }
+ hh->hcb (hh->hcb_cls,
+ response_code,
+ ec,
+ 0LLU,
+ NULL,
+ j);
+ TALER_BANK_credit_history_cancel (hh);
+}
+
+
+/**
+ * Request the credit history of the exchange's bank account.
+ *
+ * @param ctx curl context for the event loop
+ * @param bank_base_url URL of the base INCLUDING account number
+ * @param auth authentication data to use
+ * @param start_row from which row on do we want to get results,
+ * use UINT64_MAX for the latest; exclusive
+ * @param num_results how many results do we want;
+ * negative numbers to go into the past, positive numbers
+ * to go into the future starting at @a start_row;
+ * must not be zero.
+ * @param hres_cb the callback to call with the transaction
+ * history
+ * @param hres_cb_cls closure for the above callback
+ * @return NULL if the inputs are invalid (i.e. zero value for
+ * @e num_results). In this case, the callback is not
+ * called.
+ */
+struct TALER_BANK_CreditHistoryHandle *
+TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
+ const char *bank_base_url,
+ const struct TALER_BANK_AuthenticationData *auth,
+ uint64_t start_row,
+ int64_t num_results,
+ TALER_BANK_CreditResultCallback hres_cb,
+ void *hres_cb_cls)
+{
+ char *url;
+ struct TALER_BANK_CreditHistoryHandle *hh;
+ CURL *eh;
+
+ if (0 == num_results)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ if (UINT64_MAX == start_row)
+ GNUNET_asprintf (&url,
+ "/history&delta=%lld",
+ (long long) num_results);
+ else
+ GNUNET_asprintf (&url,
+ "/history&delta=%lld&start=%llu",
+ (long long) num_results,
+ start_row);
+ hh = GNUNET_new (struct TALER_BANK_CreditHistoryHandle);
+ hh->hcb = hres_cb;
+ hh->hcb_cls = hres_cb_cls;
+ hh->bank_base_url = GNUNET_strdup (bank_base_url);
+ hh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
+ url);
+
+ eh = curl_easy_init ();
+ if ( (GNUNET_OK !=
+ TALER_BANK_setup_auth_ (eh,
+ auth)) ||
+ (CURLE_OK !=
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ hh->request_url)) )
+ {
+ GNUNET_break (0);
+ TALER_BANK_credit_history_cancel (hh);
+ curl_easy_cleanup (eh);
+ GNUNET_free (url);
+ return NULL;
+ }
+ hh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ NULL,
+ &handle_history_finished,
+ hh);
+ GNUNET_free (url);
+ return hh;
+}
+
+
+/**
+ * Cancel a history request. This function cannot be
+ * used on a request handle if a response is already
+ * served for it.
+ *
+ * @param hh the history request handle
+ */
+void
+TALER_BANK_credit_history_cancel (struct TALER_BANK_CreditHistoryHandle *hh)
+{
+ if (NULL != hh->job)
+ {
+ GNUNET_CURL_job_cancel (hh->job);
+ hh->job = NULL;
+ }
+ GNUNET_free (hh->request_url);
+ GNUNET_free (hh->bank_base_url);
+ GNUNET_free (hh);
+}
+
+
+/* end of bank_api_credit.c */
diff --git a/src/bank-lib/bank_api_history.c b/src/bank-lib/bank_api_debit.c
index f5013b85b..848362438 100644
--- a/src/bank-lib/bank_api_history.c
+++ b/src/bank-lib/bank_api_debit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2017 GNUnet e.V. & Inria
+ Copyright (C) 2017--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
@@ -32,7 +32,7 @@
/**
* @brief A /history Handle
*/
-struct TALER_BANK_HistoryHandle
+struct TALER_BANK_DebitHistoryHandle
{
/**
@@ -51,14 +51,9 @@ struct TALER_BANK_HistoryHandle
struct GNUNET_CURL_Job *job;
/**
- * HTTP authentication-related headers for the request.
- */
- struct curl_slist *authh;
-
- /**
* Function to call with the result.
*/
- TALER_BANK_HistoryResultCallback hcb;
+ TALER_BANK_DebitResultCallback hcb;
/**
* Closure for @a cb.
@@ -77,37 +72,39 @@ struct TALER_BANK_HistoryHandle
* #GNUNET_SYSERR if there was a protocol violation in @a history
*/
static int
-parse_account_history (struct TALER_BANK_HistoryHandle *hh,
+parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
const json_t *history)
{
json_t *history_array;
- char *bank_hostname;
- if (NULL == (history_array = json_object_get (history, "data")))
+ if (NULL == (history_array = json_object_get (history,
+ "data")))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! json_is_array (history_array))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
for (unsigned int i = 0; i<json_array_size (history_array); i++)
{
- struct TALER_BANK_TransferDetails td;
- const char *sign;
- uint64_t other_account;
+ struct TALER_BANK_DebitDetails td;
uint64_t row_id;
- enum TALER_BANK_Direction direction;
struct GNUNET_JSON_Specification hist_spec[] = {
- GNUNET_JSON_spec_string ("sign",
- &sign),
TALER_JSON_spec_amount ("amount",
&td.amount),
GNUNET_JSON_spec_absolute_time ("date",
&td.execution_date),
GNUNET_JSON_spec_uint64 ("row_id",
&row_id),
- GNUNET_JSON_spec_string ("wt_subject",
- (const char **) &td.wire_transfer_subject),
- GNUNET_JSON_spec_uint64 ("counterpart",
- &other_account),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &td.wtid),
+ GNUNET_JSON_spec_string ("counterpart",
+ &td.account_url),
+ GNUNET_JSON_spec_string ("exchange_base_url",
+ &td.exchange_base_url),
GNUNET_JSON_spec_end ()
};
json_t *transaction = json_array_get (history_array,
@@ -121,45 +118,12 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
-
- if (0 == strcasecmp (sign,
- "+"))
- direction = TALER_BANK_DIRECTION_CREDIT;
- else if (0 == strcasecmp (sign,
- "-"))
- direction = TALER_BANK_DIRECTION_DEBIT;
- else if (0 == strcasecmp (sign,
- "cancel+"))
- direction = TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_CANCEL;
- else if (0 == strcasecmp (sign,
- "cancel-"))
- direction = TALER_BANK_DIRECTION_DEBIT | TALER_BANK_DIRECTION_CANCEL;
- else
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (hist_spec);
- return GNUNET_SYSERR;
- }
- /* Note, bank_base_url has _always_ the protocol scheme
- * and it proved to be good at this point. */
- bank_hostname = strchr (hh->bank_base_url, ':');
- GNUNET_assert (NULL != bank_hostname);
- bank_hostname += 3;
-
- GNUNET_asprintf (&td.account_url,
- ('/' == bank_hostname[strlen (bank_hostname) - 1])
- ? "payto://x-taler-bank/%s%llu"
- : "payto://x-taler-bank/%s/%llu",
- bank_hostname,
- (unsigned long long) other_account);
hh->hcb (hh->hcb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
- direction,
row_id,
&td,
transaction);
- GNUNET_free (td.account_url);
GNUNET_JSON_parse_free (hist_spec);
}
return GNUNET_OK;
@@ -170,7 +134,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
* Function called when we're done processing the
* HTTP /history request.
*
- * @param cls the `struct TALER_BANK_HistoryHandle`
+ * @param cls the `struct TALER_BANK_DebitHistoryHandle`
* @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error
*/
@@ -179,7 +143,7 @@ handle_history_finished (void *cls,
long response_code,
const void *response)
{
- struct TALER_BANK_HistoryHandle *hh = cls;
+ struct TALER_BANK_DebitHistoryHandle *hh = cls;
enum TALER_ErrorCode ec;
const json_t *j = response;
@@ -243,120 +207,19 @@ handle_history_finished (void *cls,
hh->hcb (hh->hcb_cls,
response_code,
ec,
- TALER_BANK_DIRECTION_NONE,
0LLU,
NULL,
j);
- TALER_BANK_history_cancel (hh);
-}
-
-
-/**
- * Backend of both the /history[-range] requests.
- *
- * @param ctx curl context for the event loop
- * @param bank_base_url base URL of the bank.
- * @param urlargs path + URL arguments.
- * @param auth authentication data to use
- * @param hres_cb the callback to call with the transaction
- * history
- * @param hres_cb_cls closure for the above callback
- * @return NULL if the inputs are invalid (i.e. zero value for
- * @e num_results). In this case, the callback is not
- * called.
- */
-static struct TALER_BANK_HistoryHandle *
-put_history_job (struct GNUNET_CURL_Context *ctx,
- const char *bank_base_url,
- const char *urlargs,
- const struct TALER_BANK_AuthenticationData *auth,
- TALER_BANK_HistoryResultCallback hres_cb,
- void *hres_cb_cls)
-{
- struct TALER_BANK_HistoryHandle *hh;
- CURL *eh;
-
- hh = GNUNET_new (struct TALER_BANK_HistoryHandle);
- hh->hcb = hres_cb;
- hh->hcb_cls = hres_cb_cls;
- hh->bank_base_url = GNUNET_strdup (bank_base_url);
- hh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
- urlargs);
-
- hh->authh = TALER_BANK_make_auth_header_ (auth);
- eh = curl_easy_init ();
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_URL,
- hh->request_url));
- hh->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- hh->authh,
- &handle_history_finished,
- hh);
- return hh;
+ TALER_BANK_debit_history_cancel (hh);
}
/**
- * Convert fixed value 'direction' into string.
- *
- * @param direction the value to convert.
- * @return string representation of @a direction. NULL on error
- */
-static const char *
-conv_direction (enum TALER_BANK_Direction direction)
-{
- if (TALER_BANK_DIRECTION_NONE == direction)
- {
- /* Should just never happen. */
- GNUNET_break (0);
- return NULL;
- }
- if (TALER_BANK_DIRECTION_BOTH ==
- (TALER_BANK_DIRECTION_BOTH & direction))
- return "both";
- else if (TALER_BANK_DIRECTION_CREDIT ==
- (TALER_BANK_DIRECTION_CREDIT & direction))
- return "credit";
- else if (TALER_BANK_DIRECTION_DEBIT ==
- (TALER_BANK_DIRECTION_BOTH & direction)) /*why use 'both' flag?*/
- return "debit";
- /* Should just never happen. */
- GNUNET_break (0);
- return NULL;
-}
-
-
-/**
- * Convert fixed value 'direction' into string representation
- * of the "cancel" argument.
- *
- * @param direction the value to convert.
- * @return string representation of @a direction
- */
-static const char *
-conv_cancel (enum TALER_BANK_Direction direction)
-{
- if (TALER_BANK_DIRECTION_CANCEL ==
- (TALER_BANK_DIRECTION_CANCEL & direction))
- return "show";
- return "omit";
-}
-
-
-/**
- * Request the wire transfer history of a bank account.
+ * Request the debit history of the exchange's bank account.
*
* @param ctx curl context for the event loop
- * @param bank_base_url URL of the bank (used to execute this
- * request)
+ * @param bank_base_url URL of the base INCLUDING account number
* @param auth authentication data to use
- * @param account_number which account number should we query
- * @param direction what kinds of wire transfers should be
- * returned
- * @param ascending if GNUNET_YES, history elements will
- * be returned in chronological order.
* @param start_row from which row on do we want to get results,
* use UINT64_MAX for the latest; exclusive
* @param num_results how many results do we want;
@@ -370,20 +233,18 @@ conv_cancel (enum TALER_BANK_Direction direction)
* @e num_results). In this case, the callback is not
* called.
*/
-struct TALER_BANK_HistoryHandle *
-TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
- const char *bank_base_url,
- const struct TALER_BANK_AuthenticationData *auth,
- uint64_t account_number,
- enum TALER_BANK_Direction direction,
- unsigned int ascending,
- uint64_t start_row,
- int64_t num_results,
- TALER_BANK_HistoryResultCallback hres_cb,
- void *hres_cb_cls)
+struct TALER_BANK_DebitHistoryHandle *
+TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
+ const char *bank_base_url,
+ const struct TALER_BANK_AuthenticationData *auth,
+ uint64_t start_row,
+ int64_t num_results,
+ TALER_BANK_DebitResultCallback hres_cb,
+ void *hres_cb_cls)
{
- struct TALER_BANK_HistoryHandle *hh;
char *url;
+ struct TALER_BANK_DebitHistoryHandle *hh;
+ CURL *eh;
if (0 == num_results)
{
@@ -393,28 +254,40 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
if (UINT64_MAX == start_row)
GNUNET_asprintf (&url,
- "/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s&ordering=%s",
- (unsigned long long) account_number,
- (long long) num_results,
- conv_direction (direction),
- conv_cancel (direction),
- (GNUNET_YES == ascending) ? "ascending" : "descending");
+ "/history&delta=%lld",
+ (long long) num_results);
else
GNUNET_asprintf (&url,
- "/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s&ordering=%s&start=%llu",
- (unsigned long long) account_number,
+ "/history&delta=%lld&start=%llu",
(long long) num_results,
- conv_direction (direction),
- conv_cancel (direction),
- (GNUNET_YES == ascending) ? "ascending" : "descending",
start_row);
- hh = put_history_job (ctx,
- bank_base_url,
- url,
- auth,
- hres_cb,
- hres_cb_cls);
+ hh = GNUNET_new (struct TALER_BANK_DebitHistoryHandle);
+ hh->hcb = hres_cb;
+ hh->hcb_cls = hres_cb_cls;
+ hh->bank_base_url = GNUNET_strdup (bank_base_url);
+ hh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
+ url);
+ eh = curl_easy_init ();
+ if ( (GNUNET_OK !=
+ TALER_BANK_setup_auth_ (eh,
+ auth)) ||
+ (CURLE_OK !=
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ hh->request_url)) )
+ {
+ GNUNET_break (0);
+ TALER_BANK_debit_history_cancel (hh);
+ curl_easy_cleanup (eh);
+ GNUNET_free (url);
+ return NULL;
+ }
+ hh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ NULL,
+ &handle_history_finished,
+ hh);
GNUNET_free (url);
return hh;
}
@@ -428,18 +301,17 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
* @param hh the history request handle
*/
void
-TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh)
+TALER_BANK_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *hh)
{
if (NULL != hh->job)
{
GNUNET_CURL_job_cancel (hh->job);
hh->job = NULL;
}
- curl_slist_free_all (hh->authh);
GNUNET_free (hh->request_url);
GNUNET_free (hh->bank_base_url);
GNUNET_free (hh);
}
-/* end of bank_api_history.c */
+/* end of bank_api_debit.c */
diff --git a/src/bank-lib/bank_api_parse.c b/src/bank-lib/bank_api_parse.c
index 582e2a7dd..86288802d 100644
--- a/src/bank-lib/bank_api_parse.c
+++ b/src/bank-lib/bank_api_parse.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2018 Taler Systems SA
+ 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
@@ -24,6 +24,58 @@
/**
+ * Convenience method for parsing configuration section with bank account data.
+ *
+ * @param cfg configuration to parse
+ * @param section the section with the configuration data
+ * @param acc[out] set to the account details
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_BANK_account_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ struct TALER_Account *acc)
+{
+ char *account_url;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "URL",
+ &account_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "URL");
+ return GNUNET_SYSERR;
+ }
+ if (TALER_EC_NONE !=
+ TALER_WIRE_payto_to_account (account_url,
+ acc))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "URL",
+ "Malformed payto:// URL for x-taler-bank method");
+ GNUNET_free (account_url);
+ return GNUNET_SYSERR;
+ }
+ if (TALER_PAC_X_TALER_BANK != acc->type)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "URL",
+ "Malformed payto:// URL for x-taler-bank method");
+ GNUNET_free (account_url);
+ TALER_WIRE_account_free (acc);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (account_url);
+ return GNUNET_OK;
+}
+
+
+/**
* Parse configuration section with bank authentication data.
*
* @param cfg configuration to parse
diff --git a/src/bank-lib/bank_api_reject.c b/src/bank-lib/bank_api_reject.c
deleted file mode 100644
index 3f181bc31..000000000
--- a/src/bank-lib/bank_api_reject.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2015, 2016, 2017 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/bank_api_reject.c
- * @brief Implementation of the /reject request of the bank's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "bank_api_common.h"
-#include <microhttpd.h> /* just for HTTP status codes */
-#include "taler_signatures.h"
-
-
-/**
- * @brief A /reject Handle
- */
-struct TALER_BANK_RejectHandle
-{
-
- /**
- * The url for this request.
- */
- char *request_url;
-
- /**
- * JSON encoding of the request to POST.
- */
- char *json_enc;
-
- /**
- * Handle for the request.
- */
- struct GNUNET_CURL_Job *job;
-
- /**
- * HTTP authentication-related headers for the request.
- */
- struct curl_slist *authh;
-
- /**
- * Function to call with the result.
- */
- TALER_BANK_RejectResultCallback cb;
-
- /**
- * Closure for @a cb.
- */
- void *cb_cls;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reject request.
- *
- * @param cls the `struct TALER_BANK_RejectHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reject_finished (void *cls,
- long response_code,
- const void *response)
-{
- struct TALER_BANK_RejectHandle *rh = cls;
- enum TALER_ErrorCode ec;
- const json_t *j = response;
-
- rh->job = NULL;
- switch (response_code)
- {
- case 0:
- ec = TALER_EC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_OK:
- GNUNET_break_op (0);
- response_code = 0;
- ec = TALER_EC_INVALID_RESPONSE;
- break;
- case MHD_HTTP_NO_CONTENT:
- ec = TALER_EC_NONE;
- break;
- case MHD_HTTP_BAD_REQUEST:
- /* This should never happen, either us or the bank is buggy
- (or API version conflict); just pass JSON reply to the application */
- ec = TALER_BANK_parse_ec_ (j);
- break;
- case MHD_HTTP_FORBIDDEN:
- /* Access denied */
- ec = TALER_BANK_parse_ec_ (j);
- break;
- case MHD_HTTP_UNAUTHORIZED:
- /* Nothing really to verify, bank says one of the signatures is
- invalid; as we checked them, this should never happen, we
- should pass the JSON reply to the application */
- ec = TALER_BANK_parse_ec_ (j);
- break;
- case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- ec = TALER_BANK_parse_ec_ (j);
- break;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- /* Server had an internal issue; we should retry, but this API
- leaves this to the application */
- ec = TALER_BANK_parse_ec_ (j);
- break;
- default:
- /* unexpected response code */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unexpected response code %u\n",
- (unsigned int) response_code);
- GNUNET_break (0);
- ec = TALER_BANK_parse_ec_ (j);
- response_code = 0;
- break;
- }
- rh->cb (rh->cb_cls,
- response_code,
- ec);
- TALER_BANK_reject_cancel (rh);
-}
-
-
-/**
- * Request rejection of a wire transfer, marking it as cancelled and voiding
- * its effects.
- *
- * @param ctx curl context for the event loop
- * @param bank_base_url URL of the bank (used to execute this request)
- * @param auth authentication data to use
- * @param account_number which account number should we query
- * @param rowid transfer to reject
- * @param rcb the callback to call with the operation result
- * @param rcb_cls closure for @a rcb
- * @return NULL
- * if the inputs are invalid.
- * In this case, the callback is not called.
- */
-struct TALER_BANK_RejectHandle *
-TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
- const char *bank_base_url,
- const struct TALER_BANK_AuthenticationData *auth,
- uint64_t account_number,
- uint64_t rowid,
- TALER_BANK_RejectResultCallback rcb,
- void *rcb_cls)
-{
- struct TALER_BANK_RejectHandle *rh;
- json_t *reject_obj;
- CURL *eh;
-
- reject_obj = json_pack ("{s:{s:s}, s:I, s:I}",
- "auth", "type", "basic",
- "row_id", (json_int_t) rowid,
- "account_number", (json_int_t) account_number);
- if (NULL == reject_obj)
- {
- GNUNET_break (0);
- return NULL;
- }
- rh = GNUNET_new (struct TALER_BANK_RejectHandle);
- rh->cb = rcb;
- rh->cb_cls = rcb_cls;
- rh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
- "/reject");
- rh->authh = TALER_BANK_make_auth_header_ (auth);
- /* Append content type header here, can't do it in GNUNET_CURL_job_add
- as that would override the CURLOPT_HTTPHEADER instead of appending. */
- {
- struct curl_slist *ext;
-
- ext = curl_slist_append (rh->authh,
- "Content-Type: application/json");
- if (NULL == ext)
- GNUNET_break (0);
- else
- rh->authh = ext;
- }
- eh = curl_easy_init ();
- GNUNET_assert (NULL != (rh->json_enc =
- json_dumps (reject_obj,
- JSON_COMPACT)));
- json_decref (reject_obj);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_URL,
- rh->request_url));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_POSTFIELDS,
- rh->json_enc));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_POSTFIELDSIZE,
- strlen (rh->json_enc)));
- rh->job = GNUNET_CURL_job_add2 (ctx,
- eh,
- rh->authh,
- &handle_reject_finished,
- rh);
- return rh;
-}
-
-
-/**
- * Cancel an reject request. This function cannot be used on a request
- * handle if the response was is already served for it.
- *
- * @param rh the reject request handle
- */
-void
-TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh)
-{
- if (NULL != rh->job)
- {
- GNUNET_CURL_job_cancel (rh->job);
- rh->job = NULL;
- }
- curl_slist_free_all (rh->authh);
- GNUNET_free (rh->request_url);
- GNUNET_free (rh->json_enc);
- GNUNET_free (rh);
-}
-
-
-/* end of bank_api_reject.c */
diff --git a/src/bank-lib/bank_api_transaction.c b/src/bank-lib/bank_api_transaction.c
new file mode 100644
index 000000000..177328482
--- /dev/null
+++ b/src/bank-lib/bank_api_transaction.c
@@ -0,0 +1,368 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015--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/bank_api_transaction.c
+ * @brief Implementation of the /transaction/ requests of the bank's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "bank_api_common.h"
+#include <microhttpd.h> /* just for HTTP status codes */
+#include "taler_signatures.h"
+#include "taler_curl_lib.h"
+#include "taler_bank_service.h"
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Data structure serialized in the prepare stage.
+ */
+struct WirePackP
+{
+ /**
+ * Random unique identifier for the request.
+ */
+ struct GNUNET_HashCode request_uid;
+
+ /**
+ * Amount to be transferred.
+ */
+ struct TALER_AmountNBO amount;
+
+ /**
+ * Wire transfer identifier to use.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Length of the payto:// URL of the target account,
+ * including 0-terminator, in network byte order.
+ */
+ uint32_t account_len GNUNET_PACKED;
+
+ /**
+ * Length of the exchange's base URL,
+ * including 0-terminator, in network byte order.
+ */
+ uint32_t exchange_url_len GNUNET_PACKED;
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Prepare for exeuction of a wire transfer.
+ *
+ * @param destination_account_url payto:// URL identifying where to send the money
+ * @param amount amount to transfer, already rounded
+ * @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_size[out] set to number of bytes in @a buf, 0 on error
+ */
+void
+TALER_BANK_prepare_wire_transfer (const char *destination_account_url,
+ const struct TALER_Amount *amount,
+ const char *exchange_base_url,
+ const struct
+ TALER_WireTransferIdentifierRawP *wtid,
+ void **buf,
+ size_t *buf_size)
+{
+ struct WirePackP *wp;
+ size_t d_len = strlen (destination_account_url) + 1;
+ size_t u_len = strlen (exchange_base_url) + 1;
+ char *end;
+
+ *buf_size = sizeof (*wp) + d_len + u_len;
+ wp = GNUNET_malloc (*buf_size);
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
+ &wp->request_uid);
+ TALER_amount_hton (&wp->amount,
+ amount);
+ wp->wtid = *wtid;
+ wp->account_len = htonl ((uint32_t) d_len);
+ wp->exchange_url_len = htonl ((uint32_t) u_len);
+ end = (char *) &wp[1];
+ memcpy (end,
+ destination_account_url,
+ d_len);
+ memcpy (end + d_len,
+ exchange_base_url,
+ u_len);
+ *buf = (char *) wp;
+}
+
+
+/**
+ * @brief An transaction Handle
+ */
+struct TALER_BANK_WireExecuteHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *request_url;
+
+ /**
+ * POST context.
+ */
+ struct TEAH_PostContext post_ctx;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_BANK_ConfirmationCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /transaction 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)
+{
+ struct TALER_BANK_WireExecuteHandle *weh = cls;
+ uint64_t row_id = UINT64_MAX;
+ struct GNUNET_TIME_Absolute timestamp;
+ enum TALER_ErrorCode ec;
+ const json_t *j = response;
+
+ weh->job = NULL;
+ timestamp = GNUNET_TIME_UNIT_FOREVER_ABS;
+ switch (response_code)
+ {
+ case 0:
+ ec = TALER_EC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint64 ("row_id",
+ &row_id),
+ GNUNET_JSON_spec_absolute_time ("timestamp",
+ &timestamp),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (j,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_INVALID_RESPONSE;
+ break;
+ }
+ ec = TALER_EC_NONE;
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the bank is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ /* Access denied */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ /* Nothing really to verify, bank says one of the signatures is
+ invalid; as we checked them, this should never happen, we
+ should pass the JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_NOT_ACCEPTABLE:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ ec = TALER_BANK_parse_ec_ (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ (unsigned int) response_code);
+ GNUNET_break (0);
+ ec = TALER_BANK_parse_ec_ (j);
+ response_code = 0;
+ break;
+ }
+ weh->cb (weh->cb_cls,
+ response_code,
+ ec,
+ row_id,
+ timestamp);
+ TALER_BANK_execute_wire_transfer_cancel (weh);
+}
+
+
+/**
+ * Execute a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param buf buffer with the prepared execution details
+ * @param buf_size number of bytes in @a buf
+ * @param cc function to call upon success
+ * @param cc_cls closure for @a cc
+ * @return NULL on error
+ */
+struct TALER_BANK_WireExecuteHandle *
+TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,
+ const char *bank_base_url,
+ const struct
+ TALER_BANK_AuthenticationData *auth,
+ const void *buf,
+ size_t buf_size,
+ TALER_BANK_ConfirmationCallback cc,
+ void *cc_cls)
+{
+ struct TALER_BANK_WireExecuteHandle *weh;
+ json_t *transaction_obj;
+ CURL *eh;
+ const struct WirePackP *wp = buf;
+ uint32_t d_len;
+ uint32_t u_len;
+ const char *destination_account_url;
+ const char *exchange_base_url;
+ struct TALER_Amount amount;
+
+ if (sizeof (*wp) > buf_size)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ d_len = ntohl (wp->account_len);
+ u_len = ntohl (wp->exchange_url_len);
+ if (sizeof (*wp) + d_len + u_len != buf_size)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ destination_account_url = (const char *) &wp[1];
+ exchange_base_url = destination_account_url + d_len;
+ if (NULL == bank_base_url)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ 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)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ weh = GNUNET_new (struct TALER_BANK_WireExecuteHandle);
+ weh->cb = cc;
+ weh->cb_cls = cc_cls;
+ weh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
+ "/transaction");
+ weh->post_ctx.headers = curl_slist_append
+ (weh->post_ctx.headers,
+ "Content-Type: application/json");
+
+ eh = curl_easy_init ();
+ if ( (GNUNET_OK !=
+ TALER_BANK_setup_auth_ (eh,
+ auth)) ||
+ (CURLE_OK !=
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ weh->request_url)) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&weh->post_ctx,
+ eh,
+ transaction_obj)) )
+ {
+ GNUNET_break (0);
+ TALER_BANK_execute_wire_transfer_cancel (weh);
+ curl_easy_cleanup (eh);
+ json_decref (transaction_obj);
+ return NULL;
+ }
+ json_decref (transaction_obj);
+
+ weh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ weh->post_ctx.headers,
+ &handle_transaction_finished,
+ weh);
+ return weh;
+}
+
+
+/**
+ * Cancel a wire transfer. This function cannot be used on a request handle
+ * if a response is already served for it.
+ *
+ * @param weh the wire transfer request handle
+ */
+void
+TALER_BANK_execute_wire_transfer_cancel (struct
+ TALER_BANK_WireExecuteHandle *weh)
+{
+ if (NULL != weh->job)
+ {
+ GNUNET_CURL_job_cancel (weh->job);
+ weh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&weh->post_ctx);
+ GNUNET_free (weh->request_url);
+ GNUNET_free (weh);
+}
+
+
+/* end of bank_api_transaction.c */
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index d8ea392b5..1706ca8fb 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2016, 2017, 2018 Inria and GNUnet e.V.
+ (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
@@ -25,7 +25,7 @@
#include "platform.h"
#include "taler_fakebank_lib.h"
#include "taler_bank_service.h"
-#include "fakebank.h"
+#include "taler_mhd_lib.h"
/**
* Maximum POST request size (for /admin/add/incoming)
@@ -34,6 +34,65 @@
/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction
+{
+ /**
+ * We store transactions in a DLL.
+ */
+ struct Transaction *next;
+
+ /**
+ * We store transactions in a DLL.
+ */
+ struct Transaction *prev;
+
+ /**
+ * Amount to be transferred.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Account to debit.
+ */
+ char *debit_account;
+
+ /**
+ * Account to credit.
+ */
+ char *credit_account;
+
+ /**
+ * Subject of the transfer.
+ */
+ char *subject;
+
+ /**
+ * Base URL of the exchange.
+ */
+ char *exchange_base_url;
+
+ /**
+ * When did the transaction happen?
+ */
+ struct GNUNET_TIME_Absolute date;
+
+ /**
+ * Number of this transaction.
+ */
+ uint64_t row_id;
+
+ /**
+ * Has this transaction been subjected to #TALER_FAKEBANK_check()
+ * and should thus no longer be counted in
+ * #TALER_FAKEBANK_check_empty()?
+ */
+ int checked;
+};
+
+
+/**
* Handle for the fake bank.
*/
struct TALER_FAKEBANK_Handle
@@ -63,6 +122,11 @@ struct TALER_FAKEBANK_Handle
*/
uint64_t serial_counter;
+ /**
+ * Our port number.
+ */
+ uint16_t port;
+
#if EPOLL_SUPPORT
/**
* Boxed @e mhd_fd.
@@ -95,8 +159,8 @@ struct TALER_FAKEBANK_Handle
int
TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
const struct TALER_Amount *want_amount,
- uint64_t want_debit,
- uint64_t want_credit,
+ const char *want_debit,
+ const char *want_credit,
const char *exchange_base_url,
char **subject)
{
@@ -151,8 +215,8 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
*/
uint64_t
TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
- uint64_t debit_account,
- uint64_t credit_account,
+ const char *debit_account,
+ const char *credit_account,
const struct TALER_Amount *amount,
const char *subject,
const char *exchange_base_url)
@@ -160,15 +224,15 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
struct Transaction *t;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Making transfer from %llu to %llu over %s and subject %s; for exchange: %s\n",
- (unsigned long long) debit_account,
- (unsigned long long) credit_account,
+ "Making transfer from %s to %s over %s and subject %s; for exchange: %s\n",
+ debit_account,
+ credit_account,
TALER_amount2s (amount),
subject,
exchange_base_url);
t = GNUNET_new (struct Transaction);
- t->debit_account = debit_account;
- t->credit_account = credit_account;
+ t->debit_account = GNUNET_strdup (debit_account);
+ t->credit_account = GNUNET_strdup (credit_account);
t->amount = *amount;
t->exchange_base_url = GNUNET_strdup (exchange_base_url);
t->row_id = ++h->serial_counter;
@@ -183,31 +247,6 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
/**
- * Reject incoming wire transfer to account @a credit_account
- * as identified by @a rowid.
- *
- * @param h fake bank handle
- * @param rowid identifies transfer to reject
- * @param credit_account account number of owner of credited account
- * @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not found
- */
-int
-TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
- uint64_t rowid,
- uint64_t credit_account)
-{
- for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
- if ( (t->row_id == rowid) &&
- (t->credit_account == credit_account) )
- {
- t->rejected = GNUNET_YES;
- return GNUNET_YES;
- }
- return GNUNET_NO;
-}
-
-
-/**
* Check that no wire transfers were ordered (or at least none
* that have not been taken care of via #TALER_FAKEBANK_check()).
* If any transactions are onrecord, return #GNUNET_SYSERR.
@@ -223,8 +262,7 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
t = h->transactions_head;
while (NULL != t)
{
- if ( (GNUNET_YES != t->checked) &&
- (GNUNET_YES != t->rejected) )
+ if (GNUNET_YES != t->checked)
break;
t = t->next;
}
@@ -234,16 +272,15 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
"Expected empty transaction set, but I have:\n");
while (NULL != t)
{
- if ( (GNUNET_YES != t->checked) &&
- (GNUNET_YES != t->rejected) )
+ if (GNUNET_YES != t->checked)
{
char *s;
s = TALER_amount_to_string (&t->amount);
fprintf (stderr,
- "%llu -> %llu (%s) from %s\n",
- (unsigned long long) t->debit_account,
- (unsigned long long) t->credit_account,
+ "%s -> %s (%s) from %s\n",
+ t->debit_account,
+ t->credit_account,
s,
t->exchange_base_url);
GNUNET_free (s);
@@ -270,6 +307,8 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
h->transactions_tail,
t);
GNUNET_free (t->subject);
+ GNUNET_free (t->debit_account);
+ GNUNET_free (t->credit_account);
GNUNET_free (t->exchange_base_url);
GNUNET_free (t);
}
@@ -291,62 +330,6 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
/**
- * Create and queue a bank error message with the HTTP response
- * code @a response_code on connection @a connection.
- *
- * @param connection where to queue the reply
- * @param response_code http status code to use
- * @param ec taler error code to use
- * @param message human readable error message
- * @return MHD status code
- */
-static int
-create_bank_error (struct MHD_Connection *connection,
- unsigned int response_code,
- enum TALER_ErrorCode ec,
- const char *message)
-{
- json_t *json;
- struct MHD_Response *resp;
- void *json_str;
- size_t json_len;
- int ret;
-
- json = json_pack ("{s:s, s:I}",
- "error",
- message,
- "ec",
- (json_int_t) ec);
- json_str = json_dumps (json,
- JSON_INDENT (2));
- json_decref (json);
- 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,
- response_code,
- resp);
- MHD_destroy_response (resp);
- return ret;
-}
-
-
-/**
* Function called whenever MHD is done with a request. If the
* request was a POST, we may have stored a `struct Buffer *` in the
* @a con_cls that might still need to be cleaned up. Call the
@@ -394,8 +377,6 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
{
enum GNUNET_JSON_PostResult pr;
json_t *json;
- struct MHD_Response *resp;
- int ret;
uint64_t row_id;
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
@@ -422,15 +403,14 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
}
{
const char *subject;
- uint64_t debit_account;
- uint64_t credit_account;
+ const char *debit_account;
+ const char *credit_account;
const char *base_url;
struct TALER_Amount amount;
- char *amount_s;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("subject", &subject),
- GNUNET_JSON_spec_uint64 ("debit_account", &debit_account),
- GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
+ GNUNET_JSON_spec_string ("debit_account", &debit_account),
+ GNUNET_JSON_spec_string ("credit_account", &credit_account),
TALER_JSON_spec_amount ("amount", &amount),
GNUNET_JSON_spec_string ("exchange_url", &base_url),
GNUNET_JSON_spec_end ()
@@ -450,80 +430,49 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
&amount,
subject,
base_url);
- amount_s = TALER_amount_to_string (&amount);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %llu->%llu, subject: %s, amount: %s, from %s\n",
- (unsigned long long) debit_account,
- (unsigned long long) credit_account,
+ "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
+ debit_account,
+ credit_account,
subject,
- amount_s,
+ TALER_amount2s (&amount),
base_url);
- GNUNET_free (amount_s);
}
json_decref (json);
/* Finally build response object */
- {
- void *json_str;
- size_t json_len;
-
- json = json_pack ("{s:I, s:o}",
- "row_id",
- (json_int_t) row_id,
- "timestamp", GNUNET_JSON_from_time_abs (GNUNET_TIME_UNIT_ZERO_ABS)); /*dummy tmp */
-
- json_str = json_dumps (json,
- JSON_INDENT (2));
- json_decref (json);
- 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;
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:I, s:o}",
+ "row_id",
+ (json_int_t) row_id,
+ "timestamp", GNUNET_JSON_from_time_abs (
+ GNUNET_TIME_UNIT_ZERO_ABS)); /*dummy tmp */
}
/**
- * Handle incoming HTTP request for /reject.
+ * Handle incoming HTTP request for /transaction.
*
* @param h the fakebank handle
* @param connection the connection
+ * @param account account making the transaction
* @param upload_data request data
* @param upload_data_size size of @a upload_data in bytes
* @param con_cls closure for request (a `struct Buffer *`)
* @return MHD result code
*/
static int
-handle_reject (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
+handle_transaction (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
{
enum GNUNET_JSON_PostResult pr;
json_t *json;
- struct MHD_Response *resp;
- int ret;
- int found;
+ uint64_t row_id;
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
connection,
@@ -548,13 +497,25 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
break;
}
{
- uint64_t row_id;
- uint64_t credit_account;
+ struct GNUNET_HashCode uuid;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ const char *credit_account;
+ const char *base_url;
+ struct TALER_Amount amount;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_uint64 ("row_id", &row_id),
- GNUNET_JSON_spec_uint64 ("account_number", &credit_account),
+ GNUNET_JSON_spec_fixed_auto ("request_uid",
+ &uuid),
+ TALER_JSON_spec_amount ("amount",
+ &amount),
+ GNUNET_JSON_spec_string ("exchange_base_url",
+ &base_url),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &wtid),
+ GNUNET_JSON_spec_string ("credit_account",
+ &credit_account),
GNUNET_JSON_spec_end ()
};
+
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
@@ -564,31 +525,38 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
json_decref (json);
return MHD_NO;
}
- found = TALER_FAKEBANK_reject_transfer (h,
- row_id,
- credit_account);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Rejected wire transfer #%llu (to %llu)\n",
- (unsigned long long) row_id,
- (unsigned long long) credit_account);
+ {
+ char *subject;
+
+ subject = GNUNET_STRINGS_data_to_string_alloc (&wtid,
+ sizeof (wtid));
+ // FIXME: use uuid here!!!
+ row_id = TALER_FAKEBANK_make_transfer (h,
+ account,
+ credit_account,
+ &amount,
+ subject,
+ base_url);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
+ account,
+ credit_account,
+ subject,
+ TALER_amount2s (&amount),
+ base_url);
+ GNUNET_free (subject);
+ }
}
json_decref (json);
- if (GNUNET_OK != found)
- return create_bank_error
- (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- "transaction unknown");
- /* finally build regular response */
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NO_CONTENT,
- resp);
- MHD_destroy_response (resp);
- return ret;
+ /* Finally build response object */
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:I, s:o}",
+ "row_id",
+ (json_int_t) row_id,
+ "timestamp", GNUNET_JSON_from_time_abs (
+ GNUNET_TIME_UNIT_ZERO_ABS)); /*dummy tmp */
}
@@ -626,75 +594,245 @@ handle_home_page (struct TALER_FAKEBANK_Handle *h,
/**
- * Handle incoming HTTP request for /history
+ * This is the "base" structure for both the /history and the
+ * /history-range API calls.
+ */
+struct HistoryArgs
+{
+
+ /**
+ * Bank account number of the requesting client.
+ */
+ uint64_t account_number;
+
+ /**
+ * Index of the starting transaction.
+ */
+ uint64_t start_idx;
+
+ /**
+ * Requested number of results and order
+ * (positive: ascending, negative: descending)
+ */
+ int64_t delta;
+
+ /**
+ * Timeout for long polling.
+ */
+ struct GNUNET_TIME_Relative lp_timeout;
+
+ /**
+ * #GNUNET_YES if starting point was given.
+ */
+ int have_start;
+
+};
+
+
+/**
+ * Parse URL history arguments, of _both_ APIs:
+ * /history/incoming and /history/outgoing.
+ *
+ * @param connection MHD connection.
+ * @param function_name name of the caller.
+ * @param ha[out] will contain the parsed values.
+ * @return GNUNET_OK only if the parsing succeedes.
+ */
+static int
+parse_history_common_args (struct MHD_Connection *connection,
+ struct HistoryArgs *ha)
+{
+ const char *start;
+ const char *delta;
+ const char *long_poll_ms;
+ unsigned long long lp_timeout;
+ unsigned long long sval;
+ long long d;
+
+ start = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "start");
+ ha->have_start = (NULL != start);
+ delta = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "delta");
+ long_poll_ms = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "long_poll_ms");
+ lp_timeout = 0;
+ if ( (NULL == delta) ||
+ (1 != sscanf (delta,
+ "%lld",
+ &d)) ||
+ ( (NULL != long_poll_ms) &&
+ (1 != sscanf (long_poll_ms,
+ "%llu",
+ &lp_timeout)) ) ||
+ ( (NULL != start) &&
+ (1 != sscanf (start,
+ "%llu",
+ &sval)) ) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ if (NULL == start)
+ ha->start_idx = (d > 0) ? 0 : UINT64_MAX;
+ else
+ ha->start_idx = (uint64_t) sval;
+ ha->delta = (int64_t) d;
+ ha->lp_timeout
+ = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ lp_timeout);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle incoming HTTP request for /history/incoming
*
* @param h the fakebank handle
* @param connection the connection
- * @param con_cls place to store state, not used
+ * @param account which account the request is about
* @return MHD result code
*/
static int
-handle_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- void **con_cls)
+handle_credit_history (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account)
{
struct HistoryArgs ha;
- struct HistoryRangeIds hri;
- const char *start;
- const char *delta;
struct Transaction *pos;
+ json_t *history;
- (void) con_cls;
if (GNUNET_OK !=
- TFH_parse_history_common_args (connection,
- &ha))
+ parse_history_common_args (connection,
+ &ha))
{
GNUNET_break (0);
return MHD_NO;
}
- start = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "start");
- delta = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "delta");
- if ( ((NULL != start) && (1 != sscanf (start,
- "%llu",
- &hri.start))) ||
- (NULL == delta) || (1 != sscanf (delta,
- "%lld",
- &hri.count)) )
+ if (! ha.have_start)
+ {
+ pos = (0 > ha.delta)
+ ? h->transactions_tail
+ : h->transactions_head;
+ }
+ else if (NULL != h->transactions_head)
+ {
+ for (pos = h->transactions_head;
+ NULL != pos;
+ pos = pos->next)
+ if (pos->row_id == ha.start_idx)
+ break;
+ if (NULL == pos)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid start specified, transaction %llu not known!\n",
+ (unsigned long long) ha.start_idx);
+ return MHD_NO;
+ }
+ /* range is exclusive, skip the matching entry */
+ if (ha.delta > 0)
+ pos = pos->next;
+ if (ha.delta < 0)
+ pos = pos->prev;
+ }
+ else
+ {
+ /* list is empty */
+ pos = NULL;
+ }
+ history = json_array ();
+ while ( (0 != ha.delta) &&
+ (NULL != pos) )
+ {
+ if (0 == strcasecmp (pos->credit_account,
+ account))
+ {
+ json_t *trans;
+
+ trans = json_pack
+ ("{s:I, s:o, s:o, s:s, s:s, 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),
+ "credit_account", account,
+ "debit_account", pos->debit_account,
+ "wtid", pos->subject /* we "know" it is OK */);
+ GNUNET_assert (0 ==
+ json_array_append_new (history,
+ trans));
+ if (ha.delta > 0)
+ ha.delta--;
+ else
+ ha.delta++;
+ }
+ if (ha.delta > 0)
+ pos = pos->prev;
+ else
+ pos = pos->next;
+ }
+ return TALER_MHD_reply_json (connection,
+ history,
+ MHD_HTTP_OK);
+}
+
+
+/**
+ * Handle incoming HTTP request for /history/incoming
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @return MHD result code
+ */
+static int
+handle_debit_history (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account)
+{
+ struct HistoryArgs ha;
+ struct Transaction *pos;
+ json_t *history;
+
+ if (GNUNET_OK !=
+ parse_history_common_args (connection,
+ &ha))
{
GNUNET_break (0);
return MHD_NO;
}
- ha.range = &hri;
- if (NULL == start)
+ if (! ha.have_start)
{
- pos = 0 > hri.count ?
- h->transactions_tail : h->transactions_head;
+ pos = (0 > ha.delta)
+ ? h->transactions_tail
+ : h->transactions_head;
}
else if (NULL != h->transactions_head)
{
for (pos = h->transactions_head;
NULL != pos;
pos = pos->next)
- if (pos->row_id == hri.start)
+ if (pos->row_id == ha.start_idx)
break;
if (NULL == pos)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid range specified,"
- " transaction %llu not known!\n",
- (unsigned long long) hri.start);
+ "Invalid start specified, transaction %llu not known!\n",
+ (unsigned long long) ha.start_idx);
return MHD_NO;
}
/* range is exclusive, skip the matching entry */
- if (hri.count > 0)
+ if (ha.delta > 0)
pos = pos->next;
- if (hri.count < 0)
+ if (ha.delta < 0)
pos = pos->prev;
}
else
@@ -702,56 +840,77 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
/* list is empty */
pos = NULL;
}
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "/history, start row (0 == no transactions exist): %llu\n",
- NULL != pos ? pos->row_id : 0LL);
- return TFH_build_history_response (connection,
- pos,
- &ha,
- &TFH_handle_history_skip,
- &TFH_handle_history_step,
- &TFH_handle_history_advance);
+ history = json_array ();
+ while ( (0 != ha.delta) &&
+ (NULL != pos) )
+ {
+ if (0 == strcasecmp (pos->debit_account,
+ account))
+ {
+ json_t *trans;
+
+ trans = json_pack
+ ("{s:I, s:o, s:o, s:s, s:s, 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),
+ "credit_account", pos->credit_account,
+ "debit_account", account,
+ "reserve_pub", pos->subject /* we "know" it is OK */);
+ GNUNET_assert (0 ==
+ json_array_append_new (history,
+ trans));
+ if (ha.delta > 0)
+ ha.delta--;
+ else
+ ha.delta++;
+ }
+ if (ha.delta > 0)
+ pos = pos->prev;
+ else
+ pos = pos->next;
+ }
+ return TALER_MHD_reply_json (connection,
+ history,
+ MHD_HTTP_OK);
}
/**
* Handle incoming HTTP request.
*
- * @param cls a `struct TALER_FAKEBANK_Handle`
+ * @param h our handle
* @param connection the connection
* @param url the requested url
* @param method the method (POST, GET, ...)
- * @param version HTTP version (ignored)
+ * @param account which account should process the request
* @param upload_data request data
* @param upload_data_size size of @a upload_data in bytes
* @param con_cls closure for request (a `struct Buffer *`)
* @return MHD result code
*/
static int
-handle_mhd_request (void *cls,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *version,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
+serve (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
{
- struct TALER_FAKEBANK_Handle *h = cls;
-
- (void) version;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Fakebank, serving: %s\n",
url);
- if ( (0 == strcasecmp (url,
- "/")) &&
+ if ( (0 == strcmp (url,
+ "/")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_GET)) )
return handle_home_page (h,
connection,
con_cls);
- if ( (0 == strcasecmp (url,
- "/admin/add/incoming")) &&
+ if ( (0 == strcmp (url,
+ "/admin/add/incoming")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
return handle_admin_add_incoming (h,
@@ -759,22 +918,33 @@ handle_mhd_request (void *cls,
upload_data,
upload_data_size,
con_cls);
- if ( (0 == strcasecmp (url,
- "/reject")) &&
+ if ( (0 == strcmp (url,
+ "/transaction")) &&
+ (NULL != account) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
- return handle_reject (h,
- connection,
- upload_data,
- upload_data_size,
- con_cls);
- if ( (0 == strcasecmp (url,
- "/history")) &&
+ return handle_transaction (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ if ( (0 == strcmp (url,
+ "/history/incoming")) &&
+ (NULL != account) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ return handle_credit_history (h,
+ connection,
+ account);
+ if ( (0 == strcmp (url,
+ "/history/outgoing")) &&
+ (NULL != account) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_GET)) )
- return handle_history (h,
- connection,
- con_cls);
+ return handle_debit_history (h,
+ connection,
+ account);
/* Unexpected URL path, just close the connection. */
/* we're rather impolite here, but it's a testcase. */
@@ -786,6 +956,55 @@ handle_mhd_request (void *cls,
/**
+ * Handle incoming HTTP request.
+ *
+ * @param cls a `struct TALER_FAKEBANK_Handle`
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param version HTTP version (ignored)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct Buffer *`)
+ * @return MHD result code
+ */
+static int
+handle_mhd_request (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct TALER_FAKEBANK_Handle *h = cls;
+ char *account = NULL;
+ char *end;
+ int ret;
+
+ (void) version;
+ if ( (strlen (url) > 1) &&
+ (NULL != (end = strchr (url + 1, '/'))) )
+ {
+ account = GNUNET_strndup (url + 1,
+ end - url - 1);
+ url = end;
+ }
+ ret = serve (h,
+ connection,
+ account,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ GNUNET_free_non_null (account);
+ return ret;
+}
+
+
+/**
* Task run whenever HTTP server operations are pending.
*
* @param cls the `struct TALER_FAKEBANK_Handle`
@@ -918,6 +1137,7 @@ TALER_FAKEBANK_start (uint16_t port)
struct TALER_FAKEBANK_Handle *h;
h = GNUNET_new (struct TALER_FAKEBANK_Handle);
+ h->port = port;
h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
#if EPOLL_SUPPORT
| MHD_USE_EPOLL_INTERNAL_THREAD
diff --git a/src/bank-lib/fakebank.h b/src/bank-lib/fakebank.h
index cc2359014..c52902f14 100644
--- a/src/bank-lib/fakebank.h
+++ b/src/bank-lib/fakebank.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2016, 2017, 2018 Inria and GNUnet e.V.
+ (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
@@ -19,7 +19,7 @@
/**
* @file bank-lib/fakebank.h
- * @brief definitions for the "/history[-range]" layer.
+ * @brief definitions for the "/history" layer.
* @author Marcello Stanisci <stanisci.m@gmail.com>
*/
@@ -29,283 +29,4 @@
#include <gnunet/gnunet_util_lib.h>
#include "taler_bank_service.h"
-/**
- * Details about a transcation we (as the simulated bank) received.
- */
-struct Transaction
-{
- /**
- * We store transactions in a DLL.
- */
- struct Transaction *next;
-
- /**
- * We store transactions in a DLL.
- */
- struct Transaction *prev;
-
- /**
- * Amount to be transferred.
- */
- struct TALER_Amount amount;
-
- /**
- * Account to debit.
- */
- uint64_t debit_account;
-
- /**
- * Account to credit.
- */
- uint64_t credit_account;
-
- /**
- * Subject of the transfer.
- */
- char *subject;
-
- /**
- * Base URL of the exchange.
- */
- char *exchange_base_url;
-
- /**
- * When did the transaction happen?
- */
- struct GNUNET_TIME_Absolute date;
-
- /**
- * Number of this transaction.
- */
- long long unsigned int row_id;
-
- /**
- * Flag set if the transfer was rejected.
- */
- int rejected;
-
- /**
- * Has this transaction been subjected to #TALER_FAKEBANK_check()
- * and should thus no longer be counted in
- * #TALER_FAKEBANK_check_empty()?
- */
- int checked;
-};
-
-
-/******************************************
- * Definitions for "/history" start here. *
- ******************************************/
-
-/**
- * Needed to implement ascending/descending ordering
- * of /history results.
- */
-struct HistoryElement
-{
-
- /**
- * History JSON element.
- */
- json_t *element;
-
- /**
- * Previous element.
- */
- struct HistoryElement *prev;
-
- /**
- * Next element.
- */
- struct HistoryElement *next;
-};
-
-
-/**
- * Values to implement the "/history-range" range.
- */
-struct HistoryRangeDates
-{
- /**
- * Oldest row in the results.
- */
- struct GNUNET_TIME_Absolute start;
-
- /**
- * Youngest row in the results.
- */
- struct GNUNET_TIME_Absolute end;
-};
-
-/**
- * Values to implement the "/history" range.
- */
-struct HistoryRangeIds
-{
-
- /**
- * (Exclusive) row ID for the result set.
- */
- unsigned long long start;
-
- /**
- * How many transactions we want in the result set. If
- * negative/positive, @a start will be strictly younger/older
- * of any element in the result set.
- */
- long long count;
-};
-
-
-/**
- * This is the "base" structure for both the /history and the
- * /history-range API calls.
- */
-struct HistoryArgs
-{
-
- /**
- * Direction asked by the client: CREDIT / DEBIT / BOTH / CANCEL.
- */
- enum TALER_BANK_Direction direction;
-
- /**
- * Bank account number of the requesting client.
- */
- unsigned long long account_number;
-
- /**
- * Ordering of the results.
- */
- unsigned int ascending;
-
- /**
- * Overloaded type that indicates the "range" to be returned
- * in the results; this can be either a date range, or a
- * starting row id + the count.
- */
- void *range;
-};
-
-
-/**
- * Type for a function that decides whether or not
- * the history-building loop should iterate once again.
- * Typically called from inside the 'while' condition.
- *
- * @param ha history argument.
- * @param pos current position.
- * @return GNUNET_YES if the iteration shuold go on.
- */
-typedef int (*CheckAdvance)(const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Type for a function that steps over the next element
- * in the list of all transactions, after the current @a pos
- * _got_ included in the result.
- */
-typedef struct Transaction * (*Step)(const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/*
- * Type for a function that steps over the next element
- * in the list of all transactions, after the current @a pos
- * did _not_ get included in the result.
- */
-typedef struct Transaction * (*Skip)(const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Actual history response builder.
- *
- * @param pos first (included) element in the result set.
- * @param ha history arguments.
- * @param caller_name which function is building the history.
- * @return MHD_YES / MHD_NO, after having enqueued the response
- * object into MHD.
- */
-int
-TFH_build_history_response (struct MHD_Connection *connection,
- struct Transaction *pos,
- struct HistoryArgs *ha,
- Skip skip,
- Step step,
- CheckAdvance advance);
-
-
-/**
- * Parse URL history arguments, of _both_ APIs:
- * /history and /history-range.
- *
- * @param connection MHD connection.
- * @param function_name name of the caller.
- * @param ha[out] will contain the parsed values.
- * @return GNUNET_OK only if the parsing succeedes.
- */
-int
-TFH_parse_history_common_args (struct MHD_Connection *connection,
- struct HistoryArgs *ha);
-
-
-/**
- * Decides whether the history builder will advance or not
- * to the next element.
- *
- * @param ha history args
- * @return GNUNET_YES/NO to advance/not-advance.
- */
-int
-TFH_handle_history_advance (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element does not get inserted in
- * the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-struct Transaction *
-TFH_handle_history_skip (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element _gets_ inserted in the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-struct Transaction *
-TFH_handle_history_step (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Decides whether the history builder will advance or not
- * to the next element.
- *
- * @param ha history args
- * @return GNUNET_YES/NO to advance/not-advance.
- */
-int
-TFH_handle_history_range_advance (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Iterates towards the "next" element to be processed. To
- * be used when the current element does not get inserted in
- * the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-struct Transaction *
-TFH_handle_history_range_skip (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
#endif
diff --git a/src/bank-lib/fakebank_history.c b/src/bank-lib/fakebank_history.c
index f4c615c52..2781cdca8 100644
--- a/src/bank-lib/fakebank_history.c
+++ b/src/bank-lib/fakebank_history.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2016, 2017, 2018 Inria and GNUnet e.V.
+ (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
@@ -19,92 +19,15 @@
/**
* @file bank-lib/fakebank_history.c
- * @brief definitions for the "/history[-range]" layer.
+ * @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"
-/**
- * Decides whether the history builder will advance or not
- * to the next element.
- *
- * @param ha history args
- * @return GNUNET_YES/NO to advance/not-advance.
- */
-int
-TFH_handle_history_advance (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- const struct HistoryRangeIds *hri = ha->range;
-
- return (NULL != pos) && (0 != hri->count);
-}
-
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element does not get inserted in
- * the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-struct Transaction *
-TFH_handle_history_skip (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- const struct HistoryRangeIds *hri = ha->range;
-
- if (hri->count > 0)
- return pos->next;
- if (hri->count < 0)
- return pos->prev;
- return NULL;
-}
-
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element _gets_ inserted in the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-struct Transaction *
-TFH_handle_history_step (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- struct HistoryRangeIds *hri = ha->range;
-
- if (hri->count > 0)
- {
- hri->count--;
- return pos->next;
- }
- if (hri->count < 0)
- {
- hri->count++;
- return pos->prev;
- }
- return NULL;
-}
-
-/**
- * Actual history response builder.
- *
- * @param pos first (included) element in the result set, NULL if history is empty
- * @param ha history arguments.
- * @param caller_name which function is building the history.
- * @return MHD_YES / MHD_NO, after having enqueued the response
- * object into MHD.
- */
int
TFH_build_history_response (struct MHD_Connection *connection,
struct Transaction *pos,
@@ -257,126 +180,3 @@ TFH_build_history_response (struct MHD_Connection *connection,
}
return ret;
}
-
-
-/**
- * Parse URL history arguments, of _both_ APIs:
- * /history and /history-range.
- *
- * @param connection MHD connection.
- * @param function_name name of the caller.
- * @param ha[out] will contain the parsed values.
- * @return GNUNET_OK only if the parsing succeedes.
- */
-int
-TFH_parse_history_common_args (struct MHD_Connection *connection,
- struct HistoryArgs *ha)
-{
- /**
- * @variable
- * Just check if given and == "basic", no need to keep around.
- */
- const char *auth;
-
- /**
- * All those will go into the structure, after parsing.
- */
- const char *direction;
- const char *cancelled;
- const char *ordering;
- const char *account_number;
-
-
- auth = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "auth");
- direction = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "direction");
- cancelled = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "cancelled");
- ordering = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "ordering");
- account_number = MHD_lookup_connection_value
- (connection,
- MHD_GET_ARGUMENT_KIND,
- "account_number");
-
- /* Fail if one of the above failed. */
- if ( (NULL == direction) ||
- (NULL == cancelled) ||
- ( (0 != strcasecmp (cancelled,
- "OMIT")) &&
- (0 != strcasecmp (cancelled,
- "SHOW")) ) ||
- ( (0 != strcasecmp (direction,
- "BOTH")) &&
- (0 != strcasecmp (direction,
- "CREDIT")) &&
- (0 != strcasecmp (direction,
- "DEBIT")) ) ||
- (1 != sscanf (account_number,
- "%llu",
- &ha->account_number)) ||
- ( (NULL == auth) || (0 != strcasecmp (auth,
- "basic")) ) )
- {
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break (0);
- return GNUNET_NO;
- }
-
- if (0 == strcasecmp (direction,
- "CREDIT"))
- {
- ha->direction = TALER_BANK_DIRECTION_CREDIT;
- }
- else if (0 == strcasecmp (direction,
- "DEBIT"))
- {
- ha->direction = TALER_BANK_DIRECTION_DEBIT;
- }
- else if (0 == strcasecmp (direction,
- "BOTH"))
- {
- ha->direction = TALER_BANK_DIRECTION_BOTH;
- }
-
- /* Direction is invalid. */
- else
- {
- GNUNET_break (0);
- return GNUNET_NO;
- }
-
- if (0 == strcasecmp (cancelled,
- "OMIT"))
- {
- /* nothing */
- }
- else if (0 == strcasecmp (cancelled,
- "SHOW"))
- {
- ha->direction |= TALER_BANK_DIRECTION_CANCEL;
- }
-
- /* Cancel-showing policy is invalid. */
- else
- {
- GNUNET_break (0);
- return GNUNET_NO;
- }
-
- if ((NULL != ordering)
- && (0 == strcmp ("ascending",
- ordering)))
- ha->ascending = GNUNET_YES;
- else
- ha->ascending = GNUNET_NO;
-
- return GNUNET_OK;
-}
diff --git a/src/bank-lib/taler-bank-transfer.c b/src/bank-lib/taler-bank-transfer.c
index 625545b6b..c52c4b38d 100644
--- a/src/bank-lib/taler-bank-transfer.c
+++ b/src/bank-lib/taler-bank-transfer.c
@@ -25,9 +25,9 @@
#include "taler_bank_service.h"
/**
- * Bank URL.
+ * Account base URL.
*/
-static char *bank_url;
+static char *account_base_url;
/**
* Amount to transfer.
@@ -35,14 +35,9 @@ static char *bank_url;
static struct TALER_Amount amount;
/**
- * Debit account number.
+ * Credit account payto://-URI.
*/
-static unsigned long long debit_account_no;
-
-/**
- * Credit account number.
- */
-static unsigned long long credit_account_no;
+static char *credit_account;
/**
* Wire transfer subject.
@@ -168,11 +163,23 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
struct TALER_BANK_AuthenticationData auth;
+ struct TALER_ReservePublicKeyP reserve_pub;
(void) cls;
(void) args;
(void) cfgfile;
(void) cfg;
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (subject,
+ strlen (subject),
+ &reserve_pub,
+ sizeof (reserve_pub)))
+ {
+ fprintf (stderr,
+ "Error: wire transfer subject must be a reserve public key\n");
+ return;
+ }
+
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
GNUNET_assert (NULL != ctx);
@@ -182,13 +189,11 @@ run (void *cls,
auth.details.basic.username = username;
auth.details.basic.password = password;
op = TALER_BANK_admin_add_incoming (ctx,
- bank_url,
+ account_base_url,
&auth,
- "https://exchange.com/legacy",
- subject,
+ &reserve_pub,
&amount,
- debit_account_no,
- credit_account_no,
+ credit_account,
&res_cb,
NULL);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
@@ -219,26 +224,20 @@ main (int argc, char *const *argv)
(GNUNET_GETOPT_option_string ('b',
"bank",
"URL",
- "base URL of the bank",
- &bank_url)),
+ "base URL of the account at the bank",
+ &account_base_url)),
GNUNET_GETOPT_option_help ("Deposit funds into a Taler reserve"),
GNUNET_GETOPT_option_mandatory
- (GNUNET_GETOPT_option_ulong ('C',
- "credit",
- "ACCOUNT",
- "number of the bank account to credit",
- &credit_account_no)),
- GNUNET_GETOPT_option_mandatory
- (GNUNET_GETOPT_option_ulong ('D',
- "debit",
- "ACCOUNT",
- "number of the bank account to debit",
- &debit_account_no)),
+ (GNUNET_GETOPT_option_string ('C',
+ "credit",
+ "ACCOUNT",
+ "payto URL of the bank account to credit",
+ &credit_account)),
GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_string ('s',
"subject",
"STRING",
- "specifies the wire transfer subject",
+ "specifies the wire transfer subject (must be a reserve public key)",
&subject)),
GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_string ('u',
diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c
index d15984a9c..087e44848 100644
--- a/src/bank-lib/test_bank_api.c
+++ b/src/bank-lib/test_bank_api.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2016, 2017 GNUnet e.V.
+ Copyright (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
diff --git a/src/bank-lib/testing_api_cmd_history.c b/src/bank-lib/testing_api_cmd_history_credit.c
index dc5cd2d99..5c2b34d06 100644
--- a/src/bank-lib/testing_api_cmd_history.c
+++ b/src/bank-lib/testing_api_cmd_history_credit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2018 Taler Systems SA
+ 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
@@ -16,13 +16,11 @@
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
-
/**
* @file bank-lib/testing_api_cmd_history.c
* @brief command to check the /history API from the bank.
* @author Marcello Stanisci
*/
-
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
@@ -40,35 +38,26 @@
struct HistoryState
{
/**
- * Base URL of the bank offering the "history" operation.
- */
- const char *bank_url;
-
- /**
- * Account number to ask the history for.
- */
- uint64_t account_no;
-
- /**
- * Which type of records we are interested: in-transfers
- * / out-transfers / rejected transfers.
+ * Base URL of the account offering the "history" operation.
*/
- enum TALER_BANK_Direction direction;
+ char *account_url;
/**
- * First row number we want in the result.
+ * Reference to command defining the
+ * first row number we want in the result.
*/
const char *start_row_reference;
/**
- * How many rows we want in the result, _at most_.
+ * How many rows we want in the result, _at most_,
+ * and ascending/descending.
*/
- unsigned long long num_results;
+ long long num_results;
/**
* Handle to a pending "history" operation.
*/
- struct TALER_BANK_HistoryHandle *hh;
+ struct TALER_BANK_CreditHistoryHandle *hh;
/**
* Expected number of results (= rows).
@@ -81,34 +70,9 @@ struct HistoryState
*/
int failed;
- /**
- * If GNUNET_YES, this parameter will ask for results in
- * chronological order.
- */
- unsigned int ascending;
-
- /**********************************
- * Following defs are specific to *
- * the "/history-range" version. *
- **********************************/
-
- /**
- * Last row number we want in the result. Only used
- * as a trait source when using the /history-range API.
- */
- const char *end_row_reference;
-
- /**
- * Start date for /history-range.
- */
- struct GNUNET_TIME_Absolute start_date;
-
- /**
- * End date for /history-range.
- */
- struct GNUNET_TIME_Absolute end_date;
};
+
/**
* Item in the transaction history, as reconstructed from the
* command history.
@@ -119,7 +83,7 @@ struct History
/**
* Wire details.
*/
- struct TALER_BANK_TransferDetails details;
+ struct TALER_BANK_CreditDetails details;
/**
* Serial ID of the wire transfer.
@@ -127,20 +91,13 @@ struct History
uint64_t row_id;
/**
- * Direction of the transfer.
+ * URL to free.
*/
- enum TALER_BANK_Direction direction;
-
+ char *url;
};
/**
- * Array mapping bank account numbers to login credentials.
- */
-extern struct TALER_BANK_AuthenticationData AUTHS[];
-
-
-/**
* Offer internal data to other commands.
*
* @param cls closure.
@@ -167,51 +124,6 @@ history_traits (void *cls,
/**
- * Test if the CMD at offset @a off has been /rejected, and
- * is indeed a wire transfer CMD.
- *
- * @param is interpreter state (where we are right now)
- * @param off offset of the command to test for rejection.
- *
- * @return GNUNET_YES if the command at @a off was cancelled.
- */
-static int
-test_cancelled (struct TALER_TESTING_Interpreter *is,
- unsigned int off)
-{
- const char *rejected_reference;
- const struct TALER_TESTING_Command *current_cmd;
-
- current_cmd = &is->commands[off];
- TALER_LOG_INFO ("Is `%s' rejected?\n",
- current_cmd->label);
- for (int i = 0; i<is->ip; i++)
- {
- const struct TALER_TESTING_Command *c = &is->commands[i];
-
-
- /* XXX: Errors reported here are NOT fatal */
-
- /* Rejected wire transfers have a non-NULL reference to a
- * reject command to mark them as rejected. So errors
- * about "reject traits" not found are NOT fatal here */
- if (GNUNET_OK != TALER_TESTING_get_trait_rejected
- (c, 0, &rejected_reference))
- continue;
-
- TALER_LOG_INFO ("Command `%s' was rejected by `%s'.\n",
- current_cmd->label,
- c->label);
-
- if (0 == strcmp (rejected_reference,
- current_cmd->label))
- return GNUNET_YES;
- }
- return GNUNET_NO;
-}
-
-
-/**
* Free history @a h of length @a h_len.
*
* @param h history array to free.
@@ -222,10 +134,7 @@ free_history (struct History *h,
uint64_t h_len)
{
for (uint64_t off = 0; off<h_len; off++)
- {
- GNUNET_free (h[off].details.wire_transfer_subject);
- GNUNET_free (h[off].details.account_url);
- }
+ GNUNET_free (h[off].url);
GNUNET_free_non_null (h);
}
@@ -251,14 +160,12 @@ print_expected (struct History *h,
for (uint64_t i = 0; i<h_len; i++)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "H(%llu): %s%s (serial: %llu, subject: %s,"
+ "H(%llu): %s (serial: %llu, subject: %s,"
" counterpart: %s)\n",
(unsigned long long) i,
- (TALER_BANK_DIRECTION_CREDIT == h[i].direction) ?
- "+" : "-",
TALER_amount2s (&h[i].details.amount),
(unsigned long long) h[i].row_id,
- h[i].details.wire_transfer_subject,
+ TALER_B2S (&h[i].details.reserve_pub),
h[i].details.account_url);
}
}
@@ -280,20 +187,6 @@ build_history_hit_limit (uint64_t total,
const struct HistoryState *hs,
const struct TALER_TESTING_Command *pos)
{
- /* "/history-range" case. */
- if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
- hs->start_date.abs_value_us)
- {
- const struct GNUNET_TIME_Absolute *timestamp;
-
- GNUNET_assert (GNUNET_OK ==
- TALER_TESTING_get_trait_absolute_time (pos,
- 0,
- &timestamp));
- GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
- hs->end_date.abs_value_us);
- return timestamp->abs_value_us >= hs->end_date.abs_value_us;
- }
return total >= hs->num_results;
}
@@ -305,13 +198,6 @@ build_history_hit_limit (uint64_t total,
* to be allocated, and the second to actually populate every
* element.
*
- * This command has a limitation currently: it orders the history
- * list with descending elements if and only if the 'delta' was
- * given negative; and will order the list with ascending elements
- * if and only if the 'delta' was given positive. Therefore,
- * for now it is NOT possible to test such a "/history" request:
- * "/history?auth=basic&direction=both&delta=10&ordering=descending"
- *
* @param is interpreter state (supposedly having the
* current CMD pointing at a "history" CMD).
* @param[out] rh history array to initialize.
@@ -350,10 +236,7 @@ build_history (struct TALER_TESTING_Interpreter *is,
(add_incoming_cmd, 0, &row_id_start));
}
- GNUNET_assert ((0 != hs->num_results) || /* "/history" */
- (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != /* "/history-range" */
- hs->start_date.abs_value_us));
-
+ GNUNET_assert (0 != hs->num_results);
if (0 == is->ip)
{
TALER_LOG_DEBUG ("Checking history at first CMD..\n");
@@ -387,8 +270,9 @@ build_history (struct TALER_TESTING_Interpreter *is,
for (unsigned int off = start; off != end + inc; off += inc)
{
const struct TALER_TESTING_Command *pos = &is->commands[off];
- int cancelled;
const uint64_t *row_id;
+ const char *credit_account;
+ const char *debit_account;
/**
* The following command allows us to skip over those CMDs
@@ -411,28 +295,6 @@ build_history (struct TALER_TESTING_Interpreter *is,
}
}
- /* Seek "/history-range" starting row, _if_ that's the case */
- if ((GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
- hs->start_date.abs_value_us) && (GNUNET_YES != ok))
- {
- const struct GNUNET_TIME_Absolute *timestamp;
-
- TALER_TESTING_get_trait_absolute_time (pos,
- 0,
- &timestamp);
- TALER_LOG_DEBUG
- ("Seeking first row, start vs timestamp: %llu vs %llu\n",
- (long long unsigned int) hs->start_date.abs_value_us,
- (long long unsigned int) timestamp->abs_value_us);
-
- if (hs->start_date.abs_value_us <= timestamp->abs_value_us)
- {
- total = 0;
- ok = GNUNET_YES;
- continue;
- }
- }
-
/* when 'start' was _not_ given, then ok == GNUNET_YES */
if (GNUNET_NO == ok)
continue; /* skip until we find the marker */
@@ -447,37 +309,23 @@ build_history (struct TALER_TESTING_Interpreter *is,
break;
}
- cancelled = test_cancelled (is, off);
-
- if ( (GNUNET_YES == cancelled) &&
- (0 == (hs->direction & TALER_BANK_DIRECTION_CANCEL)) )
- {
- TALER_LOG_INFO ("Ignoring canceled wire"
- " transfer from history\n");
- continue;
- }
-
- const uint64_t *credit_account_no;
- const uint64_t *debit_account_no;
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
- (pos, &credit_account_no));
+ (pos, &credit_account));
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
- (pos, &debit_account_no));
+ (pos, &debit_account));
TALER_LOG_INFO ("Potential history element:"
- " %llu->%llu; my account: %llu\n",
- (unsigned long long) *debit_account_no,
- (unsigned long long) *credit_account_no,
- (unsigned long long) hs->account_no);
-
- if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) &&
- (hs->account_no == *credit_account_no)) ||
- ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
- (hs->account_no == *debit_account_no)) )
+ " %s->%s; my account: %s\n",
+ debit_account,
+ credit_account,
+ hs->account_url);
+
+ if (0 == strcasecmp (hs->account_url,
+ credit_account))
{
TALER_LOG_INFO ("+1 my history\n");
total++; /* found matching record */
@@ -508,11 +356,10 @@ build_history (struct TALER_TESTING_Interpreter *is,
for (unsigned int off = start; off != end + inc; off += inc)
{
const struct TALER_TESTING_Command *pos = &is->commands[off];
- int cancelled;
const uint64_t *row_id;
char *bank_hostname;
- const uint64_t *credit_account_no;
- const uint64_t *debit_account_no;
+ const char *credit_account;
+ const char *debit_account;
if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID
(pos, &row_id))
@@ -533,28 +380,6 @@ build_history (struct TALER_TESTING_Interpreter *is,
}
}
- /* Seek "/history-range" starting row, _if_ that's the case */
- if ((GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
- hs->start_date.abs_value_us) && (GNUNET_YES != ok))
- {
- const struct GNUNET_TIME_Absolute *timestamp;
-
- TALER_TESTING_get_trait_absolute_time (pos,
- 0,
- &timestamp);
- TALER_LOG_DEBUG
- ("Seeking first row, start vs timestamp (2): %llu vs %llu\n",
- (long long unsigned int) hs->start_date.abs_value_us,
- (long long unsigned int) timestamp->abs_value_us);
-
- if (hs->start_date.abs_value_us <= timestamp->abs_value_us)
- {
- total = 0;
- ok = GNUNET_YES;
- continue;
- }
- }
-
TALER_LOG_INFO ("Found first row (2)\n");
if (GNUNET_NO == ok)
@@ -574,43 +399,31 @@ build_history (struct TALER_TESTING_Interpreter *is,
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
- (pos, &credit_account_no));
+ (pos, &credit_account));
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
- (pos, &debit_account_no));
+ (pos, &debit_account));
TALER_LOG_INFO ("Potential history bit:"
- " %llu->%llu; my account: %llu\n",
- (unsigned long long) *debit_account_no,
- (unsigned long long) *credit_account_no,
- (unsigned long long) hs->account_no);
+ " %s->%s; my account: %s\n",
+ debit_account,
+ credit_account,
+ hs->account_url);
/**
* Discard transactions where the audited account played
* _both_ the credit and the debit roles, but _only if_
* the audit goes on both directions.. This needs more
* explaination!
- */if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) &&
- (hs->account_no == *credit_account_no)) &&
- ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
- (hs->account_no == *debit_account_no)) )
+ */if (0 == strcasecmp (hs->account_url,
+ credit_account))
{
GNUNET_break (0);
continue;
}
- cancelled = test_cancelled (is, off);
- if ( (GNUNET_YES == cancelled) &&
- (0 == (hs->direction & TALER_BANK_DIRECTION_CANCEL)) )
- {
- TALER_LOG_WARNING ("`%s' was cancelled\n",
- TALER_TESTING_interpreter_get_current_label
- (is));
- continue;
- }
-
- bank_hostname = strchr (hs->bank_url, ':');
+ bank_hostname = strchr (hs->account_url, ':');
GNUNET_assert (NULL != bank_hostname);
bank_hostname += 3;
@@ -618,68 +431,36 @@ build_history (struct TALER_TESTING_Interpreter *is,
* information. */
/* Asked for credit, and account got the credit. */
- if ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) &&
- (hs->account_no == *credit_account_no))
- {
- h[total].direction = TALER_BANK_DIRECTION_CREDIT;
- if (GNUNET_YES == cancelled)
- h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
-
- GNUNET_asprintf
- (&h[total].details.account_url,
- ('/' == bank_hostname[strlen (bank_hostname) - 1])
- ? "payto://x-taler-bank/%s%llu"
- : "payto://x-taler-bank/%s/%llu",
- bank_hostname,
- (unsigned long long) *debit_account_no);
- }
-
- /* Asked for debit, and account got the debit. */
- if ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
- (hs->account_no == *debit_account_no))
+ if (0 == strcasecmp (hs->account_url,
+ credit_account))
{
- h[total].direction = TALER_BANK_DIRECTION_DEBIT;
- if (GNUNET_YES == cancelled)
- h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
-
- GNUNET_asprintf
- (&h[total].details.account_url,
- ('/' == bank_hostname[strlen (bank_hostname) - 1])
- ? "payto://x-taler-bank/%s%llu"
- : "payto://x-taler-bank/%s/%llu",
- bank_hostname,
- (unsigned long long) *credit_account_no);
+ h[total].url = GNUNET_strdup (debit_account);
+ h[total].details.account_url = h[total].url;
}
/* This block _completes_ the information of the current item,
* with amount / subject / exchange URL. */
- if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) &&
- (hs->account_no == *credit_account_no)) ||
- ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
- (hs->account_no == *debit_account_no)) )
+ if (0 == strcasecmp (hs->account_url,
+ credit_account))
{
const struct TALER_Amount *amount;
- const char *subject;
- const char *exchange_url;
-
- GNUNET_assert
- (GNUNET_OK == TALER_TESTING_get_trait_amount_obj
- (pos, 0, &amount));
-
- GNUNET_assert
- (GNUNET_OK == TALER_TESTING_get_trait_transfer_subject
- (pos, 0, &subject));
-
- GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_url
- (pos, 0, &exchange_url));
-
+ const struct TALER_ReservePublicKeyP *reserve_pub;
+ const char *account_url;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_amount_obj
+ (pos, 0, &amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_reserve_pub
+ (pos, 0, &reserve_pub));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_url
+ (pos, 1,
+ &account_url));
h[total].details.amount = *amount;
-
h[total].row_id = *row_id;
- GNUNET_asprintf (&h[total].details.wire_transfer_subject,
- "%s %s",
- subject,
- exchange_url);
+ h[total].details.reserve_pub = *reserve_pub;
+ h[total].details.account_url = account_url;
TALER_LOG_INFO ("+1-bit of my history\n");
total++;
}
@@ -723,8 +504,7 @@ compute_result_count (struct TALER_TESTING_Interpreter *is)
static int
check_result (struct TALER_TESTING_Interpreter *is,
unsigned int off,
- enum TALER_BANK_Direction dir,
- const struct TALER_BANK_TransferDetails *details)
+ const struct TALER_BANK_CreditDetails *details)
{
uint64_t total;
struct History *h;
@@ -737,27 +517,22 @@ check_result (struct TALER_TESTING_Interpreter *is,
" results, but got result #%u to check\n",
(unsigned int) total,
off);
- print_expected (h, total, off);
- return GNUNET_SYSERR;
- }
- if (h[off].direction != dir)
- {
- GNUNET_break (0);
- print_expected (h, total, off);
- free_history (h,
- total);
+ print_expected (h,
+ total,
+ off);
return GNUNET_SYSERR;
}
-
- if ( (0 != strcmp (h[off].details.wire_transfer_subject,
- details->wire_transfer_subject)) ||
+ if ( (0 != GNUNET_memcmp (&h[off].details.reserve_pub,
+ &details->reserve_pub)) ||
(0 != TALER_amount_cmp (&h[off].details.amount,
&details->amount)) ||
(0 != strcasecmp (h[off].details.account_url,
details->account_url)) )
{
GNUNET_break (0);
- print_expected (h, total, off);
+ print_expected (h,
+ total,
+ off);
free_history (h,
total);
return GNUNET_SYSERR;
@@ -789,22 +564,30 @@ check_result (struct TALER_TESTING_Interpreter *is,
* @param details details about the wire transfer.
* @param json detailed response from the HTTPD, or NULL if
* reply was not in JSON.
+ * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/
-static void
+static int
history_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
- enum TALER_BANK_Direction dir,
uint64_t row_id,
- const struct TALER_BANK_TransferDetails *details,
+ const struct TALER_BANK_CreditDetails *details,
const json_t *json)
{
struct TALER_TESTING_Interpreter *is = cls;
struct HistoryState *hs = is->commands[is->ip].cls;
(void) row_id;
- /*NOTE: "204 No Content" is used to signal the end of results.*/
- if (MHD_HTTP_NO_CONTENT == http_status)
+ if (MHD_HTTP_OK != http_status)
+ {
+ hs->hh = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unwanted response code from /history: %u\n",
+ http_status);
+ TALER_TESTING_interpreter_fail (is);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == details)
{
hs->hh = NULL;
if ( (hs->results_obtained != compute_result_count (is)) ||
@@ -829,47 +612,33 @@ history_cb (void *cls,
free_history (h,
total);
TALER_TESTING_interpreter_fail (is);
- return;
+ return GNUNET_SYSERR;
}
TALER_TESTING_interpreter_next (is);
- return;
- }
-
- if (MHD_HTTP_OK != http_status)
- {
- hs->hh = NULL;
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unwanted response code from /history[-range]: %u\n",
- http_status);
- TALER_TESTING_interpreter_fail (is);
- return;
+ return GNUNET_OK;
}
/* check current element */
if (GNUNET_OK != check_result (is,
hs->results_obtained,
- dir,
details))
{
- GNUNET_break (0);
-
- {
- char *acc;
-
- acc = json_dumps (json,
- JSON_COMPACT);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Result %u was `%s'\n",
- (unsigned int) hs->results_obtained++,
- acc);
- if (NULL != acc)
- free (acc);
- }
+ char *acc;
+ GNUNET_break (0);
+ acc = json_dumps (json,
+ JSON_COMPACT);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Result %u was `%s'\n",
+ (unsigned int) hs->results_obtained++,
+ acc);
+ if (NULL != acc)
+ free (acc);
hs->failed = GNUNET_YES;
- return;
+ return GNUNET_SYSERR;
}
hs->results_obtained++;
+ return GNUNET_OK;
}
@@ -886,9 +655,8 @@ history_run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct HistoryState *hs = cls;
- uint64_t row_id = UINT64_MAX;
- const uint64_t *row_id_ptr = &row_id;
- struct TALER_BANK_AuthenticationData *auth;
+ uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX;
+ const uint64_t *row_ptr;
(void) cmd;
/* Get row_id from trait. */
@@ -902,27 +670,24 @@ history_run (void *cls,
if (NULL == history_cmd)
TALER_TESTING_FAIL (is);
- if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (history_cmd,
- 0,
- &row_id_ptr))
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_uint64 (history_cmd,
+ 0,
+ &row_ptr))
TALER_TESTING_FAIL (is);
- row_id = *row_id_ptr;
-
+ else
+ row_id = *row_ptr;
TALER_LOG_DEBUG ("row id (from trait) is %llu\n",
(unsigned long long) row_id);
}
- auth = &AUTHS[hs->account_no - 1];
- hs->hh = TALER_BANK_history (is->ctx,
- hs->bank_url,
- auth,
- hs->account_no,
- hs->direction,
- hs->ascending,
- row_id,
- hs->num_results,
- &history_cb,
- is);
+ hs->hh = TALER_BANK_credit_history (is->ctx,
+ hs->account_url,
+ NULL,
+ row_id,
+ hs->num_results,
+ &history_cb,
+ is);
GNUNET_assert (NULL != hs->hh);
}
@@ -944,8 +709,9 @@ history_cleanup (void *cls,
if (NULL != hs->hh)
{
TALER_LOG_WARNING ("/history did not complete\n");
- TALER_BANK_history_cancel (hs->hh);
+ TALER_BANK_credit_history_cancel (hs->hh);
}
+ GNUNET_free (hs->account_url);
GNUNET_free (hs);
}
@@ -954,12 +720,8 @@ history_cleanup (void *cls,
* Make a "history" CMD.
*
* @param label command label.
- * @param bank_url base URL of the bank offering the "history"
+ * @param account_url base URL of the account offering the "history"
* operation.
- * @param account_no bank account number to ask the history for.
- * @param direction which direction this operation is interested.
- * @param ascending if #GNUNET_YES, the bank will return the rows
- * in ascending (= chronological) order.
* @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.
@@ -967,25 +729,17 @@ history_cleanup (void *cls,
* @return the command.
*/
struct TALER_TESTING_Command
-TALER_TESTING_cmd_bank_history (const char *label,
- const char *bank_url,
- uint64_t account_no,
- enum TALER_BANK_Direction direction,
- unsigned int ascending,
+TALER_TESTING_cmd_bank_credits (const char *label,
+ const char *account_url,
const char *start_row_reference,
- unsigned long long num_results)
+ long long num_results)
{
struct HistoryState *hs;
hs = GNUNET_new (struct HistoryState);
- hs->bank_url = bank_url;
- hs->account_no = account_no;
- hs->direction = direction;
+ hs->account_url = GNUNET_strdup (account_url);
hs->start_row_reference = start_row_reference;
hs->num_results = num_results;
- hs->ascending = ascending;
- hs->start_date = GNUNET_TIME_UNIT_FOREVER_ABS;
- hs->end_date = GNUNET_TIME_UNIT_FOREVER_ABS;
{
struct TALER_TESTING_Command cmd = {
@@ -1001,4 +755,4 @@ TALER_TESTING_cmd_bank_history (const char *label,
}
-/* end of testing_api_cmd_history.c */
+/* end of testing_api_cmd_credit_history.c */
diff --git a/src/bank-lib/testing_api_cmd_history_debit.c b/src/bank-lib/testing_api_cmd_history_debit.c
new file mode 100644
index 000000000..93f84da01
--- /dev/null
+++ b/src/bank-lib/testing_api_cmd_history_debit.c
@@ -0,0 +1,758 @@
+/*
+ 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 bank-lib/testing_api_cmd_history.c
+ * @brief command to check the /history API from the bank.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_testing_lib.h"
+#include "taler_testing_bank_lib.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_fakebank_lib.h"
+
+
+/**
+ * State for a "history" CMD.
+ */
+struct HistoryState
+{
+ /**
+ * Base URL of the account offering the "history" operation.
+ */
+ const char *account_url;
+
+ /**
+ * Reference to command defining the
+ * first row number we want in the result.
+ */
+ const char *start_row_reference;
+
+ /**
+ * How many rows we want in the result, _at most_,
+ * and ascending/descending.
+ */
+ long long num_results;
+
+ /**
+ * Handle to a pending "history" operation.
+ */
+ struct TALER_BANK_DebitHistoryHandle *hh;
+
+ /**
+ * Expected number of results (= rows).
+ */
+ uint64_t results_obtained;
+
+ /**
+ * Set to GNUNET_YES if the callback detects something
+ * unexpected.
+ */
+ int failed;
+
+};
+
+
+/**
+ * Item in the transaction history, as reconstructed from the
+ * command history.
+ */
+struct History
+{
+
+ /**
+ * Wire details.
+ */
+ struct TALER_BANK_DebitDetails details;
+
+ /**
+ * Serial ID of the wire transfer.
+ */
+ uint64_t row_id;
+
+ /**
+ * URL to free.
+ */
+ char *url;
+};
+
+
+/**
+ * Offer internal data to other commands.
+ *
+ * @param cls closure.
+ * @param ret[out] set to the wanted data.
+ * @param trait name of the trait.
+ * @param index index number of the traits to be returned.
+ *
+ * @return #GNUNET_OK on success
+ */
+static int
+history_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ (void) cls;
+ (void) ret;
+ (void) trait;
+ (void) index;
+ /* Must define this function because some callbacks
+ * look for certain traits on _all_ the commands. */
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Free history @a h of length @a h_len.
+ *
+ * @param h history array to free.
+ * @param h_len number of entries in @a h.
+ */
+static void
+free_history (struct History *h,
+ uint64_t h_len)
+{
+ for (uint64_t off = 0; off<h_len; off++)
+ GNUNET_free (h[off].url);
+ GNUNET_free_non_null (h);
+}
+
+
+/**
+ * Log which history we expected. Called when an error occurs.
+ *
+ * @param h what we expected.
+ * @param h_len number of entries in @a h.
+ * @param off position of the missmatch.
+ */
+static void
+print_expected (struct History *h,
+ uint64_t h_len,
+ unsigned int off)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Transaction history missmatch at position %u/%llu\n",
+ off,
+ (unsigned long long) h_len);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected history:\n");
+ for (uint64_t i = 0; i<h_len; i++)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "H(%llu): %s (serial: %llu, subject: %s,"
+ " counterpart: %s)\n",
+ (unsigned long long) i,
+ TALER_amount2s (&h[i].details.amount),
+ (unsigned long long) h[i].row_id,
+ TALER_B2S (&h[i].details.wtid),
+ h[i].details.account_url);
+ }
+}
+
+
+/**
+ * Tell if the current item is beyond the allowed limit.
+ *
+ * @param total current number of items in the built history list.
+ * Note, this is the list we build locally and compare with
+ * what the server returned.
+ * @param hs the history CMD state.
+ * @param pos current item to be evaluated or not (if the list
+ * has already enough elements).
+ * @return GNUNET_OK / GNUNET_NO.
+ */
+static int
+build_history_hit_limit (uint64_t total,
+ const struct HistoryState *hs,
+ const struct TALER_TESTING_Command *pos)
+{
+ return total >= hs->num_results;
+}
+
+
+/**
+ * This function constructs the list of history elements that
+ * interest the account number of the caller. It has two main
+ * loops: the first to figure out how many history elements have
+ * to be allocated, and the second to actually populate every
+ * element.
+ *
+ * @param is interpreter state (supposedly having the
+ * current CMD pointing at a "history" CMD).
+ * @param[out] rh history array to initialize.
+ *
+ * @return number of entries in @a rh.
+ */
+static uint64_t
+build_history (struct TALER_TESTING_Interpreter *is,
+ struct History **rh)
+{
+ struct HistoryState *hs = is->commands[is->ip].cls;
+ uint64_t total;
+ struct History *h;
+ const struct TALER_TESTING_Command *add_incoming_cmd;
+ int inc;
+ unsigned int start;
+ unsigned int end;
+
+ /**
+ * @var turns GNUNET_YES whenever either no 'start' value was
+ * given for the history query, or the given value is found
+ * in the list of all the CMDs.
+ */int ok;
+ const uint64_t *row_id_start = NULL;
+
+ if (NULL != hs->start_row_reference)
+ {
+ TALER_LOG_INFO
+ ("`%s': start row given via reference `%s'\n",
+ TALER_TESTING_interpreter_get_current_label (is),
+ hs->start_row_reference);
+ add_incoming_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, hs->start_row_reference);
+ GNUNET_assert (NULL != add_incoming_cmd);
+ GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64
+ (add_incoming_cmd, 0, &row_id_start));
+ }
+
+ GNUNET_assert (0 != hs->num_results);
+ if (0 == is->ip)
+ {
+ TALER_LOG_DEBUG ("Checking history at first CMD..\n");
+ *rh = NULL;
+ return 0;
+ }
+
+ /* AKA 'delta'. */
+ if (hs->num_results > 0)
+ {
+ inc = 1; /* _inc_rement */
+ start = 0;
+ end = is->ip - 1;
+ }
+ else
+ {
+ inc = -1;
+ start = is->ip - 1;
+ end = 0;
+ }
+
+ total = 0;
+ ok = GNUNET_NO;
+
+ if (NULL == row_id_start)
+ ok = GNUNET_YES;
+
+ /* This loop counts how many commands _later than "start"_ belong
+ * to the history of the caller. This is stored in the @var total
+ * variable. */
+ for (unsigned int off = start; off != end + inc; off += inc)
+ {
+ const struct TALER_TESTING_Command *pos = &is->commands[off];
+ const uint64_t *row_id;
+ const char *debit_account;
+ const char *credit_account;
+
+ /**
+ * The following command allows us to skip over those CMDs
+ * that do not offer a "row_id" trait. Such skipped CMDs are
+ * not interesting for building a history.
+ */if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (pos,
+ 0,
+ &row_id))
+ continue;
+
+ /* Seek "/history" starting row. */
+ if (NULL != row_id_start)
+ {
+ if (*row_id_start == *row_id)
+ {
+ /* Doesn't count, start is excluded from output. */
+ total = 0;
+ ok = GNUNET_YES;
+ continue;
+ }
+ }
+
+ /* when 'start' was _not_ given, then ok == GNUNET_YES */
+ if (GNUNET_NO == ok)
+ continue; /* skip until we find the marker */
+
+ TALER_LOG_DEBUG ("Found first row\n");
+
+ if (build_history_hit_limit (total,
+ hs,
+ pos))
+ {
+ TALER_LOG_DEBUG ("Hit history limit\n");
+ break;
+ }
+
+ GNUNET_assert
+ (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
+ (pos, &debit_account));
+
+ GNUNET_assert
+ (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
+ (pos, &credit_account));
+
+ TALER_LOG_INFO ("Potential history element:"
+ " %s->%s; my account: %s\n",
+ debit_account,
+ credit_account,
+ hs->account_url);
+
+ if (0 == strcasecmp (hs->account_url,
+ debit_account))
+ {
+ TALER_LOG_INFO ("+1 my history\n");
+ total++; /* found matching record */
+ }
+ }
+
+ GNUNET_assert (GNUNET_YES == ok);
+
+ if (0 == total)
+ {
+ TALER_LOG_DEBUG ("Checking history at first CMD.. (2)\n");
+ *rh = NULL;
+ return 0;
+ }
+
+
+ GNUNET_assert (total < UINT_MAX);
+ h = GNUNET_new_array ((unsigned int) total,
+ struct History);
+ total = 0;
+ ok = GNUNET_NO;
+ if (NULL == row_id_start)
+ ok = GNUNET_YES;
+
+ /**
+ * This loop _only_ populates the array of history elements.
+ */
+ for (unsigned int off = start; off != end + inc; off += inc)
+ {
+ const struct TALER_TESTING_Command *pos = &is->commands[off];
+ const uint64_t *row_id;
+ char *bank_hostname;
+ const char *credit_account;
+ const char *debit_account;
+
+ if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID
+ (pos, &row_id))
+ continue;
+
+ if (NULL != row_id_start)
+ {
+
+ if (*row_id_start == *row_id)
+ {
+ /**
+ * Warning: this zeroing is superfluous, as
+ * total doesn't get incremented if 'start'
+ * was given and couldn't be found.
+ */total = 0;
+ ok = GNUNET_YES;
+ continue;
+ }
+ }
+
+ TALER_LOG_INFO ("Found first row (2)\n");
+
+ if (GNUNET_NO == ok)
+ {
+ TALER_LOG_INFO ("Skip on `%s'\n",
+ pos->label);
+ continue; /* skip until we find the marker */
+ }
+
+ if (build_history_hit_limit (total,
+ hs,
+ pos))
+ {
+ TALER_LOG_INFO ("Hit history limit (2)\n");
+ break;
+ }
+
+ GNUNET_assert
+ (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
+ (pos, &debit_account));
+
+ GNUNET_assert
+ (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
+ (pos, &credit_account));
+
+ TALER_LOG_INFO ("Potential history bit:"
+ " %s->%s; my account: %s\n",
+ debit_account,
+ credit_account,
+ hs->account_url);
+
+ /**
+ * Discard transactions where the audited account played
+ * _both_ the debit and the debit roles, but _only if_
+ * the audit goes on both directions.. This needs more
+ * explaination!
+ */if (0 == strcasecmp (hs->account_url,
+ debit_account))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+
+ bank_hostname = strchr (hs->account_url, ':');
+ GNUNET_assert (NULL != bank_hostname);
+ bank_hostname += 3;
+
+ /* Next two blocks only put the 'direction' and 'banking'
+ * information. */
+
+ /* Asked for debit, and account got the debit. */
+ if (0 == strcasecmp (hs->account_url,
+ debit_account))
+ {
+ h[total].url = GNUNET_strdup (credit_account);
+ h[total].details.account_url = h[total].url;
+ }
+
+ /* This block _completes_ the information of the current item,
+ * with amount / subject / exchange URL. */
+ if (0 == strcasecmp (hs->account_url,
+ debit_account))
+ {
+ const struct TALER_Amount *amount;
+ const struct TALER_WireTransferIdentifierRawP *wtid;
+ const char *account_url;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_amount_obj
+ (pos, 0, &amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_wtid
+ (pos, 0, &wtid));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_url
+ (pos, 1,
+ &account_url));
+ h[total].details.amount = *amount;
+ h[total].row_id = *row_id;
+ h[total].details.wtid = *wtid;
+ h[total].details.account_url = account_url;
+ TALER_LOG_INFO ("+1-bit of my history\n");
+ total++;
+ }
+ }
+ *rh = h;
+ return total;
+}
+
+
+/**
+ * Compute how many results we expect to be returned for
+ * the current command at @a is.
+ *
+ * @param is the interpreter state to inspect.
+ * @return number of results expected.
+ */
+static uint64_t
+compute_result_count (struct TALER_TESTING_Interpreter *is)
+{
+ uint64_t total;
+ struct History *h;
+
+ total = build_history (is, &h);
+ free_history (h, total);
+ return total;
+}
+
+
+/**
+ * Check that the "/history" response matches the
+ * CMD whose offset in the list of CMDs is @a off.
+ *
+ * @param is the interpreter state.
+ * @param off the offset (of the CMD list) where the command
+ * to check is.
+ * @param dir the expected direction of the transaction.
+ * @param details the expected transaction details.
+ *
+ * @return #GNUNET_OK if the transaction is what we expect.
+ */
+static int
+check_result (struct TALER_TESTING_Interpreter *is,
+ unsigned int off,
+ const struct TALER_BANK_DebitDetails *details)
+{
+ uint64_t total;
+ struct History *h;
+
+ total = build_history (is, &h);
+ if (off >= total)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Test says history has at most %u"
+ " results, but got result #%u to check\n",
+ (unsigned int) total,
+ off);
+ print_expected (h,
+ total,
+ off);
+ return GNUNET_SYSERR;
+ }
+ if ( (0 != GNUNET_memcmp (&h[off].details.wtid,
+ &details->wtid)) ||
+ (0 != TALER_amount_cmp (&h[off].details.amount,
+ &details->amount)) ||
+ (0 != strcasecmp (h[off].details.account_url,
+ details->account_url)) )
+ {
+ GNUNET_break (0);
+ print_expected (h,
+ total,
+ off);
+ free_history (h,
+ total);
+ return GNUNET_SYSERR;
+ }
+ free_history (h,
+ total);
+ return GNUNET_OK;
+}
+
+
+/**
+ * This callback will (1) check that the HTTP response code
+ * is acceptable and (2) that the history is consistent. The
+ * consistency is checked by going through all the past CMDs,
+ * reconstructing then the expected history as of those, and
+ * finally check it against what the bank returned.
+ *
+ * @param cls closure.
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200)
+ * for successful status request 0 if the bank's reply is
+ * bogus (fails to follow the protocol),
+ * #MHD_HTTP_NO_CONTENT if there are no more results; on
+ * success the last callback is always of this status
+ * (even if `abs(num_results)` were already returned).
+ * @param ec taler status code.
+ * @param dir direction of the transfer.
+ * @param row_id monotonically increasing counter corresponding to
+ * the transaction.
+ * @param details details about the wire transfer.
+ * @param json detailed response from the HTTPD, or NULL if
+ * reply was not in JSON.
+ * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
+ */
+static int
+history_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ uint64_t row_id,
+ const struct TALER_BANK_DebitDetails *details,
+ const json_t *json)
+{
+ struct TALER_TESTING_Interpreter *is = cls;
+ struct HistoryState *hs = is->commands[is->ip].cls;
+
+ (void) row_id;
+ if (MHD_HTTP_OK != http_status)
+ {
+ hs->hh = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unwanted response code from /history: %u\n",
+ http_status);
+ TALER_TESTING_interpreter_fail (is);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == details)
+ {
+ hs->hh = NULL;
+ if ( (hs->results_obtained != compute_result_count (is)) ||
+ (GNUNET_YES == hs->failed) )
+ {
+ uint64_t total;
+ struct History *h;
+
+ GNUNET_break (0);
+ total = build_history (is, &h);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected history of length %llu, got %llu;"
+ " HTTP status code: %u/%d, failed: %d\n",
+ (unsigned long long) total,
+ (unsigned long long) hs->results_obtained,
+ http_status,
+ (int) ec,
+ hs->failed);
+ print_expected (h,
+ total,
+ UINT_MAX);
+ free_history (h,
+ total);
+ TALER_TESTING_interpreter_fail (is);
+ return GNUNET_SYSERR;
+ }
+ TALER_TESTING_interpreter_next (is);
+ return GNUNET_OK;
+ }
+
+ /* check current element */
+ if (GNUNET_OK != check_result (is,
+ hs->results_obtained,
+ details))
+ {
+ char *acc;
+
+ GNUNET_break (0);
+ acc = json_dumps (json,
+ JSON_COMPACT);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Result %u was `%s'\n",
+ (unsigned int) hs->results_obtained++,
+ acc);
+ if (NULL != acc)
+ free (acc);
+ hs->failed = GNUNET_YES;
+ return GNUNET_SYSERR;
+ }
+ hs->results_obtained++;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Run the command.
+ *
+ * @param cls closure.
+ * @param cmd the command to execute.
+ * @param is the interpreter state.
+ */
+static void
+history_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct HistoryState *hs = cls;
+ uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX;
+ const uint64_t *row_ptr;
+
+ (void) cmd;
+ /* Get row_id from trait. */
+ if (NULL != hs->start_row_reference)
+ {
+ const struct TALER_TESTING_Command *history_cmd;
+
+ history_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, hs->start_row_reference);
+
+ if (NULL == history_cmd)
+ TALER_TESTING_FAIL (is);
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_uint64 (history_cmd,
+ 0,
+ &row_ptr))
+ TALER_TESTING_FAIL (is);
+ else
+ row_id = *row_ptr;
+ TALER_LOG_DEBUG ("row id (from trait) is %llu\n",
+ (unsigned long long) row_id);
+ }
+
+ hs->hh = TALER_BANK_debit_history (is->ctx,
+ hs->account_url,
+ NULL,
+ row_id,
+ hs->num_results,
+ &history_cb,
+ is);
+ GNUNET_assert (NULL != hs->hh);
+}
+
+
+/**
+ * Free the state from a "history" CMD, and possibly cancel
+ * a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd the command which is being cleaned up.
+ */
+static void
+history_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct HistoryState *hs = cls;
+
+ (void) cmd;
+ if (NULL != hs->hh)
+ {
+ TALER_LOG_WARNING ("/history did not complete\n");
+ TALER_BANK_debit_history_cancel (hs->hh);
+ }
+ GNUNET_free (hs);
+}
+
+
+/**
+ * Make a "history" CMD.
+ *
+ * @param label command label.
+ * @param account_url base URL of the account offering the "history"
+ * operation.
+ * @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.
+ * @param num_results how many rows we want in the result.
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_bank_debits (const char *label,
+ const char *account_url,
+ const char *start_row_reference,
+ long long num_results)
+{
+ struct HistoryState *hs;
+
+ hs = GNUNET_new (struct HistoryState);
+ hs->account_url = account_url;
+ hs->start_row_reference = start_row_reference;
+ hs->num_results = num_results;
+
+ {
+ struct TALER_TESTING_Command cmd = {
+ .label = label,
+ .cls = hs,
+ .run = &history_run,
+ .cleanup = &history_cleanup,
+ .traits = &history_traits
+ };
+
+ return cmd;
+ }
+}
+
+
+/* end of testing_api_cmd_history_debit.c */
diff --git a/src/bank-lib/testing_api_cmd_reject.c b/src/bank-lib/testing_api_cmd_reject.c
deleted file mode 100644
index 01c189f16..000000000
--- a/src/bank-lib/testing_api_cmd_reject.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2018 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/testing_api_cmd_reject.c
- * @brief command to check the /reject API from the bank.
- * @author Marcello Stanisci
- */
-
-#include "platform.h"
-#include "taler_json_lib.h"
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_exchange_service.h"
-#include "taler_testing_lib.h"
-#include "taler_fakebank_lib.h"
-#include "taler_bank_service.h"
-#include "taler_fakebank_lib.h"
-
-
-/**
- * State for a "reject" CMD.
- */
-struct RejectState
-{
-
- /**
- * Handle of a ongoing "reject" operation.
- */
- struct TALER_BANK_RejectHandle *rh;
-
- /**
- * Reference to any command that can offer a wire
- * transfer "row id" and its credit account so as
- * to give input data to the "reject" operation.
- */
- const char *deposit_reference;
-
- /**
- * Base URL of the bank implementing the "reject"
- * operation.
- */
- const char *bank_url;
-};
-
-/**
- * Check that the response code from the "reject" opetation
- * is acceptable, namely it equals "204 No Content".
- *
- * @param cls closure.
- * @param http_status HTTP response code.
- * @param ec taler-specific error code.
- */
-static void
-reject_cb (void *cls,
- unsigned int http_status,
- enum TALER_ErrorCode ec)
-{
- struct TALER_TESTING_Interpreter *is = cls;
- struct RejectState *rs = is->commands[is->ip].cls;
-
- rs->rh = NULL;
- if (MHD_HTTP_NO_CONTENT != http_status)
- {
- GNUNET_break (0);
- fprintf (stderr,
- "Unexpected response code %u/%d\n",
- http_status,
- (int) ec);
- TALER_TESTING_interpreter_fail (is);
- return;
- }
- TALER_TESTING_interpreter_next (is);
-}
-
-
-/**
- * Cleanup the state of a "reject" CMD, and possibly
- * cancel a pending operation thereof.
- *
- * @param cls closure.
- * @param cmd the command.
- */
-static void
-reject_cleanup (void *cls,
- const struct TALER_TESTING_Command *cmd)
-{
- struct RejectState *rs = cls;
-
- (void) cmd;
- if (NULL != rs->rh)
- {
- TALER_LOG_WARNING ("/reject did not complete\n");
- TALER_BANK_reject_cancel (rs->rh);
- }
- GNUNET_free (rs);
-}
-
-
-/**
- * Run the command.
- *
- * @param cls closure.
- * @param cmd the command to execute.
- * @param is the interpreter state.
- */
-static void
-reject_run (void *cls,
- const struct TALER_TESTING_Command *cmd,
- struct TALER_TESTING_Interpreter *is)
-{
- struct RejectState *rs = cls;
- const struct TALER_TESTING_Command *deposit_cmd;
- const uint64_t *credit_account;
- const uint64_t *row_id;
- extern struct TALER_BANK_AuthenticationData AUTHS[];
-
- (void) cmd;
- deposit_cmd
- = TALER_TESTING_interpreter_lookup_command (is,
- rs->deposit_reference);
- if (NULL == deposit_cmd)
- TALER_TESTING_FAIL (is);
- GNUNET_assert (GNUNET_OK ==
- TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT (deposit_cmd,
- &credit_account));
- GNUNET_assert (GNUNET_OK ==
- TALER_TESTING_GET_TRAIT_ROW_ID (deposit_cmd,
- &row_id));
- TALER_LOG_INFO ("Account %llu rejects deposit\n",
- (unsigned long long) *credit_account);
- rs->rh = TALER_BANK_reject (is->ctx,
- rs->bank_url,
- &AUTHS[*credit_account - 1],
- *credit_account,
- *row_id,
- &reject_cb,
- is);
- GNUNET_assert (NULL != rs->rh);
-}
-
-
-/**
- * Offer internal data from a "reject" CMD to other commands.
- *
- * @param cls closure.
- * @param ret[out] result.
- * @param trait name of the trait.
- * @param index index number of the trait to return.
- *
- * @return #GNUNET_OK on success.
- */
-static int
-reject_traits (void *cls,
- const void **ret,
- const char *trait,
- unsigned int index)
-{
- struct RejectState *rs = cls;
- struct TALER_TESTING_Trait traits[] = {
- TALER_TESTING_make_trait_rejected (0, rs->deposit_reference),
- TALER_TESTING_trait_end ()
- };
-
- return TALER_TESTING_get_trait (traits,
- ret,
- trait,
- index);
-}
-
-
-/**
- * Create a "reject" CMD.
- *
- * @param label command label.
- * @param bank_url base URL of the bank implementing the
- * "reject" operation.
- * @param deposit_reference reference to a command that will
- * provide a "row id" and credit (bank) account to craft
- * the "reject" request.
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_bank_reject (const char *label,
- const char *bank_url,
- const char *deposit_reference)
-{
- struct RejectState *rs;
-
- rs = GNUNET_new (struct RejectState);
- rs->bank_url = bank_url;
- rs->deposit_reference = deposit_reference;
-
- {
- struct TALER_TESTING_Command cmd = {
- .cls = rs,
- .run = &reject_run,
- .cleanup = &reject_cleanup,
- .label = label,
- .traits = &reject_traits
- };
-
- return cmd;
- }
-}
-
-
-/* end of testing_api_cmd_reject.c */