diff options
author | Christian Grothoff <christian@grothoff.org> | 2017-03-03 20:31:29 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2017-03-03 20:31:29 +0100 |
commit | f406f96129766c144c1531dc853969664f410d8c (patch) | |
tree | 6fdbae6ed117d3057ae9101ab4f799f5e3354d59 | |
parent | 364abbaea1383bd7d8311269d596bdc3c1d4b591 (diff) |
implement #4929: add wire transfer fee to /wire (but not yet charged by aggregator)
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | src/exchange-lib/test_exchange_api.conf | 27 | ||||
-rw-r--r-- | src/exchange-tools/Makefile.am | 1 | ||||
-rw-r--r-- | src/exchange-tools/taler-exchange-keyup.c | 197 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_validation.c | 50 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_wire.c | 73 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_wire.h | 10 | ||||
-rw-r--r-- | src/exchangedb/Makefile.am | 14 | ||||
-rw-r--r-- | src/exchangedb/exchangedb_denomkeys.c | 9 | ||||
-rw-r--r-- | src/exchangedb/exchangedb_fees.c | 2 | ||||
-rw-r--r-- | src/exchangedb/test_exchangedb_fees.c | 104 | ||||
-rw-r--r-- | src/include/taler_exchangedb_lib.h | 78 | ||||
-rw-r--r-- | src/include/taler_signatures.h | 38 |
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 (¶ms->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 (§ion, + "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. |