diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-01-11 15:19:56 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-01-11 15:20:17 +0100 |
commit | 9443c10d7feb0d91323869dd08ec61ca781564f4 (patch) | |
tree | fd617ea56cc1d2ea370ce7e5467574a536b52d28 /src | |
parent | 554da10133eb491b352a106b98ebeaed797133bb (diff) | |
download | exchange-9443c10d7feb0d91323869dd08ec61ca781564f4.tar.xz |
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')
65 files changed, 3771 insertions, 6523 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 761b9c335..cc749e275 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,7 +22,7 @@ pkgcfg_DATA = \ EXTRA_DIST = \ taler.conf -SUBDIRS = include util wire json curl $(PQ_DIR) mhd $(BANK_LIB) wire-plugins exchangedb exchange exchange-tools auditordb auditor +SUBDIRS = include util wire json curl $(PQ_DIR) mhd $(BANK_LIB) exchangedb exchange exchange-tools auditordb auditor if HAVE_LIBCURL SUBDIRS += lib benchmark else diff --git a/src/auditor/Makefile.am b/src/auditor/Makefile.am index b942076ff..d5d7669bc 100644 --- a/src/auditor/Makefile.am +++ b/src/auditor/Makefile.am @@ -73,10 +73,12 @@ taler_wire_auditor_LDADD = \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/wire/libtalerwire.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/auditordb/libtalerauditordb.la \ -ljansson \ -lgnunetjson \ + -lgnunetcurl \ -lgnunetutil taler_auditor_sign_SOURCES = \ diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index 0cc2868f6..bb769fd6c 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -1010,8 +1010,7 @@ struct ReserveContext * @param reserve_pub public key of the reserve (also the WTID) * @param credit amount that was received * @param sender_account_details information about the sender's bank account - * @param wire_reference unique reference identifying the wire transfer (binary blob) - * @param wire_reference_size number of bytes in @a wire_reference + * @param wire_reference unique reference identifying the wire transfer * @param execution_date when did we receive the funds * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ @@ -1021,8 +1020,7 @@ handle_reserve_in (void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, const char *sender_account_details, - const void *wire_reference, - size_t wire_reference_size, + uint64_t wire_reference, struct GNUNET_TIME_Absolute execution_date) { struct ReserveContext *rc = cls; @@ -2032,35 +2030,6 @@ analyze_reserves (void *cls) /** - * Information we keep per loaded wire plugin. - */ -struct WirePlugin -{ - - /** - * Kept in a DLL. - */ - struct WirePlugin *next; - - /** - * Kept in a DLL. - */ - struct WirePlugin *prev; - - /** - * Name of the wire method. - */ - char *type; - - /** - * Handle to the wire plugin. - */ - struct TALER_WIRE_Plugin *plugin; - -}; - - -/** * Information about wire fees charged by the exchange. */ struct WireFeeInfo @@ -2106,16 +2075,6 @@ struct AggregationContext { /** - * DLL of wire plugins encountered. - */ - struct WirePlugin *wire_head; - - /** - * DLL of wire plugins encountered. - */ - struct WirePlugin *wire_tail; - - /** * DLL of wire fees charged by the exchange. */ struct WireFeeInfo *fee_head; @@ -2133,46 +2092,6 @@ struct AggregationContext /** - * Find the relevant wire plugin. - * - * @param ac context to search - * @param type type of the wire plugin to load; it - * will be used _as is_ from the dynamic loader. - * @return NULL on error - */ -static struct TALER_WIRE_Plugin * -get_wire_plugin (struct AggregationContext *ac, - const char *type) -{ - struct WirePlugin *wp; - struct TALER_WIRE_Plugin *plugin; - - for (wp = ac->wire_head; NULL != wp; wp = wp->next) - if (0 == strcmp (type, - wp->type)) - return wp->plugin; - - /* Wants the exact *plugin name* (!= method) */ - plugin = TALER_WIRE_plugin_load (cfg, - type); - if (NULL == plugin) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to locate wire plugin for `%s'\n", - type); - return NULL; - } - wp = GNUNET_new (struct WirePlugin); - wp->type = GNUNET_strdup (type); - wp->plugin = plugin; - GNUNET_CONTAINER_DLL_insert (ac->wire_head, - ac->wire_tail, - wp); - return plugin; -} - - -/** * Closure for #wire_transfer_information_cb. */ struct WireCheckContext @@ -2884,7 +2803,6 @@ check_wire_out_cb { struct AggregationContext *ac = cls; struct WireCheckContext wcc; - struct TALER_WIRE_Plugin *plugin; struct TALER_Amount final_amount; struct TALER_Amount exchange_gain; enum GNUNET_DB_QueryStatus qs; @@ -2976,19 +2894,7 @@ check_wire_out_cb } /* Round down to amount supported by wire method */ - plugin = get_wire_plugin - (ac, - TALER_WIRE_get_plugin_from_method (method)); - if (NULL == plugin) - { - GNUNET_break (0); - GNUNET_free (method); - return GNUNET_SYSERR; - } - GNUNET_free (method); - GNUNET_break (GNUNET_SYSERR != - plugin->amount_round (plugin->cls, - &final_amount)); + GNUNET_break (TALER_WIRE_amount_round (&final_amount)); /* Calculate the exchange's gain as the fees plus rounding differences! */ if (GNUNET_OK != @@ -3071,7 +2977,6 @@ static enum GNUNET_DB_QueryStatus analyze_aggregations (void *cls) { struct AggregationContext ac; - struct WirePlugin *wc; struct WireFeeInfo *wfi; enum GNUNET_DB_QueryStatus qsx; enum GNUNET_DB_QueryStatus qs; @@ -3125,15 +3030,6 @@ analyze_aggregations (void *cls) GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); ac.qs = qs; } - while (NULL != (wc = ac.wire_head)) - { - GNUNET_CONTAINER_DLL_remove (ac.wire_head, - ac.wire_tail, - wc); - TALER_WIRE_plugin_unload (wc->plugin); - GNUNET_free (wc->type); - GNUNET_free (wc); - } while (NULL != (wfi = ac.fee_head)) { GNUNET_CONTAINER_DLL_remove (ac.fee_head, diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c index e66834a13..bd9617466 100644 --- a/src/auditor/taler-wire-auditor.c +++ b/src/auditor/taler-wire-auditor.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2017-2019 Taler Systems SA + 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 @@ -27,9 +27,11 @@ */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> #include "taler_auditordb_plugin.h" #include "taler_exchangedb_plugin.h" #include "taler_json_lib.h" +#include "taler_bank_service.h" #include "taler_wire_lib.h" #include "taler_signatures.h" @@ -65,9 +67,14 @@ struct WireAccount struct WireAccount *prev; /** - * Handle to the plugin. + * Authentication data for the account. */ - struct TALER_WIRE_Plugin *wire_plugin; + struct TALER_BANK_AuthenticationData auth; + + /** + * Our bank account number. + */ + struct TALER_Account account; /** * Name of the section that configures this account. @@ -77,7 +84,12 @@ struct WireAccount /** * Active wire request for the transaction history. */ - struct TALER_WIRE_HistoryHandle *hh; + struct TALER_BANK_CreditHistoryHandle *chh; + + /** + * Active wire request for the transaction history. + */ + struct TALER_BANK_DebitHistoryHandle *dhh; /** * Progress point for this account. @@ -92,17 +104,12 @@ struct WireAccount /** * Where we are in the inbound (CREDIT) transaction history. */ - void *in_wire_off; + uint64_t in_wire_off; /** * Where we are in the inbound (DEBIT) transaction history. */ - void *out_wire_off; - - /** - * Number of bytes in #in_wire_off and #out_wire_off. - */ - size_t wire_off_size; + uint64_t out_wire_off; /** * We should check for inbound transactions to this account. @@ -341,6 +348,16 @@ static struct TALER_Amount total_wire_format_amount; */ static struct TALER_Amount zero; +/** + * Handle to the context for interacting with the bank. + */ +static struct GNUNET_CURL_Context *ctx; + +/** + * Scheduler context for running the @e ctx. + */ +static struct GNUNET_CURL_RescheduleContext *rc; + /* ***************************** Shutdown **************************** */ @@ -357,14 +374,11 @@ struct ReserveInInfo struct GNUNET_HashCode row_off_hash; /** - * Number of bytes in @e row_off. - */ - size_t row_off_size; - - /** * Expected details about the wire transfer. + * The member "account_url" is to be allocated + * at the end of this struct! */ - struct TALER_WIRE_TransferDetails details; + struct TALER_BANK_CreditDetails details; /** * RowID in reserves_in table. @@ -389,7 +403,7 @@ struct ReserveOutInfo /** * Expected details about the wire transfer. */ - struct TALER_WIRE_TransferDetails details; + struct TALER_BANK_DebitDetails details; }; @@ -427,8 +441,6 @@ free_rii (void *cls, GNUNET_CONTAINER_multihashmap_remove (in_map, key, rii)); - GNUNET_free (rii->details.account_url); - GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */ GNUNET_free (rii); return GNUNET_OK; } @@ -453,8 +465,6 @@ free_roi (void *cls, GNUNET_CONTAINER_multihashmap_remove (out_map, key, roi)); - GNUNET_free (roi->details.account_url); - GNUNET_free_non_null (roi->details.wtid_s); /* field not used (yet) */ GNUNET_free (roi); return GNUNET_OK; } @@ -495,6 +505,17 @@ do_shutdown (void *cls) { struct WireAccount *wa; + if (NULL != ctx) + { + GNUNET_CURL_fini (ctx); + ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } + if (NULL != report_row_inconsistencies) { json_t *report; @@ -617,21 +638,22 @@ do_shutdown (void *cls) } while (NULL != (wa = wa_head)) { - if (NULL != wa->hh) + if (NULL != wa->dhh) { - struct TALER_WIRE_Plugin *wp = wa->wire_plugin; - - wp->get_history_cancel (wp->cls, - wa->hh); - wa->hh = NULL; + TALER_BANK_debit_history_cancel (wa->dhh); + wa->dhh = NULL; + } + if (NULL != wa->chh) + { + TALER_BANK_credit_history_cancel (wa->chh); + wa->chh = NULL; } GNUNET_CONTAINER_DLL_remove (wa_head, wa_tail, wa); - TALER_WIRE_plugin_unload (wa->wire_plugin); + TALER_BANK_auth_free (&wa->auth); + TALER_WIRE_account_free (&wa->account); GNUNET_free (wa->section_name); - GNUNET_free_non_null (wa->in_wire_off); - GNUNET_free_non_null (wa->out_wire_off); GNUNET_free (wa); } if (NULL != adb) @@ -787,8 +809,7 @@ commit (enum GNUNET_DB_QueryStatus qs) wa->section_name, &wa->pp, wa->in_wire_off, - wa->out_wire_off, - wa->wire_off_size); + wa->out_wire_off); else qs = adb->insert_wire_auditor_account_progress (adb->cls, asession, @@ -796,8 +817,7 @@ commit (enum GNUNET_DB_QueryStatus qs) wa->section_name, &wa->pp, wa->in_wire_off, - wa->out_wire_off, - wa->wire_off_size); + wa->out_wire_off); if (0 >= qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1250,9 +1270,7 @@ complain_out_not_found (void *cls, "amount_wired", TALER_JSON_from_amount ( &roi->details.amount), "amount_justified", TALER_JSON_from_amount (&zero), - "wtid", (NULL == roi->details.wtid_s) - ? GNUNET_JSON_from_data_auto (&roi->details.wtid) - : json_string (roi->details.wtid_s), + "wtid", GNUNET_JSON_from_data_auto (&roi->details.wtid), "timestamp", json_from_time_abs ( roi->details.execution_date), "diagnostic", @@ -1317,29 +1335,28 @@ check_exchange_wire_out (struct WireAccount *wa) /** * This function is called for all transactions that - * are credited to the exchange's account (incoming + * are debited from the exchange's account (outgoing * transactions). * * @param cls `struct WireAccount` with current wire account to process + * @param http_status_code http status of the request * @param ec error code in case something went wrong - * @param dir direction of the transfer * @param row_off identification of the position at which we are querying - * @param row_off_size number of bytes in @a row_off * @param details details about the wire transfer * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ static int history_debit_cb (void *cls, + unsigned int http_status_code, enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - const void *row_off, - size_t row_off_size, - const struct TALER_WIRE_TransferDetails *details) + uint64_t row_off, + const struct TALER_BANK_DebitDetails *details, + const json_t *json) { struct WireAccount *wa = cls; struct ReserveOutInfo *roi; - if (TALER_BANK_DIRECTION_NONE == dir) + if (NULL == details) { if (TALER_EC_NONE != ec) { @@ -1348,7 +1365,7 @@ history_debit_cb (void *cls, "Error fetching history: %u!\n", (unsigned int) ec); } - wa->hh = NULL; + wa->dhh = NULL; check_exchange_wire_out (wa); return GNUNET_OK; } @@ -1357,45 +1374,8 @@ history_debit_cb (void *cls, GNUNET_STRINGS_absolute_time_to_string (details->execution_date), TALER_amount2s (&details->amount), TALER_B2S (&details->wtid)); - if (NULL != details->wtid_s) - { - char *diagnostic; - - GNUNET_asprintf (&diagnostic, - "malformed subject `%s'", - details->wtid_s); - GNUNET_break (GNUNET_OK == - TALER_amount_add (&total_wire_format_amount, - &total_wire_format_amount, - &details->amount)); - report (report_wire_format_inconsistencies, - json_pack ("{s:o, s:o, s:s}", - "amount", TALER_JSON_from_amount (&details->amount), - "wire_offset", GNUNET_JSON_from_data (row_off, - row_off_size), - "diagnostic", diagnostic)); - GNUNET_free (diagnostic); - return GNUNET_OK; - } - /* Update offset */ - if (NULL == wa->out_wire_off) - { - wa->wire_off_size = row_off_size; - wa->out_wire_off = GNUNET_malloc (row_off_size); - } - if (wa->wire_off_size != row_off_size) - { - GNUNET_break (0); - commit (GNUNET_DB_STATUS_HARD_ERROR); - wa->hh = NULL; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - memcpy (wa->out_wire_off, - row_off, - row_off_size); - + wa->out_wire_off = row_off; roi = GNUNET_new (struct ReserveOutInfo); GNUNET_CRYPTO_hash (&details->wtid, sizeof (details->wtid), @@ -1420,10 +1400,9 @@ history_debit_cb (void *cls, &total_wire_format_amount, &details->amount)); report (report_wire_format_inconsistencies, - json_pack ("{s:o, s:o, s:s}", + json_pack ("{s:o, s:I, s:s}", "amount", TALER_JSON_from_amount (&details->amount), - "wire_offset", GNUNET_JSON_from_data (row_off, - row_off_size), + "wire_offset", (json_int_t) row_off, "diagnostic", diagnostic)); GNUNET_free (diagnostic); return GNUNET_OK; @@ -1443,7 +1422,6 @@ static void process_debits (void *cls) { struct WireAccount *wa = cls; - struct TALER_WIRE_Plugin *wp; /* skip accounts where DEBIT is not enabled */ while ( (NULL != wa) && @@ -1459,17 +1437,16 @@ process_debits (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Checking bank DEBIT records of account `%s'\n", wa->section_name); - GNUNET_assert (NULL == wa->hh); - wp = wa->wire_plugin; - wa->hh = wp->get_history (wp->cls, - wa->section_name, - TALER_BANK_DIRECTION_DEBIT, - wa->out_wire_off, - wa->wire_off_size, - INT64_MAX, - &history_debit_cb, - wa); - if (NULL == wa->hh) + GNUNET_assert (NULL == wa->dhh); + wa->dhh = TALER_BANK_debit_history (ctx, + wa->account.details.x_taler_bank. + account_base_url, + &wa->auth, + wa->out_wire_off, + INT64_MAX, + &history_debit_cb, + wa); + if (NULL == wa->dhh) { fprintf (stderr, "Failed to obtain bank transaction history for `%s'\n", @@ -1519,24 +1496,25 @@ conclude_credit_history () * @param rowid unique serial ID for the entry in our DB * @param reserve_pub public key of the reserve (also the WTID) * @param credit amount that was received - * @param sender_url payto://-URL of the sender's bank account - * @param wire_reference unique identifier for the wire transfer (plugin-specific format) - * @param wire_reference_size number of bytes in @a wire_reference + * @param sender_account_details payto://-URL of the sender's bank account + * @param wire_reference unique identifier for the wire transfer * @param execution_date when did we receive the funds * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ static int reserve_in_cb (void *cls, uint64_t rowid, - const struct TALER_ReservePublicKeyP *reserve_pub, + const struct + TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, - const char *sender_url, - const void *wire_reference, - size_t wire_reference_size, - struct GNUNET_TIME_Absolute execution_date) + const char *sender_account_details, + uint64_t wire_reference, + struct GNUNET_TIME_Absolute + execution_date) { struct WireAccount *wa = cls; struct ReserveInInfo *rii; + size_t slen; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n", @@ -1544,21 +1522,20 @@ reserve_in_cb (void *cls, GNUNET_STRINGS_absolute_time_to_string (execution_date), TALER_amount2s (credit), TALER_B2S (reserve_pub)); - rii = GNUNET_new (struct ReserveInInfo); - GNUNET_CRYPTO_hash (wire_reference, - wire_reference_size, - &rii->row_off_hash); - rii->row_off_size = wire_reference_size; + slen = strlen (sender_account_details) + 1; + rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + + slen); + rii->rowid = rowid; rii->details.amount = *credit; rii->details.execution_date = execution_date; - /* reserve public key should be the WTID */ - GNUNET_assert (sizeof (rii->details.wtid) == - sizeof (*reserve_pub)); - memcpy (&rii->details.wtid, - reserve_pub, - sizeof (*reserve_pub)); - rii->details.account_url = GNUNET_strdup (sender_url); - rii->rowid = rowid; + rii->details.reserve_pub = *reserve_pub; + rii->details.account_url = (const char *) &rii[1]; + memcpy (&rii[1], + sender_account_details, + slen); + GNUNET_CRYPTO_hash (&wire_reference, + sizeof (uint64_t), + &rii->row_off_hash); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (in_map, &rii->row_off_hash, @@ -1572,8 +1549,6 @@ reserve_in_cb (void *cls, "wire_offset_hash", GNUNET_JSON_from_data_auto ( &rii->row_off_hash), "diagnostic", "duplicate wire offset")); - GNUNET_free (rii->details.account_url); - GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */ GNUNET_free (rii); return GNUNET_OK; } @@ -1604,7 +1579,8 @@ complain_in_not_found (void *cls, "amount_exchange_expected", TALER_JSON_from_amount ( &rii->details.amount), "amount_wired", TALER_JSON_from_amount (&zero), - "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid), + "reserve_pub", GNUNET_JSON_from_data_auto ( + &rii->details.reserve_pub), "timestamp", json_from_time_abs ( rii->details.execution_date), "account", wa->section_name, @@ -1635,25 +1611,24 @@ process_credits (void *cls); * * @param cls `struct WireAccount` we are processing * @param ec error code in case something went wrong - * @param dir direction of the transfer * @param row_off identification of the position at which we are querying - * @param row_off_size number of bytes in @a row_off * @param details details about the wire transfer + * @param json raw response * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ static int history_credit_cb (void *cls, + unsigned int http_status, enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - const void *row_off, - size_t row_off_size, - const struct TALER_WIRE_TransferDetails *details) + uint64_t row_off, + const struct TALER_BANK_CreditDetails *details, + const json_t *json) { struct WireAccount *wa = cls; struct ReserveInInfo *rii; struct GNUNET_HashCode key; - if (TALER_BANK_DIRECTION_NONE == dir) + if (NULL == details) { if (TALER_EC_NONE != ec) { @@ -1663,7 +1638,7 @@ history_credit_cb (void *cls, (unsigned int) ec); } /* end of operation */ - wa->hh = NULL; + wa->chh = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Reconciling CREDIT processing of account `%s'\n", wa->section_name); @@ -1678,12 +1653,12 @@ history_credit_cb (void *cls, return GNUNET_OK; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Analyzing bank CREDIT at %s of %s with WTID %s\n", + "Analyzing bank CREDIT at %s of %s with Reserve-pub %s\n", GNUNET_STRINGS_absolute_time_to_string (details->execution_date), TALER_amount2s (&details->amount), - TALER_B2S (&details->wtid)); - GNUNET_CRYPTO_hash (row_off, - row_off_size, + TALER_B2S (&details->reserve_pub)); + GNUNET_CRYPTO_hash (&row_off, + sizeof (row_off), &key); rii = GNUNET_CONTAINER_multihashmap_get (in_map, &key); @@ -1693,55 +1668,26 @@ history_credit_cb (void *cls, "Failed to find wire transfer at `%s' in exchange database. Audit ends at this point in time.\n", GNUNET_STRINGS_absolute_time_to_string ( details->execution_date)); - wa->hh = NULL; + wa->chh = NULL; process_credits (wa->next); return GNUNET_SYSERR; /* not an error, just end of processing */ } /* Update offset */ - if (NULL == wa->in_wire_off) - { - wa->wire_off_size = row_off_size; - wa->in_wire_off = GNUNET_malloc (row_off_size); - } - if (wa->wire_off_size != row_off_size) - { - GNUNET_break (0); - commit (GNUNET_DB_STATUS_HARD_ERROR); - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - memcpy (wa->in_wire_off, - row_off, - row_off_size); - - + wa->in_wire_off = row_off; /* compare records with expected data */ - if (row_off_size != rii->row_off_size) - { - GNUNET_break (0); - report (report_row_inconsistencies, - json_pack ("{s:s, s:I, s:o, s:o, s:s}", - "table", "reserves_in", - "row", (json_int_t) rii->rowid, - "raw_bank_row", GNUNET_JSON_from_data (row_off, - row_off_size), - "wire_offset_hash", GNUNET_JSON_from_data_auto (&key), - "diagnostic", "wire reference size missmatch")); - return GNUNET_OK; - } - if (0 != GNUNET_memcmp (&details->wtid, - &rii->details.wtid)) + if (0 != GNUNET_memcmp (&details->reserve_pub, + &rii->details.reserve_pub)) { report (report_reserve_in_inconsistencies, - json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}", + json_pack ("{s:I, s:I, s:o, s:o, s:o, s:o, s:s}", "row", (json_int_t) rii->rowid, - "raw_bank_row", GNUNET_JSON_from_data (row_off, - row_off_size), + "bank_row", (json_int_t) row_off, "amount_exchange_expected", TALER_JSON_from_amount ( &rii->details.amount), "amount_wired", TALER_JSON_from_amount (&zero), - "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid), + "reserve_pub", GNUNET_JSON_from_data_auto ( + &rii->details.reserve_pub), "timestamp", json_from_time_abs ( rii->details.execution_date), "diagnostic", "wire subject does not match")); @@ -1750,15 +1696,15 @@ history_credit_cb (void *cls, &total_bad_amount_in_minus, &rii->details.amount)); report (report_reserve_in_inconsistencies, - json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}", + json_pack ("{s:I, s:I, s:o, s:o, s:o, s:o, s:s}", "row", (json_int_t) rii->rowid, - "raw_bank_row", GNUNET_JSON_from_data (row_off, - row_off_size), + "bank_row", (json_int_t) row_off, "amount_exchange_expected", TALER_JSON_from_amount ( &zero), "amount_wired", TALER_JSON_from_amount ( &details->amount), - "wtid", GNUNET_JSON_from_data_auto (&details->wtid), + "reserve_pub", GNUNET_JSON_from_data_auto ( + &details->reserve_pub), "timestamp", json_from_time_abs ( details->execution_date), "diagnostic", "wire subject does not match")); @@ -1773,15 +1719,15 @@ history_credit_cb (void *cls, &details->amount)) { report (report_reserve_in_inconsistencies, - json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}", + json_pack ("{s:I, s:I, s:o, s:o, s:o, s:o, s:s}", "row", (json_int_t) rii->rowid, - "raw_bank_row", GNUNET_JSON_from_data (row_off, - row_off_size), + "bank_row", (json_int_t) row_off, "amount_exchange_expected", TALER_JSON_from_amount ( &rii->details.amount), "amount_wired", TALER_JSON_from_amount ( &details->amount), - "wtid", GNUNET_JSON_from_data_auto (&details->wtid), + "reserve_pub", GNUNET_JSON_from_data_auto ( + &details->reserve_pub), "timestamp", json_from_time_abs ( details->execution_date), "diagnostic", "wire amount does not match")); @@ -1820,13 +1766,12 @@ history_credit_cb (void *cls, rii->details.account_url)) { report (report_missattribution_in_inconsistencies, - json_pack ("{s:o, s:I, s:o, s:o}", + json_pack ("{s:o, s:I, s:I, s:o}", "amount", TALER_JSON_from_amount (&rii->details.amount), "row", (json_int_t) rii->rowid, - "raw_bank_row", GNUNET_JSON_from_data (row_off, - row_off_size), - "wtid", GNUNET_JSON_from_data_auto ( - &rii->details.wtid))); + "bank_row", (json_int_t) row_off, + "reserve_pub", GNUNET_JSON_from_data_auto ( + &rii->details.reserve_pub))); GNUNET_break (GNUNET_OK == TALER_amount_add (&total_missattribution_in, &total_missattribution_in, @@ -1836,11 +1781,10 @@ history_credit_cb (void *cls, rii->details.execution_date.abs_value_us) { report (report_row_minor_inconsistencies, - json_pack ("{s:s, s:I, s:o, s:s}", + json_pack ("{s:s, s:I, s:I, s:s}", "table", "reserves_in", "row", (json_int_t) rii->rowid, - "raw_bank_row", GNUNET_JSON_from_data (row_off, - row_off_size), + "bank_row", (json_int_t) row_off, "diagnostic", "execution date missmatch")); } cleanup: @@ -1865,7 +1809,6 @@ static void process_credits (void *cls) { struct WireAccount *wa = cls; - struct TALER_WIRE_Plugin *wp; enum GNUNET_DB_QueryStatus qs; /* skip accounts where CREDIT is not enabled */ @@ -1899,16 +1842,15 @@ process_credits (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting bank CREDIT history of account `%s'\n", wa->section_name); - wp = wa->wire_plugin; - wa->hh = wp->get_history (wp->cls, - wa->section_name, - TALER_BANK_DIRECTION_CREDIT, - wa->in_wire_off, - wa->wire_off_size, - INT64_MAX, - &history_credit_cb, - wa); - if (NULL == wa->hh) + wa->chh = TALER_BANK_credit_history (ctx, + wa->account.details.x_taler_bank. + account_base_url, + &wa->auth, + wa->in_wire_off, + INT64_MAX, + &history_credit_cb, + wa); + if (NULL == wa->chh) { fprintf (stderr, "Failed to obtain bank transaction history\n"); @@ -2037,8 +1979,7 @@ begin_transaction () wa->section_name, &wa->pp, &wa->in_wire_off, - &wa->out_wire_off, - &wa->wire_off_size); + &wa->out_wire_off); if (0 > qsx) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); @@ -2096,30 +2037,62 @@ process_account_cb (void *cls, const struct TALER_EXCHANGEDB_AccountInfo *ai) { struct WireAccount *wa; - struct TALER_WIRE_Plugin *wp; if ( (GNUNET_NO == ai->debit_enabled) && (GNUNET_NO == ai->credit_enabled) ) return; /* not an active exchange account */ - wp = TALER_WIRE_plugin_load (cfg, - ai->plugin_name); - if (NULL == wp) - { - fprintf (stderr, - "Failed to load wire plugin `%s'\n", - ai->plugin_name); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Found exchange account `%s'\n", ai->section_name); wa = GNUNET_new (struct WireAccount); - wa->wire_plugin = wp; wa->section_name = GNUNET_strdup (ai->section_name); wa->watch_debit = ai->debit_enabled; wa->watch_credit = ai->credit_enabled; + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (cfg, + ai->section_name, + &wa->auth)) + { + GNUNET_break (0); + GNUNET_free (wa->section_name); + GNUNET_free (wa); + fprintf (stderr, + "Failed to access bank account `%s'\n", + wa->section_name); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + TALER_BANK_account_parse_cfg (cfg, + wa->section_name, + &wa->account)) + { + GNUNET_break (0); + TALER_BANK_auth_free (&wa->auth); + GNUNET_free (wa->section_name); + GNUNET_free (wa); + fprintf (stderr, + "Failed to access bank account `%s'\n", + wa->section_name); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (TALER_PAC_X_TALER_BANK != wa->account.type) + { + GNUNET_break (0); + TALER_WIRE_account_free (&wa->account); + TALER_BANK_auth_free (&wa->auth); + GNUNET_free (wa->section_name); + GNUNET_free (wa); + fprintf (stderr, + "Need x-taler-bank account URL in `%s'\n", + wa->section_name); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } GNUNET_CONTAINER_DLL_insert (wa_head, wa_tail, wa); @@ -2258,6 +2231,14 @@ run (void *cls, } GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + if (NULL == ctx) + { + GNUNET_break (0); + return; + } esession = edb->get_session (edb->cls); if (NULL == esession) { diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c index 060e745df..7360258ad 100644 --- a/src/auditordb/plugin_auditordb_postgres.c +++ b/src/auditordb/plugin_auditordb_postgres.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 GNUnet e.V. + Copyright (C) 2014-2020 GNUnet e.V. 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 @@ -241,8 +241,8 @@ postgres_create_tables (void *cls) ",account_name TEXT NOT NULL" ",last_wire_reserve_in_serial_id INT8 NOT NULL DEFAULT 0" ",last_wire_wire_out_serial_id INT8 NOT NULL DEFAULT 0" - ",wire_in_off BYTEA" - ",wire_out_off BYTEA" + ",wire_in_off INT8" + ",wire_out_off INT8" ")"), GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS wire_auditor_progress" "(master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE" @@ -2135,23 +2135,16 @@ postgres_insert_wire_auditor_account_progress (void *cls, const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - const void *in_wire_off, - const void *out_wire_off, - size_t wire_off_size) + uint64_t in_wire_off, + uint64_t out_wire_off) { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id), - GNUNET_PQ_query_param_fixed_size (in_wire_off, - NULL == in_wire_off - ? 0 - : wire_off_size), - GNUNET_PQ_query_param_fixed_size (out_wire_off, - NULL == out_wire_off - ? 0 - : wire_off_size), + GNUNET_PQ_query_param_uint64 (&in_wire_off), + GNUNET_PQ_query_param_uint64 (&out_wire_off), GNUNET_PQ_query_param_end }; @@ -2182,21 +2175,14 @@ postgres_update_wire_auditor_account_progress (void *cls, const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - const void *in_wire_off, - const void *out_wire_off, - size_t wire_off_size) + uint64_t in_wire_off, + uint64_t out_wire_off) { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id), - GNUNET_PQ_query_param_fixed_size (in_wire_off, - NULL == in_wire_off - ? 0 - : wire_off_size), - GNUNET_PQ_query_param_fixed_size (out_wire_off, - NULL == out_wire_off - ? 0 - : wire_off_size), + GNUNET_PQ_query_param_uint64 (&in_wire_off), + GNUNET_PQ_query_param_uint64 (&out_wire_off), GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_end @@ -2231,12 +2217,9 @@ postgres_get_wire_auditor_account_progress (void *cls, struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - void **in_wire_off, - void **out_wire_off, - size_t *wire_off_size) + uint64_t *in_wire_off, + uint64_t *out_wire_off) { - size_t xsize; - enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_string (account_name), @@ -2247,30 +2230,17 @@ postgres_get_wire_auditor_account_progress (void *cls, &pp->last_reserve_in_serial_id), GNUNET_PQ_result_spec_uint64 ("last_wire_wire_out_serial_id", &pp->last_wire_out_serial_id), - GNUNET_PQ_result_spec_variable_size ("wire_in_off", - in_wire_off, - wire_off_size), - GNUNET_PQ_result_spec_variable_size ("wire_out_off", - out_wire_off, - &xsize), + GNUNET_PQ_result_spec_uint64 ("wire_in_off", + in_wire_off), + GNUNET_PQ_result_spec_uint64 ("wire_out_off", + out_wire_off), GNUNET_PQ_result_spec_end }; - qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "wire_auditor_account_progress_select", - params, - rs); - if (qs <= 0) - { - *wire_off_size = 0; - xsize = 0; - } - if ( (0 != xsize) && - (0 != *wire_off_size) ) - { - GNUNET_assert (xsize == *wire_off_size); - } - return qs; + return GNUNET_PQ_eval_prepared_singleton_select (session->conn, + "wire_auditor_account_progress_select", + params, + rs); } 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", + ×tamp), + 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, - ×tamp)); - 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, - ×tamp); - 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, - ×tamp); - 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 */ diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index 31dbdf15a..b931c3c90 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014-2019 Taler Systems SA + (C) 2014-2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -16,15 +16,13 @@ along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - /** - * @file merchant/backend/taler-merchant-httpd.c + * @file benchmark/taler-exchange-benchmark.c * @brief HTTP serving layer intended to perform crypto-work and * communication with the exchange * @author Marcello Stanisci * @author Christian Grothoff */ - #include "platform.h" #include <gnunet/gnunet_util_lib.h> #include <microhttpd.h> @@ -58,23 +56,14 @@ enum BenchmarkError */ #define UNITY_SIZE 6 -/** - * Account number of the merchant. Fakebank likes any number, - * the only requirement is that this number then matches the - * number given when building payto URLs at deposit time. - */ -#define TALER_TESTING_USER_ACCOUNT_NUMBER 3 - #define FIRST_INSTRUCTION -1 #define CMD_TRANSFER_TO_EXCHANGE(label, amount) \ TALER_TESTING_cmd_fakebank_transfer_retry \ (TALER_TESTING_cmd_fakebank_transfer (label, amount, \ - exchange_bank_account.details. \ - x_taler_bank.bank_base_url, \ - TALER_TESTING_USER_ACCOUNT_NUMBER, \ - exchange_bank_account.details. \ - x_taler_bank.no, \ + user_bank_account.details. \ + x_taler_bank.account_base_url, \ + exchange_payto_url, \ "dummy_user", \ "dummy_password", \ "http://example.com/")) @@ -108,6 +97,11 @@ enum BenchmarkMode static struct TALER_Account exchange_bank_account; /** + * Hold information about a user at the bank. + */ +static struct TALER_Account user_bank_account; + +/** * Time snapshot taken right before executing the CMDs. */ static struct GNUNET_TIME_Absolute start_time; @@ -169,6 +163,11 @@ static enum BenchmarkMode mode; static char *cfg_filename; /** + * payto://-URL of the exchange's bank account. + */ +static char *exchange_payto_url; + +/** * Currency used. */ static char *currency; @@ -405,12 +404,12 @@ stop_fakebank (void *cls) static void launch_fakebank (void *cls) { - const char *bank_base_url = cls; + const char *hostname = cls; const char *port; long pnum; struct TALER_FAKEBANK_Handle *fakebank; - port = strrchr (bank_base_url, + port = strrchr (hostname, (unsigned char) ':'); if (NULL == port) pnum = 80; @@ -419,7 +418,7 @@ launch_fakebank (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting Fakebank on port %u (%s)\n", (unsigned int) pnum, - bank_base_url); + hostname); fakebank = TALER_FAKEBANK_start ((uint16_t) pnum); if (NULL == fakebank) { @@ -466,8 +465,7 @@ parallel_benchmark (TALER_TESTING_Main main_cb, NULL == loglev ? "INFO" : loglev, logfile); GNUNET_SCHEDULER_run (&launch_fakebank, - exchange_bank_account.details.x_taler_bank. - bank_base_url); + exchange_bank_account.details.x_taler_bank.hostname); exit (0); } if (-1 == fakebank) @@ -824,10 +822,36 @@ main (int argc, return BAD_CLI_ARG; } + { + char *user_payto_url; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string + (cfg, + "benchmark", + "user-url", + &user_payto_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "benchmark", + "user-url"); + return BAD_CONFIG_FILE; + } + if (TALER_EC_NONE != + TALER_WIRE_payto_to_account (user_payto_url, + &user_bank_account)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Malformed payto:// URL `%s' in configuration\n"), + user_payto_url); + GNUNET_free (user_payto_url); + return BAD_CONFIG_FILE; + } + GNUNET_free (user_payto_url); + } { const char *bank_details_section; - char *exchange_payto_url; GNUNET_CONFIGURATION_iterate_sections (cfg, @@ -873,7 +897,6 @@ main (int argc, GNUNET_free (exchange_payto_url); return BAD_CONFIG_FILE; } - GNUNET_free (exchange_payto_url); } if ( (MODE_EXCHANGE == mode) || (MODE_BOTH == mode) ) { diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am index 64b4cee87..0b9a2d528 100644 --- a/src/exchange-tools/Makefile.am +++ b/src/exchange-tools/Makefile.am @@ -23,7 +23,9 @@ taler_wire_SOURCES = \ taler-wire.c taler_wire_LDADD = \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/wire/libtalerwire.la \ + -lgnunetcurl \ -lgnunetutil taler_exchange_keyup_SOURCES = \ @@ -32,6 +34,7 @@ taler_exchange_keyup_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/pq/libtalerpq.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ -lgnunetutil $(XLIB) @@ -44,6 +47,7 @@ taler_exchange_wire_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetjson \ diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index e6e3db0d1..f2ec3ca8f 100644 --- a/src/exchange-tools/taler-exchange-keyup.c +++ b/src/exchange-tools/taler-exchange-keyup.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-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 @@ -923,17 +923,17 @@ exchange_keys_update_denomkeys () * Sign @a af with @a priv * * @param[in,out] af fee structure to sign - * @param wireplugin name of the plugin for which we sign + * @param method name of the wire method for which we sign * @param priv private key to use for signing */ static void sign_af (struct TALER_EXCHANGEDB_AggregateFees *af, - const char *wireplugin, + const char *method, const struct GNUNET_CRYPTO_EddsaPrivateKey *priv) { struct TALER_MasterWireFeePS wf; - TALER_EXCHANGEDB_fees_2_wf (wireplugin, + TALER_EXCHANGEDB_fees_2_wf (method, af, &wf); GNUNET_assert (GNUNET_OK == @@ -1101,28 +1101,15 @@ create_wire_fee_by_account (void *cls, const struct TALER_EXCHANGEDB_AccountInfo *ai) { int *ret = cls; - struct TALER_WIRE_Plugin *plugin; if (GNUNET_NO == ai->credit_enabled) return; - plugin = TALER_WIRE_plugin_load (kcfg, - ai->plugin_name); - if (NULL == plugin) - { - fprintf (stderr, - "Failed to load wire plugin `%s' configured for account `%s'\n", - ai->plugin_name, - ai->section_name); - *ret = GNUNET_SYSERR; - return; - } /* We may call this function repeatedly for the same method if there are multiple accounts with plugins using the same method, but except for some minor performance loss, this is harmless. */ create_wire_fee_for_method (ret, - plugin->method); - TALER_WIRE_plugin_unload (plugin); + ai->method); } diff --git a/src/exchange-tools/taler-wire.c b/src/exchange-tools/taler-wire.c index 12963324a..5e3c18a61 100644 --- a/src/exchange-tools/taler-wire.c +++ b/src/exchange-tools/taler-wire.c @@ -22,11 +22,10 @@ * @author Marcello Stanisci * @author Christian Grothoff */ - #include <platform.h> #include <gnunet/gnunet_util_lib.h> #include "taler_util.h" -#include "taler_wire_lib.h" +#include "taler_bank_service.h" /** * If set to #GNUNET_YES, then we'll ask the bank for a list @@ -36,17 +35,12 @@ static int history; /** - * If set to GNUNET_YES, then we'll ask the bank to execute a + * If set to #GNUNET_YES, then we'll ask the bank to execute a * wire transfer. */ static int transfer; /** - * Name of the wire plugin to use with the bank. - */ -static char *plugin_name; - -/** * Global return code. */ static unsigned int global_ret = 1; @@ -59,11 +53,9 @@ static unsigned int global_ret = 1; static char *amount; /** - * Base32 encoding of a transaction ID. When asking the - * bank for a transaction history, all the results will - * have a transaction ID settled *after* this one. + * Starting row. */ -static char *since_when; +static unsigned long long start_row; /** * Which config section has the credentials to access the bank. @@ -77,19 +69,29 @@ static char *account_section; static char *destination_account_url; /** - * Handle for the wire transfer preparation task. + * Handle for executing the wire transfer. */ -static struct TALER_WIRE_PrepareHandle *ph; +static struct TALER_BANK_WireExecuteHandle *eh; /** - * Wire plugin handle. + * Handle to ongoing history operation. */ -static struct TALER_WIRE_Plugin *plugin_handle; +static struct TALER_BANK_CreditHistoryHandle *hh; /** - * Handle to ongoing history operation. + * For authentication. + */ +static struct TALER_BANK_AuthenticationData auth; + +/** + * Handle to the context for interacting with the bank. */ -static struct TALER_WIRE_HistoryHandle *hh; +static struct GNUNET_CURL_Context *ctx; + +/** + * Scheduler context for running the @e ctx. + */ +static struct GNUNET_CURL_RescheduleContext *rc; /** @@ -108,16 +110,16 @@ static struct TALER_WIRE_HistoryHandle *hh; */ static int history_cb (void *cls, + unsigned int http_status, enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - const void *row_off, - size_t row_off_size, - const struct TALER_WIRE_TransferDetails *details) + uint64_t serial_id, + const struct TALER_BANK_CreditDetails *details, + const json_t *json) { - char *row_off_enc; - (void) cls; - if (TALER_BANK_DIRECTION_NONE == dir) + (void) ec; + (void) http_status; + if (NULL == details) { fprintf (stdout, "End of transactions list.\n"); @@ -126,15 +128,9 @@ history_cb (void *cls, return GNUNET_NO; } - row_off_enc = GNUNET_STRINGS_data_to_string_alloc (row_off, - row_off_size); - /* Give more details on screen (??) */ fprintf (stdout, - "%s\n", - row_off_enc); - - GNUNET_free (row_off_enc); - + "%llu\n", + (unsigned long long) serial_id); return GNUNET_OK; } @@ -142,72 +138,37 @@ history_cb (void *cls, /** * Callback that processes the outcome of a wire transfer * execution. + * + * @param cls closure + * @param response_code HTTP status code + * @param ec taler error code + * @param row_id unique ID of the wire transfer in the bank's records + * @param timestamp when did the transaction go into effect */ static void confirmation_cb (void *cls, - int success, - const void *row_id, - size_t row_id_size, - const char *emsg) + unsigned int response_code, + enum TALER_ErrorCode ec, + uint64_t row_id, + struct GNUNET_TIME_Absolute timestamp) { - if (GNUNET_YES != success) + if (MHD_HTTP_OK != response_code) { fprintf (stderr, - "The wire transfer didn't execute correctly.\n"); - GNUNET_assert (NULL != emsg); - fprintf (stderr, - "%s", - emsg); + "The wire transfer didn't execute correctly (%d).\n", + ec); GNUNET_SCHEDULER_shutdown (); return; } fprintf (stdout, "Wire transfer executed successfully.\n"); - global_ret = 0; GNUNET_SCHEDULER_shutdown (); } /** - * Takes prepared blob and executes the wire-transfer. - * - * @param cls NULL. - * @param buf prepared wire transfer data. - * @param buf_size size of the prepared wire transfer data. - */ -static void -prepare_cb (void *cls, - const char *buf, - size_t buf_size) -{ - struct TALER_WIRE_ExecuteHandle *eh; - - if (NULL == (eh = plugin_handle->execute_wire_transfer - (plugin_handle->cls, - buf, - buf_size, - confirmation_cb, - NULL))) - { - fprintf (stderr, - "Could not execute the wire transfer\n"); - - plugin_handle->prepare_wire_transfer_cancel - (plugin_handle->cls, - ph); - - plugin_handle->execute_wire_transfer_cancel - (plugin_handle->cls, - eh); - - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** * Ask the bank to execute a wire transfer. */ static void @@ -215,6 +176,8 @@ execute_wire_transfer () { struct TALER_Amount a; struct TALER_WireTransferIdentifierRawP wtid; + void *buf; + size_t buf_size; if (NULL == amount) { @@ -223,7 +186,6 @@ execute_wire_transfer () GNUNET_SCHEDULER_shutdown (); return; } - if (GNUNET_OK != TALER_string_to_amount (amount, &a)) { @@ -240,19 +202,25 @@ execute_wire_transfer () GNUNET_SCHEDULER_shutdown (); return; } - if (NULL == (ph = plugin_handle->prepare_wire_transfer - (plugin_handle->cls, - account_section, - destination_account_url, - &a, - "http://exchange.example.com/", - &wtid, /* Any value will do. */ - prepare_cb, - NULL))) + TALER_BANK_prepare_wire_transfer (destination_account_url, + &a, + "http://exchange.example.com/", + &wtid, + &buf, + &buf_size); + eh = TALER_BANK_execute_wire_transfer (ctx, + destination_account_url, + &auth, + buf, + buf_size, + &confirmation_cb, + NULL); + if (NULL == eh) { fprintf (stderr, - "Could not prepare the wire transfer\n"); + "Could not execute the wire transfer\n"); GNUNET_SCHEDULER_shutdown (); + return; } } @@ -264,30 +232,14 @@ execute_wire_transfer () static void execute_history () { - size_t bin_len = 0; - void *since_when_bin = NULL; - - if (NULL != since_when) - { - bin_len = (strlen (since_when) * 5) / 8; - - since_when_bin = GNUNET_malloc (bin_len); - GNUNET_assert - (GNUNET_OK == GNUNET_STRINGS_string_to_data - (since_when, - strlen (since_when), - since_when_bin, - bin_len)); - } - - if (NULL == (hh = plugin_handle->get_history (plugin_handle->cls, - account_section, - TALER_BANK_DIRECTION_BOTH, - since_when_bin, - bin_len, - -10, - &history_cb, - NULL))) + hh = TALER_BANK_credit_history (ctx, + destination_account_url, + &auth, + start_row, + -10, + &history_cb, + NULL); + if (NULL == hh) { fprintf (stderr, "Could not request the transaction history.\n"); @@ -305,19 +257,27 @@ execute_history () static void do_shutdown (void *cls) { + if (NULL != ctx) + { + GNUNET_CURL_fini (ctx); + ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } if (NULL != hh) { - plugin_handle->get_history_cancel (plugin_handle->cls, - hh); + TALER_BANK_credit_history_cancel (hh); hh = NULL; } - if (NULL != ph) + if (NULL != eh) { - plugin_handle->prepare_wire_transfer_cancel (plugin_handle->cls, - ph); - ph = NULL; + TALER_BANK_execute_wire_transfer_cancel (eh); + eh = NULL; } - TALER_WIRE_plugin_unload (plugin_handle); + TALER_BANK_auth_free (&auth); } @@ -342,27 +302,18 @@ run (void *cls, "The option: -s ACCOUNT-SECTION, is mandatory.\n"); return; } - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string - (cfg, - account_section, - "plugin", - &plugin_name)) + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (cfg, + account_section, + &auth)) { fprintf (stderr, - "Could not find the 'plugin' value under %s\n", + "Authentication information not found in configuration section `%s'\n", account_section); + GNUNET_SCHEDULER_shutdown (); return; } - plugin_handle = TALER_WIRE_plugin_load (cfg, - plugin_name); - if (NULL == plugin_handle) - { - fprintf (stderr, - "Could not load the wire plugin\n"); - return; - } if (GNUNET_YES == history) execute_history (); @@ -372,6 +323,14 @@ run (void *cls, fprintf (stderr, "Please give either --history/-H or --transfer/t\n"); + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + if (NULL == ctx) + { + GNUNET_break (0); + return; + } GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); } @@ -400,22 +359,23 @@ main (int argc, "transfer", "Execute a wire transfer.", &transfer), - GNUNET_GETOPT_option_string ('w', - "since-when", - "SW", - "When asking the bank for" - " transactions history, this" - " option commands that all the" - " results should have IDs settled" - " after SW. If not given, then" - " the 10 youngest transactions" - " are returned.", - &since_when), - GNUNET_GETOPT_option_string ('s', - "section", - "ACCOUNT-SECTION", - "Which config section has the credentials to access the bank. Mandatory.\n", - &account_section), + GNUNET_GETOPT_option_ulong ('w', + "since-when", + "SW", + "When asking the bank for" + " transactions history, this" + " option commands that all the" + " results should have IDs settled" + " after SW. If not given, then" + " the 10 youngest transactions" + " are returned.", + &start_row), + GNUNET_GETOPT_option_mandatory + (GNUNET_GETOPT_option_string ('s', + "section", + "ACCOUNT-SECTION", + "Which config section has the credentials to access the bank. Mandatory.\n", + &account_section)), GNUNET_GETOPT_option_string ('a', "amount", "AMOUNT", diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 3453683ae..b08c85c6f 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -29,9 +29,11 @@ taler_exchange_aggregator_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ -ljansson \ + -lgnunetcurl \ -lgnunetutil taler_exchange_wirewatch_SOURCES = \ @@ -40,9 +42,11 @@ taler_exchange_wirewatch_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ -ljansson \ + -lgnunetcurl \ -lgnunetutil taler_exchange_httpd_SOURCES = \ @@ -66,6 +70,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_validation.c taler-exchange-httpd_validation.h taler_exchange_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/mhd/libtalermhd.la \ $(top_builddir)/src/json/libtalerjson.la \ diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index 71a3efd7d..2704f5910 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016-2018 Taler Systems SA + 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 Affero General Public License as published by the Free Software @@ -26,6 +26,7 @@ #include "taler_exchangedb_lib.h" #include "taler_exchangedb_plugin.h" #include "taler_json_lib.h" +#include "taler_bank_service.h" #include "taler_wire_lib.h" @@ -45,9 +46,14 @@ struct WireAccount struct WireAccount *prev; /** - * Handle to the plugin. + * Account information. */ - struct TALER_WIRE_Plugin *wire_plugin; + struct TALER_Account account; + + /** + * Authentication data. + */ + struct TALER_BANK_AuthenticationData auth; /** * Wire transfer fee structure. @@ -59,6 +65,11 @@ struct WireAccount */ char *section_name; + /** + * Name of the wire method underlying the account. + */ + char *method; + }; @@ -77,7 +88,7 @@ struct WirePrepareData /** * Wire execution handle. */ - struct TALER_WIRE_ExecuteHandle *eh; + struct TALER_BANK_WireExecuteHandle *eh; /** * Wire plugin used for this preparation. @@ -187,10 +198,6 @@ struct AggregationUnit */ struct CloseTransferContext { - /** - * Handle for preparing the wire transfer. - */ - struct TALER_WIRE_PrepareHandle *ph; /** * Our database session. @@ -263,6 +270,16 @@ static struct WirePrepareData *wpd; static struct AggregationUnit *au; /** + * Handle to the context for interacting with the bank. + */ +static struct GNUNET_CURL_Context *ctx; + +/** + * Scheduler context for running the @e ctx. + */ +static struct GNUNET_CURL_RescheduleContext *rc; + +/** * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR * on serious errors. */ @@ -339,7 +356,7 @@ update_fees (struct WireAccount *wa, return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* Let's try to load it from disk... */ wa->af = TALER_EXCHANGEDB_fees_read (cfg, - wa->wire_plugin->method); + wa->method); advance_fees (wa, now); for (struct TALER_EXCHANGEDB_AggregateFees *p = wa->af; @@ -348,7 +365,7 @@ update_fees (struct WireAccount *wa, { qs = db_plugin->insert_wire_fee (db_plugin->cls, session, - wa->wire_plugin->method, + wa->method, p->start_date, p->end_date, &p->wire_fee, @@ -365,7 +382,7 @@ update_fees (struct WireAccount *wa, return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to find current wire transfer fees for `%s'\n", - wa->wire_plugin->method); + wa->method); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } @@ -381,7 +398,7 @@ find_account_by_method (const char *method) { for (struct WireAccount *wa = wa_head; NULL != wa; wa = wa->next) if (0 == strcmp (method, - wa->wire_plugin->method)) + wa->method)) return wa; return NULL; } @@ -431,13 +448,40 @@ add_account_cb (void *cls, if (GNUNET_YES != ai->debit_enabled) return; /* not enabled for us, skip */ wa = GNUNET_new (struct WireAccount); - wa->wire_plugin = TALER_WIRE_plugin_load (cfg, - ai->plugin_name); - if (NULL == wa->wire_plugin) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + ai->section_name, + "METHOD", + &wa->method)) { - fprintf (stderr, - "Failed to load wire plugin for `%s'\n", - ai->plugin_name); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ai->section_name, + "METHOD"); + GNUNET_free (wa); + return; + } + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (cfg, + ai->section_name, + &wa->auth)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Failed to load account `%s'\n", + ai->section_name); + GNUNET_free (wa->method); + GNUNET_free (wa); + return; + } + if (GNUNET_OK != + TALER_BANK_account_parse_cfg (cfg, + ai->section_name, + &wa->account)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Failed to load account `%s'\n", + ai->section_name); + TALER_BANK_auth_free (&wa->auth); + GNUNET_free (wa->method); GNUNET_free (wa); return; } @@ -476,6 +520,16 @@ static void shutdown_task (void *cls) { (void) cls; + if (NULL != ctx) + { + GNUNET_CURL_fini (ctx); + ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running shutdown\n"); if (NULL != task) @@ -487,9 +541,7 @@ shutdown_task (void *cls) { if (NULL != wpd->eh) { - wpd->wa->wire_plugin->execute_wire_transfer_cancel ( - wpd->wa->wire_plugin->cls, - wpd->eh); + TALER_BANK_execute_wire_transfer_cancel (wpd->eh); wpd->eh = NULL; } db_plugin->rollback (db_plugin->cls, @@ -499,23 +551,12 @@ shutdown_task (void *cls) } if (NULL != au) { - if (NULL != au->ph) - { - au->wa->wire_plugin->prepare_wire_transfer_cancel ( - au->wa->wire_plugin->cls, - au->ph); - au->ph = NULL; - } db_plugin->rollback (db_plugin->cls, au->session); cleanup_au (); } if (NULL != ctc) { - ctc->wa->wire_plugin->prepare_wire_transfer_cancel ( - ctc->wa->wire_plugin->cls, - ctc->ph); - ctc->ph = NULL; db_plugin->rollback (db_plugin->cls, ctc->session); GNUNET_free (ctc->method); @@ -532,9 +573,11 @@ shutdown_task (void *cls) GNUNET_CONTAINER_DLL_remove (wa_head, wa_tail, wa); - TALER_WIRE_plugin_unload (wa->wire_plugin); + TALER_WIRE_account_free (&wa->account); + TALER_BANK_auth_free (&wa->auth); TALER_EXCHANGEDB_fees_free (wa->af); GNUNET_free (wa->section_name); + GNUNET_free (wa->method); GNUNET_free (wa); } } @@ -922,20 +965,6 @@ aggregate_cb (void *cls, /** - * Function to be called with the prepared transfer data - * when running an aggregation on a merchant. - * - * @param cls closure with the `struct AggregationUnit` - * @param buf transaction data to persist, NULL on error - * @param buf_size number of bytes in @a buf, 0 on error - */ -static void -prepare_cb (void *cls, - const char *buf, - size_t buf_size); - - -/** * Main work function that finds and triggers transfers for reserves * closures. * @@ -989,83 +1018,6 @@ commit_or_warn (struct TALER_EXCHANGEDB_Session *session) /** - * Function to be called with the prepared transfer data - * when closing a reserve. - * - * @param cls closure with a `struct CloseTransferContext` - * @param buf transaction data to persist, NULL on error - * @param buf_size number of bytes in @a buf, 0 on error - */ -static void -prepare_close_cb (void *cls, - const char *buf, - size_t buf_size) -{ - enum GNUNET_DB_QueryStatus qs; - - GNUNET_assert (cls == ctc); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Prepared for reserve closing\n"); - ctc->ph = NULL; - if (NULL == buf) - { - GNUNET_break (0); /* why? how to best recover? */ - db_plugin->rollback (db_plugin->cls, - ctc->session); - /* start again */ - GNUNET_free (ctc->method); - GNUNET_free (ctc); - ctc = NULL; - task = GNUNET_SCHEDULER_add_now (&run_aggregation, - NULL); - return; - } - - /* Commit our intention to execute the wire transfer! */ - qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, - ctc->session, - ctc->method, - buf, - buf_size); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - db_plugin->rollback (db_plugin->cls, - ctc->session); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (ctc->method); - GNUNET_free (ctc); - ctc = NULL; - return; - } - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - db_plugin->rollback (db_plugin->cls, - ctc->session); - /* start again */ - task = GNUNET_SCHEDULER_add_now (&run_aggregation, - NULL); - GNUNET_free (ctc->method); - GNUNET_free (ctc); - ctc = NULL; - return; - } - - /* finally commit */ - (void) commit_or_warn (ctc->session); - GNUNET_free (ctc->method); - GNUNET_free (ctc); - ctc = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Reserve closure committed, running transfer\n"); - task = GNUNET_SCHEDULER_add_now (&run_transfers, - NULL); -} - - -/** * Closure for #expired_reserve_cb(). */ struct ExpiredReserveContext @@ -1113,6 +1065,8 @@ expired_reserve_cb (void *cls, int ret; enum GNUNET_DB_QueryStatus qs; struct WireAccount *wa; + void *buf; + size_t buf_size; /* NOTE: potential optimization: use custom SQL API to not fetch this: */ @@ -1121,7 +1075,7 @@ expired_reserve_cb (void *cls, now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); - /* lookup wire plugin */ + /* lookup account we should use */ wa = find_account_by_url (account_details); if (NULL == wa) { @@ -1161,6 +1115,18 @@ expired_reserve_cb (void *cls, TALER_amount_get_zero (left->currency, &amount_without_fee)); } + /* round down to enable transfer */ + if (GNUNET_SYSERR == + TALER_WIRE_amount_round (&amount_without_fee)) + { + GNUNET_break (0); + global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if ( (0 == amount_without_fee.value) && + (0 == amount_without_fee.fraction) ) + ret = GNUNET_NO; /* NOTE: sizeof (*reserve_pub) == sizeof (wtid) right now, but to be future-compatible, we use the memset + min construction */ @@ -1171,61 +1137,23 @@ expired_reserve_cb (void *cls, reserve_pub, GNUNET_MIN (sizeof (wtid), sizeof (*reserve_pub))); - - qs = db_plugin->insert_reserve_closed (db_plugin->cls, - session, - reserve_pub, - now, - account_details, - &wtid, - left, - closing_fee); - + if (GNUNET_SYSERR != ret) + qs = db_plugin->insert_reserve_closed (db_plugin->cls, + session, + reserve_pub, + now, + account_details, + &wtid, + left, + closing_fee); + else + ret = GNUNET_DB_STATUS_HARD_ERROR; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Closing reserve %s over %s (%d, %d)\n", TALER_B2S (reserve_pub), TALER_amount2s (left), ret, qs); - if ( (GNUNET_OK == ret) && - (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) ) - { - /* success, perform wire transfer */ - if (GNUNET_SYSERR == - wa->wire_plugin->amount_round (wa->wire_plugin->cls, - &amount_without_fee)) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_DB_STATUS_HARD_ERROR; - } - ctc = GNUNET_new (struct CloseTransferContext); - ctc->wa = wa; - ctc->session = session; - ctc->method = TALER_WIRE_payto_get_method (account_details); - ctc->ph - = wa->wire_plugin->prepare_wire_transfer (wa->wire_plugin->cls, - wa->section_name, - account_details, - &amount_without_fee, - exchange_base_url, - &wtid, - &prepare_close_cb, - ctc); - if (NULL == ctc->ph) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (ctc->method); - GNUNET_free (ctc); - ctc = NULL; - return GNUNET_DB_STATUS_HARD_ERROR; - } - erc->async_cont = GNUNET_YES; - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - } /* Check for hard failure */ if ( (GNUNET_SYSERR == ret) || (GNUNET_DB_STATUS_HARD_ERROR == qs) ) @@ -1235,10 +1163,59 @@ expired_reserve_cb (void *cls, GNUNET_SCHEDULER_shutdown (); return GNUNET_DB_STATUS_HARD_ERROR; } - /* Reserve balance was almost zero OR soft error */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Reserve was virtually empty, moving on\n"); - return qs; + if ( (GNUNET_OK != ret) || + (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ) + { + /* Reserve balance was almost zero OR soft error */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Reserve was virtually empty, moving on\n"); + (void) commit_or_warn (ctc->session); + GNUNET_free (ctc->method); + GNUNET_free (ctc); + ctc = NULL; + task = GNUNET_SCHEDULER_add_now (&run_transfers, + NULL); + return qs; + } + + /* success, perform wire transfer */ + ctc = GNUNET_new (struct CloseTransferContext); + ctc->wa = wa; + ctc->session = session; + ctc->method = TALER_WIRE_payto_get_method (account_details); + TALER_BANK_prepare_wire_transfer (account_details, + &amount_without_fee, + exchange_base_url, + &wtid, + &buf, + &buf_size); + /* Commit our intention to execute the wire transfer! */ + qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, + ctc->session, + ctc->method, + buf, + buf_size); + GNUNET_free (buf); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + GNUNET_free (ctc->method); + GNUNET_free (ctc); + ctc = NULL; + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + /* start again */ + GNUNET_free (ctc->method); + GNUNET_free (ctc); + ctc = NULL; + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } + erc->async_cont = GNUNET_YES; + task = GNUNET_SCHEDULER_add_now (&run_transfers, + NULL); + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } @@ -1344,6 +1321,8 @@ run_aggregation (void *cls) struct TALER_EXCHANGEDB_Session *session; enum GNUNET_DB_QueryStatus qs; const struct GNUNET_SCHEDULER_TaskContext *tc; + void *buf; + size_t buf_size; (void) cls; task = NULL; @@ -1470,8 +1449,7 @@ run_aggregation (void *cls) &au->total_amount, &au->wire_fee)) || (GNUNET_SYSERR == - au->wa->wire_plugin->amount_round (au->wa->wire_plugin->cls, - &au->final_amount)) || + TALER_WIRE_amount_round (&au->final_amount)) || ( (0 == au->final_amount.value) && (0 == au->final_amount.fraction) ) ) { @@ -1555,70 +1533,26 @@ run_aggregation (void *cls) char *url; url = TALER_JSON_wire_to_payto (au->wire); - au->ph = au->wa->wire_plugin->prepare_wire_transfer ( - au->wa->wire_plugin->cls, - au->wa->section_name, - url, - &au->final_amount, - exchange_base_url, - &au->wtid, - &prepare_cb, - au); + TALER_BANK_prepare_wire_transfer (url, + &au->final_amount, + exchange_base_url, + &au->wtid, + &buf, + &buf_size); GNUNET_free (url); } - if (NULL == au->ph) - { - /* something went very wrong, likely bad configuration, - abort */ - db_plugin->rollback (db_plugin->cls, - session); - cleanup_au (); - GNUNET_SCHEDULER_shutdown (); - return; - } - /* otherwise we continue with #prepare_cb(), see below */ -} - - -/** - * Function to be called with the prepared transfer data. - * - * @param cls NULL - * @param buf transaction data to persist, NULL on error - * @param buf_size number of bytes in @a buf, 0 on error - */ -static void -prepare_cb (void *cls, - const char *buf, - size_t buf_size) -{ - struct TALER_EXCHANGEDB_Session *session = au->session; - enum GNUNET_DB_QueryStatus qs; - - (void) cls; GNUNET_free_non_null (au->additional_rows); au->additional_rows = NULL; - if (NULL == buf) - { - GNUNET_break (0); /* why? how to best recover? */ - db_plugin->rollback (db_plugin->cls, - session); - /* start again */ - task = GNUNET_SCHEDULER_add_now (&run_aggregation, - NULL); - cleanup_au (); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Storing %u bytes of wire prepare data\n", (unsigned int) buf_size); /* Commit our intention to execute the wire transfer! */ qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, session, - au->wa->wire_plugin->method, + au->wa->method, buf, buf_size); + GNUNET_free (buf); /* Commit the WTID data to 'wire_out' to finally satisfy aggregation table constraints */ if (qs >= 0) @@ -1691,29 +1625,30 @@ prepare_cb (void *cls, * Function called with the result from the execute step. * * @param cls NULL - * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure - * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error - * @param emsg NULL on success, otherwise an error message + * @param http_status_code #MHD_HTTP_OK on success + * @param ec taler error code + * @param row_id unique ID of the wire transfer in the bank's records + * @param wire_timestamp when did the transfer happen */ static void wire_confirm_cb (void *cls, - int success, - const void *row_id, - size_t row_id_size, - const char *emsg) + unsigned int http_status_code, + enum TALER_ErrorCode ec, + uint64_t row_id, + struct GNUNET_TIME_Absolute wire_timestamp) { struct TALER_EXCHANGEDB_Session *session = wpd->session; enum GNUNET_DB_QueryStatus qs; (void) cls; (void) row_id; - (void) row_id_size; wpd->eh = NULL; - if (GNUNET_SYSERR == success) + if (MHD_HTTP_OK != http_status_code) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire transaction failed: %s\n", - emsg); + "Wire transaction failed: %u/%d\n", + http_status_code, + ec); db_plugin->rollback (db_plugin->cls, session); global_ret = GNUNET_SYSERR; @@ -1792,6 +1727,8 @@ wire_prepare_cb (void *cls, const char *buf, size_t buf_size) { + struct WireAccount *wa; + (void) cls; wpd->row_id = rowid; GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1811,12 +1748,15 @@ wire_prepare_cb (void *cls, wpd = NULL; return; } - wpd->eh = wpd->wa->wire_plugin->execute_wire_transfer ( - wpd->wa->wire_plugin->cls, - buf, - buf_size, - &wire_confirm_cb, - NULL); + wa = wpd->wa; + wpd->eh = TALER_BANK_execute_wire_transfer (ctx, + wa->account.details.x_taler_bank. + account_base_url, + &wa->auth, + buf, + buf_size, + &wire_confirm_cb, + NULL); if (NULL == wpd->eh) { GNUNET_break (0); /* why? how to best recover? */ @@ -1927,6 +1867,7 @@ run (void *cls, (void) cls; (void) args; (void) cfgfile; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "exchange", @@ -1947,6 +1888,15 @@ run (void *cls, global_ret = 1; return; } + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + if (NULL == ctx) + { + GNUNET_break (0); + return; + } + task = GNUNET_SCHEDULER_add_now (&run_transfers, NULL); GNUNET_SCHEDULER_add_shutdown (&shutdown_task, diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 024482389..96e30e437 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -400,7 +400,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, json_t *json; int res; json_t *wire; - char *emsg; enum TALER_ErrorCode ec; unsigned int hc; struct TALER_EXCHANGEDB_Deposit deposit; @@ -460,18 +459,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, "refund_deadline"); } - if (TALER_EC_NONE != - (ec = TEH_json_validate_wireformat (wire, - &emsg))) - { - GNUNET_JSON_parse_free (spec); - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - ec, - emsg); - GNUNET_free (emsg); - return res; - } if (GNUNET_OK != check_timestamp_current (deposit.timestamp)) { diff --git a/src/exchange/taler-exchange-httpd_validation.c b/src/exchange/taler-exchange-httpd_validation.c index 23dbbf249..d0371e930 100644 --- a/src/exchange/taler-exchange-httpd_validation.c +++ b/src/exchange/taler-exchange-httpd_validation.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016, 2017, 2018 Taler Systems SA + 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 Affero General Public License as published by the Free Software @@ -30,40 +30,6 @@ /** - * Information we keep for each plugin. - */ -struct Plugin -{ - - /** - * We keep plugins in a DLL. - */ - struct Plugin *next; - - /** - * We keep plugins in a DLL. - */ - struct Plugin *prev; - - /** - * Pointer to the plugin. - */ - struct TALER_WIRE_Plugin *plugin; - -}; - - -/** - * Head of DLL of wire plugins. - */ -static struct Plugin *wire_head; - -/** - * Tail of DLL of wire plugins. - */ -static struct Plugin *wire_tail; - -/** * Array of wire methods supported by this exchange. */ static json_t *wire_accounts_array; @@ -191,9 +157,8 @@ load_account (void *cls, else { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fees not specified for `%s', ignoring plugin %s\n", - method, - ai->plugin_name); + "Wire fees not specified for `%s'\n", + method); *ret = GNUNET_SYSERR; } GNUNET_free (method); @@ -201,35 +166,15 @@ load_account (void *cls, if (GNUNET_YES == ai->debit_enabled) { - struct Plugin *p; - - p = GNUNET_new (struct Plugin); - p->plugin = TALER_WIRE_plugin_load (cfg, - ai->plugin_name); - if (NULL == p->plugin) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load plugin %s\n", - ai->plugin_name); - GNUNET_free (p); - *ret = GNUNET_SYSERR; - return; - } if (GNUNET_OK != - load_fee (p->plugin->method)) + load_fee (ai->method)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Disabling plugin `%s' as wire transfer fees for `%s' are not given correctly\n", - ai->plugin_name, - p->plugin->method); - TALER_WIRE_plugin_unload (p->plugin); - GNUNET_free (p); + "Wire transfer fees for `%s' are not given correctly\n", + ai->method); *ret = GNUNET_SYSERR; return; } - GNUNET_CONTAINER_DLL_insert (wire_head, - wire_tail, - p); } } @@ -251,12 +196,6 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg) TALER_EXCHANGEDB_find_accounts (cfg, &load_account, &ret); - if (NULL == wire_head) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to find properly configured wire transfer method\n"); - ret = GNUNET_SYSERR; - } if (GNUNET_OK != ret) TEH_VALIDATION_done (); return ret; @@ -269,16 +208,6 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg) void TEH_VALIDATION_done () { - struct Plugin *p; - - while (NULL != (p = wire_head)) - { - GNUNET_CONTAINER_DLL_remove (wire_head, - wire_tail, - p); - TALER_WIRE_plugin_unload (p->plugin); - GNUNET_free (p); - } json_decref (wire_fee_object); wire_fee_object = NULL; json_decref (wire_accounts_array); @@ -287,65 +216,6 @@ TEH_VALIDATION_done () /** - * Check if the given wire format JSON object is correctly formatted as - * a wire address. - * - * @param wire the JSON wire format object - * @param[out] emsg set to error message if we return an error code - * @return #TALER_EC_NONE if correctly formatted; otherwise error code - */ -enum TALER_ErrorCode -TEH_json_validate_wireformat (const json_t *wire, - char **emsg) -{ - const char *payto_url; - json_error_t error; - char *method; - - *emsg = NULL; - if (0 != json_unpack_ex ((json_t *) wire, - &error, 0, - "{s:s}", - "url", &payto_url)) - { - GNUNET_asprintf (emsg, - "No `url' specified in the wire details\n"); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_MISSING; - } - method = TALER_WIRE_payto_get_method (payto_url); - if (NULL == method) - { - GNUNET_asprintf (emsg, - "Malformed payto URL `%s'\n", - payto_url); - return TALER_EC_PAYTO_MALFORMED; - } - for (struct Plugin *p = wire_head; NULL != p; p = p->next) - { - if (0 == strcasecmp (p->plugin->method, - method)) - { - enum TALER_ErrorCode ec; - - GNUNET_free (method); - ec = p->plugin->wire_validate (p->plugin->cls, - payto_url); - if (TALER_EC_NONE != ec) - GNUNET_asprintf (emsg, - "Payto URL `%s' rejected by plugin\n", - payto_url); - return ec; - } - } - GNUNET_asprintf (emsg, - "Wire format type `%s' is not supported by this exchange\n", - method); - GNUNET_free (method); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_UNSUPPORTED; -} - - -/** * Obtain JSON response for /wire * * @return JSON array with the supported validation methods, NULL on error diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c index 21f62cd82..091f4ba20 100644 --- a/src/exchange/taler-exchange-wirewatch.c +++ b/src/exchange/taler-exchange-wirewatch.c @@ -27,6 +27,7 @@ #include "taler_exchangedb_lib.h" #include "taler_exchangedb_plugin.h" #include "taler_json_lib.h" +#include "taler_bank_service.h" #include "taler_wire_lib.h" /** @@ -37,23 +38,6 @@ /** - * Closure for #reject_cb(). - */ -struct RejectContext -{ - /** - * Wire transfer subject that was illformed. - */ - char *wtid_s; - - /** - * Database session that encountered the problem. - */ - struct TALER_EXCHANGEDB_Session *session; -}; - - -/** * Information we keep for each supported account. */ struct WireAccount @@ -69,14 +53,19 @@ struct WireAccount struct WireAccount *prev; /** - * Handle to the plugin. + * Name of the section that configures this account. */ - struct TALER_WIRE_Plugin *wire_plugin; + char *section_name; /** - * Name of the section that configures this account. + * Account information. */ - char *section_name; + struct TALER_Account account; + + /** + * Authentication data. + */ + struct TALER_BANK_AuthenticationData auth; /** * Are we running from scratch and should re-process all transactions @@ -108,6 +97,16 @@ static struct WireAccount *wa_tail; static struct WireAccount *wa_pos; /** + * Handle to the context for interacting with the bank. + */ +static struct GNUNET_CURL_Context *ctx; + +/** + * Scheduler context for running the @e ctx. + */ +static struct GNUNET_CURL_RescheduleContext *rc; + +/** * Which currency is used by this exchange? */ static char *exchange_currency_string; @@ -132,23 +131,13 @@ static int global_ret; * Encoded offset in the wire transfer list from where * to start the next query with the bank. */ -static void *last_row_off; - -/** - * Number of bytes in #last_row_off. - */ -static size_t last_row_off_size; +static uint64_t last_row_off; /** * Latest row offset seen in this transaction, becomes * the new #last_row_off upon commit. */ -static void *latest_row_off; - -/** - * Number of bytes in #latest_row_off. - */ -static size_t latest_row_off_size; +static uint64_t latest_row_off; /** * Should we delay the next request to the wire plugin a bit? @@ -183,12 +172,7 @@ static struct GNUNET_SCHEDULER_Task *task; /** * Active request for history. */ -static struct TALER_WIRE_HistoryHandle *hh; - -/** - * Active request to reject a wire transfer. - */ -static struct TALER_WIRE_RejectHandle *rt; +static struct TALER_BANK_CreditHistoryHandle *hh; /** @@ -202,6 +186,16 @@ shutdown_task (void *cls) struct WireAccount *wa; (void) cls; + if (NULL != ctx) + { + GNUNET_CURL_fini (ctx); + ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } if (NULL != task) { GNUNET_SCHEDULER_cancel (task); @@ -209,20 +203,9 @@ shutdown_task (void *cls) } if (NULL != hh) { - wa_pos->wire_plugin->get_history_cancel (wa_pos->wire_plugin->cls, - hh); + TALER_BANK_credit_history_cancel (hh); hh = NULL; } - if (NULL != rt) - { - char *wtid_s; - - wtid_s = wa_pos->wire_plugin->reject_transfer_cancel ( - wa_pos->wire_plugin->cls, - rt); - rt = NULL; - GNUNET_free (wtid_s); - } TALER_EXCHANGEDB_plugin_unload (db_plugin); db_plugin = NULL; while (NULL != (wa = wa_head)) @@ -230,14 +213,13 @@ shutdown_task (void *cls) GNUNET_CONTAINER_DLL_remove (wa_head, wa_tail, wa); - TALER_WIRE_plugin_unload (wa->wire_plugin); + TALER_WIRE_account_free (&wa->account); + TALER_BANK_auth_free (&wa->auth); GNUNET_free (wa->section_name); GNUNET_free (wa); } wa_pos = NULL; - GNUNET_free_non_null (last_row_off); - last_row_off = NULL; - last_row_off_size = 0; + last_row_off = 0; } @@ -259,13 +241,26 @@ add_account_cb (void *cls, return; /* not enabled for us, skip */ wa = GNUNET_new (struct WireAccount); wa->reset_mode = reset_mode; - wa->wire_plugin = TALER_WIRE_plugin_load (cfg, - ai->plugin_name); - if (NULL == wa->wire_plugin) + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (cfg, + ai->section_name, + &wa->auth)) { GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Failed to load wire plugin for `%s'\n", - ai->plugin_name); + "Failed to load account `%s'\n", + ai->section_name); + GNUNET_free (wa); + return; + } + if (GNUNET_OK != + TALER_BANK_account_parse_cfg (cfg, + ai->section_name, + &wa->account)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Failed to load account `%s'\n", + ai->section_name); + TALER_BANK_auth_free (&wa->auth); GNUNET_free (wa); return; } @@ -336,70 +331,28 @@ find_transfers (void *cls); /** - * Function called upon completion of the rejection of a wire transfer. - * - * @param cls closure with the `struct RejectContext` - * @param ec error code for the operation - */ -static void -reject_cb (void *cls, - enum TALER_ErrorCode ec) -{ - struct RejectContext *rtc = cls; - enum GNUNET_DB_QueryStatus qs; - - rt = NULL; - if (TALER_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to wire back transfer `%s': %d\n", - rtc->wtid_s, - ec); - GNUNET_free (rtc->wtid_s); - db_plugin->rollback (db_plugin->cls, - rtc->session); - GNUNET_free (rtc); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_free (rtc->wtid_s); - qs = db_plugin->commit (db_plugin->cls, - rtc->session); - GNUNET_break (0 <= qs); - GNUNET_free (rtc); - task = GNUNET_SCHEDULER_add_now (&find_transfers, - NULL); -} - - -/** * Callbacks of this type are used to serve the result of asking * the bank for the transaction history. * * @param cls closure with the `struct TALER_EXCHANGEDB_Session *` * @param ec taler error code - * @param dir direction of the transfer - * @param row_off identification of the position at which we are querying - * @param row_off_size number of bytes in @a row_off + * @param serial_id identification of the position at which we are querying * @param details details about the wire transfer + * @param json raw JSON response * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ static int history_cb (void *cls, + unsigned int http_status, enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - const void *row_off, - size_t row_off_size, - const struct TALER_WIRE_TransferDetails *details) + uint64_t serial_id, + const struct TALER_BANK_CreditDetails *details, + const json_t *json) { struct TALER_EXCHANGEDB_Session *session = cls; enum GNUNET_DB_QueryStatus qs; - struct TALER_ReservePublicKeyP reserve_pub; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got history callback, direction %u!\n", - (unsigned int) dir); - if (TALER_BANK_DIRECTION_NONE == dir) + if (NULL == details) { hh = NULL; if (TALER_EC_NONE != ec) @@ -428,11 +381,8 @@ history_cb (void *cls, if (0 < qs) { /* transaction success, update #last_row_off */ - GNUNET_free_non_null (last_row_off); last_row_off = latest_row_off; - last_row_off_size = latest_row_off_size; - latest_row_off = NULL; - latest_row_off_size = 0; + latest_row_off = 0; /* if successful at limit, try increasing transaction batch size (AIMD) */ if (current_batch_size == batch_size) @@ -462,49 +412,10 @@ history_cb (void *cls, NULL); return GNUNET_OK; /* will be ignored anyway */ } - if (NULL != details->wtid_s) - { - struct RejectContext *rtc; - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Wire transfer over %s has invalid subject `%s', sending it back!\n", - TALER_amount2s (&details->amount), - details->wtid_s); - GNUNET_break (0 != row_off_size); - if (latest_row_off_size != row_off_size) - { - GNUNET_free_non_null (latest_row_off); - latest_row_off = GNUNET_malloc (row_off_size); - latest_row_off_size = row_off_size; - } - memcpy (latest_row_off, - row_off, - row_off_size); - rtc = GNUNET_new (struct RejectContext); - rtc->session = session; - rtc->wtid_s = GNUNET_strdup (details->wtid_s); - rt = wa_pos->wire_plugin->reject_transfer (wa_pos->wire_plugin->cls, - wa_pos->section_name, - row_off, - row_off_size, - &reject_cb, - rtc); - if (NULL == rt) - { - GNUNET_break (0); - db_plugin->rollback (db_plugin->cls, - session); - GNUNET_assert (NULL == task); - task = GNUNET_SCHEDULER_add_now (&find_transfers, - NULL); - } - return GNUNET_SYSERR; /* will continue later... */ - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Adding wire transfer over %s with (hashed) subject `%s'\n", TALER_amount2s (&details->amount), - TALER_B2S (&details->wtid)); + TALER_B2S (&details->reserve_pub)); /** * Debug block. @@ -515,8 +426,8 @@ history_cb (void *cls, char wtid_s[PUBSIZE]; GNUNET_break - (NULL != GNUNET_STRINGS_data_to_string (&details->wtid, - sizeof (details->wtid), + (NULL != GNUNET_STRINGS_data_to_string (&details->reserve_pub, + sizeof (details->reserve_pub), &wtid_s[0], PUBSIZE)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -525,20 +436,14 @@ history_cb (void *cls, } current_batch_size++; - /* Wire transfer identifier == reserve public key */ - GNUNET_assert (sizeof (reserve_pub) == sizeof (details->wtid)); - memcpy (&reserve_pub, - &details->wtid, - sizeof (reserve_pub)); qs = db_plugin->reserves_in_insert (db_plugin->cls, session, - &reserve_pub, + &details->reserve_pub, &details->amount, details->execution_date, details->account_url, wa_pos->section_name, - row_off, - row_off_size); + serial_id); if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_break (0); @@ -560,15 +465,7 @@ history_cb (void *cls, return GNUNET_SYSERR; } - if (latest_row_off_size != row_off_size) - { - GNUNET_free_non_null (latest_row_off); - latest_row_off = GNUNET_malloc (row_off_size); - latest_row_off_size = row_off_size; - } - memcpy (latest_row_off, - row_off, - row_off_size); + latest_row_off = serial_id; return GNUNET_OK; } @@ -615,8 +512,7 @@ find_transfers (void *cls) qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls, session, wa_pos->section_name, - &last_row_off, - &last_row_off_size); + &last_row_off); if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -638,20 +534,17 @@ find_transfers (void *cls) } } wa_pos->reset_mode = GNUNET_NO; - GNUNET_assert ( (NULL == last_row_off) || - ( (NULL != last_row_off) && - (0 != last_row_off_size) ) ); delay = GNUNET_YES; current_batch_size = 0; - hh = wa_pos->wire_plugin->get_history (wa_pos->wire_plugin->cls, - wa_pos->section_name, - TALER_BANK_DIRECTION_CREDIT, - last_row_off, - last_row_off_size, - batch_size, - &history_cb, - session); + hh = TALER_BANK_credit_history (ctx, + wa_pos->account.details.x_taler_bank. + account_base_url, + &wa_pos->auth, + last_row_off, + batch_size, + &history_cb, + session); if (NULL == hh) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -695,6 +588,14 @@ run (void *cls, NULL); GNUNET_SCHEDULER_add_shutdown (&shutdown_task, cls); + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + if (NULL == ctx) + { + GNUNET_break (0); + return; + } } diff --git a/src/exchangedb/exchangedb_accounts.c b/src/exchangedb/exchangedb_accounts.c index e31509021..037793953 100644 --- a/src/exchangedb/exchangedb_accounts.c +++ b/src/exchangedb/exchangedb_accounts.c @@ -58,7 +58,7 @@ check_for_account (void *cls, const char *section) { struct FindAccountContext *ctx = cls; - char *plugin_name; + char *method; char *payto_url; char *wire_response_filename; struct TALER_EXCHANGEDB_AccountInfo ai; @@ -81,12 +81,12 @@ check_for_account (void *cls, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ctx->cfg, section, - "PLUGIN", - &plugin_name)) + "METHOD", + &method)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, section, - "PLUGIN"); + "METHOD"); GNUNET_free (payto_url); return; } @@ -97,7 +97,7 @@ check_for_account (void *cls, &wire_response_filename)) wire_response_filename = NULL; ai.section_name = section; - ai.plugin_name = plugin_name; + ai.method = method; ai.payto_url = payto_url; ai.wire_response_filename = wire_response_filename; @@ -112,7 +112,7 @@ check_for_account (void *cls, ctx->cb (ctx->cb_cls, &ai); GNUNET_free (payto_url); - GNUNET_free (plugin_name); + GNUNET_free (method); GNUNET_free_non_null (wire_response_filename); } diff --git a/src/exchangedb/exchangedb_fees.c b/src/exchangedb/exchangedb_fees.c index f06be73ad..386c5de90 100644 --- a/src/exchangedb/exchangedb_fees.c +++ b/src/exchangedb/exchangedb_fees.c @@ -144,12 +144,12 @@ TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg, /** * Convert @a af to @a wf. * - * @param wireplugin name of the wire plugin the fees are for + * @param method name of the wire method the fees are for * @param[in,out] af aggregate fees, host format (updated to round time) * @param[out] wf aggregate fees, disk / signature format */ void -TALER_EXCHANGEDB_fees_2_wf (const char *wireplugin, +TALER_EXCHANGEDB_fees_2_wf (const char *method, struct TALER_EXCHANGEDB_AggregateFees *af, struct TALER_MasterWireFeePS *wf) { @@ -157,8 +157,8 @@ TALER_EXCHANGEDB_fees_2_wf (const char *wireplugin, (void) GNUNET_TIME_round_abs (&af->end_date); wf->purpose.size = htonl (sizeof (*wf)); wf->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES); - GNUNET_CRYPTO_hash (wireplugin, - strlen (wireplugin) + 1, + GNUNET_CRYPTO_hash (method, + strlen (method) + 1, &wf->h_wire_method); wf->start_date = GNUNET_TIME_absolute_hton (af->start_date); wf->end_date = GNUNET_TIME_absolute_hton (af->end_date); diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 6ddbead9e..5f2c67161 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -253,7 +253,7 @@ postgres_create_tables (void *cls) GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves_in" "(reserve_in_serial_id BIGSERIAL UNIQUE" ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE" - ",wire_reference BYTEA NOT NULL" + ",wire_reference INT8 NOT NULL" ",credit_val INT8 NOT NULL" ",credit_frac INT4 NOT NULL" ",sender_account_details TEXT NOT NULL" @@ -2158,8 +2158,7 @@ reserves_update (void *cls, * @param sender_account_details account information for the sender (payto://-URL) * @param exchange_account_section name of the section in the configuration for the exchange's * account into which the deposit was made - * @param wire_reference unique reference identifying the wire transfer (binary blob) - * @param wire_reference_size number of bytes in @a wire_reference + * @param wire_ref unique reference identifying the wire transfer * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -2170,8 +2169,7 @@ postgres_reserves_in_insert (void *cls, struct GNUNET_TIME_Absolute execution_time, const char *sender_account_details, const char *exchange_account_section, - const void *wire_reference, - size_t wire_reference_size) + uint64_t wire_ref) { struct PostgresClosure *pg = cls; enum GNUNET_DB_QueryStatus reserve_exists; @@ -2252,8 +2250,7 @@ postgres_reserves_in_insert (void *cls, { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (&reserve.pub), - GNUNET_PQ_query_param_fixed_size (wire_reference, - wire_reference_size), + GNUNET_PQ_query_param_uint64 (&wire_ref), TALER_PQ_query_param_amount (balance), GNUNET_PQ_query_param_string (exchange_account_section), GNUNET_PQ_query_param_string (sender_account_details), @@ -2311,8 +2308,7 @@ postgres_reserves_in_insert (void *cls, * @param session the database session handle * @param exchange_account_name name of the section in the exchange's configuration * for the account that we are tracking here - * @param[out] wire_reference set to unique reference identifying the wire transfer (binary blob) - * @param[out] wire_reference_size set to number of bytes in @a wire_reference + * @param[out] wire_ref set to unique reference identifying the wire transfer * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -2320,17 +2316,15 @@ postgres_get_latest_reserve_in_reference (void *cls, struct TALER_EXCHANGEDB_Session * session, const char *exchange_account_name, - void **wire_reference, - size_t *wire_reference_size) + uint64_t *wire_reference) { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (exchange_account_name), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_variable_size ("wire_reference", - wire_reference, - wire_reference_size), + GNUNET_PQ_result_spec_uint64 ("wire_reference", + wire_reference), GNUNET_PQ_result_spec_end }; @@ -6192,14 +6186,12 @@ reserves_in_serial_helper_cb (void *cls, char *sender_account_details; struct GNUNET_TIME_Absolute execution_date; uint64_t rowid; - void *wire_reference; - size_t wire_reference_size; + uint64_t wire_reference; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", &reserve_pub), - GNUNET_PQ_result_spec_variable_size ("wire_reference", - &wire_reference, - &wire_reference_size), + GNUNET_PQ_result_spec_uint64 ("wire_reference", + &wire_reference), TALER_PQ_RESULT_SPEC_AMOUNT ("credit", &credit), TALER_PQ_result_spec_absolute_time ("execution_date", @@ -6227,7 +6219,6 @@ reserves_in_serial_helper_cb (void *cls, &credit, sender_account_details, wire_reference, - wire_reference_size, execution_date); GNUNET_PQ_cleanup_result (rs); if (GNUNET_OK != ret) diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index a1e20e83a..583e3c178 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -944,8 +944,7 @@ audit_refund_cb (void *cls, * @param reserve_pub public key of the reserve (also the WTID) * @param credit amount that was received * @param sender_account_details information about the sender's bank account - * @param wire_reference unique reference identifying the wire transfer (binary blob) - * @param wire_reference_size number of bytes in @a wire_reference + * @param wire_reference unique reference identifying the wire transfer * @param execution_date when did we receive the funds * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ @@ -955,8 +954,7 @@ audit_reserve_in_cb (void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, const char *sender_account_details, - const void *wire_reference, - size_t wire_reference_size, + uint64_t wire_reference, struct GNUNET_TIME_Absolute execution_date) { auditor_row_cnt++; @@ -1507,8 +1505,7 @@ run (void *cls) const char *sndr = "payto://x-taler-bank/localhost:8080/1"; unsigned int matched; unsigned int cnt; - void *rr; - size_t rr_size; + uint64_t rr; enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Absolute now; @@ -1578,8 +1575,7 @@ run (void *cls) plugin->get_latest_reserve_in_reference (plugin->cls, session, "account-1", - &rr, - &rr_size)); + &rr)); now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != @@ -1590,17 +1586,13 @@ run (void *cls) now, sndr, "account-1", - "TEST", 4)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_latest_reserve_in_reference (plugin->cls, session, "account-1", - &rr, - &rr_size)); - FAILIF (4 != rr_size); - FAILIF (0 != memcmp ("TEST", rr, 4)); - GNUNET_free (rr); + &rr)); + FAILIF (4 != rr); FAILIF (GNUNET_OK != check_reserve (session, &reserve_pub, @@ -1617,24 +1609,18 @@ run (void *cls) now, sndr, "account-1", - "TEST2", 5)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_latest_reserve_in_reference (plugin->cls, session, "account-1", - &rr, - &rr_size)); - GNUNET_free (rr); + &rr)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_latest_reserve_in_reference (plugin->cls, session, "account-1", - &rr, - &rr_size)); - FAILIF (5 != rr_size); - FAILIF (0 != memcmp ("TEST2", rr, 5)); - GNUNET_free (rr); + &rr)); + FAILIF (5 != rr); FAILIF (GNUNET_OK != check_reserve (session, &reserve_pub, diff --git a/src/include/Makefile.am b/src/include/Makefile.am index af91f1178..25bc67b25 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -31,8 +31,6 @@ talerinclude_HEADERS = \ taler_mhd_lib.h \ taler_pq_lib.h \ taler_signatures.h \ - taler_wire_lib.h \ - taler_wire_plugin.h \ taler_testing_bank_lib.h endif diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h index 03a1c6d47..14d477d30 100644 --- a/src/include/taler_auditordb_plugin.h +++ b/src/include/taler_auditordb_plugin.h @@ -870,7 +870,6 @@ struct TALER_AUDITORDB_Plugin * @param pp where is the auditor in processing * @param in_wire_off how far are we in the incoming wire transaction history * @param out_wire_off how far are we in the outgoing wire transaction history - * @param wire_off_size how many bytes do @a in_wire_off and @a out_wire_off take? * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -883,9 +882,8 @@ struct TALER_AUDITORDB_Plugin const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - const void *in_wire_off, - const void *out_wire_off, - size_t wire_off_size); + uint64_t in_wire_off, + uint64_t out_wire_off); /** @@ -899,7 +897,6 @@ struct TALER_AUDITORDB_Plugin * @param pp where is the auditor in processing * @param in_wire_off how far are we in the incoming wire transaction history * @param out_wire_off how far are we in the outgoing wire transaction history - * @param wire_off_size how many bytes do @a in_wire_off and @a out_wire_off take? * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -912,9 +909,8 @@ struct TALER_AUDITORDB_Plugin const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - const void *in_wire_off, - const void *out_wire_off, - size_t wire_off_size); + uint64_t in_wire_off, + uint64_t out_wire_off); /** @@ -927,7 +923,6 @@ struct TALER_AUDITORDB_Plugin * @param[out] pp where is the auditor in processing * @param[out] in_wire_off how far are we in the incoming wire transaction history * @param[out] out_wire_off how far are we in the outgoing wire transaction history - * @param[out] wire_off_size how many bytes do @a in_wire_off and @a out_wire_off take? * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -939,9 +934,8 @@ struct TALER_AUDITORDB_Plugin struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - void **in_wire_off, - void **out_wire_off, - size_t *wire_off_size); + uint64_t *in_wire_off, + uint64_t *out_wire_off); /** diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h index a7246ad2d..a2eec49aa 100644 --- a/src/include/taler_bank_service.h +++ b/src/include/taler_bank_service.h @@ -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 Affero General Public License as published by the Free Software @@ -25,6 +25,7 @@ #include <jansson.h> #include <gnunet/gnunet_curl_lib.h> #include "taler_util.h" +#include "taler_wire_lib.h" #include "taler_error_codes.h" @@ -122,13 +123,11 @@ typedef void * 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 account (used to execute this request) * @param auth authentication data to use - * @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 * @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 @@ -137,13 +136,12 @@ typedef void */ 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); @@ -159,53 +157,103 @@ TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai); +/* ********************* /taler/transfer *********************** */ + /** - * Which types of transactions should be (or is being) returned? + * 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 */ -enum TALER_BANK_Direction -{ +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); - /** - * Base case, used to indicate errors or end of list. - */ - TALER_BANK_DIRECTION_NONE = 0, - /** - * Transactions where the bank account receives money. - */ - TALER_BANK_DIRECTION_CREDIT = 1, +/** + * Handle for active wire transfer. + */ +struct TALER_BANK_WireExecuteHandle; - /** - * Transactions where the bank account looses money. - */ - TALER_BANK_DIRECTION_DEBIT = 2, - /** - * Return both types of transactions. - */ - TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT - | TALER_BANK_DIRECTION_DEBIT), +/** + * Function called with the result from the execute step. + * + * @param cls closure + * @param response_code HTTP status code + * @param ec taler error code + * @param row_id unique ID of the wire transfer in the bank's records + * @param timestamp when did the transaction go into effect + */ +typedef void +(*TALER_BANK_ConfirmationCallback)(void *cls, + unsigned int response_code, + enum TALER_ErrorCode ec, + uint64_t row_id, + struct GNUNET_TIME_Absolute timestamp); - /** - * Bit mask that is applied to view transactions that have been - * cancelled. The bit is set for cancelled transactions that are - * returned from /history, and must also be set in order for - * cancelled transactions to show up in the /history. - */ - TALER_BANK_DIRECTION_CANCEL = 4 -}; +/** + + * Execute a wire transfer. + * + * @param ctx context for HTTP interaction + * @param bank_base_url URL of the base INCLUDING account number + * @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); /** - * Handle for querying the bank's transaction history. + * Abort execution of a wire transfer. For example, because we are + * shutting down. Note that if an execution is aborted, it may or + * may not still succeed. The caller MUST run @e + * execute_wire_transfer again for the same request as soon as + * possilbe, to ensure that the request either ultimately succeeds + * or ultimately fails. Until this has been done, the transaction is + * in limbo (i.e. may or may not have been committed). + * + * @param eh execution to cancel */ -struct TALER_BANK_HistoryHandle; +void +TALER_BANK_execute_wire_transfer_cancel (struct + TALER_BANK_WireExecuteHandle *eh); + + +/* ********************* /taler/credits *********************** */ /** - * Details about a wire transfer. + * Handle for querying the bank for transactions + * made to the exchange. */ -struct TALER_BANK_TransferDetails +struct TALER_BANK_CreditHistoryHandle; + +/** + * Details about a wire transfer to the exchange. + */ +struct TALER_BANK_CreditDetails { /** * Amount that was transferred @@ -218,21 +266,22 @@ struct TALER_BANK_TransferDetails struct GNUNET_TIME_Absolute execution_date; /** - * Wire transfer subject. Usually a reserve public key - * followed by the base URL of the exchange. + * Reserve public key encoded in the wire + * transfer subject. */ - char *wire_transfer_subject; + struct TALER_ReservePublicKeyP reserve_pub; /** - * payto://-URL of the other account that was involved + * payto://-URL of the source account that + * send the funds. */ - char *account_url; + const char *account_url; }; /** * Callbacks of this type are used to serve the result of asking - * the bank for the transaction history. + * the bank for the credit transaction history. * * @param cls closure * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request @@ -241,31 +290,27 @@ struct TALER_BANK_TransferDetails * last callback is always of this status (even if `abs(num_results)` were * already returned). * @param ec detailed error code - * @param dir direction of the transfer * @param serial_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 */ -typedef void -(*TALER_BANK_HistoryResultCallback) (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - uint64_t serial_id, - const struct - TALER_BANK_TransferDetails *details, - const json_t *json); +typedef int +(*TALER_BANK_CreditResultCallback) (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t serial_id, + const struct + TALER_BANK_CreditDetails *details, + const json_t *json); /** - * Request the wire transfer history of a bank account. + * Request the wire credit history of an 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 account_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; negative numbers to go into the past, * positive numbers to go into the future starting at @a start_row; @@ -276,17 +321,14 @@ typedef void * if the inputs are invalid (i.e. zero value for @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_CreditHistoryHandle * +TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx, + const char *account_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); /** @@ -297,64 +339,127 @@ 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_credit_history_cancel (struct TALER_BANK_CreditHistoryHandle *hh); + +/* ********************* /taler/debits *********************** */ /** - * Handle for #TALER_BANK_reject() operation. + * Handle for querying the bank for transactions + * made from the exchange to merchants. */ -struct TALER_BANK_RejectHandle; +struct TALER_BANK_DebitHistoryHandle; + +/** + * Details about a wire transfer made by the exchange + * to a merchant. + */ +struct TALER_BANK_DebitDetails +{ + /** + * Amount that was transferred + */ + struct TALER_Amount amount; + + /** + * Time of the the transfer + */ + struct GNUNET_TIME_Absolute execution_date; + + /** + * Wire transfer identifier used by the exchange. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /** + * Exchange's base URL as given in the wire transfer. + */ + const char *exchange_base_url; + + /** + * payto://-URL of the source account that + * send the funds. + */ + const char *account_url; +}; /** * Callbacks of this type are used to serve the result of asking - * the bank to reject an incoming wire transfer. + * the bank for the debit transaction history. * * @param cls closure - * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for successful status request; - * #MHD_HTTP_NOT_FOUND if the rowid is unknown; + * @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 detailed error code + * @param serial_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 */ -typedef void -(*TALER_BANK_RejectResultCallback) (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec); +typedef int +(*TALER_BANK_DebitResultCallback) (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + uint64_t serial_id, + const struct + TALER_BANK_DebitDetails *details, + const json_t *json); /** - * Request rejection of a wire transfer, marking it as cancelled and voiding - * its effects. + * Request the wire credit history of an 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 rowid transfer to reject - * @param rcb the callback to call with the operation result - * @param rcb_cls closure for @a rcb + * @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. + * if the inputs are invalid (i.e. zero value for @e num_results). * 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_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); /** - * Cancel an reject request. This function cannot be used on a request - * handle if the response was is already served for it. + * Cancel an history request. This function cannot be used on a request + * handle if the last response (anything with a status code other than + * 200) is already served for it. * - * @param rh the reject request handle + * @param hh the history request handle */ void -TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh); +TALER_BANK_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *hh); + + +/** + * 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); /** diff --git a/src/include/taler_exchangedb_lib.h b/src/include/taler_exchangedb_lib.h index e63640628..1681c45aa 100644 --- a/src/include/taler_exchangedb_lib.h +++ b/src/include/taler_exchangedb_lib.h @@ -425,12 +425,12 @@ TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg, /** * Convert @a af to @a wf. * - * @param wireplugin name of the wire plugin the fees are for + * @param wiremethod name of the wire method the fees are for * @param[in,out] af aggregate fees, host format (updated to round time) * @param[out] wf aggregate fees, disk / signature format */ void -TALER_EXCHANGEDB_fees_2_wf (const char *wireplugin, +TALER_EXCHANGEDB_fees_2_wf (const char *wiremethod, struct TALER_EXCHANGEDB_AggregateFees *af, struct TALER_MasterWireFeePS *wf); @@ -470,10 +470,9 @@ struct TALER_EXCHANGEDB_AccountInfo const char *section_name; /** - * Name of the wire plugin that should be used to access - * the account. + * Name of the wire method used by this account. */ - const char *plugin_name; + const char *method; /** * payto://-URL of the account. diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 0dcf4b841..09e3b1800 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -960,8 +960,7 @@ typedef int * @param reserve_pub public key of the reserve (also the WTID) * @param credit amount that was received * @param sender_account_details information about the sender's bank account, in payto://-format - * @param wire_reference unique identifier for the wire transfer (plugin-specific format) - * @param wire_reference_size number of bytes in @a wire_reference + * @param wire_reference unique identifier for the wire transfer * @param execution_date when did we receive the funds * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ @@ -972,8 +971,7 @@ typedef int TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, const char *sender_account_details, - const void *wire_reference, - size_t wire_reference_size, + uint64_t wire_reference, struct GNUNET_TIME_Absolute execution_date); @@ -1500,8 +1498,7 @@ struct TALER_EXCHANGEDB_Plugin * @param balance the amount that has to be added to the reserve * @param execution_time when was the amount added * @param sender_account_details information about the sender's bank account, in payto://-format - * @param wire_reference unique reference identifying the wire transfer (binary blob) - * @param wire_reference_size number of bytes in @a wire_reference + * @param wire_reference unique reference identifying the wire transfer * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -1512,8 +1509,7 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Absolute execution_time, const char *sender_account_details, const char *exchange_account_name, - const void *wire_reference, - size_t wire_reference_size); + uint64_t wire_reference); /** @@ -1521,16 +1517,14 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param db the database connection handle - * @param[out] wire_reference set to unique reference identifying the wire transfer (binary blob) - * @param[out] wire_reference_size set to number of bytes in @a wire_reference + * @param[out] wire_ref set to unique reference identifying the wire transfer * @return transaction status code */ enum GNUNET_DB_QueryStatus (*get_latest_reserve_in_reference)(void *cls, struct TALER_EXCHANGEDB_Session *db, const char *exchange_account_name, - void **wire_reference, - size_t *wire_reference_size); + uint64_t *wire_ref); /** diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h index f3f92534d..474c6627d 100644 --- a/src/include/taler_fakebank_lib.h +++ b/src/include/taler_fakebank_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2016 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 as published by the Free Software @@ -74,8 +74,8 @@ TALER_FAKEBANK_check_empty (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); @@ -101,28 +101,13 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h, 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); /** - * 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); - - -/** * Stop running the fake bank. * * @param h bank to stop diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h index 6aefbd778..c836e4469 100644 --- a/src/include/taler_mhd_lib.h +++ b/src/include/taler_mhd_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 GNUnet e.V. + Copyright (C) 2014-2020 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software diff --git a/src/include/taler_testing_bank_lib.h b/src/include/taler_testing_bank_lib.h index 350aca3b7..43d12d336 100644 --- a/src/include/taler_testing_bank_lib.h +++ b/src/include/taler_testing_bank_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2018 Taler Systems SA + (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 @@ -103,46 +103,42 @@ TALER_TESTING_has_in_name (const char *prog, /* ************** Specific interpreter commands ************ */ /** - * Make a "history" CMD. + * Make a credit "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, it ask the bank to return results - * in 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. - * @param num_results how many rows we want in the result. + * @param num_results how many rows we want in the result, + * and ascending/descending call * @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); /** - * Create a "reject" CMD. + * Make a debit "history" 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. - * + * @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_reject (const char *label, - const char *bank_url, - const char *deposit_reference); +TALER_TESTING_cmd_bank_debits (const char *label, + const char *account_url, + const char *start_row_reference, + long long num_results); + #endif diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index a039f0ec6..33f070701 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -48,23 +48,24 @@ } while (0) +#define TALER_TESTING_GET_TRAIT_ROW_ID(cmd,out) \ + TALER_TESTING_get_trait_uint64 (cmd, 3, out) + +#define TALER_TESTING_MAKE_TRAIT_ROW_ID(data) \ + TALER_TESTING_make_trait_uint64 (3, data) + + #define TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT(cmd,out) \ - TALER_TESTING_get_trait_uint64 (cmd, 0, out) + TALER_TESTING_get_trait_string (cmd, 4, out) #define TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT(data) \ - TALER_TESTING_make_trait_uint64 (0, data) + TALER_TESTING_make_trait_string (4, data) #define TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT(cmd,out) \ - TALER_TESTING_get_trait_uint64 (cmd, 1, out) + TALER_TESTING_get_trait_string (cmd, 5, out) #define TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT(data) \ - TALER_TESTING_make_trait_uint64 (1, data) - -#define TALER_TESTING_GET_TRAIT_ROW_ID(cmd,out) \ - TALER_TESTING_get_trait_uint64 (cmd, 3, out) - -#define TALER_TESTING_MAKE_TRAIT_ROW_ID(data) \ - TALER_TESTING_make_trait_uint64 (3, data) + TALER_TESTING_make_trait_string (5, data) /** @@ -715,13 +716,9 @@ TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb, * * @param label command label. * @param amount amount to transfer. - * @param bank_url base URL of the bank that implements this - * wire transer. For simplicity, both credit and debit - * bank account exist at the same bank. - * @param debit_account_no which account (expressed as a number) - * gives money. - * @param credit_account_no which account (expressed as a number) - * receives money. + * @param account_base_url base URL of the account that implements this + * wire transer (which account gives money). + * @param payto_credit_account which account receives money. * @param auth_username username identifying the @a * debit_account_no at the bank. * @param auth_password password for @a auth_username. @@ -734,51 +731,14 @@ TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb, struct TALER_TESTING_Command TALER_TESTING_cmd_fakebank_transfer (const char *label, const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, + const char *account_base_url, + const char *payto_credit_account, const char *auth_username, const char *auth_password, const char *exchange_url); /** - * Create "fakebank transfer" CMD, letting the caller specifying - * the subject line. - * - * @param label command label. - * @param amount amount to transfer. - * @param bank_url base URL of the bank that implements this - * wire transer. For simplicity, both credit and debit - * bank account exist at the same bank. - * @param debit_account_no which account (expressed as a number) - * gives money. - * @param credit_account_no which account (expressed as a number) - * receives money. - * - * @param auth_username username identifying the @a - * debit_account_no at the bank. - * @param auth_password password for @a auth_username. - * @param subject wire transfer's subject line. - * @param exchange_url which exchange is involved in this transfer. - * This data is used for tracking purposes (FIXME: explain - * _how_). - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label, - const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, - const char *auth_username, - const char *auth_password, - const char *subject, - const char *exchange_url); - - -/** * Create "fakebank transfer" CMD, letting the caller specify * a reference to a command that can offer a reserve private key. * This private key will then be used to construct the subject line @@ -786,11 +746,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label, * * @param label command label. * @param amount the amount to transfer. - * @param bank_url base URL of the bank running the transfer. - * @param debit_account_no which account (expressed as a number) - * gives money. - * @param credit_account_no which account (expressed as a number) - * receives money. + * @param account_base_url base URL of the account that implements this + * wire transer (which account gives money). + * @param payto_credit_account which account receives money. * @param auth_username username identifying the @a * debit_account_no at the bank. * @param auth_password password for @a auth_username. @@ -804,9 +762,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label, struct TALER_TESTING_Command TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, + const char *account_base_url, + const char *payto_credit_account, + const char *auth_username, const char *auth_password, const char *ref, @@ -822,14 +780,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, * * @param label command label. * @param amount amount to transfer. - * @param bank_url base URL of the bank that implements this - * wire transer. For simplicity, both credit and debit - * bank account exist at the same bank. - * @param debit_account_no which account (expressed as a number) - * gives money. - * @param credit_account_no which account (expressed as a number) - * receives money. - * + * @param account_base_url base URL of the account that implements this + * wire transer (which account gives money). + * @param payto_credit_account which account receives money. * @param auth_username username identifying the @a * debit_account_no at the bank. * @param auth_password password for @a auth_username. @@ -847,9 +800,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, struct TALER_TESTING_Command TALER_TESTING_cmd_fakebank_transfer_with_instance (const char *label, const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, + const char *account_base_url, + const char * + payto_credit_account, const char *auth_username, const char *auth_password, const char *instance, @@ -1268,8 +1221,8 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_check_bank_transfer (const char *label, const char *exchange_base_url, const char *amount, - uint64_t debit_account, - uint64_t credit_account); + const char *debit_account, + const char *credit_account); /** @@ -1617,7 +1570,6 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits, * * @return the trait. */ - struct TALER_TESTING_Trait TALER_TESTING_make_trait_reserve_priv (unsigned int index, const struct @@ -1641,6 +1593,34 @@ TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd, /** + * Offer a reserve public key. + * + * @param index reserve pubs's index number. + * @param reserve_priv reserve public key to offer. + * @return the trait. + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_reserve_pub (unsigned int index, + const struct + TALER_ReservePublicKeyP *reserve_pub); + + +/** + * Obtain a reserve public key from a @a cmd. + * + * @param cmd command to extract the reserve pub from. + * @param index reserve pub's index number. + * @param reserve_pub[out] set to the reserve pub. + * @return #GNUNET_OK on success. + */ +int +TALER_TESTING_get_trait_reserve_pub (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const struct + TALER_ReservePublicKeyP **reserve_pub); + + +/** * Make a trait for a exchange signature. * * @param index index number to associate to the offered exchange pub. @@ -2129,34 +2109,34 @@ TALER_TESTING_make_trait_peer_key_pub (unsigned int index, /** - * Obtain a transfer subject from @a cmd. + * Obtain a string from @a cmd. * * @param cmd command to extract the subject from. * @param index index number associated with the transfer * subject to offer. - * @param transfer_subject[out] where to write the offered - * transfer subject. + * @param s[out] where to write the offered + * string. * * @return #GNUNET_OK on success. */ int -TALER_TESTING_get_trait_transfer_subject (const struct - TALER_TESTING_Command *cmd, - unsigned int index, - const char **transfer_subject); +TALER_TESTING_get_trait_string (const struct + TALER_TESTING_Command *cmd, + unsigned int index, + const char **s); /** - * Offer transfer subject. + * Offer string subject. * * @param index index number associated with the transfer * subject being offered. - * @param transfer_subject transfer subject to offer. + * @param s string to offer. * @return the trait. */ struct TALER_TESTING_Trait -TALER_TESTING_make_trait_transfer_subject (unsigned int index, - const char *transfer_subject); +TALER_TESTING_make_trait_string (unsigned int index, + const char *s); /** @@ -2223,7 +2203,6 @@ TALER_TESTING_get_trait_amount (const struct TALER_TESTING_Command *cmd, * @param index which url is to be picked, * in case multiple are offered. * @param url the url to offer. - * * @return the trait. */ struct TALER_TESTING_Trait diff --git a/src/include/taler_wire_lib.h b/src/include/taler_wire_lib.h index 3c8510919..c7d9f78d8 100644 --- a/src/include/taler_wire_lib.h +++ b/src/include/taler_wire_lib.h @@ -22,7 +22,7 @@ #define TALER_WIRE_H #include <gnunet/gnunet_util_lib.h> -#include "taler_wire_plugin.h" + /** * Different account types supported by payto://. @@ -46,6 +46,7 @@ enum TALER_PaytoAccountType TALER_PAC_IBAN }; + /** * Information about an account extracted from a payto://-URL. */ @@ -71,19 +72,15 @@ struct TALER_Account { /** - * Hostname of the bank (possibly including port). + * Bank account base URL. */ - char *hostname; + char *account_base_url; /** - * Bank account number. + * Only the hostname of the bank. */ - unsigned long long no; + char *hostname; - /** - * Base URL of the bank hosting the account above. - */ - char *bank_base_url; } x_taler_bank; /** @@ -114,6 +111,18 @@ TALER_WIRE_account_free (struct TALER_Account *acc); /** + * Round the amount to something that can be + * transferred on the wire. + * + * @param[in,out] amount amount to round down + * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, + * #GNUNET_SYSERR if the amount or currency was invalid + */ +int +TALER_WIRE_amount_round (struct TALER_Amount *amount); + + +/** * Parse @a payto_url and store the result in @a acc * * @param payto_url URL to parse @@ -135,36 +144,4 @@ char * TALER_WIRE_payto_get_method (const char *payto_url); -/** - * Get the plugin name from the payment method. - * - * @param method the method implemented by the plugin (for - * simplicity, we assume 1 method is implemented by 1 plugin). - * @return the plugin name, NULL if not found. - */ -const char * -TALER_WIRE_get_plugin_from_method (const char *method); - - -/** - * Load a WIRE plugin. - * - * @param cfg configuration to use - * @param plugin_name name of the plugin to load - * @return #GNUNET_OK on success - */ -struct TALER_WIRE_Plugin * -TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *plugin_name); - - -/** - * Unload a WIRE plugin. - * - * @param plugin the plugin to unload - */ -void -TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin); - - #endif diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index ffc7adf59..b5621e686 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016, 2017 GNUnet e.V. & Inria + 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 published by the Free Software @@ -41,22 +41,9 @@ typedef void /** - * Callback to process a merchant registration outcome. - * - * @param cls closure - * @param status GNUNET_OK if the registration succeeded, - * GNUNET_NO otherwise. - */ -typedef void -(*TALER_WIRE_MerchantRegisterCallback) (void *cls, - unsigned int status); - -/** * Details about a valid wire transfer to the exchange. - * It is the plugin's responsibility to filter and undo - * invalid transfers. */ -struct TALER_WIRE_TransferDetails +struct TALER_WIRE_CreditDetails { /** * Amount that was transferred @@ -69,22 +56,44 @@ struct TALER_WIRE_TransferDetails struct GNUNET_TIME_Absolute execution_date; /** - * Binary data that was encoded in the wire transfer subject, if - * it decoded properly. Otherwise all-zeros and @e wtid_s is set. + * Binary data that was encoded in the wire transfer subject. */ - struct TALER_WireTransferIdentifierRawP wtid; + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * payto://-URL of the source's account (used + * when the reserve is closed or for debugging). + */ + const char *source_account_url; +}; + + +/** + * Details about a valid wire transfer made by the + * exchange's aggregator to a merchant. + */ +struct TALER_WIRE_DebitDetails +{ + /** + * Amount that was transferred + */ + struct TALER_Amount amount; /** - * Wire transfer identifer as a string. Set to NULL if the - * identifier was properly Base32 encoded and this @e wtid could be - * set instead. + * Time of the the transfer + */ + struct GNUNET_TIME_Absolute execution_date; + + /** + * Binary data that was encoded in the wire transfer subject. */ - char *wtid_s; + struct TALER_WireTransferIdentifierRawP wtid; /** - * payto://-URL of the other account that was involved + * payto://-URL of the target account which received + * the funds. */ - char *account_url; + const char *target_account_url; }; @@ -96,33 +105,40 @@ struct TALER_WIRE_TransferDetails * * @param cls closure * @param ec taler error code - * @param dir direction of the transfer, #TALER_BANK_DIRECTION_NONE when - * the iteration is complete * @param row_off identification of the position at which we are querying * @param row_off_size number of bytes in @a row_off * @param details details about the wire transfer * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ typedef int -(*TALER_WIRE_HistoryResultCallback) (void *cls, - enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - const void *row_off, - size_t row_off_size, - const struct - TALER_WIRE_TransferDetails *details); +(*TALER_WIRE_CreditResultCallback) (void *cls, + enum TALER_ErrorCode ec, + const void *row_off, + size_t row_off_size, + const struct + TALER_WIRE_CreditDetails *details); /** * Callbacks of this type are used to serve the result of asking - * the bank to reject a wire transfer. + * the bank for the transaction history. NOTE: this function will + * NOT get the list of history elements, but rather get (iteratively) + * called for each (parsed) history element. * * @param cls closure - * @param ec status of the operation, #TALER_EC_NONE on success + * @param ec taler error code + * @param row_off identification of the position at which we are querying + * @param row_off_size number of bytes in @a row_off + * @param details details about the wire transfer + * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ -typedef void -(*TALER_WIRE_RejectTransferCallback) (void *cls, - enum TALER_ErrorCode ec); +typedef int +(*TALER_WIRE_DebitResultCallback) (void *cls, + enum TALER_ErrorCode ec, + const void *row_off, + size_t row_off_size, + const struct + TALER_WIRE_DebitDetails *details); /** @@ -136,9 +152,14 @@ struct TALER_WIRE_PrepareHandle; struct TALER_WIRE_ExecuteHandle; /** - * Handle returned for querying the transaction history. + * Handle returned for querying the credit transaction history. */ -struct TALER_WIRE_HistoryHandle; +struct TALER_WIRE_CreditHistoryHandle; + +/** + * Handle returned for querying the debit transaction history. + */ +struct TALER_WIRE_DebitHistoryHandle; /** @@ -146,7 +167,8 @@ struct TALER_WIRE_HistoryHandle; * * @param cls closure * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure - * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error + * @param row_id unique ID of the wire transfer in the bank's records; NULL on error + * @param row_id_size number of bytes in @e row_id * @param emsg NULL on success, otherwise an error message */ typedef void @@ -156,6 +178,7 @@ typedef void size_t row_id_size, const char *emsg); + /** * @brief The plugin API, returned from the plugin's "init" function. * The argument given to "init" is simply a configuration handle. @@ -176,11 +199,10 @@ struct TALER_WIRE_Plugin /** * Which wire method (payto://METHOD/") is supported by this plugin? - * For example, "iban" or "x-taler-bank". + * For example, "x-taler-bank" or "iban". */ const char *method; - /** * Round amount DOWN to the amount that can be transferred via the wire * method. For example, Taler may support 0.000001 EUR as a unit of @@ -210,80 +232,7 @@ struct TALER_WIRE_Plugin /** - * Prepare for exeuction of a wire transfer. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param origin_account_section configuration section specifying the origin - * account of the exchange to use - * @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 ptc function to call with the prepared data to persist - * @param ptc_cls closure for @a ptc - * @return NULL on failure - */ - struct TALER_WIRE_PrepareHandle * - (*prepare_wire_transfer) (void *cls, - const char *origin_account_section, - const char *destination_account_url, - const struct TALER_Amount *amount, - const char *exchange_base_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_WIRE_PrepareTransactionCallback ptc, - void *ptc_cls); - - - /** - * Abort preparation of a wire transfer. For example, - * because we are shutting down. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param pth preparation to cancel - */ - void - (*prepare_wire_transfer_cancel) (void *cls, - struct TALER_WIRE_PrepareHandle *pth); - - - /** - * 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_WIRE_ExecuteHandle * - (*execute_wire_transfer) (void *cls, - const char *buf, - size_t buf_size, - TALER_WIRE_ConfirmationCallback cc, - void *cc_cls); - - - /** - * Abort execution of a wire transfer. For example, because we are - * shutting down. Note that if an execution is aborted, it may or - * may not still succeed. The caller MUST run @e - * execute_wire_transfer again for the same request as soon as - * possilbe, to ensure that the request either ultimately succeeds - * or ultimately fails. Until this has been done, the transaction is - * in limbo (i.e. may or may not have been committed). - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param eh execution to cancel - */ - void - (*execute_wire_transfer_cancel) (void *cls, - struct TALER_WIRE_ExecuteHandle *eh); - - - /** - * Query transfer history of an account. We use the variable-size + * Query credits made to exchange account. We use the variable-size * @a start_off to indicate which transfers we are interested in as * different banking systems may have different ways to identify * transfers. The @a start_off value must thus match the value of @@ -295,7 +244,6 @@ struct TALER_WIRE_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param account_section specifies the configuration section which * identifies the account for which we should get the history - * @param direction what kinds of wire transfers should be returned * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive * @param start_off_len number of bytes in @a start_off * @param num_results how many results do we want; negative numbers to go into the past, @@ -304,70 +252,65 @@ struct TALER_WIRE_Plugin * @param hres_cb the callback to call with the transaction history * @param hres_cb_cls closure for the above callback */ - struct TALER_WIRE_HistoryHandle * - (*get_history) (void *cls, + struct TALER_WIRE_CreditHistoryHandle * + (*get_credits) (void *cls, const char *account_section, - enum TALER_BANK_Direction direction, const void *start_off, size_t start_off_len, int64_t num_results, - TALER_WIRE_HistoryResultCallback hres_cb, + TALER_WIRE_CreditResultCallback hres_cb, void *hres_cb_cls); /** * Cancel going over the account's history. * * @param cls plugins' closure - * @param whh operation to cancel + * @param chh operation to cancel */ void - (*get_history_cancel) (void *cls, - struct TALER_WIRE_HistoryHandle *whh); + (*get_credits_cancel) (void *cls, + struct TALER_WIRE_CreditHistoryHandle *chh); /** - * Reject an incoming wire transfer that was obtained from the - * history. This function can be used to transfer funds back to - * the sender if the WTID was malformed (i.e. due to a typo). - * - * Calling `reject_transfer` twice on the same wire transfer should - * be idempotent, i.e. not cause the funds to be wired back twice. - * Furthermore, the transfer should henceforth be removed from the - * results returned by @e get_history. + * Query debits (transfers to merchants) made by an exchange. We use the + * variable-size @a start_off to indicate which transfers we are interested + * in as different banking systems may have different ways to identify + * transfers. The @a start_off value must thus match the value of a + * `row_off` argument previously given to the @a hres_cb. Use NULL to query + * transfers from the beginning of time (with positive @a num_results) or + * from the latest committed transfers (with negative @a num_results). * - * @param cls plugin's closure + * @param cls the @e cls of this struct with the plugin-specific state * @param account_section specifies the configuration section which - * identifies the account to use to reject the transfer - * @param start_off offset of the wire transfer in plugin-specific format + * identifies the account for which we should get the history + * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive * @param start_off_len number of bytes in @a start_off - * @param rej_cb function to call with the result of the operation - * @param rej_cb_cls closure for @a rej_cb - * @return handle to cancel the operation + * @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 */ - struct TALER_WIRE_RejectHandle * - (*reject_transfer)(void *cls, - const char *account_section, - const void *start_off, - size_t start_off_len, - TALER_WIRE_RejectTransferCallback rej_cb, - void *rej_cb_cls); - + struct TALER_WIRE_DebitHistoryHandle * + (*get_debits) (void *cls, + const char *account_section, + const void *start_off, + size_t start_off_len, + int64_t num_results, + TALER_WIRE_DebitResultCallback hres_cb, + void *hres_cb_cls); /** - * Cancel ongoing reject operation. Note that the rejection may still - * proceed. Basically, if this function is called, the rejection may - * have happened or not. This function is usually used during shutdown - * or system upgrades. At a later point, the application must call - * @e reject_transfer again for this wire transfer, unless the - * @e get_history shows that the wire transfer no longer exists. + * Cancel going over the account's history. * * @param cls plugins' closure - * @param rh operation to cancel - * @return closure of the callback of the operation + * @param dhh operation to cancel */ - void * - (*reject_transfer_cancel)(void *cls, - struct TALER_WIRE_RejectHandle *rh); + void + (*get_debits_cancel) (void *cls, + struct TALER_WIRE_DebitHistoryHandle *dhh); + }; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index de0c8935c..ee8389f60 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -103,6 +103,7 @@ libtalertesting_la_SOURCES = \ testing_api_trait_exchange_sig.c \ testing_api_trait_json.c \ testing_api_trait_process.c \ + testing_api_trait_reserve_pub.c \ testing_api_trait_reserve_priv.c \ testing_api_trait_number.c \ testing_api_trait_fresh_coin.c \ diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index abb95816c..6b51519fd 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -1262,7 +1262,7 @@ keys_completed_cb (void *cls, for (unsigned int i = 0; i<kd_old.num_denom_keys; i++) kd.denom_keys[i].key.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup ( - kd_old.denom_keys[i].key.rsa_public_key); + kd_old.denom_keys[i].key.rsa_public_key); kd.num_auditors = kd_old.num_auditors; kd.auditors = GNUNET_new_array (kd.num_auditors, diff --git a/src/lib/testing_api_cmd_bank_check.c b/src/lib/testing_api_cmd_bank_check.c index f51c535a1..f74f7afec 100644 --- a/src/lib/testing_api_cmd_bank_check.c +++ b/src/lib/testing_api_cmd_bank_check.c @@ -52,12 +52,12 @@ struct BankCheckState /** * Expected debit bank account. */ - uint64_t debit_account; + const char *debit_account; /** * Expected credit bank account. */ - uint64_t credit_account; + const char *credit_account; /** * Wire transfer subject (set by fakebank-lib). @@ -95,18 +95,16 @@ check_bank_transfer_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct BankCheckState *bcs = cls; - struct TALER_Amount amount; - const uint64_t *debit_account; - const uint64_t *credit_account; + const char *debit_account; + const char *credit_account; const char *exchange_base_url; - if (NULL == bcs->deposit_reference) { TALER_LOG_INFO ("Deposit reference NOT given\n"); - debit_account = &bcs->debit_account; - credit_account = &bcs->credit_account; + debit_account = bcs->debit_account; + credit_account = bcs->credit_account; exchange_base_url = bcs->exchange_base_url; if (GNUNET_OK != @@ -154,14 +152,13 @@ check_bank_transfer_run (void *cls, GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_url (deposit_cmd, 0, &exchange_base_url)); // check 0 works! - } if (GNUNET_OK != TALER_FAKEBANK_check (is->fakebank, &amount, - *debit_account, - *credit_account, + debit_account, + credit_account, exchange_base_url, &bcs->subject)) { @@ -217,18 +214,18 @@ check_bank_transfer_traits (void *cls, wtid_ptr = NULL; else wtid_ptr = &bcs->wtid; - - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_transfer_subject (0, bcs->subject), - TALER_TESTING_make_trait_wtid (0, wtid_ptr), - TALER_TESTING_make_trait_url (0, bcs->exchange_base_url), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); + { + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_wtid (0, wtid_ptr), + TALER_TESTING_make_trait_url (0, bcs->exchange_base_url), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); + } } @@ -250,8 +247,8 @@ TALER_TESTING_cmd_check_bank_transfer (const char *label, const char *exchange_base_url, const char *amount, - uint64_t debit_account, - uint64_t credit_account) + const char *debit_account, + const char *credit_account) { struct BankCheckState *bcs; @@ -260,18 +257,18 @@ TALER_TESTING_cmd_check_bank_transfer bcs->amount = amount; bcs->debit_account = debit_account; bcs->credit_account = credit_account; - bcs->deposit_reference = NULL; - - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = bcs, - .run = &check_bank_transfer_run, - .cleanup = &check_bank_transfer_cleanup, - .traits = &check_bank_transfer_traits - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .label = label, + .cls = bcs, + .run = &check_bank_transfer_run, + .cleanup = &check_bank_transfer_cleanup, + .traits = &check_bank_transfer_traits + }; + + return cmd; + } } diff --git a/src/lib/testing_api_cmd_fakebank_transfer.c b/src/lib/testing_api_cmd_fakebank_transfer.c index 81378238c..e212fd31f 100644 --- a/src/lib/testing_api_cmd_fakebank_transfer.c +++ b/src/lib/testing_api_cmd_fakebank_transfer.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 @@ -51,24 +51,14 @@ struct FakebankTransferState struct TALER_Amount amount; /** - * Wire transfer subject. + * Base URL of the debit account. */ - const char *subject; + const char *debit_url; /** - * Base URL of the bank serving the request. + * Money receiver account URL. */ - const char *bank_url; - - /** - * Money sender account number. - */ - uint64_t debit_account_no; - - /** - * Money receiver account number. - */ - uint64_t credit_account_no; + const char *payto_credit_account; /** * Username to use for authentication. @@ -87,6 +77,11 @@ struct FakebankTransferState struct TALER_ReservePrivateKeyP reserve_priv; /** + * Reserve public key matching @e reserve_priv. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** * Handle to the pending request at the fakebank. */ struct TALER_BANK_AdminAddIncomingHandle *aih; @@ -188,16 +183,15 @@ do_retry (void *cls) * @param ec taler-specific error code, #TALER_EC_NONE on success * @param serial_id unique ID of the wire transfer * @param timestamp time stamp of the transaction made. - * @param full_response full response from the exchange (for - * logging, in case of errors) + * @param json raw response */ static void -add_incoming_cb (void *cls, +confirmation_cb (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, uint64_t serial_id, struct GNUNET_TIME_Absolute timestamp, - const json_t *full_response) + const json_t *json) { struct FakebankTransferState *fts = cls; struct TALER_TESTING_Interpreter *is = fts->is; @@ -256,130 +250,115 @@ fakebank_transfer_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct FakebankTransferState *fts = cls; - char *subject; struct TALER_BANK_AuthenticationData auth; - struct TALER_ReservePublicKeyP reserve_pub; - if (NULL != fts->subject) + /* Use reserve public key as subject */ + if (NULL != fts->reserve_reference) { - subject = GNUNET_strdup (fts->subject); + const struct TALER_TESTING_Command *ref; + const struct TALER_ReservePrivateKeyP *reserve_priv; + + ref = TALER_TESTING_interpreter_lookup_command + (is, fts->reserve_reference); + if (NULL == ref) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (ref, + 0, + &reserve_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv; } else { - /* Use reserve public key as subject */ - if (NULL != fts->reserve_reference) + if (NULL != fts->instance) { - const struct TALER_TESTING_Command *ref; - const struct TALER_ReservePrivateKeyP *reserve_priv; + char *section; + char *keys; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + struct GNUNET_CONFIGURATION_Handle *cfg; - ref = TALER_TESTING_interpreter_lookup_command - (is, fts->reserve_reference); - if (NULL == ref) + GNUNET_assert (NULL != fts->config_filename); + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (cfg, + fts->config_filename)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } + + GNUNET_asprintf (§ion, + "instance-%s", + fts->instance); if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_priv (ref, - 0, - &reserve_priv)) + GNUNET_CONFIGURATION_get_value_filename + (cfg, + section, + "TIP_RESERVE_PRIV_FILENAME", + &keys)) { - GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Configuration fails to specify reserve" + " private key filename in section %s\n", + section); + GNUNET_free (section); TALER_TESTING_interpreter_fail (is); return; } - fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv; - } - else - { - if (NULL != fts->instance) + priv = GNUNET_CRYPTO_eddsa_key_create_from_file (keys); + GNUNET_free (keys); + if (NULL == priv) { - char *section; - char *keys; - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - struct GNUNET_CONFIGURATION_Handle *cfg; - - GNUNET_assert (NULL != fts->config_filename); - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - fts->config_filename)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); - return; - } - - GNUNET_asprintf (§ion, - "instance-%s", - fts->instance); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename - (cfg, - section, - "TIP_RESERVE_PRIV_FILENAME", - &keys)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Configuration fails to specify reserve" - " private key filename in section %s\n", - section); - GNUNET_free (section); - TALER_TESTING_interpreter_fail (is); - return; - } - priv = GNUNET_CRYPTO_eddsa_key_create_from_file (keys); - GNUNET_free (keys); - if (NULL == priv) - { - GNUNET_log_config_invalid - (GNUNET_ERROR_TYPE_ERROR, - section, - "TIP_RESERVE_PRIV_FILENAME", - "Failed to read private key"); - GNUNET_free (section); - TALER_TESTING_interpreter_fail (is); - return; - } - fts->reserve_priv.eddsa_priv = *priv; + GNUNET_log_config_invalid + (GNUNET_ERROR_TYPE_ERROR, + section, + "TIP_RESERVE_PRIV_FILENAME", + "Failed to read private key"); GNUNET_free (section); - GNUNET_free (priv); - GNUNET_CONFIGURATION_destroy (cfg); + TALER_TESTING_interpreter_fail (is); + return; } - else - { - /* No referenced reserve, no instance to take priv - * from, no explicit subject given: create new key! */ - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + fts->reserve_priv.eddsa_priv = *priv; + GNUNET_free (section); + GNUNET_free (priv); + GNUNET_CONFIGURATION_destroy (cfg); + } + else + { + /* No referenced reserve, no instance to take priv + * from, no explicit subject given: create new key! */ + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - priv = GNUNET_CRYPTO_eddsa_key_create (); - fts->reserve_priv.eddsa_priv = *priv; - GNUNET_free (priv); - } + priv = GNUNET_CRYPTO_eddsa_key_create (); + fts->reserve_priv.eddsa_priv = *priv; + GNUNET_free (priv); } - GNUNET_CRYPTO_eddsa_key_get_public - (&fts->reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub); - subject = GNUNET_STRINGS_data_to_string_alloc - (&reserve_pub, sizeof (reserve_pub)); } - + GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv, + &fts->reserve_pub.eddsa_pub); auth.method = TALER_BANK_AUTH_BASIC; auth.details.basic.username = (char *) fts->auth_username; auth.details.basic.password = (char *) fts->auth_password; fts->is = is; fts->aih = TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context (is), - fts->bank_url, + fts->debit_url, &auth, - fts->exchange_url, - subject, + &fts->reserve_pub, &fts->amount, - fts->debit_account_no, - fts->credit_account_no, - &add_incoming_cb, + fts->payto_credit_account, + &confirmation_cb, fts); - GNUNET_free (subject); if (NULL == fts->aih) { GNUNET_break (0); @@ -408,6 +387,7 @@ fakebank_transfer_cleanup (void *cls, "Command %s did not complete\n", cmd->label); TALER_BANK_admin_add_incoming_cancel (fts->aih); + fts->aih = NULL; } if (NULL != fts->retry_task) { @@ -435,32 +415,21 @@ fakebank_transfer_traits (void *cls, unsigned int index) { struct FakebankTransferState *fts = cls; - #define MANDATORY 7 - struct TALER_TESTING_Trait traits[MANDATORY + 1] = { - TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT - (&fts->debit_account_no), - TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT - (&fts->credit_account_no), + struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_url (0, fts->exchange_url), + TALER_TESTING_make_trait_url (1, fts->debit_url), TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), + TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->payto_credit_account), + TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT (fts->debit_url), TALER_TESTING_make_trait_amount_obj (0, &fts->amount), - TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp) + TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp), + TALER_TESTING_make_trait_reserve_priv (0, + &fts->reserve_priv), + TALER_TESTING_make_trait_reserve_pub (0, + &fts->reserve_pub), + TALER_TESTING_trait_end () }; - /** - * The user gave explicit subject, - * there must be NO reserve priv. */ - if (NULL != fts->subject) - traits[MANDATORY - 1] = - TALER_TESTING_make_trait_transfer_subject (0, - fts->subject); - /* A reserve priv must exist if no subject was given. */ - else - traits[MANDATORY - 1] = TALER_TESTING_make_trait_reserve_priv - (0, &fts->reserve_priv), - - traits[MANDATORY] = TALER_TESTING_trait_end (); - return TALER_TESTING_get_trait (traits, ret, trait, @@ -475,27 +444,21 @@ fakebank_transfer_traits (void *cls, * * @param label command label. * @param amount amount to transfer. - * @param bank_url base URL of the bank that implements this - * wire transer. For simplicity, both credit and debit - * bank account exist at the same bank. - * @param debit_account_no which account (expressed as a number) - * gives money. - * @param credit_account_no which account (expressed as a number) - * receives money. + * @param account_base_url base URL of the account that implements this + * wire transer (which account gives money). + * @param payto_credit_account which account receives money. * @param auth_username username identifying the @a * debit_account_no at the bank. * @param auth_password password for @a auth_username. * @param exchange_url which exchange is involved in this transfer. - * * @return the command. */ struct TALER_TESTING_Command TALER_TESTING_cmd_fakebank_transfer (const char *label, const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, + const char *account_base_url, + const char *payto_credit_account, const char *auth_username, const char *auth_password, const char *exchange_url) @@ -503,9 +466,8 @@ TALER_TESTING_cmd_fakebank_transfer struct FakebankTransferState *fts; fts = GNUNET_new (struct FakebankTransferState); - fts->bank_url = bank_url; - fts->credit_account_no = credit_account_no; - fts->debit_account_no = debit_account_no; + fts->debit_url = account_base_url; + fts->payto_credit_account = payto_credit_account; fts->auth_username = auth_username; fts->auth_password = auth_password; fts->exchange_url = exchange_url; @@ -520,87 +482,17 @@ TALER_TESTING_cmd_fakebank_transfer GNUNET_assert (0); } - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &fakebank_transfer_run, - .cleanup = &fakebank_transfer_cleanup, - .traits = &fakebank_transfer_traits - }; - - return cmd; -} - - -/** - * Create "fakebank transfer" CMD, letting the caller specifying - * the subject line. - * - * @param label command label. - * @param amount amount to transfer. - * @param bank_url base URL of the bank that implements this - * wire transer. For simplicity, both credit and debit - * bank account exist at the same bank. - * @param debit_account_no which account (expressed as a number) - * gives money. - * @param credit_account_no which account (expressed as a number) - * receives money. - * - * @param auth_username username identifying the @a - * debit_account_no at the bank. - * @param auth_password password for @a auth_username. - * @param subject wire transfer's subject line. - * @param exchange_url which exchange is involved in this transfer. - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_fakebank_transfer_with_subject - (const char *label, - const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, - const char *auth_username, - const char *auth_password, - const char *subject, - const char *exchange_url) -{ - struct FakebankTransferState *fts; - - fts = GNUNET_new (struct FakebankTransferState); - - TALER_LOG_DEBUG ("%s:FTS@%p\n", - label, - fts); - - fts->bank_url = bank_url; - fts->credit_account_no = credit_account_no; - fts->debit_account_no = debit_account_no; - fts->auth_username = auth_username; - fts->auth_password = auth_password; - fts->subject = subject; - fts->exchange_url = exchange_url; - if (GNUNET_OK != - TALER_string_to_amount (amount, - &fts->amount)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %s\n", - amount, - label); - GNUNET_assert (0); + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &fakebank_transfer_run, + .cleanup = &fakebank_transfer_cleanup, + .traits = &fakebank_transfer_traits + }; + + return cmd; } - - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &fakebank_transfer_run, - .cleanup = &fakebank_transfer_cleanup, - .traits = &fakebank_transfer_traits - }; - - return cmd; } @@ -631,9 +523,8 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, + const char *account_base_url, + const char *payto_credit_account, const char *auth_username, const char *auth_password, const char *ref, @@ -642,9 +533,8 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref struct FakebankTransferState *fts; fts = GNUNET_new (struct FakebankTransferState); - fts->bank_url = bank_url; - fts->credit_account_no = credit_account_no; - fts->debit_account_no = debit_account_no; + fts->debit_url = account_base_url; + fts->payto_credit_account = payto_credit_account; fts->auth_username = auth_username; fts->auth_password = auth_password; fts->reserve_reference = ref; @@ -659,16 +549,17 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref label); GNUNET_assert (0); } - - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &fakebank_transfer_run, - .cleanup = &fakebank_transfer_cleanup, - .traits = &fakebank_transfer_traits - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &fakebank_transfer_run, + .cleanup = &fakebank_transfer_cleanup, + .traits = &fakebank_transfer_traits + }; + + return cmd; + } } @@ -705,9 +596,8 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_fakebank_transfer_with_instance (const char *label, const char *amount, - const char *bank_url, - uint64_t debit_account_no, - uint64_t credit_account_no, + const char *account_base_url, + const char *payto_credit_account, const char *auth_username, const char *auth_password, const char *instance, @@ -717,9 +607,8 @@ TALER_TESTING_cmd_fakebank_transfer_with_instance struct FakebankTransferState *fts; fts = GNUNET_new (struct FakebankTransferState); - fts->bank_url = bank_url; - fts->credit_account_no = credit_account_no; - fts->debit_account_no = debit_account_no; + fts->debit_url = account_base_url; + fts->payto_credit_account = payto_credit_account; fts->auth_username = auth_username; fts->auth_password = auth_password; fts->instance = instance; @@ -735,16 +624,17 @@ TALER_TESTING_cmd_fakebank_transfer_with_instance label); GNUNET_assert (0); } - - struct TALER_TESTING_Command cmd = { - .cls = fts, - .label = label, - .run = &fakebank_transfer_run, - .cleanup = &fakebank_transfer_cleanup, - .traits = &fakebank_transfer_traits - }; - - return cmd; + { + struct TALER_TESTING_Command cmd = { + .cls = fts, + .label = label, + .run = &fakebank_transfer_run, + .cleanup = &fakebank_transfer_cleanup, + .traits = &fakebank_transfer_traits + }; + + return cmd; + } } diff --git a/src/lib/testing_api_cmd_status.c b/src/lib/testing_api_cmd_status.c index 398221a10..2da404fb8 100644 --- a/src/lib/testing_api_cmd_status.c +++ b/src/lib/testing_api_cmd_status.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-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 @@ -146,6 +146,7 @@ status_run (void *cls, const struct TALER_TESTING_Command *create_reserve; const struct TALER_ReservePrivateKeyP *reserve_priv; struct TALER_ReservePublicKeyP reserve_pub; + const struct TALER_ReservePublicKeyP *reserve_pubp; ss->is = is; GNUNET_assert (NULL != ss->reserve_reference); @@ -163,44 +164,31 @@ status_run (void *cls, /* NOTE: the following line might generate a ERROR log * statements, but it can be ignored. */ - if (GNUNET_OK == TALER_TESTING_get_trait_reserve_priv - (create_reserve, - 0, - &reserve_priv)) + if (GNUNET_OK == + TALER_TESTING_get_trait_reserve_priv (create_reserve, + 0, + &reserve_priv)) { GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, &reserve_pub.eddsa_pub); + reserve_pubp = &reserve_pub; } else { - const char *transfer_subject; - - if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject - (create_reserve, - 0, - &transfer_subject)) - { - GNUNET_break (0); - TALER_LOG_ERROR ("The reserve has neither a priv nor a subject line.\n"); - TALER_TESTING_interpreter_fail (is); - return; - } - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (transfer_subject, - strlen (transfer_subject), - &reserve_pub.eddsa_pub, - sizeof (struct TALER_ReservePublicKeyP))) + TALER_TESTING_get_trait_reserve_pub (create_reserve, + 0, + &reserve_pubp)) { GNUNET_break (0); - TALER_LOG_ERROR ("Transfer subject is not a public key.\n"); + TALER_LOG_ERROR ("The reserve has neither a priv nor a pub.\n"); TALER_TESTING_interpreter_fail (is); return; } } ss->rsh = TALER_EXCHANGE_reserve_status (is->exchange, - &reserve_pub, + reserve_pubp, &reserve_status_cb, ss); } diff --git a/src/lib/testing_api_cmd_track.c b/src/lib/testing_api_cmd_track.c index 41c6be171..070218cfa 100644 --- a/src/lib/testing_api_cmd_track.c +++ b/src/lib/testing_api_cmd_track.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-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 @@ -190,15 +190,11 @@ deposit_wtid_cb if (NULL != tts->bank_transfer_reference) { const struct TALER_TESTING_Command *bank_transfer_cmd; - char *ws; + const struct TALER_WireTransferIdentifierRawP *wtid_want; /* _this_ wire transfer subject line. */ - ws = GNUNET_STRINGS_data_to_string_alloc (wtid, - sizeof (*wtid)); - bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command (is, tts->bank_transfer_reference); - if (NULL == bank_transfer_cmd) { GNUNET_break (0); @@ -206,12 +202,9 @@ deposit_wtid_cb return; } - /* expected wire transfer subject line. */ - const char *transfer_subject; - if (GNUNET_OK != - TALER_TESTING_get_trait_transfer_subject - (bank_transfer_cmd, 0, &transfer_subject)) + TALER_TESTING_get_trait_wtid + (bank_transfer_cmd, 0, &wtid_want)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); @@ -219,15 +212,13 @@ deposit_wtid_cb } /* Compare that expected and gotten subjects match. */ - if (0 != strcmp (ws, transfer_subject)) + if (0 != GNUNET_memcmp (wtid, + wtid_want)) { GNUNET_break (0); - GNUNET_free (ws); TALER_TESTING_interpreter_fail (tts->is); return; } - - GNUNET_free (ws); } break; case MHD_HTTP_ACCEPTED: diff --git a/src/lib/testing_api_trait_reserve_pub.c b/src/lib/testing_api_trait_reserve_pub.c new file mode 100644 index 000000000..b9cd070d3 --- /dev/null +++ b/src/lib/testing_api_trait_reserve_pub.c @@ -0,0 +1,77 @@ +/* + This file is part of TALER + Copyright (C) 2018-2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file exchange-lib/testing_api_trait_reserve_pub.c + * @brief implements reserve public key trait + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + +#define TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY \ + "reserve-public-key" + +/** + * Obtain a reserve public key from a @a cmd. + * + * @param cmd command to extract the reserve pub from. + * @param index reserve pub's index number. + * @param reserve_pub[out] set to the reserve pub. + * @return #GNUNET_OK on success. + */ +int +TALER_TESTING_get_trait_reserve_pub + (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const struct TALER_ReservePublicKeyP **reserve_pub) +{ + return cmd->traits (cmd->cls, + (const void **) reserve_pub, + TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY, + index); +} + + +/** + * Offer a reserve public key. + * + * @param index reserve pub's index number. + * @param reserve_pub reserve public key to offer. + * @return the trait. + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_reserve_pub + (unsigned int index, + const struct TALER_ReservePublicKeyP *reserve_pub) +{ + struct TALER_TESTING_Trait ret = { + .index = index, + .trait_name = TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY, + .ptr = (const void *) reserve_pub + }; + return ret; +} + + +/* end of testing_api_trait_reserve_pub.c */ diff --git a/src/lib/testing_api_trait_string.c b/src/lib/testing_api_trait_string.c index 8d3d5df04..b9f57ab78 100644 --- a/src/lib/testing_api_trait_string.c +++ b/src/lib/testing_api_trait_string.c @@ -30,7 +30,7 @@ #include "taler_testing_lib.h" #define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms" -#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject" +#define TALER_TESTING_TRAIT_STRING "string" #define TALER_TESTING_TRAIT_AMOUNT "amount" #define TALER_TESTING_TRAIT_URL "url" #define TALER_TESTING_TRAIT_ORDER_ID "order-id" @@ -80,46 +80,45 @@ TALER_TESTING_make_trait_contract_terms /** - * Obtain a transfer subject from @a cmd. + * Obtain a string from @a cmd. * * @param cmd command to extract the subject from. * @param index index number associated with the transfer * subject to offer. - * @param transfer_subject[out] where to write the offered - * transfer subject. + * @param s[out] where to write the offered + * string * @return #GNUNET_OK on success. */ int -TALER_TESTING_get_trait_transfer_subject +TALER_TESTING_get_trait_string (const struct TALER_TESTING_Command *cmd, unsigned int index, - const char **transfer_subject) + const char **s) { return cmd->traits (cmd->cls, - (const void **) transfer_subject, - TALER_TESTING_TRAIT_TRANSFER_SUBJECT, + (const void **) s, + TALER_TESTING_TRAIT_STRING, index); } /** - * Offer transfer subject. + * Offer string. * * @param index index number associated with the transfer * subject being offered. - * @param transfer_subject transfer subject to offer. - * + * @param s transfer subject to offer. * @return the trait. */ struct TALER_TESTING_Trait -TALER_TESTING_make_trait_transfer_subject +TALER_TESTING_make_trait_string (unsigned int index, - const char *transfer_subject) + const char *s) { struct TALER_TESTING_Trait ret = { .index = index, - .trait_name = TALER_TESTING_TRAIT_TRANSFER_SUBJECT, - .ptr = (const void *) transfer_subject + .trait_name = TALER_TESTING_TRAIT_STRING, + .ptr = (const void *) s }; return ret; } diff --git a/src/wire-plugins/Makefile.am b/src/wire-plugins/Makefile.am deleted file mode 100644 index 6d5b3e8be..000000000 --- a/src/wire-plugins/Makefile.am +++ /dev/null @@ -1,81 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -pkgcfgdir = $(prefix)/share/taler/config.d/ - -EXTRA_DIST = \ - test_wire_plugin.conf \ - test_wire_plugin_transactions_taler-bank.conf - -plugindir = $(libdir)/taler - -if HAVE_LIBCURL -plugin_LTLIBRARIES = \ - libtaler_plugin_wire_taler_bank.la -else -if HAVE_LIBGNURL -plugin_LTLIBRARIES = \ - libtaler_plugin_wire_taler_bank.la -endif -endif - -noinst_LTLIBRARIES = \ - libtaler_plugin_wire_template.la - - -libtaler_plugin_wire_taler_bank_la_SOURCES = \ - plugin_wire_taler-bank.c -libtaler_plugin_wire_taler_bank_la_LIBADD = \ - $(LTLIBINTL) -libtaler_plugin_wire_taler_bank_la_LDFLAGS = \ - $(TALER_PLUGIN_LDFLAGS) \ - $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/wire/libtalerwire.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetcurl \ - -lgnunetutil $(XLIB) - - -libtaler_plugin_wire_template_la_SOURCES = \ - plugin_wire_template.c -libtaler_plugin_wire_template_la_LIBADD = \ - $(LTLIBINTL) -libtaler_plugin_wire_template_la_LDFLAGS = \ - $(TALER_PLUGIN_LDFLAGS) \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil $(XLIB) - - -AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; - -TESTS = \ - test_wire_plugin \ - test_wire_plugin_transactions_taler_bank - -check_PROGRAMS= $(TESTS) - - -test_wire_plugin_SOURCES = \ - test_wire_plugin.c -test_wire_plugin_LDADD = \ - -lgnunetutil \ - $(top_builddir)/src/wire/libtalerwire.la \ - $(top_builddir)/src/util/libtalerutil.la - - -test_wire_plugin_transactions_taler_bank_SOURCES = \ - test_wire_plugin_transactions_taler-bank.c -test_wire_plugin_transactions_taler_bank_LDADD = \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - $(top_builddir)/src/wire/libtalerwire.la \ - $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/bank-lib/libtalerfakebank.la \ - $(top_builddir)/src/util/libtalerutil.la diff --git a/src/wire-plugins/plugin_wire_taler-bank.c b/src/wire-plugins/plugin_wire_taler-bank.c deleted file mode 100644 index ad8daa4ef..000000000 --- a/src/wire-plugins/plugin_wire_taler-bank.c +++ /dev/null @@ -1,1306 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2017, 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 plugin_wire_taler_bank.c - * @brief plugin for the "x-taler-bank" wire method - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_wire_plugin.h" -#include "taler_json_lib.h" -#include "taler_wire_lib.h" -#include "taler_bank_service.h" -#include "taler_signatures.h" -#include <gnunet/gnunet_curl_lib.h> - -/* only for HTTP status codes */ -#include <microhttpd.h> - -/** - * Type of the "cls" argument given to each of the functions in - * our API. - */ -struct TalerBankClosure -{ - - /** - * Which currency do we support? - */ - char *currency; - - /** - * Handle to the context for sending funds to the bank. - */ - struct GNUNET_CURL_Context *ctx; - - /** - * Scheduler context for running the @e ctx. - */ - struct GNUNET_CURL_RescheduleContext *rc; - - /** - * Configuration we use to lookup account information. - */ - struct GNUNET_CONFIGURATION_Handle *cfg; - -}; - - -/** - * Handle returned by #taler_bank_prepare_wire_transfer. - */ -struct TALER_WIRE_PrepareHandle -{ - - /** - * Task we use for async execution. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * TalerBank closure we run in. - */ - struct TalerBankClosure *tc; - - /** - * Authentication information. - */ - struct TALER_BANK_AuthenticationData auth; - - /** - * Which account should be debited? Given as the respective - * section in the configuration file. - */ - char *origin_account_url; - - /** - * Which account should be credited? - */ - char *destination_account_url; - - /** - * Base URL to use for the exchange. - */ - char *exchange_base_url; - - /** - * Function to call with the serialized data. - */ - TALER_WIRE_PrepareTransactionCallback ptc; - - /** - * Closure for @e ptc. - */ - void *ptc_cls; - - /** - * Amount to transfer. - */ - struct TALER_Amount amount; - - /** - * Subject of the wire transfer. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - -}; - - -/** - * Handle returned by #taler_bank_execute_wire_transfer. - */ -struct TALER_WIRE_ExecuteHandle -{ - - /** - * Handle to the HTTP request to the bank. - */ - struct TALER_BANK_AdminAddIncomingHandle *aaih; - - /** - * Function to call with the result. - */ - TALER_WIRE_ConfirmationCallback cc; - - /** - * Closure for @e cc. - */ - void *cc_cls; -}; - - -/** - * Round amount DOWN to the amount that can be transferred via the wire - * method. For example, Taler may support 0.000001 EUR as a unit of - * payment, but SEPA only supports 0.01 EUR. This function would - * round 0.125 EUR to 0.12 EUR in this case. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param[in,out] amount amount to round down - * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, - * #GNUNET_SYSERR if the amount or currency was invalid - */ -static int -taler_bank_amount_round (void *cls, - struct TALER_Amount *amount) -{ - struct TalerBankClosure *tc = cls; - uint32_t delta; - - if (NULL == tc->currency) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler", - "CURRENCY"); - return GNUNET_SYSERR; /* not configured with currency */ - } - if (0 != strcasecmp (amount->currency, - tc->currency)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* 'taler_bank' method supports 1/100 of the unit currency, i.e. 0.01 CUR */ - delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100); - if (0 == delta) - return GNUNET_NO; - amount->fraction -= delta; - return GNUNET_OK; -} - - -/** - * Check if the given payto:// URL is correctly formatted. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param account_url an account URL - * @return #TALER_EC_NONE if correctly formatted - */ -static enum TALER_ErrorCode -taler_bank_wire_validate (void *cls, - const char *account_url) -{ - (void) cls; - struct TALER_Account acc; - enum TALER_ErrorCode ec; - - ec = TALER_WIRE_payto_to_account (account_url, - &acc); - if (TALER_EC_NONE == ec) - { - if (TALER_PAC_X_TALER_BANK != acc.type) - ec = TALER_EC_PAYTO_WRONG_METHOD; - TALER_WIRE_account_free (&acc); - } - return ec; -} - - -GNUNET_NETWORK_STRUCT_BEGIN -/** - * Format we used for serialized transaction data. - */ -struct BufFormatP -{ - - /** - * The wire transfer identifier. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * The amount. - */ - struct TALER_AmountNBO amount; - - /* followed by 0-terminated origin account URL */ - - /* followed by 0-terminated destination account URL */ - - /* followed by 0-terminated exchange base URL */ - - /* optionally followed by 0-terminated origin username URL */ - - /* optionally followed by 0-terminated origin password URL */ - -}; -GNUNET_NETWORK_STRUCT_END - - -/** - * Abort preparation of a wire transfer. For example, - * because we are shutting down. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param pth preparation to cancel - */ -static void -taler_bank_prepare_wire_transfer_cancel (void *cls, - struct TALER_WIRE_PrepareHandle *pth) -{ - (void) cls; - if (NULL != pth->task) - GNUNET_SCHEDULER_cancel (pth->task); - TALER_BANK_auth_free (&pth->auth); - GNUNET_free (pth->origin_account_url); - GNUNET_free (pth->destination_account_url); - GNUNET_free (pth->exchange_base_url); - GNUNET_free (pth); -} - - -/** - * Prepare for exeuction of a wire transfer. Calls the - * callback with the serialized state. - * - * @param cls the `struct TALER_WIRE_PrepareHandle` - */ -static void -do_prepare (void *cls) -{ - struct TALER_WIRE_PrepareHandle *pth = cls; - size_t len_i; - size_t len_o; - size_t len_au; - size_t len_ap; - size_t len_b; - struct BufFormatP bf; - - pth->task = NULL; - /* serialize the state into a 'buf' */ - len_o = strlen (pth->origin_account_url) + 1; - len_i = strlen (pth->destination_account_url) + 1; - len_b = strlen (pth->exchange_base_url) + 1; - len_au = 0; - len_ap = 0; - switch (pth->auth.method) - { - case TALER_BANK_AUTH_NONE: - break; - case TALER_BANK_AUTH_BASIC: - len_au = strlen (pth->auth.details.basic.username) + 1; - len_ap = strlen (pth->auth.details.basic.password) + 1; - break; - } - bf.wtid = pth->wtid; - TALER_amount_hton (&bf.amount, - &pth->amount); - { - char buf[sizeof (struct BufFormatP) + len_o + len_i + len_b + len_au - + len_ap]; - - memcpy (buf, - &bf, - sizeof (struct BufFormatP)); - memcpy (&buf[sizeof (struct BufFormatP)], - pth->origin_account_url, - len_o); - memcpy (&buf[sizeof (struct BufFormatP) + len_o], - pth->destination_account_url, - len_i); - memcpy (&buf[sizeof (struct BufFormatP) + len_o + len_i], - pth->exchange_base_url, - len_b); - switch (pth->auth.method) - { - case TALER_BANK_AUTH_NONE: - break; - case TALER_BANK_AUTH_BASIC: - memcpy (&buf[sizeof (struct BufFormatP) + len_o + len_i + len_b], - pth->auth.details.basic.username, - len_au); - memcpy (&buf[sizeof (struct BufFormatP) + len_o + len_i + len_b + len_au], - pth->auth.details.basic.password, - len_ap); - break; - } - /* finally give the state back */ - pth->ptc (pth->ptc_cls, - buf, - sizeof (buf)); - } - taler_bank_prepare_wire_transfer_cancel (NULL, - pth); -} - - -/** - * Parse account configuration from @a cfg in @a section into @a account. - * Obtains the URL option and initializes @a account from it. - * - * @param cfg configuration to parse - * @param section section with the account configuration - * @param account[out] account information to initialize - * @return #GNUNET_OK on success - */ -static int -parse_account_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - struct TALER_Account *account) -{ - 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, - account)) - { - 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 != account->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 (account); - return GNUNET_SYSERR; - } - GNUNET_free (account_url); - return GNUNET_OK; -} - - -/** - * Prepare for exeuction of a wire transfer. Note that we should call - * @a ptc asynchronously (as that is what the API requires, because - * some transfer methods need it). So while we could immediately call - * @a ptc, we first bundle up all the data and schedule a task to do - * the work. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param origin_account_section configuration section specifying the origin - * account of the exchange to use - * @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 - * @param wtid wire transfer identifier to use - * @param ptc function to call with the prepared data to persist - * @param ptc_cls closure for @a ptc - * @return NULL on failure - */ -static struct TALER_WIRE_PrepareHandle * -taler_bank_prepare_wire_transfer (void *cls, - const char *origin_account_section, - const char *destination_account_url, - const struct TALER_Amount *amount, - const char *exchange_base_url, - const struct - TALER_WireTransferIdentifierRawP *wtid, - TALER_WIRE_PrepareTransactionCallback ptc, - void *ptc_cls) -{ - struct TalerBankClosure *tc = cls; - struct TALER_WIRE_PrepareHandle *pth; - char *origin_account_url; - struct TALER_Account a_in; - struct TALER_Account a_out; - - /* Check that payto:// URLs are valid */ - if (TALER_EC_NONE != - TALER_WIRE_payto_to_account (destination_account_url, - &a_out)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "payto://-URL `%s' is invalid!\n", - destination_account_url); - return NULL; - } - if (TALER_PAC_X_TALER_BANK != a_out.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "payto://-URL `%s' is invalid!\n", - destination_account_url); - TALER_WIRE_account_free (&a_out); - return NULL; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (tc->cfg, - origin_account_section, - "URL", - &origin_account_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - origin_account_section, - "URL"); - TALER_WIRE_account_free (&a_out); - return NULL; - } - if (TALER_EC_NONE != - TALER_WIRE_payto_to_account (origin_account_url, - &a_in)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - origin_account_section, - "URL", - "Malformed payto:// URL for x-taler-bank method"); - GNUNET_free (origin_account_url); - TALER_WIRE_account_free (&a_out); - return NULL; - } - if (TALER_PAC_X_TALER_BANK != a_in.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "payto://-URL `%s' is invalid!\n", - origin_account_url); - GNUNET_free (origin_account_url); - TALER_WIRE_account_free (&a_in); - TALER_WIRE_account_free (&a_out); - return NULL; - } - - /* Make sure the bank is the same! */ - if (0 != strcasecmp (a_in.details.x_taler_bank.hostname, - a_out.details.x_taler_bank.hostname)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "x-taler-bank hostname missmatch: `%s' != `%s'\n", - a_in.details.x_taler_bank.hostname, - a_out.details.x_taler_bank.hostname); - TALER_WIRE_account_free (&a_in); - TALER_WIRE_account_free (&a_out); - GNUNET_free (origin_account_url); - return NULL; - } - TALER_WIRE_account_free (&a_in); - TALER_WIRE_account_free (&a_out); - - pth = GNUNET_new (struct TALER_WIRE_PrepareHandle); - if (GNUNET_OK != - TALER_BANK_auth_parse_cfg (tc->cfg, - origin_account_section, - &pth->auth)) - { - GNUNET_free (pth); - GNUNET_free (origin_account_url); - return NULL; - } - - pth->tc = tc; - pth->origin_account_url = origin_account_url; - pth->destination_account_url = GNUNET_strdup (destination_account_url); - pth->exchange_base_url = GNUNET_strdup (exchange_base_url); - pth->wtid = *wtid; - pth->ptc = ptc; - pth->ptc_cls = ptc_cls; - pth->amount = *amount; - pth->task = GNUNET_SCHEDULER_add_now (&do_prepare, - pth); - return pth; -} - - -/** - * Called with the result of submitting information about an incoming - * transaction to a bank. - * - * @param cls closure with the `struct TALER_WIRE_ExecuteHandle` - * @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) - * @param ec error code from the bank - * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error - * @param timestamp time when the transfer was settled by the bank. - * @param json detailed response from the HTTPD, or NULL if reply was not JSON - */ -static void -execute_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t serial_id, - struct GNUNET_TIME_Absolute timestamp, - const json_t *json) -{ - struct TALER_WIRE_ExecuteHandle *eh = cls; - json_t *reason; - const char *emsg; - char *s; - uint64_t serial_id_nbo; - - (void) timestamp; - eh->aaih = NULL; - emsg = NULL; - if (NULL != json) - { - reason = json_object_get (json, - "reason"); - if (NULL != reason) - emsg = json_string_value (reason); - } - if (NULL != emsg) - GNUNET_asprintf (&s, - "%u/%u (%s)", - http_status, - (unsigned int) ec, - emsg); - else - GNUNET_asprintf (&s, - "%u/%u", - http_status, - (unsigned int) ec); - serial_id_nbo = GNUNET_htonll (serial_id); - eh->cc (eh->cc_cls, - (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR, - &serial_id_nbo, - sizeof (uint64_t), - (MHD_HTTP_OK == http_status) ? NULL : s); - GNUNET_free (s); - GNUNET_free (eh); -} - - -/** - * 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 - */ -static struct TALER_WIRE_ExecuteHandle * -taler_bank_execute_wire_transfer (void *cls, - const char *buf, - size_t buf_size, - TALER_WIRE_ConfirmationCallback cc, - void *cc_cls) -{ - struct TalerBankClosure *tc = cls; - struct TALER_WIRE_ExecuteHandle *eh; - struct TALER_Amount amount; - struct TALER_Account origin_account; - struct TALER_Account destination_account; - struct BufFormatP bf; - const char *exchange_base_url; - const char *origin_account_url; - const char *destination_account_url; - struct TALER_BANK_AuthenticationData auth; - size_t left; - size_t slen; - char *wire_s; - - if (NULL == tc->ctx) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Bank not initialized, cannot do transfers!\n"); - return NULL; /* not initialized with configuration, cannot do transfers */ - } - if ( (buf_size <= sizeof (struct BufFormatP)) || - ('\0' != buf[buf_size - 1]) ) - { - GNUNET_break (0); - return NULL; - } - memcpy (&bf, - buf, - sizeof (bf)); - TALER_amount_ntoh (&amount, - &bf.amount); - origin_account_url = &buf[sizeof (struct BufFormatP)]; - left = buf_size - sizeof (struct BufFormatP); - slen = strlen (origin_account_url) + 1; - /* make sure there's enough space to accomodate what's been taken now */ - GNUNET_assert (left >= slen); - left -= slen; - if (0 == left) - { - GNUNET_break (0); - return NULL; - } - destination_account_url = &origin_account_url[slen]; - slen = strlen (destination_account_url) + 1; - GNUNET_assert (left >= slen); - left -= slen; - if (0 == left) - { - GNUNET_break (0); - return NULL; - } - exchange_base_url = &destination_account_url[slen]; - slen = strlen (exchange_base_url) + 1; - GNUNET_assert (left >= slen); - left -= slen; - if (0 == left) - { - auth.method = TALER_BANK_AUTH_NONE; - } - else - { - auth.method = TALER_BANK_AUTH_BASIC; - auth.details.basic.username = (char *) &exchange_base_url[slen]; - slen = strlen (auth.details.basic.username) + 1; - GNUNET_assert (left >= slen); - left -= slen; - if (0 == left) - { - GNUNET_break (0); - return NULL; - } - auth.details.basic.password = &auth.details.basic.username[slen]; - slen = strlen (auth.details.basic.password) + 1; - GNUNET_assert (left >= slen); - left -= slen; - if (0 != left) - { - GNUNET_break (0); - return NULL; - } - } - - if (TALER_EC_NONE != - TALER_WIRE_payto_to_account (origin_account_url, - &origin_account)) - { - GNUNET_break (0); - return NULL; - } - if (TALER_EC_NONE != - TALER_WIRE_payto_to_account (destination_account_url, - &destination_account)) - { - TALER_WIRE_account_free (&origin_account); - GNUNET_break (0); - return NULL; - } - if ( (TALER_PAC_X_TALER_BANK != origin_account.type) || - (TALER_PAC_X_TALER_BANK != destination_account.type) || - (0 != strcasecmp (origin_account.details.x_taler_bank.hostname, - destination_account.details.x_taler_bank.hostname)) ) - { - GNUNET_break (0); - TALER_WIRE_account_free (&origin_account); - TALER_WIRE_account_free (&destination_account); - return NULL; - } - - eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle); - eh->cc = cc; - eh->cc_cls = cc_cls; - wire_s = GNUNET_STRINGS_data_to_string_alloc (&bf.wtid, - sizeof (bf.wtid)); - eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx, - origin_account.details.x_taler_bank. - bank_base_url, - &auth, - exchange_base_url, - wire_s, - &amount, - (uint64_t) origin_account.details. - x_taler_bank.no, - (uint64_t) destination_account. - details.x_taler_bank.no, - &execute_cb, - eh); - TALER_WIRE_account_free (&origin_account); - TALER_WIRE_account_free (&destination_account); - GNUNET_free (wire_s); - if (NULL == eh->aaih) - { - GNUNET_break (0); - GNUNET_free (eh); - return NULL; - } - return eh; -} - - -/** - * Abort execution of a wire transfer. For example, because we are - * shutting down. Note that if an execution is aborted, it may or - * may not still succeed. The caller MUST run @e - * execute_wire_transfer again for the same request as soon as - * possilbe, to ensure that the request either ultimately succeeds - * or ultimately fails. Until this has been done, the transaction is - * in limbo (i.e. may or may not have been committed). - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param eh execution to cancel - */ -static void -taler_bank_execute_wire_transfer_cancel (void *cls, - struct TALER_WIRE_ExecuteHandle *eh) -{ - (void) cls; - TALER_BANK_admin_add_incoming_cancel (eh->aaih); - GNUNET_free (eh); -} - - -/** - * Handle for a #taler_bank_get_history() request. - */ -struct TALER_WIRE_HistoryHandle -{ - - /** - * Function to call with results, can become NULL if the - * application cancels the iteration. - */ - TALER_WIRE_HistoryResultCallback hres_cb; - - /** - * Closure for @e hres_cb. - */ - void *hres_cb_cls; - - /** - * Request to the bank. - */ - struct TALER_BANK_HistoryHandle *hh; - - /** - * Authentication to use for access. - */ - struct TALER_BANK_AuthenticationData auth; - -}; - - -/** - * Cancel going over the account's history. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param whh operation to cancel - */ -static void -taler_bank_get_history_cancel (void *cls, - struct TALER_WIRE_HistoryHandle *whh) -{ - (void) cls; - if (NULL != whh->hh) - { - TALER_BANK_history_cancel (whh->hh); - whh->hh = NULL; - } - TALER_BANK_auth_free (&whh->auth); - GNUNET_free (whh); -} - - -/** - * Function called with results from the bank - * about the transaction history. - * - * @param cls the `struct TALER_WIRE_HistoryHandle` - * @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 error code - * @param dir direction of the transfer - * @param serial_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 - */ -static void -bhist_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - uint64_t serial_id, - const struct TALER_BANK_TransferDetails *details, - const json_t *json) -{ - struct TALER_WIRE_HistoryHandle *whh = cls; - uint64_t bserial_id = GNUNET_htonll (serial_id); - struct TALER_WIRE_TransferDetails wd; - - (void) json; - switch (http_status) - { - case MHD_HTTP_OK: - { - char *subject; - char *space; - - wd.amount = details->amount; - wd.execution_date = details->execution_date; - subject = GNUNET_strdup (details->wire_transfer_subject); - space = strchr (subject, - (unsigned char) ' '); - if (NULL != space) - { - /* Space separates the actual wire transfer subject - from the exchange base URL (if present, expected - only for outgoing transactions). So we cut the - string off at the space. */ - *space = '\0'; - } - /* NOTE: For a real bank, the subject should include a - checksum! */ - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (subject, - strlen (subject), - &wd.wtid, - sizeof (wd.wtid))) - { - /* Ill-formed wire subject, set binary version to all zeros - and pass as a string, this time including the part after - the space. */ - memset (&wd.wtid, - 0, - sizeof (wd.wtid)); - wd.wtid_s = details->wire_transfer_subject; - } - else - { - wd.wtid_s = NULL; - } - GNUNET_free (subject); - wd.account_url = details->account_url; - if ( (NULL != whh->hres_cb) && - (GNUNET_OK != - whh->hres_cb (whh->hres_cb_cls, - TALER_EC_NONE, - dir, - &bserial_id, - sizeof (bserial_id), - &wd)) ) - whh->hres_cb = NULL; - GNUNET_break (NULL != whh->hh); - /* Once we get the sentinel element, the handle becomes invalid. */ - if (TALER_BANK_DIRECTION_NONE == dir) - { - whh->hh = NULL; - taler_bank_get_history_cancel (NULL, - whh); - } - return; - } - case MHD_HTTP_NO_CONTENT: - if (NULL != whh->hres_cb) - (void) whh->hres_cb (whh->hres_cb_cls, - ec, - TALER_BANK_DIRECTION_NONE, - NULL, - 0, - NULL); - whh->hh = NULL; - taler_bank_get_history_cancel (NULL, - whh); - return; - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Bank failed with HTTP status %u (EC: %u)\n", - http_status, - ec); - if (NULL != whh->hres_cb) - (void) whh->hres_cb (whh->hres_cb_cls, - ec, - TALER_BANK_DIRECTION_NONE, - NULL, - 0, - NULL); - whh->hh = NULL; - taler_bank_get_history_cancel (NULL, - whh); - return; - } -} - - -/** - * Query transfer history of an account. We use the variable-size - * @a start_off to indicate which transfers we are interested in as - * different banking systems may have different ways to identify - * transfers. The @a start_off value must thus match the value of - * a `row_off` argument previously given to the @a hres_cb. Use - * NULL to query transfers from the beginning of time (with - * positive @a num_results) or from the lataler_bank committed - * transfers (with negative @a num_results). - * - * @param cls the @e cls of this struct with the plugin-specific - * state - * @param account_section specifies the configuration section which - * identifies the account for which we should get the history - * @param direction what kinds of wire transfers should be returned - * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive - * @param start_off_len number of bytes in @a start_off; must be `sizeof(uint64_t)`. - * @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 - */ -static struct TALER_WIRE_HistoryHandle * -taler_bank_get_history (void *cls, - const char *account_section, - enum TALER_BANK_Direction direction, - const void *start_off, - size_t start_off_len, - int64_t num_results, - TALER_WIRE_HistoryResultCallback hres_cb, - void *hres_cb_cls) -{ - struct TalerBankClosure *tc = cls; - struct TALER_WIRE_HistoryHandle *whh; - const uint64_t *start_off_b64; - uint64_t start_row; - struct TALER_Account account; - - GNUNET_assert (NULL != hres_cb); - if (0 == num_results) - { - GNUNET_break (0); - return NULL; - } - if (TALER_BANK_DIRECTION_NONE == direction) - { - GNUNET_break (0); - return NULL; - } - if ( (NULL != start_off) && - (sizeof (uint64_t) != start_off_len) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire plugin 'taler_bank' got" - " start offset of wrong size (%llu" - " instead of %llu)\n", - (unsigned long long) start_off_len, - (unsigned long long) sizeof (uint64_t)); - GNUNET_break (0); - - /** - * Probably something is wrong with the DB, some - * other component wrote a wrong value to it. Instead - * of completely stopping to work, we just scan from the - * beginning. */start_off = NULL; - } - if (NULL == start_off) - { - start_row = UINT64_MAX; /* no start row */ - } - else - { - start_off_b64 = start_off; - start_row = GNUNET_ntohll (*start_off_b64); - } - if (GNUNET_OK != - parse_account_cfg (tc->cfg, - account_section, - &account)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not parse the config section '%s'\n", - account_section); - return NULL; - } - - whh = GNUNET_new (struct TALER_WIRE_HistoryHandle); - if (GNUNET_OK != - TALER_BANK_auth_parse_cfg (tc->cfg, - account_section, - &whh->auth)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not parse the auth values from '%s'\n", - account_section); - TALER_WIRE_account_free (&account); - GNUNET_free (whh); - return NULL; - } - whh->hres_cb = hres_cb; - whh->hres_cb_cls = hres_cb_cls; - whh->hh = TALER_BANK_history (tc->ctx, - account.details.x_taler_bank.bank_base_url, - &whh->auth, - (uint64_t) account.details.x_taler_bank.no, - direction, - /* Defaults to descending ordering always. */ - GNUNET_NO, - start_row, - num_results, - &bhist_cb, - whh); - if (NULL == whh->hh) - { - GNUNET_break (0); - taler_bank_get_history_cancel (tc, - whh); - TALER_WIRE_account_free (&account); - return NULL; - } - TALER_WIRE_account_free (&account); - return whh; -} - - -/** - * Context for a rejection operation. - */ -struct TALER_WIRE_RejectHandle -{ - /** - * Function to call with the result. - */ - TALER_WIRE_RejectTransferCallback rej_cb; - - /** - * Closure for @e rej_cb. - */ - void *rej_cb_cls; - - /** - * Handle for the reject operation. - */ - struct TALER_BANK_RejectHandle *brh; - - /** - * Authentication information to use. - */ - struct TALER_BANK_AuthenticationData auth; -}; - - -/** - * Callbacks of this type are used to serve the result of asking - * the bank to reject an incoming wire transfer. - * - * @param cls closure - * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for successful status request; - * #MHD_HTTP_NOT_FOUND if the rowid is unknown; - * 0 if the bank's reply is bogus (fails to follow the protocol), - * @param ec detailed error code - */ -static void -reject_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec) -{ - struct TALER_WIRE_RejectHandle *rh = cls; - - (void) http_status; - rh->brh = NULL; - rh->rej_cb (rh->rej_cb_cls, - ec); - GNUNET_free (rh); -} - - -/** - * Cancel ongoing reject operation. Note that the rejection may still - * proceed. Basically, if this function is called, the rejection may - * have happened or not. This function is usually used during shutdown - * or system upgrades. At a later point, the application must call - * @e reject_transfer again for this wire transfer, unless the - * @e get_history shows that the wire transfer no longer exists. - * - * @param cls plugins' closure - * @param rh operation to cancel - * @return closure of the callback of the operation - */ -static void * -taler_bank_reject_transfer_cancel (void *cls, - struct TALER_WIRE_RejectHandle *rh) -{ - void *ret = rh->rej_cb_cls; - - (void) cls; - if (NULL != rh->brh) - TALER_BANK_reject_cancel (rh->brh); - TALER_BANK_auth_free (&rh->auth); - GNUNET_free (rh); - return ret; -} - - -/** - * Reject an incoming wire transfer that was obtained from the - * history. This function can be used to transfer funds back to - * the sender if the WTID was malformed (i.e. due to a typo). - * - * Calling `reject_transfer` twice on the same wire transfer should - * be idempotent, i.e. not cause the funds to be wired back twice. - * Furthermore, the transfer should henceforth be removed from the - * results returned by @e get_history. - * - * @param cls plugin's closure - * @param account_section specifies the configuration section which - * identifies the account to use to reject the transfer - * @param start_off offset of the wire transfer in plugin-specific format - * @param start_off_len number of bytes in @a start_off - * @param rej_cb function to call with the result of the operation - * @param rej_cb_cls closure for @a rej_cb - * @return handle to cancel the operation - */ -static struct TALER_WIRE_RejectHandle * -taler_bank_reject_transfer (void *cls, - const char *account_section, - const void *start_off, - size_t start_off_len, - TALER_WIRE_RejectTransferCallback rej_cb, - void *rej_cb_cls) -{ - struct TalerBankClosure *tc = cls; - const uint64_t *rowid_b64 = start_off; - struct TALER_WIRE_RejectHandle *rh; - struct TALER_Account account; - - if (sizeof (uint64_t) != start_off_len) - { - GNUNET_break (0); - return NULL; - } - rh = GNUNET_new (struct TALER_WIRE_RejectHandle); - if (GNUNET_OK != - TALER_BANK_auth_parse_cfg (tc->cfg, - account_section, - &rh->auth)) - { - GNUNET_free (rh); - return NULL; - } - if (GNUNET_OK != - parse_account_cfg (tc->cfg, - account_section, - &account)) - { - (void) taler_bank_reject_transfer_cancel (tc, - rh); - return NULL; - } - rh->rej_cb = rej_cb; - rh->rej_cb_cls = rej_cb_cls; - TALER_LOG_INFO ("Rejecting over %s bank URL\n", - account.details.x_taler_bank.hostname); - rh->brh = TALER_BANK_reject (tc->ctx, - account.details.x_taler_bank.bank_base_url, - &rh->auth, - (uint64_t) account.details.x_taler_bank.no, - GNUNET_ntohll (*rowid_b64), - &reject_cb, - rh); - if (NULL == rh->brh) - { - (void) taler_bank_reject_transfer_cancel (tc, - rh); - TALER_WIRE_account_free (&account); - return NULL; - } - TALER_WIRE_account_free (&account); - return rh; -} - - -/** - * Initialize taler_bank-wire subsystem. - * - * @param cls a configuration instance - * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` - */ -void * -libtaler_plugin_wire_taler_bank_init (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct TalerBankClosure *tc; - struct TALER_WIRE_Plugin *plugin; - - tc = GNUNET_new (struct TalerBankClosure); - tc->cfg = cfg; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "taler", - "CURRENCY", - &tc->currency)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler", - "CURRENCY"); - GNUNET_free (tc); - return NULL; - } - tc->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, - &tc->rc); - tc->rc = GNUNET_CURL_gnunet_rc_create (tc->ctx); - if (NULL == tc->ctx) - { - GNUNET_break (0); - GNUNET_free (tc->currency); - GNUNET_free (tc); - return NULL; - } - plugin = GNUNET_new (struct TALER_WIRE_Plugin); - plugin->cls = tc; - plugin->method = "x-taler-bank"; - plugin->amount_round = &taler_bank_amount_round; - plugin->wire_validate = &taler_bank_wire_validate; - plugin->prepare_wire_transfer = &taler_bank_prepare_wire_transfer; - plugin->prepare_wire_transfer_cancel = - &taler_bank_prepare_wire_transfer_cancel; - plugin->execute_wire_transfer = &taler_bank_execute_wire_transfer; - plugin->execute_wire_transfer_cancel = - &taler_bank_execute_wire_transfer_cancel; - plugin->get_history = &taler_bank_get_history; - plugin->get_history_cancel = &taler_bank_get_history_cancel; - plugin->reject_transfer = &taler_bank_reject_transfer; - plugin->reject_transfer_cancel = &taler_bank_reject_transfer_cancel; - return plugin; -} - - -/** - * Shutdown taler-bank wire subsystem. - * - * @param cls a `struct TALER_WIRE_Plugin` - * @return NULL (always) - */ -void * -libtaler_plugin_wire_taler_bank_done (void *cls) -{ - struct TALER_WIRE_Plugin *plugin = cls; - struct TalerBankClosure *tc = plugin->cls; - - if (NULL != tc->ctx) - { - GNUNET_CURL_fini (tc->ctx); - tc->ctx = NULL; - } - if (NULL != tc->rc) - { - GNUNET_CURL_gnunet_rc_destroy (tc->rc); - tc->rc = NULL; - } - GNUNET_free_non_null (tc->currency); - GNUNET_free (tc); - GNUNET_free (plugin); - return NULL; -} - - -/* end of plugin_wire_taler-bank.c */ diff --git a/src/wire-plugins/plugin_wire_template.c b/src/wire-plugins/plugin_wire_template.c deleted file mode 100644 index 731885ac6..000000000 --- a/src/wire-plugins/plugin_wire_template.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2016, 2018 GNUnet e.V. - - 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 plugin_wire_template.c - * @brief template for wire plugins; replace "template" with real plugin name! - * @author Florian Dold - * @author Christian Grothoff - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include "taler_wire_plugin.h" - - -/** - * Type of the "cls" argument given to each of the functions in - * our API. - */ -struct TemplateClosure -{ - - /** - * Which currency do we support? - */ - char *currency; - - /** - * Which configuration do we use to lookup accounts? - */ - struct GNUNET_CONFIGURATION_Handle *cfg; - -}; - - -/** - * Round amount DOWN to the amount that can be transferred via the wire - * method. For example, Taler may support 0.000001 EUR as a unit of - * payment, but SEPA only supports 0.01 EUR. This function would - * round 0.125 EUR to 0.12 EUR in this case. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param[in,out] amount amount to round down - * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, - * #GNUNET_SYSERR if the amount or currency was invalid - */ -static int -template_amount_round (void *cls, - struct TALER_Amount *amount) -{ - struct TemplateClosure *tc = cls; - - if (0 != strcasecmp (amount->currency, - tc->currency)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_break (0); // not implemented - return GNUNET_SYSERR; -} - - -/** - * Check if the given payto:// URL is correctly formatted for this plugin - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param account_url the payto:// URL - * @return #TALER_EC_NONE if correctly formatted - */ -static enum TALER_ErrorCode -template_wire_validate (void *cls, - const char *account_url) -{ - (void) cls; - (void) account_url; - GNUNET_break (0); - return TALER_EC_NOT_IMPLEMENTED; -} - - -/** - * Prepare for exeuction of a wire transfer. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param origin_account_section configuration section specifying the origin - * account of the exchange to use - * @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 the exchange (for tracking) - * @param wtid wire transfer identifier to use - * @param ptc function to call with the prepared data to persist - * @param ptc_cls closure for @a ptc - * @return NULL on failure - */ -static struct TALER_WIRE_PrepareHandle * -template_prepare_wire_transfer (void *cls, - const char *origin_account_section, - const char *destination_account_url, - const struct TALER_Amount *amount, - const char *exchange_base_url, - const struct - TALER_WireTransferIdentifierRawP *wtid, - TALER_WIRE_PrepareTransactionCallback ptc, - void *ptc_cls) -{ - (void) cls; - (void) origin_account_section; - (void) destination_account_url; - (void) amount; - (void) exchange_base_url; - (void) wtid; - (void) ptc; - (void) ptc_cls; - GNUNET_break (0); - return NULL; -} - - -/** - * Abort preparation of a wire transfer. For example, - * because we are shutting down. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param pth preparation to cancel - */ -static void -template_prepare_wire_transfer_cancel (void *cls, - struct TALER_WIRE_PrepareHandle *pth) -{ - (void) cls; - (void) pth; - GNUNET_break (0); -} - - -/** - * 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 - */ -static struct TALER_WIRE_ExecuteHandle * -template_execute_wire_transfer (void *cls, - const char *buf, - size_t buf_size, - TALER_WIRE_ConfirmationCallback cc, - void *cc_cls) -{ - (void) cls; - (void) buf; - (void) buf_size; - (void) cc; - (void) cc_cls; - GNUNET_break (0); - return NULL; -} - - -/** - * Abort execution of a wire transfer. For example, because we are - * shutting down. Note that if an execution is aborted, it may or - * may not still succeed. The caller MUST run @e - * execute_wire_transfer again for the same request as soon as - * possilbe, to ensure that the request either ultimately succeeds - * or ultimately fails. Until this has been done, the transaction is - * in limbo (i.e. may or may not have been committed). - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param eh execution to cancel - */ -static void -template_execute_wire_transfer_cancel (void *cls, - struct TALER_WIRE_ExecuteHandle *eh) -{ - (void) cls; - (void) eh; - GNUNET_break (0); -} - - -/** - * Query transfer history of an account. We use the variable-size - * @a start_off to indicate which transfers we are interested in as - * different banking systems may have different ways to identify - * transfers. The @a start_off value must thus match the value of - * a `row_off` argument previously given to the @a hres_cb. Use - * NULL to query transfers from the beginning of time (with - * positive @a num_results) or from the latest committed transfers - * (with negative @a num_results). - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param account_section specifies the configuration section which - * identifies the account for which we should get the history - * @param direction what kinds of wire transfers should be returned - * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive - * @param start_off_len number of bytes in @a start_off; must be `sizeof(uint64_t)`. - * @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 - */ -static struct TALER_WIRE_HistoryHandle * -template_get_history (void *cls, - const char *account_section, - enum TALER_BANK_Direction direction, - const void *start_off, - size_t start_off_len, - int64_t num_results, - TALER_WIRE_HistoryResultCallback hres_cb, - void *hres_cb_cls) -{ - (void) cls; - (void) account_section; - (void) direction; - (void) start_off; - (void) start_off_len; - (void) num_results; - (void) hres_cb; - (void) hres_cb_cls; - GNUNET_break (0); - return NULL; -} - - -/** - * Cancel going over the account's history. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param whh operation to cancel - */ -static void -template_get_history_cancel (void *cls, - struct TALER_WIRE_HistoryHandle *whh) -{ - (void) cls; - (void) whh; - GNUNET_break (0); -} - - -/** - * Reject an incoming wire transfer that was obtained from the - * history. This function can be used to transfer funds back to - * the sender if the WTID was malformed (i.e. due to a typo). - * - * Calling `reject_transfer` twice on the same wire transfer should - * be idempotent, i.e. not cause the funds to be wired back twice. - * Furthermore, the transfer should henceforth be removed from the - * results returned by @e get_history. - * - * @param cls plugin's closure - * @param account_section specifies the configuration section which - * identifies the account to use to reject the transfer - * @param start_off offset of the wire transfer in plugin-specific format - * @param start_off_len number of bytes in @a start_off - * @param rej_cb function to call with the result of the operation - * @param rej_cb_cls closure for @a rej_cb - * @return handle to cancel the operation - */ -static struct TALER_WIRE_RejectHandle * -template_reject_transfer (void *cls, - const char *account_section, - const void *start_off, - size_t start_off_len, - TALER_WIRE_RejectTransferCallback rej_cb, - void *rej_cb_cls) -{ - (void) cls; - (void) account_section; - (void) start_off; - (void) start_off_len; - (void) rej_cb; - (void) rej_cb_cls; - GNUNET_break (0); - return NULL; -} - - -/** - * Cancel ongoing reject operation. Note that the rejection may still - * proceed. Basically, if this function is called, the rejection may - * have happened or not. This function is usually used during shutdown - * or system upgrades. At a later point, the application must call - * @e reject_transfer again for this wire transfer, unless the - * @e get_history shows that the wire transfer no longer exists. - * - * @param cls plugins' closure - * @param rh operation to cancel - * @return closure of the callback of the operation - */ -static void * -template_reject_transfer_cancel (void *cls, - struct TALER_WIRE_RejectHandle *rh) -{ - (void) cls; - (void) rh; - GNUNET_break (0); - return NULL; -} - - -/** - * Initialize template-wire subsystem. - * - * @param cls a configuration instance - * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` - */ -void * -libtaler_plugin_wire_template_init (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct TemplateClosure *tc; - struct TALER_WIRE_Plugin *plugin; - - tc = GNUNET_new (struct TemplateClosure); - tc->cfg = cfg; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "taler", - "CURRENCY", - &tc->currency)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler", - "CURRENCY"); - GNUNET_free (tc); - return NULL; - } - - plugin = GNUNET_new (struct TALER_WIRE_Plugin); - plugin->cls = tc; - plugin->method = "FIXME-REPLACE-BY-METHOD"; - plugin->amount_round = &template_amount_round; - plugin->wire_validate = &template_wire_validate; - plugin->prepare_wire_transfer = &template_prepare_wire_transfer; - plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel; - plugin->execute_wire_transfer = &template_execute_wire_transfer; - plugin->execute_wire_transfer_cancel = &template_execute_wire_transfer_cancel; - plugin->get_history = &template_get_history; - plugin->get_history_cancel = &template_get_history_cancel; - plugin->reject_transfer = &template_reject_transfer; - plugin->reject_transfer_cancel = &template_reject_transfer_cancel; - return plugin; -} - - -/** - * Shutdown Template wire subsystem. - * - * @param cls a `struct TALER_WIRE_Plugin` - * @return NULL (always) - */ -void * -libtaler_plugin_wire_template_done (void *cls) -{ - struct TALER_WIRE_Plugin *plugin = cls; - struct TemplateClosure *tc = plugin->cls; - - GNUNET_free (tc->currency); - GNUNET_free (tc); - GNUNET_free (plugin); - return NULL; -} - - -/* end of plugin_wire_template.c */ diff --git a/src/wire-plugins/test_wire_plugin.c b/src/wire-plugins/test_wire_plugin.c deleted file mode 100644 index 65867113f..000000000 --- a/src/wire-plugins/test_wire_plugin.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - This file is part of TALER - (C) 2015, 2016, 2017 GNUnet e.V. and Inria - - 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 wire/test_wire_plugin.c - * @brief Tests for wire plugins - * @author Christian Grothoff - * @author Sree Harsha Totakura <sreeharsha@totakura.in> - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_wire_lib.h" -#include "taler_wire_plugin.h" -#include <gnunet/gnunet_json_lib.h> - - -/** - * Definitions for a test with a plugin. - */ -struct TestBlock -{ - - /** - * Name of the plugin to test. - */ - const char *plugin_name; - - /** - * Amount to give to the rounding function. - */ - const char *round_in; - - /** - * Expected result from rounding. - */ - const char *round_out; - - /** - * Currency to give to the plugin. - */ - const char *currency; -}; - - -/** - * List of plugins and (unsigned) JSON account definitions - * to use for the tests. - */ -static struct TestBlock tests[] = { -#if HAVE_LIBCURL - { - .plugin_name = "taler_bank", - .round_in = "KUDOS:0.123456", - .round_out = "KUDOS:0.12", - .currency = "KUDOS" - }, -#endif - { - NULL, NULL, NULL, NULL - } -}; - - -/** - * Our configuration. - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * Run the test. - * - * @param test details of the test - * @param plugin plugin to test - * @return #GNUNET_OK on success - */ -static int -run_test (const struct TestBlock *test, - struct TALER_WIRE_Plugin *plugin) -{ - struct GNUNET_HashCode salt; - struct TALER_Amount in; - struct TALER_Amount expect; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &salt, - sizeof (salt)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (test->round_in, - &in)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (test->round_out, - &expect)); - if (GNUNET_OK != - plugin->amount_round (plugin->cls, - &in)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 != TALER_amount_cmp (&in, &expect)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_NO != - plugin->amount_round (plugin->cls, - &in)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - memset (&in, 0, sizeof (in)); - GNUNET_log_skip (GNUNET_ERROR_TYPE_ERROR, 1); - if (GNUNET_SYSERR != - plugin->amount_round (plugin->cls, - &in)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -int -main (int argc, - const char *const argv[]) -{ - int ret; - struct TALER_WIRE_Plugin *plugin; - const struct TestBlock *test; - - GNUNET_log_setup ("test-wire-plugin", - "WARNING", - NULL); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_load (cfg, - "test_wire_plugin.conf")); - ret = GNUNET_OK; - for (unsigned int i = 0; NULL != (test = &tests[i])->plugin_name; i++) - { - GNUNET_CONFIGURATION_set_value_string (cfg, - "taler", - "CURRENCY", - test->currency); - plugin = TALER_WIRE_plugin_load (cfg, - test->plugin_name); - if (NULL == plugin) - { - TALER_LOG_ERROR ("Could not load plugin `%s'\n", - test->plugin_name); - return 77; - } - - ret = run_test (test, plugin); - TALER_WIRE_plugin_unload (plugin); - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%s FAILED\n", - test->plugin_name); - break; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "%s PASS\n", - test->plugin_name); - } - } - GNUNET_CONFIGURATION_destroy (cfg); - if (GNUNET_OK != ret) - return 1; - return 0; -} diff --git a/src/wire-plugins/test_wire_plugin.conf b/src/wire-plugins/test_wire_plugin.conf deleted file mode 100644 index d1d699b0f..000000000 --- a/src/wire-plugins/test_wire_plugin.conf +++ /dev/null @@ -1,25 +0,0 @@ -# This file is in the public domain. -# -[account-taler-bank] -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -WIRE_JSON = test_wire_plugin_test.json - -# Our bank account URL -URL = payto://x-taler-bank/2 - -# Which wire plugin should we used to access the account? -PLUGIN = taler_bank - - -[account-sepa] -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -WIRE_JSON = test_wire_plugin_sepa.json - -# Which wire plugin should we used to access the account? -PLUGIN = ebics - - -[taler] -CURRENCY = "EUR" diff --git a/src/wire-plugins/test_wire_plugin_transactions_taler-bank.c b/src/wire-plugins/test_wire_plugin_transactions_taler-bank.c deleted file mode 100644 index 79700818e..000000000 --- a/src/wire-plugins/test_wire_plugin_transactions_taler-bank.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - This file is part of TALER - (C) 2015-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 wire/test_wire_plugin_transactions_taler-bank.c - * @brief Tests performing actual transactions with the taler-bank wire plugin against FAKEBANK - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_wire_lib.h" -#include "taler_wire_plugin.h" -#include "taler_fakebank_lib.h" -#include <gnunet/gnunet_json_lib.h> - - -/** - * When does the test timeout? Right now, we expect this to be very - * fast. - */ -#define TIMEOUT GNUNET_TIME_UNIT_SECONDS - - -/** - * Destination account to use. - */ -static const char *dest_account = "payto://x-taler-bank/localhost:8088/42"; - -/** - * Origin account, section in the configuration file. - */ -static const char *my_account = "account-test"; - -/** - * Our configuration. - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Set to #GNUNET_SYSERR if the test failed. - */ -static int global_ret; - -/** - * The 'test' plugin that we are using for the test. - */ -static struct TALER_WIRE_Plugin *plugin; - -/** - * Active preparation handle, or NULL if not active. - */ -static struct TALER_WIRE_PrepareHandle *ph; - -/** - * Active execution handle, or NULL if not active. - */ -static struct TALER_WIRE_ExecuteHandle *eh; - -/** - * Handle to the bank. - */ -static struct TALER_FAKEBANK_Handle *fb; - -/** - * Handle to the history request. - */ -static struct TALER_WIRE_HistoryHandle *hh; - -/** - * Handle to the history-range request (the "legacy" bank API). - */ -static struct TALER_WIRE_HistoryHandle *hhr; - -/** - * Handle for the timeout task. - */ -static struct GNUNET_SCHEDULER_Task *tt; - -/** - * Which serial ID do we expect to get from /history? - */ -static uint64_t serial_target; - -/** - * Wire transfer identifier we are using. - */ -static struct TALER_WireTransferIdentifierRawP wtid; - - -/** - * Function called on shutdown (regular, error or CTRL-C). - * - * @param cls NULL - */ -static void -do_shutdown (void *cls) -{ - (void) cls; - TALER_FAKEBANK_stop (fb); - fb = NULL; - if (NULL != eh) - { - plugin->execute_wire_transfer_cancel (plugin->cls, - eh); - eh = NULL; - } - if (NULL != ph) - { - plugin->prepare_wire_transfer_cancel (plugin->cls, - ph); - ph = NULL; - } - if (NULL != hh) - { - plugin->get_history_cancel (plugin->cls, - hh); - hh = NULL; - } - - if (NULL != hhr) - { - plugin->get_history_cancel (plugin->cls, - hhr); - hhr = NULL; - } - - if (NULL != tt) - { - GNUNET_SCHEDULER_cancel (tt); - tt = NULL; - } - TALER_WIRE_plugin_unload (plugin); -} - - -/** - * Function called on timeout. - * - * @param cls NULL - */ -static void -timeout_cb (void *cls) -{ - tt = NULL; - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Callbacks of this type are used to serve the result of asking - * the bank for the transaction history. - * - * @param cls closure - * @param ec taler status code - * @param dir direction of the transfer - * @param row_off identification of the position at - * which we are querying - * @param row_off_size number of bytes in @a row_off - * @param details details about the wire transfer - * @return #GNUNET_OK to continue, #GNUNET_SYSERR to - * abort iteration - */ -static int -history_result_cb - (void *cls, - enum TALER_ErrorCode ec, - enum TALER_BANK_Direction dir, - const void *row_off, - size_t row_off_size, - const struct TALER_WIRE_TransferDetails *details) -{ - uint64_t *serialp; - uint64_t serialh; - struct TALER_Amount amount; - - if ( (TALER_BANK_DIRECTION_NONE == dir) && - (GNUNET_OK == global_ret) ) - { - GNUNET_SCHEDULER_shutdown (); - hh = NULL; - return GNUNET_OK; - } - if (sizeof (uint64_t) != row_off_size) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - serialp = (uint64_t *) row_off; - serialh = GNUNET_ntohll (*serialp); - if (serialh != serial_target) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("KUDOS:5.01", - &amount)); - if (0 != TALER_amount_cmp (&amount, - &details->amount)) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - if (0 != GNUNET_memcmp (&wtid, - &details->wtid)) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - global_ret = GNUNET_OK; - return GNUNET_OK; -} - - -/** - * Function called with the result from the execute step. - * - * @param cls closure - * @param success #GNUNET_OK on success, - * #GNUNET_SYSERR on failure - * @param row_id ID of the fresh transaction, - * in _network_ byte order. - * @param emsg NULL on success, otherwise an error message - */ -static void -confirmation_cb (void *cls, - int success, - const void *row_id, - size_t row_id_size, - const char *emsg) -{ - uint64_t tmp; - - eh = NULL; - if (GNUNET_OK != success) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return; - } - memcpy (&tmp, - row_id, - row_id_size); - serial_target = GNUNET_ntohll (tmp); - hh = plugin->get_history (plugin->cls, - my_account, - TALER_BANK_DIRECTION_BOTH, - NULL, - 0, - 5, - &history_result_cb, - NULL); -} - - -/** - * Callback with prepared transaction. - * - * @param cls closure - * @param buf transaction data to persist, NULL on error - * @param buf_size number of bytes in @a buf, 0 on error - */ -static void -prepare_cb (void *cls, - const char *buf, - size_t buf_size) -{ - ph = NULL; - if (NULL == buf) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return; - } - plugin->execute_wire_transfer (plugin->cls, - buf, - buf_size, - &confirmation_cb, - NULL); -} - - -/** - * Run the test. - * - * @param cls NULL - */ -static void -run (void *cls) -{ - struct TALER_Amount amount; - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - tt = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &timeout_cb, - NULL); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &wtid, - sizeof (wtid)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("KUDOS:5.01", - &amount)); - fb = TALER_FAKEBANK_start (8088); - ph = plugin->prepare_wire_transfer (plugin->cls, - my_account, - dest_account, - &amount, - "https://exchange.net/", - &wtid, - &prepare_cb, - NULL); -} - - -int -main (int argc, - const char *const argv[]) -{ - - GNUNET_log_setup ("test-wire-plugin-transactions-test", - "WARNING", - NULL); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_load (cfg, - "test_wire_plugin_transactions_taler-bank.conf")); - global_ret = GNUNET_OK; - plugin = TALER_WIRE_plugin_load (cfg, - "taler_bank"); - GNUNET_assert (NULL != plugin); - GNUNET_SCHEDULER_run (&run, - NULL); - GNUNET_CONFIGURATION_destroy (cfg); - if (GNUNET_OK != global_ret) - return 1; - return 0; -} - - -/* end of test_wire_plugin_transactions_taler-bank.c */ diff --git a/src/wire-plugins/test_wire_plugin_transactions_taler-bank.conf b/src/wire-plugins/test_wire_plugin_transactions_taler-bank.conf deleted file mode 100644 index d6d2e8346..000000000 --- a/src/wire-plugins/test_wire_plugin_transactions_taler-bank.conf +++ /dev/null @@ -1,12 +0,0 @@ -# This file is in the public domain. -# -[account-test] -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. - -TALER_BANK_AUTH_METHOD = NONE - -URL = payto://x-taler-bank/localhost:8088/2 - -[taler] -CURRENCY = "KUDOS" diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am index 6bf38aa2a..2a82282a3 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -13,7 +13,6 @@ lib_LTLIBRARIES = \ libtalerwire_la_SOURCES = \ payto.c \ - wire.c \ wire_helper.c libtalerwire_la_LIBADD = \ -lgnunetutil \ diff --git a/src/wire/payto.c b/src/wire/payto.c index 741549772..0c3fce957 100644 --- a/src/wire/payto.c +++ b/src/wire/payto.c @@ -20,6 +20,7 @@ */ #include "platform.h" #include "taler_util.h" +#include "taler_bank_service.h" #include "taler_wire_lib.h" /** @@ -43,8 +44,8 @@ TALER_WIRE_account_free (struct TALER_Account *acc) case TALER_PAC_X_TALER_BANK: GNUNET_free (acc->details.x_taler_bank.hostname); acc->details.x_taler_bank.hostname = NULL; - GNUNET_free (acc->details.x_taler_bank.bank_base_url); - acc->details.x_taler_bank.bank_base_url = NULL; + GNUNET_free (acc->details.x_taler_bank.account_base_url); + acc->details.x_taler_bank.account_base_url = NULL; break; case TALER_PAC_IBAN: GNUNET_free (acc->details.iban.number); @@ -410,7 +411,8 @@ parse_payto_x_taler_bank (const char *account_url, const char *hostname; const char *account; const char *q; - unsigned long long no; + unsigned int port; + char *p; #define PREFIX "payto://x-taler-bank/" if (0 != strncasecmp (account_url, @@ -422,74 +424,52 @@ parse_payto_x_taler_bank (const char *account_url, (unsigned char) '/'))) return TALER_EC_PAYTO_MALFORMED; account++; - if (NULL != (q = strchr (account, - (unsigned char) '?'))) + if (NULL == r_account) + return TALER_EC_NONE; + q = strchr (account, + (unsigned char) '?'); + if (0 == q) + q = account + strlen (account); + r_account->details.x_taler_bank.hostname + = GNUNET_strndup (hostname, + account - hostname); + port = 443; /* if non given, equals 443. */ + if (NULL != (p = strchr (r_account->details.x_taler_bank.hostname, + (unsigned char) ':'))) { - char *s; - - s = GNUNET_strndup (account, - q - account); - if (1 != sscanf (s, - "%llu", - &no)) + p++; + if (1 != sscanf (p, + "%u", + &port)) { - GNUNET_free (s); + GNUNET_break (0); + TALER_LOG_ERROR ("Malformed host from payto:// URI\n"); + GNUNET_free (r_account->details.x_taler_bank.hostname); + r_account->details.x_taler_bank.hostname = NULL; return TALER_EC_PAYTO_MALFORMED; } - GNUNET_free (s); } - else if (1 != sscanf (account, - "%llu", - &no)) + if (443 != port) { - return TALER_EC_PAYTO_MALFORMED; + GNUNET_assert + (GNUNET_SYSERR != GNUNET_asprintf + (&r_account->details.x_taler_bank.account_base_url, + "http://%s/%.*s", + r_account->details.x_taler_bank.hostname, + (int) (q - account), + account)); } - if (no > MAX_ACCOUNT_NO) - return TALER_EC_PAYTO_MALFORMED; - - if (NULL != r_account) + else { - long long unsigned port; - char *p; - - r_account->details.x_taler_bank.hostname - = GNUNET_strndup (hostname, - account - hostname); - r_account->details.x_taler_bank.no = no; - port = 443; /* if non given, equals 443. */ - if (NULL != (p = strchr (r_account->details.x_taler_bank.hostname, - (unsigned char) ':'))) - { - p++; - if (1 != sscanf (p, - "%llu", - &port)) - { - GNUNET_break (0); - TALER_LOG_ERROR ("Malformed host from payto:// URI\n"); - GNUNET_free (r_account->details.x_taler_bank.hostname); - r_account->details.x_taler_bank.hostname = NULL; - return TALER_EC_PAYTO_MALFORMED; - } - } - if (443 != port) - { - GNUNET_assert - (GNUNET_SYSERR != GNUNET_asprintf - (&r_account->details.x_taler_bank.bank_base_url, - "http://%s", - r_account->details.x_taler_bank.hostname)); - } - else - { - GNUNET_assert - (GNUNET_SYSERR != GNUNET_asprintf - (&r_account->details.x_taler_bank.bank_base_url, - "https://%s", - r_account->details.x_taler_bank.hostname)); - } - r_account->type = TALER_PAC_X_TALER_BANK; + GNUNET_assert + (GNUNET_SYSERR != GNUNET_asprintf + (&r_account->details.x_taler_bank.account_base_url, + "https://%s/%.*s", + r_account->details.x_taler_bank.hostname, + (int) (q - account), + account)); } + r_account->type = TALER_PAC_X_TALER_BANK; return TALER_EC_NONE; } diff --git a/src/wire/wire.c b/src/wire/wire.c deleted file mode 100644 index 60ae9e6ed..000000000 --- a/src/wire/wire.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - This file is part of TALER - (C) 2015, 2016, 2017, 2018 GNUnet e.V. - - 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 wire/wire.c - * @brief Functions for loading wire plugins - * @author Christian Grothoff <christian@grothoff.org> - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_wire_lib.h" - - -/** - * A wire plugin that we have loaded. - */ -struct WirePlugin -{ - /** - * We keep these in a DLL. - */ - struct WirePlugin *next; - - /** - * We keep these in a DLL. - */ - struct WirePlugin *prev; - - /** - * Type of this wire plugin. - */ - char *type; - - /** - * Wire plugin - */ - struct TALER_WIRE_Plugin *plugin; - - /** - * Reference counter for the plugin. - */ - unsigned int rc; -}; - - -/** - * Head of the DLL of loaded wire plugins. - */ -static struct WirePlugin *wp_head; - -/** - * Tail of the DLL of loaded wire plugins. - */ -static struct WirePlugin *wp_tail; - - -/** - * Load a WIRE plugin. - * - * @param cfg configuration to use - * @param plugin_name name of the plugin to load - * @return the plugin object pointer, or NULL upon errors. - */ -struct TALER_WIRE_Plugin * -TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *plugin_name) -{ - char *lib_name; - struct TALER_WIRE_Plugin *plugin; - struct WirePlugin *wp; - - for (wp = wp_head; NULL != wp; wp = wp->next) - if (0 == strcasecmp (plugin_name, - wp->type)) - { - wp->rc++; - return wp->plugin; - } - (void) GNUNET_asprintf (&lib_name, - "libtaler_plugin_wire_%s", - plugin_name); - plugin = GNUNET_PLUGIN_load (lib_name, - (void *) cfg); - if (NULL != plugin) - plugin->library_name = lib_name; - else - GNUNET_free (lib_name); - if (NULL == plugin) - return NULL; - wp = GNUNET_new (struct WirePlugin); - wp->plugin = plugin; - wp->type = GNUNET_strdup (plugin_name); - GNUNET_CONTAINER_DLL_insert (wp_head, - wp_tail, - wp); - wp->rc = 1; - return plugin; -} - - -/** - * Unload a WIRE plugin. - * - * @param plugin the plugin to unload - */ -void -TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin) -{ - struct WirePlugin *wp; - char *lib_name; - - if (NULL == plugin) - return; - for (wp = wp_head; NULL != wp; wp = wp->next) - { - if (plugin == wp->plugin) - { - wp->rc--; - if (0 < wp->rc) - return; - GNUNET_CONTAINER_DLL_remove (wp_head, - wp_tail, - wp); - GNUNET_free (wp->type); - GNUNET_free (wp); - break; - } - } - lib_name = plugin->library_name; - GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, - plugin)); - GNUNET_free (lib_name); -} - - -/* end of wire.c */ diff --git a/src/wire/wire_helper.c b/src/wire/wire_helper.c index e62539832..4b7f092a2 100644 --- a/src/wire/wire_helper.c +++ b/src/wire/wire_helper.c @@ -20,6 +20,7 @@ /** * @file wire/wire_helper.c * @brief Helper functions for dealing with wire formats + * @author Christian Grothoff <christian@grothoff.org> */ #include "platform.h" @@ -33,23 +34,6 @@ /** - * Maps wire methods to plugin names. - */ -struct ConversionTable -{ - - /** - * Wire method (e.g. 'iban', 'x-taler-bank', ..) - */ - const char *method; - - /** - * Plugin name, e.g. 'taler_bank', .. - */ - const char *plugin_name; -}; - -/** * Obtain the payment method from a @a payto_url * * @param payto_url the URL to parse @@ -76,35 +60,23 @@ TALER_WIRE_payto_get_method (const char *payto_url) /** - * Get the plugin name from the payment method. + * Round the amount to something that can be + * transferred on the wire. * - * FIXME: this is ugly, would be better to have - * a way to iterate over all plugins and interrogate - * them as to what wire method(s) they support! - * - * @param method the method implemented by the plugin (for - * simplicity, we assume 1 method is implemented by 1 plugin). - * @return the plugin name, NULL if not found. + * @param[in,out] amount amount to round down + * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, + * #GNUNET_SYSERR if the amount or currency was invalid */ -const char * -TALER_WIRE_get_plugin_from_method (const char *method) +int +TALER_WIRE_amount_round (struct TALER_Amount *amount) { - static const struct ConversionTable ct[] = { - {"x-taler-bank", "taler_bank"}, - {"iban", "taler_bank"}, - {NULL, NULL} - }; - - for (unsigned int i = 0; - NULL != ct[i].method; - i++) - { - if (0 == strcmp (method, - ct[i].method)) - return ct[i].plugin_name; - } + uint32_t delta; - return NULL; + delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100); + if (0 == delta) + return GNUNET_NO; + amount->fraction -= delta; + return GNUNET_OK; } |