summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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