summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-01-30 13:49:29 +0100
committerChristian Grothoff <christian@grothoff.org>2016-01-30 13:49:29 +0100
commita396f4e7fa39f17aec35360eb12098629649c831 (patch)
treee3e94b05bd85f548e52859af50660e5aea44d456 /src
parent3bb757b3a7bddbd132805dc1147158b40238e543 (diff)
parentae45b7ac9579cc9fcb9c3008162e07b694eb52f8 (diff)
downloadexchange-a396f4e7fa39f17aec35360eb12098629649c831.tar.gz
exchange-a396f4e7fa39f17aec35360eb12098629649c831.tar.bz2
exchange-a396f4e7fa39f17aec35360eb12098629649c831.zip
Merge branch 'master' of ssh://taler.net:/var/git/mint
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/bank-lib/Makefile.am45
-rw-r--r--src/bank-lib/bank_api_admin.c240
-rw-r--r--src/bank-lib/bank_api_context.c570
-rw-r--r--src/bank-lib/bank_api_context.h181
-rw-r--r--src/bank-lib/bank_api_json.c525
-rw-r--r--src/bank-lib/bank_api_json.h352
-rw-r--r--src/bank-lib/test_bank_api.c542
-rw-r--r--src/include/Makefile.am4
-rw-r--r--src/include/taler_amount_lib.h2
-rw-r--r--src/include/taler_bank_service.h161
-rw-r--r--src/include/taler_crypto_lib.h57
-rw-r--r--src/include/taler_json_lib.h2
-rw-r--r--src/include/taler_mint_service.h159
-rw-r--r--src/include/taler_mintdb_lib.h2
-rw-r--r--src/include/taler_mintdb_plugin.h270
-rw-r--r--src/include/taler_pq_lib.h14
-rw-r--r--src/include/taler_signatures.h93
-rw-r--r--src/include/taler_util.h2
-rw-r--r--src/include/taler_util_wallet.h2
-rw-r--r--src/include/taler_wire_lib.h48
-rw-r--r--src/include/taler_wire_plugin.h180
-rw-r--r--src/mint-lib/Makefile.am4
-rw-r--r--src/mint-lib/mint_api_admin.c2
-rw-r--r--src/mint-lib/mint_api_common.c2
-rw-r--r--src/mint-lib/mint_api_common.h2
-rw-r--r--src/mint-lib/mint_api_context.c4
-rw-r--r--src/mint-lib/mint_api_context.h2
-rw-r--r--src/mint-lib/mint_api_deposit.c13
-rw-r--r--src/mint-lib/mint_api_deposit_wtid.c379
-rw-r--r--src/mint-lib/mint_api_handle.c4
-rw-r--r--src/mint-lib/mint_api_handle.h2
-rw-r--r--src/mint-lib/mint_api_json.c34
-rw-r--r--src/mint-lib/mint_api_json.h21
-rw-r--r--src/mint-lib/mint_api_refresh.c13
-rw-r--r--src/mint-lib/mint_api_refresh_link.c40
-rw-r--r--src/mint-lib/mint_api_reserve.c2
-rw-r--r--src/mint-lib/mint_api_wire.c2
-rw-r--r--src/mint-lib/mint_api_wire_deposits.c284
-rw-r--r--src/mint-lib/test_mint_api.c406
-rw-r--r--src/mint-tools/taler-auditor-sign.c2
-rw-r--r--src/mint-tools/taler-mint-dbinit.c2
-rw-r--r--src/mint-tools/taler-mint-keycheck.c2
-rw-r--r--src/mint-tools/taler-mint-keyup.c2
-rw-r--r--src/mint-tools/taler-mint-reservemod.c2
-rw-r--r--src/mint-tools/taler-mint-sepa.c2
-rw-r--r--src/mint/Makefile.am24
-rw-r--r--src/mint/taler-mint-aggregator.c914
-rw-r--r--src/mint/taler-mint-httpd.c45
-rw-r--r--src/mint/taler-mint-httpd.h8
-rw-r--r--src/mint/taler-mint-httpd_admin.c4
-rw-r--r--src/mint/taler-mint-httpd_db.c297
-rw-r--r--src/mint/taler-mint-httpd_db.h16
-rw-r--r--src/mint/taler-mint-httpd_deposit.c4
-rw-r--r--src/mint/taler-mint-httpd_responses.c101
-rw-r--r--src/mint/taler-mint-httpd_responses.h43
-rw-r--r--src/mint/taler-mint-httpd_tracking.c24
-rw-r--r--src/mint/taler-mint-httpd_validation.c231
-rw-r--r--src/mint/taler-mint-httpd_validation.h76
-rw-r--r--src/mint/taler-mint-httpd_wire.c31
-rw-r--r--src/mintdb/mintdb_keyio.c2
-rw-r--r--src/mintdb/mintdb_plugin.c64
-rw-r--r--src/mintdb/perf_taler_mintdb.c2
-rw-r--r--src/mintdb/perf_taler_mintdb_init.c20
-rw-r--r--src/mintdb/perf_taler_mintdb_init.h2
-rw-r--r--src/mintdb/perf_taler_mintdb_interpreter.c2
-rw-r--r--src/mintdb/perf_taler_mintdb_interpreter.h2
-rw-r--r--src/mintdb/perf_taler_mintdb_values.h2
-rw-r--r--src/mintdb/plugin_mintdb_common.c2
-rw-r--r--src/mintdb/plugin_mintdb_postgres.c919
-rw-r--r--src/mintdb/test_mintdb.c209
-rw-r--r--src/mintdb/test_mintdb_deposits.c2
-rw-r--r--src/mintdb/test_perf_taler_mintdb.c2
-rw-r--r--src/pq/db_pq.c2
-rw-r--r--src/pq/pq_helper.c2
-rw-r--r--src/pq/test_pq.c2
-rw-r--r--src/util/Makefile.am14
-rw-r--r--src/util/amount.c2
-rw-r--r--src/util/crypto.c2
-rw-r--r--src/util/json.c2
-rw-r--r--src/util/os_installation.c2
-rw-r--r--src/util/plugin.c88
-rw-r--r--src/util/test_amount.c2
-rw-r--r--src/util/test_crypto.c2
-rw-r--r--src/util/test_json.c2
-rw-r--r--src/util/util.c2
-rw-r--r--src/util/wireformats.c2
-rw-r--r--src/wire/Makefile.am78
-rw-r--r--src/wire/plugin_wire_sepa.c545
-rw-r--r--src/wire/plugin_wire_template.c243
-rw-r--r--src/wire/plugin_wire_test.c581
-rw-r--r--src/wire/test_sepa_wireformat.c (renamed from src/util/test_wireformats.c)47
-rw-r--r--src/wire/wire.c72
93 files changed, 8991 insertions, 432 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0d1d4572f..ac839d52a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,10 +7,10 @@ if WALLET_ONLY
SUBDIRS = include util
else
-SUBDIRS = include util $(PQ_DIR) mintdb mint mint-tools
+SUBDIRS = include util $(PQ_DIR) bank-lib wire mintdb mint mint-tools
if HAVE_LIBCURL
SUBDIRS += mint-lib
-else
+else
if HAVE_LIBGNURL
SUBDIRS += mint-lib
endif
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
new file mode 100644
index 000000000..2f44adada
--- /dev/null
+++ b/src/bank-lib/Makefile.am
@@ -0,0 +1,45 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+lib_LTLIBRARIES = \
+ libtalerbank.la
+
+libtalerbank_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtalerbank_la_SOURCES = \
+ bank_api_context.c bank_api_context.h \
+ bank_api_json.c bank_api_json.h \
+ bank_api_admin.c
+
+libtalerbank_la_LIBADD = \
+ -lgnunetutil \
+ -ljansson \
+ $(XLIB)
+
+if HAVE_LIBCURL
+libtalerbank_la_LIBADD += -lcurl
+else
+if HAVE_LIBGNURL
+libtalerbank_la_LIBADD += -lgnurl
+endif
+endif
+
+check_PROGRAMS = \
+ test_bank_api
+
+TESTS = \
+ $(check_PROGRAMS)
+
+test_bank_api_SOURCES = \
+ test_bank_api.c
+test_bank_api_LDADD = \
+ libtalerbank.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil
diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c
new file mode 100644
index 000000000..bfcf16a23
--- /dev/null
+++ b/src/bank-lib/bank_api_admin.c
@@ -0,0 +1,240 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/bank_api_admin.c
+ * @brief Implementation of the /admin/ requests of the bank's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_bank_service.h"
+#include "bank_api_json.h"
+#include "bank_api_context.h"
+#include "taler_signatures.h"
+
+
+/**
+ * @brief An admin/add/incoming Handle
+ */
+struct TALER_BANK_AdminAddIncomingHandle
+{
+
+ /**
+ * The connection to bank this request handle will use
+ */
+ struct TALER_BANK_Context *bank;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * JSON encoding of the request to POST.
+ */
+ char *json_enc;
+
+ /**
+ * Handle for the request.
+ */
+ struct BAC_Job *job;
+
+ /**
+ * HTTP headers for the request.
+ */
+ struct curl_slist *headers;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_BANK_AdminAddIncomingResultCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Download buffer
+ */
+ struct BAC_DownloadBuffer db;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /admin/add/incoming request.
+ *
+ * @param cls the `struct TALER_BANK_AdminAddIncomingHandle`
+ * @param eh the curl request handle
+ */
+static void
+handle_admin_add_incoming_finished (void *cls,
+ CURL *eh)
+{
+ struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
+ long response_code;
+ json_t *json;
+
+ aai->job = NULL;
+ json = BAC_download_get_result (&aai->db,
+ eh,
+ &response_code);
+ switch (response_code)
+ {
+ case 0:
+ break;
+ case MHD_HTTP_OK:
+ 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 */
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ /* Access denied */
+ 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 */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Nothing really to verify, this should never
+ happen, we should pass the JSON reply to the application */
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ response_code);
+ GNUNET_break (0);
+ response_code = 0;
+ break;
+ }
+ aai->cb (aai->cb_cls,
+ response_code);
+ json_decref (json);
+ TALER_BANK_admin_add_incoming_cancel (aai);
+}
+
+
+/**
+ * Notify the bank that we have received an incoming transaction
+ * which fills a reserve. Note that this API is an administrative
+ * API and thus not accessible to typical bank clients, but only
+ * to the operators of the bank.
+ *
+ * @param bank the bank handle; the bank must be ready to operate
+ * @param reserve_pub public key of the reserve
+ * @param amount amount that was deposited
+ * @param execution_date when did we receive the amount
+ * @param account_no account number (53 bits at most)
+ * @param res_cb the callback to call when the final result for this request is available
+ * @param res_cb_cls closure for the above callback
+ * @return NULL
+ * if the inputs are invalid (i.e. invalid amount).
+ * In this case, the callback is not called.
+ */
+struct TALER_BANK_AdminAddIncomingHandle *
+TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *amount,
+ uint64_t account_no,
+ TALER_BANK_AdminAddIncomingResultCallback res_cb,
+ void *res_cb_cls)
+{
+ struct TALER_BANK_AdminAddIncomingHandle *aai;
+ json_t *admin_obj;
+ CURL *eh;
+
+ admin_obj = json_pack ("{s:o, s:o," /* reserve_pub/amount */
+ " s:I}", /* execution_Date/wire */
+ "wtid", TALER_json_from_data (wtid,
+ sizeof (*wtid)),
+ "amount", TALER_json_from_amount (amount),
+ "account", (json_int_t) account_no);
+ aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle);
+ aai->bank = bank;
+ aai->cb = res_cb;
+ aai->cb_cls = res_cb_cls;
+ aai->url = BAC_path_to_url (bank, "/admin/add/incoming");
+
+ eh = curl_easy_init ();
+ GNUNET_assert (NULL != (aai->json_enc =
+ json_dumps (admin_obj,
+ JSON_COMPACT)));
+ json_decref (admin_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ aai->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDS,
+ aai->json_enc));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDSIZE,
+ strlen (aai->json_enc)));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEFUNCTION,
+ &BAC_download_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEDATA,
+ &aai->db));
+ aai->job = BAC_job_add (bank,
+ eh,
+ GNUNET_YES,
+ &handle_admin_add_incoming_finished,
+ aai);
+ return aai;
+}
+
+
+/**
+ * Cancel an add incoming. This function cannot be used on a request
+ * handle if a response is already served for it.
+ *
+ * @param aai the admin add incoming request handle
+ */
+void
+TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai)
+{
+ if (NULL != aai->job)
+ {
+ BAC_job_cancel (aai->job);
+ aai->job = NULL;
+ }
+ curl_slist_free_all (aai->headers);
+ GNUNET_free_non_null (aai->db.buf);
+ GNUNET_free (aai->url);
+ GNUNET_free (aai->json_enc);
+ GNUNET_free (aai);
+}
+
+
+/* end of bank_api_admin.c */
diff --git a/src/bank-lib/bank_api_context.c b/src/bank-lib/bank_api_context.c
new file mode 100644
index 000000000..a47b4072a
--- /dev/null
+++ b/src/bank-lib/bank_api_context.c
@@ -0,0 +1,570 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/bank_api_context.c
+ * @brief Implementation of the context part of the bank's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include "taler_bank_service.h"
+#include "bank_api_context.h"
+
+
+/**
+ * Log error related to CURL operations.
+ *
+ * @param type log level
+ * @param function which function failed to run
+ * @param code what was the curl error code
+ */
+#define CURL_STRERROR(type, function, code) \
+ GNUNET_log (type, \
+ "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
+ function, __FILE__, __LINE__, curl_easy_strerror (code));
+
+/**
+ * Print JSON parsing related error information
+ */
+#define JSON_WARN(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)\n", \
+ __FILE__, __LINE__, error.text, error.source)
+
+
+/**
+ * Failsafe flag. Raised if our constructor fails to initialize
+ * the Curl library.
+ */
+static int TALER_BANK_curl_fail;
+
+
+/**
+ * Jobs are CURL requests running within a `struct TALER_BANK_Context`.
+ */
+struct BAC_Job
+{
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct BAC_Job *next;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct BAC_Job *prev;
+
+ /**
+ * Easy handle of the job.
+ */
+ CURL *easy_handle;
+
+ /**
+ * Context this job runs in.
+ */
+ struct TALER_BANK_Context *ctx;
+
+ /**
+ * Function to call upon completion.
+ */
+ BAC_JobCompletionCallback jcc;
+
+ /**
+ * Closure for @e jcc.
+ */
+ void *jcc_cls;
+
+};
+
+
+/**
+ * Context
+ */
+struct TALER_BANK_Context
+{
+ /**
+ * Curl multi handle
+ */
+ CURLM *multi;
+
+ /**
+ * Curl share handle
+ */
+ CURLSH *share;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct BAC_Job *jobs_head;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct BAC_Job *jobs_tail;
+
+ /**
+ * HTTP header "application/json", created once and used
+ * for all requests that need it.
+ */
+ struct curl_slist *json_header;
+
+ /**
+ * Base URL of the bank.
+ */
+ char *url;
+
+};
+
+
+/**
+ * Initialise this library. This function should be called before using any of
+ * the following functions.
+ *
+ * @param url HTTP base URL for the bank
+ * @return library context
+ */
+struct TALER_BANK_Context *
+TALER_BANK_init (const char *url)
+{
+ struct TALER_BANK_Context *ctx;
+ CURLM *multi;
+ CURLSH *share;
+
+ if (TALER_BANK_curl_fail)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Curl was not initialised properly\n");
+ return NULL;
+ }
+ if (NULL == (multi = curl_multi_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to create a Curl multi handle\n");
+ return NULL;
+ }
+ if (NULL == (share = curl_share_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to create a Curl share handle\n");
+ return NULL;
+ }
+ ctx = GNUNET_new (struct TALER_BANK_Context);
+ ctx->multi = multi;
+ ctx->share = share;
+ ctx->url = GNUNET_strdup (url);
+ GNUNET_assert (NULL != (ctx->json_header =
+ curl_slist_append (NULL,
+ "Content-Type: application/json")));
+ return ctx;
+}
+
+
+/**
+ * Schedule a CURL request to be executed and call the given @a jcc
+ * upon its completion. Note that the context will make use of the
+ * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
+ * instead use #BAC_easy_to_closure to extract the @a jcc_cls argument
+ * from a valid @a eh afterwards.
+ *
+ * This function modifies the CURL handle to add the
+ * "Content-Type: application/json" header if @a add_json is set.
+ *
+ * @param ctx context to execute the job in
+ * @param eh curl easy handle for the request, will
+ * be executed AND cleaned up
+ * @param add_json add "application/json" content type header
+ * @param jcc callback to invoke upon completion
+ * @param jcc_cls closure for @a jcc
+ */
+struct BAC_Job *
+BAC_job_add (struct TALER_BANK_Context *ctx,
+ CURL *eh,
+ int add_json,
+ BAC_JobCompletionCallback jcc,
+ void *jcc_cls)
+{
+ struct BAC_Job *job;
+
+ if (GNUNET_YES == add_json)
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_HTTPHEADER,
+ ctx->json_header));
+
+ job = GNUNET_new (struct BAC_Job);
+ job->easy_handle = eh;
+ job->ctx = ctx;
+ job->jcc = jcc;
+ job->jcc_cls = jcc_cls;
+ GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
+ ctx->jobs_tail,
+ job);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_PRIVATE,
+ job));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_SHARE,
+ ctx->share));
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_add_handle (ctx->multi,
+ eh));
+ return job;
+}
+
+
+/**
+ * Obtain the `jcc_cls` argument from an `eh` that was
+ * given to #BAC_job_add().
+ *
+ * @param eh easy handle that was used
+ * @return the `jcc_cls` that was given to #BAC_job_add().
+ */
+void *
+BAC_easy_to_closure (CURL *eh)
+{
+ struct BAC_Job *job;
+
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_getinfo (eh,
+ CURLINFO_PRIVATE,
+ (char **) &job));
+ return job->jcc_cls;
+}
+
+
+/**
+ * Cancel a job. Must only be called before the job completion
+ * callback is called for the respective job.
+ *
+ * @param job job to cancel
+ */
+void
+BAC_job_cancel (struct BAC_Job *job)
+{
+ struct TALER_BANK_Context *ctx = job->ctx;
+
+ GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
+ ctx->jobs_tail,
+ job);
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_remove_handle (ctx->multi,
+ job->easy_handle));
+ curl_easy_cleanup (job->easy_handle);
+ GNUNET_free (job);
+}
+
+
+/**
+ * Run the main event loop for the Taler interaction.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_BANK_perform (struct TALER_BANK_Context *ctx)
+{
+ CURLMsg *cmsg;
+ struct BAC_Job *job;
+ int n_running;
+ int n_completed;
+
+ (void) curl_multi_perform (ctx->multi,
+ &n_running);
+ while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
+ &n_completed)))
+ {
+ /* Only documented return value is CURLMSG_DONE */
+ GNUNET_break (CURLMSG_DONE == cmsg->msg);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_getinfo (cmsg->easy_handle,
+ CURLINFO_PRIVATE,
+ (char **) &job));
+ GNUNET_assert (job->ctx == ctx);
+ job->jcc (job->jcc_cls,
+ cmsg->easy_handle);
+ BAC_job_cancel (job);
+ }
+}
+
+
+/**
+ * Obtain the information for a select() call to wait until
+ * #TALER_BANK_perform() is ready again. Note that calling
+ * any other TALER_BANK-API may also imply that the library
+ * is again ready for #TALER_BANK_perform().
+ *
+ * Basically, a client should use this API to prepare for select(),
+ * then block on select(), then call #TALER_BANK_perform() and then
+ * start again until the work with the context is done.
+ *
+ * This function will NOT zero out the sets and assumes that @a max_fd
+ * and @a timeout are already set to minimal applicable values. It is
+ * safe to give this API FD-sets and @a max_fd and @a timeout that are
+ * already initialized to some other descriptors that need to go into
+ * the select() call.
+ *
+ * @param ctx context to get the event loop information for
+ * @param read_fd_set will be set for any pending read operations
+ * @param write_fd_set will be set for any pending write operations
+ * @param except_fd_set is here because curl_multi_fdset() has this argument
+ * @param max_fd set to the highest FD included in any set;
+ * if the existing sets have no FDs in it, the initial
+ * value should be "-1". (Note that `max_fd + 1` will need
+ * to be passed to select().)
+ * @param timeout set to the timeout in milliseconds (!); -1 means
+ * no timeout (NULL, blocking forever is OK), 0 means to
+ * proceed immediately with #TALER_BANK_perform().
+ */
+void
+TALER_BANK_get_select_info (struct TALER_BANK_Context *ctx,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ fd_set *except_fd_set,
+ int *max_fd,
+ long *timeout)
+{
+ long to;
+ int m;
+
+ m = -1;
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_fdset (ctx->multi,
+ read_fd_set,
+ write_fd_set,
+ except_fd_set,
+ &m));
+ to = *timeout;
+ *max_fd = GNUNET_MAX (m, *max_fd);
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_timeout (ctx->multi,
+ &to));
+
+ /* Only if what we got back from curl is smaller than what we
+ already had (-1 == infinity!), then update timeout */
+ if ( (to < *timeout) &&
+ (-1 != to) )
+ *timeout = to;
+ if ( (-1 == (*timeout)) &&
+ (NULL != ctx->jobs_head) )
+ *timeout = to;
+}
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_BANK_fini (struct TALER_BANK_Context *ctx)
+{
+ /* all jobs must have been cancelled at this time, assert this */
+ GNUNET_assert (NULL == ctx->jobs_head);
+ curl_share_cleanup (ctx->share);
+ curl_multi_cleanup (ctx->multi);
+ curl_slist_free_all (ctx->json_header);
+ GNUNET_free (ctx->url);
+ GNUNET_free (ctx);
+}
+
+
+/**
+ * Obtain the URL to use for an API request.
+ *
+ * @param h the mint handle to query
+ * @param path Taler API path (i.e. "/reserve/withdraw")
+ * @return the full URI to use with cURL
+ */
+char *
+BAC_path_to_url (struct TALER_BANK_Context *h,
+ const char *path)
+{
+ char *url;
+
+ if ( ('/' == path[0]) &&
+ (0 < strlen (h->url)) &&
+ ('/' == h->url[strlen (h->url) - 1]) )
+ path++; /* avoid generating URL with "//" from concat */
+ GNUNET_asprintf (&url,
+ "%s%s",
+ h->url,
+ path);
+ return url;
+}
+
+
+/**
+ * Callback used when downloading the reply to an HTTP request.
+ * Just appends all of the data to the `buf` in the
+ * `struct BAC_DownloadBuffer` for further processing. The size of
+ * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
+ * the download exceeds this size, we abort with an error.
+ *
+ * @param bufptr data downloaded via HTTP
+ * @param size size of an item in @a bufptr
+ * @param nitems number of items in @a bufptr
+ * @param cls the `struct KeysRequest`
+ * @return number of bytes processed from @a bufptr
+ */
+size_t
+BAC_download_cb (char *bufptr,
+ size_t size,
+ size_t nitems,
+ void *cls)
+{
+ struct BAC_DownloadBuffer *db = cls;
+ size_t msize;
+ void *buf;
+
+ if (0 == size * nitems)
+ {
+ /* Nothing (left) to do */
+ return 0;
+ }
+ msize = size * nitems;
+ if ( (msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
+ {
+ db->eno = ENOMEM;
+ return 0; /* signals an error to curl */
+ }
+ db->buf = GNUNET_realloc (db->buf,
+ db->buf_size + msize);
+ buf = db->buf + db->buf_size;
+ memcpy (buf, bufptr, msize);
+ db->buf_size += msize;
+ return msize;
+}
+
+
+/**
+ * Obtain information about the final result about the
+ * HTTP download. If the download was successful, parses
+ * the JSON in the @a db and returns it. Also returns
+ * the HTTP @a response_code. If the download failed,
+ * the return value is NULL. The response code is set
+ * in any case, on download errors to zero.
+ *
+ * Calling this function also cleans up @a db.
+ *
+ * @param db download buffer
+ * @param eh CURL handle (to get the response code)
+ * @param[out] response_code set to the HTTP response code
+ * (or zero if we aborted the download, i.e.
+ * because the response was too big, or if
+ * the JSON we received was malformed).
+ * @return NULL if downloading a JSON reply failed
+ */
+json_t *
+BAC_download_get_result (struct BAC_DownloadBuffer *db,
+ CURL *eh,
+ long *response_code)
+{
+ json_t *json;
+ json_error_t error;
+ char *ct;
+
+ if ( (CURLE_OK !=
+ curl_easy_getinfo (eh,
+ CURLINFO_CONTENT_TYPE,
+ &ct)) ||
+ (NULL == ct) ||
+ (0 != strcasecmp (ct,
+ "application/json")) )
+ {
+ /* No content type or explicitly not JSON, refuse to parse
+ (but keep response code) */
+ if (CURLE_OK !=
+ curl_easy_getinfo (eh,
+ CURLINFO_RESPONSE_CODE,
+ response_code))
+ {
+ /* unexpected error... */
+ GNUNET_break (0);
+ *response_code = 0;
+ }
+ return NULL;
+ }
+
+ json = NULL;
+ if (0 == db->eno)
+ {
+ json = json_loadb (db->buf,
+ db->buf_size,
+ JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
+ &error);
+ if (NULL == json)
+ {
+ JSON_WARN (error);
+ *response_code = 0;
+ }
+ }
+ GNUNET_free_non_null (db->buf);
+ db->buf = NULL;
+ db->buf_size = 0;
+ if (NULL != json)
+ {
+ if (CURLE_OK !=
+ curl_easy_getinfo (eh,
+ CURLINFO_RESPONSE_CODE,
+ response_code))
+ {
+ /* unexpected error... */
+ GNUNET_break (0);
+ *response_code = 0;
+ }
+ }
+ return json;
+}
+
+
+/**
+ * Initial global setup logic, specifically runs the Curl setup.
+ */
+__attribute__ ((constructor))
+void
+TALER_BANK_constructor__ (void)
+{
+ CURLcode ret;
+
+ if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
+ {
+ CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
+ "curl_global_init",
+ ret);
+ TALER_BANK_curl_fail = 1;
+ }
+}
+
+
+/**
+ * Cleans up after us, specifically runs the Curl cleanup.
+ */
+__attribute__ ((destructor))
+void
+TALER_BANK_destructor__ (void)
+{
+ if (TALER_BANK_curl_fail)
+ return;
+ curl_global_cleanup ();
+}
+
+/* end of bank_api_context.c */
diff --git a/src/bank-lib/bank_api_context.h b/src/bank-lib/bank_api_context.h
new file mode 100644
index 000000000..552cbe440
--- /dev/null
+++ b/src/bank-lib/bank_api_context.h
@@ -0,0 +1,181 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/bank_api_context.h
+ * @brief Internal interface to the context part of the bank's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_bank_service.h"
+#include "taler_signatures.h"
+
+
+/**
+ * Entry in the context's job queue.
+ */
+struct BAC_Job;
+
+/**
+ * Function to call upon completion of a job.
+ *
+ * @param cls closure
+ * @param eh original easy handle (for inspection)
+ */
+typedef void
+(*BAC_JobCompletionCallback)(void *cls,
+ CURL *eh);
+
+
+/**
+ * Schedule a CURL request to be executed and call the given @a jcc
+ * upon its completion. Note that the context will make use of the
+ * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
+ * instead use #BAC_easy_to_closure to extract the @a jcc_cls argument
+ * from a valid @a eh afterwards.
+ *
+ * This function modifies the CURL handle to add the
+ * "Content-Type: application/json" header if @a add_json is set.
+ *
+ * @param ctx context to execute the job in
+ * @param eh curl easy handle for the request, will
+ * be executed AND cleaned up
+ * @param add_json add "application/json" content type header
+ * @param jcc callback to invoke upon completion
+ * @param jcc_cls closure for @a jcc
+ */
+struct BAC_Job *
+BAC_job_add (struct TALER_BANK_Context *ctx,
+ CURL *eh,
+ int add_json,
+ BAC_JobCompletionCallback jcc,
+ void *jcc_cls);
+
+
+/**
+ * Obtain the `jcc_cls` argument from an `eh` that was
+ * given to #BAC_job_add().
+ *
+ * @param eh easy handle that was used
+ * @return the `jcc_cls` that was given to #BAC_job_add().
+ */
+void *
+BAC_easy_to_closure (CURL *eh);
+
+
+/**
+ * Cancel a job. Must only be called before the job completion
+ * callback is called for the respective job.
+ *
+ * @param job job to cancel
+ */
+void
+BAC_job_cancel (struct BAC_Job *job);
+
+
+/**
+ * @brief Buffer data structure we use to buffer the HTTP download
+ * before giving it to the JSON parser.
+ */
+struct BAC_DownloadBuffer
+{
+
+ /**
+ * Download buffer
+ */
+ void *buf;
+
+ /**
+ * The size of the download buffer
+ */
+ size_t buf_size;
+
+ /**
+ * Error code (based on libc errno) if we failed to download
+ * (i.e. response too large).
+ */
+ int eno;
+
+};
+
+
+/**
+ * Callback used when downloading the reply to an HTTP request.
+ * Just appends all of the data to the `buf` in the
+ * `struct BAC_DownloadBuffer` for further processing. The size of
+ * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
+ * the download exceeds this size, we abort with an error.
+ *
+ * Should be used by the various routines as the
+ * CURLOPT_WRITEFUNCTION. A `struct BAC_DownloadBuffer` needs to be
+ * passed to the CURLOPT_WRITEDATA.
+ *
+ * Afterwards, `eno` needs to be checked to ensure that the download
+ * completed correctly.
+ *
+ * @param bufptr data downloaded via HTTP
+ * @param size size of an item in @a bufptr
+ * @param nitems number of items in @a bufptr
+ * @param cls the `struct KeysRequest`
+ * @return number of bytes processed from @a bufptr
+ */
+size_t
+BAC_download_cb (char *bufptr,
+ size_t size,
+ size_t nitems,
+ void *cls);
+
+
+/**
+ * Obtain information about the final result about the
+ * HTTP download. If the download was successful, parses
+ * the JSON in the @a db and returns it. Also returns
+ * the HTTP @a response_code. If the download failed,
+ * the return value is NULL. The response code is set
+ * in any case, on download errors to zero.
+ *
+ * Calling this function also cleans up @a db.
+ *
+ * @param db download buffer
+ * @param eh CURL handle (to get the response code)
+ * @param[out] response_code set to the HTTP response code
+ * (or zero if we aborted the download, i.e.
+ * because the response was too big, or if
+ * the JSON we received was malformed).
+ * @return NULL if downloading a JSON reply failed
+ */
+json_t *
+BAC_download_get_result (struct BAC_DownloadBuffer *db,
+ CURL *eh,
+ long *response_code);
+
+
+/**
+ * Obtain the URL to use for an API request.
+ *
+ * @param h the bank handle to query
+ * @param path Taler API path (i.e. "/reserve/withdraw")
+ * @return the full URI to use with cURL
+ */
+char *
+BAC_path_to_url (struct TALER_BANK_Context *h,
+ const char *path);
+
+
+/* end of bank_api_context.h */
diff --git a/src/bank-lib/bank_api_json.c b/src/bank-lib/bank_api_json.c
new file mode 100644
index 000000000..2a09e5272
--- /dev/null
+++ b/src/bank-lib/bank_api_json.c
@@ -0,0 +1,525 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ 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/>
+*/
+/**
+ * @file bank-lib/bank_api_json.c
+ * @brief functions to parse incoming requests (JSON snippets)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "bank_api_json.h"
+
+/**
+ * Navigate and parse data in a JSON tree.
+ *
+ * @param root the JSON node to start the navigation at.
+ * @param spec parse specification array
+ * @return offset in @a spec where parsing failed, -1 on success (!)
+ */
+static int
+parse_json (json_t *root,
+ struct BAJ_Specification *spec)
+{
+ int i;
+ json_t *pos; /* what's our current position? */
+
+ pos = root;
+ for (i=0;BAJ_CMD_END != spec[i].cmd;i++)
+ {
+ pos = json_object_get (root,
+ spec[i].field);
+ if (NULL == pos)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ switch (spec[i].cmd)
+ {
+ case BAJ_CMD_END:
+ GNUNET_assert (0);
+ return i;
+ case BAJ_CMD_AMOUNT:
+ if (GNUNET_OK !=
+ TALER_json_to_amount (pos,
+ spec[i].details.amount))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ break;
+ case BAJ_CMD_TIME_ABSOLUTE:
+ if (GNUNET_OK !=
+ TALER_json_to_abs (pos,
+ spec[i].details.abs_time))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ break;
+
+ case BAJ_CMD_STRING:
+ {
+ const char *str;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.strptr = str;
+ }
+ break;
+
+ case BAJ_CMD_BINARY_FIXED:
+ {
+ const char *str;
+ int res;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ spec[i].details.fixed_data.dest,
+ spec[i].details.fixed_data.dest_size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ }
+ break;
+
+ case BAJ_CMD_BINARY_VARIABLE:
+ {
+ const char *str;
+ size_t size;
+ void *data;
+ int res;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ size = (strlen (str) * 5) / 8;
+ if (size >= 1024)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ data = GNUNET_malloc (size);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ data,
+ size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (data);
+ return i;
+ }
+ *spec[i].details.variable_data.dest_p = data;
+ *spec[i].details.variable_data.dest_size_p = size;
+ }
+ break;
+
+ case BAJ_CMD_RSA_PUBLIC_KEY:
+ {
+ size_t size;
+ const char *str;
+ int res;
+ void *buf;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ size = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (size);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (buf);
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.rsa_public_key
+ = GNUNET_CRYPTO_rsa_public_key_decode (buf,
+ size);
+ GNUNET_free (buf);
+ if (NULL == spec[i].details.rsa_public_key)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ }
+ break;
+
+ case BAJ_CMD_RSA_SIGNATURE:
+ {
+ size_t size;
+ const char *str;
+ int res;
+ void *buf;
+
+ str = json_string_value (pos);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ size = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (size);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (buf);
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.rsa_signature
+ = GNUNET_CRYPTO_rsa_signature_decode (buf,
+ size);
+ GNUNET_free (buf);
+ if (NULL == spec[i].details.rsa_signature)
+ return i;
+ }
+ break;
+
+ case BAJ_CMD_UINT16:
+ {
+ json_int_t val;
+
+ if (! json_is_integer (pos))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ val = json_integer_value (pos);
+ if ( (0 > val) || (val > UINT16_MAX) )
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ *spec[i].details.u16 = (uint16_t) val;
+ }
+ break;
+
+ case BAJ_CMD_UINT64:
+ {
+ json_int_t val;
+
+ if (! json_is_integer (pos))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ val = json_integer_value (pos);
+ *spec[i].details.u64 = (uint64_t) val;
+ }
+ break;
+
+ case BAJ_CMD_JSON_OBJECT:
+ {
+ if (! (json_is_object (pos) || json_is_array (pos)) )
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ json_incref (pos);
+ *spec[i].details.obj = pos;
+ }
+ break;
+
+ default:
+ GNUNET_break (0);
+ return i;
+ }
+ }
+ return -1; /* all OK! */
+}
+
+
+/**
+ * Free all elements allocated during a
+ * #BAJ_parse_json() operation.
+ *
+ * @param spec specification of the parse operation
+ * @param end number of elements in @a spec to process
+ */
+static void
+parse_free (struct BAJ_Specification *spec,
+ int end)
+{
+ int i;
+
+ for (i=0;i<end;i++)
+ {
+ switch (spec[i].cmd)
+ {
+ case BAJ_CMD_END:
+ GNUNET_assert (0);
+ return;
+ case BAJ_CMD_AMOUNT:
+ break;
+ case BAJ_CMD_TIME_ABSOLUTE:
+ break;
+ case BAJ_CMD_BINARY_FIXED:
+ break;
+ case BAJ_CMD_STRING:
+ break;
+ case BAJ_CMD_BINARY_VARIABLE:
+ GNUNET_free (*spec[i].details.variable_data.dest_p);
+ *spec[i].details.variable_data.dest_p = NULL;
+ *spec[i].details.variable_data.dest_size_p = 0;
+ break;
+ case BAJ_CMD_RSA_PUBLIC_KEY:
+ GNUNET_CRYPTO_rsa_public_key_free (*spec[i].details.rsa_public_key);
+ *spec[i].details.rsa_public_key = NULL;
+ break;
+ case BAJ_CMD_RSA_SIGNATURE:
+ GNUNET_CRYPTO_rsa_signature_free (*spec[i].details.rsa_signature);
+ *spec[i].details.rsa_signature = NULL;
+ break;
+ case BAJ_CMD_JSON_OBJECT:
+ json_decref (*spec[i].details.obj);
+ *spec[i].details.obj = NULL;
+ break;
+ default:
+ GNUNET_break (0);
+ break;
+ }
+ }
+}
+
+
+/**
+ * Navigate and parse data in a JSON tree.
+ *
+ * @param root the JSON node to start the navigation at.
+ * @param spec parse specification array
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+BAJ_parse_json (const json_t *root,
+ struct BAJ_Specification *spec)
+{
+ int ret;
+
+ ret = parse_json ((json_t *) root,
+ spec);
+ if (-1 == ret)
+ return GNUNET_OK;
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "JSON field `%s` (%d) had unexpected value\n",
+ spec[ret].field,
+ ret);
+ parse_free (spec, ret);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Free all elements allocated during a
+ * #BAJ_parse_json() operation.
+ *
+ * @param spec specification of the parse operation
+ */
+void
+BAJ_parse_free (struct BAJ_Specification *spec)
+{
+ int i;
+
+ for (i=0;BAJ_CMD_END != spec[i].cmd;i++) ;
+ parse_free (spec, i);
+}
+
+
+/**
+ * The expected field stores a string.
+ *
+ * @param name name of the JSON field
+ * @param strptr where to store a pointer to the field
+ */
+struct BAJ_Specification
+BAJ_spec_string (const char *name,
+ const char **strptr)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_STRING,
+ .field = name,
+ .details.strptr = strptr
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an absolute time value.
+ *
+ * @param name name of the JSON field
+ * @param at where to store the absolute time found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_absolute_time (const char *name,
+ struct GNUNET_TIME_Absolute *at)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_TIME_ABSOLUTE,
+ .field = name,
+ .details.abs_time = at
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an amount value.
+ *
+ * @param name name of the JSON field
+ * @param amount where to store the amount found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_amount (const char *name,
+ struct TALER_Amount *amount)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_AMOUNT,
+ .field = name,
+ .details.amount = amount
+ };
+ return ret;
+}
+
+
+/**
+ * 16-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u16 where to store the integer found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_uint16 (const char *name,
+ uint16_t *u16)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_UINT16,
+ .field = name,
+ .details.u16 = u16
+ };
+ return ret;
+}
+
+
+/**
+ * 64-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u64 where to store the integer found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_uint64 (const char *name,
+ uint64_t *u64)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_UINT64,
+ .field = name,
+ .details.u64 = u64
+ };
+ return ret;
+}
+
+
+/**
+ * JSON object.
+ *
+ * @param name name of the JSON field
+ * @param[out] jsonp where to store the JSON found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_json (const char *name,
+ json_t **jsonp)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_JSON_OBJECT,
+ .field = name,
+ .details.obj = jsonp
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an RSA public key.
+ *
+ * @param name name of the JSON field
+ * @param pk where to store the RSA key found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_rsa_public_key (const char *name,
+ struct GNUNET_CRYPTO_rsa_PublicKey **pk)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_RSA_PUBLIC_KEY,
+ .field = name,
+ .details.rsa_public_key = pk
+ };
+ return ret;
+}
+
+
+/**
+ * Specification for parsing an RSA signature.
+ *
+ * @param name name of the JSON field
+ * @param sig where to store the RSA signature found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_rsa_signature (const char *name,
+ struct GNUNET_CRYPTO_rsa_Signature **sig)
+{
+ struct BAJ_Specification ret =
+ {
+ .cmd = BAJ_CMD_RSA_SIGNATURE,
+ .field = name,
+ .details.rsa_signature = sig
+ };
+ return ret;
+}
+
+
+/* end of bank_api_json.c */
diff --git a/src/bank-lib/bank_api_json.h b/src/bank-lib/bank_api_json.h
new file mode 100644
index 000000000..2ecaf8ef1
--- /dev/null
+++ b/src/bank-lib/bank_api_json.h
@@ -0,0 +1,352 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ 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/>
+*/
+/**
+ * @file mint-lib/mint_api_json.h
+ * @brief functions to parse incoming requests (JSON snippets)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include <jansson.h>
+
+
+/**
+ * Enumeration with the various commands for the
+ * #BAJ_parse_json interpreter.
+ */
+enum BAJ_Command
+{
+
+ /**
+ * End of command list.
+ */
+ BAJ_CMD_END,
+
+ /**
+ * Parse amount at current position.
+ */
+ BAJ_CMD_AMOUNT,
+
+ /**
+ * Parse absolute time at current position.
+ */
+ BAJ_CMD_TIME_ABSOLUTE,
+
+ /**
+ * Parse fixed binary value at current position.
+ */
+ BAJ_CMD_BINARY_FIXED,
+
+ /**
+ * Parse variable-size binary value at current position.
+ */
+ BAJ_CMD_BINARY_VARIABLE,
+
+ /**
+ * Parse RSA public key at current position.
+ */
+ BAJ_CMD_RSA_PUBLIC_KEY,
+
+ /**
+ * Parse RSA signature at current position.
+ */
+ BAJ_CMD_RSA_SIGNATURE,
+
+ /**
+ * Parse `const char *` JSON string at current position.
+ */
+ BAJ_CMD_STRING,
+
+ /**
+ * Parse `uint16_t` integer at the current position.
+ */
+ BAJ_CMD_UINT16,
+
+ /**
+ * Parse `uint64_t` integer at the current position.
+ */
+ BAJ_CMD_UINT64,
+
+ /**
+ * Parse JSON object at the current position.
+ */
+ BAJ_CMD_JSON_OBJECT,
+
+ /**
+ * Parse ??? at current position.
+ */
+ BAJ_CMD_C
+
+};
+
+
+/**
+ * @brief Entry in parser specification for #BAJ_parse_json.
+ */
+struct BAJ_Specification
+{
+
+ /**
+ * Command to execute.
+ */
+ enum BAJ_Command cmd;
+
+ /**
+ * Name of the field to access.
+ */
+ const char *field;
+
+ /**
+ * Further details for the command.
+ */
+ union {
+
+ /**
+ * Where to store amount for #BAJ_CMD_AMOUNT.
+ */
+ struct TALER_Amount *amount;
+
+ /**
+ * Where to store time, for #BAJ_CMD_TIME_ABSOLUTE.
+ */
+ struct GNUNET_TIME_Absolute *abs_time;
+
+ /**
+ * Where to write binary data, for #BAJ_CMD_BINARY_FIXED.
+ */
+ struct {
+ /**
+ * Where to write the data.
+ */
+ void *dest;
+
+ /**
+ * How many bytes to write to @e dest.
+ */
+ size_t dest_size;
+
+ } fixed_data;
+
+ /**
+ * Where to write binary data, for #BAJ_CMD_BINARY_VARIABLE.
+ */
+ struct {
+ /**
+ * Where to store the pointer with the data (is allocated).
+ */
+ void **dest_p;
+
+ /**
+ * Where to store the number of bytes allocated at `*dest`.
+ */
+ size_t *dest_size_p;
+
+ } variable_data;
+
+ /**
+ * Where to store the RSA public key for #BAJ_CMD_RSA_PUBLIC_KEY
+ */
+ struct GNUNET_CRYPTO_rsa_PublicKey **rsa_public_key;
+
+ /**
+ * Where to store the RSA signature for #BAJ_CMD_RSA_SIGNATURE
+ */
+ struct GNUNET_CRYPTO_rsa_Signature **rsa_signature;
+
+ /**
+ * Details for #BAJ_CMD_EDDSA_SIGNATURE
+ */
+ struct {
+
+ /**
+ * Where to store the purpose.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose **purpose_p;
+
+ /**
+ * Key to verify the signature against.
+ */
+ const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key;
+
+ } eddsa_signature;
+
+ /**
+ * Where to store a pointer to the string.
+ */
+ const char **strptr;
+
+ /**
+ * Where to store 16-bit integer.
+ */
+ uint16_t *u16;
+
+ /**
+ * Where to store 64-bit integer.
+ */
+ uint64_t *u64;
+
+ /**
+ * Where to store a JSON object.
+ */
+ json_t **obj;
+
+ } details;
+
+};
+
+
+/**
+ * Navigate and parse data in a JSON tree.
+ *
+ * @param root the JSON node to start the navigation at.
+ * @param spec parse specification array
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+BAJ_parse_json (const json_t *root,
+ struct BAJ_Specification *spec);
+
+
+/**
+ * Free all elements allocated during a
+ * #BAJ_parse_json() operation.
+ *
+ * @param spec specification of the parse operation
+ */
+void
+BAJ_parse_free (struct BAJ_Specification *spec);
+
+
+/**
+ * End of a parser specification.
+ */
+#define BAJ_spec_end { .cmd = BAJ_CMD_END }
+
+/**
+ * Fixed size object (in network byte order, encoded using Crockford
+ * Base32hex encoding).
+ *
+ * @param name name of the JSON field
+ * @param obj pointer where to write the data (type of `*obj` will determine size)
+ */
+#define BAJ_spec_fixed_auto(name,obj) { .cmd = BAJ_CMD_BINARY_FIXED, .field = name, .details.fixed_data.dest = obj, .details.fixed_data.dest_size = sizeof (*obj) }
+
+
+/**
+ * Variable size object (in network byte order, encoded using Crockford
+ * Base32hex encoding).
+ *
+ * @param name name of the JSON field
+ * @param obj pointer where to write the data (a `void **`)
+ * @param size where to store the number of bytes allocated for @a obj (of type `size_t *`
+ */
+#define BAJ_spec_varsize(name,obj,size) { .cmd = BAJ_CMD_BINARY_VARIABLE, .field = name, .details.variable_data.dest_p = obj, .details.variable_data.dest_size_p = size }
+
+
+/**
+ * The expected field stores a string.
+ *
+ * @param name name of the JSON field
+ * @param strptr where to store a pointer to the field
+ */
+struct BAJ_Specification
+BAJ_spec_string (const char *name,
+ const char **strptr);
+
+
+/**
+ * Absolute time.
+ *
+ * @param name name of the JSON field
+ * @param[out] at where to store the absolute time found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_absolute_time (const char *name,
+ struct GNUNET_TIME_Absolute *at);
+
+
+/**
+ * 16-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u16 where to store the integer found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_uint16 (const char *name,
+ uint16_t *u16);
+
+
+/**
+ * 64-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u64 where to store the integer found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_uint64 (const char *name,
+ uint64_t *u64);
+
+
+/**
+ * JSON object.
+ *
+ * @param name name of the JSON field
+ * @param[out] jsonp where to store the JSON found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_json (const char *name,
+ json_t **jsonp);
+
+
+/**
+ * Specification for parsing an amount value.
+ *
+ * @param name name of the JSON field
+ * @param amount where to store the amount under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_amount (const char *name,
+ struct TALER_Amount *amount);
+
+
+/**
+ * Specification for parsing an RSA public key.
+ *
+ * @param name name of the JSON field
+ * @param pk where to store the RSA key found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_rsa_public_key (const char *name,
+ struct GNUNET_CRYPTO_rsa_PublicKey **pk);
+
+
+/**
+ * Specification for parsing an RSA signature.
+ *
+ * @param name name of the JSON field
+ * @param sig where to store the RSA signature found under @a name
+ */
+struct BAJ_Specification
+BAJ_spec_rsa_signature (const char *name,
+ struct GNUNET_CRYPTO_rsa_Signature **sig);
+
+
+
+
+/* end of mint_api_json.h */
diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c
new file mode 100644
index 000000000..b14f523ba
--- /dev/null
+++ b/src/bank-lib/test_bank_api.c
@@ -0,0 +1,542 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank/test_bank_api.c
+ * @brief testcase to test bank's HTTP API interface
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_bank_service.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+
+
+/**
+ * Main execution context for the main loop.
+ */
+static struct TALER_BANK_Context *ctx;
+
+/**
+ * Task run on shutdown.
+ */
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+/**
+ * Task that runs the main event loop.
+ */
+static struct GNUNET_SCHEDULER_Task *ctx_task;
+
+/**
+ * Result of the testcases, #GNUNET_OK on success
+ */
+static int result;
+
+
+/**
+ * Opcodes for the interpreter.
+ */
+enum OpCode
+{
+ /**
+ * Termination code, stops the interpreter loop (with success).
+ */
+ OC_END = 0,
+
+ /**
+ * Add funds to a reserve by (faking) incoming wire transfer.
+ */
+ OC_ADMIN_ADD_INCOMING
+
+};
+
+
+/**
+ * Details for a bank operation to execute.
+ */
+struct Command
+{
+ /**
+ * Opcode of the command.
+ */
+ enum OpCode oc;
+
+ /**
+ * Label for the command, can be NULL.
+ */
+ const char *label;
+
+ /**
+ * Which response code do we expect for this command?
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Details about the command.
+ */
+ union
+ {
+
+ /**
+ * Information for a #OC_ADMIN_ADD_INCOMING command.
+ */
+ struct
+ {
+
+ /**
+ * String describing the amount to add to the reserve.
+ */
+ const char *amount;
+
+ /**
+ * Account number.
+ */
+ uint64_t account_no;
+
+ /**
+ * Wire transfer identifier to use. Initialized to
+ * a random value.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Set to the API's handle during the operation.
+ */
+ struct TALER_BANK_AdminAddIncomingHandle *aih;
+
+ } admin_add_incoming;
+
+ } details;
+
+};
+
+
+/**
+ * State of the interpreter loop.
+ */
+struct InterpreterState
+{
+ /**
+ * Keys from the bank.
+ */
+ const struct TALER_BANK_Keys *keys;
+
+ /**
+ * Commands the interpreter will run.
+ */
+ struct Command *commands;
+
+ /**
+ * Interpreter task (if one is scheduled).
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Instruction pointer. Tells #interpreter_run() which
+ * instruction to run next.
+ */
+ unsigned int ip;
+
+};
+
+
+/**
+ * Task that runs the context's event loop with the GNUnet scheduler.
+ *
+ * @param cls unused
+ * @param tc scheduler context (unused)
+ */
+static void
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Run the context task, the working set has changed.
+ */
+static void
+trigger_context_task ()
+{
+ GNUNET_SCHEDULER_cancel (ctx_task);
+ ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
+ NULL);
+}
+
+
+/**
+ * The testcase failed, return with an error code.
+ *
+ * @param is interpreter state to clean up
+ */
+static void
+fail (struct InterpreterState *is)
+{
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+#if 0
+/**
+ * Find a command by label.
+ *
+ * @param is interpreter state to search
+ * @param label label to look for
+ * @return NULL if command was not found
+ */
+static const struct Command *
+find_command (const struct InterpreterState *is,
+ const char *label)
+{
+ unsigned int i;
+ const struct Command *cmd;
+
+ if (NULL == label)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Attempt to lookup command for empty label\n");
+ return NULL;
+ }
+ for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
+ if ( (NULL != cmd->label) &&
+ (0 == strcmp (cmd->label,
+ label)) )
+ return cmd;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command not found: %s\n",
+ label);
+ return NULL;
+}
+#endif
+
+
+/**
+ * Run the main interpreter loop that performs bank operations.
+ *
+ * @param cls contains the `struct InterpreterState`
+ * @param tc scheduler context
+ */
+static void
+interpreter_run (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Function called upon completion of our /admin/add/incoming request.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
+ * 0 if the bank's reply is bogus (fails to follow the protocol)
+ */
+static void
+add_incoming_cb (void *cls,
+ unsigned int http_status)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.admin_add_incoming.aih = NULL;
+ if (cmd->expected_response_code != http_status)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Run the main interpreter loop that performs bank operations.
+ *
+ * @param cls contains the `struct InterpreterState`
+ * @param tc scheduler context
+ */
+static void
+interpreter_run (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+ struct TALER_Amount amount;
+
+ is->task = NULL;
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+ fprintf (stderr,
+ "Test aborted by shutdown request\n");
+ fail (is);
+ return;
+ }
+ switch (cmd->oc)
+ {
+ case OC_END:
+ result = GNUNET_OK;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ case OC_ADMIN_ADD_INCOMING:
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
+ &amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %u\n",
+ cmd->details.admin_add_incoming.amount,
+ is->ip);
+ fail (is);
+ return;
+ }
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &cmd->details.admin_add_incoming.wtid,
+ sizeof (cmd->details.admin_add_incoming.wtid));
+ cmd->details.admin_add_incoming.aih
+ = TALER_BANK_admin_add_incoming (ctx,
+ &cmd->details.admin_add_incoming.wtid,
+ &amount,
+ cmd->details.admin_add_incoming.account_no,
+ &add_incoming_cb,
+ is);
+ if (NULL == cmd->details.admin_add_incoming.aih)
+ {
+ GNUNET_break (0);
+ fail (is);
+ return;
+ }
+ trigger_context_task ();
+ return;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown instruction %d at %u (%s)\n",
+ cmd->oc,
+ is->ip,
+ cmd->label);
+ fail (is);
+ return;
+ }
+}
+
+
+/**
+ * Function run when the test terminates (good or bad).
+ * Cleans up our state.
+ *
+ * @param cls the interpreter state.
+ * @param tc unused
+ */
+static void
+do_shutdown (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd;
+ unsigned int i;
+
+ shutdown_task = NULL;
+ for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
+ {
+ switch (cmd->oc)
+ {
+ case OC_END:
+ GNUNET_assert (0);
+ break;
+ case OC_ADMIN_ADD_INCOMING:
+ if (NULL != cmd->details.admin_add_incoming.aih)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_BANK_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih);
+ cmd->details.admin_add_incoming.aih = NULL;
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown instruction %d at %u (%s)\n",
+ cmd->oc,
+ i,
+ cmd->label);
+ break;
+ }
+ }
+ if (NULL != is->task)
+ {
+ GNUNET_SCHEDULER_cancel (is->task);
+ is->task = NULL;
+ }
+ GNUNET_free (is);
+ if (NULL != ctx_task)
+ {
+ GNUNET_SCHEDULER_cancel (ctx_task);
+ ctx_task = NULL;
+ }
+ if (NULL != ctx)
+ {
+ TALER_BANK_fini (ctx);
+ ctx = NULL;
+ }
+}
+
+
+/**
+ * Task that runs the context's event loop with the GNUnet scheduler.
+ *
+ * @param cls unused
+ * @param tc scheduler context (unused)
+ */
+static void
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ long timeout;
+ int max_fd;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ fd_set except_fd_set;
+ struct GNUNET_NETWORK_FDSet *rs;
+ struct GNUNET_NETWORK_FDSet *ws;
+ struct GNUNET_TIME_Relative delay;
+
+ ctx_task = NULL;
+ TALER_BANK_perform (ctx);
+ max_fd = -1;
+ timeout = -1;
+ FD_ZERO (&read_fd_set);
+ FD_ZERO (&write_fd_set);
+ FD_ZERO (&except_fd_set);
+ TALER_BANK_get_select_info (ctx,
+ &read_fd_set,
+ &write_fd_set,
+ &except_fd_set,
+ &max_fd,
+ &timeout);
+ if (timeout >= 0)
+ delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
+ else
+ delay = GNUNET_TIME_UNIT_FOREVER_REL;
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (rs,
+ &read_fd_set,
+ max_fd + 1);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (ws,
+ &write_fd_set,
+ max_fd + 1);
+ ctx_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ delay,
+ rs,
+ ws,
+ &context_task,
+ cls);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ GNUNET_NETWORK_fdset_destroy (ws);
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct InterpreterState *is;
+ static struct Command commands[] =
+ {
+ /* Add EUR:5.01 to account 42 */
+ { .oc = OC_ADMIN_ADD_INCOMING,
+ .label = "deposit-1",
+ .expected_response_code = MHD_HTTP_OK,
+ .details.admin_add_incoming.account_no = 42,
+ .details.admin_add_incoming.amount = "EUR:5.01" },
+
+ { .oc = OC_END }
+ };
+
+ is = GNUNET_new (struct InterpreterState);
+ is->commands = commands;
+
+ ctx = TALER_BANK_init ("http://localhost:8081");
+ GNUNET_assert (NULL != ctx);
+ ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
+ ctx);
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+ shutdown_task
+ = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 150),
+ &do_shutdown, is);
+}
+
+
+/**
+ * Main function for the testcase for the bank API.
+ *
+ * @param argc expected to be 1
+ * @param argv expected to only contain the program name
+ */
+int
+main (int argc,
+ char * const *argv)
+{
+ struct GNUNET_OS_Process *bankd;
+
+ GNUNET_log_setup ("test-bank-api",
+ "WARNING",
+ NULL);
+ bankd = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-bank-httpd",
+ "taler-bank-httpd",
+ "-d", "test-bank-home",
+ NULL);
+ if (NULL == bankd)
+ {
+ fprintf (stderr,
+ "taler-bank-httpd not found, skipping test\n");
+ return 77; /* report 'skip' */
+ }
+ /* give child time to start and bind against the socket */
+ fprintf (stderr,
+ "Waiting for taler-bank-httpd to be ready");
+ do
+ {
+ fprintf (stderr, ".");
+ sleep (1);
+ }
+ while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null"));
+ fprintf (stderr, "\n");
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_run (&run, NULL);
+ GNUNET_OS_process_kill (bankd,
+ SIGTERM);
+ GNUNET_OS_process_wait (bankd);
+ GNUNET_OS_process_destroy (bankd);
+ return (GNUNET_OK == result) ? 0 : 1;
+}
+
+/* end of test_bank_api.c */
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index bfdcbe7ca..4d7ae3cb2 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -20,7 +20,9 @@ talerinclude_HEADERS = \
taler_mintdb_lib.h \
taler_mintdb_plugin.h \
taler_pq_lib.h \
- taler_signatures.h
+ taler_signatures.h \
+ taler_wire_lib.h \
+ taler_wire_plugin.h
endif
diff --git a/src/include/taler_amount_lib.h b/src/include/taler_amount_lib.h
index 8661ed913..094b96f7f 100644
--- a/src/include/taler_amount_lib.h
+++ b/src/include/taler_amount_lib.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h
new file mode 100644
index 000000000..a4f33fc97
--- /dev/null
+++ b/src/include/taler_bank_service.h
@@ -0,0 +1,161 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ 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/>
+*/
+/**
+ * @file include/taler_bank_service.h
+ * @brief C interface of libtalerbank, a C library to use the Taler bank's HTTP API
+ * This is currently ONLY used to provide the "test" wire transfer protocol.
+ * @author Christian Grothoff
+ */
+#ifndef _TALER_BANK_SERVICE_H
+#define _TALER_BANK_SERVICE_H
+
+#include "taler_util.h"
+
+/* ********************* event loop *********************** */
+
+/**
+ * @brief Handle to this library context. This is where the
+ * main event loop logic lives.
+ */
+struct TALER_BANK_Context;
+
+
+/**
+ * Initialise a context. A context should be used for each thread and should
+ * not be shared among multiple threads.
+ *
+ * @param url HTTP base URL for the bank
+ * @return the context, NULL on error (failure to initialize)
+ */
+struct TALER_BANK_Context *
+TALER_BANK_init (const char *url);
+
+
+/**
+ * Obtain the information for a select() call to wait until
+ * #TALER_BANK_perform() is ready again. Note that calling
+ * any other TALER_BANK-API may also imply that the library
+ * is again ready for #TALER_BANK_perform().
+ *
+ * Basically, a client should use this API to prepare for select(),
+ * then block on select(), then call #TALER_BANK_perform() and then
+ * start again until the work with the context is done.
+ *
+ * This function will NOT zero out the sets and assumes that @a max_fd
+ * and @a timeout are already set to minimal applicable values. It is
+ * safe to give this API FD-sets and @a max_fd and @a timeout that are
+ * already initialized to some other descriptors that need to go into
+ * the select() call.
+ *
+ * @param ctx context to get the event loop information for
+ * @param read_fd_set will be set for any pending read operations
+ * @param write_fd_set will be set for any pending write operations
+ * @param except_fd_set is here because curl_multi_fdset() has this argument
+ * @param max_fd set to the highest FD included in any set;
+ * if the existing sets have no FDs in it, the initial
+ * value should be "-1". (Note that `max_fd + 1` will need
+ * to be passed to select().)
+ * @param timeout set to the timeout in milliseconds (!); -1 means
+ * no timeout (NULL, blocking forever is OK), 0 means to
+ * proceed immediately with #TALER_BANK_perform().
+ */
+void
+TALER_BANK_get_select_info (struct TALER_BANK_Context *ctx,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ fd_set *except_fd_set,
+ int *max_fd,
+ long *timeout);
+
+
+/**
+ * Run the main event loop for the Taler interaction.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_BANK_perform (struct TALER_BANK_Context *ctx);
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_BANK_fini (struct TALER_BANK_Context *ctx);
+
+
+/* ********************* /admin/add/incoming *********************** */
+
+
+/**
+ * @brief A /admin/add/incoming Handle
+ */
+struct TALER_BANK_AdminAddIncomingHandle;
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting
+ * information about an incoming transaction to a bank.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
+ * 0 if the bank's reply is bogus (fails to follow the protocol)
+ */
+typedef void
+(*TALER_BANK_AdminAddIncomingResultCallback) (void *cls,
+ unsigned int http_status);
+
+
+/**
+ * Notify the bank that we have received an incoming transaction
+ * which fills a reserve. Note that this API is an administrative
+ * API and thus not accessible to typical bank clients, but only
+ * to the operators of the bank.
+ *
+ * @param bank the bank handle; the bank must be ready to operate
+ * @param reserve_pub public key of the reserve
+ * @param amount amount that was deposited
+ * @param execution_date when did we receive the amount
+ * @param account_no account number (53 bits at most)
+ * @param res_cb the callback to call when the final result for this request is available
+ * @param res_cb_cls closure for the above callback
+ * @return NULL
+ * if the inputs are invalid (i.e. invalid amount).
+ * In this case, the callback is not called.
+ */
+struct TALER_BANK_AdminAddIncomingHandle *
+TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *amount,
+ uint64_t account_no,
+ TALER_BANK_AdminAddIncomingResultCallback res_cb,
+ void *res_cb_cls);
+
+
+/**
+ * Cancel an add incoming. This function cannot be used on a request
+ * handle if a response is already served for it.
+ *
+ * @param aai the admin add incoming request handle
+ */
+void
+TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai);
+
+#endif /* _TALER_BANK_SERVICE_H */
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 16240e5a3..6056270fa 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -428,6 +428,61 @@ struct TALER_RefreshLinkDecrypted
};
+/**
+ * Length of the raw value in the Taler wire transfer identifier
+ * (in binary representation).
+ */
+#define TALER_WIRE_TRANSFER_IDENTIFIER_LEN 32
+
+/**
+ * #TALER_WIRE_TRANSFER_IDENTIFIER_LEN as a string.
+ */
+#define TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR "32"
+
+/**
+ * Raw value of a wire transfer subjects, without the checksum.
+ */
+struct TALER_WireTransferIdentifierRawP
+{
+
+ /**
+ * Raw value. Note that typical payment systems (SEPA, ACH) support
+ * at least two lines of 27 ASCII characters to encode a transaction
+ * subject or "details", for a total of 54 characters. (The payment
+ * system protocols often support more lines, but the forms presented
+ * to customers are usually limited to 54 characters.)
+ *
+ * With a Base32-encoding of 5 bit per character, this gives us 270
+ * bits or (rounded down) 33 bytes. So we use the first 32 bytes to
+ * encode the actual value (i.e. a 256-bit / 32-byte public key or
+ * a hash code), and the last byte for a minimalistic checksum.
+ */
+ uint8_t raw[TALER_WIRE_TRANSFER_IDENTIFIER_LEN];
+};
+
+
+/**
+ * Binary information encoded in Crockford's Base32 in wire transfer
+ * subjects of transfers from Taler to a merchant. The actual value
+ * is chosen by the mint and has no particular semantics, other than
+ * being unique so that the mint can lookup details about the wire
+ * transfer when needed.
+ */
+struct TALER_WireTransferIdentifierP
+{
+
+ /**
+ * Raw value.
+ */
+ struct TALER_WireTransferIdentifierRawP raw;
+
+ /**
+ * Checksum using CRC8 over the @e raw data.
+ */
+ uint8_t crc8;
+};
+
+
GNUNET_NETWORK_STRUCT_END
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 8bdcf2709..d9fa05188 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h
index 78650edfe..1502edfbc 100644
--- a/src/include/taler_mint_service.h
+++ b/src/include/taler_mint_service.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -1059,5 +1059,162 @@ void
TALER_MINT_admin_add_incoming_cancel (struct TALER_MINT_AdminAddIncomingHandle *aai);
+/* ********************* /wire/deposits *********************** */
+
+/**
+ * @brief A /wire/deposits Handle
+ */
+struct TALER_MINT_WireDepositsHandle;
+
+
+/**
+ * Details for one of the /deposit operations that the
+ * mint combined into a single wire transfer.
+ */
+struct TALER_WireDepositDetails
+{
+ /**
+ * Hash of the contract.
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Which coin was deposited?
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Value of the deposit (including fee).
+ */
+ struct TALER_Amount coin_value;
+
+ /**
+ * Fee charged by the mint for the deposit.
+ */
+ struct TALER_Amount coin_fee;
+
+ /**
+ * Merchant's transaction identifier.
+ */
+ uint64_t transaction_id;
+
+};
+
+
+/**
+ * Function called with detailed wire transfer data, including all
+ * of the coin transactions that were combined into the wire transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on mint protocol violation
+ * @param json original json reply (may include signatures, those have then been
+ * validated already)
+ * @param wtid extracted wire transfer identifier, or NULL if the mint could
+ * not provide any (set only if @a http_status is #MHD_HTTP_OK)
+ * @param total_amount total amount of the wire transfer, or NULL if the mint could
+ * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
+ * @param details_length length of the @a details array
+ * @param details array with details about the combined transactions
+ */
+typedef void
+(*TALER_MINT_WireDepositsCallback)(void *cls,
+ unsigned int http_status,
+ json_t *json,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_Amount *total_amount,
+ unsigned int details_length,
+ const struct TALER_WireDepositDetails *details);
+
+
+/**
+ * Query the mint about which transactions were combined
+ * to create a wire transfer.
+ *
+ * @param mint mint to query
+ * @param wtid raw wire transfer identifier to get information about
+ * @param cb callback to call
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel operation
+ */
+struct TALER_MINT_WireDepositsHandle *
+TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_MINT_WireDepositsCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Cancel wire deposits request. This function cannot be used on a request
+ * handle if a response is already served for it.
+ *
+ * @param wdh the wire deposits request handle
+ */
+void
+TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh);
+
+
+/* ********************* /deposit/wtid *********************** */
+
+
+/**
+ * @brief A /deposit/wtid Handle
+ */
+struct TALER_MINT_DepositWtidHandle;
+
+
+/**
+ * Function called with detailed wire transfer data.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on mint protocol violation
+ * @param json original json reply (may include signatures, those have then been
+ * validated already)
+ * @param wtid wire transfer identifier used by the mint, NULL if mint did not
+ * yet execute the transaction
+ * @param execution_time actual or planned execution time for the wire transfer
+ * @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL)
+ */
+typedef void
+(*TALER_MINT_DepositWtidCallback)(void *cls,
+ unsigned int http_status,
+ json_t *json,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct GNUNET_TIME_Absolute execution_time,
+ const struct TALER_Amount *coin_contribution);
+
+
+/**
+ * Obtain the wire transfer details for a given deposit.
+ *
+ * @param mint the mint to query
+ * @param merchant_priv the merchant's private key
+ * @param h_wire hash of merchant's wire transfer details
+ * @param h_contract hash of the contract
+ * @param coin_pub public key of the coin
+ * @param transaction_id transaction identifier
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle to abort request
+ */
+struct TALER_MINT_DepositWtidHandle *
+TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t transaction_id,
+ TALER_MINT_DepositWtidCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Cancel deposit wtid request. This function cannot be used on a request
+ * handle if a response is already served for it.
+ *
+ * @param dwh the wire deposits request handle
+ */
+void
+TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh);
+
#endif /* _TALER_MINT_SERVICE_H */
diff --git a/src/include/taler_mintdb_lib.h b/src/include/taler_mintdb_lib.h
index 7dfef8dc5..70e314d9a 100644
--- a/src/include/taler_mintdb_lib.h
+++ b/src/include/taler_mintdb_lib.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h
index d9a1c6c85..d2cc3d76e 100644
--- a/src/include/taler_mintdb_plugin.h
+++ b/src/include/taler_mintdb_plugin.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -530,20 +530,24 @@ struct TALER_MINTDB_Session;
* corresponding wire transaction.
*
* @param cls closure
- * @param id transaction ID (used as future `min_id` to avoid
- * iterating over transactions more than once)
+ * @param rowid unique ID for the deposit in our DB, used for marking
+ * it as 'tiny' or 'done'
+ * @param merchant_pub public key of the merchant
+ * @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
* @param deposit_fee amount the mint gets to keep as transaction fees
* @param transaction_id unique transaction ID chosen by the merchant
* @param h_contract hash of the contract between merchant and customer
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
- * @param wire wire details for the merchant
+ * @param wire wire details for the merchant, NULL from iterate_matching_deposits()
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
typedef int
(*TALER_MINTDB_DepositIterator)(void *cls,
- uint64_t id,
+ unsigned long long rowid,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
uint64_t transaction_id,
@@ -570,21 +574,67 @@ typedef void
/**
* Function called with the results of the lookup of the
- * wire transfer identifier information.
- *
+ * wire transfer identifier information. Only called if
+ * we are at least aware of the transaction existing.
+ *
* @param cls closure
- * @param wtid base32-encoded wire transfer identifier, NULL
+ * @param wtid wire transfer identifier, NULL
* if the transaction was not yet done
+ * @param coin_contribution how much did the coin we asked about
+ * contribute to the total transfer value? (deposit value including fee)
+ * @param coin_fee how much did the mint charge for the deposit fee
* @param execution_time when was the transaction done, or
- * when we expect it to be done (if @a wtid was NULL);
- * #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
- * to the mint
+ * when we expect it to be done (if @a wtid was NULL)
*/
typedef void
(*TALER_MINTDB_DepositWtidCallback)(void *cls,
- const char *wtid,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_Amount *coin_fee,
struct GNUNET_TIME_Absolute execution_time);
+
+/**
+ * Function called with the results of the lookup of the
+ * transaction data associated with a wire transfer identifier.
+ *
+ * @param cls closure
+ * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_contract which contract was this payment about
+ * @param transaction_id merchant's transaction ID for the payment
+ * @param coin_pub which public key was this payment about
+ * @param coin_value amount contributed by this coin in total (with fee)
+ * @param coin_fee applicable fee for this coin
+ * @param transfer_value total amount of the wire transfer
+ */
+typedef void
+(*TALER_MINTDB_WireTransferDataCallback)(void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_value,
+ const struct TALER_Amount *coin_fee,
+ const struct TALER_Amount *transfer_value);
+
+
+/**
+ * Callback with data about a prepared transaction.
+ *
+ * @param cls closure
+ * @param rowid row identifier used to mark prepared transaction as done
+ * @param buf transaction data that was persisted, NULL on error
+ * @param buf_size number of bytes in @a buf, 0 on error
+ */
+typedef void
+(*TALER_MINTDB_WirePreparationCallback) (void *cls,
+ unsigned long long rowid,
+ const char *buf,
+ size_t buf_size);
+
+
/**
* @brief The plugin API, returned from the plugin's "init" function.
* The argument given to "init" is simply a configuration handle.
@@ -848,27 +898,78 @@ struct TALER_MINTDB_Plugin
/**
- * Obtain information about deposits. Iterates over all deposits
- * above a certain ID. Use a @a min_id of 0 to start at the beginning.
- * This operation is executed in its own transaction in transaction
- * mode "REPEATABLE READ", i.e. we should only see valid deposits.
+ * Mark a deposit as tiny, thereby declaring that it cannot be
+ * executed by itself and should no longer be returned by
+ * @e iterate_ready_deposits()
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param deposit_rowid identifies the deposit row to modify
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+ int
+ (*mark_deposit_tiny) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ unsigned long long rowid);
+
+
+ /**
+ * Mark a deposit as done, thereby declaring that it cannot be
+ * executed at all anymore, and should no longer be returned by
+ * @e iterate_ready_deposits() or @e iterate_matching_deposits().
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param deposit_rowid identifies the deposit row to modify
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+ int
+ (*mark_deposit_done) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ unsigned long long rowid);
+
+
+ /**
+ * Obtain information about deposits that are ready to be executed.
+ * Such deposits must not be marked as "tiny" or "done", and the
+ * execution time must be in the past.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param deposit_cb function to call for ONE such deposit
+ * @param deposit_cb_cls closure for @a deposit_cb
+ * @return number of rows processed, 0 if none exist,
+ * #GNUNET_SYSERR on error
+ */
+ int
+ (*get_ready_deposit) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ TALER_MINTDB_DepositIterator deposit_cb,
+ void *deposit_cb_cls);
+
+
+ /**
+ * Obtain information about other pending deposits for the same
+ * destination. Those deposits must not already be "done".
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
- * @param min_id deposit to start at
- * @param limit maximum number of transactions to fetch
+ * @param h_wire destination of the wire transfer
+ * @param merchant_pub public key of the merchant
* @param deposit_cb function to call for each deposit
* @param deposit_cb_cls closure for @a deposit_cb
+ * @param limit maximum number of matching deposits to return
* @return number of rows processed, 0 if none exist,
* #GNUNET_SYSERR on error
*/
int
- (*iterate_deposits) (void *cls,
- struct TALER_MINTDB_Session *session,
- uint64_t min_id,
- uint32_t limit,
- TALER_MINTDB_DepositIterator deposit_cb,
- void *deposit_cb_cls);
+ (*iterate_matching_deposits) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ TALER_MINTDB_DepositIterator deposit_cb,
+ void *deposit_cb_cls,
+ uint32_t limit);
/**
@@ -1112,10 +1213,10 @@ struct TALER_MINTDB_Plugin
*/
int
(*insert_refresh_out) (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- uint16_t newcoin_index,
- const struct TALER_DenominationSignature *ev_sig);
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t newcoin_index,
+ const struct TALER_DenominationSignature *ev_sig);
/**
@@ -1195,11 +1296,32 @@ struct TALER_MINTDB_Plugin
/**
+ * Lookup the list of Taler transactions that was aggregated
+ * into a wire transfer by the respective @a raw_wtid.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session database connection
+ * @param wtid the raw wire transfer identifier we used
+ * @param cb function to call on each transaction found
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
+ * #GNUNET_NO if we found no results
+ */
+ int
+ (*lookup_wire_transfer) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_MINTDB_WireTransferDataCallback cb,
+ void *cb_cls);
+
+
+ /**
* Try to find the wire transfer details for a deposit operation.
* If we did not execute the deposit yet, return when it is supposed
* to be executed.
- *
+ *
* @param cls closure
+ * @param session database connection
* @param h_contract hash of the contract
* @param h_wire hash of merchant wire details
* @param coin_pub public key of deposited coin
@@ -1207,10 +1329,12 @@ struct TALER_MINTDB_Plugin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
+ * #GNUNET_NO if nothing was found
*/
int
(*wire_lookup_deposit_wtid)(void *cls,
+ struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@@ -1219,7 +1343,91 @@ struct TALER_MINTDB_Plugin
TALER_MINTDB_DepositWtidCallback cb,
void *cb_cls);
+
+ /**
+ * Function called to insert aggregation information into the DB.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param wtid the raw wire transfer identifier we used
+ * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_contract which contract was this payment about
+ * @param transaction_id merchant's transaction ID for the payment
+ * @param execution_time when did we execute the transaction
+ * @param coin_pub which public key was this payment about
+ * @param coin_value amount contributed by this coin in total
+ * @param coin_fee deposit fee charged by mint for this coin
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ */
+ int
+ (*insert_aggregation_tracking)(void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ struct GNUNET_TIME_Absolute execution_time,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_value,
+ const struct TALER_Amount *coin_fee);
+
+
+ /**
+ * Function called to insert wire transfer commit data into the DB.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param type type of the wire transfer (i.e. "sepa")
+ * @param buf buffer with wire transfer preparation data
+ * @param buf_size number of bytes in @a buf
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ */
+ int
+ (*wire_prepare_data_insert)(void *cls,
+ struct TALER_MINTDB_Session *session,
+ const char *type,
+ const char *buf,
+ size_t buf_size);
+
+
+ /**
+ * Function called to mark wire transfer commit data as finished.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param rowid which entry to mark as finished
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ */
+ int
+ (*wire_prepare_data_mark_finished)(void *cls,
+ struct TALER_MINTDB_Session *session,
+ unsigned long long rowid);
+
+
+ /**
+ * Function called to get an unfinished wire transfer
+ * preparation data. Fetches at most one item.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param type type fo the wire transfer (i.e. "sepa")
+ * @param cb function to call for ONE unfinished item
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO if there are no entries,
+ * #GNUNET_SYSERR on DB errors
+ */
+ int
+ (*wire_prepare_data_get)(void *cls,
+ struct TALER_MINTDB_Session *session,
+ const char *type,
+ TALER_MINTDB_WirePreparationCallback cb,
+ void *cb_cls);
+
+
};
-#endif /* _NEURO_MINT_DB_H */
+#endif /* _TALER_MINT_DB_H */
diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h
index 2fe66c52f..c9a9ebdc5 100644
--- a/src/include/taler_pq_lib.h
+++ b/src/include/taler_pq_lib.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -156,7 +156,7 @@ struct TALER_PQ_QueryParam
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
-TALER_PQ_query_param_amount_nbo(const struct TALER_AmountNBO *x);
+TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x);
/**
@@ -168,7 +168,7 @@ TALER_PQ_query_param_amount_nbo(const struct TALER_AmountNBO *x);
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
-TALER_PQ_query_param_amount(const struct TALER_Amount *x);
+TALER_PQ_query_param_amount (const struct TALER_Amount *x);
/**
@@ -178,7 +178,7 @@ TALER_PQ_query_param_amount(const struct TALER_Amount *x);
* @param x the query parameter to pass.
*/
struct TALER_PQ_QueryParam
-TALER_PQ_query_param_rsa_public_key(const struct GNUNET_CRYPTO_rsa_PublicKey *x);
+TALER_PQ_query_param_rsa_public_key (const struct GNUNET_CRYPTO_rsa_PublicKey *x);
/**
@@ -188,7 +188,7 @@ TALER_PQ_query_param_rsa_public_key(const struct GNUNET_CRYPTO_rsa_PublicKey *x)
* @param x the query parameter to pass
*/
struct TALER_PQ_QueryParam
-TALER_PQ_query_param_rsa_signature(const struct GNUNET_CRYPTO_rsa_Signature *x);
+TALER_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_rsa_Signature *x);
/**
@@ -198,7 +198,7 @@ TALER_PQ_query_param_rsa_signature(const struct GNUNET_CRYPTO_rsa_Signature *x);
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
-TALER_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x);
+TALER_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x);
/**
@@ -208,7 +208,7 @@ TALER_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x);
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
-TALER_PQ_query_param_absolute_time_nbo(const struct GNUNET_TIME_AbsoluteNBO *x);
+TALER_PQ_query_param_absolute_time_nbo (const struct GNUNET_TIME_AbsoluteNBO *x);
/**
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index fb2916cff..2526597ee 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -106,6 +106,11 @@
*/
#define TALER_SIGNATURE_MINT_WIRE_TYPES 1036
+/**
+ * Signature where the Mint confirms the /deposit/wtid response.
+ */
+#define TALER_SIGNATURE_MINT_CONFIRM_WIRE 1036
+
/*********************/
/* Wallet signatures */
@@ -855,6 +860,33 @@ struct TALER_ContractPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions, in big endian. The merchant must communicate a
+ * merchant-unique ID to the customer for each transaction. Note
+ * that different coins that are part of the same transaction can
+ * use the same transaction ID. The transaction ID is useful for
+ * later disputes, and the merchant's contract offer (@e h_contract)
+ * with the customer should include the offer's term and transaction
+ * ID signed with a key from the merchant. This field must match
+ * the corresponding field in the JSON contract.
+ */
+ uint64_t transaction_id GNUNET_PACKED;
+
+ /**
+ * The total amount to be paid to the merchant. Note that if deposit
+ * fees are higher than @e max_fee, the actual total must be higher
+ * to cover the additional fees. This field must match the
+ * corresponding field in the JSON contract.
+ */
+ struct TALER_AmountNBO total_amount;
+
+ /**
+ * The maximum fee the merchant is willing to cover. This field
+ * must match the corresponding field in the JSON contract.
+ */
+ struct TALER_AmountNBO max_fee;
+
+ /**
* Hash of the JSON contract in UTF-8 including 0-termination,
* using JSON_COMPACT | JSON_SORT_KEYS
*/
@@ -863,6 +895,65 @@ struct TALER_ContractPS
};
+/**
+ * Details affirmed by the mint about a wire transfer the mint
+ * claims to have done with respect to a deposit operation.
+ */
+struct TALER_ConfirmWirePS
+{
+ /**
+ * Purpose header for the signature over the contract with
+ * purpose #TALER_SIGNATURE_MINT_CONFIRM_WIRE.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash over the wiring information of the merchant.
+ */
+ struct GNUNET_HashCode h_wire GNUNET_PACKED;
+
+ /**
+ * Hash over the contract for which this deposit is made.
+ */
+ struct GNUNET_HashCode h_contract GNUNET_PACKED;
+
+ /**
+ * Raw value (binary encoding) of the wire transfer subject.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * The coin's public key. This is the value that must have been
+ * signed (blindly) by the Mint.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions, in big endian. The merchant must communicate a
+ * merchant-unique ID to the customer for each transaction. Note
+ * that different coins that are part of the same transaction can
+ * use the same transaction ID. The transaction ID is useful for
+ * later disputes, and the merchant's contract offer (@e h_contract)
+ * with the customer should include the offer's term and transaction
+ * ID signed with a key from the merchant.
+ */
+ uint64_t transaction_id GNUNET_PACKED;
+
+ /**
+ * When did the mint execute this transfer? Note that the
+ * timestamp may not be exactly the same on the wire, i.e.
+ * because the wire has a different timezone or resolution.
+ */
+ struct GNUNET_TIME_AbsoluteNBO execution_time;
+
+ /**
+ * The contribution of @e coin_pub to the total transfer volume.
+ * This is the value of the deposit minus the fee.
+ */
+ struct TALER_AmountNBO coin_contribution;
+
+};
GNUNET_NETWORK_STRUCT_END
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index b6dd9596a..380901812 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/include/taler_util_wallet.h b/src/include/taler_util_wallet.h
index 4699a469d..87efcddaa 100644
--- a/src/include/taler_util_wallet.h
+++ b/src/include/taler_util_wallet.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/include/taler_wire_lib.h b/src/include/taler_wire_lib.h
new file mode 100644
index 000000000..4b0fcbbcc
--- /dev/null
+++ b/src/include/taler_wire_lib.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_wire_lib.h
+ * @brief Interface for loading and unloading wire plugins
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef TALER_WIRE_H
+#define TALER_WIRE_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_wire_plugin.h"
+
+
+/**
+ * Load a WIRE plugin.
+ *
+ * @param cfg configuration to use
+ * @param plugin_name name of the plugin to load
+ * @return #GNUNET_OK on success
+ */
+struct TALER_WIRE_Plugin *
+TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *plugin_name);
+
+/**
+ * Unload a WIRE plugin.
+ *
+ * @param plugin the plugin to unload
+ */
+void
+TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin);
+
+
+#endif
diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h
new file mode 100644
index 000000000..8fb194c57
--- /dev/null
+++ b/src/include/taler_wire_plugin.h
@@ -0,0 +1,180 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_wire_plugin.h
+ * @brief Plugin API for the handling of wire transactions
+ * @author Christian Grothoff
+ */
+#ifndef TALER_WIRE_PLUGIN_H
+#define TALER_WIRE_PLUGIN_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_util.h"
+
+
+/**
+ * Callback with prepared transaction.
+ *
+ * @param cls closure
+ * @param buf transaction data to persist, NULL on error
+ * @param buf_size number of bytes in @a buf, 0 on error
+ */
+typedef void
+(*TALER_WIRE_PrepareTransactionCallback) (void *cls,
+ const char *buf,
+ size_t buf_size);
+
+
+/**
+ * Handle returned for cancelling a preparation step.
+ */
+struct TALER_WIRE_PrepareHandle;
+
+
+/**
+ * Handle returned for cancelling an execution step.
+ */
+struct TALER_WIRE_ExecuteHandle;
+
+
+/**
+ * Function called with the result from the execute step.
+ *
+ * @param cls closure
+ * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * @param emsg NULL on success, otherwise an error message
+ */
+typedef void
+(*TALER_WIRE_ConfirmationCallback)(void *cls,
+ int success,
+ const char *emsg);
+
+
+/**
+ * @brief The plugin API, returned from the plugin's "init" function.
+ * The argument given to "init" is simply a configuration handle.
+ */
+struct TALER_WIRE_Plugin
+{
+
+ /**
+ * Closure for all callbacks.
+ */
+ void *cls;
+
+ /**
+ * Name of the library which generated this plugin. Set by the
+ * plugin loader.
+ */
+ char *library_name;
+
+ /**
+ * Round amount DOWN to the amount that can be transferred via the wire
+ * method. For example, Taler may support 0.000001 EUR as a unit of
+ * payment, but SEPA only supports 0.01 EUR. This function would
+ * round 0.125 EUR to 0.12 EUR in this case.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param[in,out] amount amount to round down
+ * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
+ * #GNUNET_SYSERR if the amount or currency was invalid
+ */
+ int
+ (*amount_round) (void *cls,
+ struct TALER_Amount *amount);
+
+
+ /**
+ * Check if the given wire format JSON object is correctly formatted
+ *
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+ int
+ (*wire_validate) (const json_t *wire);
+
+
+ /**
+ * Prepare for exeuction of a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param wire valid wire account information
+ * @param amount amount to transfer, already rounded
+ * @param wtid wire transfer identifier to use
+ * @param ptc function to call with the prepared data to persist
+ * @param ptc_cls closure for @a ptc
+ * @return NULL on failure
+ */
+ struct TALER_WIRE_PrepareHandle *
+ (*prepare_wire_transfer) (void *cls,
+ const json_t *wire,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_WIRE_PrepareTransactionCallback ptc,
+ void *ptc_cls);
+
+ /**
+ * Abort preparation of a wire transfer. For example,
+ * because we are shutting down.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param pth preparation to cancel
+ */
+ void
+ (*prepare_wire_transfer_cancel) (void *cls,
+ struct TALER_WIRE_PrepareHandle *pth);
+
+
+ /**
+ * Execute a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param buf buffer with the prepared execution details
+ * @param buf_size number of bytes in @a buf
+ * @param cc function to call upon success
+ * @param cc_cls closure for @a cc
+ * @return NULL on error
+ */
+ struct TALER_WIRE_ExecuteHandle *
+ (*execute_wire_transfer) (void *cls,
+ const char *buf,
+ size_t buf_size,
+ TALER_WIRE_ConfirmationCallback cc,
+ void *cc_cls);
+
+
+ /**
+ * Abort execution of a wire transfer. For example, because we are
+ * shutting down. Note that if an execution is aborted, it may or
+ * may not still succeed. The caller MUST run @e
+ * execute_wire_transfer again for the same request as soon as
+ * possilbe, to ensure that the request either ultimately succeeds
+ * or ultimately fails. Until this has been done, the transaction is
+ * in limbo (i.e. may or may not have been committed).
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param eh execution to cancel
+ */
+ void
+ (*execute_wire_transfer_cancel) (void *cls,
+ struct TALER_WIRE_ExecuteHandle *eh);
+
+
+};
+
+
+#endif /* TALER_WIRE_PLUGIN_H */
diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am
index ccea4ec58..171a42464 100644
--- a/src/mint-lib/Makefile.am
+++ b/src/mint-lib/Makefile.am
@@ -20,10 +20,12 @@ libtalermint_la_SOURCES = \
mint_api_handle.c mint_api_handle.h \
mint_api_admin.c \
mint_api_deposit.c \
+ mint_api_deposit_wtid.c \
mint_api_refresh.c \
mint_api_refresh_link.c \
mint_api_reserve.c \
- mint_api_wire.c
+ mint_api_wire.c \
+ mint_api_wire_deposits.c
libtalermint_la_LIBADD = \
-lgnunetutil \
diff --git a/src/mint-lib/mint_api_admin.c b/src/mint-lib/mint_api_admin.c
index 472b02e7d..641e0690f 100644
--- a/src/mint-lib/mint_api_admin.c
+++ b/src/mint-lib/mint_api_admin.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_common.c b/src/mint-lib/mint_api_common.c
index fd85fbdc4..faba38c74 100644
--- a/src/mint-lib/mint_api_common.c
+++ b/src/mint-lib/mint_api_common.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_common.h b/src/mint-lib/mint_api_common.h
index d256fa428..10a202146 100644
--- a/src/mint-lib/mint_api_common.h
+++ b/src/mint-lib/mint_api_common.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_context.c b/src/mint-lib/mint_api_context.c
index e60e01b07..2767906b5 100644
--- a/src/mint-lib/mint_api_context.c
+++ b/src/mint-lib/mint_api_context.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -288,7 +288,7 @@ TALER_MINT_perform (struct TALER_MINT_Context *ctx)
GNUNET_assert (CURLE_OK ==
curl_easy_getinfo (cmsg->easy_handle,
CURLINFO_PRIVATE,
- (char *) &job));
+ (char **) &job));
GNUNET_assert (job->ctx == ctx);
job->jcc (job->jcc_cls,
cmsg->easy_handle);
diff --git a/src/mint-lib/mint_api_context.h b/src/mint-lib/mint_api_context.h
index 79613cc8b..181a4808f 100644
--- a/src/mint-lib/mint_api_context.h
+++ b/src/mint-lib/mint_api_context.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_deposit.c b/src/mint-lib/mint_api_deposit.c
index 8f7b6db06..400372925 100644
--- a/src/mint-lib/mint_api_deposit.c
+++ b/src/mint-lib/mint_api_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -434,6 +434,14 @@ TALER_MINT_deposit (struct TALER_MINT_Handle *mint,
TALER_LOG_WARNING ("Denomination key unknown to mint\n");
return NULL;
}
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&amount_without_fee,
+ amount,
+ &dki->fee_deposit))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
if (GNUNET_OK !=
verify_signatures (dki,
@@ -492,9 +500,6 @@ TALER_MINT_deposit (struct TALER_MINT_Handle *mint,
dh->depconf.transaction_id = GNUNET_htonll (transaction_id);
dh->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dh->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
- TALER_amount_subtract (&amount_without_fee,
- amount,
- &dki->fee_deposit);
TALER_amount_hton (&dh->depconf.amount_without_fee,
&amount_without_fee);
dh->depconf.coin_pub = *coin_pub;
diff --git a/src/mint-lib/mint_api_deposit_wtid.c b/src/mint-lib/mint_api_deposit_wtid.c
new file mode 100644
index 000000000..d29f406e3
--- /dev/null
+++ b/src/mint-lib/mint_api_deposit_wtid.c
@@ -0,0 +1,379 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint-lib/mint_api_deposit_wtid.c
+ * @brief Implementation of the /deposit/wtid request of the mint's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mint_service.h"
+#include "mint_api_common.h"
+#include "mint_api_json.h"
+#include "mint_api_context.h"
+#include "mint_api_handle.h"
+#include "taler_signatures.h"
+
+
+/**
+ * @brief A Deposit Wtid Handle
+ */
+struct TALER_MINT_DepositWtidHandle
+{
+
+ /**
+ * The connection to mint this request handle will use
+ */
+ struct TALER_MINT_Handle *mint;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * JSON encoding of the request to POST.
+ */
+ char *json_enc;
+
+ /**
+ * Handle for the request.
+ */
+ struct MAC_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MINT_DepositWtidCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Download buffer
+ */
+ struct MAC_DownloadBuffer db;
+
+ /**
+ * Information the mint should sign in response.
+ * (with pre-filled fields from the request).
+ */
+ struct TALER_ConfirmWirePS depconf;
+
+};
+
+
+/**
+ * Verify that the signature on the "200 OK" response
+ * from the mint is valid.
+ *
+ * @param dwh deposit wtid handle
+ * @param json json reply with the signature
+ * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_deposit_wtid_signature_ok (const struct TALER_MINT_DepositWtidHandle *dwh,
+ json_t *json)
+{
+ struct TALER_MintSignatureP mint_sig;
+ struct TALER_MintPublicKeyP mint_pub;
+ const struct TALER_MINT_Keys *key_state;
+ struct MAJ_Specification spec[] = {
+ MAJ_spec_fixed_auto ("mint_sig", &mint_sig),
+ MAJ_spec_fixed_auto ("mint_pub", &mint_pub),
+ MAJ_spec_end
+ };
+
+ if (GNUNET_OK !=
+ MAJ_parse_json (json,
+ spec))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ key_state = TALER_MINT_get_keys (dwh->mint);
+ if (GNUNET_OK !=
+ TALER_MINT_test_signing_key (key_state,
+ &mint_pub))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_WIRE,
+ &dwh->depconf.purpose,
+ &mint_sig.eddsa_signature,
+ &mint_pub.eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /deposit/wtid request.
+ *
+ * @param cls the `struct TALER_MINT_DepositWtidHandle`
+ * @param eh the curl request handle
+ */
+static void
+handle_deposit_wtid_finished (void *cls,
+ CURL *eh)
+{
+ struct TALER_MINT_DepositWtidHandle *dwh = cls;
+ long response_code;
+ json_t *json;
+ const struct TALER_WireTransferIdentifierRawP *wtid = NULL;
+ struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS;
+ const struct TALER_Amount *coin_contribution = NULL;
+ struct TALER_Amount coin_contribution_s;
+
+ dwh->job = NULL;
+ json = MAC_download_get_result (&dwh->db,
+ eh,
+ &response_code);
+ switch (response_code)
+ {
+ case 0:
+ break;
+ case MHD_HTTP_OK:
+ {
+ struct MAJ_Specification spec[] = {
+ MAJ_spec_fixed_auto ("wtid", &dwh->depconf.wtid),
+ MAJ_spec_absolute_time ("execution_time", &execution_time),
+ MAJ_spec_amount ("coin_contribution", &coin_contribution_s),
+ MAJ_spec_end
+ };
+
+ if (GNUNET_OK !=
+ MAJ_parse_json (json,
+ spec))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ break;
+ }
+ wtid = &dwh->depconf.wtid;
+ dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (execution_time);
+ TALER_amount_hton (&dwh->depconf.coin_contribution,
+ &coin_contribution_s);
+ coin_contribution = &coin_contribution_s;
+ if (GNUNET_OK !=
+ verify_deposit_wtid_signature_ok (dwh,
+ json))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ }
+ }
+ break;
+ case MHD_HTTP_ACCEPTED:
+ {
+ /* Transaction known, but not executed yet */
+ struct MAJ_Specification spec[] = {
+ MAJ_spec_absolute_time ("execution_time", &execution_time),
+ MAJ_spec_end
+ };
+
+ if (GNUNET_OK !=
+ MAJ_parse_json (json,
+ spec))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ break;
+ }
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the mint is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ /* Nothing really to verify, mint says one of the signatures is
+ invalid; as we checked them, this should never happen, we
+ should pass the JSON reply to the application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Mint does not know about transaction;
+ we should pass the reply to the application */
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ response_code);
+ GNUNET_break (0);
+ response_code = 0;
+ break;
+ }
+ dwh->cb (dwh->cb_cls,
+ response_code,
+ json,
+ wtid,
+ execution_time,
+ coin_contribution);
+ json_decref (json);
+ TALER_MINT_deposit_wtid_cancel (dwh);
+}
+
+
+/**
+ * Obtain wire transfer details about an existing deposit operation.
+ *
+ * @param mint the mint to query
+ * @param merchant_priv the merchant's private key
+ * @param h_wire hash of merchant's wire transfer details
+ * @param h_contract hash of the contract
+ * @param coin_pub public key of the coin
+ * @param transaction_id transaction identifier
+ * @param cb function to call with the result
+ * @param cb_cls closure for @a cb
+ * @return handle to abort request
+ */
+struct TALER_MINT_DepositWtidHandle *
+TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint,
+ const struct TALER_MerchantPrivateKeyP *merchant_priv,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t transaction_id,
+ TALER_MINT_DepositWtidCallback cb,
+ void *cb_cls)
+{
+ struct TALER_DepositTrackPS dtp;
+ struct TALER_MerchantSignatureP merchant_sig;
+ struct TALER_MINT_DepositWtidHandle *dwh;
+ struct TALER_MINT_Context *ctx;
+ json_t *deposit_wtid_obj;
+ CURL *eh;
+
+ if (GNUNET_YES !=
+ MAH_handle_is_ready (mint))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ dtp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_DEPOSIT_WTID);
+ dtp.purpose.size = htonl (sizeof (dtp));
+ dtp.h_contract = *h_contract;
+ dtp.h_wire = *h_wire;
+ dtp.transaction_id = GNUNET_htonll (transaction_id);
+ GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
+ &dtp.merchant.eddsa_pub);
+
+ dtp.coin_pub = *coin_pub;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
+ &dtp.purpose,
+ &merchant_sig.eddsa_sig));
+ deposit_wtid_obj = json_pack ("{s:o, s:o," /* H_wire, H_contract */
+ " s:o, s:I," /* coin_pub, transaction_id */
+ " s:o, s:o}", /* merchant_pub, merchant_sig */
+ "H_wire", TALER_json_from_data (h_wire,
+ sizeof (struct GNUNET_HashCode)),
+ "H_contract", TALER_json_from_data (h_contract,
+ sizeof (struct GNUNET_HashCode)),
+ "coin_pub", TALER_json_from_data (coin_pub,
+ sizeof (*coin_pub)),
+ "transaction_id", (json_int_t) transaction_id,
+ "merchant_pub", TALER_json_from_data (&dtp.merchant,
+ sizeof (struct TALER_MerchantPublicKeyP)),
+ "merchant_sig", TALER_json_from_data (&merchant_sig,
+ sizeof (merchant_sig)));
+
+ dwh = GNUNET_new (struct TALER_MINT_DepositWtidHandle);
+ dwh->mint = mint;
+ dwh->cb = cb;
+ dwh->cb_cls = cb_cls;
+ dwh->url = MAH_path_to_url (mint, "/deposit/wtid");
+ dwh->depconf.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
+ dwh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE);
+ dwh->depconf.h_wire = *h_wire;
+ dwh->depconf.h_contract = *h_contract;
+ dwh->depconf.coin_pub = *coin_pub;
+ dwh->depconf.transaction_id = GNUNET_htonll (transaction_id);
+
+ eh = curl_easy_init ();
+ GNUNET_assert (NULL != (dwh->json_enc =
+ json_dumps (deposit_wtid_obj,
+ JSON_COMPACT)));
+ json_decref (deposit_wtid_obj);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ dwh->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDS,
+ dwh->json_enc));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_POSTFIELDSIZE,
+ strlen (dwh->json_enc)));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEFUNCTION,
+ &MAC_download_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEDATA,
+ &dwh->db));
+ ctx = MAH_handle_to_context (mint);
+ dwh->job = MAC_job_add (ctx,
+ eh,
+ GNUNET_YES,
+ &handle_deposit_wtid_finished,
+ dwh);
+ return dwh;
+}
+
+
+/**
+ * Cancel deposit wtid request. This function cannot be used on a request
+ * handle if a response is already served for it.
+ *
+ * @param dwh the wire deposits request handle
+ */
+void
+TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh)
+{
+ if (NULL != dwh->job)
+ {
+ MAC_job_cancel (dwh->job);
+ dwh->job = NULL;
+ }
+ GNUNET_free_non_null (dwh->db.buf);
+ GNUNET_free (dwh->url);
+ GNUNET_free (dwh->json_enc);
+ GNUNET_free (dwh);
+}
+
+
+/* end of mint_api_deposit_wtid.c */
diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c
index 1b0b73c50..ef26cd838 100644
--- a/src/mint-lib/mint_api_handle.c
+++ b/src/mint-lib/mint_api_handle.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -755,7 +755,7 @@ TALER_MINT_connect (struct TALER_MINT_Context *ctx,
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (c,
CURLOPT_VERBOSE,
- 1));
+ 0));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (c,
CURLOPT_STDERR,
diff --git a/src/mint-lib/mint_api_handle.h b/src/mint-lib/mint_api_handle.h
index fae30a309..0dae58db6 100644
--- a/src/mint-lib/mint_api_handle.h
+++ b/src/mint-lib/mint_api_handle.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_json.c b/src/mint-lib/mint_api_json.c
index a728a5495..7de33e5eb 100644
--- a/src/mint-lib/mint_api_json.c
+++ b/src/mint-lib/mint_api_json.c
@@ -232,6 +232,20 @@ parse_json (json_t *root,
}
break;
+ case MAJ_CMD_UINT64:
+ {
+ json_int_t val;
+
+ if (! json_is_integer (pos))
+ {
+ GNUNET_break_op (0);
+ return i;
+ }
+ val = json_integer_value (pos);
+ *spec[i].details.u64 = (uint64_t) val;
+ }
+ break;
+
case MAJ_CMD_JSON_OBJECT:
{
if (! (json_is_object (pos) || json_is_array (pos)) )
@@ -429,6 +443,26 @@ MAJ_spec_uint16 (const char *name,
/**
+ * 64-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u64 where to store the integer found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_uint64 (const char *name,
+ uint64_t *u64)
+{
+ struct MAJ_Specification ret =
+ {
+ .cmd = MAJ_CMD_UINT64,
+ .field = name,
+ .details.u64 = u64
+ };
+ return ret;
+}
+
+
+/**
* JSON object.
*
* @param name name of the JSON field
diff --git a/src/mint-lib/mint_api_json.h b/src/mint-lib/mint_api_json.h
index 68809059e..6bc3a5572 100644
--- a/src/mint-lib/mint_api_json.h
+++ b/src/mint-lib/mint_api_json.h
@@ -79,6 +79,11 @@ enum MAJ_Command
MAJ_CMD_UINT16,
/**
+ * Parse `uint64_t` integer at the current position.
+ */
+ MAJ_CMD_UINT64,
+
+ /**
* Parse JSON object at the current position.
*/
MAJ_CMD_JSON_OBJECT,
@@ -192,6 +197,11 @@ struct MAJ_Specification
uint16_t *u16;
/**
+ * Where to store 64-bit integer.
+ */
+ uint64_t *u64;
+
+ /**
* Where to store a JSON object.
*/
json_t **obj;
@@ -283,6 +293,17 @@ MAJ_spec_uint16 (const char *name,
/**
+ * 64-bit integer.
+ *
+ * @param name name of the JSON field
+ * @param[out] u64 where to store the integer found under @a name
+ */
+struct MAJ_Specification
+MAJ_spec_uint64 (const char *name,
+ uint64_t *u64);
+
+
+/**
* JSON object.
*
* @param name name of the JSON field
diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c
index a779bdbc3..cea16b153 100644
--- a/src/mint-lib/mint_api_refresh.c
+++ b/src/mint-lib/mint_api_refresh.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -334,12 +334,9 @@ free_melt_data (struct MeltData *md)
for (i=0;i<TALER_CNC_KAPPA;i++)
{
- if (NULL != md->fresh_coins)
- {
- for (j=0;j<md->num_fresh_coins;j++)
- free_fresh_coin (&md->fresh_coins[i][j]);
- GNUNET_free (md->fresh_coins[i]);
- }
+ for (j=0;j<md->num_fresh_coins;j++)
+ free_fresh_coin (&md->fresh_coins[i][j]);
+ GNUNET_free (md->fresh_coins[i]);
}
/* Finally, clean up a bit...
(NOTE: compilers might optimize this away, so this is
@@ -774,7 +771,7 @@ deserialize_melt_data (const char *buf,
&buf[off],
buf_size - off,
&ok);
- if (off != buf_size)
+ if (off != buf_size)
{
GNUNET_break (0);
ok = GNUNET_NO;
diff --git a/src/mint-lib/mint_api_refresh_link.c b/src/mint-lib/mint_api_refresh_link.c
index d4060bd1c..dcd2326ca 100644
--- a/src/mint-lib/mint_api_refresh_link.c
+++ b/src/mint-lib/mint_api_refresh_link.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -185,6 +185,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
return GNUNET_SYSERR;
}
num_coins = 0;
+ /* Theoretically, a coin may have been melted repeatedly
+ into different sessions; so the response is an array
+ which contains information by melting session. That
+ array contains another array. However, our API returns
+ a single 1d array, so we flatten the 2d array that is
+ returned into a single array. Note that usually a coin
+ is melted at most once, and so we'll only run this
+ loop once for 'session=0' in most cases.
+
+ num_coins tracks the size of the 1d array we return,
+ whilst 'i' and 'session' track the 2d array. */
for (session=0;session<json_array_size (json); session++)
{
json_t *jsona;
@@ -194,7 +205,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
};
if (GNUNET_OK !=
- MAJ_parse_json (json_array_get (json,
+ MAJ_parse_json (json_array_get (json,
session),
spec))
{
@@ -212,13 +223,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
num_coins += json_array_size (jsona);
MAJ_parse_free (spec);
}
+ /* Now that we know how big the 1d array is, allocate
+ and fill it. */
{
- unsigned int off_coin;
+ unsigned int off_coin; /* index into 1d array */
unsigned int i;
struct TALER_CoinSpendPrivateKeyP coin_privs[num_coins];
struct TALER_DenominationSignature sigs[num_coins];
struct TALER_DenominationPublicKey pubs[num_coins];
-
+
+ memset (sigs, 0, sizeof (sigs));
+ memset (pubs, 0, sizeof (pubs));
off_coin = 0;
for (session=0;session<json_array_size (json); session++)
{
@@ -233,7 +248,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
};
if (GNUNET_OK !=
- MAJ_parse_json (json_array_get (json,
+ MAJ_parse_json (json_array_get (json,
session),
spec))
{
@@ -246,13 +261,13 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
MAJ_parse_free (spec);
return GNUNET_SYSERR;
}
-
+
/* decode all coins */
for (i=0;i<json_array_size (jsona);i++)
{
if (GNUNET_OK !=
parse_refresh_link_coin (rlh,
- json_array_get (jsona,
+ json_array_get (jsona,
i),
&trans_pub,
&secret_enc,
@@ -265,6 +280,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
}
}
/* check if we really got all, then invoke callback */
+ off_coin += i;
if (i != json_array_size (jsona))
{
GNUNET_break_op (0);
@@ -272,9 +288,9 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
MAJ_parse_free (spec);
break;
}
- off_coin += json_array_size (jsona);
MAJ_parse_free (spec);
- }
+ } /* end of for (session) */
+
if (off_coin == num_coins)
{
rlh->link_cb (rlh->link_cb_cls,
@@ -294,9 +310,13 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
}
/* clean up */
- for (i=0;i<num_coins;i++)
+ for (i=0;i<off_coin;i++)
+ {
if (NULL != sigs[i].rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
+ if (NULL != pubs[i].rsa_public_key)
+ GNUNET_CRYPTO_rsa_public_key_free (pubs[i].rsa_public_key);
+ }
}
return ret;
}
diff --git a/src/mint-lib/mint_api_reserve.c b/src/mint-lib/mint_api_reserve.c
index a726eca46..1f8140cff 100644
--- a/src/mint-lib/mint_api_reserve.c
+++ b/src/mint-lib/mint_api_reserve.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_wire.c b/src/mint-lib/mint_api_wire.c
index 5fc82f72d..0947354ad 100644
--- a/src/mint-lib/mint_api_wire.c
+++ b/src/mint-lib/mint_api_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-lib/mint_api_wire_deposits.c b/src/mint-lib/mint_api_wire_deposits.c
new file mode 100644
index 000000000..f71c5b696
--- /dev/null
+++ b/src/mint-lib/mint_api_wire_deposits.c
@@ -0,0 +1,284 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint-lib/mint_api_wire_deposits.c
+ * @brief Implementation of the /wire/deposits request of the mint's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mint_service.h"
+#include "mint_api_common.h"
+#include "mint_api_json.h"
+#include "mint_api_context.h"
+#include "mint_api_handle.h"
+#include "taler_signatures.h"
+
+
+/**
+ * @brief A /wire/deposits Handle
+ */
+struct TALER_MINT_WireDepositsHandle
+{
+
+ /**
+ * The connection to mint this request handle will use
+ */
+ struct TALER_MINT_Handle *mint;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct MAC_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MINT_WireDepositsCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Download buffer
+ */
+ struct MAC_DownloadBuffer db;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /wire/deposits request.
+ *
+ * @param cls the `struct TALER_MINT_WireDepositsHandle`
+ * @param eh the curl request handle
+ */
+static void
+handle_wire_deposits_finished (void *cls,
+ CURL *eh)
+{
+ struct TALER_MINT_WireDepositsHandle *wdh = cls;
+ long response_code;
+ json_t *json;
+
+ wdh->job = NULL;
+ json = MAC_download_get_result (&wdh->db,
+ eh,
+ &response_code);
+ switch (response_code)
+ {
+ case 0:
+ break;
+ case MHD_HTTP_OK:
+ {
+ json_t *details_j;
+ struct GNUNET_HashCode h_wire;
+ struct TALER_Amount total_amount;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ unsigned int num_details;
+ struct MAJ_Specification spec[] = {
+ MAJ_spec_fixed_auto ("H_wire", &h_wire),
+ MAJ_spec_fixed_auto ("merchant_pub", &merchant_pub),
+ MAJ_spec_amount ("total_amount", &total_amount),
+ MAJ_spec_json ("details", &details_j),
+ MAJ_spec_end
+ };
+
+ if (GNUNET_OK !=
+ MAJ_parse_json (json,
+ spec))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ break;
+ }
+ num_details = json_array_size (details_j);
+ {
+ struct TALER_WireDepositDetails details[num_details];
+ unsigned int i;
+
+ for (i=0;i<num_details;i++)
+ {
+ struct TALER_WireDepositDetails *detail = &details[i];
+ struct json_t *detail_j = json_array_get (details_j, i);
+ struct MAJ_Specification spec_detail[] = {
+ MAJ_spec_fixed_auto ("H_contract", &detail->h_contract),
+ MAJ_spec_amount ("deposit_value", &detail->coin_value),
+ MAJ_spec_amount ("deposit_fee", &detail->coin_fee),
+ MAJ_spec_uint64 ("transaction_id", &detail->transaction_id),
+ MAJ_spec_fixed_auto ("coin_pub", &detail->coin_pub),
+ MAJ_spec_end
+ };
+
+ if (GNUNET_OK !=
+ MAJ_parse_json (detail_j,
+ spec_detail))
+ {
+ GNUNET_break_op (0);
+ response_code = 0;
+ break;
+ }
+ }
+ if (0 == response_code)
+ break;
+ wdh->cb (wdh->cb_cls,
+ response_code,
+ json,
+ &h_wire,
+ &total_amount,
+ num_details,
+ details);
+ json_decref (json);
+ TALER_MINT_wire_deposits_cancel (wdh);
+ return;
+ }
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* This should never happen, either us or the mint is buggy
+ (or API version conflict); just pass JSON reply to the application */
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ /* Nothing really to verify, mint says one of the signatures is
+ invalid; as we checked them, this should never happen, we
+ should pass the JSON reply to the application */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* Mint does not know about transaction;
+ we should pass the reply to the application */
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves this to the application */
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ response_code);
+ GNUNET_break (0);
+ response_code = 0;
+ break;
+ }
+ wdh->cb (wdh->cb_cls,
+ response_code,
+ json,
+ NULL, NULL, 0, NULL);
+ json_decref (json);
+ TALER_MINT_wire_deposits_cancel (wdh);
+}
+
+
+/**
+ * Query the mint about which transactions were combined
+ * to create a wire transfer.
+ *
+ * @param mint mint to query
+ * @param wtid raw wire transfer identifier to get information about
+ * @param cb callback to call
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel operation
+ */
+struct TALER_MINT_WireDepositsHandle *
+TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_MINT_WireDepositsCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MINT_WireDepositsHandle *wdh;
+ struct TALER_MINT_Context *ctx;
+ char *buf;
+ char *path;
+ CURL *eh;
+
+ if (GNUNET_YES !=
+ MAH_handle_is_ready (mint))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ wdh = GNUNET_new (struct TALER_MINT_WireDepositsHandle);
+ wdh->mint = mint;
+ wdh->cb = cb;
+ wdh->cb_cls = cb_cls;
+
+ buf = GNUNET_STRINGS_data_to_string_alloc (wtid,
+ sizeof (struct TALER_WireTransferIdentifierRawP));
+ GNUNET_asprintf (&path,
+ "/wire/deposits?wtid=%s",
+ buf);
+ wdh->url = MAH_path_to_url (wdh->mint,
+ path);
+ GNUNET_free (buf);
+ GNUNET_free (path);
+
+ eh = curl_easy_init ();
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ wdh->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEFUNCTION,
+ &MAC_download_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_WRITEDATA,
+ &wdh->db));
+ ctx = MAH_handle_to_context (mint);
+ wdh->job = MAC_job_add (ctx,
+ eh,
+ GNUNET_YES,
+ &handle_wire_deposits_finished,
+ wdh);
+ return wdh;
+}
+
+
+/**
+ * Cancel wire deposits request. This function cannot be used on a request
+ * handle if a response is already served for it.
+ *
+ * @param wdh the wire deposits request handle
+ */
+void
+TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh)
+{
+ if (NULL != wdh->job)
+ {
+ MAC_job_cancel (wdh->job);
+ wdh->job = NULL;
+ }
+ GNUNET_free_non_null (wdh->db.buf);
+ GNUNET_free (wdh->url);
+ GNUNET_free (wdh);
+}
+
+
+/* end of mint_api_wire_deposits.c */
diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c
index 024e080fc..e41d01805 100644
--- a/src/mint-lib/test_mint_api.c
+++ b/src/mint-lib/test_mint_api.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -111,7 +111,17 @@ enum OpCode
/**
* Verify the mint's /wire-method.
*/
- OC_WIRE
+ OC_WIRE,
+
+ /**
+ * Verify mint's /wire/deposits method.
+ */
+ OC_WIRE_DEPOSITS,
+
+ /**
+ * Verify mint's /deposit/wtid method.
+ */
+ OC_DEPOSIT_WTID
};
@@ -470,6 +480,60 @@ struct Command
} wire;
+ /**
+ * Information for the /wire/deposits's command.
+ */
+ struct {
+
+ /**
+ * Handle to the wire deposits request.
+ */
+ struct TALER_MINT_WireDepositsHandle *wdh;
+
+ /**
+ * Reference to a /deposit/wtid command. If set, we use the
+ * WTID from that command.
+ */
+ const char *wtid_ref;
+
+ /**
+ * WTID to use (used if @e wtid_ref is NULL).
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /* TODO: may want to add list of deposits we expected
+ to see aggregated here in the future. */
+
+ } wire_deposits;
+
+ /**
+ * Information for the /deposit/wtid command.
+ */
+ struct {
+
+ /**
+ * Handle to the deposit wtid request.
+ */
+ struct TALER_MINT_DepositWtidHandle *dwh;
+
+ /**
+ * Which /deposit operation should we obtain WTID data for?
+ */
+ const char *deposit_ref;
+
+ /**
+ * What is the expected total amount? Only used if
+ * @e expected_response_code was #MHD_HTTP_OK.
+ */
+ struct TALER_Amount total_amount_expected;
+
+ /**
+ * Wire transfer identifier, set if #MHD_HTTP_OK was the response code.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ } deposit_wtid;
+
} details;
};
@@ -1220,6 +1284,158 @@ wire_cb (void *cls,
/**
+ * Function called with detailed wire transfer data, including all
+ * of the coin transactions that were combined into the wire transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on mint protocol violation
+ * @param json original json reply (may include signatures, those have then been
+ * validated already)
+ * @param wtid extracted wire transfer identifier, or NULL if the mint could
+ * not provide any (set only if @a http_status is #MHD_HTTP_OK)
+ * @param total_amount total amount of the wire transfer, or NULL if the mint could
+ * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
+ * @param details_length length of the @a details array
+ * @param details array with details about the combined transactions
+ */
+static void
+wire_deposits_cb (void *cls,
+ unsigned int http_status,
+ json_t *json,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_Amount *total_amount,
+ unsigned int details_length,
+ const struct TALER_WireDepositDetails *details)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+ const struct Command *ref;
+
+ cmd->details.wire_deposits.wdh = NULL;
+ ref = find_command (is,
+ cmd->details.wire_deposits.wtid_ref);
+ if (cmd->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ fail (is);
+ return;
+ }
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ if (0 != TALER_amount_cmp (total_amount,
+ &ref->details.deposit_wtid.total_amount_expected))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Total amount missmatch to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ fail (is);
+ return;
+ }
+ if (NULL != ref->details.deposit_wtid.deposit_ref)
+ {
+ const struct Command *dep;
+ struct GNUNET_HashCode hw;
+
+ dep = find_command (is,
+ ref->details.deposit_wtid.deposit_ref);
+ GNUNET_CRYPTO_hash (dep->details.deposit.wire_details,
+ strlen (dep->details.deposit.wire_details),
+ &hw);
+ if (0 != memcmp (&hw,
+ h_wire,
+ sizeof (struct GNUNET_HashCode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire hash missmatch to command %s\n",
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ fail (is);
+ return;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* move to next command */
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
+ * Function called with detailed wire transfer data.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on mint protocol violation
+ * @param json original json reply (may include signatures, those have then been
+ * validated already)
+ * @param wtid wire transfer identifier used by the mint, NULL if mint did not
+ * yet execute the transaction
+ * @param execution_time actual or planned execution time for the wire transfer
+ * @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL)
+ * @param total_amount total amount of the wire transfer, or NULL if the mint could
+ * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
+ */
+static void
+deposit_wtid_cb (void *cls,
+ unsigned int http_status,
+ json_t *json,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct GNUNET_TIME_Absolute execution_time,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_Amount *total_amount)
+{
+ struct InterpreterState *is = cls;
+ struct Command *cmd = &is->commands[is->ip];
+
+ cmd->details.deposit_wtid.dwh = NULL;
+ if (cmd->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ fail (is);
+ return;
+ }
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ cmd->details.deposit_wtid.wtid = *wtid;
+ if (0 != TALER_amount_cmp (total_amount,
+ &cmd->details.deposit_wtid.total_amount_expected))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Total amount missmatch to command %s\n",
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ fail (is);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* move to next command */
+ is->ip++;
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+ is);
+}
+
+
+/**
* Run the main interpreter loop that performs mint operations.
*
* @param cls contains the `struct InterpreterState`
@@ -1401,7 +1617,9 @@ interpreter_run (void *cls,
struct GNUNET_TIME_Absolute refund_deadline;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_TIME_Absolute timestamp;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
struct TALER_MerchantPublicKeyP merchant_pub;
+ json_t *contract;
json_t *wire;
GNUNET_assert (NULL !=
@@ -1444,37 +1662,51 @@ interpreter_run (void *cls,
fail (is);
return;
}
- GNUNET_CRYPTO_hash (cmd->details.deposit.contract,
- strlen (cmd->details.deposit.contract),
- &h_contract);
+ contract = json_loads (cmd->details.deposit.contract,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ if (NULL == contract)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse contract details `%s' at %u/%s\n",
+ cmd->details.deposit.contract,
+ is->ip,
+ cmd->label);
+ fail (is);
+ return;
+ }
+ TALER_hash_json (contract,
+ &h_contract);
wire = json_loads (cmd->details.deposit.wire_details,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == wire)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to parse wire details `%s' at %u\n",
+ "Failed to parse wire details `%s' at %u/%s\n",
cmd->details.deposit.wire_details,
- is->ip);
+ is->ip,
+ cmd->label);
fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
+ GNUNET_free (priv);
if (0 != cmd->details.deposit.refund_deadline.rel_value_us)
{
- struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
-
- priv = GNUNET_CRYPTO_eddsa_key_create ();
- cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
- GNUNET_free (priv);
refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline);
}
else
{
refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS;
}
+ GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.deposit.merchant_priv.eddsa_priv,
+ &merchant_pub.eddsa_pub);
+
wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
timestamp = GNUNET_TIME_absolute_get ();
TALER_round_abs_time (&timestamp);
@@ -1686,6 +1918,85 @@ interpreter_run (void *cls,
is);
trigger_context_task ();
return;
+ case OC_WIRE_DEPOSITS:
+ if (NULL != cmd->details.wire_deposits.wtid_ref)
+ {
+ ref = find_command (is,
+ cmd->details.wire_deposits.wtid_ref);
+ GNUNET_assert (NULL != ref);
+ cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid;
+ }
+ cmd->details.wire_deposits.wdh
+ = TALER_MINT_wire_deposits (mint,
+ &cmd->details.wire_deposits.wtid,
+ &wire_deposits_cb,
+ is);
+ trigger_context_task ();
+ return;
+ case OC_DEPOSIT_WTID:
+ {
+ struct GNUNET_HashCode h_wire;
+ struct GNUNET_HashCode h_contract;
+ json_t *wire;
+ json_t *contract;
+ const struct Command *coin;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ ref = find_command (is,
+ cmd->details.deposit_wtid.deposit_ref);
+ GNUNET_assert (NULL != ref);
+ coin = find_command (is,
+ ref->details.deposit.coin_ref);
+ GNUNET_assert (NULL != coin);
+ switch (coin->oc)
+ {
+ case OC_WITHDRAW_SIGN:
+ GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ break;
+ case OC_REFRESH_REVEAL:
+ {
+ const struct FreshCoin *fc;
+ unsigned int idx;
+
+ idx = ref->details.deposit.coin_idx;
+ GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins);
+ fc = &coin->details.refresh_reveal.fresh_coins[idx];
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+
+ wire = json_loads (ref->details.deposit.wire_details,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ GNUNET_assert (NULL != wire);
+ TALER_hash_json (wire,
+ &h_wire);
+ json_decref (wire);
+ contract = json_loads (ref->details.deposit.contract,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ GNUNET_assert (NULL != contract);
+ TALER_hash_json (contract,
+ &h_contract);
+ json_decref (contract);
+ cmd->details.deposit_wtid.dwh
+ = TALER_MINT_deposit_wtid (mint,
+ &ref->details.deposit.merchant_priv,
+ &h_wire,
+ &h_contract,
+ &coin_pub,
+ ref->details.deposit.transaction_id,
+ &deposit_wtid_cb,
+ is);
+ trigger_context_task ();
+ }
+ return;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@@ -1695,8 +2006,6 @@ interpreter_run (void *cls,
fail (is);
return;
}
- is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
- is);
}
@@ -1829,10 +2138,36 @@ do_shutdown (void *cls,
case OC_WIRE:
if (NULL != cmd->details.wire.wh)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
TALER_MINT_wire_cancel (cmd->details.wire.wh);
cmd->details.wire.wh = NULL;
}
break;
+ case OC_WIRE_DEPOSITS:
+ if (NULL != cmd->details.wire_deposits.wdh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_MINT_wire_deposits_cancel (cmd->details.wire_deposits.wdh);
+ cmd->details.wire_deposits.wdh = NULL;
+ }
+ break;
+ case OC_DEPOSIT_WTID:
+ if (NULL != cmd->details.deposit_wtid.dwh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ i,
+ cmd->label);
+ TALER_MINT_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh);
+ cmd->details.deposit_wtid.dwh = NULL;
+ }
+ break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@@ -2047,7 +2382,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
+ .details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 1 },
/* Try to overdraw funds ... */
@@ -2064,7 +2399,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":43 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
+ .details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 1 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
transaction ID) */
@@ -2074,7 +2409,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
+ .details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 2 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
contract) */
@@ -2084,7 +2419,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }",
+ .details.deposit.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }",
.details.deposit.transaction_id = 1 },
/* ***************** /refresh testing ******************** */
@@ -2109,7 +2444,7 @@ run (void *cls,
.details.deposit.amount = "EUR:1",
.details.deposit.coin_ref = "refresh-withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\"EUR:1 } }",
+ .details.deposit.contract = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:1\" } ] }",
.details.deposit.transaction_id = 42421 },
/* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
@@ -2143,7 +2478,7 @@ run (void *cls,
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 0,
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }",
+ .details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
.details.deposit.transaction_id = 2 },
/* Test successfully spending coins from the refresh operation:
@@ -2155,7 +2490,7 @@ run (void *cls,
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 4,
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
- .details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }",
+ .details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
.details.deposit.transaction_id = 2 },
/* Test running a failing melt operation (same operation again must fail) */
@@ -2168,6 +2503,35 @@ run (void *cls,
// FIXME: also test with coin that was already melted
// (signature differs from coin that was deposited...)
/* *************** end of /refresh testing ************** */
+
+ /* ************** Test tracking API ******************** */
+ /* Try resolving a deposit's WTID, as we never triggered
+ execution of transactions, the answer should be that
+ the mint knows about the deposit, but has no WTID yet. */
+ { .oc = OC_DEPOSIT_WTID,
+ .label = "deposit-wtid-found",
+ .expected_response_code = MHD_HTTP_ACCEPTED,
+ .details.deposit_wtid.deposit_ref = "deposit-simple" },
+ /* Try resolving a deposit's WTID for a failed deposit.
+ As the deposit failed, the answer should be that
+ the mint does NOT know about the deposit. */
+ { .oc = OC_DEPOSIT_WTID,
+ .label = "deposit-wtid-failing",
+ .expected_response_code = MHD_HTTP_NOT_FOUND,
+ .details.deposit_wtid.deposit_ref = "deposit-double-2" },
+ /* Try resolving an undefined (all zeros) WTID; this
+ should fail as obviously the mint didn't use that
+ WTID value for any transaction. */
+ { .oc = OC_WIRE_DEPOSITS,
+ .label = "wire-deposit-failing",
+ .expected_response_code = MHD_HTTP_NOT_FOUND },
+
+ /* TODO: trigger aggregation logic and then check the
+ cases where tracking succeeds! */
+
+ /* ************** End of tracking API testing************* */
+
+
#endif
{ .oc = OC_END }
diff --git a/src/mint-tools/taler-auditor-sign.c b/src/mint-tools/taler-auditor-sign.c
index bd37e68de..7e6d3b12a 100644
--- a/src/mint-tools/taler-auditor-sign.c
+++ b/src/mint-tools/taler-auditor-sign.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-tools/taler-mint-dbinit.c b/src/mint-tools/taler-mint-dbinit.c
index 3293a69ab..2d9f77764 100644
--- a/src/mint-tools/taler-mint-dbinit.c
+++ b/src/mint-tools/taler-mint-dbinit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-tools/taler-mint-keycheck.c b/src/mint-tools/taler-mint-keycheck.c
index c5ac86cb6..4fa2707f8 100644
--- a/src/mint-tools/taler-mint-keycheck.c
+++ b/src/mint-tools/taler-mint-keycheck.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-tools/taler-mint-keyup.c b/src/mint-tools/taler-mint-keyup.c
index e2c8d7986..b82554b94 100644
--- a/src/mint-tools/taler-mint-keyup.c
+++ b/src/mint-tools/taler-mint-keyup.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-tools/taler-mint-reservemod.c b/src/mint-tools/taler-mint-reservemod.c
index 11d67ed5d..38d27054a 100644
--- a/src/mint-tools/taler-mint-reservemod.c
+++ b/src/mint-tools/taler-mint-reservemod.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint-tools/taler-mint-sepa.c b/src/mint-tools/taler-mint-sepa.c
index 21f4af9c4..e66db541e 100644
--- a/src/mint-tools/taler-mint-sepa.c
+++ b/src/mint-tools/taler-mint-sepa.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am
index a115d63a0..8e2eae77b 100644
--- a/src/mint/Makefile.am
+++ b/src/mint/Makefile.am
@@ -7,21 +7,33 @@ if USE_COVERAGE
endif
bin_PROGRAMS = \
+ taler-mint-aggregator \
taler-mint-httpd
+taler_mint_aggregator_SOURCES = \
+ taler-mint-aggregator.c
+taler_mint_aggregator_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/wire/libtalerwire.la \
+ $(top_builddir)/src/mintdb/libtalermintdb.la \
+ -ljansson \
+ -lgnunetutil
+
taler_mint_httpd_SOURCES = \
taler-mint-httpd.c taler-mint-httpd.h \
- taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
- taler-mint-httpd_db.c taler-mint-httpd_db.h \
- taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
- taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
- taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
taler-mint-httpd_admin.c taler-mint-httpd_admin.h \
+ taler-mint-httpd_db.c taler-mint-httpd_db.h \
taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \
+ taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
+ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
+ taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
+ taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h \
taler-mint-httpd_reserve.c taler-mint-httpd_reserve.h \
+ taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
taler-mint-httpd_tracking.c taler-mint-httpd_tracking.h \
taler-mint-httpd_wire.c taler-mint-httpd_wire.h \
- taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h
+ taler-mint-httpd_validation.c taler-mint-httpd_validation.h
taler_mint_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \
diff --git a/src/mint/taler-mint-aggregator.c b/src/mint/taler-mint-aggregator.c
new file mode 100644
index 000000000..5e05c8673
--- /dev/null
+++ b/src/mint/taler-mint-aggregator.c
@@ -0,0 +1,914 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ 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/>
+*/
+
+/**
+ * @file taler-mint-aggregator.c
+ * @brief Process that aggregates outgoing transactions and executes them
+ * @author Christian Grothoff
+ *
+ * TODO:
+ * - simplify global_ret: make it a global!
+ * - handle shutdown more nicely (call 'cancel' method on wire transfers)
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <pthread.h>
+#include "taler_mintdb_lib.h"
+#include "taler_mintdb_plugin.h"
+#include "taler_wire_lib.h"
+
+/**
+ * Which currency is used by this mint?
+ */
+static char *mint_currency_string;
+
+/**
+ * Which wireformat should be supported by this aggregator?
+ */
+static char *mint_wireformat;
+
+/**
+ * Base directory of the mint (global)
+ */
+static char *mint_directory;
+
+/**
+ * The mint's configuration (global)
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Our DB plugin.
+ */
+static struct TALER_MINTDB_Plugin *db_plugin;
+
+/**
+ * Our wire plugin.
+ */
+static struct TALER_WIRE_Plugin *wire_plugin;
+
+/**
+ * Task for the main #run() function.
+ */
+static struct GNUNET_SCHEDULER_Task *task;
+
+/**
+ * Limit on the number of transactions we aggregate at once. Note
+ * that the limit must be big enough to ensure that when transactions
+ * of the smallest possible unit are aggregated, they do surpass the
+ * "tiny" threshold beyond which we never trigger a wire transaction!
+ *
+ * TODO: make configurable (via config file or command line option)
+ */
+static unsigned int aggregation_limit = 10000;
+
+
+/**
+ * Load configuration parameters for the mint
+ * server into the corresponding global variables.
+ *
+ * @param mint_directory the mint's directory
+ * @return #GNUNET_OK on success
+ */
+static int
+mint_serve_process_config (const char *mint_directory)
+{
+ char *type;
+
+ cfg = TALER_config_load (mint_directory);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "Failed to load mint configuration\n");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "currency",
+ &mint_currency_string))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "currency");
+ return GNUNET_SYSERR;
+ }
+ if (strlen (mint_currency_string) >= TALER_CURRENCY_LEN)
+ {
+ fprintf (stderr,
+ "Currency `%s' longer than the allowed limit of %u characters.",
+ mint_currency_string,
+ (unsigned int) TALER_CURRENCY_LEN);
+ return GNUNET_SYSERR;
+ }
+ if (NULL != mint_wireformat)
+ GNUNET_CONFIGURATION_set_value_string (cfg,
+ "mint",
+ "wireformat",
+ mint_wireformat);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "wireformat",
+ &type))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "wireformat");
+ return GNUNET_SYSERR;
+ }
+
+ if (NULL ==
+ (db_plugin = TALER_MINTDB_plugin_load (cfg)))
+ {
+ fprintf (stderr,
+ "Failed to initialize DB subsystem\n");
+ GNUNET_free (type);
+ return GNUNET_SYSERR;
+ }
+
+ if (NULL ==
+ (wire_plugin = TALER_WIRE_plugin_load (cfg,
+ type)))
+ {
+ fprintf (stderr,
+ "Failed to load wire plugin for `%s'\n",
+ type);
+ GNUNET_free (type);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (type);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Information about one aggregation process to
+ * be executed.
+ */
+struct AggregationUnit
+{
+ /**
+ * Public key of the merchant.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Total amount to be transferred.
+ */
+ struct TALER_Amount total_amount;
+
+ /**
+ * Hash of @e wire.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Wire transfer identifier we use.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Row ID of the transaction that started it all.
+ */
+ unsigned long long row_id;
+
+ /**
+ * The current time.
+ */
+ struct GNUNET_TIME_Absolute execution_time;
+
+ /**
+ * Wire details of the merchant.
+ */
+ json_t *wire;
+
+ /**
+ * Database session for all of our transactions.
+ */
+ struct TALER_MINTDB_Session *session;
+
+ /**
+ * Wire preparation handle.
+ */
+ struct TALER_WIRE_PrepareHandle *ph;
+
+ /**
+ * Array of #aggregation_limit row_ids from the
+ * aggregation.
+ */
+ unsigned long long *additional_rows;
+
+ /**
+ * Pointer to global return value. Closure for #run().
+ */
+ int *global_ret;
+
+ /**
+ * Offset specifying how many #additional_rows are in use.
+ */
+ unsigned int rows_offset;
+
+ /**
+ * Set to #GNUNET_YES if we have to abort due to failure.
+ */
+ int failed;
+
+};
+
+
+/**
+ * Function called with details about deposits that have been made,
+ * with the goal of executing the corresponding wire transaction.
+ *
+ * @param cls closure with the `struct AggregationUnit`
+ * @param row_id identifies database entry
+ * @param merchant_pub public key of the merchant
+ * @param coin_pub public key of the coin
+ * @param amount_with_fee amount that was deposited including fee
+ * @param deposit_fee amount the mint gets to keep as transaction fees
+ * @param transaction_id unique transaction ID chosen by the merchant
+ * @param h_contract hash of the contract between merchant and customer
+ * @param wire_deadline by which the merchant adviced that he would like the
+ * wire transfer to be executed
+ * @param wire wire details for the merchant
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+deposit_cb (void *cls,
+ unsigned long long row_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *amount_with_fee,
+ const struct TALER_Amount *deposit_fee,
+ uint64_t transaction_id,
+ const struct GNUNET_HashCode *h_contract,
+ struct GNUNET_TIME_Absolute wire_deadline,
+ const json_t *wire)
+{
+ struct AggregationUnit *au = cls;
+
+ au->merchant_pub = *merchant_pub;
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&au->total_amount,
+ amount_with_fee,
+ deposit_fee))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fatally malformed record at %llu\n",
+ row_id);
+ return GNUNET_SYSERR;
+ }
+ au->row_id = row_id;
+ au->wire = (json_t *) wire;
+ au->execution_time = GNUNET_TIME_absolute_get ();
+ TALER_hash_json (au->wire,
+ &au->h_wire);
+ json_incref (au->wire);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &au->wtid,
+ sizeof (au->wtid));
+ if (GNUNET_OK !=
+ db_plugin->insert_aggregation_tracking (db_plugin->cls,
+ au->session,
+ &au->wtid,
+ merchant_pub,
+ &au->h_wire,
+ h_contract,
+ transaction_id,
+ au->execution_time,
+ coin_pub,
+ amount_with_fee,
+ deposit_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ db_plugin->mark_deposit_done (db_plugin->cls,
+ au->session,
+ row_id))
+ {
+ GNUNET_break (0);
+ au->failed = GNUNET_YES;
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+
+/**
+ * Function called with details about another deposit we
+ * can aggregate into an existing aggregation unit.
+ *
+ * @param cls closure with the `struct AggregationUnit`
+ * @param row_id identifies database entry
+ * @param merchant_pub public key of the merchant
+ * @param coin_pub public key of the coin
+ * @param amount_with_fee amount that was deposited including fee
+ * @param deposit_fee amount the mint gets to keep as transaction fees
+ * @param transaction_id unique transaction ID chosen by the merchant
+ * @param h_contract hash of the contract between merchant and customer
+ * @param wire_deadline by which the merchant adviced that he would like the
+ * wire transfer to be executed
+ * @param wire wire details for the merchant
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+aggregate_cb (void *cls,
+ unsigned long long row_id,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *amount_with_fee,
+ const struct TALER_Amount *deposit_fee,
+ uint64_t transaction_id,
+ const struct GNUNET_HashCode *h_contract,
+ struct GNUNET_TIME_Absolute wire_deadline,
+ const json_t *wire)
+{
+ struct AggregationUnit *au = cls;
+ struct TALER_Amount delta;
+
+ GNUNET_break (0 ==
+ memcmp (&au->merchant_pub,
+ merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP)));
+ /* compute contribution of this coin after fees */
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&delta,
+ amount_with_fee,
+ deposit_fee))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fatally malformed record at %llu\n",
+ row_id);
+ return GNUNET_SYSERR;
+ }
+ /* add to total */
+ if (GNUNET_OK !=
+ TALER_amount_add (&au->total_amount,
+ &au->total_amount,
+ &delta))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Overflow or currency incompatibility during aggregation at %llu\n",
+ row_id);
+ /* Skip this one, but keep going! */
+ return GNUNET_OK;
+ }
+ if (au->rows_offset >= aggregation_limit)
+ {
+ /* Bug: we asked for at most #aggregation_limit results! */
+ GNUNET_break (0);
+ /* Skip this one, but keep going. */
+ return GNUNET_OK;
+ }
+ if (NULL == au->additional_rows)
+ au->additional_rows = GNUNET_new_array (aggregation_limit,
+ unsigned long long);
+ /* "append" to our list of rows */
+ au->additional_rows[au->rows_offset++] = row_id;
+ /* insert into aggregation tracking table */
+ if (GNUNET_OK !=
+ db_plugin->insert_aggregation_tracking (db_plugin->cls,
+ au->session,
+ &au->wtid,
+ merchant_pub,
+ &au->h_wire,
+ h_contract,
+ transaction_id,
+ au->execution_time,
+ coin_pub,
+ amount_with_fee,
+ deposit_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ db_plugin->mark_deposit_done (db_plugin->cls,
+ au->session,
+ row_id))
+ {
+ GNUNET_break (0);
+ au->failed = GNUNET_YES;
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function to be called with the prepared transfer data.
+ *
+ * @param cls closure with the `struct AggregationUnit`
+ * @param buf transaction data to persist, NULL on error
+ * @param buf_size number of bytes in @a buf, 0 on error
+ */
+static void
+prepare_cb (void *cls,
+ const char *buf,
+ size_t buf_size);
+
+
+/**
+ * Main work function that queries the DB and aggregates transactions
+ * into larger wire transfers.
+ *
+ * @param cls pointer to an `int` which we will return from main()
+ * @param tc scheduler context
+ */
+static void
+run_aggregation (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ int *global_ret = cls;
+ struct TALER_MINTDB_Session *session;
+ struct AggregationUnit *au;
+ unsigned int i;
+ int ret;
+
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ return;
+ if (NULL == (session = db_plugin->get_session (db_plugin->cls,
+ GNUNET_NO)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to obtain database session!\n");
+ *global_ret = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ db_plugin->start (db_plugin->cls,
+ session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to start database transaction!\n");
+ *global_ret = GNUNET_SYSERR;
+ return;
+ }
+ au = GNUNET_new (struct AggregationUnit);
+ au->session = session;
+ ret = db_plugin->get_ready_deposit (db_plugin->cls,
+ session,
+ &deposit_cb,
+ au);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_free (au);
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ if (0 != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to execute deposit iteration!\n");
+ *global_ret = GNUNET_SYSERR;
+ return;
+ }
+ /* nothing to do, sleep for a minute and try again */
+ task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
+ &run_aggregation,
+ global_ret);
+ return;
+ }
+ /* Now try to find other deposits to aggregate */
+ ret = db_plugin->iterate_matching_deposits (db_plugin->cls,
+ session,
+ &au->h_wire,
+ &au->merchant_pub,
+ &aggregate_cb,
+ au,
+ aggregation_limit);
+ if ( (GNUNET_SYSERR == ret) ||
+ (GNUNET_YES == au->failed) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to execute deposit iteration!\n");
+ GNUNET_free_non_null (au->additional_rows);
+ GNUNET_free (au);
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ *global_ret = GNUNET_SYSERR;
+ return;
+ }
+ /* Round to the unit supported by the wire transfer method */
+ GNUNET_assert (GNUNET_SYSERR !=
+ wire_plugin->amount_round (wire_plugin->cls,
+ &au->total_amount));
+ /* Check if after rounding down, we still have an amount to transfer */
+ if ( (0 == au->total_amount.value) &&
+ (0 == au->total_amount.fraction) )
+ {
+ /* Rollback ongoing transaction, as we will not use the respective
+ WTID and thus need to remove the tracking data */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ /* Start another transaction to mark all* of the selected deposits
+ *as minor! */
+ if (GNUNET_OK !=
+ db_plugin->start (db_plugin->cls,
+ session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to start database transaction!\n");
+ *global_ret = GNUNET_SYSERR;
+ GNUNET_free_non_null (au->additional_rows);
+ GNUNET_free (au);
+ return;
+ }
+ /* Mark transactions by row_id as minor */
+ ret = GNUNET_OK;
+ if (GNUNET_OK !=
+ db_plugin->mark_deposit_tiny (db_plugin->cls,
+ session,
+ au->row_id))
+ ret = GNUNET_SYSERR;
+ else
+ for (i=0;i<au->rows_offset;i++)
+ if (GNUNET_OK !=
+ db_plugin->mark_deposit_tiny (db_plugin->cls,
+ session,
+ au->additional_rows[i]))
+ ret = GNUNET_SYSERR;
+ /* commit */
+ if (GNUNET_OK !=
+ db_plugin->commit (db_plugin->cls,
+ session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to commit database transaction!\n");
+ }
+ GNUNET_free_non_null (au->additional_rows);
+ GNUNET_free (au);
+ /* start again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ return;
+ }
+ au->global_ret = global_ret;
+ au->ph = wire_plugin->prepare_wire_transfer (wire_plugin->cls,
+ au->wire,
+ &au->total_amount,
+ &au->wtid,
+ &prepare_cb,
+ au);
+ /* FIXME: currently we have no clean-up plan on
+ shutdown to call prepare_wire_transfer_cancel!
+ Maybe make 'au' global? */
+ if (NULL == au->ph)
+ {
+ GNUNET_break (0); /* why? how to best recover? */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ GNUNET_free_non_null (au->additional_rows);
+ GNUNET_free (au);
+ /* start again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ return;
+ }
+ /* otherwise we continue with #prepare_cb(), see below */
+}
+
+
+/**
+ * Execute the wire transfers that we have committed to
+ * do.
+ *
+ * @param cls pointer to an `int` which we will return from main()
+ * @param tc scheduler context
+ */
+static void
+run_transfers (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Function to be called with the prepared transfer data.
+ *
+ * @param cls closure with the `struct AggregationUnit`
+ * @param buf transaction data to persist, NULL on error
+ * @param buf_size number of bytes in @a buf, 0 on error
+ */
+static void
+prepare_cb (void *cls,
+ const char *buf,
+ size_t buf_size)
+{
+ struct AggregationUnit *au = cls;
+ int *global_ret = au->global_ret;
+ struct TALER_MINTDB_Session *session = au->session;
+
+ GNUNET_free_non_null (au->additional_rows);
+ GNUNET_free (au);
+ if (NULL == buf)
+ {
+ GNUNET_break (0); /* why? how to best recover? */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ /* start again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ return;
+ }
+
+ /* Commit our intention to execute the wire transfer! */
+ if (GNUNET_OK !=
+ db_plugin->wire_prepare_data_insert (db_plugin->cls,
+ session,
+ mint_wireformat,
+ buf,
+ buf_size))
+ {
+ GNUNET_break (0); /* why? how to best recover? */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ /* start again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ return;
+ }
+
+ /* Now we can finally commit the overall transaction, as we are
+ again consistent if all of this passes. */
+ if (GNUNET_OK !=
+ db_plugin->commit (db_plugin->cls,
+ session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Failed to commit database transaction!\n");
+ /* try again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ return;
+ }
+
+ /* run alternative task: actually do wire transfer! */
+ task = GNUNET_SCHEDULER_add_now (&run_transfers,
+ &global_ret);
+}
+
+
+/**
+ * Data we keep to #run_transfers().
+ */
+struct WirePrepareData
+{
+
+ /**
+ * Database session for all of our transactions.
+ */
+ struct TALER_MINTDB_Session *session;
+
+ /**
+ * Wire execution handle.
+ */
+ struct TALER_WIRE_ExecuteHandle *eh;
+
+ /**
+ * Pointer to global return value. Closure for #run().
+ */
+ int *global_ret;
+
+
+ /**
+ * Row ID of the transfer.
+ */
+ unsigned long long row_id;
+
+};
+
+
+/**
+ * Function called with the result from the execute step.
+ *
+ * @param cls closure with the `struct WirePrepareData`
+ * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * @param emsg NULL on success, otherwise an error message
+ */
+static void
+wire_confirm_cb (void *cls,
+ int success,
+ const char *emsg)
+{
+ struct WirePrepareData *wpd = cls;
+ int *global_ret = wpd->global_ret;
+ struct TALER_MINTDB_Session *session = wpd->session;
+
+ wpd->eh = NULL;
+ if (GNUNET_SYSERR == success)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire transaction failed: %s\n",
+ emsg);
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ *global_ret = GNUNET_SYSERR;
+ GNUNET_free (wpd);
+ return;
+ }
+ if (GNUNET_OK !=
+ db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
+ session,
+ wpd->row_id))
+ {
+ GNUNET_break (0); /* why!? */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ *global_ret = GNUNET_SYSERR;
+ GNUNET_free (wpd);
+ return;
+ }
+ GNUNET_free (wpd);
+ if (GNUNET_OK !=
+ db_plugin->commit (db_plugin->cls,
+ session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Failed to commit database transaction!\n");
+ /* try again */
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ return;
+ }
+ /* continue with #run_transfers(), just to guard
+ against the unlikely case that there are more. */
+ task = GNUNET_SCHEDULER_add_now (&run_transfers,
+ &global_ret);
+
+}
+
+
+/**
+ * Callback with data about a prepared transaction.
+ *
+ * @param cls closure with the `struct WirePrepareData`
+ * @param rowid row identifier used to mark prepared transaction as done
+ * @param buf transaction data that was persisted, NULL on error
+ * @param buf_size number of bytes in @a buf, 0 on error
+ */
+static void
+wire_prepare_cb (void *cls,
+ unsigned long long rowid,
+ const char *buf,
+ size_t buf_size)
+{
+ struct WirePrepareData *wpd = cls;
+ int *global_ret = wpd->global_ret;
+
+ wpd->row_id = rowid;
+ wpd->eh = wire_plugin->execute_wire_transfer (wire_plugin->cls,
+ buf,
+ buf_size,
+ &wire_confirm_cb,
+ wpd);
+ /* FIXME: currently we have no clean-up plan on
+ shutdown to call execute_wire_transfer_cancel!
+ Maybe make 'wpd' global? */
+ if (NULL == wpd->eh)
+ {
+ GNUNET_break (0); /* why? how to best recover? */
+ db_plugin->rollback (db_plugin->cls,
+ wpd->session);
+ *global_ret = GNUNET_SYSERR;
+ GNUNET_free (wpd);
+ return;
+ }
+}
+
+
+/**
+ * Execute the wire transfers that we have committed to
+ * do.
+ *
+ * @param cls pointer to an `int` which we will return from main()
+ * @param tc scheduler context
+ */
+static void
+run_transfers (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ int *global_ret = cls;
+ int ret;
+ struct WirePrepareData *wpd;
+ struct TALER_MINTDB_Session *session;
+
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ return;
+ if (NULL == (session = db_plugin->get_session (db_plugin->cls,
+ GNUNET_NO)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to obtain database session!\n");
+ *global_ret = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ db_plugin->start (db_plugin->cls,
+ session))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to start database transaction!\n");
+ *global_ret = GNUNET_SYSERR;
+ return;
+ }
+ wpd = GNUNET_new (struct WirePrepareData);
+ wpd->session = session;
+ wpd->global_ret = global_ret;
+ ret = db_plugin->wire_prepare_data_get (db_plugin->cls,
+ session,
+ mint_wireformat,
+ &wire_prepare_cb,
+ wpd);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_break (0); /* why? how to best recover? */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ *global_ret = GNUNET_SYSERR;
+ GNUNET_free (wpd);
+ return;
+ }
+ if (GNUNET_NO == ret)
+ {
+ /* no more prepared wire transfers, go back to aggregation! */
+ db_plugin->rollback (db_plugin->cls,
+ session);
+ task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+ global_ret);
+ GNUNET_free (wpd);
+ return;
+ }
+ /* otherwise, continues in #wire_prepare_cb() */
+}
+
+
+/**
+ * The main function of the taler-mint-httpd server ("the mint").
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'d', "mint-dir", "DIR",
+ "mint directory with configuration and keys for operating the mint", 1,
+ &GNUNET_GETOPT_set_filename, &mint_directory},
+ {'f', "format", "WIREFORMAT",
+ "wireformat to use, overrides WIREFORMAT option in [mint] section", 1,
+ &GNUNET_GETOPT_set_filename, &mint_wireformat},
+ TALER_GETOPT_OPTION_HELP ("background process that aggregates and executes wire transfers to merchants"),
+ GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+ int ret = GNUNET_OK;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-aggregator",
+ "INFO",
+ NULL));
+ if (0 >=
+ GNUNET_GETOPT_run ("taler-mint-aggregator",
+ options,
+ argc, argv))
+ return 1;
+ if (NULL == mint_directory)
+ {
+ fprintf (stderr,
+ "Mint directory not specified\n");
+ return 1;
+ }
+ if (GNUNET_OK !=
+ mint_serve_process_config (mint_directory))
+ {
+ return 1;
+ }
+
+ GNUNET_SCHEDULER_run (&run_transfers, &ret);
+
+ TALER_MINTDB_plugin_unload (db_plugin);
+ TALER_WIRE_plugin_unload (wire_plugin);
+ return (GNUNET_SYSERR == ret) ? 1 : 0;
+}
+
+/* end of taler-mint-aggregator.c */
diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c
index 26a440c95..5d6aa0589 100644
--- a/src/mint/taler-mint-httpd.c
+++ b/src/mint/taler-mint-httpd.c
@@ -39,6 +39,7 @@
#include "taler-mint-httpd_test.h"
#endif
#include "taler_mintdb_plugin.h"
+#include "taler-mint-httpd_validation.h"
/**
* Which currency is used by this mint?
@@ -67,13 +68,6 @@ struct GNUNET_CONFIGURATION_Handle *cfg;
struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
/**
- * In which format does this MINT expect wiring instructions?
- * NULL-terminated array of 0-terminated wire format types,
- * suitable for passing to #TALER_json_validate_wireformat().
- */
-const char **TMH_expected_wire_formats;
-
-/**
* Our DB plugin.
*/
struct TALER_MINTDB_Plugin *TMH_plugin;
@@ -384,9 +378,6 @@ mint_serve_process_config (const char *mint_directory)
{
unsigned long long port;
char *TMH_master_public_key_str;
- char *wireformats;
- const char *token;
- unsigned int len;
cfg = TALER_config_load (mint_directory);
if (NULL == cfg)
@@ -414,35 +405,9 @@ mint_serve_process_config (const char *mint_directory)
(unsigned int) TALER_CURRENCY_LEN);
return GNUNET_SYSERR;
}
- /* Find out list of supported wire formats */
if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint",
- "wireformat",
- &wireformats))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "wireformat");
+ TMH_VALIDATION_init (cfg))
return GNUNET_SYSERR;
- }
- /* build NULL-terminated array of TMH_expected_wire_formats */
- TMH_expected_wire_formats = GNUNET_new_array (1,
- const char *);
- len = 1;
- for (token = strtok (wireformats,
- " ");
- NULL != token;
- token = strtok (NULL,
- " "))
- {
- /* Grow by 1, appending NULL-terminator */
- GNUNET_array_append (TMH_expected_wire_formats,
- len,
- NULL);
- TMH_expected_wire_formats[len - 2] = GNUNET_strdup (token);
- }
- GNUNET_free (wireformats);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -453,6 +418,7 @@ mint_serve_process_config (const char *mint_directory)
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"master_public_key");
+ TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -463,6 +429,7 @@ mint_serve_process_config (const char *mint_directory)
fprintf (stderr,
"Invalid master public key given in mint configuration.");
GNUNET_free (TMH_master_public_key_str);
+ TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
GNUNET_free (TMH_master_public_key_str);
@@ -472,6 +439,7 @@ mint_serve_process_config (const char *mint_directory)
{
fprintf (stderr,
"Failed to initialize DB subsystem\n");
+ TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
if (GNUNET_YES ==
@@ -496,6 +464,7 @@ mint_serve_process_config (const char *mint_directory)
"mint",
"port",
"port number required");
+ TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
@@ -505,6 +474,7 @@ mint_serve_process_config (const char *mint_directory)
fprintf (stderr,
"Invalid configuration (value out of range): %llu is not a valid port\n",
port);
+ TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
serve_port = (uint16_t) port;
@@ -772,6 +742,7 @@ main (int argc,
session);
}
TALER_MINTDB_plugin_unload (TMH_plugin);
+ TMH_VALIDATION_done ();
return (GNUNET_SYSERR == ret) ? 1 : 0;
}
diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h
index e83dd66f2..d45325aa6 100644
--- a/src/mint/taler-mint-httpd.h
+++ b/src/mint/taler-mint-httpd.h
@@ -27,6 +27,7 @@
#include <microhttpd.h>
+
/**
* Which currency is used by this mint?
*/
@@ -53,13 +54,6 @@ extern int TMH_test_mode;
extern char *TMH_mint_directory;
/**
- * In which formats does this MINT expect wiring instructions?
- * NULL-terminated array of 0-terminated wire format types,
- * suitable for passing to #TALER_json_validate_wireformat().
- */
-extern const char **TMH_expected_wire_formats;
-
-/**
* Master public key (according to the
* configuration in the mint directory).
*/
diff --git a/src/mint/taler-mint-httpd_admin.c b/src/mint/taler-mint-httpd_admin.c
index 99f256418..e6f186f0b 100644
--- a/src/mint/taler-mint-httpd_admin.c
+++ b/src/mint/taler-mint-httpd_admin.c
@@ -24,6 +24,7 @@
#include "taler-mint-httpd_admin.h"
#include "taler-mint-httpd_parsing.h"
#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd_validation.h"
/**
@@ -144,8 +145,7 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh,
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
if (GNUNET_YES !=
- TALER_json_validate_wireformat (TMH_expected_wire_formats,
- wire))
+ TMH_json_validate_wireformat (wire))
{
GNUNET_break_op (0);
TMH_PARSE_release_data (spec);
diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c
index 2b4ade595..b93ead3af 100644
--- a/src/mint/taler-mint-httpd_db.c
+++ b/src/mint/taler-mint-httpd_db.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -1552,9 +1552,214 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
/**
+ * Closure for #handle_transaction_data.
+ */
+struct WtidTransactionContext
+{
+
+ /**
+ * Total amount of the wire transfer, as calculated by
+ * summing up the individual amounts. To be rounded down
+ * to calculate the real transfer amount at the end.
+ * Only valid if @e is_valid is #GNUNET_YES.
+ */
+ struct TALER_Amount total;
+
+ /**
+ * Value we find in the DB for the @e total; only valid if @e is_valid
+ * is #GNUNET_YES.
+ */
+ struct TALER_Amount db_transaction_value;
+
+ /**
+ * Public key of the merchant, only valid if @e is_valid
+ * is #GNUNET_YES.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Hash of the wire details of the merchant (identical for all
+ * deposits), only valid if @e is_valid is #GNUNET_YES.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * JSON array with details about the individual deposits.
+ */
+ json_t *deposits;
+
+ /**
+ * Initially #GNUNET_NO, if we found no deposits so far. Set to
+ * #GNUNET_YES if we got transaction data, and the database replies
+ * remained consistent with respect to @e merchant_pub and @e h_wire
+ * (as they should). Set to #GNUNET_SYSERR if we encountered an
+ * internal error.
+ */
+ int is_valid;
+
+};
+
+
+/**
+ * Function called with the results of the lookup of the
+ * transaction data for the given wire transfer identifier.
+ *
+ * @param cls our context for transmission
+ * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_contract which contract was this payment about
+ * @param transaction_id merchant's transaction ID for the payment
+ * @param coin_pub which public key was this payment about
+ * @param deposit_value amount contributed by this coin in total
+ * @param deposit_fee deposit fee charged by mint for this coin
+ * @param transaction_value total value of the wire transaction
+ */
+static void
+handle_transaction_data (void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *deposit_value,
+ const struct TALER_Amount *deposit_fee,
+ const struct TALER_Amount *transaction_value)
+{
+ struct WtidTransactionContext *ctx = cls;
+ struct TALER_Amount delta;
+
+ if (GNUNET_SYSERR == ctx->is_valid)
+ return;
+ if (GNUNET_NO == ctx->is_valid)
+ {
+ ctx->merchant_pub = *merchant_pub;
+ ctx->h_wire = *h_wire;
+ ctx->db_transaction_value = *transaction_value;
+ ctx->is_valid = GNUNET_YES;
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&ctx->total,
+ deposit_value,
+ deposit_fee))
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ }
+ else
+ {
+ if ( (0 != memcmp (&ctx->merchant_pub,
+ merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP))) ||
+ (0 != memcmp (&ctx->h_wire,
+ h_wire,
+ sizeof (struct GNUNET_HashCode))) ||
+ (0 != TALER_amount_cmp (transaction_value,
+ &ctx->db_transaction_value)) )
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&delta,
+ deposit_value,
+ deposit_fee))
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_amount_add (&ctx->total,
+ &ctx->total,
+ &delta))
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ }
+ /* NOTE: We usually keep JSON stuff out of the _DB file, and this
+ is also ugly if we ever add signatures over this data. (#4135) */
+ json_array_append (ctx->deposits,
+ json_pack ("{s:o, s:o, s:o, s:I, s:o}",
+ "deposit_value", TALER_json_from_amount (deposit_value),
+ "deposit_fee", TALER_json_from_amount (deposit_fee),
+ "H_contract", TALER_json_from_data (h_contract,
+ sizeof (struct GNUNET_HashCode)),
+ "transaction_id", (json_int_t) transaction_id,
+ "coin_pub", TALER_json_from_data (coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP))));
+}
+
+
+/**
+ * Execute a "/wire/deposits". Returns the transaction information
+ * associated with the given wire transfer identifier.
+ *
+ * @param connection the MHD connection to handle
+ * @param wtid wire transfer identifier to resolve
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
+ const struct TALER_WireTransferIdentifierRawP *wtid)
+{
+ int ret;
+ struct WtidTransactionContext ctx;
+ struct TALER_MINTDB_Session *session;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ ctx.is_valid = GNUNET_NO;
+ ctx.deposits = json_array ();
+ ret = TMH_plugin->lookup_wire_transfer (TMH_plugin->cls,
+ session,
+ wtid,
+ &handle_transaction_data,
+ &ctx);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_break (0);
+ json_decref (ctx.deposits);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ if (GNUNET_SYSERR == ctx.is_valid)
+ {
+ GNUNET_break (0);
+ json_decref (ctx.deposits);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ if (GNUNET_NO == ctx.is_valid)
+ {
+ json_decref (ctx.deposits);
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "wtid");
+ }
+ if (0 != TALER_amount_cmp (&ctx.total,
+ &ctx.db_transaction_value))
+ {
+ /* FIXME: this CAN actually differ, due to rounding
+ down. But we should still check that the values
+ do match after rounding 'total' down! */
+ }
+ return TMH_RESPONSE_reply_wire_deposit_details (connection,
+ &ctx.db_transaction_value,
+ &ctx.merchant_pub,
+ &ctx.h_wire,
+ ctx.deposits);
+}
+
+
+/**
* Closure for #handle_wtid_data.
*/
-struct DepositWtidContext
+struct DepositWtidContext
{
/**
@@ -1563,6 +1768,26 @@ struct DepositWtidContext
struct MHD_Connection *connection;
/**
+ * Hash of the contract we are looking up.
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Hash of the wire transfer details we are looking up.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Public key we are looking up.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Transaction ID we are looking up.
+ */
+ uint64_t transaction_id;
+
+ /**
* MHD result code to return.
*/
int res;
@@ -1572,10 +1797,13 @@ struct DepositWtidContext
/**
* Function called with the results of the lookup of the
* wire transfer identifier information.
- *
+ *
* @param cls our context for transmission
- * @param wtid base32-encoded wire transfer identifier, NULL
+ * @param wtid raw wire transfer identifier, NULL
* if the transaction was not yet done
+ * @param coin_contribution how much did the coin we asked about
+ * contribute to the total transfer value? (deposit value including fee)
+ * @param coin_fee how much did the mint charge for the deposit fee
* @param execution_time when was the transaction done, or
* when we expect it to be done (if @a wtid was NULL);
* #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
@@ -1583,23 +1811,41 @@ struct DepositWtidContext
*/
static void
handle_wtid_data (void *cls,
- const char *wtid,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_Amount *coin_fee,
struct GNUNET_TIME_Absolute execution_time)
{
struct DepositWtidContext *ctx = cls;
+ struct TALER_Amount coin_delta;
if (NULL == wtid)
{
- if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us ==
- execution_time.abs_value_us)
- ctx->res = TMH_RESPONSE_reply_deposit_unknown (ctx->connection);
- else
- ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection);
+ ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection,
+ execution_time);
}
else
{
- ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection);
- }
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&coin_delta,
+ coin_contribution,
+ coin_fee))
+ {
+ GNUNET_break (0);
+ ctx->res = TMH_RESPONSE_reply_internal_db_error (ctx->connection);
+ }
+ else
+ {
+ ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection,
+ &ctx->h_contract,
+ &ctx->h_wire,
+ &ctx->coin_pub,
+ &coin_delta,
+ ctx->transaction_id,
+ wtid,
+ execution_time);
+ }
+ }
}
@@ -1625,21 +1871,46 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection,
{
int ret;
struct DepositWtidContext ctx;
+ struct TALER_MINTDB_Session *session;
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
ctx.connection = connection;
+ ctx.h_contract = *h_contract;
+ ctx.h_wire = *h_wire;
+ ctx.coin_pub = *coin_pub;
+ ctx.transaction_id = transaction_id;
+ ctx.res = GNUNET_SYSERR;
ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls,
+ session,
h_contract,
h_wire,
coin_pub,
merchant_pub,
transaction_id,
&handle_wtid_data,
- connection);
+ &ctx);
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
+ GNUNET_break (GNUNET_SYSERR == ctx.res);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
+ if (GNUNET_NO == ret)
+ {
+ GNUNET_break (GNUNET_SYSERR == ctx.res);
+ return TMH_RESPONSE_reply_deposit_unknown (connection);
+ }
+ if (GNUNET_SYSERR == ctx.res)
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "bug resolving deposit wtid");
+ }
return ctx.res;
}
diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h
index d9adba2d9..0327bef2a 100644
--- a/src/mint/taler-mint-httpd_db.h
+++ b/src/mint/taler-mint-httpd_db.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -193,6 +193,19 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
/**
+ * Execute a "/wire/deposits". Returns the transaction information
+ * associated with the given wire transfer identifier.
+ *
+ * @param connection the MHD connection to handle
+ * @param wtid wire transfer identifier to resolve
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
+ const struct TALER_WireTransferIdentifierRawP *wtid);
+
+
+/**
* Execute a "/deposit/wtid". Returns the transfer information
* associated with the given deposit.
*
@@ -212,5 +225,6 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection,
const struct TALER_MerchantPublicKeyP *merchant_pub,
uint64_t transaction_id);
+
#endif
/* TALER_MINT_HTTPD_DB_H */
diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c
index 0aef4775c..40c5a4db7 100644
--- a/src/mint/taler-mint-httpd_deposit.c
+++ b/src/mint/taler-mint-httpd_deposit.c
@@ -34,6 +34,7 @@
#include "taler-mint-httpd_deposit.h"
#include "taler-mint-httpd_responses.h"
#include "taler-mint-httpd_keystate.h"
+#include "taler-mint-httpd_validation.h"
/**
@@ -162,8 +163,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
return MHD_YES; /* failure */
if (GNUNET_YES !=
- TALER_json_validate_wireformat (TMH_expected_wire_formats,
- wire))
+ TMH_json_validate_wireformat (wire))
{
TMH_PARSE_release_data (spec);
return TMH_RESPONSE_reply_arg_unknown (connection,
diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c
index f3498b469..2ebd0d331 100644
--- a/src/mint/taler-mint-httpd_responses.c
+++ b/src/mint/taler-mint-httpd_responses.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -1056,15 +1056,15 @@ TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
* 404 reply.
*
* @param connection connection to the client
- * @param
* @return MHD result code
*/
int
-TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
- ...)
+TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection)
{
- GNUNET_break (0); // FIXME: not implemented
- return MHD_NO;
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error", "Deposit unknown");
}
@@ -1073,15 +1073,17 @@ TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
* we did not execute the deposit yet. Generate a 202 reply.
*
* @param connection connection to the client
- * @param
+ * @param planned_exec_time planned execution time
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
- ...)
+ struct GNUNET_TIME_Absolute planned_exec_time)
{
- GNUNET_break (0); // FIXME: not implemented
- return MHD_NO;
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_ACCEPTED,
+ "{s:o}",
+ "execution_time", TALER_json_from_abs (planned_exec_time));
}
@@ -1090,15 +1092,86 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
* them. Generates the 200 reply.
*
* @param connection connection to the client
- * @param
+ * @param h_contract hash of the contract
+ * @param h_wire hash of wire account details
+ * @param coin_pub public key of the coin
+ * @param coin_contribution how much did the coin we asked about
+ * contribute to the total transfer value? (deposit value minus fee)
+ * @param transaction_id merchant transaction identifier
+ * @param wtid raw wire transfer identifier
+ * @param exec_time execution time of the wire transfer
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection,
- ...)
+ const struct GNUNET_HashCode *h_contract,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_contribution,
+ uint64_t transaction_id,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct GNUNET_TIME_Absolute exec_time)
+{
+ struct TALER_ConfirmWirePS cw;
+ struct TALER_MintPublicKeyP pub;
+ struct TALER_MintSignatureP sig;
+
+ cw.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE);
+ cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
+ cw.h_wire = *h_wire;
+ cw.h_contract = *h_contract;
+ cw.wtid = *wtid;
+ cw.coin_pub = *coin_pub;
+ cw.transaction_id = GNUNET_htonll (transaction_id);
+ cw.execution_time = GNUNET_TIME_absolute_hton (exec_time);
+ TALER_amount_hton (&cw.coin_contribution,
+ coin_contribution);
+ TMH_KS_sign (&cw.purpose,
+ &pub,
+ &sig);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o, s:o, s:o, s:o, s:o}",
+ "wtid", TALER_json_from_data (wtid,
+ sizeof (*wtid)),
+ "execution_time", TALER_json_from_abs (exec_time),
+ "coin_contribution", TALER_json_from_amount (coin_contribution),
+ "mint_sig", TALER_json_from_data (&sig,
+ sizeof (sig)),
+ "mint_pub", TALER_json_from_data (&pub,
+ sizeof (pub)));
+}
+
+
+/**
+ * A merchant asked for transaction details about a wire transfer.
+ * Provide them. Generates the 200 reply.
+ *
+ * @param connection connection to the client
+ * @param total total amount that was transferred
+ * @param merchant_pub public key of the merchant
+ * @param h_wire destination account
+ * @param deposits details about the combined deposits
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection,
+ const struct TALER_Amount *total,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ json_t *deposits)
{
- GNUNET_break (0); // FIXME: not implemented
- return MHD_NO;
+ /* FIXME: #4135: signing not implemented here */
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o, s:o, s:o}",
+ "total", TALER_json_from_amount (total),
+ "merchant_pub", TALER_json_from_data (merchant_pub,
+ sizeof (struct TALER_MerchantPublicKeyP)),
+ "h_wire", TALER_json_from_data (h_wire,
+ sizeof (struct GNUNET_HashCode)),
+ "deposits", deposits);
}
+
/* end of taler-mint-httpd_responses.c */
diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h
index 5d1523b4b..a0396c8a1 100644
--- a/src/mint/taler-mint-httpd_responses.h
+++ b/src/mint/taler-mint-httpd_responses.h
@@ -253,12 +253,10 @@ TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection
* 404 reply.
*
* @param connection connection to the client
- * @param
* @return MHD result code
*/
int
-TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
- ...);
+TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection);
/**
@@ -266,12 +264,12 @@ TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
* we did not execute the deposit yet. Generate a 202 reply.
*
* @param connection connection to the client
- * @param
+ * @param planned_exec_time planned execution time
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
- ...);
+ struct GNUNET_TIME_Absolute planned_exec_time);
/**
@@ -279,12 +277,43 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
* them. Generates the 200 reply.
*
* @param connection connection to the client
- * @param
+ * @param h_contract hash of the contract
+ * @param h_wire hash of wire account details
+ * @param coin_pub public key of the coin
+ * @param coin_contribution contribution of this coin to the total amount transferred
+ * @param transaction_id merchant transaction identifier
+ * @param wtid raw wire transfer identifier
+ * @param exec_time execution time of the wire transfer
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection,
- ...);
+ const struct GNUNET_HashCode *h_contract,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_contribution,
+ uint64_t transaction_id,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct GNUNET_TIME_Absolute exec_time);
+
+
+/**
+ * A merchant asked for transaction details about a wire transfer.
+ * Provide them. Generates the 200 reply.
+ *
+ * @param connection connection to the client
+ * @param total total amount that was transferred
+ * @param merchant_pub public key of the merchant
+ * @param h_wire destination account
+ * @param deposits details about the combined deposits
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection,
+ const struct TALER_Amount *total,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ json_t *deposits);
/**
diff --git a/src/mint/taler-mint-httpd_tracking.c b/src/mint/taler-mint-httpd_tracking.c
index 59d029429..a6b41cf86 100644
--- a/src/mint/taler-mint-httpd_tracking.c
+++ b/src/mint/taler-mint-httpd_tracking.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 GNUnet e.V.
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -46,8 +46,19 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size)
{
- GNUNET_break (0); // not implemented
- return MHD_NO;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ int res;
+
+ res = TMH_PARSE_mhd_request_arg_data (connection,
+ "wtid",
+ &wtid,
+ sizeof (struct TALER_WireTransferIdentifierRawP));
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* internal error */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* parse error */
+ return TMH_DB_execute_wire_deposits (connection,
+ &wtid);
}
@@ -57,7 +68,7 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh,
*
* @param connection the MHD connection to handle
* @param tps signed request to execute
- * @param merchant_pub public key from the merchant
+ * @param merchant_pub public key from the merchant
* @param merchant_sig signature from the merchant (to be checked)
* @param transaction_id transaction ID (in host byte order)
* @return MHD result code
@@ -110,13 +121,12 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh,
struct TALER_DepositTrackPS tps;
uint64_t transaction_id;
struct TALER_MerchantSignatureP merchant_sig;
- struct TALER_MerchantPublicKeyP merchant_pub;
struct TMH_PARSE_FieldSpecification spec[] = {
TMH_PARSE_member_fixed ("H_wire", &tps.h_wire),
TMH_PARSE_member_fixed ("H_contract", &tps.h_contract),
TMH_PARSE_member_fixed ("coin_pub", &tps.coin_pub),
TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
- TMH_PARSE_member_fixed ("merchant_pub", &merchant_pub),
+ TMH_PARSE_member_fixed ("merchant_pub", &tps.merchant),
TMH_PARSE_member_fixed ("merchant_sig", &merchant_sig),
TMH_PARSE_MEMBER_END
};
@@ -143,7 +153,7 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh,
tps.transaction_id = GNUNET_htonll (transaction_id);
res = check_and_handle_deposit_wtid_request (connection,
&tps,
- &merchant_pub,
+ &tps.merchant,
&merchant_sig,
transaction_id);
TMH_PARSE_release_data (spec);
diff --git a/src/mint/taler-mint-httpd_validation.c b/src/mint/taler-mint-httpd_validation.c
new file mode 100644
index 000000000..461e88759
--- /dev/null
+++ b/src/mint/taler-mint-httpd_validation.c
@@ -0,0 +1,231 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ 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/>
+*/
+
+/**
+ * @file taler-mint-httpd_validation.c
+ * @brief helpers for calling the wire plugins to validate addresses
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler-mint-httpd_validation.h"
+#include "taler_wire_plugin.h"
+
+
+/**
+ * Information we keep for each plugin.
+ */
+struct Plugin
+{
+
+ /**
+ * We keep plugins in a DLL.
+ */
+ struct Plugin *next;
+
+ /**
+ * We keep plugins in a DLL.
+ */
+ struct Plugin *prev;
+
+ /**
+ * Type of the wireformat.
+ */
+ char *type;
+
+ /**
+ * Pointer to the plugin.
+ */
+ struct TALER_WIRE_Plugin *plugin;
+
+};
+
+/**
+ * Head of DLL of wire plugins.
+ */
+static struct Plugin *wire_head;
+
+/**
+ * Tail of DLL of wire plugins.
+ */
+static struct Plugin *wire_tail;
+
+
+/**
+ * Initialize validation subsystem.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+int
+TMH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct Plugin *p;
+ char *wireformats;
+ char *lib_name;
+ const char *token;
+
+ /* Find out list of supported wire formats */
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "wireformat",
+ &wireformats))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "wireformat");
+ return GNUNET_SYSERR;
+ }
+ for (token = strtok (wireformats,
+ " ");
+ NULL != token;
+ token = strtok (NULL,
+ " "))
+ {
+ (void) GNUNET_asprintf (&lib_name,
+ "libtaler_plugin_wire_%s",
+ lib_name);
+ p = GNUNET_new (struct Plugin);
+ p->type = GNUNET_strdup (token);
+ p->plugin = GNUNET_PLUGIN_load (lib_name,
+ (void *) cfg);
+ if (NULL == p->plugin)
+ {
+ GNUNET_free (p);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to load plugin %s\n",
+ lib_name);
+ GNUNET_free (lib_name);
+ TMH_VALIDATION_done ();
+ return GNUNET_SYSERR;
+ }
+ p->plugin->library_name = lib_name;
+ GNUNET_CONTAINER_DLL_insert (wire_head,
+ wire_tail,
+ p);
+ }
+ GNUNET_free (wireformats);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Shutdown validation subsystem.
+ */
+void
+TMH_VALIDATION_done ()
+{
+ struct Plugin *p;
+ char *lib_name;
+
+ while (NULL != (p = wire_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (wire_head,
+ wire_tail,
+ p);
+ lib_name = p->plugin->library_name;
+ GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
+ p->plugin));
+ GNUNET_free (lib_name);
+ GNUNET_free (p->type);
+ GNUNET_free (p);
+ }
+}
+
+
+/**
+ * Check if the given wire format JSON object is correctly formatted as
+ * a wire address.
+ *
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+int
+TMH_json_validate_wireformat (const json_t *wire)
+{
+ const char *stype;
+ json_error_t error;
+ struct Plugin *p;
+
+ if (0 != json_unpack_ex ((json_t *) wire,
+ &error, 0,
+ "{s:s}",
+ "type", &stype))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ for (p=wire_head; NULL != p; p = p->next)
+ if (0 == strcasecmp (p->type,
+ stype))
+ return p->plugin->wire_validate (wire);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Check if we support the given wire method.
+ *
+ * @param type type of wire method to check
+ * @return #GNUNET_YES if the method is supported
+ */
+int
+TMH_VALIDATION_test_method (const char *type)
+{
+ struct Plugin *p;
+
+ for (p=wire_head;NULL != p;p = p->next)
+ if (0 == strcasecmp (type,
+ p->type))
+ return GNUNET_YES;
+ return GNUNET_NO;
+}
+
+
+/**
+ * Obtain supported validation methods as a JSON array,
+ * and as a hash.
+ *
+ * @param[out] h set to the hash of the JSON methods
+ * @return JSON array with the supported validation methods
+ */
+json_t *
+TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h)
+{
+ json_t *methods;
+ struct GNUNET_HashContext *hc;
+ const char *wf;
+ struct Plugin *p;
+
+ methods = json_array ();
+ hc = GNUNET_CRYPTO_hash_context_start ();
+ for (p=wire_head;NULL != p;p = p->next)
+ {
+ wf = p->type;
+ json_array_append_new (methods,
+ json_string (wf));
+ GNUNET_CRYPTO_hash_context_read (hc,
+ wf,
+ strlen (wf) + 1);
+ }
+ GNUNET_CRYPTO_hash_context_finish (hc,
+ h);
+ return methods;
+}
+
+
+/* end of taler-mint-httpd_validation.c */
diff --git a/src/mint/taler-mint-httpd_validation.h b/src/mint/taler-mint-httpd_validation.h
new file mode 100644
index 000000000..f5fb19003
--- /dev/null
+++ b/src/mint/taler-mint-httpd_validation.h
@@ -0,0 +1,76 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ 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/>
+*/
+
+/**
+ * @file taler-mint-httpd_validation.h
+ * @brief helpers for calling the wire plugins to validate addresses
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_VALIDATION_H
+#define TALER_MINT_HTTPD_VALIDATION_H
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+
+
+/**
+ * Initialize validation subsystem.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+int
+TMH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Shutdown validation subsystem.
+ */
+void
+TMH_VALIDATION_done (void);
+
+
+/**
+ * Check if the given wire format JSON object is correctly formatted as
+ * a wire address.
+ *
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+int
+TMH_json_validate_wireformat (const json_t *wire);
+
+/**
+ * Check if we support the given wire method.
+ *
+ * @param type type of wire method to check
+ * @return #GNUNET_YES if the method is supported
+ */
+int
+TMH_VALIDATION_test_method (const char *type);
+
+
+/**
+ * Obtain supported validation methods as a JSON array,
+ * and as a hash.
+ *
+ * @param[out] h set to the hash of the JSON methods
+ * @return JSON array with the supported validation methods
+ */
+json_t *
+TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h);
+
+
+#endif
diff --git a/src/mint/taler-mint-httpd_wire.c b/src/mint/taler-mint-httpd_wire.c
index 1eb3f6bef..020a7e108 100644
--- a/src/mint/taler-mint-httpd_wire.c
+++ b/src/mint/taler-mint-httpd_wire.c
@@ -21,6 +21,7 @@
#include "platform.h"
#include "taler-mint-httpd_keystate.h"
#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd_validation.h"
#include "taler-mint-httpd_wire.h"
#include <jansson.h>
@@ -45,24 +46,10 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh,
struct TALER_MintPublicKeyP pub;
struct TALER_MintSignatureP sig;
json_t *methods;
- struct GNUNET_HashContext *hc;
- unsigned int i;
- const char *wf;
- methods = json_array ();
- hc = GNUNET_CRYPTO_hash_context_start ();
- for (i=0;NULL != (wf = TMH_expected_wire_formats[i]); i++)
- {
- json_array_append_new (methods,
- json_string (wf));
- GNUNET_CRYPTO_hash_context_read (hc,
- wf,
- strlen (wf) + 1);
- }
wsm.purpose.size = htonl (sizeof (wsm));
wsm.purpose.purpose = htonl (TALER_SIGNATURE_MINT_WIRE_TYPES);
- GNUNET_CRYPTO_hash_context_finish (hc,
- &wsm.h_wire_types);
+ methods = TMH_VALIDATION_get_methods (&wsm.h_wire_types);
TMH_KS_sign (&wsm.purpose,
&pub,
&sig);
@@ -97,7 +84,6 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh,
struct MHD_Response *response;
int ret;
char *wire_test_redirect;
- unsigned int i;
response = MHD_create_response_from_buffer (0, NULL,
MHD_RESPMEM_PERSISTENT);
@@ -107,11 +93,7 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh,
return MHD_NO;
}
TMH_RESPONSE_add_global_headers (response);
- for (i=0;NULL != TMH_expected_wire_formats[i];i++)
- if (0 == strcasecmp ("test",
- TMH_expected_wire_formats[i]))
- break;
- if (NULL == TMH_expected_wire_formats[i])
+ if (GNUNET_NO == TMH_VALIDATION_test_method ("test"))
{
/* Return 501: not implemented */
ret = MHD_queue_response (connection,
@@ -165,13 +147,8 @@ TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh,
char *sepa_wire_file;
int fd;
struct stat sbuf;
- unsigned int i;
- for (i=0;NULL != TMH_expected_wire_formats[i];i++)
- if (0 == strcasecmp ("sepa",
- TMH_expected_wire_formats[i]))
- break;
- if (NULL == TMH_expected_wire_formats[i])
+ if (GNUNET_NO == TMH_VALIDATION_test_method ("sepa"))
{
/* Return 501: not implemented */
response = MHD_create_response_from_buffer (0, NULL,
diff --git a/src/mintdb/mintdb_keyio.c b/src/mintdb/mintdb_keyio.c
index 5bfe5bb12..89ac9055a 100644
--- a/src/mintdb/mintdb_keyio.c
+++ b/src/mintdb/mintdb_keyio.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/mintdb_plugin.c b/src/mintdb/mintdb_plugin.c
index 2e8d206fd..4a0f1dc04 100644
--- a/src/mintdb/mintdb_plugin.c
+++ b/src/mintdb/mintdb_plugin.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -83,67 +83,5 @@ TALER_MINTDB_plugin_unload (struct TALER_MINTDB_Plugin *plugin)
}
-/**
- * Libtool search path before we started.
- */
-static char *old_dlsearchpath;
-
-
-/**
- * Setup libtool paths.
- */
-void __attribute__ ((constructor))
-plugin_init ()
-{
- int err;
- const char *opath;
- char *path;
- char *cpath;
-
- err = lt_dlinit ();
- if (err > 0)
- {
- FPRINTF (stderr,
- _("Initialization of plugin mechanism failed: %s!\n"),
- lt_dlerror ());
- return;
- }
- opath = lt_dlgetsearchpath ();
- if (NULL != opath)
- old_dlsearchpath = GNUNET_strdup (opath);
- path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
- if (NULL != path)
- {
- if (NULL != opath)
- {
- GNUNET_asprintf (&cpath, "%s:%s", opath, path);
- lt_dlsetsearchpath (cpath);
- GNUNET_free (path);
- GNUNET_free (cpath);
- }
- else
- {
- lt_dlsetsearchpath (path);
- GNUNET_free (path);
- }
- }
-}
-
-
-/**
- * Shutdown libtool.
- */
-void __attribute__ ((destructor))
-plugin_fini ()
-{
- lt_dlsetsearchpath (old_dlsearchpath);
- if (NULL != old_dlsearchpath)
- {
- GNUNET_free (old_dlsearchpath);
- old_dlsearchpath = NULL;
- }
- lt_dlexit ();
-}
-
/* end of mintdb_plugin.c */
diff --git a/src/mintdb/perf_taler_mintdb.c b/src/mintdb/perf_taler_mintdb.c
index 73708a6c7..fbaa2ce34 100644
--- a/src/mintdb/perf_taler_mintdb.c
+++ b/src/mintdb/perf_taler_mintdb.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/perf_taler_mintdb_init.c b/src/mintdb/perf_taler_mintdb_init.c
index 141ac1ab8..ccfc6a05a 100644
--- a/src/mintdb/perf_taler_mintdb_init.c
+++ b/src/mintdb/perf_taler_mintdb_init.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -67,9 +67,11 @@ PERF_TALER_MINTDB_denomination_init ()
properties.expire_withdraw = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
properties.expire_spend = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
properties.expire_legal = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
- TALER_string_to_amount (CURRENCY ":1.1", &amount);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.1", &amount));
TALER_amount_hton (&properties.value, &amount);
- TALER_string_to_amount (CURRENCY ":0.1", &amount);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.1", &amount));
TALER_amount_hton (&properties.fee_withdraw, &amount);
TALER_amount_hton (&properties.fee_deposit, &amount);
TALER_amount_hton (&properties.fee_refresh, &amount);
@@ -467,8 +469,8 @@ PERF_TALER_MINTDB_refresh_session_free (struct TALER_MINTDB_RefreshSession *refr
{
if (NULL == refresh_session)
return GNUNET_OK;
- return GNUNET_OK;
GNUNET_free (refresh_session);
+ return GNUNET_OK;
}
@@ -502,10 +504,12 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session,
&to_sign.purpose,
&coin_sig.eddsa_signature);
}
- GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1.1",
- &amount));
- GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.1",
- &amount_with_fee));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":1.1",
+ &amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":0.1",
+ &amount_with_fee));
melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt);
melt->coin.coin_pub = coin->public_info.coin_pub;
melt->coin.denom_sig.rsa_signature =
diff --git a/src/mintdb/perf_taler_mintdb_init.h b/src/mintdb/perf_taler_mintdb_init.h
index 063259f51..f94beef10 100644
--- a/src/mintdb/perf_taler_mintdb_init.h
+++ b/src/mintdb/perf_taler_mintdb_init.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/perf_taler_mintdb_interpreter.c b/src/mintdb/perf_taler_mintdb_interpreter.c
index 9084513fb..293d5f35f 100644
--- a/src/mintdb/perf_taler_mintdb_interpreter.c
+++ b/src/mintdb/perf_taler_mintdb_interpreter.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/perf_taler_mintdb_interpreter.h b/src/mintdb/perf_taler_mintdb_interpreter.h
index 722d1a07d..3510e3dd4 100644
--- a/src/mintdb/perf_taler_mintdb_interpreter.h
+++ b/src/mintdb/perf_taler_mintdb_interpreter.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/perf_taler_mintdb_values.h b/src/mintdb/perf_taler_mintdb_values.h
index 25202edd1..35f87f5b0 100644
--- a/src/mintdb/perf_taler_mintdb_values.h
+++ b/src/mintdb/perf_taler_mintdb_values.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/plugin_mintdb_common.c b/src/mintdb/plugin_mintdb_common.c
index bbe104f6c..1f2fdc58b 100644
--- a/src/mintdb/plugin_mintdb_common.c
+++ b/src/mintdb/plugin_mintdb_common.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c
index 0f32cfb8e..2ab3e81ac 100644
--- a/src/mintdb/plugin_mintdb_postgres.c
+++ b/src/mintdb/plugin_mintdb_postgres.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -425,7 +425,7 @@ postgres_create_tables (void *cls,
/* This table contains the wire transfers the mint is supposed to
execute to transmit funds to the merchants (and manage refunds). */
SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
- "(serial_id BIGSERIAL"
+ "(serial_id BIGSERIAL PRIMARY KEY"
",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
",denom_pub BYTEA NOT NULL REFERENCES denominations (pub)"
",denom_sig BYTEA NOT NULL"
@@ -444,10 +444,49 @@ postgres_create_tables (void *cls,
",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
",coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)"
",wire TEXT NOT NULL"
+ ",tiny BOOLEAN NOT NULL DEFAULT false"
+ ",done BOOLEAN NOT NULL DEFAULT false"
")");
- /* Index for get_deposit statement on coin_pub, transactiojn_id and merchant_pub */
+ /* Index for get_deposit statement on coin_pub, transaction_id and merchant_pub */
SQLEXEC_INDEX("CREATE INDEX deposits_coin_pub_index "
"ON deposits(coin_pub, transaction_id, merchant_pub)");
+ /* Table for the tracking API, mapping from wire transfer identifiers
+ to transactions and back */
+ SQLEXEC("CREATE TABLE IF NOT EXISTS aggregation_tracking "
+ "(h_contract BYTEA CHECK (LENGTH(h_contract)=64)"
+ ",h_wire BYTEA CHECK (LENGTH(h_wire)=64)"
+ ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)"
+ ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
+ ",transaction_id INT8 NOT NULL"
+ ",wtid_raw BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=" TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")"
+ ",execution_time INT8 NOT NULL"
+ ",coin_amount_val INT8 NOT NULL"
+ ",coin_amount_frac INT4 NOT NULL"
+ ",coin_amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",coin_fee_val INT8 NOT NULL"
+ ",coin_fee_frac INT4 NOT NULL"
+ ",coin_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+ /* Index for lookup_transactions statement on wtid */
+ SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
+ "ON aggregation_tracking(wtid_raw)");
+ /* Index for lookup_deposit_wtid statement */
+ SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_deposit_index "
+ "ON aggregation_tracking(coin_pub,h_contract,h_wire,transaction_id,merchant_pub)");
+
+ /* This table contains the pre-commit data for
+ wire transfers the mint is about to execute. */
+ SQLEXEC("CREATE TABLE IF NOT EXISTS prewire "
+ "(serial_id BIGSERIAL PRIMARY KEY"
+ ",type TEXT NOT NULL"
+ ",finished BOOLEAN NOT NULL DEFAULT false"
+ ",buf BYTEA NOT NULL"
+ ")");
+ /* Index for prepare_data_iterate statement */
+ SQLEXEC_INDEX("CREATE INDEX prepare_iteration_index "
+ "ON prewire(type,finished)");
+
+
#undef SQLEXEC
#undef SQLEXEC_INDEX
@@ -558,6 +597,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4, $5);",
5, NULL);
+
/* Used in #postgres_reserves_update() when the reserve is updated */
PREPARE ("reserve_update",
"UPDATE reserves"
@@ -567,6 +607,7 @@ postgres_prepare (PGconn *db_conn)
",current_balance_frac=$3 "
"WHERE current_balance_curr=$4 AND reserve_pub=$5",
5, NULL);
+
/* Used in #postgres_reserves_in_insert() to store transaction details */
PREPARE ("reserves_in_add_transaction",
"INSERT INTO reserves_in "
@@ -579,6 +620,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4, $5, $6);",
6, NULL);
+
/* Used in #postgres_get_reserve_history() to obtain inbound transactions
for a reserve */
PREPARE ("reserves_in_get_transactions",
@@ -591,6 +633,7 @@ postgres_prepare (PGconn *db_conn)
" FROM reserves_in"
" WHERE reserve_pub=$1",
1, NULL);
+
/* Used in #postgres_insert_withdraw_info() to store
the signature of a blinded coin with the blinded coin's
details before returning it during /reserve/withdraw. We store
@@ -615,6 +658,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);",
12, NULL);
+
/* Used in #postgres_get_withdraw_info() to
locate the response for a /reserve/withdraw request
using the hash of the blinded message. Used to
@@ -635,6 +679,7 @@ postgres_prepare (PGconn *db_conn)
" FROM reserves_out"
" WHERE h_blind_ev=$1",
1, NULL);
+
/* Used during #postgres_get_reserve_history() to
obtain all of the /reserve/withdraw operations that
have been performed on a given reserve. (i.e. to
@@ -655,6 +700,7 @@ postgres_prepare (PGconn *db_conn)
" FROM reserves_out"
" WHERE reserve_pub=$1;",
1, NULL);
+
/* Used in #postgres_get_refresh_session() to fetch
high-level information about a refresh session */
PREPARE ("get_refresh_session",
@@ -665,6 +711,7 @@ postgres_prepare (PGconn *db_conn)
" FROM refresh_sessions "
" WHERE session_hash=$1 ",
1, NULL);
+
/* Used in #postgres_create_refresh_session() to store
high-level information about a refresh session */
PREPARE ("insert_refresh_session",
@@ -676,6 +723,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4);",
4, NULL);
+
/* Used in #postgres_get_known_coin() to fetch
the denomination public key and signature for
a coin known to the mint. */
@@ -683,9 +731,10 @@ postgres_prepare (PGconn *db_conn)
"SELECT"
" denom_pub"
",denom_sig"
- " FROM known_coins "
+ " FROM known_coins"
" WHERE coin_pub=$1",
1, NULL);
+
/* Used in #postgres_insert_known_coin() to store
the denomination public key and signature for
a coin known to the mint. */
@@ -697,6 +746,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1,$2,$3);",
3, NULL);
+
/* Store information about the desired denominations for a
refresh operation, used in #postgres_insert_refresh_order() */
PREPARE ("insert_refresh_order",
@@ -707,6 +757,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3);",
3, NULL);
+
/* Obtain information about the desired denominations for a
refresh operation, used in #postgres_get_refresh_order() */
PREPARE ("get_refresh_order",
@@ -732,6 +783,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
10, NULL);
+
/* Used in #postgres_get_refresh_melt to obtain information
about melted coins */
PREPARE ("get_refresh_melt",
@@ -747,6 +799,7 @@ postgres_prepare (PGconn *db_conn)
" FROM refresh_melts"
" WHERE session_hash=$1 AND oldcoin_index=$2",
2, NULL);
+
/* Query the 'refresh_melts' by coin public key */
PREPARE ("get_refresh_melt_by_coin",
"SELECT"
@@ -762,6 +815,7 @@ postgres_prepare (PGconn *db_conn)
" FROM refresh_melts"
" WHERE coin_pub=$1",
1, NULL);
+
/* Used in #postgres_insert_refresh_commit_links() to
store commitments */
PREPARE ("insert_refresh_commit_link",
@@ -774,6 +828,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4, $5);",
5, NULL);
+
/* Used in #postgres_get_refresh_commit_links() to
retrieve original commitments during /refresh/reveal */
PREPARE ("get_refresh_commit_link",
@@ -783,6 +838,7 @@ postgres_prepare (PGconn *db_conn)
" FROM refresh_commit_link"
" WHERE session_hash=$1 AND cnc_index=$2 AND oldcoin_index=$3",
3, NULL);
+
/* Used in #postgres_insert_refresh_commit_coins() to
store coin commitments. */
PREPARE ("insert_refresh_commit_coin",
@@ -795,6 +851,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3, $4, $5);",
5, NULL);
+
/* Used in #postgres_get_refresh_commit_coins() to
retrieve the original coin envelopes, to either be
verified or signed. */
@@ -805,6 +862,7 @@ postgres_prepare (PGconn *db_conn)
" FROM refresh_commit_coin"
" WHERE session_hash=$1 AND cnc_index=$2 AND newcoin_index=$3",
3, NULL);
+
/* Store information about a /deposit the mint is to execute.
Used in #postgres_insert_deposit(). */
PREPARE ("insert_deposit",
@@ -831,6 +889,7 @@ postgres_prepare (PGconn *db_conn)
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
" $11, $12, $13, $14, $15, $16, $17, $18);",
18, NULL);
+
/* Fetch an existing deposit request, used to ensure idempotency
during /deposit processing. Used in #postgres_have_deposit(). */
PREPARE ("get_deposit",
@@ -851,8 +910,29 @@ postgres_prepare (PGconn *db_conn)
" )",
3, NULL);
- /* Used in #postgres_iterate_deposits() */
- PREPARE ("deposits_iterate",
+ /* Fetch an existing deposit request.
+ Used in #postgres_wire_lookup_deposit_wtid(). */
+ PREPARE ("get_deposit_for_wtid",
+ "SELECT"
+ " amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",deposit_fee_val"
+ ",deposit_fee_frac"
+ ",deposit_fee_curr"
+ ",wire_deadline"
+ " FROM deposits"
+ " WHERE ("
+ " (coin_pub=$1) AND"
+ " (transaction_id=$2) AND"
+ " (merchant_pub=$3) AND"
+ " (h_contract=$4) AND"
+ " (h_wire=$5)"
+ " )",
+ 5, NULL);
+
+ /* Used in #postgres_get_ready_deposit() */
+ PREPARE ("deposits_get_ready",
"SELECT"
" serial_id"
",amount_with_fee_val"
@@ -865,11 +945,53 @@ postgres_prepare (PGconn *db_conn)
",transaction_id"
",h_contract"
",wire"
+ ",merchant_pub"
+ ",coin_pub"
" FROM deposits"
- " WHERE serial_id>=$1"
- " ORDER BY serial_id ASC"
- " LIMIT $2;",
- 2, NULL);
+ " WHERE"
+ " tiny=false AND"
+ " done=false"
+ " ORDER BY wire_deadline ASC"
+ " LIMIT 1;",
+ 0, NULL);
+
+ /* Used in #postgres_iterate_matching_deposits() */
+ PREPARE ("deposits_iterate_matching",
+ "SELECT"
+ " serial_id"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",amount_with_fee_curr"
+ ",deposit_fee_val"
+ ",deposit_fee_frac"
+ ",deposit_fee_curr"
+ ",wire_deadline"
+ ",transaction_id"
+ ",h_contract"
+ ",coin_pub"
+ " FROM deposits"
+ " WHERE"
+ " merchant_pub=$1 AND"
+ " h_wire=$2 AND"
+ " done=false"
+ " ORDER BY wire_deadline ASC"
+ " LIMIT $3",
+ 3, NULL);
+
+ /* Used in #postgres_mark_deposit_tiny() */
+ PREPARE ("mark_deposit_tiny",
+ "UPDATE deposits"
+ " SET tiny=true"
+ " WHERE serial_id=$1",
+ 1, NULL);
+
+ /* Used in #postgres_mark_deposit_done() */
+ PREPARE ("mark_deposit_done",
+ "UPDATE deposits"
+ " SET done=true"
+ " WHERE serial_id=$1",
+ 1, NULL);
+
/* Used in #postgres_get_coin_transactions() to obtain information
about how a coin has been spend with /deposit requests. */
PREPARE ("get_deposit_with_coin_pub",
@@ -904,6 +1026,7 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3)",
3, NULL);
+
/* Used in #postgres_get_link_data_list(). We use the session_hash
to obtain the "noreveal_index" for that session, and then select
the encrypted link vectors (link_vector_enc) and the
@@ -929,6 +1052,7 @@ postgres_prepare (PGconn *db_conn)
" AND ro.newcoin_index=rc.newcoin_index"
" AND rcc.cnc_index=rs.noreveal_index",
1, NULL);
+
/* Used in #postgres_get_transfer(). Given the public key of a
melted coin, we obtain the corresponding encrypted link secret
and the transfer public key. This is done by first finding
@@ -947,6 +1071,97 @@ postgres_prepare (PGconn *db_conn)
" AND rm.oldcoin_index = rcl.oldcoin_index"
" AND rcl.cnc_index=rs.noreveal_index",
1, NULL);
+
+ /* Used in #postgres_lookup_wire_transfer */
+ PREPARE ("lookup_transactions",
+ "SELECT"
+ " h_contract"
+ ",h_wire"
+ ",coin_pub"
+ ",merchant_pub"
+ ",transaction_id"
+ ",execution_time"
+ ",coin_amount_val"
+ ",coin_amount_frac"
+ ",coin_amount_curr"
+ ",coin_fee_val"
+ ",coin_fee_frac"
+ ",coin_fee_curr"
+ " FROM aggregation_tracking"
+ " WHERE wtid_raw=$1",
+ 1, NULL);
+
+ /* Used in #postgres_wire_lookup_deposit_wtid */
+ PREPARE ("lookup_deposit_wtid",
+ "SELECT"
+ " wtid_raw"
+ ",execution_time"
+ ",coin_amount_val"
+ ",coin_amount_frac"
+ ",coin_amount_curr"
+ ",coin_fee_val"
+ ",coin_fee_frac"
+ ",coin_fee_curr"
+ " FROM aggregation_tracking"
+ " WHERE"
+ " coin_pub=$1 AND"
+ " h_contract=$2 AND"
+ " h_wire=$3 AND"
+ " transaction_id=$4 AND"
+ " merchant_pub=$5",
+ 5, NULL);
+
+ /* Used in #postgres_insert_aggregation_tracking */
+ PREPARE ("insert_aggregation_tracking",
+ "INSERT INTO aggregation_tracking "
+ "(h_contract"
+ ",h_wire"
+ ",coin_pub"
+ ",merchant_pub"
+ ",transaction_id"
+ ",wtid_raw"
+ ",execution_time"
+ ",coin_amount_val"
+ ",coin_amount_frac"
+ ",coin_amount_curr"
+ ",coin_fee_val"
+ ",coin_fee_frac"
+ ",coin_fee_curr"
+ ") VALUES "
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
+ 13, NULL);
+
+
+ /* Used in #postgres_wire_prepare_data_insert() to store
+ wire transfer information before actually committing it with the bank */
+ PREPARE ("wire_prepare_data_insert",
+ "INSERT INTO prewire "
+ "(type"
+ ",buf"
+ ") VALUES "
+ "($1, $2)",
+ 2, NULL);
+
+ /* Used in #postgres_wire_prepare_data_mark_finished() */
+ PREPARE ("wire_prepare_data_mark_done",
+ "UPDATE prewire"
+ " SET finished=true"
+ " WHERE serial_id=$1",
+ 1, NULL);
+
+ /* Used in #postgres_wire_prepare_data_get() */
+ PREPARE ("wire_prepare_data_get",
+ "SELECT"
+ " serial_id"
+ ",buf"
+ " FROM prewire"
+ " WHERE"
+ " type=$1 AND"
+ " finished=false"
+ " ORDER BY serial_id ASC"
+ " LIMIT 1",
+ 1, NULL);
+
return GNUNET_OK;
#undef PREPARE
}
@@ -1925,82 +2140,239 @@ postgres_have_deposit (void *cls,
/**
- * Obtain information about deposits. Iterates over all deposits
- * above a certain ID. Use a @a min_id of 0 to start at the beginning.
- * This operation is executed in its own transaction in transaction
- * mode "REPEATABLE READ", i.e. we should only see valid deposits.
+ * Mark a deposit as tiny, thereby declaring that it cannot be
+ * executed by itself and should no longer be returned by
+ * @e iterate_ready_deposits()
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
- * @param min_id deposit to start at
- * @param limit maximum number of transactions to fetch
- * @param deposit_cb function to call for each deposit
+ * @param deposit_rowid identifies the deposit row to modify
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+postgres_mark_deposit_tiny (void *cls,
+ struct TALER_MINTDB_Session *session,
+ unsigned long long rowid)
+{
+ uint64_t serial_id = rowid;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&serial_id),
+ TALER_PQ_query_param_end
+ };
+ PGresult *result;
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "mark_deposit_tiny",
+ params);
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Mark a deposit as done, thereby declaring that it cannot be
+ * executed at all anymore, and should no longer be returned by
+ * @e iterate_ready_deposits() or @e iterate_matching_deposits().
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param deposit_rowid identifies the deposit row to modify
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+postgres_mark_deposit_done (void *cls,
+ struct TALER_MINTDB_Session *session,
+ unsigned long long rowid)
+{
+ uint64_t serial_id = rowid;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&serial_id),
+ TALER_PQ_query_param_end
+ };
+ PGresult *result;
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "mark_deposit_done",
+ params);
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Obtain information about deposits that are ready to be executed.
+ * Such deposits must not be marked as "tiny" or "done", and the
+ * execution time must be in the past.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param deposit_cb function to call for ONE such deposit
* @param deposit_cb_cls closure for @a deposit_cb
* @return number of rows processed, 0 if none exist,
* #GNUNET_SYSERR on error
*/
static int
-postgres_iterate_deposits (void *cls,
- struct TALER_MINTDB_Session *session,
- uint64_t min_id,
- uint32_t limit,
- TALER_MINTDB_DepositIterator deposit_cb,
- void *deposit_cb_cls)
+postgres_get_ready_deposit (void *cls,
+ struct TALER_MINTDB_Session *session,
+ TALER_MINTDB_DepositIterator deposit_cb,
+ void *deposit_cb_cls)
{
struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint64 (&min_id),
- TALER_PQ_query_param_uint32 (&limit),
TALER_PQ_query_param_end
};
PGresult *result;
- unsigned int i;
unsigned int n;
+ int ret;
- if (GNUNET_OK !=
- postgres_start (cls, session))
- return GNUNET_SYSERR;
- result = PQexec (session->conn,
- "SET TRANSACTION REPEATABLE READ");
- if (PGRES_COMMAND_OK !=
+ result = TALER_PQ_exec_prepared (session->conn,
+ "deposits_get_ready",
+ params);
+ if (PGRES_TUPLES_OK !=
PQresultStatus (result))
{
- TALER_LOG_ERROR ("Failed to set transaction to REPEATABL EREAD: %s\n",
- PQresultErrorMessage (result));
- GNUNET_break (0);
+ BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
+ if (0 == (n = PQntuples (result)))
+ {
+ PQclear (result);
+ return 0;
+ }
+ GNUNET_break (1 == n);
+ {
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount deposit_fee;
+ struct GNUNET_TIME_Absolute wire_deadline;
+ struct GNUNET_HashCode h_contract;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ uint64_t transaction_id;
+ uint64_t serial_id;
+ json_t *wire;
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("serial_id",
+ &serial_id),
+ TALER_PQ_result_spec_uint64 ("transaction_id",
+ &transaction_id),
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &amount_with_fee),
+ TALER_PQ_result_spec_amount ("deposit_fee",
+ &deposit_fee),
+ TALER_PQ_result_spec_absolute_time ("wire_deadline",
+ &wire_deadline),
+ TALER_PQ_result_spec_auto_from_type ("h_contract",
+ &h_contract),
+ TALER_PQ_result_spec_auto_from_type ("merchant_pub",
+ &merchant_pub),
+ TALER_PQ_result_spec_auto_from_type ("coin_pub",
+ &coin_pub),
+ TALER_PQ_result_spec_json ("wire",
+ &wire),
+ TALER_PQ_result_spec_end
+ };
+ if (GNUNET_OK !=
+ TALER_PQ_extract_result (result, rs, 0))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ ret = deposit_cb (deposit_cb_cls,
+ serial_id,
+ &merchant_pub,
+ &coin_pub,
+ &amount_with_fee,
+ &deposit_fee,
+ transaction_id,
+ &h_contract,
+ wire_deadline,
+ wire);
+ TALER_PQ_cleanup_result (rs);
+ PQclear (result);
+ }
+ return (GNUNET_OK == ret) ? 1 : 0;
+}
+
+
+/**
+ * Obtain information about other pending deposits for the same
+ * destination. Those deposits must not already be "done".
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param h_wire destination of the wire transfer
+ * @param merchant_pub public key of the merchant
+ * @param deposit_cb function to call for each deposit
+ * @param deposit_cb_cls closure for @a deposit_cb
+ * @param limit maximum number of matching deposits to return
+ * @return number of rows processed, 0 if none exist,
+ * #GNUNET_SYSERR on error
+ */
+static int
+postgres_iterate_matching_deposits (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *h_wire,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ TALER_MINTDB_DepositIterator deposit_cb,
+ void *deposit_cb_cls,
+ uint32_t limit)
+{
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_auto_from_type (merchant_pub),
+ TALER_PQ_query_param_auto_from_type (h_wire),
+ TALER_PQ_query_param_uint32 (&limit),
+ TALER_PQ_query_param_end
+ };
+ PGresult *result;
+ unsigned int i;
+ unsigned int n;
result = TALER_PQ_exec_prepared (session->conn,
- "deposits_iterate",
+ "deposits_iterate_matching",
params);
if (PGRES_TUPLES_OK !=
PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
- postgres_rollback (cls, session);
return GNUNET_SYSERR;
}
if (0 == (n = PQntuples (result)))
{
PQclear (result);
- postgres_rollback (cls, session);
return 0;
}
+ if (n > limit)
+ n = limit;
for (i=0;i<n;i++)
{
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_HashCode h_contract;
- json_t *wire;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
uint64_t transaction_id;
- uint64_t id;
+ uint64_t serial_id;
int ret;
struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("id",
- &id),
+ TALER_PQ_result_spec_uint64 ("serial_id",
+ &serial_id),
TALER_PQ_result_spec_uint64 ("transaction_id",
&transaction_id),
TALER_PQ_result_spec_amount ("amount_with_fee",
@@ -2011,8 +2383,10 @@ postgres_iterate_deposits (void *cls,
&wire_deadline),
TALER_PQ_result_spec_auto_from_type ("h_contract",
&h_contract),
- TALER_PQ_result_spec_json ("wire",
- &wire),
+ TALER_PQ_result_spec_auto_from_type ("merchant_pub",
+ &merchant_pub),
+ TALER_PQ_result_spec_auto_from_type ("coin_pub",
+ &coin_pub),
TALER_PQ_result_spec_end
};
if (GNUNET_OK !=
@@ -2020,23 +2394,23 @@ postgres_iterate_deposits (void *cls,
{
GNUNET_break (0);
PQclear (result);
- postgres_rollback (cls, session);
return GNUNET_SYSERR;
}
ret = deposit_cb (deposit_cb_cls,
- id,
+ serial_id,
+ &merchant_pub,
+ &coin_pub,
&amount_with_fee,
&deposit_fee,
transaction_id,
&h_contract,
wire_deadline,
- wire);
+ NULL);
TALER_PQ_cleanup_result (rs);
PQclear (result);
if (GNUNET_OK != ret)
break;
}
- postgres_rollback (cls, session);
return i;
}
@@ -3365,11 +3739,101 @@ postgres_get_coin_transactions (void *cls,
/**
+ * Lookup the list of Taler transactions that was aggregated
+ * into a wire transfer by the respective @a wtid.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param wtid the raw wire transfer identifier we used
+ * @param cb function to call on each transaction found
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
+ * #GNUNET_NO if we found no results
+ */
+static int
+postgres_lookup_wire_transfer (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_MINTDB_WireTransferDataCallback cb,
+ void *cb_cls)
+{
+ PGresult *result;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_auto_from_type (wtid),
+ TALER_PQ_query_param_end
+ };
+ int nrows;
+ int i;
+
+ /* check if the melt record exists and get it */
+ result = TALER_PQ_exec_prepared (session->conn,
+ "lookup_transactions",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ nrows = PQntuples (result);
+ if (0 == nrows)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "lookup_wire_transfer() returned 0 matching rows\n");
+ PQclear (result);
+ return GNUNET_NO;
+ }
+ for (i=0;i<nrows;i++)
+ {
+ struct GNUNET_HashCode h_contract;
+ struct GNUNET_HashCode h_wire;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ uint64_t transaction_id;
+ struct GNUNET_TIME_Absolute exec_time;
+ struct TALER_Amount coin_amount;
+ struct TALER_Amount coin_fee;
+ struct TALER_Amount transfer_amount;
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_auto_from_type ("h_contract", &h_contract),
+ TALER_PQ_result_spec_auto_from_type ("h_wire", &h_wire),
+ TALER_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub),
+ TALER_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub),
+ TALER_PQ_result_spec_uint64 ("transaction_id", &transaction_id),
+ TALER_PQ_result_spec_absolute_time ("execution_time", &exec_time),
+ TALER_PQ_result_spec_amount ("coin_amount", &coin_amount),
+ TALER_PQ_result_spec_amount ("coin_fee", &coin_fee),
+ TALER_PQ_result_spec_amount ("transfer_total", &transfer_amount),
+ TALER_PQ_result_spec_end
+ };
+ if (GNUNET_OK != TALER_PQ_extract_result (result, rs, i))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ cb (cb_cls,
+ &merchant_pub,
+ &h_wire,
+ &h_contract,
+ transaction_id,
+ &coin_pub,
+ &coin_amount,
+ &coin_fee,
+ &transfer_amount);
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
* Try to find the wire transfer details for a deposit operation.
* If we did not execute the deposit yet, return when it is supposed
* to be executed.
- *
+ *
* @param cls closure
+ * @param session database connection
* @param h_contract hash of the contract
* @param h_wire hash of merchant wire details
* @param coin_pub public key of deposited coin
@@ -3377,10 +3841,12 @@ postgres_get_coin_transactions (void *cls,
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
+ * #GNUNET_NO if nothing was found
*/
static int
postgres_wire_lookup_deposit_wtid (void *cls,
+ struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@@ -3389,8 +3855,341 @@ postgres_wire_lookup_deposit_wtid (void *cls,
TALER_MINTDB_DepositWtidCallback cb,
void *cb_cls)
{
- GNUNET_break (0); // not implemented
- return GNUNET_SYSERR;
+ PGresult *result;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_auto_from_type (coin_pub),
+ TALER_PQ_query_param_auto_from_type (h_contract),
+ TALER_PQ_query_param_auto_from_type (h_wire),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_auto_from_type (merchant_pub),
+ TALER_PQ_query_param_end
+ };
+ int nrows;
+
+ /* check if the melt record exists and get it */
+ result = TALER_PQ_exec_prepared (session->conn,
+ "lookup_deposit_wtid",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ nrows = PQntuples (result);
+ if (0 == nrows)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "lookup_deposit_wtid returned 0 matching rows\n");
+ PQclear (result);
+
+ /* Check if transaction exists in deposits, so that we just
+ do not have a WTID yet, if so, do call the CB with a NULL wtid
+ and return GNUNET_YES! */
+ {
+ struct TALER_PQ_QueryParam params2[] = {
+ TALER_PQ_query_param_auto_from_type (coin_pub),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_auto_from_type (merchant_pub),
+ TALER_PQ_query_param_auto_from_type (h_contract),
+ TALER_PQ_query_param_auto_from_type (h_wire),
+ TALER_PQ_query_param_end
+ };
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "get_deposit_for_wtid",
+ params2);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ }
+ nrows = PQntuples (result);
+ if (0 == nrows)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "get_deposit_for_wtid returned 0 matching rows\n");
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ /* Ok, we're aware of the transaction, but it has not yet been
+ executed */
+ {
+ struct GNUNET_TIME_Absolute exec_time;
+ struct TALER_Amount coin_amount;
+ struct TALER_Amount coin_fee;
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount_with_fee", &coin_amount),
+ TALER_PQ_result_spec_amount ("deposit_fee", &coin_fee),
+ TALER_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
+ TALER_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ cb (cb_cls,
+ NULL,
+ &coin_amount,
+ &coin_fee,
+ exec_time);
+ PQclear (result);
+ return GNUNET_YES;
+ }
+ }
+ if (1 != nrows)
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct GNUNET_TIME_Absolute exec_time;
+ struct TALER_Amount coin_amount;
+ struct TALER_Amount coin_fee;
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
+ TALER_PQ_result_spec_absolute_time ("execution_time", &exec_time),
+ TALER_PQ_result_spec_amount ("coin_amount", &coin_amount),
+ TALER_PQ_result_spec_amount ("coin_fee", &coin_fee),
+ TALER_PQ_result_spec_end
+ };
+ if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ cb (cb_cls,
+ &wtid,
+ &coin_amount,
+ &coin_fee,
+ exec_time);
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called to insert aggregation information into the DB.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param wtid the raw wire transfer identifier we used
+ * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
+ * @param h_contract which contract was this payment about
+ * @param transaction_id merchant's transaction ID for the payment
+ * @param coin_pub which public key was this payment about
+ * @param coin_value amount contributed by this coin in total
+ * @param coin_fee deposit fee charged by mint for this coin
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ */
+static int
+postgres_insert_aggregation_tracking (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ struct GNUNET_TIME_Absolute execution_time,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_value,
+ const struct TALER_Amount *coin_fee)
+{
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_auto_from_type (h_contract),
+ TALER_PQ_query_param_auto_from_type (h_wire),
+ TALER_PQ_query_param_auto_from_type (coin_pub),
+ TALER_PQ_query_param_auto_from_type (merchant_pub),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_auto_from_type (wtid),
+ TALER_PQ_query_param_absolute_time (&execution_time),
+ TALER_PQ_query_param_amount (coin_value),
+ TALER_PQ_query_param_amount (coin_fee),
+ TALER_PQ_query_param_end
+ };
+ PGresult *result;
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "insert_aggregation_tracking",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called to insert wire transfer commit data into the DB.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param type type of the wire transfer (i.e. "sepa")
+ * @param buf buffer with wire transfer preparation data
+ * @param buf_size number of bytes in @a buf
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ */
+static int
+postgres_wire_prepare_data_insert (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const char *type,
+ const char *buf,
+ size_t buf_size)
+{
+ PGresult *result;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_fixed_size (type, strlen (type) + 1),
+ TALER_PQ_query_param_fixed_size (buf, buf_size),
+ TALER_PQ_query_param_end
+ };
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "wire_prepare_data_insert",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called to mark wire transfer commit data as finished.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param rowid which entry to mark as finished
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ */
+static int
+postgres_wire_prepare_data_mark_finished (void *cls,
+ struct TALER_MINTDB_Session *session,
+ unsigned long long rowid)
+{
+ uint64_t serial_id = rowid;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&serial_id),
+ TALER_PQ_query_param_end
+ };
+ PGresult *result;
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "wire_prepare_data_mark_done",
+ params);
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called to get an unfinished wire transfer
+ * preparation data. Fetches at most one item.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param type type fo the wire transfer (i.e. "sepa")
+ * @param cb function to call for ONE unfinished item
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO if there are no entries,
+ * #GNUNET_SYSERR on DB errors
+ */
+static int
+postgres_wire_prepare_data_get (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const char *type,
+ TALER_MINTDB_WirePreparationCallback cb,
+ void *cb_cls)
+{
+ PGresult *result;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_fixed_size (type, strlen (type) + 1),
+ TALER_PQ_query_param_end
+ };
+
+ result = TALER_PQ_exec_prepared (session->conn,
+ "wire_prepare_data_get",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+ if (1 != PQntuples (result))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ uint64_t serial_id;
+ void *buf = NULL;
+ size_t buf_size;
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("serial_id",
+ &serial_id),
+ TALER_PQ_result_spec_variable_size ("buf",
+ &buf,
+ &buf_size),
+ TALER_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ TALER_PQ_extract_result (result,
+ rs,
+ 0))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ cb (cb_cls,
+ serial_id,
+ buf,
+ buf_size);
+ TALER_PQ_cleanup_result (rs);
+ }
+ PQclear (result);
+ return GNUNET_OK;
}
@@ -3413,6 +4212,7 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
&db_conn_destroy))
{
TALER_LOG_ERROR ("Cannnot create pthread key.\n");
+ GNUNET_free (pg);
return NULL;
}
if (GNUNET_OK !=
@@ -3422,8 +4222,9 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
&pg->connection_cfg_str))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
+ "mintdb-postgres",
"db_conn_str");
+ GNUNET_free (pg);
return NULL;
}
plugin = GNUNET_new (struct TALER_MINTDB_Plugin);
@@ -3443,9 +4244,11 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
plugin->get_reserve_history = &postgres_get_reserve_history;
plugin->free_reserve_history = &common_free_reserve_history;
plugin->have_deposit = &postgres_have_deposit;
- plugin->iterate_deposits = &postgres_iterate_deposits;
+ plugin->mark_deposit_tiny = &postgres_mark_deposit_tiny;
+ plugin->mark_deposit_done = &postgres_mark_deposit_done;
+ plugin->get_ready_deposit = &postgres_get_ready_deposit;
+ plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits;
plugin->insert_deposit = &postgres_insert_deposit;
-
plugin->get_refresh_session = &postgres_get_refresh_session;
plugin->create_refresh_session = &postgres_create_refresh_session;
plugin->insert_refresh_melt = &postgres_insert_refresh_melt;
@@ -3456,7 +4259,6 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins;
plugin->insert_refresh_commit_links = &postgres_insert_refresh_commit_links;
plugin->get_refresh_commit_links = &postgres_get_refresh_commit_links;
-
plugin->get_melt_commitment = &postgres_get_melt_commitment;
plugin->free_melt_commitment = &common_free_melt_commitment;
plugin->insert_refresh_out = &postgres_insert_refresh_out;
@@ -3465,7 +4267,12 @@ libtaler_plugin_mintdb_postgres_init (void *cls)
plugin->get_transfer = &postgres_get_transfer;
plugin->get_coin_transactions = &postgres_get_coin_transactions;
plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
+ plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid;
+ plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
+ plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
+ plugin->wire_prepare_data_mark_finished = &postgres_wire_prepare_data_mark_finished;
+ plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get;
return plugin;
}
diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c
index 5b9d1c2ec..07f5d078a 100644
--- a/src/mintdb/test_mintdb.c
+++ b/src/mintdb/test_mintdb.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
- * @file mint/test_mintdb.c
+ * @file mintdb/test_mintdb.c
* @brief test cases for DB interaction functions
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
@@ -305,8 +305,10 @@ test_melting (struct TALER_MINTDB_Session *session)
RND_BLK (&refresh_session);
RND_BLK (&session_hash);
melts = NULL;
+ dkp = NULL;
new_dkp = NULL;
new_denom_pubs = NULL;
+ ret_denom_pubs = NULL;
/* create and test a refresh session */
refresh_session.num_oldcoins = MELT_OLD_COINS;
refresh_session.num_newcoins = 1;
@@ -324,11 +326,11 @@ test_melting (struct TALER_MINTDB_Session *session)
sizeof (refresh_session)));
/* create a denomination (value: 1; fraction: 100) */
- dkp = create_denom_key_pair(512, session,
- &value,
- &fee_withdraw,
- &fee_deposit,
- &fee_refresh);
+ dkp = create_denom_key_pair (512, session,
+ &value,
+ &fee_withdraw,
+ &fee_deposit,
+ &fee_refresh);
/* create MELT_OLD_COINS number of refresh melts */
melts = GNUNET_new_array (MELT_OLD_COINS, struct TALER_MINTDB_RefreshMelt);
for (cnt=0; cnt < MELT_OLD_COINS; cnt++)
@@ -416,7 +418,8 @@ test_melting (struct TALER_MINTDB_Session *session)
ret = GNUNET_OK;
drop:
- destroy_denom_key_pair (dkp);
+ if (NULL != dkp)
+ destroy_denom_key_pair (dkp);
if (NULL != melts)
{
for (cnt = 0; cnt < MELT_OLD_COINS; cnt++)
@@ -440,6 +443,114 @@ test_melting (struct TALER_MINTDB_Session *session)
/**
+ * Callback that should never be called.
+ */
+static void
+cb_wt_never (void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_value,
+ const struct TALER_Amount *coin_fee,
+ const struct TALER_Amount *transfer_value)
+{
+ GNUNET_assert (0); /* this statement should be unreachable */
+}
+
+
+/**
+ * Callback that should never be called.
+ */
+static void
+cb_wtid_never (void *cls,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_Amount *coin_fee,
+ const struct TALER_Amount *total_amount,
+ struct GNUNET_TIME_Absolute execution_time)
+{
+ GNUNET_assert (0);
+}
+
+
+static struct TALER_MerchantPublicKeyP merchant_pub_wt;
+static struct GNUNET_HashCode h_wire_wt;
+static struct GNUNET_HashCode h_contract_wt;
+static uint64_t transaction_id_wt;
+static struct TALER_CoinSpendPublicKeyP coin_pub_wt;
+static struct TALER_Amount coin_value_wt;
+static struct TALER_Amount coin_fee_wt;
+static struct TALER_Amount transfer_value_wt;
+static struct GNUNET_TIME_Absolute execution_time_wt;
+static struct TALER_WireTransferIdentifierRawP wtid_wt;
+
+
+/**
+ * Callback that should be called with the WT data.
+ */
+static void
+cb_wt_check (void *cls,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *coin_value,
+ const struct TALER_Amount *coin_fee,
+ const struct TALER_Amount *transfer_value)
+{
+ GNUNET_assert (cls == &cb_wt_never);
+ GNUNET_assert (0 == memcmp (merchant_pub,
+ &merchant_pub_wt,
+ sizeof (struct TALER_MerchantPublicKeyP)));
+ GNUNET_assert (0 == memcmp (h_wire,
+ &h_wire_wt,
+ sizeof (struct GNUNET_HashCode)));
+ GNUNET_assert (0 == memcmp (h_contract,
+ &h_contract_wt,
+ sizeof (struct GNUNET_HashCode)));
+ GNUNET_assert (transaction_id == transaction_id_wt);
+ GNUNET_assert (0 == memcmp (coin_pub,
+ &coin_pub_wt,
+ sizeof (struct TALER_CoinSpendPublicKeyP)));
+ GNUNET_assert (0 == TALER_amount_cmp (coin_value,
+ &coin_value_wt));
+ GNUNET_assert (0 == TALER_amount_cmp (coin_fee,
+ &coin_fee_wt));
+ GNUNET_assert (0 == TALER_amount_cmp (transfer_value,
+ &transfer_value_wt));
+}
+
+
+/**
+ * Callback that should be called with the WT data.
+ */
+static void
+cb_wtid_check (void *cls,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *coin_contribution,
+ const struct TALER_Amount *coin_fee,
+ const struct TALER_Amount *total_amount,
+ struct GNUNET_TIME_Absolute execution_time)
+{
+ GNUNET_assert (cls == &cb_wtid_never);
+ GNUNET_assert (0 == memcmp (wtid,
+ &wtid_wt,
+ sizeof (struct TALER_WireTransferIdentifierRawP)));
+ GNUNET_assert (execution_time.abs_value_us ==
+ execution_time_wt.abs_value_us);
+ GNUNET_assert (0 == TALER_amount_cmp (coin_contribution,
+ &coin_value_wt));
+ GNUNET_assert (0 == TALER_amount_cmp (coin_fee,
+ &coin_fee_wt));
+ GNUNET_assert (0 == TALER_amount_cmp (total_amount,
+ &transfer_value_wt));
+}
+
+
+/**
* Main function that will be run by the scheduler.
*
* @param cls closure
@@ -455,7 +566,6 @@ run (void *cls,
{
struct TALER_MINTDB_Session *session;
struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_Amount amount;
struct DenomKeyPair *dkp;
struct TALER_MINTDB_CollectableBlindcoin cbc;
struct TALER_MINTDB_CollectableBlindcoin cbc2;
@@ -465,6 +575,7 @@ run (void *cls,
struct TALER_MINTDB_CollectableBlindcoin *withdraw;
struct TALER_MINTDB_Deposit deposit;
struct TALER_MINTDB_Deposit deposit2;
+ struct TALER_WireTransferIdentifierRawP wtid;
json_t *wire;
json_t *just;
const char * const json_wire_str =
@@ -563,11 +674,7 @@ run (void *cls,
= GNUNET_CRYPTO_rsa_sign (dkp->priv.rsa_private_key,
&cbc.h_coin_envelope,
sizeof (cbc.h_coin_envelope));
- (void) memcpy (&cbc.reserve_pub,
- &reserve_pub,
- sizeof (reserve_pub));
- amount.value--;
- amount.fraction--;
+ cbc.reserve_pub = reserve_pub;
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee));
@@ -652,9 +759,7 @@ run (void *cls,
plugin->have_deposit (plugin->cls,
session,
&deposit));
- (void) memcpy (&deposit2,
- &deposit,
- sizeof (deposit));
+ deposit2 = deposit;
deposit2.transaction_id++; /* should fail if transaction id is different */
FAILIF (GNUNET_NO !=
plugin->have_deposit (plugin->cls,
@@ -666,15 +771,79 @@ run (void *cls,
plugin->have_deposit (plugin->cls,
session,
&deposit2));
- (void) memcpy (&deposit2.merchant_pub,
- &deposit.merchant_pub,
- sizeof (deposit.merchant_pub));
+ deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
FAILIF (GNUNET_NO !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
FAILIF (GNUNET_OK != test_melting (session));
+
+ /* setup values for wire transfer aggregation data */
+ memset (&wtid, 42, sizeof (wtid));
+ memset (&merchant_pub_wt, 43, sizeof (merchant_pub_wt));
+ memset (&h_wire_wt, 44, sizeof (h_wire_wt));
+ memset (&h_contract_wt, 45, sizeof (h_contract_wt));
+ memset (&coin_pub_wt, 46, sizeof (coin_pub_wt));
+ transaction_id_wt = 47;
+ execution_time_wt = GNUNET_TIME_absolute_get ();
+ memset (&merchant_pub_wt, 48, sizeof (merchant_pub_wt));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY "KUDOS:1.000010",
+ &coin_value_wt));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY "KUDOS:0.000010",
+ &coin_fee_wt));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY "KUDOS:1.000000",
+ &transfer_value_wt));
+
+ FAILIF (GNUNET_NO !=
+ plugin->lookup_wire_transfer (plugin->cls,
+ session,
+ &wtid_wt,
+ &cb_wt_never,
+ NULL));
+ FAILIF (GNUNET_NO !=
+ plugin->wire_lookup_deposit_wtid (plugin->cls,
+ session,
+ &h_contract_wt,
+ &h_wire_wt,
+ &coin_pub_wt,
+ &merchant_pub_wt,
+ transaction_id_wt,
+ &cb_wtid_never,
+ NULL));
+ /* insert WT data */
+ FAILIF (GNUNET_OK !=
+ plugin->insert_aggregation_tracking (plugin->cls,
+ session,
+ &wtid_wt,
+ &merchant_pub_wt,
+ &h_wire_wt,
+ &h_contract_wt,
+ transaction_id_wt,
+ execution_time_wt,
+ &coin_pub_wt,
+ &coin_value_wt,
+ &coin_fee_wt,
+ &transfer_value_wt));
+ FAILIF (GNUNET_OK !=
+ plugin->lookup_wire_transfer (plugin->cls,
+ session,
+ &wtid_wt,
+ &cb_wt_check,
+ &cb_wt_never));
+ FAILIF (GNUNET_OK !=
+ plugin->wire_lookup_deposit_wtid (plugin->cls,
+ session,
+ &h_contract_wt,
+ &h_wire_wt,
+ &coin_pub_wt,
+ &merchant_pub_wt,
+ transaction_id_wt,
+ &cb_wtid_check,
+ &cb_wtid_never));
result = 0;
drop:
diff --git a/src/mintdb/test_mintdb_deposits.c b/src/mintdb/test_mintdb_deposits.c
index 171fa2c69..3ce0a35a5 100644
--- a/src/mintdb/test_mintdb_deposits.c
+++ b/src/mintdb/test_mintdb_deposits.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/mintdb/test_perf_taler_mintdb.c b/src/mintdb/test_perf_taler_mintdb.c
index 2830ef812..789a0dd4f 100644
--- a/src/mintdb/test_perf_taler_mintdb.c
+++ b/src/mintdb/test_perf_taler_mintdb.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c
index e1a082e44..59a1db022 100644
--- a/src/pq/db_pq.c
+++ b/src/pq/db_pq.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/pq/pq_helper.c b/src/pq/pq_helper.c
index bd54447e7..d245ffdb8 100644
--- a/src/pq/pq_helper.c
+++ b/src/pq/pq_helper.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c
index bda227587..f0413d1ff 100644
--- a/src/pq/test_pq.c
+++ b/src/pq/test_pq.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2015 Christian Grothoff (and other contributing authors)
+ (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index eaf3a4827..8efc3987a 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -33,6 +33,7 @@ libtalerutil_la_SOURCES = \
util.c \
json.c \
os_installation.c \
+ plugin.c \
wireformats.c
libtalerutil_la_LIBADD = \
@@ -48,14 +49,12 @@ libtalerutil_la_LDFLAGS = \
TESTS = \
test_amount \
test_crypto \
- test_json \
- test_wireformats
+ test_json
check_PROGRAMS= \
test_amount \
test_crypto \
- test_json \
- test_wireformats
+ test_json
test_amount_SOURCES = \
@@ -76,10 +75,3 @@ test_json_LDADD = \
-lgnunetutil \
-ljansson \
libtalerutil.la
-
-test_wireformats_SOURCES = \
- test_wireformats.c
-test_wireformats_LDADD = \
- -lgnunetutil \
- -ljansson \
- libtalerutil.la
diff --git a/src/util/amount.c b/src/util/amount.c
index 7260b150f..dc2d2e400 100644
--- a/src/util/amount.c
+++ b/src/util/amount.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/crypto.c b/src/util/crypto.c
index ebf6413db..9e6890569 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/json.c b/src/util/json.c
index cea0118e1..6aca7548c 100644
--- a/src/util/json.c
+++ b/src/util/json.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/os_installation.c b/src/util/os_installation.c
index ec1868a84..0eab118fe 100644
--- a/src/util/os_installation.c
+++ b/src/util/os_installation.c
@@ -1,6 +1,6 @@
/*
This file is part of GNUnet.
- Copyright (C) 2006-2014 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2006-2014 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
diff --git a/src/util/plugin.c b/src/util/plugin.c
new file mode 100644
index 000000000..6f8e03df6
--- /dev/null
+++ b/src/util/plugin.c
@@ -0,0 +1,88 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/plugin.c
+ * @brief Setup paths so that we can load Taler plugins
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include <ltdl.h>
+
+/**
+ * Libtool search path before we started.
+ */
+static char *old_dlsearchpath;
+
+
+/**
+ * Setup libtool paths.
+ */
+void __attribute__ ((constructor))
+plugin_init ()
+{
+ int err;
+ const char *opath;
+ char *path;
+ char *cpath;
+
+ err = lt_dlinit ();
+ if (err > 0)
+ {
+ FPRINTF (stderr,
+ _("Initialization of plugin mechanism failed: %s!\n"),
+ lt_dlerror ());
+ return;
+ }
+ opath = lt_dlgetsearchpath ();
+ if (NULL != opath)
+ old_dlsearchpath = GNUNET_strdup (opath);
+ path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+ if (NULL != path)
+ {
+ if (NULL != opath)
+ {
+ GNUNET_asprintf (&cpath, "%s:%s", opath, path);
+ lt_dlsetsearchpath (cpath);
+ GNUNET_free (path);
+ GNUNET_free (cpath);
+ }
+ else
+ {
+ lt_dlsetsearchpath (path);
+ GNUNET_free (path);
+ }
+ }
+}
+
+
+/**
+ * Shutdown libtool.
+ */
+void __attribute__ ((destructor))
+plugin_fini ()
+{
+ lt_dlsetsearchpath (old_dlsearchpath);
+ if (NULL != old_dlsearchpath)
+ {
+ GNUNET_free (old_dlsearchpath);
+ old_dlsearchpath = NULL;
+ }
+ lt_dlexit ();
+}
+
+/* end of plugin.c */
diff --git a/src/util/test_amount.c b/src/util/test_amount.c
index 4741bcf36..3f33334bc 100644
--- a/src/util/test_amount.c
+++ b/src/util/test_amount.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2015 Christian Grothoff (and other contributing authors)
+ (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c
index a5313195a..59acd7814 100644
--- a/src/util/test_crypto.c
+++ b/src/util/test_crypto.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2015 Christian Grothoff (and other contributing authors)
+ (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/test_json.c b/src/util/test_json.c
index c48fe68bd..5e2f50fe8 100644
--- a/src/util/test_json.c
+++ b/src/util/test_json.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2015 Christian Grothoff (and other contributing authors)
+ (C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/util.c b/src/util/util.c
index 08438cfab..addafacf7 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/util/wireformats.c b/src/util/wireformats.c
index dab7fb926..cd5a9c3d4 100644
--- a/src/util/wireformats.c
+++ b/src/util/wireformats.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am
new file mode 100644
index 000000000..eb2e893fa
--- /dev/null
+++ b/src/wire/Makefile.am
@@ -0,0 +1,78 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+plugindir = $(libdir)/taler
+
+plugin_LTLIBRARIES = \
+ libtaler_plugin_wire_sepa.la \
+ libtaler_plugin_wire_test.la
+
+noinst_LTLIBRARIES = \
+ libtaler_plugin_wire_template.la
+
+lib_LTLIBRARIES = \
+ libtalerwire.la
+
+
+libtaler_plugin_wire_test_la_SOURCES = \
+ plugin_wire_test.c
+libtaler_plugin_wire_test_la_LIBADD = \
+ $(LTLIBINTL)
+libtaler_plugin_wire_test_la_LDFLAGS = \
+ $(TALER_PLUGIN_LDFLAGS) \
+ $(top_builddir)/src/bank-lib/libtalerbank.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil $(XLIB)
+
+
+libtaler_plugin_wire_sepa_la_SOURCES = \
+ plugin_wire_sepa.c
+libtaler_plugin_wire_sepa_la_LIBADD = \
+ $(LTLIBINTL)
+libtaler_plugin_wire_sepa_la_LDFLAGS = \
+ $(TALER_PLUGIN_LDFLAGS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil $(XLIB)
+
+
+libtaler_plugin_wire_template_la_SOURCES = \
+ plugin_wire_template.c
+libtaler_plugin_wire_template_la_LIBADD = \
+ $(LTLIBINTL)
+libtaler_plugin_wire_template_la_LDFLAGS = \
+ $(TALER_PLUGIN_LDFLAGS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil $(XLIB)
+
+
+libtalerwire_la_SOURCES = \
+ wire.c
+libtalerwire_la_LIBADD = \
+ -lgnunetutil \
+ $(XLIB)
+libtalerwire_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -export-dynamic -no-undefined
+
+
+TESTS = \
+ test_sepa_wireformat
+
+check_PROGRAMS= \
+ test_sepa_wireformat
+
+
+
+test_sepa_wireformat_SOURCES = \
+ test_sepa_wireformat.c
+test_sepa_wireformat_LDADD = \
+ -lgnunetutil \
+ -ljansson \
+ libtalerwire.la \
+ $(top_builddir)/src/util/libtalerutil.la
+
diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c
new file mode 100644
index 000000000..00d19d4b0
--- /dev/null
+++ b/src/wire/plugin_wire_sepa.c
@@ -0,0 +1,545 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file plugin_wire_sepa.c
+ * @brief wire plugin for transfers using SEPA/EBICS
+ * @author Florian Dold
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura
+ */
+#include "platform.h"
+#include "taler_wire_plugin.h"
+
+
+/**
+ * Type of the "cls" argument given to each of the functions in
+ * our API.
+ */
+struct SepaClosure
+{
+
+ /**
+ * Which currency do we support?
+ */
+ char *currency;
+
+};
+
+
+/**
+ * Round amount DOWN to the amount that can be transferred via the wire
+ * method. For example, Taler may support 0.000001 EUR as a unit of
+ * payment, but SEPA only supports 0.01 EUR. This function would
+ * round 0.125 EUR to 0.12 EUR in this case.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param[in,out] amount amount to round down
+ * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
+ * #GNUNET_SYSERR if the amount or currency was invalid
+ */
+static int
+sepa_amount_round (void *cls,
+ struct TALER_Amount *amount)
+{
+ struct SepaClosure *sc = cls;
+ uint32_t delta;
+
+ if (0 != strcasecmp (amount->currency,
+ sc->currency))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100);
+ if (0 == delta)
+ return GNUNET_NO;
+ amount->fraction -= delta;
+ return GNUNET_SYSERR;
+}
+
+
+/* Taken from GNU gettext */
+
+/**
+ * Entry in the country table.
+ */
+struct table_entry
+{
+ /**
+ * 2-Character international country code.
+ */
+ const char *code;
+
+ /**
+ * Long English name of the country.
+ */
+ const char *english;
+};
+
+
+/* Keep the following table in sync with gettext.
+ WARNING: the entries should stay sorted according to the code */
+/**
+ * List of country codes.
+ */
+static const struct table_entry country_table[] =
+ {
+ { "AE", "U.A.E." },
+ { "AF", "Afghanistan" },
+ { "AL", "Albania" },
+ { "AM", "Armenia" },
+ { "AN", "Netherlands Antilles" },
+ { "AR", "Argentina" },
+ { "AT", "Austria" },
+ { "AU", "Australia" },
+ { "AZ", "Azerbaijan" },
+ { "BA", "Bosnia and Herzegovina" },
+ { "BD", "Bangladesh" },
+ { "BE", "Belgium" },
+ { "BG", "Bulgaria" },
+ { "BH", "Bahrain" },
+ { "BN", "Brunei Darussalam" },
+ { "BO", "Bolivia" },
+ { "BR", "Brazil" },
+ { "BT", "Bhutan" },
+ { "BY", "Belarus" },
+ { "BZ", "Belize" },
+ { "CA", "Canada" },
+ { "CG", "Congo" },
+ { "CH", "Switzerland" },
+ { "CI", "Cote d'Ivoire" },
+ { "CL", "Chile" },
+ { "CM", "Cameroon" },
+ { "CN", "People's Republic of China" },
+ { "CO", "Colombia" },
+ { "CR", "Costa Rica" },
+ { "CS", "Serbia and Montenegro" },
+ { "CZ", "Czech Republic" },
+ { "DE", "Germany" },
+ { "DK", "Denmark" },
+ { "DO", "Dominican Republic" },
+ { "DZ", "Algeria" },
+ { "EC", "Ecuador" },
+ { "EE", "Estonia" },
+ { "EG", "Egypt" },
+ { "ER", "Eritrea" },
+ { "ES", "Spain" },
+ { "ET", "Ethiopia" },
+ { "FI", "Finland" },
+ { "FO", "Faroe Islands" },
+ { "FR", "France" },
+ { "GB", "United Kingdom" },
+ { "GD", "Caribbean" },
+ { "GE", "Georgia" },
+ { "GL", "Greenland" },
+ { "GR", "Greece" },
+ { "GT", "Guatemala" },
+ { "HK", "Hong Kong" },
+ { "HK", "Hong Kong S.A.R." },
+ { "HN", "Honduras" },
+ { "HR", "Croatia" },
+ { "HT", "Haiti" },
+ { "HU", "Hungary" },
+ { "ID", "Indonesia" },
+ { "IE", "Ireland" },
+ { "IL", "Israel" },
+ { "IN", "India" },
+ { "IQ", "Iraq" },
+ { "IR", "Iran" },
+ { "IS", "Iceland" },
+ { "IT", "Italy" },
+ { "JM", "Jamaica" },
+ { "JO", "Jordan" },
+ { "JP", "Japan" },
+ { "KE", "Kenya" },
+ { "KG", "Kyrgyzstan" },
+ { "KH", "Cambodia" },
+ { "KR", "South Korea" },
+ { "KW", "Kuwait" },
+ { "KZ", "Kazakhstan" },
+ { "LA", "Laos" },
+ { "LB", "Lebanon" },
+ { "LI", "Liechtenstein" },
+ { "LK", "Sri Lanka" },
+ { "LT", "Lithuania" },
+ { "LU", "Luxembourg" },
+ { "LV", "Latvia" },
+ { "LY", "Libya" },
+ { "MA", "Morocco" },
+ { "MC", "Principality of Monaco" },
+ { "MD", "Moldava" },
+ { "MD", "Moldova" },
+ { "ME", "Montenegro" },
+ { "MK", "Former Yugoslav Republic of Macedonia" },
+ { "ML", "Mali" },
+ { "MM", "Myanmar" },
+ { "MN", "Mongolia" },
+ { "MO", "Macau S.A.R." },
+ { "MT", "Malta" },
+ { "MV", "Maldives" },
+ { "MX", "Mexico" },
+ { "MY", "Malaysia" },
+ { "NG", "Nigeria" },
+ { "NI", "Nicaragua" },
+ { "NL", "Netherlands" },
+ { "NO", "Norway" },
+ { "NP", "Nepal" },
+ { "NZ", "New Zealand" },
+ { "OM", "Oman" },
+ { "PA", "Panama" },
+ { "PE", "Peru" },
+ { "PH", "Philippines" },
+ { "PK", "Islamic Republic of Pakistan" },
+ { "PL", "Poland" },
+ { "PR", "Puerto Rico" },
+ { "PT", "Portugal" },
+ { "PY", "Paraguay" },
+ { "QA", "Qatar" },
+ { "RE", "Reunion" },
+ { "RO", "Romania" },
+ { "RS", "Serbia" },
+ { "RU", "Russia" },
+ { "RW", "Rwanda" },
+ { "SA", "Saudi Arabia" },
+ { "SE", "Sweden" },
+ { "SG", "Singapore" },
+ { "SI", "Slovenia" },
+ { "SK", "Slovak" },
+ { "SN", "Senegal" },
+ { "SO", "Somalia" },
+ { "SR", "Suriname" },
+ { "SV", "El Salvador" },
+ { "SY", "Syria" },
+ { "TH", "Thailand" },
+ { "TJ", "Tajikistan" },
+ { "TM", "Turkmenistan" },
+ { "TN", "Tunisia" },
+ { "TR", "Turkey" },
+ { "TT", "Trinidad and Tobago" },
+ { "TW", "Taiwan" },
+ { "TZ", "Tanzania" },
+ { "UA", "Ukraine" },
+ { "US", "United States" },
+ { "UY", "Uruguay" },
+ { "VA", "Vatican" },
+ { "VE", "Venezuela" },
+ { "VN", "Viet Nam" },
+ { "YE", "Yemen" },
+ { "ZA", "South Africa" },
+ { "ZW", "Zimbabwe" }
+ };
+
+
+/**
+ * Country code comparator function, for binary search with bsearch().
+ *
+ * @param ptr1 pointer to a `struct table_entry`
+ * @param ptr2 pointer to a `struct table_entry`
+ * @return result of strncmp()'ing the 2-digit country codes of the entries
+ */
+static int
+cmp_country_code (const void *ptr1,
+ const void *ptr2)
+{
+ const struct table_entry *cc1 = ptr1;
+ const struct table_entry *cc2 = ptr2;
+
+ return strncmp (cc1->code,
+ cc2->code,
+ 2);
+}
+
+
+/**
+ * Validates given IBAN according to the European Banking Standards. See:
+ * http://www.europeanpaymentscouncil.eu/documents/ECBS%20IBAN%20standard%20EBS204_V3.2.pdf
+ *
+ * @param iban the IBAN number to validate
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+static int
+validate_iban (const char *iban)
+{
+ char cc[2];
+ char ibancpy[35];
+ struct table_entry cc_entry;
+ unsigned int len;
+ char *nbuf;
+ unsigned int i;
+ unsigned int j;
+ unsigned long long dividend;
+ unsigned long long remainder;
+ int nread;
+ int ret;
+
+ len = strlen (iban);
+ if (len > 34)
+ return GNUNET_NO;
+ strncpy (cc, iban, 2);
+ strncpy (ibancpy, iban + 4, len - 4);
+ strncpy (ibancpy + len - 4, iban, 4);
+ ibancpy[len] = '\0';
+ cc_entry.code = cc;
+ cc_entry.english = NULL;
+ if (NULL ==
+ bsearch (&cc_entry,
+ country_table,
+ sizeof (country_table) / sizeof (struct table_entry),
+ sizeof (struct table_entry),
+ &cmp_country_code))
+ return GNUNET_NO;
+ nbuf = GNUNET_malloc ((len * 2) + 1);
+ for (i=0, j=0; i < len; i++)
+ {
+ if (isalpha ((int) ibancpy[i]))
+ {
+ if (2 != snprintf(&nbuf[j],
+ 3,
+ "%2u",
+ (ibancpy[i] - 'A' + 10)))
+ {
+ GNUNET_free (nbuf);
+ return GNUNET_NO;
+ }
+ j += 2;
+ continue;
+ }
+ nbuf[j] = ibancpy[i];
+ j++;
+ }
+ for (j=0;'\0' != nbuf[j];j++)
+ GNUNET_assert (isdigit(nbuf[j]));
+ GNUNET_assert (sizeof(dividend) >= 8);
+ remainder = 0;
+ for (i=0; i<j; i+=16)
+ {
+ if (1 !=
+ (ret = sscanf (&nbuf[i],
+ "%16llu %n",
+ &dividend,
+ &nread)))
+ {
+ GNUNET_free (nbuf);
+ return GNUNET_NO;
+ }
+ if (0 != remainder)
+ dividend += remainder * (pow (10, nread));
+ remainder = dividend % 97;
+ }
+ GNUNET_free (nbuf);
+ if (1 == remainder)
+ return GNUNET_YES;
+ return GNUNET_NO;
+}
+
+
+/**
+ * Check if the given wire format JSON object is correctly formatted
+ *
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+static int
+sepa_wire_validate (const json_t *wire)
+{
+ json_error_t error;
+ const char *type;
+ const char *iban;
+ const char *name;
+ const char *bic;
+ uint64_t r;
+ const char *address;
+
+ if (0 != json_unpack_ex
+ ((json_t *) wire,
+ &error, JSON_STRICT,
+ "{"
+ "s:s," /* TYPE: sepa */
+ "s:s," /* IBAN: iban */
+ "s:s," /* name: beneficiary name */
+ "s:s," /* BIC: beneficiary bank's BIC */
+ "s:i," /* r: random 64-bit integer nounce */
+ "s:s" /* address: address of the beneficiary */
+ "}",
+ "type", &type,
+ "IBAN", &iban,
+ "name", &name,
+ "bic", &bic,
+ "r", &r,
+ "address", &address))
+ {
+ TALER_json_warn (error);
+ return GNUNET_SYSERR;
+ }
+ if (0 != strcasecmp (type,
+ "sepa"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Transfer type `%s' invalid\n",
+ type);
+ return GNUNET_SYSERR;
+ }
+ if (1 != validate_iban (iban))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "IBAN `%s' invalid\n",
+ iban);
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * Prepare for exeuction of a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param wire valid wire account information
+ * @param amount amount to transfer, already rounded
+ * @param wtid wire transfer identifier to use
+ * @param psc function to call with the prepared data to persist
+ * @param psc_cls closure for @a psc
+ * @return NULL on failure
+ */
+static struct TALER_WIRE_PrepareHandle *
+sepa_prepare_wire_transfer (void *cls,
+ const json_t *wire,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_WIRE_PrepareTransactionCallback psc,
+ void *psc_cls)
+{
+ GNUNET_break (0); // FIXME: not implemented
+ return NULL;
+}
+
+
+/**
+ * Abort preparation of a wire transfer. For example,
+ * because we are shutting down.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param pth preparation to cancel
+ */
+static void
+sepa_prepare_wire_transfer_cancel (void *cls,
+ struct TALER_WIRE_PrepareHandle *pth)
+{
+ GNUNET_break (0); // FIXME: not implemented
+}
+
+
+/**
+ * Execute a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param buf buffer with the prepared execution details
+ * @param buf_size number of bytes in @a buf
+ * @param cc function to call upon success
+ * @param cc_cls closure for @a cc
+ * @return NULL on error
+ */
+static struct TALER_WIRE_ExecuteHandle *
+sepa_execute_wire_transfer (void *cls,
+ const char *buf,
+ size_t buf_size,
+ TALER_WIRE_ConfirmationCallback cc,
+ void *cc_cls)
+{
+ GNUNET_break (0); // FIXME: not implemented
+ return NULL;
+}
+
+
+/**
+ * Abort execution of a wire transfer. For example, because we are
+ * shutting down. Note that if an execution is aborted, it may or
+ * may not still succeed. The caller MUST run @e
+ * execute_wire_transfer again for the same request as soon as
+ * possilbe, to ensure that the request either ultimately succeeds
+ * or ultimately fails. Until this has been done, the transaction is
+ * in limbo (i.e. may or may not have been committed).
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param eh execution to cancel
+ */
+static void
+sepa_execute_wire_transfer_cancel (void *cls,
+ struct TALER_WIRE_ExecuteHandle *eh)
+{
+ GNUNET_break (0); // FIXME: not implemented
+}
+
+
+/**
+ * Initialize sepa-wire subsystem.
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
+ */
+void *
+libtaler_plugin_wire_sepa_init (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct SepaClosure *sc;
+ struct TALER_WIRE_Plugin *plugin;
+
+ sc = GNUNET_new (struct SepaClosure);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "CURRENCY",
+ &sc->currency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "CURRENCY");
+ GNUNET_free (sc);
+ return NULL;
+ }
+
+ plugin = GNUNET_new (struct TALER_WIRE_Plugin);
+ plugin->cls = sc;
+ plugin->amount_round = &sepa_amount_round;
+ plugin->wire_validate = &sepa_wire_validate;
+ plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer;
+ plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel;
+ plugin->execute_wire_transfer = &sepa_execute_wire_transfer;
+ plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel;
+ return plugin;
+}
+
+
+/**
+ * Shutdown Sepa wire subsystem.
+ *
+ * @param cls a `struct TALER_WIRE_Plugin`
+ * @return NULL (always)
+ */
+void *
+libtaler_plugin_wire_sepa_done (void *cls)
+{
+ struct TALER_WIRE_Plugin *plugin = cls;
+ struct SepaClosure *sc = plugin->cls;
+
+ GNUNET_free (sc->currency);
+ GNUNET_free (sc);
+ GNUNET_free (plugin);
+ return NULL;
+}
+
+/* end of plugin_wire_sepa.c */
diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c
new file mode 100644
index 000000000..baf0ee7d5
--- /dev/null
+++ b/src/wire/plugin_wire_template.c
@@ -0,0 +1,243 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file plugin_wire_template.c
+ * @brief template for wire plugins; replace "template" with real plugin name!
+ * @author Florian Dold
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura
+ */
+#include "platform.h"
+#include "taler_wire_plugin.h"
+
+
+/**
+ * Type of the "cls" argument given to each of the functions in
+ * our API.
+ */
+struct TemplateClosure
+{
+
+ /**
+ * URI of the bank for sending funds to the bank.
+ */
+ char *bank_uri;
+
+ /**
+ * Which currency do we support?
+ */
+ char *currency;
+
+};
+
+
+/**
+ * Round amount DOWN to the amount that can be transferred via the wire
+ * method. For example, Taler may support 0.000001 EUR as a unit of
+ * payment, but SEPA only supports 0.01 EUR. This function would
+ * round 0.125 EUR to 0.12 EUR in this case.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param[in,out] amount amount to round down
+ * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
+ * #GNUNET_SYSERR if the amount or currency was invalid
+ */
+static int
+template_amount_round (void *cls,
+ struct TALER_Amount *amount)
+{
+ struct TemplateClosure *tc = cls;
+
+ if (0 != strcasecmp (amount->currency,
+ tc->currency))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_break (0); // not implemented
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Check if the given wire format JSON object is correctly formatted
+ *
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+static int
+template_wire_validate (const json_t *wire)
+{
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Prepare for exeuction of a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param wire valid wire account information
+ * @param amount amount to transfer, already rounded
+ * @param wtid wire transfer identifier to use
+ * @param ptc function to call with the prepared data to persist
+ * @param ptc_cls closure for @a ptc
+ * @return NULL on failure
+ */
+static struct TALER_WIRE_PrepareHandle *
+template_prepare_wire_transfer (void *cls,
+ const json_t *wire,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_WIRE_PrepareTransactionCallback ptc,
+ void *ptc_cls)
+{
+ GNUNET_break (0);
+ return NULL;
+}
+
+
+/**
+ * Abort preparation of a wire transfer. For example,
+ * because we are shutting down.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param pth preparation to cancel
+ */
+static void
+template_prepare_wire_transfer_cancel (void *cls,
+ struct TALER_WIRE_PrepareHandle *pth)
+{
+ GNUNET_break (0);
+}
+
+
+/**
+ * Execute a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param buf buffer with the prepared execution details
+ * @param buf_size number of bytes in @a buf
+ * @param cc function to call upon success
+ * @param cc_cls closure for @a cc
+ * @return NULL on error
+ */
+static struct TALER_WIRE_ExecuteHandle *
+template_execute_wire_transfer (void *cls,
+ const char *buf,
+ size_t buf_size,
+ TALER_WIRE_ConfirmationCallback cc,
+ void *cc_cls)
+{
+ GNUNET_break (0);
+ return NULL;
+}
+
+
+/**
+ * Abort execution of a wire transfer. For example, because we are
+ * shutting down. Note that if an execution is aborted, it may or
+ * may not still succeed. The caller MUST run @e
+ * execute_wire_transfer again for the same request as soon as
+ * possilbe, to ensure that the request either ultimately succeeds
+ * or ultimately fails. Until this has been done, the transaction is
+ * in limbo (i.e. may or may not have been committed).
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param eh execution to cancel
+ */
+static void
+template_execute_wire_transfer_cancel (void *cls,
+ struct TALER_WIRE_ExecuteHandle *eh)
+{
+ GNUNET_break (0);
+}
+
+
+/**
+ * Initialize template-wire subsystem.
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
+ */
+void *
+libtaler_plugin_wire_template_init (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct TemplateClosure *tc;
+ struct TALER_WIRE_Plugin *plugin;
+
+ tc = GNUNET_new (struct TemplateClosure);
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-template",
+ "bank_uri",
+ &tc->bank_uri))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "wire-template",
+ "bank_uri");
+ GNUNET_free (tc);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "CURRENCY",
+ &tc->currency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "CURRENCY");
+ GNUNET_free (tc->bank_uri);
+ GNUNET_free (tc);
+ return NULL;
+ }
+
+ plugin = GNUNET_new (struct TALER_WIRE_Plugin);
+ plugin->cls = tc;
+ plugin->amount_round = &template_amount_round;
+ plugin->wire_validate = &template_wire_validate;
+ plugin->prepare_wire_transfer = &template_prepare_wire_transfer;
+ plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel;
+ plugin->execute_wire_transfer = &template_execute_wire_transfer;
+ plugin->execute_wire_transfer_cancel = &template_execute_wire_transfer_cancel;
+ return plugin;
+}
+
+
+/**
+ * Shutdown Template wire subsystem.
+ *
+ * @param cls a `struct TALER_WIRE_Plugin`
+ * @return NULL (always)
+ */
+void *
+libtaler_plugin_wire_template_done (void *cls)
+{
+ struct TALER_WIRE_Plugin *plugin = cls;
+ struct TemplateClosure *tc = plugin->cls;
+
+ GNUNET_free (tc->bank_uri);
+ GNUNET_free (tc->currency);
+ GNUNET_free (tc);
+ GNUNET_free (plugin);
+ return NULL;
+}
+
+/* end of plugin_wire_template.c */
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
new file mode 100644
index 000000000..7940b4775
--- /dev/null
+++ b/src/wire/plugin_wire_test.c
@@ -0,0 +1,581 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file plugin_wire_test.c
+ * @brief plugin for the "test" wire method
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_wire_plugin.h"
+#include "taler_bank_service.h"
+
+/* only for HTTP status codes */
+#include <microhttpd.h>
+
+/**
+ * Type of the "cls" argument given to each of the functions in
+ * our API.
+ */
+struct TestClosure
+{
+
+ /**
+ * Handle to the bank for sending funds to the bank.
+ */
+ struct TALER_BANK_Context *bank;
+
+ /**
+ * Which currency do we support?
+ */
+ char *currency;
+
+ /**
+ * Handle to the bank task, or NULL.
+ */
+ struct GNUNET_SCHEDULER_Task *bt;
+
+};
+
+
+/**
+ * Handle returned by #test_prepare_wire_transfer.
+ */
+struct TALER_WIRE_PrepareHandle
+{
+
+ /**
+ * Task we use for async execution.
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Test closure we run in.
+ */
+ struct TestClosure *tc;
+
+ /**
+ * Wire data for the transfer.
+ */
+ json_t *wire;
+
+ /**
+ * Function to call with the serialized data.
+ */
+ TALER_WIRE_PrepareTransactionCallback ptc;
+
+ /**
+ * Closure for @e ptc.
+ */
+ void *ptc_cls;
+
+ /**
+ * Amount to transfer.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Subject of the wire transfer.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+
+};
+
+
+/**
+ * Handle returned by #test_execute_wire_transfer.
+ */
+struct TALER_WIRE_ExecuteHandle
+{
+
+ /**
+ * Handle to the HTTP request to the bank.
+ */
+ struct TALER_BANK_AdminAddIncomingHandle *aaih;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_WIRE_ConfirmationCallback cc;
+
+ /**
+ * Closure for @e cc.
+ */
+ void *cc_cls;
+};
+
+
+/**
+ * Task that runs the bank's context's event loop with the GNUnet
+ * scheduler.
+ *
+ * @param cls our `struct TestClosure`
+ * @param tc scheduler context (unused)
+ */
+static void
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *sct)
+{
+ struct TestClosure *tc = cls;
+ long timeout;
+ int max_fd;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ fd_set except_fd_set;
+ struct GNUNET_NETWORK_FDSet *rs;
+ struct GNUNET_NETWORK_FDSet *ws;
+ struct GNUNET_TIME_Relative delay;
+
+ tc->bt = NULL;
+ TALER_BANK_perform (tc->bank);
+ max_fd = -1;
+ timeout = -1;
+ FD_ZERO (&read_fd_set);
+ FD_ZERO (&write_fd_set);
+ FD_ZERO (&except_fd_set);
+ TALER_BANK_get_select_info (tc->bank,
+ &read_fd_set,
+ &write_fd_set,
+ &except_fd_set,
+ &max_fd,
+ &timeout);
+ if (timeout >= 0)
+ delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
+ else
+ delay = GNUNET_TIME_UNIT_FOREVER_REL;
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (rs,
+ &read_fd_set,
+ max_fd + 1);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (ws,
+ &write_fd_set,
+ max_fd + 1);
+ tc->bt = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ delay,
+ rs,
+ ws,
+ &context_task,
+ cls);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ GNUNET_NETWORK_fdset_destroy (ws);
+}
+
+
+/**
+ * Run the bank task now.
+ *
+ * @param tc context for which we should initiate running the task
+ */
+static void
+run_bt (struct TestClosure *tc)
+{
+ if (NULL != tc->bt)
+ GNUNET_SCHEDULER_cancel (tc->bt);
+ tc->bt = GNUNET_SCHEDULER_add_now (&context_task,
+ tc);
+}
+
+
+/**
+ * Round amount DOWN to the amount that can be transferred via the wire
+ * method. For example, Taler may support 0.000001 EUR as a unit of
+ * payment, but SEPA only supports 0.01 EUR. This function would
+ * round 0.125 EUR to 0.12 EUR in this case.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param[in,out] amount amount to round down
+ * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
+ * #GNUNET_SYSERR if the amount or currency was invalid
+ */
+static int
+test_amount_round (void *cls,
+ struct TALER_Amount *amount)
+{
+ struct TestClosure *tc = cls;
+ uint32_t delta;
+
+ if (0 != strcasecmp (amount->currency,
+ tc->currency))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ /* 'test' method supports 1/100 of the unit currency, i.e. 0.01 CUR */
+ delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100);
+ if (0 == delta)
+ return GNUNET_NO;
+ amount->fraction -= delta;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Check if the given wire format JSON object is correctly formatted
+ *
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+static int
+test_wire_validate (const json_t *wire)
+{
+ GNUNET_break (0); /* FIXME: we still need to define the
+ proper wire format for 'test' */
+ return GNUNET_YES;
+}
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+/**
+ * Format we used for serialized transaction data.
+ */
+struct BufFormatP
+{
+
+ /**
+ * The wire transfer identifier.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * The amount.
+ */
+ struct TALER_AmountNBO amount;
+
+ /* followed by serialized 'wire' JSON data */
+
+};
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * Prepare for exeuction of a wire transfer. Calls the
+ * callback with the serialized state.
+ *
+ * @param cls the `struct TALER_WIRE_PrepareHandle`
+ * @param sct unused
+ */
+static void
+do_prepare (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *sct)
+{
+ struct TALER_WIRE_PrepareHandle *pth = cls;
+ char *wire_enc;
+ size_t len;
+ struct BufFormatP bf;
+
+ pth->task = NULL;
+ /* serialize the state into a 'buf' */
+ wire_enc = json_dumps (pth->wire,
+ JSON_COMPACT | JSON_SORT_KEYS);
+ if (NULL == wire_enc)
+ {
+ GNUNET_break (0);
+ pth->ptc (pth->ptc_cls,
+ NULL,
+ 0);
+ GNUNET_free (pth);
+ return;
+ }
+ len = strlen (wire_enc) + 1;
+ bf.wtid = pth->wtid;
+ TALER_amount_hton (&bf.amount,
+ &pth->amount);
+ {
+ char buf[sizeof (struct BufFormatP) + len];
+
+ memcpy (buf,
+ &bf,
+ sizeof (struct BufFormatP));
+ memcpy (&buf[sizeof (struct BufFormatP)],
+ wire_enc,
+ len);
+
+ /* finally give the state back */
+ pth->ptc (pth->ptc_cls,
+ buf,
+ sizeof (buf));
+ }
+ free (wire_enc); /* not using GNUNET_free(),
+ as this one is allocated by libjansson */
+ GNUNET_free (pth);
+}
+
+
+/**
+ * Prepare for exeuction of a wire transfer. Note that we should call
+ * @a ptc asynchronously (as that is what the API requires, because
+ * some transfer methods need it). So while we could immediately call
+ * @a ptc, we first bundle up all the data and schedule a task to do
+ * the work.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param wire valid wire account information
+ * @param amount amount to transfer, already rounded
+ * @param wtid wire transfer identifier to use
+ * @param ptc function to call with the prepared data to persist
+ * @param ptc_cls closure for @a ptc
+ * @return NULL on failure
+ */
+static struct TALER_WIRE_PrepareHandle *
+test_prepare_wire_transfer (void *cls,
+ const json_t *wire,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ TALER_WIRE_PrepareTransactionCallback ptc,
+ void *ptc_cls)
+{
+ struct TestClosure *tc = cls;
+ struct TALER_WIRE_PrepareHandle *pth;
+
+ if (GNUNET_YES !=
+ test_wire_validate (wire))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ pth = GNUNET_new (struct TALER_WIRE_PrepareHandle);
+ pth->tc = tc;
+ pth->wire = (json_t *) wire;
+ json_incref (pth->wire);
+ pth->wtid = *wtid;
+ pth->ptc = ptc;
+ pth->ptc_cls = ptc_cls;
+ pth->amount = *amount;
+ pth->task = GNUNET_SCHEDULER_add_now (&do_prepare,
+ pth);
+ return pth;
+}
+
+
+/**
+ * Abort preparation of a wire transfer. For example,
+ * because we are shutting down.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param pth preparation to cancel
+ */
+static void
+test_prepare_wire_transfer_cancel (void *cls,
+ struct TALER_WIRE_PrepareHandle *pth)
+{
+ GNUNET_SCHEDULER_cancel (pth->task);
+ json_decref (pth->wire);
+ GNUNET_free (pth);
+}
+
+
+/**
+ * Called with the result of submitting information about an incoming
+ * transaction to a bank.
+ *
+ * @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
+ * 0 if the bank's reply is bogus (fails to follow the protocol)
+ */
+static void
+execute_cb (void *cls,
+ unsigned int http_status)
+{
+ struct TALER_WIRE_ExecuteHandle *eh = cls;
+ char s[14];
+
+ eh->aaih = NULL;
+ GNUNET_snprintf (s,
+ sizeof (s),
+ "%u",
+ http_status);
+ eh->cc (eh->cc_cls,
+ (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
+ (MHD_HTTP_OK == http_status) ? NULL : s);
+ GNUNET_free (eh);
+}
+
+
+/**
+ * Execute a wire transfer.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param buf buffer with the prepared execution details
+ * @param buf_size number of bytes in @a buf
+ * @param cc function to call upon success
+ * @param cc_cls closure for @a cc
+ * @return NULL on error
+ */
+static struct TALER_WIRE_ExecuteHandle *
+test_execute_wire_transfer (void *cls,
+ const char *buf,
+ size_t buf_size,
+ TALER_WIRE_ConfirmationCallback cc,
+ void *cc_cls)
+{
+ struct TestClosure *tc = cls;
+ struct TALER_WIRE_ExecuteHandle *eh;
+ json_t *wire;
+ struct TALER_Amount amount;
+ struct BufFormatP bf;
+
+ if ( (buf_size <= sizeof (struct BufFormatP)) ||
+ ('\0' != buf[buf_size -1]) )
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ memcpy (&bf,
+ buf,
+ sizeof (bf));
+ TALER_amount_ntoh (&amount,
+ &bf.amount);
+ wire = json_loads (&buf[sizeof (struct BufFormatP)],
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ if (NULL == wire)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ GNUNET_assert (GNUNET_YES ==
+ test_wire_validate (wire));
+ eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
+ eh->cc = cc;
+ eh->cc_cls = cc_cls;
+ eh->aaih = TALER_BANK_admin_add_incoming (tc->bank,
+ &bf.wtid,
+ &amount,
+ wire,
+ &execute_cb,
+ eh);
+ json_decref (wire);
+ if (NULL == eh->aaih)
+ {
+ GNUNET_break (0);
+ GNUNET_free (eh);
+ return NULL;
+ }
+ run_bt (tc);
+ return eh;
+}
+
+
+/**
+ * Abort execution of a wire transfer. For example, because we are
+ * shutting down. Note that if an execution is aborted, it may or
+ * may not still succeed. The caller MUST run @e
+ * execute_wire_transfer again for the same request as soon as
+ * possilbe, to ensure that the request either ultimately succeeds
+ * or ultimately fails. Until this has been done, the transaction is
+ * in limbo (i.e. may or may not have been committed).
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param eh execution to cancel
+ */
+static void
+test_execute_wire_transfer_cancel (void *cls,
+ struct TALER_WIRE_ExecuteHandle *eh)
+{
+ TALER_BANK_admin_add_incoming_cancel (eh->aaih);
+ GNUNET_free (eh);
+}
+
+
+/**
+ * Initialize test-wire subsystem.
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
+ */
+void *
+libtaler_plugin_wire_test_init (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct TestClosure *tc;
+ struct TALER_WIRE_Plugin *plugin;
+ char *uri;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-test",
+ "bank_uri",
+ &uri))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "wire-test",
+ "bank_uri");
+ return NULL;
+ }
+ tc = GNUNET_new (struct TestClosure);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "CURRENCY",
+ &tc->currency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "CURRENCY");
+ GNUNET_free (uri);
+ GNUNET_free (tc);
+ return NULL;
+ }
+ tc->bank = TALER_BANK_init (uri);
+ if (NULL == tc->bank)
+ {
+ GNUNET_break (0);
+ GNUNET_free (tc->currency);
+ GNUNET_free (tc);
+ return NULL;
+ }
+
+ plugin = GNUNET_new (struct TALER_WIRE_Plugin);
+ plugin->cls = tc;
+ plugin->amount_round = &test_amount_round;
+ plugin->wire_validate = &test_wire_validate;
+ plugin->prepare_wire_transfer = &test_prepare_wire_transfer;
+ plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel;
+ plugin->execute_wire_transfer = &test_execute_wire_transfer;
+ plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel;
+ return plugin;
+}
+
+
+/**
+ * Shutdown Test wire subsystem.
+ *
+ * @param cls a `struct TALER_WIRE_Plugin`
+ * @return NULL (always)
+ */
+void *
+libtaler_plugin_wire_test_done (void *cls)
+{
+ struct TALER_WIRE_Plugin *plugin = cls;
+ struct TestClosure *tc = plugin->cls;
+
+ if (NULL != tc->bt)
+ {
+ GNUNET_SCHEDULER_cancel (tc->bt);
+ tc->bt = NULL;
+ }
+ TALER_BANK_fini (tc->bank);
+ GNUNET_free (tc->currency);
+ GNUNET_free (tc);
+ GNUNET_free (plugin);
+ return NULL;
+}
+
+/* end of plugin_wire_test.c */
diff --git a/src/util/test_wireformats.c b/src/wire/test_sepa_wireformat.c
index 7e0e5bbc4..edbe5bc45 100644
--- a/src/util/test_wireformats.c
+++ b/src/wire/test_sepa_wireformat.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014 Christian Grothoff (and other contributing authors)
+ (C) 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -15,14 +15,15 @@
*/
/**
- * @file util/test_wireformats.c
- * @brief Tests for JSON validations
+ * @file wire/test_sepa_wireformat.c
+ * @brief Tests for JSON SEPA format validation
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
#include "platform.h"
#include "taler_util.h"
-#include "taler_json_lib.h"
+#include "taler_wire_lib.h"
+
/* Valid SEPA data */
static const char * const valid_wire_str =
@@ -65,33 +66,39 @@ int
main(int argc,
const char *const argv[])
{
- const char *unsupported[] = {
- "unsupported",
- NULL
- };
- const char *sepa[] = {
- "SEPA",
- NULL
- };
json_t *wire;
json_error_t error;
int ret;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct TALER_WIRE_Plugin *plugin;
- GNUNET_log_setup ("test-json-validations", "WARNING", NULL);
+ GNUNET_log_setup ("test-sepa-wireformats",
+ "WARNING",
+ NULL);
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_string (cfg,
+ "mint",
+ "currency",
+ "EUR");
+ plugin = TALER_WIRE_plugin_load (cfg,
+ "sepa");
+ GNUNET_assert (NULL != plugin);
(void) memset(&error, 0, sizeof(error));
GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL)));
- GNUNET_assert (1 != TALER_json_validate_wireformat (unsupported, wire));
+ GNUNET_assert (GNUNET_YES != plugin->wire_validate (wire));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL)));
- GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire));
+ GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL)));
- GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire));
+ GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error)));
- ret = TALER_json_validate_wireformat (sepa, wire);
+ ret = plugin->wire_validate (wire);
json_decref (wire);
- if (1 == ret)
- return 0;
- return 1;
+ TALER_WIRE_plugin_unload (plugin);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ if (GNUNET_NO == ret)
+ return 1;
+ return 0;
}
diff --git a/src/wire/wire.c b/src/wire/wire.c
new file mode 100644
index 000000000..f0c0cd7a9
--- /dev/null
+++ b/src/wire/wire.c
@@ -0,0 +1,72 @@
+/*
+ This file is part of TALER
+ (C) 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file wire/wire.c
+ * @brief Functions for loading wire plugins
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_wire_lib.h"
+
+/**
+ * Load a WIRE plugin.
+ *
+ * @param cfg configuration to use
+ * @param plugin_name name of the plugin to load
+ * @return #GNUNET_OK on success
+ */
+struct TALER_WIRE_Plugin *
+TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *plugin_name)
+{
+ char *lib_name;
+ struct TALER_WIRE_Plugin *plugin;
+
+ (void) GNUNET_asprintf (&lib_name,
+ "libtaler_plugin_wire_%s",
+ plugin_name);
+ plugin = GNUNET_PLUGIN_load (lib_name,
+ (void *) cfg);
+ if (NULL != plugin)
+ plugin->library_name = lib_name;
+ else
+ GNUNET_free (lib_name);
+ return plugin;
+}
+
+
+/**
+ * Unload a WIRE plugin.
+ *
+ * @param plugin the plugin to unload
+ */
+void
+TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin)
+{
+ char *lib_name;
+
+ if (NULL == plugin)
+ return;
+ lib_name = plugin->library_name;
+ GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
+ plugin));
+ GNUNET_free (lib_name);
+}
+
+
+/* end of wire.c */