summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-02-11 15:40:37 +0100
committerChristian Grothoff <christian@grothoff.org>2020-02-11 15:40:37 +0100
commit3ad698d01865c9f1b5c5aadc2f4d8b0a3658b81c (patch)
tree671de5acc0a13cafe636bf1e220496a5882a8b74 /src
parentde0d987e34d426f6ed3593840078442191255587 (diff)
parentcdaf1ce69b5fb56f09bbdc00942b03f039710614 (diff)
downloadexchange-3ad698d01865c9f1b5c5aadc2f4d8b0a3658b81c.tar.gz
exchange-3ad698d01865c9f1b5c5aadc2f4d8b0a3658b81c.tar.bz2
exchange-3ad698d01865c9f1b5c5aadc2f4d8b0a3658b81c.zip
merging
Diffstat (limited to 'src')
-rw-r--r--src/auditor/taler-auditor-httpd.c7
-rw-r--r--src/auditor/taler-auditor-httpd_deposit-confirmation.c113
-rw-r--r--src/auditor/taler-auditor-httpd_deposit-confirmation.h12
-rw-r--r--src/auditor/taler-auditor.c2
-rw-r--r--src/auditor/taler-wire-auditor.c18
-rwxr-xr-xsrc/auditor/test-auditor.sh43
-rw-r--r--src/auditordb/Makefile.am4
-rw-r--r--src/auditordb/auditor-0000.sql (renamed from src/auditordb/0000.sql)0
-rw-r--r--src/auditordb/auditor-0001.sql (renamed from src/auditordb/0001.sql)4
-rw-r--r--src/auditordb/auditordb_plugin.c5
-rw-r--r--src/auditordb/plugin_auditordb_postgres.c95
-rw-r--r--src/bank-lib/Makefile.am1
-rw-r--r--src/bank-lib/bank_api_config.c255
-rw-r--r--src/bank-lib/bank_api_credit.c23
-rw-r--r--src/bank-lib/bank_api_debit.c30
-rw-r--r--src/bank-lib/fakebank.c63
-rw-r--r--src/bank-lib/taler-fakebank-run.c28
-rw-r--r--src/benchmark/taler-exchange-benchmark.c36
-rw-r--r--src/exchange-tools/taler-exchange-keyup.c2
-rw-r--r--src/exchange-tools/taler-exchange-wire.c2
-rw-r--r--src/exchange/Makefile.am4
-rw-r--r--src/exchange/taler-exchange-aggregator.c36
-rw-r--r--src/exchange/taler-exchange-httpd.c2
-rw-r--r--src/exchange/taler-exchange-httpd_mhd.c9
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_melt.c2
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.c2
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c57
-rw-r--r--src/exchange/taler-exchange-httpd_reserve_status.c2
-rw-r--r--src/exchange/taler-exchange-httpd_reserve_withdraw.c2
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c2
-rw-r--r--src/exchange/taler-exchange-wirewatch.c6
-rwxr-xr-xsrc/exchange/test_taler_exchange_httpd_restart.sh114
-rw-r--r--src/exchange/test_taler_exchange_unix.conf137
-rw-r--r--src/exchangedb/Makefile.am4
-rw-r--r--src/exchangedb/exchange-0000.sql (renamed from src/exchangedb/0000.sql)0
-rw-r--r--src/exchangedb/exchange-0001.sql (renamed from src/exchangedb/0001.sql)54
-rw-r--r--src/exchangedb/exchangedb_plugin.c6
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c77
-rw-r--r--src/include/taler_bank_service.h73
-rw-r--r--src/include/taler_error_codes.h5
-rw-r--r--src/include/taler_fakebank_lib.h4
-rw-r--r--src/include/taler_testing_lib.h8
-rw-r--r--src/json/json.c3
-rw-r--r--src/testing/test_bank_api.c36
-rw-r--r--src/testing/test_taler_exchange_aggregator.c78
-rw-r--r--src/testing/testing_api_cmd_exec_wirewatch.c2
-rw-r--r--src/testing/testing_api_helpers_bank.c7
-rw-r--r--src/testing/testing_api_helpers_exchange.c2
-rw-r--r--src/testing/testing_api_loop.c88
49 files changed, 1164 insertions, 401 deletions
diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c
index 4d5537e51..043d5b148 100644
--- a/src/auditor/taler-auditor-httpd.c
+++ b/src/auditor/taler-auditor-httpd.c
@@ -594,7 +594,7 @@ main (int argc,
if (GNUNET_OK !=
auditor_serve_process_config ())
return 1;
-
+ TEAH_DEPOSIT_CONFIRMATION_init ();
/* check for systemd-style FD passing */
listen_pid = getenv ("LISTEN_PID");
listen_fds = getenv ("LISTEN_FDS");
@@ -635,7 +635,10 @@ main (int argc,
fh = TALER_MHD_open_unix_path (serve_unixpath,
unixpath_mode);
if (-1 == fh)
+ {
+ TEAH_DEPOSIT_CONFIRMATION_done ();
return 1;
+ }
}
mhd
@@ -659,6 +662,7 @@ main (int argc,
{
fprintf (stderr,
"Failed to start HTTP server.\n");
+ TEAH_DEPOSIT_CONFIRMATION_done ();
return 1;
}
@@ -732,6 +736,7 @@ main (int argc,
break;
}
TALER_AUDITORDB_plugin_unload (TAH_plugin);
+ TEAH_DEPOSIT_CONFIRMATION_done ();
return (GNUNET_SYSERR == ret) ? 1 : 0;
}
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.c b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
index 7759b5538..ab233ebc3 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.c
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
@@ -33,6 +33,19 @@
/**
+ * Cache of already verified exchange signing keys. Maps the hash of the
+ * `struct TALER_ExchangeSigningKeyValidityPS` to the (static) string
+ * "verified". Access to this map is guarded by the #lock.
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *cache;
+
+/**
+ * Lock for operations on #cache.
+ */
+static pthread_mutex_t lock;
+
+
+/**
* We have parsed the JSON information about the deposit, do some
* basic sanity checks (especially that the signature on the coin is
* valid, and that this type of coin exists) and then execute the
@@ -55,6 +68,8 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
struct TALER_AUDITORDB_Session *session;
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Absolute now;
+ struct GNUNET_HashCode h;
+ int cached;
now = GNUNET_TIME_absolute_get ();
if ( (es->ep_start.abs_value_us > now.abs_value_us) ||
@@ -68,10 +83,6 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
"master_sig (expired)");
}
- /* TODO (#6052): consider having an in-memory cache of already
- verified exchange signing keys, this could save us
- a signature check AND a database transaction per
- operation. */
/* check exchange signing key signature */
skv.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
skv.purpose.size = htonl (sizeof (struct TALER_ExchangeSigningKeyValidityPS));
@@ -80,18 +91,15 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
skv.expire = GNUNET_TIME_absolute_hton (es->ep_expire);
skv.end = GNUNET_TIME_absolute_hton (es->ep_end);
skv.signkey_pub = es->exchange_pub;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
- &skv.purpose,
- &es->master_sig.eddsa_signature,
- &es->master_public_key.eddsa_pub))
- {
- TALER_LOG_WARNING ("Invalid signature on exchange signing key\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID,
- "master_sig");
- }
+
+ /* check our cache */
+ GNUNET_CRYPTO_hash (&skv,
+ sizeof (skv),
+ &h);
+ GNUNET_assert (0 == pthread_mutex_lock (&lock));
+ cached = GNUNET_CONTAINER_multihashmap_contains (cache,
+ &h);
+ GNUNET_assert (0 == pthread_mutex_unlock (&lock));
session = TAH_plugin->get_session (TAH_plugin->cls);
if (NULL == session)
@@ -102,18 +110,45 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
TALER_EC_DB_SETUP_FAILED,
"failed to establish session with database");
}
- /* execute transaction */
- qs = TAH_plugin->insert_exchange_signkey (TAH_plugin->cls,
- session,
- es);
- if (0 > qs)
+ if (! cached)
{
- GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- TALER_LOG_WARNING ("Failed to store exchange signing key in database\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_AUDITOR_EXCHANGE_STORE_DB_ERROR,
- "failed to persist exchange signing key");
+ /* Not in cache, need to verify the signature, persist it, and possibly cache it */
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
+ &skv.purpose,
+ &es->master_sig.eddsa_signature,
+ &es->master_public_key.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Invalid signature on exchange signing key\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID,
+ "master_sig");
+ }
+
+ /* execute transaction */
+ qs = TAH_plugin->insert_exchange_signkey (TAH_plugin->cls,
+ session,
+ es);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+ TALER_LOG_WARNING ("Failed to store exchange signing key in database\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_AUDITOR_EXCHANGE_STORE_DB_ERROR,
+ "failed to persist exchange signing key");
+ }
+
+ /* Cache it, due to concurreny it might already be in the cache,
+ so we do not cache it twice but also don't insist on the 'put' to
+ succeed. */
+ GNUNET_assert (0 == pthread_mutex_lock (&lock));
+ (void) GNUNET_CONTAINER_multihashmap_put (cache,
+ &h,
+ "verified",
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ GNUNET_assert (0 == pthread_mutex_unlock (&lock));
}
/* check deposit confirmation signature */
@@ -237,4 +272,28 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
}
+/**
+ * Initialize subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_init (void)
+{
+ cache = GNUNET_CONTAINER_multihashmap_create (32,
+ GNUNET_NO);
+ GNUNET_assert (0 == pthread_mutex_init (&lock, NULL));
+}
+
+
+/**
+ * Shut down subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_done (void)
+{
+ GNUNET_CONTAINER_multihashmap_destroy (cache);
+ cache = NULL;
+ GNUNET_assert (0 == pthread_mutex_destroy (&lock));
+}
+
+
/* end of taler-auditor-httpd_deposit-confirmation.c */
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.h b/src/auditor/taler-auditor-httpd_deposit-confirmation.h
index 842eb3562..531f3c93c 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.h
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.h
@@ -25,6 +25,18 @@
#include <microhttpd.h>
#include "taler-auditor-httpd.h"
+/**
+ * Initialize subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_init (void);
+
+/**
+ * Shut down subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_done (void);
+
/**
* Handle a "/deposit-confirmation" request. Parses the JSON, and, if
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 4e32d2a4e..0ec5e2d75 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -5709,6 +5709,8 @@ main (int argc,
"restart",
"restart audit from the beginning (required on first run)",
&restart),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_OPTION_END
};
diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 94fd773c9..92664fb8f 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -704,13 +704,15 @@ check_pending_rc (void *cls,
TALER_amount_add (&total_closure_amount_lag,
&total_closure_amount_lag,
&rc->amount));
- report (report_closure_lags,
- json_pack ("{s:I, s:o, s:o, s:o, s:s}",
- "row", (json_int_t) rc->rowid,
- "amount", TALER_JSON_from_amount (&rc->amount),
- "deadline", json_from_time_abs (rc->execution_date),
- "wtid", GNUNET_JSON_from_data_auto (&rc->wtid),
- "account", rc->receiver_account));
+ if ( (0 != rc->amount.value) ||
+ (0 != rc->amount.fraction) )
+ report (report_closure_lags,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s}",
+ "row", (json_int_t) rc->rowid,
+ "amount", TALER_JSON_from_amount (&rc->amount),
+ "deadline", json_from_time_abs (rc->execution_date),
+ "wtid", GNUNET_JSON_from_data_auto (&rc->wtid),
+ "account", rc->receiver_account));
pp.last_reserve_close_uuid
= GNUNET_MIN (pp.last_reserve_close_uuid,
rc->rowid);
@@ -2312,6 +2314,8 @@ main (int argc,
"restart",
"restart audit from the beginning (required on first run)",
&restart),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_OPTION_END
};
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index 00ed05230..5f7d0c8c3 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -1473,6 +1473,49 @@ echo "UPDATE deposits SET wire='$OLD_WIRE' WHERE deposit_serial_id=${SERIAL}" |
}
+# Test for duplicate wire transfer subject
+function test_27() {
+echo "===========27: duplicate WTID detection ================="
+
+# Check wire transfer lag reported (no aggregator!)
+# NOTE: This test is EXPECTED to fail for ~1h after
+# re-generating the test database as we do not
+# report lag of less than 1h (see GRACE_PERIOD in
+# taler-wire-auditor.c)
+if [ $DATABASE_AGE -gt 3600 ]
+then
+
+ pre_audit aggregator
+
+ # Obtain data to duplicate.
+ ID=`echo "SELECT id FROM app_banktransaction WHERE debit_account_id=2 LIMIT 1" | psql $DB -Aqt`
+ WTID=`echo "SELECT subject FROM app_banktransaction WHERE debit_account_id=2 LIMIT 1" | psql $DB -Aqt`
+ echo "INSERT INTO app_banktransaction (amount,subject,date,credit_account_id,debit_account_id,cancelled) VALUES ('TESTKUDOS:1','$WTID',NOW(),12,2,'f')" | psql -Aqt $DB
+
+ audit_only
+ post_audit
+
+ echo -n "Testing inconsistency detection... "
+
+ AMOUNT=`jq -r .wire_format_inconsistencies[0].amount < test-wire-audit.json`
+ if test "${AMOUNT}" != "TESTKUDOS:1"
+ then
+ exit_fail "Amount wrong, got ${AMOUNT}"
+ fi
+
+ AMOUNT=`jq -r .total_wire_format_amount < test-wire-audit.json`
+ if test "${AMOUNT}" != "TESTKUDOS:1"
+ then
+ exit_fail "Wrong total wire format amount, got $AMOUNT"
+ fi
+
+ # cannot easily undo aggregator, hence full reload
+ full_reload
+else
+ echo "Test skipped (database too new)"
+fi
+
+}
# **************************************************
diff --git a/src/auditordb/Makefile.am b/src/auditordb/Makefile.am
index 1378c5496..3efdc200e 100644
--- a/src/auditordb/Makefile.am
+++ b/src/auditordb/Makefile.am
@@ -14,8 +14,8 @@ pkgcfg_DATA = \
sqldir = $(prefix)/share/taler/sql/auditor/
sql_DATA = \
- 0000.sql \
- 0001.sql \
+ auditor-0000.sql \
+ auditor-0001.sql \
drop0000.sql \
restart0000.sql
diff --git a/src/auditordb/0000.sql b/src/auditordb/auditor-0000.sql
index 1483e2015..1483e2015 100644
--- a/src/auditordb/0000.sql
+++ b/src/auditordb/auditor-0000.sql
diff --git a/src/auditordb/0001.sql b/src/auditordb/auditor-0001.sql
index 7f47f0357..3e666519c 100644
--- a/src/auditordb/0001.sql
+++ b/src/auditordb/auditor-0001.sql
@@ -115,7 +115,7 @@ CREATE TABLE IF NOT EXISTS auditor_reserves
,auditor_reserves_rowid BIGSERIAL UNIQUE
,origin_account TEXT
);
-CREATE INDEX auditor_reserves_by_reserve_pub
+CREATE INDEX IF NOT EXISTS auditor_reserves_by_reserve_pub
ON auditor_reserves
(reserve_pub);
-- Table with the sum of the balances of all customer reserves
@@ -203,7 +203,7 @@ CREATE TABLE IF NOT EXISTS auditor_historic_reserve_summary
,reserve_profits_val INT8 NOT NULL
,reserve_profits_frac INT4 NOT NULL
);
-CREATE INDEX auditor_historic_reserve_summary_by_master_pub_start_date
+CREATE INDEX IF NOT EXISTS auditor_historic_reserve_summary_by_master_pub_start_date
ON auditor_historic_reserve_summary
(master_pub
,start_date);
diff --git a/src/auditordb/auditordb_plugin.c b/src/auditordb/auditordb_plugin.c
index 19717fe87..d04e8815a 100644
--- a/src/auditordb/auditordb_plugin.c
+++ b/src/auditordb/auditordb_plugin.c
@@ -35,7 +35,6 @@ TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *plugin_name;
char *lib_name;
- struct GNUNET_CONFIGURATION_Handle *cfg_dup;
struct TALER_AUDITORDB_Plugin *plugin;
if (GNUNET_SYSERR ==
@@ -53,14 +52,12 @@ TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
"libtaler_plugin_auditordb_%s",
plugin_name);
GNUNET_free (plugin_name);
- cfg_dup = GNUNET_CONFIGURATION_dup (cfg);
plugin = GNUNET_PLUGIN_load (lib_name,
- cfg_dup);
+ (void *) cfg);
if (NULL != plugin)
plugin->library_name = lib_name;
else
GNUNET_free (lib_name);
- GNUNET_CONFIGURATION_destroy (cfg_dup);
return plugin;
}
diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c
index 30db303b0..ca07d406b 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -80,15 +80,9 @@ struct PostgresClosure
pthread_key_t db_conn_threadlocal;
/**
- * Directory with SQL statements to run to create tables.
+ * Our configuration.
*/
- char *sql_dir;
-
- /**
- * Database connection string, as read from
- * the configuration.
- */
- char *connection_cfg_str;
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Which currency should we assume all amounts to be in?
@@ -114,16 +108,12 @@ postgres_drop_tables (void *cls,
{
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_Context *conn;
- char *exec_dir;
-
- GNUNET_asprintf (&exec_dir,
- (drop_exchangelist) ? "%sdrop" : "%srestart",
- pc->sql_dir);
- conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- exec_dir,
- NULL,
- NULL);
- GNUNET_free (exec_dir);
+
+ conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "auditordb-postgres",
+ (drop_exchangelist) ? "drop" : "restart",
+ NULL,
+ NULL);
if (NULL == conn)
return GNUNET_SYSERR;
GNUNET_PQ_disconnect (conn);
@@ -143,10 +133,11 @@ postgres_create_tables (void *cls)
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_Context *conn;
- conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- pc->sql_dir,
- NULL,
- NULL);
+ conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "auditordb-postgres",
+ "auditor-",
+ NULL,
+ NULL);
if (NULL == conn)
return GNUNET_SYSERR;
GNUNET_PQ_disconnect (conn);
@@ -734,10 +725,11 @@ postgres_get_session (void *cls)
GNUNET_PQ_reconnect_if_down (session->conn);
return session;
}
- db_conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- NULL,
- NULL,
- ps);
+ db_conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "auditordb-postgres",
+ NULL,
+ NULL,
+ ps);
if (NULL == db_conn)
return NULL;
session = GNUNET_new (struct TALER_AUDITORDB_Session);
@@ -897,10 +889,11 @@ postgres_gc (void *cls)
};
now = GNUNET_TIME_absolute_get ();
- conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- NULL,
- NULL,
- ps);
+ conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "auditordb-postgres",
+ NULL,
+ NULL,
+ ps);
if (NULL == conn)
return GNUNET_SYSERR;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -3244,53 +3237,19 @@ postgres_get_predicted_balance (void *cls,
void *
libtaler_plugin_auditordb_postgres_init (void *cls)
{
- struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct PostgresClosure *pg;
struct TALER_AUDITORDB_Plugin *plugin;
- const char *ec;
pg = GNUNET_new (struct PostgresClosure);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- "auditordb-postgres",
- "SQL_DIR",
- &pg->sql_dir))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "auditordb-postgres",
- "SQL_DIR");
- GNUNET_free (pg);
- return NULL;
- }
+ pg->cfg = cfg;
if (0 != pthread_key_create (&pg->db_conn_threadlocal,
&db_conn_destroy))
{
TALER_LOG_ERROR ("Cannnot create pthread key.\n");
- GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
return NULL;
}
- ec = getenv ("TALER_AUDITORDB_POSTGRES_CONFIG");
- if (NULL != ec)
- {
- pg->connection_cfg_str = GNUNET_strdup (ec);
- }
- else
- {
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "auditordb-postgres",
- "CONFIG",
- &pg->connection_cfg_str))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "auditordb-postgres",
- "CONFIG");
- GNUNET_free (pg->sql_dir);
- GNUNET_free (pg);
- return NULL;
- }
- }
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"taler",
@@ -3300,8 +3259,6 @@ libtaler_plugin_auditordb_postgres_init (void *cls)
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"taler",
"CURRENCY");
- GNUNET_free (pg->connection_cfg_str);
- GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
return NULL;
}
@@ -3407,8 +3364,6 @@ libtaler_plugin_auditordb_postgres_done (void *cls)
struct TALER_AUDITORDB_Plugin *plugin = cls;
struct PostgresClosure *pg = plugin->cls;
- GNUNET_free (pg->connection_cfg_str);
- GNUNET_free (pg->sql_dir);
GNUNET_free (pg->currency);
GNUNET_free (pg);
GNUNET_free (plugin);
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 729f96e15..7227ad068 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -38,6 +38,7 @@ libtalerbank_la_LDFLAGS = \
libtalerbank_la_SOURCES = \
bank_api_admin.c \
bank_api_common.c bank_api_common.h \
+ bank_api_config.c \
bank_api_credit.c \
bank_api_debit.c \
bank_api_transfer.c \
diff --git a/src/bank-lib/bank_api_config.c b/src/bank-lib/bank_api_config.c
new file mode 100644
index 000000000..a84e4ff85
--- /dev/null
+++ b/src/bank-lib/bank_api_config.c
@@ -0,0 +1,255 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2017--2020 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/bank_api_config.c
+ * @brief Implementation of the /config request
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "bank_api_common.h"
+#include <microhttpd.h> /* just for HTTP status codes */
+#include "taler_signatures.h"
+
+/**
+ * Protocol version we implement.
+ */
+#define BANK_PROTOCOL_CURRENT 0
+
+/**
+ * How many revisions back are we compatible to.
+ */
+#define BANK_PROTOCOL_AGE 0
+
+
+/**
+ * @brief A /config Handle
+ */
+struct TALER_BANK_ConfigHandle
+{
+
+ /**
+ * The url for this request.
+ */
+ char *request_url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_BANK_ConfigCallback hcb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *hcb_cls;
+};
+
+
+/**
+ * Parse configuration given in JSON format and invoke the callback on each item.
+ *
+ * @param ch handle to the account configuration request
+ * @param config JSON object with the configuration
+ * @return #GNUNET_OK if configuration was valid and @a rconfiguration and @a balance
+ * were set,
+ * #GNUNET_SYSERR if there was a protocol violation in @a configuration
+ */
+static int
+parse_config (struct TALER_BANK_ConfigHandle *ch,
+ const json_t *config)
+{
+ struct TALER_BANK_Configuration cfg;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("version",
+ &cfg.version),
+ GNUNET_JSON_spec_string ("currency",
+ &cfg.version),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (config,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ ch->hcb (ch->hcb_cls,
+ MHD_HTTP_OK,
+ TALER_EC_NONE,
+ &cfg);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /config request.
+ *
+ * @param cls the `struct TALER_BANK_ConfigHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_configuration_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_BANK_ConfigHandle *ch = cls;
+ enum TALER_ErrorCode ec;
+ const json_t *j = response;
+
+ ch->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ ec = TALER_EC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ parse_config (ch,
+ j))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_INVALID_RESPONSE;
+ break;
+ }
+ response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
+ ec = TALER_EC_NONE;
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the bank is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ GNUNET_break_op (0);
+ ec = TALER_JSON_get_error_code (j);
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ /* Nothing really to verify, bank says the HTTP Authentication
+ failed. May happen if HTTP authentication is used and the
+ user supplied a wrong username/password combination. */
+ ec = TALER_JSON_get_error_code (j);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ ec = TALER_JSON_get_error_code (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ (unsigned int) response_code);
+ GNUNET_break_op (0);
+ ec = TALER_JSON_get_error_code (j);
+ response_code = 0;
+ break;
+ }
+ ch->hcb (ch->hcb_cls,
+ response_code,
+ ec,
+ NULL);
+ TALER_BANK_configuration_cancel (ch);
+}
+
+
+/**
+ * Request the configuration of the bank.
+ *
+ * @param ctx curl context for the event loop
+ * @param auth authentication data to use
+ * @param hres_cb the callback to call with the
+ * configuration
+ * @param hres_cb_cls closure for the above callback
+ * @return NULL if the inputs are invalid
+ */
+struct TALER_BANK_ConfigHandle *
+TALER_BANK_configuration (struct GNUNET_CURL_Context *ctx,
+ const struct TALER_BANK_AuthenticationData *auth,
+ TALER_BANK_ConfigCallback hres_cb,
+ void *hres_cb_cls)
+{
+ struct TALER_BANK_ConfigHandle *ch;
+ CURL *eh;
+
+ ch = GNUNET_new (struct TALER_BANK_ConfigHandle);
+ ch->hcb = hres_cb;
+ ch->hcb_cls = hres_cb_cls;
+ ch->request_url = TALER_url_join (auth->wire_gateway_url,
+ "config",
+ NULL);
+ if (NULL == ch->request_url)
+ {
+ GNUNET_free (ch);
+ GNUNET_break (0);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Requesting configuration at `%s'\n",
+ ch->request_url);
+ eh = curl_easy_init ();
+ if ( (GNUNET_OK !=
+ TALER_BANK_setup_auth_ (eh,
+ auth)) ||
+ (CURLE_OK !=
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ ch->request_url)) )
+ {
+ GNUNET_break (0);
+ TALER_BANK_configuration_cancel (ch);
+ curl_easy_cleanup (eh);
+ return NULL;
+ }
+ ch->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ NULL,
+ &handle_configuration_finished,
+ ch);
+ return ch;
+}
+
+
+/**
+ * Cancel a configuration request. This function cannot be
+ * used on a request handle if a response is already
+ * served for it.
+ *
+ * @param ch the configuration request handle
+ */
+void
+TALER_BANK_configuration_cancel (struct TALER_BANK_ConfigHandle *ch)
+{
+ if (NULL != ch->job)
+ {
+ GNUNET_CURL_job_cancel (ch->job);
+ ch->job = NULL;
+ }
+ GNUNET_free (ch->request_url);
+ GNUNET_free (ch);
+}
+
+
+/* end of bank_api_config.c */
diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c
index 50725a4ed..66e128da1 100644
--- a/src/bank-lib/bank_api_credit.c
+++ b/src/bank-lib/bank_api_credit.c
@@ -30,7 +30,7 @@
/**
- * @brief A /history Handle
+ * @brief A /history/incoming Handle
*/
struct TALER_BANK_CreditHistoryHandle
{
@@ -173,30 +173,21 @@ handle_credit_history_finished (void *cls,
GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j);
break;
- case MHD_HTTP_FORBIDDEN:
- /* Access denied */
- GNUNET_break_op (0);
- ec = TALER_JSON_get_error_code (j);
- break;
case MHD_HTTP_UNAUTHORIZED:
- /* FIXME(dold): I don't get this comment below. What signatures would the
- bank even verify?! */
- /* Nothing really to verify, bank says one of the signatures is
- invalid; as we checked them, this should never happen, we
- should pass the JSON reply to the application */
- GNUNET_break_op (0);
+ /* Nothing really to verify, bank says the HTTP Authentication
+ failed. May happen if HTTP authentication is used and the
+ user supplied a wrong username/password combination. */
ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
- GNUNET_break_op (0);
+ /* Nothing really to verify: the bank is either unaware
+ of the endpoint (not a bank), or of the account.
+ We should pass the JSON (?) reply to the application */
ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j);
break;
default:
diff --git a/src/bank-lib/bank_api_debit.c b/src/bank-lib/bank_api_debit.c
index 0e218eb48..58f6ae6d4 100644
--- a/src/bank-lib/bank_api_debit.c
+++ b/src/bank-lib/bank_api_debit.c
@@ -30,7 +30,7 @@
/**
- * @brief A /history Handle
+ * @brief A /history/outgoing Handle
*/
struct TALER_BANK_DebitHistoryHandle
{
@@ -129,16 +129,16 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
/**
* Function called when we're done processing the
- * HTTP /history request.
+ * HTTP /history/outgoing request.
*
* @param cls the `struct TALER_BANK_DebitHistoryHandle`
* @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error
*/
static void
-handle_history_finished (void *cls,
- long response_code,
- const void *response)
+handle_debit_history_finished (void *cls,
+ long response_code,
+ const void *response)
{
struct TALER_BANK_DebitHistoryHandle *hh = cls;
enum TALER_ErrorCode ec;
@@ -169,21 +169,19 @@ handle_history_finished (void *cls,
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
- ec = TALER_JSON_get_error_code (j);
- break;
- case MHD_HTTP_FORBIDDEN:
- /* Access denied */
+ GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_UNAUTHORIZED:
- /* Nothing really to verify, bank says one of the signatures is
- invalid; as we checked them, this should never happen, we
- should pass the JSON reply to the application */
+ /* Nothing really to verify, bank says the HTTP Authentication
+ failed. May happen if HTTP authentication is used and the
+ user supplied a wrong username/password combination. */
ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_NOT_FOUND:
- /* Nothing really to verify, this should never
- happen, we should pass the JSON reply to the application */
+ /* Nothing really to verify: the bank is either unaware
+ of the endpoint (not a bank), or of the account.
+ We should pass the JSON (?) reply to the application */
ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
@@ -196,7 +194,7 @@ handle_history_finished (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
- GNUNET_break (0);
+ GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j);
response_code = 0;
break;
@@ -292,7 +290,7 @@ TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
hh->job = GNUNET_CURL_job_add2 (ctx,
eh,
NULL,
- &handle_history_finished,
+ &handle_debit_history_finished,
hh);
return hh;
}
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index c7e324dec..47a06c59c 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -27,6 +27,24 @@
#include "taler_mhd_lib.h"
/**
+ * Taler protocol version in the format CURRENT:REVISION:AGE
+ * as used by GNU libtool. See
+ * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
+ *
+ * Please be very careful when updating and follow
+ * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
+ * precisely. Note that this version has NOTHING to do with the
+ * release version, and the format is NOT the same that semantic
+ * versioning uses either.
+ *
+ * When changing this version, you likely want to also update
+ * #BANK_PROTOCOL_CURRENT and #BANK_PROTOCOL_AGE in
+ * bank_api_config.c!
+ */
+#define BANK_PROTOCOL_VERSION "0:0:0"
+
+
+/**
* Maximum POST request size (for /admin/add-incoming)
*/
#define REQUEST_BUFFER_MAX (4 * 1024)
@@ -173,6 +191,11 @@ struct TALER_FAKEBANK_Handle
uint64_t serial_counter;
/**
+ * Currency used by the fakebank.
+ */
+ char *currency;
+
+ /**
* BaseURL of the fakebank.
*/
char *my_baseurl;
@@ -527,6 +550,7 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
h->mhd_bank = NULL;
}
GNUNET_free (h->my_baseurl);
+ GNUNET_free (h->currency);
GNUNET_free (h);
}
@@ -776,7 +800,7 @@ handle_transfer (struct TALER_FAKEBANK_Handle *h,
/**
- * Handle incoming HTTP request for /
+ * Handle incoming HTTP request for / (home page).
*
* @param h the fakebank handle
* @param connection the connection
@@ -809,6 +833,29 @@ handle_home_page (struct TALER_FAKEBANK_Handle *h,
/**
+ * Handle incoming HTTP request for /config
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param con_cls place to store state, not used
+ * @return MHD result code
+ */
+static int
+handle_config (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ void **con_cls)
+{
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s, s:s}",
+ "currency",
+ h->currency,
+ "version"
+ BANK_PROTOCOL_VERSION);
+}
+
+
+/**
* This is the "base" structure for both the /history and the
* /history-range API calls.
*/
@@ -1221,6 +1268,13 @@ serve (struct TALER_FAKEBANK_Handle *h,
connection,
con_cls);
if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ return handle_config (h,
+ connection,
+ con_cls);
+ if ( (0 == strcmp (url,
"/admin/add-incoming")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
@@ -1451,15 +1505,19 @@ run_mhd (void *cls)
* would have issued the correct wire transfer orders.
*
* @param port port to listen to
+ * @param currency currency the bank uses
* @return NULL on error
*/
struct TALER_FAKEBANK_Handle *
-TALER_FAKEBANK_start (uint16_t port)
+TALER_FAKEBANK_start (uint16_t port,
+ const char *currency)
{
struct TALER_FAKEBANK_Handle *h;
+ GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
h = GNUNET_new (struct TALER_FAKEBANK_Handle);
h->port = port;
+ h->currency = GNUNET_strdup (currency);
GNUNET_asprintf (&h->my_baseurl,
"http://localhost:%u/",
(unsigned int) port);
@@ -1480,6 +1538,7 @@ TALER_FAKEBANK_start (uint16_t port)
MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
+ GNUNET_free (h->currency);
GNUNET_free (h);
return NULL;
}
diff --git a/src/bank-lib/taler-fakebank-run.c b/src/bank-lib/taler-fakebank-run.c
index 55b3da54f..588777c94 100644
--- a/src/bank-lib/taler-fakebank-run.c
+++ b/src/bank-lib/taler-fakebank-run.c
@@ -43,12 +43,38 @@ run (void *cls,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
+ char *currency_string;
+
(void) cls;
(void) args;
(void) cfgfile;
(void) cfg;
- if (NULL == TALER_FAKEBANK_start (8082))
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "taler",
+ "CURRENCY",
+ &currency_string))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "taler",
+ "CURRENCY");
+ ret = 1;
+ return;
+ }
+ if (strlen (currency_string) >= TALER_CURRENCY_LEN)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "taler",
+ "CURRENCY",
+ "Value is too long");
+ GNUNET_free (currency_string);
+ ret = 1;
+ return;
+ }
+ if (NULL == TALER_FAKEBANK_start (8082,
+ currency_string))
ret = 1;
+ GNUNET_free (currency_string);
ret = 0;
}
diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c
index bd4d3eafe..91ed5b047 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -156,9 +156,9 @@ static char *mode_str;
static enum BenchmarkMode mode;
/**
- * Config filename.
+ * Configuration.
*/
-static char *cfg_filename;
+static struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Currency used.
@@ -445,7 +445,8 @@ launch_fakebank (void *cls)
(void) cls;
fakebank
- = TALER_TESTING_run_fakebank (exchange_bank_account.wire_gateway_url);
+ = TALER_TESTING_run_fakebank (exchange_bank_account.wire_gateway_url,
+ currency);
if (NULL == fakebank)
{
GNUNET_break (0);
@@ -624,12 +625,11 @@ parallel_benchmark (TALER_TESTING_Main main_cb,
NULL == loglev ? "INFO" : loglev,
logfile);
- result = TALER_TESTING_setup
- (main_cb,
- main_cb_cls,
- cfg_filename,
- exchanged,
- GNUNET_YES);
+ result = TALER_TESTING_setup (main_cb,
+ main_cb_cls,
+ cfg,
+ exchanged,
+ GNUNET_YES);
if (GNUNET_OK != result)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failure in child process test suite!\n");
@@ -746,7 +746,7 @@ int
main (int argc,
char *const *argv)
{
- struct GNUNET_CONFIGURATION_Handle *cfg;
+ char *cfg_filename = NULL;
struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_cfgfile (&cfg_filename)),
@@ -798,6 +798,7 @@ main (int argc,
argc,
argv)))
{
+ GNUNET_free_non_null (cfg_filename);
return BAD_CLI_ARG;
}
GNUNET_log_setup ("taler-exchange-benchmark",
@@ -814,6 +815,7 @@ main (int argc,
else
{
TALER_LOG_ERROR ("Unknown mode given: '%s'\n", mode_str);
+ GNUNET_free_non_null (cfg_filename);
return BAD_CONFIG_FILE;
}
if (NULL == cfg_filename)
@@ -825,8 +827,10 @@ main (int argc,
cfg_filename))
{
TALER_LOG_ERROR ("Could not parse configuration\n");
+ GNUNET_free (cfg_filename);
return BAD_CONFIG_FILE;
}
+ GNUNET_free (cfg_filename);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"taler",
@@ -839,6 +843,16 @@ main (int argc,
GNUNET_CONFIGURATION_destroy (cfg);
return BAD_CONFIG_FILE;
}
+
+ if (strlen (currency) >= TALER_CURRENCY_LEN)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "taler",
+ "CURRENCY",
+ "Value is too long");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return BAD_CONFIG_FILE;
+ }
if (howmany_clients > 10240)
{
TALER_LOG_ERROR ("-p option value given is too large\n");
@@ -950,11 +964,11 @@ main (int argc,
return BAD_CONFIG_FILE;
}
}
- GNUNET_CONFIGURATION_destroy (cfg);
result = parallel_benchmark (&run,
NULL,
cfg_filename);
+ GNUNET_CONFIGURATION_destroy (cfg);
/* If we're the exchange worker, we're done now. No need to print results */
if (MODE_EXCHANGE == mode)
diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c
index 33039c3d1..a11ec3f89 100644
--- a/src/exchange-tools/taler-exchange-keyup.c
+++ b/src/exchange-tools/taler-exchange-keyup.c
@@ -1437,6 +1437,8 @@ main (int argc,
"DKH",
"revoke denomination key hash (DKH) and request wallets to initiate /recoup",
&revoke_dkh),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_option_absolute_time ('t',
"time",
"TIMESTAMP",
diff --git a/src/exchange-tools/taler-exchange-wire.c b/src/exchange-tools/taler-exchange-wire.c
index 5ddc24040..0d6cdddbf 100644
--- a/src/exchange-tools/taler-exchange-wire.c
+++ b/src/exchange-tools/taler-exchange-wire.c
@@ -228,6 +228,8 @@ main (int argc,
char *const *argv)
{
const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_option_filename ('m',
"master-key",
"FILENAME",
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 50eb545b0..cf9f984de 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -83,7 +83,8 @@ taler_exchange_httpd_LDADD = \
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
check_SCRIPTS = \
- test_taler_exchange_httpd.sh
+ test_taler_exchange_httpd.sh \
+ test_taler_exchange_httpd_restart.sh
if HAVE_EXPENSIVE_TESTS
check_SCRIPTS += \
test_taler_exchange_httpd_afl.sh
@@ -97,6 +98,7 @@ TESTS = \
EXTRA_DIST = \
test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv \
test_taler_exchange_httpd.conf \
+ test_taler_exchange_unix.conf \
test_taler_exchange_httpd.get \
test_taler_exchange_httpd.post \
exchange.conf \
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
index a43277b23..d380c3915 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -582,6 +582,7 @@ shutdown_task (void *cls)
ctc = NULL;
}
TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
{
struct WireAccount *wa;
@@ -670,6 +671,7 @@ parse_wirewatch_config ()
fprintf (stderr,
"Failed to initialize DB tables\n");
TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
return GNUNET_SYSERR;
}
TALER_EXCHANGEDB_find_accounts (cfg,
@@ -680,6 +682,7 @@ parse_wirewatch_config ()
fprintf (stderr,
"No wire accounts configured for debit!\n");
TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
return GNUNET_SYSERR;
}
return GNUNET_OK;
@@ -1179,10 +1182,9 @@ expired_reserve_cb (void *cls,
/* Reserve balance was almost zero OR soft error */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reserve was virtually empty, moving on\n");
- (void) commit_or_warn (ctc->session);
- GNUNET_free (ctc->method);
- GNUNET_free (ctc);
- ctc = NULL;
+ (void) commit_or_warn (session);
+ erc->async_cont = GNUNET_YES;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return qs;
@@ -1223,6 +1225,7 @@ expired_reserve_cb (void *cls,
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
erc->async_cont = GNUNET_YES;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
GNUNET_free (ctc->method);
@@ -1299,6 +1302,7 @@ run_reserve_closures (void *cls)
case GNUNET_DB_STATUS_SOFT_ERROR:
db_plugin->rollback (db_plugin->cls,
session);
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
@@ -1308,6 +1312,7 @@ run_reserve_closures (void *cls)
reserves_idle = GNUNET_YES;
db_plugin->rollback (db_plugin->cls,
session);
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1315,6 +1320,7 @@ run_reserve_closures (void *cls)
(void) commit_or_warn (session);
if (GNUNET_YES == erc.async_cont)
break;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
@@ -1345,6 +1351,7 @@ run_aggregation (void *cls)
return;
if (0 == (++swap % 2))
{
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
@@ -1392,6 +1399,7 @@ run_aggregation (void *cls)
{
/* should re-try immediately */
swap--; /* do not count failed attempts */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1408,15 +1416,21 @@ run_aggregation (void *cls)
{
if ( (GNUNET_NO == reserves_idle) ||
(GNUNET_YES == test_mode) )
+ {
/* Possibly more to on reserves, go for it immediately */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
+ }
else
+ {
/* FIXME(dold): We might want to read the duration to sleep from the config */
/* nothing to do, sleep for a minute and try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
&run_aggregation,
NULL);
+ }
}
return;
}
@@ -1451,6 +1465,7 @@ run_aggregation (void *cls)
"Serialization issue, trying again later!\n");
db_plugin->rollback (db_plugin->cls,
session);
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1515,6 +1530,7 @@ run_aggregation (void *cls)
session);
cleanup_au ();
/* start again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1531,6 +1547,7 @@ run_aggregation (void *cls)
(void) commit_or_warn (session);
cleanup_au ();
/* start again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1587,6 +1604,7 @@ run_aggregation (void *cls)
db_plugin->rollback (db_plugin->cls,
session);
/* start again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1613,6 +1631,7 @@ run_aggregation (void *cls)
/* try again */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Commit issue for prepared wire data; trying again later!\n");
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1625,6 +1644,7 @@ run_aggregation (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Preparation complete, switching to transfer mode\n");
/* run alternative task: actually do wire transfer! */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return;
@@ -1685,6 +1705,7 @@ wire_confirm_cb (void *cls,
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
/* try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
}
@@ -1703,6 +1724,7 @@ wire_confirm_cb (void *cls,
{
case GNUNET_DB_STATUS_SOFT_ERROR:
/* try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1716,6 +1738,7 @@ wire_confirm_cb (void *cls,
"Wire transfer complete\n");
/* continue with #run_transfers(), just to guard
against the unlikely case that there are more. */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return;
@@ -1846,6 +1869,7 @@ run_transfers (void *cls)
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
/* try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return;
@@ -1853,6 +1877,7 @@ run_transfers (void *cls)
/* no more prepared wire transfers, go back to aggregation! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No more pending wire transfers, starting aggregation\n");
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1898,6 +1923,7 @@ run (void *cls,
return;
}
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
@@ -1917,6 +1943,8 @@ main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_option_flag ('t',
"test",
"run in test mode and exit when idle",
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 666cec0c1..426c48655 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -999,6 +999,8 @@ main (int argc,
"SECONDS",
"after how long do connections timeout by default (in seconds)",
&connection_timeout),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
#if HAVE_DEVELOPER
GNUNET_GETOPT_option_filename ('f',
"file-input",
diff --git a/src/exchange/taler-exchange-httpd_mhd.c b/src/exchange/taler-exchange-httpd_mhd.c
index 53944b6a7..0f2ce033a 100644
--- a/src/exchange/taler-exchange-httpd_mhd.c
+++ b/src/exchange/taler-exchange-httpd_mhd.c
@@ -128,11 +128,10 @@ TEH_MHD_handler_send_json_pack_error (struct TEH_RequestHandler *rh,
(void) connection_cls;
(void) upload_data;
(void) upload_data_size;
- return TALER_MHD_reply_json_pack (connection,
- rh->response_code,
- "{s:s}",
- "error",
- rh->data);
+ return TALER_MHD_reply_with_error (connection,
+ rh->response_code,
+ TALER_EC_METHOD_INVALID,
+ rh->data);
}
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c b/src/exchange/taler-exchange-httpd_refresh_melt.c
index 71200037d..c7dc700f7 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.c
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -69,7 +69,7 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
- "error",
+ "hint",
"insufficient funds",
"code",
(json_int_t)
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c
index bbafdd1a1..6ac91caf8 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -108,7 +108,7 @@ reply_refresh_reveal_mismatch (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o}",
- "error", "commitment violation",
+ "hint", "commitment violation",
"code",
(json_int_t)
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index 8c6e90302..8e24b9b47 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -83,28 +83,6 @@ reply_refund_success (struct MHD_Connection *connection,
/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code. The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec taler error code to include
- * @return MHD result code
- */
-static int
-reply_refund_failure (struct MHD_Connection *connection,
- unsigned int response_code,
- enum TALER_ErrorCode ec)
-{
- return TALER_MHD_reply_json_pack (connection,
- response_code,
- "{s:s, s:I}",
- "hint", "refund failure",
- "code", (json_int_t) ec);
-}
-
-
-/**
* Generate refund conflict failure message. Returns the
* transaction list @a tl with the details about the conflict.
*
@@ -194,9 +172,10 @@ refund_transaction (void *cls,
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_REFUND_COIN_NOT_FOUND,
+ "database transaction failure");
return qs;
}
deposit_found = GNUNET_NO;
@@ -319,9 +298,10 @@ refund_transaction (void *cls,
GNUNET_break_op (0); /* currency mismatch */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_PRECONDITION_FAILED,
- TALER_EC_REFUND_CURRENCY_MISSMATCH);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_REFUND_CURRENCY_MISSMATCH,
+ "currencies involved do not match");
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -353,9 +333,10 @@ refund_transaction (void *cls,
/* money was already transferred to merchant, can no longer refund */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_GONE,
- TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_GONE,
+ TALER_EC_REFUND_MERCHANT_ALREADY_PAID,
+ "money already sent to merchant");
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -366,9 +347,10 @@ refund_transaction (void *cls,
GNUNET_break_op (0); /* cannot refund more than original value */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_PRECONDITION_FAILED,
- TALER_EC_REFUND_INSUFFICIENT_FUNDS);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_REFUND_INSUFFICIENT_FUNDS,
+ "refund requested exceeds original value");
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* Check refund fee matches fee of denomination key! */
@@ -481,9 +463,10 @@ verify_and_execute_refund (struct MHD_Connection *connection,
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return reply_refund_failure (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_REFUND_COIN_NOT_FOUND,
+ "denomination of coin to be refunded not found in DB");
}
}
diff --git a/src/exchange/taler-exchange-httpd_reserve_status.c b/src/exchange/taler-exchange-httpd_reserve_status.c
index 89bf8b38a..e2d35aaec 100644
--- a/src/exchange/taler-exchange-httpd_reserve_status.c
+++ b/src/exchange/taler-exchange-httpd_reserve_status.c
@@ -164,7 +164,7 @@ TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
"{s:s, s:s, s:I}",
- "error", "Reserve not found",
+ "hint", "Reserve not found",
"parameter", "reserve_pub",
"code",
(json_int_t)
diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
index 86633cd98..9daad0a02 100644
--- a/src/exchange/taler-exchange-httpd_reserve_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
@@ -74,7 +74,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o, s:o}",
- "error", "Insufficient funds",
+ "hint", "insufficient funds",
"code",
(json_int_t)
TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index c88a8a254..90ca14c88 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -446,7 +446,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o}",
- "error", "insufficient funds",
+ "hint", "insufficient funds",
"code", (json_int_t) ec,
"history", history);
}
diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c
index 21d96668b..69929e127 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -340,7 +340,7 @@ history_cb (void *cls,
hh = NULL;
if (TALER_EC_NONE != ec)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Error fetching history: ec=%u, http_status=%u\n",
(unsigned int) ec,
http_status);
@@ -597,7 +597,9 @@ main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_option_flag ('T',
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
+ GNUNET_GETOPT_option_flag ('t',
"test",
"run in test mode and exit when idle",
&test_mode),
diff --git a/src/exchange/test_taler_exchange_httpd_restart.sh b/src/exchange/test_taler_exchange_httpd_restart.sh
new file mode 100755
index 000000000..f7d7a3050
--- /dev/null
+++ b/src/exchange/test_taler_exchange_httpd_restart.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+#
+# This file is part of TALER
+# Copyright (C) 2020 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
+# Foundation; either version 3, or (at your option) any later version.
+#
+# TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License along with
+# TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+#
+#
+# This script launches an exchange (binding to a UNIX domain socket) and then
+# restarts it in various ways (SIGHUP to re-read configuration, and SIGUSR1 to
+# re-spawn a new binary). Basically, the goal is to make sure that the HTTP
+# server survives these less common operations.
+#
+#
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo $1
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo $1
+ kill `jobs -p` >/dev/null 2>/dev/null || true
+ wait
+ exit 1
+}
+
+echo -n "Testing for curl"
+curl --version >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+
+# Clear environment from variables that override config.
+unset XDG_DATA_HOME
+unset XDG_CONFIG_HOME
+#
+echo -n "Launching exchange ..."
+PREFIX=
+# Uncomment this line to run with valgrind...
+# PREFIX="valgrind --trace-children=yes --leak-check=yes --track-fds=yes --error-exitcode=1 --log-file=valgrind.%p"
+
+# Setup keys.
+taler-exchange-keyup -c test_taler_exchange_unix.conf || exit 1
+# Setup wire accounts.
+taler-exchange-wire -c test_taler_exchange_unix.conf > /dev/null || exit 1
+# Run Exchange HTTPD (in background)
+$PREFIX taler-exchange-httpd -c test_taler_exchange_unix.conf -i 2> test-exchange.log &
+
+# Where should we be bound to?
+UNIXPATH=`taler-config -s exchange -f -o UNIXPATH`
+
+# Give HTTP time to start
+
+for n in `seq 1 100`
+do
+ echo -n "."
+ sleep 0.1
+ OK=1
+ curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null && break
+ OK=0
+done
+if [ 1 != $OK ]
+then
+ exit_fail "Failed to launch exchange"
+fi
+echo " DONE"
+
+# Finally run test...
+echo -n "Reloading keys ..."
+kill -SIGUSR1 $!
+sleep 1
+curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGUSR1 killed HTTP service"
+echo " DONE"
+
+# Finally run test...
+echo -n "Restarting program ..."
+kill -SIGHUP $!
+sleep 1
+curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGHUP killed HTTP service"
+echo " DONE"
+
+echo -n "Waiting for parent to die ..."
+wait $!
+echo " DONE"
+
+echo -n "Testing child still alive ..."
+curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGHUP killed HTTP service"
+echo " DONE"
+
+
+echo -n "Killing grandchild ..."
+CPID=`ps x | grep taler-exchange-httpd | grep -v grep | awk '{print $1}'`
+kill -TERM $CPID
+while true
+do
+ ps x | grep -v grep | grep taler-exchange-httpd > /dev/null || break
+ sleep 0.1
+done
+echo " DONE"
+
+# Return status code from exchange for this script
+exit 0
diff --git a/src/exchange/test_taler_exchange_unix.conf b/src/exchange/test_taler_exchange_unix.conf
new file mode 100644
index 000000000..d41df9abe
--- /dev/null
+++ b/src/exchange/test_taler_exchange_unix.conf
@@ -0,0 +1,137 @@
+[PATHS]
+# Persistant data storage for the testcase
+TALER_TEST_HOME = test_taler_exchange_httpd_home/
+
+[taler]
+# Currency supported by the exchange (can only be one)
+CURRENCY = EUR
+CURRENCY_ROUND_UNIT = EUR:0.01
+
+[exchange]
+
+# Directory with our terms of service.
+TERMS_DIR = ../../contrib/tos
+
+# Etag / filename for the terms of service.
+TERMS_ETAG = 0
+
+
+# Directory with our privacy policy.
+PRIVACY_DIR = ../../contrib/pp
+
+# Etag / filename for the privacy policy.
+PRIVACY_ETAG = 0
+
+# MAX_REQUESTS = 2
+# 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
+
+# HTTP port the exchange listens to (we want to use UNIX domain sockets,
+# so we use a port that just won't work on GNU/Linux without root rights)
+PORT = 999
+
+# Here we say we want to use a UNIX domain socket (to test that logic).
+SERVE = unix
+
+# Master public key used to sign the exchange's various keys
+MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
+
+# How to access our database
+DB = postgres
+
+
+[exchangedb]
+# After how long do we close idle reserves? The exchange
+# and the auditor must agree on this value. We currently
+# expect it to be globally defined for the whole system,
+# as there is no way for wallets to query this value. Thus,
+# it is only configurable for testing, and should be treated
+# as constant in production.
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+
+
+[exchangedb-postgres]
+CONFIG = "postgres:///talercheck"
+
+[exchange-account-1]
+PAYTO_URI = "payto://x-taler-bank/localhost:8082/3"
+WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
+ENABLE_DEBIT = YES
+ENABLE_CREDIT = YES
+TALER_BANK_AUTH_METHOD = NONE
+
+
+# Wire fees are specified by wire method
+[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.
+[coin_eur_ct_1]
+value = EUR:0.01
+duration_overlap = 5 minutes
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_ct_10]
+value = EUR:0.10
+duration_overlap = 5 minutes
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_1]
+value = EUR:1
+duration_overlap = 5 minutes
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index e7ac4d719..37809f6b4 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -15,8 +15,8 @@ pkgcfg_DATA = \
sqldir = $(prefix)/share/taler/sql/exchange/
sql_DATA = \
- 0000.sql \
- 0001.sql \
+ exchange-0000.sql \
+ exchange-0001.sql \
drop0000.sql
EXTRA_DIST = \
diff --git a/src/exchangedb/0000.sql b/src/exchangedb/exchange-0000.sql
index 1483e2015..1483e2015 100644
--- a/src/exchangedb/0000.sql
+++ b/src/exchangedb/exchange-0000.sql
diff --git a/src/exchangedb/0001.sql b/src/exchangedb/exchange-0001.sql
index 8e7ea0bf5..02dc68cf4 100644
--- a/src/exchangedb/0001.sql
+++ b/src/exchangedb/exchange-0001.sql
@@ -42,7 +42,7 @@ CREATE TABLE IF NOT EXISTS denominations
,fee_refund_val INT8 NOT NULL
,fee_refund_frac INT4 NOT NULL
);
-CREATE INDEX denominations_expire_legal_index
+CREATE INDEX IF NOT EXISTS denominations_expire_legal_index
ON denominations
(expire_legal);
@@ -65,18 +65,18 @@ CREATE TABLE IF NOT EXISTS reserves
,gc_date INT8 NOT NULL
);
-- index on reserves table (TODO: useless due to primary key!?)
-CREATE INDEX reserves_reserve_pub_index
+CREATE INDEX IF NOT EXISTS reserves_reserve_pub_index
ON reserves
(reserve_pub);
-- index for get_expired_reserves
-CREATE INDEX reserves_expiration_index
+CREATE INDEX IF NOT EXISTS reserves_expiration_index
ON reserves
(expiration_date
,current_balance_val
,current_balance_frac
);
-- index for reserve GC operations
-CREATE INDEX reserves_gc_index
+CREATE INDEX IF NOT EXISTS reserves_gc_index
ON reserves
(gc_date);
-- reserves_in table collects the transactions which transfer funds
@@ -94,12 +94,12 @@ CREATE TABLE IF NOT EXISTS reserves_in
,PRIMARY KEY (reserve_pub, wire_reference)
);
-- Create indices on reserves_in
-CREATE INDEX reserves_in_execution_index
+CREATE INDEX IF NOT EXISTS reserves_in_execution_index
ON reserves_in
(exchange_account_section
,execution_date
);
-CREATE INDEX reserves_in_exchange_account_serial
+CREATE INDEX IF NOT EXISTS reserves_in_exchange_account_serial
ON reserves_in
(exchange_account_section,
reserve_in_serial_id DESC
@@ -116,7 +116,7 @@ CREATE TABLE IF NOT EXISTS reserves_close
,amount_frac INT4 NOT NULL
,closing_fee_val INT8 NOT NULL
,closing_fee_frac INT4 NOT NULL);
-CREATE INDEX reserves_close_by_reserve
+CREATE INDEX IF NOT EXISTS reserves_close_by_reserve
ON reserves_close
(reserve_pub);
-- Table with the withdraw operations that have been performed on a reserve.
@@ -137,13 +137,13 @@ CREATE TABLE IF NOT EXISTS reserves_out
,amount_with_fee_frac INT4 NOT NULL
);
-- Index blindcoins(reserve_pub) for get_reserves_out statement
-CREATE INDEX reserves_out_reserve_pub_index
+CREATE INDEX IF NOT EXISTS reserves_out_reserve_pub_index
ON reserves_out
(reserve_pub);
-CREATE INDEX reserves_out_execution_date
+CREATE INDEX IF NOT EXISTS reserves_out_execution_date
ON reserves_out
(execution_date);
-CREATE INDEX reserves_out_for_get_withdraw_info
+CREATE INDEX IF NOT EXISTS reserves_out_for_get_withdraw_info
ON reserves_out
(denom_pub_hash
,h_blind_ev
@@ -155,7 +155,7 @@ CREATE TABLE IF NOT EXISTS known_coins
,denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE
,denom_sig BYTEA NOT NULL
);
-CREATE INDEX known_coins_by_denomination
+CREATE INDEX IF NOT EXISTS known_coins_by_denomination
ON known_coins
(denom_pub_hash);
-- Table with the commitments made when melting a coin. */
@@ -168,7 +168,7 @@ CREATE TABLE IF NOT EXISTS refresh_commitments
,amount_with_fee_frac INT4 NOT NULL
,noreveal_index INT4 NOT NULL
);
-CREATE INDEX refresh_commitments_old_coin_pub_index
+CREATE INDEX IF NOT EXISTS refresh_commitments_old_coin_pub_index
ON refresh_commitments
(old_coin_pub);
-- Table with the revelations about the new coins that are to be created
@@ -188,7 +188,7 @@ CREATE TABLE IF NOT EXISTS refresh_revealed_coins
,PRIMARY KEY (rc, newcoin_index)
,UNIQUE (h_coin_ev)
);
-CREATE INDEX refresh_revealed_coins_coin_pub_index
+CREATE INDEX IF NOT EXISTS refresh_revealed_coins_coin_pub_index
ON refresh_revealed_coins
(denom_pub_hash);
-- Table with the transfer keys of a refresh operation; includes
@@ -203,7 +203,7 @@ CREATE TABLE IF NOT EXISTS refresh_transfer_keys
-- for get_link (not sure if this helps, as there should be very few
-- transfer_pubs per rc, but at least in theory this helps the ORDER BY
-- clause.
-CREATE INDEX refresh_transfer_keys_coin_tpub
+CREATE INDEX IF NOT EXISTS refresh_transfer_keys_coin_tpub
ON refresh_transfer_keys
(rc
,transfer_pub
@@ -228,14 +228,14 @@ CREATE TABLE IF NOT EXISTS deposits
,UNIQUE (coin_pub, merchant_pub, h_contract_terms)
);
-- Index for get_deposit_for_wtid and get_deposit_statement */
-CREATE INDEX deposits_coin_pub_merchant_contract_index
+CREATE INDEX IF NOT EXISTS deposits_coin_pub_merchant_contract_index
ON deposits
(coin_pub
,merchant_pub
,h_contract_terms
);
-- Index for deposits_get_ready
-CREATE INDEX deposits_get_ready_index
+CREATE INDEX IF NOT EXISTS deposits_get_ready_index
ON deposits
(tiny
,done
@@ -243,7 +243,7 @@ CREATE INDEX deposits_get_ready_index
,refund_deadline
);
-- Index for deposits_iterate_matching
-CREATE INDEX deposits_iterate_matching
+CREATE INDEX IF NOT EXISTS deposits_iterate_matching
ON deposits
(merchant_pub
,h_wire
@@ -265,7 +265,7 @@ CREATE TABLE IF NOT EXISTS refunds
,amount_with_fee_frac INT4 NOT NULL
,PRIMARY KEY (coin_pub, merchant_pub, h_contract_terms, rtransaction_id)
);
-CREATE INDEX refunds_coin_pub_index
+CREATE INDEX IF NOT EXISTS refunds_coin_pub_index
ON refunds
(coin_pub);
-- This table contains the data for
@@ -287,7 +287,7 @@ CREATE TABLE IF NOT EXISTS aggregation_tracking
,wtid_raw BYTEA CONSTRAINT wire_out_ref REFERENCES wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE
);
-- Index for lookup_transactions statement on wtid
-CREATE INDEX aggregation_tracking_wtid_index
+CREATE INDEX IF NOT EXISTS aggregation_tracking_wtid_index
ON aggregation_tracking
(wtid_raw);
-- Table for the wire fees.
@@ -302,7 +302,7 @@ CREATE TABLE IF NOT EXISTS wire_fee
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,PRIMARY KEY (wire_method, start_date)
);
-CREATE INDEX wire_fee_gc_index
+CREATE INDEX IF NOT EXISTS wire_fee_gc_index
ON wire_fee
(end_date);
-- Table for /recoup information
@@ -317,13 +317,13 @@ CREATE TABLE IF NOT EXISTS recoup
,timestamp INT8 NOT NULL
,h_blind_ev BYTEA NOT NULL REFERENCES reserves_out (h_blind_ev) ON DELETE CASCADE
);
-CREATE INDEX recoup_by_coin_index
+CREATE INDEX IF NOT EXISTS recoup_by_coin_index
ON recoup
(coin_pub);
-CREATE INDEX recoup_by_h_blind_ev
+CREATE INDEX IF NOT EXISTS recoup_by_h_blind_ev
ON recoup
(h_blind_ev);
-CREATE INDEX recoup_for_by_reserve
+CREATE INDEX IF NOT EXISTS recoup_for_by_reserve
ON recoup
(coin_pub
,h_blind_ev
@@ -340,13 +340,13 @@ CREATE TABLE IF NOT EXISTS recoup_refresh
,timestamp INT8 NOT NULL
,h_blind_ev BYTEA NOT NULL REFERENCES refresh_revealed_coins (h_coin_ev) ON DELETE CASCADE
);
-CREATE INDEX recoup_refresh_by_coin_index
+CREATE INDEX IF NOT EXISTS recoup_refresh_by_coin_index
ON recoup_refresh
(coin_pub);
-CREATE INDEX recoup_refresh_by_h_blind_ev
+CREATE INDEX IF NOT EXISTS recoup_refresh_by_h_blind_ev
ON recoup_refresh
(h_blind_ev);
-CREATE INDEX recoup_refresh_for_by_reserve
+CREATE INDEX IF NOT EXISTS recoup_refresh_for_by_reserve
ON recoup_refresh
(coin_pub
,h_blind_ev
@@ -360,7 +360,7 @@ CREATE TABLE IF NOT EXISTS prewire
,buf BYTEA NOT NULL
);
-- Index for wire_prepare_data_get and gc_prewire statement
-CREATE INDEX prepare_iteration_index
+CREATE INDEX IF NOT EXISTS prepare_iteration_index
ON prewire
(finished);
diff --git a/src/exchangedb/exchangedb_plugin.c b/src/exchangedb/exchangedb_plugin.c
index 8e61f860b..f4c2eea99 100644
--- a/src/exchangedb/exchangedb_plugin.c
+++ b/src/exchangedb/exchangedb_plugin.c
@@ -35,7 +35,6 @@ TALER_EXCHANGEDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *plugin_name;
char *lib_name;
- struct GNUNET_CONFIGURATION_Handle *cfg_dup;
struct TALER_EXCHANGEDB_Plugin *plugin;
if (GNUNET_SYSERR ==
@@ -53,13 +52,12 @@ TALER_EXCHANGEDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
"libtaler_plugin_exchangedb_%s",
plugin_name);
GNUNET_free (plugin_name);
- cfg_dup = GNUNET_CONFIGURATION_dup (cfg);
- plugin = GNUNET_PLUGIN_load (lib_name, cfg_dup);
+ plugin = GNUNET_PLUGIN_load (lib_name,
+ (void *) cfg);
if (NULL != plugin)
plugin->library_name = lib_name;
else
GNUNET_free (lib_name);
- GNUNET_CONFIGURATION_destroy (cfg_dup);
return plugin;
}
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 4d0b1bb6a..9df2fe770 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -109,10 +109,9 @@ struct PostgresClosure
pthread_key_t db_conn_threadlocal;
/**
- * Database connection string, as read from
- * the configuration.
+ * Our configuration.
*/
- char *connection_cfg_str;
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Directory with SQL statements to run to create tables.
@@ -158,16 +157,12 @@ postgres_drop_tables (void *cls)
{
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_Context *conn;
- char *drop_dir;
-
- GNUNET_asprintf (&drop_dir,
- "%sdrop",
- pc->sql_dir);
- conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- drop_dir,
- NULL,
- NULL);
- GNUNET_free (drop_dir);
+
+ conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "exchangedb-postgres",
+ "drop",
+ NULL,
+ NULL);
if (NULL == conn)
return GNUNET_SYSERR;
GNUNET_PQ_disconnect (conn);
@@ -187,10 +182,11 @@ postgres_create_tables (void *cls)
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_Context *conn;
- conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- pc->sql_dir,
- NULL,
- NULL);
+ conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "exchangedb-postgres",
+ "exchange-",
+ NULL,
+ NULL);
if (NULL == conn)
return GNUNET_SYSERR;
GNUNET_PQ_disconnect (conn);
@@ -1387,10 +1383,11 @@ postgres_get_session (void *cls)
GNUNET_PQ_PREPARED_STATEMENT_END
};
- db_conn = GNUNET_PQ_connect (pc->connection_cfg_str,
- NULL,
- es,
- ps);
+ db_conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
+ "exchangedb-postgres",
+ NULL,
+ es,
+ ps);
}
if (NULL == db_conn)
return NULL;
@@ -5371,10 +5368,11 @@ postgres_gc (void *cls)
GNUNET_PQ_PREPARED_STATEMENT_END
};
- conn = GNUNET_PQ_connect (pg->connection_cfg_str,
- NULL,
- NULL,
- ps);
+ conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
+ "exchangedb-postgres",
+ NULL,
+ NULL,
+ ps);
}
if (NULL == conn)
return GNUNET_SYSERR;
@@ -7212,12 +7210,12 @@ postgres_select_deposits_missing_wire (void *cls,
void *
libtaler_plugin_exchangedb_postgres_init (void *cls)
{
- struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct PostgresClosure *pg;
struct TALER_EXCHANGEDB_Plugin *plugin;
- const char *ec;
pg = GNUNET_new (struct PostgresClosure);
+ pg->cfg = cfg;
pg->main_self = pthread_self (); /* loaded while single-threaded! */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg,
@@ -7239,28 +7237,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
GNUNET_free (pg);
return NULL;
}
- ec = getenv ("TALER_EXCHANGEDB_POSTGRES_CONFIG");
- if (NULL != ec)
- {
- pg->connection_cfg_str = GNUNET_strdup (ec);
- }
- else
- {
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "exchangedb-postgres",
- "CONFIG",
- &pg->connection_cfg_str))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchangedb-postgres",
- "CONFIG");
- GNUNET_free (pg->sql_dir);
- GNUNET_free (pg);
- return NULL;
- }
- }
-
if ( (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (cfg,
"exchangedb",
@@ -7276,7 +7252,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchangedb",
"LEGAL/IDLE_RESERVE_EXPIRATION_TIME");
- GNUNET_free (pg->connection_cfg_str);
GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
return NULL;
@@ -7290,7 +7265,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"taler",
"CURRENCY");
- GNUNET_free (pg->connection_cfg_str);
GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
return NULL;
@@ -7406,7 +7380,6 @@ libtaler_plugin_exchangedb_postgres_done (void *cls)
/* If we launched a session for the main thread,
kill it here before we unload */
db_conn_destroy (pg->main_session);
- GNUNET_free (pg->connection_cfg_str);
GNUNET_free (pg->sql_dir);
GNUNET_free (pg->currency);
GNUNET_free (pg);
diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h
index 98a1017c2..c9c93fac5 100644
--- a/src/include/taler_bank_service.h
+++ b/src/include/taler_bank_service.h
@@ -89,6 +89,73 @@ struct TALER_BANK_AuthenticationData
};
+/* ********************* /config *********************** */
+
+/**
+ * @brief A /config Handle
+ */
+struct TALER_BANK_ConfigHandle;
+
+/**
+ * Configuration data provided by the bank.
+ */
+struct TALER_BANK_Configuration
+{
+ /**
+ * Current protocol version. Libtool style.
+ */
+ const char *version;
+
+ /**
+ * Currency used by the bank.
+ */
+ const char *currency;
+};
+
+
+/**
+ * Function called with configuration details from the bank.
+ *
+ * @param cls closure
+ * @param http status code
+ * @param ec taler error code
+ * @param config the configuration, NULL on error
+ */
+typedef void
+(*TALER_BANK_ConfigCallback)(void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const struct TALER_BANK_Configuration *config);
+
+/**
+ * Request the configuration of the bank.
+ *
+ * @param ctx curl context for the event loop
+ * @param auth authentication data to use
+ * @param hres_cb the callback to call with the
+ * configuration
+ * @param hres_cb_cls closure for the above callback
+ * @return NULL if the inputs are invalid (i.e. zero value for
+ * @e num_results). In this case, the callback is not
+ * called.
+ */
+struct TALER_BANK_ConfigHandle *
+TALER_BANK_configuration (struct GNUNET_CURL_Context *ctx,
+ const struct TALER_BANK_AuthenticationData *auth,
+ TALER_BANK_ConfigCallback hres_cb,
+ void *hres_cb_cls);
+
+
+/**
+ * Cancel a configuration request. This function cannot be
+ * used on a request handle if a response is already
+ * served for it.
+ *
+ * @param ch the configuration request handle
+ */
+void
+TALER_BANK_configuration_cancel (struct TALER_BANK_ConfigHandle *ch);
+
/* ********************* /admin/add/incoming *********************** */
@@ -160,7 +227,7 @@ TALER_BANK_admin_add_incoming_cancel (struct
TALER_BANK_AdminAddIncomingHandle *aai);
-/* ********************* /taler/transfer *********************** */
+/* ********************* /transfer *********************** */
/**
* Prepare for exeuction of a wire transfer.
@@ -243,7 +310,7 @@ TALER_BANK_execute_wire_transfer_cancel (struct
TALER_BANK_WireExecuteHandle *weh);
-/* ********************* /taler/credits *********************** */
+/* ********************* /history/incoming *********************** */
/**
* Handle for querying the bank for transactions
@@ -347,7 +414,7 @@ void
TALER_BANK_credit_history_cancel (struct TALER_BANK_CreditHistoryHandle *hh);
-/* ********************* /taler/debits *********************** */
+/* ********************* /history/outgoing *********************** */
/**
* Handle for querying the bank for transactions
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index ac7f92888..917ac36db 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -81,6 +81,11 @@ enum TALER_ErrorCode
TALER_EC_JSON_ALLOCATION_FAILURE = 7,
/**
+ * HTTP method invalid for this URL.
+ */
+ TALER_EC_METHOD_INVALID = 8,
+
+ /**
* The exchange failed to even just initialize its connection to the
* database. This response is provided with HTTP status code
* MHD_HTTP_INTERNAL_SERVER_ERROR.
diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h
index 9324bdce1..8601a8d8d 100644
--- a/src/include/taler_fakebank_lib.h
+++ b/src/include/taler_fakebank_lib.h
@@ -44,10 +44,12 @@ struct TALER_FAKEBANK_Handle;
* would have issued the correct wire transfer orders.
*
* @param port port to listen to
+ * @param currency which currency should the bank offer
* @return NULL on error
*/
struct TALER_FAKEBANK_Handle *
-TALER_FAKEBANK_start (uint16_t port);
+TALER_FAKEBANK_start (uint16_t port,
+ const char *currency);
/**
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 0b3d19999..5aa50155c 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -635,7 +635,7 @@ typedef void
* @param main_cb the "run" method which coontains all the
* commands.
* @param main_cb_cls a closure for "run", typically NULL.
- * @param config_filename configuration filename.
+ * @param cfg configuration to use
* @param exchanged exchange process handle: will be put in the
* state as some commands - e.g. revoke - need to send
* signal to it, for example to let it know to reload the
@@ -650,7 +650,7 @@ typedef void
int
TALER_TESTING_setup (TALER_TESTING_Main main_cb,
void *main_cb_cls,
- const char *config_filename,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
struct GNUNET_OS_Process *exchanged,
int exchange_connect);
@@ -789,11 +789,13 @@ TALER_TESTING_run_bank (const char *config_filename,
* from the base URL.
*
* @param bank_url bank's base URL.
+ * @param currency currency the bank uses
* @return the fakebank process handle, or NULL if any
* error occurs.
*/
struct TALER_FAKEBANK_Handle *
-TALER_TESTING_run_fakebank (const char *bank_url);
+TALER_TESTING_run_fakebank (const char *bank_url,
+ const char *currency);
/**
diff --git a/src/json/json.c b/src/json/json.c
index 6de299319..f0c0aff57 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -81,7 +81,8 @@ TALER_JSON_get_error_code (const json_t *json)
so we are dealing with a missing error code here. */
if (NULL == jc)
{
- GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected Taler error code `code' in JSON, but field does not exist!\n");
return TALER_EC_INVALID;
}
if (json_is_integer (jc))
diff --git a/src/testing/test_bank_api.c b/src/testing/test_bank_api.c
index bdafdc5fd..75e22b59e 100644
--- a/src/testing/test_bank_api.c
+++ b/src/testing/test_bank_api.c
@@ -52,6 +52,7 @@ static struct GNUNET_OS_Process *bankd;
*/
static int with_fakebank;
+
/**
* Main function that will tell the interpreter what commands to
* run.
@@ -116,12 +117,32 @@ run (void *cls,
}
+/**
+ * Runs #TALER_TESTING_setup() using the configuration.
+ *
+ * @param cls unused
+ * @param cfg configuration to use
+ * @return status code
+ */
+static int
+setup_with_cfg (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ (void) cls;
+ return TALER_TESTING_setup (&run,
+ NULL,
+ cfg,
+ NULL,
+ GNUNET_NO);
+}
+
+
int
main (int argc,
char *const *argv)
{
- int rv;
const char *cfgfilename;
+ int rv;
/* These environment variables get in the way... */
unsetenv ("XDG_DATA_HOME");
@@ -165,12 +186,13 @@ main (int argc,
return 77;
}
}
-
- rv = (GNUNET_OK == TALER_TESTING_setup (&run,
- NULL,
- cfgfilename,
- NULL,
- GNUNET_NO)) ? 0 : 1;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_parse_and_run (cfgfilename,
+ &setup_with_cfg,
+ NULL))
+ rv = 1;
+ else
+ rv = 0;
if (GNUNET_NO == with_fakebank)
{
diff --git a/src/testing/test_taler_exchange_aggregator.c b/src/testing/test_taler_exchange_aggregator.c
index c709e5fe9..2e8a35ce2 100644
--- a/src/testing/test_taler_exchange_aggregator.c
+++ b/src/testing/test_taler_exchange_aggregator.c
@@ -57,36 +57,6 @@ static char *config_filename;
#define USER42_ACCOUNT "42"
-/**
- * @return GNUNET_NO if database could not be prepared,
- * otherwise GNUNET_OK
- */
-static int
-prepare_database (void *cls,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- dbc.plugin = TALER_EXCHANGEDB_plugin_load (cfg);
- if (NULL == dbc.plugin)
- {
- GNUNET_break (0);
- result = 77;
- return GNUNET_NO;
- }
- if (GNUNET_OK !=
- dbc.plugin->create_tables (dbc.plugin->cls))
- {
- GNUNET_break (0);
- TALER_EXCHANGEDB_plugin_unload (dbc.plugin);
- dbc.plugin = NULL;
- result = 77;
- return GNUNET_NO;
- }
- dbc.session = dbc.plugin->get_session (dbc.plugin->cls);
- GNUNET_assert (NULL != dbc.session);
-
- return GNUNET_OK;
-}
-
/**
* Collects all the tests.
@@ -454,6 +424,48 @@ run (void *cls,
}
+/**
+ * Prepare database an launch the test.
+ *
+ * @param cls unused
+ * @param cfg our configuration
+ * @return #GNUNET_NO if database could not be prepared,
+ * otherwise #GNUNET_OK
+ */
+static int
+prepare_database (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ dbc.plugin = TALER_EXCHANGEDB_plugin_load (cfg);
+ if (NULL == dbc.plugin)
+ {
+ GNUNET_break (0);
+ result = 77;
+ return GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ dbc.plugin->create_tables (dbc.plugin->cls))
+ {
+ GNUNET_break (0);
+ TALER_EXCHANGEDB_plugin_unload (dbc.plugin);
+ dbc.plugin = NULL;
+ result = 77;
+ return GNUNET_NO;
+ }
+ dbc.session = dbc.plugin->get_session (dbc.plugin->cls);
+ GNUNET_assert (NULL != dbc.session);
+
+ result = TALER_TESTING_setup (&run,
+ NULL,
+ cfg,
+ NULL, // no exchange process handle.
+ GNUNET_NO); // do not try to connect to the exchange
+
+
+ return GNUNET_OK;
+}
+
+
int
main (int argc,
char *const argv[])
@@ -507,12 +519,6 @@ main (int argc,
return result;
}
- result = TALER_TESTING_setup (&run,
- NULL,
- config_filename,
- NULL, // no exchange process handle.
- GNUNET_NO); // do not try to connect to the exchange
-
GNUNET_free (config_filename);
GNUNET_free (testname);
dbc.plugin->drop_tables (dbc.plugin->cls);
diff --git a/src/testing/testing_api_cmd_exec_wirewatch.c b/src/testing/testing_api_cmd_exec_wirewatch.c
index 44de96838..7a1a27a51 100644
--- a/src/testing/testing_api_cmd_exec_wirewatch.c
+++ b/src/testing/testing_api_cmd_exec_wirewatch.c
@@ -67,7 +67,7 @@ wirewatch_run (void *cls,
"taler-exchange-wirewatch",
"taler-exchange-wirewatch",
"-c", ws->config_filename,
- "-T", /* exit when done */
+ "-t", /* exit when done */
NULL);
if (NULL == ws->wirewatch_proc)
{
diff --git a/src/testing/testing_api_helpers_bank.c b/src/testing/testing_api_helpers_bank.c
index 7b8b203a3..35ef4792f 100644
--- a/src/testing/testing_api_helpers_bank.c
+++ b/src/testing/testing_api_helpers_bank.c
@@ -36,11 +36,13 @@
* from the base URL.
*
* @param bank_url bank's base URL.
+ * @param currency currency the bank uses
* @return the fakebank process handle, or NULL if any
* error occurs.
*/
struct TALER_FAKEBANK_Handle *
-TALER_TESTING_run_fakebank (const char *bank_url)
+TALER_TESTING_run_fakebank (const char *bank_url,
+ const char *currency)
{
const char *port;
long pnum;
@@ -56,7 +58,8 @@ TALER_TESTING_run_fakebank (const char *bank_url)
"Starting Fakebank on port %u (%s)\n",
(unsigned int) pnum,
bank_url);
- fakebankd = TALER_FAKEBANK_start ((uint16_t) pnum);
+ fakebankd = TALER_FAKEBANK_start ((uint16_t) pnum,
+ currency);
if (NULL == fakebankd)
{
GNUNET_break (0);
diff --git a/src/testing/testing_api_helpers_exchange.c b/src/testing/testing_api_helpers_exchange.c
index 29c96db19..911bc6f0e 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -788,7 +788,7 @@ TALER_TESTING_setup_with_exchange_cfg (void *cls,
/* NOTE: this call blocks. */
result = TALER_TESTING_setup (setup_ctx->main_cb,
setup_ctx->main_cb_cls,
- setup_ctx->config_filename,
+ cfg,
exchanged,
GNUNET_YES);
GNUNET_break (0 ==
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index 61b95c2e1..e9ccdb81c 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -124,7 +124,33 @@ TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands,
const char *bank_url)
{
- is->fakebank = TALER_TESTING_run_fakebank (bank_url);
+ char *currency;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (is->cfg,
+ "taler",
+ "CURRENCY",
+ &currency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "taler",
+ "CURRENCY");
+ is->result = GNUNET_SYSERR;
+ return;
+ }
+ if (strlen (currency) >= TALER_CURRENCY_LEN)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "taler",
+ "CURRENCY",
+ "Value is too long");
+ GNUNET_free (currency);
+ is->result = GNUNET_SYSERR;
+ return;
+ }
+ is->fakebank = TALER_TESTING_run_fakebank (bank_url,
+ currency);
+ GNUNET_free (currency);
if (NULL == is->fakebank)
{
GNUNET_break (0);
@@ -530,14 +556,6 @@ struct MainContext
struct TALER_TESTING_Interpreter *is;
/**
- * Configuration filename. The wrapper uses it to fetch
- * the exchange port number; We could have passed the port
- * number here, but having the config filename seems more
- * generic.
- */
- const char *config_filename;
-
- /**
* URL of the exchange.
*/
char *exchange_url;
@@ -683,18 +701,16 @@ do_abort (void *cls)
* and responsible to run the "run" method.
*
* @param cls a `struct MainContext *`
- * @param cfg configuration to use
*/
-static int
-main_exchange_connect_with_cfg (void *cls,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
+static void
+main_wrapper_exchange_connect (void *cls)
{
struct MainContext *main_ctx = cls;
struct TALER_TESTING_Interpreter *is = main_ctx->is;
char *exchange_url;
if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
+ GNUNET_CONFIGURATION_get_value_string (is->cfg,
"exchange",
"BASE_URL",
&exchange_url))
@@ -702,40 +718,18 @@ main_exchange_connect_with_cfg (void *cls,
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"exchange",
"BASE_URL");
- return GNUNET_SYSERR;
+ return;
}
main_ctx->exchange_url = exchange_url;
- is->cfg = cfg;
is->timeout_task = GNUNET_SCHEDULER_add_shutdown (&do_abort,
main_ctx);
GNUNET_break
- (NULL != (is->exchange = TALER_EXCHANGE_connect
- (is->ctx,
- exchange_url,
- &TALER_TESTING_cert_cb,
- main_ctx,
- TALER_EXCHANGE_OPTION_END)));
- is->cfg = NULL;
- return GNUNET_OK;
-}
-
-
-/**
- * Initialize scheduler loop and curl context for the testcase,
- * and responsible to run the "run" method.
- *
- * @param cls a `struct MainContext *`
- */
-static void
-main_wrapper_exchange_connect (void *cls)
-{
- struct MainContext *main_ctx = cls;
-
- GNUNET_break (GNUNET_OK ==
- GNUNET_CONFIGURATION_parse_and_run (main_ctx->config_filename,
- &
- main_exchange_connect_with_cfg,
- main_ctx));
+ (NULL != (is->exchange =
+ TALER_EXCHANGE_connect (is->ctx,
+ exchange_url,
+ &TALER_TESTING_cert_cb,
+ main_ctx,
+ TALER_EXCHANGE_OPTION_END)));
}
@@ -746,7 +740,7 @@ main_wrapper_exchange_connect (void *cls)
* @param main_cb the "run" method which contains all the
* commands.
* @param main_cb_cls a closure for "run", typically NULL.
- * @param config_filename configuration filename.
+ * @param cfg configuration to use
* @param exchanged exchange process handle: will be put in the
* state as some commands - e.g. revoke - need to send
* signal to it, for example to let it know to reload the
@@ -761,7 +755,7 @@ main_wrapper_exchange_connect (void *cls)
int
TALER_TESTING_setup (TALER_TESTING_Main main_cb,
void *main_cb_cls,
- const char *config_filename,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
struct GNUNET_OS_Process *exchanged,
int exchange_connect)
{
@@ -771,9 +765,6 @@ TALER_TESTING_setup (TALER_TESTING_Main main_cb,
.main_cb_cls = main_cb_cls,
/* needed to init the curl ctx */
.is = &is,
- /* needed to read values like exchange port
- * number to construct the exchange url.*/
- .config_filename = config_filename
};
struct GNUNET_SIGNAL_Context *shc_chld;
@@ -781,6 +772,7 @@ TALER_TESTING_setup (TALER_TESTING_Main main_cb,
0,
sizeof (is));
is.exchanged = exchanged;
+ is.cfg = cfg;
sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
GNUNET_NO, GNUNET_NO);
GNUNET_assert (NULL != sigpipe);