summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2015-08-06 17:34:02 +0200
committerMarcello Stanisci <marcello.stanisci@inria.fr>2015-08-06 17:34:02 +0200
commit3db6e952517da29135b1e0fd76bfeef8808f5a04 (patch)
treed7a3a8c089cef29c756fe78fe6e823d5d0ddce13
parent21d71d7eae49f63d82ee91db62e3b2f7fb739abf (diff)
downloadmerchant-3db6e952517da29135b1e0fd76bfeef8808f5a04.tar.gz
merchant-3db6e952517da29135b1e0fd76bfeef8808f5a04.tar.bz2
merchant-3db6e952517da29135b1e0fd76bfeef8808f5a04.zip
The tree has got back its original layout (i.e. getting rid of this
'melted' directory) and *a lot* of files from the mint's tree have been imported there. The main expectation is to cut off from these imported files as much as source code possible, which is actually not needed; although presumably, that will regard just a small percentage of that source code.
-rw-r--r--configure.ac4
-rw-r--r--src/Makefile.am2
-rw-r--r--src/backend/Makefile.am38
-rw-r--r--src/backend/merchant.conf18
-rw-r--r--src/backend/merchant_db.c84
-rw-r--r--src/backend/merchant_db.h11
-rw-r--r--src/backend/myconf.sh3
-rw-r--r--src/backend/taler-merchant-httpd.c667
-rw-r--r--src/backend/taler-mint-httpd.h127
-rw-r--r--src/backend/taler-mint-httpd_admin.c163
-rw-r--r--src/backend/taler-mint-httpd_admin.h46
-rw-r--r--src/backend/taler-mint-httpd_db.c1444
-rw-r--r--src/backend/taler-mint-httpd_db.h190
-rw-r--r--src/backend/taler-mint-httpd_deposit.c270
-rw-r--r--src/backend/taler-mint-httpd_deposit.h54
-rw-r--r--src/backend/taler-mint-httpd_keystate.c867
-rw-r--r--src/backend/taler-mint-httpd_keystate.h142
-rw-r--r--src/backend/taler-mint-httpd_mhd.c152
-rw-r--r--src/backend/taler-mint-httpd_mhd.h111
-rw-r--r--src/backend/taler-mint-httpd_parsing.c1123
-rw-r--r--src/backend/taler-mint-httpd_parsing.h408
-rw-r--r--src/backend/taler-mint-httpd_refresh.c907
-rw-r--r--src/backend/taler-mint-httpd_refresh.h94
-rw-r--r--src/backend/taler-mint-httpd_responses.c997
-rw-r--r--src/backend/taler-mint-httpd_responses.h390
-rw-r--r--src/backend/taler-mint-httpd_withdraw.c180
-rw-r--r--src/backend/taler-mint-httpd_withdraw.h73
-rw-r--r--src/backend/taler_amount_lib.h273
-rw-r--r--src/backend/taler_crypto_lib.h569
-rw-r--r--src/backend/taler_json_lib.h181
-rw-r--r--src/backend/taler_mintdb_lib.h224
-rw-r--r--src/backend/taler_mintdb_plugin.h1218
-rw-r--r--src/backend/taler_signatures.h653
-rw-r--r--src/backend/taler_util.h162
34 files changed, 11584 insertions, 261 deletions
diff --git a/configure.ac b/configure.ac
index 2792558f..400045be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -177,5 +177,7 @@ AC_TYPE_UINTMAX_T
# Checks for library functions.
AC_CHECK_FUNCS([strdup])
-AC_CONFIG_FILES([src/backend/Makefile])
+AC_CONFIG_FILES([Makefile
+src/Makefile
+src/backend/Makefile])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 0bd932c0..46fa8eba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,2 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/include
-SUBDIRS = include merchant
+SUBDIRS = include backend
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index f7afde8e..f068f774 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -1,24 +1,32 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
+# This Makefile.am is in the public domain
+# AM_CPPFLAGS = -I$(top_srcdir)/src/include
-MERCHANT_DB = merchant_db.c merchant_db.h
bin_PROGRAMS = \
- taler-merchant-httpd \
- taler-merchant-dbinit
+ taler-merchant-httpd
taler_merchant_httpd_SOURCES = \
- taler-merchant-httpd.c
-
-taler_merchant_dbinit_SOURCES = \
- taler-merchant-dbinit.c \
- $(MERCHANT_DB)
+ taler-merchant-httpd.c \
+ merchant.c merchant.h \
+ merchant_db.c merchant_db.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_deposit.c taler-mint-httpd_deposit.h \
+ taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \
+ taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h
taler_merchant_httpd_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ /home/marcello/trans_mint/src/util/libtalerutil.la \
+ /home/marcello/trans_mint/src/mintdb/libtalermintdb.la \
-lmicrohttpd \
- -lgnunetutil
-
-
-taler_merchant_dbinit_LDADD = \
- -ltalerpq \
+ -ljansson \
-lgnunetutil \
+ -ltalermint \
+ -ltalerpq \
-lgnunetpostgres \
- -lpq
+ -lpq \
+ -lpthread
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
new file mode 100644
index 00000000..3b637448
--- /dev/null
+++ b/src/backend/merchant.conf
@@ -0,0 +1,18 @@
+[merchant]
+PORT = 9966
+HOSTNAME = localhost
+TRUSTED_MINTS = taler
+KEYFILE = merchant.priv
+
+[mint-taler]
+HOSTNAME = demo.taler.net
+PORT = 80
+PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
+
+[merchant-db]
+CONFIG = postgres:///taler
+
+[wire-sepa]
+IBAN = DE67830654080004822650
+NAME = GNUNET E.V
+BIC = GENODEF1SRL
diff --git a/src/backend/merchant_db.c b/src/backend/merchant_db.c
index 66ab5bcf..274de25a 100644
--- a/src/backend/merchant_db.c
+++ b/src/backend/merchant_db.c
@@ -87,23 +87,24 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
(void) GNUNET_asprintf (&sql,
"BEGIN TRANSACTION;"
"CREATE %1$s TABLE IF NOT EXISTS contracts ("
- "transaction_id SERIAL8 PRIMARY KEY,"
- "amount INT4 NOT NULL,"
+ "contract_id INT8 PRIMARY KEY,"
+ "amount INT8 NOT NULL,"
"amount_fraction INT4 NOT NULL,"
+ "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL,"
"description TEXT NOT NULL,"
- "nounce BYTEA NOT NULL,"
+ "nounce INT8 NOT NULL,"
"expiry INT8 NOT NULL,"
"product INT8 NOT NULL);"
"CREATE %1$s TABLE IF NOT EXISTS checkouts ("
"coin_pub BYTEA PRIMARY KEY,"
- "transaction_id INT8 REFERENCES contracts(transaction_id),"
+ "contract_id INT8 REFERENCES contracts(contract_id),"
"amount INT4 NOT NULL,"
"amount_fraction INT4 NOT NULL,"
"coin_sig BYTEA NOT NULL);",
tmp_str);
ret = GNUNET_POSTGRES_exec (conn, sql);
(void) GNUNET_POSTGRES_exec (conn,
- (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK");
+ (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;");
GNUNET_free (sql);
if (GNUNET_OK != ret)
return ret;
@@ -118,11 +119,10 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
(conn,
"contract_create",
"INSERT INTO contracts"
- "(amount, amount_fraction, description,"
- "nounce, expiry, product) VALUES"
- "($1, $2, $3, $4, $5, $6)"
- "RETURNING transaction_id",
- 6, NULL)));
+ "(contract_id, amount, amount_fraction, amount_currency,"
+ "description, nounce, expiry, product) VALUES"
+ "($1, $2, $3, $4, $5, $6, $7, $8)",
+ 8, NULL)));
EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
PQclear (res);
@@ -133,7 +133,7 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
"product"
") FROM contracts "
"WHERE ("
- "transaction_id=$1"
+ "contract_id=$1"
")",
1, NULL)));
EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
@@ -144,7 +144,7 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
"checkout_create",
"INSERT INTO checkouts ("
"coin_pub,"
- "transaction_id,"
+ "contract_id,"
"amount,"
"amount_fraction,"
"coin_sig"
@@ -162,8 +162,8 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
"product"
") FROM contracts "
"WHERE "
- "transaction_id IN ("
- "SELECT (transaction_id) FROM checkouts "
+ "contract_id IN ("
+ "SELECT (contract_id) FROM checkouts "
"WHERE coin_pub=$1"
")",
1, NULL)));
@@ -189,61 +189,67 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
* @param conn the database connection
* @param expiry the time when the contract will expire
* @param amount the taler amount corresponding to the contract
+ * @param c_id contract's id
* @param desc descripition of the contract
* @param nounce a random 64-bit nounce
* @param product description to identify a product
- * @return -1 upon error; the serial id of the inserted contract upon success
+ * @return GNUNET_OK on success, GNUNET_SYSERR upon error
*/
-long long
+
+uint32_t
MERCHANT_DB_contract_create (PGconn *conn,
- struct GNUNET_TIME_Absolute expiry,
- struct TALER_Amount *amount,
+ const struct GNUNET_TIME_Absolute *expiry,
+ const struct TALER_Amount *amount,
+ uint64_t c_id,
const char *desc,
uint64_t nounce,
uint64_t product)
{
PGresult *res;
+ #if 0
uint64_t expiry_ms_nbo;
- uint32_t value_nbo;
+ uint64_t value_nbo;
uint32_t fraction_nbo;
uint64_t nounce_nbo;
+ #endif
ExecStatusType status;
- uint64_t id;
+
+ #if 0
+ /*
+ NOTE: the conversion to nl(l) happens *inside* the query param helpers; since
+ the policy imposes this format for storing values. */
+ value_nbo = GNUNET_htonll (amount->value);
+ fraction_nbo = GNUNET_htonll (amount->fraction);
+ nounce_nbo = GNUNET_htonll (nounce);
+ expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us);
+ product = GNUNET_htonll (product);
+ #endif
+
/* ported. To be tested/compiled */
struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint32 (&value_nbo),
- TALER_PQ_query_param_uint32 (&fraction_nbo),
+ TALER_PQ_query_param_uint64 (&c_id),
+ TALER_PQ_query_param_amount (amount),
/* a *string* is being put in the following statement,
though the API talks about a *blob*. Will this be liked by
the DB ? */
+ // the following inserts a string as a blob. Will Taler provide a param-from-string helper?
TALER_PQ_query_param_fixed_size (desc, strlen(desc)),
- TALER_PQ_query_param_uint64 (&nounce_nbo),
- TALER_PQ_query_param_uint64 (&expiry_ms_nbo),
+ TALER_PQ_query_param_uint64 (&nounce),
+ TALER_PQ_query_param_absolute_time (expiry),
TALER_PQ_query_param_uint64 (&product),
TALER_PQ_query_param_end
};
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("transaction_id", &id),
- TALER_PQ_result_spec_end
- };
-
- expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us);
- value_nbo = htonl (amount->value);
- fraction_nbo = htonl (amount->fraction);
- nounce_nbo = GNUNET_htonll (nounce);
- product = GNUNET_htonll (product);
+
/* NOTE: the statement is prepared by MERCHANT_DB_initialize function */
res = TALER_PQ_exec_prepared (conn, "contract_create", params);
status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
+ EXITIF (PGRES_COMMAND_OK != status);
PQclear (res);
- return GNUNET_ntohll ((uint64_t) id);
+ return GNUNET_OK;
EXITIF_exit:
PQclear (res);
- return -1;
+ return GNUNET_SYSERR;
}
long long
diff --git a/src/backend/merchant_db.h b/src/backend/merchant_db.h
index bf334989..a723b229 100644
--- a/src/backend/merchant_db.h
+++ b/src/backend/merchant_db.h
@@ -64,15 +64,18 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp);
* @param conn the database connection
* @param expiry the time when the contract will expire
* @param amount the taler amount corresponding to the contract
+ * @param c_id this contract's identification number
* @param desc descripition of the contract
* @param nounce a random 64-bit nounce
* @param product description to identify a product
- * @return -1 upon error; the serial id of the inserted contract upon success
+ * @return GNUNET_OK on success, GNUNET_SYSERR upon error
*/
-long long
+
+uint32_t
MERCHANT_DB_contract_create (PGconn *conn,
- struct GNUNET_TIME_Absolute expiry,
- struct TALER_Amount *amount,
+ const struct GNUNET_TIME_Absolute *expiry,
+ const struct TALER_Amount *amount,
+ uint64_t c_id,
const char *desc,
uint64_t nounce,
uint64_t product);
diff --git a/src/backend/myconf.sh b/src/backend/myconf.sh
new file mode 100644
index 00000000..0b7d2594
--- /dev/null
+++ b/src/backend/myconf.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./configure CFLAGS='-I/usr/include/postgresql'
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 6ca70937..46379809 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -22,9 +22,23 @@
#include "platform.h"
#include <microhttpd.h>
+#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
#include <taler/taler_json_lib.h>
-
+#include <taler/taler_mint_service.h>
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_admin.h"
+#include "taler-mint-httpd_deposit.h"
+#include "taler-mint-httpd_withdraw.h"
+#include "taler-mint-httpd_refresh.h"
+#include "taler-mint-httpd_keystate.h"
+#include "taler-mint-httpd_responses.h"
+#include "merchant.h"
+#include "merchant_db.h"
+
+extern struct MERCHANT_WIREFORMAT_Sepa *
+TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
/**
* Shorthand for exit jumps.
@@ -34,232 +48,168 @@
if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
} while (0)
-// task 1. Just implement a hello world server launched a` la GNUNET
-
/**
- * The port we are running on
+ * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
*/
-unsigned short port;
+#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000)
/**
- * The MHD Daemon
+ * Our hostname
*/
-static struct MHD_Daemon *mhd;
+static char *hostname;
/**
- * Shutdown task identifier
+ * The port we are running on
*/
-static struct GNUNET_SCHEDULER_Task *shutdown_task;
+static long long unsigned port;
/**
- * Should we do a dry run where temporary tables are used for storing the data.
+ * Merchant's private key
*/
-static int dry;
+struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
/**
- * Global return code
+ * The MHD Daemon
*/
-static int result;
-
-/** Beginning of JSON parse logic
-* Located here only for testing purposes since the service they provide is already
-* implemented in the mint's code; it just needs to be exported as a library. To be announced as a issue.
-*/
+static struct MHD_Daemon *mhd;
/**
- * Initial size for POST
- * request buffer.
+ * Connection handle to the our database
*/
-#define REQUEST_BUFFER_INITIAL 1024
+PGconn *db_conn;
/**
- * Maximum POST request size
+ * Which currency is used by this mint?
+ * (verbatim copy from mint's code, just to make this
+ * merchant's source compile)
*/
-#define REQUEST_BUFFER_MAX (1024*1024)
+char *TMH_mint_currency_string;
+
+/* As above */
+struct TALER_MINTDB_Plugin *TMH_plugin;
/**
- * Buffer for POST requests.
+ * As above, though the merchant does need some form of
+ * configuration
*/
-struct Buffer
-{
- /**
- * Allocated memory
- */
- char *data;
+struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Number of valid bytes in buffer.
- */
- size_t fill;
- /**
- * Number of allocated bytes in buffer.
- */
- size_t alloc;
-};
+/**
+ * As above
+ */
+int TMH_test_mode;
/**
- * Initialize a buffer.
- *
- * @param buf the buffer to initialize
- * @param data the initial data
- * @param data_size size of the initial data
- * @param alloc_size size of the buffer
- * @param max_size maximum size that the buffer can grow to
- * @return a GNUnet result code
+ * As above
*/
-static int
-buffer_init (struct Buffer *buf, const void *data, size_t data_size, size_t alloc_size, size_t max_size)
-{
- if (data_size > max_size || alloc_size > max_size)
- return GNUNET_SYSERR;
- if (data_size > alloc_size)
- alloc_size = data_size;
- buf->data = GNUNET_malloc (alloc_size);
- memcpy (buf->data, data, data_size);
- return GNUNET_OK;
-}
+char *TMH_mint_directory;
/**
- * Free the data in a buffer. Does *not* free
- * the buffer object itself.
- *
- * @param buf buffer to de-initialize
+ * As above
*/
-static void
-buffer_deinit (struct Buffer *buf)
-{
- GNUNET_free (buf->data);
- buf->data = NULL;
-}
+struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
+/**
+ * As above
+ */
+char *TMH_expected_wire_format;
/**
- * Append data to a buffer, growing the buffer if necessary.
- *
- * @param buf the buffer to append to
- * @param data the data to append
- * @param size the size of @a data
- * @param max_size maximum size that the buffer can grow to
- * @return GNUNET_OK on success,
- * GNUNET_NO if the buffer can't accomodate for the new data
- * GNUNET_SYSERR on fatal error (out of memory?)
+ * Shutdown task identifier
*/
-static int
-buffer_append (struct Buffer *buf, const void *data, size_t data_size, size_t max_size)
-{
- if (buf->fill + data_size > max_size)
- return GNUNET_NO;
- if (data_size + buf->fill > buf->alloc)
- {
- char *new_buf;
- size_t new_size = buf->alloc;
- while (new_size < buf->fill + data_size)
- new_size += 2;
- if (new_size > max_size)
- return GNUNET_NO;
- new_buf = GNUNET_malloc (new_size);
- memcpy (new_buf, buf->data, buf->fill);
- buf->data = new_buf;
- buf->alloc = new_size;
- }
- memcpy (buf->data + buf->fill, data, data_size);
- buf->fill += data_size;
- return GNUNET_OK;
-}
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+/**
+ * Our wireformat
+ */
+static struct MERCHANT_WIREFORMAT_Sepa *wire;
+/**
+ * Hash of the wireformat
+ */
+static struct GNUNET_HashCode h_wire;
/**
- * Process a POST request containing a JSON object.
- *
- * @param connection the MHD connection
- * @param con_cs the closure (contains a 'struct Buffer *')
- * @param upload_data the POST data
- * @param upload_data_size the POST data size
- * @param json the JSON object for a completed request
- *
- * @returns
- * GNUNET_YES if json object was parsed
- * GNUNET_NO is request incomplete or invalid
- * GNUNET_SYSERR on internal error
+ * Should we do a dry run where temporary tables are used for storing the data.
*/
-static int
-process_post_json (struct MHD_Connection *connection,
- void **con_cls,
- const char *upload_data,
- size_t *upload_data_size,
- json_t **json)
+static int dry;
+
+/**
+ * Global return code
+ */
+static int result;
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct Contract
{
- struct Buffer *r = *con_cls;
+ /**
+ * The signature of the merchant for this contract
+ */
+ struct GNUNET_CRYPTO_EddsaSignature sig;
- if (NULL == *con_cls)
- {
- /* We are seeing a fresh POST request. */
+ /**
+ * Purpose header for the signature over contract
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
- r = GNUNET_new (struct Buffer);
- if (GNUNET_OK != buffer_init (r, upload_data, *upload_data_size,
- REQUEST_BUFFER_INITIAL, REQUEST_BUFFER_MAX))
- {
- *con_cls = NULL;
- buffer_deinit (r);
- GNUNET_free (r);
- return GNUNET_SYSERR;
- }
- *upload_data_size = 0;
- *con_cls = r;
- return GNUNET_NO;
- }
- if (0 != *upload_data_size)
- {
- /* We are seeing an old request with more data available. */
+ /**
+ * The transaction identifier
+ */
+ char m[13];
- if (GNUNET_OK != buffer_append (r, upload_data, *upload_data_size,
- REQUEST_BUFFER_MAX))
- {
- /* Request too long or we're out of memory. */
+ /**
+ * Expiry time
+ */
+ struct GNUNET_TIME_AbsoluteNBO t;
- *con_cls = NULL;
- buffer_deinit (r);
- GNUNET_free (r);
- return GNUNET_SYSERR;
- }
- *upload_data_size = 0;
- return GNUNET_NO;
- }
+ /**
+ * The invoice amount
+ */
+ struct TALER_AmountNBO amount;
- /* We have seen the whole request. */
+ /**
+ * The hash of the preferred wire format + nounce
+ */
+ struct GNUNET_HashCode h_wire;
- *json = json_loadb (r->data, r->fill, 0, NULL);
- buffer_deinit (r);
- GNUNET_free (r);
- if (NULL == *json)
- {
- struct MHD_Response *resp;
- int ret;
-
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Can't parse JSON request body\n");
- resp = MHD_create_response_from_buffer (strlen ("parse error"),
- "parse error",
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_BAD_REQUEST,
- resp);
- MHD_destroy_response (resp);
- return ret;
- }
- *con_cls = NULL;
+ /**
+ * The contract data
+ */
+ char a[];
+};
- return GNUNET_YES;
-}
+GNUNET_NETWORK_STRUCT_END
+/**
+ * Mint context
+ */
+static struct TALER_MINT_Context *mctx;
-/* ************** END of JSON POST processing logic ************ */
+/**
+ * Context information of the mints we trust
+ */
+struct Mint
+{
+ /**
+ * Public key of this mint
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
+ /**
+ * Connection handle to this mint
+ */
+ struct TALER_MINT_Handle *conn;
+};
+/**
+ * Hashmap to store the mint context information
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
/**
* Return the given message to the other end of connection
@@ -296,7 +246,7 @@ static unsigned int
generate_hello (struct MHD_Response **resp) // this parameter was preceded by a '_' in its original file. Why?
{
- const char *hello = "Hello customer";
+ const char *hello = "Hello customer\n";
unsigned int ret;
*resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello,
@@ -307,6 +257,26 @@ generate_hello (struct MHD_Response **resp) // this parameter was preceded by a
}
+/**
+ * Callback for catching serious error conditions from MHD.
+ *
+ * @param cls user specified value
+ * @param file where the error occured
+ * @param line where the error occured
+ * @param reason error detail, may be NULL
+ */
+static void
+mhd_panic_cb (void *cls,
+ const char *file,
+ unsigned int line,
+ const char *reason)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "MHD panicked at %s:%u: %s",
+ file, line, reason);
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+}
/**
* Manage a non 200 HTTP status. I.e. it shows a 'failure' page to
@@ -320,7 +290,6 @@ generate_hello (struct MHD_Response **resp) // this parameter was preceded by a
static int
failure_resp (struct MHD_Connection *connection, unsigned int status)
{
- printf ("called failure mgmt\n");
static char page_404[]="\
<!DOCTYPE html> \
<html><title>Resource not found</title><body><center> \
@@ -365,6 +334,91 @@ request</h3></center></body></html>";
/**
+* Generate the hash containing the information (= a nounce + merchant's IBAN) to
+* redeem money from mint in a subsequent /deposit operation
+* @param nounce the nounce
+* @return the hash to be included in the contract's blob
+*
+*/
+
+static struct GNUNET_HashCode
+hash_wireformat (uint64_t nounce)
+{
+ struct GNUNET_HashContext *hc;
+ struct GNUNET_HashCode hash;
+
+ hc = GNUNET_CRYPTO_hash_context_start ();
+ GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban));
+ GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name));
+ GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic));
+ nounce = GNUNET_htonll (nounce);
+ GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce));
+ GNUNET_CRYPTO_hash_context_finish (hc, &hash);
+ return hash;
+}
+
+
+
+/*
+* Make a binary blob representing a contract, store it into the DB, sign it
+* and return a pointer to it.
+* @param a 0-terminated string representing the description of this
+* @param c_id contract id provided by the frontend
+* purchase (it should contain a human readable description of the good
+* in question)
+* @param product some product numerical id. Its indended use is to link the
+* good, or service being sold to some entry in the DB managed by the frontend
+* @price the cost of this good or service
+* @return pointer to the allocated contract (which has a field, 'sig', holding
+* its own signature), NULL upon errors
+*/
+
+struct Contract *
+generate_and_store_contract (const char *a, uint64_t c_id, uint64_t product, struct TALER_Amount *price)
+{
+
+ struct Contract *contract;
+ struct GNUNET_TIME_Absolute expiry;
+ uint64_t nounce;
+ uint64_t contract_id_nbo;
+
+ expiry = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+ GNUNET_TIME_UNIT_DAYS);
+ ROUND_TO_SECS (expiry, abs_value_us);
+ nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
+ EXITIF (GNUNET_SYSERR == MERCHANT_DB_contract_create (db_conn,
+ &expiry,
+ price,
+ c_id,
+ a,
+ nounce,
+ product));
+ contract_id_nbo = GNUNET_htonll ((uint64_t) c_id);
+ contract = GNUNET_malloc (sizeof (struct Contract) + strlen (a) + 1);
+ contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
+ contract->purpose.size = htonl (sizeof (struct Contract)
+ - offsetof (struct Contract, purpose)
+ + strlen (a) + 1);
+ GNUNET_STRINGS_data_to_string (&contract_id_nbo, sizeof (contract_id_nbo),
+ contract->m, sizeof (contract->m));
+ contract->t = GNUNET_TIME_absolute_hton (expiry);
+ (void) strcpy (contract->a, a);
+ contract->h_wire = hash_wireformat (nounce);
+ TALER_amount_hton (&contract->amount, price);
+ GNUNET_CRYPTO_eddsa_sign (privkey, &contract->purpose, &contract->sig);
+ return contract;
+
+ /* legacy from old merchant */
+ EXITIF_exit:
+ if (NULL != contract)
+ {
+ GNUNET_free (contract);
+ }
+ return NULL;
+}
+
+
+/**
* A client has requested the given url using the given method
* (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
* #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
@@ -412,19 +466,32 @@ url_handler (void *cls,
const char *version,
const char *upload_data,
size_t *upload_data_size,
- void **con_cls)
+ void **connection_cls)
{
unsigned int status;
unsigned int no_destroy;
+ json_int_t prod_id;
+ json_int_t contract_id;
+ struct Contract *contract;
struct MHD_Response *resp;
-
+ struct TALER_Amount price;
+ struct GNUNET_CRYPTO_EddsaPublicKey pub;
+ json_t *json_price;
+ json_t *root;
+ json_t *contract_enc;
+ json_t *sig_enc;
+ json_t *eddsa_pub_enc;
+ json_t *response;
+
+ int res;
+ const char *desc;
#define URL_HELLO "/hello"
#define URL_CONTRACT "/contract"
no_destroy = 0;
resp = NULL;
- status = 500;
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO)))
{
if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
@@ -436,24 +503,134 @@ url_handler (void *cls,
// to be called by the frontend passing all the product's information
// which are relevant for the contract's generation
if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
+ {
+ if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
+ status = generate_message (&resp, "Sorry, only POST is allowed");
+ else
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+
+ if (GNUNET_SYSERR == res)
{
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- status = generate_message (&resp, "Sorry, only POST is allowed");
- else
-
- /*
- 1. parse the json
- 2. generate the contract
- 3. pack the contract's json
- 4. return it
- */
-
- GNUNET_break (0);
-
+ status = generate_message (&resp, "unable to parse JSON root");
+ return MHD_NO;
}
+ if ((GNUNET_NO == res) || (NULL == root))
+ return MHD_YES;
+
+ /* The frontend should supply a JSON in the follwoing format:
+ {
+
+ "desc" : string human-readable describing this deal,
+ "product" : uint64-like integer referring to the product in some
+ DB adminstered by the frontend,
+ "cid" : uint64-like integer, this contract's id
+ "price" : a 'struct TALER_Amount' in the Taler compliant JSON format }
+
+ */
+
+ #if 0
+ /*res = json_typeof (root); <- seg fault*/
+ json_int_t id;
+ const char *desc_test;
+ const char *cur_test;
+ json_t *id_json;
+ json_t *desc_json;
+ json_t *cur_json;
+ id_json = json_object_get (root, "product");
+ desc_json = json_object_get (root, "desc");
+ id = json_integer_value (id_json);
+ desc_test = json_string_value (desc_json);
+ json_price = json_object_get (root, "price");
+ json_typeof (json_price);
+ cur_json = json_object_get (json_price, "currency");
+ cur_test = json_string_value (cur_json);
+ printf ("id is %" JSON_INTEGER_FORMAT "\n", id);
+ printf ("desc is %s\n", desc_test);
+ TALER_json_to_amount (json_price, &price);
+ printf ("cur_test is %s\n", price.currency);
+ json_error_t err;
+ if (res = json_unpack_ex (root, &err, JSON_VALIDATE_ONLY, "{s:s, s:I, s:o}",
+ "desc",
+ //&desc,
+ "product",
+ //&prod_id,
+ "price"//,
+ //json_price
+ ))
+ #else
+ if ((res = json_unpack (root, "{s:s, s:I, s:I, s:o}",
+ "desc",
+ &desc,
+ "product",
+ &prod_id,
+ "cid",
+ &contract_id,
+ "price",
+ &json_price
+ )))
+ #endif
+
+
+ /* still not possible to return a taler-compliant error message
+ since this JSON format is not among the taler officials ones */
+ {
+ status = generate_message (&resp, "unable to parse /contract JSON\n");
+ }
+ else
+ {
+ if (GNUNET_OK != TALER_json_to_amount (json_price, &price))
+ {/* still not possible to return a taler-compliant error message
+ since this JSON format is not among the taler officials ones */
+ status = generate_message (&resp, "unable to parse `price' field in /contract JSON");}
+ else
+ {
+ /* Let's generate this contract! */
+ if (NULL == (contract = generate_and_store_contract (desc, contract_id, prod_id, &price)))
+ {
+ /* status equals 500, so the user will get a "Internal server error" */
+ //failure_resp (connection, status);
+ status = generate_message (&resp, "unable to generate and store this contract");
+ //return MHD_YES;
+
+ }
+ else
+ {
+ json_decref (root);
+ json_decref (json_price);
+
+ printf ("Good contract\n");
+ /* the contract is good and stored in DB, produce now JSON to return.
+ As of now, the format is {"contract" : base32contract,
+ "sig" : contractSignature,
+ "eddsa_pub" : keyToCheckSignatureAgainst
+ }
+
+ */
+
+ sig_enc = TALER_json_from_eddsa_sig (&contract->purpose, &contract->sig);
+ GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
+ eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub));
+ /* cutting of the signature at the beginning */
+ contract_enc = TALER_json_from_data (&contract->purpose, sizeof (*contract)
+ - offsetof (struct Contract, purpose)
+ + strlen (desc) +1);
+ response = json_pack ("{s:o, s:o, s:o}", "contract", contract_enc, "sig", sig_enc,
+ "eddsa_pub", eddsa_pub_enc);
+ TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK);
+ printf ("Got something?\n");
+ return MHD_YES;
+ /* FRONTIER - CODE ABOVE STILL NOT TESTED */
+ }
+ }
+ }
+ }
if (NULL != resp)
{
@@ -461,15 +638,14 @@ url_handler (void *cls,
if (!no_destroy)
MHD_destroy_response (resp);
}
- else
- EXITIF (GNUNET_OK != failure_resp (connection, status));
- return MHD_YES;
-
- EXITIF_exit:
- result = GNUNET_SYSERR;
- //GNUNET_SCHEDULER_shutdown (); to a later stage, maybe
- return MHD_NO;
+ else
+ EXITIF (GNUNET_OK != failure_resp (connection, status));
+ return MHD_YES;
+ EXITIF_exit:
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return MHD_NO;
}
/**
@@ -488,10 +664,38 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
MHD_stop_daemon (mhd);
mhd = NULL;
}
+
+ if (NULL != db_conn)
+ {
+ MERCHANT_DB_disconnect (db_conn);
+ db_conn = NULL;
+ }
+
}
+
+/**
+ * Function called with information about who is auditing
+ * a particular mint and what key the mint is using.
+ *
+ * @param cls closure
+ * @param keys information about the various keys used
+ * by the mint
+ */
+void
+keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
+{
+ /* which kind of mint's keys a merchant should need? Sign
+ keys? It has already the mint's (master?) public key from
+ the conf file */
+ return;
+
+}
+
+
+
/**
* Main function that will be run by the scheduler.
*
@@ -505,10 +709,63 @@ run (void *cls, char *const *args, const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *config)
{
- port = 9966;
+ char *keyfile;
+ unsigned int nmints;
+ unsigned int cnt;
+ struct MERCHANT_MintInfo *mint_infos;
+ void *keys_mgmt_cls;
+ mint_infos = NULL;
+ keyfile = NULL;
+ result = GNUNET_SYSERR;
shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
&do_shutdown, NULL);
+ EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config,
+ &mint_infos)));
+ EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config)));
+ EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile));
+ EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config)));
+ EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_NO));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_number (config,
+ "merchant",
+ "port",
+ &port));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "hostname",
+ &hostname));
+
+ EXITIF (NULL == (mctx = TALER_MINT_init ()));
+ EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES)));
+
+ for (cnt = 0; cnt < nmints; cnt++)
+ {
+ struct Mint *mint;
+
+ mint = GNUNET_new (struct Mint);
+ mint->pubkey = mint_infos[cnt].pubkey;
+ /* port this to the new API */
+ mint->conn = TALER_MINT_connect (mctx,
+ mint_infos[cnt].hostname,
+ &keys_mgmt_cb,
+ keys_mgmt_cls); /*<- safe?segfault friendly?*/
+
+ /* NOTE: the keys mgmt callback should roughly do what the following lines do */
+ EXITIF (NULL == mint->conn);
+
+ EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put
+ (mints_map,
+ (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey,
+ mint,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
+ }
+
mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
port,
@@ -518,12 +775,17 @@ run (void *cls, char *const *args, const char *cfgfile,
EXITIF (NULL == mhd);
+ /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that
+ mandatory ? */
+ GNUNET_CRYPTO_hash (wire, sizeof (*wire), &h_wire);
result = GNUNET_OK;
EXITIF_exit:
if (GNUNET_OK != result)
GNUNET_SCHEDULER_shutdown ();
-
+ GNUNET_free_non_null (keyfile);
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
}
@@ -539,7 +801,10 @@ main (int argc, char *const *argv)
{
static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_END
+ {'t', "temp", NULL,
+ gettext_noop ("Use temporary database tables"), GNUNET_NO,
+ &GNUNET_GETOPT_set_one, &dry},
+ GNUNET_GETOPT_OPTION_END
};
diff --git a/src/backend/taler-mint-httpd.h b/src/backend/taler-mint-httpd.h
new file mode 100644
index 00000000..a54e5aa2
--- /dev/null
+++ b/src/backend/taler-mint-httpd.h
@@ -0,0 +1,127 @@
+/*
+ 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 taler-mint-httpd.h
+ * @brief Global declarations for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ *
+ * FIXME: Consider which of these need to really be globals...
+ */
+#ifndef TALER_MINT_HTTPD_H
+#define TALER_MINT_HTTPD_H
+
+#include <microhttpd.h>
+
+/**
+ * Which currency is used by this mint?
+ */
+extern char *TMH_mint_currency_string;
+
+/**
+ * The mint's configuration.
+ */
+extern struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Are we running in test mode?
+ */
+extern int TMH_test_mode;
+
+/**
+ * Main directory with mint data.
+ */
+extern char *TMH_mint_directory;
+
+/**
+ * In which format does this MINT expect wiring instructions?
+ */
+extern char *TMH_expected_wire_format;
+
+/**
+ * Master public key (according to the
+ * configuration in the mint directory).
+ */
+extern struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
+
+/**
+ * Private key of the mint we use to sign messages.
+ */
+extern struct GNUNET_CRYPTO_EddsaPrivateKey TMH_mint_private_signing_key;
+
+/**
+ * Our DB plugin.
+ */
+extern struct TALER_MINTDB_Plugin *TMH_plugin;
+
+
+/**
+ * @brief Struct describing an URL and the handler for it.
+ */
+struct TMH_RequestHandler
+{
+
+ /**
+ * URL the handler is for.
+ */
+ const char *url;
+
+ /**
+ * Method the handler is for, NULL for "all".
+ */
+ const char *method;
+
+ /**
+ * Mime type to use in reply (hint, can be NULL).
+ */
+ const char *mime_type;
+
+ /**
+ * Raw data for the @e handler
+ */
+ const void *data;
+
+ /**
+ * Number of bytes in @e data, 0 for 0-terminated.
+ */
+ size_t data_size;
+
+ /**
+ * Function to call to handle the request.
+ *
+ * @param rh this struct
+ * @param mime_type the @e mime_type for the reply (hint, can be NULL)
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+ int (*handler)(struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+ /**
+ * Default response code.
+ */
+ int response_code;
+};
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_admin.c b/src/backend/taler-mint-httpd_admin.c
new file mode 100644
index 00000000..5fdfa58e
--- /dev/null
+++ b/src/backend/taler-mint-httpd_admin.c
@@ -0,0 +1,163 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_admin.c
+ * @brief Handle /admin/ requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler-mint-httpd_admin.h"
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_responses.h"
+
+
+/**
+ * Check permissions (we only allow access to /admin/ from loopback).
+ *
+ * @param connection connection to perform access check for
+ * @return #GNUNET_OK if permitted,
+ * #GNUNET_NO if denied and error was queued,
+ * #GNUNET_SYSERR if denied and we failed to report
+ */
+static int
+check_permissions (struct MHD_Connection *connection)
+{
+ const union MHD_ConnectionInfo *ci;
+ const struct sockaddr *addr;
+ int res;
+
+ ci = MHD_get_connection_info (connection,
+ MHD_CONNECTION_INFO_CLIENT_ADDRESS);
+ if (NULL == ci)
+ {
+ GNUNET_break (0);
+ res = TMH_RESPONSE_reply_internal_error (connection,
+ "Failed to verify client address");
+ return (MHD_YES == res) ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ addr = ci->client_addr;
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ {
+ const struct sockaddr_in *sin = (const struct sockaddr_in *) addr;
+
+ if (INADDR_LOOPBACK != ntohl (sin->sin_addr.s_addr))
+ {
+ res = TMH_RESPONSE_reply_permission_denied (connection,
+ "/admin/ only allowed via loopback");
+ return (MHD_YES == res) ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ break;
+ }
+ case AF_INET6:
+ {
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *) addr;
+
+ if (! IN6_IS_ADDR_LOOPBACK (&sin6->sin6_addr))
+ {
+ res = TMH_RESPONSE_reply_permission_denied (connection,
+ "/admin/ only allowed via loopback");
+ return (MHD_YES == res) ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ break;
+ }
+ default:
+ GNUNET_break (0);
+ res = TMH_RESPONSE_reply_internal_error (connection,
+ "Unsupported AF");
+ return (MHD_YES == res) ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+
+/**
+ * Handle a "/admin/add/incoming" request. Parses the
+ * given "reserve_pub", "amount", "transaction" and "h_wire"
+ * details and adds the respective transaction to the database.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_Amount amount;
+ struct GNUNET_TIME_Absolute at;
+ json_t *wire;
+ json_t *root;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_fixed ("reserve_pub", &reserve_pub),
+ TMH_PARSE_member_amount ("amount", &amount),
+ TMH_PARSE_member_time_abs ("execution_date", &at),
+ TMH_PARSE_member_object ("wire", &wire),
+ TMH_PARSE_MEMBER_END
+ };
+ int res;
+
+ res = check_permissions (connection);
+ if (GNUNET_OK != res)
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == root) )
+ return MHD_YES;
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ if (GNUNET_OK != res)
+ {
+ json_decref (root);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ if (GNUNET_YES !=
+ TALER_json_validate_wireformat (TMH_expected_wire_format,
+ wire))
+ {
+ TMH_PARSE_release_data (spec);
+ json_decref (root);
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "wire");
+ }
+ res = TMH_DB_execute_admin_add_incoming (connection,
+ &reserve_pub,
+ &amount,
+ at,
+ wire);
+ TMH_PARSE_release_data (spec);
+ json_decref (root);
+ return res;
+}
+
+/* end of taler-mint-httpd_admin.c */
diff --git a/src/backend/taler-mint-httpd_admin.h b/src/backend/taler-mint-httpd_admin.h
new file mode 100644
index 00000000..b8ca3ce5
--- /dev/null
+++ b/src/backend/taler-mint-httpd_admin.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_admin.h
+ * @brief Handle /admin/ requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_ADMIN_H
+#define TALER_MINT_HTTPD_ADMIN_H
+
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+/**
+ * Handle a "/admin/add/incoming" request. Parses the
+ * given "reserve_pub", "amount", "transaction" and "h_wire"
+ * details and adds the respective transaction to the database.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/backend/taler-mint-httpd_db.c b/src/backend/taler-mint-httpd_db.c
new file mode 100644
index 00000000..4e91e7e7
--- /dev/null
+++ b/src/backend/taler-mint-httpd_db.c
@@ -0,0 +1,1444 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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 taler-mint-httpd_db.c
+ * @brief High-level (transactional-layer) database operations for the mint.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <pthread.h>
+#include <jansson.h>
+#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd_keystate.h"
+
+
+/**
+ * Calculate the total value of all transactions performed.
+ * Stores @a off plus the cost of all transactions in @a tl
+ * in @a ret.
+ *
+ * @param tl transaction list to process
+ * @param off offset to use as the starting value
+ * @param ret where the resulting total is to be stored
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+calculate_transaction_list_totals (struct TALER_MINTDB_TransactionList *tl,
+ const struct TALER_Amount *off,
+ struct TALER_Amount *ret)
+{
+ struct TALER_Amount spent = *off;
+ struct TALER_MINTDB_TransactionList *pos;
+
+ for (pos = tl; NULL != pos; pos = pos->next)
+ {
+ switch (pos->type)
+ {
+ case TALER_MINTDB_TT_DEPOSIT:
+ if (GNUNET_OK !=
+ TALER_amount_add (&spent,
+ &spent,
+ &pos->details.deposit->amount_with_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ break;
+ case TALER_MINTDB_TT_REFRESH_MELT:
+ if (GNUNET_OK !=
+ TALER_amount_add (&spent,
+ &spent,
+ &pos->details.melt->amount_with_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ break;
+ case TALER_MINTDB_TT_LOCK:
+ /* should check if lock is still active,
+ and if it is for THIS operation; if
+ lock is inactive, delete it; if lock
+ is for THIS operation, ignore it;
+ if lock is for another operation,
+ count it! */
+ GNUNET_assert (0); // FIXME: not implemented! (#3625)
+ return GNUNET_SYSERR;
+ }
+ }
+ *ret = spent;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Execute a deposit. The validity of the coin and signature
+ * have already been checked. The database must now check that
+ * the coin is not (double or over) spent, and execute the
+ * transaction (record details, generate success or failure response).
+ *
+ * @param connection the MHD connection to handle
+ * @param deposit information about the deposit
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_deposit (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_Deposit *deposit)
+{
+ struct TALER_MINTDB_Session *session;
+ struct TALER_MINTDB_TransactionList *tl;
+ struct TALER_Amount spent;
+ struct TALER_Amount value;
+ struct TALER_Amount amount_without_fee;
+ struct TMH_KS_StateHandle *mks;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ int ret;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ if (GNUNET_YES ==
+ TMH_plugin->have_deposit (TMH_plugin->cls,
+ session,
+ deposit))
+ {
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_subtract (&amount_without_fee,
+ &deposit->amount_with_fee,
+ &deposit->deposit_fee));
+ return TMH_RESPONSE_reply_deposit_success (connection,
+ &deposit->coin.coin_pub,
+ &deposit->h_wire,
+ &deposit->h_contract,
+ deposit->transaction_id,
+ deposit->timestamp,
+ deposit->refund_deadline,
+ &deposit->merchant_pub,
+ &amount_without_fee);
+ }
+ mks = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (mks,
+ &deposit->coin.denom_pub,
+ TMH_KS_DKU_DEPOSIT);
+ TALER_amount_ntoh (&value,
+ &dki->issue.properties.value);
+ TMH_KS_release (mks);
+
+ if (GNUNET_OK !=
+ TMH_plugin->start (TMH_plugin->cls,
+ session))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ /* fee for THIS transaction */
+ spent = deposit->amount_with_fee;
+ /* add cost of all previous transactions */
+ tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls,
+ session,
+ &deposit->coin.coin_pub);
+ if (GNUNET_OK !=
+ calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
+ {
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ /* Check that cost of all transactions is smaller than
+ the value of the coin. */
+ if (0 < TALER_amount_cmp (&spent,
+ &value))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ ret = TMH_RESPONSE_reply_deposit_insufficient_funds (connection,
+ tl);
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return ret;
+ }
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+
+ if (GNUNET_OK !=
+ TMH_plugin->insert_deposit (TMH_plugin->cls,
+ session,
+ deposit))
+ {
+ TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ if (GNUNET_OK !=
+ TMH_plugin->commit (TMH_plugin->cls,
+ session))
+ {
+ TALER_LOG_WARNING ("/deposit transaction commit failed\n");
+ return TMH_RESPONSE_reply_commit_error (connection);
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_subtract (&amount_without_fee,
+ &deposit->amount_with_fee,
+ &deposit->deposit_fee));
+ return TMH_RESPONSE_reply_deposit_success (connection,
+ &deposit->coin.coin_pub,
+ &deposit->h_wire,
+ &deposit->h_contract,
+ deposit->transaction_id,
+ deposit->timestamp,
+ deposit->refund_deadline,
+ &deposit->merchant_pub,
+ &amount_without_fee);
+}
+
+
+/**
+ * Execute a /withdraw/status. Given the public key of a reserve,
+ * return the associated transaction history.
+ *
+ * @param connection the MHD connection to handle
+ * @param reserve_pub public key of the reserve to check
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_withdraw_status (struct MHD_Connection *connection,
+ const struct TALER_ReservePublicKeyP *reserve_pub)
+{
+ struct TALER_MINTDB_Session *session;
+ struct TALER_MINTDB_ReserveHistory *rh;
+ int res;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ rh = TMH_plugin->get_reserve_history (TMH_plugin->cls,
+ session,
+ reserve_pub);
+ if (NULL == rh)
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s, s:s}",
+ "error", "Reserve not found",
+ "parameter", "withdraw_pub");
+ res = TMH_RESPONSE_reply_withdraw_status_success (connection,
+ rh);
+ TMH_plugin->free_reserve_history (TMH_plugin->cls,
+ rh);
+ return res;
+}
+
+
+/**
+ * Execute a "/withdraw/sign". Given a reserve and a properly signed
+ * request to withdraw a coin, check the balance of the reserve and
+ * if it is sufficient, store the request and return the signed
+ * blinded envelope.
+ *
+ * @param connection the MHD connection to handle
+ * @param reserve public key of the reserve
+ * @param denomination_pub public key of the denomination requested
+ * @param blinded_msg blinded message to be signed
+ * @param blinded_msg_len number of bytes in @a blinded_msg
+ * @param signature signature over the withdraw request, to be stored in DB
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
+ const struct TALER_ReservePublicKeyP *reserve,
+ const struct TALER_DenominationPublicKey *denomination_pub,
+ const char *blinded_msg,
+ size_t blinded_msg_len,
+ const struct TALER_ReserveSignatureP *signature)
+{
+ struct TALER_MINTDB_Session *session;
+ struct TALER_MINTDB_ReserveHistory *rh;
+ const struct TALER_MINTDB_ReserveHistory *pos;
+ struct TMH_KS_StateHandle *key_state;
+ struct TALER_MINTDB_CollectableBlindcoin collectable;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *tdki;
+ struct GNUNET_CRYPTO_rsa_Signature *sig;
+ struct TALER_Amount amount_required;
+ struct TALER_Amount deposit_total;
+ struct TALER_Amount withdraw_total;
+ struct TALER_Amount balance;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct GNUNET_HashCode h_blind;
+ int res;
+
+ GNUNET_CRYPTO_hash (blinded_msg,
+ blinded_msg_len,
+ &h_blind);
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ res = TMH_plugin->get_withdraw_info (TMH_plugin->cls,
+ session,
+ &h_blind,
+ &collectable);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ /* Don't sign again if we have already signed the coin */
+ if (GNUNET_YES == res)
+ {
+ res = TMH_RESPONSE_reply_withdraw_sign_success (connection,
+ &collectable);
+ GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature);
+ GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key);
+ return res;
+ }
+ GNUNET_assert (GNUNET_NO == res);
+
+ /* Check if balance is sufficient */
+ key_state = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (key_state,
+ denomination_pub,
+ TMH_KS_DKU_WITHDRAW);
+ if (NULL == dki)
+ {
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error",
+ "Denomination not found");
+ }
+ if (GNUNET_OK !=
+ TMH_plugin->start (TMH_plugin->cls,
+ session))
+ {
+ GNUNET_break (0);
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ rh = TMH_plugin->get_reserve_history (TMH_plugin->cls,
+ session,
+ reserve);
+ if (NULL == rh)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "reserve_pub");
+ }
+
+ /* calculate amount required including fees */
+ TALER_amount_ntoh (&value,
+ &dki->issue.properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &dki->issue.properties.fee_withdraw);
+
+ if (GNUNET_OK !=
+ TALER_amount_add (&amount_required,
+ &value,
+ &fee_withdraw))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ /* calculate balance of the reserve */
+ res = 0;
+ for (pos = rh; NULL != pos; pos = pos->next)
+ {
+ switch (pos->type)
+ {
+ case TALER_MINTDB_RO_BANK_TO_MINT:
+ if (0 == (res & 1))
+ deposit_total = pos->details.bank->amount;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&deposit_total,
+ &deposit_total,
+ &pos->details.bank->amount))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ res |= 1;
+ break;
+ case TALER_MINTDB_RO_WITHDRAW_COIN:
+ tdki = TMH_KS_denomination_key_lookup (key_state,
+ &pos->details.withdraw->denom_pub,
+ TMH_KS_DKU_WITHDRAW);
+ TALER_amount_ntoh (&value,
+ &tdki->issue.properties.value);
+ if (0 == (res & 2))
+ withdraw_total = value;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&withdraw_total,
+ &withdraw_total,
+ &value))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ res |= 2;
+ break;
+ }
+ }
+ if (0 == (res & 1))
+ {
+ /* did not encounter any deposit operations, how can we have a reserve? */
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ if (0 == (res & 2))
+ {
+ /* did not encounter any withdraw operations, set to zero */
+ TALER_amount_get_zero (deposit_total.currency,
+ &withdraw_total);
+ }
+ /* All reserve balances should be non-negative */
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&balance,
+ &deposit_total,
+ &withdraw_total));
+ if (0 < TALER_amount_cmp (&amount_required,
+ &balance))
+ {
+ TMH_KS_release (key_state);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ res = TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (connection,
+ rh);
+ TMH_plugin->free_reserve_history (TMH_plugin->cls,
+ rh);
+ return res;
+ }
+ TMH_plugin->free_reserve_history (TMH_plugin->cls,
+ rh);
+
+ /* Balance is good, sign the coin! */
+ sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key,
+ blinded_msg,
+ blinded_msg_len);
+ TMH_KS_release (key_state);
+ if (NULL == sig)
+ {
+ GNUNET_break (0);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "Internal error");
+ }
+ collectable.sig.rsa_signature = sig;
+ collectable.denom_pub = *denomination_pub;
+ collectable.amount_with_fee = amount_required;
+ collectable.withdraw_fee = fee_withdraw;
+ collectable.reserve_pub = *reserve;
+ collectable.h_coin_envelope = h_blind;
+ collectable.reserve_sig = *signature;
+ if (GNUNET_OK !=
+ TMH_plugin->insert_withdraw_info (TMH_plugin->cls,
+ session,
+ &collectable))
+ {
+ GNUNET_break (0);
+ GNUNET_CRYPTO_rsa_signature_free (sig);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ if (GNUNET_OK !=
+ TMH_plugin->commit (TMH_plugin->cls,
+ session))
+ {
+ TALER_LOG_WARNING ("/withdraw/sign transaction commit failed\n");
+ return TMH_RESPONSE_reply_commit_error (connection);
+ }
+ res = TMH_RESPONSE_reply_withdraw_sign_success (connection,
+ &collectable);
+ GNUNET_CRYPTO_rsa_signature_free (sig);
+ return res;
+}
+
+
+/**
+ * Parse coin melt requests from a JSON object and write them to
+ * the database.
+ *
+ * @param connection the connection to send errors to
+ * @param session the database connection
+ * @param key_state the mint's key state
+ * @param session_hash hash identifying the refresh session
+ * @param coin_details details about the coin being melted
+ * @param oldcoin_index what is the number assigned to this coin
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO if an error message was generated,
+ * #GNUNET_SYSERR on internal errors (no response generated)
+ */
+static int
+refresh_accept_melts (struct MHD_Connection *connection,
+ struct TALER_MINTDB_Session *session,
+ const struct TMH_KS_StateHandle *key_state,
+ const struct GNUNET_HashCode *session_hash,
+ const struct TMH_DB_MeltDetails *coin_details,
+ uint16_t oldcoin_index)
+{
+ struct TALER_MINTDB_DenominationKeyInformationP *dki;
+ struct TALER_MINTDB_TransactionList *tl;
+ struct TALER_Amount coin_value;
+ struct TALER_Amount coin_residual;
+ struct TALER_Amount spent;
+ struct TALER_MINTDB_RefreshMelt melt;
+ int res;
+
+ dki = &TMH_KS_denomination_key_lookup (key_state,
+ &coin_details->coin_info.denom_pub,
+ TMH_KS_DKU_DEPOSIT)->issue;
+
+ if (NULL == dki)
+ return (MHD_YES ==
+ TMH_RESPONSE_reply_arg_unknown (connection,
+ "denom_pub"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+
+ TALER_amount_ntoh (&coin_value,
+ &dki->properties.value);
+ /* fee for THIS transaction; the melt amount includes the fee! */
+ spent = coin_details->melt_amount_with_fee;
+ /* add historic transaction costs of this coin */
+ tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls,
+ session,
+ &coin_details->coin_info.coin_pub);
+ if (GNUNET_OK !=
+ calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
+ {
+ GNUNET_break (0);
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ /* Refuse to refresh when the coin's value is insufficient
+ for the cost of all transactions. */
+ if (TALER_amount_cmp (&coin_value,
+ &spent) < 0)
+ {
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_subtract (&coin_residual,
+ &spent,
+ &coin_details->melt_amount_with_fee));
+ res = (MHD_YES ==
+ TMH_RESPONSE_reply_refresh_melt_insufficient_funds (connection,
+ &coin_details->coin_info.coin_pub,
+ coin_value,
+ tl,
+ coin_details->melt_amount_with_fee,
+ coin_residual))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+ return res;
+ }
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
+ tl);
+
+ melt.coin = coin_details->coin_info;
+ melt.coin_sig = coin_details->melt_sig;
+ melt.session_hash = *session_hash;
+ melt.amount_with_fee = coin_details->melt_amount_with_fee;
+ if (GNUNET_OK !=
+ TMH_plugin->insert_refresh_melt (TMH_plugin->cls,
+ session,
+ oldcoin_index,
+ &melt))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Execute a "/refresh/melt". We have been given a list of valid
+ * coins and a request to melt them into the given
+ * @a refresh_session_pub. Check that the coins all have the
+ * required value left and if so, store that they have been
+ * melted and confirm the melting operation to the client.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash code of the session the coins are melted into
+ * @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @a commit_coin array
+ * @param denom_pubs public keys of the coins we want to withdraw in the end
+ * @param coin_count number of entries in @a coin_melt_details, size of y-dimension of @a commit_link array
+ * @param coin_melt_details signatures and (residual) value of the respective coin should be melted
+ * @param commit_coin 2d array of coin commitments (what the mint is to sign
+ * once the "/refres/reveal" of cut and choose is done),
+ * x-dimension must be #TALER_CNC_KAPPA
+ * @param commit_link 2d array of coin link commitments (what the mint is
+ * to return via "/refresh/link" to enable linkage in the
+ * future)
+ * x-dimension must be #TALER_CNC_KAPPA
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int num_new_denoms,
+ const struct TALER_DenominationPublicKey *denom_pubs,
+ unsigned int coin_count,
+ const struct TMH_DB_MeltDetails *coin_melt_details,
+ struct TALER_MINTDB_RefreshCommitCoin *const* commit_coin,
+ struct TALER_MINTDB_RefreshCommitLinkP *const* commit_link)
+{
+ struct TMH_KS_StateHandle *key_state;
+ struct TALER_MINTDB_RefreshSession refresh_session;
+ struct TALER_MINTDB_Session *session;
+ int res;
+ unsigned int i;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ if (GNUNET_OK !=
+ TMH_plugin->start (TMH_plugin->cls,
+ session))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ res = TMH_plugin->get_refresh_session (TMH_plugin->cls,
+ session,
+ session_hash,
+ &refresh_session);
+ if (GNUNET_YES == res)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ res = TMH_RESPONSE_reply_refresh_melt_success (connection,
+ session_hash,
+ refresh_session.noreveal_index);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ if (GNUNET_SYSERR == res)
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ /* store 'global' session data */
+ refresh_session.num_oldcoins = coin_count;
+ refresh_session.num_newcoins = num_new_denoms;
+ refresh_session.noreveal_index
+ = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+ TALER_CNC_KAPPA);
+ if (GNUNET_OK !=
+ (res = TMH_plugin->create_refresh_session (TMH_plugin->cls,
+ session,
+ session_hash,
+ &refresh_session)))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ /* Melt old coins and check that they had enough residual value */
+ key_state = TMH_KS_acquire ();
+ for (i=0;i<coin_count;i++)
+ {
+ if (GNUNET_OK !=
+ (res = refresh_accept_melts (connection,
+ session,
+ key_state,
+ session_hash,
+ &coin_melt_details[i],
+ i)))
+ {
+ TMH_KS_release (key_state);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ }
+ TMH_KS_release (key_state);
+
+ /* store requested new denominations */
+ if (GNUNET_OK !=
+ TMH_plugin->insert_refresh_order (TMH_plugin->cls,
+ session,
+ session_hash,
+ num_new_denoms,
+ denom_pubs))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ for (i = 0; i < TALER_CNC_KAPPA; i++)
+ {
+ if (GNUNET_OK !=
+ TMH_plugin->insert_refresh_commit_coins (TMH_plugin->cls,
+ session,
+ session_hash,
+ i,
+ num_new_denoms,
+ commit_coin[i]))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ }
+ for (i = 0; i < TALER_CNC_KAPPA; i++)
+ {
+ if (GNUNET_OK !=
+ TMH_plugin->insert_refresh_commit_links (TMH_plugin->cls,
+ session,
+ session_hash,
+ i,
+ coin_count,
+ commit_link[i]))
+ {
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ }
+
+ if (GNUNET_OK !=
+ TMH_plugin->commit (TMH_plugin->cls,
+ session))
+ {
+ TALER_LOG_WARNING ("/refresh/melt transaction commit failed\n");
+ return TMH_RESPONSE_reply_commit_error (connection);
+ }
+ return TMH_RESPONSE_reply_refresh_melt_success (connection,
+ session_hash,
+ refresh_session.noreveal_index);
+}
+
+
+/**
+ * Send an error response with the details of the original melt
+ * commitment and the location of the mismatch.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param off commitment offset to check
+ * @param index index of the mismatch
+ * @param object_name name of the object with the problem
+ * @return #GNUNET_NO if we generated the error message
+ * #GNUNET_SYSERR if we could not even generate an error message
+ */
+static int
+send_melt_commitment_error (struct MHD_Connection *connection,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int off,
+ unsigned int index,
+ const char *object_name)
+{
+ struct TALER_MINTDB_MeltCommitment *mc;
+ int ret;
+
+ mc = TMH_plugin->get_melt_commitment (TMH_plugin->cls,
+ session,
+ session_hash);
+ if (NULL == mc)
+ {
+ GNUNET_break (0);
+ return (MHD_YES ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "Melt commitment assembly"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_refresh_reveal_missmatch (connection,
+ mc,
+ off,
+ index,
+ object_name))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ TMH_plugin->free_melt_commitment (TMH_plugin->cls,
+ mc);
+ return ret;
+}
+
+
+/**
+ * Check if the given @a transfer_privs correspond to an honest
+ * commitment for the given session.
+ * Checks that the transfer private keys match their commitments.
+ * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that they match.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param off commitment offset to check
+ * @param num_oldcoins size of the @a transfer_privs and @a melts arrays
+ * @param transfer_privs private transfer keys
+ * @param melts array of melted coins
+ * @param num_newcoins number of newcoins being generated
+ * @param denom_pubs array of @a num_newcoins keys for the new coins
+ * @return #GNUNET_OK if the committment was honest,
+ * #GNUNET_NO if there was a problem and we generated an error message
+ * #GNUNET_SYSERR if we could not even generate an error message
+ */
+static int
+check_commitment (struct MHD_Connection *connection,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int off,
+ unsigned int num_oldcoins,
+ const struct TALER_TransferPrivateKeyP *transfer_privs,
+ const struct TALER_MINTDB_RefreshMelt *melts,
+ unsigned int num_newcoins,
+ const struct TALER_DenominationPublicKey *denom_pubs)
+{
+ unsigned int j;
+ struct TALER_LinkSecretP last_shared_secret;
+ int secret_initialized = GNUNET_NO;
+ struct TALER_MINTDB_RefreshCommitLinkP *commit_links;
+ struct TALER_MINTDB_RefreshCommitCoin *commit_coins;
+
+ commit_links = GNUNET_malloc (num_oldcoins *
+ sizeof (struct TALER_MINTDB_RefreshCommitLinkP));
+ if (GNUNET_OK !=
+ TMH_plugin->get_refresh_commit_links (TMH_plugin->cls,
+ session,
+ session_hash,
+ off,
+ num_oldcoins,
+ commit_links))
+ {
+ GNUNET_break (0);
+ GNUNET_free (commit_links);
+ return (MHD_YES == TMH_RESPONSE_reply_internal_db_error (connection))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+
+ for (j = 0; j < num_oldcoins; j++)
+ {
+ struct TALER_LinkSecretP shared_secret;
+ struct TALER_TransferPublicKeyP transfer_pub_check;
+
+ GNUNET_CRYPTO_ecdhe_key_get_public (&transfer_privs[j].ecdhe_priv,
+ &transfer_pub_check.ecdhe_pub);
+ if (0 !=
+ memcmp (&transfer_pub_check,
+ &commit_links[j].transfer_pub,
+ sizeof (struct TALER_TransferPublicKeyP)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "transfer keys do not match\n");
+ GNUNET_free (commit_links);
+ return send_melt_commitment_error (connection,
+ session,
+ session_hash,
+ off,
+ j,
+ "transfer key");
+ }
+
+ if (GNUNET_OK !=
+ TALER_link_decrypt_secret (&commit_links[j].shared_secret_enc,
+ &transfer_privs[j],
+ &melts[j].coin.coin_pub,
+ &shared_secret))
+ {
+ GNUNET_free (commit_links);
+ return (MHD_YES ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "Transfer secret decryption error"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ if (GNUNET_NO == secret_initialized)
+ {
+ secret_initialized = GNUNET_YES;
+ last_shared_secret = shared_secret;
+ }
+ else if (0 != memcmp (&shared_secret,
+ &last_shared_secret,
+ sizeof (struct GNUNET_HashCode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "shared secrets do not match\n");
+ GNUNET_free (commit_links);
+ return send_melt_commitment_error (connection,
+ session,
+ session_hash,
+ off,
+ j,
+ "transfer secret");
+ }
+ }
+ GNUNET_break (GNUNET_YES == secret_initialized);
+ GNUNET_free (commit_links);
+
+ /* Check that the commitments for all new coins were correct */
+ commit_coins = GNUNET_malloc (num_newcoins *
+ sizeof (struct TALER_MINTDB_RefreshCommitCoin));
+
+ if (GNUNET_OK !=
+ TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls,
+ session,
+ session_hash,
+ off,
+ num_newcoins,
+ commit_coins))
+ {
+ GNUNET_break (0);
+ GNUNET_free (commit_coins);
+ return (MHD_YES == TMH_RESPONSE_reply_internal_db_error (connection))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+
+ for (j = 0; j < num_newcoins; j++)
+ {
+ struct TALER_RefreshLinkDecrypted *link_data;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct GNUNET_HashCode h_msg;
+ char *buf;
+ size_t buf_len;
+
+ link_data = TALER_refresh_decrypt (commit_coins[j].refresh_link,
+ &last_shared_secret);
+ if (NULL == link_data)
+ {
+ GNUNET_break (0);
+ GNUNET_free (commit_coins);
+ return (MHD_YES == TMH_RESPONSE_reply_internal_error (connection,
+ "Decryption error"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&link_data->coin_priv.eddsa_priv,
+ &coin_pub.eddsa_pub);
+ GNUNET_CRYPTO_hash (&coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP),
+ &h_msg);
+ if (0 == (buf_len =
+ GNUNET_CRYPTO_rsa_blind (&h_msg,
+ link_data->blinding_key.rsa_blinding_key,
+ denom_pubs[j].rsa_public_key,
+ &buf)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "blind failed\n");
+ GNUNET_free (commit_coins);
+ return (MHD_YES == TMH_RESPONSE_reply_internal_error (connection,
+ "Blinding error"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+
+ if ( (buf_len != commit_coins[j].coin_ev_size) ||
+ (0 != memcmp (buf,
+ commit_coins[j].coin_ev,
+ buf_len)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "blind envelope does not match for k=%u, old=%d\n",
+ off,
+ (int) j);
+ GNUNET_free (commit_coins);
+ return send_melt_commitment_error (connection,
+ session,
+ session_hash,
+ off,
+ j,
+ "envelope");
+ }
+ GNUNET_free (buf);
+ }
+ GNUNET_free (commit_coins);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Mint a coin as part of a refresh operation. Obtains the
+ * envelope from the database and performs the signing operation.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param key_state key state to lookup denomination pubs
+ * @param denom_pub denomination key for the coin to create
+ * @param commit_coin the coin that was committed
+ * @param coin_off number of the coin
+ * @return NULL on error, otherwise signature over the coin
+ */
+static struct TALER_DenominationSignature
+refresh_mint_coin (struct MHD_Connection *connection,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ struct TMH_KS_StateHandle *key_state,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ const struct TALER_MINTDB_RefreshCommitCoin *commit_coin,
+ unsigned int coin_off)
+{
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct TALER_DenominationSignature ev_sig;
+
+ dki = TMH_KS_denomination_key_lookup (key_state,
+ denom_pub,
+ TMH_KS_DKU_WITHDRAW);
+ if (NULL == dki)
+ {
+ GNUNET_break (0);
+ ev_sig.rsa_signature = NULL;
+ return ev_sig;
+ }
+ ev_sig.rsa_signature
+ = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key,
+ commit_coin->coin_ev,
+ commit_coin->coin_ev_size);
+ if (NULL == ev_sig.rsa_signature)
+ {
+ GNUNET_break (0);
+ return ev_sig;
+ }
+ if (GNUNET_OK !=
+ TMH_plugin->insert_refresh_out (TMH_plugin->cls,
+ session,
+ session_hash,
+ coin_off,
+ &ev_sig))
+ {
+ GNUNET_break (0);
+ GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
+ ev_sig.rsa_signature = NULL;
+ }
+ return ev_sig;
+}
+
+
+/**
+ * Execute a "/refresh/reveal". The client is revealing to us the
+ * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the
+ * revealed transfer keys would allow linkage to the blinded coins,
+ * and if so, return the signed coins for corresponding to the set of
+ * coins that was not chosen.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash identifying the refresh session
+ * @param num_oldcoins size of y-dimension of @a transfer_privs array
+ * @param transfer_privs array with the revealed transfer keys,
+ * x-dimension must be #TALER_CNC_KAPPA - 1
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int num_oldcoins,
+ struct TALER_TransferPrivateKeyP **transfer_privs)
+{
+ int res;
+ struct TALER_MINTDB_Session *session;
+ struct TALER_MINTDB_RefreshSession refresh_session;
+ struct TMH_KS_StateHandle *key_state;
+ struct TALER_MINTDB_RefreshMelt *melts;
+ struct TALER_DenominationPublicKey *denom_pubs;
+ struct TALER_DenominationSignature *ev_sigs;
+ struct TALER_MINTDB_RefreshCommitCoin *commit_coins;
+ unsigned int i;
+ unsigned int j;
+ unsigned int off;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ res = TMH_plugin->get_refresh_session (TMH_plugin->cls,
+ session,
+ session_hash,
+ &refresh_session);
+ if (GNUNET_NO == res)
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "session_hash");
+ if (GNUNET_SYSERR == res)
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ if (0 == refresh_session.num_oldcoins)
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ melts = GNUNET_malloc (refresh_session.num_oldcoins *
+ sizeof (struct TALER_MINTDB_RefreshMelt));
+ for (j=0;j<refresh_session.num_oldcoins;j++)
+ {
+ if (GNUNET_OK !=
+ TMH_plugin->get_refresh_melt (TMH_plugin->cls,
+ session,
+ session_hash,
+ j,
+ &melts[j]))
+ {
+ GNUNET_break (0);
+ GNUNET_free (melts);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ }
+ denom_pubs = GNUNET_malloc (refresh_session.num_newcoins *
+ sizeof (struct TALER_DenominationPublicKey));
+ if (GNUNET_OK !=
+ TMH_plugin->get_refresh_order (TMH_plugin->cls,
+ session,
+ session_hash,
+ refresh_session.num_newcoins,
+ denom_pubs))
+ {
+ GNUNET_break (0);
+ GNUNET_free (denom_pubs);
+ GNUNET_free (melts);
+ return (MHD_YES == TMH_RESPONSE_reply_internal_db_error (connection))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+
+
+ off = 0;
+ for (i=0;i<TALER_CNC_KAPPA - 1;i++)
+ {
+ if (i == refresh_session.noreveal_index)
+ off = 1;
+ if (GNUNET_OK !=
+ (res = check_commitment (connection,
+ session,
+ session_hash,
+ i + off,
+ refresh_session.num_oldcoins,
+ transfer_privs[i + off],
+ melts,
+ refresh_session.num_newcoins,
+ denom_pubs)))
+ {
+ for (j=0;j<refresh_session.num_newcoins;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (denom_pubs);
+ GNUNET_free (melts);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ }
+ GNUNET_free (melts);
+
+ /* Client request OK, start transaction */
+ if (GNUNET_OK !=
+ TMH_plugin->start (TMH_plugin->cls,
+ session))
+ {
+ GNUNET_break (0);
+ for (j=0;j<refresh_session.num_newcoins;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (denom_pubs);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+
+ commit_coins = GNUNET_malloc (refresh_session.num_newcoins *
+ sizeof (struct TALER_MINTDB_RefreshCommitCoin));
+ if (GNUNET_OK !=
+ TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls,
+ session,
+ session_hash,
+ refresh_session.noreveal_index,
+ refresh_session.num_newcoins,
+ commit_coins))
+ {
+ GNUNET_break (0);
+ GNUNET_free (commit_coins);
+ for (j=0;j<refresh_session.num_newcoins;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (denom_pubs);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ ev_sigs = GNUNET_malloc (refresh_session.num_newcoins *
+ sizeof (struct TALER_DenominationSignature));
+ key_state = TMH_KS_acquire ();
+ for (j=0;j<refresh_session.num_newcoins;j++)
+ {
+ ev_sigs[j] = refresh_mint_coin (connection,
+ session,
+ session_hash,
+ key_state,
+ &denom_pubs[j],
+ &commit_coins[j],
+ j);
+ if (NULL == ev_sigs[j].rsa_signature)
+ {
+ TMH_KS_release (key_state);
+ for (i=0;i<j;i++)
+ GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
+ GNUNET_free (ev_sigs);
+ for (j=0;j<refresh_session.num_newcoins;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (denom_pubs);
+ GNUNET_free (commit_coins);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ }
+ TMH_KS_release (key_state);
+ for (j=0;j<refresh_session.num_newcoins;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (denom_pubs);
+ GNUNET_free (commit_coins);
+
+ if (GNUNET_OK !=
+ TMH_plugin->commit (TMH_plugin->cls,
+ session))
+ {
+ TALER_LOG_WARNING ("/refresh/reveal transaction commit failed\n");
+ for (i=0;i<refresh_session.num_newcoins;i++)
+ GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
+ GNUNET_free (ev_sigs);
+ return TMH_RESPONSE_reply_commit_error (connection);
+ }
+
+ res = TMH_RESPONSE_reply_refresh_reveal_success (connection,
+ refresh_session.num_newcoins,
+ ev_sigs);
+ for (i=0;i<refresh_session.num_newcoins;i++)
+ GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
+ GNUNET_free (ev_sigs);
+ return res;
+}
+
+
+/**
+ * Closure for #handle_transfer_data().
+ */
+struct HTD_Context
+{
+
+ /**
+ * Session link data we collect.
+ */
+ struct TMH_RESPONSE_LinkSessionInfo *sessions;
+
+ /**
+ * Database session. Nothing to do with @a sessions.
+ */
+ struct TALER_MINTDB_Session *session;
+
+ /**
+ * MHD connection, for queueing replies.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Number of sessions the coin was melted into.
+ */
+ unsigned int num_sessions;
+
+ /**
+ * How are we expected to proceed. #GNUNET_SYSERR if we
+ * failed to return an error (should return #MHD_NO).
+ * #GNUNET_NO if we succeeded in queueing an MHD error
+ * (should return #MHD_YES from #TMH_execute_refresh_link),
+ * #GNUNET_OK if we should call #TMH_RESPONSE_reply_refresh_link_success().
+ */
+ int status;
+};
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin. Gets the linkage data and
+ * builds the reply for the client.
+ *
+ *
+ * @param cls closure, a `struct HTD_Context`
+ * @param session_hash a session the coin was melted in
+ * @param transfer_pub public transfer key for the session
+ * @param shared_secret_enc set to shared secret for the session
+ */
+static void
+handle_transfer_data (void *cls,
+ const struct GNUNET_HashCode *session_hash,
+ const struct TALER_TransferPublicKeyP *transfer_pub,
+ const struct TALER_EncryptedLinkSecretP *shared_secret_enc)
+{
+ struct HTD_Context *ctx = cls;
+ struct TALER_MINTDB_LinkDataList *ldl;
+ struct TMH_RESPONSE_LinkSessionInfo *lsi;
+
+ if (GNUNET_OK != ctx->status)
+ return;
+ ldl = TMH_plugin->get_link_data_list (TMH_plugin->cls,
+ ctx->session,
+ session_hash);
+ if (NULL == ldl)
+ {
+ GNUNET_break (0);
+ ctx->status = GNUNET_NO;
+ if (MHD_NO ==
+ TMH_RESPONSE_reply_json_pack (ctx->connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s}",
+ "error",
+ "link data not found (link)"))
+ ctx->status = GNUNET_SYSERR;
+ return;
+ }
+ GNUNET_array_grow (ctx->sessions,
+ ctx->num_sessions,
+ ctx->num_sessions + 1);
+ lsi = &ctx->sessions[ctx->num_sessions - 1];
+ lsi->transfer_pub = *transfer_pub;
+ lsi->shared_secret_enc = *shared_secret_enc;
+ lsi->ldl = ldl;
+}
+
+
+/**
+ * Execute a "/refresh/link". Returns the linkage information that
+ * will allow the owner of a coin to follow the refresh trail to
+ * the refreshed coin.
+ *
+ * @param connection the MHD connection to handle
+ * @param coin_pub public key of the coin to link
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_refresh_link (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub)
+{
+ struct HTD_Context ctx;
+ int res;
+ unsigned int i;
+
+ if (NULL == (ctx.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.num_sessions = 0;
+ ctx.sessions = NULL;
+ ctx.status = GNUNET_OK;
+ res = TMH_plugin->get_transfer (TMH_plugin->cls,
+ ctx.session,
+ coin_pub,
+ &handle_transfer_data,
+ &ctx);
+ if (GNUNET_SYSERR == ctx.status)
+ {
+ res = MHD_NO;
+ goto cleanup;
+ }
+ if (GNUNET_NO == ctx.status)
+ {
+ res = MHD_YES;
+ goto cleanup;
+ }
+ GNUNET_assert (GNUNET_OK == ctx.status);
+ if (0 == ctx.num_sessions)
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "coin_pub");
+ res = TMH_RESPONSE_reply_refresh_link_success (connection,
+ ctx.num_sessions,
+ ctx.sessions);
+ cleanup:
+ for (i=0;i<ctx.num_sessions;i++)
+ TMH_plugin->free_link_data_list (TMH_plugin->cls,
+ ctx.sessions[i].ldl);
+ GNUNET_free (ctx.sessions);
+ return res;
+}
+
+
+/**
+ * Add an incoming transaction to the database. Checks if the
+ * transaction is fresh (not a duplicate) and if so adds it to
+ * the database.
+ *
+ * @param connection the MHD connection to handle
+ * @param reserve_pub public key of the reserve
+ * @param amount amount to add to the reserve
+ * @param execution_time when did we receive the wire transfer
+ * @param wire details about the wire transfer
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *amount,
+ struct GNUNET_TIME_Absolute execution_time,
+ json_t *wire)
+{
+ struct TALER_MINTDB_Session *session;
+ int ret;
+
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode)))
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ ret = TMH_plugin->reserves_in_insert (TMH_plugin->cls,
+ session,
+ reserve_pub,
+ amount,
+ execution_time,
+ wire);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_break (0);
+ return TMH_RESPONSE_reply_internal_db_error (connection);
+ }
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s}",
+ "status",
+ (GNUNET_OK == ret)
+ ? "NEW"
+ : "DUP");
+}
+
+
+/* end of taler-mint-httpd_db.c */
diff --git a/src/backend/taler-mint-httpd_db.h b/src/backend/taler-mint-httpd_db.h
new file mode 100644
index 00000000..8a171153
--- /dev/null
+++ b/src/backend/taler-mint-httpd_db.h
@@ -0,0 +1,190 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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/taler-mint-httpd_db.h
+ * @brief High-level (transactional-layer) database operations for the mint
+ * @author Chrisitan Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_DB_H
+#define TALER_MINT_HTTPD_DB_H
+
+#include <microhttpd.h>
+#include "taler_mintdb_plugin.h"
+
+
+/**
+ * Execute a "/deposit". The validity of the coin and signature
+ * have already been checked. The database must now check that
+ * the coin is not (double or over) spent, and execute the
+ * transaction (record details, generate success or failure response).
+ *
+ * @param connection the MHD connection to handle
+ * @param deposit information about the deposit
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_deposit (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_Deposit *deposit);
+
+
+/**
+ * Execute a "/withdraw/status". Given the public key of a reserve,
+ * return the associated transaction history.
+ *
+ * @param connection the MHD connection to handle
+ * @param reserve_pub public key of the reserve to check
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_withdraw_status (struct MHD_Connection *connection,
+ const struct TALER_ReservePublicKeyP *reserve_pub);
+
+
+/**
+ * Execute a "/withdraw/sign". Given a reserve and a properly signed
+ * request to withdraw a coin, check the balance of the reserve and
+ * if it is sufficient, store the request and return the signed
+ * blinded envelope.
+ *
+ * @param connection the MHD connection to handle
+ * @param reserve public key of the reserve
+ * @param denomination_pub public key of the denomination requested
+ * @param blinded_msg blinded message to be signed
+ * @param blinded_msg_len number of bytes in @a blinded_msg
+ * @param signature signature over the withdraw request, to be stored in DB
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
+ const struct TALER_ReservePublicKeyP *reserve,
+ const struct TALER_DenominationPublicKey *denomination_pub,
+ const char *blinded_msg,
+ size_t blinded_msg_len,
+ const struct TALER_ReserveSignatureP *signature);
+
+
+/**
+ * @brief Details about a melt operation of an individual coin.
+ */
+struct TMH_DB_MeltDetails
+{
+
+ /**
+ * Information about the coin being melted.
+ */
+ struct TALER_CoinPublicInfo coin_info;
+
+ /**
+ * Signature allowing the melt (using
+ * a `struct TALER_MINTDB_RefreshMeltConfirmSignRequestBody`) to sign over.
+ */
+ struct TALER_CoinSpendSignatureP melt_sig;
+
+ /**
+ * How much of the coin's value did the client allow to be melted?
+ * This amount includes the fees, so the final amount contributed
+ * to the melt is this value minus the fee for melting the coin.
+ */
+ struct TALER_Amount melt_amount_with_fee;
+};
+
+
+/**
+ * Execute a "/refresh/melt". We have been given a list of valid
+ * coins and a request to melt them into the given
+ * @a refresh_session_pub. Check that the coins all have the
+ * required value left and if so, store that they have been
+ * melted and confirm the melting operation to the client.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash code of the session the coins are melted into
+ * @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @a commit_coin array
+ * @param denom_pubs array of public denomination keys for the refresh (?)
+ * @param coin_count number of entries in @ a coin_melt_details, size of y-dimension of @a commit_link array
+ * @param coin_melt_details signatures and (residual) value of and information about the respective coin to be melted
+ * @param commit_coin 2d array of coin commitments (what the mint is to sign
+ * once the "/refres/reveal" of cut and choose is done)
+ * @param commit_link 2d array of coin link commitments (what the mint is
+ * to return via "/refresh/link" to enable linkage in the
+ * future)
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int num_new_denoms,
+ const struct TALER_DenominationPublicKey *denom_pubs,
+ unsigned int coin_count,
+ const struct TMH_DB_MeltDetails *coin_melt_details,
+ struct TALER_MINTDB_RefreshCommitCoin *const* commit_coin,
+ struct TALER_MINTDB_RefreshCommitLinkP *const* commit_link);
+
+
+/**
+ * Execute a "/refresh/reveal". The client is revealing to us the
+ * transfer keys for #TALER_CNC_KAPPA-1 sets of coins. Verify that the
+ * revealed transfer keys would allow linkage to the blinded coins,
+ * and if so, return the signed coins for corresponding to the set of
+ * coins that was not chosen.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash over the refresh session
+ * @param num_oldcoins size of y-dimension of @a transfer_privs array
+ * @param transfer_privs array with the revealed transfer keys, #TALER_CNC_KAPPA is 1st-dimension
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int num_oldcoins,
+ struct TALER_TransferPrivateKeyP **transfer_privs);
+
+
+/**
+ * Execute a "/refresh/link". Returns the linkage information that
+ * will allow the owner of a coin to follow the refresh trail to the
+ * refreshed coin.
+ *
+ * @param connection the MHD connection to handle
+ * @param coin_pub public key of the coin to link
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_refresh_link (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub);
+
+
+
+/**
+ * Add an incoming transaction to the database.
+ *
+ * @param connection the MHD connection to handle
+ * @param reserve_pub public key of the reserve
+ * @param amount amount to add to the reserve
+ * @param execution_time when did we receive the wire transfer
+ * @param wire details about the wire transfer
+ * @return MHD result code
+ */
+int
+TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *amount,
+ struct GNUNET_TIME_Absolute execution_time,
+ json_t *wire);
+
+
+#endif
+/* TALER_MINT_HTTPD_DB_H */
diff --git a/src/backend/taler-mint-httpd_deposit.c b/src/backend/taler-mint-httpd_deposit.c
new file mode 100644
index 00000000..5725cd1c
--- /dev/null
+++ b/src/backend/taler-mint-httpd_deposit.c
@@ -0,0 +1,270 @@
+/*
+ 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 taler-mint-httpd_deposit.c
+ * @brief Handle /deposit requests; parses the POST and JSON and
+ * verifies the coin signature before handing things off
+ * to the database.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ *
+ * TODO:
+ * - ugly if-construction for deposit type
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_deposit.h"
+#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd_keystate.h"
+
+
+/**
+ * We have parsed the JSON information about the deposit, do some
+ * basic sanity checks (especially that the signature on the coin is
+ * valid, and that this type of coin exists) and then execute the
+ * deposit.
+ *
+ * @param connection the MHD connection to handle
+ * @param deposit information about the deposit
+ * @return MHD result code
+ */
+static int
+verify_and_execute_deposit (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_Deposit *deposit)
+{
+ struct TMH_KS_StateHandle *key_state;
+ struct TALER_DepositRequestPS dr;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct TALER_Amount fee_deposit;
+
+ dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
+ dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
+ dr.h_contract = deposit->h_contract;
+ dr.h_wire = deposit->h_wire;
+ dr.timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp);
+ dr.refund_deadline = GNUNET_TIME_absolute_hton (deposit->refund_deadline);
+ dr.transaction_id = GNUNET_htonll (deposit->transaction_id);
+ TALER_amount_hton (&dr.amount_with_fee,
+ &deposit->amount_with_fee);
+ TALER_amount_hton (&dr.deposit_fee,
+ &deposit->deposit_fee);
+ dr.merchant = deposit->merchant_pub;
+ dr.coin_pub = deposit->coin.coin_pub;
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
+ &dr.purpose,
+ &deposit->csig.eddsa_signature,
+ &deposit->coin.coin_pub.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Invalid signature on /deposit request\n");
+ return TMH_RESPONSE_reply_signature_invalid (connection,
+ "coin_sig");
+ }
+ /* check denomination exists and is valid */
+ key_state = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (key_state,
+ &deposit->coin.denom_pub,
+ TMH_KS_DKU_DEPOSIT);
+ if (NULL == dki)
+ {
+ TMH_KS_release (key_state);
+ TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n");
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "denom_pub");
+ }
+ /* check coin signature */
+ if (GNUNET_YES !=
+ TALER_test_coin_valid (&deposit->coin))
+ {
+ TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_signature_invalid (connection,
+ "ub_sig");
+ }
+ TALER_amount_ntoh (&fee_deposit,
+ &dki->issue.properties.fee_deposit);
+ if (0 < TALER_amount_cmp (&fee_deposit,
+ &deposit->amount_with_fee))
+ {
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "deposited amount smaller than depositing fee");
+ }
+ TMH_KS_release (key_state);
+
+ return TMH_DB_execute_deposit (connection,
+ deposit);
+}
+
+
+/**
+ * Handle a "/deposit" request. This function parses the
+ * JSON information and then calls #verify_and_execute_deposit()
+ * to verify the signatures and execute the deposit.
+ *
+ * @param connection the MHD connection to handle
+ * @param root root of the posted JSON
+ * @param amount how much should be deposited
+ * @param wire json describing the wire details (?)
+ * @return MHD result code
+ */
+static int
+parse_and_handle_deposit_request (struct MHD_Connection *connection,
+ const json_t *root,
+ const struct TALER_Amount *amount,
+ json_t *wire)
+{
+ int res;
+ struct TALER_MINTDB_Deposit deposit;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct TMH_KS_StateHandle *ks;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &deposit.coin.denom_pub),
+ TMH_PARSE_member_denomination_signature ("ub_sig", &deposit.coin.denom_sig),
+ TMH_PARSE_member_fixed ("coin_pub", &deposit.coin.coin_pub),
+ TMH_PARSE_member_fixed ("merchant_pub", &deposit.merchant_pub),
+ TMH_PARSE_member_fixed ("H_contract", &deposit.h_contract),
+ TMH_PARSE_member_fixed ("H_wire", &deposit.h_wire),
+ TMH_PARSE_member_fixed ("coin_sig", &deposit.csig),
+ TMH_PARSE_member_uint64 ("transaction_id", &deposit.transaction_id),
+ TMH_PARSE_member_time_abs ("timestamp", &deposit.timestamp),
+ TMH_PARSE_member_time_abs ("refund_deadline", &deposit.refund_deadline),
+ TMH_PARSE_MEMBER_END
+ };
+
+ memset (&deposit, 0, sizeof (deposit));
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* hard failure */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* failure */
+
+ if (GNUNET_YES !=
+ TALER_json_validate_wireformat (TMH_expected_wire_format,
+ wire))
+ {
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "wire");
+ }
+ if (GNUNET_OK !=
+ TALER_hash_json (wire,
+ &deposit.h_wire))
+ {
+ TALER_LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n");
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "wire");
+ }
+ ks = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (ks,
+ &deposit.coin.denom_pub,
+ TMH_KS_DKU_DEPOSIT);
+ if (NULL == dki)
+ {
+ TMH_KS_release (ks);
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "denom_pub");
+ }
+ TALER_amount_ntoh (&deposit.deposit_fee,
+ &dki->issue.properties.fee_deposit);
+ TMH_KS_release (ks);
+ deposit.wire = wire;
+ deposit.amount_with_fee = *amount;
+ if (-1 == TALER_amount_cmp (&deposit.amount_with_fee,
+ &deposit.deposit_fee))
+ {
+ /* Total amount smaller than fee, invalid */
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "f");
+ }
+ res = verify_and_execute_deposit (connection,
+ &deposit);
+ TMH_PARSE_release_data (spec);
+ return res;
+}
+
+
+/**
+ * Handle a "/deposit" request. Parses the JSON in the post to find
+ * the "type" (either DIRECT_DEPOSIT or INCREMENTAL_DEPOSIT), and, if
+ * successful, passes the JSON data to
+ * #parse_and_handle_deposit_request() to further check the details
+ * of the operation specified in the "wire" field of the JSON data.
+ * If everything checks out, this will ultimately lead to the
+ * "/deposit" being executed, or rejected.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_DEPOSIT_handler_deposit (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *json;
+ json_t *wire;
+ int res;
+ struct TALER_Amount amount;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_object ("wire", &wire),
+ TMH_PARSE_member_amount ("f", &amount),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &json);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == json) )
+ return MHD_YES;
+ res = TMH_PARSE_json_data (connection,
+ json,
+ spec);
+ if (GNUNET_OK != res)
+ {
+ json_decref (json);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ res = parse_and_handle_deposit_request (connection,
+ json,
+ &amount,
+ wire);
+ TMH_PARSE_release_data (spec);
+ json_decref (json);
+ return res;
+}
+
+
+/* end of taler-mint-httpd_deposit.c */
diff --git a/src/backend/taler-mint-httpd_deposit.h b/src/backend/taler-mint-httpd_deposit.h
new file mode 100644
index 00000000..c2d3fe13
--- /dev/null
+++ b/src/backend/taler-mint-httpd_deposit.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_deposit.h
+ * @brief Handle /deposit requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_DEPOSIT_H
+#define TALER_MINT_HTTPD_DEPOSIT_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Handle a "/deposit" request. Parses the JSON in the post to find
+ * the "type" (either DIRECT_DEPOSIT or INCREMENTAL_DEPOSIT), and, if
+ * successful, passes the JSON data to
+ * #parse_and_handle_deposit_request() to further check the details
+ * of the operation specified in the "wire" field of the JSON data.
+ * If everything checks out, this will ultimately lead to the
+ * "/deposit" being executed, or rejected.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_DEPOSIT_handler_deposit (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/backend/taler-mint-httpd_keystate.c b/src/backend/taler-mint-httpd_keystate.c
new file mode 100644
index 00000000..ec09ab44
--- /dev/null
+++ b/src/backend/taler-mint-httpd_keystate.c
@@ -0,0 +1,867 @@
+/*
+ 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 taler-mint-httpd_keystate.c
+ * @brief management of our coin signing keys
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler-mint-httpd_keystate.h"
+#include "taler_mintdb_plugin.h"
+
+
+/**
+ * Snapshot of the (coin and signing) keys (including private keys) of
+ * the mint. There can be multiple instances of this struct, as it is
+ * reference counted and only destroyed once the last user is done
+ * with it. The current instance is acquired using
+ * #TMH_KS_acquire(). Using this function increases the
+ * reference count. The contents of this structure (except for the
+ * reference counter) should be considered READ-ONLY until it is
+ * ultimately destroyed (as there can be many concurrent users).
+ */
+struct TMH_KS_StateHandle
+{
+ /**
+ * JSON array with denomination keys. (Currently not really used
+ * after initialization.)
+ */
+ json_t *denom_keys_array;
+
+ /**
+ * JSON array with signing keys. (Currently not really used
+ * after initialization.)
+ */
+ json_t *sign_keys_array;
+
+ /**
+ * Cached JSON text that the mint will send for a "/keys" request.
+ * Includes our @e TMH_master_public_key public key, the signing and
+ * denomination keys as well as the @e reload_time.
+ */
+ char *keys_json;
+
+ /**
+ * Mapping from denomination keys to denomination key issue struct.
+ * Used to lookup the key by hash.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
+
+ /**
+ * Hash context we used to combine the hashes of all denomination
+ * keys into one big hash.
+ */
+ struct GNUNET_HashContext *hash_context;
+
+ /**
+ * When did we initiate the key reloading?
+ */
+ struct GNUNET_TIME_Absolute reload_time;
+
+ /**
+ * When is the next key invalid and we have to reload? (We also
+ * reload on SIGUSR1.)
+ */
+ struct GNUNET_TIME_Absolute next_reload;
+
+ /**
+ * Mint signing key that should be used currently.
+ */
+ struct TALER_MINTDB_PrivateSigningKeyInformationP current_sign_key_issue;
+
+ /**
+ * Reference count. The struct is released when the RC hits zero.
+ */
+ unsigned int refcnt;
+};
+
+
+/**
+ * Mint key state. Never use directly, instead access via
+ * #TMH_KS_acquire() and #TMH_KS_release().
+ */
+static struct TMH_KS_StateHandle *internal_key_state;
+
+/**
+ * Mutex protecting access to #internal_key_state.
+ */
+static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Pipe used for signaling reloading of our key state.
+ */
+static int reload_pipe[2];
+
+
+/**
+ * Convert the public part of a denomination key issue to a JSON
+ * object.
+ *
+ * @param pk public key of the denomination key
+ * @param dki the denomination key issue
+ * @return a JSON object describing the denomination key isue (public part)
+ */
+static json_t *
+denom_key_issue_to_json (const struct TALER_DenominationPublicKey *pk,
+ const struct TALER_MINTDB_DenominationKeyInformationP *dki)
+{
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_deposit;
+ struct TALER_Amount fee_refresh;
+
+ TALER_amount_ntoh (&value,
+ &dki->properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &dki->properties.fee_withdraw);
+ TALER_amount_ntoh (&fee_deposit,
+ &dki->properties.fee_deposit);
+ TALER_amount_ntoh (&fee_refresh,
+ &dki->properties.fee_refresh);
+ return
+ json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
+ "master_sig",
+ TALER_json_from_data (&dki->signature,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature)),
+ "stamp_start",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (dki->properties.start)),
+ "stamp_expire_withdraw",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (dki->properties.expire_withdraw)),
+ "stamp_expire_deposit",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (dki->properties.expire_spend)),
+ "stamp_expire_legal",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (dki->properties.expire_legal)),
+ "denom_pub",
+ TALER_json_from_rsa_public_key (pk->rsa_public_key),
+ "value",
+ TALER_json_from_amount (&value),
+ "fee_withdraw",
+ TALER_json_from_amount (&fee_withdraw),
+ "fee_deposit",
+ TALER_json_from_amount (&fee_deposit),
+ "fee_refresh",
+ TALER_json_from_amount (&fee_refresh));
+}
+
+
+/**
+ * Get the relative time value that describes how
+ * far in the future do we want to provide coin keys.
+ *
+ * @return the provide duration
+ */
+static struct GNUNET_TIME_Relative
+TALER_MINT_conf_duration_provide ()
+{
+ struct GNUNET_TIME_Relative rel;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (cfg,
+ "mint_keys",
+ "lookahead_provide",
+ &rel))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "mint_keys",
+ "lookahead_provide",
+ "time value required");
+ GNUNET_assert (0);
+ }
+ return rel;
+}
+
+
+/**
+ * Iterator for (re)loading/initializing denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key issue
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+reload_keys_denom_iter (void *cls,
+ const char *alias,
+ const struct TALER_MINTDB_DenominationKeyIssueInformation *dki)
+{
+ struct TMH_KS_StateHandle *ctx = cls;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Absolute horizon;
+ struct GNUNET_HashCode denom_key_hash;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *d2;
+ struct TALER_MINTDB_Session *session;
+ int res;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Loading denomination key `%s'\n",
+ alias);
+ horizon = GNUNET_TIME_relative_to_absolute (TALER_MINT_conf_duration_provide ());
+ if (GNUNET_TIME_absolute_ntoh (dki->issue.properties.start).abs_value_us >
+ horizon.abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Skipping future denomination key `%s'\n",
+ alias);
+ return GNUNET_OK;
+ }
+ now = GNUNET_TIME_absolute_get ();
+ if (GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_spend).abs_value_us <
+ now.abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Skipping expired denomination key `%s'\n",
+ alias);
+ return GNUNET_OK;
+ }
+
+ GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
+ &denom_key_hash);
+ GNUNET_CRYPTO_hash_context_read (ctx->hash_context,
+ &denom_key_hash,
+ sizeof (struct GNUNET_HashCode));
+ session = TMH_plugin->get_session (TMH_plugin->cls,
+ TMH_test_mode);
+ if (NULL == session)
+ return GNUNET_SYSERR;
+ /* Try to insert DKI into DB until we succeed; note that if the DB
+ failure is persistent, this code may loop forever (as there is no
+ sane alternative, we cannot continue without the DKI being in the
+ DB). */
+ res = GNUNET_SYSERR;
+ while (GNUNET_OK != res)
+ {
+ res = TMH_plugin->start (TMH_plugin->cls,
+ session);
+ if (GNUNET_OK != res)
+ {
+ /* Transaction start failed!? Very bad error, log and retry */
+ GNUNET_break (0);
+ continue;
+ }
+ res = TMH_plugin->get_denomination_info (TMH_plugin->cls,
+ session,
+ &dki->denom_pub,
+ NULL);
+ if (GNUNET_SYSERR == res)
+ {
+ /* Fetch failed!? Very bad error, log and retry */
+ GNUNET_break (0);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ continue;
+ }
+ if (GNUNET_OK == res)
+ {
+ /* Record exists, we're good, just exit */
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ break;
+ }
+ res = TMH_plugin->insert_denomination_info (TMH_plugin->cls,
+ session,
+ &dki->denom_pub,
+ &dki->issue);
+ if (GNUNET_OK != res)
+ {
+ /* Insert failed!? Very bad error, log and retry */
+ GNUNET_break (0);
+ TMH_plugin->rollback (TMH_plugin->cls,
+ session);
+ continue;
+ }
+ res = TMH_plugin->commit (TMH_plugin->cls,
+ session);
+ /* If commit succeeded, we're done, otherwise we retry; this
+ time without logging, as theroetically commits can fail
+ in a transactional DB due to concurrent activities that
+ cannot be reconciled. This should be rare for DKIs, but
+ as it is possible we just retry until we succeed. */
+ }
+
+ d2 = GNUNET_new (struct TALER_MINTDB_DenominationKeyIssueInformation);
+ d2->issue = dki->issue;
+ d2->denom_priv.rsa_private_key
+ = GNUNET_CRYPTO_rsa_private_key_dup (dki->denom_priv.rsa_private_key);
+ d2->denom_pub.rsa_public_key
+ = GNUNET_CRYPTO_rsa_public_key_dup (dki->denom_pub.rsa_public_key);
+ res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map,
+ &denom_key_hash,
+ d2,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Duplicate denomination key `%s'\n",
+ alias);
+ GNUNET_CRYPTO_rsa_private_key_free (d2->denom_priv.rsa_private_key);
+ GNUNET_CRYPTO_rsa_public_key_free (d2->denom_pub.rsa_public_key);
+ GNUNET_free (d2);
+ return GNUNET_OK;
+ }
+ json_array_append_new (ctx->denom_keys_array,
+ denom_key_issue_to_json (&dki->denom_pub,
+ &dki->issue));
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert the public part of a sign key issue to a JSON object.
+ *
+ * @param ski the sign key issue
+ * @return a JSON object describing the sign key isue (public part)
+ */
+static json_t *
+sign_key_issue_to_json (const struct TALER_MintSigningKeyValidityPS *ski)
+{
+ return
+ json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
+ "stamp_start",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (ski->start)),
+ "stamp_expire",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire)),
+ "stamp_end",
+ TALER_json_from_abs (GNUNET_TIME_absolute_ntoh (ski->end)),
+ "master_pub",
+ TALER_json_from_data (&ski->master_public_key,
+ sizeof (struct TALER_MasterPublicKeyP)),
+ "master_sig",
+ TALER_json_from_data (&ski->signature,
+ sizeof (struct TALER_MasterSignatureP)),
+ "key",
+ TALER_json_from_data (&ski->signkey_pub,
+ sizeof (struct TALER_MintPublicKeyP)));
+}
+
+
+/**
+ * Iterator for sign keys.
+ *
+ * @param cls closure
+ * @param filename name of the file the key came from
+ * @param ski the sign key issue
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+reload_keys_sign_iter (void *cls,
+ const char *filename,
+ const struct TALER_MINTDB_PrivateSigningKeyInformationP *ski)
+{
+ struct TMH_KS_StateHandle *ctx = cls;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Absolute horizon;
+
+ horizon = GNUNET_TIME_relative_to_absolute (TALER_MINT_conf_duration_provide ());
+ if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us >
+ horizon.abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Skipping future signing key `%s'\n",
+ filename);
+ return GNUNET_OK;
+ }
+ now = GNUNET_TIME_absolute_get ();
+ if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us <
+ now.abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Skipping expired signing key `%s'\n",
+ filename);
+ return GNUNET_OK;
+ }
+
+ /* The signkey is valid at this time, check if it's more recent than
+ what we have so far! */
+ if ( (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us <
+ GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us) &&
+ (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us <
+ now.abs_value_us) )
+ {
+ /* We use the most recent one, if it is valid now (not just in the near future) */
+ ctx->current_sign_key_issue = *ski;
+ }
+ json_array_append_new (ctx->sign_keys_array,
+ sign_key_issue_to_json (&ski->issue));
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Iterator for freeing denomination keys.
+ *
+ * @param cls closure with the `struct TMH_KS_StateHandle`
+ * @param key key for the denomination key
+ * @param value coin details
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+free_denom_key (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki = value;
+
+ GNUNET_CRYPTO_rsa_private_key_free (dki->denom_priv.rsa_private_key);
+ GNUNET_CRYPTO_rsa_public_key_free (dki->denom_pub.rsa_public_key);
+ GNUNET_free (dki);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ * Internal method used when the mutex is already held.
+ *
+ * @param key_state the key state to release
+ */
+static void
+TMH_KS_release_ (struct TMH_KS_StateHandle *key_state)
+{
+ GNUNET_assert (0 < key_state->refcnt);
+ key_state->refcnt--;
+ if (0 == key_state->refcnt)
+ {
+ if (NULL != key_state->denom_keys_array)
+ {
+ json_decref (key_state->denom_keys_array);
+ key_state->denom_keys_array = NULL;
+ }
+ if (NULL != key_state->sign_keys_array)
+ {
+ json_decref (key_state->sign_keys_array);
+ key_state->sign_keys_array = NULL;
+ }
+ if (NULL != key_state->denomkey_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (key_state->denomkey_map,
+ &free_denom_key,
+ key_state);
+ GNUNET_CONTAINER_multihashmap_destroy (key_state->denomkey_map);
+ key_state->denomkey_map = NULL;
+ }
+ GNUNET_free_non_null (key_state->keys_json);
+ GNUNET_free (key_state);
+ }
+}
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ *
+ * @param key_state the key state to release
+ */
+void
+TMH_KS_release (struct TMH_KS_StateHandle *key_state)
+{
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ TMH_KS_release_ (key_state);
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+}
+
+
+/**
+ * Acquire the key state of the mint. Updates keys if necessary.
+ * For every call to #TMH_KS_acquire(), a matching call
+ * to #TMH_KS_release() must be made.
+ *
+ * @return the key state
+ */
+struct TMH_KS_StateHandle *
+TMH_KS_acquire (void)
+{
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ struct TMH_KS_StateHandle *key_state;
+ json_t *keys;
+ struct TALER_MintKeySetPS ks;
+ struct TALER_MintSignatureP sig;
+
+ GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
+ if ( (NULL != internal_key_state) &&
+ (internal_key_state->next_reload.abs_value_us <= now.abs_value_us) )
+ {
+ TMH_KS_release_ (internal_key_state);
+ internal_key_state = NULL;
+ }
+ if (NULL == internal_key_state)
+ {
+ key_state = GNUNET_new (struct TMH_KS_StateHandle);
+ key_state->hash_context = GNUNET_CRYPTO_hash_context_start ();
+ key_state->denom_keys_array = json_array ();
+ GNUNET_assert (NULL != key_state->denom_keys_array);
+ key_state->sign_keys_array = json_array ();
+ GNUNET_assert (NULL != key_state->sign_keys_array);
+ key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32,
+ GNUNET_NO);
+ key_state->reload_time = GNUNET_TIME_absolute_get ();
+ TALER_round_abs_time (&key_state->reload_time);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Loading keys from `%s'\n",
+ TMH_mint_directory);
+ TALER_MINTDB_denomination_keys_iterate (TMH_mint_directory,
+ &reload_keys_denom_iter,
+ key_state);
+ TALER_MINTDB_signing_keys_iterate (TMH_mint_directory,
+ &reload_keys_sign_iter,
+ key_state);
+ ks.purpose.size = htonl (sizeof (ks));
+ ks.purpose.purpose = htonl (TALER_SIGNATURE_MINT_KEY_SET);
+ ks.list_issue_date = GNUNET_TIME_absolute_hton (key_state->reload_time);
+ GNUNET_CRYPTO_hash_context_finish (key_state->hash_context,
+ &ks.hc);
+ key_state->hash_context = NULL;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv.eddsa_priv,
+ &ks.purpose,
+ &sig.eddsa_signature));
+ key_state->next_reload = GNUNET_TIME_absolute_ntoh (key_state->current_sign_key_issue.issue.expire);
+ if (0 == key_state->next_reload.abs_value_us)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid signing key found!\n");
+
+ keys = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
+ "master_public_key",
+ TALER_json_from_data (&TMH_master_public_key,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)),
+ "signkeys", key_state->sign_keys_array,
+ "denoms", key_state->denom_keys_array,
+ "list_issue_date", TALER_json_from_abs (key_state->reload_time),
+ "eddsa_pub", TALER_json_from_data (&key_state->current_sign_key_issue.issue.signkey_pub,
+ sizeof (struct TALER_MintPublicKeyP)),
+ "eddsa_sig", TALER_json_from_data (&sig,
+ sizeof (struct TALER_MintSignatureP)));
+ key_state->sign_keys_array = NULL;
+ key_state->denom_keys_array = NULL;
+ key_state->keys_json = json_dumps (keys,
+ JSON_INDENT (2));
+ json_decref (keys);
+ internal_key_state = key_state;
+ }
+ key_state = internal_key_state;
+ key_state->refcnt++;
+ GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
+
+ return key_state;
+}
+
+
+/**
+ * Look up the issue for a denom public key.
+ *
+ * @param key_state state to look in
+ * @param denom_pub denomination public key
+ * @param use purpose for which the key is being located
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found
+ */
+struct TALER_MINTDB_DenominationKeyIssueInformation *
+TMH_KS_denomination_key_lookup (const struct TMH_KS_StateHandle *key_state,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ enum TMH_KS_DenominationKeyUse use)
+{
+ struct GNUNET_HashCode hc;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct GNUNET_TIME_Absolute now;
+
+ GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
+ &hc);
+ dki = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map,
+ &hc);
+ if (NULL == dki)
+ return NULL;
+ now = GNUNET_TIME_absolute_get ();
+ if (now.abs_value_us <
+ GNUNET_TIME_absolute_ntoh (dki->issue.properties.start).abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Not returning DKI for %s, as start time is in the future\n",
+ GNUNET_h2s (&hc));
+ return NULL;
+ }
+ now = GNUNET_TIME_absolute_get ();
+ switch (use)
+ {
+ case TMH_KS_DKU_WITHDRAW:
+ if (now.abs_value_us >
+ GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_withdraw).abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Not returning DKI for %s, as time to create coins has passed\n",
+ GNUNET_h2s (&hc));
+ return NULL;
+ }
+ break;
+ case TMH_KS_DKU_DEPOSIT:
+ if (now.abs_value_us >
+ GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_spend).abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Not returning DKI for %s, as time to spend coin has passed\n",
+ GNUNET_h2s (&hc));
+ return NULL;
+ }
+ break;
+ }
+ return dki;
+}
+
+
+/**
+ * Handle a signal, writing relevant signal numbers to the pipe.
+ *
+ * @param signal_number the signal number
+ */
+static void
+handle_signal (int signal_number)
+{
+ ssize_t res;
+ char c = signal_number;
+
+ res = write (reload_pipe[1],
+ &c,
+ 1);
+ if ( (res < 0) &&
+ (EINTR != errno) )
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (0 == res)
+ {
+ GNUNET_break (0);
+ return;
+ }
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sigusr1 ()
+{
+ handle_signal (SIGUSR1);
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sigint ()
+{
+ handle_signal (SIGINT);
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sigterm ()
+{
+ handle_signal (SIGTERM);
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sighup ()
+{
+ handle_signal (SIGHUP);
+}
+
+
+/**
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
+ * restart if SIGHUP is received.
+ *
+ * @return #GNUNET_SYSERR on errors,
+ * #GNUNET_OK to terminate normally
+ * #GNUNET_NO to restart an update version of the binary
+ */
+int
+TMH_KS_loop (void)
+{
+ struct GNUNET_SIGNAL_Context *sigusr1;
+ struct GNUNET_SIGNAL_Context *sigterm;
+ struct GNUNET_SIGNAL_Context *sigint;
+ struct GNUNET_SIGNAL_Context *sighup;
+ int ret;
+
+ if (0 != pipe (reload_pipe))
+ {
+ fprintf (stderr,
+ "Failed to create pipe.\n");
+ return GNUNET_SYSERR;
+ }
+ sigusr1 = GNUNET_SIGNAL_handler_install (SIGUSR1,
+ &handle_sigusr1);
+ sigterm = GNUNET_SIGNAL_handler_install (SIGTERM,
+ &handle_sigterm);
+ sigint = GNUNET_SIGNAL_handler_install (SIGINT,
+ &handle_sigint);
+ sighup = GNUNET_SIGNAL_handler_install (SIGHUP,
+ &handle_sighup);
+
+ ret = 0;
+ while (0 == ret)
+ {
+ char c;
+ ssize_t res;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "(re-)loading keys\n");
+ if (NULL != internal_key_state)
+ {
+ TMH_KS_release (internal_key_state);
+ internal_key_state = NULL;
+ }
+ /* This will re-initialize 'internal_key_state' with
+ an initial refcnt of 1 */
+ (void) TMH_KS_acquire ();
+
+read_again:
+ errno = 0;
+ res = read (reload_pipe[0],
+ &c,
+ 1);
+ if ((res < 0) && (EINTR != errno))
+ {
+ GNUNET_break (0);
+ ret = GNUNET_SYSERR;
+ break;
+ }
+ if (EINTR == errno)
+ goto read_again;
+ switch (c)
+ {
+ case SIGUSR1:
+ /* reload internal key state, we do this in the loop */
+ break;
+ case SIGTERM:
+ case SIGINT:
+ /* terminate */
+ ret = GNUNET_OK;
+ break;
+ case SIGHUP:
+ /* restart updated binary */
+ ret = GNUNET_NO;
+ break;
+ default:
+ /* unexpected character */
+ GNUNET_break (0);
+ break;
+ }
+ }
+ if (NULL != internal_key_state)
+ {
+ TMH_KS_release (internal_key_state);
+ internal_key_state = NULL;
+ }
+ GNUNET_SIGNAL_handler_uninstall (sigusr1);
+ GNUNET_SIGNAL_handler_uninstall (sigterm);
+ GNUNET_SIGNAL_handler_uninstall (sigint);
+ GNUNET_SIGNAL_handler_uninstall (sighup);
+ return ret;
+}
+
+
+/**
+ * Sign the message in @a purpose with the mint's signing key.
+ *
+ * @param purpose the message to sign
+ * @param[out] pub set to the current public signing key of the mint
+ * @param[out] sig signature over purpose using current signing key
+ */
+void
+TMH_KS_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+ struct TALER_MintPublicKeyP *pub,
+ struct TALER_MintSignatureP *sig)
+
+{
+ struct TMH_KS_StateHandle *key_state;
+
+ key_state = TMH_KS_acquire ();
+ *pub = key_state->current_sign_key_issue.issue.signkey_pub;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv.eddsa_priv,
+ purpose,
+ &sig->eddsa_signature));
+ TMH_KS_release (key_state);
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_KS_handler_keys (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct TMH_KS_StateHandle *key_state;
+ struct MHD_Response *response;
+ int ret;
+
+ key_state = TMH_KS_acquire ();
+ response = MHD_create_response_from_buffer (strlen (key_state->keys_json),
+ key_state->keys_json,
+ MHD_RESPMEM_MUST_COPY);
+ TMH_KS_release (key_state);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ (void) MHD_add_response_header (response,
+ "Content-Type",
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/* end of taler-mint-httpd_keystate.c */
diff --git a/src/backend/taler-mint-httpd_keystate.h b/src/backend/taler-mint-httpd_keystate.h
new file mode 100644
index 00000000..62b041e9
--- /dev/null
+++ b/src/backend/taler-mint-httpd_keystate.h
@@ -0,0 +1,142 @@
+/*
+ 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/taler-mint-httpd_keystate.h
+ * @brief management of our private signing keys (denomination keys)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_KEYSTATE_H
+#define TALER_MINT_HTTPD_KEYSTATE_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+#include "taler_mintdb_lib.h"
+
+
+/**
+ * Snapshot of the (coin and signing)
+ * keys (including private keys) of the mint.
+ */
+struct TMH_KS_StateHandle;
+
+
+/**
+ * Acquire the key state of the mint. Updates keys if necessary.
+ * For every call to #TMH_KS_acquire(), a matching call
+ * to #TMH_KS_release() must be made.
+ *
+ * @return the key state
+ */
+struct TMH_KS_StateHandle *
+TMH_KS_acquire (void);
+
+
+/**
+ * Release key state, free if necessary (if reference count gets to zero).
+ *
+ * @param key_state the key state to release
+ */
+void
+TMH_KS_release (struct TMH_KS_StateHandle *key_state);
+
+
+/**
+ * Denomination key lookups can be for signing of fresh coins
+ * or to validate signatures on existing coins. As the validity
+ * periods for a key differ, the caller must specify which
+ * use is relevant for the current operation.
+ */
+enum TMH_KS_DenominationKeyUse {
+
+ /**
+ * The key is to be used for a /withdraw/sign or /refresh (mint)
+ * operation.
+ */
+ TMH_KS_DKU_WITHDRAW,
+
+ /**
+ * The key is to be usd for a /deposit or /refresh (melt) operation.
+ */
+ TMH_KS_DKU_DEPOSIT
+
+};
+
+
+/**
+ * Look up the issue for a denom public key. Note that the result
+ * is only valid while the @a key_state is not released!
+ *
+ * @param key_state state to look in
+ * @param denom_pub denomination public key
+ * @param use purpose for which the key is being located
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use)
+ */
+struct TALER_MINTDB_DenominationKeyIssueInformation *
+TMH_KS_denomination_key_lookup (const struct TMH_KS_StateHandle *key_state,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ enum TMH_KS_DenominationKeyUse use);
+
+
+/**
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
+ * restart if SIGHUP is received.
+ *
+ * @return #GNUNET_SYSERR on errors,
+ * #GNUNET_OK to terminate normally
+ * #GNUNET_NO to restart an update version of the binary
+ */
+int
+TMH_KS_loop (void);
+
+
+/**
+ * Sign the message in @a purpose with the mint's signing
+ * key.
+ *
+ * @param purpose the message to sign
+ * @param[out] pub set to the current public signing key of the mint
+ * @param[out] sig signature over purpose using current signing key
+ */
+void
+TMH_KS_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+ struct TALER_MintPublicKeyP *pub,
+ struct TALER_MintSignatureP *sig);
+
+
+/**
+ * Handle a "/keys" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_KS_handler_keys (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_mhd.c b/src/backend/taler-mint-httpd_mhd.c
new file mode 100644
index 00000000..b4e3c1f6
--- /dev/null
+++ b/src/backend/taler-mint-httpd_mhd.c
@@ -0,0 +1,152 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_mhd.c
+ * @brief helpers for MHD interaction; these are TALER_MINT_handler_ functions
+ * that generate simple MHD replies that do not require any real operations
+ * to be performed (error handling, static pages, etc.)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_responses.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct MHD_Response *response;
+ int ret;
+
+ if (0 == rh->data_size)
+ rh->data_size = strlen ((const char *) rh->data);
+ response = MHD_create_response_from_buffer (rh->data_size,
+ (void *) rh->data,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ const char *agpl =
+ "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
+ struct MHD_Response *response;
+ int ret;
+
+ response = MHD_create_response_from_buffer (strlen (agpl),
+ (void *) agpl,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_LOCATION,
+ "http://www.git.taler.net/?p=mint.git");
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ rh->response_code,
+ "{s:s}",
+ "error",
+ rh->data);
+}
+
+
+/* end of taler-mint-httpd_mhd.c */
diff --git a/src/backend/taler-mint-httpd_mhd.h b/src/backend/taler-mint-httpd_mhd.h
new file mode 100644
index 00000000..a9f575df
--- /dev/null
+++ b/src/backend/taler-mint-httpd_mhd.h
@@ -0,0 +1,111 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_mhd.h
+ * @brief helpers for MHD interaction, used to generate simple responses
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_MHD_H
+#define TALER_MINT_HTTPD_MHD_H
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from varargs.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param response_code HTTP response code to use
+ * @param do_cache can the response be cached? (0: no, 1: yes)
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TMH_MHD_helper_send_json_pack (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void *connection_cls,
+ int response_code,
+ int do_cache,
+ const char *fmt,
+ ...);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a rh.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-mint-httpd_parsing.c
new file mode 100644
index 00000000..1844fa88
--- /dev/null
+++ b/src/backend/taler-mint-httpd_parsing.c
@@ -0,0 +1,1123 @@
+/*
+ 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 taler-mint-httpd_parsing.c
+ * @brief functions to parse incoming requests (MHD arguments and JSON snippets)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_responses.h"
+
+
+/**
+ * Initial size for POST request buffer.
+ */
+#define REQUEST_BUFFER_INITIAL (2*1024)
+
+/**
+ * Maximum POST request size.
+ */
+#define REQUEST_BUFFER_MAX (1024*1024)
+
+
+/**
+ * Buffer for POST requests.
+ */
+struct Buffer
+{
+ /**
+ * Allocated memory
+ */
+ char *data;
+
+ /**
+ * Number of valid bytes in buffer.
+ */
+ size_t fill;
+
+ /**
+ * Number of allocated bytes in buffer.
+ */
+ size_t alloc;
+};
+
+
+/**
+ * Initialize a buffer.
+ *
+ * @param buf the buffer to initialize
+ * @param data the initial data
+ * @param data_size size of the initial data
+ * @param alloc_size size of the buffer
+ * @param max_size maximum size that the buffer can grow to
+ * @return a GNUnet result code
+ */
+static int
+buffer_init (struct Buffer *buf,
+ const void *data,
+ size_t data_size,
+ size_t alloc_size,
+ size_t max_size)
+{
+ if (data_size > max_size || alloc_size > max_size)
+ return GNUNET_SYSERR;
+ if (data_size > alloc_size)
+ alloc_size = data_size;
+ buf->data = GNUNET_malloc (alloc_size);
+ memcpy (buf->data, data, data_size);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Free the data in a buffer. Does *not* free
+ * the buffer object itself.
+ *
+ * @param buf buffer to de-initialize
+ */
+static void
+buffer_deinit (struct Buffer *buf)
+{
+ GNUNET_free (buf->data);
+ buf->data = NULL;
+}
+
+
+/**
+ * Append data to a buffer, growing the buffer if necessary.
+ *
+ * @param buf the buffer to append to
+ * @param data the data to append
+ * @param data_size the size of @a data
+ * @param max_size maximum size that the buffer can grow to
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO if the buffer can't accomodate for the new data
+ */
+static int
+buffer_append (struct Buffer *buf,
+ const void *data,
+ size_t data_size,
+ size_t max_size)
+{
+ if (buf->fill + data_size > max_size)
+ return GNUNET_NO;
+ if (data_size + buf->fill > buf->alloc)
+ {
+ char *new_buf;
+ size_t new_size = buf->alloc;
+ while (new_size < buf->fill + data_size)
+ new_size += 2;
+ if (new_size > max_size)
+ return GNUNET_NO;
+ new_buf = GNUNET_malloc (new_size);
+ memcpy (new_buf, buf->data, buf->fill);
+ GNUNET_free (buf->data);
+ buf->data = new_buf;
+ buf->alloc = new_size;
+ }
+ memcpy (buf->data + buf->fill, data, data_size);
+ buf->fill += data_size;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Release all memory allocated for the variable-size fields in
+ * the parser specification.
+ *
+ * @param spec specification to free
+ * @param spec_len number of items in @a spec to look at
+ */
+static void
+release_data (struct TMH_PARSE_FieldSpecification *spec,
+ unsigned int spec_len)
+{
+ unsigned int i;
+
+ for (i=0; i < spec_len; i++)
+ {
+ switch (spec[i].command)
+ {
+ case TMH_PARSE_JNC_FIELD:
+ GNUNET_break (0);
+ return;
+ case TMH_PARSE_JNC_INDEX:
+ GNUNET_break (0);
+ return;
+ case TMH_PARSE_JNC_RET_DATA:
+ break;
+ case TMH_PARSE_JNC_RET_DATA_VAR:
+ if (NULL != spec[i].destination)
+ {
+ GNUNET_free (* (void**) spec[i].destination);
+ *(void**) spec[i].destination = NULL;
+ *spec[i].destination_size_out = 0;
+ }
+ break;
+ case TMH_PARSE_JNC_RET_TYPED_JSON:
+ {
+ json_t *json;
+
+ json = *(json_t **) spec[i].destination;
+ if (NULL != json)
+ {
+ json_decref (json);
+ *(json_t**) spec[i].destination = NULL;
+ }
+ }
+ break;
+ case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY:
+ {
+ struct TALER_DenominationPublicKey *pk;
+
+ pk = spec[i].destination;
+ if (NULL != pk->rsa_public_key)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (pk->rsa_public_key);
+ pk->rsa_public_key = NULL;
+ }
+ }
+ break;
+ case TMH_PARSE_JNC_RET_RSA_SIGNATURE:
+ {
+ struct TALER_DenominationSignature *sig;
+
+ sig = spec[i].destination;
+ if (NULL != sig->rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (sig->rsa_signature);
+ sig->rsa_signature = NULL;
+ }
+ }
+ break;
+ case TMH_PARSE_JNC_RET_AMOUNT:
+ memset (spec[i].destination,
+ 0,
+ sizeof (struct TALER_Amount));
+ break;
+ case TMH_PARSE_JNC_RET_TIME_ABSOLUTE:
+ break;
+ case TMH_PARSE_JNC_RET_UINT64:
+ break;
+ }
+ }
+}
+
+
+/**
+ * Process a POST request containing a JSON object. This function
+ * realizes an MHD POST processor that will (incrementally) process
+ * JSON data uploaded to the HTTP server. It will store the required
+ * state in the @a con_cls, which must be cleaned up using
+ * #TMH_PARSE_post_cleanup_callback().
+ *
+ * @param connection the MHD connection
+ * @param con_cls the closure (points to a `struct Buffer *`)
+ * @param upload_data the POST data
+ * @param upload_data_size number of bytes in @a upload_data
+ * @param json the JSON object for a completed request
+ * @return
+ * #GNUNET_YES if json object was parsed or at least
+ * may be parsed in the future (call again);
+ * `*json` will be NULL if we need to be called again,
+ * and non-NULL if we are done.
+ * #GNUNET_NO is request incomplete or invalid
+ * (error message was generated)
+ * #GNUNET_SYSERR on internal error
+ * (we could not even queue an error message,
+ * close HTTP session with MHD_NO)
+ */
+int
+TMH_PARSE_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json)
+{
+ struct Buffer *r = *con_cls;
+
+ *json = NULL;
+ if (NULL == *con_cls)
+ {
+ /* We are seeing a fresh POST request. */
+ r = GNUNET_new (struct Buffer);
+ if (GNUNET_OK !=
+ buffer_init (r,
+ upload_data,
+ *upload_data_size,
+ REQUEST_BUFFER_INITIAL,
+ REQUEST_BUFFER_MAX))
+ {
+ *con_cls = NULL;
+ buffer_deinit (r);
+ GNUNET_free (r);
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "out of memory"))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ }
+ /* everything OK, wait for more POST data */
+ *upload_data_size = 0;
+ *con_cls = r;
+ return GNUNET_YES;
+ }
+ if (0 != *upload_data_size)
+ {
+ /* We are seeing an old request with more data available. */
+
+ if (GNUNET_OK !=
+ buffer_append (r,
+ upload_data,
+ *upload_data_size,
+ REQUEST_BUFFER_MAX))
+ {
+ /* Request too long */
+ *con_cls = NULL;
+ buffer_deinit (r);
+ GNUNET_free (r);
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_request_too_large (connection))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ }
+ /* everything OK, wait for more POST data */
+ *upload_data_size = 0;
+ return GNUNET_YES;
+ }
+
+ /* We have seen the whole request. */
+
+ *json = json_loadb (r->data,
+ r->fill,
+ 0,
+ NULL);
+ if (NULL == *json)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to parse JSON request body\n");
+ return (MHD_YES ==
+ TMH_RESPONSE_reply_invalid_json (connection))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ buffer_deinit (r);
+ GNUNET_free (r);
+ *con_cls = NULL;
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function called whenever we are done with a request
+ * to clean up our state.
+ *
+ * @param con_cls value as it was left by
+ * #TMH_PARSE_post_json(), to be cleaned up
+ */
+void
+TMH_PARSE_post_cleanup_callback (void *con_cls)
+{
+ struct Buffer *r = con_cls;
+
+ if (NULL != r)
+ {
+ buffer_deinit (r);
+ GNUNET_free (r);
+ }
+}
+
+
+/**
+ * Extract base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of data
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TMH_PARSE_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size)
+{
+ const char *str;
+
+ str = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ param_name);
+ if (NULL == str)
+ {
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_arg_missing (connection, param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ out_data,
+ out_size))
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_arg_invalid (connection, param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Extraxt variable-size base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing
+ * or the encoding is invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to allocate buffer and store the result
+ * @param[out] out_size set to the size of the buffer allocated in @a out_data
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TMH_PARSE_mhd_request_var_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void **out_data,
+ size_t *out_size)
+{
+ const char *str;
+ size_t slen;
+ size_t olen;
+ void *out;
+
+ str = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ param_name);
+ if (NULL == str)
+ {
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_arg_missing (connection, param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ }
+ slen = strlen (str);
+ olen = (slen * 5) / 8;
+ out = GNUNET_malloc (olen);
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ out,
+ olen))
+ {
+ GNUNET_free (out);
+ *out_size = 0;
+ return (MHD_NO ==
+ TMH_RESPONSE_reply_arg_invalid (connection, param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ }
+ *out_data = out;
+ *out_size = olen;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`)
+ * @return
+ * #GNUNET_YES if navigation was successful
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error (no response was generated,
+ * connection must be closed)
+ */
+int
+TMH_PARSE_navigate_json (struct MHD_Connection *connection,
+ const json_t *root,
+ ...)
+{
+ va_list argp;
+ int ret;
+ json_t *path; /* what's our current path from 'root'? */
+
+ path = json_array ();
+ va_start (argp, root);
+ ret = 2; /* just not any of the valid return values */
+ while (2 == ret)
+ {
+ enum TMH_PARSE_JsonNavigationCommand command
+ = va_arg (argp,
+ enum TMH_PARSE_JsonNavigationCommand);
+
+ switch (command)
+ {
+ case TMH_PARSE_JNC_FIELD:
+ {
+ const char *fname = va_arg(argp, const char *);
+
+ json_array_append_new (path,
+ json_string (fname));
+ root = json_object_get (root,
+ fname);
+ if (NULL == root)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s, s:O}",
+ "error", "missing field in JSON",
+ "field", fname,
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ break;
+
+ case TMH_PARSE_JNC_INDEX:
+ {
+ int fnum = va_arg(argp, int);
+
+ json_array_append_new (path,
+ json_integer (fnum));
+ root = json_array_get (root,
+ fnum);
+ if (NULL == root)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "missing index in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_DATA:
+ {
+ void *where = va_arg (argp, void *);
+ size_t len = va_arg (argp, size_t);
+ const char *str;
+ int res;
+
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "string expected",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ where, len);
+ if (GNUNET_OK != res)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_DATA_VAR:
+ {
+ void **where = va_arg (argp, void **);
+ size_t *len = va_arg (argp, size_t *);
+ const char *str;
+ int res;
+
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "json_string_value() failed"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ *len = (strlen (str) * 5) / 8;
+ if (NULL != where)
+ {
+ *where = GNUNET_malloc (*len);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ *where,
+ *len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (*where);
+ *where = NULL;
+ *len = 0;
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_TYPED_JSON:
+ {
+ int typ = va_arg (argp, int);
+ const json_t **r_json = va_arg (argp, const json_t **);
+
+ if ( (NULL == root) ||
+ ( (-1 != typ) &&
+ (json_typeof (root) != typ)) )
+ {
+ *r_json = NULL;
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:i, s:i, s:O}",
+ "error", "wrong JSON field type",
+ "type_expected", typ,
+ "type_actual", json_typeof (root),
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ *r_json = root;
+ json_incref ((json_t *) root);
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_UINT64:
+ {
+ uint64_t *r_u64 = va_arg (argp, uint64_t *);
+
+ if (json_typeof (root) != JSON_INTEGER)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s, s:i, s:O}",
+ "error", "wrong JSON field type",
+ "type_expected", "integer",
+ "type_actual", json_typeof (root),
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ *r_u64 = (uint64_t) json_integer_value (root);
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY:
+ {
+ struct TALER_DenominationPublicKey *where;
+ size_t len;
+ const char *str;
+ int res;
+ void *buf;
+
+ where = va_arg (argp,
+ struct TALER_DenominationPublicKey *);
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "string expected",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ len = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (len);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (buf);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ where->rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (buf,
+ len);
+ GNUNET_free (buf);
+ if (NULL == where->rsa_public_key)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed RSA public key in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ case TMH_PARSE_JNC_RET_RSA_SIGNATURE:
+ {
+ struct TALER_DenominationSignature *where;
+ size_t len;
+ const char *str;
+ int res;
+ void *buf;
+
+ where = va_arg (argp,
+ struct TALER_DenominationSignature *);
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "string expected",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ len = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (len);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_free (buf);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ where->rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (buf,
+ len);
+ GNUNET_free (buf);
+ if (NULL == where->rsa_signature)
+ {
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed RSA signature in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ case TMH_PARSE_JNC_RET_AMOUNT:
+ {
+ struct TALER_Amount *where = va_arg (argp, void *);
+
+ if (GNUNET_OK !=
+ TALER_json_to_amount ((json_t *) root,
+ where))
+ {
+ if (MHD_YES !=
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "Bad format",
+ "path", path))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+ if (0 != strcmp (where->currency,
+ TMH_mint_currency_string))
+ {
+ if (MHD_YES !=
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O, s:s}",
+ "error", "Currency not supported",
+ "path", path,
+ "currency", where->currency))
+ {
+ memset (where, 0, sizeof (struct TALER_Amount));
+ return GNUNET_SYSERR;
+ }
+ memset (where, 0, sizeof (struct TALER_Amount));
+ return GNUNET_NO;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ case TMH_PARSE_JNC_RET_TIME_ABSOLUTE:
+ {
+ struct GNUNET_TIME_Absolute *where = va_arg (argp, void *);
+
+ if (GNUNET_OK !=
+ TALER_json_to_abs ((json_t *) root,
+ where))
+ {
+ if (MHD_YES !=
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s, s:O}",
+ "error", "Bad format",
+ "hint", "expected absolute time",
+ "path", path))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ default:
+ GNUNET_break (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "unhandled value in switch"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ va_end (argp);
+ json_decref (path);
+ return ret;
+}
+
+
+/**
+ * Parse JSON object into components based on the given field
+ * specification.
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param spec field specification for the parser
+ * @return
+ * #GNUNET_YES if navigation was successful (caller is responsible
+ * for freeing allocated variable-size data using
+ * #TMH_PARSE_release_data() when done)
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TMH_PARSE_json_data (struct MHD_Connection *connection,
+ const json_t *root,
+ struct TMH_PARSE_FieldSpecification *spec)
+{
+ unsigned int i;
+ int ret;
+
+ ret = GNUNET_YES;
+ for (i=0; NULL != spec[i].field_name; i++)
+ {
+ if (GNUNET_YES != ret)
+ break;
+ switch (spec[i].command)
+ {
+ case TMH_PARSE_JNC_FIELD:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ case TMH_PARSE_JNC_INDEX:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ case TMH_PARSE_JNC_RET_DATA:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_DATA,
+ spec[i].destination,
+ spec[i].destination_size_in);
+ break;
+ case TMH_PARSE_JNC_RET_DATA_VAR:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_DATA_VAR,
+ (void **) spec[i].destination,
+ spec[i].destination_size_out);
+ break;
+ case TMH_PARSE_JNC_RET_TYPED_JSON:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_TYPED_JSON,
+ spec[i].type,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_RSA_SIGNATURE:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_RSA_SIGNATURE,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_AMOUNT:
+ GNUNET_assert (sizeof (struct TALER_Amount) ==
+ spec[i].destination_size_in);
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_AMOUNT,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_TIME_ABSOLUTE:
+ GNUNET_assert (sizeof (struct GNUNET_TIME_Absolute) ==
+ spec[i].destination_size_in);
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_TIME_ABSOLUTE,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_UINT64:
+ GNUNET_assert (sizeof (uint64_t) ==
+ spec[i].destination_size_in);
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_UINT64,
+ spec[i].destination);
+ break;
+ }
+ }
+ if (GNUNET_YES != ret)
+ release_data (spec,
+ i - 1);
+ return ret;
+}
+
+
+/**
+ * Release all memory allocated for the variable-size fields in
+ * the parser specification.
+ *
+ * @param spec specification to free
+ */
+void
+TMH_PARSE_release_data (struct TMH_PARSE_FieldSpecification *spec)
+{
+ unsigned int i;
+
+ for (i=0; NULL != spec[i].field_name; i++) ;
+ release_data (spec, i);
+}
+
+
+/**
+ * Generate line in parser specification for 64-bit integer
+ * given as an integer in JSON.
+ *
+ * @param field name of the field
+ * @param[out] u64 integer to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_uint64 (const char *field,
+ uint64_t *u64)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, (void *) u64, sizeof (uint64_t), NULL, TMH_PARSE_JNC_RET_UINT64, 0 };
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for JSON object value.
+ *
+ * @param field name of the field
+ * @param[out] jsonp address of pointer to JSON to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_object (const char *field,
+ json_t **jsonp)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_OBJECT };
+ *jsonp = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for JSON array value.
+ *
+ * @param field name of the field
+ * @param[out] jsonp address of JSON pointer to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_array (const char *field,
+ json_t **jsonp)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_ARRAY };
+ *jsonp = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for an absolute time.
+ *
+ * @param field name of the field
+ * @param[out] atime time to initialize
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_time_abs (const char *field,
+ struct GNUNET_TIME_Absolute *atime)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, atime, sizeof(struct GNUNET_TIME_Absolute), NULL, TMH_PARSE_JNC_RET_TIME_ABSOLUTE, 0 };
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for RSA public key.
+ *
+ * @param field name of the field
+ * @param[out] pk key to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_denomination_public_key (const char *field,
+ struct TALER_DenominationPublicKey *pk)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, pk, 0, NULL, TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, 0 };
+ pk->rsa_public_key = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for RSA public key.
+ *
+ * @param field name of the field
+ * @param sig the signature to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_denomination_signature (const char *field,
+ struct TALER_DenominationSignature *sig)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, sig, 0, NULL, TMH_PARSE_JNC_RET_RSA_SIGNATURE, 0 };
+ sig->rsa_signature = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for an amount.
+ *
+ * @param field name of the field
+ * @param amount a `struct TALER_Amount *` to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_amount (const char *field,
+ struct TALER_Amount *amount)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, amount, sizeof(struct TALER_Amount), NULL, TMH_PARSE_JNC_RET_AMOUNT, 0 };
+ memset (amount, 0, sizeof (struct TALER_Amount));
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for variable-size value.
+ *
+ * @param field name of the field
+ * @param[out] ptr pointer to initialize
+ * @param[out] ptr_size size to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_variable (const char *field,
+ void **ptr,
+ size_t *ptr_size)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, ptr, 0, ptr_size, TMH_PARSE_JNC_RET_DATA_VAR, 0 };
+ *ptr = NULL;
+ return ret;
+}
+
+/* end of taler-mint-httpd_parsing.c */
diff --git a/src/backend/taler-mint-httpd_parsing.h b/src/backend/taler-mint-httpd_parsing.h
new file mode 100644
index 00000000..a2cf4c46
--- /dev/null
+++ b/src/backend/taler-mint-httpd_parsing.h
@@ -0,0 +1,408 @@
+/*
+ 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 taler-mint-httpd_parsing.h
+ * @brief functions to parse incoming requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_PARSING_H
+#define TALER_MINT_HTTPD_PARSING_H
+
+#include <microhttpd.h>
+#include <jansson.h>
+#include "taler_util.h"
+
+
+/**
+ * Process a POST request containing a JSON object. This
+ * function realizes an MHD POST processor that will
+ * (incrementally) process JSON data uploaded to the HTTP
+ * server. It will store the required state in the
+ * "connection_cls", which must be cleaned up using
+ * #TMH_PARSE_post_cleanup_callback().
+ *
+ * @param connection the MHD connection
+ * @param con_cls the closure (points to a `struct Buffer *`)
+ * @param upload_data the POST data
+ * @param upload_data_size number of bytes in @a upload_data
+ * @param json the JSON object for a completed request
+ * @return
+ * #GNUNET_YES if json object was parsed or at least
+ * may be parsed in the future (call again);
+ * `*json` will be NULL if we need to be called again,
+ * and non-NULL if we are done.
+ * #GNUNET_NO is request incomplete or invalid
+ * (error message was generated)
+ * #GNUNET_SYSERR on internal error
+ * (we could not even queue an error message,
+ * close HTTP session with MHD_NO)
+ */
+int
+TMH_PARSE_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json);
+
+
+/**
+ * Function called whenever we are done with a request
+ * to clean up our state.
+ *
+ * @param con_cls value as it was left by
+ * #TMH_PARSE_post_json(), to be cleaned up
+ */
+void
+TMH_PARSE_post_cleanup_callback (void *con_cls);
+
+
+/**
+ * Constants for JSON navigation description.
+ */
+enum TMH_PARSE_JsonNavigationCommand
+{
+ /**
+ * Access a field.
+ * Param: const char *
+ */
+ TMH_PARSE_JNC_FIELD,
+
+ /**
+ * Access an array index.
+ * Param: int
+ */
+ TMH_PARSE_JNC_INDEX,
+
+ /**
+ * Return base32crockford encoded data of
+ * constant size.
+ * Params: (void *, size_t)
+ */
+ TMH_PARSE_JNC_RET_DATA,
+
+ /**
+ * Return base32crockford encoded data of
+ * variable size.
+ * Params: (void **, size_t *)
+ */
+ TMH_PARSE_JNC_RET_DATA_VAR,
+
+ /**
+ * Return a json object, which must be
+ * of the given type (JSON_* type constants,
+ * or -1 for any type).
+ * Params: (int, json_t **)
+ */
+ TMH_PARSE_JNC_RET_TYPED_JSON,
+
+ /**
+ * Return a `struct GNUNET_CRYPTO_rsa_PublicKey` which was
+ * encoded as variable-size base32crockford encoded data.
+ */
+ TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY,
+
+ /**
+ * Return a `struct GNUNET_CRYPTO_rsa_Signature` which was
+ * encoded as variable-size base32crockford encoded data.
+ */
+ TMH_PARSE_JNC_RET_RSA_SIGNATURE,
+
+ /**
+ * Return a `struct TALER_Amount` which was
+ * encoded within its own json object.
+ */
+ TMH_PARSE_JNC_RET_AMOUNT,
+
+ /**
+ * Return a `struct GNUNET_TIME_Absolute` which was
+ * encoded within its own json object.
+ * Param: struct GNUNET_TIME_Absolute *
+ */
+ TMH_PARSE_JNC_RET_TIME_ABSOLUTE,
+
+ /**
+ * Return a `uint64_t` which was
+ * encoded as a JSON integer.
+ * Param: uint64_t *
+ */
+ TMH_PARSE_JNC_RET_UINT64
+
+};
+
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`)
+ * @return
+ * #GNUNET_YES if navigation was successful
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TMH_PARSE_navigate_json (struct MHD_Connection *connection,
+ const json_t *root,
+ ...);
+
+
+/**
+ * @brief Specification for how to parse a JSON field.
+ */
+struct TMH_PARSE_FieldSpecification
+{
+ /**
+ * Name of the field. NULL only to terminate array.
+ */
+ const char *field_name;
+
+ /**
+ * Where to store the result. Must have exactly
+ * @e destination_size bytes, except if @e destination_size is zero.
+ * NULL to skip assignment (but check presence of the value).
+ */
+ void *destination;
+
+ /**
+ * How big should the result be, 0 for variable size. In
+ * this case, @e destination must be a "void **", pointing
+ * to a location that is currently NULL and is to be allocated.
+ */
+ size_t destination_size_in;
+
+ /**
+ * @e destination_size_out will then be set to the size of the
+ * value that was stored in @e destination (useful for
+ * variable-size allocations).
+ */
+ size_t *destination_size_out;
+
+ /**
+ * Navigation command to use to extract the value. Note that
+ * #TMH_PARSE_JNC_RET_DATA or #TMH_PARSE_JNC_RET_DATA_VAR must be used for @e
+ * destination_size_in and @e destination_size_out to have a
+ * meaning. #TMH_PARSE_JNC_FIELD and #TMH_PARSE_JNC_INDEX must not be used here!
+ */
+ enum TMH_PARSE_JsonNavigationCommand command;
+
+ /**
+ * JSON type to use, only meaningful in connection with a @e command
+ * value of #TMH_PARSE_JNC_RET_TYPED_JSON. Typical values are
+ * #JSON_ARRAY and #JSON_OBJECT.
+ */
+ int type;
+
+};
+
+
+/**
+ * Parse JSON object into components based on the given field
+ * specification.
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param spec field specification for the parser
+ * @return
+ * #GNUNET_YES if navigation was successful (caller is responsible
+ * for freeing allocated variable-size data using
+ * #TMH_PARSE_release_data() when done)
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TMH_PARSE_json_data (struct MHD_Connection *connection,
+ const json_t *root,
+ struct TMH_PARSE_FieldSpecification *spec);
+
+
+/**
+ * Release all memory allocated for the variable-size fields in
+ * the parser specification.
+ *
+ * @param spec specification to free
+ */
+void
+TMH_PARSE_release_data (struct TMH_PARSE_FieldSpecification *spec);
+
+
+/**
+ * Generate line in parser specification for fixed-size value.
+ *
+ * @param field name of the field
+ * @param value where to store the value
+ */
+#define TMH_PARSE_member_fixed(field,value) { field, value, sizeof (*value), NULL, TMH_PARSE_JNC_RET_DATA, 0 }
+
+
+/**
+ * Generate line in parser specification for variable-size value.
+ *
+ * @param field name of the field
+ * @param[out] ptr pointer to initialize
+ * @param[out] ptr_size size to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_variable (const char *field,
+ void **ptr,
+ size_t *ptr_size);
+
+
+/**
+ * Generate line in parser specification for 64-bit integer
+ * given as an integer in JSON.
+ *
+ * @param field name of the field
+ * @param[out] u64 integer to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_uint64 (const char *field,
+ uint64_t *u64);
+
+
+/**
+ * Generate line in parser specification for JSON array value.
+ *
+ * @param field name of the field
+ * @param[out] jsonp address of JSON pointer to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_array (const char *field,
+ json_t **jsonp);
+
+
+/**
+ * Generate line in parser specification for JSON object value.
+ *
+ * @param field name of the field
+ * @param[out] jsonp address of pointer to JSON to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_object (const char *field,
+ json_t **jsonp);
+
+
+/**
+ * Generate line in parser specification for RSA public key.
+ *
+ * @param field name of the field
+ * @param[out] pk key to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_denomination_public_key (const char *field,
+ struct TALER_DenominationPublicKey *pk);
+
+
+/**
+ * Generate line in parser specification for RSA public key.
+ *
+ * @param field name of the field
+ * @param sig the signature to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_denomination_signature (const char *field,
+ struct TALER_DenominationSignature *sig);
+
+
+/**
+ * Generate line in parser specification for an amount.
+ *
+ * @param field name of the field
+ * @param[out] amount a `struct TALER_Amount *` to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_amount (const char *field,
+ struct TALER_Amount *amount);
+
+
+/**
+ * Generate line in parser specification for an absolute time.
+ *
+ * @param field name of the field
+ * @param[out] atime time to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_time_abs (const char *field,
+ struct GNUNET_TIME_Absolute *atime);
+
+
+
+/**
+ * Generate line in parser specification indicating the end of the spec.
+ */
+#define TMH_PARSE_MEMBER_END { NULL, NULL, 0, NULL, TMH_PARSE_JNC_FIELD, 0 }
+
+
+/**
+ * Extraxt fixed-size base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing or
+ * invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to store the result
+ * @param out_size expected size of @a out_data
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TMH_PARSE_mhd_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size);
+
+
+/**
+ * Extraxt variable-size base32crockford encoded data from request.
+ *
+ * Queues an error response to the connection if the parameter is missing
+ * or the encoding is invalid.
+ *
+ * @param connection the MHD connection
+ * @param param_name the name of the parameter with the key
+ * @param[out] out_data pointer to allocate buffer and store the result
+ * @param[out] out_size set to the size of the buffer allocated in @a out_data
+ * @return
+ * #GNUNET_YES if the the argument is present
+ * #GNUNET_NO if the argument is absent or malformed
+ * #GNUNET_SYSERR on internal error (error response could not be sent)
+ */
+int
+TMH_PARSE_mhd_request_var_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void **out_data,
+ size_t *out_size);
+
+
+
+
+#endif /* TALER_MINT_HTTPD_PARSING_H */
diff --git a/src/backend/taler-mint-httpd_refresh.c b/src/backend/taler-mint-httpd_refresh.c
new file mode 100644
index 00000000..687fb998
--- /dev/null
+++ b/src/backend/taler-mint-httpd_refresh.c
@@ -0,0 +1,907 @@
+/*
+ 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 taler-mint-httpd_refresh.c
+ * @brief Handle /refresh/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_refresh.h"
+#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd_keystate.h"
+
+
+/**
+ * Handle a "/refresh/melt" request after the main JSON parsing has happened.
+ * We now need to validate the coins being melted and the session signature
+ * and then hand things of to execute the melt operation.
+ *
+ * @param connection the MHD connection to handle
+ * @param num_new_denoms number of coins to be created, size of y-dimension of @a commit_link array
+ * @param denom_pubs array of @a num_new_denoms keys
+ * @param coin_count number of coins to be melted, size of y-dimension of @a commit_coin array
+ * @param coin_melt_details array with @a coin_count entries with melting details
+ * @param session_hash hash over the data that the client commits to
+ * @param commit_coin 2d array of coin commitments (what the mint is to sign
+ * once the "/refres/reveal" of cut and choose is done)
+ * @param commit_link 2d array of coin link commitments (what the mint is
+ * to return via "/refresh/link" to enable linkage in the
+ * future)
+ * @return MHD result code
+ */
+static int
+handle_refresh_melt_binary (struct MHD_Connection *connection,
+ unsigned int num_new_denoms,
+ const struct TALER_DenominationPublicKey *denom_pubs,
+ unsigned int coin_count,
+ const struct TMH_DB_MeltDetails *coin_melt_details,
+ const struct GNUNET_HashCode *session_hash,
+ struct TALER_MINTDB_RefreshCommitCoin *const* commit_coin,
+ struct TALER_MINTDB_RefreshCommitLinkP *const* commit_link)
+{
+ unsigned int i;
+ struct TMH_KS_StateHandle *key_state;
+ struct TALER_MINTDB_DenominationKeyInformationP *dki;
+ struct TALER_Amount cost;
+ struct TALER_Amount total_cost;
+ struct TALER_Amount melt;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_melt;
+ struct TALER_Amount total_melt;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (TMH_mint_currency_string,
+ &total_cost));
+ key_state = TMH_KS_acquire ();
+ for (i=0;i<num_new_denoms;i++)
+ {
+ dki = &TMH_KS_denomination_key_lookup (key_state,
+ &denom_pubs[i],
+ TMH_KS_DKU_WITHDRAW)->issue;
+ TALER_amount_ntoh (&value,
+ &dki->properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &dki->properties.fee_withdraw);
+ if ( (GNUNET_OK !=
+ TALER_amount_add (&cost,
+ &value,
+ &fee_withdraw)) ||
+ (GNUNET_OK !=
+ TALER_amount_add (&total_cost,
+ &cost,
+ &total_cost)) )
+ {
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "cost calculation failure");
+ }
+ }
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (TMH_mint_currency_string,
+ &total_melt));
+ for (i=0;i<coin_count;i++)
+ {
+ /* calculate contribution of the i-th melt by subtracting
+ the fee; add the rest to the total_melt value */
+ dki = &TMH_KS_denomination_key_lookup (key_state,
+ &coin_melt_details[i].coin_info.denom_pub,
+ TMH_KS_DKU_DEPOSIT)->issue;
+ TALER_amount_ntoh (&fee_melt,
+ &dki->properties.fee_refresh);
+ if (GNUNET_OK !=
+ TALER_amount_subtract (&melt,
+ &coin_melt_details->melt_amount_with_fee,
+ &fee_melt))
+ {
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "Melt contribution below melting fee");
+ }
+ if (GNUNET_OK !=
+ TALER_amount_add (&total_melt,
+ &melt,
+ &total_melt))
+ {
+ TMH_KS_release (key_state);
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "balance calculation failure");
+ }
+ }
+ TMH_KS_release (key_state);
+ if (0 !=
+ TALER_amount_cmp (&total_cost,
+ &total_melt))
+ {
+ /* We require total value of coins being melted and
+ total value of coins being generated to match! */
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "value mismatch");
+ }
+ return TMH_DB_execute_refresh_melt (connection,
+ session_hash,
+ num_new_denoms,
+ denom_pubs,
+ coin_count,
+ coin_melt_details,
+ commit_coin,
+ commit_link);
+}
+
+
+/**
+ * Extract public coin information from a JSON object.
+ *
+ * @param connection the connection to send error responses to
+ * @param coin_info the JSON object to extract the coin info from
+ * @param[out] r_melt_detail set to details about the coin's melting permission (if valid)
+ * @return #GNUNET_YES if coin public info in JSON was valid
+ * #GNUNET_NO JSON was invalid, response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+get_coin_public_info (struct MHD_Connection *connection,
+ json_t *coin_info,
+ struct TMH_DB_MeltDetails *r_melt_detail)
+{
+ int ret;
+ struct TALER_CoinSpendSignatureP melt_sig;
+ struct TALER_DenominationSignature sig;
+ struct TALER_DenominationPublicKey pk;
+ struct TALER_Amount amount;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_fixed ("coin_pub", &r_melt_detail->coin_info.coin_pub),
+ TMH_PARSE_member_denomination_signature ("denom_sig", &sig),
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &pk),
+ TMH_PARSE_member_fixed ("confirm_sig", &melt_sig),
+ TMH_PARSE_member_amount ("value_with_fee", &amount),
+ TMH_PARSE_MEMBER_END
+ };
+
+ ret = TMH_PARSE_json_data (connection,
+ coin_info,
+ spec);
+ if (GNUNET_OK != ret)
+ return ret;
+ /* check mint signature on the coin */
+ r_melt_detail->coin_info.denom_sig = sig;
+ r_melt_detail->coin_info.denom_pub = pk;
+ if (GNUNET_OK !=
+ TALER_test_coin_valid (&r_melt_detail->coin_info))
+ {
+ TMH_PARSE_release_data (spec);
+ r_melt_detail->coin_info.denom_sig.rsa_signature = NULL;
+ r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL;
+ return (MHD_YES ==
+ TMH_RESPONSE_reply_signature_invalid (connection,
+ "denom_sig"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+ r_melt_detail->melt_sig = melt_sig;
+ r_melt_detail->melt_amount_with_fee = amount;
+ TMH_PARSE_release_data (spec);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Verify that the signature shows that this coin is to be melted into
+ * the given @a session_pub melting session, and that this is a valid
+ * coin (we know the denomination key and the signature on it is
+ * valid). Essentially, this does all of the per-coin checks that can
+ * be done before the transaction starts.
+ *
+ * @param connection the connection to send error responses to
+ * @param session_hash hash over refresh session the coin is melted into
+ * @param melt_detail details about the coin's melting permission (if valid)
+ * @return #GNUNET_YES if coin public info in JSON was valid
+ * #GNUNET_NO JSON was invalid, response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+verify_coin_public_info (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ const struct TMH_DB_MeltDetails *melt_detail)
+{
+ struct TALER_RefreshMeltCoinAffirmationPS body;
+ struct TMH_KS_StateHandle *key_state;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct TALER_Amount fee_refresh;
+
+ key_state = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (key_state,
+ &melt_detail->coin_info.denom_pub,
+ TMH_KS_DKU_DEPOSIT);
+ if (NULL == dki)
+ {
+ TMH_KS_release (key_state);
+ TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "denom_pub");
+ }
+ /* FIXME: need to check if denomination key is still
+ valid for issuing! (#3634) */
+ TALER_amount_ntoh (&fee_refresh,
+ &dki->issue.properties.fee_refresh);
+ body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+ body.session_hash = *session_hash;
+ TALER_amount_hton (&body.amount_with_fee,
+ &melt_detail->melt_amount_with_fee);
+ TALER_amount_hton (&body.melt_fee,
+ &fee_refresh);
+ body.coin_pub = melt_detail->coin_info.coin_pub;
+ if (TALER_amount_cmp (&fee_refresh,
+ &melt_detail->melt_amount_with_fee) < 0)
+ {
+ TMH_KS_release (key_state);
+ return (MHD_YES ==
+ TMH_RESPONSE_reply_external_error (connection,
+ "melt amount smaller than melting fee"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
+
+ TMH_KS_release (key_state);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
+ &body.purpose,
+ &melt_detail->melt_sig.eddsa_signature,
+ &melt_detail->coin_info.coin_pub.eddsa_pub))
+ {
+ if (MHD_YES !=
+ TMH_RESPONSE_reply_signature_invalid (connection,
+ "confirm_sig"))
+ return GNUNET_SYSERR;
+ return GNUNET_NO;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Release memory from the @a commit_coin array.
+ *
+ * @param commit_coin array to release
+ * @param kappa size of 1st dimension
+ * @param num_new_coins size of 2nd dimension
+ */
+static void
+free_commit_coins (struct TALER_MINTDB_RefreshCommitCoin **commit_coin,
+ unsigned int kappa,
+ unsigned int num_new_coins)
+{
+ unsigned int i;
+ unsigned int j;
+
+ for (i=0;i<kappa;i++)
+ {
+ if (NULL == commit_coin[i])
+ break;
+ for (j=0;j<num_new_coins;j++)
+ {
+ GNUNET_free_non_null (commit_coin[i][j].coin_ev);
+ GNUNET_free_non_null (commit_coin[i][j].refresh_link);
+ }
+ GNUNET_free (commit_coin[i]);
+ }
+}
+
+
+/**
+ * Release memory from the @a commit_link array.
+ *
+ * @param commit_link array to release
+ * @param kappa size of 1st dimension
+ * @param num_old_coins size of 2nd dimension
+ */
+static void
+free_commit_links (struct TALER_MINTDB_RefreshCommitLinkP **commit_link,
+ unsigned int kappa,
+ unsigned int num_old_coins)
+{
+ unsigned int i;
+
+ for (i=0;i<kappa;i++)
+ {
+ if (NULL == commit_link[i])
+ break;
+ GNUNET_free (commit_link[i]);
+ }
+}
+
+
+/**
+ * Handle a "/refresh/melt" request after the first parsing has happened.
+ * We now need to validate the coins being melted and the session signature
+ * and then hand things of to execute the melt operation. This function
+ * parses the JSON arrays and then passes processing on to
+ * #handle_refresh_melt_binary().
+ *
+ * @param connection the MHD connection to handle
+ * @param new_denoms array of denomination keys
+ * @param melt_coins array of coins to melt
+ * @param num_oldcoins number of coins that are being melted
+ * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of @a num_oldcoins transfer keys
+ * @param secret_encs #TALER_CNC_KAPPA-dimensional array of @a num_oldcoins secrets
+ * @param num_newcoins number of coins that the refresh will generate
+ * @param coin_evs #TALER_CNC_KAPPA-dimensional array of @a num_newcoins envelopes to sign
+ * @param link_encs #TALER_CNC_KAPPA-dimensional array of @a num_newcoins encrypted links
+ * @return MHD result code
+ */
+static int
+handle_refresh_melt_json (struct MHD_Connection *connection,
+ const json_t *new_denoms,
+ const json_t *melt_coins,
+ unsigned int num_oldcoins,
+ const json_t *transfer_pubs,
+ const json_t *secret_encs,
+ unsigned int num_newcoins,
+ const json_t *coin_evs,
+ const json_t *link_encs)
+
+{
+ int res;
+ unsigned int i;
+ unsigned int j;
+ struct TALER_DenominationPublicKey *denom_pubs;
+ unsigned int num_new_denoms;
+ struct TMH_DB_MeltDetails *coin_melt_details;
+ unsigned int coin_count;
+ struct GNUNET_HashCode session_hash;
+ struct GNUNET_HashContext *hash_context;
+ struct TALER_MINTDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
+ struct TALER_MINTDB_RefreshCommitLinkP *commit_link[TALER_CNC_KAPPA];
+
+ /* For the signature check, we hash most of the inputs together
+ (except for the signatures on the coins). */
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
+ num_new_denoms = json_array_size (new_denoms);
+ denom_pubs = GNUNET_malloc (num_new_denoms *
+ sizeof (struct TALER_DenominationPublicKey));
+ for (i=0;i<num_new_denoms;i++)
+ {
+ char *buf;
+ size_t buf_size;
+
+ res = TMH_PARSE_navigate_json (connection,
+ new_denoms,
+ TMH_PARSE_JNC_INDEX, (int) i,
+ TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY,
+ &denom_pubs[i].rsa_public_key);
+ if (GNUNET_OK != res)
+ {
+ for (j=0;j<i;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (denom_pubs);
+ return res;
+ }
+ buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs[i].rsa_public_key,
+ &buf);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ buf,
+ buf_size);
+ GNUNET_free (buf);
+ }
+
+ coin_count = json_array_size (melt_coins);
+ coin_melt_details = GNUNET_malloc (coin_count *
+ sizeof (struct TMH_DB_MeltDetails));
+ for (i=0;i<coin_count;i++)
+ {
+ /* decode JSON data on coin to melt */
+ struct TALER_AmountNBO melt_amount;
+
+ res = get_coin_public_info (connection,
+ json_array_get (melt_coins, i),
+ &coin_melt_details[i]);
+ if (GNUNET_OK != res)
+ {
+ for (j=0;j<i;j++)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (coin_melt_details[j].coin_info.denom_pub.rsa_public_key);
+ GNUNET_CRYPTO_rsa_signature_free (coin_melt_details[j].coin_info.denom_sig.rsa_signature);
+ }
+ for (j=0;j<num_new_denoms;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (coin_melt_details);
+ GNUNET_free (denom_pubs);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ /* Check that the client does not try to melt the same coin twice
+ into the same session! */
+ for (j=0;j<i;j++)
+ {
+ if (0 == memcmp (&coin_melt_details[i].coin_info.coin_pub,
+ &coin_melt_details[j].coin_info.coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP)))
+ {
+ for (j=0;j<i;j++)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (coin_melt_details[j].coin_info.denom_pub.rsa_public_key);
+ GNUNET_CRYPTO_rsa_signature_free (coin_melt_details[j].coin_info.denom_sig.rsa_signature);
+ }
+ for (j=0;j<num_new_denoms;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (coin_melt_details);
+ GNUNET_free (denom_pubs);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "melting same coin twice in same session is not allowed");
+ }
+ }
+ TALER_amount_hton (&melt_amount,
+ &coin_melt_details[i].melt_amount_with_fee);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &coin_melt_details[i].coin_info.coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP));
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &melt_amount,
+ sizeof (struct TALER_AmountNBO));
+
+ }
+
+ /* parse JSON arrays into 2d binary arrays and hash everything
+ together for the signature check */
+ memset (commit_coin, 0, sizeof (commit_coin));
+ memset (commit_link, 0, sizeof (commit_link));
+ for (i = 0; i < TALER_CNC_KAPPA; i++)
+ {
+ commit_coin[i] = GNUNET_malloc (num_newcoins *
+ sizeof (struct TALER_MINTDB_RefreshCommitCoin));
+ for (j = 0; j < num_newcoins; j++)
+ {
+ char *link_enc;
+ size_t link_enc_size;
+ struct TALER_MINTDB_RefreshCommitCoin *rcc = &commit_coin[i][j];
+
+ res = TMH_PARSE_navigate_json (connection,
+ coin_evs,
+ TMH_PARSE_JNC_INDEX, (int) i,
+ TMH_PARSE_JNC_INDEX, (int) j,
+ TMH_PARSE_JNC_RET_DATA_VAR,
+ &rcc->coin_ev,
+ &rcc->coin_ev_size);
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ free_commit_coins (commit_coin,
+ TALER_CNC_KAPPA,
+ num_newcoins);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ rcc->coin_ev,
+ rcc->coin_ev_size);
+ res = TMH_PARSE_navigate_json (connection,
+ link_encs,
+ TMH_PARSE_JNC_INDEX, (int) i,
+ TMH_PARSE_JNC_INDEX, (int) j,
+ TMH_PARSE_JNC_RET_DATA_VAR,
+ &link_enc,
+ &link_enc_size);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ free_commit_coins (commit_coin,
+ TALER_CNC_KAPPA,
+ num_newcoins);
+ GNUNET_free (link_enc);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ rcc->refresh_link
+ = TALER_refresh_link_encrypted_decode (link_enc,
+ link_enc_size);
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ link_enc,
+ link_enc_size);
+ GNUNET_free (link_enc);
+ }
+ }
+
+ for (i = 0; i < TALER_CNC_KAPPA; i++)
+ {
+ commit_link[i] = GNUNET_malloc (num_oldcoins *
+ sizeof (struct TALER_MINTDB_RefreshCommitLinkP));
+ for (j = 0; j < num_oldcoins; j++)
+ {
+ struct TALER_MINTDB_RefreshCommitLinkP *rcl = &commit_link[i][j];
+
+ res = TMH_PARSE_navigate_json (connection,
+ transfer_pubs,
+ TMH_PARSE_JNC_INDEX, (int) i,
+ TMH_PARSE_JNC_INDEX, (int) j,
+ TMH_PARSE_JNC_RET_DATA,
+ &rcl->transfer_pub,
+ sizeof (struct TALER_TransferPublicKeyP));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ free_commit_coins (commit_coin,
+ TALER_CNC_KAPPA,
+ num_newcoins);
+ free_commit_links (commit_link,
+ TALER_CNC_KAPPA,
+ num_oldcoins);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ res = TMH_PARSE_navigate_json (connection,
+ secret_encs,
+ TMH_PARSE_JNC_INDEX, (int) i,
+ TMH_PARSE_JNC_INDEX, (int) j,
+ TMH_PARSE_JNC_RET_DATA,
+ &rcl->shared_secret_enc,
+ sizeof (struct TALER_EncryptedLinkSecretP));
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (GNUNET_SYSERR != res);
+ GNUNET_CRYPTO_hash_context_abort (hash_context);
+ free_commit_coins (commit_coin,
+ TALER_CNC_KAPPA,
+ num_newcoins);
+ free_commit_links (commit_link,
+ TALER_CNC_KAPPA,
+ num_oldcoins);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ rcl,
+ sizeof (struct TALER_MINTDB_RefreshCommitLinkP));
+ }
+
+ }
+ GNUNET_CRYPTO_hash_context_finish (hash_context,
+ &session_hash);
+
+ for (i=0;i<coin_count;i++)
+ {
+ /* verify signatures on coins to melt */
+ res = verify_coin_public_info (connection,
+ &session_hash,
+ &coin_melt_details[i]);
+ if (GNUNET_OK != res)
+ {
+ res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ goto cleanup;
+ }
+ }
+
+ /* execute commit */
+ res = handle_refresh_melt_binary (connection,
+ num_new_denoms,
+ denom_pubs,
+ coin_count,
+ coin_melt_details,
+ &session_hash,
+ commit_coin,
+ commit_link);
+ cleanup:
+ free_commit_coins (commit_coin,
+ TALER_CNC_KAPPA,
+ num_newcoins);
+ free_commit_links (commit_link,
+ TALER_CNC_KAPPA,
+ num_oldcoins);
+ for (j=0;j<coin_count;j++)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (coin_melt_details[j].coin_info.denom_pub.rsa_public_key);
+ GNUNET_CRYPTO_rsa_signature_free (coin_melt_details[j].coin_info.denom_sig.rsa_signature);
+ }
+ for (j=0;j<num_new_denoms;j++)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
+ GNUNET_free (coin_melt_details);
+ GNUNET_free (denom_pubs);
+ return res;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request. Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TMH_DB_execute_refresh_melt().
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFRESH_handler_refresh_melt (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *root;
+ json_t *new_denoms;
+ json_t *melt_coins;
+ json_t *coin_evs;
+ json_t *link_encs;
+ json_t *transfer_pubs;
+ json_t *secret_encs;
+ unsigned int num_oldcoins;
+ unsigned int num_newcoins;
+ json_t *coin_detail;
+ int res;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_array ("new_denoms", &new_denoms),
+ TMH_PARSE_member_array ("melt_coins", &melt_coins),
+ TMH_PARSE_member_array ("coin_evs", &coin_evs),
+ TMH_PARSE_member_array ("link_encs", &link_encs),
+ TMH_PARSE_member_array ("transfer_pubs", &transfer_pubs),
+ TMH_PARSE_member_array ("secret_encs", &secret_encs),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == root) )
+ return MHD_YES;
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ json_decref (root);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+
+ /* Determine dimensionality of the request (kappa, #old and #new coins) */
+ if (TALER_CNC_KAPPA != json_array_size (coin_evs))
+ {
+ GNUNET_break_op (0);
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "coin_evs");
+ }
+ if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
+ {
+ GNUNET_break_op (0);
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "transfer_pubs");
+ }
+ res = TMH_PARSE_navigate_json (connection, coin_evs,
+ TMH_PARSE_JNC_INDEX, (int) 0,
+ TMH_PARSE_JNC_RET_DATA,
+ JSON_ARRAY, &coin_detail);
+ if (GNUNET_OK != res)
+ {
+ TMH_PARSE_release_data (spec);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ num_newcoins = json_array_size (coin_detail);
+ res = TMH_PARSE_navigate_json (connection,
+ transfer_pubs,
+ TMH_PARSE_JNC_INDEX, (int) 0,
+ TMH_PARSE_JNC_RET_DATA,
+ JSON_ARRAY, &coin_detail);
+ if (GNUNET_OK != res)
+ {
+ TMH_PARSE_release_data (spec);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ num_oldcoins = json_array_size (coin_detail);
+
+ res = handle_refresh_melt_json (connection,
+ new_denoms,
+ melt_coins,
+ num_oldcoins,
+ transfer_pubs,
+ secret_encs,
+ num_newcoins,
+ coin_evs,
+ link_encs);
+
+ TMH_PARSE_release_data (spec);
+ return res;
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request. Parses the given JSON
+ * transfer private keys and if successful, passes everything to
+ * #TMH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash identifying the melting session
+ * @param num_oldcoins length of the 2nd dimension of @a transfer_privs array
+ * @param tp_json private transfer keys in JSON format
+ * @return MHD result code
+ */
+static int
+handle_refresh_reveal_json (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int num_oldcoins,
+ const json_t *tp_json)
+{
+ struct TALER_TransferPrivateKeyP *transfer_privs[TALER_CNC_KAPPA - 1];
+ unsigned int i;
+ unsigned int j;
+ int res;
+
+ for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
+ transfer_privs[i] = GNUNET_malloc (num_oldcoins *
+ sizeof (struct TALER_TransferPrivateKeyP));
+ res = GNUNET_OK;
+ for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
+ {
+ if (GNUNET_OK != res)
+ break;
+ for (j = 0; j < num_oldcoins; j++)
+ {
+ if (GNUNET_OK != res)
+ break;
+ res = TMH_PARSE_navigate_json (connection,
+ tp_json,
+ TMH_PARSE_JNC_INDEX, (int) i,
+ TMH_PARSE_JNC_INDEX, (int) j,
+ TMH_PARSE_JNC_RET_DATA,
+ &transfer_privs[i][j],
+ sizeof (struct TALER_TransferPrivateKeyP));
+ }
+ }
+ if (GNUNET_OK != res)
+ res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ else
+ res = TMH_DB_execute_refresh_reveal (connection,
+ session_hash,
+ num_oldcoins,
+ transfer_privs);
+ for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
+ GNUNET_free (transfer_privs[i]);
+ return res;
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request. This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/melt". This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TMH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFRESH_handler_refresh_reveal (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct GNUNET_HashCode session_hash;
+ int res;
+ unsigned int num_oldcoins;
+ json_t *reveal_detail;
+ json_t *root;
+ json_t *transfer_privs;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_fixed ("session_hash", &session_hash),
+ TMH_PARSE_member_array ("transfer_privs", &transfer_privs),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == root) )
+ return MHD_YES;
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ json_decref (root);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+
+ /* Determine dimensionality of the request (kappa and #old coins) */
+ /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
+ if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
+ {
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_invalid (connection,
+ "transfer_privs");
+ }
+ res = TMH_PARSE_navigate_json (connection,
+ transfer_privs,
+ TMH_PARSE_JNC_INDEX, 0,
+ TMH_PARSE_JNC_RET_TYPED_JSON,
+ JSON_ARRAY,
+ &reveal_detail);
+ if (GNUNET_OK != res)
+ {
+ TMH_PARSE_release_data (spec);
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ }
+ num_oldcoins = json_array_size (reveal_detail);
+ res = handle_refresh_reveal_json (connection,
+ &session_hash,
+ num_oldcoins,
+ transfer_privs);
+ TMH_PARSE_release_data (spec);
+ return res;
+}
+
+
+/**
+ * Handle a "/refresh/link" request. Note that for "/refresh/link"
+ * we do use a simple HTTP GET, and a HTTP POST!
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFRESH_handler_refresh_link (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ int res;
+
+ res = TMH_PARSE_mhd_request_arg_data (connection,
+ "coin_pub",
+ &coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP));
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if (GNUNET_OK != res)
+ return MHD_YES;
+ return TMH_DB_execute_refresh_link (connection,
+ &coin_pub);
+}
+
+
+/* end of taler-mint-httpd_refresh.c */
diff --git a/src/backend/taler-mint-httpd_refresh.h b/src/backend/taler-mint-httpd_refresh.h
new file mode 100644
index 00000000..8fe12a27
--- /dev/null
+++ b/src/backend/taler-mint-httpd_refresh.h
@@ -0,0 +1,94 @@
+/*
+ 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 taler-mint-httpd_refresh.h
+ * @brief Handle /refresh/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_REFRESH_H
+#define TALER_MINT_HTTPD_REFRESH_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Handle a "/refresh/melt" request. Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TMH_DB_execute_refresh_melt().
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFRESH_handler_refresh_melt (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/reveal" request. This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/commit". This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TMH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFRESH_handler_refresh_reveal (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/refresh/link" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_REFRESH_handler_refresh_link (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_responses.c b/src/backend/taler-mint-httpd_responses.c
new file mode 100644
index 00000000..57b233e7
--- /dev/null
+++ b/src/backend/taler-mint-httpd_responses.c
@@ -0,0 +1,997 @@
+/*
+ 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 taler-mint-httpd_responses.c
+ * @brief API for generating the various replies of the mint; these
+ * functions are called TMH_RESPONSE_reply_ and they generate
+ * and queue MHD response objects for a given connection.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-mint-httpd_responses.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler-mint-httpd_keystate.h"
+
+
+/**
+ * Send JSON object as response.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param response_code the http response code
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
+ const json_t *json,
+ unsigned int response_code)
+{
+ struct MHD_Response *resp;
+ char *json_str;
+ int ret;
+
+ json_str = json_dumps (json, JSON_INDENT(2));
+ GNUNET_assert (NULL != json_str);
+ resp = MHD_create_response_from_buffer (strlen (json_str), json_str,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL == resp)
+ {
+ free (json_str);
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ (void) MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ "application/json");
+ ret = MHD_queue_response (connection,
+ response_code,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from a format string and varargs.
+ *
+ * @param connection the MHD connection to handle
+ * @param response_code HTTP response code to use
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
+ unsigned int response_code,
+ const char *fmt,
+ ...)
+{
+ json_t *json;
+ va_list argp;
+ int ret;
+ json_error_t jerror;
+
+ va_start (argp, fmt);
+ json = json_vpack_ex (&jerror, 0, fmt, argp);
+ va_end (argp);
+ if (NULL == json)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to pack JSON with format `%s': %s\n",
+ fmt,
+ jerror.text);
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ ret = TMH_RESPONSE_reply_json (connection,
+ json,
+ response_code);
+ json_decref (json);
+ return ret;
+}
+
+
+/**
+ * Send a response indicating an invalid argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s}",
+ "error", "invalid parameter",
+ "parameter", param_name);
+}
+
+
+/**
+ * Send a response indicating an argument refering to a
+ * resource unknown to the mint (i.e. unknown reserve or
+ * denomination key).
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_unknown (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s, s:s}",
+ "error", "unknown entity referenced",
+ "parameter", param_name);
+}
+
+
+/**
+ * Send a response indicating an invalid signature.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_signature_invalid (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ "{s:s, s:s}",
+ "error", "invalid signature",
+ "parameter", param_name);
+}
+
+
+/**
+ * Send a response indicating a missing argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is missing
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
+ const char *param_name)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{ s:s, s:s}",
+ "error", "missing parameter",
+ "parameter", param_name);
+}
+
+
+/**
+ * Send a response indicating permission denied.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about why access was denied
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_permission_denied (struct MHD_Connection *connection,
+ const char *hint)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_FORBIDDEN,
+ "{s:s, s:s}",
+ "error", "permission denied",
+ "hint", hint);
+}
+
+
+/**
+ * Send a response indicating an internal error.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about the internal error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
+ const char *hint)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ "{s:s, s:s}",
+ "error", "internal error",
+ "hint", hint);
+}
+
+
+/**
+ * Send a response indicating an external error.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about the error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
+ const char *hint)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s}",
+ "error", "client error",
+ "hint", hint);
+}
+
+
+/**
+ * Send a response indicating an error committing a
+ * transaction (concurrent interference).
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_commit_error (struct MHD_Connection *connection)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error", "commit failure");
+}
+
+
+/**
+ * Send a response indicating a failure to talk to the Mint's
+ * database.
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_internal_db_error (struct MHD_Connection *connection)
+{
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "Failed to connect to database");
+}
+
+
+/**
+ * Send a response indicating that the request was too big.
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection)
+{
+ struct MHD_Response *resp;
+ int ret;
+
+ resp = MHD_create_response_from_buffer (0,
+ NULL,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == resp)
+ return MHD_NO;
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
+
+
+/**
+ * Send a response indicating that the JSON was malformed.
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s}",
+ "error",
+ "invalid json");
+}
+
+
+/**
+ * Send confirmation of deposit success to client. This function
+ * will create a signed message affirming the given information
+ * and return it to the client. By this, the mint affirms that
+ * the coin had sufficient (residual) value for the specified
+ * transaction and that it will execute the requested deposit
+ * operation with the given wiring details.
+ *
+ * @param connection connection to the client
+ * @param coin_pub public key of the coin
+ * @param h_wire hash of wire details
+ * @param h_contract hash of contract details
+ * @param transaction_id transaction ID
+ * @param timestamp client's timestamp
+ * @param refund_deadline until when this deposit be refunded
+ * @param merchant merchant public key
+ * @param amount_without_fee fraction of coin value to deposit, without the fee
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ struct GNUNET_TIME_Absolute timestamp,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ const struct TALER_MerchantPublicKeyP *merchant,
+ const struct TALER_Amount *amount_without_fee)
+{
+ struct TALER_DepositConfirmationPS dc;
+ struct TALER_MintPublicKeyP pub;
+ struct TALER_MintSignatureP sig;
+
+ dc.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT);
+ dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
+ dc.h_contract = *h_contract;
+ dc.h_wire = *h_wire;
+ dc.transaction_id = GNUNET_htonll (transaction_id);
+ dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+ TALER_amount_hton (&dc.amount_without_fee,
+ amount_without_fee);
+ dc.coin_pub = *coin_pub;
+ dc.merchant = *merchant;
+ TMH_KS_sign (&dc.purpose,
+ &pub,
+ &sig);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s, s:o, s:o}",
+ "status", "DEPOSIT_OK",
+ "sig", TALER_json_from_data (&sig,
+ sizeof (sig)),
+ "pub", TALER_json_from_data (&pub,
+ sizeof (pub)));
+}
+
+
+/**
+ * Compile the transaction history of a coin into a JSON object.
+ *
+ * @param tl transaction history to JSON-ify
+ * @return json representation of the @a rh
+ */
+static json_t *
+compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl)
+{
+ json_t *transaction;
+ const char *type;
+ struct TALER_Amount value;
+ json_t *history;
+ const struct TALER_MINTDB_TransactionList *pos;
+
+ history = json_array ();
+ for (pos = tl; NULL != pos; pos = pos->next)
+ {
+ switch (pos->type)
+ {
+ case TALER_MINTDB_TT_DEPOSIT:
+ {
+ struct TALER_DepositRequestPS dr;
+ const struct TALER_MINTDB_Deposit *deposit = pos->details.deposit;
+
+ type = "deposit";
+ value = deposit->amount_with_fee;
+ dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
+ dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
+ dr.h_contract = deposit->h_contract;
+ dr.h_wire = deposit->h_wire;
+ dr.timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp);
+ dr.refund_deadline = GNUNET_TIME_absolute_hton (deposit->refund_deadline);
+ dr.transaction_id = GNUNET_htonll (deposit->transaction_id);
+ TALER_amount_hton (&dr.amount_with_fee,
+ &deposit->amount_with_fee);
+ TALER_amount_hton (&dr.deposit_fee,
+ &deposit->deposit_fee);
+ dr.merchant = deposit->merchant_pub;
+ dr.coin_pub = deposit->coin.coin_pub;
+ transaction = TALER_json_from_eddsa_sig (&dr.purpose,
+ &deposit->csig.eddsa_signature);
+ break;
+ }
+ case TALER_MINTDB_TT_REFRESH_MELT:
+ {
+ struct TALER_RefreshMeltCoinAffirmationPS ms;
+ const struct TALER_MINTDB_RefreshMelt *melt = pos->details.melt;
+
+ type = "melt";
+ value = melt->amount_with_fee;
+ ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+ ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
+ ms.session_hash = melt->session_hash;
+ TALER_amount_hton (&ms.amount_with_fee,
+ &melt->amount_with_fee);
+ TALER_amount_hton (&ms.melt_fee,
+ &melt->melt_fee);
+ ms.coin_pub = melt->coin.coin_pub;
+ transaction = TALER_json_from_eddsa_sig (&ms.purpose,
+ &melt->coin_sig.eddsa_signature);
+ }
+ break;
+ case TALER_MINTDB_TT_LOCK:
+ {
+ type = "lock";
+ value = pos->details.lock->amount;
+ transaction = NULL;
+ GNUNET_break (0); /* #3625: Lock NOT implemented! */
+ break;
+ }
+ default:
+ GNUNET_assert (0);
+ }
+ json_array_append_new (history,
+ json_pack ("{s:s, s:o, s:o}",
+ "type", type,
+ "amount", TALER_json_from_amount (&value),
+ "signature", transaction));
+ }
+ return history;
+}
+
+
+/**
+ * Send proof that a /deposit request is invalid to client. This
+ * function will create a message with all of the operations affecting
+ * the coin that demonstrate that the coin has insufficient value.
+ *
+ * @param connection connection to the client
+ * @param tl transaction list to use to build reply
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_TransactionList *tl)
+{
+ json_t *history;
+
+ history = compile_transaction_history (tl);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_FORBIDDEN,
+ "{s:s, s:o}",
+ "error", "insufficient funds",
+ "history", history);
+}
+
+
+/**
+ * Compile the history of a reserve into a JSON object
+ * and calculate the total balance.
+ *
+ * @param rh reserve history to JSON-ify
+ * @param[out] balance set to current reserve balance
+ * @return json representation of the @a rh, NULL on error
+ */
+static json_t *
+compile_reserve_history (const struct TALER_MINTDB_ReserveHistory *rh,
+ struct TALER_Amount *balance)
+{
+ struct TALER_Amount deposit_total;
+ struct TALER_Amount withdraw_total;
+ struct TALER_Amount value;
+ json_t *json_history;
+ json_t *transaction;
+ int ret;
+ const struct TALER_MINTDB_ReserveHistory *pos;
+ struct TALER_WithdrawRequestPS wr;
+
+ json_history = json_array ();
+ ret = 0;
+ for (pos = rh; NULL != pos; pos = pos->next)
+ {
+ switch (pos->type)
+ {
+ case TALER_MINTDB_RO_BANK_TO_MINT:
+ if (0 == ret)
+ deposit_total = pos->details.bank->amount;
+ else
+ if (GNUNET_OK !=
+ TALER_amount_add (&deposit_total,
+ &deposit_total,
+ &pos->details.bank->amount))
+ {
+ json_decref (json_history);
+ return NULL;
+ }
+ ret = 1;
+ json_array_append_new (json_history,
+ json_pack ("{s:s, s:O, s:o}",
+ "type", "DEPOSIT",
+ "wire", pos->details.bank->wire,
+ "amount", TALER_json_from_amount (&pos->details.bank->amount)));
+ break;
+ case TALER_MINTDB_RO_WITHDRAW_COIN:
+ break;
+ }
+ }
+
+ ret = 0;
+ for (pos = rh; NULL != pos; pos = pos->next)
+ {
+ switch (pos->type)
+ {
+ case TALER_MINTDB_RO_BANK_TO_MINT:
+ break;
+ case TALER_MINTDB_RO_WITHDRAW_COIN:
+ value = pos->details.withdraw->amount_with_fee;
+ if (0 == ret)
+ {
+ withdraw_total = value;
+ }
+ else
+ {
+ if (GNUNET_OK !=
+ TALER_amount_add (&withdraw_total,
+ &withdraw_total,
+ &value))
+ {
+ json_decref (json_history);
+ return NULL;
+ }
+ }
+ ret = 1;
+ wr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+ wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
+ wr.reserve_pub = pos->details.withdraw->reserve_pub;
+ TALER_amount_hton (&wr.amount_with_fee,
+ &value);
+ TALER_amount_hton (&wr.withdraw_fee,
+ &pos->details.withdraw->withdraw_fee);
+ GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key,
+ &wr.h_denomination_pub);
+ wr.h_coin_envelope = pos->details.withdraw->h_coin_envelope;
+
+ transaction = TALER_json_from_eddsa_sig (&wr.purpose,
+ &pos->details.withdraw->reserve_sig.eddsa_signature);
+
+ json_array_append_new (json_history,
+ json_pack ("{s:s, s:o, s:o}",
+ "type", "WITHDRAW",
+ "signature", transaction,
+ "amount", TALER_json_from_amount (&value)));
+ break;
+ }
+ }
+ if (0 == ret)
+ {
+ /* did not encounter any withdraw operations, set to zero */
+ TALER_amount_get_zero (deposit_total.currency,
+ &withdraw_total);
+ }
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (balance,
+ &deposit_total,
+ &withdraw_total))
+ {
+ GNUNET_break (0);
+ json_decref (json_history);
+ return NULL;
+ }
+
+ return json_history;
+}
+
+
+/**
+ * Send reserve status information to client.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_ReserveHistory *rh)
+{
+ json_t *json_balance;
+ json_t *json_history;
+ struct TALER_Amount balance;
+
+ json_history = compile_reserve_history (rh,
+ &balance);
+ if (NULL == json_history)
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "balance calculation failure");
+ json_balance = TALER_json_from_amount (&balance);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o}",
+ "balance", json_balance,
+ "history", json_history);
+}
+
+
+/**
+ * Send reserve status information to client with the
+ * message that we have insufficient funds for the
+ * requested /withdraw/sign operation.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_ReserveHistory *rh)
+{
+ json_t *json_balance;
+ json_t *json_history;
+ struct TALER_Amount balance;
+
+ json_history = compile_reserve_history (rh,
+ &balance);
+ if (NULL == json_history)
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "balance calculation failure");
+ json_balance = TALER_json_from_amount (&balance);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_PAYMENT_REQUIRED,
+ "{s:s, s:o, s:o}",
+ "error", "Insufficient funds",
+ "balance", json_balance,
+ "history", json_history);
+}
+
+
+/**
+ * Send blinded coin information to client.
+ *
+ * @param connection connection to the client
+ * @param collectable blinded coin to return
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_CollectableBlindcoin *collectable)
+{
+ json_t *sig_json;
+
+ sig_json = TALER_json_from_rsa_signature (collectable->sig.rsa_signature);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o}",
+ "ev_sig", sig_json);
+}
+
+
+/**
+ * Send a response for a failed "/refresh/melt" request. The
+ * transaction history of the given coin demonstrates that the
+ * @a residual value of the coin is below the @a requested
+ * contribution of the coin for the melt. Thus, the mint
+ * refuses the melt operation.
+ *
+ * @param connection the connection to send the response to
+ * @param coin_pub public key of the coin
+ * @param coin_value original value of the coin
+ * @param tl transaction history for the coin
+ * @param requested how much this coin was supposed to contribute
+ * @param residual remaining value of the coin (after subtracting @a tl)
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_Amount coin_value,
+ struct TALER_MINTDB_TransactionList *tl,
+ struct TALER_Amount requested,
+ struct TALER_Amount residual)
+{
+ json_t *history;
+
+ history = compile_transaction_history (tl);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:s, s:o, s:o, s:o, s:o, s:o}",
+ "error", "insufficient funds",
+ "coin-pub", TALER_json_from_data (coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP)),
+ "original-value", TALER_json_from_amount (&coin_value),
+ "residual-value", TALER_json_from_amount (&residual),
+ "requested-value", TALER_json_from_amount (&requested),
+ "history", history);
+}
+
+
+/**
+ * Send a response to a "/refresh/melt" request.
+ *
+ * @param connection the connection to send the response to
+ * @param session_hash hash of the refresh session
+ * @param noreveal_index which index will the client not have to reveal
+ * @return a MHD status code
+ */
+int
+TMH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t noreveal_index)
+{
+ struct TALER_RefreshMeltConfirmationPS body;
+ struct TALER_MintPublicKeyP pub;
+ struct TALER_MintSignatureP sig;
+ json_t *sig_json;
+
+ body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
+ body.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_MELT);
+ body.session_hash = *session_hash;
+ body.noreveal_index = htons (noreveal_index);
+ TMH_KS_sign (&body.purpose,
+ &pub,
+ &sig);
+ sig_json = TALER_json_from_data (&sig,
+ sizeof (sig));
+ GNUNET_assert (NULL != sig_json);
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:i, s:o, s:o}",
+ "noreveal_index", (int) noreveal_index,
+ "mint_sig", sig_json,
+ "mint_pub", TALER_json_from_data (&pub,
+ sizeof (pub)));
+}
+
+
+/**
+ * Send a response for "/refresh/reveal".
+ *
+ * @param connection the connection to send the response to
+ * @param num_newcoins number of new coins for which we reveal data
+ * @param sigs array of @a num_newcoins signatures revealed
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
+ unsigned int num_newcoins,
+ const struct TALER_DenominationSignature *sigs)
+{
+ int newcoin_index;
+ json_t *root;
+ json_t *list;
+ int ret;
+
+ root = json_object ();
+ list = json_array ();
+ json_object_set_new (root,
+ "ev_sigs",
+ list);
+ for (newcoin_index = 0; newcoin_index < num_newcoins; newcoin_index++)
+ json_array_append_new (list,
+ TALER_json_from_rsa_signature (sigs[newcoin_index].rsa_signature));
+ ret = TMH_RESPONSE_reply_json (connection,
+ root,
+ MHD_HTTP_OK);
+ json_decref (root);
+ return ret;
+}
+
+
+/**
+ * Send a response for a failed "/refresh/reveal", where the
+ * revealed value(s) do not match the original commitment.
+ *
+ * @param connection the connection to send the response to
+ * @param mc all information about the original commitment
+ * @param off offset in the array of kappa-commitments where
+ * the missmatch was detected
+ * @param j index of the coin for which the missmatch was
+ * detected
+ * @param missmatch_object name of the object that was
+ * bogus (i.e. "transfer key").
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_MeltCommitment *mc,
+ unsigned int off,
+ unsigned int j,
+ const char *missmatch_object)
+{
+ json_t *info_old;
+ json_t *info_new;
+ json_t *info_commit;
+ json_t *info_links;
+ unsigned int i;
+ unsigned int k;
+
+ info_old = json_array ();
+ for (i=0;i<mc->num_oldcoins;i++)
+ {
+ const struct TALER_MINTDB_RefreshMelt *rm;
+ json_t *rm_json;
+
+ rm = &mc->melts[i];
+ rm_json = json_object ();
+ json_object_set_new (rm_json,
+ "coin_sig",
+ TALER_json_from_data (&rm->coin_sig,
+ sizeof (struct TALER_CoinSpendSignatureP)));
+ json_object_set_new (rm_json,
+ "coin_pub",
+ TALER_json_from_data (&rm->coin.coin_pub,
+ sizeof (struct TALER_CoinSpendPublicKeyP)));
+ json_object_set_new (rm_json,
+ "melt_amount_with_fee",
+ TALER_json_from_amount (&rm->amount_with_fee));
+ json_object_set_new (rm_json,
+ "melt_fee",
+ TALER_json_from_amount (&rm->melt_fee));
+ json_array_append_new (info_old,
+ rm_json);
+ }
+ info_new = json_array ();
+ for (i=0;i<mc->num_newcoins;i++)
+ {
+ const struct TALER_DenominationPublicKey *pk;
+
+ pk = &mc->denom_pubs[i];
+ json_array_append_new (info_new,
+ TALER_json_from_rsa_public_key (pk->rsa_public_key));
+
+ }
+ info_commit = json_array ();
+ info_links = json_array ();
+ for (k=0;k<TALER_CNC_KAPPA;k++)
+ {
+ json_t *info_commit_k;
+ json_t *info_link_k;
+
+ info_commit_k = json_array ();
+ for (i=0;i<mc->num_newcoins;i++)
+ {
+ const struct TALER_MINTDB_RefreshCommitCoin *cc;
+ json_t *cc_json;
+
+ cc = &mc->commit_coins[k][i];
+ cc_json = json_object ();
+ json_object_set_new (cc_json,
+ "coin_ev",
+ TALER_json_from_data (cc->coin_ev,
+ cc->coin_ev_size));
+ json_object_set_new (cc_json,
+ "coin_priv_enc",
+ TALER_json_from_data (cc->refresh_link->coin_priv_enc,
+ sizeof (struct TALER_CoinSpendPrivateKeyP)));
+ json_object_set_new (cc_json,
+ "blinding_key_enc",
+ TALER_json_from_data (cc->refresh_link->blinding_key_enc,
+ cc->refresh_link->blinding_key_enc_size));
+
+ json_array_append_new (info_commit_k,
+ cc_json);
+ }
+ json_array_append_new (info_commit,
+ info_commit_k);
+ info_link_k = json_array ();
+ for (i=0;i<mc->num_newcoins;i++)
+ {
+ const struct TALER_MINTDB_RefreshCommitLinkP *cl;
+ json_t *cl_json;
+
+ cl = &mc->commit_links[k][i];
+ cl_json = json_object ();
+ json_object_set_new (cl_json,
+ "transfer_pub",
+ TALER_json_from_data (&cl->transfer_pub,
+ sizeof (struct TALER_TransferPublicKeyP)));
+ json_object_set_new (cl_json,
+ "shared_secret_enc",
+ TALER_json_from_data (&cl->shared_secret_enc,
+ sizeof (struct TALER_EncryptedLinkSecretP)));
+ json_array_append_new (info_link_k,
+ cl_json);
+ }
+ json_array_append_new (info_links,
+ info_link_k);
+ }
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_CONFLICT,
+ "{s:s, s:i, s:i, s:o, s:o, s:o, s:o, s:s}",
+ "error", "commitment violation",
+ "offset", (int) off,
+ "index", (int) j,
+ "oldcoin_infos", info_old,
+ "newcoin_infos", info_new,
+ "commit_infos", info_commit,
+ "link_infos", info_links,
+ "object", missmatch_object);
+}
+
+
+/**
+ * Send a response for "/refresh/link".
+ *
+ * @param connection the connection to send the response to
+ * @param num_sessions number of sessions the coin was used in
+ * @param sessions array of @a num_session entries with
+ * information for each session
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
+ unsigned int num_sessions,
+ const struct TMH_RESPONSE_LinkSessionInfo *sessions)
+{
+ json_t *root;
+ json_t *mlist;
+ int res;
+ unsigned int i;
+
+ mlist = json_array ();
+ for (i=0;i<num_sessions;i++)
+ {
+ const struct TALER_MINTDB_LinkDataList *pos;
+ json_t *list = json_array ();
+
+ for (pos = sessions[i].ldl; NULL != pos; pos = pos->next)
+ {
+ json_t *obj;
+
+ obj = json_object ();
+ json_object_set_new (obj,
+ "link_enc",
+ TALER_json_from_data (pos->link_data_enc->coin_priv_enc,
+ sizeof (struct TALER_CoinSpendPrivateKeyP) +
+ pos->link_data_enc->blinding_key_enc_size));
+ json_object_set_new (obj,
+ "denom_pub",
+ TALER_json_from_rsa_public_key (pos->denom_pub.rsa_public_key));
+ json_object_set_new (obj,
+ "ev_sig",
+ TALER_json_from_rsa_signature (pos->ev_sig.rsa_signature));
+ json_array_append_new (list,
+ obj);
+ }
+ root = json_object ();
+ json_object_set_new (root,
+ "new_coins",
+ list);
+ json_object_set_new (root,
+ "transfer_pub",
+ TALER_json_from_data (&sessions[i].transfer_pub,
+ sizeof (struct TALER_TransferPublicKeyP)));
+ json_object_set_new (root,
+ "secret_enc",
+ TALER_json_from_data (&sessions[i].shared_secret_enc,
+ sizeof (struct TALER_EncryptedLinkSecretP)));
+ json_array_append_new (mlist,
+ root);
+ }
+ res = TMH_RESPONSE_reply_json (connection,
+ mlist,
+ MHD_HTTP_OK);
+ json_decref (mlist);
+ return res;
+}
+
+
+/* end of taler-mint-httpd_responses.c */
diff --git a/src/backend/taler-mint-httpd_responses.h b/src/backend/taler-mint-httpd_responses.h
new file mode 100644
index 00000000..7afd0188
--- /dev/null
+++ b/src/backend/taler-mint-httpd_responses.h
@@ -0,0 +1,390 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_responses.h
+ * @brief API for generating the various replies of the mint; these
+ * functions are called TMH_RESPONSE_reply_ and they generate
+ * and queue MHD response objects for a given connection.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_RESPONSES_H
+#define TALER_MINT_HTTPD_RESPONSES_H
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler-mint-httpd.h"
+#include "taler-mint-httpd_db.h"
+
+
+/**
+ * Send JSON object as response.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param response_code the http response code
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
+ const json_t *json,
+ unsigned int response_code);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from a format string and varargs.
+ *
+ * @param connection the MHD connection to handle
+ * @param response_code HTTP response code to use
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
+ unsigned int response_code,
+ const char *fmt,
+ ...);
+
+
+/**
+ * Send a response indicating an invalid signature.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_signature_invalid (struct MHD_Connection *connection,
+ const char *param_name);
+
+
+/**
+ * Send a response indicating an invalid argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection,
+ const char *param_name);
+
+
+/**
+ * Send a response indicating an argument refering to a
+ * resource unknown to the mint (i.e. unknown reserve or
+ * denomination key).
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is invalid
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_unknown (struct MHD_Connection *connection,
+ const char *param_name);
+
+
+/**
+ * Send a response indicating a missing argument.
+ *
+ * @param connection the MHD connection to use
+ * @param param_name the parameter that is missing
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
+ const char *param_name);
+
+
+/**
+ * Send a response indicating permission denied.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about why access was denied
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_permission_denied (struct MHD_Connection *connection,
+ const char *hint);
+
+
+/**
+ * Send a response indicating an internal error.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about the internal error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
+ const char *hint);
+
+
+/**
+ * Send a response indicating an external error.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about the error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
+ const char *hint);
+
+
+/**
+ * Send a response indicating an error committing a
+ * transaction (concurrent interference).
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_commit_error (struct MHD_Connection *connection);
+
+
+/**
+ * Send a response indicating a failure to talk to the Mint's
+ * database.
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_internal_db_error (struct MHD_Connection *connection);
+
+
+/**
+ * Send a response indicating that the request was too big.
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection);
+
+
+/**
+ * Send a response indicating that the JSON was malformed.
+ *
+ * @param connection the MHD connection to use
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection);
+
+
+/**
+ * Send confirmation of deposit success to client. This function
+ * will create a signed message affirming the given information
+ * and return it to the client. By this, the mint affirms that
+ * the coin had sufficient (residual) value for the specified
+ * transaction and that it will execute the requested deposit
+ * operation with the given wiring details.
+ *
+ * @param connection connection to the client
+ * @param coin_pub public key of the coin
+ * @param h_wire hash of wire details
+ * @param h_contract hash of contract details
+ * @param transaction_id transaction ID
+ * @param timestamp client's timestamp
+ * @param refund_deadline until when this deposit be refunded
+ * @param merchant merchant public key
+ * @param amount_without_fee fraction of coin value to deposit (without fee)
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract,
+ uint64_t transaction_id,
+ struct GNUNET_TIME_Absolute timestamp,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ const struct TALER_MerchantPublicKeyP *merchant,
+ const struct TALER_Amount *amount_without_fee);
+
+
+/**
+ * Send proof that a /deposit request is invalid to client. This
+ * function will create a message with all of the operations affecting
+ * the coin that demonstrate that the coin has insufficient value.
+ *
+ * @param connection connection to the client
+ * @param tl transaction list to use to build reply
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_TransactionList *tl);
+
+
+/**
+ * Send reserve status information to client.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_ReserveHistory *rh);
+
+
+/**
+ * Send reserve status information to client with the
+ * message that we have insufficient funds for the
+ * requested /withdraw/sign operation.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_ReserveHistory *rh);
+
+
+/**
+ * Send blinded coin information to client.
+ *
+ * @param connection connection to the client
+ * @param collectable blinded coin to return
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_CollectableBlindcoin *collectable);
+
+
+/**
+ * Send a confirmation response to a "/refresh/melt" request.
+ *
+ * @param connection the connection to send the response to
+ * @param session_hash hash of the refresh session
+ * @param noreveal_index which index will the client not have to reveal
+ * @return a MHD status code
+ */
+int
+TMH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t noreveal_index);
+
+
+/**
+ * Send a response for a failed "/refresh/melt" request. The
+ * transaction history of the given coin demonstrates that the
+ * @a residual value of the coin is below the @a requested
+ * contribution of the coin for the melt. Thus, the mint
+ * refuses the melt operation.
+ *
+ * @param connection the connection to send the response to
+ * @param coin_pub public key of the coin
+ * @param coin_value original value of the coin
+ * @param tl transaction history for the coin
+ * @param requested how much this coin was supposed to contribute
+ * @param residual remaining value of the coin (after subtracting @a tl)
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_Amount coin_value,
+ struct TALER_MINTDB_TransactionList *tl,
+ struct TALER_Amount requested,
+ struct TALER_Amount residual);
+
+
+/**
+ * Send a response for "/refresh/reveal".
+ *
+ * @param connection the connection to send the response to
+ * @param num_newcoins number of new coins for which we reveal data
+ * @param sigs array of @a num_newcoins signatures revealed
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
+ unsigned int num_newcoins,
+ const struct TALER_DenominationSignature *sigs);
+
+
+/**
+ * Send a response for a failed "/refresh/reveal", where the
+ * revealed value(s) do not match the original commitment.
+ *
+ * @param connection the connection to send the response to
+ * @param mc all information about the original commitment
+ * @param off offset in the array of kappa-commitments where
+ * the missmatch was detected
+ * @param j index of the coin for which the missmatch was
+ * detected
+ * @param missmatch_object name of the object that was
+ * bogus (i.e. "transfer key").
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
+ const struct TALER_MINTDB_MeltCommitment *mc,
+ unsigned int off,
+ unsigned int j,
+ const char *missmatch_object);
+
+
+/**
+ * Information for each session a coin was melted into.
+ */
+struct TMH_RESPONSE_LinkSessionInfo
+{
+ /**
+ * Transfer public key of the coin.
+ */
+ struct TALER_TransferPublicKeyP transfer_pub;
+
+ /**
+ * Encrypted shared secret for decrypting the transfer secrets.
+ */
+ struct TALER_EncryptedLinkSecretP shared_secret_enc;
+
+ /**
+ * Linked data of coins being created in the session.
+ */
+ struct TALER_MINTDB_LinkDataList *ldl;
+
+};
+
+
+/**
+ * Send a response for "/refresh/link".
+ *
+ * @param connection the connection to send the response to
+ * @param num_sessions number of sessions the coin was used in
+ * @param sessions array of @a num_session entries with
+ * information for each session
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
+ unsigned int num_sessions,
+ const struct TMH_RESPONSE_LinkSessionInfo *sessions);
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_withdraw.c b/src/backend/taler-mint-httpd_withdraw.c
new file mode 100644
index 00000000..4f558164
--- /dev/null
+++ b/src/backend/taler-mint-httpd_withdraw.c
@@ -0,0 +1,180 @@
+/*
+ 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 taler-mint-httpd_withdraw.c
+ * @brief Handle /withdraw/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler-mint-httpd_withdraw.h"
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd_keystate.h"
+
+
+/**
+ * Handle a "/withdraw/status" request. Parses the
+ * given "reserve_pub" argument (which should contain the
+ * EdDSA public key of a reserve) and then respond with the
+ * status of the reserve.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_WITHDRAW_handler_withdraw_status (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct TALER_ReservePublicKeyP reserve_pub;
+ int res;
+
+ res = TMH_PARSE_mhd_request_arg_data (connection,
+ "reserve_pub",
+ &reserve_pub,
+ sizeof (struct TALER_ReservePublicKeyP));
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* internal error */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* parse error */
+ return TMH_DB_execute_withdraw_status (connection,
+ &reserve_pub);
+}
+
+
+/**
+ * Handle a "/withdraw/sign" request. Parses the "reserve_pub"
+ * EdDSA key of the reserve and the requested "denom_pub" which
+ * specifies the key/value of the coin to be withdrawn, and checks
+ * that the signature "reserve_sig" makes this a valid withdrawl
+ * request from the specified reserve. If so, the envelope
+ * with the blinded coin "coin_ev" is passed down to execute the
+ * withdrawl operation.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_WITHDRAW_handler_withdraw_sign (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ json_t *root;
+ struct TALER_WithdrawRequestPS wsrd;
+ int res;
+ struct TALER_DenominationPublicKey denomination_pub;
+ char *blinded_msg;
+ size_t blinded_msg_len;
+ struct TALER_Amount amount;
+ struct TALER_Amount amount_with_fee;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_ReserveSignatureP signature;
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
+ struct TMH_KS_StateHandle *ks;
+
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_variable ("coin_ev", (void **) &blinded_msg, &blinded_msg_len),
+ TMH_PARSE_member_fixed ("reserve_pub", &wsrd.reserve_pub),
+ TMH_PARSE_member_fixed ("reserve_sig", &signature),
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &denomination_pub),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ if ( (GNUNET_NO == res) || (NULL == root) )
+ return MHD_YES;
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+ json_decref (root);
+ if (GNUNET_OK != res)
+ return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ ks = TMH_KS_acquire ();
+ dki = TMH_KS_denomination_key_lookup (ks,
+ &denomination_pub,
+ TMH_KS_DKU_WITHDRAW);
+ if (NULL == dki)
+ {
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_arg_unknown (connection,
+ "denom_pub");
+ }
+ TALER_amount_ntoh (&amount,
+ &dki->issue.properties.value);
+ TALER_amount_ntoh (&fee_withdraw,
+ &dki->issue.properties.fee_withdraw);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_add (&amount_with_fee,
+ &amount,
+ &fee_withdraw));
+ TALER_amount_hton (&wsrd.amount_with_fee,
+ &amount_with_fee);
+ TALER_amount_hton (&wsrd.withdraw_fee,
+ &fee_withdraw);
+ TMH_KS_release (ks);
+ /* verify signature! */
+ wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
+ wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+
+ GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key,
+ &wsrd.h_denomination_pub);
+ GNUNET_CRYPTO_hash (blinded_msg,
+ blinded_msg_len,
+ &wsrd.h_coin_envelope);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
+ &wsrd.purpose,
+ &signature.eddsa_signature,
+ &wsrd.reserve_pub.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Client supplied invalid signature for /withdraw/sign request\n");
+ TMH_PARSE_release_data (spec);
+ return TMH_RESPONSE_reply_signature_invalid (connection,
+ "reserve_sig");
+ }
+ res = TMH_DB_execute_withdraw_sign (connection,
+ &wsrd.reserve_pub,
+ &denomination_pub,
+ blinded_msg,
+ blinded_msg_len,
+ &signature);
+ TMH_PARSE_release_data (spec);
+ return res;
+}
+
+/* end of taler-mint-httpd_withdraw.c */
diff --git a/src/backend/taler-mint-httpd_withdraw.h b/src/backend/taler-mint-httpd_withdraw.h
new file mode 100644
index 00000000..668178b1
--- /dev/null
+++ b/src/backend/taler-mint-httpd_withdraw.h
@@ -0,0 +1,73 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 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_withdraw.h
+ * @brief Handle /withdraw/ requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_WITHDRAW_H
+#define TALER_MINT_HTTPD_WITHDRAW_H
+
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+/**
+ * Handle a "/withdraw/status" request. Parses the
+ * given "reserve_pub" argument (which should contain the
+ * EdDSA public key of a reserve) and then respond with the
+ * status of the reserve.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_WITHDRAW_handler_withdraw_status (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Handle a "/withdraw/sign" request. Parses the "reserve_pub"
+ * EdDSA key of the reserve and the requested "denom_pub" which
+ * specifies the key/value of the coin to be withdrawn, and checks
+ * that the signature "reserve_sig" makes this a valid withdrawl
+ * request from the specified reserve. If so, the envelope
+ * with the blinded coin "coin_ev" is passed down to execute the
+ * withdrawl operation.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TMH_WITHDRAW_handler_withdraw_sign (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+#endif
diff --git a/src/backend/taler_amount_lib.h b/src/backend/taler_amount_lib.h
new file mode 100644
index 00000000..8661ed91
--- /dev/null
+++ b/src/backend/taler_amount_lib.h
@@ -0,0 +1,273 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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_amount_lib.h
+ * @brief amount-representation utility functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#ifndef TALER_AMOUNT_LIB_H
+#define TALER_AMOUNT_LIB_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0 /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include <gnunet/platform.h>
+
+
+/**
+ * @brief Number of characters (plus 1 for 0-termination) we use to
+ * represent currency names (i.e. EUR, USD, etc.). We use 8+4 for
+ * alignment in the `struct TALER_Amount`. The amount is typically an
+ * ISO 4217 currency code when an alpha-numeric 3-digit code is used.
+ * For regional currencies, the first character should be a "*" followed
+ * by a region-specific name (i.e. "*BRETAGNEFR").
+ */
+#define TALER_CURRENCY_LEN 12
+
+/**
+ * Taler currency length as a string.
+ */
+#define TALER_CURRENCY_LEN_STR "12"
+
+/**
+ * @brief The "fraction" value in a `struct TALER_Amount` represents which
+ * fraction of the "main" value?
+ *
+ * Note that we need sub-cent precision here as transaction fees might
+ * be that low, and as we want to support microdonations.
+ */
+#define TALER_AMOUNT_FRAC_BASE 1000000
+
+/**
+ * @brief How many digits behind the comma are required to represent the
+ * fractional value in human readable decimal format? Must match
+ * lg(#TALER_AMOUNT_FRAC_BASE).
+ */
+#define TALER_AMOUNT_FRAC_LEN 6
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+
+/**
+ * @brief Amount, encoded for network transmission.
+ */
+struct TALER_AmountNBO
+{
+ /**
+ * Value in the main currency, in NBO.
+ */
+ uint64_t value GNUNET_PACKED;
+
+ /**
+ * Additinal fractional value, in NBO.
+ */
+ uint32_t fraction GNUNET_PACKED;
+
+ /**
+ * Type of the currency being represented.
+ */
+ char currency[TALER_CURRENCY_LEN];
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * @brief Representation of monetary value in a given currency.
+ */
+struct TALER_Amount
+{
+ /**
+ * Value (numerator of fraction)
+ */
+ uint64_t value;
+
+ /**
+ * Fraction (denominator of fraction)
+ */
+ uint32_t fraction;
+
+ /**
+ * Currency string, left adjusted and padded with zeros. All zeros
+ * for "invalid" values.
+ */
+ char currency[TALER_CURRENCY_LEN];
+};
+
+
+/**
+ * Parse denomination description, in the format "T:V.F".
+ *
+ * @param str denomination description
+ * @param denom denomination to write the result to
+ * @return #GNUNET_OK if the string is a valid denomination specification,
+ * #GNUNET_SYSERR if it is invalid.
+ */
+int
+TALER_string_to_amount (const char *str,
+ struct TALER_Amount *denom);
+
+
+/**
+ * Get the value of "zero" in a particular currency.
+ *
+ * @param cur currency description
+ * @param denom denomination to write the result to
+ * @return #GNUNET_OK if @a cur is a valid currency specification,
+ * #GNUNET_SYSERR if it is invalid.
+ */
+int
+TALER_amount_get_zero (const char *cur,
+ struct TALER_Amount *denom);
+
+
+/**
+ * Convert amount from host to network representation.
+ *
+ * @param res where to store amount in network representation
+ * @param d amount in host representation
+ */
+void
+TALER_amount_hton (struct TALER_AmountNBO *res,
+ const struct TALER_Amount *d);
+
+
+/**
+ * Convert amount from network to host representation.
+ *
+ * @param res where to store amount in host representation
+ * @param dn amount in network representation
+ */
+void
+TALER_amount_ntoh (struct TALER_Amount *res,
+ const struct TALER_AmountNBO *dn);
+
+
+/**
+ * Compare the value/fraction of two amounts. Does not compare the currency.
+ * Comparing amounts of different currencies will cause the program to abort().
+ * If unsure, check with #TALER_amount_cmp_currency() first to be sure that
+ * the currencies of the two amounts are identical.
+ *
+ * @param a1 first amount
+ * @param a2 second amount
+ * @return result of the comparison
+ * -1 if `a1 < a2`
+ * 1 if `a1 > a2`
+ * 0 if `a1 == a2`.
+ */
+int
+TALER_amount_cmp (const struct TALER_Amount *a1,
+ const struct TALER_Amount *a2);
+
+
+/**
+ * Test if @a a1 and @a a2 are the same currency.
+ *
+ * @param a1 amount to test
+ * @param a2 amount to test
+ * @return #GNUNET_YES if @a a1 and @a a2 are the same currency
+ * #GNUNET_NO if the currencies are different
+ * #GNUNET_SYSERR if either amount is invalid
+ */
+int
+TALER_amount_cmp_currency (const struct TALER_Amount *a1,
+ const struct TALER_Amount *a2);
+
+
+/**
+ * Test if @a a1 and @a a2 are the same currency, NBO variant.
+ *
+ * @param a1 amount to test
+ * @param a2 amount to test
+ * @return #GNUNET_YES if @a a1 and @a a2 are the same currency
+ * #GNUNET_NO if the currencies are different
+ * #GNUNET_SYSERR if either amount is invalid
+ */
+int
+TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
+ const struct TALER_AmountNBO *a2);
+
+
+/**
+ * Perform saturating subtraction of amounts.
+ *
+ * @param diff where to store (@a a1 - @a a2), or invalid if @a a2 > @a a1
+ * @param a1 amount to subtract from
+ * @param a2 amount to subtract
+ * @return #GNUNET_OK if the subtraction worked,
+ * #GNUNET_NO if @a a1 = @a a2
+ * #GNUNET_SYSERR if @a a2 > @a a1 or currencies are incompatible;
+ * @a diff is set to invalid
+ */
+int
+TALER_amount_subtract (struct TALER_Amount *diff,
+ const struct TALER_Amount *a1,
+ const struct TALER_Amount *a2);
+
+
+/**
+ * Perform addition of amounts.
+ *
+ * @param sum where to store @a a1 + @a a2, set to "invalid" on overflow
+ * @param a1 first amount to add
+ * @param a2 second amount to add
+ * @return #GNUNET_OK if the addition worked,
+ * #GNUNET_SYSERR on overflow
+ */
+int
+TALER_amount_add (struct TALER_Amount *sum,
+ const struct TALER_Amount *a1,
+ const struct TALER_Amount *a2);
+
+
+/**
+ * Normalize the given amount.
+ *
+ * @param amount amount to normalize
+ * @return #GNUNET_OK if normalization worked
+ * #GNUNET_NO if value was already normalized
+ * #GNUNET_SYSERR if value was invalid or could not be normalized
+ */
+int
+TALER_amount_normalize (struct TALER_Amount *amount);
+
+
+/**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return freshly allocated string representation,
+ * NULL if the @a amount was invalid
+ */
+char *
+TALER_amount_to_string (const struct TALER_Amount *amount);
+
+#if 0 /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/src/backend/taler_crypto_lib.h b/src/backend/taler_crypto_lib.h
new file mode 100644
index 00000000..4126894a
--- /dev/null
+++ b/src/backend/taler_crypto_lib.h
@@ -0,0 +1,569 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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_crypto_lib.h
+ * @brief taler-specific crypto functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef TALER_CRYPTO_LIB_H
+#define TALER_CRYPTO_LIB_H
+
+#if HAVE_GNUNET_GNUNET_UTIL_LIB_H
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#elif HAVE_GNUNET_GNUNET_UTIL_TALER_WALLET_LIB_H
+#include <gnunet/gnunet_util_taler_wallet_lib.h>
+#include "taler_util_wallet.h"
+#endif
+
+#include <gcrypt.h>
+
+
+/* ****************** Coin crypto primitives ************* */
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * @brief Type of public keys for Taler reserves.
+ */
+struct TALER_ReservePublicKeyP
+{
+ /**
+ * Taler uses EdDSA for reserves.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+};
+
+
+/**
+ * @brief Type of private keys for Taler reserves.
+ */
+struct TALER_ReservePrivateKeyP
+{
+ /**
+ * Taler uses EdDSA for reserves.
+ */
+ struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+};
+
+
+/**
+ * @brief Type of signatures used with Taler reserves.
+ */
+struct TALER_ReserveSignatureP
+{
+ /**
+ * Taler uses EdDSA for reserves.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
+};
+
+
+/**
+ * @brief Type of public keys to for merchant authorizations.
+ * Merchants can issue refunds using the corresponding
+ * private key.
+ */
+struct TALER_MerchantPublicKeyP
+{
+ /**
+ * Taler uses EdDSA for merchants.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+};
+
+
+/**
+ * @brief Type of private keys for merchant authorizations.
+ * Merchants can issue refunds using the corresponding
+ * private key.
+ */
+struct TALER_MerchantPrivateKeyP
+{
+ /**
+ * Taler uses EdDSA for merchants.
+ */
+ struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+};
+
+
+/**
+ * @brief Type of transfer public keys used during refresh
+ * operations.
+ */
+struct TALER_TransferPublicKeyP
+{
+ /**
+ * Taler uses ECDHE for transfer keys.
+ */
+ struct GNUNET_CRYPTO_EcdhePublicKey ecdhe_pub;
+};
+
+
+/**
+ * @brief Type of transfer public keys used during refresh
+ * operations.
+ */
+struct TALER_TransferPrivateKeyP
+{
+ /**
+ * Taler uses ECDHE for melting session keys.
+ */
+ struct GNUNET_CRYPTO_EcdhePrivateKey ecdhe_priv;
+};
+
+
+/**
+ * @brief Type of online public keys used by the mint to sign
+ * messages.
+ */
+struct TALER_MintPublicKeyP
+{
+ /**
+ * Taler uses EdDSA for online mint message signing.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+};
+
+
+/**
+ * @brief Type of online public keys used by the mint to
+ * sign messages.
+ */
+struct TALER_MintPrivateKeyP
+{
+ /**
+ * Taler uses EdDSA for online signatures sessions.
+ */
+ struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+};
+
+
+/**
+ * @brief Type of signatures used by the mint to sign messages online.
+ */
+struct TALER_MintSignatureP
+{
+ /**
+ * Taler uses EdDSA for online signatures sessions.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
+};
+
+
+/**
+ * @brief Type of the offline master public key used by the mint.
+ */
+struct TALER_MasterPublicKeyP
+{
+ /**
+ * Taler uses EdDSA for the long-term offline master key.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+};
+
+
+/**
+ * @brief Type of the public key used by the auditor.
+ */
+struct TALER_AuditorPublicKeyP
+{
+ /**
+ * Taler uses EdDSA for the auditor's signing key.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+};
+
+
+/**
+ * @brief Type of the offline master public keys used by the mint.
+ */
+struct TALER_MasterPrivateKeyP
+{
+ /**
+ * Taler uses EdDSA for the long-term offline master key.
+ */
+ struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+};
+
+
+/**
+ * @brief Type of signatures by the offline master public key used by the mint.
+ */
+struct TALER_MasterSignatureP
+{
+ /**
+ * Taler uses EdDSA for the long-term offline master key.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
+};
+
+
+
+/**
+ * @brief Type of public keys for Taler coins. The same key material is used
+ * for EdDSA and ECDHE operations.
+ */
+struct TALER_CoinSpendPublicKeyP
+{
+ /**
+ * Taler uses EdDSA for coins when signing deposit requests.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+
+};
+
+
+/**
+ * @brief Type of private keys for Taler coins. The same key material is used
+ * for EdDSA and ECDHE operations.
+ */
+struct TALER_CoinSpendPrivateKeyP
+{
+ /**
+ * Taler uses EdDSA for coins when signing deposit requests.
+ */
+ struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+};
+
+
+/**
+ * @brief Type of signatures made with Taler coins.
+ */
+struct TALER_CoinSpendSignatureP
+{
+ /**
+ * Taler uses EdDSA for coins.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * @brief Type of blinding keys for Taler.
+ */
+struct TALER_DenominationBlindingKey
+{
+ /**
+ * Taler uses RSA for blinding.
+ */
+ struct GNUNET_CRYPTO_rsa_BlindingKey *rsa_blinding_key;
+};
+
+
+/**
+ * @brief Type of (unblinded) coin signatures for Taler.
+ */
+struct TALER_DenominationSignature
+{
+ /**
+ * Taler uses RSA for blinding.
+ */
+ struct GNUNET_CRYPTO_rsa_Signature *rsa_signature;
+};
+
+
+/**
+ * @brief Type of public signing keys for verifying blindly signed coins.
+ */
+struct TALER_DenominationPublicKey
+{
+ /**
+ * Taler uses RSA for signing coins.
+ */
+ struct GNUNET_CRYPTO_rsa_PublicKey *rsa_public_key;
+};
+
+
+/**
+ * @brief Type of private signing keys for blind signing of coins.
+ */
+struct TALER_DenominationPrivateKey
+{
+ /**
+ * Taler uses RSA for signing coins.
+ */
+ struct GNUNET_CRYPTO_rsa_PrivateKey *rsa_private_key;
+};
+
+
+/**
+ * @brief Public information about a coin (including the public key
+ * of the coin, the denomination key and the signature with
+ * the denomination key).
+ */
+struct TALER_CoinPublicInfo
+{
+ /**
+ * The coin's public key.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Public key representing the denomination of the coin
+ * that is being deposited.
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * (Unblinded) signature over @e coin_pub with @e denom_pub,
+ * which demonstrates that the coin is valid.
+ */
+ struct TALER_DenominationSignature denom_sig;
+};
+
+
+/**
+ * Check if a coin is valid; that is, whether the denomination key exists,
+ * is not expired, and the signature is correct.
+ *
+ * @param coin_public_info the coin public info to check for validity
+ * @return #GNUNET_YES if the coin is valid,
+ * #GNUNET_NO if it is invalid
+ * #GNUNET_SYSERROR if an internal error occured
+ */
+int
+TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info);
+
+
+/* ****************** Refresh crypto primitives ************* */
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * @brief Secret used to decrypt the key to decrypt link secrets.
+ */
+struct TALER_TransferSecretP
+{
+ /**
+ * Secret used to encrypt/decrypt the `struct TALER_LinkSecretP`.
+ * Must be (currently) a hash as this is what
+ * #GNUNET_CRYPTO_ecc_ecdh() returns to us.
+ */
+ struct GNUNET_HashCode key;
+};
+
+
+/**
+ * @brief Secret used to decrypt refresh links.
+ */
+struct TALER_LinkSecretP
+{
+ /**
+ * Secret used to decrypt the refresh link data.
+ */
+ char key[sizeof (struct GNUNET_HashCode)];
+};
+
+
+/**
+ * @brief Encrypted secret used to decrypt refresh links.
+ */
+struct TALER_EncryptedLinkSecretP
+{
+ /**
+ * Encrypted secret, must be the given size!
+ */
+ char enc[sizeof (struct TALER_LinkSecretP)];
+};
+
+
+/**
+ * @brief Representation of an refresh link in cleartext.
+ */
+struct TALER_RefreshLinkDecrypted
+{
+
+ /**
+ * Private key of the coin.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+ /**
+ * Blinding key.
+ */
+ struct TALER_DenominationBlindingKey blinding_key;
+
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * @brief Representation of an encrypted refresh link.
+ */
+struct TALER_RefreshLinkEncrypted
+{
+
+ /**
+ * Encrypted blinding key with @e blinding_key_enc_size bytes,
+ * must be allocated at the end of this struct.
+ */
+ const char *blinding_key_enc;
+
+ /**
+ * Number of bytes in @e blinding_key_enc.
+ */
+ size_t blinding_key_enc_size;
+
+ /**
+ * Encrypted private key of the coin.
+ */
+ char coin_priv_enc[sizeof (struct TALER_CoinSpendPrivateKeyP)];
+
+};
+
+
+/**
+ * Decrypt the shared @a secret from the information in the
+ * encrypted link secret @e secret_enc using the transfer
+ * private key and the coin's public key.
+ *
+ * @param secret_enc encrypted link secret
+ * @param trans_priv transfer private key
+ * @param coin_pub coin public key
+ * @param[out] secret set to the shared secret
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+TALER_link_decrypt_secret (const struct TALER_EncryptedLinkSecretP *secret_enc,
+ const struct TALER_TransferPrivateKeyP *trans_priv,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_LinkSecretP *secret);
+
+
+/**
+ * Decrypt the shared @a secret from the information in the
+ * encrypted link secret @e secret_enc using the transfer
+ * public key and the coin's private key.
+ *
+ * @param secret_enc encrypted link secret
+ * @param trans_pub transfer public key
+ * @param coin_priv coin private key
+ * @param[out] secret set to the shared secret
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+TALER_link_decrypt_secret2 (const struct TALER_EncryptedLinkSecretP *secret_enc,
+ const struct TALER_TransferPublicKeyP *trans_pub,
+ const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+ struct TALER_LinkSecretP *secret);
+
+
+/**
+ * Encrypt the shared @a secret to generate the encrypted link secret.
+ * Also creates the transfer key.
+ *
+ * @param secret link secret to encrypt
+ * @param coin_pub coin public key
+ * @param[out] trans_priv set to transfer private key
+ * @param[out] trans_pub set to transfer public key
+ * @param[out] secret_enc set to the encryptd @a secret
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+TALER_link_encrypt_secret (const struct TALER_LinkSecretP *secret,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_TransferPrivateKeyP *trans_priv,
+ struct TALER_TransferPublicKeyP *trans_pub,
+ struct TALER_EncryptedLinkSecretP *secret_enc);
+
+
+/**
+ * Use the @a trans_sec (from ECDHE) to decrypt the @a secret_enc
+ * to obtain the @a secret to decrypt the linkage data.
+ *
+ * @param secret_enc encrypted secret
+ * @param trans_sec transfer secret
+ * @param secret shared secret for refresh link decryption
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_transfer_decrypt (const struct TALER_EncryptedLinkSecretP *secret_enc,
+ const struct TALER_TransferSecretP *trans_sec,
+ struct TALER_LinkSecretP *secret);
+
+
+/**
+ * Use the @a trans_sec (from ECDHE) to encrypt the @a secret
+ * to obtain the @a secret_enc.
+ *
+ * @param secret shared secret for refresh link decryption
+ * @param trans_sec transfer secret
+ * @param[out] secret_enc encrypted secret
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_transfer_encrypt (const struct TALER_LinkSecretP *secret,
+ const struct TALER_TransferSecretP *trans_sec,
+ struct TALER_EncryptedLinkSecretP *secret_enc);
+
+
+/**
+ * Decrypt refresh link information.
+ *
+ * @param input encrypted refresh link data
+ * @param secret shared secret to use for decryption
+ * @return NULL on error
+ */
+struct TALER_RefreshLinkDecrypted *
+TALER_refresh_decrypt (const struct TALER_RefreshLinkEncrypted *input,
+ const struct TALER_LinkSecretP *secret);
+
+
+/**
+ * Encrypt refresh link information.
+ *
+ * @param input plaintext refresh link data
+ * @param secret shared secret to use for encryption
+ * @return NULL on error (should never happen)
+ */
+struct TALER_RefreshLinkEncrypted *
+TALER_refresh_encrypt (const struct TALER_RefreshLinkDecrypted *input,
+ const struct TALER_LinkSecretP *secret);
+
+
+/**
+ * Decode encrypted refresh link information from buffer.
+ *
+ * @param buf buffer with refresh link data
+ * @param buf_len number of bytes in @a buf
+ * @return NULL on error (@a buf_len too small)
+ */
+struct TALER_RefreshLinkEncrypted *
+TALER_refresh_link_encrypted_decode (const char *buf,
+ size_t buf_len);
+
+
+/**
+ * Encode encrypted refresh link information to buffer.
+ *
+ * @param rle refresh link to encode
+ * @param[out] buf_len set number of bytes returned
+ * @return NULL on error, otherwise buffer with encoded @a rle
+ */
+char *
+TALER_refresh_link_encrypted_encode (const struct TALER_RefreshLinkEncrypted *rle,
+ size_t *buf_len);
+
+
+
+#endif
diff --git a/src/backend/taler_json_lib.h b/src/backend/taler_json_lib.h
new file mode 100644
index 00000000..5a13b9bc
--- /dev/null
+++ b/src/backend/taler_json_lib.h
@@ -0,0 +1,181 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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_json_lib.h
+ * @brief helper functions for JSON processing using libjansson
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#ifndef TALER_json_LIB_H_
+#define TALER_json_LIB_H_
+
+#include <jansson.h>
+
+/**
+ * Print JSON parsing related error information
+ */
+#define TALER_json_warn(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)\n", \
+ __FILE__, __LINE__, error.text, error.source)
+
+
+/**
+ * Convert a TALER amount to a JSON object.
+ *
+ * @param amount the amount
+ * @return a json object describing the amount
+ */
+json_t *
+TALER_json_from_amount (const struct TALER_Amount *amount);
+
+
+/**
+ * Convert absolute timestamp to a json string.
+ *
+ * @param stamp the time stamp
+ * @return a json string with the timestamp in @a stamp
+ */
+json_t *
+TALER_json_from_abs (struct GNUNET_TIME_Absolute stamp);
+
+
+/**
+ * Convert a signature (with purpose) to a JSON object representation.
+ *
+ * @param purpose purpose of the signature
+ * @param signature the signature
+ * @return the JSON reporesentation of the signature with purpose
+ */
+json_t *
+TALER_json_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+ const struct GNUNET_CRYPTO_EddsaSignature *signature);
+
+
+/**
+ * Convert RSA public key to JSON.
+ *
+ * @param pk public key to convert
+ * @return corresponding JSON encoding
+ */
+json_t *
+TALER_json_from_rsa_public_key (struct GNUNET_CRYPTO_rsa_PublicKey *pk);
+
+
+/**
+ * Convert RSA signature to JSON.
+ *
+ * @param sig signature to convert
+ * @return corresponding JSON encoding
+ */
+json_t *
+TALER_json_from_rsa_signature (struct GNUNET_CRYPTO_rsa_Signature *sig);
+
+
+/**
+ * Convert binary data to a JSON string
+ * with the base32crockford encoding.
+ *
+ * @param data binary data
+ * @param size size of @a data in bytes
+ * @return json string that encodes @a data
+ */
+json_t *
+TALER_json_from_data (const void *data, size_t size);
+
+
+/**
+ * Parse given JSON object to Amount
+ *
+ * @param json the json object representing Amount
+ * @param[out] r_amount where the amount has to be written
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+int
+TALER_json_to_amount (json_t *json,
+ struct TALER_Amount *r_amount);
+
+/**
+ * Parse given JSON object to absolute time.
+ *
+ * @param json the json object representing absolute time in seconds
+ * @param[out] abs where the time has to be written
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+int
+TALER_json_to_abs (json_t *json,
+ struct GNUNET_TIME_Absolute *abs);
+
+/**
+ * Parse given JSON object to data
+ *
+ * @param json the json object representing data
+ * @param out the pointer to hold the parsed data.
+ * @param out_size the size of @a out
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+int
+TALER_json_to_data (json_t *json,
+ void *out,
+ size_t out_size);
+
+
+/**
+ * Convert JSON to RSA public key.
+ *
+ * @param json JSON encoding to convert
+ * @return corresponding public key
+ */
+struct GNUNET_CRYPTO_rsa_PublicKey *
+TALER_json_to_rsa_public_key (json_t *json);
+
+
+/**
+ * Convert JSON to RSA signature.
+ *
+ * @param json JSON encoding to convert
+ * @return corresponding signature
+ */
+struct GNUNET_CRYPTO_rsa_Signature *
+TALER_json_to_rsa_signature (json_t *json);
+
+
+/**
+ * Hash a JSON for binary signing.
+ *
+ * @param[in] json some JSON value to hash
+ * @param[out] hc resulting hash code
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+TALER_hash_json (json_t *json,
+ struct GNUNET_HashCode *hc);
+
+
+/**
+ * Check if the given wire format JSON object is correctly formatted
+ *
+ * @param type the type of the wire format
+ * @param wire the JSON wire format object
+ * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
+ */
+int
+TALER_json_validate_wireformat (const char *type,
+ const json_t *wire);
+
+
+#endif /* TALER_json_LIB_H_ */
+
+/* End of taler_json_lib.h */
diff --git a/src/backend/taler_mintdb_lib.h b/src/backend/taler_mintdb_lib.h
new file mode 100644
index 00000000..24f67761
--- /dev/null
+++ b/src/backend/taler_mintdb_lib.h
@@ -0,0 +1,224 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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_mintdb_lib.h
+ * @brief IO operations for the mint's private keys
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINTDB_LIB_H
+#define TALER_MINTDB_LIB_H
+
+#include "taler_signatures.h"
+
+/**
+ * Subdirectroy under the mint's base directory which contains
+ * the mint's signing keys.
+ */
+#define TALER_MINTDB_DIR_SIGNING_KEYS "signkeys"
+
+/**
+ * Subdirectory under the mint's base directory which contains
+ * the mint's denomination keys.
+ */
+#define TALER_MINTDB_DIR_DENOMINATION_KEYS "denomkeys"
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * @brief On disk format used for a mint signing key. Signing keys are used
+ * by the mint to affirm its messages, but not to create coins.
+ * Includes the private key followed by the public information about
+ * the signing key.
+ */
+struct TALER_MINTDB_PrivateSigningKeyInformationP
+{
+ /**
+ * Private key part of the mint's signing key.
+ */
+ struct TALER_MintPrivateKeyP signkey_priv;
+
+ /**
+ * Public information about a mint signing key.
+ */
+ struct TALER_MintSigningKeyValidityPS issue;
+};
+
+
+/**
+ * Information about a denomination key.
+ */
+struct TALER_MINTDB_DenominationKeyInformationP
+{
+
+ /**
+ * Signature over this struct to affirm the validity of the key.
+ */
+ struct TALER_MasterSignatureP signature;
+
+ /**
+ * Signed properties of the denomination key.
+ */
+ struct TALER_DenominationKeyValidityPS properties;
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * @brief All information about a denomination key (which is used to
+ * sign coins into existence).
+ */
+struct TALER_MINTDB_DenominationKeyIssueInformation
+{
+ /**
+ * The private key of the denomination. Will be NULL if the private
+ * key is not available (this is the case after the key has expired
+ * for signing coins, but is still valid for depositing coins).
+ */
+ struct TALER_DenominationPrivateKey denom_priv;
+
+ /**
+ * Decoded denomination public key (the hash of it is in
+ * @e issue, but we sometimes need the full public key as well).
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * Signed public information about a denomination key.
+ */
+ struct TALER_MINTDB_DenominationKeyInformationP issue;
+};
+
+
+/**
+ * @brief Iterator over signing keys.
+ *
+ * @param cls closure
+ * @param filename name of the file the key came from
+ * @param ski the sign key
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int
+(*TALER_MINTDB_SigningKeyIterator)(void *cls,
+ const char *filename,
+ const struct TALER_MINTDB_PrivateSigningKeyInformationP *ski);
+
+
+/**
+ * @brief Iterator over denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int
+(*TALER_MINTDB_DenominationKeyIterator)(void *cls,
+ const char *alias,
+ const struct TALER_MINTDB_DenominationKeyIssueInformation *dki);
+
+
+
+/**
+ * Call @a it for each signing key found in the @a mint_base_dir.
+ *
+ * @param mint_base_dir base directory for the mint,
+ * the signing keys must be in the #TALER_MINTDB_DIR_SIGNING_KEYS
+ * subdirectory
+ * @param it function to call on each signing key
+ * @param it_cls closure for @a it
+ * @return number of files found (may not match
+ * number of keys given to @a it as malformed
+ * files are simply skipped), -1 on error
+ */
+int
+TALER_MINTDB_signing_keys_iterate (const char *mint_base_dir,
+ TALER_MINTDB_SigningKeyIterator it,
+ void *it_cls);
+
+
+/**
+ * Call @a it for each denomination key found in the @a mint_base_dir.
+ *
+ * @param mint_base_dir base directory for the mint,
+ * the signing keys must be in the #TALER_MINTDB_DIR_DENOMINATION_KEYS
+ * subdirectory
+ * @param it function to call on each denomination key found
+ * @param it_cls closure for @a it
+ * @return -1 on error, 0 if no files were found, otherwise
+ * a positive number (however, even with a positive
+ * number it is possible that @a it was never called
+ * as maybe none of the files were well-formed)
+ */
+int
+TALER_MINTDB_denomination_keys_iterate (const char *mint_base_dir,
+ TALER_MINTDB_DenominationKeyIterator it,
+ void *it_cls);
+
+
+/**
+ * Exports a denomination key to the given file.
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINTDB_denomination_key_write (const char *filename,
+ const struct TALER_MINTDB_DenominationKeyIssueInformation *dki);
+
+
+/**
+ * Import a denomination key from the given file.
+ *
+ * @param filename the file to import the key from
+ * @param[out] dki set to the imported denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINTDB_denomination_key_read (const char *filename,
+ struct TALER_MINTDB_DenominationKeyIssueInformation *dki);
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return NULL on failure
+ */
+struct TALER_MINTDB_Plugin *
+TALER_MINTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Shutdown the plugin.
+ *
+ * @param plugin plugin to unload
+ */
+void
+TALER_MINTDB_plugin_unload (struct TALER_MINTDB_Plugin *plugin);
+
+
+
+#endif
diff --git a/src/backend/taler_mintdb_plugin.h b/src/backend/taler_mintdb_plugin.h
new file mode 100644
index 00000000..21d83d9d
--- /dev/null
+++ b/src/backend/taler_mintdb_plugin.h
@@ -0,0 +1,1218 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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_mintdb_plugin.h
+ * @brief Low-level (statement-level) database access for the mint
+ * @author Florian Dold
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINTDB_PLUGIN_H
+#define TALER_MINTDB_PLUGIN_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mintdb_lib.h"
+
+
+/**
+ * @brief Information we keep on bank transfer(s) that established a reserve.
+ */
+struct TALER_MINTDB_BankTransfer
+{
+
+ /**
+ * Public key of the reserve that was filled.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Amount that was transferred to the mint.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * When did the mint receive the incoming transaction?
+ * (This is the execution date of the mint's database,
+ * the execution date of the bank should be in @e wire).
+ */
+ struct GNUNET_TIME_Absolute execution_date;
+
+ /**
+ * Detailed wire information about the transaction.
+ */
+ json_t *wire;
+
+};
+
+
+/**
+ * @brief A summary of a Reserve
+ */
+struct TALER_MINTDB_Reserve
+{
+ /**
+ * The reserve's public key. This uniquely identifies the reserve
+ */
+ struct TALER_ReservePublicKeyP pub;
+
+ /**
+ * The balance amount existing in the reserve
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * The expiration date of this reserve
+ */
+ struct GNUNET_TIME_Absolute expiry;
+};
+
+
+/**
+ * @brief Information we keep for a withdrawn coin to reproduce
+ * the /withdraw operation if needed, and to have proof
+ * that a reserve was drained by this amount.
+ */
+struct TALER_MINTDB_CollectableBlindcoin
+{
+
+ /**
+ * Our signature over the (blinded) coin.
+ */
+ struct TALER_DenominationSignature sig;
+
+ /**
+ * Denomination key (which coin was generated).
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * Value of the coin being minted (matching the denomination key)
+ * plus the transaction fee. We include this in what is being
+ * signed so that we can verify a reserve's remaining total balance
+ * without needing to access the respective denomination key
+ * information each time.
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * Withdrawl fee charged by the mint. This must match the Mint's
+ * denomination key's withdrawl fee. If the client puts in an
+ * invalid withdrawl fee (too high or too low) that does not match
+ * the Mint's denomination key, the withdraw operation is invalid
+ * and will be rejected by the mint. The @e amount_with_fee minus
+ * the @e withdraw_fee is must match the value of the generated
+ * coin. We include this in what is being signed so that we can
+ * verify a mint's accounting without needing to access the
+ * respective denomination key information each time.
+ */
+ struct TALER_Amount withdraw_fee;
+
+ /**
+ * Public key of the reserve that was drained.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Hash over the blinded message, needed to verify
+ * the @e reserve_sig.
+ */
+ struct GNUNET_HashCode h_coin_envelope;
+
+ /**
+ * Signature confirming the withdrawl, matching @e reserve_pub,
+ * @e denom_pub and @e h_coin_envelope.
+ */
+ struct TALER_ReserveSignatureP reserve_sig;
+};
+
+
+
+/**
+ * @brief Types of operations on a reserved.
+ */
+enum TALER_MINTDB_ReserveOperation
+{
+ /**
+ * Money was deposited into the reserve via a bank transfer.
+ */
+ TALER_MINTDB_RO_BANK_TO_MINT = 0,
+
+ /**
+ * A Coin was withdrawn from the reserve using /withdraw.
+ */
+ TALER_MINTDB_RO_WITHDRAW_COIN = 1
+};
+
+
+/**
+ * @brief Reserve history as a linked list. Lists all of the transactions
+ * associated with this reserve (such as the bank transfers that
+ * established the reserve and all /withdraw operations we have done
+ * since).
+ */
+struct TALER_MINTDB_ReserveHistory
+{
+
+ /**
+ * Next entry in the reserve history.
+ */
+ struct TALER_MINTDB_ReserveHistory *next;
+
+ /**
+ * Type of the event, determins @e details.
+ */
+ enum TALER_MINTDB_ReserveOperation type;
+
+ /**
+ * Details of the operation, depending on @e type.
+ */
+ union
+ {
+
+ /**
+ * Details about a bank transfer to the mint.
+ */
+ struct TALER_MINTDB_BankTransfer *bank;
+
+ /**
+ * Details about a /withdraw operation.
+ */
+ struct TALER_MINTDB_CollectableBlindcoin *withdraw;
+
+ } details;
+
+};
+
+
+/**
+ * @brief Specification for a /deposit operation. The combination of
+ * the coin's public key, the merchant's public key and the
+ * transaction ID must be unique. While a coin can (theoretically) be
+ * deposited at the same merchant twice (with partial spending), the
+ * merchant must either use a different public key or a different
+ * transaction ID for the two transactions. The same coin must not
+ * be used twice at the same merchant for the same transaction
+ * (as determined by transaction ID). (Note: we might want to
+ * fix #3819 and include at least h_contract as well.)
+ */
+struct TALER_MINTDB_Deposit
+{
+ /**
+ * Information about the coin that is being deposited.
+ */
+ struct TALER_CoinPublicInfo coin;
+
+ /**
+ * ECDSA signature affirming that the customer intends
+ * this coin to be deposited at the merchant identified
+ * by @e h_wire in relation to the contract identified
+ * by @e h_contract.
+ */
+ struct TALER_CoinSpendSignatureP csig;
+
+ /**
+ * Public key of the merchant. Enables later identification
+ * of the merchant in case of a need to rollback transactions.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * Hash over the contract between merchant and customer
+ * (remains unknown to the Mint).
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Hash of the (canonical) representation of @e wire, used
+ * to check the signature on the request. Generated by
+ * the mint from the detailed wire data provided by the
+ * merchant.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Detailed wire information for executing the transaction.
+ */
+ json_t *wire;
+
+ /**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions.
+ */
+ uint64_t transaction_id;
+
+ /**
+ * Time when this request was generated. Used, for example, to
+ * assess when (roughly) the income was achieved for tax purposes.
+ * Note that the Mint will only check that the timestamp is not "too
+ * far" into the future (i.e. several days). The fact that the
+ * timestamp falls within the validity period of the coin's
+ * denomination key is irrelevant for the validity of the deposit
+ * request, as obviously the customer and merchant could conspire to
+ * set any timestamp. Also, the Mint must accept very old deposit
+ * requests, as the merchant might have been unable to transmit the
+ * deposit request in a timely fashion (so back-dating is not
+ * prevented).
+ */
+ struct GNUNET_TIME_Absolute timestamp;
+
+ /**
+ * How much time does the merchant have to issue a refund request?
+ * Zero if refunds are not allowed. After this time, the coin
+ * cannot be refunded.
+ */
+ struct GNUNET_TIME_Absolute refund_deadline;
+
+ /**
+ * Fraction of the coin's remaining value to be deposited, including
+ * depositing fee (if any). The coin is identified by @e coin_pub.
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * Depositing fee.
+ */
+ struct TALER_Amount deposit_fee;
+
+};
+
+
+/**
+ * @brief Global information for a refreshing session. Includes
+ * dimensions of the operation, security parameters and
+ * client signatures from "/refresh/melt" and "/refresh/commit".
+ */
+struct TALER_MINTDB_RefreshSession
+{
+
+ /**
+ * Number of coins we are melting.
+ */
+ uint16_t num_oldcoins;
+
+ /**
+ * Number of new coins we are creating.
+ */
+ uint16_t num_newcoins;
+
+ /**
+ * Index (smaller #TALER_CNC_KAPPA) which the mint has chosen to not
+ * have revealed during cut and choose.
+ */
+ uint16_t noreveal_index;
+
+};
+
+
+/**
+ * @brief Specification for coin in a /refresh/melt operation.
+ */
+struct TALER_MINTDB_RefreshMelt
+{
+ /**
+ * Information about the coin that is being melted.
+ */
+ struct TALER_CoinPublicInfo coin;
+
+ /**
+ * Signature over the melting operation.
+ */
+ struct TALER_CoinSpendSignatureP coin_sig;
+
+ /**
+ * Hash of the refresh session this coin is melted into.
+ */
+ struct GNUNET_HashCode session_hash;
+
+ /**
+ * How much value is being melted? This amount includes the fees,
+ * so the final amount contributed to the melt is this value minus
+ * the fee for melting the coin. We include the fee in what is
+ * being signed so that we can verify a reserve's remaining total
+ * balance without needing to access the respective denomination key
+ * information each time.
+ */
+ struct TALER_Amount amount_with_fee;
+
+ /**
+ * Melting fee charged by the mint. This must match the Mint's
+ * denomination key's melting fee. If the client puts in an invalid
+ * melting fee (too high or too low) that does not match the Mint's
+ * denomination key, the melting operation is invalid and will be
+ * rejected by the mint. The @e amount_with_fee minus the @e
+ * melt_fee is the amount that will be credited to the melting
+ * session.
+ */
+ struct TALER_Amount melt_fee;
+
+};
+
+
+/**
+ * @brief We have as many `struct TALER_MINTDB_RefreshCommitCoin` as there are new
+ * coins being created by the refresh (for each of the #TALER_CNC_KAPPA
+ * sets). These are the coins we ask the mint to sign if the
+ * respective set is selected.
+ */
+struct TALER_MINTDB_RefreshCommitCoin
+{
+
+ /**
+ * Encrypted data allowing those able to decrypt it to derive
+ * the private keys of the new coins created by the refresh.
+ */
+ struct TALER_RefreshLinkEncrypted *refresh_link;
+
+ /**
+ * Blinded message to be signed (in envelope), with @e coin_env_size bytes.
+ */
+ char *coin_ev;
+
+ /**
+ * Number of bytes in @e coin_ev.
+ */
+ size_t coin_ev_size;
+
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * @brief For each (old) coin being melted, we have a `struct
+ * RefreshCommitLinkP` that allows the user to find the shared secret
+ * to decrypt the respective refresh links for the new coins in the
+ * `struct TALER_MINTDB_RefreshCommitCoin`.
+ */
+struct TALER_MINTDB_RefreshCommitLinkP
+{
+ /**
+ * Transfer public key, used to decrypt the @e shared_secret_enc
+ * in combintation with the corresponding private key of the
+ * coin.
+ */
+ struct TALER_TransferPublicKeyP transfer_pub;
+
+ /**
+ * Encrypted shared secret to decrypt the link.
+ */
+ struct TALER_EncryptedLinkSecretP shared_secret_enc;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+
+/**
+ * @brief Linked list of refresh information linked to a coin.
+ */
+struct TALER_MINTDB_LinkDataList
+{
+ /**
+ * Information is stored in a NULL-terminated linked list.
+ */
+ struct TALER_MINTDB_LinkDataList *next;
+
+ /**
+ * Link data, used to recover the private key of the coin
+ * by the owner of the old coin.
+ */
+ struct TALER_RefreshLinkEncrypted *link_data_enc;
+
+ /**
+ * Denomination public key, determines the value of the coin.
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * Signature over the blinded envelope.
+ */
+ struct TALER_DenominationSignature ev_sig;
+};
+
+
+/**
+ * @brief Specification for a /lock operation.
+ */
+struct TALER_MINTDB_LockOperation
+{
+ /**
+ * Information about the coin that is being locked.
+ */
+ struct TALER_CoinPublicInfo coin;
+
+ /**
+ * Signature over the locking operation.
+ */
+ struct TALER_CoinSpendSignatureP coin_sig;
+
+ /**
+ * How much value is being locked?
+ */
+ struct TALER_Amount amount;
+
+ // FIXME: more needed...
+};
+
+
+/**
+ * @brief Enumeration to classify the different types of transactions
+ * that can be done with a coin.
+ */
+enum TALER_MINTDB_TransactionType
+{
+ /**
+ * /deposit operation.
+ */
+ TALER_MINTDB_TT_DEPOSIT = 0,
+
+ /**
+ * /refresh/melt operation.
+ */
+ TALER_MINTDB_TT_REFRESH_MELT = 1,
+
+ /**
+ * /lock operation.
+ */
+ TALER_MINTDB_TT_LOCK = 2
+};
+
+
+/**
+ * @brief List of transactions we performed for a particular coin.
+ */
+struct TALER_MINTDB_TransactionList
+{
+
+ /**
+ * Next pointer in the NULL-terminated linked list.
+ */
+ struct TALER_MINTDB_TransactionList *next;
+
+ /**
+ * Type of the transaction, determines what is stored in @e details.
+ */
+ enum TALER_MINTDB_TransactionType type;
+
+ /**
+ * Details about the transaction, depending on @e type.
+ */
+ union
+ {
+
+ /**
+ * Details if transaction was a /deposit operation.
+ */
+ struct TALER_MINTDB_Deposit *deposit;
+
+ /**
+ * Details if transaction was a /refresh/melt operation.
+ */
+ struct TALER_MINTDB_RefreshMelt *melt;
+
+ /**
+ * Details if transaction was a /lock operation.
+ */
+ struct TALER_MINTDB_LockOperation *lock;
+
+ } details;
+
+};
+
+
+/**
+ * @brief All of the information from a /refresh/melt commitment.
+ */
+struct TALER_MINTDB_MeltCommitment
+{
+
+ /**
+ * Number of coins we are melting.
+ */
+ uint16_t num_oldcoins;
+
+ /**
+ * Number of new coins we are creating.
+ */
+ uint16_t num_newcoins;
+
+ /**
+ * Array of @e num_oldcoins melt operation details.
+ */
+ struct TALER_MINTDB_RefreshMelt *melts;
+
+ /**
+ * Array of @e num_newcoins denomination keys
+ */
+ struct TALER_DenominationPublicKey *denom_pubs;
+
+ /**
+ * 2D-Array of #TALER_CNC_KAPPA and @e num_newcoins commitments.
+ */
+ struct TALER_MINTDB_RefreshCommitCoin *commit_coins[TALER_CNC_KAPPA];
+
+ /**
+ * 2D-Array of #TALER_CNC_KAPPA and @e new_oldcoins links.
+ */
+ struct TALER_MINTDB_RefreshCommitLinkP *commit_links[TALER_CNC_KAPPA];
+};
+
+
+/**
+ * @brief Handle for a database session (per-thread, for transactions).
+ */
+struct TALER_MINTDB_Session;
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin.
+ *
+ * @param cls closure
+ * @param session_hash a session the coin was melted in
+ * @param transfer_pub public transfer key for the session
+ * @param shared_secret_enc set to shared secret for the session
+ */
+typedef void
+(*TALER_MINTDB_TransferDataCallback)(void *cls,
+ const struct GNUNET_HashCode *session_hash,
+ const struct TALER_TransferPublicKeyP *transfer_pub,
+ const struct TALER_EncryptedLinkSecretP *shared_secret_enc);
+
+
+/**
+ * @brief The plugin API, returned from the plugin's "init" function.
+ * The argument given to "init" is simply a configuration handle.
+ */
+struct TALER_MINTDB_Plugin
+{
+
+ /**
+ * Closure for all callbacks.
+ */
+ void *cls;
+
+ /**
+ * Name of the library which generated this plugin. Set by the
+ * plugin loader.
+ */
+ char *library_name;
+
+ /**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the
+ * database default one
+ * @param the database connection, or NULL on error
+ */
+ struct TALER_MINTDB_Session *
+ (*get_session) (void *cls,
+ int temporary);
+
+
+ /**
+ * Drop the temporary taler schema. This is only useful for testcases.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+ int
+ (*drop_temporary) (void *cls,
+ struct TALER_MINTDB_Session *db);
+
+
+ /**
+ * Create the necessary tables if they are not present
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param temporary should we use a temporary schema
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+ int
+ (*create_tables) (void *cls,
+ int temporary);
+
+
+ /**
+ * Start a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*start) (void *cls,
+ struct TALER_MINTDB_Session *session);
+
+
+ /**
+ * Commit a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion connection to use
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*commit) (void *cls,
+ struct TALER_MINTDB_Session *sesssion);
+
+
+ /**
+ * Abort/rollback a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion connection to use
+ */
+ void
+ (*rollback) (void *cls,
+ struct TALER_MINTDB_Session *sesssion);
+
+
+ /**
+ * Insert information about a denomination key and in particular
+ * the properties (value, fees, expiration times) the coins signed
+ * with this key have.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion connection to use
+ * @param denom_pub the public key used for signing coins of this denomination
+ * @param issue issuing information with value, fees and other info about the coin
+ * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ */
+ int
+ (*insert_denomination_info) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ const struct TALER_MINTDB_DenominationKeyInformationP *issue);
+
+
+ /**
+ * Fetch information about a denomination key.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion connection to use
+ * @param denom_pub the public key used for signing coins of this denomination
+ * @param[out] issue set to issue information with value, fees and other info about the coin, can be NULL
+ * @return #GNUNET_OK on success; #GNUNET_NO if no record was found, #GNUNET_SYSERR on failure
+ */
+ int
+ (*get_denomination_info) (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ struct TALER_MINTDB_DenominationKeyInformationP *issue);
+
+
+ /**
+ * Get the summary of a reserve.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db the database connection handle
+ * @param[in,out] reserve the reserve data. The public key of the reserve should be set
+ * in this structure; it is used to query the database. The balance
+ * and expiration are then filled accordingly.
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+ int
+ (*reserve_get) (void *cls,
+ struct TALER_MINTDB_Session *db,
+ struct TALER_MINTDB_Reserve *reserve);
+
+
+ /**
+ * Insert a incoming transaction into reserves. New reserves are
+ * also created through this function. Note that this API call
+ * starts (and stops) its own transaction scope (so the application
+ * must not do so).
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db the database connection handle
+ * @param reserve_pub public key of the reserve
+ * @param balance the amount that has to be added to the reserve
+ * @param execution_time when was the amount added
+ * @param details bank transaction details justifying the increment,
+ * must be unique for each incoming transaction
+ * @return #GNUNET_OK upon success; #GNUNET_NO if the given
+ * @a details are already known for this @a reserve_pub,
+ * #GNUNET_SYSERR upon failures (DB error, incompatible currency)
+ */
+ int
+ (*reserves_in_insert) (void *cls,
+ struct TALER_MINTDB_Session *db,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *balance,
+ struct GNUNET_TIME_Absolute execution_time,
+ const json_t *details);
+
+
+ /**
+ * Locate the response for a /withdraw request under the
+ * key of the hash of the blinded message.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param h_blind hash of the blinded coin to be signed (will match
+ * `h_coin_envelope` in the @a collectable to be returned)
+ * @param collectable corresponding collectable coin (blind signature)
+ * if a coin is found
+ * @return #GNUNET_SYSERR on internal error
+ * #GNUNET_NO if the collectable was not found
+ * #GNUNET_YES on success
+ */
+ int
+ (*get_withdraw_info) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *h_blind,
+ struct TALER_MINTDB_CollectableBlindcoin *collectable);
+
+
+ /**
+ * Store collectable bit coin under the corresponding
+ * hash of the blinded message.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param collectable corresponding collectable coin (blind signature)
+ * if a coin is found
+ * @return #GNUNET_SYSERR on internal error
+ * #GNUNET_NO if the collectable was not found
+ * #GNUNET_YES on success
+ */
+ int
+ (*insert_withdraw_info) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_MINTDB_CollectableBlindcoin *collectable);
+
+
+ /**
+ * Get all of the transaction history associated with the specified
+ * reserve.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion connection to use
+ * @param reserve_pub public key of the reserve
+ * @return known transaction history (NULL if reserve is unknown)
+ */
+ struct TALER_MINTDB_ReserveHistory *
+ (*get_reserve_history) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_ReservePublicKeyP *reserve_pub);
+
+
+ /**
+ * Free memory associated with the given reserve history.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param rh history to free.
+ */
+ void
+ (*free_reserve_history) (void *cls,
+ struct TALER_MINTDB_ReserveHistory *rh);
+
+
+ /**
+ * Check if we have the specified deposit already in the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param deposit deposit to search for
+ * @return #GNUNET_YES if we know this operation,
+ * #GNUNET_NO if this deposit is unknown to us,
+ * #GNUNET_SYSERR on DB error or if same coin(pub), merchant(pub) and
+ * transaction ID are already in DB, but for different
+ * other transaction details (contract, wiring details,
+ * amount, etc.)
+ */
+ int
+ (*have_deposit) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_MINTDB_Deposit *deposit);
+
+
+ /**
+ * Insert information about deposited coin into the
+ * database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion connection to the database
+ * @param deposit deposit information to store
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+ int
+ (*insert_deposit) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_MINTDB_Deposit *deposit);
+
+
+ /**
+ * Lookup refresh session data under the given @a session_hash.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database handle to use
+ * @param session_hash hash over the melt to use for the lookup
+ * @param[out] refresh_session where to store the result
+ * @return #GNUNET_YES on success,
+ * #GNUNET_NO if not found,
+ * #GNUNET_SYSERR on DB failure
+ */
+ int
+ (*get_refresh_session) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ struct TALER_MINTDB_RefreshSession *refresh_session);
+
+
+ /**
+ * Store new refresh session data under the given @a session_hash.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database handle to use
+ * @param session_hash hash over the melt to use to locate the session
+ * @param refresh_session session data to store
+ * @return #GNUNET_YES on success,
+ * #GNUNET_SYSERR on DB failure
+ */
+ int
+ (*create_refresh_session) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ const struct TALER_MINTDB_RefreshSession *refresh_session);
+
+
+ /**
+ * Store the given /refresh/melt request in the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param oldcoin_index index of the coin to store
+ * @param melt coin melt operation details to store; includes
+ * the session hash of the melt
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*insert_refresh_melt) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ uint16_t oldcoin_index,
+ const struct TALER_MINTDB_RefreshMelt *melt);
+
+
+ /**
+ * Get information about melted coin details from the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param session_hash hash to identify refresh session
+ * @param oldcoin_index index of the coin to retrieve
+ * @param melt melt data to fill in, can be NULL
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*get_refresh_melt) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t oldcoin_index,
+ struct TALER_MINTDB_RefreshMelt *melt);
+
+
+ /**
+ * Store in the database which coin(s) we want to create
+ * in a given refresh operation.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param session_hash hash to identify refresh session
+ * @param num_newcoins number of coins to generate, size of the @a denom_pubs array
+ * @param denom_pubs array denominations of the coins to create
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*insert_refresh_order) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t num_newcoins,
+ const struct TALER_DenominationPublicKey *denom_pubs);
+
+
+ /**
+ * Lookup in the database for the @a num_newcoins coins that we want to
+ * create in the given refresh operation.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param session_hash hash to identify refresh session
+ * @param num_newcoins size of the @a denom_pubs array
+ * @param[out] denom_pubs where to write @a num_newcoins denomination keys
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*get_refresh_order) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t num_newcoins,
+ struct TALER_DenominationPublicKey *denom_pubs);
+
+
+ /**
+ * Store information about the commitments of the given index @a i
+ * for the given refresh session in the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param session_hash hash to identify refresh session
+ * @param cnc_index cut and choose index (1st dimension), relating to #TALER_CNC_KAPPA
+ * @param num_newcoins coin index size of the @a commit_coins array
+ * @param commit_coin array of coin commitments to store
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on error
+ */
+ int
+ (*insert_refresh_commit_coins) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t cnc_index,
+ uint16_t num_newcoins,
+ const struct TALER_MINTDB_RefreshCommitCoin *commit_coins);
+
+
+ /**
+ * Obtain information about the commitment of the
+ * given coin of the given refresh session from the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param session_hash hash to identify refresh session
+ * @param cnc_index cut and choose set index (1st dimension)
+ * @param num_coins size of the @a commit_coins array
+ * @param[out] commit_coins array of coin commitments to return
+ * @return #GNUNET_OK on success
+ * #GNUNET_NO if not found
+ * #GNUNET_SYSERR on error
+ */
+ int
+ (*get_refresh_commit_coins) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t cnc_index,
+ uint16_t num_coins,
+ struct TALER_MINTDB_RefreshCommitCoin *commit_coins);
+
+
+ /**
+ * Store the commitment to the given (encrypted) refresh link data
+ * for the given refresh session.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param session_hash hash to identify refresh session
+ * @param cnc_index cut and choose index (1st dimension), relating to #TALER_CNC_KAPPA
+ * @param num_links size of the @a commit_link array
+ * @param commit_links array of link information to store
+ * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success
+ */
+ int
+ (*insert_refresh_commit_links) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t cnc_index,
+ uint16_t num_links,
+ const struct TALER_MINTDB_RefreshCommitLinkP *commit_links);
+
+ /**
+ * Obtain the commited (encrypted) refresh link data
+ * for the given refresh session.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param session_hash hash to identify refresh session
+ * @param cnc_index cut and choose index (1st dimension)
+ * @param num_links size of the @a links array to return
+ * @param[out] links array link information to return
+ * @return #GNUNET_SYSERR on internal error,
+ * #GNUNET_NO if commitment was not found
+ * #GNUNET_OK on success
+ */
+ int
+ (*get_refresh_commit_links) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t cnc_index,
+ uint16_t num_links,
+ struct TALER_MINTDB_RefreshCommitLinkP *links);
+
+
+ /**
+ * Get all of the information from the given melt commit operation.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection to use
+ * @param session_hash hash to identify refresh session
+ * @return NULL if the @a session_hash does not correspond to any known melt
+ * operation
+ */
+ struct TALER_MINTDB_MeltCommitment *
+ (*get_melt_commitment) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash);
+
+
+ /**
+ * Free information about a melt commitment.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param mc melt commitment data to free
+ */
+ void
+ (*free_melt_commitment) (void *cls,
+ struct TALER_MINTDB_MeltCommitment *mc);
+
+
+ /**
+ * Insert signature of a new coin generated during refresh into
+ * the database indexed by the refresh session and the index
+ * of the coin. This data is later used should an old coin
+ * be used to try to obtain the private keys during "/refresh/link".
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param session_hash hash to identify refresh session
+ * @param newcoin_index coin index
+ * @param ev_sig coin signature
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*insert_refresh_out) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t newcoin_index,
+ const struct TALER_DenominationSignature *ev_sig);
+
+
+ /**
+ * Obtain the link data of a coin, that is the encrypted link
+ * information, the denomination keys and the signatures.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param session_hash session to get linkage data for
+ * @return all known link data for the session
+ */
+ struct TALER_MINTDB_LinkDataList *
+ (*get_link_data_list) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct GNUNET_HashCode *session_hash);
+
+
+ /**
+ * Free memory of the link data list.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param ldl link data list to release
+ */
+ void
+ (*free_link_data_list) (void *cls,
+ struct TALER_MINTDB_LinkDataList *ldl);
+
+
+ /**
+ * Obtain shared secret and transfer public key from the public key of
+ * the coin. This information and the link information returned by
+ * @e get_link_data_list() enable the owner of an old coin to determine
+ * the private keys of the new coins after the melt.
+ *
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param coin_pub public key of the coin
+ * @param tdc function to call for each session the coin was melted into
+ * @param tdc_cls closure for @a tdc
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO on failure (not found)
+ * #GNUNET_SYSERR on internal failure (database issue)
+ */
+ int
+ (*get_transfer) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ TALER_MINTDB_TransferDataCallback tdc,
+ void *tdc_cls);
+
+
+
+ /**
+ * Test if the given /lock request is known to us.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param lock lock operation
+ * @return #GNUNET_YES if known,
+ * #GNUNET_NO if not,
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*have_lock) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_MINTDB_LockOperation *lock);
+
+
+ /**
+ * Store the given /lock request in the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param lock lock operation
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*insert_lock) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_MINTDB_LockOperation *lock);
+
+
+ /**
+ * Compile a list of all (historic) transactions performed
+ * with the given coin (/refresh/melt and /deposit operations).
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param sesssion database connection
+ * @param coin_pub coin to investigate
+ * @return list of transactions, NULL if coin is fresh
+ */
+ struct TALER_MINTDB_TransactionList *
+ (*get_coin_transactions) (void *cls,
+ struct TALER_MINTDB_Session *sesssion,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub);
+
+
+ /**
+ * Free linked list of transactions.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param list list to free
+ */
+ void
+ (*free_coin_transaction_list) (void *cls,
+ struct TALER_MINTDB_TransactionList *list);
+
+
+};
+
+
+#endif /* _NEURO_MINT_DB_H */
diff --git a/src/backend/taler_signatures.h b/src/backend/taler_signatures.h
new file mode 100644
index 00000000..402e67fe
--- /dev/null
+++ b/src/backend/taler_signatures.h
@@ -0,0 +1,653 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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 taler_signatures.h
+ * @brief message formats and signature constants used to define
+ * the binary formats of signatures in Taler
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ *
+ * This file should define the constants and C structs that one needs
+ * to know to implement Taler clients (wallets or merchants or
+ * auditor) that need to produce or verify Taler signatures.
+ */
+
+#ifndef TALER_SIGNATURES_H
+#define TALER_SIGNATURES_H
+
+#if HAVE_GNUNET_GNUNET_UTIL_LIB_H
+#include <gnunet/gnunet_util_lib.h>
+#elif HAVE_GNUNET_GNUNET_UTIL_TALER_WALLET_LIB_H
+#include <gnunet/gnunet_util_taler_wallet_lib.h>
+#endif
+
+#include "taler_amount_lib.h"
+#include "taler_crypto_lib.h"
+
+/**
+ * Cut-and-choose size for refreshing. Client looses the gamble (of
+ * unaccountable transfers) with probability 1/TALER_CNC_KAPPA. Refresh cost
+ * increases linearly with TALER_CNC_KAPPA, and 3 is sufficient up to a
+ * income/sales tax of 66% of total transaction value. As there is
+ * no good reason to change this security parameter, we declare it
+ * fixed and part of the protocol.
+ */
+#define TALER_CNC_KAPPA 3
+
+/**
+ * After what time do idle reserves "expire"? We might want to make
+ * this a configuration option (eventually).
+ */
+#define TALER_IDLE_RESERVE_EXPIRATION_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 5)
+
+/*********************************************/
+/* Mint offline signatures (with master key) */
+/*********************************************/
+
+/**
+ * Purpose for signing public keys signed by the mint master key.
+ */
+#define TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY 1024
+
+/**
+ * Purpose for denomination keys signed by the mint master key.
+ */
+#define TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY 1025
+
+
+/*********************************************/
+/* Mint online signatures (with signing key) */
+/*********************************************/
+
+/**
+ * Purpose for the state of a reserve, signed by the mint's signing
+ * key.
+ */
+#define TALER_SIGNATURE_MINT_RESERVE_STATUS 1032
+
+/**
+ * Signature where the Mint confirms a deposit request.
+ */
+#define TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT 1033
+
+/**
+ * Signature where the mint (current signing key) confirms the
+ * no-reveal index for cut-and-choose and the validity of the melted
+ * coins.
+ */
+#define TALER_SIGNATURE_MINT_CONFIRM_MELT 1034
+
+/**
+ * Signature where the Mint confirms the full /keys response set.
+ */
+#define TALER_SIGNATURE_MINT_KEY_SET 1035
+
+
+/*********************/
+/* Wallet signatures */
+/*********************/
+
+/**
+ * Signature where the auditor confirms that he is
+ * aware of certain denomination keys from the mint.
+ */
+#define TALER_SIGNATURE_AUDITOR_MINT_KEYS 1064
+
+
+/***********************/
+/* Merchant signatures */
+/***********************/
+
+/**
+ * Signature where the merchant confirms a contract (to the customer).
+ */
+#define TALER_SIGNATURE_MERCHANT_CONTRACT 1101
+
+/**
+ * Signature where the merchant confirms a refund (of a coin).
+ */
+#define TALER_SIGNATURE_MERCHANT_REFUND 1102
+
+
+/*********************/
+/* Wallet signatures */
+/*********************/
+
+/**
+ * Signature where the reserve key confirms a withdraw request.
+ */
+#define TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW 1200
+
+/**
+ * Signature made by the wallet of a user to confirm a deposit of a coin.
+ */
+#define TALER_SIGNATURE_WALLET_COIN_DEPOSIT 1201
+
+/**
+ * Signature using a coin key confirming the melting of a coin.
+ */
+#define TALER_SIGNATURE_WALLET_COIN_MELT 1202
+
+
+/*******************/
+/* Test signatures */
+/*******************/
+
+/**
+ * EdDSA test signature.
+ */
+#define TALER_SIGNATURE_CLIENT_TEST_EDDSA 1302
+
+/**
+ * EdDSA test signature.
+ */
+#define TALER_SIGNATURE_MINT_TEST_EDDSA 1303
+
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * @brief Format used for to generate the signature on a request to withdraw
+ * coins from a reserve.
+ */
+struct TALER_WithdrawRequestPS
+{
+
+ /**
+ * Purpose must be #TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW.
+ * Used with an EdDSA signature of a `struct TALER_ReservePublicKeyP`.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Reserve public key (which reserve to withdraw from). This is
+ * the public key which must match the signature.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Value of the coin being minted (matching the denomination key)
+ * plus the transaction fee. We include this in what is being
+ * signed so that we can verify a reserve's remaining total balance
+ * without needing to access the respective denomination key
+ * information each time.
+ */
+ struct TALER_AmountNBO amount_with_fee;
+
+ /**
+ * Withdrawl fee charged by the mint. This must match the Mint's
+ * denomination key's withdrawl fee. If the client puts in an
+ * invalid withdrawl fee (too high or too low) that does not match
+ * the Mint's denomination key, the withdraw operation is invalid
+ * and will be rejected by the mint. The @e amount_with_fee minus
+ * the @e withdraw_fee is must match the value of the generated
+ * coin. We include this in what is being signed so that we can
+ * verify a mint's accounting without needing to access the
+ * respective denomination key information each time.
+ */
+ struct TALER_AmountNBO withdraw_fee;
+
+ /**
+ * Hash of the denomination public key for the coin that is withdrawn.
+ */
+ struct GNUNET_HashCode h_denomination_pub;
+
+ /**
+ * Hash of the (blinded) message to be signed by the Mint.
+ */
+ struct GNUNET_HashCode h_coin_envelope;
+};
+
+
+/**
+ * @brief Format used to generate the signature on a request to deposit
+ * a coin into the account of a merchant.
+ */
+struct TALER_DepositRequestPS
+{
+ /**
+ * Purpose must be #TALER_SIGNATURE_WALLET_COIN_DEPOSIT.
+ * Used for an EdDSA signature with the `struct TALER_CoinSpendPublicKeyP`.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash over the contract for which this deposit is made.
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Hash over the wiring information of the merchant.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Time when this request was generated. Used, for example, to
+ * assess when (roughly) the income was achieved for tax purposes.
+ * Note that the Mint will only check that the timestamp is not "too
+ * far" into the future (i.e. several days). The fact that the
+ * timestamp falls within the validity period of the coin's
+ * denomination key is irrelevant for the validity of the deposit
+ * request, as obviously the customer and merchant could conspire to
+ * set any timestamp. Also, the Mint must accept very old deposit
+ * requests, as the merchant might have been unable to transmit the
+ * deposit request in a timely fashion (so back-dating is not
+ * prevented).
+ */
+ struct GNUNET_TIME_AbsoluteNBO timestamp;
+
+ /**
+ * How much time does the merchant have to issue a refund request?
+ * Zero if refunds are not allowed. After this time, the coin
+ * cannot be refunded.
+ */
+ struct GNUNET_TIME_AbsoluteNBO refund_deadline;
+
+ /**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions. 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;
+
+ /**
+ * Amount to be deposited, including deposit fee charged by the
+ * mint. This is the total amount that the coin's value at the mint
+ * will be reduced by.
+ */
+ struct TALER_AmountNBO amount_with_fee;
+
+ /**
+ * Depositing fee charged by the mint. This must match the Mint's
+ * denomination key's depositing fee. If the client puts in an
+ * invalid deposit fee (too high or too low) that does not match the
+ * Mint's denomination key, the deposit operation is invalid and
+ * will be rejected by the mint. The @e amount_with_fee minus the
+ * @e deposit_fee is the amount that will be transferred to the
+ * account identified by @e h_wire.
+ */
+ struct TALER_AmountNBO deposit_fee;
+
+ /**
+ * The Merchant's public key. Allows the merchant to later refund
+ * the transaction. All zeros if nobody is allowed to refund the
+ * transaction later.
+ */
+ struct TALER_MerchantPublicKeyP merchant;
+
+ /**
+ * The coin's public key. This is the value that must have been
+ * signed (blindly) by the Mint. The deposit request is to be
+ * signed by the corresponding private key (using EdDSA).
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+};
+
+
+/**
+ * @brief Format used to generate the signature on a confirmation
+ * from the mint that a deposit request succeeded.
+ */
+struct TALER_DepositConfirmationPS
+{
+ /**
+ * Purpose must be #TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT. Signed
+ * by a `struct TALER_MintPublicKeyP` using EdDSA.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash over the contract for which this deposit is made.
+ */
+ struct GNUNET_HashCode h_contract;
+
+ /**
+ * Hash over the wiring information of the merchant.
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions.
+ */
+ uint64_t transaction_id GNUNET_PACKED;
+
+ /**
+ * Time when this confirmation was generated.
+ */
+ struct GNUNET_TIME_AbsoluteNBO timestamp;
+
+ /**
+ * How much time does the @e merchant have to issue a refund
+ * request? Zero if refunds are not allowed. After this time, the
+ * coin cannot be refunded. Note that the wire transfer will not be
+ * performed by the mint until the refund deadline. This value
+ * is taken from the original deposit request.
+ */
+ struct GNUNET_TIME_AbsoluteNBO refund_deadline;
+
+ /**
+ * Amount to be deposited, excluding fee. Calculated from the
+ * amount with fee and the fee from the deposit request.
+ */
+ struct TALER_AmountNBO amount_without_fee;
+
+ /**
+ * The coin's public key. This is the value that must have been
+ * signed (blindly) by the Mint. The deposit request is to be
+ * signed by the corresponding private key (using EdDSA).
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * The Merchant's public key. Allows the merchant to later refund
+ * the transaction. All zeros if nobody is allowed to refund the
+ * transaction later.
+ */
+ struct TALER_MerchantPublicKeyP merchant;
+
+};
+
+
+/**
+ * @brief Message signed by a coin to indicate that the coin should be
+ * melted.
+ */
+struct TALER_RefreshMeltCoinAffirmationPS
+{
+ /**
+ * Purpose is #TALER_SIGNATURE_WALLET_COIN_MELT.
+ * Used for an EdDSA signature with the `struct TALER_CoinSpendPublicKeyP`.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Which melting session should the coin become a part of.
+ */
+ struct GNUNET_HashCode session_hash;
+
+ /**
+ * How much of the value of the coin should be melted? This amount
+ * includes the fees, so the final amount contributed to the melt is
+ * this value minus the fee for melting the coin. We include the
+ * fee in what is being signed so that we can verify a reserve's
+ * remaining total balance without needing to access the respective
+ * denomination key information each time.
+ */
+ struct TALER_AmountNBO amount_with_fee;
+
+ /**
+ * Melting fee charged by the mint. This must match the Mint's
+ * denomination key's melting fee. If the client puts in an invalid
+ * melting fee (too high or too low) that does not match the Mint's
+ * denomination key, the melting operation is invalid and will be
+ * rejected by the mint. The @e amount_with_fee minus the @e
+ * melt_fee is the amount that will be credited to the melting
+ * session.
+ */
+ struct TALER_AmountNBO melt_fee;
+
+ /**
+ * The coin's public key. This is the value that must have been
+ * signed (blindly) by the Mint. The deposit request is to be
+ * signed by the corresponding private key (using EdDSA).
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+};
+
+
+/**
+ * @brief Format of the block signed by the Mint in response to a successful
+ * "/refresh/melt" request. Hereby the mint affirms that all of the
+ * coins were successfully melted. This also commits the mint to a
+ * particular index to not be revealed during the refresh.
+ */
+struct TALER_RefreshMeltConfirmationPS
+{
+ /**
+ * Purpose is #TALER_SIGNATURE_MINT_CONFIRM_MELT. Signed
+ * by a `struct TALER_MintPublicKeyP` using EdDSA.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash of the refresh session.
+ */
+ struct GNUNET_HashCode session_hash;
+
+ /**
+ * Index that the client will not have to reveal, in NBO.
+ * Must be smaller than #TALER_CNC_KAPPA.
+ */
+ uint16_t noreveal_index GNUNET_PACKED;
+};
+
+
+/**
+ * @brief Information about a signing key of the mint. Signing keys are used
+ * to sign mint messages other than coins, i.e. to confirm that a
+ * deposit was successful or that a refresh was accepted.
+ */
+struct TALER_MintSigningKeyValidityPS
+{
+ /**
+ * Signature over the signing key (by the master key of the mint).
+ *
+ * FIXME: should be moved outside of the "PS" struct, this is ugly.
+ * (and makes this struct different from all of the others)
+ */
+ struct TALER_MasterSignatureP signature;
+
+ /**
+ * Purpose is #TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Master public key of the mint corresponding to @e signature.
+ * This is the long-term offline master key of the mint.
+ */
+ struct TALER_MasterPublicKeyP master_public_key;
+
+ /**
+ * When does this signing key begin to be valid?
+ */
+ struct GNUNET_TIME_AbsoluteNBO start;
+
+ /**
+ * When does this signing key expire? Note: This is currently when
+ * the Mint will definitively stop using it. Signatures made with
+ * the key remain valid until @e end. When checking validity periods,
+ * clients should allow for some overlap between keys and tolerate
+ * the use of either key during the overlap time (due to the
+ * possibility of clock skew).
+ */
+ struct GNUNET_TIME_AbsoluteNBO expire;
+
+ /**
+ * When do signatures with this signing key become invalid? After
+ * this point, these signatures cannot be used in (legal) disputes
+ * anymore, as the Mint is then allowed to destroy its side of the
+ * evidence. @e end is expected to be significantly larger than @e
+ * expire (by a year or more).
+ */
+ struct GNUNET_TIME_AbsoluteNBO end;
+
+ /**
+ * The public online signing key that the mint will use
+ * between @e start and @e expire.
+ */
+ struct TALER_MintPublicKeyP signkey_pub;
+};
+
+
+/**
+ * @brief Signature made by the mint over the full set of keys, used
+ * to detect cheating mints that give out different sets to
+ * different users.
+ */
+struct TALER_MintKeySetPS
+{
+
+ /**
+ * Purpose is #TALER_SIGNATURE_MINT_KEY_SET. Signed
+ * by a `struct TALER_MintPublicKeyP` using EdDSA.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Time of the key set issue.
+ */
+ struct GNUNET_TIME_AbsoluteNBO list_issue_date;
+
+ /**
+ * Hash over the various denomination signing keys returned.
+ */
+ struct GNUNET_HashCode hc;
+};
+
+
+/**
+ * @brief Information about a denomination key. Denomination keys
+ * are used to sign coins of a certain value into existence.
+ */
+struct TALER_DenominationKeyValidityPS
+{
+
+ /**
+ * Purpose is #TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * The long-term offline master key of the mint that was
+ * used to create @e signature.
+ */
+ struct TALER_MasterPublicKeyP master;
+
+ /**
+ * Start time of the validity period for this key.
+ */
+ struct GNUNET_TIME_AbsoluteNBO start;
+
+ /**
+ * The mint will sign fresh coins between @e start and this time.
+ * @e expire_withdraw will be somewhat larger than @e start to
+ * ensure a sufficiently large anonymity set, while also allowing
+ * the Mint to limit the financial damage in case of a key being
+ * compromised. Thus, mints with low volume are expected to have a
+ * longer withdraw period (@e expire_withdraw - @e start) than mints
+ * with high transaction volume. The period may also differ between
+ * types of coins. A mint may also have a few denomination keys
+ * with the same value with overlapping validity periods, to address
+ * issues such as clock skew.
+ */
+ struct GNUNET_TIME_AbsoluteNBO expire_withdraw;
+
+ /**
+ * Coins signed with the denomination key must be spent or refreshed
+ * between @e start and this expiration time. After this time, the
+ * mint will refuse transactions involving this key as it will
+ * "drop" the table with double-spending information (shortly after)
+ * this time. Note that wallets should refresh coins significantly
+ * before this time to be on the safe side. @e expire_spend must be
+ * significantly larger than @e expire_withdraw (by months or even
+ * years).
+ */
+ struct GNUNET_TIME_AbsoluteNBO expire_spend;
+
+ /**
+ * When do signatures with this denomination key become invalid?
+ * After this point, these signatures cannot be used in (legal)
+ * disputes anymore, as the Mint is then allowed to destroy its side
+ * of the evidence. @e expire_legal is expected to be significantly
+ * larger than @e expire_spend (by a year or more).
+ */
+ struct GNUNET_TIME_AbsoluteNBO expire_legal;
+
+ /**
+ * The value of the coins signed with this denomination key.
+ */
+ struct TALER_AmountNBO value;
+
+ /**
+ * The fee the mint charges when a coin of this type is withdrawn.
+ * (can be zero).
+ */
+ struct TALER_AmountNBO fee_withdraw;
+
+ /**
+ * The fee the mint charges when a coin of this type is deposited.
+ * (can be zero).
+ */
+ struct TALER_AmountNBO fee_deposit;
+
+ /**
+ * The fee the mint charges when a coin of this type is refreshed.
+ * (can be zero).
+ */
+ struct TALER_AmountNBO fee_refresh;
+
+ /**
+ * Hash code of the denomination public key. (Used to avoid having
+ * the variable-size RSA key in this struct.)
+ */
+ struct GNUNET_HashCode denom_hash;
+
+};
+
+
+/**
+ * @brief Information signed by an auditor affirming
+ * the master public key and the denomination keys
+ * of a mint.
+ */
+struct TALER_MintKeyValidityPS
+{
+
+ /**
+ * Purpose is #TALER_SIGNATURE_AUDITOR_MINT_KEYS.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * The long-term offline master key of the mint, affirmed by the
+ * auditor.
+ */
+ struct TALER_MasterPublicKeyP master;
+
+ /**
+ * Array of hash(es) of the mint's denomination keys.
+ * Specifically, this is the hash over the
+ * `struct TALER_DenominationKeyValidityPS`, not just
+ * the public key (as the auditor needs to check against
+ * the correct valuations and fee structure).
+ */
+ /* struct GNUNET_HashCode h_dks; */
+
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/backend/taler_util.h b/src/backend/taler_util.h
new file mode 100644
index 00000000..00397cc8
--- /dev/null
+++ b/src/backend/taler_util.h
@@ -0,0 +1,162 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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_util.h
+ * @brief Interface for common utility functions
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#ifndef TALER_UTIL_H
+#define TALER_UTIL_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_amount_lib.h"
+#include "taler_crypto_lib.h"
+#include "taler_json_lib.h"
+
+
+
+/* Define logging functions */
+#define TALER_LOG_DEBUG(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
+
+#define TALER_LOG_WARNING(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, __VA_ARGS__)
+
+#define TALER_LOG_ERROR(...) \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__)
+
+
+/**
+ * Tests a given as assertion and if failed prints it as a warning with the
+ * given reason
+ *
+ * @param EXP the expression to test as assertion
+ * @param reason string to print as warning
+ */
+#define TALER_assert_as(EXP, reason) \
+ do { \
+ if (EXP) break; \
+ TALER_LOG_ERROR("%s at %s:%d\n", reason, __FILE__, __LINE__); \
+ abort(); \
+ } while(0)
+
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' with the message given
+ * by gcry_strerror(rc).
+ */
+#define TALER_LOG_GCRY_ERROR(cmd, rc) do { TALER_LOG_ERROR("`%s' failed at %s:%d with error: %s\n", cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0)
+
+
+#define TALER_gcry_ok(cmd) \
+ do {int rc; rc = cmd; if (!rc) break; TALER_LOG_ERROR("A Gcrypt call failed at %s:%d with error: %s\n", __FILE__, __LINE__, gcry_strerror(rc)); abort(); } while (0)
+
+
+/**
+ * Initialize Gcrypt library.
+ */
+void
+TALER_gcrypt_init (void);
+
+
+/**
+ * Round a time value so that it is suitable for transmission
+ * via JSON encodings.
+ *
+ * @param at time to round
+ * @return #GNUNET_OK if time was already rounded, #GNUNET_NO if
+ * it was just now rounded
+ */
+int
+TALER_round_abs_time (struct GNUNET_TIME_Absolute *at);
+
+
+/**
+ * Round a time value so that it is suitable for transmission
+ * via JSON encodings.
+ *
+ * @param rt time to round
+ * @return #GNUNET_OK if time was already rounded, #GNUNET_NO if
+ * it was just now rounded
+ */
+int
+TALER_round_rel_time (struct GNUNET_TIME_Relative *rt);
+
+
+/**
+ * Load configuration by parsing all configuration
+ * files in the given directory.
+ *
+ * @param base_dir directory with the configuration files
+ * @return NULL on error, otherwise configuration
+ */
+struct GNUNET_CONFIGURATION_Handle *
+TALER_config_load (const char *base_dir);
+
+
+/**
+ * Obtain denomination amount from configuration file.
+ *
+ * @param section section of the configuration to access
+ * @param option option of the configuration to access
+ * @param[out] denom set to the amount found in configuration
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+TALER_config_get_denom (struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ const char *option,
+ struct TALER_Amount *denom);
+
+
+/**
+ * Get the path to a specific Taler installation directory or, with
+ * #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation
+ * directory.
+ *
+ * @param dirkind what kind of directory is desired?
+ * @return a pointer to the dir path (to be freed by the caller)
+ */
+char *
+TALER_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind);
+
+
+/**
+ * Print out details on command line options (implements --help).
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (points to about text)
+ * @param option name of the option
+ * @param value not used (NULL)
+ * @return #GNUNET_NO (do not continue, not an error)
+ */
+int
+TALER_GETOPT_format_help_ (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
+ void *scls,
+ const char *option,
+ const char *value);
+
+/**
+ * Macro defining the option to print the command line
+ * help text (-h option).
+ *
+ * @param about string with brief description of the application
+ */
+#define TALER_GETOPT_OPTION_HELP(about) \
+ { 'h', "help", (const char *) NULL, gettext_noop("print this help"), 0, &TALER_GETOPT_format_help_, (void *) about }
+
+#endif