From 9443c10d7feb0d91323869dd08ec61ca781564f4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 11 Jan 2020 15:19:56 +0100 Subject: 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 --- src/Makefile.am | 2 +- src/auditor/Makefile.am | 2 + src/auditor/taler-auditor.c | 110 +- src/auditor/taler-wire-auditor.c | 411 +++--- src/auditordb/plugin_auditordb_postgres.c | 72 +- src/bank-lib/Makefile.am | 13 +- src/bank-lib/bank_api_admin.c | 71 +- src/bank-lib/bank_api_common.c | 86 +- src/bank-lib/bank_api_common.h | 12 + src/bank-lib/bank_api_credit.c | 315 +++++ src/bank-lib/bank_api_debit.c | 317 +++++ src/bank-lib/bank_api_history.c | 445 ------- src/bank-lib/bank_api_parse.c | 54 +- src/bank-lib/bank_api_reject.c | 242 ---- src/bank-lib/bank_api_transaction.c | 368 ++++++ src/bank-lib/fakebank.c | 728 +++++++---- src/bank-lib/fakebank.h | 283 +---- src/bank-lib/fakebank_history.c | 204 +-- src/bank-lib/taler-bank-transfer.c | 55 +- src/bank-lib/test_bank_api.c | 2 +- src/bank-lib/testing_api_cmd_history.c | 1004 --------------- src/bank-lib/testing_api_cmd_history_credit.c | 758 ++++++++++++ src/bank-lib/testing_api_cmd_history_debit.c | 758 ++++++++++++ src/bank-lib/testing_api_cmd_reject.c | 223 ---- src/benchmark/taler-exchange-benchmark.c | 69 +- src/exchange-tools/Makefile.am | 4 + src/exchange-tools/taler-exchange-keyup.c | 23 +- src/exchange-tools/taler-wire.c | 268 ++-- src/exchange/Makefile.am | 5 + src/exchange/taler-exchange-aggregator.c | 448 +++---- src/exchange/taler-exchange-httpd_deposit.c | 13 - src/exchange/taler-exchange-httpd_validation.c | 142 +-- src/exchange/taler-exchange-wirewatch.c | 271 ++-- src/exchangedb/exchangedb_accounts.c | 12 +- src/exchangedb/exchangedb_fees.c | 8 +- src/exchangedb/plugin_exchangedb_postgres.c | 31 +- src/exchangedb/test_exchangedb.c | 32 +- src/include/Makefile.am | 2 - src/include/taler_auditordb_plugin.h | 18 +- src/include/taler_bank_service.h | 313 +++-- src/include/taler_exchangedb_lib.h | 9 +- src/include/taler_exchangedb_plugin.h | 18 +- src/include/taler_fakebank_lib.h | 25 +- src/include/taler_mhd_lib.h | 2 +- src/include/taler_testing_bank_lib.h | 44 +- src/include/taler_testing_lib.h | 159 ++- src/include/taler_wire_lib.h | 59 +- src/include/taler_wire_plugin.h | 259 ++-- src/lib/Makefile.am | 1 + src/lib/exchange_api_handle.c | 2 +- src/lib/testing_api_cmd_bank_check.c | 69 +- src/lib/testing_api_cmd_fakebank_transfer.c | 402 +++--- src/lib/testing_api_cmd_status.c | 36 +- src/lib/testing_api_cmd_track.c | 21 +- src/lib/testing_api_trait_reserve_pub.c | 77 ++ src/lib/testing_api_trait_string.c | 29 +- src/wire-plugins/Makefile.am | 81 -- src/wire-plugins/plugin_wire_taler-bank.c | 1306 -------------------- src/wire-plugins/plugin_wire_template.c | 384 ------ src/wire-plugins/test_wire_plugin.c | 189 --- src/wire-plugins/test_wire_plugin.conf | 25 - .../test_wire_plugin_transactions_taler-bank.c | 365 ------ .../test_wire_plugin_transactions_taler-bank.conf | 12 - src/wire/Makefile.am | 1 - src/wire/payto.c | 104 +- src/wire/wire.c | 149 --- src/wire/wire_helper.c | 56 +- 67 files changed, 4663 insertions(+), 7415 deletions(-) create mode 100644 src/bank-lib/bank_api_credit.c create mode 100644 src/bank-lib/bank_api_debit.c delete mode 100644 src/bank-lib/bank_api_history.c delete mode 100644 src/bank-lib/bank_api_reject.c create mode 100644 src/bank-lib/bank_api_transaction.c delete mode 100644 src/bank-lib/testing_api_cmd_history.c create mode 100644 src/bank-lib/testing_api_cmd_history_credit.c create mode 100644 src/bank-lib/testing_api_cmd_history_debit.c delete mode 100644 src/bank-lib/testing_api_cmd_reject.c create mode 100644 src/lib/testing_api_trait_reserve_pub.c delete mode 100644 src/wire-plugins/Makefile.am delete mode 100644 src/wire-plugins/plugin_wire_taler-bank.c delete mode 100644 src/wire-plugins/plugin_wire_template.c delete mode 100644 src/wire-plugins/test_wire_plugin.c delete mode 100644 src/wire-plugins/test_wire_plugin.conf delete mode 100644 src/wire-plugins/test_wire_plugin_transactions_taler-bank.c delete mode 100644 src/wire-plugins/test_wire_plugin_transactions_taler-bank.conf delete mode 100644 src/wire/wire.c (limited to 'src') 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; @@ -2031,35 +2029,6 @@ analyze_reserves (void *cls) paying each merchant what they were due (and on time). */ -/** - * 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. */ @@ -2105,16 +2074,6 @@ struct WireFeeInfo 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. */ @@ -2132,46 +2091,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. */ @@ -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 +#include #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 **************************** */ @@ -356,15 +373,12 @@ 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 @@ -39,6 +39,18 @@ struct curl_slist * 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. * 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 +*/ +/** + * @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 /* 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; ihcb (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_debit.c b/src/bank-lib/bank_api_debit.c new file mode 100644 index 000000000..848362438 --- /dev/null +++ b/src/bank-lib/bank_api_debit.c @@ -0,0 +1,317 @@ +/* + 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 +*/ +/** + * @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 /* just for HTTP status codes */ +#include "taler_signatures.h" + + +/** + * @brief A /history Handle + */ +struct TALER_BANK_DebitHistoryHandle +{ + + /** + * 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_DebitResultCallback 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_DebitHistoryHandle *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; ihcb (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_DebitHistoryHandle` + * @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_DebitHistoryHandle *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_debit_history_cancel (hh); +} + + +/** + * 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 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_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) +{ + char *url; + struct TALER_BANK_DebitHistoryHandle *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_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; +} + + +/** + * 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_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *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_debit.c */ diff --git a/src/bank-lib/bank_api_history.c b/src/bank-lib/bank_api_history.c deleted file mode 100644 index f5013b85b..000000000 --- a/src/bank-lib/bank_api_history.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2017 GNUnet e.V. & 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 -*/ -/** - * @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 /* just for HTTP status codes */ -#include "taler_signatures.h" - - -/** - * @brief A /history Handle - */ -struct TALER_BANK_HistoryHandle -{ - - /** - * 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; - - /** - * HTTP authentication-related headers for the request. - */ - struct curl_slist *authh; - - /** - * Function to call with the result. - */ - TALER_BANK_HistoryResultCallback 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_HistoryHandle *hh, - const json_t *history) -{ - json_t *history_array; - char *bank_hostname; - - if (NULL == (history_array = json_object_get (history, "data"))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 0; ibank_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; -} - - -/** - * Function called when we're done processing the - * HTTP /history request. - * - * @param cls the `struct TALER_BANK_HistoryHandle` - * @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_HistoryHandle *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, - 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; -} - - -/** - * 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. - * - * @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 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; - * 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_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_HistoryHandle *hh; - char *url; - - if (0 == num_results) - { - GNUNET_break (0); - return NULL; - } - - 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"); - 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, - (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); - - 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_history_cancel (struct TALER_BANK_HistoryHandle *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 */ 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 @@ -23,6 +23,58 @@ #include "taler_bank_service.h" +/** + * 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. * 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 - -*/ -/** - * @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 /* 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 + +*/ +/** + * @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 /* 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) @@ -33,6 +33,65 @@ #define REQUEST_BUFFER_MAX (4 * 1024) +/** + * 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. */ @@ -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; @@ -182,31 +246,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()). @@ -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); } @@ -290,62 +329,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 @@ -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. */ @@ -785,6 +955,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. * @@ -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 */ @@ -29,283 +29,4 @@ #include #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 */ - #include "platform.h" #include #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.c deleted file mode 100644 index dc5cd2d99..000000000 --- a/src/bank-lib/testing_api_cmd_history.c +++ /dev/null @@ -1,1004 +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 - -*/ - -/** - * @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 -#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 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. - */ - enum TALER_BANK_Direction direction; - - /** - * First row number we want in the result. - */ - const char *start_row_reference; - - /** - * How many rows we want in the result, _at most_. - */ - unsigned long long num_results; - - /** - * Handle to a pending "history" operation. - */ - struct TALER_BANK_HistoryHandle *hh; - - /** - * Expected number of results (= rows). - */ - uint64_t results_obtained; - - /** - * Set to GNUNET_YES if the callback detects something - * unexpected. - */ - 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. - */ -struct History -{ - - /** - * Wire details. - */ - struct TALER_BANK_TransferDetails details; - - /** - * Serial ID of the wire transfer. - */ - uint64_t row_id; - - /** - * Direction of the transfer. - */ - enum TALER_BANK_Direction direction; - -}; - - -/** - * Array mapping bank account numbers to login credentials. - */ -extern struct TALER_BANK_AuthenticationData AUTHS[]; - - -/** - * 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; -} - - -/** - * 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; iip; 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. - * @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; offstart_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; -} - - -/** - * 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. - * - * 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. - * - * @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) || /* "/history" */ - (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != /* "/history-range" */ - hs->start_date.abs_value_us)); - - 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]; - int cancelled; - const uint64_t *row_id; - - /** - * 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; - } - } - - /* 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 */ - - TALER_LOG_DEBUG ("Found first row\n"); - - if (build_history_hit_limit (total, - hs, - pos)) - { - TALER_LOG_DEBUG ("Hit history limit\n"); - 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)); - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT - (pos, &debit_account_no)); - - 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)) ) - { - 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]; - int cancelled; - const uint64_t *row_id; - char *bank_hostname; - const uint64_t *credit_account_no; - const uint64_t *debit_account_no; - - 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; - } - } - - /* 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) - { - 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_CREDIT_ACCOUNT - (pos, &credit_account_no)); - - GNUNET_assert - (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT - (pos, &debit_account_no)); - - 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); - - /** - * 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)) ) - { - 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, ':'); - GNUNET_assert (NULL != bank_hostname); - bank_hostname += 3; - - /* Next two blocks only put the 'direction' and 'banking' - * 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)) - { - 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); - } - - /* 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)) ) - { - 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)); - - h[total].details.amount = *amount; - - h[total].row_id = *row_id; - GNUNET_asprintf (&h[total].details.wire_transfer_subject, - "%s %s", - subject, - exchange_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, - enum TALER_BANK_Direction dir, - const struct TALER_BANK_TransferDetails *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 (h[off].direction != dir) - { - GNUNET_break (0); - print_expected (h, total, off); - free_history (h, - total); - return GNUNET_SYSERR; - } - - if ( (0 != strcmp (h[off].details.wire_transfer_subject, - details->wire_transfer_subject)) || - (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. - */ -static void -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 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) - { - 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; - } - 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; - } - - /* 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); - } - - hs->failed = GNUNET_YES; - return; - } - hs->results_obtained++; -} - - -/** - * 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 = UINT64_MAX; - const uint64_t *row_id_ptr = &row_id; - struct TALER_BANK_AuthenticationData *auth; - - (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_id_ptr)) - TALER_TESTING_FAIL (is); - row_id = *row_id_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); - 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_history_cancel (hs->hh); - } - GNUNET_free (hs); -} - - -/** - * Make a "history" CMD. - * - * @param label command label. - * @param bank_url base URL of the bank 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. - * @param num_results how many rows we want in the result. - * @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, - const char *start_row_reference, - unsigned 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->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 = { - .label = label, - .cls = hs, - .run = &history_run, - .cleanup = &history_cleanup, - .traits = &history_traits - }; - - return cmd; - } -} - - -/* end of testing_api_cmd_history.c */ diff --git a/src/bank-lib/testing_api_cmd_history_credit.c b/src/bank-lib/testing_api_cmd_history_credit.c new file mode 100644 index 000000000..5c2b34d06 --- /dev/null +++ b/src/bank-lib/testing_api_cmd_history_credit.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 + +*/ +/** + * @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 +#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. + */ + 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_CreditHistoryHandle *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_CreditDetails 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= 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 *credit_account; + const char *debit_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_CREDIT_ACCOUNT + (pos, &credit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_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, + credit_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_CREDIT_ACCOUNT + (pos, &credit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_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 credit and the debit roles, but _only if_ + * the audit goes on both directions.. This needs more + * explaination! + */if (0 == strcasecmp (hs->account_url, + credit_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 credit, and account got the credit. */ + if (0 == strcasecmp (hs->account_url, + credit_account)) + { + 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 == strcasecmp (hs->account_url, + credit_account)) + { + const struct TALER_Amount *amount; + 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; + h[total].details.reserve_pub = *reserve_pub; + 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_CreditDetails *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.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); + 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_CreditDetails *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_credit_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_credit_history_cancel (hs->hh); + } + GNUNET_free (hs->account_url); + 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_credits (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 = GNUNET_strdup (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_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 + +*/ + +/** + * @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 +#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= 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 - -*/ - -/** - * @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 -#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 */ - /** - * @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 #include @@ -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/")) @@ -107,6 +96,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. */ @@ -168,6 +162,11 @@ static enum BenchmarkMode mode; */ static char *cfg_filename; +/** + * payto://-URL of the exchange's bank account. + */ +static char *exchange_payto_url; + /** * Currency used. */ @@ -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 #include #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,16 +35,11 @@ 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. */ @@ -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,71 +138,36 @@ 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. */ @@ -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. @@ -262,6 +269,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); } } @@ -921,20 +964,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. @@ -988,83 +1017,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(). */ @@ -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 @@ -29,40 +29,6 @@ #include "taler_wire_lib.h" -/** - * 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. */ @@ -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); @@ -286,65 +215,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 * 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" /** @@ -36,23 +37,6 @@ #define DELAY GNUNET_TIME_UNIT_SECONDS -/** - * 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. */ @@ -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 @@ -107,6 +96,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? */ @@ -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; } @@ -335,71 +330,29 @@ static void 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 #include #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,27 +101,12 @@ 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. * 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,50 +731,13 @@ 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. @@ -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 @@ -1640,6 +1592,34 @@ TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd, TALER_ReservePrivateKeyP **reserve_priv); +/** + * 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. * @@ -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 -#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; /** @@ -113,6 +110,18 @@ void 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 * @@ -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 @@ -40,23 +40,10 @@ typedef void size_t buf_size); -/** - * 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; ideposit_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. @@ -86,6 +76,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. */ @@ -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 + +*/ +/** + * @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 +#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 -*/ - -/** - * @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 - -/* only for HTTP status codes */ -#include - -/** - * 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 -*/ - -/** - * @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 -*/ -/** - * @file wire/test_wire_plugin.c - * @brief Tests for wire plugins - * @author Christian Grothoff - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_wire_lib.h" -#include "taler_wire_plugin.h" -#include - - -/** - * 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 -*/ -/** - * @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 - - -/** - * 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 -*/ - -/** - * @file wire/wire.c - * @brief Functions for loading wire plugins - * @author Christian Grothoff - */ -#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 */ #include "platform.h" @@ -32,23 +33,6 @@ #define PAYTO "payto://" -/** - * 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 * @@ -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; } -- cgit v1.2.3