diff options
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/.gitignore | 1 | ||||
-rw-r--r-- | src/exchange/exchange.conf | 15 | ||||
-rw-r--r-- | src/exchange/taler-exchange-aggregator.c | 319 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_deposit.c | 12 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_responses.c | 17 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_track_transfer.c | 18 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_validation.c | 310 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_validation.h | 9 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_wire.c | 17 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_wire.h | 4 | ||||
-rw-r--r-- | src/exchange/taler-exchange-wirewatch.c | 203 | ||||
-rw-r--r-- | src/exchange/test-taler-exchange-aggregator-postgres.conf | 34 | ||||
-rw-r--r-- | src/exchange/test-taler-exchange-wirewatch-postgres.conf | 30 | ||||
-rw-r--r-- | src/exchange/test_taler_exchange_aggregator.c | 26 | ||||
-rw-r--r-- | src/exchange/test_taler_exchange_httpd.conf | 49 | ||||
-rw-r--r-- | src/exchange/test_taler_exchange_httpd.data | 17 | ||||
-rwxr-xr-x | src/exchange/test_taler_exchange_httpd.sh | 4 | ||||
-rw-r--r-- | src/exchange/test_taler_exchange_wirewatch.c | 3 |
18 files changed, 665 insertions, 423 deletions
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) |