aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-03-03 20:31:29 +0100
committerChristian Grothoff <christian@grothoff.org>2017-03-03 20:31:29 +0100
commitf406f96129766c144c1531dc853969664f410d8c (patch)
tree6fdbae6ed117d3057ae9101ab4f799f5e3354d59
parent364abbaea1383bd7d8311269d596bdc3c1d4b591 (diff)
implement #4929: add wire transfer fee to /wire (but not yet charged by aggregator)
-rw-r--r--.gitignore2
-rw-r--r--src/exchange-lib/test_exchange_api.conf27
-rw-r--r--src/exchange-tools/Makefile.am1
-rw-r--r--src/exchange-tools/taler-exchange-keyup.c197
-rw-r--r--src/exchange/taler-exchange-httpd_validation.c50
-rw-r--r--src/exchange/taler-exchange-httpd_wire.c73
-rw-r--r--src/exchange/taler-exchange-httpd_wire.h10
-rw-r--r--src/exchangedb/Makefile.am14
-rw-r--r--src/exchangedb/exchangedb_denomkeys.c9
-rw-r--r--src/exchangedb/exchangedb_fees.c2
-rw-r--r--src/exchangedb/test_exchangedb_fees.c104
-rw-r--r--src/include/taler_exchangedb_lib.h78
-rw-r--r--src/include/taler_signatures.h38
13 files changed, 578 insertions, 27 deletions
diff --git a/.gitignore b/.gitignore
index 6a6c27d38..0896a4c67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,9 +35,11 @@ src/bank-lib/test_bank_api
src/bank-lib/test_bank_api_with_fakebank
src/exchange-lib/test_exchange_api
src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/live-keys/
+src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/wirefees/
src/exchange/taler-exchange-aggregator
src/exchange/test_taler_exchange_aggregator-postgres
src/exchange/test_taler_exchange_httpd_home/.local/share/taler/exchange/live-keys/
+src/exchange/test_taler_exchange_httpd_home/.local/share/taler/exchange/wirefees/
src/exchange-tools/taler-auditor-sign
src/exchange-tools/taler-exchange-dbinit
src/exchange-tools/taler-exchange-keycheck
diff --git a/src/exchange-lib/test_exchange_api.conf b/src/exchange-lib/test_exchange_api.conf
index 76ade18fe..42861eae1 100644
--- a/src/exchange-lib/test_exchange_api.conf
+++ b/src/exchange-lib/test_exchange_api.conf
@@ -30,6 +30,19 @@ DB_CONN_STR = "postgres:///talercheck"
# Enable 'sepa' to test SEPA-specific routines.
ENABLE = YES
+# Fees for the forseeable future...
+# If you see this after 2017, update to match the next 10 years...
+WIRE-FEE-2017 = EUR:0.01
+WIRE-FEE-2018 = EUR:0.01
+WIRE-FEE-2019 = EUR:0.01
+WIRE-FEE-2020 = EUR:0.01
+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
+
[exchange-wire-incoming-sepa]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
@@ -39,6 +52,20 @@ SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/sepa.json
# Enable 'test' for testing of the actual coin operations.
ENABLE = YES
+# Fees for the forseeable future...
+# If you see this after 2017, update to match the next 10 years...
+WIRE-FEE-2017 = EUR:0.01
+WIRE-FEE-2018 = EUR:0.01
+WIRE-FEE-2019 = EUR:0.01
+WIRE-FEE-2020 = EUR:0.01
+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
+
+
[exchange-wire-incoming-test]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index 9c2580e77..60b2cc2c5 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -25,6 +25,7 @@ taler_exchange_keyup_LDADD = \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-lgnunetutil $(XLIB)
taler_exchange_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c
index 76801738e..c3e58db9c 100644
--- a/src/exchange-tools/taler-exchange-keyup.c
+++ b/src/exchange-tools/taler-exchange-keyup.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -23,6 +23,7 @@
*/
#include <platform.h>
#include "taler_exchangedb_lib.h"
+#include "taler_wire_lib.h"
/**
* When generating filenames from a cryptographic hash, we do not use
@@ -189,6 +190,11 @@ static char *exchange_directory;
static char *pretend_time_str;
/**
+ * Directory where we should write the wire transfer fee structure.
+ */
+static char *feedir;
+
+/**
* Handle to the exchange's configuration
*/
static const struct GNUNET_CONFIGURATION_Handle *kcfg;
@@ -215,6 +221,11 @@ static struct TALER_MasterPublicKeyP master_public_key;
static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
/**
+ * Largest duration for spending of any key.
+ */
+static struct GNUNET_TIME_Relative max_duration_spend;
+
+/**
* Return value from main().
*/
static int global_ret;
@@ -598,6 +609,8 @@ get_cointype_params (const char *ct,
return GNUNET_SYSERR;
}
GNUNET_TIME_round_rel (&params->duration_spend);
+ max_duration_spend = GNUNET_TIME_relative_max (max_duration_spend,
+ params->duration_spend);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
ct,
@@ -862,6 +875,151 @@ exchange_keys_update_denomkeys ()
/**
+ * Sign @a af with @a priv
+ *
+ * @param[in|out] af fee structure to sign
+ * @param priv private key to use for signing
+ */
+static void
+sign_af (struct TALER_EXCHANGEDB_AggregateFees *af,
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
+{
+ struct TALER_MasterWireFeePS wf;
+
+ TALER_EXCHANGEDB_fees_2_wf (af,
+ &wf);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (priv,
+ &wf.purpose,
+ &af->master_sig.eddsa_signature));
+}
+
+
+/**
+ * Output the wire fee structure. Must be run after #max_duration_spend
+ * was initialized.
+ *
+ * @param cls pointer to `int`, set to #GNUNET_SYSERR on error
+ * @param wiremethod method to write fees for
+ */
+static void
+create_wire_fee_for_method (void *cls,
+ const char *wiremethod)
+{
+ int *ret = cls;
+ struct TALER_EXCHANGEDB_AggregateFees *af_head;
+ struct TALER_EXCHANGEDB_AggregateFees *af_tail;
+ unsigned int year;
+ struct GNUNET_TIME_Absolute last_date;
+ struct GNUNET_TIME_Absolute start_date;
+ struct GNUNET_TIME_Absolute end_date;
+ char yearstr[12];
+ char *fn;
+ char *section;
+
+ if (GNUNET_OK != *ret)
+ return;
+ last_date = GNUNET_TIME_absolute_max (last_date,
+ GNUNET_TIME_absolute_add (lookahead_sign_stamp,
+ max_duration_spend));
+ GNUNET_asprintf (&section,
+ "exchange-wire-%s",
+ wiremethod);
+ GNUNET_asprintf (&fn,
+ "%s%s.fee",
+ feedir,
+ wiremethod);
+ af_head = NULL;
+ af_tail = NULL;
+ year = GNUNET_TIME_get_current_year ();
+ start_date = GNUNET_TIME_year_to_time (year);
+ while (start_date.abs_value_us < last_date.abs_value_us)
+ {
+ struct TALER_EXCHANGEDB_AggregateFees *af;
+ char *opt;
+ char *amounts;
+
+ GNUNET_snprintf (yearstr,
+ sizeof (yearstr),
+ "%u",
+ year);
+ end_date = GNUNET_TIME_year_to_time (year + 1);
+ af = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees);
+ af->start_date = start_date;
+ af->end_date = end_date;
+ GNUNET_asprintf (&opt,
+ "wire-fee-%u",
+ year);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ section,
+ opt,
+ &amounts))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ opt);
+ *ret = GNUNET_SYSERR;
+ GNUNET_free (opt);
+ break;
+ }
+ if (GNUNET_OK !=
+ TALER_string_to_amount (amounts,
+ &af->wire_fee))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid amount `%s' specified in `%s' under `%s'\n",
+ amounts,
+ wiremethod,
+ opt);
+ *ret = GNUNET_SYSERR;
+ GNUNET_free (amounts);
+ GNUNET_free (opt);
+ break;
+ }
+ GNUNET_free (amounts);
+ GNUNET_free (opt);
+ sign_af (af,
+ &master_priv.eddsa_priv);
+ if (NULL == af_tail)
+ af_head = af;
+ else
+ af_tail->next = af;
+ af_tail = af;
+ start_date = end_date;
+ year++;
+ }
+ if ( (GNUNET_OK == *ret) &&
+ (GNUNET_OK !=
+ TALER_EXCHANGEDB_fees_write (fn,
+ af_head)) )
+ *ret = GNUNET_SYSERR;
+ GNUNET_free (section);
+ GNUNET_free (fn);
+ TALER_EXCHANGEDB_fees_free (af_head);
+}
+
+
+/**
+ * Output the wire fee structure. Must be run after #max_duration_spend
+ * was initialized.
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+create_wire_fees ()
+{
+ int ret;
+
+ ret = GNUNET_OK;
+ TALER_WIRE_find_enabled (kcfg,
+ &create_wire_fee_for_method,
+ &ret);
+ return ret;
+}
+
+
+/**
* Main function that will be run.
*
* @param cls closure
@@ -896,6 +1054,29 @@ run (void *cls,
{
now = GNUNET_TIME_absolute_get ();
}
+ if (NULL == feedir)
+ {
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (kcfg,
+ "exchangedb",
+ "WIREFEE_BASE_DIR",
+ &feedir))
+ {
+ fprintf (stderr,
+ "Wire fee directory not given in neither configuration nor command-line\n");
+ global_ret = 1;
+ return;
+ }
+ }
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create (feedir))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "mkdir",
+ feedir);
+ global_ret = 1;
+ return;
+ }
GNUNET_TIME_round_abs (&now);
if ( (NULL == masterkeyfile) &&
(GNUNET_OK !=
@@ -1022,10 +1203,10 @@ run (void *cls,
global_ret = 1;
return;
}
- if (NULL != auditor_output_file)
+ if (GNUNET_OK != create_wire_fees ())
{
- FCLOSE (auditor_output_file);
- auditor_output_file = NULL;
+ global_ret = 1;
+ return;
}
}
@@ -1051,6 +1232,9 @@ main (int argc,
{'m', "master-key", "FILE",
"master key file (private key)", 1,
&GNUNET_GETOPT_set_filename, &masterkeyfile},
+ {'f', "feedir", "DIRNAME",
+ "directory where to write wire transfer fee structure", 1,
+ &GNUNET_GETOPT_set_filename, &feedir},
{'o', "output", "FILE",
"auditor denomination key signing request file to create", 1,
&GNUNET_GETOPT_set_filename, &auditorrequestfile},
@@ -1072,6 +1256,11 @@ main (int argc,
options,
&run, NULL))
return 1;
+ if (NULL != auditor_output_file)
+ {
+ FCLOSE (auditor_output_file);
+ auditor_output_file = NULL;
+ }
return global_ret;
}
diff --git a/src/exchange/taler-exchange-httpd_validation.c b/src/exchange/taler-exchange-httpd_validation.c
index 14c1476b8..f8a1f7cba 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 GNUnet e.V.
+ Copyright (C) 2016, 2017 GNUnet e.V.
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,6 +23,7 @@
#include <gnunet/gnunet_util_lib.h>
#include "taler-exchange-httpd.h"
#include "taler-exchange-httpd_validation.h"
+#include "taler-exchange-httpd_wire.h"
#include "taler_wire_lib.h"
@@ -77,6 +78,7 @@ load_plugin (void *cls,
{
int *ret = cls;
struct Plugin *p;
+ json_t *fees;
p = GNUNET_new (struct Plugin);
p->type = GNUNET_strdup (name);
@@ -84,13 +86,26 @@ load_plugin (void *cls,
name);
if (NULL == p->plugin)
{
- GNUNET_free (p);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to load plugin %s\n",
name);
+ GNUNET_free (p->type);
+ GNUNET_free (p);
+ *ret = GNUNET_SYSERR;
+ return;
+ }
+ fees = TEH_WIRE_get_fees (name);
+ if (NULL == fees)
+ {
+ 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;
}
+ json_decref (fees);
GNUNET_CONTAINER_DLL_insert (wire_head,
wire_tail,
p);
@@ -114,9 +129,8 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
&ret);
if (NULL == wire_head)
{
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "wireformat");
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to find properly configured wire transfer method\n");
ret = GNUNET_SYSERR;
}
if (GNUNET_OK != ret)
@@ -201,17 +215,17 @@ json_t *
TEH_VALIDATION_get_wire_methods (const char *prefix)
{
json_t *methods;
- json_t *method;
- struct Plugin *p;
- struct TALER_WIRE_Plugin *plugin;
char *account_name;
char *emsg;
enum TALER_ErrorCode ec;
methods = json_object ();
- for (p=wire_head;NULL != p;p = p->next)
+ for (struct Plugin *p=wire_head;NULL != p;p = p->next)
{
- plugin = p->plugin;
+ struct TALER_WIRE_Plugin *plugin = p->plugin;
+ json_t *method;
+ json_t *fees;
+
GNUNET_asprintf (&account_name,
"%s-%s",
prefix,
@@ -233,6 +247,22 @@ TEH_VALIDATION_get_wire_methods (const char *prefix)
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,
diff --git a/src/exchange/taler-exchange-httpd_wire.c b/src/exchange/taler-exchange-httpd_wire.c
index 7857a5bc1..9c8b2ff13 100644
--- a/src/exchange/taler-exchange-httpd_wire.c
+++ b/src/exchange/taler-exchange-httpd_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015, 2016 GNUnet e.V. and INRIA
+ Copyright (C) 2015-2017 GNUnet e.V. and INRIA
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,7 @@
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_validation.h"
#include "taler-exchange-httpd_wire.h"
+#include "taler_json_lib.h"
#include <jansson.h>
/**
@@ -33,6 +34,76 @@ static json_t *wire_methods;
/**
+ * Convert fee structure to JSON result to be returned
+ * as part of a /wire response.
+ *
+ * @param af fee structure to convert
+ * @return NULL on error, otherwise json data structure for /wire.
+ */
+static json_t *
+fees_to_json (struct TALER_EXCHANGEDB_AggregateFees *af)
+{
+ json_t *a;
+
+ a = json_array ();
+ while (NULL != af)
+ {
+ if ( (GNUNET_NO == GNUNET_TIME_round_abs (&af->start_date)) ||
+ (GNUNET_NO == GNUNET_TIME_round_abs (&af->end_date)) )
+ {
+ json_decref (a);
+ return NULL;
+ }
+ json_array_append_new (a,
+ json_pack ("{s:o, s:o, s:o, s:o}",
+ "wire_fee", TALER_JSON_from_amount (&af->wire_fee),
+ "start_date", GNUNET_JSON_from_time_abs (af->start_date),
+ "end_date", GNUNET_JSON_from_time_abs (af->end_date),
+ "sig", GNUNET_JSON_from_data_auto (&af->master_sig)));
+ af = af->next;
+ }
+ return a;
+}
+
+
+/**
+ * Obtain fee structure for @a wire_plugin_name wire transfers.
+ *
+ * @param wire_plugin_name name of the plugin 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)
+{
+ struct TALER_EXCHANGEDB_AggregateFees *af;
+ json_t *j;
+ struct GNUNET_TIME_Absolute now;
+
+ af = TALER_EXCHANGEDB_fees_read (cfg,
+ wire_plugin_name);
+ now = GNUNET_TIME_absolute_get ();
+ while ( (NULL != af) &&
+ (af->end_date.abs_value_us < now.abs_value_us) )
+ {
+ struct TALER_EXCHANGEDB_AggregateFees *n = af->next;
+
+ GNUNET_free (af);
+ af = n;
+ }
+ if (NULL == af)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to find current wire transfer fees for `%s'\n",
+ wire_plugin_name);
+ return NULL;
+ }
+ j = fees_to_json (af);
+ TALER_EXCHANGEDB_fees_free (af);
+ return j;
+}
+
+
+/**
* Handle a "/wire" request.
*
* @param rh context of the handler
diff --git a/src/exchange/taler-exchange-httpd_wire.h b/src/exchange/taler-exchange-httpd_wire.h
index d67c16a82..a85fde696 100644
--- a/src/exchange/taler-exchange-httpd_wire.h
+++ b/src/exchange/taler-exchange-httpd_wire.h
@@ -27,6 +27,16 @@
/**
+ * Obtain fee structure for @a wire_plugin_name wire transfers.
+ *
+ * @param wire_plugin_name name of the plugin 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);
+
+
+/**
* Handle a "/wire" request.
*
* @param rh context of the handler
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index f6754e803..2122c174c 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -44,6 +44,7 @@ lib_LTLIBRARIES = \
libtalerexchangedb_la_SOURCES = \
exchangedb_auditorkeys.c \
exchangedb_denomkeys.c \
+ exchangedb_fees.c \
exchangedb_signkeys.c \
exchangedb_plugin.c
@@ -60,6 +61,7 @@ libtalerexchangedb_la_LDFLAGS = \
check_PROGRAMS = \
test-exchangedb-auditors \
test-exchangedb-denomkeys \
+ test-exchangedb-fees \
test-exchangedb-signkeys \
test-exchangedb-postgres \
test-perf-taler-exchangedb \
@@ -68,10 +70,11 @@ check_PROGRAMS = \
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \
test-exchangedb-auditors \
+ test-exchangedb-denomkeys \
+ test-exchangedb-fees \
test-exchangedb-postgres \
test-exchangedb-signkeys \
- test-perf-taler-exchangedb \
- test-exchangedb-denomkeys
+ test-perf-taler-exchangedb
test_exchangedb_auditors_SOURCES = \
test_exchangedb_auditors.c
@@ -87,6 +90,13 @@ test_exchangedb_denomkeys_LDADD = \
$(top_srcdir)/src/util/libtalerutil.la \
-lgnunetutil
+test_exchangedb_fees_SOURCES = \
+ test_exchangedb_fees.c
+test_exchangedb_fees_LDADD = \
+ libtalerexchangedb.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ -lgnunetutil
+
test_exchangedb_signkeys_SOURCES = \
test_exchangedb_signkeys.c
test_exchangedb_signkeys_LDADD = \
diff --git a/src/exchangedb/exchangedb_denomkeys.c b/src/exchangedb/exchangedb_denomkeys.c
index 7e7a73a32..de8dfa6f2 100644
--- a/src/exchangedb/exchangedb_denomkeys.c
+++ b/src/exchangedb/exchangedb_denomkeys.c
@@ -42,10 +42,11 @@ TALER_EXCHANGEDB_denomination_key_read (const char *filename,
void *data;
struct GNUNET_CRYPTO_RsaPrivateKey *priv;
- if (GNUNET_OK != GNUNET_DISK_file_size (filename,
- &size,
- GNUNET_YES,
- GNUNET_YES))
+ if (GNUNET_OK !=
+ GNUNET_DISK_file_size (filename,
+ &size,
+ GNUNET_YES,
+ GNUNET_YES))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Skipping inaccessable denomination key file `%s'\n",
diff --git a/src/exchangedb/exchangedb_fees.c b/src/exchangedb/exchangedb_fees.c
index 938b61c19..5adee5d41 100644
--- a/src/exchangedb/exchangedb_fees.c
+++ b/src/exchangedb/exchangedb_fees.c
@@ -92,7 +92,7 @@ TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg,
&wirefee_base_dir))
return NULL;
GNUNET_asprintf (&fn,
- "%s" DIR_SEPARATOR_STR "%s.fee",
+ "%s/%s.fee",
wirefee_base_dir,
wireplugin);
GNUNET_free (wirefee_base_dir);
diff --git a/src/exchangedb/test_exchangedb_fees.c b/src/exchangedb/test_exchangedb_fees.c
index e23879ead..b82abc5fd 100644
--- a/src/exchangedb/test_exchangedb_fees.c
+++ b/src/exchangedb/test_exchangedb_fees.c
@@ -24,23 +24,121 @@
#include "taler_exchangedb_lib.h"
+/**
+ * Sign @a af with @a priv
+ *
+ * @param[in|out] af fee structure to sign
+ * @param priv private key to use for signing
+ */
+static void
+sign_af (struct TALER_EXCHANGEDB_AggregateFees *af,
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
+{
+ struct TALER_MasterWireFeePS wf;
+
+ TALER_EXCHANGEDB_fees_2_wf (af,
+ &wf);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (priv,
+ &wf.purpose,
+ &af->master_sig.eddsa_signature));
+}
+
+
int
main (int argc,
const char *const argv[])
{
struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct TALER_EXCHANGEDB_AggregateFees *af;
+ struct TALER_EXCHANGEDB_AggregateFees *n;
+ struct TALER_MasterPublicKeyP master_pub;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+ char *tmpdir;
+ char *tmpfile = NULL;
int ret;
+ unsigned int year;
- ret = 1;
GNUNET_log_setup ("test-exchangedb-fees",
"WARNING",
NULL);
+ tmpdir = GNUNET_DISK_mkdtemp ("test_exchangedb_fees");
+ if (NULL == tmpdir)
+ return 77; /* skip test */
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ GNUNET_CRYPTO_eddsa_key_get_public (priv,
+ &master_pub.eddsa_pub);
cfg = GNUNET_CONFIGURATION_create ();
-
GNUNET_CONFIGURATION_set_value_string (cfg,
"exchangedb",
- "AUDITOR_BASE_DIR",
+ "WIREFEE_BASE_DIR",
tmpdir);
+ GNUNET_asprintf (&tmpfile,
+ "%s/%s.fee",
+ tmpdir,
+ "test");
ret = 0;
+ af = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees);
+ year = GNUNET_TIME_get_current_year ();
+ af->start_date = GNUNET_TIME_year_to_time (year);
+ af->end_date = GNUNET_TIME_year_to_time (year + 1);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount ("EUR:1.0",
+ &af->wire_fee));
+ sign_af (af,
+ priv);
+ n = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees);
+ n->start_date = GNUNET_TIME_year_to_time (year + 1);
+ n->end_date = GNUNET_TIME_year_to_time (year + 2);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount ("EUR:0.1",
+ &n->wire_fee));
+ sign_af (n,
+ priv);
+ af->next = n;
+
+ if (GNUNET_OK !=
+ TALER_EXCHANGEDB_fees_write (tmpfile,
+ af))
+ {
+ GNUNET_break (0);
+ ret = 1;
+ }
+ TALER_EXCHANGEDB_fees_free (af);
+ GNUNET_free (tmpfile);
+ af = TALER_EXCHANGEDB_fees_read (cfg,
+ "test");
+ if (NULL == af)
+ {
+ GNUNET_break (0);
+ ret = 1;
+ }
+ else
+ {
+ for (struct TALER_EXCHANGEDB_AggregateFees *p = af;
+ NULL != p;
+ p = p->next)
+ {
+ struct TALER_MasterWireFeePS wf;
+
+ TALER_EXCHANGEDB_fees_2_wf (p,
+ &wf);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
+ &wf.purpose,
+ &p->master_sig.eddsa_signature,
+ &master_pub.eddsa_pub))
+ {
+ GNUNET_break (0);
+ ret = 1;
+ }
+ }
+ TALER_EXCHANGEDB_fees_free (af);
+ }
+
+ (void) GNUNET_DISK_directory_remove (tmpdir);
+ GNUNET_free (tmpdir);
+ GNUNET_free (priv);
+ GNUNET_CONFIGURATION_destroy (cfg);
return ret;
}
diff --git a/src/include/taler_exchangedb_lib.h b/src/include/taler_exchangedb_lib.h
index 4ebe4841f..02e8cdfec 100644
--- a/src/include/taler_exchangedb_lib.h
+++ b/src/include/taler_exchangedb_lib.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 Inria & GNUnet e.V.
+ Copyright (C) 2014-2017 Inria & GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -25,6 +25,7 @@
#include "taler_signatures.h"
+
/**
* Subdirectroy under the exchange's base directory which contains
* the exchange's signing keys.
@@ -297,4 +298,79 @@ void
TALER_EXCHANGEDB_plugin_unload (struct TALER_EXCHANGEDB_Plugin *plugin);
+/**
+ * Sorted list of fees to be paid for aggregate wire transfers.
+ */
+struct TALER_EXCHANGEDB_AggregateFees
+{
+ /**
+ * This is a linked list.
+ */
+ struct TALER_EXCHANGEDB_AggregateFees *next;
+
+ /**
+ * Fee to be paid.
+ */
+ struct TALER_Amount wire_fee;
+
+ /**
+ * Time when this fee goes into effect (inclusive)
+ */
+ struct GNUNET_TIME_Absolute start_date;
+
+ /**
+ * Time when this fee stops being in effect (exclusive).
+ */
+ struct GNUNET_TIME_Absolute end_date;
+
+ /**
+ * Signature affirming the above fee structure.
+ */
+ struct TALER_MasterSignatureP master_sig;
+};
+
+
+/**
+ * Read the current fee structure from disk.
+ *
+ * @param cfg configuration to use
+ * @param wireplugin name of the wire plugin to read fees for
+ * @return sorted list of aggregation fees, NULL on error
+ */
+struct TALER_EXCHANGEDB_AggregateFees *
+TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *wireplugin);
+
+
+/**
+ * Convert @a af to @a wf.
+ *
+ * @param[in,out] af aggregate fees, host format (updated to round time)
+ * @param[out] wf aggregate fees, disk / signature format
+ */
+void
+TALER_EXCHANGEDB_fees_2_wf (struct TALER_EXCHANGEDB_AggregateFees *af,
+ struct TALER_MasterWireFeePS *wf);
+
+
+/**
+ * Write given fee structure to disk.
+ *
+ * @param filename where to write the fees
+ * @param af fee structure to write
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+TALER_EXCHANGEDB_fees_write (const char *filename,
+ struct TALER_EXCHANGEDB_AggregateFees *af);
+
+
+/**
+ * Free @a af data structure
+ *
+ * @param af list to free
+ */
+void
+TALER_EXCHANGEDB_fees_free (struct TALER_EXCHANGEDB_AggregateFees *af);
+
#endif
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index ef241c8b8..35967399d 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+ Copyright (C) 2014-2017 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -78,6 +78,11 @@
*/
#define TALER_SIGNATURE_MASTER_TEST_DETAILS 1027
+/**
+ * Fees charged per (aggregate) wire transfer to the merchant.
+ */
+#define TALER_SIGNATURE_MASTER_WIRE_FEES 1028
+
/*********************************************/
/* Exchange online signatures (with signing key) */
@@ -879,6 +884,37 @@ struct TALER_MasterWireDetailsPS
};
+
+/**
+ * @brief Information signed by the exchange's master
+ * key stating the wire fee to be paid per wire transfer.
+ */
+struct TALER_MasterWireFeePS
+{
+
+ /**
+ * Purpose is #TALER_SIGNATURE_MASTER_WIRE_FEES.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Start date when the fee goes into effect.
+ */
+ struct GNUNET_TIME_AbsoluteNBO start_date;
+
+ /**
+ * End date when the fee stops being in effect (exclusive)
+ */
+ struct GNUNET_TIME_AbsoluteNBO end_date;
+
+ /**
+ * Fee charged to the merchant per wire transfer.
+ */
+ struct TALER_AmountNBO wire_fee;
+
+};
+
+
/**
* @brief Format used to generate the signature on a request to obtain
* the wire transfer identifier associated with a deposit.