From cb55c1a3af9f56a6da38e5589e72df0b70d355b1 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 2 Apr 2018 14:24:45 +0200 Subject: Changing configuration structure to enable multiple accounts. This change enables using multiple wire plugins at the same time. Also, we now distinguish between the wire plugin (i.e. EBICS or taler_bank) and the wire method (i.e. SEPA or x-taler-bank) that the wire plugin is implementing. The "taler-bank" wire method was renamed from "test" to "x-taler-bank". This also changes the format of the /wire response of the exchange, as we now need to return multiple accounts. Note that wire fees are specified per wire method, not per wire account. taler-exchange-keyup now automatically signs all of the /wire responses in the location specified by the configuration. Account identification in wire plugins was changed to use payto://-URLs instead of method-specific JSON fields. Signing and validation of /wire responses was moved from each wire plugin to a generic validation method in libtalerutil (crypto) or libtalerjson (for JSON-formatted inputs). Convenience methods were added to generate JSON for wire accounts (salting, signing). Various section and option names were adjusted to streamline the configuration and make it more consistent overall. Documentation was updated as well. --- src/Makefile.am | 2 +- src/auditor/taler-auditor.c | 73 +- src/auditor/taler-wire-auditor.c | 455 ++++--- src/auditordb/auditordb-postgres.conf | 2 +- src/auditordb/plugin_auditordb_postgres.c | 27 +- src/auditordb/test-auditor-db-postgres.conf | 2 +- src/bank-lib/Makefile.am | 4 +- src/bank-lib/bank.conf | 5 +- src/bank-lib/bank_api_history.c | 12 +- src/bank-lib/bank_api_parse.c | 133 ++ src/bank-lib/test_bank_api_with_fakebank_new.c | 5 +- src/bank-lib/test_bank_interpreter.c | 40 +- src/bank-lib/testing_api_cmd_history.c | 47 +- src/bank-lib/testing_api_cmd_reject.c | 4 +- src/benchmark/taler-exchange-benchmark.c | 4 +- src/benchmark/taler-exchange-benchmark.conf | 14 +- src/exchange-lib/Makefile.am | 3 +- src/exchange-lib/exchange_api_deposit.c | 4 +- src/exchange-lib/exchange_api_reserve.c | 15 +- src/exchange-lib/exchange_api_wire.c | 475 ++++--- src/exchange-lib/test_exchange_api.c | 80 +- src/exchange-lib/test_exchange_api.conf | 78 +- .../.config/taler/account-1.json | 5 + .../.config/taler/account-2.json | 5 + .../.config/taler/x-taler-bank.json | 5 + .../test_exchange_api_keys_cherry_picking.conf | 64 +- ..._exchange_api_keys_cherry_picking_extended.conf | 2 +- src/exchange-lib/test_exchange_api_new.c | 99 +- src/exchange-lib/testing_api_cmd_deposit.c | 52 +- src/exchange-lib/testing_api_cmd_exec_wirewatch.c | 1 - src/exchange-lib/testing_api_cmd_payback.c | 41 +- src/exchange-lib/testing_api_cmd_refund.c | 11 +- src/exchange-lib/testing_api_cmd_track.c | 64 +- src/exchange-lib/testing_api_cmd_wire.c | 129 +- src/exchange-lib/testing_api_helpers.c | 74 +- src/exchange-lib/testing_api_trait_json.c | 76 ++ src/exchange-lib/testing_api_trait_string.c | 48 +- src/exchange-tools/Makefile.am | 3 +- src/exchange-tools/exchange-signkeys.conf | 16 - src/exchange-tools/taler-exchange-keyup.c | 66 +- src/exchange-tools/taler-exchange-wire.c | 182 +-- src/exchange/.gitignore | 1 + src/exchange/exchange.conf | 15 + src/exchange/taler-exchange-aggregator.c | 319 ++--- src/exchange/taler-exchange-httpd_deposit.c | 12 +- src/exchange/taler-exchange-httpd_responses.c | 17 +- src/exchange/taler-exchange-httpd_track_transfer.c | 18 +- src/exchange/taler-exchange-httpd_validation.c | 310 +++-- src/exchange/taler-exchange-httpd_validation.h | 9 +- src/exchange/taler-exchange-httpd_wire.c | 17 +- src/exchange/taler-exchange-httpd_wire.h | 4 +- src/exchange/taler-exchange-wirewatch.c | 203 ++- .../test-taler-exchange-aggregator-postgres.conf | 34 +- .../test-taler-exchange-wirewatch-postgres.conf | 30 +- src/exchange/test_taler_exchange_aggregator.c | 26 +- src/exchange/test_taler_exchange_httpd.conf | 49 +- src/exchange/test_taler_exchange_httpd.data | 17 +- src/exchange/test_taler_exchange_httpd.sh | 4 +- src/exchange/test_taler_exchange_wirewatch.c | 3 +- src/exchangedb/Makefile.am | 12 +- src/exchangedb/exchangedb-postgres.conf | 2 +- src/exchangedb/exchangedb_accounts.c | 141 +++ src/exchangedb/perf_taler_exchangedb.c | 2 +- src/exchangedb/perf_taler_exchangedb_init.c | 22 +- src/exchangedb/perf_taler_exchangedb_interpreter.c | 14 +- src/exchangedb/plugin_exchangedb_common.c | 8 +- src/exchangedb/plugin_exchangedb_postgres.c | 89 +- src/exchangedb/test-exchange-db-postgres.conf | 2 +- src/exchangedb/test_exchangedb.c | 98 +- src/include/taler_auditordb_plugin.h | 6 + src/include/taler_bank_service.h | 29 +- src/include/taler_crypto_lib.h | 49 +- src/include/taler_error_codes.h | 22 + src/include/taler_exchange_service.h | 68 +- src/include/taler_exchangedb_lib.h | 68 + src/include/taler_exchangedb_plugin.h | 51 +- src/include/taler_json_lib.h | 59 + src/include/taler_signatures.h | 27 +- src/include/taler_testing_lib.h | 38 +- src/include/taler_wire_lib.h | 36 +- src/include/taler_wire_plugin.h | 75 +- src/json/Makefile.am | 4 +- src/json/json_wire.c | 193 +++ src/util/Makefile.am | 1 + src/util/crypto_wire.c | 108 ++ src/wire-plugins/Makefile.am | 101 ++ src/wire-plugins/plugin_wire_ebics.c | 753 +++++++++++ src/wire-plugins/plugin_wire_taler-bank.c | 1284 +++++++++++++++++++ src/wire-plugins/plugin_wire_template.c | 345 ++++++ src/wire-plugins/test_ebics_wireformat.c | 80 ++ src/wire-plugins/test_wire_plugin.c | 186 +++ src/wire-plugins/test_wire_plugin.conf | 25 + .../test_wire_plugin_transactions_taler-bank.c | 338 +++++ .../test_wire_plugin_transactions_taler-bank.conf | 12 + src/wire/Makefile.am | 103 +- src/wire/plugin_wire_sepa.c | 960 --------------- src/wire/plugin_wire_template.c | 346 ------ src/wire/plugin_wire_test.c | 1302 -------------------- src/wire/test_sepa_wireformat.c | 120 -- src/wire/test_wire_plugin.c | 269 ---- src/wire/test_wire_plugin.conf | 21 - src/wire/test_wire_plugin_key.priv | 1 - src/wire/test_wire_plugin_sepa.json | 8 - src/wire/test_wire_plugin_test.json | 7 - src/wire/test_wire_plugin_transactions_test.c | 356 ------ src/wire/test_wire_plugin_transactions_test.conf | 15 - src/wire/wire-sepa.conf | 15 - src/wire/wire-test.conf | 22 - src/wire/wire.c | 76 -- src/wire/wire_helper.c | 57 + 110 files changed, 6150 insertions(+), 5391 deletions(-) create mode 100644 src/bank-lib/bank_api_parse.c create mode 100644 src/exchange-lib/test_exchange_api_home/.config/taler/account-1.json create mode 100644 src/exchange-lib/test_exchange_api_home/.config/taler/account-2.json create mode 100644 src/exchange-lib/test_exchange_api_home/.config/taler/x-taler-bank.json create mode 100644 src/exchange-lib/testing_api_trait_json.c delete mode 100644 src/exchange-tools/exchange-signkeys.conf create mode 100644 src/exchangedb/exchangedb_accounts.c create mode 100644 src/json/json_wire.c create mode 100644 src/util/crypto_wire.c create mode 100644 src/wire-plugins/Makefile.am create mode 100644 src/wire-plugins/plugin_wire_ebics.c create mode 100644 src/wire-plugins/plugin_wire_taler-bank.c create mode 100644 src/wire-plugins/plugin_wire_template.c create mode 100644 src/wire-plugins/test_ebics_wireformat.c create mode 100644 src/wire-plugins/test_wire_plugin.c create mode 100644 src/wire-plugins/test_wire_plugin.conf create mode 100644 src/wire-plugins/test_wire_plugin_transactions_taler-bank.c create mode 100644 src/wire-plugins/test_wire_plugin_transactions_taler-bank.conf delete mode 100644 src/wire/plugin_wire_sepa.c delete mode 100644 src/wire/plugin_wire_template.c delete mode 100644 src/wire/plugin_wire_test.c delete mode 100644 src/wire/test_sepa_wireformat.c delete mode 100644 src/wire/test_wire_plugin.c delete mode 100644 src/wire/test_wire_plugin.conf delete mode 100644 src/wire/test_wire_plugin_key.priv delete mode 100644 src/wire/test_wire_plugin_sepa.json delete mode 100644 src/wire/test_wire_plugin_test.json delete mode 100644 src/wire/test_wire_plugin_transactions_test.c delete mode 100644 src/wire/test_wire_plugin_transactions_test.conf delete mode 100644 src/wire/wire-sepa.conf delete mode 100644 src/wire/wire-test.conf create mode 100644 src/wire/wire_helper.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 2f158a1da..d2c925545 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,7 +22,7 @@ pkgcfg_DATA = \ EXTRA_DIST = \ taler.conf -SUBDIRS = include util json $(PQ_DIR) $(BANK_LIB) wire exchangedb exchange exchange-tools auditordb auditor +SUBDIRS = include util wire json $(PQ_DIR) $(BANK_LIB) wire-plugins exchangedb exchange exchange-tools auditordb auditor if HAVE_LIBCURL SUBDIRS += exchange-lib benchmark else diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index b8f6c624c..f5d185cf0 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -761,7 +761,7 @@ handle_reserve_in (void *cls, uint64_t rowid, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, - const json_t *sender_account_details, + const char *sender_account_details, const void *wire_reference, size_t wire_reference_size, struct GNUNET_TIME_Absolute execution_date) @@ -1193,7 +1193,7 @@ handle_reserve_closed (void *cls, const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *closing_fee, const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *receiver_account, + const char *receiver_account, const struct TALER_WireTransferIdentifierRawP *transfer_details) { struct ReserveContext *rc = cls; @@ -1773,11 +1773,6 @@ struct WireCheckContext */ struct GNUNET_TIME_Absolute date; - /** - * Wire method used for the transfer. - */ - const char *method; - /** * Database transaction status. */ @@ -2069,8 +2064,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, * @param cls a `struct WireCheckContext` * @param rowid which row in the table is the information from (for diagnostics) * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) - * @param wire_method which wire plugin was used for the transfer? * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param account_details where did we transfer the funds? * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param coin_pub which public key was this payment about @@ -2082,8 +2077,8 @@ static void wire_transfer_information_cb (void *cls, uint64_t rowid, const struct TALER_MerchantPublicKeyP *merchant_pub, - const char *wire_method, const struct GNUNET_HashCode *h_wire, + const json_t *account_details, struct GNUNET_TIME_Absolute exec_time, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -2097,6 +2092,29 @@ wire_transfer_information_cb (void *cls, struct TALER_EXCHANGEDB_TransactionList *tl; const struct TALER_CoinPublicInfo *coin; enum GNUNET_DB_QueryStatus qs; + struct GNUNET_HashCode hw; + + if (GNUNET_OK != + TALER_JSON_wire_signature_hash (account_details, + &hw)) + { + wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; + report_row_inconsistency ("aggregation", + rowid, + "failed to compute hash of given wire data"); + return; + } + if (0 != + memcmp (&hw, + h_wire, + sizeof (struct GNUNET_HashCode))) + { + wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; + report_row_inconsistency ("aggregation", + rowid, + "database contains wrong hash code for wire details"); + return; + } /* Obtain coin's transaction history */ qs = edb->get_coin_transactions (edb->cls, @@ -2183,8 +2201,9 @@ wire_transfer_information_cb (void *cls, tl); /* Check other details of wire transfer match */ - if (0 != strcmp (wire_method, - wcc->method)) + if (0 != memcmp (h_wire, + &wcc->h_wire, + sizeof (struct GNUNET_HashCode))) { wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; report_row_inconsistency ("aggregation", @@ -2360,12 +2379,12 @@ check_wire_out_cb (void *cls, { struct AggregationContext *ac = cls; struct WireCheckContext wcc; - json_t *method; struct TALER_WIRE_Plugin *plugin; const struct TALER_Amount *wire_fee; struct TALER_Amount final_amount; struct TALER_Amount exchange_gain; enum GNUNET_DB_QueryStatus qs; + char *method; /* should be monotonically increasing */ GNUNET_assert (rowid >= pp.last_wire_out_serial_id); @@ -2376,26 +2395,23 @@ check_wire_out_cb (void *cls, TALER_B2S (wtid), TALER_amount2s (amount), GNUNET_STRINGS_absolute_time_to_string (date)); - wcc.ac = ac; - method = json_object_get (wire, - "type"); - if ( (NULL == method) || - (! json_is_string (method)) ) + if (NULL == (method = TALER_JSON_wire_to_method (wire))) { report_row_inconsistency ("wire_out", rowid, - "specified wire address lacks type"); + "specified wire address lacks method"); return GNUNET_OK; } - wcc.method = json_string_value (method); + + wcc.ac = ac; wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; wcc.date = date; GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (amount->currency, &wcc.total_deposits)); if (GNUNET_OK != - TALER_JSON_hash (wire, - &wcc.h_wire)) + TALER_JSON_wire_signature_hash (wire, + &wcc.h_wire)) { GNUNET_break (0); return GNUNET_SYSERR; @@ -2409,6 +2425,7 @@ check_wire_out_cb (void *cls, { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); ac->qs = qs; + GNUNET_free (method); return GNUNET_SYSERR; } if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs) @@ -2417,17 +2434,19 @@ check_wire_out_cb (void *cls, report_row_inconsistency ("wire_out", rowid, "audit of associated transactions failed"); + GNUNET_free (method); return GNUNET_OK; } /* Subtract aggregation fee from total */ wire_fee = get_wire_fee (ac, - wcc.method, + method, date); if (NULL == wire_fee) { GNUNET_break (0); ac->qs = GNUNET_DB_STATUS_HARD_ERROR; + GNUNET_free (method); return GNUNET_SYSERR; } if (GNUNET_SYSERR == @@ -2440,18 +2459,20 @@ check_wire_out_cb (void *cls, &wcc.total_deposits, wire_fee, -1); + GNUNET_free (method); return GNUNET_OK; } /* Round down to amount supported by wire method */ plugin = get_wire_plugin (ac, - wcc.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)); @@ -3408,8 +3429,8 @@ deposit_cb (void *cls, dr.purpose.size = htonl (sizeof (dr)); dr.h_contract_terms = *h_contract_terms; if (GNUNET_OK != - TALER_JSON_hash (receiver_wire_account, - &dr.h_wire)) + TALER_JSON_wire_signature_hash (receiver_wire_account, + &dr.h_wire)) { GNUNET_break (0); cc->qs = GNUNET_DB_STATUS_HARD_ERROR; diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c index 55a2a05fb..d9c2d820b 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 Taler Systems SA + 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 @@ -40,6 +40,45 @@ */ #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS + +/** + * Information we keep for each supported account. + */ +struct WireAccount +{ + /** + * Accounts are kept in a DLL. + */ + struct WireAccount *next; + + /** + * Plugins are kept in a DLL. + */ + struct WireAccount *prev; + + /** + * Handle to the plugin. + */ + struct TALER_WIRE_Plugin *wire_plugin; + + /** + * Name of the section that configures this account. + */ + char *section_name; + + /** + * We should check for inbound transactions to this account. + */ + int watch_credit; + + /** + * We should check for outbound transactions from this account. + */ + int watch_debit; + +}; + + /** * Return value from main(). */ @@ -50,11 +89,6 @@ static int global_ret; */ static int restart; -/** - * Name of the wire plugin to load to access the exchange's bank account. - */ -static char *wire_plugin; - /** * Handle to access the exchange's database. */ @@ -103,11 +137,27 @@ static struct TALER_AUDITORDB_Session *asession; */ static struct TALER_MasterPublicKeyP master_pub; +/** + * Head of list of wire accounts we still need to look at. + */ +static struct WireAccount *wa_head; + +/** + * Tail of list of wire accounts we still need to look at. + */ +static struct WireAccount *wa_tail; + /** * Handle to the wire plugin for wire operations. */ static struct TALER_WIRE_Plugin *wp; +/** + * Name of the section that configures the account + * we are currently processing (matches #wp). + */ +static char *wp_section_name; + /** * Active wire request for the transaction history. */ @@ -289,7 +339,8 @@ free_rii (void *cls, GNUNET_CONTAINER_multihashmap_remove (in_map, key, rii)); - json_decref (rii->details.account_details); + GNUNET_free (rii->details.account_url); + GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */ GNUNET_free (rii); return GNUNET_OK; } @@ -314,7 +365,8 @@ free_roi (void *cls, GNUNET_CONTAINER_multihashmap_remove (out_map, key, roi)); - json_decref (roi->details.account_details); + GNUNET_free (roi->details.account_url); + GNUNET_free_non_null (roi->details.wtid_s); /* field not used (yet) */ GNUNET_free (roi); return GNUNET_OK; } @@ -328,6 +380,8 @@ free_roi (void *cls, static void do_shutdown (void *cls) { + struct WireAccount *wa; + if (NULL != report_row_inconsistencies) { json_t *report; @@ -407,6 +461,20 @@ do_shutdown (void *cls) TALER_WIRE_plugin_unload (wp); wp = NULL; } + if (NULL != wp_section_name) + { + GNUNET_free (wp_section_name); + wp_section_name = NULL; + } + while (NULL != (wa = wa_head)) + { + GNUNET_CONTAINER_DLL_remove (wa_head, + wa_tail, + wa); + TALER_WIRE_plugin_unload (wa->wire_plugin); + GNUNET_free (wa->section_name); + GNUNET_free (wa); + } if (NULL != adb) { TALER_AUDITORDB_plugin_unload (adb); @@ -470,6 +538,7 @@ commit (enum GNUNET_DB_QueryStatus qs) qs = adb->update_wire_auditor_progress (adb->cls, asession, &master_pub, + wp_section_name, &pp, in_wire_off, out_wire_off, @@ -478,6 +547,7 @@ commit (enum GNUNET_DB_QueryStatus qs) qs = adb->insert_wire_auditor_progress (adb->cls, asession, &master_pub, + wp_section_name, &pp, in_wire_off, out_wire_off, @@ -583,37 +653,44 @@ wire_out_cb (void *cls, amount)); return GNUNET_OK; } - if (! json_equal ((json_t *) wire, - roi->details.account_details)) { - /* Destination bank account is wrong in actual wire transfer, so - we should count the wire transfer as entirely spurious, and - additionally consider the justified wire transfer as missing. */ - report (report_wire_out_inconsistencies, - json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}", - "row", (json_int_t) rowid, - "amount_wired", TALER_JSON_from_amount (&roi->details.amount), - "amount_justified", TALER_JSON_from_amount (&zero), - "wtid", GNUNET_JSON_from_data_auto (wtid), - "timestamp", GNUNET_STRINGS_absolute_time_to_string (date), - "diagnostic", "recevier account missmatch")); - GNUNET_break (GNUNET_OK == - TALER_amount_add (&total_bad_amount_out_plus, - &total_bad_amount_out_plus, - &roi->details.amount)); - report (report_wire_out_inconsistencies, - json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}", - "row", (json_int_t) rowid, - "amount_wired", TALER_JSON_from_amount (&zero), - "amount_justified", TALER_JSON_from_amount (amount), - "wtid", GNUNET_JSON_from_data_auto (wtid), - "timestamp", GNUNET_STRINGS_absolute_time_to_string (date), - "diagnostic", "receiver account missmatch")); - GNUNET_break (GNUNET_OK == - TALER_amount_add (&total_bad_amount_out_minus, - &total_bad_amount_out_minus, - amount)); - goto cleanup; + char *payto_url; + + payto_url = TALER_JSON_wire_to_payto (wire); + if (0 != strcasecmp (payto_url, + roi->details.account_url)) + { + /* Destination bank account is wrong in actual wire transfer, so + we should count the wire transfer as entirely spurious, and + additionally consider the justified wire transfer as missing. */ + report (report_wire_out_inconsistencies, + json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}", + "row", (json_int_t) rowid, + "amount_wired", TALER_JSON_from_amount (&roi->details.amount), + "amount_justified", TALER_JSON_from_amount (&zero), + "wtid", GNUNET_JSON_from_data_auto (wtid), + "timestamp", GNUNET_STRINGS_absolute_time_to_string (date), + "diagnostic", "recevier account missmatch")); + GNUNET_break (GNUNET_OK == + TALER_amount_add (&total_bad_amount_out_plus, + &total_bad_amount_out_plus, + &roi->details.amount)); + report (report_wire_out_inconsistencies, + json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}", + "row", (json_int_t) rowid, + "amount_wired", TALER_JSON_from_amount (&zero), + "amount_justified", TALER_JSON_from_amount (amount), + "wtid", GNUNET_JSON_from_data_auto (wtid), + "timestamp", GNUNET_STRINGS_absolute_time_to_string (date), + "diagnostic", "receiver account missmatch")); + GNUNET_break (GNUNET_OK == + TALER_amount_add (&total_bad_amount_out_minus, + &total_bad_amount_out_minus, + amount)); + GNUNET_free (payto_url); + goto cleanup; + } + GNUNET_free (payto_url); } if (0 != TALER_amount_cmp (&roi->details.amount, amount)) @@ -764,6 +841,16 @@ wire_missing_cb (void *cls, } +/** + * Start processing the next wire account. + * Shuts down if we are done. + * + * @param cls NULL + */ +static void +process_next_account (void *cls); + + /** * Go over the "wire_out" table of the exchange and * verify that all wire outs are in that table. @@ -818,9 +905,8 @@ check_exchange_wire_out () } pp.last_timestamp = next_timestamp; - /* conclude with: */ - commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT); - GNUNET_SCHEDULER_shutdown (); + /* continue with next account: */ + process_next_account (NULL); } @@ -892,7 +978,7 @@ history_debit_cb (void *cls, roi->details.amount = details->amount; roi->details.execution_date = details->execution_date; roi->details.wtid = details->wtid; - roi->details.account_details = json_incref ((json_t *) details->account_details); + roi->details.account_url = GNUNET_strdup (details->account_url); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (out_map, &roi->subject_hash, @@ -936,6 +1022,7 @@ process_debits () out_map = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_YES); hh = wp->get_history (wp->cls, + wp_section_name, TALER_BANK_DIRECTION_DEBIT, out_wire_off, wire_off_size, @@ -965,7 +1052,7 @@ process_debits () * @param rowid unique serial ID for the refresh session in our DB * @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 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 execution_date when did we receive the funds @@ -976,7 +1063,7 @@ reserve_in_cb (void *cls, uint64_t rowid, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, - const json_t *sender_account_details, + const char *sender_url, const void *wire_reference, size_t wire_reference_size, struct GNUNET_TIME_Absolute execution_date) @@ -997,7 +1084,7 @@ reserve_in_cb (void *cls, memcpy (&rii->details.wtid, reserve_pub, sizeof (*reserve_pub)); - rii->details.account_details = json_incref ((json_t *) sender_account_details); + rii->details.account_url = GNUNET_strdup (sender_url); rii->rowid = rowid; if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (in_map, @@ -1011,7 +1098,8 @@ reserve_in_cb (void *cls, "row", (json_int_t) rowid, "wire_offset_hash", GNUNET_JSON_from_data_auto (&rii->row_off_hash), "diagnostic", "duplicate wire offset")); - json_decref (rii->details.account_details); + GNUNET_free (rii->details.account_url); + GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */ GNUNET_free (rii); return GNUNET_OK; } @@ -1228,8 +1316,8 @@ history_credit_cb (void *cls, } goto cleanup; } - if (! json_equal (details->account_details, - rii->details.account_details)) + if (0 != strcasecmp (details->account_url, + rii->details.account_url)) { report (report_missattribution_in_inconsistencies, json_pack ("{s:s, s:o, s:o}", @@ -1266,6 +1354,167 @@ history_credit_cb (void *cls, /* ***************************** Setup logic ************************ */ +/** + * Start processing the next wire account. + * Shuts down if we are done. + * + * @param cls NULL + */ +static void +process_next_account (void *cls) +{ + struct WireAccount *wa; + enum GNUNET_DB_QueryStatus qs; + int ret; + + (void) cls; + if (NULL == (wa = wa_head)) + { + commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_CONTAINER_DLL_remove (wa_head, + wa_tail, + wa); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting audit of account `%s'\n", + wa->section_name); + /* setup globals */ + if (NULL != wp) + TALER_WIRE_plugin_unload (wp); + wp = wa->wire_plugin; + GNUNET_free_non_null (wp_section_name); + wp_section_name = wa->section_name; + GNUNET_free (wa); + + ret = adb->start (adb->cls, + asession); + if (GNUNET_OK != ret) + { + GNUNET_break (0); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + edb->preflight (edb->cls, + esession); + ret = edb->start (edb->cls, + esession, + "wire auditor"); + if (GNUNET_OK != ret) + { + GNUNET_break (0); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + qsx = adb->get_wire_auditor_progress (adb->cls, + asession, + &master_pub, + wp_section_name, + &pp, + &in_wire_off, + &out_wire_off, + &wire_off_size); + if (0 > qsx) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + _("First analysis using this auditor, starting audit from scratch\n")); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Resuming audit at %llu/%llu\n"), + (unsigned long long) pp.last_reserve_in_serial_id, + (unsigned long long) pp.last_wire_out_serial_id); + } + + in_map = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + qs = edb->select_reserves_in_above_serial_id (edb->cls, + esession, + pp.last_reserve_in_serial_id, + &reserve_in_cb, + NULL); + if (0 > qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "No new incoming transactions available, skipping CREDIT phase\n"); + process_debits (); + return; + } + hh = wp->get_history (wp->cls, + wp_section_name, + TALER_BANK_DIRECTION_CREDIT, + in_wire_off, + wire_off_size, + INT64_MAX, + &history_credit_cb, + NULL); + if (NULL == hh) + { + fprintf (stderr, + "Failed to obtain bank transaction history\n"); + commit (GNUNET_DB_STATUS_HARD_ERROR); + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * Function called with information about a wire account. Adds the + * account to our list for processing (if it is enabled and we can + * load the plugin). + * + * @param cls closure, NULL + * @param ai account information + */ +static void +process_account_cb (void *cls, + const struct TALER_EXCHANGEDB_AccountInfo *ai) +{ + struct WireAccount *wa; + struct TALER_WIRE_Plugin *wp; + + 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; + } + 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; + GNUNET_CONTAINER_DLL_insert (wa_head, + wa_tail, + wa); +} + + /** * Main function that will be run. * @@ -1281,8 +1530,6 @@ run (void *cls, const struct GNUNET_CONFIGURATION_Handle *c) { static const struct TALER_MasterPublicKeyP zeromp; - enum GNUNET_DB_QueryStatus qs; - int ret; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching auditor\n"); @@ -1390,40 +1637,6 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - wp = TALER_WIRE_plugin_load (cfg, - wire_plugin); - if (NULL == wp) - { - fprintf (stderr, - "Failed to load wire plugin `%s'\n", - wire_plugin); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting audit\n"); - ret = adb->start (adb->cls, - asession); - if (GNUNET_OK != ret) - { - GNUNET_break (0); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - edb->preflight (edb->cls, - esession); - ret = edb->start (edb->cls, - esession, - "wire auditor"); - if (GNUNET_OK != ret) - { - GNUNET_break (0); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } GNUNET_assert (NULL != (report_wire_out_inconsistencies = json_array ())); GNUNET_assert (NULL != @@ -1462,71 +1675,9 @@ run (void *cls, GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (currency, &zero)); - - qsx = adb->get_wire_auditor_progress (adb->cls, - asession, - &master_pub, - &pp, - &in_wire_off, - &out_wire_off, - &wire_off_size); - if (0 > qsx) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - _("First analysis using this auditor, starting audit from scratch\n")); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Resuming audit at %llu/%llu\n"), - (unsigned long long) pp.last_reserve_in_serial_id, - (unsigned long long) pp.last_wire_out_serial_id); - } - - in_map = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - qs = edb->select_reserves_in_above_serial_id (edb->cls, - esession, - pp.last_reserve_in_serial_id, - &reserve_in_cb, - NULL); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "No new incoming transactions available, skipping CREDIT phase\n"); - process_debits (); - return; - } - hh = wp->get_history (wp->cls, - TALER_BANK_DIRECTION_CREDIT, - in_wire_off, - wire_off_size, - INT64_MAX, - &history_credit_cb, - NULL); - if (NULL == hh) - { - fprintf (stderr, - "Failed to obtain bank transaction history\n"); - commit (GNUNET_DB_STATUS_HARD_ERROR); - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } + TALER_EXCHANGEDB_find_accounts (cfg, + &process_account_cb, + NULL); } @@ -1552,12 +1703,6 @@ main (int argc, "restart", "restart audit from the beginning (required on first run)", &restart), - GNUNET_GETOPT_option_mandatory - (GNUNET_GETOPT_option_string ('w', - "wire", - "PLUGINNAME", - "name of the wire plugin to use", - &wire_plugin)), GNUNET_GETOPT_OPTION_END }; diff --git a/src/auditordb/auditordb-postgres.conf b/src/auditordb/auditordb-postgres.conf index 4fe2064f1..9a6d39884 100644 --- a/src/auditordb/auditordb-postgres.conf +++ b/src/auditordb/auditordb-postgres.conf @@ -1,3 +1,3 @@ [auditordb-postgres] # Argument for Postgres for how to connect to the database. -DB_CONN_STR = "postgres:///taler" +CONFIG = "postgres:///taler" diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c index 2f881ae29..13fbbc2f2 100644 --- a/src/auditordb/plugin_auditordb_postgres.c +++ b/src/auditordb/plugin_auditordb_postgres.c @@ -231,6 +231,7 @@ postgres_create_tables (void *cls) ")"), GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS wire_auditor_progress" "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)" + ",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" ",last_timestamp INT8 NOT NULL" @@ -521,13 +522,14 @@ postgres_prepare (PGconn *db_conn) GNUNET_PQ_make_prepare ("wire_auditor_progress_insert", "INSERT INTO wire_auditor_progress " "(master_pub" + ",account_name" ",last_wire_reserve_in_serial_id" ",last_wire_wire_out_serial_id" ",last_timestamp" ",wire_in_off" ",wire_out_off" - ") VALUES ($1,$2,$3,$4,$5,$6);", - 6), + ") VALUES ($1,$2,$3,$4,$5,$6,$7);", + 7), /* Used in #postgres_update_wire_auditor_progress() */ GNUNET_PQ_make_prepare ("wire_auditor_progress_update", "UPDATE wire_auditor_progress SET " @@ -536,8 +538,8 @@ postgres_prepare (PGconn *db_conn) ",last_timestamp=$3" ",wire_in_off=$4" ",wire_out_off=$5" - " WHERE master_pub=$6", - 6), + " WHERE master_pub=$6 AND account_name=$7", + 7), /* Used in #postgres_get_wire_auditor_progress() */ GNUNET_PQ_make_prepare ("wire_auditor_progress_select", "SELECT" @@ -547,8 +549,8 @@ postgres_prepare (PGconn *db_conn) ",wire_in_off" ",wire_out_off" " FROM wire_auditor_progress" - " WHERE master_pub=$1;", - 1), + " WHERE master_pub=$1 AND account_name=$2;", + 2), /* Used in #postgres_insert_reserve_info() */ GNUNET_PQ_make_prepare ("auditor_reserves_insert", "INSERT INTO auditor_reserves " @@ -1342,6 +1344,7 @@ postgres_get_auditor_progress (void *cls, * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param master_pub master key of the exchange + * @param account_name name of the wire account we are auditing * @param pp where is the auditor in processing * @return transaction status code */ @@ -1349,6 +1352,7 @@ static enum GNUNET_DB_QueryStatus postgres_insert_wire_auditor_progress (void *cls, struct TALER_AUDITORDB_Session *session, const struct TALER_MasterPublicKeyP *master_pub, + const char *account_name, const struct TALER_AUDITORDB_WireProgressPoint *pp, const void *in_wire_off, const void *out_wire_off, @@ -1356,6 +1360,7 @@ postgres_insert_wire_auditor_progress (void *cls, { 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), TALER_PQ_query_param_absolute_time (&pp->last_timestamp), @@ -1379,6 +1384,7 @@ postgres_insert_wire_auditor_progress (void *cls, * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param master_pub master key of the exchange + * @param account_name name of the wire account we are auditing * @param pp where is the auditor in processing * @return transaction status code */ @@ -1386,6 +1392,7 @@ static enum GNUNET_DB_QueryStatus postgres_update_wire_auditor_progress (void *cls, struct TALER_AUDITORDB_Session *session, const struct TALER_MasterPublicKeyP *master_pub, + const char *account_name, const struct TALER_AUDITORDB_WireProgressPoint *pp, const void *in_wire_off, const void *out_wire_off, @@ -1400,6 +1407,7 @@ postgres_update_wire_auditor_progress (void *cls, GNUNET_PQ_query_param_fixed_size (out_wire_off, wire_off_size), GNUNET_PQ_query_param_auto_from_type (master_pub), + GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_end }; @@ -1415,6 +1423,7 @@ postgres_update_wire_auditor_progress (void *cls, * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param master_pub master key of the exchange + * @param account_name name of the wire account we are auditing * @param[out] pp set to where the auditor is in processing * @return transaction status code */ @@ -1422,6 +1431,7 @@ static enum GNUNET_DB_QueryStatus postgres_get_wire_auditor_progress (void *cls, struct TALER_AUDITORDB_Session *session, const struct TALER_MasterPublicKeyP *master_pub, + const char *account_name, struct TALER_AUDITORDB_WireProgressPoint *pp, void **in_wire_off, void **out_wire_off, @@ -1431,6 +1441,7 @@ postgres_get_wire_auditor_progress (void *cls, 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), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -2594,12 +2605,12 @@ libtaler_plugin_auditordb_postgres_init (void *cls) if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "auditordb-postgres", - "db_conn_str", + "CONFIG", &pg->connection_cfg_str)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "auditordb-postgres", - "db_conn_str"); + "CONFIG"); GNUNET_free (pg); return NULL; } diff --git a/src/auditordb/test-auditor-db-postgres.conf b/src/auditordb/test-auditor-db-postgres.conf index 5c1e7fbc1..fd8ffbfdd 100644 --- a/src/auditordb/test-auditor-db-postgres.conf +++ b/src/auditordb/test-auditor-db-postgres.conf @@ -4,4 +4,4 @@ DB = postgres [auditordb-postgres] # Argument for Postgres for how to connect to the database. -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index abe820a64..9f2740b24 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -32,7 +32,8 @@ 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_reject.c \ + bank_api_parse.c libtalerbank_la_LIBADD = \ $(top_builddir)/src/json/libtalerjson.la \ @@ -148,6 +149,7 @@ test_bank_api_new_SOURCES = \ test_bank_api_new_LDADD = \ $(top_builddir)/src/exchange-lib/libtalertesting.la \ + $(top_builddir)/src/json/libtalerjson.la \ libtalerbanktesting.la \ -ltalerexchange \ -lgnunetutil \ diff --git a/src/bank-lib/bank.conf b/src/bank-lib/bank.conf index f6e4e7fe0..9befeba9e 100644 --- a/src/bank-lib/bank.conf +++ b/src/bank-lib/bank.conf @@ -1,8 +1,11 @@ [taler] currency = KUDOS +[account-1] +URL = payto://x-taler-bank/localhost:8081/1 + [bank] -http_port = 8081 +HTTP_PORT = 8081 [exchange-wire-test] bank_url = http://localhost:8081/ diff --git a/src/bank-lib/bank_api_history.c b/src/bank-lib/bank_api_history.c index d2035e881..17cd9fd07 100644 --- a/src/bank-lib/bank_api_history.c +++ b/src/bank-lib/bank_api_history.c @@ -136,10 +136,12 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh, GNUNET_JSON_parse_free (hist_spec); return GNUNET_SYSERR; } - td.account_details = json_pack ("{s:s, s:s, s:I}", - "type", "test", - "bank_url", hh->bank_base_url, - "account_number", (json_int_t) other_account); + GNUNET_asprintf (&td.account_url, + ('/' == hh->bank_base_url[strlen(hh->bank_base_url)-1]) + ? "payto://x-taler-bank/%s%llu" + : "payto://x-taler-bank/%s/%llu", + hh->bank_base_url, + (unsigned long long) other_account); hh->hcb (hh->hcb_cls, MHD_HTTP_OK, TALER_EC_NONE, @@ -147,7 +149,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh, row_id, &td, transaction); - json_decref (td.account_details); + GNUNET_free (td.account_url); GNUNET_JSON_parse_free (hist_spec); } return GNUNET_OK; diff --git a/src/bank-lib/bank_api_parse.c b/src/bank-lib/bank_api_parse.c new file mode 100644 index 000000000..04cf7b85f --- /dev/null +++ b/src/bank-lib/bank_api_parse.c @@ -0,0 +1,133 @@ +/* + 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/bank_api_parse.c + * @brief Convenience function to parse authentication configuration + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_bank_service.h" + + +/** + * Parse configuration section with bank authentication data. + * + * @param cfg configuration to parse + * @param section the section with the configuration data + * @param auth[out] set to the configuration data found + * @return #GNUNET_OK on success + */ +int +TALER_BANK_auth_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + struct TALER_BANK_AuthenticationData *auth) +{ + const struct { + const char *m; + enum TALER_BANK_AuthenticationMethod e; + } methods[] = { + { "NONE", TALER_BANK_AUTH_NONE }, + { "BASIC", TALER_BANK_AUTH_BASIC }, + { NULL, TALER_BANK_AUTH_NONE } + }; + char *method; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "TALER_BANK_AUTH_METHOD", + &method)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "TALER_BANK_AUTH_METHOD"); + return GNUNET_SYSERR; + } + for (unsigned int i=0; NULL != methods[i].m;i++) + { + if (0 == strcasecmp (method, + methods[i].m)) + { + switch (methods[i].e) + { + case TALER_BANK_AUTH_NONE: + auth->method = TALER_BANK_AUTH_NONE; + return GNUNET_OK; + case TALER_BANK_AUTH_BASIC: + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "USERNAME", + &auth->details.basic.username)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "USERNAME"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "PASSWORD", + &auth->details.basic.password)) + { + GNUNET_free (auth->details.basic.username); + auth->details.basic.username = NULL; + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "USERNAME"); + return GNUNET_SYSERR; + } + auth->method = TALER_BANK_AUTH_BASIC; + return GNUNET_OK; + } + } + } + return GNUNET_SYSERR; +} + + +/** + * Free memory inside of @a auth (but not auth itself). + * Dual to #TALER_BANK_auth_parse_cfg(). + * + * @param auth authentication data to free + */ +void +TALER_BANK_auth_free (struct TALER_BANK_AuthenticationData *auth) +{ + switch (auth->method) + { + case TALER_BANK_AUTH_NONE: + break; + case TALER_BANK_AUTH_BASIC: + if (NULL != auth->details.basic.username) + { + GNUNET_free (auth->details.basic.username); + auth->details.basic.username = NULL; + } + if (NULL != auth->details.basic.password) + { + GNUNET_free (auth->details.basic.password); + auth->details.basic.password = NULL; + } + break; + } +} + + +/* end of bank_api_parse.c */ diff --git a/src/bank-lib/test_bank_api_with_fakebank_new.c b/src/bank-lib/test_bank_api_with_fakebank_new.c index 1a70f5714..002010b29 100644 --- a/src/bank-lib/test_bank_api_with_fakebank_new.c +++ b/src/bank-lib/test_bank_api_with_fakebank_new.c @@ -210,9 +210,10 @@ main (int argc, "DEBUG", NULL); if (NULL == - (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE))) + (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE, + "account-1"))) return 77; - + return (GNUNET_OK == TALER_TESTING_setup (&run, NULL, CONFIG_FILE, diff --git a/src/bank-lib/test_bank_interpreter.c b/src/bank-lib/test_bank_interpreter.c index 40adea54f..e503bd8dc 100644 --- a/src/bank-lib/test_bank_interpreter.c +++ b/src/bank-lib/test_bank_interpreter.c @@ -326,15 +326,10 @@ build_history (struct InterpreterState *is, h[total].direction = TALER_BANK_DIRECTION_CREDIT; if (GNUNET_YES == cancelled) h[total].direction |= TALER_BANK_DIRECTION_CANCEL; - h[total].details.account_details - = json_pack ("{s:s, s:s, s:I}", - "type", - "test", - "bank_url", - "http://localhost:8080", - "account_number", - (json_int_t) pos->details.admin_add_incoming.debit_account_no); - GNUNET_assert (NULL != h[total].details.account_details); + GNUNET_asprintf (&h[total].details.account_url, + "payto://x-taler-bank/%s/%llu", + "http://localhost:8080", + (unsigned long long) pos->details.admin_add_incoming.debit_account_no); } if ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_DEBIT)) && (cmd->details.history.account_number == @@ -343,15 +338,10 @@ build_history (struct InterpreterState *is, h[total].direction = TALER_BANK_DIRECTION_DEBIT; if (GNUNET_YES == cancelled) h[total].direction |= TALER_BANK_DIRECTION_CANCEL; - h[total].details.account_details - = json_pack ("{s:s, s:s, s:I}", - "type", - "test", - "bank_url", - "http://localhost:8080", - "account_number", - (json_int_t) pos->details.admin_add_incoming.credit_account_no); - GNUNET_assert (NULL != h[total].details.account_details); + GNUNET_asprintf (&h[total].details.account_url, + "payto://x-taler-bank/%s/%llu", + "http://localhost:8080", + (unsigned long long) pos->details.admin_add_incoming.credit_account_no); } if ( ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) && (cmd->details.history.account_number == @@ -398,10 +388,6 @@ print_expected (struct History *h, "Expected history:\n"); for (uint64_t i=0;iwire_transfer_subject)) || (0 != TALER_amount_cmp (&h[off].details.amount, &details->amount)) || - (1 != json_equal (h[off].details.account_details, - details->account_details)) ) + (0 != strcasecmp (h[off].details.account_url, + details->account_url)) ) { GNUNET_break (0); print_expected (h, total, off); diff --git a/src/bank-lib/testing_api_cmd_history.c b/src/bank-lib/testing_api_cmd_history.c index 30090bf8c..e94009fb3 100644 --- a/src/bank-lib/testing_api_cmd_history.c +++ b/src/bank-lib/testing_api_cmd_history.c @@ -99,6 +99,7 @@ history_traits (void *cls, return GNUNET_SYSERR; } + /** * Test if the /admin/add/incoming transaction at offset @a off * has been /rejected. @@ -115,7 +116,8 @@ test_cancelled (struct TALER_TESTING_Interpreter *is, const struct TALER_TESTING_Command *current_cmd; current_cmd = &is->commands[off]; - TALER_LOG_INFO ("Is `%s' rejected?\n", current_cmd->label); + TALER_LOG_INFO ("Is `%s' rejected?\n", + current_cmd->label); for (unsigned int i=0;iip;i++) { const struct TALER_TESTING_Command *c = &is->commands[i]; @@ -140,6 +142,7 @@ test_cancelled (struct TALER_TESTING_Interpreter *is, return GNUNET_NO; } + /** * Free history @a h of length @a h_len. * @@ -153,11 +156,12 @@ free_history (struct History *h, for (uint64_t off = 0;offbank_url, - "account_number", - (json_int_t) *debit_account_no); - GNUNET_assert (NULL != h[total].details.account_details); + GNUNET_asprintf (&h[total].details.account_url, + "payto://x-taler-bank/%s/%llu", + hs->bank_url, + (unsigned long long) *debit_account_no); } if ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) && (hs->account_no == *debit_account_no)) @@ -425,15 +419,10 @@ build_history (struct TALER_TESTING_Interpreter *is, h[total].direction = TALER_BANK_DIRECTION_DEBIT; if (GNUNET_YES == cancelled) h[total].direction |= TALER_BANK_DIRECTION_CANCEL; - h[total].details.account_details - = json_pack ("{s:s, s:s, s:I}", - "type", - "test", - "bank_url", - hs->bank_url, - "account_number", - (json_int_t) *credit_account_no); - GNUNET_assert (NULL != h[total].details.account_details); + GNUNET_asprintf (&h[total].details.account_url, + "payto://x-taler-bank/%s/%llu", + hs->bank_url, + (unsigned long long) *credit_account_no); } if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && (hs->account_no == *credit_account_no)) || @@ -533,8 +522,8 @@ check_result (struct TALER_TESTING_Interpreter *is, details->wire_transfer_subject)) || (0 != TALER_amount_cmp (&h[off].details.amount, &details->amount)) || - (1 != json_equal (h[off].details.account_details, - details->account_details)) ) + (0 != strcasecmp (h[off].details.account_url, + details->account_url)) ) { GNUNET_break (0); print_expected (h, total, off); diff --git a/src/bank-lib/testing_api_cmd_reject.c b/src/bank-lib/testing_api_cmd_reject.c index c01c27d87..2eb6d4f9b 100644 --- a/src/bank-lib/testing_api_cmd_reject.c +++ b/src/bank-lib/testing_api_cmd_reject.c @@ -127,7 +127,7 @@ reject_run (void *cls, (GNUNET_OK == TALER_TESTING_GET_TRAIT_ROW_ID (deposit_cmd, &row_id)); TALER_LOG_INFO ("Account %llu rejects deposit\n", - *credit_account); + (unsigned long long) *credit_account); rs->rh = TALER_BANK_reject (is->ctx, rs->bank_url, &AUTHS[*credit_account -1], @@ -180,7 +180,7 @@ TALER_TESTING_cmd_bank_reject (const char *label, rs = GNUNET_new (struct RejectState); rs->bank_url = bank_url; rs->deposit_reference = deposit_reference; - + cmd.cls = rs; cmd.run = &reject_run; cmd.cleanup = &reject_cleanup; diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index fbdfe825c..3fd31eda7 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -895,8 +895,8 @@ spend_coin (struct Coin *coin, dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.h_contract_terms = h_contract_terms; GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (merchant_details, - &dr.h_wire)); + TALER_JSON_wire_signature_hash (merchant_details, + &dr.h_wire)); dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); diff --git a/src/benchmark/taler-exchange-benchmark.conf b/src/benchmark/taler-exchange-benchmark.conf index 453782c7c..887673850 100644 --- a/src/benchmark/taler-exchange-benchmark.conf +++ b/src/benchmark/taler-exchange-benchmark.conf @@ -20,22 +20,20 @@ DB = postgres MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG [exchangedb-postgres] -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" -[exchange-wire-test] -# Enable 'test' for testing of the actual coin operations. -ENABLE = YES +[account-exchange] # What is the main website of the bank? # (Not used unless the aggregator is run.) -BANK_URL = "http://localhost:8082/" -# From which account at the 'bank' should outgoing wire transfers be made? -BANK_ACCOUNT_NUMBER = 2 +URL = "payto://x-taler-bank/localhost:8082/2" # This is the response we give out for the /wire request. It provides # wallets with the bank information for transfers to the exchange. -TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account.json + +[fees-x-taler-bank] WIRE-FEE-2017 = KUDOS:0.01 WIRE-FEE-2018 = KUDOS:0.01 WIRE-FEE-2019 = KUDOS:0.01 diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am index 5e0833b31..57c935088 100644 --- a/src/exchange-lib/Makefile.am +++ b/src/exchange-lib/Makefile.am @@ -61,6 +61,7 @@ libtalertesting_la_SOURCES = \ testing_api_trait_coin_priv.c \ testing_api_trait_denom_pub.c \ testing_api_trait_denom_sig.c \ + testing_api_trait_json.c \ testing_api_trait_process.c \ testing_api_trait_reserve_priv.c \ testing_api_trait_number.c \ @@ -69,8 +70,8 @@ libtalertesting_la_SOURCES = \ testing_api_trait_key_peer.c \ testing_api_trait_wtid.c \ testing_api_trait_amount.c - libtalertesting_la_LIBADD = \ + $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/bank-lib/libtalerfakebank.la \ diff --git a/src/exchange-lib/exchange_api_deposit.c b/src/exchange-lib/exchange_api_deposit.c index 76e3e4da9..7e499a434 100644 --- a/src/exchange-lib/exchange_api_deposit.c +++ b/src/exchange-lib/exchange_api_deposit.c @@ -416,8 +416,8 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle *exchange, MAH_handle_is_ready (exchange)); /* initialize h_wire */ if (GNUNET_OK != - TALER_JSON_hash (wire_details, - &h_wire)) + TALER_JSON_wire_signature_hash (wire_details, + &h_wire)) { GNUNET_break (0); return NULL; diff --git a/src/exchange-lib/exchange_api_reserve.c b/src/exchange-lib/exchange_api_reserve.c index 72429d4e8..86a83fdb0 100644 --- a/src/exchange-lib/exchange_api_reserve.c +++ b/src/exchange-lib/exchange_api_reserve.c @@ -139,7 +139,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, if (0 == strcasecmp (type, "DEPOSIT")) { - json_t *wire_account; + const char *wire_url; void *wire_reference; size_t wire_reference_size; struct GNUNET_TIME_Absolute timestamp; @@ -150,8 +150,8 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, &wire_reference_size), GNUNET_JSON_spec_absolute_time ("timestamp", ×tamp), - GNUNET_JSON_spec_json ("sender_account_details", - &wire_account), + GNUNET_JSON_spec_string ("sender_account_url", + &wire_url), GNUNET_JSON_spec_end() }; @@ -173,7 +173,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, GNUNET_break_op (0); return GNUNET_SYSERR; } - rhistory[off].details.in_details.sender_account_details = wire_account; + rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url); rhistory[off].details.in_details.wire_reference = wire_reference; rhistory[off].details.in_details.wire_reference_size = wire_reference_size; rhistory[off].details.in_details.timestamp = timestamp; @@ -361,8 +361,8 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange, TALER_amount_hton (&rcc.closing_amount, &amount); if (GNUNET_OK != - TALER_JSON_hash (rhistory[off].details.close_details.receiver_account_details, - &rcc.h_wire)) + TALER_JSON_wire_signature_hash (rhistory[off].details.close_details.receiver_account_details, + &rcc.h_wire)) { GNUNET_break (0); return GNUNET_SYSERR; @@ -440,8 +440,7 @@ free_rhistory (struct TALER_EXCHANGE_ReserveHistory *rhistory, { case TALER_EXCHANGE_RTT_DEPOSIT: GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference); - if (NULL != rhistory[i].details.in_details.sender_account_details) - json_decref (rhistory[i].details.in_details.sender_account_details); + GNUNET_free_non_null (rhistory[i].details.in_details.sender_url); break; case TALER_EXCHANGE_RTT_WITHDRAWAL: break; diff --git a/src/exchange-lib/exchange_api_wire.c b/src/exchange-lib/exchange_api_wire.c index f1056fdd2..12789b2bc 100644 --- a/src/exchange-lib/exchange_api_wire.c +++ b/src/exchange-lib/exchange_api_wire.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V. + Copyright (C) 2014-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 @@ -27,6 +27,7 @@ #include #include "taler_exchange_service.h" #include "taler_json_lib.h" +#include "taler_wire_lib.h" #include "taler_signatures.h" #include "taler_wire_plugin.h" #include "exchange_api_handle.h" @@ -63,66 +64,133 @@ struct TALER_EXCHANGE_WireHandle */ void *cb_cls; +}; + + +/** + * List of wire fees by method. + */ +struct FeeMap +{ /** - * Set to the "methods" JSON array returned by the - * /wire request. + * Next entry in list. */ - json_t *methods; + struct FeeMap *next; /** - * Current iteration offset in the @e methods array. + * Wire method this fee structure is for. */ - unsigned int methods_off; + char *method; + /** + * Array of wire fees, also linked list, but allocated + * only once. + */ + struct TALER_EXCHANGE_WireAggregateFees *fee_list; }; /** - * Verify that the signature on the "200 OK" response - * for /wire/METHOD from the exchange is valid. + * Frees @a fm. * - * @param wh wire handle with key material - * @param method method to verify the reply for - * @param json json reply with the signature - * @return #GNUNET_SYSERR if @a json is invalid, - * #GNUNET_NO if the method is unknown, - * #GNUNET_OK if the json is valid + * @param fm memory to release */ -static int -verify_wire_method_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh, - const char *method, - const json_t *json) +static void +free_fees (struct FeeMap *fm) { - const struct TALER_EXCHANGE_Keys *key_state; - struct TALER_WIRE_Plugin *plugin; - char *lib_name; - char *emsg; - enum TALER_ErrorCode ec; - - key_state = TALER_EXCHANGE_get_keys (wh->exchange); - (void) GNUNET_asprintf (&lib_name, - "libtaler_plugin_wire_%s", - method); - plugin = GNUNET_PLUGIN_load (lib_name, - NULL); - if (NULL == plugin) + while (NULL != fm) { - GNUNET_free (lib_name); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Wire transfer method `%s' not supported\n", - method); - return GNUNET_NO; + struct FeeMap *fe = fm->next; + + GNUNET_free (fm->fee_list); + GNUNET_free (fm->method); + GNUNET_free (fm); + fm = fe; + } +} + + +/** + * Parse wire @a fees and return map. + * + * @param fees json AggregateTransferFee to parse + * @return NULL on error + */ +static struct FeeMap * +parse_fees (json_t *fees) +{ + struct FeeMap *fm = NULL; + const char *key; + json_t *fee_array; + + json_object_foreach (fees, key, fee_array) { + struct FeeMap *fe = GNUNET_new (struct FeeMap); + int len; + unsigned int idx; + json_t *fee; + + if (0 == (len = json_array_size (fee_array))) + { + GNUNET_break_op (0); + GNUNET_free (fe); + continue; /* skip */ + } + fe->method = GNUNET_strdup (key); + fe->next = fm; + fe->fee_list = GNUNET_new_array (len, + struct TALER_EXCHANGE_WireAggregateFees); + fm = fe; + json_array_foreach (fee_array, idx, fee) + { + struct TALER_EXCHANGE_WireAggregateFees *wa = &fe->fee_list[idx]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("sig", + &wa->master_sig), + TALER_JSON_spec_amount ("wire_fee", + &wa->wire_fee), + TALER_JSON_spec_amount ("closing_fee", + &wa->closing_fee), + GNUNET_JSON_spec_absolute_time ("start_date", + &wa->start_date), + GNUNET_JSON_spec_absolute_time ("end_date", + &wa->end_date), + GNUNET_JSON_spec_end() + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (fee, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + free_fees (fm); + return NULL; + } + if (idx < len) + wa->next = &fe->fee_list[idx + 1]; + } } - plugin->library_name = lib_name; - ec = plugin->wire_validate (plugin->cls, - json, - &key_state->master_pub, - &emsg); - GNUNET_free_non_null (emsg); - GNUNET_PLUGIN_unload (lib_name, - plugin); - GNUNET_free (lib_name); - return (TALER_EC_NONE == ec) ? GNUNET_OK : GNUNET_SYSERR; + return fm; +} + + +/** + * Find fee by @a method. + * + * @param fm map to look in + * @param method key to look for + * @return NULL if fee is not specified in @a fm + */ +static const struct TALER_EXCHANGE_WireAggregateFees * +lookup_fee (const struct FeeMap *fm, + const char *method) +{ + for (;NULL != fm; fm = fm->next) + if (0 == strcasecmp (fm->method, + method)) + return fm->fee_list; + return NULL; } @@ -140,70 +208,131 @@ handle_wire_finished (void *cls, const json_t *json) { struct TALER_EXCHANGE_WireHandle *wh = cls; - json_t *keep = NULL; + enum TALER_ErrorCode ec; wh->job = NULL; + ec = TALER_EC_NONE; switch (response_code) { case 0: break; case MHD_HTTP_OK: { - const struct TALER_EXCHANGE_Keys *keys; - const struct TALER_MasterPublicKeyP *master_pub; - const char *key; - json_t *method; - int ret; - - /* We 'keep' methods that we support and that are well-formed; - we fail (by setting response_code=0) if any method that we do - support fails to verify. */ - keep = json_object (); - json_object_foreach ((json_t *) json, key, method) { - ret = verify_wire_method_signature_ok (wh, - key, - method); - if (GNUNET_SYSERR == ret) - { - /* bogus reply */ - GNUNET_break_op (0); - response_code = 0; - } - /* GNUNET_NO: not understood by us, simply skip! */ - if (GNUNET_OK == ret) - { - /* supported and valid, keep! */ - json_object_set (keep, - key, - method); - } - } - /* check fees */ - keys = TALER_EXCHANGE_get_keys (wh->exchange); - if (NULL == keys) - master_pub = NULL; - else - master_pub = &keys->master_pub; + json_t *accounts; + json_t *fees; + int num_accounts; + struct FeeMap *fm; + const struct TALER_EXCHANGE_Keys *key_state; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("accounts", &accounts), + GNUNET_JSON_spec_json ("fees", &fees), + GNUNET_JSON_spec_end() + }; + if (GNUNET_OK != - TALER_EXCHANGE_wire_get_fees (master_pub, - keep, - NULL, - NULL)) + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) { /* bogus reply */ GNUNET_break_op (0); response_code = 0; + ec = TALER_EC_SERVER_JSON_INVALID; + break; } - if (0 != response_code) + if (0 == (num_accounts = json_array_size (accounts))) { - /* all supported methods were valid, use 'keep' for 'json' */ + /* bogus reply */ + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + response_code = 0; + ec = TALER_EC_SERVER_JSON_INVALID; break; } - /* some supported methods were invalid, release 'keep', preserve - full 'json' for application-level error handling. */ - json_decref (keep); - keep = NULL; - } + if (NULL == (fm = parse_fees (fees))) + { + /* bogus reply */ + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + response_code = 0; + ec = TALER_EC_SERVER_JSON_INVALID; + break; + } + + key_state = TALER_EXCHANGE_get_keys (wh->exchange); + /* parse accounts */ + { + struct TALER_EXCHANGE_WireAccount was[num_accounts]; + + for (unsigned int i=0;iurl), + GNUNET_JSON_spec_string ("salt", &wa->salt), + GNUNET_JSON_spec_fixed_auto ("master_sig", &wa->master_sig), + GNUNET_JSON_spec_end() + }; + char *method; + + account = json_array_get (accounts, + i); + if (GNUNET_OK != + TALER_JSON_wire_signature_check (account, + &key_state->master_pub)) + { + /* bogus reply */ + GNUNET_break_op (0); + response_code = 0; + ec = TALER_EC_SERVER_SIGNATURE_INVALID; + break; + } + if (GNUNET_OK != + GNUNET_JSON_parse (account, + spec_account, + NULL, NULL)) + { + /* bogus reply */ + GNUNET_break_op (0); + response_code = 0; + ec = TALER_EC_SERVER_JSON_INVALID; + break; + } + if (NULL == (method = TALER_WIRE_payto_get_method (wa->url))) + { + /* bogus reply */ + GNUNET_break_op (0); + response_code = 0; + ec = TALER_EC_SERVER_JSON_INVALID; + break; + } + if (NULL == (wa->fees = lookup_fee (fm, + method))) + { + /* bogus reply */ + GNUNET_break_op (0); + response_code = 0; + ec = TALER_EC_SERVER_JSON_INVALID; + GNUNET_free (method); + break; + } + GNUNET_free (method); + } /* end 'for all accounts */ + if ( (0 != response_code) && + (NULL != wh->cb) ) + { + wh->cb (wh->cb_cls, + response_code, + TALER_EC_NONE, + num_accounts, + was); + wh->cb = NULL; + } + } /* end of 'parse accounts */ + free_fees (fm); + GNUNET_JSON_parse_free (spec); + } /* end of MHD_HTTP_OK */ break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the exchange is buggy @@ -226,12 +355,12 @@ handle_wire_finished (void *cls, response_code = 0; break; } - wh->cb (wh->cb_cls, - response_code, - TALER_JSON_get_error_code (json), - (NULL != keep) ? keep : json); - if (NULL != keep) - json_decref (keep); + if (NULL != wh->cb) + wh->cb (wh->cb_cls, + response_code, + (0 == response_code) ? ec : TALER_JSON_get_error_code (json), + 0, + NULL); TALER_EXCHANGE_wire_cancel (wh); } @@ -306,157 +435,9 @@ TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh) GNUNET_CURL_job_cancel (wh->job); wh->job = NULL; } - if (NULL != wh->methods) - { - json_decref (wh->methods); - wh->methods = NULL; - } GNUNET_free (wh->url); GNUNET_free (wh); } -/** - * Parse wire @a fee and store the result in @a af. - * - * @param[out] af where to write the result - * @param fee json AggregateTransferFee to parse - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -parse_json_fees (struct TALER_EXCHANGE_WireAggregateFees *af, - json_t *fee) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("sig", - &af->master_sig), - TALER_JSON_spec_amount ("wire_fee", - &af->wire_fee), - TALER_JSON_spec_amount ("closing_fee", - &af->closing_fee), - GNUNET_JSON_spec_absolute_time ("start_date", - &af->start_date), - GNUNET_JSON_spec_absolute_time ("end_date", - &af->end_date), - GNUNET_JSON_spec_end() - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (fee, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Check the #TALER_SIGNATURE_MASTER_WIRE_FEES signature. - * - * @param af record to check - * @param wire_method wire method to check against - * @param master_pub expected signing key - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure - */ -static int -check_sig (const struct TALER_EXCHANGE_WireAggregateFees *af, - const char *wire_method, - const struct TALER_MasterPublicKeyP *master_pub) -{ - struct TALER_MasterWireFeePS wp; - - wp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES); - wp.purpose.size = htonl (sizeof (wp)); - GNUNET_CRYPTO_hash (wire_method, - strlen (wire_method) + 1, - &wp.h_wire_method); - wp.start_date = GNUNET_TIME_absolute_hton (af->start_date); - wp.end_date = GNUNET_TIME_absolute_hton (af->end_date); - TALER_amount_hton (&wp.wire_fee, - &af->wire_fee); - TALER_amount_hton (&wp.closing_fee, - &af->closing_fee); - return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES, - &wp.purpose, - &af->master_sig.eddsa_signature, - &master_pub->eddsa_pub); -} - - -/** - * Obtain information about wire fees encoded in @a obj - * by wire method. - * - * @param master_pub public key to use to verify signatures, NULL to not verify - * @param obj wire information as encoded in the #TALER_EXCHANGE_WireResultCallback - * @param cb callback to invoke for the fees - * @param cb_cls closure for @a cb - * @return #GNUNET_OK in success, #GNUNET_SYSERR if @a obj is ill-formed - */ -int -TALER_EXCHANGE_wire_get_fees (const struct TALER_MasterPublicKeyP *master_pub, - const json_t *obj, - TALER_EXCHANGE_WireFeeCallback cb, - void *cb_cls) -{ - const char *wire_method; - json_t *value; - - json_object_foreach (((json_t *) obj), wire_method, value) - { - json_t *fees; - size_t num_fees; - - fees = json_object_get (value, "fees"); - if ( (NULL == fees) || - (! json_is_array (fees)) ) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - num_fees = json_array_size (fees); - if (num_fees > 1024) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - { - struct TALER_EXCHANGE_WireAggregateFees af[num_fees + 1]; - - for (size_t i=0;icommands[is->ip]; struct TALER_Amount expected_amount; - GNUNET_break ( (0 == strcasecmp ("test", + GNUNET_break ( (0 == strcasecmp ("x-taler-bank", wire_method)) || - (0 == strcasecmp ("sepa", + (0 == strcasecmp ("ebics", wire_method)) ); if (GNUNET_OK != TALER_string_to_amount (cmd->details.wire.expected_fee, @@ -1582,6 +1588,7 @@ check_fee_cb (void *cls, fees = fees->next; } } +#endif /** @@ -1592,14 +1599,15 @@ check_fee_cb (void *cls, * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request; * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param obj the received JSON reply, if successful this should be the wire - * format details as provided by /wire. + * @param accounts_len length of the @a accounts array + * @param accounts list of wire accounts of the exchange, NULL on error */ static void wire_cb (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, - const json_t *obj) + unsigned int accounts_len, + const struct TALER_EXCHANGE_WireAccount *accounts) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; @@ -1611,7 +1619,9 @@ wire_cb (void *cls, "Unexpected response code %u to command %s\n", http_status, cmd->label); +#if LEGACY json_dumpf (obj, stderr, 0); +#endif fail (is); return; } @@ -1619,6 +1629,7 @@ wire_cb (void *cls, { case MHD_HTTP_OK: { +#if LEGACY json_t *method; method = json_object_get (obj, @@ -1646,6 +1657,7 @@ wire_cb (void *cls, fail (is); return; } +#endif } break; default: @@ -1760,8 +1772,8 @@ wire_deposits_cb (void *cls, JSON_REJECT_DUPLICATES, NULL); GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (wire, - &hw)); + TALER_JSON_wire_signature_hash (wire, + &hw)); json_decref (wire); if (0 != memcmp (&hw, h_wire, @@ -2331,13 +2343,15 @@ interpreter_run (void *cls) { struct TALER_DepositRequestPS dr; - memset (&dr, 0, sizeof (dr)); + memset (&dr, + 0, + sizeof (dr)); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.h_contract_terms = h_contract_terms; GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (wire, - &dr.h_wire)); + TALER_JSON_wire_signature_hash (wire, + &dr.h_wire)); dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); TALER_amount_hton (&dr.amount_with_fee, @@ -2581,8 +2595,8 @@ interpreter_run (void *cls) NULL); GNUNET_assert (NULL != wire); GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (wire, - &h_wire)); + TALER_JSON_wire_signature_hash (wire, + &h_wire)); json_decref (wire); contract_terms = json_loads (ref->details.deposit.contract_terms, JSON_REJECT_DUPLICATES, @@ -2640,7 +2654,6 @@ interpreter_run (void *cls) "taler-exchange-wirewatch", "taler-exchange-wirewatch", "-c", "test_exchange_api.conf", - "-t", "test", /* use Taler's bank/fakebank */ "-T", /* exit when done */ NULL); if (NULL == cmd->details.run_wirewatch.wirewatch_proc) @@ -3204,15 +3217,15 @@ run (void *cls) .label = "wire-test", /* expecting 'test' method in response */ .expected_response_code = MHD_HTTP_OK, - .details.wire.format = "test", + .details.wire.format = "x-taler-bank", .details.wire.expected_fee = "EUR:0.01" }, #endif -#if WIRE_SEPA +#if WIRE_EBICS { .oc = OC_WIRE, .label = "wire-sepa", - /* expecting 'sepa' method in response */ + /* expecting 'ebics' method in response */ .expected_response_code = MHD_HTTP_OK, - .details.wire.format = "sepa", + .details.wire.format = "ebics", .details.wire.expected_fee = "EUR:0.01" }, #endif /* *************** end of /wire testing ************** */ @@ -3252,7 +3265,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_OK, .details.deposit.amount = "EUR:5", .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" }, /* Try to overdraw funds ... */ @@ -3268,7 +3281,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_FORBIDDEN, .details.deposit.amount = "EUR:5", .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":43 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" }, /* Try to double-spend the 5 EUR coin at the same merchant (but different transaction ID) */ @@ -3277,7 +3290,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_FORBIDDEN, .details.deposit.amount = "EUR:5", .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }" }, /* Try to double-spend the 5 EUR coin at the same merchant (but different proposal) */ @@ -3286,7 +3299,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_FORBIDDEN, .details.deposit.amount = "EUR:5", .details.deposit.coin_ref = "withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }" }, /* ***************** /refresh testing ******************** */ @@ -3316,7 +3329,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_OK, .details.deposit.amount = "EUR:1", .details.deposit.coin_ref = "refresh-withdraw-coin-1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:1\" } ] }" }, /* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */ @@ -3357,7 +3370,7 @@ run (void *cls) .details.deposit.amount = "EUR:1", .details.deposit.coin_ref = "refresh-reveal-1-idempotency", .details.deposit.coin_idx = 0, - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }" }, /* Test successfully spending coins from the refresh operation: @@ -3368,7 +3381,7 @@ run (void *cls) .details.deposit.amount = "EUR:0.1", .details.deposit.coin_ref = "refresh-reveal-1", .details.deposit.coin_idx = 4, - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":43 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/43\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }" }, /* Test running a failing melt operation (same operation again must fail) */ @@ -3507,7 +3520,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_OK, .details.deposit.amount = "EUR:5", .details.deposit.coin_ref = "withdraw-coin-r1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }", .details.deposit.refund_deadline = { 60LL * 1000 * 1000 } /* 60 s */, }, @@ -3539,7 +3552,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_OK, .details.deposit.amount = "EUR:4.99", .details.deposit.coin_ref = "withdraw-coin-r1", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"more ice cream\", \"value\":\"EUR:5\" } ] }", }, /* Run transfers. This will do the transfer as refund deadline was 0 */ @@ -3591,9 +3604,9 @@ run (void *cls) .expected_response_code = MHD_HTTP_OK, .details.deposit.amount = "EUR:5", .details.deposit.coin_ref = "withdraw-coin-rb", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }", .details.deposit.refund_deadline = { 0 }, + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }" }, { .oc = OC_CHECK_BANK_TRANSFER, .label = "check_bank_transfer-aai-3b", @@ -3695,7 +3708,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_OK, .details.deposit.amount = "EUR:0.5", .details.deposit.coin_ref = "payback-withdraw-coin-2a", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice cream\", \"value\":1 } ] }" }, { .oc = OC_REVOKE, .label = "revoke-2", @@ -3716,7 +3729,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_NOT_FOUND, .details.deposit.amount = "EUR:1", .details.deposit.coin_ref = "payback-withdraw-coin-2b", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice cream\", \"value\":1 } ] }" }, /* Test deposit fails after payback, with proof in payback */ @@ -3727,7 +3740,7 @@ run (void *cls) .expected_response_code = MHD_HTTP_NOT_FOUND, .details.deposit.amount = "EUR:0.5", .details.deposit.coin_ref = "payback-withdraw-coin-2a", - .details.deposit.wire_details = "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8082/\", \"account_number\":42 }", + .details.deposit.wire_details = "{ \"url\":\"payto://x-taler-bank/localhost:8082/42\", \"salt\":\"my salt\" }", .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"extra ice cream\", \"value\":1 } ] }" }, @@ -3999,6 +4012,7 @@ main (int argc, } while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null")); fprintf (stderr, "\n"); + result = GNUNET_NO; sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO); GNUNET_assert (NULL != sigpipe); diff --git a/src/exchange-lib/test_exchange_api.conf b/src/exchange-lib/test_exchange_api.conf index 7d8761f94..8c8bd7ef5 100644 --- a/src/exchange-lib/test_exchange_api.conf +++ b/src/exchange-lib/test_exchange_api.conf @@ -9,7 +9,6 @@ TALER_TEST_HOME = test_exchange_api_home/ CURRENCY = EUR [exchange] - # HTTP port the exchange listens to PORT = 8081 @@ -23,23 +22,58 @@ DB = postgres # exchange (or the twister) is actually listening. BASE_URL = "http://localhost:8081/" +# Keep it short so the test runs fast. +LOOKAHEAD_SIGN = 12 h + [exchangedb-postgres] -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" [auditordb-postgres] -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" + +# Sections starting with "account-" configure the bank accounts +# of the exchange. The "URL" specifies the account in +# payto://-format, while the WIRE_JSON specifies the +# (possibly offline) signed version to be returned in /wire. +# WIRE_JSON is optional, as not all accounts must be +# advertised in /wire. +[account-1] +# What is the URL of our account? +URL = "payto://sepa/CH9300762011623852957" +# This is the response we give out for the /wire request. It provides +# wallets with the bank information for transfers to the exchange. +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json +# Which wire plugin should we used to access the account? +PLUGIN = ebics -[exchange-wire-sepa] -# Enable 'sepa' to test SEPA-specific routines. -ENABLE = YES +# ENABLE_CREDIT = YES + +[account-2] +# What is the bank account (with the "Taler Bank" demo system)? +URL = "payto://x-taler-bank/localhost:8082/2" # This is the response we give out for the /wire request. It provides # wallets with the bank information for transfers to the exchange. -SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/sepa.json +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-2.json + +# Which wire plugin should we used to access the account? +PLUGIN = taler_bank + +# Authentication information for basic authentication +TALER_BANK_AUTH_METHOD = "basic" +USERNAME = user +PASSWORD = pass + +ENABLE_DEBIT = YES + +ENABLE_CREDIT = YES + +# Sections starting with "fee-" configure the wire fee for the +# respective wire method. +[fees-sepa] # Fees for the forseeable future... # If you see this after 2017, update to match the next 10 years... -WIRE-FEE-2017 = EUR:0.01 WIRE-FEE-2018 = EUR:0.01 WIRE-FEE-2019 = EUR:0.01 WIRE-FEE-2020 = EUR:0.01 @@ -49,8 +83,8 @@ WIRE-FEE-2023 = EUR:0.01 WIRE-FEE-2024 = EUR:0.01 WIRE-FEE-2025 = EUR:0.01 WIRE-FEE-2026 = EUR:0.01 +WIRE-FEE-2027 = EUR:0.01 -CLOSING-FEE-2017 = EUR:0.01 CLOSING-FEE-2018 = EUR:0.01 CLOSING-FEE-2019 = EUR:0.01 CLOSING-FEE-2020 = EUR:0.01 @@ -60,18 +94,11 @@ CLOSING-FEE-2023 = EUR:0.01 CLOSING-FEE-2024 = EUR:0.01 CLOSING-FEE-2025 = EUR:0.01 CLOSING-FEE-2026 = EUR:0.01 +CLOSING-FEE-2027 = EUR:0.01 -[exchange_keys] -# Keep it short so the test runs fast. -LOOKAHEAD_SIGN = 12 h - -[exchange-wire-test] -# Enable 'test' for testing of the actual coin operations. -ENABLE = YES - +[fees-x-taler-bank] # Fees for the forseeable future... # If you see this after 2017, update to match the next 10 years... -WIRE-FEE-2017 = EUR:0.01 WIRE-FEE-2018 = EUR:0.01 WIRE-FEE-2019 = EUR:0.01 WIRE-FEE-2020 = EUR:0.01 @@ -81,8 +108,8 @@ WIRE-FEE-2023 = EUR:0.01 WIRE-FEE-2024 = EUR:0.01 WIRE-FEE-2025 = EUR:0.01 WIRE-FEE-2026 = EUR:0.01 +WIRE-FEE-2027 = EUR:0.01 -CLOSING-FEE-2017 = EUR:0.01 CLOSING-FEE-2018 = EUR:0.01 CLOSING-FEE-2019 = EUR:0.01 CLOSING-FEE-2020 = EUR:0.01 @@ -92,17 +119,10 @@ CLOSING-FEE-2023 = EUR:0.01 CLOSING-FEE-2024 = EUR:0.01 CLOSING-FEE-2025 = EUR:0.01 CLOSING-FEE-2026 = EUR:0.01 +CLOSING-FEE-2027 = EUR:0.01 -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json - -# What is the main website of the bank? -BANK_URL = "http://localhost:8082/" -# From which account at the 'bank' should outgoing wire transfers be made? -BANK_ACCOUNT_NUMBER = 2 - - +# Sections starting with "coin_" specify which denominations +# the exchange should support (and their respective fee structure) [coin_eur_ct_1] value = EUR:0.01 duration_overlap = 5 minutes diff --git a/src/exchange-lib/test_exchange_api_home/.config/taler/account-1.json b/src/exchange-lib/test_exchange_api_home/.config/taler/account-1.json new file mode 100644 index 000000000..48093f2aa --- /dev/null +++ b/src/exchange-lib/test_exchange_api_home/.config/taler/account-1.json @@ -0,0 +1,5 @@ +{ + "url": "payto://sepa/CH9300762011623852957", + "salt": "N83T9J9202WCC8TQFDMJDWEGZNBEKA33C1ZM241VNYH88RZNTHPW509Y1M2YF7Y098R8VRESWQ05H03BK1SPAZCWE54KARDCKT5N8AG", + "master_sig": "D4V5GJ998YK7D6N0N56AD0J6MZNFEW6MRZT2CFPVQ5ME3NMQ59AA2007CXYESSFGRN70CNCFM06858QSSENCWTZM8VHEJ93YQ20ZJ1R" +} \ No newline at end of file diff --git a/src/exchange-lib/test_exchange_api_home/.config/taler/account-2.json b/src/exchange-lib/test_exchange_api_home/.config/taler/account-2.json new file mode 100644 index 000000000..85d80de56 --- /dev/null +++ b/src/exchange-lib/test_exchange_api_home/.config/taler/account-2.json @@ -0,0 +1,5 @@ +{ + "url": "payto://x-taler-bank/localhost:8082/2", + "salt": "TMXB995ZZVKA02AG4074X3C6XX0BFTHY8XK76EF4BSG5XVDF069FEBN4TCKW9GS7NKZH409GKAVHMQPA3T361MC6VM7J268V3GBH42R", + "master_sig": "CK7BGHKYVAT7DMVCN00DQ0761NCTJVESZT69049BCF3SKNJKVHXXEQ5X6FH2HFGHCJ18YA1MGHBD8RRG4W3G4KJWQJDY2CGPGTHDJ2G" +} \ No newline at end of file diff --git a/src/exchange-lib/test_exchange_api_home/.config/taler/x-taler-bank.json b/src/exchange-lib/test_exchange_api_home/.config/taler/x-taler-bank.json new file mode 100644 index 000000000..9445f048e --- /dev/null +++ b/src/exchange-lib/test_exchange_api_home/.config/taler/x-taler-bank.json @@ -0,0 +1,5 @@ +{ + "url": "payto://x-taler-bank/http://localhost:8082/2", + "salt": "WGRD0W7YKD8ZAN960B0JBRARRY0K5FQ4920Q3DJBTYH4GY7W0XNAX1F04R5B1E0RWH1NFG08TM8K1517WNCXTJM9KMH4913Q5XPK0N8", + "master_sig": "J4N0KP64MGNEQX9HST9TDWK67152MSHHM9CTZH8GSMKD607BXSAF209AQYDKYT6QJP0NQXYXC1JMM9Z405DJHGV75JFMWP4G6WB6A00" +} \ No newline at end of file diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf index 38b952824..7193bf11e 100644 --- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf +++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf @@ -23,20 +23,53 @@ DB = postgres # exchange (or the twister) is actually listening. BASE_URL = "http://localhost:8081/" +# Keep it short so we can prolong later! +LOOKAHEAD_SIGN = 60 s + + [exchangedb-postgres] -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" [auditordb-postgres] -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" + + +[account-1] +# This is the response we give out for the /wire request. It provides +# wallets with the bank information for transfers to the exchange. +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/iban.json + +# What is the URL of our bank account? Must match WIRE_RESPONSE above! +URL = payto://sepa/FIXME -[exchange-wire-sepa] -# Enable 'sepa' to test SEPA-specific routines. -ENABLE = YES +# Which plugin implements access for this account? +PLUGIN = "ebics" + +[account-2] # This is the response we give out for the /wire request. It provides # wallets with the bank information for transfers to the exchange. -SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/sepa.json +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/x-taler-bank.json + +# What is the URL of our bank account? Must match WIRE_RESPONSE above! +URL = payto://x-taler-bank/http://localhost:8082/2 + +# Which plugin implements access for this account? +PLUGIN = "taler_bank" + +# Authentication information for basic authentication +TALER_BANK_AUTH_METHOD = "basic" +USERNAME = user +PASSWORD = pass + +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES + + + + +[fees-x-taler-bank] # Fees for the forseeable future... # If you see this after 2017, update to match the next 10 years... WIRE-FEE-2017 = EUR:0.01 @@ -61,14 +94,7 @@ CLOSING-FEE-2024 = EUR:0.01 CLOSING-FEE-2025 = EUR:0.01 CLOSING-FEE-2026 = EUR:0.01 -[exchange_keys] -# Keep it short so we can prolong later! -LOOKAHEAD_SIGN = 60 s - -[exchange-wire-test] -# Enable 'test' for testing of the actual coin operations. -ENABLE = YES - +[fees-sepa] # Fees for the forseeable future... # If you see this after 2017, update to match the next 10 years... WIRE-FEE-2017 = EUR:0.01 @@ -93,16 +119,6 @@ CLOSING-FEE-2024 = EUR:0.01 CLOSING-FEE-2025 = EUR:0.01 CLOSING-FEE-2026 = EUR:0.01 -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json - -# What is the main website of the bank? -BANK_URL = "http://localhost:8082/" -# From which account at the 'bank' should outgoing wire transfers be made? -BANK_ACCOUNT_NUMBER = 2 - - [coin_eur_ct_1] value = EUR:0.01 duration_overlap = 5 s diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking_extended.conf b/src/exchange-lib/test_exchange_api_keys_cherry_picking_extended.conf index 3becf3d68..29290c99c 100644 --- a/src/exchange-lib/test_exchange_api_keys_cherry_picking_extended.conf +++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking_extended.conf @@ -1,5 +1,5 @@ @INLINE@ test_exchange_api_keys_cherry_picking.conf -[exchange_keys] +[exchange] # Lengthen over original value (60 s) LOOKAHEAD_SIGN = 100 s diff --git a/src/exchange-lib/test_exchange_api_new.c b/src/exchange-lib/test_exchange_api_new.c index c3e1ce85e..a33f8d014 100644 --- a/src/exchange-lib/test_exchange_api_new.c +++ b/src/exchange-lib/test_exchange_api_new.c @@ -42,6 +42,14 @@ */ #define CONFIG_FILE "test_exchange_api.conf" +/** + * Is the configuration file is set to include wire format 'ebics'? + * Requires that EBICS /history function is implemented, which it + * is currently not. Once it is, set ENABLE_CREDIT to YES in the + * configuration and then set this option to 1. + */ +#define WIRE_EBICS 0 + /** * URL of the fakebank. Obtained from CONFIG_FILE's * "exchange-wire-test:BANK_URI" option. @@ -145,23 +153,23 @@ run (void *cls, CMD_EXEC_WIREWATCH ("wirewatch-1"), /** - * Check if 'test' wire method is offered by the exchange. + * Check if 'x-taler-bank' wire method is offered by the exchange. */ - TALER_TESTING_cmd_wire ("wire-test-1", + TALER_TESTING_cmd_wire ("wire-taler-bank-1", is->exchange, - "test", + "x-taler-bank", NULL, MHD_HTTP_OK), - +#if WIRE_EBICS /** - * Check if 'sepa' wire method is offered by the exchange. + * Check if 'ebics' wire method is offered by the exchange. */ TALER_TESTING_cmd_wire ("wire-sepa-1", is->exchange, - "sepa", + "ebics", NULL, MHD_HTTP_OK), - +#endif /****** End of "wire" testing ******/ /****** Start of withdraw and spend testing ******/ @@ -188,9 +196,8 @@ run (void *cls, */ TALER_TESTING_cmd_deposit ("deposit-simple", is->exchange, "withdraw-coin-1", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK), @@ -208,9 +215,8 @@ run (void *cls, */ TALER_TESTING_cmd_deposit ("deposit-double-1", is->exchange, "withdraw-coin-1", 0, - TALER_TESTING_make_wire_details - ("{\"type\":\"test\",\"account_number\":43}", - fakebank_url), + TALER_TESTING_make_wire_details (43, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN), @@ -225,9 +231,8 @@ run (void *cls, */ TALER_TESTING_cmd_deposit ("deposit-double-1", is->exchange, "withdraw-coin-1", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\", \"account_number\":43}", - fakebank_url), + TALER_TESTING_make_wire_details (43, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN), @@ -236,12 +241,11 @@ run (void *cls, */ TALER_TESTING_cmd_deposit ("deposit-double-2", is->exchange, "withdraw-coin-1", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\", \"account_number\":43}", - fakebank_url), + TALER_TESTING_make_wire_details (43, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN), - + /****** End of withdraw and spend testing ******/ /****** Start of refresh testing ******/ @@ -254,7 +258,7 @@ run (void *cls, */ CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1", "EUR:5.01"), - + /** * Make previous command effective. */ @@ -277,9 +281,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-partial", is->exchange, "refresh-withdraw-coin-1", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\ \"value\":\"EUR:1\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK), @@ -297,7 +300,7 @@ run (void *cls, TALER_TESTING_cmd_refresh_reveal ("refresh-reveal-1", is->exchange, "refresh-melt-1", MHD_HTTP_OK), - + /** * Do it again to check idempotency */ @@ -318,9 +321,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-1a", is->exchange, "refresh-reveal-1-idempotency", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\ \"value\":3}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK), @@ -331,9 +333,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("refresh-deposit-refreshed-1b", is->exchange, "refresh-reveal-1", 4, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":43}", - fakebank_url), + TALER_TESTING_make_wire_details (43, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\",\ \"value\":3}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.1", MHD_HTTP_OK), @@ -463,9 +464,8 @@ run (void *cls, */ TALER_TESTING_cmd_deposit ("deposit-refund-1", is->exchange, "withdraw-coin-r1", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\", \"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\"," "\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_MINUTES, "EUR:5", MHD_HTTP_OK), @@ -502,9 +502,8 @@ run (void *cls, * 1 ct deposit fee) */ TALER_TESTING_cmd_deposit ("deposit-refund-2", is->exchange, "withdraw-coin-r1", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\", \"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"more ice cream\"," "\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:4.99", MHD_HTTP_OK), @@ -555,9 +554,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("deposit-refund-1b", is->exchange, "withdraw-coin-rb", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\", \"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"ice cream\"," "\"value\":\"EUR:5\"}]}", GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK), @@ -602,7 +600,7 @@ run (void *cls, is->exchange, "payback-create-reserve-1", "EUR:5", - MHD_HTTP_OK), + MHD_HTTP_OK), TALER_TESTING_cmd_revoke ("revoke-1", MHD_HTTP_OK, "payback-withdraw-coin-1", @@ -648,9 +646,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("payback-deposit-partial", is->exchange, "payback-withdraw-coin-2a", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_OK), @@ -670,9 +667,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("payback-deposit-revoked", is->exchange, "payback-withdraw-coin-2b", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_NOT_FOUND), @@ -685,9 +681,8 @@ run (void *cls, TALER_TESTING_cmd_deposit ("payback-deposit-partial-after-payback", is->exchange, "payback-withdraw-coin-2a", 0, - TALER_TESTING_make_wire_details - ("{ \"type\":\"test\",\"account_number\":42}", - fakebank_url), + TALER_TESTING_make_wire_details (42, + fakebank_url), "{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}", GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_NOT_FOUND), @@ -740,6 +735,7 @@ run (void *cls, fakebank_url); } + int main (int argc, char * const *argv) @@ -753,7 +749,8 @@ main (int argc, if (NULL == (fakebank_url /* Check fakebank port is available and config cares * about bank url. */ - = TALER_TESTING_prepare_fakebank (CONFIG_FILE))) + = TALER_TESTING_prepare_fakebank (CONFIG_FILE, + "account-2"))) return 77; TALER_TESTING_cleanup_files (CONFIG_FILE); /* @helpers. Run keyup, create tables, ... Note: it diff --git a/src/exchange-lib/testing_api_cmd_deposit.c b/src/exchange-lib/testing_api_cmd_deposit.c index 6f66e8ad0..5854bf8ba 100644 --- a/src/exchange-lib/testing_api_cmd_deposit.c +++ b/src/exchange-lib/testing_api_cmd_deposit.c @@ -50,9 +50,9 @@ struct DepositState unsigned int coin_index; /** - * JSON string describing the merchant's "wire details". + * payto://-URL of the merchant's bank account. */ - char *wire_details; + json_t *wire_details; /** * JSON string describing what a proposal is about. @@ -126,6 +126,7 @@ deposit_cb (void *cls, TALER_TESTING_interpreter_next (ds->is); } + /** * Run the command. * @@ -133,7 +134,7 @@ deposit_cb (void *cls, * @param cmd the command to execute, a /wire one. * @param i the interpreter state. */ -void +static void deposit_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) @@ -153,7 +154,6 @@ deposit_run (void *cls, struct TALER_MerchantPublicKeyP merchant_pub; struct GNUNET_HashCode h_contract_terms; json_t *contract_terms; - json_t *wire; struct TALER_Amount amount; ds->is = is; @@ -166,7 +166,7 @@ deposit_run (void *cls, if (NULL == coin_cmd) { GNUNET_break (0); - TALER_TESTING_interpreter_fail (is); + TALER_TESTING_interpreter_fail (is); return; } @@ -213,21 +213,6 @@ deposit_run (void *cls, TALER_JSON_hash (contract_terms, &h_contract_terms)); json_decref (contract_terms); - - wire = json_loads (ds->wire_details, - JSON_REJECT_DUPLICATES, - NULL); - if (NULL == wire) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse wire details `%s' at %u/%s\n", - ds->wire_details, - is->ip, - this_cmd->label); - TALER_TESTING_interpreter_fail (is); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); @@ -267,8 +252,9 @@ deposit_run (void *cls, dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.h_contract_terms = h_contract_terms; - GNUNET_assert (GNUNET_OK == TALER_JSON_hash - (wire, &dr.h_wire)); + GNUNET_assert (GNUNET_OK == + TALER_JSON_wire_signature_hash (ds->wire_details, + &dr.h_wire)); dr.timestamp = GNUNET_TIME_absolute_hton (timestamp); dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); @@ -286,7 +272,7 @@ deposit_run (void *cls, (ds->exchange, &amount, wire_deadline, - wire, + ds->wire_details, &h_contract_terms, &coin_pub, denom_pub_sig, @@ -301,21 +287,20 @@ deposit_run (void *cls, if (NULL == ds->dh) { GNUNET_break (0); - json_decref (wire); TALER_TESTING_interpreter_fail (is); return; } - json_decref (wire); return; } + /** * Cleanup the state. * * @param cls closure, typically a #struct WireState. * @param cmd the command which is being cleaned up. */ -void +static void deposit_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { @@ -331,10 +316,11 @@ deposit_cleanup (void *cls, ds->dh = NULL; } - GNUNET_free (ds->wire_details); + json_decref (ds->wire_details); GNUNET_free (ds); } + /** * Extract information from a command that is useful for other * commands. @@ -354,7 +340,7 @@ deposit_traits (void *cls, unsigned int index) { struct DepositState *ds = cls; - const struct TALER_TESTING_Command *coin_cmd; + const struct TALER_TESTING_Command *coin_cmd; /* Will point to coin cmd internals. */ struct TALER_CoinSpendPrivateKeyP *coin_spent_priv; @@ -382,7 +368,7 @@ deposit_traits (void *cls, TALER_TESTING_make_trait_contract_terms (0, ds->contract_terms), TALER_TESTING_make_trait_peer_key (0, &ds->merchant_priv.eddsa_priv), - TALER_TESTING_trait_end () + TALER_TESTING_trait_end () }; return TALER_TESTING_get_trait (traits, @@ -402,8 +388,8 @@ deposit_traits (void *cls, * coins, this parameter selects which one in that array. * This value is currently ignored, as only one-coin * withdrawals are implemented. - * @param wire_details bank details of the merchant performing the - * deposit + * @param wire_details JSON details of the wire account of the merchant performing the + * deposit, reference is captured by this command * @param contract_terms contract terms to be signed over by the * coin * @param refund_deadline refund deadline, zero means 'no refunds' @@ -419,7 +405,7 @@ TALER_TESTING_cmd_deposit struct TALER_EXCHANGE_Handle *exchange, const char *coin_reference, unsigned int coin_index, - char *wire_details, + json_t *wire_details, const char *contract_terms, struct GNUNET_TIME_Relative refund_deadline, const char *amount, @@ -427,7 +413,7 @@ TALER_TESTING_cmd_deposit { struct TALER_TESTING_Command cmd; struct DepositState *ds; - + ds = GNUNET_new (struct DepositState); ds->exchange = exchange; ds->coin_reference = coin_reference; diff --git a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c index 1ff466f56..fd8404bec 100644 --- a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c +++ b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c @@ -68,7 +68,6 @@ wirewatch_run (void *cls, "taler-exchange-wirewatch", "taler-exchange-wirewatch", "-c", ws->config_filename, - "-t", "test", /* use Taler's bank/fakebank */ "-T", /* exit when done */ NULL); if (NULL == ws->wirewatch_proc) diff --git a/src/exchange-lib/testing_api_cmd_payback.c b/src/exchange-lib/testing_api_cmd_payback.c index 3a750e7fc..65665c9c8 100644 --- a/src/exchange-lib/testing_api_cmd_payback.c +++ b/src/exchange-lib/testing_api_cmd_payback.c @@ -31,7 +31,7 @@ struct RevokeState { - /** + /** * Expected HTTP status code. */ unsigned int expected_response_code; @@ -65,7 +65,7 @@ struct RevokeState struct PaybackState { - /** + /** * Expected HTTP status code. */ unsigned int expected_response_code; @@ -154,7 +154,7 @@ payback_cb (void *cls, TALER_TESTING_interpreter_fail (is); return; } - + if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv (reserve_cmd, 0, &reserve_priv)) { @@ -201,6 +201,7 @@ payback_cb (void *cls, TALER_TESTING_interpreter_next (is); } + /** * Run the command. * @@ -208,7 +209,7 @@ payback_cb (void *cls, * @param cmd the command to execute, a /wire one. * @param is the interpreter state. */ -void +static void payback_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) @@ -230,7 +231,7 @@ payback_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv @@ -238,7 +239,7 @@ payback_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key @@ -246,7 +247,7 @@ payback_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } planchet.coin_priv = *coin_priv; planchet.blinding_key = *blinding_key; @@ -256,7 +257,7 @@ payback_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig @@ -264,13 +265,13 @@ payback_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Trying to get '%s..' paid back\n", TALER_B2S (&denom_pub->h_key)); - + ps->ph = TALER_EXCHANGE_payback (ps->exchange, denom_pub, coin_sig, @@ -280,13 +281,14 @@ payback_run (void *cls, GNUNET_assert (NULL != ps->ph); } + /** * Cleanup the state. * * @param cls closure, typically a #struct WireState. * @param cmd the command which is being cleaned up. */ -void +static void revoke_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { @@ -302,17 +304,18 @@ revoke_cleanup (void *cls, rs->revoke_proc = NULL; } - GNUNET_free (rs->dhks); + GNUNET_free_non_null (rs->dhks); GNUNET_free (rs); } + /** * Cleanup the state. * * @param cls closure, typically a #struct WireState. * @param cmd the command which is being cleaned up. */ -void +static void payback_cleanup (void *cls, const struct TALER_TESTING_Command *cmd) { @@ -325,6 +328,7 @@ payback_cleanup (void *cls, GNUNET_free (ps); } + /** * Extract information from a command that is useful for other * commands. @@ -366,7 +370,7 @@ revoke_traits (void *cls, * @param cmd the command to execute, a /wire one. * @param is the interpreter state. */ -void +static void revoke_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) @@ -384,7 +388,7 @@ revoke_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_denom_pub @@ -395,8 +399,8 @@ revoke_run (void *cls, TALER_B2S (&denom_pub->h_key)); rs->dhks = GNUNET_STRINGS_data_to_string_alloc - (&denom_pub->h_key, sizeof (struct GNUNET_HashCode)); - + (&denom_pub->h_key, sizeof (struct GNUNET_HashCode)); + rs->revoke_proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, @@ -441,7 +445,7 @@ TALER_TESTING_cmd_payback (const char *label, { struct PaybackState *ps; struct TALER_TESTING_Command cmd; - + ps = GNUNET_new (struct PaybackState); ps->expected_response_code = expected_response_code; ps->coin_reference = coin_reference; @@ -455,6 +459,7 @@ TALER_TESTING_cmd_payback (const char *label, return cmd; } + /** * Make a /revoke command. * diff --git a/src/exchange-lib/testing_api_cmd_refund.c b/src/exchange-lib/testing_api_cmd_refund.c index a092ee2d2..35cb20d26 100644 --- a/src/exchange-lib/testing_api_cmd_refund.c +++ b/src/exchange-lib/testing_api_cmd_refund.c @@ -139,7 +139,7 @@ refund_run (void *cls, const struct TALER_TESTING_Command *cmd, struct TALER_TESTING_Interpreter *is) { - struct RefundState *rs = cls; + struct RefundState *rs = cls; struct TALER_CoinSpendPrivateKeyP *coin_priv; struct TALER_CoinSpendPublicKeyP coin; const char *contract_terms; @@ -193,7 +193,7 @@ refund_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } j_contract_terms = json_loads @@ -201,8 +201,9 @@ refund_run (void *cls, /* Very unlikely to fail */ GNUNET_assert (NULL != j_contract_terms); - GNUNET_assert (GNUNET_OK == TALER_JSON_hash - (j_contract_terms, &h_contract_terms)); + GNUNET_assert (GNUNET_OK == + TALER_JSON_hash (j_contract_terms, + &h_contract_terms)); json_decref (j_contract_terms); @@ -222,7 +223,7 @@ refund_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } rs->rh = TALER_EXCHANGE_refund diff --git a/src/exchange-lib/testing_api_cmd_track.c b/src/exchange-lib/testing_api_cmd_track.c index 3f14c5110..638329d69 100644 --- a/src/exchange-lib/testing_api_cmd_track.c +++ b/src/exchange-lib/testing_api_cmd_track.c @@ -183,7 +183,7 @@ deposit_wtid_cb tts->wtid = *wtid; if (NULL != tts->bank_transfer_reference) { - const struct TALER_TESTING_Command *bank_transfer_cmd; + const struct TALER_TESTING_Command *bank_transfer_cmd; char *ws; ws = GNUNET_STRINGS_data_to_string_alloc (wtid, @@ -205,7 +205,7 @@ deposit_wtid_cb { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } if (0 != strcmp (ws, transfer_subject)) @@ -248,14 +248,13 @@ track_transaction_run (void *cls, const struct TALER_TESTING_Command *transaction_cmd; struct TALER_CoinSpendPrivateKeyP *coin_priv; struct TALER_CoinSpendPublicKeyP coin_pub; - const char *wire_details; const char *contract_terms; - json_t *j_wire_details; + const json_t *wire_details; json_t *j_contract_terms; struct GNUNET_HashCode h_wire_details; struct GNUNET_HashCode h_contract_terms; const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv; - + tts->is = is; transaction_cmd = TALER_TESTING_interpreter_lookup_command (tts->is, tts->transaction_reference); @@ -264,7 +263,7 @@ track_transaction_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); - return; + return; } if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv @@ -272,7 +271,7 @@ track_transaction_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); - return; + return; } GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, @@ -296,31 +295,31 @@ track_transaction_run (void *cls, } /* Parse them.. */ - j_wire_details = json_loads - (wire_details, JSON_REJECT_DUPLICATES, NULL); j_contract_terms = json_loads (contract_terms, JSON_REJECT_DUPLICATES, NULL); - - if ((NULL == j_wire_details) || (NULL == j_contract_terms)) + + if ((NULL == wire_details) || (NULL == j_contract_terms)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); - return; + return; } /* Should not fail here, json has been parsed already */ GNUNET_assert - ( (GNUNET_OK == TALER_JSON_hash (j_wire_details, - &h_wire_details)) && - (GNUNET_OK == TALER_JSON_hash (j_contract_terms, - &h_contract_terms)) ); + ( (GNUNET_OK == + TALER_JSON_wire_signature_hash (wire_details, + &h_wire_details)) && + (GNUNET_OK == + TALER_JSON_hash (j_contract_terms, + &h_contract_terms)) ); if (GNUNET_OK != TALER_TESTING_get_trait_peer_key (transaction_cmd, 0, &merchant_priv)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); - return; + return; } tts->tth = TALER_EXCHANGE_track_transaction @@ -580,7 +579,7 @@ track_transfer_cb { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } /** @@ -594,8 +593,7 @@ track_transfer_cb if (NULL != tts->wire_details_reference) { const struct TALER_TESTING_Command *wire_details_cmd; - const char *wire_details; - json_t *j_wire_details; + const json_t *wire_details; struct GNUNET_HashCode h_wire_details; if (NULL == (wire_details_cmd @@ -615,13 +613,9 @@ track_transfer_cb return; } - j_wire_details = json_loads - (wire_details, JSON_REJECT_DUPLICATES, NULL); - - GNUNET_assert (NULL != j_wire_details); - - GNUNET_assert (GNUNET_OK == TALER_JSON_hash - (j_wire_details, &h_wire_details)); + GNUNET_assert (GNUNET_OK == + TALER_JSON_wire_signature_hash (wire_details, + &h_wire_details)); if (0 != memcmp (&h_wire_details, h_wire, @@ -647,7 +641,7 @@ track_transfer_cb { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); - return; + return; } if (GNUNET_OK != TALER_TESTING_get_trait_amount @@ -657,7 +651,7 @@ track_transfer_cb TALER_TESTING_interpreter_fail (is); return; } - + GNUNET_assert (GNUNET_OK == TALER_string_to_amount (total_amount_from_reference_str, &total_amount_from_reference)); @@ -699,7 +693,7 @@ track_transfer_run (void *cls, * WTID */ memset (&wtid, 0, sizeof (wtid)); wtid_ptr = &wtid; - + tts->is = is; if (NULL != tts->wtid_reference) { @@ -720,7 +714,7 @@ track_transfer_run (void *cls, { GNUNET_break (0); TALER_TESTING_interpreter_fail (tts->is); - return; + return; } GNUNET_assert (NULL != wtid_ptr); } @@ -756,7 +750,7 @@ TALER_TESTING_cmd_track_transfer_empty { struct TrackTransferState *tts; struct TALER_TESTING_Command cmd; - + tts = GNUNET_new (struct TrackTransferState); tts->wtid_reference = wtid_reference; @@ -769,7 +763,7 @@ TALER_TESTING_cmd_track_transfer_empty cmd.run = &track_transfer_run; cmd.cleanup = &track_transfer_cleanup; - return cmd; + return cmd; } /** @@ -801,7 +795,7 @@ TALER_TESTING_cmd_track_transfer { struct TrackTransferState *tts; struct TALER_TESTING_Command cmd; - + tts = GNUNET_new (struct TrackTransferState); tts->wtid_reference = wtid_reference; @@ -816,5 +810,5 @@ TALER_TESTING_cmd_track_transfer cmd.run = &track_transfer_run; cmd.cleanup = &track_transfer_cleanup; - return cmd; + return cmd; } diff --git a/src/exchange-lib/testing_api_cmd_wire.c b/src/exchange-lib/testing_api_cmd_wire.c index f65daec00..cf8304e29 100644 --- a/src/exchange-lib/testing_api_cmd_wire.c +++ b/src/exchange-lib/testing_api_cmd_wire.c @@ -27,6 +27,7 @@ #include "taler_json_lib.h" #include #include "exchange_api_handle.h" +#include "taler_wire_lib.h" #include "taler_testing_lib.h" struct WireState @@ -70,19 +71,6 @@ struct WireState }; -/** - * Check all the expected values have been returned by /wire. - * - * @param cls closure - * @param wire_method name of the wire method (i.e. "sepa") - * @param fees fee structure for this method - */ -static void -check_method_and_fee_cb - (void *cls, - const char *wire_method, - const struct TALER_EXCHANGE_WireAggregateFees *fees); - /** * Callbacks called with the result(s) of a wire format inquiry * request to the exchange. @@ -92,23 +80,20 @@ check_method_and_fee_cb * for successful request; 0 if the exchange's * reply is bogus (fails to follow the protocol) * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param obj the received JSON reply, if successful this should - * be the wire format details as provided by /wire. + * @param accounts_len length of the @a accounts array + * @param accounts list of wire accounts of the exchange, NULL on error */ static void wire_cb (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, - const json_t *obj) + unsigned int accounts_len, + const struct TALER_EXCHANGE_WireAccount *accounts) { struct WireState *ws = cls; - struct TALER_TESTING_Command *cmd - = &ws->is->commands[ws->is->ip]; + struct TALER_TESTING_Command *cmd = &ws->is->commands[ws->is->ip]; + struct TALER_Amount expected_fee; - /** - * The handle has been free'd by GNUnet curl-lib. FIXME: - * shouldn't GNUnet nullify it once it frees it? - */ ws->wh = NULL; if (ws->expected_response_code != http_status) { @@ -117,74 +102,54 @@ wire_cb (void *cls, return; } - if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees - (&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub, - obj, - // will check synchronously. - &check_method_and_fee_cb, - ws)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fee extraction in command %s failed\n", - cmd->label); - json_dumpf (obj, stderr, 0); - TALER_TESTING_interpreter_fail (ws->is); - return; - } - - if (ws->method_found != GNUNET_OK) + if (MHD_HTTP_OK == http_status) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "/wire does not offer method '%s'\n", - ws->expected_method); - TALER_TESTING_interpreter_fail (ws->is); - return; - } - TALER_TESTING_interpreter_next (ws->is); -} - -/** - * Check all the expected values have been returned by /wire. - * - * @param cls closure - * @param wire_method name of the wire method (i.e. "sepa") - * @param fees fee structure for this method - */ -static void -check_method_and_fee_cb - (void *cls, - const char *wire_method, - const struct TALER_EXCHANGE_WireAggregateFees *fees) -{ - struct WireState *ws = cls; - struct TALER_TESTING_Command *cmd - = &ws->is->commands[ws->is->ip]; // ugly? - struct TALER_Amount expected_fee; - - if (0 == strcmp (ws->expected_method, wire_method)) - ws->method_found = GNUNET_OK; - - if ( ws->expected_fee && (ws->method_found == GNUNET_OK) ) - { - GNUNET_assert (GNUNET_OK == TALER_string_to_amount - (ws->expected_fee, - &expected_fee)); - while (NULL != fees) + for (unsigned int i=0;iwire_fee, - &expected_fee)) + char *method; + + method = TALER_WIRE_payto_get_method (accounts[i].url); + if (0 == strcmp (ws->expected_method, + method)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wire fee missmatch to command %s\n", - cmd->label); - TALER_TESTING_interpreter_fail (ws->is); - return; + ws->method_found = GNUNET_OK; + if (NULL != ws->expected_fee) + { + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ws->expected_fee, + &expected_fee)); + for (const struct TALER_EXCHANGE_WireAggregateFees *waf = accounts[i].fees; + NULL != waf; + waf = waf->next) + { + if (0 != TALER_amount_cmp (&waf->wire_fee, + &expected_fee)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire fee missmatch to command %s\n", + cmd->label); + TALER_TESTING_interpreter_fail (ws->is); + GNUNET_free (method); + return; + } + } + } } - fees = fees->next; + GNUNET_free (method); + } + if (GNUNET_OK != ws->method_found) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "/wire does not offer method '%s'\n", + ws->expected_method); + TALER_TESTING_interpreter_fail (ws->is); + return; } } + TALER_TESTING_interpreter_next (ws->is); } + /** * Run the command. * diff --git a/src/exchange-lib/testing_api_helpers.c b/src/exchange-lib/testing_api_helpers.c index a942b6522..3aff0a3cc 100644 --- a/src/exchange-lib/testing_api_helpers.c +++ b/src/exchange-lib/testing_api_helpers.c @@ -416,42 +416,54 @@ TALER_TESTING_url_port_free (const char *url) return GNUNET_OK; } + /** - * Allocate and return a piece of wire-details. Mostly, it adds - * the bank_url to the JSON. + * Allocate and return a piece of wire-details. Combines + * the @a account_no and the @a bank_url to a + * @a payto://-URL and adds some salt to create the JSON. * - * @param template the wire-details template. + * @param account_no account number * @param bank_url the bank_url - * - * @return the filled out and stringified wire-details. To - * be manually free'd. + * @return JSON describing the account, including the + * payto://-URL of the account, must be manually decref'd */ -char * -TALER_TESTING_make_wire_details (const char *template, +json_t * +TALER_TESTING_make_wire_details (unsigned long long account_no, const char *bank_url) { - json_t *jtemplate; - - GNUNET_assert (NULL != (jtemplate = json_loads - (template, JSON_REJECT_DUPLICATES, NULL))); - GNUNET_assert (0 == json_object_set - (jtemplate, "bank_url", json_string (bank_url))); - return json_dumps (jtemplate, JSON_COMPACT); + char *payto; + json_t *ret; + + GNUNET_asprintf (&payto, + "payto://x-taler-bank/%s/%llu", + bank_url, + account_no); + ret = json_pack ("{s:s, s:s}", + "url", payto, + "salt", "test-salt (must be constant for aggregation tests)"); + GNUNET_free (payto); + return ret; } + /** * Prepare launching a fakebank. Check that the configuration * file has the right option, and that the port is available. * If everything is OK, return the configured URL of the fakebank. * * @param config_filename configuration file to use + * @param config_section which account to use (must match x-taler-bank) * @return NULL on error, fakebank URL otherwise */ char * -TALER_TESTING_prepare_fakebank (const char *config_filename) +TALER_TESTING_prepare_fakebank (const char *config_filename, + const char *config_section) { struct GNUNET_CONFIGURATION_Handle *cfg; + char *payto_url; char *fakebank_url; + const char *start; + const char *end; cfg = GNUNET_CONFIGURATION_create (); if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, @@ -459,17 +471,37 @@ TALER_TESTING_prepare_fakebank (const char *config_filename) return NULL; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange-wire-test", - "BANK_URL", - &fakebank_url)) + config_section, + "URL", + &payto_url)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - "exchange-wire-test", - "BANK_URL"); + config_section, + "URL"); GNUNET_CONFIGURATION_destroy (cfg); return NULL; } GNUNET_CONFIGURATION_destroy (cfg); + if (0 != strncasecmp (payto_url, + "payto://x-taler-bank/", + strlen ("payto://x-taler-bank/"))) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + config_section, + "URL", + "expected `x-taler-bank' payto://-URL"); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (payto_url); + return NULL; + } + start = &payto_url [strlen ("payto://x-taler-bank/")]; + end = strchr (start, + (unsigned char) '/'); + if (NULL == end) + end = &start[strlen (start)]; + fakebank_url = GNUNET_strndup (start, + end - start); + GNUNET_free (payto_url); if (GNUNET_OK != TALER_TESTING_url_port_free (fakebank_url)) { diff --git a/src/exchange-lib/testing_api_trait_json.c b/src/exchange-lib/testing_api_trait_json.c new file mode 100644 index 000000000..40dddbfa3 --- /dev/null +++ b/src/exchange-lib/testing_api_trait_json.c @@ -0,0 +1,76 @@ +/* + 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 exchange-lib/testing_api_trait_json.c + * @brief offers JSON traits. + * @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_WIRE_DETAILS "wire-details" + +/** + * Obtain wire details from @a cmd. + * + * @param cmd command to extract trait from + * @param index always (?) zero, as one command sticks + * to one bank account + * @param wire_details[out] where to write the wire details. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_wire_details + (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const json_t **wire_details) +{ + return cmd->traits (cmd->cls, + (void **) wire_details, + TALER_TESTING_TRAIT_WIRE_DETAILS, + index); +} + +/** + * Offer wire details in a trait. + * + * @param index always (?) zero, as one command sticks + * to one bank account + * @param wire_details wire details to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_wire_details + (unsigned int index, + const json_t *wire_details) +{ + struct TALER_TESTING_Trait ret = { + .index = index, + .trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS, + .ptr = (const json_t *) wire_details + }; + return ret; +} + +/* end of testing_api_trait_json.c */ diff --git a/src/exchange-lib/testing_api_trait_string.c b/src/exchange-lib/testing_api_trait_string.c index 1b1b42219..fb5af93e5 100644 --- a/src/exchange-lib/testing_api_trait_string.c +++ b/src/exchange-lib/testing_api_trait_string.c @@ -30,7 +30,6 @@ #include "taler_signatures.h" #include "taler_testing_lib.h" -#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details" #define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms" #define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject" #define TALER_TESTING_TRAIT_AMOUNT "amount" @@ -80,49 +79,6 @@ TALER_TESTING_make_trait_contract_terms } -/** - * Obtain wire details from @a cmd. - * - * @param cmd command to extract trait from - * @param index always (?) zero, as one command sticks - * to one bank account - * @param wire_details[out] where to write the wire details. - * @return #GNUNET_OK on success - */ -int -TALER_TESTING_get_trait_wire_details - (const struct TALER_TESTING_Command *cmd, - unsigned int index, - const char **wire_details) -{ - return cmd->traits (cmd->cls, - (void **) wire_details, - TALER_TESTING_TRAIT_WIRE_DETAILS, - index); -} - -/** - * Offer wire details in a trait. - * - * @param index always (?) zero, as one command sticks - * to one bank account - * @param wire_details wire details to offer - * @return the trait, to be put in the traits array of the command - */ -struct TALER_TESTING_Trait -TALER_TESTING_make_trait_wire_details - (unsigned int index, - const char *wire_details) -{ - struct TALER_TESTING_Trait ret = { - .index = index, - .trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS, - .ptr = (const void *) wire_details - }; - return ret; -} - - /** * Obtain a transfer subject from @a cmd. * @@ -289,7 +245,7 @@ TALER_TESTING_make_trait_order_id struct TALER_TESTING_Trait ret = { .index = index, .trait_name = TALER_TESTING_TRAIT_ORDER_ID, - .ptr = (const void *) order_id + .ptr = (const void *) order_id }; return ret; } @@ -333,7 +289,7 @@ TALER_TESTING_make_trait_rejected struct TALER_TESTING_Trait ret = { .index = index, .trait_name = TALER_TESTING_TRAIT_REJECTED, - .ptr = (const void *) rejected + .ptr = (const void *) rejected }; return ret; } diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am index 8d00415c8..2c1ff0f4d 100644 --- a/src/exchange-tools/Makefile.am +++ b/src/exchange-tools/Makefile.am @@ -4,7 +4,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include pkgcfgdir = $(prefix)/share/taler/config.d/ pkgcfg_DATA = \ - exchange-signkeys.conf \ coins.conf if USE_COVERAGE @@ -34,6 +33,8 @@ taler_exchange_wire_SOURCES = \ taler-exchange-wire.c taler_exchange_wire_LDADD = \ $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetjson \ diff --git a/src/exchange-tools/exchange-signkeys.conf b/src/exchange-tools/exchange-signkeys.conf deleted file mode 100644 index 3146f09b2..000000000 --- a/src/exchange-tools/exchange-signkeys.conf +++ /dev/null @@ -1,16 +0,0 @@ -# General data for signing keys. -[exchange_keys] - -# how long is one signkey valid? -signkey_duration = 4 weeks - -# how long are the signatures with the signkey valid? -legal_duration = 2 years - -# how long do we generate denomination and signing keys -# ahead of time? -lookahead_sign = 32 weeks 1 day - -# how long do we provide to clients denomination and signing keys -# ahead of time? -lookahead_provide = 4 weeks 1 day diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index 5e069af5b..9842a37a8 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-2017 GNUnet e.V. + Copyright (C) 2014-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 @@ -499,23 +499,23 @@ exchange_keys_update_signkeys () if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, - "exchange_keys", + "exchange", "signkey_duration", &signkey_duration)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange_keys", + "exchange", "signkey_duration"); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, - "exchange_keys", + "exchange", "legal_duration", &legal_duration)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange_keys", + "exchange", "legal_duration", "fails to specify valid timeframe"); return GNUNET_SYSERR; @@ -523,7 +523,7 @@ exchange_keys_update_signkeys () if (signkey_duration.rel_value_us > legal_duration.rel_value_us) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange_keys", + "exchange", "legal_duration", "must be longer than signkey_duration"); return GNUNET_SYSERR; @@ -926,10 +926,13 @@ create_wire_fee_for_method (void *cls, if (GNUNET_OK != *ret) return; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Setting up wire fees for `%s'\n", + wiremethod); last_date = GNUNET_TIME_absolute_add (lookahead_sign_stamp, max_duration_spend); GNUNET_asprintf (§ion, - "exchange-wire-%s", + "fees-%s", wiremethod); GNUNET_asprintf (&fn, "%s%s.fee", @@ -1045,6 +1048,43 @@ create_wire_fee_for_method (void *cls, } +/** + * Output the wire fee structure. Must be run after #max_duration_spend + * was initialized. + * + * @param cls pointer to `int`, set to #GNUNET_SYSERR on error + * @param ai information about enabled accounts + */ +static void +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); +} + + /** * Output the wire fee structure. Must be run after #max_duration_spend * was initialized. @@ -1057,9 +1097,9 @@ create_wire_fees () int ret; ret = GNUNET_OK; - TALER_WIRE_find_enabled (kcfg, - &create_wire_fee_for_method, - &ret); + TALER_EXCHANGEDB_find_accounts (kcfg, + &create_wire_fee_by_account, + &ret); return ret; } @@ -1305,12 +1345,12 @@ run (void *cls, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, - "exchange_keys", + "exchange", "lookahead_sign", &lookahead_sign)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange_keys", + "exchange", "lookahead_sign"); global_ret = 1; return; @@ -1318,7 +1358,7 @@ run (void *cls, if (0 == lookahead_sign.rel_value_us) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange_keys", + "exchange", "lookahead_sign", _("must not be zero")); global_ret = 1; diff --git a/src/exchange-tools/taler-exchange-wire.c b/src/exchange-tools/taler-exchange-wire.c index 60244f154..390c0e5e3 100644 --- a/src/exchange-tools/taler-exchange-wire.c +++ b/src/exchange-tools/taler-exchange-wire.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015, 2016, 2017 Inria + Copyright (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 @@ -24,6 +24,8 @@ #include "taler_crypto_lib.h" #include "taler_util.h" #include "taler_wire_lib.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_lib.h" #include "taler_signatures.h" @@ -33,24 +35,64 @@ static char *masterkeyfile; /** - * Account holder information in JSON format. + * Private key for signing. */ -static json_t *account_holder; +static struct TALER_MasterPrivateKeyP master_priv; /** - * Which wire method is this for? + * Return value from main(). */ -static char *method; +static int global_ret; -/** - * Where to write the result. - */ -static char *output_filename; /** - * Return value from main(). + * Function called with information about a wire account. Signs + * the account's wire details and writes out the JSON file to disk. + * + * @param cls closure + * @param ai account information */ -static int global_ret; +static void +sign_account_data (void *cls, + const struct TALER_EXCHANGEDB_AccountInfo *ai) +{ + json_t *wire; + char *json_out; + FILE *out; + + if (GNUNET_NO == ai->credit_enabled) + return; + if (NULL == ai->wire_response_filename) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ai->section_name, + "WIRE_RESPONSE"); + global_ret = 1; + return; + } + wire = TALER_JSON_wire_signature_make (ai->payto_url, + &master_priv); + json_out = json_dumps (wire, + JSON_INDENT(2)); + json_decref (wire); + GNUNET_assert (NULL != json_out); + + out = fopen (ai->wire_response_filename, + "w+"); + if (NULL == out) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "fopen", + ai->wire_response_filename); + global_ret = 1; + return; + } + fprintf (out, + "%s", + json_out); + fclose (out); + free (json_out); +} /** @@ -68,11 +110,6 @@ run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; - struct TALER_MasterPrivateKeyP key; - struct TALER_MasterSignatureP sig; - char *json_out; - struct GNUNET_HashCode salt; - struct TALER_WIRE_Plugin *plugin; if ( (NULL == masterkeyfile) && (GNUNET_OK != @@ -86,7 +123,8 @@ run (void *cls, global_ret = 1; return; } - if (GNUNET_YES != GNUNET_DISK_file_test (masterkeyfile)) + if (GNUNET_YES != + GNUNET_DISK_file_test (masterkeyfile)) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Exchange master private key `%s' does not exist yet, creating it!\n", masterkeyfile); @@ -99,97 +137,11 @@ run (void *cls, global_ret = 1; return; } - if (NULL == method) - { - json_t *test; - const char *m; - - test = json_object_get(account_holder, - "type"); - if ( (NULL == test) || - (NULL == (m = json_string_value (test)))) - { - fprintf (stderr, - "Required -t argument missing\n"); - global_ret = 1; - return; - } - method = GNUNET_strdup (m); - } - else - { - json_object_set_new (account_holder, - "type", - json_string (method)); - } - key.eddsa_priv = *eddsa_priv; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &salt, - sizeof (salt)); - plugin = TALER_WIRE_plugin_load (cfg, - method); - if (NULL == plugin) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Wire transfer method `%s' not supported\n", - method); - GNUNET_free (method); - global_ret = 1; - return; - } - GNUNET_free (method); - if (GNUNET_OK != - plugin->sign_wire_details (plugin->cls, - account_holder, - &key, - &salt, - &sig)) - { - /* sign function should have logged applicable errors */ - json_decref (account_holder); - TALER_WIRE_plugin_unload (plugin); - global_ret = 1; - return; - } - TALER_WIRE_plugin_unload (plugin); + master_priv.eddsa_priv = *eddsa_priv; + TALER_EXCHANGEDB_find_accounts (cfg, + &sign_account_data, + NULL); GNUNET_free (eddsa_priv); - - /* add signature and salt to JSON message */ - json_object_set_new (account_holder, - "salt", - GNUNET_JSON_from_data (&salt, - sizeof (salt))); - json_object_set_new (account_holder, - "sig", - GNUNET_JSON_from_data (&sig, - sizeof (sig))); - - /* dump result to stdout */ - json_out = json_dumps (account_holder, - JSON_INDENT(2)); - json_decref (account_holder); - GNUNET_assert (NULL != json_out); - - if (NULL != output_filename) - { - if (NULL != stdout) - fclose (stdout); - stdout = fopen (output_filename, - "w+"); - if (NULL == stdout) - { - fprintf (stderr, - "Failed to open `%s': %s\n", - output_filename, - STRERROR (errno)); - return; - } - } - fprintf (stdout, - "%s", - json_out); - fflush (stdout); - free (json_out); } @@ -206,27 +158,11 @@ main (int argc, char *const *argv) { const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_mandatory - (GNUNET_JSON_getopt ('j', - "json", - "JSON", - "account information in JSON format", - &account_holder)), GNUNET_GETOPT_option_filename ('m', "master-key", "FILENAME", "master key file (private key)", &masterkeyfile), - GNUNET_GETOPT_option_string ('t', - "type", - "METHOD", - "which wire transfer method (i.e. 'test' or 'sepa') is this for?", - &method), - GNUNET_GETOPT_option_filename ('o', - "output", - "FILENAME", - "where to write the result", - &output_filename), GNUNET_GETOPT_OPTION_END }; diff --git a/src/exchange/.gitignore b/src/exchange/.gitignore index e54fe4932..b0fe0a0f6 100644 --- a/src/exchange/.gitignore +++ b/src/exchange/.gitignore @@ -6,3 +6,4 @@ taler-exchange-reservemod taler-exchange-httpd taler-exchange-wirewatch test_taler_exchange_wirewatch-postgres +test_taler_exchange_httpd_home/.config/taler/account-1.json diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 39c496ec5..5b9ff9be3 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -36,3 +36,18 @@ PORT = 8081 # Required for wire transfers as we need to include it in the wire # transfers to enable tracking. BASE_URL = http://localhost:8081/ + + +# how long is one signkey valid? +SIGNKEY_DURATION = 4 weeks + +# how long are the signatures with the signkey valid? +LEGAL_DURATION = 2 years + +# how long do we generate denomination and signing keys +# ahead of time? +LOOKAHEAD_SIGN = 32 weeks 1 day + +# how long do we provide to clients denomination and signing keys +# ahead of time? +LOOKAHEAD_PROVIDE = 4 weeks 1 day diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index 49cbb2b93..fa76cfb03 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, 2017 GNUnet e.V. + Copyright (C) 2016-2018 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -30,19 +30,19 @@ /** - * Information we keep for each loaded wire plugin. + * Information we keep for each supported account. */ -struct WirePlugin +struct WireAccount { /** - * Plugins are kept in a DLL. + * Accounts are kept in a DLL. */ - struct WirePlugin *next; + struct WireAccount *next; /** * Plugins are kept in a DLL. */ - struct WirePlugin *prev; + struct WireAccount *prev; /** * Handle to the plugin. @@ -50,14 +50,14 @@ struct WirePlugin struct TALER_WIRE_Plugin *wire_plugin; /** - * Name of the plugin. + * Wire transfer fee structure. */ - char *type; + struct TALER_EXCHANGEDB_AggregateFees *af; /** - * Wire transfer fee structure. + * Name of the section that configures this account. */ - struct TALER_EXCHANGEDB_AggregateFees *af; + char *section_name; }; @@ -82,7 +82,7 @@ struct WirePrepareData /** * Wire plugin used for this preparation. */ - struct WirePlugin *wp; + struct WireAccount *wa; /** * Row ID of the transfer. @@ -149,9 +149,9 @@ struct AggregationUnit json_t *wire; /** - * Wire plugin to be used for the preparation. + * Wire account to be used for the preparation. */ - struct WirePlugin *wp; + struct WireAccount *wa; /** * Database session for all of our transactions. @@ -200,12 +200,12 @@ struct CloseTransferContext /** * Wire transfer method. */ - char *type; + char *method; /** - * Wire plugin used for closing the reserve. + * Wire account used for closing the reserve. */ - struct WirePlugin *wp; + struct WireAccount *wa; }; @@ -238,12 +238,12 @@ static struct TALER_EXCHANGEDB_Plugin *db_plugin; /** * Head of list of loaded wire plugins. */ -static struct WirePlugin *wp_head; +static struct WireAccount *wa_head; /** * Tail of list of loaded wire plugins. */ -static struct WirePlugin *wp_tail; +static struct WireAccount *wa_tail; /** * Next task to run, if any. @@ -290,49 +290,22 @@ static int reserves_idle; static unsigned int aggregation_limit = TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT; -/** - * Extract wire plugin type from @a wire address - * - * @param wire a wire address - * @return NULL if @a wire is ill-formed - */ -const char * -extract_type (const json_t *wire) -{ - const char *type; - json_t *t; - - t = json_object_get (wire, "type"); - if (NULL == t) - { - GNUNET_break (0); - return NULL; - } - type = json_string_value (t); - if (NULL == type) - { - GNUNET_break (0); - return NULL; - } - return type; -} - /** * Advance the "af" pointer in @a wp to point to the * currently valid record. * - * @param wp wire transfer fee data structure to update + * @param wa wire transfer fee data structure to update * @param now timestamp to update fees to */ static void -advance_fees (struct WirePlugin *wp, +advance_fees (struct WireAccount *wa, struct GNUNET_TIME_Absolute now) { struct TALER_EXCHANGEDB_AggregateFees *af; /* First, try to see if we have current fee information in memory */ - af = wp->af; + af = wa->af; while ( (NULL != af) && (af->end_date.abs_value_us < now.abs_value_us) ) { @@ -341,7 +314,7 @@ advance_fees (struct WirePlugin *wp, GNUNET_free (af); af = n; } - wp->af = af; + wa->af = af; } @@ -354,28 +327,28 @@ advance_fees (struct WirePlugin *wp, * @return transaction status */ static enum GNUNET_DB_QueryStatus -update_fees (struct WirePlugin *wp, +update_fees (struct WireAccount *wa, struct GNUNET_TIME_Absolute now, struct TALER_EXCHANGEDB_Session *session) { enum GNUNET_DB_QueryStatus qs; - advance_fees (wp, + advance_fees (wa, now); - if (NULL != wp->af) + if (NULL != wa->af) return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* Let's try to load it from disk... */ - wp->af = TALER_EXCHANGEDB_fees_read (cfg, - wp->type); - advance_fees (wp, + wa->af = TALER_EXCHANGEDB_fees_read (cfg, + wa->wire_plugin->method); + advance_fees (wa, now); - for (struct TALER_EXCHANGEDB_AggregateFees *p = wp->af; + for (struct TALER_EXCHANGEDB_AggregateFees *p = wa->af; NULL != p; p = p->next) { qs = db_plugin->insert_wire_fee (db_plugin->cls, session, - wp->type, + wa->wire_plugin->method, p->start_date, p->end_date, &p->wire_fee, @@ -383,53 +356,94 @@ update_fees (struct WirePlugin *wp, &p->master_sig); if (qs < 0) { - TALER_EXCHANGEDB_fees_free (wp->af); - wp->af = NULL; + TALER_EXCHANGEDB_fees_free (wa->af); + wa->af = NULL; return qs; } } - if (NULL != wp->af) + if (NULL != wa->af) return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to find current wire transfer fees for `%s'\n", - wp->type); + wa->wire_plugin->method); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } /** - * Find the wire plugin for the given wire address. + * Find the wire plugin for the given payto:// URL * - * @param type wire plugin type we need a plugin for + * @param method wire method we need an account for * @return NULL on error */ -static struct WirePlugin * -find_plugin (const char *type) +static struct WireAccount * +find_account_by_method (const char *method) { - struct WirePlugin *wp; + for (struct WireAccount *wa = wa_head; NULL != wa; wa = wa->next) + if (0 == strcmp (method, + wa->wire_plugin->method)) + return wa; + return NULL; +} + - if (NULL == type) +/** + * Find the wire plugin for the given payto:// URL + * + * @param url wire address we need an account for + * @return NULL on error + */ +static struct WireAccount * +find_account_by_url (const char *url) +{ + char *method; + struct WireAccount *wa; + + method = TALER_WIRE_payto_get_method (url); + if (NULL == method) + { + fprintf (stderr, + "Invalid payto:// URL `%s'\n", + url); return NULL; - for (wp = wp_head; NULL != wp; wp = wp->next) - if (0 == strcmp (type, - wp->type)) - return wp; - wp = GNUNET_new (struct WirePlugin); - wp->wire_plugin = TALER_WIRE_plugin_load (cfg, - type); - if (NULL == wp->wire_plugin) + } + wa = find_account_by_method (method); + GNUNET_free (method); + return wa; +} + + +/** + * Function called with information about a wire account. Adds the + * account to our list (if it is enabled and we can load the plugin). + * + * @param cls closure, NULL + * @param ai account information + */ +static void +add_account_cb (void *cls, + const struct TALER_EXCHANGEDB_AccountInfo *ai) +{ + struct WireAccount *wa; + + (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) { fprintf (stderr, "Failed to load wire plugin for `%s'\n", - type); - GNUNET_free (wp); - return NULL; + ai->plugin_name); + GNUNET_free (wa); + return; } - wp->type = GNUNET_strdup (type); - GNUNET_CONTAINER_DLL_insert (wp_head, - wp_tail, - wp); - return wp; + wa->section_name = GNUNET_strdup (ai->section_name); + GNUNET_CONTAINER_DLL_insert (wa_head, + wa_tail, + wa); } @@ -471,7 +485,7 @@ shutdown_task (void *cls) { if (NULL != wpd->eh) { - wpd->wp->wire_plugin->execute_wire_transfer_cancel (wpd->wp->wire_plugin->cls, + wpd->wa->wire_plugin->execute_wire_transfer_cancel (wpd->wa->wire_plugin->cls, wpd->eh); wpd->eh = NULL; } @@ -484,7 +498,7 @@ shutdown_task (void *cls) { if (NULL != au->ph) { - au->wp->wire_plugin->prepare_wire_transfer_cancel (au->wp->wire_plugin->cls, + au->wa->wire_plugin->prepare_wire_transfer_cancel (au->wa->wire_plugin->cls, au->ph); au->ph = NULL; } @@ -494,29 +508,29 @@ shutdown_task (void *cls) } if (NULL != ctc) { - ctc->wp->wire_plugin->prepare_wire_transfer_cancel (ctc->wp->wire_plugin->cls, + 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->type); + GNUNET_free (ctc->method); GNUNET_free (ctc); ctc = NULL; } TALER_EXCHANGEDB_plugin_unload (db_plugin); { - struct WirePlugin *wp; + struct WireAccount *wa; - while (NULL != (wp = wp_head)) + while (NULL != (wa = wa_head)) { - GNUNET_CONTAINER_DLL_remove (wp_head, - wp_tail, - wp); - TALER_WIRE_plugin_unload (wp->wire_plugin); - TALER_EXCHANGEDB_fees_free (wp->af); - GNUNET_free (wp->type); - GNUNET_free (wp); + GNUNET_CONTAINER_DLL_remove (wa_head, + wa_tail, + wa); + TALER_WIRE_plugin_unload (wa->wire_plugin); + TALER_EXCHANGEDB_fees_free (wa->af); + GNUNET_free (wa->section_name); + GNUNET_free (wa); } } GNUNET_CONFIGURATION_destroy (cfg); @@ -568,7 +582,16 @@ exchange_serve_process_config () TALER_EXCHANGEDB_plugin_unload (db_plugin); return GNUNET_SYSERR; } - + TALER_EXCHANGEDB_find_accounts (cfg, + &add_account_cb, + NULL); + if (NULL == wa_head) + { + fprintf (stderr, + "No wire accounts configured for debit!\n"); + TALER_EXCHANGEDB_plugin_unload (db_plugin); + return GNUNET_SYSERR; + } return GNUNET_OK; } @@ -677,16 +700,13 @@ deposit_cb (void *cls, } GNUNET_assert (NULL == au->wire); - au->wire = json_incref ((json_t *) wire); - if (GNUNET_OK != - TALER_JSON_hash (au->wire, - &au->h_wire)) + if (NULL == (au->wire = json_incref ((json_t *) wire))) { GNUNET_break (0); - json_decref (au->wire); - au->wire = NULL; return GNUNET_DB_STATUS_HARD_ERROR; } + TALER_JSON_wire_signature_hash (wire, + &au->h_wire); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &au->wtid, sizeof (au->wtid)); @@ -695,15 +715,20 @@ deposit_cb (void *cls, TALER_B2S (&au->wtid), TALER_amount2s (amount_with_fee), (unsigned long long) row_id); + { + char *url; - au->wp = find_plugin (extract_type (au->wire)); - if (NULL == au->wp) + url = TALER_JSON_wire_to_payto (au->wire); + au->wa = find_account_by_url (url); + GNUNET_free (url); + } + if (NULL == au->wa) return GNUNET_DB_STATUS_HARD_ERROR; /* make sure we have current fees */ au->execution_time = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&au->execution_time); - qs = update_fees (au->wp, + qs = update_fees (au->wa, au->execution_time, au->session); if (qs <= 0) @@ -713,7 +738,7 @@ deposit_cb (void *cls, GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return qs; } - au->wire_fee = au->wp->af->wire_fee; + au->wire_fee = au->wa->af->wire_fee; qs = db_plugin->insert_aggregation_tracking (db_plugin->cls, au->session, @@ -942,7 +967,7 @@ prepare_close_cb (void *cls, db_plugin->rollback (db_plugin->cls, ctc->session); /* start again */ - GNUNET_free (ctc->type); + GNUNET_free (ctc->method); GNUNET_free (ctc); ctc = NULL; task = GNUNET_SCHEDULER_add_now (&run_aggregation, @@ -953,7 +978,7 @@ prepare_close_cb (void *cls, /* Commit our intention to execute the wire transfer! */ qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, ctc->session, - ctc->type, + ctc->method, buf, buf_size); if (GNUNET_DB_STATUS_HARD_ERROR == qs) @@ -963,7 +988,7 @@ prepare_close_cb (void *cls, ctc->session); global_ret = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown (); - GNUNET_free (ctc->type); + GNUNET_free (ctc->method); GNUNET_free (ctc); ctc = NULL; return; @@ -975,7 +1000,7 @@ prepare_close_cb (void *cls, /* start again */ task = GNUNET_SCHEDULER_add_now (&run_aggregation, NULL); - GNUNET_free (ctc->type); + GNUNET_free (ctc->method); GNUNET_free (ctc); ctc = NULL; return; @@ -983,7 +1008,7 @@ prepare_close_cb (void *cls, /* finally commit */ (void) commit_or_warn (ctc->session); - GNUNET_free (ctc->type); + GNUNET_free (ctc->method); GNUNET_free (ctc); ctc = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1029,7 +1054,7 @@ static enum GNUNET_DB_QueryStatus expired_reserve_cb (void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *left, - const json_t *account_details, + const char *account_details, struct GNUNET_TIME_Absolute expiration_date) { struct ExpiredReserveContext *erc = cls; @@ -1040,24 +1065,15 @@ expired_reserve_cb (void *cls, const struct TALER_Amount *closing_fee; int ret; enum GNUNET_DB_QueryStatus qs; - const char *type; - struct WirePlugin *wp; + struct WireAccount *wa; GNUNET_assert (NULL == ctc); now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); /* lookup wire plugin */ - type = extract_type (account_details); - if (NULL == type) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return GNUNET_DB_STATUS_HARD_ERROR; - } - wp = find_plugin (type); - if (NULL == wp) + wa = find_account_by_url (account_details); + if (NULL == wa) { GNUNET_break (0); global_ret = GNUNET_SYSERR; @@ -1066,7 +1082,7 @@ expired_reserve_cb (void *cls, } /* lookup `closing_fee` */ - qs = update_fees (wp, + qs = update_fees (wa, now, session); if (qs <= 0) @@ -1079,7 +1095,7 @@ expired_reserve_cb (void *cls, GNUNET_SCHEDULER_shutdown (); return qs; } - closing_fee = &wp->af->closing_fee; + closing_fee = &wa->af->closing_fee; /* calculate transfer amount */ ret = TALER_amount_subtract (&amount_without_fee, @@ -1124,7 +1140,7 @@ expired_reserve_cb (void *cls, { /* success, perform wire transfer */ if (GNUNET_SYSERR == - wp->wire_plugin->amount_round (wp->wire_plugin->cls, + wa->wire_plugin->amount_round (wa->wire_plugin->cls, &amount_without_fee)) { GNUNET_break (0); @@ -1133,11 +1149,12 @@ expired_reserve_cb (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } ctc = GNUNET_new (struct CloseTransferContext); - ctc->wp = wp; + ctc->wa = wa; ctc->session = session; - ctc->type = GNUNET_strdup (type); + ctc->method = TALER_WIRE_payto_get_method (account_details); ctc->ph - = wp->wire_plugin->prepare_wire_transfer (wp->wire_plugin->cls, + = wa->wire_plugin->prepare_wire_transfer (wa->wire_plugin->cls, + wa->section_name, account_details, &amount_without_fee, exchange_base_url, @@ -1149,7 +1166,7 @@ expired_reserve_cb (void *cls, GNUNET_break (0); global_ret = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown (); - GNUNET_free (ctc->type); + GNUNET_free (ctc->method); GNUNET_free (ctc); ctc = NULL; return GNUNET_DB_STATUS_HARD_ERROR; @@ -1398,7 +1415,7 @@ run_aggregation (void *cls) &au->total_amount, &au->wire_fee)) || (GNUNET_SYSERR == - au->wp->wire_plugin->amount_round (au->wp->wire_plugin->cls, + au->wa->wire_plugin->amount_round (au->wa->wire_plugin->cls, &au->final_amount)) || ( (0 == au->final_amount.value) && (0 == au->final_amount.fraction) ) ) @@ -1479,22 +1496,28 @@ run_aggregation (void *cls) TALER_B2S (&au->merchant_pub)); GNUNET_free (amount_s); } - au->ph = au->wp->wire_plugin->prepare_wire_transfer (au->wp->wire_plugin->cls, - au->wire, - &au->final_amount, - exchange_base_url, - &au->wtid, - &prepare_cb, - au); + { + 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); + GNUNET_free (url); + } if (NULL == au->ph) { - GNUNET_break (0); /* why? how to best recover? */ + /* something went very wrong, likely bad configuration, + abort */ db_plugin->rollback (db_plugin->cls, session); cleanup_au (); - /* start again */ - task = GNUNET_SCHEDULER_add_now (&run_aggregation, - NULL); + GNUNET_SCHEDULER_shutdown (); return; } /* otherwise we continue with #prepare_cb(), see below */ @@ -1536,7 +1559,7 @@ prepare_cb (void *cls, /* Commit our intention to execute the wire transfer! */ qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, session, - au->wp->type, + au->wa->wire_plugin->method, buf, buf_size); /* Commit the WTID data to 'wire_out' to finally satisfy aggregation @@ -1711,8 +1734,8 @@ wire_prepare_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting wire transfer %llu\n", (unsigned long long) rowid); - wpd->wp = find_plugin (wire_method); - if (NULL == wpd->wp) + wpd->wa = find_account_by_method (wire_method); + if (NULL == wpd->wa) { /* Should really never happen here, as when we get here the plugin should be in the cache. */ @@ -1725,7 +1748,7 @@ wire_prepare_cb (void *cls, wpd = NULL; return; } - wpd->eh = wpd->wp->wire_plugin->execute_wire_transfer (wpd->wp->wire_plugin->cls, + wpd->eh = wpd->wa->wire_plugin->execute_wire_transfer (wpd->wa->wire_plugin->cls, buf, buf_size, &wire_confirm_cb, diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 277430b2d..8bf47717e 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -402,9 +402,12 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, &json); if (GNUNET_SYSERR == res) return MHD_NO; - if ( (GNUNET_NO == res) || (NULL == json) ) + if ( (GNUNET_NO == res) || + (NULL == json) ) return MHD_YES; - memset (&deposit, 0, sizeof (deposit)); + memset (&deposit, + 0, + sizeof (deposit)); res = TEH_PARSE_json_data (connection, json, spec); @@ -426,7 +429,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, if (TALER_EC_NONE != (ec = TEH_json_validate_wireformat (wire, - GNUNET_NO, &emsg))) { GNUNET_JSON_parse_free (spec); @@ -446,8 +448,8 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, "timestamp"); } if (GNUNET_OK != - TALER_JSON_hash (wire, - &my_h_wire)) + TALER_JSON_wire_signature_hash (wire, + &my_h_wire)) { TALER_LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n"); GNUNET_JSON_parse_free (spec); diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index c7874ed15..99ee08a80 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -148,7 +148,7 @@ TEH_RESPONSE_reply_json (struct MHD_Connection *connection, JSON_INDENT(2)); if (NULL == json_str) { - GNUNET_break (0); + GNUNET_assert (0); return MHD_NO; } json_len = strlen (json_str); @@ -733,10 +733,10 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto ret |= 1; GNUNET_assert (0 == json_array_append_new (json_history, - json_pack ("{s:s, s:o, s:O, s:o, s:o}", + json_pack ("{s:s, s:o, s:s, s:o, s:o}", "type", "DEPOSIT", "timestamp", GNUNET_JSON_from_time_abs (pos->details.bank->execution_date), - "sender_account_details", pos->details.bank->sender_account_details, + "sender_account_url", pos->details.bank->sender_account_details, "wire_reference", GNUNET_JSON_from_data (pos->details.bank->wire_reference, pos->details.bank->wire_reference_size), "amount", TALER_JSON_from_amount (&pos->details.bank->amount)))); @@ -858,14 +858,9 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto TALER_amount_hton (&rcc.closing_fee, &pos->details.closing->closing_fee); rcc.reserve_pub = pos->details.closing->reserve_pub; - if (GNUNET_OK != - TALER_JSON_hash (pos->details.closing->receiver_account_details, - &rcc.h_wire)) - { - GNUNET_break (0); - json_decref (json_history); - return NULL; - } + GNUNET_CRYPTO_hash (pos->details.closing->receiver_account_details, + strlen (pos->details.closing->receiver_account_details) + 1, + &rcc.h_wire); rcc.wtid = pos->details.closing->wtid; if (GNUNET_OK != TEH_KS_sign (&rcc.purpose, diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c b/src/exchange/taler-exchange-httpd_track_transfer.c index 493febc21..429c86f35 100644 --- a/src/exchange/taler-exchange-httpd_track_transfer.c +++ b/src/exchange/taler-exchange-httpd_track_transfer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017 GNUnet e.V. + Copyright (C) 2014-2018 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 @@ -28,6 +28,7 @@ #include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_track_transfer.h" #include "taler-exchange-httpd_responses.h" +#include "taler_wire_lib.h" /** @@ -236,8 +237,8 @@ struct WtidTransactionContext * @param cls our context for transmission * @param rowid which row in the DB is the information from (for diagnostics) * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) - * @param wire_method which wire plugin was used * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param wire where the funds were sent * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param coin_pub which public key was this payment about @@ -248,8 +249,8 @@ static void handle_transaction_data (void *cls, uint64_t rowid, const struct TALER_MerchantPublicKeyP *merchant_pub, - const char *wire_method, const struct GNUNET_HashCode *h_wire, + const json_t *wire, struct GNUNET_TIME_Absolute exec_time, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -259,15 +260,22 @@ handle_transaction_data (void *cls, struct WtidTransactionContext *ctx = cls; struct TALER_Amount delta; struct TEH_TrackTransferDetail *wdd; + char *wire_method; if (GNUNET_SYSERR == ctx->is_valid) return; + if (NULL == (wire_method = TALER_JSON_wire_to_method (wire))) + { + GNUNET_break (0); + ctx->is_valid = GNUNET_SYSERR; + return; + } if (GNUNET_NO == ctx->is_valid) { ctx->merchant_pub = *merchant_pub; ctx->h_wire = *h_wire; ctx->exec_time = exec_time; - ctx->wire_method = GNUNET_strdup (wire_method); + ctx->wire_method = wire_method; ctx->is_valid = GNUNET_YES; if (GNUNET_OK != TALER_amount_subtract (&ctx->total, @@ -292,8 +300,10 @@ handle_transaction_data (void *cls, { GNUNET_break (0); ctx->is_valid = GNUNET_SYSERR; + GNUNET_free (wire_method); return; } + GNUNET_free (wire_method); if (GNUNET_OK != TALER_amount_subtract (&delta, deposit_value, diff --git a/src/exchange/taler-exchange-httpd_validation.c b/src/exchange/taler-exchange-httpd_validation.c index bef6aec59..7daa18aa7 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 GNUnet e.V. + Copyright (C) 2016, 2017, 2018 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 @@ -24,6 +24,8 @@ #include "taler-exchange-httpd.h" #include "taler-exchange-httpd_validation.h" #include "taler-exchange-httpd_wire.h" +#include "taler_exchangedb_lib.h" +#include "taler_json_lib.h" #include "taler_wire_lib.h" @@ -43,11 +45,6 @@ struct Plugin */ struct Plugin *prev; - /** - * Type of the wireformat. - */ - char *type; - /** * Pointer to the plugin. */ @@ -55,6 +52,7 @@ struct Plugin }; + /** * Head of DLL of wire plugins. */ @@ -65,50 +63,162 @@ static struct Plugin *wire_head; */ static struct Plugin *wire_tail; +/** + * Array of wire methods supported by this exchange. + */ +static json_t *wire_accounts_array; + +/** + * Object mapping wire methods to the respective fee structure. + */ +static json_t *wire_fee_object; + /** - * Load plugin @a name. + * Load wire fees for @a method. + * + * @param method wire method to load fee structure for + * @return #GNUNET_OK on success + */ +static int +load_fee (const char *method) +{ + json_t *fees; + + if (NULL != json_object_get (wire_fee_object, + method)) + return GNUNET_OK; /* already have them */ + fees = TEH_WIRE_get_fees (method); + if (NULL == fees) + return GNUNET_SYSERR; + /* Add fees to #wire_fee_object */ + GNUNET_assert (-1 != + json_object_set_new (wire_fee_object, + method, + fees)); + return GNUNET_OK; +} + + +/** + * Initialize account; checks if @ai has /wire information, and if so, + * adds the /wire information (if included) to our responses. Also, if + * the account is debitable, we try to load the plugin. * * @param cls pointer to `int` to set to #GNUNET_SYSERR on errors * @param name name of the plugin to load */ static void -load_plugin (void *cls, - const char *name) +load_account (void *cls, + const struct TALER_EXCHANGEDB_AccountInfo *ai) { int *ret = cls; - struct Plugin *p; - json_t *fees; - p = GNUNET_new (struct Plugin); - p->type = GNUNET_strdup (name); - p->plugin = TALER_WIRE_plugin_load (cfg, - name); - if (NULL == p->plugin) + if ( (NULL != ai->wire_response_filename) && + (GNUNET_YES == ai->credit_enabled) ) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load plugin %s\n", - name); - GNUNET_free (p->type); - GNUNET_free (p); - *ret = GNUNET_SYSERR; - return; + json_t *wire_s; + json_error_t error; + char *url; + char *method; + + if (NULL == (wire_s = json_load_file (ai->wire_response_filename, + JSON_REJECT_DUPLICATES, + &error))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse `%s': %s at %d:%d (%d)\n", + ai->wire_response_filename, + error.text, + error.line, + error.column, + error.position); + *ret = GNUNET_SYSERR; + return; + } + if (NULL == (url = TALER_JSON_wire_to_payto (wire_s))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire response file `%s' lacks `url' entry\n", + ai->wire_response_filename); + json_decref (wire_s); + *ret = GNUNET_SYSERR; + return; + } + if (0 != strcasecmp (url, + ai->payto_url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "URL in Wire response file `%s' does not match URL in configuration!\n", + ai->wire_response_filename); + json_decref (wire_s); + GNUNET_free (url); + *ret = GNUNET_SYSERR; + return; + } + GNUNET_free (url); + if (GNUNET_OK != + TALER_JSON_wire_signature_check (wire_s, + &TEH_master_public_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid signature in `%s'\n", + ai->wire_response_filename); + json_decref (wire_s); + *ret = GNUNET_SYSERR; + return; + } + method = TALER_WIRE_payto_get_method (ai->payto_url); + if (GNUNET_OK == + load_fee (method)) + { + GNUNET_assert (-1 != + json_array_append_new (wire_accounts_array, + wire_s)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wire fees not specified for `%s', ignoring plugin %s\n", + method, + ai->plugin_name); + *ret = GNUNET_SYSERR; + } + GNUNET_free (method); } - fees = TEH_WIRE_get_fees (name); - if (NULL == fees) + + if (GNUNET_YES == ai->debit_enabled) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Disabling method `%s' as wire transfer fees are not given correctly\n", - name); - GNUNET_free (p->type); - GNUNET_free (p); - *ret = GNUNET_SYSERR; - return; + 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)) + { + 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); + *ret = GNUNET_SYSERR; + return; + } + GNUNET_CONTAINER_DLL_insert (wire_head, + wire_tail, + p); } - json_decref (fees); - GNUNET_CONTAINER_DLL_insert (wire_head, - wire_tail, - p); } @@ -124,9 +234,11 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg) int ret; ret = GNUNET_OK; - TALER_WIRE_find_enabled (cfg, - &load_plugin, - &ret); + wire_accounts_array = json_array (); + wire_fee_object = json_object (); + TALER_EXCHANGEDB_find_accounts (cfg, + &load_account, + &ret); if (NULL == wire_head) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -153,9 +265,12 @@ TEH_VALIDATION_done () wire_tail, p); TALER_WIRE_plugin_unload (p->plugin); - GNUNET_free (p->type); GNUNET_free (p); } + json_decref (wire_fee_object); + wire_fee_object = NULL; + json_decref (wire_accounts_array); + wire_accounts_array = NULL; } @@ -164,109 +279,74 @@ TEH_VALIDATION_done () * a wire address. * * @param wire the JSON wire format object - * @param ours #GNUNET_YES if the signature should match our master key * @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, - int ours, char **emsg) { - const char *stype; + const char *payto_url; json_error_t error; - struct Plugin *p; + char *method; *emsg = NULL; if (0 != json_unpack_ex ((json_t *) wire, &error, 0, "{s:s}", - "type", &stype)) + "url", &payto_url)) { GNUNET_asprintf (emsg, - "No `type' specified in the wire details\n"); + "No `url' specified in the wire details\n"); return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_MISSING; } - for (p=wire_head; NULL != p; p = p->next) - if (0 == strcasecmp (p->type, - stype)) - return p->plugin->wire_validate (p->plugin->cls, - wire, - (GNUNET_YES == ours) - ? &TEH_master_public_key - : NULL, - emsg); + 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", - stype); + method); + GNUNET_free (method); return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_UNSUPPORTED; } /** - * Obtain JSON of the supported wire methods for a given - * account name prefix. + * Obtain JSON response for /wire * - * @return JSON array with the supported validation methods + * @return JSON array with the supported validation methods, NULL on error */ json_t * -TEH_VALIDATION_get_wire_methods () +TEH_VALIDATION_get_wire_response () { - json_t *methods; - char *account_name; - char *emsg; - enum TALER_ErrorCode ec; - - methods = json_object (); - for (struct Plugin *p=wire_head;NULL != p;p = p->next) - { - struct TALER_WIRE_Plugin *plugin = p->plugin; - json_t *method; - json_t *fees; - - GNUNET_asprintf (&account_name, - "exchange-wire-%s", - p->type); - method = plugin->get_wire_details (plugin->cls, - cfg, - account_name); - if (TALER_EC_NONE != - (ec = TEH_json_validate_wireformat (method, - GNUNET_YES, - &emsg))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Disabling method `%s' as details are ill-formed: %s (%d)\n", - p->type, - emsg, - ec); - GNUNET_free (emsg); - json_decref (method); - method = NULL; - } - fees = TEH_WIRE_get_fees (p->type); - if (NULL == fees) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Disabling method `%s' as wire transfer fees are not given correctly\n", - p->type); - json_decref (method); - method = NULL; - } - else - { - json_object_set_new (method, - "fees", - fees); - } - - if (NULL != method) - json_object_set_new (methods, - p->type, - method); - GNUNET_free (account_name); - } - return methods; + if ( (0 == json_array_size (wire_accounts_array)) || + (0 == json_object_size (wire_fee_object)) ) + return NULL; + return json_pack ("{s:O, s:O}", + "accounts", wire_accounts_array, + "fees", wire_fee_object); } diff --git a/src/exchange/taler-exchange-httpd_validation.h b/src/exchange/taler-exchange-httpd_validation.h index d910da74f..a0d0795ff 100644 --- a/src/exchange/taler-exchange-httpd_validation.h +++ b/src/exchange/taler-exchange-httpd_validation.h @@ -47,24 +47,21 @@ TEH_VALIDATION_done (void); * a wire address. * * @param wire the JSON wire format object - * @param ours #GNUNET_YES if the signature should match our master key * @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, - int ours, char **emsg); /** - * Obtain JSON of the supported wire methods for a given - * account name prefix. + * Obtain JSON response for /wire * - * @return JSON array with the supported validation methods + * @return JSON object with the supported wire transfer options, NULL on error */ json_t * -TEH_VALIDATION_get_wire_methods (void); +TEH_VALIDATION_get_wire_response (void); #endif diff --git a/src/exchange/taler-exchange-httpd_wire.c b/src/exchange/taler-exchange-httpd_wire.c index 69b800ff8..bbbf3fb48 100644 --- a/src/exchange/taler-exchange-httpd_wire.c +++ b/src/exchange/taler-exchange-httpd_wire.c @@ -74,20 +74,20 @@ fees_to_json (struct TALER_EXCHANGEDB_AggregateFees *af) /** - * Obtain fee structure for @a wire_plugin_name wire transfers. + * Obtain fee structure for @a method wire transfers. * - * @param wire_plugin_name name of the plugin to load fees for + * @param method method to load fees for * @return JSON object (to be freed by caller) with fee structure */ json_t * -TEH_WIRE_get_fees (const char *wire_plugin_name) +TEH_WIRE_get_fees (const char *method) { struct TALER_EXCHANGEDB_AggregateFees *af; json_t *j; struct GNUNET_TIME_Absolute now; af = TALER_EXCHANGEDB_fees_read (cfg, - wire_plugin_name); + method); now = GNUNET_TIME_absolute_get (); while ( (NULL != af) && (af->end_date.abs_value_us < now.abs_value_us) ) @@ -101,7 +101,7 @@ TEH_WIRE_get_fees (const char *wire_plugin_name) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to find current wire transfer fees for `%s'\n", - wire_plugin_name); + method); return NULL; } j = fees_to_json (af); @@ -143,9 +143,8 @@ TEH_WIRE_handler_wire (struct TEH_RequestHandler *rh, int TEH_WIRE_init () { - wire_methods = TEH_VALIDATION_get_wire_methods (); - if ( (NULL == wire_methods) || - (0 == json_object_size (wire_methods)) ) + wire_methods = TEH_VALIDATION_get_wire_response (); + if (NULL == wire_methods) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to find properly configured wire transfer method\n"); @@ -156,7 +155,7 @@ TEH_WIRE_init () /** - * Initialize libgcrypt. + * Clean up wire subsystem. */ void __attribute__ ((destructor)) TEH_wire_cleanup () diff --git a/src/exchange/taler-exchange-httpd_wire.h b/src/exchange/taler-exchange-httpd_wire.h index 72dd2198f..48f82beff 100644 --- a/src/exchange/taler-exchange-httpd_wire.h +++ b/src/exchange/taler-exchange-httpd_wire.h @@ -39,11 +39,11 @@ TEH_WIRE_init (void); /** * Obtain fee structure for @a wire_plugin_name wire transfers. * - * @param wire_plugin_name name of the plugin to load fees for + * @param method method to load fees for * @return JSON object (to be freed by caller) with fee structure */ json_t * -TEH_WIRE_get_fees (const char *wire_plugin_name); +TEH_WIRE_get_fees (const char *method); /** diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c index e0985366d..cabfac7f4 100644 --- a/src/exchange/taler-exchange-wirewatch.c +++ b/src/exchange/taler-exchange-wirewatch.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016, 2017 GNUnet e.V. + Copyright (C) 2016, 2017, 2018 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 @@ -54,9 +54,58 @@ struct RejectContext /** - * Handle to the plugin. + * Information we keep for each supported account. */ -static struct TALER_WIRE_Plugin *wire_plugin; +struct WireAccount +{ + /** + * Accounts are kept in a DLL. + */ + struct WireAccount *next; + + /** + * Plugins are kept in a DLL. + */ + struct WireAccount *prev; + + /** + * Handle to the plugin. + */ + struct TALER_WIRE_Plugin *wire_plugin; + + /** + * Name of the section that configures this account. + */ + char *section_name; + + /** + * Are we running from scratch and should re-process all transactions + * for this account? + */ + int reset_mode; + + /** + * Until when is processing this wire plugin delayed? + */ + struct GNUNET_TIME_Absolute delayed_until; + +}; + + +/** + * Head of list of loaded wire plugins. + */ +static struct WireAccount *wa_head; + +/** + * Tail of list of loaded wire plugins. + */ +static struct WireAccount *wa_tail; + +/** + * Wire plugin we are currently using. + */ +static struct WireAccount *wa_pos; /** * Which currency is used by this exchange? @@ -90,11 +139,6 @@ static void *last_row_off; */ static size_t last_row_off_size; -/** - * Which wire plugin are we watching? - */ -static char *type; - /** * Should we delay the next request to the wire plugin a bit? */ @@ -134,6 +178,8 @@ static struct TALER_WIRE_RejectHandle *rt; static void shutdown_task (void *cls) { + struct WireAccount *wa; + if (NULL != task) { GNUNET_SCHEDULER_cancel (task); @@ -141,29 +187,72 @@ shutdown_task (void *cls) } if (NULL != hh) { - wire_plugin->get_history_cancel (wire_plugin->cls, - hh); + wa_pos->wire_plugin->get_history_cancel (wa_pos->wire_plugin->cls, + hh); hh = NULL; } if (NULL != rt) { char *wtid_s; - wtid_s = wire_plugin->reject_transfer_cancel (wire_plugin->cls, - rt); + 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; - TALER_WIRE_plugin_unload (wire_plugin); - wire_plugin = NULL; + while (NULL != (wa = wa_head)) + { + GNUNET_CONTAINER_DLL_remove (wa_head, + wa_tail, + wa); + TALER_WIRE_plugin_unload (wa->wire_plugin); + 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; } +/** + * Function called with information about a wire account. Adds the + * account to our list (if it is enabled and we can load the plugin). + * + * @param cls closure, NULL + * @param ai account information + */ +static void +add_account_cb (void *cls, + const struct TALER_EXCHANGEDB_AccountInfo *ai) +{ + struct WireAccount *wa; + + (void) cls; + if (GNUNET_YES != ai->credit_enabled) + 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) + { + fprintf (stderr, + "Failed to load wire plugin for `%s'\n", + ai->plugin_name); + GNUNET_free (wa); + return; + } + wa->section_name = GNUNET_strdup (ai->section_name); + GNUNET_CONTAINER_DLL_insert (wa_head, + wa_tail, + wa); +} + + /** * Parse configuration parameters for the exchange server into the * corresponding global variables. @@ -173,12 +262,6 @@ shutdown_task (void *cls) static int exchange_serve_process_config () { - if (NULL == type) - { - fprintf (stderr, - "Option `-t' to specify wire plugin is mandatory.\n"); - return GNUNET_SYSERR; - } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "taler", @@ -206,17 +289,16 @@ exchange_serve_process_config () "Failed to initialize DB subsystem\n"); return GNUNET_SYSERR; } - if (NULL == - (wire_plugin = TALER_WIRE_plugin_load (cfg, - type))) + TALER_EXCHANGEDB_find_accounts (cfg, + &add_account_cb, + NULL); + if (NULL == wa_head) { fprintf (stderr, - "Failed to load wire plugin for `%s'\n", - type); + "No wire accounts configured for credit!\n"); TALER_EXCHANGEDB_plugin_unload (db_plugin); return GNUNET_SYSERR; } - return GNUNET_OK; } @@ -292,12 +374,11 @@ history_cb (void *cls, struct TALER_ReservePublicKeyP reserve_pub; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got history callback, direction %u!\n", (unsigned int) dir); - + "Got history callback, direction %u!\n", + (unsigned int) dir); if (TALER_BANK_DIRECTION_NONE == dir) { hh = NULL; - if (TALER_EC_NONE != ec) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -309,18 +390,27 @@ history_cb (void *cls, qs = db_plugin->commit (db_plugin->cls, session); if ( (GNUNET_YES == delay) && - (test_mode) ) + (test_mode) && + (NULL == wa_pos->next) ) { GNUNET_SCHEDULER_shutdown (); return GNUNET_OK; } if (GNUNET_YES == delay) - task = GNUNET_SCHEDULER_add_delayed (DELAY, - &find_transfers, - NULL); - else - task = GNUNET_SCHEDULER_add_now (&find_transfers, - NULL); + { + wa_pos->delayed_until + = GNUNET_TIME_relative_to_absolute (DELAY); + GNUNET_free_non_null (last_row_off); + last_row_off = NULL; + last_row_off_size = 0; + wa_pos = wa_pos->next; + if (NULL == wa_pos) + wa_pos = wa_head; + GNUNET_assert (NULL != wa_pos); + } + task = GNUNET_SCHEDULER_add_at (wa_pos->delayed_until, + &find_transfers, + NULL); return GNUNET_OK; /* will be ignored anyway */ } if (NULL != details->wtid_s) @@ -344,11 +434,12 @@ history_cb (void *cls, rtc = GNUNET_new (struct RejectContext); rtc->session = session; rtc->wtid_s = GNUNET_strdup (details->wtid_s); - rt = wire_plugin->reject_transfer (wire_plugin->cls, - row_off, - row_off_size, - &reject_cb, - rtc); + rt = wa_pos->wire_plugin->reject_transfer (wa_pos->wire_plugin->cls, + wa_pos->section_name, + row_off, + row_off_size, + &reject_cb, + rtc); return GNUNET_SYSERR; /* will continue later... */ } @@ -366,7 +457,8 @@ history_cb (void *cls, &reserve_pub, &details->amount, details->execution_date, - details->account_details, + details->account_url, + wa_pos->section_name, row_off, row_off_size); if (GNUNET_DB_STATUS_HARD_ERROR == qs) @@ -434,10 +526,11 @@ find_transfers (void *cls) GNUNET_SCHEDULER_shutdown (); return; } - if (! reset_mode) + if (! wa_pos->reset_mode) { qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls, session, + wa_pos->section_name, &last_row_off, &last_row_off_size); if (GNUNET_DB_STATUS_HARD_ERROR == qs) @@ -456,17 +549,19 @@ find_transfers (void *cls) return; } } + wa_pos->reset_mode = GNUNET_NO; GNUNET_assert ( (NULL == last_row_off) || ( (NULL != last_row_off) && (0 != last_row_off_size) ) ); delay = GNUNET_YES; - hh = wire_plugin->get_history (wire_plugin->cls, - TALER_BANK_DIRECTION_CREDIT, - last_row_off, - last_row_off_size, - 1024, - &history_cb, - session); + 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, + 1024, + &history_cb, + session); if (NULL == hh) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -501,7 +596,8 @@ run (void *cls, global_ret = 1; return; } - + wa_pos = wa_head; + GNUNET_assert (NULL != wa_pos); task = GNUNET_SCHEDULER_add_now (&find_transfers, NULL); GNUNET_SCHEDULER_add_shutdown (&shutdown_task, @@ -521,11 +617,6 @@ main (int argc, char *const *argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_string ('t', - "type", - "PLUGINNAME", - "which wire plugin to use", - &type), GNUNET_GETOPT_option_flag ('T', "test", "run in test mode and exit when idle", diff --git a/src/exchange/test-taler-exchange-aggregator-postgres.conf b/src/exchange/test-taler-exchange-aggregator-postgres.conf index a5f35196b..c2c8aef1d 100644 --- a/src/exchange/test-taler-exchange-aggregator-postgres.conf +++ b/src/exchange/test-taler-exchange-aggregator-postgres.conf @@ -16,7 +16,8 @@ PORT = 8081 # Master public key used to sign the exchange's various keys MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG -# Expected base URL of the exchange. +# Expected base URL of the exchange. Used in wire transfers for +# the tracking API. BASE_URL = "https://exchange.taler.net/" [exchangedb] @@ -31,16 +32,24 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks [exchangedb-postgres] #The connection string the plugin has to use for connecting to the database -DB_CONN_STR = postgres:///talercheck +CONFIG = postgres:///talercheck -[exchange-wire-test] -# Enable 'test' for testing of the actual coin operations. -ENABLE = YES +[account-1] + +# What is the account URL? +URL = "payto://x-taler-bank/localhost:8082/3" + +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json +PLUGIN = "taler_bank" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES +TALER_BANK_AUTH_METHOD = NONE + +[fees-x-taler-bank] # Fees for the forseeable future... -# If you see this after 2017, update to match the next 10 years... -WIRE-FEE-2017 = EUR:0.01 +# If you see this after 2018, update to match the next 10 years... WIRE-FEE-2018 = EUR:0.01 WIRE-FEE-2019 = EUR:0.01 WIRE-FEE-2020 = EUR:0.01 @@ -50,8 +59,8 @@ WIRE-FEE-2023 = EUR:0.01 WIRE-FEE-2024 = EUR:0.01 WIRE-FEE-2025 = EUR:0.01 WIRE-FEE-2026 = EUR:0.01 +WIRE-FEE-2027 = EUR:0.01 -CLOSING-FEE-2017 = EUR:0.01 CLOSING-FEE-2018 = EUR:0.01 CLOSING-FEE-2019 = EUR:0.01 CLOSING-FEE-2020 = EUR:0.01 @@ -61,11 +70,4 @@ CLOSING-FEE-2023 = EUR:0.01 CLOSING-FEE-2024 = EUR:0.01 CLOSING-FEE-2025 = EUR:0.01 CLOSING-FEE-2026 = EUR:0.01 - - -# What is the main website of the bank? -BANK_URL = "http://localhost:8082/" - -# From which account at the 'bank' should outgoing -# wire transfers be made? -EXCHANGE_ACCOUNT_NUMBER = 3 +CLOSING-FEE-2027 = EUR:0.01 diff --git a/src/exchange/test-taler-exchange-wirewatch-postgres.conf b/src/exchange/test-taler-exchange-wirewatch-postgres.conf index cc614fc83..7f8cc4793 100644 --- a/src/exchange/test-taler-exchange-wirewatch-postgres.conf +++ b/src/exchange/test-taler-exchange-wirewatch-postgres.conf @@ -33,16 +33,23 @@ IDLE_RESERVE_EXPIRATION_TIME = 5 s [exchangedb-postgres] #The connection string the plugin has to use for connecting to the database -DB_CONN_STR = postgres:///talercheck +CONFIG = postgres:///talercheck +[account-1] -[exchange-wire-test] -# Enable 'test' for testing of the actual coin operations. -ENABLE = YES +# What is the account URL? +URL = "payto://x-taler-bank/localhost:8082/3" + +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json +PLUGIN = "taler_bank" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES +TALER_BANK_AUTH_METHOD = NONE + +[fees-x-taler-bank] # Fees for the forseeable future... -# If you see this after 2017, update to match the next 10 years... -WIRE-FEE-2017 = EUR:0.01 +# If you see this after 2018, update to match the next 10 years... WIRE-FEE-2018 = EUR:0.01 WIRE-FEE-2019 = EUR:0.01 WIRE-FEE-2020 = EUR:0.01 @@ -52,8 +59,8 @@ WIRE-FEE-2023 = EUR:0.01 WIRE-FEE-2024 = EUR:0.01 WIRE-FEE-2025 = EUR:0.01 WIRE-FEE-2026 = EUR:0.01 +WIRE-FEE-2027 = EUR:0.01 -CLOSING-FEE-2017 = EUR:0.01 CLOSING-FEE-2018 = EUR:0.01 CLOSING-FEE-2019 = EUR:0.01 CLOSING-FEE-2020 = EUR:0.01 @@ -63,11 +70,4 @@ CLOSING-FEE-2023 = EUR:0.01 CLOSING-FEE-2024 = EUR:0.01 CLOSING-FEE-2025 = EUR:0.01 CLOSING-FEE-2026 = EUR:0.01 - - -# What is the main website of the bank? -BANK_URL = "http://localhost:8082/" - -# From which account at the 'bank' should outgoing -# wire transfers be made? -EXCHANGE_ACCOUNT_NUMBER = 3 +CLOSING-FEE-2027 = EUR:0.01 diff --git a/src/exchange/test_taler_exchange_aggregator.c b/src/exchange/test_taler_exchange_aggregator.c index 0335bcd44..f22e63815 100644 --- a/src/exchange/test_taler_exchange_aggregator.c +++ b/src/exchange/test_taler_exchange_aggregator.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2016, 2017 Inria and GNUnet e.V. + (C) 2016, 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 @@ -403,7 +403,9 @@ do_deposit (struct Command *cmd) struct TALER_MerchantPrivateKeyP merchant_priv; int ret; - memset (&deposit, 0, sizeof (deposit)); + memset (&deposit, + 0, + sizeof (deposit)); /* we derive the merchant's private key from the name, to ensure that the same name always results in the same key pair. */ @@ -432,13 +434,21 @@ do_deposit (struct Command *cmd) } fake_coin (&deposit.coin); /* Build JSON for wire details */ - deposit.receiver_wire_account = json_pack ("{s:s, s:s, s:I}", - "type", "test", - "bank_url", "http://localhost:8082/", - "account_number", (json_int_t) cmd->details.deposit.merchant_account); + { + char *str; + + GNUNET_asprintf (&str, + "payto://x-taler-bank/localhost:8082/%llu", + (unsigned long long) cmd->details.deposit.merchant_account); + deposit.receiver_wire_account + = json_pack ("{s:s, s:s}", + "salt", "this-is-a-salt-value", + "url", str); + GNUNET_free (str); + } GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (deposit.receiver_wire_account, - &deposit.h_wire)); + TALER_JSON_wire_signature_hash (deposit.receiver_wire_account, + &deposit.h_wire)); deposit.timestamp = GNUNET_TIME_absolute_get (); GNUNET_TIME_round_abs (&deposit.timestamp); deposit.wire_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.wire_deadline); diff --git a/src/exchange/test_taler_exchange_httpd.conf b/src/exchange/test_taler_exchange_httpd.conf index 7df8d9b22..743dbfede 100644 --- a/src/exchange/test_taler_exchange_httpd.conf +++ b/src/exchange/test_taler_exchange_httpd.conf @@ -30,18 +30,49 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks [exchangedb-postgres] -DB_CONN_STR = "postgres:///talercheck" +CONFIG = "postgres:///talercheck" -[exchange-wire-test] -# Enable 'test' for testing of the actual coin operations. -ENABLE = YES +[account-1] # What is the main website of the bank? -BANK_URL = "http://localhost:8082/" - -# From which account at the 'bank' should outgoing -# wire transfers be made? -EXCHANGE_ACCOUNT_NUMBER = 3 +URL = "payto://x-taler-bank/localhost:8082/3" + +WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json + +PLUGIN = "taler_bank" + +ENABLE_DEBIT = YES + +ENABLE_CREDIT = YES + +TALER_BANK_AUTH_METHOD = NONE + + +# Wire fees are specified by wire method, NOT by wire plugin. +[fees-x-taler-bank] +# Fees for the forseeable future... +# If you see this after 2018, update to match the next 10 years... +WIRE-FEE-2018 = EUR:0.01 +WIRE-FEE-2019 = EUR:0.01 +WIRE-FEE-2020 = EUR:0.01 +WIRE-FEE-2021 = EUR:0.01 +WIRE-FEE-2022 = EUR:0.01 +WIRE-FEE-2023 = EUR:0.01 +WIRE-FEE-2024 = EUR:0.01 +WIRE-FEE-2025 = EUR:0.01 +WIRE-FEE-2026 = EUR:0.01 +WIRE-FEE-2027 = EUR:0.01 + +CLOSING-FEE-2018 = EUR:0.01 +CLOSING-FEE-2019 = EUR:0.01 +CLOSING-FEE-2020 = EUR:0.01 +CLOSING-FEE-2021 = EUR:0.01 +CLOSING-FEE-2022 = EUR:0.01 +CLOSING-FEE-2023 = EUR:0.01 +CLOSING-FEE-2024 = EUR:0.01 +CLOSING-FEE-2025 = EUR:0.01 +CLOSING-FEE-2026 = EUR:0.01 +CLOSING-FEE-2027 = EUR:0.01 # Coins for the tests. diff --git a/src/exchange/test_taler_exchange_httpd.data b/src/exchange/test_taler_exchange_httpd.data index f88f42063..48fd8eec2 100644 --- a/src/exchange/test_taler_exchange_httpd.data +++ b/src/exchange/test_taler_exchange_httpd.data @@ -1,5 +1,5 @@ # This file is part of TALER -# Copyright (C) 2015 GNUnet e.V. +# Copyright (C) 2015, 2018 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 @@ -23,21 +23,6 @@ # Note that neither element may contain any spaces! # # -# Bad amount: -/admin/add/incoming {"reserve_pub":"7RZBZ86677QMASD2KAYGEPD246C7B7RC6P101FNTG6ZK8X61A620","amount":"1","execution_date":"\/Date(1435934428788)\/","wire":{"empty":"empty"}} -# -# Bad wire format: -/admin/add/incoming {"reserve_pub":"6VRFYZRVHJ434BV3J018MS6H7Q1V5Q6YECNMEF9G4WKB8QJQCAX0","amount":{"currency":"EUR","value":10,"fraction":3},"execution_date":"\/Date(1436258333286)\/","wire":{"empty":"empty"}} -# -# Malformed JSON (ill-balanced quotes around 'amount') -/admin/add/incoming {"reserve_pub":"BSEFVVNZ4C3724BPVKTJMQMD73HQREA5FWSS1C1BZ36ZFF2WBTK0",amount":{"currency":"EUR","value":5,"fraction":3},"execution_date":"\/Date(1436271156447)\/","wire":{"type":"test","IBAN":0,"name":"Jack","BIC":999,"edate":"\/Date(1436271156447)\/","r":50}} -# -# Bad amount (value not a string) -/admin/add/incoming {"reserve_pub":"BSEFVVNZ4C3724BPVKTJMQMD73HQREA5FWSS1C1BZ36ZFF2WBTK0","amount":{"currency":"EUR","value":"5","fraction":3},"execution_date":"\/Date(1436271156447)\/","wire":{"type":"test","IBAN":0,"name":"Jack","BIC":999,"edate":"\/Date(1436271156447)\/","r":50}} -# -# Bad amount (overall amount is a string) -/admin/add/incoming {"reserve_pub":"BSEFVVNZ4C3724BPVKTJMQMD73HQREA5FWSS1C1BZ36ZFF2WBTK0","amount":"{\"currency\":\"EUR\",\"value\":5,\"fraction\":3}","execution_date":"\/Date(1436271156447)\/","wire":{"type":"test"}} -# # Bogus denomination key /deposit {"f":{"currency":"EUR","value":5,"fraction":0},"h_contract_terms":"NRT9E07FYT147V4VCDG0102P0YX0FZ11ZRG90F4X1HDV95M0J64ZVE4XQGNN9MJ3B5K3JX6TJ181KNGRYSZSTYZ5PQHBM1F9QKQ5B50","wire":{"bank":"dest bank","type":"TEST","account":42},"timestamp":"/Date(1436823947)/","coin_pub":"2KCPBGZ77VGJT4DG99EZAY0GQ5TJ89DF53FWYR5RFRTK0CCXRMFG","denom_pub":"51B7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30E9S6GVK2DHM8S234C236CR32C9N8RW44E9M712KAH1R60VM2CJ16RT3GGA18RR36CA575144DJ58CTK0E9M8D2M2E9S8GTKGH1Q8S0KACT174S3AD2670R4ADJ664W32C1N8N23CHA58MSK6DJ26WSMAD1P8H132CHP8GWKAG9K8RS46GJ6890M6GT28GSK4GJ66X2KCCA168RM4GA67113GDA28RR4AGA36RVK6GA460VKJDT58CVK6HA488R48E9R6D2KEH258N246HHJ850K4H9R8N0KEC9N68SM2EA48RR3JEA284SM6C9M6D130D228MSK6H1J6MSKCH1K8CR38CJ48MV36GJ38513CE9P60TM6CA56D1K8HHQ75244DA26WW4CG9M8MW3JE9M7133JGH354520818CMG26C1H60R30C935452081918G2J2G0","coin_sig":"W1TDFCSW5XQX9ZF4QPVP3JAJFYA7G4X6SY2B49KRNTDMA685M9YNFETV4610RFKZMSQ3RBRCYBJQH1ZQSMTDMW9W8X6C9SGPCA5ST0R","H_wire":"YQED9FDYPKK2QQYB3FS19Y15ZMKBAXJP2C73CXASAF1KM6ZYY723TEJ3HBR6D864A7X5W58G92QJ0A9PFMZNB81ZP9NJAQQCCABM4RG","ub_sig":"51SPJSSDESGPR80A40M74WV140520818ECG26DSQ8913AC1S7513EC9G6914AE228H236HHR68VKCCSK650MAGSM6GV3GCHP6D330H1R8GVKJD9P68W46H9Q8GVK8HA6752M2CSN8N248DHJ8H346E9J8RS4CDA28D33ECJ38S33JC9R6MRM8D9G74WM8HA668T44H1N6RT44GHS8CSK8H1G6D346C9J6CS3EC1N8H2M4HA38CSK2D246CW4CD1P70VMAD1Q891K8H1M64TK6C258MRM6G9R88RM6E2488WK2CSQ6GW3GH9N64RKGH2375136GA66533GCSJ65344CHH84W38HHP75330DA58RSKJCJ364SK0C1R8GVK6DSP61134HA48GT4CE1J6MW36C9G891K2GT68GTMCCSQ890M8E1P88R44DA174VK4E135452081918G2J2G0","merchant_pub":"4032W2ZXFW000KRJQDH3CZR00000000004000030P6YG1NR50000","refund_deadline":"/Date(0)/"} # diff --git a/src/exchange/test_taler_exchange_httpd.sh b/src/exchange/test_taler_exchange_httpd.sh index 7cd2e2762..9c9ef40e1 100755 --- a/src/exchange/test_taler_exchange_httpd.sh +++ b/src/exchange/test_taler_exchange_httpd.sh @@ -25,7 +25,9 @@ unset XDG_DATA_HOME unset XDG_CONFIG_HOME # # Setup keys. -taler-exchange-keyup -c test_taler_exchange_httpd.conf +taler-exchange-keyup -c test_taler_exchange_httpd.conf || exit 1 +# Setup wire accounts. +taler-exchange-wire -c test_taler_exchange_httpd.conf || exit 1 # Run Exchange HTTPD (in background) taler-exchange-httpd -c test_taler_exchange_httpd.conf -i & # Give HTTP time to start diff --git a/src/exchange/test_taler_exchange_wirewatch.c b/src/exchange/test_taler_exchange_wirewatch.c index 69502d9d4..8c1210da2 100644 --- a/src/exchange/test_taler_exchange_wirewatch.c +++ b/src/exchange/test_taler_exchange_wirewatch.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2016, 2017 Inria and GNUnet e.V. + (C) 2016, 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 @@ -499,7 +499,6 @@ interpreter (void *cls) "taler-exchange-wirewatch", "taler-exchange-wirewatch", "-c", config_filename, - "-t", "test", "-T", /* run in test mode, exit instead of looping */ NULL); if (NULL == cmd->details.wirewatch.wirewatch_proc) diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 66299d786..03322f8c5 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -42,6 +42,7 @@ lib_LTLIBRARIES = \ libtalerexchangedb.la libtalerexchangedb_la_SOURCES = \ + exchangedb_accounts.c \ exchangedb_auditorkeys.c \ exchangedb_denomkeys.c \ exchangedb_fees.c \ @@ -111,7 +112,8 @@ test_exchangedb_postgres_LDADD = \ $(top_builddir)/src/json/libtalerjson.la \ $(top_srcdir)/src/util/libtalerutil.la \ $(top_srcdir)/src/pq/libtalerpq.la \ - -lgnunetutil -ljansson + -ljansson \ + -lgnunetutil test_perf_taler_exchangedb_SOURCES = \ test_perf_taler_exchangedb.c \ @@ -121,8 +123,8 @@ test_perf_taler_exchangedb_LDADD = \ libtalerexchangedb.la \ $(top_srcdir)/src/util/libtalerutil.la \ $(top_srcdir)/src/pq/libtalerpq.la \ - -ljansson \ - -lgnunetutil + -lgnunetutil \ + -ljansson perf_exchangedb_SOURCES = \ perf_taler_exchangedb.c \ @@ -132,8 +134,8 @@ perf_exchangedb_LDADD = \ libtalerexchangedb.la \ $(top_srcdir)/src/util/libtalerutil.la \ $(top_srcdir)/src/pq/libtalerpq.la \ - -ljansson \ - -lgnunetutil + -lgnunetutil \ + -ljansson EXTRA_test_exchangedb_postgres_DEPENDENCIES = \ diff --git a/src/exchangedb/exchangedb-postgres.conf b/src/exchangedb/exchangedb-postgres.conf index 3de7474ff..5ebd656c8 100644 --- a/src/exchangedb/exchangedb-postgres.conf +++ b/src/exchangedb/exchangedb-postgres.conf @@ -1,2 +1,2 @@ [exchangedb-postgres] -DB_CONN_STR = "postgres:///taler" +CONFIG = "postgres:///taler" diff --git a/src/exchangedb/exchangedb_accounts.c b/src/exchangedb/exchangedb_accounts.c new file mode 100644 index 000000000..275e92674 --- /dev/null +++ b/src/exchangedb/exchangedb_accounts.c @@ -0,0 +1,141 @@ +/* + 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 exchangedb/exchangedb_accounts.c + * @brief Logic to parse account information from the configuration + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_exchangedb_lib.h" + + +/** + * Closure of #check_for_account. + */ +struct FindAccountContext +{ + /** + * Configuration we are usign. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Callback to invoke. + */ + TALER_EXCHANGEDB_AccountCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; +}; + + +/** + * Check if @a section begins with "exchange-wire-", and if + * so if the "ENABLE" option is set to "YES". If both are + * true, call the callback from the context with the + * rest of the section name. + * + * @param cls our `struct FindEnabledWireContext` + * @param section name of a section in the configuration + */ +static void +check_for_account (void *cls, + const char *section) +{ + struct FindAccountContext *ctx = cls; + char *plugin_name; + char *payto_url; + char *wire_response_filename; + struct TALER_EXCHANGEDB_AccountInfo ai; + + if (0 != strncasecmp (section, + "account-", + strlen ("account-"))) + return; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ctx->cfg, + section, + "URL", + &payto_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + section, + "URL"); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (ctx->cfg, + section, + "PLUGIN", + &plugin_name)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + section, + "PLUGIN"); + GNUNET_free (payto_url); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (ctx->cfg, + section, + "WIRE_RESPONSE", + &wire_response_filename)) + wire_response_filename = NULL; + ai.section_name = section; + ai.plugin_name = plugin_name; + ai.payto_url = payto_url; + ai.wire_response_filename = wire_response_filename; + ai.debit_enabled = (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (ctx->cfg, + section, + "ENABLE_DEBIT")); + ai.credit_enabled = (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (ctx->cfg, + section, + "ENABLE_CREDIT")); + ctx->cb (ctx->cb_cls, + &ai); + GNUNET_free (payto_url); + GNUNET_free (plugin_name); + GNUNET_free_non_null (wire_response_filename); +} + + +/** + * Parse the configuration to find account information. + * + * @param cfg configuration to use + * @param cb callback to invoke + * @param cb_cls closure for @a cb + */ +void +TALER_EXCHANGEDB_find_accounts (const struct GNUNET_CONFIGURATION_Handle *cfg, + TALER_EXCHANGEDB_AccountCallback cb, + void *cb_cls) +{ + struct FindAccountContext ctx; + + ctx.cfg = cfg; + ctx.cb = cb; + ctx.cb_cls = cb_cls; + GNUNET_CONFIGURATION_iterate_sections (cfg, + &check_for_account, + &ctx); +} + +/* end of exchangedb_accounts.c */ diff --git a/src/exchangedb/perf_taler_exchangedb.c b/src/exchangedb/perf_taler_exchangedb.c index e2591c88b..5cde719c1 100644 --- a/src/exchangedb/perf_taler_exchangedb.c +++ b/src/exchangedb/perf_taler_exchangedb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V. + Copyright (C) 2014-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 diff --git a/src/exchangedb/perf_taler_exchangedb_init.c b/src/exchangedb/perf_taler_exchangedb_init.c index 4efec3911..789aaea00 100644 --- a/src/exchangedb/perf_taler_exchangedb_init.c +++ b/src/exchangedb/perf_taler_exchangedb_init.c @@ -204,6 +204,7 @@ PERF_TALER_EXCHANGEDB_reserve_free (struct PERF_TALER_EXCHANGEDB_Reserve *reserv /** * Generate a dummy deposit for testing purposes + * * @param dki the denomination key used to sign the key */ struct TALER_EXCHANGEDB_Deposit * @@ -214,19 +215,12 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coi struct TALER_MerchantPublicKeyP merchant_pub; struct GNUNET_HashCode h_contract_terms; struct GNUNET_HashCode h_wire; - const char wire[] = "{" - "\"type\":\"SEPA\"," - "\"IBAN\":\"DE67830654080004822650\"," - "\"NAME\":\"GNUNET E.\"," - "\"BIC\":\"GENODEF1SRL\"" - "}"; struct GNUNET_TIME_Absolute timestamp; struct GNUNET_TIME_Absolute refund_deadline; struct TALER_Amount amount_with_fee; struct TALER_Amount deposit_fee; - GNUNET_assert (NULL != - (deposit = GNUNET_malloc (sizeof (struct TALER_EXCHANGEDB_Deposit) + sizeof (wire)))); + deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &h_contract_terms); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, @@ -253,9 +247,8 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coi eddsa_prv = GNUNET_CRYPTO_eddsa_key_create (); GNUNET_assert(NULL != eddsa_prv); - GNUNET_CRYPTO_eddsa_key_get_public ( - eddsa_prv, - &merchant_pub.eddsa_pub); + GNUNET_CRYPTO_eddsa_key_get_public (eddsa_prv, + &merchant_pub.eddsa_pub); GNUNET_free (eddsa_prv); } timestamp = GNUNET_TIME_absolute_get (); @@ -280,7 +273,10 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct PERF_TALER_EXCHANGEDB_Coin *coi deposit->csig = csig; deposit->h_contract_terms = h_contract_terms; deposit->h_wire = h_wire; - deposit->receiver_wire_account = json_loads (wire, 0, NULL); + deposit->receiver_wire_account + = json_pack ("{s:s, s:s}", + "url", "payto://sepa/DE67830654080004822650", + "salt", "this-is-a-salt-value"); deposit->timestamp = timestamp; deposit->refund_deadline = refund_deadline; deposit->amount_with_fee = amount_with_fee; @@ -301,7 +297,7 @@ PERF_TALER_EXCHANGEDB_deposit_copy (const struct TALER_EXCHANGEDB_Deposit *depos copy = GNUNET_new (struct TALER_EXCHANGEDB_Deposit); *copy = *deposit; - json_incref (copy->receiver_wire_account); + copy->receiver_wire_account = json_incref (deposit->receiver_wire_account); copy->coin.denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (deposit->coin.denom_pub.rsa_public_key); copy->coin.denom_sig.rsa_signature = diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c b/src/exchangedb/perf_taler_exchangedb_interpreter.c index 8a81befdb..43891e55f 100644 --- a/src/exchangedb/perf_taler_exchangedb_interpreter.c +++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c @@ -1243,19 +1243,18 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) unsigned int reserve_index; int ret; struct PERF_TALER_EXCHANGEDB_Reserve *reserve; - json_t *sndr; + char *sndr; uint32_t uid; struct GNUNET_TIME_Absolute now; reserve_index = state->cmd[state->i].details.insert_reserve.index_reserve; reserve = state->cmd[reserve_index].exposed.data.reserve; - sndr = json_pack ("{s:i}", - "account", - (int) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT32_MAX)); + GNUNET_asprintf (&sndr, + "payto://x-taler-test/localhost:8080/%u", + (unsigned int) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT32_MAX)); uid = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); - GNUNET_assert (NULL != sndr); now = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&now); ret = state->plugin->reserves_in_insert (state->plugin->cls, @@ -1264,10 +1263,11 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) &reserve->reserve.balance, now, sndr, + "account-1", &uid, sizeof (uid)); GNUNET_assert (GNUNET_SYSERR != ret); - json_decref (sndr); + GNUNET_free (sndr); } break; diff --git a/src/exchangedb/plugin_exchangedb_common.c b/src/exchangedb/plugin_exchangedb_common.c index e4b832491..8344974b4 100644 --- a/src/exchangedb/plugin_exchangedb_common.c +++ b/src/exchangedb/plugin_exchangedb_common.c @@ -42,8 +42,7 @@ common_free_reserve_history (void *cls, { case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE: bt = rh->details.bank; - if (NULL != bt->sender_account_details) - json_decref (bt->sender_account_details); + GNUNET_free_non_null (bt->sender_account_details); GNUNET_free_non_null (bt->wire_reference); GNUNET_free (bt); break; @@ -61,8 +60,7 @@ common_free_reserve_history (void *cls, break; case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK: closing = rh->details.closing; - if (NULL != closing->receiver_account_details) - json_decref (closing->receiver_account_details); + GNUNET_free_non_null (closing->receiver_account_details); GNUNET_free (closing); break; } @@ -92,7 +90,7 @@ common_free_coin_transaction_list (void *cls, switch (list->type) { case TALER_EXCHANGEDB_TT_DEPOSIT: - json_decref (list->details.deposit->receiver_wire_account); + GNUNET_free_non_null (list->details.deposit->receiver_wire_account); if (NULL != list->details.deposit->coin.denom_pub.rsa_public_key) GNUNET_CRYPTO_rsa_public_key_free (list->details.deposit->coin.denom_pub.rsa_public_key); if (NULL != list->details.deposit->coin.denom_sig.rsa_signature) diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 6c6330556..140bceb2e 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -127,6 +127,7 @@ postgres_drop_tables (void *cls) PGconn *conn; int ret; + /* FIXME: use GNUNET_PQ_connect_with_cfg instead? */ conn = GNUNET_PQ_connect (pc->connection_cfg_str); if (NULL == conn) return GNUNET_SYSERR; @@ -218,12 +219,13 @@ postgres_create_tables (void *cls) ",credit_frac INT4 NOT NULL" ",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" ",sender_account_details TEXT NOT NULL" + ",exchange_account_section TEXT NOT NULL" ",execution_date INT8 NOT NULL" ",PRIMARY KEY (reserve_pub, wire_reference)" ");"), /* Create indices on reserves_in */ GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_in_execution_index" - " ON reserves_in (execution_date);"), + " ON reserves_in (exchange_account_section,execution_date);"), /* This table contains the data for wire transfers the exchange has executed to close a reserve. */ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_close " @@ -433,6 +435,7 @@ postgres_create_tables (void *cls) PGconn *conn; int ret; + /* FIXME: use GNUNET_PQ_connect_with_cfg instead? */ conn = GNUNET_PQ_connect (pc->connection_cfg_str); if (NULL == conn) return GNUNET_SYSERR; @@ -585,21 +588,23 @@ postgres_prepare (PGconn *db_conn) ",credit_val" ",credit_frac" ",credit_curr" + ",exchange_account_section" ",sender_account_details" ",execution_date" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7) " + "($1, $2, $3, $4, $5, $6, $7, $8) " "ON CONFLICT DO NOTHING;", - 7), + 8), /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound transactions for reserves with serial id '\geq' the given parameter */ GNUNET_PQ_make_prepare ("reserves_in_get_latest_wire_reference", "SELECT" " wire_reference" " FROM reserves_in" + " WHERE exchange_account_section=$1" " ORDER BY reserve_in_serial_id DESC" " LIMIT 1;", - 0), + 1), /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound transactions for reserves with serial id '\geq' the given parameter */ GNUNET_PQ_make_prepare ("audit_reserves_in_get_transactions_incr", @@ -1509,6 +1514,7 @@ postgres_get_session (void *cls) return session; } } + /* FIXME: use GNUNET_PQ_connect_with_cfg instead? */ db_conn = GNUNET_PQ_connect (pc->connection_cfg_str); if (NULL == db_conn) return NULL; @@ -1836,7 +1842,9 @@ reserves_update (void *cls, * @param reserve_pub public key of the reserve * @param balance the amount that has to be added to the reserve * @param execution_time when was the amount added - * @param sender_account_details account information for the sender + * @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 * @return transaction status code @@ -1847,7 +1855,8 @@ postgres_reserves_in_insert (void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *balance, struct GNUNET_TIME_Absolute execution_time, - const json_t *sender_account_details, + const char *sender_account_details, + const char *exchange_account_section, const void *wire_reference, size_t wire_reference_size) { @@ -1900,7 +1909,7 @@ postgres_reserves_in_insert (void *cls, as a foreign key. */ struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (reserve_pub), - TALER_PQ_query_param_json (sender_account_details), + GNUNET_PQ_query_param_string (sender_account_details), TALER_PQ_query_param_amount (balance), TALER_PQ_query_param_absolute_time (&expiry), GNUNET_PQ_query_param_end @@ -1929,7 +1938,8 @@ postgres_reserves_in_insert (void *cls, GNUNET_PQ_query_param_fixed_size (wire_reference, wire_reference_size), TALER_PQ_query_param_amount (balance), - TALER_PQ_query_param_json (sender_account_details), + GNUNET_PQ_query_param_string (exchange_account_section), + GNUNET_PQ_query_param_string (sender_account_details), TALER_PQ_query_param_absolute_time (&execution_time), GNUNET_PQ_query_param_end }; @@ -1976,6 +1986,8 @@ postgres_reserves_in_insert (void *cls, * * @param cls the @e cls of this struct with the plugin-specific state * @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 * @return transaction status code @@ -1983,10 +1995,12 @@ postgres_reserves_in_insert (void *cls, static enum GNUNET_DB_QueryStatus 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) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (exchange_account_name), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -2222,8 +2236,8 @@ add_bank_to_exchange (void *cls, &bt->amount), TALER_PQ_result_spec_absolute_time ("execution_date", &bt->execution_date), - TALER_PQ_result_spec_json ("sender_account_details", - &bt->sender_account_details), + GNUNET_PQ_result_spec_string ("sender_account_details", + &bt->sender_account_details), GNUNET_PQ_result_spec_end }; @@ -2389,8 +2403,8 @@ add_exchange_to_bank (void *cls, &closing->closing_fee), TALER_PQ_result_spec_absolute_time ("execution_date", &closing->execution_date), - TALER_PQ_result_spec_json ("receiver_account", - &closing->receiver_account_details), + GNUNET_PQ_result_spec_string ("receiver_account", + &closing->receiver_account_details), GNUNET_PQ_result_spec_auto_from_type ("wtid", &closing->wtid), GNUNET_PQ_result_spec_end @@ -2710,7 +2724,7 @@ postgres_get_ready_deposit (void *cls, GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub), TALER_PQ_result_spec_json ("wire", - &wire), + &wire), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; @@ -3811,7 +3825,7 @@ add_coin_deposit (void *cls, GNUNET_PQ_result_spec_auto_from_type ("h_wire", &deposit->h_wire), TALER_PQ_result_spec_json ("wire", - &deposit->receiver_wire_account), + &deposit->receiver_wire_account), GNUNET_PQ_result_spec_auto_from_type ("coin_sig", &deposit->csig), GNUNET_PQ_result_spec_end @@ -4178,8 +4192,6 @@ handle_wt_result (void *cls, struct TALER_Amount amount_with_fee; struct TALER_Amount deposit_fee; json_t *wire; - json_t *t; - const char *wire_method; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("aggregation_serial_id", &rowid), GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract_terms), @@ -4202,25 +4214,11 @@ handle_wt_result (void *cls, ctx->status = GNUNET_SYSERR; return; } - t = json_object_get (wire, "type"); - if (NULL == t) - { - GNUNET_break (0); - ctx->status = GNUNET_SYSERR; - return; - } - wire_method = json_string_value (t); - if (NULL == wire_method) - { - GNUNET_break (0); - ctx->status = GNUNET_SYSERR; - return; - } ctx->cb (ctx->cb_cls, rowid, &merchant_pub, - wire_method, &h_wire, + wire, exec_time, &h_contract_terms, &coin_pub, @@ -4580,14 +4578,14 @@ reserve_expired_cb (void *cls, for (unsigned int i=0;iconnection_cfg_str); if (NULL == conn) return GNUNET_SYSERR; @@ -5421,7 +5420,7 @@ reserves_in_serial_helper_cb (void *cls, { struct TALER_ReservePublicKeyP reserve_pub; struct TALER_Amount credit; - json_t *sender_account_details; + char *sender_account_details; struct GNUNET_TIME_Absolute execution_date; uint64_t rowid; void *wire_reference; @@ -5436,8 +5435,8 @@ reserves_in_serial_helper_cb (void *cls, &credit), TALER_PQ_result_spec_absolute_time("execution_date", &execution_date), - TALER_PQ_result_spec_json ("sender_account_details", - &sender_account_details), + GNUNET_PQ_result_spec_string ("sender_account_details", + &sender_account_details), GNUNET_PQ_result_spec_uint64 ("reserve_in_serial_id", &rowid), GNUNET_PQ_result_spec_end @@ -5943,7 +5942,7 @@ reserve_closed_serial_helper_cb (void *cls, { uint64_t rowid; struct TALER_ReservePublicKeyP reserve_pub; - json_t *receiver_account; + char *receiver_account; struct TALER_WireTransferIdentifierRawP wtid; struct TALER_Amount amount_with_fee; struct TALER_Amount closing_fee; @@ -5957,8 +5956,8 @@ reserve_closed_serial_helper_cb (void *cls, &execution_date), GNUNET_PQ_result_spec_auto_from_type ("wtid", &wtid), - TALER_PQ_result_spec_json ("receiver_account", - &receiver_account), + GNUNET_PQ_result_spec_string ("receiver_account", + &receiver_account), TALER_PQ_result_spec_amount ("amount", &amount_with_fee), TALER_PQ_result_spec_amount ("closing_fee", @@ -6271,7 +6270,7 @@ missing_wire_cb (void *cls, TALER_PQ_result_spec_amount ("amount_with_fee", &amount), TALER_PQ_result_spec_json ("wire", - &wire), + &wire), TALER_PQ_result_spec_absolute_time ("wire_deadline", &deadline), GNUNET_PQ_result_spec_auto_from_type ("tiny", @@ -6380,12 +6379,12 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "exchangedb-postgres", - "db_conn_str", + "CONFIG", &pg->connection_cfg_str)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "exchangedb-postgres", - "db_conn_str"); + "CONFIG"); GNUNET_free (pg); return NULL; } diff --git a/src/exchangedb/test-exchange-db-postgres.conf b/src/exchangedb/test-exchange-db-postgres.conf index 926e2997e..d0afc535e 100644 --- a/src/exchangedb/test-exchange-db-postgres.conf +++ b/src/exchangedb/test-exchange-db-postgres.conf @@ -5,7 +5,7 @@ DB = postgres [exchangedb-postgres] #The connection string the plugin has to use for connecting to the database -DB_CONN_STR = postgres:///talercheck +CONFIG = postgres:///talercheck [exchangedb] diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 36f0cce4e..a112af248 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V. + Copyright (C) 2014-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 @@ -703,8 +703,8 @@ static void cb_wt_never (void *cls, uint64_t serial_id, const struct TALER_MerchantPublicKeyP *merchant_pub, - const char *wire_method, const struct GNUNET_HashCode *h_wire, + const json_t *wire, struct GNUNET_TIME_Absolute exec_time, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -747,8 +747,8 @@ static void cb_wt_check (void *cls, uint64_t rowid, const struct TALER_MerchantPublicKeyP *merchant_pub, - const char *wire_method, const struct GNUNET_HashCode *h_wire, + const json_t *wire, struct GNUNET_TIME_Absolute exec_time, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -759,8 +759,9 @@ cb_wt_check (void *cls, GNUNET_assert (0 == memcmp (merchant_pub, &merchant_pub_wt, sizeof (struct TALER_MerchantPublicKeyP))); - GNUNET_assert (0 == strcmp (wire_method, - "SEPA")); + GNUNET_assert (0 == strcmp (json_string_value (json_object_get (wire, + "url")), + "payto://sepa/DE67830654080004822650")); GNUNET_assert (0 == memcmp (h_wire, &h_wire_wt, sizeof (struct GNUNET_HashCode))); @@ -842,8 +843,8 @@ deposit_cb (void *cls, deposit_rowid = rowid; if (NULL != wire) GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (wire, - &h_wire)); + TALER_JSON_wire_signature_hash (wire, + &h_wire)); if ( (0 != memcmp (merchant_pub, &deposit->merchant_pub, sizeof (struct TALER_MerchantPublicKeyP))) || @@ -960,7 +961,7 @@ audit_reserve_in_cb (void *cls, uint64_t rowid, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, - const json_t *sender_account_details, + const char *sender_account_details, const void *wire_reference, size_t wire_reference_size, struct GNUNET_TIME_Absolute execution_date) @@ -1156,8 +1157,6 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) } -static json_t *wire_out_account; - static struct TALER_Amount wire_out_amount; @@ -1204,11 +1203,11 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session, const struct TALER_EXCHANGEDB_Deposit *deposit) { auditor_row_cnt = 0; - memset (&wire_out_wtid, 42, sizeof (wire_out_wtid)); + memset (&wire_out_wtid, + 42, + sizeof (wire_out_wtid)); wire_out_date = GNUNET_TIME_absolute_get (); (void) GNUNET_TIME_round_abs (&wire_out_date); - wire_out_account = json_loads ("{ \"account\":\"1\" }", 0, NULL); - GNUNET_assert (NULL != wire_out_account); GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1", &wire_out_amount)); @@ -1262,13 +1261,25 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session, /* Now let's fix the transient constraint violation by putting in the WTID into the wire_out table */ - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_wire_transfer_out (plugin->cls, - session, - wire_out_date, - &wire_out_wtid, - wire_out_account, - &wire_out_amount)); + { + json_t *wire_out_account; + + wire_out_account = json_pack ("{s:s,s:s}", + "url", "payto://x-taler-bank/localhost:8080/1", + "salt", "this-is-my-salt"); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->store_wire_transfer_out (plugin->cls, + session, + wire_out_date, + &wire_out_wtid, + wire_out_account, + &wire_out_amount)) + { + json_decref (wire_out_account); + FAILIF (1); + } + json_decref (wire_out_account); + } /* And now the commit should still succeed! */ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->commit (plugin->cls, @@ -1365,8 +1376,8 @@ wire_missing_cb (void *cls, if (NULL != wire) GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (wire, - &h_wire)); + TALER_JSON_wire_signature_hash (wire, + &h_wire)); else memset (&h_wire, 0, @@ -1500,16 +1511,8 @@ run (void *cls) struct TALER_EXCHANGEDB_TransactionList *tl; struct TALER_EXCHANGEDB_TransactionList *tlp; json_t *wire; - json_t *sndr; + const char *sndr = "payto://x-taler-bank/localhost:8080/1"; unsigned int matched; - const char * const json_wire_str = - "{ \"type\":\"SEPA\", \ -\"IBAN\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"wire_transfer_deadline\":\"1449930207000\", \ -\"r\":123456789, \ -\"address\": \"foobar\"}"; unsigned int cnt; void *rr; size_t rr_size; @@ -1518,9 +1521,11 @@ run (void *cls) dkp = NULL; rh = NULL; - wire = NULL; session = NULL; deposit.coin.denom_sig.rsa_signature = NULL; + wire = json_pack ("{s:s, s:s}", + "url", "payto://sepa/DE67830654080004822650", + "salt", "this-is-a-salt-value"); ZR_BLK (&cbc); ZR_BLK (&cbc2); if (NULL == @@ -1576,11 +1581,10 @@ run (void *cls) &amount_with_fee)); result = 4; - sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL); - GNUNET_assert (NULL != sndr); FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->get_latest_reserve_in_reference (plugin->cls, session, + "account-1", &rr, &rr_size)); now = GNUNET_TIME_absolute_get (); @@ -1592,11 +1596,13 @@ run (void *cls) &value, 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); @@ -1617,23 +1623,25 @@ run (void *cls) &value, 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); 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); - json_decref (sndr); FAILIF (GNUNET_OK != check_reserve (session, &reserve_pub, @@ -1731,7 +1739,6 @@ run (void *cls) TALER_amount_add (&amount_with_fee, &value, &value)); - sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL); GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.000010", &fee_closing)); @@ -1753,7 +1760,6 @@ run (void *cls) 0, value.currency)); - json_decref (sndr); result = 7; qs = plugin->get_reserve_history (plugin->cls, session, @@ -1840,17 +1846,18 @@ run (void *cls) FAILIF (3 != auditor_row_cnt); /* Tests for deposits */ - memset (&deposit, 0, sizeof (deposit)); + memset (&deposit, + 0, + sizeof (deposit)); RND_BLK (&deposit.coin.coin_pub); deposit.coin.denom_pub = dkp->pub; deposit.coin.denom_sig = cbc.sig; RND_BLK (&deposit.csig); RND_BLK (&deposit.merchant_pub); RND_BLK (&deposit.h_contract_terms); - wire = json_loads (json_wire_str, 0, NULL); GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (wire, - &deposit.h_wire)); + TALER_JSON_wire_signature_hash (wire, + &deposit.h_wire)); deposit.receiver_wire_account = wire; deposit.amount_with_fee = value; deposit.deposit_fee = fee_deposit; @@ -1897,7 +1904,8 @@ run (void *cls) session, &deposit.h_wire, &deposit.merchant_pub, - &deposit_cb, &deposit, + &deposit_cb, + &deposit, 2)); sleep (2); /* giv deposit time to be ready */ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != @@ -2179,8 +2187,9 @@ run (void *cls) result = 0; drop: - if (NULL != wire) - json_decref (wire); + if (0 != result) + plugin->rollback (plugin->cls, + session); if (NULL != rh) plugin->free_reserve_history (plugin->cls, rh); @@ -2196,6 +2205,7 @@ run (void *cls) if (NULL != cbc2.sig.rsa_signature) GNUNET_CRYPTO_rsa_signature_free (cbc2.sig.rsa_signature); dkp = NULL; + json_decref (wire); TALER_EXCHANGEDB_plugin_unload (plugin); plugin = NULL; } diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h index 80974cca0..cc583e8e7 100644 --- a/src/include/taler_auditordb_plugin.h +++ b/src/include/taler_auditordb_plugin.h @@ -382,6 +382,7 @@ struct TALER_AUDITORDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param master_pub master key of the exchange + * @param account_name name of the wire account we are auditing * @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 @@ -392,6 +393,7 @@ struct TALER_AUDITORDB_Plugin (*insert_wire_auditor_progress)(void *cls, struct TALER_AUDITORDB_Session *session, const struct TALER_MasterPublicKeyP *master_pub, + const char *account_name, const struct TALER_AUDITORDB_WireProgressPoint *pp, const void *in_wire_off, const void *out_wire_off, @@ -405,6 +407,7 @@ struct TALER_AUDITORDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param master_pub master key of the exchange + * @param account_name name of the wire account we are auditing * @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 @@ -415,6 +418,7 @@ struct TALER_AUDITORDB_Plugin (*update_wire_auditor_progress)(void *cls, struct TALER_AUDITORDB_Session *session, const struct TALER_MasterPublicKeyP *master_pub, + const char *account_name, const struct TALER_AUDITORDB_WireProgressPoint *pp, const void *in_wire_off, const void *out_wire_off, @@ -428,6 +432,7 @@ struct TALER_AUDITORDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param master_pub master key of the exchange + * @param account_name name of the wire account we are auditing * @param[out] pp set to where the auditor is 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 @@ -438,6 +443,7 @@ struct TALER_AUDITORDB_Plugin (*get_wire_auditor_progress)(void *cls, struct TALER_AUDITORDB_Session *session, const struct TALER_MasterPublicKeyP *master_pub, + const char *account_name, struct TALER_AUDITORDB_WireProgressPoint *pp, void **in_wire_off, void **out_wire_off, diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h index bfa050336..1cad5710a 100644 --- a/src/include/taler_bank_service.h +++ b/src/include/taler_bank_service.h @@ -217,9 +217,9 @@ struct TALER_BANK_TransferDetails char *wire_transfer_subject; /** - * The other account that was involved + * payto://-URL of the other account that was involved */ - json_t *account_details; + char *account_url; }; @@ -347,4 +347,29 @@ void TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh); +/** + * Convenience method for parsing configuration section with bank + * authentication data. The section must contain an option + * "METHOD", plus other options that depend on the METHOD specified. + * + * @param cfg configuration to parse + * @param section the section with the configuration data + * @param auth[out] set to the configuration data found + * @return #GNUNET_OK on success + */ +int +TALER_BANK_auth_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + struct TALER_BANK_AuthenticationData *auth); + + +/** + * Free memory inside of @a auth (but not auth itself). + * Dual to #TALER_BANK_auth_parse_cfg(). + * + * @param auth authentication data to free + */ +void +TALER_BANK_auth_free (struct TALER_BANK_AuthenticationData *auth); + #endif /* _TALER_BANK_SERVICE_H */ diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index d45e7bb28..b22c55ce3 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2017 Taler Systems SA + Copyright (C) 2014-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 @@ -730,4 +730,51 @@ TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, const struct TALER_Amount *amount_with_fee); +/* **************** /wire account offline signing **************** */ + + +/** + * Compute the hash of the given wire details. The resulting + * hash is what is put into the contract. + * + * @param payto_url bank account + * @param salt salt used to eliminate brute-force inversion + * @param hc[out] set to the hash + */ +void +TALER_wire_signature_hash (const char *payto_url, + const char *salt, + struct GNUNET_HashCode *hc); + +/** + * Check the signature in @a wire_s. + * + * @param payto_url URL that is signed + * @param salt the salt used to salt the @a payto_url when hashing + * @param master_pub master public key of the exchange + * @param master_sig signature of the exchange + * @return #GNUNET_OK if signature is valid + */ +int +TALER_wire_signature_check (const char *payto_url, + const char *salt, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_MasterSignatureP *master_sig); + + +/** + * Create a signed wire statement for the given account. + * + * @param payto_url account specification + * @param salt the salt used to salt the @a payto_url when hashing + * @param master_priv private key to sign with + * @param master_sig[out] where to write the signature + */ +void +TALER_wire_signature_make (const char *payto_url, + const char *salt, + const struct TALER_MasterPrivateKeyP *master_priv, + struct TALER_MasterSignatureP *master_sig); + + #endif diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index 1a20889d8..dbde04df1 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -165,7 +165,15 @@ enum TALER_ErrorCode */ TALER_EC_INTERNAL_LOGIC_ERROR = 1011, + /** + * The method specified in a payto:// URL is not one we expected. + */ + TALER_EC_PAYTO_WRONG_METHOD = 1012, + /** + * The PAYTO URL is malformed. + */ + TALER_EC_PAYTO_MALFORMED = 1013, /* ********** request-specific error codes ************* */ @@ -1535,6 +1543,20 @@ enum TALER_ErrorCode */ TALER_EC_TEST_RSA_SIGN_ERROR = 4005, + + /** + * The JSON in the server's response was malformed. This response + * is provided with HTTP status code of 0. + */ + TALER_EC_SERVER_JSON_INVALID = 5000, + + /** + * A signature in the server's response was malformed. This response + * is provided with HTTP status code of 0. + */ + TALER_EC_SERVER_SIGNATURE_INVALID = 5001, + + /* *************** Taler BANK/FAKEBANK error codes *************** */ /** diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 30ea4ce66..22ca049eb 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -472,40 +472,32 @@ struct TALER_EXCHANGE_WireAggregateFees /** - * Function called with information about the wire fees - * for each wire method. - * - * @param cls closure - * @param wire_method name of the wire method (i.e. "sepa") - * @param fees fee structure for this method + * Information about a wire account of the exchange. */ -typedef void -(*TALER_EXCHANGE_WireFeeCallback)(void *cls, - const char *wire_method, - const struct TALER_EXCHANGE_WireAggregateFees *fees); +struct TALER_EXCHANGE_WireAccount +{ + /** + * payto://-URL of the exchange. + */ + const char *url; + /** + * Salt used to generate @e master_sig. + */ + const char *salt; -/** - * Obtain information about wire fees encoded in @a obj - * by wire method. - * - * @param master_pub public key to use to verify signatures, NULL to not verify - * @param obj wire information as encoded in the #TALER_EXCHANGE_WireResultCallback - * @param cb callback to invoke for the fees - * @param cb_cls closure for @a cb - * @return #GNUNET_OK in success, #GNUNET_SYSERR if @a obj is ill-formed - */ -int -TALER_EXCHANGE_wire_get_fees (const struct TALER_MasterPublicKeyP *master_pub, - const json_t *obj, - TALER_EXCHANGE_WireFeeCallback cb, - void *cb_cls); + /** + * Signature of the exchange over the account (was checked by the API). + */ + struct TALER_MasterSignatureP master_sig; + /** + * Linked list of wire fees the exchange charges for + * accounts of the wire method matching @e url. + */ + const struct TALER_EXCHANGE_WireAggregateFees *fees; -/** - * @brief A Wire format inquiry handle - */ -struct TALER_EXCHANGE_WireHandle; +}; /** @@ -519,15 +511,21 @@ struct TALER_EXCHANGE_WireHandle; * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request; * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param obj the received JSON reply, if successful this should be the wire - * format details as provided by /wire, or NULL if the - * reply was not in JSON format. + * @param accounts_len length of the @a accounts array + * @param accounts list of wire accounts of the exchange, NULL on error */ typedef void (*TALER_EXCHANGE_WireResultCallback) (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, - const json_t *obj); + unsigned int accounts_len, + const struct TALER_EXCHANGE_WireAccount *accounts); + + +/** + * @brief A Wire format inquiry handle + */ +struct TALER_EXCHANGE_WireHandle; /** @@ -843,9 +841,9 @@ struct TALER_EXCHANGE_ReserveHistory */ struct { /** - * Sender account information for the incoming transfer. + * Sender account payto://-URL of the incoming transfer. */ - json_t *sender_account_details; + char *sender_url; /** * Information that uniquely identifies the wire transfer. diff --git a/src/include/taler_exchangedb_lib.h b/src/include/taler_exchangedb_lib.h index e4284c27f..b89dd087d 100644 --- a/src/include/taler_exchangedb_lib.h +++ b/src/include/taler_exchangedb_lib.h @@ -406,4 +406,72 @@ TALER_EXCHANGEDB_fees_write (const char *filename, void TALER_EXCHANGEDB_fees_free (struct TALER_EXCHANGEDB_AggregateFees *af); + +/** + * Information about an account from the configuration. + */ +struct TALER_EXCHANGEDB_AccountInfo +{ + /** + * Section in the configuration file that specifies the + * account. Must start with "account-". + */ + const char *section_name; + + /** + * Name of the wire plugin that should be used to access + * the account. + */ + const char *plugin_name; + + /** + * payto://-URL of the account. + */ + const char *payto_url; + + /** + * Filename containing the signed /wire response, or NULL + * if not given. + */ + const char *wire_response_filename; + + /** + * #GNUNET_YES if this account is enabed to be debited + * by the taler-exchange-aggregator. + */ + int debit_enabled; + + /** + * #GNUNET_YES if this account is enabed to be credited by wallets + * and needs to be watched by the taler-exchange-wirewatch. + * Also, the account will only be included in /wire if credit + * is enabled. + */ + int credit_enabled; +}; + + +/** + * Function called with information about a wire account. + * + * @param cls closure + * @param ai account information + */ +typedef void +(*TALER_EXCHANGEDB_AccountCallback)(void *cls, + const struct TALER_EXCHANGEDB_AccountInfo *ai); + +/** + * Parse the configuration to find account information. + * + * @param cfg configuration to use + * @param cb callback to invoke + * @param cb_cls closure for @a cb + */ +void +TALER_EXCHANGEDB_find_accounts (const struct GNUNET_CONFIGURATION_Handle *cfg, + TALER_EXCHANGEDB_AccountCallback cb, + void *cb_cls); + + #endif diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 33677559a..0d6f9cd90 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -21,7 +21,6 @@ */ #ifndef TALER_EXCHANGEDB_PLUGIN_H #define TALER_EXCHANGEDB_PLUGIN_H - #include #include #include @@ -52,9 +51,10 @@ struct TALER_EXCHANGEDB_BankTransfer struct GNUNET_TIME_Absolute execution_date; /** - * Detailed wire information about the sending account. + * Detailed wire information about the sending account + * in "payto://" format. */ - json_t *sender_account_details; + char *sender_account_details; /** * Data uniquely identifying the wire transfer (wire transfer-type specific) @@ -97,9 +97,10 @@ struct TALER_EXCHANGEDB_ClosingTransfer struct GNUNET_TIME_Absolute execution_date; /** - * Detailed wire information about the receiving account. + * Detailed wire information about the receiving account + * in payto://-format. */ - json_t *receiver_account_details; + char *receiver_account_details; /** * Detailed wire transfer information that uniquely identifies the @@ -361,6 +362,7 @@ struct TALER_EXCHANGEDB_Deposit /** * Detailed information about the receiver for executing the transaction. + * Includes URL in payto://-format and salt. */ json_t *receiver_wire_account; @@ -638,7 +640,8 @@ struct TALER_EXCHANGEDB_Session; * @param h_contract_terms hash of the proposal data known to merchant and customer * @param wire_deadline by which the merchant adviced that he would like the * wire transfer to be executed - * @param receiver_wire_account wire details for the merchant, NULL from iterate_matching_deposits() + * @param receiver_wire_account wire details for the merchant, includes + * 'url' in payto://-format; NULL from iterate_matching_deposits() * @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to continue to iterate */ typedef enum GNUNET_DB_QueryStatus @@ -687,7 +690,8 @@ typedef void * to get a refund * @param wire_deadline by which the merchant adviced that he would like the * wire transfer to be executed - * @param receiver_wire_account wire details for the merchant, NULL from iterate_matching_deposits() + * @param receiver_wire_account wire details for the merchant including 'url' in payto://-format; + * NULL from iterate_matching_deposits() * @param done flag set if the deposit was already executed (or not) * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ @@ -837,7 +841,7 @@ typedef int * @param rowid unique serial ID for the refresh session in our DB * @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 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 execution_date when did we receive the funds @@ -848,7 +852,7 @@ typedef int uint64_t rowid, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *credit, - const json_t *sender_account_details, + const char *sender_account_details, const void *wire_reference, size_t wire_reference_size, struct GNUNET_TIME_Absolute execution_date); @@ -923,8 +927,8 @@ typedef void * @param cls closure * @param rowid which row in the table is the information from (for diagnostics) * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) - * @param wire_method which wire plugin was used for the transfer? * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) + * @param account_details which account did the transfer go to? * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param h_contract_terms which proposal was this payment about * @param coin_pub which public key was this payment about @@ -935,8 +939,8 @@ typedef void (*TALER_EXCHANGEDB_WireTransferDataCallback)(void *cls, uint64_t rowid, const struct TALER_MerchantPublicKeyP *merchant_pub, - const char *wire_method, const struct GNUNET_HashCode *h_wire, + const json_t *account_details, struct GNUNET_TIME_Absolute exec_time, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -952,7 +956,7 @@ typedef void * @param rowid identifier of the respective row in the database * @param date timestamp of the wire transfer (roughly) * @param wtid wire transfer subject - * @param wire wire transfer details of the receiver + * @param wire wire transfer details of the receiver, including "url" in payto://-format * @param amount amount that was wired * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration */ @@ -1019,7 +1023,7 @@ typedef int * @param amount_with_fee how much did we debit the reserve * @param closing_fee how much did we charge for closing the reserve * @param reserve_pub public key of the reserve - * @param receiver_account where did we send the funds + * @param receiver_account where did we send the funds, in payto://-format * @param wtid identifier used for the wire transfer * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ @@ -1030,7 +1034,7 @@ typedef int const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *closing_fee, const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *receiver_account, + const char *receiver_account, const struct TALER_WireTransferIdentifierRawP *wtid); @@ -1040,7 +1044,7 @@ typedef int * @param cls closure * @param reserve_pub public key of the reserve * @param left amount left in the reserve - * @param account_details information about the reserve's bank account + * @param account_details information about the reserve's bank account, in payto://-format * @param expiration_date when did the reserve expire * @return transaction status code to pass on */ @@ -1048,7 +1052,7 @@ typedef enum GNUNET_DB_QueryStatus (*TALER_EXCHANGEDB_ReserveExpiredCallback)(void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *left, - const json_t *account_details, + const char *account_details, struct GNUNET_TIME_Absolute expiration_date); @@ -1082,7 +1086,7 @@ typedef void * @param rowid deposit table row of the coin's deposit * @param coin_pub public key of the coin * @param amount value of the deposit, including fee - * @param wire where should the funds be wired + * @param wire where should the funds be wired, including 'url' in payto://-format * @param deadline what was the requested wire transfer deadline * @param tiny did the exchange defer this transfer because it is too small? * @param done did the exchange claim that it made a transfer? @@ -1259,7 +1263,7 @@ struct TALER_EXCHANGEDB_Plugin * @param reserve_pub public key of the reserve * @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 + * @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 * @return transaction status code @@ -1270,7 +1274,8 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *balance, struct GNUNET_TIME_Absolute execution_time, - const json_t *sender_account_details, + const char *sender_account_details, + const char *exchange_account_name, const void *wire_reference, size_t wire_reference_size); @@ -1287,6 +1292,7 @@ struct TALER_EXCHANGEDB_Plugin 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); @@ -1781,7 +1787,7 @@ struct TALER_EXCHANGEDB_Plugin * @param session database connection * @param reserve_pub which reserve is this about? * @param execution_date when did we perform the transfer? - * @param receiver_account to which account do we transfer? + * @param receiver_account to which account do we transfer, in payto://-format * @param wtid identifier for the wire transfer * @param amount_with_fee amount we charged to the reserve * @param closing_fee how high is the closing fee @@ -1792,7 +1798,7 @@ struct TALER_EXCHANGEDB_Plugin struct TALER_EXCHANGEDB_Session *session, const struct TALER_ReservePublicKeyP *reserve_pub, struct GNUNET_TIME_Absolute execution_date, - const json_t *receiver_account, + const char *receiver_account, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *amount_with_fee, const struct TALER_Amount *closing_fee); @@ -1869,7 +1875,8 @@ struct TALER_EXCHANGEDB_Plugin * @param session database connection * @param date time of the wire transfer * @param wtid subject of the wire transfer - * @param wire_account details about the receiver account of the wire transfer + * @param wire_account details about the receiver account of the wire transfer, + * including 'url' in payto://-format * @param amount amount that was transmitted * @return transaction status code */ diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 6d019ecca..0504ddfb8 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -126,6 +126,65 @@ enum TALER_ErrorCode TALER_JSON_get_error_code (const json_t *json); +/* **************** /wire account offline signing **************** */ + +/** + * Compute the hash of the given wire details. The resulting + * hash is what is put into the contract. + * + * @param wire_s wire details to hash + * @param hc[out] set to the hash + * @return #GNUNET_OK on success, #GNUNET_SYSERR if @a wire_s is malformed + */ +int +TALER_JSON_wire_signature_hash (const json_t *wire_s, + struct GNUNET_HashCode *hc); + +/** + * Check the signature in @a wire_s. + * + * @param wire_s signed wire information of an exchange + * @param master_pub master public key of the exchange + * @return #GNUNET_OK if signature is valid + */ +int +TALER_JSON_wire_signature_check (const json_t *wire_s, + const struct TALER_MasterPublicKeyP *master_pub); + + +/** + * Create a signed wire statement for the given account. + * + * @param payto_url account specification + * @param master_priv private key to sign with, NULL to not sign + */ +json_t * +TALER_JSON_wire_signature_make (const char *payto_url, + const struct TALER_MasterPrivateKeyP *master_priv); + + +/** + * Obtain the wire method associated with the given + * wire account details. @a wire_s must contain a payto://-URL + * under 'url'. + * + * @return NULL on error + */ +char * +TALER_JSON_wire_to_method (const json_t *wire_s); + + +/** + * Obtain the payto://-URL associated with the given + * wire account details. @a wire_s must contain a payto://-URL + * under 'url'. + * + * @return NULL on error + */ +char * +TALER_JSON_wire_to_payto (const json_t *wire_s); + + #endif /* TALER_JSON_LIB_H_ */ /* End of taler_json_lib.h */ diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index c281d21fd..f1148beb8 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -61,18 +61,6 @@ */ #define TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY 1025 -/** - * Signature where the Exchange confirms its SEPA details in - * the /wire response. - */ -#define TALER_SIGNATURE_MASTER_SEPA_DETAILS 1026 - -/** - * Signature where the Exchange confirms its TEST details in - * the /wire response. - */ -#define TALER_SIGNATURE_MASTER_TEST_DETAILS 1027 - /** * Fees charged per (aggregate) wire transfer to the merchant. */ @@ -83,6 +71,12 @@ */ #define TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED 1029 +/** + * Signature where the Exchange confirms its SEPA details in + * the /wire response. + */ +#define TALER_SIGNATURE_MASTER_WIRE_DETAILS 1030 + /*********************************************/ /* Exchange online signatures (with signing key) */ /*********************************************/ @@ -891,16 +885,15 @@ struct TALER_MasterWireDetailsPS { /** - * Purpose is #TALER_SIGNATURE_MASTER_SEPA_DETAILS or - * #TALER_SIGNATURE_MASTER_TEST_DETAILS. + * Purpose is #TALER_SIGNATURE_MASTER_WIRE_DETAILS. */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Hash over the account holder's name, IBAN and BIC - * code (all as 0-terminated strings). + * Hash over the account holder's payto:// URL and + * the salt, as done by #TALER_wire_signature_hash(). */ - struct GNUNET_HashCode h_sepa_details GNUNET_PACKED; + struct GNUNET_HashCode h_wire_details GNUNET_PACKED; }; diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index 3914208f1..dce92ceae 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -66,20 +66,22 @@ #define TALER_TESTING_MAKE_TRAIT_ROW_ID(data) \ TALER_TESTING_make_trait_uint64 (3, data) + /** - * Allocate and return a piece of wire-details. Mostly, it adds - * the bank_url to the JSON. + * Allocate and return a piece of wire-details. Combines + * the @a account_no and the @a bank_url to a + * @a payto://-URL and adds some salt to create the JSON. * - * @param template the wire-details template. + * @param account_no account number * @param bank_url the bank_url - * - * @return the filled out and stringified wire-details. To - * be manually free'd. + * @return JSON describing the account, including the + * payto://-URL of the account, must be manually decref'd */ -char * -TALER_TESTING_make_wire_details (const char *template, +json_t * +TALER_TESTING_make_wire_details (unsigned long long account_no, const char *bank_url); + /** * Find denomination key matching the given amount. * @@ -130,10 +132,12 @@ TALER_TESTING_url_port_free (const char *url); * If everything is OK, return the configured URL of the fakebank. * * @param config_filename configuration file to use + * @param config_section which account to use (must match x-taler-bank) * @return NULL on error, fakebank URL otherwise */ char * -TALER_TESTING_prepare_fakebank (const char *config_filename); +TALER_TESTING_prepare_fakebank (const char *config_filename, + const char *config_section); /* ******************* Generic interpreter logic ************ */ @@ -613,8 +617,8 @@ TALER_TESTING_cmd_status (const char *label, * coins, this parameter selects which one in that array * This value is currently ignored, as only one-coin * withdrawals are implemented. - * @param wire_details bank details of the merchant performing the - * deposit + * @param wire_details JSON including payto://-URL of the merchant performing the + * deposit, reference is captured by this command * @param contract_terms contract terms to be signed over by the * coin * @param refund_deadline refund deadline @@ -625,12 +629,12 @@ TALER_TESTING_cmd_status (const char *label, * @return the deposit command to be run by the interpreter */ struct TALER_TESTING_Command -TALER_TESTING_cmd_deposit +TALER_TESTING_cmd_deposit (const char *label, struct TALER_EXCHANGE_Handle *exchange, const char *coin_reference, unsigned int coin_index, - char *wire_details, + json_t *wire_details, const char *contract_terms, struct GNUNET_TIME_Relative refund_deadline, const char *amount, @@ -1209,7 +1213,7 @@ int TALER_TESTING_get_trait_wire_details (const struct TALER_TESTING_Command *cmd, unsigned int index, - const char **wire_details); + const json_t **wire_details); /** @@ -1223,7 +1227,7 @@ TALER_TESTING_get_trait_wire_details struct TALER_TESTING_Trait TALER_TESTING_make_trait_wire_details (unsigned int index, - const char *wire_details); + const json_t *wire_details); /** * Obtain a private key from a "peer". Used e.g. to obtain @@ -1300,11 +1304,11 @@ TALER_TESTING_get_trait_transfer_subject /** - * Offer wire details in a trait. + * Offer wire wire transfer subject in a trait. * * @param index always (?) zero, as one command sticks * to one bank account - * @param wire_details wire details to offer + * @param transfer_subject wire transfer subject to offer * @return the trait, to be put in the traits array of the command */ struct TALER_TESTING_Trait diff --git a/src/include/taler_wire_lib.h b/src/include/taler_wire_lib.h index 68c36e889..0cf38d6d1 100644 --- a/src/include/taler_wire_lib.h +++ b/src/include/taler_wire_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016 GNUnet e.V. + Copyright (C) 2016,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 @@ -25,6 +25,16 @@ #include "taler_wire_plugin.h" +/** + * Obtain the payment method from a @a payto_url + * + * @param payto_url the URL to parse + * @return NULL on error (malformed @a payto_url) + */ +char * +TALER_WIRE_payto_get_method (const char *payto_url); + + /** * Load a WIRE plugin. * @@ -45,28 +55,4 @@ void TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin); -/** - * Signature of a function to be called on each enabled - * wire plugin. - * - * @param cls closure - * @param name name of the enabled plugin - */ -typedef void -(*TALER_WIRE_EnabledCallback)(void *cls, - const char *name); - - -/** - * Check which wire plugins are enabled in @a cfg and call @a cb for each one. - * - * @param cfg configuration to use - * @param cb callback to invoke - * @param cb_cls closure for @a cb - */ -void -TALER_WIRE_find_enabled (const struct GNUNET_CONFIGURATION_Handle *cfg, - TALER_WIRE_EnabledCallback cb, - void *cb_cls); - #endif diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index c0e29609f..2758d2d2e 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -22,7 +22,6 @@ #define TALER_WIRE_PLUGIN_H #include -#include #include "taler_util.h" #include "taler_error_codes.h" #include "taler_bank_service.h" /* for `enum TALER_BANK_Direction` and `struct TALER_BANK_TransferDetails` */ @@ -72,9 +71,9 @@ struct TALER_WIRE_TransferDetails char *wtid_s; /** - * The other account that was involved + * payto://-URL of the other account that was involved */ - json_t *account_details; + char *account_url; }; @@ -160,6 +159,13 @@ struct TALER_WIRE_Plugin */ char *library_name; + /** + * Which wire method (payto://METHOD/") is supported by this plugin? + * For example, "sepa" or "x-taler-bank". + */ + 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 @@ -177,62 +183,27 @@ struct TALER_WIRE_Plugin /** - * Obtain wire transfer details in the plugin-specific format - * from the configuration. - * - * @param cls closure - * @param cfg configuration with details about wire accounts - * @param account_name which section in the configuration should we parse - * @return NULL if @a cfg fails to have valid wire details for @a account_name - */ - json_t * - (*get_wire_details)(void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *account_name); - - - /** - * Sign wire transfer details in the plugin-specific format. - * - * @param cls closure - * @param in wire transfer details in JSON format - * @param key private signing key to use - * @param salt salt to add - * @param[out] sig where to write the signature - * @return #GNUNET_OK on success - */ - int - (*sign_wire_details)(void *cls, - const json_t *in, - const struct TALER_MasterPrivateKeyP *key, - const struct GNUNET_HashCode *salt, - struct TALER_MasterSignatureP *sig); - - - /** - * Check if the given wire format JSON object is correctly formatted + * 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 wire the JSON wire format object - * @param master_pub public key of the exchange to verify against - * @param[out] emsg set to an error message, unless we return #TALER_EC_NONE; - * error message must be freed by the caller using GNUNET_free() + * @param account_url the payto:// URL * @return #TALER_EC_NONE if correctly formatted */ enum TALER_ErrorCode (*wire_validate) (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg); + const char *account_url); /** * Prepare for exeuction of a wire transfer. * * @param cls the @e cls of this struct with the plugin-specific state - * @param wire valid wire account information + * @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 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 @@ -240,13 +211,15 @@ struct TALER_WIRE_Plugin */ struct TALER_WIRE_PrepareHandle * (*prepare_wire_transfer) (void *cls, - const json_t *wire, + 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. @@ -305,6 +278,8 @@ struct TALER_WIRE_Plugin * (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 @@ -316,6 +291,7 @@ struct TALER_WIRE_Plugin */ struct TALER_WIRE_HistoryHandle * (*get_history) (void *cls, + const char *account_section, enum TALER_BANK_Direction direction, const void *start_off, size_t start_off_len, @@ -323,6 +299,7 @@ struct TALER_WIRE_Plugin TALER_WIRE_HistoryResultCallback hres_cb, void *hres_cb_cls); + /** * Cancel going over the account's history. * @@ -345,6 +322,8 @@ struct TALER_WIRE_Plugin * 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 @@ -353,11 +332,13 @@ struct TALER_WIRE_Plugin */ 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); + /** * Cancel ongoing reject operation. Note that the rejection may still * proceed. Basically, if this function is called, the rejection may diff --git a/src/json/Makefile.am b/src/json/Makefile.am index d26f731ac..4b40e94b4 100644 --- a/src/json/Makefile.am +++ b/src/json/Makefile.am @@ -11,12 +11,14 @@ lib_LTLIBRARIES = \ libtalerjson_la_SOURCES = \ json.c \ - json_helper.c + json_helper.c \ + json_wire.c libtalerjson_la_LDFLAGS = \ -version-info 1:0:1 \ -export-dynamic -no-undefined libtalerjson_la_LIBADD = \ -lgnunetjson \ + $(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil \ -ljansson \ diff --git a/src/json/json_wire.c b/src/json/json_wire.c new file mode 100644 index 000000000..f0bd17578 --- /dev/null +++ b/src/json/json_wire.c @@ -0,0 +1,193 @@ +/* + 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 json/json_wire.c + * @brief helper functions to generate or check /wire replies + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_wire_lib.h" + + +/** + * Compute the hash of the given wire details. The resulting + * hash is what is put into the contract. + * + * @param wire_s wire details to hash + * @param hc[out] set to the hash + * @return #GNUNET_OK on success, #GNUNET_SYSERR if @a wire_s is malformed + */ +int +TALER_JSON_wire_signature_hash (const json_t *wire_s, + struct GNUNET_HashCode *hc) +{ + const char *payto_url; + const char *salt; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("url", &payto_url), + GNUNET_JSON_spec_string ("salt", &salt), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (wire_s, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + TALER_wire_signature_hash (payto_url, + salt, + hc); + return GNUNET_OK; +} + + +/** + * Check the signature in @a wire_s. + * + * @param wire_s signed wire information of an exchange + * @param master_pub master public key of the exchange + * @return #GNUNET_OK if signature is valid + */ +int +TALER_JSON_wire_signature_check (const json_t *wire_s, + const struct TALER_MasterPublicKeyP *master_pub) +{ + const char *payto_url; + const char *salt; + struct TALER_MasterSignatureP master_sig; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("url", &payto_url), + GNUNET_JSON_spec_string ("salt", &salt), + GNUNET_JSON_spec_fixed_auto ("master_sig", &master_sig), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (wire_s, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return TALER_wire_signature_check (payto_url, + salt, + master_pub, + &master_sig); +} + + +/** + * Create a signed wire statement for the given account. + * + * @param payto_url account specification + * @param master_priv private key to sign with, NULL to not sign + */ +json_t * +TALER_JSON_wire_signature_make (const char *payto_url, + const struct TALER_MasterPrivateKeyP *master_priv) +{ + struct TALER_MasterSignatureP master_sig; + struct GNUNET_HashCode salt; + char *salt_str; + json_t *ret; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &salt, + sizeof (salt)); + salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt, + sizeof (salt)); + if (NULL != master_priv) + { + TALER_wire_signature_make (payto_url, + salt_str, + master_priv, + &master_sig); + ret = json_pack ("{s:s, s:s, s:o}", + "url", payto_url, + "salt", salt_str, + "master_sig", GNUNET_JSON_from_data_auto (&master_sig)); + } + else + { + ret = json_pack ("{s:s, s:s}", + "url", payto_url, + "salt", salt_str); + } + GNUNET_free (salt_str); + return ret; +} + + +/** + * Obtain the wire method associated with the given + * wire account details. @a wire_s must contain a payto://-URL + * under 'url'. + * + * @return NULL on error + */ +char * +TALER_JSON_wire_to_payto (const json_t *wire_s) +{ + json_t *payto_o; + const char *payto_str; + + payto_o = json_object_get (wire_s, + "url"); + if ( (NULL == payto_o) || + (NULL == (payto_str = json_string_value (payto_o))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fatally malformed wire record encountered: lacks payto://-url\n"); + return NULL; + } + return GNUNET_strdup (payto_str); +} + + +/** + * Obtain the wire method associated with the given + * wire account details. @a wire_s must contain a payto://-URL + * under 'url'. + * + * @return NULL on error + */ +char * +TALER_JSON_wire_to_method (const json_t *wire_s) +{ + json_t *payto_o; + const char *payto_str; + + payto_o = json_object_get (wire_s, + "url"); + if ( (NULL == payto_o) || + (NULL == (payto_str = json_string_value (payto_o))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fatally malformed wire record encountered: lacks payto://-url\n"); + return NULL; + } + return TALER_WIRE_payto_get_method (payto_str); +} + + +/* end of json_wire.c */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 4f9a6367d..89b31ebba 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -43,6 +43,7 @@ libtalerutil_wallet_la_LDFLAGS = \ libtalerutil_la_SOURCES = \ amount.c \ crypto.c \ + crypto_wire.c \ util.c \ os_installation.c diff --git a/src/util/crypto_wire.c b/src/util/crypto_wire.c new file mode 100644 index 000000000..494573ffa --- /dev/null +++ b/src/util/crypto_wire.c @@ -0,0 +1,108 @@ +/* + 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 util/crypto_wire.c + * @brief functions for making and verifying /wire account signatures + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_crypto_lib.h" +#include "taler_signatures.h" + +/** + * Compute the hash of the given wire details. The resulting + * hash is what is put into the contract. + * + * @param payto_url bank account + * @param salt salt used to eliminate brute-force inversion + * @param hc[out] set to the hash + */ +void +TALER_wire_signature_hash (const char *payto_url, + const char *salt, + struct GNUNET_HashCode *hc) +{ + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (hc, + sizeof (*hc), + salt, + strlen (salt) + 1, + payto_url, + strlen (payto_url) + 1, + "wire-signature", + strlen ("wire-signature"), + NULL, 0)); +} + + +/** + * Check the signature in @a wire_s. + * + * @param payto_url URL that is signed + * @param salt the salt used to salt the @a payto_url when hashing + * @param master_pub master public key of the exchange + * @param master_sig signature of the exchange + * @return #GNUNET_OK if signature is valid + */ +int +TALER_wire_signature_check (const char *payto_url, + const char *salt, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_MasterSignatureP *master_sig) +{ + struct TALER_MasterWireDetailsPS wd; + + wd.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_DETAILS); + wd.purpose.size = htonl (sizeof (wd)); + TALER_wire_signature_hash (payto_url, + salt, + &wd.h_wire_details); + return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_DETAILS, + &wd.purpose, + &master_sig->eddsa_signature, + &master_pub->eddsa_pub); +} + + +/** + * Create a signed wire statement for the given account. + * + * @param payto_url account specification + * @param salt the salt used to salt the @a payto_url when hashing + * @param master_priv private key to sign with + * @param master_sig[out] where to write the signature + */ +void +TALER_wire_signature_make (const char *payto_url, + const char *salt, + const struct TALER_MasterPrivateKeyP *master_priv, + struct TALER_MasterSignatureP *master_sig) +{ + struct TALER_MasterWireDetailsPS wd; + + wd.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_DETAILS); + wd.purpose.size = htonl (sizeof (wd)); + TALER_wire_signature_hash (payto_url, + salt, + &wd.h_wire_details); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, + &wd.purpose, + &master_sig->eddsa_signature)); +} + + +/* end of crypto_wire.c */ diff --git a/src/wire-plugins/Makefile.am b/src/wire-plugins/Makefile.am new file mode 100644 index 000000000..9f6029d7f --- /dev/null +++ b/src/wire-plugins/Makefile.am @@ -0,0 +1,101 @@ +# 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 \ + test_wire_plugin_key.priv \ + test_wire_plugin_test.json \ + test_wire_plugin_sepa.json + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_plugin_wire_ebics.la \ + libtaler_plugin_wire_taler_bank.la + +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/util/libtalerutil.la \ + -lgnunetcurl \ + -lgnunetutil $(XLIB) + + +libtaler_plugin_wire_ebics_la_SOURCES = \ + plugin_wire_ebics.c +libtaler_plugin_wire_ebics_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_wire_ebics_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetjson \ + -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_ebics_wireformat \ + test_wire_plugin \ + test_wire_plugin_transactions_taler_bank + +check_PROGRAMS= \ + test_ebics_wireformat \ + test_wire_plugin \ + test_wire_plugin_transactions_taler_bank + + +test_ebics_wireformat_SOURCES = \ + test_ebics_wireformat.c +test_ebics_wireformat_LDADD = \ + -lgnunetutil \ + $(top_builddir)/src/wire/libtalerwire.la \ + $(top_builddir)/src/util/libtalerutil.la + + +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_ebics.c b/src/wire-plugins/plugin_wire_ebics.c new file mode 100644 index 000000000..9aad9df08 --- /dev/null +++ b/src/wire-plugins/plugin_wire_ebics.c @@ -0,0 +1,753 @@ +/* + This file is part of TALER + Copyright (C) 2016, 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_ebics.c + * @brief wire plugin for transfers using SEPA/EBICS + * @author Florian Dold + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "taler_wire_plugin.h" +#include "taler_signatures.h" +#include + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct EbicsClosure +{ + + /** + * Which currency do we support? + */ + char *currency; + + /** + * Configuration we use to lookup account information. + */ + 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 +ebics_amount_round (void *cls, + struct TALER_Amount *amount) +{ + struct EbicsClosure *sc = cls; + uint32_t delta; + + if (NULL == sc->currency) + return GNUNET_SYSERR; + if (0 != strcasecmp (amount->currency, + sc->currency)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100); + if (0 == delta) + return GNUNET_NO; + amount->fraction -= delta; + return GNUNET_OK; +} + + +/* Taken from GNU gettext */ + +/** + * Entry in the country table. + */ +struct CountryTableEntry +{ + /** + * 2-Character international country code. + */ + const char *code; + + /** + * Long English name of the country. + */ + const char *english; +}; + + +/* Keep the following table in sync with gettext. + WARNING: the entries should stay sorted according to the code */ +/** + * List of country codes. + */ +static const struct CountryTableEntry country_table[] = + { + { "AE", "U.A.E." }, + { "AF", "Afghanistan" }, + { "AL", "Albania" }, + { "AM", "Armenia" }, + { "AN", "Netherlands Antilles" }, + { "AR", "Argentina" }, + { "AT", "Austria" }, + { "AU", "Australia" }, + { "AZ", "Azerbaijan" }, + { "BA", "Bosnia and Herzegovina" }, + { "BD", "Bangladesh" }, + { "BE", "Belgium" }, + { "BG", "Bulgaria" }, + { "BH", "Bahrain" }, + { "BN", "Brunei Darussalam" }, + { "BO", "Bolivia" }, + { "BR", "Brazil" }, + { "BT", "Bhutan" }, + { "BY", "Belarus" }, + { "BZ", "Belize" }, + { "CA", "Canada" }, + { "CG", "Congo" }, + { "CH", "Switzerland" }, + { "CI", "Cote d'Ivoire" }, + { "CL", "Chile" }, + { "CM", "Cameroon" }, + { "CN", "People's Republic of China" }, + { "CO", "Colombia" }, + { "CR", "Costa Rica" }, + { "CS", "Serbia and Montenegro" }, + { "CZ", "Czech Republic" }, + { "DE", "Germany" }, + { "DK", "Denmark" }, + { "DO", "Dominican Republic" }, + { "DZ", "Algeria" }, + { "EC", "Ecuador" }, + { "EE", "Estonia" }, + { "EG", "Egypt" }, + { "ER", "Eritrea" }, + { "ES", "Spain" }, + { "ET", "Ethiopia" }, + { "FI", "Finland" }, + { "FO", "Faroe Islands" }, + { "FR", "France" }, + { "GB", "United Kingdom" }, + { "GD", "Caribbean" }, + { "GE", "Georgia" }, + { "GL", "Greenland" }, + { "GR", "Greece" }, + { "GT", "Guatemala" }, + { "HK", "Hong Kong" }, + { "HK", "Hong Kong S.A.R." }, + { "HN", "Honduras" }, + { "HR", "Croatia" }, + { "HT", "Haiti" }, + { "HU", "Hungary" }, + { "ID", "Indonesia" }, + { "IE", "Ireland" }, + { "IL", "Israel" }, + { "IN", "India" }, + { "IQ", "Iraq" }, + { "IR", "Iran" }, + { "IS", "Iceland" }, + { "IT", "Italy" }, + { "JM", "Jamaica" }, + { "JO", "Jordan" }, + { "JP", "Japan" }, + { "KE", "Kenya" }, + { "KG", "Kyrgyzstan" }, + { "KH", "Cambodia" }, + { "KR", "South Korea" }, + { "KW", "Kuwait" }, + { "KZ", "Kazakhstan" }, + { "LA", "Laos" }, + { "LB", "Lebanon" }, + { "LI", "Liechtenstein" }, + { "LK", "Sri Lanka" }, + { "LT", "Lithuania" }, + { "LU", "Luxembourg" }, + { "LV", "Latvia" }, + { "LY", "Libya" }, + { "MA", "Morocco" }, + { "MC", "Principality of Monaco" }, + { "MD", "Moldava" }, + { "MD", "Moldova" }, + { "ME", "Montenegro" }, + { "MK", "Former Yugoslav Republic of Macedonia" }, + { "ML", "Mali" }, + { "MM", "Myanmar" }, + { "MN", "Mongolia" }, + { "MO", "Macau S.A.R." }, + { "MT", "Malta" }, + { "MV", "Maldives" }, + { "MX", "Mexico" }, + { "MY", "Malaysia" }, + { "NG", "Nigeria" }, + { "NI", "Nicaragua" }, + { "NL", "Netherlands" }, + { "NO", "Norway" }, + { "NP", "Nepal" }, + { "NZ", "New Zealand" }, + { "OM", "Oman" }, + { "PA", "Panama" }, + { "PE", "Peru" }, + { "PH", "Philippines" }, + { "PK", "Islamic Republic of Pakistan" }, + { "PL", "Poland" }, + { "PR", "Puerto Rico" }, + { "PT", "Portugal" }, + { "PY", "Paraguay" }, + { "QA", "Qatar" }, + { "RE", "Reunion" }, + { "RO", "Romania" }, + { "RS", "Serbia" }, + { "RU", "Russia" }, + { "RW", "Rwanda" }, + { "SA", "Saudi Arabia" }, + { "SE", "Sweden" }, + { "SG", "Singapore" }, + { "SI", "Slovenia" }, + { "SK", "Slovak" }, + { "SN", "Senegal" }, + { "SO", "Somalia" }, + { "SR", "Suriname" }, + { "SV", "El Salvador" }, + { "SY", "Syria" }, + { "TH", "Thailand" }, + { "TJ", "Tajikistan" }, + { "TM", "Turkmenistan" }, + { "TN", "Tunisia" }, + { "TR", "Turkey" }, + { "TT", "Trinidad and Tobago" }, + { "TW", "Taiwan" }, + { "TZ", "Tanzania" }, + { "UA", "Ukraine" }, + { "US", "United States" }, + { "UY", "Uruguay" }, + { "VA", "Vatican" }, + { "VE", "Venezuela" }, + { "VN", "Viet Nam" }, + { "YE", "Yemen" }, + { "ZA", "South Africa" }, + { "ZW", "Zimbabwe" } + }; + + +/** + * Country code comparator function, for binary search with bsearch(). + * + * @param ptr1 pointer to a `struct table_entry` + * @param ptr2 pointer to a `struct table_entry` + * @return result of strncmp()'ing the 2-digit country codes of the entries + */ +static int +cmp_country_code (const void *ptr1, + const void *ptr2) +{ + const struct CountryTableEntry *cc1 = ptr1; + const struct CountryTableEntry *cc2 = ptr2; + + return strncmp (cc1->code, + cc2->code, + 2); +} + + +/** + * Validates given IBAN according to the European Banking Standards. See: + * http://www.europeanpaymentscouncil.eu/documents/ECBS%20IBAN%20standard%20EBS204_V3.2.pdf + * + * @param iban the IBAN number to validate + * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not + */ +static int +validate_iban (const char *iban) +{ + char cc[2]; + char ibancpy[35]; + struct CountryTableEntry cc_entry; + unsigned int len; + char *nbuf; + unsigned long long dividend; + unsigned long long remainder; + int nread; + int ret; + unsigned int i; + unsigned int j; + + len = strlen (iban); + if (len > 34) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "IBAN number too long to be valid\n"); + return GNUNET_NO; + } + strncpy (cc, iban, 2); + strncpy (ibancpy, iban + 4, len - 4); + strncpy (ibancpy + len - 4, iban, 4); + ibancpy[len] = '\0'; + cc_entry.code = cc; + cc_entry.english = NULL; + if (NULL == + bsearch (&cc_entry, + country_table, + sizeof (country_table) / sizeof (struct CountryTableEntry), + sizeof (struct CountryTableEntry), + &cmp_country_code)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Country code `%c%c' not supported\n", + cc[0], + cc[1]); + return GNUNET_NO; + } + nbuf = GNUNET_malloc ((len * 2) + 1); + for (i=0, j=0; i < len; i++) + { + if (isalpha ((unsigned char) ibancpy[i])) + { + if (2 != snprintf(&nbuf[j], + 3, + "%2u", + (ibancpy[i] - 'A' + 10))) + { + GNUNET_free (nbuf); + return GNUNET_NO; + } + j += 2; + continue; + } + nbuf[j] = ibancpy[i]; + j++; + } + for (j=0;'\0' != nbuf[j];j++) + GNUNET_assert (isdigit( (unsigned char) nbuf[j])); + GNUNET_assert (sizeof(dividend) >= 8); + remainder = 0; + for (i=0; iiban = result; + } + else + { + GNUNET_free (result); + } + return TALER_EC_NONE; +} + + +/** + * 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 +ebics_wire_validate (void *cls, + const char *account_url) +{ + return parse_payto (account_url, + NULL); +} + + +/** + * Prepare for exeuction of a wire transfer. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param wire valid wire account information + * @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 psc function to call with the prepared data to persist + * @param psc_cls closure for @a psc + * @return NULL on failure + */ +static struct TALER_WIRE_PrepareHandle * +ebics_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 psc, + void *psc_cls) +{ + GNUNET_break (0); // FIXME: not implemented + 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 +ebics_prepare_wire_transfer_cancel (void *cls, + struct TALER_WIRE_PrepareHandle *pth) +{ + GNUNET_break (0); // FIXME: not implemented +} + + +/** + * 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 * +ebics_execute_wire_transfer (void *cls, + const char *buf, + size_t buf_size, + TALER_WIRE_ConfirmationCallback cc, + void *cc_cls) +{ + GNUNET_break (0); // FIXME: not implemented + 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 +ebics_execute_wire_transfer_cancel (void *cls, + struct TALER_WIRE_ExecuteHandle *eh) +{ + GNUNET_break (0); // FIXME: not implemented +} + + +/** + * 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 * +ebics_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) +{ + 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 +ebics_get_history_cancel (void *cls, + struct TALER_WIRE_HistoryHandle *whh) +{ + GNUNET_break (0); +} + + + +/** + * 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 to task for timeout of operation. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; +}; + + +/** + * Rejection operation failed with timeout, notify callback + * and clean up. + * + * @param cls closure with `struct TALER_WIRE_RejectHandle` + */ +static void +timeout_reject (void *cls) +{ + struct TALER_WIRE_RejectHandle *rh = cls; + + rh->timeout_task = NULL; + rh->rej_cb (rh->rej_cb_cls, + TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */); + GNUNET_free (rh); +} + + +/** + * 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 * +ebics_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_RejectHandle *rh; + + GNUNET_break (0); /* not implemented, just a stub! */ + rh = GNUNET_new (struct TALER_WIRE_RejectHandle); + rh->rej_cb = rej_cb; + rh->rej_cb_cls = rej_cb_cls; + rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject, + rh); + return 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 * +ebics_reject_transfer_cancel (void *cls, + struct TALER_WIRE_RejectHandle *rh) +{ + void *ret = rh->rej_cb_cls; + + GNUNET_SCHEDULER_cancel (rh->timeout_task); + GNUNET_free (rh); + return ret; +} + + +/** + * Initialize ebics-wire subsystem. + * + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` + */ +void * +libtaler_plugin_wire_ebics_init (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct EbicsClosure *sc; + struct TALER_WIRE_Plugin *plugin; + + sc = GNUNET_new (struct EbicsClosure); + sc->cfg = cfg; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler", + "CURRENCY", + &sc->currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler", + "CURRENCY"); + GNUNET_free (sc); + return NULL; + } + plugin = GNUNET_new (struct TALER_WIRE_Plugin); + plugin->cls = sc; + plugin->method = "sepa"; + plugin->amount_round = &ebics_amount_round; + plugin->wire_validate = &ebics_wire_validate; + plugin->prepare_wire_transfer = &ebics_prepare_wire_transfer; + plugin->prepare_wire_transfer_cancel = &ebics_prepare_wire_transfer_cancel; + plugin->execute_wire_transfer = &ebics_execute_wire_transfer; + plugin->execute_wire_transfer_cancel = &ebics_execute_wire_transfer_cancel; + plugin->get_history = &ebics_get_history; + plugin->get_history_cancel = &ebics_get_history_cancel; + plugin->reject_transfer = &ebics_reject_transfer; + plugin->reject_transfer_cancel = &ebics_reject_transfer_cancel; + return plugin; +} + + +/** + * Shutdown Ebics wire subsystem. + * + * @param cls a `struct TALER_WIRE_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_wire_ebics_done (void *cls) +{ + struct TALER_WIRE_Plugin *plugin = cls; + struct EbicsClosure *sc = plugin->cls; + + GNUNET_free_non_null (sc->currency); + GNUNET_free (sc); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_wire_ebics.c */ diff --git a/src/wire-plugins/plugin_wire_taler-bank.c b/src/wire-plugins/plugin_wire_taler-bank.c new file mode 100644 index 000000000..2d2235343 --- /dev/null +++ b/src/wire-plugins/plugin_wire_taler-bank.c @@ -0,0 +1,1284 @@ +/* + 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_bank_service.h" +#include "taler_signatures.h" +#include + +/* only for HTTP status codes */ +#include + +/** + * Maximum legal 'value' for an account number, based on IEEE double (for JavaScript compatibility). + */ +#define MAX_ACCOUNT_NO (1LLU << 52) + +/** + * 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; +} + + +/** + * Information about an account extracted from a payto://-URL. + */ +struct Account +{ + /** + * Hostname of the bank (possibly including port). + */ + char *hostname; + + /** + * Bank account number. + */ + unsigned long long no; +}; + + +/** + * Parse payto:// account URL (only account information, + * wire subject and amount are ignored). + * + * @param account_url URL to parse + * @param account[out] set to information, can be NULL + * @return #TALER_EC_NONE if @a account_url is well-formed + */ +static enum TALER_ErrorCode +parse_payto (const char *account_url, + struct Account *r_account) +{ + const char *hostname; + const char *account; + const char *q; + unsigned long long no; + +#define PREFIX "payto://x-taler-bank/" + if (0 != strncasecmp (account_url, + PREFIX, + strlen (PREFIX))) + return TALER_EC_PAYTO_WRONG_METHOD; + hostname = &account_url[strlen (PREFIX)]; + if (NULL == (account = strchr (hostname, + (unsigned char) '/'))) + return TALER_EC_PAYTO_MALFORMED; + account++; + if (NULL != (q = strchr (account, + (unsigned char) '?'))) + { + char *s; + + s = GNUNET_strndup (account, + q - account); + if (1 != sscanf (s, + "%llu", + &no)) + { + GNUNET_free (s); + return TALER_EC_PAYTO_MALFORMED; + } + GNUNET_free (s); + } + else + { + if (1 != sscanf (account, + "%llu", + &no)) + return TALER_EC_PAYTO_MALFORMED; + } + if (no > MAX_ACCOUNT_NO) + return TALER_EC_PAYTO_MALFORMED; + if (NULL != r_account) + { + r_account->hostname = GNUNET_strndup (hostname, + account - hostname); + r_account->no = no; + } + return TALER_EC_NONE; +} + + +/** + * 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; + + return parse_payto (account_url, + NULL); +} + + +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) +{ + 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; + switch (pth->auth.method) + { + case TALER_BANK_AUTH_NONE: + len_au = 0; + len_ap = 0; + 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 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 != + parse_payto (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; + } + 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 Account a_in; + struct Account a_out; + + /* Check that payto:// URLs are valid */ + if (TALER_EC_NONE != + parse_payto (destination_account_url, + &a_out)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "payto://-URL `%s' is invalid!\n", + destination_account_url); + 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"); + GNUNET_free (a_out.hostname); + return NULL; + } + if (TALER_EC_NONE != + parse_payto (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); + GNUNET_free (a_out.hostname); + return NULL; + } + + /* Make sure the bank is the same! */ + if (0 != strcasecmp (a_in.hostname, + a_out.hostname)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "x-taler-bank hostname missmatch: `%s' != `%s'\n", + a_in.hostname, + a_out.hostname); + GNUNET_free (a_in.hostname); + GNUNET_free (a_out.hostname); + return NULL; + } + GNUNET_free (a_in.hostname); + GNUNET_free (a_out.hostname); + + 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); + 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 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, + const json_t *json) +{ + struct TALER_WIRE_ExecuteHandle *eh = cls; + json_t *reason; + const char *emsg; + char *s; + + 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); + eh->cc (eh->cc_cls, + (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR, + serial_id, + (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 Account origin_account; + struct 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; + 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.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.username) + 1; + GNUNET_assert (left >= slen); + left -= slen; + if (0 != left) + { + GNUNET_break (0); + return NULL; + } + } + + if (TALER_EC_NONE != + parse_payto (origin_account_url, + &origin_account)) + { + GNUNET_break (0); + return NULL; + } + if (TALER_EC_NONE != + parse_payto (destination_account_url, + &destination_account)) + { + GNUNET_break (0); + return NULL; + } + if (0 != strcasecmp (origin_account.hostname, + destination_account.hostname)) + { + GNUNET_break (0); + 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.hostname, + &auth, + exchange_base_url, + wire_s, + &amount, + (uint64_t) origin_account.no, + (uint64_t) destination_account.no, + &execute_cb, + eh); + 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) +{ + 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. + */ + 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) +{ + 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; + + 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; + return; /* do NOT yet clean up! */ + } + 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); + break; + 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); + break; + } + whh->hh = NULL; + taler_bank_get_history_cancel (NULL, + whh); +} + + +/** + * 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 lataler_bank; 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 Account account; + + 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)) + return NULL; + + whh = GNUNET_new (struct TALER_WIRE_HistoryHandle); + if (GNUNET_OK != + TALER_BANK_auth_parse_cfg (tc->cfg, + account_section, + &whh->auth)) + { + 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.hostname, + &whh->auth, + (uint64_t) account.no, + direction, + start_row, + num_results, + &bhist_cb, + whh); + if (NULL == whh->hh) + { + GNUNET_break (0); + taler_bank_get_history_cancel (NULL, + whh); + GNUNET_free (account.hostname); + return NULL; + } + GNUNET_free (account.hostname); + 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; + + 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; + + 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 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; + rh->brh = TALER_BANK_reject (tc->ctx, + account.hostname, + &rh->auth, + (uint64_t) account.no, + GNUNET_ntohll (*rowid_b64), + &reject_cb, + rh); + if (NULL == rh->brh) + { + (void) taler_bank_reject_transfer_cancel (tc, + rh); + GNUNET_free (account.hostname); + return NULL; + } + GNUNET_free (account.hostname); + 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 new file mode 100644 index 000000000..6fadb88ce --- /dev/null +++ b/src/wire-plugins/plugin_wire_template.c @@ -0,0 +1,345 @@ +/* + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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_ebics_wireformat.c b/src/wire-plugins/test_ebics_wireformat.c new file mode 100644 index 000000000..46989d503 --- /dev/null +++ b/src/wire-plugins/test_ebics_wireformat.c @@ -0,0 +1,80 @@ +/* + This file is part of TALER + (C) 2015, 2016, 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_ebics_wireformat.c + * @brief Tests for SEPA format validation by the EBICS plugin + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_wire_lib.h" + + +/** + * Valid SEPA data + */ +static const char *valid_wire_str = "payto://sepa/DE67830654080004822650"; + +/** + * IBAN has wrong country code + */ +static const char *invalid_wire_str = "payto://sepa/XX67830654080004822650"; + +/** + * IBAN has wrong checksum + */ +static const char *invalid_wire_str2 = "payto://sepa/DE67830654080004822651"; + +/** + * Unsupported wireformat type + */ +static const char *unsupported_wire_str = "payto://sega/DE67830654080004822650"; + + +int +main (int argc, + const char *const argv[]) +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct TALER_WIRE_Plugin *plugin; + + GNUNET_log_setup ("test-sepa-wireformats", + "WARNING", + NULL); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_string (cfg, + "taler", + "currency", + "EUR"); + plugin = TALER_WIRE_plugin_load (cfg, + "ebics"); + GNUNET_assert (NULL != plugin); + GNUNET_assert (TALER_EC_NONE != + plugin->wire_validate (plugin->cls, + unsupported_wire_str)); + GNUNET_assert (TALER_EC_NONE != + plugin->wire_validate (plugin->cls, + invalid_wire_str)); + GNUNET_assert (TALER_EC_NONE != + plugin->wire_validate (plugin->cls, + invalid_wire_str2)); + GNUNET_assert (TALER_EC_NONE == + plugin->wire_validate (plugin->cls, + valid_wire_str)); + TALER_WIRE_plugin_unload (plugin); + GNUNET_CONFIGURATION_destroy (cfg); + return 0; +} diff --git a/src/wire-plugins/test_wire_plugin.c b/src/wire-plugins/test_wire_plugin.c new file mode 100644 index 000000000..0e149dde7 --- /dev/null +++ b/src/wire-plugins/test_wire_plugin.c @@ -0,0 +1,186 @@ +/* + 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[] = { + { + .plugin_name = "ebics", + .round_in = "EUR:0.123456", + .round_out = "EUR:0.12", + .currency = "EUR" + }, + { + .plugin_name = "taler_bank", + .round_in = "KUDOS:0.123456", + .round_out = "KUDOS:0.12", + .currency = "KUDOS" + }, + { + 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); + GNUNET_assert (NULL != plugin); + 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 new file mode 100644 index 000000000..d1d699b0f --- /dev/null +++ b/src/wire-plugins/test_wire_plugin.conf @@ -0,0 +1,25 @@ +# 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 new file mode 100644 index 000000000..06aef8aa1 --- /dev/null +++ b/src/wire-plugins/test_wire_plugin_transactions_taler-bank.c @@ -0,0 +1,338 @@ +/* + 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 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) +{ + 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 != 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; + + hh = NULL; + if ( (TALER_BANK_DIRECTION_NONE == dir) && + (GNUNET_OK == global_ret) ) + { + GNUNET_SCHEDULER_shutdown (); + 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 != memcmp (&wtid, + &details->wtid, + sizeof (struct TALER_WireTransferIdentifierRawP))) + { + 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 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 + */ +static void +confirmation_cb (void *cls, + int success, + uint64_t serial_id, + const char *emsg) +{ + eh = NULL; + if (GNUNET_OK != success) + { + GNUNET_break (0); + global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return; + } + serial_target = serial_id; + 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 new file mode 100644 index 000000000..d6d2e8346 --- /dev/null +++ b/src/wire-plugins/test_wire_plugin_transactions_taler-bank.conf @@ -0,0 +1,12 @@ +# 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 5f651461e..9fcf877f9 100644 --- a/src/wire/Makefile.am +++ b/src/wire/Makefile.am @@ -8,114 +8,15 @@ endif pkgcfgdir = $(prefix)/share/taler/config.d/ -pkgcfg_DATA = \ - wire-sepa.conf \ - wire-test.conf - - -EXTRA_DIST = \ - wire-sepa.conf \ - wire-test.conf \ - test_wire_plugin.conf \ - test_wire_plugin_transactions_test.conf \ - test_wire_plugin_key.priv \ - test_wire_plugin_test.json \ - test_wire_plugin_sepa.json - -plugindir = $(libdir)/taler - -plugin_LTLIBRARIES = \ - libtaler_plugin_wire_sepa.la \ - libtaler_plugin_wire_test.la - -noinst_LTLIBRARIES = \ - libtaler_plugin_wire_template.la - lib_LTLIBRARIES = \ libtalerwire.la - -libtaler_plugin_wire_test_la_SOURCES = \ - plugin_wire_test.c -libtaler_plugin_wire_test_la_LIBADD = \ - $(LTLIBINTL) -libtaler_plugin_wire_test_la_LDFLAGS = \ - $(TALER_PLUGIN_LDFLAGS) \ - $(top_builddir)/src/bank-lib/libtalerbank.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetcurl \ - -lgnunetutil $(XLIB) - - -libtaler_plugin_wire_sepa_la_SOURCES = \ - plugin_wire_sepa.c -libtaler_plugin_wire_sepa_la_LIBADD = \ - $(LTLIBINTL) -libtaler_plugin_wire_sepa_la_LDFLAGS = \ - $(TALER_PLUGIN_LDFLAGS) \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetjson \ - -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) - - libtalerwire_la_SOURCES = \ - wire.c + wire.c \ + wire_helper.c libtalerwire_la_LIBADD = \ -lgnunetutil \ $(XLIB) libtalerwire_la_LDFLAGS = \ -version-info 1:0:0 \ -export-dynamic -no-undefined - - -AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; - -TESTS = \ - test_sepa_wireformat \ - test_wire_plugin - -check_PROGRAMS= \ - test_sepa_wireformat \ - test_wire_plugin \ - test_wire_plugin_transactions_test - - -test_sepa_wireformat_SOURCES = \ - test_sepa_wireformat.c -test_sepa_wireformat_LDADD = \ - -lgnunetutil \ - -ljansson \ - libtalerwire.la \ - $(top_builddir)/src/util/libtalerutil.la - - -test_wire_plugin_SOURCES = \ - test_wire_plugin.c -test_wire_plugin_LDADD = \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - libtalerwire.la \ - $(top_builddir)/src/util/libtalerutil.la - - -test_wire_plugin_transactions_test_SOURCES = \ - test_wire_plugin_transactions_test.c -test_wire_plugin_transactions_test_LDADD = \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - libtalerwire.la \ - $(top_builddir)/src/bank-lib/libtalerfakebank.la \ - $(top_builddir)/src/util/libtalerutil.la diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c deleted file mode 100644 index 416acac7c..000000000 --- a/src/wire/plugin_wire_sepa.c +++ /dev/null @@ -1,960 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2016, 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 plugin_wire_sepa.c - * @brief wire plugin for transfers using SEPA/EBICS - * @author Florian Dold - * @author Christian Grothoff - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include "taler_wire_plugin.h" -#include "taler_signatures.h" -#include - - -/** - * Type of the "cls" argument given to each of the functions in - * our API. - */ -struct SepaClosure -{ - - /** - * Which currency do we support? - */ - char *currency; - -}; - - -/** - * 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 -sepa_amount_round (void *cls, - struct TALER_Amount *amount) -{ - struct SepaClosure *sc = cls; - uint32_t delta; - - if (NULL == sc->currency) - return GNUNET_SYSERR; - if (0 != strcasecmp (amount->currency, - sc->currency)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100); - if (0 == delta) - return GNUNET_NO; - amount->fraction -= delta; - return GNUNET_OK; -} - - -/* Taken from GNU gettext */ - -/** - * Entry in the country table. - */ -struct table_entry -{ - /** - * 2-Character international country code. - */ - const char *code; - - /** - * Long English name of the country. - */ - const char *english; -}; - - -/* Keep the following table in sync with gettext. - WARNING: the entries should stay sorted according to the code */ -/** - * List of country codes. - */ -static const struct table_entry country_table[] = - { - { "AE", "U.A.E." }, - { "AF", "Afghanistan" }, - { "AL", "Albania" }, - { "AM", "Armenia" }, - { "AN", "Netherlands Antilles" }, - { "AR", "Argentina" }, - { "AT", "Austria" }, - { "AU", "Australia" }, - { "AZ", "Azerbaijan" }, - { "BA", "Bosnia and Herzegovina" }, - { "BD", "Bangladesh" }, - { "BE", "Belgium" }, - { "BG", "Bulgaria" }, - { "BH", "Bahrain" }, - { "BN", "Brunei Darussalam" }, - { "BO", "Bolivia" }, - { "BR", "Brazil" }, - { "BT", "Bhutan" }, - { "BY", "Belarus" }, - { "BZ", "Belize" }, - { "CA", "Canada" }, - { "CG", "Congo" }, - { "CH", "Switzerland" }, - { "CI", "Cote d'Ivoire" }, - { "CL", "Chile" }, - { "CM", "Cameroon" }, - { "CN", "People's Republic of China" }, - { "CO", "Colombia" }, - { "CR", "Costa Rica" }, - { "CS", "Serbia and Montenegro" }, - { "CZ", "Czech Republic" }, - { "DE", "Germany" }, - { "DK", "Denmark" }, - { "DO", "Dominican Republic" }, - { "DZ", "Algeria" }, - { "EC", "Ecuador" }, - { "EE", "Estonia" }, - { "EG", "Egypt" }, - { "ER", "Eritrea" }, - { "ES", "Spain" }, - { "ET", "Ethiopia" }, - { "FI", "Finland" }, - { "FO", "Faroe Islands" }, - { "FR", "France" }, - { "GB", "United Kingdom" }, - { "GD", "Caribbean" }, - { "GE", "Georgia" }, - { "GL", "Greenland" }, - { "GR", "Greece" }, - { "GT", "Guatemala" }, - { "HK", "Hong Kong" }, - { "HK", "Hong Kong S.A.R." }, - { "HN", "Honduras" }, - { "HR", "Croatia" }, - { "HT", "Haiti" }, - { "HU", "Hungary" }, - { "ID", "Indonesia" }, - { "IE", "Ireland" }, - { "IL", "Israel" }, - { "IN", "India" }, - { "IQ", "Iraq" }, - { "IR", "Iran" }, - { "IS", "Iceland" }, - { "IT", "Italy" }, - { "JM", "Jamaica" }, - { "JO", "Jordan" }, - { "JP", "Japan" }, - { "KE", "Kenya" }, - { "KG", "Kyrgyzstan" }, - { "KH", "Cambodia" }, - { "KR", "South Korea" }, - { "KW", "Kuwait" }, - { "KZ", "Kazakhstan" }, - { "LA", "Laos" }, - { "LB", "Lebanon" }, - { "LI", "Liechtenstein" }, - { "LK", "Sri Lanka" }, - { "LT", "Lithuania" }, - { "LU", "Luxembourg" }, - { "LV", "Latvia" }, - { "LY", "Libya" }, - { "MA", "Morocco" }, - { "MC", "Principality of Monaco" }, - { "MD", "Moldava" }, - { "MD", "Moldova" }, - { "ME", "Montenegro" }, - { "MK", "Former Yugoslav Republic of Macedonia" }, - { "ML", "Mali" }, - { "MM", "Myanmar" }, - { "MN", "Mongolia" }, - { "MO", "Macau S.A.R." }, - { "MT", "Malta" }, - { "MV", "Maldives" }, - { "MX", "Mexico" }, - { "MY", "Malaysia" }, - { "NG", "Nigeria" }, - { "NI", "Nicaragua" }, - { "NL", "Netherlands" }, - { "NO", "Norway" }, - { "NP", "Nepal" }, - { "NZ", "New Zealand" }, - { "OM", "Oman" }, - { "PA", "Panama" }, - { "PE", "Peru" }, - { "PH", "Philippines" }, - { "PK", "Islamic Republic of Pakistan" }, - { "PL", "Poland" }, - { "PR", "Puerto Rico" }, - { "PT", "Portugal" }, - { "PY", "Paraguay" }, - { "QA", "Qatar" }, - { "RE", "Reunion" }, - { "RO", "Romania" }, - { "RS", "Serbia" }, - { "RU", "Russia" }, - { "RW", "Rwanda" }, - { "SA", "Saudi Arabia" }, - { "SE", "Sweden" }, - { "SG", "Singapore" }, - { "SI", "Slovenia" }, - { "SK", "Slovak" }, - { "SN", "Senegal" }, - { "SO", "Somalia" }, - { "SR", "Suriname" }, - { "SV", "El Salvador" }, - { "SY", "Syria" }, - { "TH", "Thailand" }, - { "TJ", "Tajikistan" }, - { "TM", "Turkmenistan" }, - { "TN", "Tunisia" }, - { "TR", "Turkey" }, - { "TT", "Trinidad and Tobago" }, - { "TW", "Taiwan" }, - { "TZ", "Tanzania" }, - { "UA", "Ukraine" }, - { "US", "United States" }, - { "UY", "Uruguay" }, - { "VA", "Vatican" }, - { "VE", "Venezuela" }, - { "VN", "Viet Nam" }, - { "YE", "Yemen" }, - { "ZA", "South Africa" }, - { "ZW", "Zimbabwe" } - }; - - -/** - * Country code comparator function, for binary search with bsearch(). - * - * @param ptr1 pointer to a `struct table_entry` - * @param ptr2 pointer to a `struct table_entry` - * @return result of strncmp()'ing the 2-digit country codes of the entries - */ -static int -cmp_country_code (const void *ptr1, - const void *ptr2) -{ - const struct table_entry *cc1 = ptr1; - const struct table_entry *cc2 = ptr2; - - return strncmp (cc1->code, - cc2->code, - 2); -} - - -/** - * Validates given IBAN according to the European Banking Standards. See: - * http://www.europeanpaymentscouncil.eu/documents/ECBS%20IBAN%20standard%20EBS204_V3.2.pdf - * - * @param iban the IBAN number to validate - * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not - */ -static int -validate_iban (const char *iban) -{ - char cc[2]; - char ibancpy[35]; - struct table_entry cc_entry; - unsigned int len; - char *nbuf; - unsigned int i; - unsigned int j; - unsigned long long dividend; - unsigned long long remainder; - int nread; - int ret; - - len = strlen (iban); - if (len > 34) - { - GNUNET_break_op (0); - return GNUNET_NO; - } - strncpy (cc, iban, 2); - strncpy (ibancpy, iban + 4, len - 4); - strncpy (ibancpy + len - 4, iban, 4); - ibancpy[len] = '\0'; - cc_entry.code = cc; - cc_entry.english = NULL; - if (NULL == - bsearch (&cc_entry, - country_table, - sizeof (country_table) / sizeof (struct table_entry), - sizeof (struct table_entry), - &cmp_country_code)) - { - GNUNET_break_op (0); - return GNUNET_NO; - } - nbuf = GNUNET_malloc ((len * 2) + 1); - for (i=0, j=0; i < len; i++) - { - if (isalpha ((unsigned char) ibancpy[i])) - { - if (2 != snprintf(&nbuf[j], - 3, - "%2u", - (ibancpy[i] - 'A' + 10))) - { - GNUNET_free (nbuf); - return GNUNET_NO; - } - j += 2; - continue; - } - nbuf[j] = ibancpy[i]; - j++; - } - for (j=0;'\0' != nbuf[j];j++) - GNUNET_assert (isdigit( (unsigned char) nbuf[j])); - GNUNET_assert (sizeof(dividend) >= 8); - remainder = 0; - for (i=0; ipurpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS)); - wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS); - hc = GNUNET_CRYPTO_hash_context_start (); - GNUNET_CRYPTO_hash_context_read (hc, - "sepa", - strlen ("sepa") + 1); - GNUNET_CRYPTO_hash_context_read (hc, - sepa_name, - strlen (sepa_name) + 1); - GNUNET_CRYPTO_hash_context_read (hc, - iban, - strlen (iban) + 1); - GNUNET_CRYPTO_hash_context_read (hc, - bic, - strlen (bic) + 1); - GNUNET_CRYPTO_hash_context_finish (hc, - &wsd->h_sepa_details); -} - - -/** - * Verify that the signature in the @a json for /wire/sepa is valid. - * - * @param json json reply with the signature - * @param master_pub public key of the exchange to verify against - * @return #GNUNET_SYSERR if @a json is invalid, - * #GNUNET_NO if the method is unknown, - * #GNUNET_OK if the json is valid - */ -static int -verify_wire_sepa_signature_ok (const json_t *json, - const struct TALER_MasterPublicKeyP *master_pub) -{ - struct TALER_MasterSignatureP exchange_sig; - struct TALER_MasterWireDetailsPS mp; - const char *name; - const char *iban; - const char *bic; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig), - GNUNET_JSON_spec_string ("name", &name), - GNUNET_JSON_spec_string ("iban", &iban), - GNUNET_JSON_spec_string ("bic", &bic), - GNUNET_JSON_spec_end() - }; - - if (NULL == master_pub) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping signature check as master public key not given\n"); - return GNUNET_OK; - } - if (GNUNET_OK != - GNUNET_JSON_parse (json, spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - compute_purpose (name, - iban, - bic, - &mp); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SEPA_DETAILS, - &mp.purpose, - &exchange_sig.eddsa_signature, - &master_pub->eddsa_pub)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - return GNUNET_OK; -} - - -/** - * Check if the given wire format JSON object is correctly formatted - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param wire the JSON wire format object - * @param master_pub public key of the exchange to verify against - * @param[out] emsg set to an error message, unless we return #TALER_EC_NONE; - * error message must be freed by the caller using GNUNET_free() - * @return #TALER_EC_NONE if correctly formatted - */ -static enum TALER_ErrorCode -sepa_wire_validate (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg) -{ - json_error_t error; - const char *type; - const char *iban; - const char *name; - const char *bic; - - *emsg = NULL; - if (0 != json_unpack_ex - ((json_t *) wire, - &error, 0, - "{" - "s:s," /* type: sepa */ - "s:s," /* iban: IBAN */ - "s:s," /* name: beneficiary name */ - "s:s" /* bic: beneficiary bank's BIC */ - "}", - "type", &type, - "iban", &iban, - "name", &name, - "bic", &bic)) - { - char *dump; - - dump = json_dumps (wire, 0); - GNUNET_asprintf (emsg, - "JSON parsing failed at %s:%u: %s (%s): %s\n", - __FILE__, __LINE__, - error.text, - error.source, - dump); - free (dump); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_JSON; - } - if (0 != strcasecmp (type, - "sepa")) - { - GNUNET_asprintf (emsg, - "Transfer type `%s' invalid for SEPA wire plugin\n", - type); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE; - } - if (1 != validate_iban (iban)) - { - GNUNET_asprintf (emsg, - "IBAN `%s' invalid\n", - iban); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER; - } - /* FIXME: don't parse again, integrate properly... */ - if (GNUNET_OK != - verify_wire_sepa_signature_ok (wire, - master_pub)) - { - GNUNET_asprintf (emsg, - "Signature using public key `%s' invalid\n", - TALER_B2S (master_pub)); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; - } - return TALER_EC_NONE; -} - - -/** - * Obtain wire transfer details in the plugin-specific format - * from the configuration. - * - * @param cls closure - * @param cfg configuration with details about wire accounts - * @param account_name which section in the configuration should we parse - * @return NULL if @a cfg fails to have valid wire details for @a account_name - */ -static json_t * -sepa_get_wire_details (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *account_name) -{ - char *sepa_wire_file; - json_error_t err; - json_t *ret; - char *emsg; - - /* Fetch reply */ - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - account_name, - "SEPA_RESPONSE_FILE", - &sepa_wire_file)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - account_name, - "SEPA_RESPONSE_FILE"); - return NULL; - } - ret = json_load_file (sepa_wire_file, - JSON_REJECT_DUPLICATES, - &err); - if (NULL == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse JSON in %s: %s (%s:%u)\n", - sepa_wire_file, - err.text, - err.source, - err.line); - GNUNET_free (sepa_wire_file); - return NULL; - } - if (TALER_EC_NONE != - sepa_wire_validate (cls, - ret, - NULL, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to validate SEPA data in %s: %s\n", - sepa_wire_file, - emsg); - GNUNET_free (emsg); - GNUNET_free (sepa_wire_file); - json_decref (ret); - return NULL; - } - GNUNET_free (sepa_wire_file); - return ret; -} - - -/** - * Sign wire transfer details in the plugin-specific format. - * - * @param cls closure - * @param in wire transfer details in JSON format - * @param key private signing key to use - * @param salt salt to add - * @param[out] sig where to write the signature - * @return #GNUNET_OK on success - */ -static int -sepa_sign_wire_details (void *cls, - const json_t *in, - const struct TALER_MasterPrivateKeyP *key, - const struct GNUNET_HashCode *salt, - struct TALER_MasterSignatureP *sig) -{ - struct TALER_MasterWireDetailsPS wsd; - const char *sepa_name; - const char *iban; - const char *bic; - const char *type; - json_error_t err; - - if (0 != - json_unpack_ex ((json_t *) in, - &err, - 0 /* flags */, - "{s:s, s:s, s:s, s:s}", - "type", &type, - "name", &sepa_name, - "iban", &iban, - "bic", &bic)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to unpack JSON: %s (at %u)\n", - err.text, - err.position); - return GNUNET_SYSERR; - } - if (0 != strcmp (type, - "sepa")) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "`type' must be `sepa' for SEPA wire details\n"); - return GNUNET_SYSERR; - } - if (1 != validate_iban (iban)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "IBAN invalid in SEPA wire details\n"); - return GNUNET_SYSERR; - } - compute_purpose (sepa_name, - iban, - bic, - &wsd); - GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv, - &wsd.purpose, - &sig->eddsa_signature); - return GNUNET_OK; -} - - -/** - * Prepare for exeuction of a wire transfer. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param wire valid wire account information - * @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 psc function to call with the prepared data to persist - * @param psc_cls closure for @a psc - * @return NULL on failure - */ -static struct TALER_WIRE_PrepareHandle * -sepa_prepare_wire_transfer (void *cls, - const json_t *wire, - const struct TALER_Amount *amount, - const char *exchange_base_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_WIRE_PrepareTransactionCallback psc, - void *psc_cls) -{ - GNUNET_break (0); // FIXME: not implemented - 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 -sepa_prepare_wire_transfer_cancel (void *cls, - struct TALER_WIRE_PrepareHandle *pth) -{ - GNUNET_break (0); // FIXME: not implemented -} - - -/** - * 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 * -sepa_execute_wire_transfer (void *cls, - const char *buf, - size_t buf_size, - TALER_WIRE_ConfirmationCallback cc, - void *cc_cls) -{ - GNUNET_break (0); // FIXME: not implemented - 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 -sepa_execute_wire_transfer_cancel (void *cls, - struct TALER_WIRE_ExecuteHandle *eh) -{ - GNUNET_break (0); // FIXME: not implemented -} - - -/** - * 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 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 * -sepa_get_history (void *cls, - 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) -{ - 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 -sepa_get_history_cancel (void *cls, - struct TALER_WIRE_HistoryHandle *whh) -{ - GNUNET_break (0); -} - - - -/** - * 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 to task for timeout of operation. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; -}; - - -/** - * Rejection operation failed with timeout, notify callback - * and clean up. - * - * @param cls closure with `struct TALER_WIRE_RejectHandle` - */ -static void -timeout_reject (void *cls) -{ - struct TALER_WIRE_RejectHandle *rh = cls; - - rh->timeout_task = NULL; - rh->rej_cb (rh->rej_cb_cls, - TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */); - GNUNET_free (rh); -} - - -/** - * 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 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 * -sepa_reject_transfer (void *cls, - const void *start_off, - size_t start_off_len, - TALER_WIRE_RejectTransferCallback rej_cb, - void *rej_cb_cls) -{ - struct TALER_WIRE_RejectHandle *rh; - - GNUNET_break (0); /* not implemented, just a stub! */ - rh = GNUNET_new (struct TALER_WIRE_RejectHandle); - rh->rej_cb = rej_cb; - rh->rej_cb_cls = rej_cb_cls; - rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject, - rh); - return 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 * -sepa_reject_transfer_cancel (void *cls, - struct TALER_WIRE_RejectHandle *rh) -{ - void *ret = rh->rej_cb_cls; - - GNUNET_SCHEDULER_cancel (rh->timeout_task); - GNUNET_free (rh); - return ret; -} - - -/** - * Initialize sepa-wire subsystem. - * - * @param cls a configuration instance - * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` - */ -void * -libtaler_plugin_wire_sepa_init (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct SepaClosure *sc; - struct TALER_WIRE_Plugin *plugin; - - sc = GNUNET_new (struct SepaClosure); - if (NULL != cfg) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "taler", - "CURRENCY", - &sc->currency)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler", - "CURRENCY"); - GNUNET_free (sc); - return NULL; - } - } - plugin = GNUNET_new (struct TALER_WIRE_Plugin); - plugin->cls = sc; - plugin->amount_round = &sepa_amount_round; - plugin->get_wire_details = &sepa_get_wire_details; - plugin->sign_wire_details = &sepa_sign_wire_details; - plugin->wire_validate = &sepa_wire_validate; - plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer; - plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel; - plugin->execute_wire_transfer = &sepa_execute_wire_transfer; - plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel; - plugin->get_history = &sepa_get_history; - plugin->get_history_cancel = &sepa_get_history_cancel; - plugin->reject_transfer = &sepa_reject_transfer; - plugin->reject_transfer_cancel = &sepa_reject_transfer_cancel; - return plugin; -} - - -/** - * Shutdown Sepa wire subsystem. - * - * @param cls a `struct TALER_WIRE_Plugin` - * @return NULL (always) - */ -void * -libtaler_plugin_wire_sepa_done (void *cls) -{ - struct TALER_WIRE_Plugin *plugin = cls; - struct SepaClosure *sc = plugin->cls; - - GNUNET_free_non_null (sc->currency); - GNUNET_free (sc); - GNUNET_free (plugin); - return NULL; -} - -/* end of plugin_wire_sepa.c */ diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c deleted file mode 100644 index 06e70c2a8..000000000 --- a/src/wire/plugin_wire_template.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2016 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 -{ - - /** - * URL of the bank for sending funds to the bank. - */ - char *bank_url; - - /** - * Which currency do we support? - */ - char *currency; - -}; - - -/** - * 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; -} - - -/** - * Obtain wire transfer details in the plugin-specific format - * from the configuration. - * - * @param cls closure - * @param cfg configuration with details about wire accounts - * @param account_name which section in the configuration should we parse - * @return NULL if @a cfg fails to have valid wire details for @a account_name - */ -static json_t * -template_get_wire_details (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *account_name) -{ - GNUNET_break (0); - return NULL; -} - - -/** - * Check if the given wire format JSON object is correctly formatted - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param wire the JSON wire format object - * @param master_pub public key of the exchange to verify against - * @param[out] emsg set to an error message, unless we return #TALER_EC_NONE; - * error message must be freed by the caller using GNUNET_free() - * @return #TALER_EC_NONE if correctly formatted - */ -static enum TALER_ErrorCode -template_wire_validate (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg) -{ - GNUNET_asprintf (emsg, - "Not implemented"); - 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 wire valid wire account information - * @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 json_t *wire, - const struct TALER_Amount *amount, - const char *exchange_base_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_WIRE_PrepareTransactionCallback 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) -{ - 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) -{ - GNUNET_break (0); - return NULL; -} - - -/** - * Sign wire transfer details in the plugin-specific format. - * - * @param cls closure - * @param in wire transfer details in JSON format - * @param key private signing key to use - * @param salt salt to add - * @param[out] sig where to write the signature - * @return #GNUNET_OK on success - */ -static int -template_sign_wire_details (void *cls, - const json_t *in, - const struct TALER_MasterPrivateKeyP *key, - const struct GNUNET_HashCode *salt, - struct TALER_MasterSignatureP *sig) -{ - GNUNET_break (0); - return GNUNET_SYSERR; -} - - -/** - * 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) -{ - 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 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, - 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) -{ - 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) -{ - GNUNET_break (0); -} - - -/** - * 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); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange-wire-template", - "bank_url", - &tc->bank_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-wire-template", - "bank_url"); - GNUNET_free (tc); - return NULL; - } - 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->bank_url); - GNUNET_free (tc); - return NULL; - } - - plugin = GNUNET_new (struct TALER_WIRE_Plugin); - plugin->cls = tc; - plugin->amount_round = &template_amount_round; - plugin->get_wire_details = &template_get_wire_details; - plugin->sign_wire_details = &template_sign_wire_details; - 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; - 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->bank_url); - GNUNET_free (tc->currency); - GNUNET_free (tc); - GNUNET_free (plugin); - return NULL; -} - -/* end of plugin_wire_template.c */ diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c deleted file mode 100644 index 11feaea7e..000000000 --- a/src/wire/plugin_wire_test.c +++ /dev/null @@ -1,1302 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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 plugin_wire_test.c - * @brief plugin for the "test" wire method - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_wire_plugin.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 TestClosure -{ - - /** - * Which currency do we support? - */ - char *currency; - - /** - * URL of our bank. - */ - char *bank_url; - - /** - * Authentication information. - */ - struct TALER_BANK_AuthenticationData auth; - - /** - * 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; - - /** - * Number of the account that the exchange has at the bank for - * transfers. - */ - unsigned long long exchange_account_no; - -}; - - -/** - * Handle returned by #test_prepare_wire_transfer. - */ -struct TALER_WIRE_PrepareHandle -{ - - /** - * Task we use for async execution. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Test closure we run in. - */ - struct TestClosure *tc; - - /** - * Wire data for the transfer. - */ - json_t *wire; - - /** - * 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 #test_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 -test_amount_round (void *cls, - struct TALER_Amount *amount) -{ - struct TestClosure *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; - } - /* 'test' 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; -} - - -/** - * Compute purpose for signing. - * - * @param account number of the account - * @param bank_url URL of the bank - * @param[out] wsd purpose to be signed - */ -static void -compute_purpose (uint64_t account, - const char *bank_url, - struct TALER_MasterWireDetailsPS *wsd) -{ - struct GNUNET_HashContext *hc; - uint64_t n = GNUNET_htonll (account); - - wsd->purpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS)); - wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_TEST_DETAILS); - hc = GNUNET_CRYPTO_hash_context_start (); - GNUNET_CRYPTO_hash_context_read (hc, - "test", - strlen ("test") + 1); - GNUNET_CRYPTO_hash_context_read (hc, - &n, - sizeof (n)); - GNUNET_CRYPTO_hash_context_read (hc, - bank_url, - strlen (bank_url) + 1); - GNUNET_CRYPTO_hash_context_finish (hc, - &wsd->h_sepa_details); -} - - -/** - * Check if the given wire format JSON object is correctly formatted. - * Right now, the only thing we require is a field - * "account_number" which must contain a positive 53-bit integer. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param wire the JSON wire format object - * @param master_pub public key of the exchange to verify against - * @param[out] emsg set to an error message, unless we return #TALER_EC_NONE; - * error message must be freed by the caller using GNUNET_free() - * @return #TALER_EC_NONE if correctly formatted - */ -static enum TALER_ErrorCode -test_wire_validate (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg) -{ - struct TestClosure *tc = cls; - json_error_t error; - json_int_t account_no; - const char *bank_url; - const char *sig_s; - struct TALER_MasterWireDetailsPS wsd; - struct TALER_MasterSignatureP sig; - - *emsg = NULL; - if (0 != - json_unpack_ex ((json_t *) wire, - &error, - 0, - "{s:I, s:s}", - "account_number", &account_no, - "bank_url", &bank_url)) - { - char *dump; - - dump = json_dumps (wire, 0); - GNUNET_asprintf (emsg, - "JSON parsing failed at %s:%u: %s (%s): %s\n", - __FILE__, __LINE__, - error.text, - error.source, - dump); - free (dump); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_JSON; - } - if ( (account_no < 0) || - (account_no > (1LL << 53)) ) - { - GNUNET_asprintf (emsg, - "Account number %llu outside of permitted range\n", - account_no); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER; - } - if ( (NULL != tc->bank_url) && - (0 != strcmp (bank_url, - tc->bank_url)) ) - { - GNUNET_asprintf (emsg, - "Wire specifies bank URL `%s', but this exchange only supports `%s'\n", - bank_url, - tc->bank_url); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_BANK; - } - if (NULL == master_pub) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping signature check as master public key not given\n"); - return TALER_EC_NONE; - } - if (0 != - json_unpack_ex ((json_t *) wire, - &error, - 0, - "{s:s}", - "sig", &sig_s)) - { - GNUNET_asprintf (emsg, - "Signature check required, but signature is missing\n"); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; - } - compute_purpose (account_no, - bank_url, - &wsd); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (sig_s, - strlen (sig_s), - &sig, - sizeof (sig))) - { - GNUNET_break (0); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_TEST_DETAILS, - &wsd.purpose, - &sig.eddsa_signature, - &master_pub->eddsa_pub)) - { - GNUNET_asprintf (emsg, - "Signature using public key `%s' invalid\n", - TALER_B2S (master_pub)); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; - } - return TALER_EC_NONE; -} - - -/** - * Obtain wire transfer details in the plugin-specific format - * from the configuration. - * - * @param cls closure - * @param cfg configuration with details about wire accounts - * @param account_name which section in the configuration should we parse - * @return NULL if @a cfg fails to have valid wire details for @a account_name - */ -static json_t * -test_get_wire_details (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *account_name) -{ - struct TestClosure *tc = cls; - char *test_wire_file; - json_error_t err; - json_t *ret; - char *emsg; - - /* Fetch reply */ - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - account_name, - "TEST_RESPONSE_FILE", - &test_wire_file)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - account_name, - "TEST_RESPONSE_FILE"); - return NULL; - } - ret = json_load_file (test_wire_file, - JSON_REJECT_DUPLICATES, - &err); - if (NULL == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse JSON in %s: %s (%s:%u)\n", - test_wire_file, - err.text, - err.source, - err.line); - GNUNET_free (test_wire_file); - return NULL; - } - if (TALER_EC_NONE != - test_wire_validate (tc, - ret, - NULL, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to validate TEST wire data in %s: %s\n", - test_wire_file, - emsg); - GNUNET_free (emsg); - GNUNET_free (test_wire_file); - json_decref (ret); - return NULL; - } - GNUNET_free (test_wire_file); - return ret; -} - - -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 serialized 'wire' JSON data (0-terminated) */ - - /* followed by 0-terminated base 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 -test_prepare_wire_transfer_cancel (void *cls, - struct TALER_WIRE_PrepareHandle *pth) -{ - if (NULL != pth->task) - GNUNET_SCHEDULER_cancel (pth->task); - json_decref (pth->wire); - 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; - char *wire_enc; - size_t len_w; - size_t len_b; - struct BufFormatP bf; - - pth->task = NULL; - /* serialize the state into a 'buf' */ - wire_enc = json_dumps (pth->wire, - JSON_COMPACT | JSON_SORT_KEYS); - if (NULL == wire_enc) - { - GNUNET_break (0); - pth->ptc (pth->ptc_cls, - NULL, - 0); - test_prepare_wire_transfer_cancel (NULL, - pth); - return; - } - len_w = strlen (wire_enc) + 1; - len_b = strlen (pth->exchange_base_url) + 1; - bf.wtid = pth->wtid; - TALER_amount_hton (&bf.amount, - &pth->amount); - { - char buf[sizeof (struct BufFormatP) + len_w + len_b]; - - memcpy (buf, - &bf, - sizeof (struct BufFormatP)); - memcpy (&buf[sizeof (struct BufFormatP)], - wire_enc, - len_w); - memcpy (&buf[sizeof (struct BufFormatP) + len_w], - pth->exchange_base_url, - len_b); - - /* finally give the state back */ - pth->ptc (pth->ptc_cls, - buf, - sizeof (buf)); - } - free (wire_enc); /* not using GNUNET_free(), - as this one is allocated by libjansson */ - test_prepare_wire_transfer_cancel (NULL, - pth); -} - - -/** - * 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 wire valid wire account information - * @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 * -test_prepare_wire_transfer (void *cls, - const json_t *wire, - const struct TALER_Amount *amount, - const char *exchange_base_url, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_WIRE_PrepareTransactionCallback ptc, - void *ptc_cls) -{ - struct TestClosure *tc = cls; - struct TALER_WIRE_PrepareHandle *pth; - char *emsg; - - if (TALER_EC_NONE != - test_wire_validate (tc, - wire, - NULL, - &emsg)) - { - GNUNET_break_op (0); - GNUNET_free (emsg); - return NULL; - } - pth = GNUNET_new (struct TALER_WIRE_PrepareHandle); - pth->tc = tc; - pth->wire = json_incref ((json_t *) wire); - 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 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, - const json_t *json) -{ - struct TALER_WIRE_ExecuteHandle *eh = cls; - json_t *reason; - const char *emsg; - char *s; - - 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); - eh->cc (eh->cc_cls, - (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR, - serial_id, - (MHD_HTTP_OK == http_status) ? NULL : s); - GNUNET_free (s); - GNUNET_free (eh); -} - - -/** - * Sign wire transfer details in the plugin-specific format. - * - * @param cls closure - * @param in wire transfer details in JSON format - * @param key private signing key to use - * @param salt salt to add - * @param[out] sig where to write the signature - * @return #GNUNET_OK on success - */ -static int -test_sign_wire_details (void *cls, - const json_t *in, - const struct TALER_MasterPrivateKeyP *key, - const struct GNUNET_HashCode *salt, - struct TALER_MasterSignatureP *sig) -{ - struct TALER_MasterWireDetailsPS wsd; - const char *bank_url; - const char *type; - json_int_t account; - json_error_t err; - - if (0 != - json_unpack_ex ((json_t *) in, - &err, - 0 /* flags */, - "{s:s, s:s, s:I}", - "type", &type, - "bank_url", &bank_url, - "account_number", &account)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to unpack JSON: %s (at %u)\n", - err.text, - err.position); - return GNUNET_SYSERR; - } - if (0 != strcmp (type, - "test")) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "`type' must be `test' for test wire details\n"); - return GNUNET_SYSERR; - } - compute_purpose (account, - bank_url, - &wsd); - GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv, - &wsd.purpose, - &sig->eddsa_signature); - return GNUNET_OK; -} - - -/** - * 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 * -test_execute_wire_transfer (void *cls, - const char *buf, - size_t buf_size, - TALER_WIRE_ConfirmationCallback cc, - void *cc_cls) -{ - struct TestClosure *tc = cls; - struct TALER_WIRE_ExecuteHandle *eh; - json_t *wire; - json_error_t error; - struct TALER_Amount amount; - json_int_t account_no; - struct BufFormatP bf; - char *emsg; - const char *json_s; - const char *exchange_base_url; - 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; - } - json_s = &buf[sizeof (struct BufFormatP)]; - exchange_base_url = &json_s[strlen (json_s) + 1]; - if (exchange_base_url > &buf[buf_size - 1]) - { - GNUNET_break (0); - return NULL; - } - memcpy (&bf, - buf, - sizeof (bf)); - TALER_amount_ntoh (&amount, - &bf.amount); - wire = json_loads (json_s, - JSON_REJECT_DUPLICATES, - NULL); - if (NULL == wire) - { - GNUNET_break (0); - return NULL; - } - GNUNET_assert (TALER_EC_NONE == - test_wire_validate (tc, - wire, - NULL, - &emsg)); - if (0 != - json_unpack_ex (wire, - &error, - 0, - "{s:I}", - "account_number", &account_no)) - { - GNUNET_break (0); - 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, - tc->bank_url, - &tc->auth, - exchange_base_url, - wire_s, - &amount, - (uint64_t) tc->exchange_account_no, - (uint64_t) account_no, - &execute_cb, - eh); - GNUNET_free (wire_s); - json_decref (wire); - 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 -test_execute_wire_transfer_cancel (void *cls, - struct TALER_WIRE_ExecuteHandle *eh) -{ - TALER_BANK_admin_add_incoming_cancel (eh->aaih); - GNUNET_free (eh); -} - - -/** - * Handle for a #test_get_history() request. - */ -struct TALER_WIRE_HistoryHandle -{ - - /** - * Function to call with results. - */ - TALER_WIRE_HistoryResultCallback hres_cb; - - /** - * Closure for @e hres_cb. - */ - void *hres_cb_cls; - - /** - * Request to the bank. - */ - struct TALER_BANK_HistoryHandle *hh; - -}; - - -/** - * 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; - - 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, (int) ' '); - 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_details = details->account_details; - - 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; - break; - } - 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); - GNUNET_free (whh); - break; - 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); - GNUNET_free (whh); - break; - } -} - - -/** - * 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 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 * -test_get_history (void *cls, - 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 TestClosure *tc = cls; - struct TALER_WIRE_HistoryHandle *whh; - const uint64_t *start_off_b64; - uint64_t start_row; - - 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 'test' 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); - } - - whh = GNUNET_new (struct TALER_WIRE_HistoryHandle); - whh->hres_cb = hres_cb; - whh->hres_cb_cls = hres_cb_cls; - whh->hh = TALER_BANK_history (tc->ctx, - tc->bank_url, - &tc->auth, - (uint64_t) tc->exchange_account_no, - direction, - start_row, - num_results, - &bhist_cb, - whh); - if (NULL == whh->hh) - { - GNUNET_break (0); - GNUNET_free (whh); - return NULL; - } - - return whh; -} - - -/** - * 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 -test_get_history_cancel (void *cls, - struct TALER_WIRE_HistoryHandle *whh) -{ - TALER_BANK_history_cancel (whh->hh); - GNUNET_free (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; -}; - - -/** - * 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; - - rh->brh = NULL; - rh->rej_cb (rh->rej_cb_cls, - ec); - GNUNET_free (rh); -} - - -/** - * 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 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 * -test_reject_transfer (void *cls, - const void *start_off, - size_t start_off_len, - TALER_WIRE_RejectTransferCallback rej_cb, - void *rej_cb_cls) -{ - struct TestClosure *tc = cls; - const uint64_t *rowid_b64 = start_off; - struct TALER_WIRE_RejectHandle *rh; - - if (sizeof (uint64_t) != start_off_len) - { - GNUNET_break (0); - return NULL; - } - rh = GNUNET_new (struct TALER_WIRE_RejectHandle); - rh->rej_cb = rej_cb; - rh->rej_cb_cls = rej_cb_cls; - rh->brh = TALER_BANK_reject (tc->ctx, - tc->bank_url, - &tc->auth, - (uint64_t) tc->exchange_account_no, - GNUNET_ntohll (*rowid_b64), - &reject_cb, - rh); - if (NULL == rh->brh) - { - GNUNET_free (rh); - return NULL; - } - return 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 * -test_reject_transfer_cancel (void *cls, - struct TALER_WIRE_RejectHandle *rh) -{ - void *ret = rh->rej_cb_cls; - - if (NULL != rh->brh) - TALER_BANK_reject_cancel (rh->brh); - GNUNET_free (rh); - return ret; -} - - -/** - * Initialize test-wire subsystem. - * - * @param cls a configuration instance - * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin` - */ -void * -libtaler_plugin_wire_test_init (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct TestClosure *tc; - struct TALER_WIRE_Plugin *plugin; - char *user; - char *pass; - - tc = GNUNET_new (struct TestClosure); - if (NULL != cfg) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange-wire-test", - "BANK_URL", - &tc->bank_url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-wire-test", - "BANK_URL"); - GNUNET_free (tc); - return NULL; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "exchange-wire-test", - "EXCHANGE_ACCOUNT_NUMBER", - &tc->exchange_account_no)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-wire-test", - "EXCHANGE_ACCOUNT_NUMBER"); - GNUNET_free (tc->bank_url); - GNUNET_free (tc); - return NULL; - } - 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->bank_url); - GNUNET_free (tc); - return NULL; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange-wire-test", - "USERNAME", - &user)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-wire-test", - "USERNAME"); - GNUNET_free (tc->bank_url); - GNUNET_free (tc); - return NULL; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange-wire-test", - "PASSWORD", - &pass)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange-wire-test", - "PASSWORD"); - GNUNET_free (tc->bank_url); - GNUNET_free (tc); - GNUNET_free (user); - return NULL; - } - tc->auth.method = TALER_BANK_AUTH_BASIC; - tc->auth.details.basic.username = user; - tc->auth.details.basic.password = pass; - 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->bank_url); - GNUNET_free (tc->auth.details.basic.username); - GNUNET_free (tc->auth.details.basic.password); - GNUNET_free (tc); - return NULL; - } - } - plugin = GNUNET_new (struct TALER_WIRE_Plugin); - plugin->cls = tc; - plugin->amount_round = &test_amount_round; - plugin->get_wire_details = &test_get_wire_details; - plugin->sign_wire_details = &test_sign_wire_details; - plugin->wire_validate = &test_wire_validate; - plugin->prepare_wire_transfer = &test_prepare_wire_transfer; - plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel; - plugin->execute_wire_transfer = &test_execute_wire_transfer; - plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel; - plugin->get_history = &test_get_history; - plugin->get_history_cancel = &test_get_history_cancel; - plugin->reject_transfer = &test_reject_transfer; - plugin->reject_transfer_cancel = &test_reject_transfer_cancel; - return plugin; -} - - -/** - * Shutdown Test wire subsystem. - * - * @param cls a `struct TALER_WIRE_Plugin` - * @return NULL (always) - */ -void * -libtaler_plugin_wire_test_done (void *cls) -{ - struct TALER_WIRE_Plugin *plugin = cls; - struct TestClosure *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; - } - switch (tc->auth.method) - { - case TALER_BANK_AUTH_NONE: - break; - case TALER_BANK_AUTH_BASIC: - if (NULL != tc->auth.details.basic.username) - { - GNUNET_free (tc->auth.details.basic.username); - tc->auth.details.basic.username = NULL; - } - if (NULL != tc->auth.details.basic.password) - { - GNUNET_free (tc->auth.details.basic.password); - tc->auth.details.basic.password = NULL; - } - break; - } - GNUNET_free_non_null (tc->currency); - GNUNET_free_non_null (tc->bank_url); - GNUNET_free (tc); - GNUNET_free (plugin); - return NULL; -} - -/* end of plugin_wire_test.c */ diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c deleted file mode 100644 index 07a3784c6..000000000 --- a/src/wire/test_sepa_wireformat.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - This file is part of TALER - (C) 2015, 2016 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/test_sepa_wireformat.c - * @brief Tests for JSON SEPA format validation - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include "taler_util.h" -#include "taler_wire_lib.h" - - -/* Valid SEPA data */ -static const char * const valid_wire_str = - "{ \"type\":\"SEPA\", \ -\"iban\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"salt\":\"123456789\", \ -\"address\": \"foobar\"}"; - -/* IBAN has wrong country code */ -static const char * const invalid_wire_str = - "{ \"type\":\"SEPA\", \ -\"iban\":\"XX67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"salt\":\"123456789\", \ -\"address\": \"foobar\"}"; - -/* IBAN has wrong checksum */ -static const char * const invalid_wire_str2 = - "{ \"type\":\"SEPA\", \ -\"iban\":\"DE67830654080004822651\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"salt\":\"123456789\", \ -\"address\": \"foobar\"}"; - -/* Unsupported wireformat type */ -static const char * const unsupported_wire_str = - "{ \"type\":\"unsupported\", \ -\"iban\":\"DE67830654080004822650\", \ -\"name\":\"GNUnet e.V.\", \ -\"bic\":\"GENODEF1SLR\", \ -\"salt\":\"123456789\", \ -\"address\": \"foobar\"}"; - - -int -main(int argc, - const char *const argv[]) -{ - json_t *wire; - char *emsg; - json_error_t error; - int ret; - struct GNUNET_CONFIGURATION_Handle *cfg; - struct TALER_WIRE_Plugin *plugin; - - GNUNET_log_setup ("test-sepa-wireformats", - "WARNING", - NULL); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_string (cfg, - "taler", - "currency", - "EUR"); - plugin = TALER_WIRE_plugin_load (cfg, - "sepa"); - GNUNET_assert (NULL != plugin); - (void) memset(&error, 0, sizeof(error)); - GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL))); - GNUNET_assert (TALER_EC_NONE != plugin->wire_validate (NULL, - wire, - NULL, - &emsg)); - GNUNET_free (emsg); - json_decref (wire); - GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL))); - GNUNET_assert (TALER_EC_NONE != plugin->wire_validate (NULL, - wire, - NULL, - &emsg)); - GNUNET_free (emsg); - json_decref (wire); - GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL))); - GNUNET_assert (TALER_EC_NONE != plugin->wire_validate (NULL, - wire, - NULL, - &emsg)); - GNUNET_free (emsg); - json_decref (wire); - GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error))); - ret = plugin->wire_validate (NULL, - wire, - NULL, - &emsg); - json_decref (wire); - TALER_WIRE_plugin_unload (plugin); - GNUNET_CONFIGURATION_destroy (cfg); - if (TALER_EC_NONE != ret) - return 1; - return 0; -} diff --git a/src/wire/test_wire_plugin.c b/src/wire/test_wire_plugin.c deleted file mode 100644 index f5dba01aa..000000000 --- a/src/wire/test_wire_plugin.c +++ /dev/null @@ -1,269 +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 -#include - - -/** - * Definitions for a test with a plugin. - */ -struct TestBlock { - - /** - * Name of the plugin to test. - */ - const char *plugin_name; - - /** - * JSON template expected by the plugin for an account definition. - */ - const char *json_proto; - - /** - * 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[] = { - { - .plugin_name = "sepa", - .json_proto = "{ \"type\":\"sepa\", \"iban\":\"DE67830654080004822650\", \"name\":\"GNUnet e.V.\", \"bic\":\"GENODEF1SLR\" }", - .round_in = "EUR:0.123456", - .round_out = "EUR:0.12", - .currency = "EUR" - }, - { - .plugin_name = "test", - .json_proto = "{ \"type\":\"test\", \"bank_url\":\"http://localhost/\", \"account_number\":42 }", - .round_in = "KUDOS:0.123456", - .round_out = "KUDOS:0.12", - .currency = "KUDOS" - }, - { - NULL, NULL, NULL, NULL, NULL - } -}; - - -/** - * Private key used to sign wire details. - */ -static struct TALER_MasterPrivateKeyP priv_key; - -/** - * Public key matching #priv_key. - */ -static struct TALER_MasterPublicKeyP pub_key; - -/** - * Our configuration. - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * Run the test. - * - * @param test details of the test - * @param plugin plugin to test - * @param wire wire details for testing - * @return #GNUNET_OK on success - */ -static int -run_test (const struct TestBlock *test, - struct TALER_WIRE_Plugin *plugin, - json_t *wire) -{ - struct GNUNET_HashCode salt; - struct TALER_MasterSignatureP sig; - json_t *lwire; - struct TALER_Amount in; - struct TALER_Amount expect; - char *emsg; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &salt, - sizeof (salt)); - if (GNUNET_OK != - plugin->sign_wire_details (plugin->cls, - wire, - &priv_key, - &salt, - &sig)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - json_object_set_new (wire, - "salt", - GNUNET_JSON_from_data (&salt, - sizeof (salt))); - json_object_set_new (wire, - "sig", - GNUNET_JSON_from_data (&sig, - sizeof (sig))); - if (TALER_EC_NONE != - plugin->wire_validate (plugin->cls, - wire, - &pub_key, - &emsg)) - { - GNUNET_break (0); - GNUNET_free (emsg); - return GNUNET_SYSERR; - } - /* load wire details from file */ - lwire = plugin->get_wire_details (plugin->cls, - cfg, - test->plugin_name); - if (NULL == lwire) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (TALER_EC_NONE != - plugin->wire_validate (plugin->cls, - lwire, - &pub_key, - &emsg)) - { - GNUNET_break (0); - GNUNET_free (emsg); - json_decref (lwire); - return GNUNET_SYSERR; - } - json_decref (lwire); - 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[]) -{ - json_t *wire; - int ret; - struct TALER_WIRE_Plugin *plugin; - const struct TestBlock *test; - unsigned int i; - struct GNUNET_CRYPTO_EddsaPrivateKey *pk; - - GNUNET_log_setup ("test-wire-plugin", - "WARNING", - NULL); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_load (cfg, - "test_wire_plugin.conf")); - pk = GNUNET_CRYPTO_eddsa_key_create_from_file ("test_wire_plugin_key.priv"); - priv_key.eddsa_priv = *pk; - GNUNET_free (pk); - GNUNET_CRYPTO_eddsa_key_get_public (&priv_key.eddsa_priv, - &pub_key.eddsa_pub); - ret = GNUNET_OK; - for (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); - GNUNET_assert (NULL != plugin); - wire = json_loads (test->json_proto, 0, NULL); - GNUNET_assert (NULL != wire); - ret = run_test (test, plugin, wire); - json_decref (wire); - 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/test_wire_plugin.conf b/src/wire/test_wire_plugin.conf deleted file mode 100644 index 90b4f0749..000000000 --- a/src/wire/test_wire_plugin.conf +++ /dev/null @@ -1,21 +0,0 @@ -# This file is in the public domain. -# -[test] -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -TEST_RESPONSE_FILE = test_wire_plugin_test.json - -[sepa] -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -SEPA_RESPONSE_FILE = test_wire_plugin_sepa.json - - -[exchange-wire-test] -# For transfers made by the exchange, we need to know -# the URL of the bank (where the /admin/add/incoming API -# is avaialble). -BANK_URL = http://localhost/ - -[taler] -CURRENCY = "EUR" diff --git a/src/wire/test_wire_plugin_key.priv b/src/wire/test_wire_plugin_key.priv deleted file mode 100644 index 26b4f26f6..000000000 --- a/src/wire/test_wire_plugin_key.priv +++ /dev/null @@ -1 +0,0 @@ -?Sgb@Js; %aKȉs_Hў \ No newline at end of file diff --git a/src/wire/test_wire_plugin_sepa.json b/src/wire/test_wire_plugin_sepa.json deleted file mode 100644 index 175345f0c..000000000 --- a/src/wire/test_wire_plugin_sepa.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "salt": "32V01R7K4T02S74PZZMVXRQ1K7FR948RBNB9BJ5Z101HEQFH7CW7J82006GY3BPTGQ4FM775PSSRD3K9MY97HSNVVCGEVBPVSAQ2710", - "type": "sepa", - "iban": "DE67830654080004822650", - "sig": "K48GPPM715ZXX0DC597WESD5ECT3R0B3TAFQMB68SBF4K5CZ5KCE9NESN1JX412SPZ82PSV7JAPVJFXDDTZ63YV4295S5RC28E4221G", - "name": "GNUnet e.V.", - "bic": "GENODEF1SLR" -} \ No newline at end of file diff --git a/src/wire/test_wire_plugin_test.json b/src/wire/test_wire_plugin_test.json deleted file mode 100644 index e5a0c3329..000000000 --- a/src/wire/test_wire_plugin_test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "test", - "bank_url": "http://localhost/", - "sig": "KX1CMHNFH1WE10244AEF07AXHJCF9PZDZVNZBC9P4EJEQ1MH1Y3C2TWF08VTQMK4N5TCV0V1VTGWSV0WB8TB9YQRZW87F5A6KCEZ81R", - "account_number": 42, - "salt": "EZV905MQPVAZEMGC6SEZQF2Z75P6ZKTN8TX00JHN11S7J81DQ78G8Z551K6TGR9WHPP0JW1X9J9X9CVRY48JTHBCP6Q4XKJ6R2G18G0" -} diff --git a/src/wire/test_wire_plugin_transactions_test.c b/src/wire/test_wire_plugin_transactions_test.c deleted file mode 100644 index ccfde1b40..000000000 --- a/src/wire/test_wire_plugin_transactions_test.c +++ /dev/null @@ -1,356 +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_transactions_test.c - * @brief Tests performing actual transactions with the TEST 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 -#include - - -/** - * When does the test timeout? Right now, we expect this to be very - * fast. - */ -#define TIMEOUT GNUNET_TIME_UNIT_SECONDS - - -/** - * Input for the wire transfer details. - */ -static const char *json_proto = - "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8088/\", \"account_number\":42 }"; - - -/** - * Private key used to sign wire details. - */ -static struct TALER_MasterPrivateKeyP priv_key; - -/** - * Public key matching #priv_key. - */ -static struct TALER_MasterPublicKeyP pub_key; - -/** - * 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 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) -{ - 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 != 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; - - hh = NULL; - if ( (TALER_BANK_DIRECTION_NONE == dir) && - (GNUNET_OK == global_ret) ) - { - GNUNET_SCHEDULER_shutdown (); - 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 != memcmp (&wtid, - &details->wtid, - sizeof (struct TALER_WireTransferIdentifierRawP))) - { - 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 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 - */ -static void -confirmation_cb (void *cls, - int success, - uint64_t serial_id, - const char *emsg) -{ - eh = NULL; - if (GNUNET_OK != success) - { - GNUNET_break (0); - global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return; - } - serial_target = serial_id; - hh = plugin->get_history (plugin->cls, - 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) -{ - json_t *wire; - 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)); - wire = json_loads (json_proto, - 0, - NULL); - fb = TALER_FAKEBANK_start (8088); - ph = plugin->prepare_wire_transfer (plugin->cls, - wire, - &amount, - "https://exchange.net/", - &wtid, - &prepare_cb, - NULL); - json_decref (wire); -} - - -int -main (int argc, - const char *const argv[]) -{ - struct GNUNET_CRYPTO_EddsaPrivateKey *pk; - - 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_test.conf")); - pk = GNUNET_CRYPTO_eddsa_key_create_from_file ("test_wire_plugin_key.priv"); - priv_key.eddsa_priv = *pk; - GNUNET_free (pk); - GNUNET_CRYPTO_eddsa_key_get_public (&priv_key.eddsa_priv, - &pub_key.eddsa_pub); - global_ret = GNUNET_OK; - plugin = TALER_WIRE_plugin_load (cfg, - "test"); - 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_test.c */ diff --git a/src/wire/test_wire_plugin_transactions_test.conf b/src/wire/test_wire_plugin_transactions_test.conf deleted file mode 100644 index 42fb51b79..000000000 --- a/src/wire/test_wire_plugin_transactions_test.conf +++ /dev/null @@ -1,15 +0,0 @@ -# This file is in the public domain. -# -[test] -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -TEST_RESPONSE_FILE = test_wire_plugin_test.json - -[exchange-wire-test] -# For transfers made by the exchange, we need to know -# the URL of the bank (where the /admin/add/incoming API -# is avaialble). -BANK_URL= http://localhost:8088/ - -[taler] -CURRENCY = "KUDOS" diff --git a/src/wire/wire-sepa.conf b/src/wire/wire-sepa.conf deleted file mode 100644 index eb43d3dcc..000000000 --- a/src/wire/wire-sepa.conf +++ /dev/null @@ -1,15 +0,0 @@ -# This file is in the public domain. -# -# Configuration for SEPA wire plugin. - -[exchange-wire-sepa] -# Set to "YES" to activate the 'sepa' plugin. -ENABLE = NO - -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/exchange/wire/sepa.json - -[exchange-wire-outgoing-sepa] -# This section should contain the options required for making outgoing -# SEPA transfers. Not yet supported (need libebics). diff --git a/src/wire/wire-test.conf b/src/wire/wire-test.conf deleted file mode 100644 index 7ee217ee1..000000000 --- a/src/wire/wire-test.conf +++ /dev/null @@ -1,22 +0,0 @@ -# This file is in the public domain. -# -# Configuration for TEST wire plugin. -# -[exchange-wire-test] -# Set to "YES" to activate the 'test' plugin. -ENABLE = NO - -# This is the response we give out for the /wire request. It provides -# wallets with the bank information for transfers to the exchange. -TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/exchange/wire/test.json - -# We need to know the exchange's account number at the bank. -EXCHANGE_ACCOUNT_NUMBER = 2 - -# For accessing transfers, we need to know -# the URL of the bank (where the /history API is available). -# BANK_URL = https://bank.demo.taler.net/ - -# Authentication information for basic authentication -USERNAME = user -PASSWORD = pass diff --git a/src/wire/wire.c b/src/wire/wire.c index 58180b7b1..aeb8689e6 100644 --- a/src/wire/wire.c +++ b/src/wire/wire.c @@ -146,80 +146,4 @@ TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin) } -/** - * Closure of #check_for_wire. - */ -struct FindEnabledWireContext -{ - /** - * Configuration we are usign. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Callback to invoke. - */ - TALER_WIRE_EnabledCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; -}; - - -/** - * Check if @a section begins with "exchange-wire-", and if - * so if the "ENABLE" option is set to "YES". If both are - * true, call the callback from the context with the - * rest of the section name. - * - * @param cls our `struct FindEnabledWireContext` - * @param section name of a section in the configuration - */ -static void -check_for_wire (void *cls, - const char *section) -{ - struct FindEnabledWireContext *ctx = cls; - const char *name; - - if (0 != strncasecmp (section, - "exchange-wire-", - strlen ("exchange-wire-"))) - return; - if (GNUNET_YES != - GNUNET_CONFIGURATION_get_value_yesno (ctx->cfg, - section, - "ENABLE")) - return; - name = §ion[strlen ("exchange-wire-")]; - ctx->cb (ctx->cb_cls, - name); -} - - -/** - * Check which wire plugins are enabled in @a cfg and call @a cb for each one. - * - * @param cfg configuration to use - * @param cb callback to invoke - * @param cb_cls closure for @a cb - */ -void -TALER_WIRE_find_enabled (const struct GNUNET_CONFIGURATION_Handle *cfg, - TALER_WIRE_EnabledCallback cb, - void *cb_cls) -{ - struct FindEnabledWireContext ctx; - - ctx.cfg = cfg; - ctx.cb = cb; - ctx.cb_cls = cb_cls; - GNUNET_CONFIGURATION_iterate_sections (cfg, - &check_for_wire, - &ctx); -} - - /* end of wire.c */ diff --git a/src/wire/wire_helper.c b/src/wire/wire_helper.c new file mode 100644 index 000000000..34d40e11e --- /dev/null +++ b/src/wire/wire_helper.c @@ -0,0 +1,57 @@ +/* + This file is part of TALER + (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 wire/wire_helper.c + * @brief Helper functions for dealing with wire formats + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_wire_lib.h" + +/** + * Prefix of PAYTO URLs. + */ +#define PAYTO "payto://" + + +/** + * Obtain the payment method from a @a payto_url + * + * @param payto_url the URL to parse + * @return NULL on error (malformed @a payto_url) + */ +char * +TALER_WIRE_payto_get_method (const char *payto_url) +{ + const char *start; + const char *end; + + if (0 != strncmp (payto_url, + PAYTO, + strlen (PAYTO))) + return NULL; + start = &payto_url[strlen(PAYTO)]; + end = strchr (start, + (unsigned char) '/'); + if (NULL == end) + return NULL; + return GNUNET_strndup (start, + end - start); +} + +/* end of wire_helper.c */ -- cgit v1.2.3