implement #4929: add wire transfer fee to /wire (but not yet charged by aggregator)

This commit is contained in:
Christian Grothoff 2017-03-03 20:31:29 +01:00
parent 364abbaea1
commit f406f96129
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
13 changed files with 578 additions and 27 deletions

2
.gitignore vendored
View File

@ -35,9 +35,11 @@ src/bank-lib/test_bank_api
src/bank-lib/test_bank_api_with_fakebank src/bank-lib/test_bank_api_with_fakebank
src/exchange-lib/test_exchange_api 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/live-keys/
src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/wirefees/
src/exchange/taler-exchange-aggregator src/exchange/taler-exchange-aggregator
src/exchange/test_taler_exchange_aggregator-postgres 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/live-keys/
src/exchange/test_taler_exchange_httpd_home/.local/share/taler/exchange/wirefees/
src/exchange-tools/taler-auditor-sign src/exchange-tools/taler-auditor-sign
src/exchange-tools/taler-exchange-dbinit src/exchange-tools/taler-exchange-dbinit
src/exchange-tools/taler-exchange-keycheck src/exchange-tools/taler-exchange-keycheck

View File

@ -30,6 +30,19 @@ DB_CONN_STR = "postgres:///talercheck"
# Enable 'sepa' to test SEPA-specific routines. # Enable 'sepa' to test SEPA-specific routines.
ENABLE = YES 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] [exchange-wire-incoming-sepa]
# This is the response we give out for the /wire request. It provides # This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange. # 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 'test' for testing of the actual coin operations.
ENABLE = YES 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] [exchange-wire-incoming-test]
# This is the response we give out for the /wire request. It provides # This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange. # wallets with the bank information for transfers to the exchange.

View File

@ -25,6 +25,7 @@ taler_exchange_keyup_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \ $(top_builddir)/src/pq/libtalerpq.la \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-lgnunetutil $(XLIB) -lgnunetutil $(XLIB)
taler_exchange_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS) taler_exchange_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER 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 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 terms of the GNU General Public License as published by the Free Software
@ -23,6 +23,7 @@
*/ */
#include <platform.h> #include <platform.h>
#include "taler_exchangedb_lib.h" #include "taler_exchangedb_lib.h"
#include "taler_wire_lib.h"
/** /**
* When generating filenames from a cryptographic hash, we do not use * When generating filenames from a cryptographic hash, we do not use
@ -188,6 +189,11 @@ static char *exchange_directory;
*/ */
static char *pretend_time_str; static char *pretend_time_str;
/**
* Directory where we should write the wire transfer fee structure.
*/
static char *feedir;
/** /**
* Handle to the exchange's configuration * Handle to the exchange's configuration
*/ */
@ -214,6 +220,11 @@ static struct TALER_MasterPublicKeyP master_public_key;
*/ */
static struct GNUNET_TIME_Absolute lookahead_sign_stamp; 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(). * Return value from main().
*/ */
@ -598,6 +609,8 @@ get_cointype_params (const char *ct,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
GNUNET_TIME_round_rel (&params->duration_spend); GNUNET_TIME_round_rel (&params->duration_spend);
max_duration_spend = GNUNET_TIME_relative_max (max_duration_spend,
params->duration_spend);
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg, GNUNET_CONFIGURATION_get_value_time (kcfg,
ct, ct,
@ -861,6 +874,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. * Main function that will be run.
* *
@ -896,6 +1054,29 @@ run (void *cls,
{ {
now = GNUNET_TIME_absolute_get (); 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); GNUNET_TIME_round_abs (&now);
if ( (NULL == masterkeyfile) && if ( (NULL == masterkeyfile) &&
(GNUNET_OK != (GNUNET_OK !=
@ -1022,10 +1203,10 @@ run (void *cls,
global_ret = 1; global_ret = 1;
return; return;
} }
if (NULL != auditor_output_file) if (GNUNET_OK != create_wire_fees ())
{ {
FCLOSE (auditor_output_file); global_ret = 1;
auditor_output_file = NULL; return;
} }
} }
@ -1051,6 +1232,9 @@ main (int argc,
{'m', "master-key", "FILE", {'m', "master-key", "FILE",
"master key file (private key)", 1, "master key file (private key)", 1,
&GNUNET_GETOPT_set_filename, &masterkeyfile}, &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", {'o', "output", "FILE",
"auditor denomination key signing request file to create", 1, "auditor denomination key signing request file to create", 1,
&GNUNET_GETOPT_set_filename, &auditorrequestfile}, &GNUNET_GETOPT_set_filename, &auditorrequestfile},
@ -1072,6 +1256,11 @@ main (int argc,
options, options,
&run, NULL)) &run, NULL))
return 1; return 1;
if (NULL != auditor_output_file)
{
FCLOSE (auditor_output_file);
auditor_output_file = NULL;
}
return global_ret; return global_ret;
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER 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 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 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 <gnunet/gnunet_util_lib.h>
#include "taler-exchange-httpd.h" #include "taler-exchange-httpd.h"
#include "taler-exchange-httpd_validation.h" #include "taler-exchange-httpd_validation.h"
#include "taler-exchange-httpd_wire.h"
#include "taler_wire_lib.h" #include "taler_wire_lib.h"
@ -77,6 +78,7 @@ load_plugin (void *cls,
{ {
int *ret = cls; int *ret = cls;
struct Plugin *p; struct Plugin *p;
json_t *fees;
p = GNUNET_new (struct Plugin); p = GNUNET_new (struct Plugin);
p->type = GNUNET_strdup (name); p->type = GNUNET_strdup (name);
@ -84,13 +86,26 @@ load_plugin (void *cls,
name); name);
if (NULL == p->plugin) if (NULL == p->plugin)
{ {
GNUNET_free (p);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to load plugin %s\n", "Failed to load plugin %s\n",
name); name);
GNUNET_free (p->type);
GNUNET_free (p);
*ret = GNUNET_SYSERR; *ret = GNUNET_SYSERR;
return; 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, GNUNET_CONTAINER_DLL_insert (wire_head,
wire_tail, wire_tail,
p); p);
@ -114,9 +129,8 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
&ret); &ret);
if (NULL == wire_head) if (NULL == wire_head)
{ {
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"exchange", "Failed to find properly configured wire transfer method\n");
"wireformat");
ret = GNUNET_SYSERR; ret = GNUNET_SYSERR;
} }
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
@ -201,17 +215,17 @@ json_t *
TEH_VALIDATION_get_wire_methods (const char *prefix) TEH_VALIDATION_get_wire_methods (const char *prefix)
{ {
json_t *methods; json_t *methods;
json_t *method;
struct Plugin *p;
struct TALER_WIRE_Plugin *plugin;
char *account_name; char *account_name;
char *emsg; char *emsg;
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
methods = json_object (); 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, GNUNET_asprintf (&account_name,
"%s-%s", "%s-%s",
prefix, prefix,
@ -233,6 +247,22 @@ TEH_VALIDATION_get_wire_methods (const char *prefix)
json_decref (method); json_decref (method);
method = NULL; 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) if (NULL != method)
json_object_set_new (methods, json_object_set_new (methods,
p->type, p->type,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER 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 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 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_responses.h"
#include "taler-exchange-httpd_validation.h" #include "taler-exchange-httpd_validation.h"
#include "taler-exchange-httpd_wire.h" #include "taler-exchange-httpd_wire.h"
#include "taler_json_lib.h"
#include <jansson.h> #include <jansson.h>
/** /**
@ -32,6 +33,76 @@
static json_t *wire_methods; 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. * Handle a "/wire" request.
* *

View File

@ -26,6 +26,16 @@
#include "taler-exchange-httpd.h" #include "taler-exchange-httpd.h"
/**
* 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. * Handle a "/wire" request.
* *

View File

@ -44,6 +44,7 @@ lib_LTLIBRARIES = \
libtalerexchangedb_la_SOURCES = \ libtalerexchangedb_la_SOURCES = \
exchangedb_auditorkeys.c \ exchangedb_auditorkeys.c \
exchangedb_denomkeys.c \ exchangedb_denomkeys.c \
exchangedb_fees.c \
exchangedb_signkeys.c \ exchangedb_signkeys.c \
exchangedb_plugin.c exchangedb_plugin.c
@ -60,6 +61,7 @@ libtalerexchangedb_la_LDFLAGS = \
check_PROGRAMS = \ check_PROGRAMS = \
test-exchangedb-auditors \ test-exchangedb-auditors \
test-exchangedb-denomkeys \ test-exchangedb-denomkeys \
test-exchangedb-fees \
test-exchangedb-signkeys \ test-exchangedb-signkeys \
test-exchangedb-postgres \ test-exchangedb-postgres \
test-perf-taler-exchangedb \ 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; AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \ TESTS = \
test-exchangedb-auditors \ test-exchangedb-auditors \
test-exchangedb-denomkeys \
test-exchangedb-fees \
test-exchangedb-postgres \ test-exchangedb-postgres \
test-exchangedb-signkeys \ test-exchangedb-signkeys \
test-perf-taler-exchangedb \ test-perf-taler-exchangedb
test-exchangedb-denomkeys
test_exchangedb_auditors_SOURCES = \ test_exchangedb_auditors_SOURCES = \
test_exchangedb_auditors.c test_exchangedb_auditors.c
@ -87,6 +90,13 @@ test_exchangedb_denomkeys_LDADD = \
$(top_srcdir)/src/util/libtalerutil.la \ $(top_srcdir)/src/util/libtalerutil.la \
-lgnunetutil -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_SOURCES = \
test_exchangedb_signkeys.c test_exchangedb_signkeys.c
test_exchangedb_signkeys_LDADD = \ test_exchangedb_signkeys_LDADD = \

View File

@ -42,10 +42,11 @@ TALER_EXCHANGEDB_denomination_key_read (const char *filename,
void *data; void *data;
struct GNUNET_CRYPTO_RsaPrivateKey *priv; struct GNUNET_CRYPTO_RsaPrivateKey *priv;
if (GNUNET_OK != GNUNET_DISK_file_size (filename, if (GNUNET_OK !=
&size, GNUNET_DISK_file_size (filename,
GNUNET_YES, &size,
GNUNET_YES)) GNUNET_YES,
GNUNET_YES))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Skipping inaccessable denomination key file `%s'\n", "Skipping inaccessable denomination key file `%s'\n",

View File

@ -92,7 +92,7 @@ TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg,
&wirefee_base_dir)) &wirefee_base_dir))
return NULL; return NULL;
GNUNET_asprintf (&fn, GNUNET_asprintf (&fn,
"%s" DIR_SEPARATOR_STR "%s.fee", "%s/%s.fee",
wirefee_base_dir, wirefee_base_dir,
wireplugin); wireplugin);
GNUNET_free (wirefee_base_dir); GNUNET_free (wirefee_base_dir);

View File

@ -24,23 +24,121 @@
#include "taler_exchangedb_lib.h" #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 int
main (int argc, main (int argc,
const char *const argv[]) const char *const argv[])
{ {
struct GNUNET_CONFIGURATION_Handle *cfg; 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; int ret;
unsigned int year;
ret = 1;
GNUNET_log_setup ("test-exchangedb-fees", GNUNET_log_setup ("test-exchangedb-fees",
"WARNING", "WARNING",
NULL); 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 (); cfg = GNUNET_CONFIGURATION_create ();
GNUNET_CONFIGURATION_set_value_string (cfg, GNUNET_CONFIGURATION_set_value_string (cfg,
"exchangedb", "exchangedb",
"AUDITOR_BASE_DIR", "WIREFEE_BASE_DIR",
tmpdir); tmpdir);
GNUNET_asprintf (&tmpfile,
"%s/%s.fee",
tmpdir,
"test");
ret = 0; 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; return ret;
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER 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 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 terms of the GNU General Public License as published by the Free Software
@ -25,6 +25,7 @@
#include "taler_signatures.h" #include "taler_signatures.h"
/** /**
* Subdirectroy under the exchange's base directory which contains * Subdirectroy under the exchange's base directory which contains
* the exchange's signing keys. * the exchange's signing keys.
@ -297,4 +298,79 @@ void
TALER_EXCHANGEDB_plugin_unload (struct TALER_EXCHANGEDB_Plugin *plugin); 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 #endif

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER 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 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 terms of the GNU General Public License as published by the Free Software
@ -78,6 +78,11 @@
*/ */
#define TALER_SIGNATURE_MASTER_TEST_DETAILS 1027 #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) */ /* 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 * @brief Format used to generate the signature on a request to obtain
* the wire transfer identifier associated with a deposit. * the wire transfer identifier associated with a deposit.