summaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2018-04-02 14:24:45 +0200
committerChristian Grothoff <christian@grothoff.org>2018-04-02 14:29:44 +0200
commitcb55c1a3af9f56a6da38e5589e72df0b70d355b1 (patch)
tree5f9a3af7d9073249f77ce56c690844a6cb27c3e7 /src/exchange
parent7a20062bafed42f937c5388aed09042aad7014c0 (diff)
downloadexchange-cb55c1a3af9f56a6da38e5589e72df0b70d355b1.tar.gz
exchange-cb55c1a3af9f56a6da38e5589e72df0b70d355b1.tar.bz2
exchange-cb55c1a3af9f56a6da38e5589e72df0b70d355b1.zip
Changing configuration structure to enable multiple accounts.
This change enables using multiple wire plugins at the same time. Also, we now distinguish between the wire plugin (i.e. EBICS or taler_bank) and the wire method (i.e. SEPA or x-taler-bank) that the wire plugin is implementing. The "taler-bank" wire method was renamed from "test" to "x-taler-bank". This also changes the format of the /wire response of the exchange, as we now need to return multiple accounts. Note that wire fees are specified per wire method, not per wire account. taler-exchange-keyup now automatically signs all of the /wire responses in the location specified by the configuration. Account identification in wire plugins was changed to use payto://-URLs instead of method-specific JSON fields. Signing and validation of /wire responses was moved from each wire plugin to a generic validation method in libtalerutil (crypto) or libtalerjson (for JSON-formatted inputs). Convenience methods were added to generate JSON for wire accounts (salting, signing). Various section and option names were adjusted to streamline the configuration and make it more consistent overall. Documentation was updated as well.
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)