aboutsummaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange')
-rw-r--r--src/exchange/.gitignore1
-rw-r--r--src/exchange/exchange.conf15
-rw-r--r--src/exchange/taler-exchange-aggregator.c319
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c12
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c17
-rw-r--r--src/exchange/taler-exchange-httpd_track_transfer.c18
-rw-r--r--src/exchange/taler-exchange-httpd_validation.c310
-rw-r--r--src/exchange/taler-exchange-httpd_validation.h9
-rw-r--r--src/exchange/taler-exchange-httpd_wire.c17
-rw-r--r--src/exchange/taler-exchange-httpd_wire.h4
-rw-r--r--src/exchange/taler-exchange-wirewatch.c203
-rw-r--r--src/exchange/test-taler-exchange-aggregator-postgres.conf34
-rw-r--r--src/exchange/test-taler-exchange-wirewatch-postgres.conf30
-rw-r--r--src/exchange/test_taler_exchange_aggregator.c26
-rw-r--r--src/exchange/test_taler_exchange_httpd.conf49
-rw-r--r--src/exchange/test_taler_exchange_httpd.data17
-rwxr-xr-xsrc/exchange/test_taler_exchange_httpd.sh4
-rw-r--r--src/exchange/test_taler_exchange_wirewatch.c3
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)