diff options
Diffstat (limited to 'src')
104 files changed, 3995 insertions, 3236 deletions
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; @@ -1774,11 +1774,6 @@ struct WireCheckContext struct GNUNET_TIME_Absolute date; /** - * Wire method used for the transfer. - */ - const char *method; - - /** * Database transaction status. */ enum GNUNET_DB_QueryStatus qs; @@ -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(). */ @@ -51,11 +90,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. */ static struct TALER_EXCHANGEDB_Plugin *edb; @@ -104,11 +138,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. */ static struct TALER_WIRE_HistoryHandle *hh; @@ -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)) @@ -765,6 +842,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}", @@ -1267,6 +1355,167 @@ history_credit_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) +{ + 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. * * @param cls closure @@ -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 + <http://www.gnu.org/licenses/> +*/ +/** + * @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;i<h_len;i++) { - char *acc; - - acc = json_dumps (h[i].details.account_details, - JSON_COMPACT); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "H(%llu): %s%s (serial: %llu, subject: %s, to: %s)\n", (unsigned long long) i, @@ -409,9 +395,7 @@ print_expected (struct History *h, TALER_amount2s (&h[i].details.amount), (unsigned long long) h[i].row_id, h[i].details.wire_transfer_subject, - acc); - if (NULL != acc) - free (acc); + h[i].details.account_url); } } @@ -429,7 +413,7 @@ free_history (struct History *h, for (uint64_t off = 0;off<h_len;off++) { GNUNET_free (h[off].details.wire_transfer_subject); - json_decref (h[off].details.account_details); + GNUNET_free (h[off].details.account_url); } GNUNET_free_non_null (h); } @@ -500,8 +484,8 @@ check_result (struct InterpreterState *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_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;i<is->ip;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;off<h_len;off++) { GNUNET_free (h[off].details.wire_transfer_subject); - json_decref (h[off].details.account_details); + GNUNET_free (h[off].details.account_url); } GNUNET_free_non_null (h); } + /** * Log which history we expected. * @@ -178,10 +182,6 @@ print_expected (struct History *h, "Expected history:\n"); for (uint64_t i=0;i<h_len;i++) { - char *acc; - - acc = json_dumps (h[i].details.account_details, - JSON_COMPACT); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "H(%llu): %s%s (serial: %llu, subject: %s, to: %s)\n", (unsigned long long) i, @@ -189,12 +189,11 @@ print_expected (struct History *h, TALER_amount2s (&h[i].details.amount), (unsigned long long) h[i].row_id, h[i].details.wire_transfer_subject, - acc); - if (NULL != acc) - free (acc); + h[i].details.account_url); } } + /** * Build history of transactions matching the current * command in @a is. @@ -409,15 +408,10 @@ build_history (struct TALER_TESTING_Interpreter *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", - hs->bank_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 <gnunet/gnunet_curl_lib.h> #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;i<num_accounts;i++) + { + struct TALER_EXCHANGE_WireAccount *wa = &was[i]; + json_t *account; + struct GNUNET_JSON_Specification spec_account[] = { + GNUNET_JSON_spec_string ("url", &wa->url), + 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;i<num_fees;i++) - { - af[i].next = &af[i+1]; - if (GNUNET_OK != - parse_json_fees (&af[i], - json_array_get (fees, - i))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if ( (NULL != master_pub) && - (GNUNET_OK != - check_sig (&af[i], - wire_method, - master_pub)) ) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - af[num_fees - 1].next = NULL; - if (NULL != cb) - cb (cb_cls, - wire_method, - &af[0]); - } - } - return GNUNET_OK; -} - - /* end of exchange_api_wire.c */ diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index fe3877790..7ca74a227 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -36,9 +36,12 @@ #define WIRE_TEST 1 /** - * Is the configuration file is set to include wire format 'sepa'? + * 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_SEPA 1 +#define WIRE_EBICS 0 /** * Account number of the exchange at the bank. @@ -1539,12 +1542,15 @@ find_pk (const struct TALER_EXCHANGE_Keys *keys, } +#if LEGACY +/* Tests the *old* /wire API, the _modern_ testcase was adapted, + but little point in right now adapting the old testcase */ /** * 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 wire_method name of the wire method (i.e. "ebics") * @param fees fee structure for this method */ static void @@ -1556,9 +1562,9 @@ check_fee_cb (void *cls, struct Command *cmd = &is->commands[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 @@ -43,6 +43,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 <gnunet/gnunet_curl_lib.h> #include "exchange_api_handle.h" +#include "taler_wire_lib.h" #include "taler_testing_lib.h" struct WireState @@ -71,19 +72,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;i<accounts_len;i++) { - if (0 != TALER_amount_cmp (&fees->wire_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 + <http://www.gnu.org/licenses/> +*/ + +/** + * @file exchange-lib/testing_api_trait_json.c + * @brief offers JSON traits. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_handle.h" +#include "taler_signatures.h" +#include "taler_testing_lib.h" + +#define TALER_TESTING_TRAIT_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" @@ -81,49 +80,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. * * @param cmd command to extract trait from @@ -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", @@ -1049,6 +1052,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. + * * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ static int @@ -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" @@ -44,17 +46,13 @@ struct Plugin struct Plugin *prev; /** - * Type of the wireformat. - */ - char *type; - - /** * Pointer to the plugin. */ struct TALER_WIRE_Plugin *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? @@ -91,11 +140,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? */ static int delay; @@ -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,23 +187,31 @@ 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; @@ -165,6 +219,41 @@ shutdown_task (void *cls) /** + * 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 <http://www.gnu.org/licenses/> +*/ +/** + * @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;i<num_results;i++) { struct GNUNET_TIME_Absolute exp_date; - json_t *account_details; + char *account_details; struct TALER_ReservePublicKeyP reserve_pub; struct TALER_Amount remaining_balance; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_absolute_time ("expiration_date", &exp_date), - TALER_PQ_result_spec_json ("account_details", - &account_details), + GNUNET_PQ_result_spec_string ("account_details", + &account_details), GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", &reserve_pub), TALER_PQ_result_spec_amount ("current_balance", @@ -4674,7 +4672,7 @@ postgres_insert_reserve_closed (void *cls, 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) @@ -4684,7 +4682,7 @@ postgres_insert_reserve_closed (void *cls, GNUNET_PQ_query_param_auto_from_type (reserve_pub), TALER_PQ_query_param_absolute_time (&execution_date), GNUNET_PQ_query_param_auto_from_type (wtid), - TALER_PQ_query_param_json (receiver_account), + GNUNET_PQ_query_param_string (receiver_account), TALER_PQ_query_param_amount (amount_with_fee), TALER_PQ_query_param_amount (closing_fee), GNUNET_PQ_query_param_end @@ -4946,6 +4944,7 @@ postgres_gc (void *cls) long_ago = GNUNET_TIME_absolute_subtract (now, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 10)); + /* FIXME: use GNUNET_PQ_connect_with_cfg instead? */ conn = GNUNET_PQ_connect (pc->connection_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 <jansson.h> #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_db_lib.h> @@ -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 @@ -62,18 +62,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. */ #define TALER_SIGNATURE_MASTER_WIRE_FEES 1028 @@ -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 @@ -26,6 +26,16 @@ /** + * 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. * * @param cfg configuration to use @@ -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 <gnunet/gnunet_util_lib.h> -#include <jansson.h> #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; }; @@ -161,6 +160,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 * payment, but SEPA only supports 0.01 EUR. This function would @@ -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 <http://www.gnu.org/licenses/> +*/ +/** + * @file json/json_wire.c + * @brief helper functions to generate or check /wire replies + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#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 <http://www.gnu.org/licenses/> +*/ +/** + * @file util/crypto_wire.c + * @brief functions for making and verifying /wire account signatures + * @author Christian Grothoff <christian@grothoff.org> + */ +#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/plugin_wire_sepa.c b/src/wire-plugins/plugin_wire_ebics.c index 416acac7c..9aad9df08 100644 --- a/src/wire/plugin_wire_sepa.c +++ b/src/wire-plugins/plugin_wire_ebics.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016, 2017 GNUnet e.V. & Inria + 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 @@ -15,7 +15,7 @@ */ /** - * @file plugin_wire_sepa.c + * @file plugin_wire_ebics.c * @brief wire plugin for transfers using SEPA/EBICS * @author Florian Dold * @author Christian Grothoff @@ -31,7 +31,7 @@ * Type of the "cls" argument given to each of the functions in * our API. */ -struct SepaClosure +struct EbicsClosure { /** @@ -39,6 +39,11 @@ struct SepaClosure */ char *currency; + /** + * Configuration we use to lookup account information. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + }; @@ -54,10 +59,10 @@ struct SepaClosure * #GNUNET_SYSERR if the amount or currency was invalid */ static int -sepa_amount_round (void *cls, - struct TALER_Amount *amount) +ebics_amount_round (void *cls, + struct TALER_Amount *amount) { - struct SepaClosure *sc = cls; + struct EbicsClosure *sc = cls; uint32_t delta; if (NULL == sc->currency) @@ -81,7 +86,7 @@ sepa_amount_round (void *cls, /** * Entry in the country table. */ -struct table_entry +struct CountryTableEntry { /** * 2-Character international country code. @@ -100,7 +105,7 @@ struct table_entry /** * List of country codes. */ -static const struct table_entry country_table[] = +static const struct CountryTableEntry country_table[] = { { "AE", "U.A.E." }, { "AF", "Afghanistan" }, @@ -259,8 +264,8 @@ static int cmp_country_code (const void *ptr1, const void *ptr2) { - const struct table_entry *cc1 = ptr1; - const struct table_entry *cc2 = ptr2; + const struct CountryTableEntry *cc1 = ptr1; + const struct CountryTableEntry *cc2 = ptr2; return strncmp (cc1->code, cc2->code, @@ -280,20 +285,21 @@ validate_iban (const char *iban) { char cc[2]; char ibancpy[35]; - struct table_entry cc_entry; + struct CountryTableEntry 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; + unsigned int i; + unsigned int j; len = strlen (iban); if (len > 34) { - GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "IBAN number too long to be valid\n"); return GNUNET_NO; } strncpy (cc, iban, 2); @@ -305,11 +311,14 @@ validate_iban (const char *iban) if (NULL == bsearch (&cc_entry, country_table, - sizeof (country_table) / sizeof (struct table_entry), - sizeof (struct table_entry), + sizeof (country_table) / sizeof (struct CountryTableEntry), + sizeof (struct CountryTableEntry), &cmp_country_code)) { - GNUNET_break_op (0); + 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); @@ -354,309 +363,89 @@ validate_iban (const char *iban) GNUNET_free (nbuf); if (1 == remainder) return GNUNET_YES; - GNUNET_break_op (0); /* checksum wrong */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "IBAN checksum wrong\n"); return GNUNET_NO; } /** - * Compute purpose for signing. - * - * @param sepa_name name of the account holder - * @param iban bank account number in IBAN format - * @param bic bank identifier - * @param[out] wsd purpose to be signed + * Information about an account extracted from a payto://-URL. */ -static void -compute_purpose (const char *sepa_name, - const char *iban, - const char *bic, - struct TALER_MasterWireDetailsPS *wsd) +struct Account { - struct GNUNET_HashContext *hc; - - wsd->purpose.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() - }; + /** + * The IBAN number. + */ + char *iban; - 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 + * Parse payto:// account URL (only account information, + * wire subject and amount are ignored). * - * @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 + * @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 -sepa_wire_validate (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg) +parse_payto (const char *account_url, + struct Account *account) { - 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")) + const char *q; + char *result; + +#define PREFIX "payto://sepa/" + if (0 != strncasecmp (account_url, + PREFIX, + strlen (PREFIX))) + return TALER_EC_PAYTO_WRONG_METHOD; + iban = &account_url[strlen (PREFIX)]; + q = strchr (iban, + '?'); + if (NULL != q) { - GNUNET_asprintf (emsg, - "Transfer type `%s' invalid for SEPA wire plugin\n", - type); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE; + result = GNUNET_strndup (iban, + q - iban); } - if (1 != validate_iban (iban)) + else { - GNUNET_asprintf (emsg, - "IBAN `%s' invalid\n", - iban); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER; + result = GNUNET_strdup (iban); } - /* FIXME: don't parse again, integrate properly... */ if (GNUNET_OK != - verify_wire_sepa_signature_ok (wire, - master_pub)) + validate_iban (result)) { - GNUNET_asprintf (emsg, - "Signature using public key `%s' invalid\n", - TALER_B2S (master_pub)); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; + GNUNET_free (result); + return TALER_EC_PAYTO_MALFORMED; } - 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)) + if (NULL != account) { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - account_name, - "SEPA_RESPONSE_FILE"); - return NULL; + account->iban = result; } - ret = json_load_file (sepa_wire_file, - JSON_REJECT_DUPLICATES, - &err); - if (NULL == ret) + else { - 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; + GNUNET_free (result); } - 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; + return TALER_EC_NONE; } /** - * Sign wire transfer details in the plugin-specific format. + * Check if the given payto:// URL is correctly formatted for this plugin * - * @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 + * @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 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) +static enum TALER_ErrorCode +ebics_wire_validate (void *cls, + const char *account_url) { - 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; + return parse_payto (account_url, + NULL); } @@ -673,13 +462,14 @@ sepa_sign_wire_details (void *cls, * @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) +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; @@ -694,7 +484,7 @@ sepa_prepare_wire_transfer (void *cls, * @param pth preparation to cancel */ static void -sepa_prepare_wire_transfer_cancel (void *cls, +ebics_prepare_wire_transfer_cancel (void *cls, struct TALER_WIRE_PrepareHandle *pth) { GNUNET_break (0); // FIXME: not implemented @@ -712,11 +502,11 @@ sepa_prepare_wire_transfer_cancel (void *cls, * @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) +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; @@ -736,8 +526,8 @@ sepa_execute_wire_transfer (void *cls, * @param eh execution to cancel */ static void -sepa_execute_wire_transfer_cancel (void *cls, - struct TALER_WIRE_ExecuteHandle *eh) +ebics_execute_wire_transfer_cancel (void *cls, + struct TALER_WIRE_ExecuteHandle *eh) { GNUNET_break (0); // FIXME: not implemented } @@ -754,6 +544,8 @@ sepa_execute_wire_transfer_cancel (void *cls, * (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)`. @@ -764,13 +556,14 @@ sepa_execute_wire_transfer_cancel (void *cls, * @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) +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; @@ -784,8 +577,8 @@ sepa_get_history (void *cls, * @param whh operation to cancel */ static void -sepa_get_history_cancel (void *cls, - struct TALER_WIRE_HistoryHandle *whh) +ebics_get_history_cancel (void *cls, + struct TALER_WIRE_HistoryHandle *whh) { GNUNET_break (0); } @@ -843,6 +636,8 @@ timeout_reject (void *cls) * 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 @@ -850,11 +645,12 @@ timeout_reject (void *cls) * @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) +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; @@ -881,8 +677,8 @@ sepa_reject_transfer (void *cls, * @return closure of the callback of the operation */ static void * -sepa_reject_transfer_cancel (void *cls, - struct TALER_WIRE_RejectHandle *rh) +ebics_reject_transfer_cancel (void *cls, + struct TALER_WIRE_RejectHandle *rh) { void *ret = rh->rej_cb_cls; @@ -893,63 +689,60 @@ sepa_reject_transfer_cancel (void *cls, /** - * Initialize sepa-wire subsystem. + * Initialize ebics-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) +libtaler_plugin_wire_ebics_init (void *cls) { struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct SepaClosure *sc; + struct EbicsClosure *sc; struct TALER_WIRE_Plugin *plugin; - sc = GNUNET_new (struct SepaClosure); - if (NULL != cfg) + sc = GNUNET_new (struct EbicsClosure); + sc->cfg = cfg; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler", + "CURRENCY", + &sc->currency)) { - 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; - } + 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; + 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 Sepa wire subsystem. + * Shutdown Ebics wire subsystem. * * @param cls a `struct TALER_WIRE_Plugin` * @return NULL (always) */ void * -libtaler_plugin_wire_sepa_done (void *cls) +libtaler_plugin_wire_ebics_done (void *cls) { struct TALER_WIRE_Plugin *plugin = cls; - struct SepaClosure *sc = plugin->cls; + struct EbicsClosure *sc = plugin->cls; GNUNET_free_non_null (sc->currency); GNUNET_free (sc); @@ -957,4 +750,4 @@ libtaler_plugin_wire_sepa_done (void *cls) return NULL; } -/* end of plugin_wire_sepa.c */ +/* end of plugin_wire_ebics.c */ diff --git a/src/wire/plugin_wire_test.c b/src/wire-plugins/plugin_wire_taler-bank.c index 11feaea7e..2d2235343 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire-plugins/plugin_wire_taler-bank.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 @@ -15,12 +15,13 @@ */ /** - * @file plugin_wire_test.c - * @brief plugin for the "test" wire method + * @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 <gnunet/gnunet_curl_lib.h> @@ -29,10 +30,15 @@ #include <microhttpd.h> /** + * 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 TestClosure +struct TalerBankClosure { /** @@ -41,16 +47,6 @@ struct TestClosure 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; @@ -61,16 +57,15 @@ struct TestClosure struct GNUNET_CURL_RescheduleContext *rc; /** - * Number of the account that the exchange has at the bank for - * transfers. + * Configuration we use to lookup account information. */ - unsigned long long exchange_account_no; + struct GNUNET_CONFIGURATION_Handle *cfg; }; /** - * Handle returned by #test_prepare_wire_transfer. + * Handle returned by #taler_bank_prepare_wire_transfer. */ struct TALER_WIRE_PrepareHandle { @@ -81,14 +76,25 @@ struct TALER_WIRE_PrepareHandle struct GNUNET_SCHEDULER_Task *task; /** - * Test closure we run in. + * 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. */ - struct TestClosure *tc; + char *origin_account_url; /** - * Wire data for the transfer. + * Which account should be credited? */ - json_t *wire; + char *destination_account_url; /** * Base URL to use for the exchange. @@ -120,7 +126,7 @@ struct TALER_WIRE_PrepareHandle /** - * Handle returned by #test_execute_wire_transfer. + * Handle returned by #taler_bank_execute_wire_transfer. */ struct TALER_WIRE_ExecuteHandle { @@ -142,7 +148,6 @@ struct TALER_WIRE_ExecuteHandle }; - /** * 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 @@ -155,10 +160,10 @@ struct TALER_WIRE_ExecuteHandle * #GNUNET_SYSERR if the amount or currency was invalid */ static int -test_amount_round (void *cls, - struct TALER_Amount *amount) +taler_bank_amount_round (void *cls, + struct TALER_Amount *amount) { - struct TestClosure *tc = cls; + struct TalerBankClosure *tc = cls; uint32_t delta; if (NULL == tc->currency) @@ -174,7 +179,7 @@ test_amount_round (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - /* 'test' method supports 1/100 of the unit currency, i.e. 0.01 CUR */ + /* '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; @@ -184,209 +189,99 @@ test_amount_round (void *cls, /** - * Compute purpose for signing. - * - * @param account number of the account - * @param bank_url URL of the bank - * @param[out] wsd purpose to be signed + * Information about an account extracted from a payto://-URL. */ -static void -compute_purpose (uint64_t account, - const char *bank_url, - struct TALER_MasterWireDetailsPS *wsd) +struct Account { - 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); -} + /** + * Hostname of the bank (possibly including port). + */ + char *hostname; + + /** + * Bank account number. + */ + unsigned long long no; +}; /** - * 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. + * Parse payto:// account URL (only account information, + * wire subject and amount are ignored). * - * @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 + * @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 -test_wire_validate (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg) +parse_payto (const char *account_url, + struct Account *r_account) { - 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)) ) + 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) '?'))) { - 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; + 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); } - compute_purpose (account_no, - bank_url, - &wsd); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (sig_s, - strlen (sig_s), - &sig, - sizeof (sig))) + else { - GNUNET_break (0); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; + if (1 != sscanf (account, + "%llu", + &no)) + return TALER_EC_PAYTO_MALFORMED; } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_TEST_DETAILS, - &wsd.purpose, - &sig.eddsa_signature, - &master_pub->eddsa_pub)) + if (no > MAX_ACCOUNT_NO) + return TALER_EC_PAYTO_MALFORMED; + if (NULL != r_account) { - GNUNET_asprintf (emsg, - "Signature using public key `%s' invalid\n", - TALER_B2S (master_pub)); - return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE; + r_account->hostname = GNUNET_strndup (hostname, + account - hostname); + r_account->no = no; } return TALER_EC_NONE; } /** - * Obtain wire transfer details in the plugin-specific format - * from the configuration. + * Check if the given payto:// URL is correctly formatted. * - * @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 + * @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 json_t * -test_get_wire_details (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *account_name) +static enum TALER_ErrorCode +taler_bank_wire_validate (void *cls, + const char *account_url) { - struct TestClosure *tc = cls; - char *test_wire_file; - json_error_t err; - json_t *ret; - char *emsg; + (void) cls; - /* 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; + return parse_payto (account_url, + NULL); } @@ -407,9 +302,15 @@ struct BufFormatP */ struct TALER_AmountNBO amount; - /* followed by serialized 'wire' JSON data (0-terminated) */ + /* followed by 0-terminated origin account URL */ - /* followed by 0-terminated base 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 @@ -423,12 +324,14 @@ GNUNET_NETWORK_STRUCT_END * @param pth preparation to cancel */ static void -test_prepare_wire_transfer_cancel (void *cls, - struct TALER_WIRE_PrepareHandle *pth) +taler_bank_prepare_wire_transfer_cancel (void *cls, + struct TALER_WIRE_PrepareHandle *pth) { if (NULL != pth->task) GNUNET_SCHEDULER_cancel (pth->task); - json_decref (pth->wire); + 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); } @@ -444,52 +347,111 @@ static void do_prepare (void *cls) { struct TALER_WIRE_PrepareHandle *pth = cls; - char *wire_enc; - size_t len_w; + 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' */ - wire_enc = json_dumps (pth->wire, - JSON_COMPACT | JSON_SORT_KEYS); - if (NULL == wire_enc) + 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) { - GNUNET_break (0); - pth->ptc (pth->ptc_cls, - NULL, - 0); - test_prepare_wire_transfer_cancel (NULL, - pth); - return; + 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; } - 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]; + 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)], - wire_enc, - len_w); - memcpy (&buf[sizeof (struct BufFormatP) + len_w], + 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)); } - free (wire_enc); /* not using GNUNET_free(), - as this one is allocated by libjansson */ - test_prepare_wire_transfer_cancel (NULL, - pth); + 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; } @@ -501,7 +463,9 @@ do_prepare (void *cls) * the work. * * @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 wtid wire transfer identifier to use @@ -510,31 +474,84 @@ do_prepare (void *cls) * @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) +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 TestClosure *tc = cls; + struct TalerBankClosure *tc = cls; struct TALER_WIRE_PrepareHandle *pth; - char *emsg; + 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 != - test_wire_validate (tc, - wire, - NULL, - &emsg)) + 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_break_op (0); - GNUNET_free (emsg); + 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->wire = json_incref ((json_t *) wire); + 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; @@ -599,61 +616,6 @@ execute_cb (void *cls, /** - * 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 @@ -664,22 +626,24 @@ test_sign_wire_details (void *cls, * @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) +taler_bank_execute_wire_transfer (void *cls, + const char *buf, + size_t buf_size, + TALER_WIRE_ConfirmationCallback cc, + void *cc_cls) { - struct TestClosure *tc = cls; + struct TalerBankClosure *tc = cls; struct TALER_WIRE_ExecuteHandle *eh; - json_t *wire; - json_error_t error; struct TALER_Amount amount; - json_int_t account_no; + struct Account origin_account; + struct Account destination_account; struct BufFormatP bf; - char *emsg; - const char *json_s; 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) @@ -694,37 +658,76 @@ test_execute_wire_transfer (void *cls, 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) + 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; } - 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)) + if (0 != strcasecmp (origin_account.hostname, + destination_account.hostname)) { GNUNET_break (0); return NULL; @@ -736,17 +739,16 @@ test_execute_wire_transfer (void *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, + origin_account.hostname, + &auth, exchange_base_url, wire_s, &amount, - (uint64_t) tc->exchange_account_no, - (uint64_t) account_no, + (uint64_t) origin_account.no, + (uint64_t) destination_account.no, &execute_cb, eh); GNUNET_free (wire_s); - json_decref (wire); if (NULL == eh->aaih) { GNUNET_break (0); @@ -770,8 +772,8 @@ test_execute_wire_transfer (void *cls, * @param eh execution to cancel */ static void -test_execute_wire_transfer_cancel (void *cls, - struct TALER_WIRE_ExecuteHandle *eh) +taler_bank_execute_wire_transfer_cancel (void *cls, + struct TALER_WIRE_ExecuteHandle *eh) { TALER_BANK_admin_add_incoming_cancel (eh->aaih); GNUNET_free (eh); @@ -779,7 +781,7 @@ test_execute_wire_transfer_cancel (void *cls, /** - * Handle for a #test_get_history() request. + * Handle for a #taler_bank_get_history() request. */ struct TALER_WIRE_HistoryHandle { @@ -799,10 +801,35 @@ struct TALER_WIRE_HistoryHandle */ 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` @@ -839,7 +866,8 @@ bhist_cb (void *cls, wd.amount = details->amount; wd.execution_date = details->execution_date; subject = GNUNET_strdup (details->wire_transfer_subject); - space = strchr (subject, (int) ' '); + space = strchr (subject, + (unsigned char) ' '); if (NULL != space) { /* Space separates the actual wire transfer subject from the @@ -867,8 +895,7 @@ bhist_cb (void *cls, wd.wtid_s = NULL; } GNUNET_free (subject); - wd.account_details = details->account_details; - + wd.account_url = details->account_url; if ( (NULL != whh->hres_cb) && (GNUNET_OK != whh->hres_cb (whh->hres_cb_cls, @@ -878,7 +905,7 @@ bhist_cb (void *cls, sizeof (bserial_id), &wd)) ) whh->hres_cb = NULL; - break; + return; /* do NOT yet clean up! */ } case MHD_HTTP_NO_CONTENT: if (NULL != whh->hres_cb) @@ -888,7 +915,6 @@ bhist_cb (void *cls, NULL, 0, NULL); - GNUNET_free (whh); break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -902,9 +928,11 @@ bhist_cb (void *cls, NULL, 0, NULL); - GNUNET_free (whh); break; } + whh->hh = NULL; + taler_bank_get_history_cancel (NULL, + whh); } @@ -915,12 +943,14 @@ bhist_cb (void *cls, * 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 + * positive @a num_results) or from the lataler_bank committed transfers * (with negative @a num_results). * * @param cls the @e cls of this struct with the plugin-specific state + * @param account_section specifies the configuration section which + * identifies the account for which we should get the history * @param direction what kinds of wire transfers should be returned - * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive + * @param start_off 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; @@ -929,18 +959,20 @@ bhist_cb (void *cls, * @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) +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 TestClosure *tc = 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) { @@ -956,11 +988,12 @@ test_get_history (void *cls, (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)); + "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, + * wrote a wrong value to it. Instead of completely stopping to work, * we just scan from the beginning. */ start_off = NULL; } @@ -973,14 +1006,28 @@ test_get_history (void *cls, 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, - tc->bank_url, - &tc->auth, - (uint64_t) tc->exchange_account_no, + account.hostname, + &whh->auth, + (uint64_t) account.no, direction, start_row, num_results, @@ -989,30 +1036,17 @@ test_get_history (void *cls, if (NULL == whh->hh) { GNUNET_break (0); - GNUNET_free (whh); + taler_bank_get_history_cancel (NULL, + whh); + GNUNET_free (account.hostname); return NULL; } - + GNUNET_free (account.hostname); 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 @@ -1031,6 +1065,11 @@ struct TALER_WIRE_RejectHandle * Handle for the reject operation. */ struct TALER_BANK_RejectHandle *brh; + + /** + * Authentication information to use. + */ + struct TALER_BANK_AuthenticationData auth; }; @@ -1059,6 +1098,32 @@ reject_cb (void *cls, /** + * Cancel ongoing reject operation. Note that the rejection may still + * proceed. Basically, if this function is called, the rejection may + * have happened or not. This function is usually used during shutdown + * or system upgrades. At a later point, the application must call + * @e reject_transfer again for this wire transfer, unless the + * @e get_history shows that the wire transfer no longer exists. + * + * @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). @@ -1069,6 +1134,8 @@ reject_cb (void *cls, * 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 @@ -1076,15 +1143,17 @@ reject_cb (void *cls, * @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) +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 TestClosure *tc = 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) { @@ -1092,178 +1161,109 @@ test_reject_transfer (void *cls, 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, - tc->bank_url, - &tc->auth, - (uint64_t) tc->exchange_account_no, + account.hostname, + &rh->auth, + (uint64_t) account.no, GNUNET_ntohll (*rowid_b64), &reject_cb, rh); if (NULL == rh->brh) { - GNUNET_free (rh); + (void) taler_bank_reject_transfer_cancel (tc, + rh); + GNUNET_free (account.hostname); return NULL; } + GNUNET_free (account.hostname); 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. + * Initialize taler_bank-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) +libtaler_plugin_wire_taler_bank_init (void *cls) { struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct TestClosure *tc; + struct TalerBankClosure *tc; struct TALER_WIRE_Plugin *plugin; - char *user; - char *pass; - tc = GNUNET_new (struct TestClosure); - if (NULL != cfg) + tc = GNUNET_new (struct TalerBankClosure); + tc->cfg = cfg; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler", + "CURRENCY", + &tc->currency)) { - 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; - } + 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->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; + 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 Test wire subsystem. + * Shutdown taler-bank wire subsystem. * * @param cls a `struct TALER_WIRE_Plugin` * @return NULL (always) */ void * -libtaler_plugin_wire_test_done (void *cls) +libtaler_plugin_wire_taler_bank_done (void *cls) { struct TALER_WIRE_Plugin *plugin = cls; - struct TestClosure *tc = plugin->cls; + struct TalerBankClosure *tc = plugin->cls; if (NULL != tc->ctx) { @@ -1275,28 +1275,10 @@ libtaler_plugin_wire_test_done (void *cls) 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 */ +/* end of plugin_wire_taler-bank.c */ diff --git a/src/wire/plugin_wire_template.c b/src/wire-plugins/plugin_wire_template.c index 06e70c2a8..6fadb88ce 100644 --- a/src/wire/plugin_wire_template.c +++ b/src/wire-plugins/plugin_wire_template.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016 GNUnet e.V. + 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 @@ -33,14 +33,14 @@ struct TemplateClosure { /** - * URL of the bank for sending funds to the bank. + * Which currency do we support? */ - char *bank_url; + char *currency; /** - * Which currency do we support? + * Which configuration do we use to lookup accounts? */ - char *currency; + struct GNUNET_CONFIGURATION_Handle *cfg; }; @@ -74,42 +74,17 @@ template_amount_round (void *cls, /** - * 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 + * 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 */ static enum TALER_ErrorCode template_wire_validate (void *cls, - const json_t *wire, - const struct TALER_MasterPublicKeyP *master_pub, - char **emsg) + const char *account_url) { - GNUNET_asprintf (emsg, - "Not implemented"); + GNUNET_break (0); return TALER_EC_NOT_IMPLEMENTED; } @@ -118,7 +93,9 @@ template_wire_validate (void *cls, * 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 the exchange (for tracking) * @param wtid wire transfer identifier to use @@ -128,7 +105,8 @@ template_wire_validate (void *cls, */ static struct TALER_WIRE_PrepareHandle * template_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, @@ -178,28 +156,6 @@ template_execute_wire_transfer (void *cls, /** - * 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 @@ -230,6 +186,8 @@ template_execute_wire_transfer_cancel (void *cls, * (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)`. @@ -241,6 +199,7 @@ template_execute_wire_transfer_cancel (void *cls, */ 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, @@ -268,6 +227,59 @@ template_get_history_cancel (void *cls, /** + * 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 @@ -281,19 +293,7 @@ libtaler_plugin_wire_template_init (void *cls) 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; - } + tc->cfg = cfg; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "taler", @@ -303,16 +303,14 @@ libtaler_plugin_wire_template_init (void *cls) 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->method = "FIXME-REPLACE-BY-METHOD"; 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; @@ -320,6 +318,8 @@ libtaler_plugin_wire_template_init (void *cls) 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; } @@ -336,7 +336,6 @@ 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); 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file wire/test_ebics_wireformat.c + * @brief Tests for SEPA format validation by the EBICS plugin + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ +#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/test_wire_plugin.c b/src/wire-plugins/test_wire_plugin.c index f5dba01aa..0e149dde7 100644 --- a/src/wire/test_wire_plugin.c +++ b/src/wire-plugins/test_wire_plugin.c @@ -24,7 +24,6 @@ #include "taler_wire_lib.h" #include "taler_wire_plugin.h" #include <gnunet/gnunet_json_lib.h> -#include <jansson.h> /** @@ -38,11 +37,6 @@ struct TestBlock { 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; @@ -54,7 +48,7 @@ struct TestBlock { /** * Currency to give to the plugin. - */ + */ const char *currency; }; @@ -65,36 +59,24 @@ struct TestBlock { */ static struct TestBlock tests[] = { { - .plugin_name = "sepa", - .json_proto = "{ \"type\":\"sepa\", \"iban\":\"DE67830654080004822650\", \"name\":\"GNUnet e.V.\", \"bic\":\"GENODEF1SLR\" }", + .plugin_name = "ebics", .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 }", + .plugin_name = "taler_bank", .round_in = "KUDOS:0.123456", .round_out = "KUDOS:0.12", .currency = "KUDOS" }, { - NULL, NULL, NULL, NULL, 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; @@ -105,73 +87,19 @@ static struct GNUNET_CONFIGURATION_Handle *cfg; * * @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 TALER_WIRE_Plugin *plugin) { 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)); @@ -214,12 +142,9 @@ 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", @@ -228,13 +153,8 @@ main (int argc, 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++) + for (unsigned int i=0;NULL != (test = &tests[i])->plugin_name;i++) { GNUNET_CONFIGURATION_set_value_string (cfg, "taler", @@ -243,10 +163,7 @@ main (int argc, 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); + ret = run_test (test, plugin); TALER_WIRE_plugin_unload (plugin); if (GNUNET_OK != ret) { diff --git a/src/wire/test_wire_plugin.conf b/src/wire-plugins/test_wire_plugin.conf index 90b4f0749..d1d699b0f 100644 --- a/src/wire/test_wire_plugin.conf +++ b/src/wire-plugins/test_wire_plugin.conf @@ -1,21 +1,25 @@ # This file is in the public domain. # -[test] +[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. -TEST_RESPONSE_FILE = test_wire_plugin_test.json +WIRE_JSON = test_wire_plugin_test.json -[sepa] +# 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. -SEPA_RESPONSE_FILE = test_wire_plugin_sepa.json +WIRE_JSON = test_wire_plugin_sepa.json +# Which wire plugin should we used to access the account? +PLUGIN = ebics -[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_transactions_test.c b/src/wire-plugins/test_wire_plugin_transactions_taler-bank.c index ccfde1b40..06aef8aa1 100644 --- a/src/wire/test_wire_plugin_transactions_test.c +++ b/src/wire-plugins/test_wire_plugin_transactions_taler-bank.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015, 2016, 2017 GNUnet e.V. and Inria + (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 @@ -14,8 +14,8 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file wire/test_wire_plugin_transactions_test.c - * @brief Tests performing actual transactions with the TEST wire plugin against FAKEBANK + * @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" @@ -24,7 +24,6 @@ #include "taler_wire_plugin.h" #include "taler_fakebank_lib.h" #include <gnunet/gnunet_json_lib.h> -#include <jansson.h> /** @@ -35,21 +34,14 @@ /** - * Input for the wire transfer details. + * Destination account to use. */ -static const char *json_proto = - "{ \"type\":\"test\", \"bank_url\":\"http://localhost:8088/\", \"account_number\":42 }"; - +static const char *dest_account = "payto://x-taler-bank/localhost:8088/42"; /** - * Private key used to sign wire details. + * Origin account, section in the configuration file. */ -static struct TALER_MasterPrivateKeyP priv_key; - -/** - * Public key matching #priv_key. - */ -static struct TALER_MasterPublicKeyP pub_key; +static const char *my_account = "account-test"; /** * Our configuration. @@ -250,6 +242,7 @@ confirmation_cb (void *cls, } serial_target = serial_id; hh = plugin->get_history (plugin->cls, + my_account, TALER_BANK_DIRECTION_BOTH, NULL, 0, 5, @@ -294,7 +287,6 @@ prepare_cb (void *cls, static void run (void *cls) { - json_t *wire; struct TALER_Amount amount; GNUNET_SCHEDULER_add_shutdown (&do_shutdown, @@ -308,18 +300,15 @@ run (void *cls) 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, + my_account, + dest_account, &amount, "https://exchange.net/", &wtid, &prepare_cb, NULL); - json_decref (wire); } @@ -327,23 +316,16 @@ 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); + "test_wire_plugin_transactions_taler-bank.conf")); global_ret = GNUNET_OK; plugin = TALER_WIRE_plugin_load (cfg, - "test"); + "taler_bank"); GNUNET_assert (NULL != plugin); GNUNET_SCHEDULER_run (&run, NULL); @@ -353,4 +335,4 @@ main (int argc, return 0; } -/* end of test_wire_plugin_transactions_test.c */ +/* 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/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 <http://www.gnu.org/licenses/> -*/ - -/** - * @file wire/test_sepa_wireformat.c - * @brief Tests for JSON SEPA format validation - * @author Sree Harsha Totakura <sreeharsha@totakura.in> - */ - -#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_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.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 <http://www.gnu.org/licenses/> +*/ + +/** + * @file wire/wire_helper.c + * @brief Helper functions for dealing with wire formats + * @author Christian Grothoff <christian@grothoff.org> + */ +#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 */ |