summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--src/Makefile.am2
-rw-r--r--src/backend-lib/Makefile.am24
-rw-r--r--src/backend-lib/merchant_db.c354
-rw-r--r--src/backend-lib/merchant_db.h101
-rw-r--r--src/backend-lib/taler-merchant-httpd_contract.c144
-rw-r--r--src/backend-lib/taler_merchant_contract_lib.h62
-rw-r--r--src/backend-lib/taler_merchant_lib.h1
-rw-r--r--src/backend/Makefile.am4
-rw-r--r--src/backend/QUESTIONS6
-rw-r--r--src/backend/merchant.priv1
-rw-r--r--src/backend/taler-merchant-httpd.c146
-rw-r--r--src/frontend/checkout.php17
-rw-r--r--src/frontend/generate_taler_contract.php3
-rw-r--r--src/frontend/index.html5
-rw-r--r--src/include/merchant.h (renamed from src/backend/merchant.h)0
-rw-r--r--src/tests/Makefile.am26
-rwxr-xr-xsrc/tests/merchant-contract-testbin0 -> 50976 bytes
-rw-r--r--src/tests/merchant-contract-test.c207
-rw-r--r--src/tests/merchant.c173
-rw-r--r--src/tests/merchant.conf18
-rw-r--r--src/tests/merchant.priv1
-rw-r--r--src/tests/merchant_db.c354
-rw-r--r--src/tests/merchant_db.h101
-rw-r--r--src/tests/test.debug37
-rw-r--r--src/tests/test.valgrind181
26 files changed, 1822 insertions, 150 deletions
diff --git a/configure.ac b/configure.ac
index f32d7fdd..55d1e196 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,5 +180,7 @@ AC_CHECK_FUNCS([strdup])
AC_CONFIG_FILES([Makefile
src/Makefile
src/include/Makefile
-src/backend/Makefile])
+src/tests/Makefile
+src/backend/Makefile
+src/backend-lib/Makefile])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 46fa8eba..bebdae8c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,2 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/include
-SUBDIRS = include backend
+SUBDIRS = include backend backend-lib
diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am
new file mode 100644
index 00000000..61867e41
--- /dev/null
+++ b/src/backend-lib/Makefile.am
@@ -0,0 +1,24 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+lib_LTLIBRARIES = \
+ libtalermerchant.la
+
+include_HEADERS = \
+ taler_merchant_lib.h \
+ taler_merchant_contract_lib.h
+
+libtalermerchant_la_SOURCES = \
+ taler-merchant-httpd_contract.c \
+ taler_merchant_contract_lib.h \
+ merchant_db.c merchant_db.h \
+ merchant.h
+
+libtalermerchant_la_LIBADD = \
+ $(LIBGCRYPT_LIBS) \
+ -ltalerutil \
+ -lgnunetutil \
+ -ltalerpq \
+ -lgnunetpostgres \
+ -lpq \
+ -lpthread
diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c
new file mode 100644
index 00000000..274de25a
--- /dev/null
+++ b/src/backend-lib/merchant_db.c
@@ -0,0 +1,354 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 merchant/merchant_db.c
+ * @brief database helper functions used by the merchant
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_pq_lib.h>
+#include "merchant_db.h"
+
+
+#define PQSQL_strerror(kind, cmd, res) \
+ GNUNET_log_from (kind, "merchant-db", \
+ "SQL %s failed at %s:%u with error: %s", \
+ cmd, __FILE__, __LINE__, PQresultErrorMessage (res));
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * Connect to postgresql database
+ *
+ * @param cfg the configuration handle
+ * @return connection to the postgresql database; NULL upon error
+ */
+PGconn *
+MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ return GNUNET_POSTGRES_connect (cfg, "merchant-db");
+}
+
+
+/**
+ * Disconnect from the database
+ *
+ * @param conn database handle to close
+ */
+void
+MERCHANT_DB_disconnect (PGconn *conn)
+{
+ PQfinish (conn);
+}
+
+
+/**
+ * Initialize merchant tables
+ *
+ * @param conn the connection handle to postgres db.
+ * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
+ * contents are dropped when the @a conn is closed
+ * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
+ */
+int
+MERCHANT_DB_initialize (PGconn *conn, int tmp)
+{
+ const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
+ char *sql;
+ PGresult *res;
+ ExecStatusType status;
+ int ret;
+
+ res = NULL;
+ (void) GNUNET_asprintf (&sql,
+ "BEGIN TRANSACTION;"
+ "CREATE %1$s TABLE IF NOT EXISTS contracts ("
+ "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 INT8 NOT NULL,"
+ "expiry INT8 NOT NULL,"
+ "product INT8 NOT NULL);"
+ "CREATE %1$s TABLE IF NOT EXISTS checkouts ("
+ "coin_pub BYTEA PRIMARY KEY,"
+ "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_free (sql);
+ if (GNUNET_OK != ret)
+ return ret;
+
+ while (NULL != (res = PQgetResult (conn)))
+ {
+ status = PQresultStatus (res);
+ PQclear (res);
+ }
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "contract_create",
+ "INSERT INTO contracts"
+ "(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);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "get_contract_product",
+ "SELECT ("
+ "product"
+ ") FROM contracts "
+ "WHERE ("
+ "contract_id=$1"
+ ")",
+ 1, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
+ PQclear (res);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "checkout_create",
+ "INSERT INTO checkouts ("
+ "coin_pub,"
+ "contract_id,"
+ "amount,"
+ "amount_fraction,"
+ "coin_sig"
+ ") VALUES ("
+ "$1, $2, $3, $4, $5"
+ ")",
+ 5, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
+ PQclear (res);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "get_checkout_product",
+ "SELECT ("
+ "product"
+ ") FROM contracts "
+ "WHERE "
+ "contract_id IN ("
+ "SELECT (contract_id) FROM checkouts "
+ "WHERE coin_pub=$1"
+ ")",
+ 1, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
+ PQclear (res);
+
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ if (NULL != res)
+ {
+ PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res);
+ PQclear (res);
+ }
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Inserts a contract record into the database and if successfull returns the
+ * serial number of the inserted row.
+ *
+ * @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 GNUNET_OK on success, GNUNET_SYSERR upon error
+ */
+
+uint32_t
+MERCHANT_DB_contract_create (PGconn *conn,
+ 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;
+ uint64_t value_nbo;
+ uint32_t fraction_nbo;
+ uint64_t nounce_nbo;
+ #endif
+ ExecStatusType status;
+
+ #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_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),
+ TALER_PQ_query_param_absolute_time (expiry),
+ TALER_PQ_query_param_uint64 (&product),
+ TALER_PQ_query_param_end
+ };
+
+ /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */
+ res = TALER_PQ_exec_prepared (conn, "contract_create", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_COMMAND_OK != status);
+ PQclear (res);
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ PQclear (res);
+ return GNUNET_SYSERR;
+}
+
+long long
+MERCHANT_DB_get_contract_product (PGconn *conn,
+ uint64_t contract_id)
+{
+ PGresult *res;
+ uint64_t product;
+ ExecStatusType status;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&contract_id),
+ TALER_PQ_query_param_end
+ };
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("product", &product),
+ TALER_PQ_result_spec_end
+ };
+
+ contract_id = GNUNET_htonll (contract_id);
+ res = TALER_PQ_exec_prepared (conn, "get_contract_product", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_TUPLES_OK != status);
+ EXITIF (1 != PQntuples (res));
+ EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
+ PQclear (res);
+ return GNUNET_ntohll ((uint64_t) product);
+
+ EXITIF_exit:
+ PQclear (res);
+ return -1;
+}
+
+unsigned int
+MERCHANT_DB_checkout_create (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
+ uint64_t transaction_id,
+ struct TALER_Amount *amount,
+ struct GNUNET_CRYPTO_rsa_Signature *coin_sig)
+{
+ PGresult *res;
+ ExecStatusType status;
+ uint32_t value_nbo;
+ uint32_t fraction_nbo;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_rsa_public_key (coin_pub),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_uint32 (&value_nbo),
+ TALER_PQ_query_param_uint32 (&fraction_nbo),
+ TALER_PQ_query_param_rsa_signature (coin_sig),
+ TALER_PQ_query_param_end
+ };
+
+ transaction_id = GNUNET_htonll (transaction_id);
+ value_nbo = htonl (amount->value);
+ fraction_nbo = htonl (amount->fraction);
+ res = TALER_PQ_exec_prepared (conn, "checkout_create", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_COMMAND_OK != status);
+ PQclear (res);
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ PQclear (res);
+ return GNUNET_SYSERR;
+}
+
+
+long long
+MERCHANT_DB_get_checkout_product (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub)
+{
+ PGresult *res;
+ ExecStatusType status;
+ uint64_t product;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_rsa_public_key (coin_pub),
+ TALER_PQ_query_param_end
+ };
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("product", &product),
+ TALER_PQ_result_spec_end
+ };
+
+ product = -1;
+ res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_TUPLES_OK != status);
+ if (0 == PQntuples (res))
+ {
+ TALER_LOG_DEBUG ("Checkout not found for given coin");
+ goto EXITIF_exit;
+ }
+ EXITIF (1 != PQntuples (res));
+ EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
+ PQclear (res);
+ return GNUNET_ntohll ((uint64_t) product);
+
+ EXITIF_exit:
+ PQclear (res);
+ return -1;
+}
+/* end of merchant-db.c */
diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h
new file mode 100644
index 00000000..a723b229
--- /dev/null
+++ b/src/backend-lib/merchant_db.h
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 merchant/merchant_db.h
+ * @brief database helper functions used by the merchant
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef MERCHANT_DB_H
+#define MERCHANT_DB_H
+
+#include <gnunet/gnunet_postgres_lib.h>
+#include <taler/taler_util.h>
+
+/**
+ * Connect to postgresql database
+ *
+ * @param cfg the configuration handle
+ * @return connection to the postgresql database; NULL upon error
+ */
+PGconn *
+MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Disconnect from the database
+ *
+ * @param conn database handle to close
+ */
+void
+MERCHANT_DB_disconnect (PGconn *conn);
+
+
+/**
+ * Initialize merchant tables
+ *
+ * @param conn the connection handle to postgres db.
+ * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
+ * contents are dropped when the @a conn is closed
+ * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
+ */
+int
+MERCHANT_DB_initialize (PGconn *conn, int tmp);
+
+
+/**
+ * Inserts a contract record into the database and if successfull returns the
+ * serial number of the inserted row.
+ *
+ * @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 GNUNET_OK on success, GNUNET_SYSERR upon error
+ */
+
+uint32_t
+MERCHANT_DB_contract_create (PGconn *conn,
+ const struct GNUNET_TIME_Absolute *expiry,
+ const struct TALER_Amount *amount,
+ uint64_t c_id,
+ const char *desc,
+ uint64_t nounce,
+ uint64_t product);
+
+long long
+MERCHANT_DB_get_contract_product (PGconn *conn,
+ uint64_t contract_id);
+
+unsigned int
+MERCHANT_DB_checkout_create (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
+ uint64_t transaction_id,
+ struct TALER_Amount *amount,
+ struct GNUNET_CRYPTO_rsa_Signature *coin_sig);
+
+
+long long
+MERCHANT_DB_get_checkout_product (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub);
+
+#endif /* MERCHANT_DB_H */
+
+/* end of merchant-db.h */
diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c
new file mode 100644
index 00000000..55c56e0a
--- /dev/null
+++ b/src/backend-lib/taler-merchant-httpd_contract.c
@@ -0,0 +1,144 @@
+#include "platform.h"
+#include <jansson.h>
+#include <taler/taler_signatures.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "merchant.h"
+#include "merchant_db.h"
+#include "taler_merchant_contract_lib.h"
+
+
+/* TODO: make this file a library, and programmatically call the following
+ * functions */
+
+/**
+ * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
+ */
+#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000)
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+/**
+* 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
+* @param wire the merchant's wire details
+* @return the hash to be included in the contract's blob
+*
+*/
+
+static struct GNUNET_HashCode
+hash_wireformat (uint64_t nounce, const struct MERCHANT_WIREFORMAT_Sepa *wire)
+{
+ 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;
+}
+
+/**
+* Take from the frontend the (partly) generated contract and fill
+* the missing values in it; for example, the SEPA-aware values.
+* Moreover, it stores the contract in the DB and does the signature of it.
+* @param contract parsed contract, originated by the frontend
+* @param db_conn the handle to the local DB
+* @param kpriv merchant's private key
+* @param wire merchant's bank's details
+* @param sig where to store the signature
+* @return pointer to the complete JSON; NULL upon errors
+*/
+
+/* TODO: this handles a simplified version (for debugging purposes)
+ of the contract. To expand to the full fledged version */
+
+json_t *
+MERCHANT_handle_contract (json_t *contract,
+ PGconn *db_conn,
+ struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv,
+ const struct MERCHANT_WIREFORMAT_Sepa *wire,
+ struct GNUNET_CRYPTO_EddsaSignature *sig)
+{
+ json_t *root;
+ json_t *j_timestamp;
+ json_t *jh_wire;
+ json_t *j_amount;
+ json_int_t j_trans_id;
+
+ uint64_t nounce;
+ uint64_t product_id;
+ char *a;
+ char *h_wire_enc;
+ char *str;
+ struct GNUNET_HashCode h_wire;
+ struct GNUNET_TIME_Absolute timestamp;
+ struct TALER_Amount amount;
+ struct TALER_AmountNBO amount_nbo;
+ struct ContractNBO contract_nbo;
+
+ nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
+ // timing mgmt
+ timestamp = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+ GNUNET_TIME_UNIT_DAYS);
+ ROUND_TO_SECS (timestamp, abs_value_us);
+ j_timestamp = TALER_json_from_abs (timestamp);
+
+ // wireformat mgmt
+ h_wire = hash_wireformat (nounce, wire);
+ h_wire_enc = GNUNET_STRINGS_data_to_string_alloc (&h_wire,
+ sizeof (struct GNUNET_HashCode));
+
+ jh_wire = json_string (h_wire_enc);
+
+ str = json_dumps (contract, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
+
+
+ json_unpack (contract, "{s:o, s:I}",
+ "amount", &j_amount,
+ "trans_id", &j_trans_id);
+
+ /* needed for DB stuff */
+ TALER_json_to_amount (j_amount, &amount);
+ /* temporary way of getting this value. To be adapted to the expanded contract
+ format. See 'TODO' above. */
+ product_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
+ /* adding the generated values in this JSON */
+ root = json_pack ("{s:o, s:I, s:s, s:o}",
+ "amount", j_amount,
+ "trans_id", j_trans_id,
+ "h_wire", jh_wire,
+ "timestamp", j_timestamp);
+ a = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER);
+
+ // DB mgmt
+ if (GNUNET_SYSERR == MERCHANT_DB_contract_create (db_conn,
+ &timestamp,
+ &amount,
+ (uint64_t) j_trans_id, // safe?
+ a,
+ nounce,
+ product_id));
+ contract_nbo.h_wire = h_wire;
+ TALER_amount_hton (&amount_nbo, &amount);
+ contract_nbo.amount = amount_nbo;
+ contract_nbo.t = GNUNET_TIME_absolute_hton (timestamp);
+ contract_nbo.m = GNUNET_htonll ((uint64_t) j_trans_id); // safe?
+ GNUNET_CRYPTO_hash (a, strlen (a) + 1, &contract_nbo.h_contract_details);
+ free (a);
+ contract_nbo.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
+ contract_nbo.purpose.size = htonl (sizeof (struct ContractNBO));
+ GNUNET_CRYPTO_eddsa_sign (kpriv, &contract_nbo.purpose, sig);
+
+ return root;
+}
diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h
new file mode 100644
index 00000000..4e345703
--- /dev/null
+++ b/src/backend-lib/taler_merchant_contract_lib.h
@@ -0,0 +1,62 @@
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+struct ContractNBO
+{
+ /**
+ * Purpose header for the signature over contract
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * The transaction identifier. NOTE: it was m[13]. TODO:
+ * change the API accordingly!
+ */
+ uint64_t m;
+
+ /**
+ * Expiry time
+ */
+ struct GNUNET_TIME_AbsoluteNBO t;
+
+ /**
+ * The invoice amount
+ */
+ struct TALER_AmountNBO amount;
+
+ /**
+ * The hash of the merchant's wire details (bank account information), with a nounce
+ */
+ struct GNUNET_HashCode h_wire;
+
+ /**
+ * Hash of the JSON contract in UTF-8 including 0-termination,
+ * using JSON_COMPACT encoding with sorted fields.
+ */
+ struct GNUNET_HashCode h_contract_details;
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+* Take from the frontend the (partly) generated contract and fill
+* the missing values in it; for example, the SEPA-aware values.
+* Moreover, it stores the contract in the DB and does the signature of it.
+* @param contract parsed contract, originated by the frontend
+* @param db_conn the handle to the local DB
+* @param kpriv merchant's private key
+* @param wire merchant's bank's details
+* @param sig where to store the signature
+* @return pointer to the complete JSON; NULL upon errors
+*/
+
+/* TODO: this handles a simplified version (for debugging purposes)
+ of the contract. To expand to the full fledged version */
+
+json_t *
+MERCHANT_handle_contract (json_t *contract,
+ PGconn *db_conn,
+ struct GNUNET_CRYPTO_EddsaPrivateKey *kpriv,
+ const struct MERCHANT_WIREFORMAT_Sepa *wire,
+ struct GNUNET_CRYPTO_EddsaSignature *sig);
diff --git a/src/backend-lib/taler_merchant_lib.h b/src/backend-lib/taler_merchant_lib.h
new file mode 100644
index 00000000..41891783
--- /dev/null
+++ b/src/backend-lib/taler_merchant_lib.h
@@ -0,0 +1 @@
+#include "taler_merchant_contract_lib.h"
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 836656b5..c0eacfae 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -9,7 +9,8 @@ taler_merchant_httpd_SOURCES = \
merchant.c merchant.h \
merchant_db.c merchant_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_responses.c taler-mint-httpd_responses.h \
+ ../backend-lib/taler-merchant-httpd_contract.h
taler_merchant_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \
@@ -17,6 +18,7 @@ taler_merchant_httpd_LDADD = \
-lmicrohttpd \
-ljansson \
-lgnunetutil \
+ -ltalermerchant \
-ltalermint \
-ltalerpq \
-lgnunetpostgres \
diff --git a/src/backend/QUESTIONS b/src/backend/QUESTIONS
new file mode 100644
index 00000000..b992e96f
--- /dev/null
+++ b/src/backend/QUESTIONS
@@ -0,0 +1,6 @@
+1. why does the merchant daemon appears to be some executable under some
+.libs directory even though it gets launched through an executable located
+elsewhere?
+
+2. why does the httpd prints three times the URL corresponding to GET /some/url
+?
diff --git a/src/backend/merchant.priv b/src/backend/merchant.priv
new file mode 100644
index 00000000..9c18c358
--- /dev/null
+++ b/src/backend/merchant.priv
@@ -0,0 +1 @@
+`&-./ jxGݢO:6l,ζXT4 \ No newline at end of file
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 900b2a1a..69353ea8 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -27,11 +27,10 @@
#include <gnunet/gnunet_util_lib.h>
#include <taler/taler_json_lib.h>
#include <taler/taler_mint_service.h>
-#include <taler/taler_signatures.h>
#include "taler-mint-httpd_parsing.h"
#include "taler-mint-httpd_responses.h"
+#include "taler_merchant_lib.h"
#include "merchant.h"
-#include "merchant_db.h"
extern struct MERCHANT_WIREFORMAT_Sepa *
TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
@@ -45,11 +44,6 @@ TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *
} while (0)
/**
- * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
- */
-#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000)
-
-/**
* Our hostname
*/
static char *hostname;
@@ -104,44 +98,6 @@ static int dry;
*/
static int result;
-GNUNET_NETWORK_STRUCT_BEGIN
-
-struct Contract
-{
- /**
- * Purpose header for the signature over contract
- */
- struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
-
- /**
- * The transaction identifier. NOTE: it was m[13]. TODO:
- * change the API accordingly!
- */
- uint64_t m;
-
- /**
- * Expiry time
- */
- struct GNUNET_TIME_AbsoluteNBO t;
-
- /**
- * The invoice amount
- */
- struct TALER_AmountNBO amount;
-
- /**
- * The hash of the preferred wire format + nounce
- */
- struct GNUNET_HashCode h_wire;
-
- /**
- * The contract data
- */
- char *a;
-};
-
-GNUNET_NETWORK_STRUCT_END
-
/**
* Mint context
*/
@@ -307,98 +263,6 @@ 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
-* @param price the cost of this good or service
-* @param sig the pointer to the contract signature's location
-* @return pointer to the allocated memory which will hold a 'struct Contract'
-* followed by a deal's description string, NULL upon errors
-*
-*/
-
-void *
-generate_and_store_contract (const char *a,
- uint64_t c_id,
- uint64_t product,
- const struct TALER_Amount *price,
- struct GNUNET_CRYPTO_EddsaSignature *sig)
-{
-
- void *contract_and_desc;
- 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_and_desc = GNUNET_malloc (sizeof (struct Contract) + strlen (a) + 1);
- contract = (struct Contract *) contract_and_desc;
- contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
- contract->purpose.size = htonl (sizeof (struct Contract) + strlen (a) + 1);
- contract->a = (char *) &contract->a + 4;
- contract->m = contract_id_nbo;
- contract->t = GNUNET_TIME_absolute_hton (expiry);
- strcpy (contract->a, a);
- printf ("msg in stored contract : %s\n", contract->a);
- contract->h_wire = hash_wireformat (nounce);
- TALER_amount_hton (&contract->amount, price);
- GNUNET_CRYPTO_eddsa_sign (privkey, &contract->purpose, sig);
- return contract_and_desc;
-
- /* legacy from old merchant */
- EXITIF_exit:
- if (NULL != contract)
- {
- GNUNET_free (contract_and_desc);
- }
- 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
@@ -542,6 +406,7 @@ url_handler (void *cls,
else
{
/* Let's generate this contract! */
+ /* FIXME : change this. Not existent anymore */
if (NULL == (contract_and_desc = generate_and_store_contract (desc,
contract_id,
prod_id,
@@ -622,6 +487,8 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
MERCHANT_DB_disconnect (db_conn);
db_conn = NULL;
}
+ if (keyfile != NULL)
+ GNUNET_free (privkey);
}
@@ -640,8 +507,7 @@ 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 */
+ keys? It has already the mint's master key from the conf file */
return;
}
@@ -675,7 +541,7 @@ run (void *cls, char *const *args, const char *cfgfile,
&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",
diff --git a/src/frontend/checkout.php b/src/frontend/checkout.php
index d2ebd814..d9542c3b 100644
--- a/src/frontend/checkout.php
+++ b/src/frontend/checkout.php
@@ -92,12 +92,12 @@ function taler_pay(form)
if (contract_request.status == 200)
{
/* display contract_requestificate (i.e. it sends the JSON string
- to the (XUL) extension) */
+ to the extension) */
handle_contract(contract_request.responseText);
}
else
{
- alert("Failed to receive contract from server. Status was " + contract_request.status);
+ alert("No contract got from merchant.\n" + contract_request.responseText);
}
}
};
@@ -118,7 +118,6 @@ function pay(form)
for (var cnt=0; cnt < form.payment_system.length; cnt++)
{
var choice = form.payment_system[cnt];
-
if (choice.checked)
{
if (choice.value == "taler")
@@ -143,8 +142,7 @@ function has_taler_wallet_cb(aEvent)
// enable the Taler payment option from the form
var tbutton = document.getElementById("taler-radio-button-id");
tbutton.removeAttribute("disabled");
-
-
+
if (aEvent.type == "taler-wallet-wfirst"){
var eve = new Event('taler-payment-wfirst');
document.body.dispatchEvent(eve);
@@ -161,12 +159,21 @@ function taler_wallet_unload_cb(aEvent)
tbutton.setAttribute("disabled", "true");
};
+/* The merchant signals its taler-friendlyness to the client */
function signal_me()
{
var eve = new Event('taler-payment-mfirst');
document.body.dispatchEvent(eve);
};
+
+function test_without_wallet(){
+ var tbutton = document.getElementById("taler-radio-button-id");
+ tbutton.removeAttribute("disabled");
+};
+
+// test_without_wallet();
+
// Register event to be triggered by the wallet as a response to our
// first event
document.body.addEventListener("taler-wallet-mfirst", has_taler_wallet_cb, false);
diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php
index ea118303..15c0841f 100644
--- a/src/frontend/generate_taler_contract.php
+++ b/src/frontend/generate_taler_contract.php
@@ -25,8 +25,6 @@
to the wallet
*/
-// --- FIXME: by not commenting out the following test and trying to get
-// 'receiver' and 'amount' from the session, it gets 404 always.
// 1) recover the session information
session_start();
if ( (! isset($_SESSION['receiver'])) ||
@@ -34,6 +32,7 @@ if ( (! isset($_SESSION['receiver'])) ||
{
http_response_code (404);
echo "Please select a contract before getting to this page...";
+ echo "attempted : " . $_SESSION['receiver'];
exit (0);
}
diff --git a/src/frontend/index.html b/src/frontend/index.html
index 27ab281f..02545475 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -159,6 +159,11 @@
b.style.display = 'none';
};
+ function test_without_wallet(){
+ wallet_installed_cb();
+ }
+
+ // test_without_wallet();
/* Set up a listener to be called whenever a Wallet gets installed
so that the user is led towards the demo's steps progressively */
diff --git a/src/backend/merchant.h b/src/include/merchant.h
index c66131ed..c66131ed 100644
--- a/src/backend/merchant.h
+++ b/src/include/merchant.h
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
new file mode 100644
index 00000000..7c531405
--- /dev/null
+++ b/src/tests/Makefile.am
@@ -0,0 +1,26 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+bin_PROGRAMS = \
+ merchant-contract-test
+
+#merchant_contract_test_CFLAGS = \
+# -Wl,--verbose
+
+merchant_contract_test_SOURCES = \
+ merchant-contract-test.c \
+ merchant.c \
+ merchant_db.c merchant_db.h
+
+merchant_contract_test_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ -ltalerutil \
+ -ltalermerchant \
+ -ljansson \
+ -lgnunetutil \
+ -ltalermint \
+ -ltalerpq \
+ -lgnunetpostgres \
+ -lpq \
+ -ltalermerchant \
+ -lpthread
diff --git a/src/tests/merchant-contract-test b/src/tests/merchant-contract-test
new file mode 100755
index 00000000..d6e3063c
--- /dev/null
+++ b/src/tests/merchant-contract-test
Binary files differ
diff --git a/src/tests/merchant-contract-test.c b/src/tests/merchant-contract-test.c
new file mode 100644
index 00000000..f11b67f9
--- /dev/null
+++ b/src/tests/merchant-contract-test.c
@@ -0,0 +1,207 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 merchant/tests/merchant-non-http-test.c
+* @brief test for various merchant's capabilities
+* @author Marcello Stanisci
+*/
+
+#include "platform.h"
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_json_lib.h>
+#include "merchant_db.h"
+#include <taler_merchant_lib.h>
+
+PGconn *db_conn;
+
+static int dry;
+struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
+char *keyfile;
+static int result;
+static struct MERCHANT_WIREFORMAT_Sepa *wire;
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+extern
+struct MERCHANT_WIREFORMAT_Sepa *
+TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+/**
+ * Shutdown task (magically invoked when the application is being
+ * quit)
+ *
+ * @param cls NULL
+ * @param tc scheduler task context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ if (NULL != db_conn)
+ {
+ MERCHANT_DB_disconnect (db_conn);
+ db_conn = NULL;
+ }
+}
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+
+{
+ json_t *j_fake_contract;
+ json_t *j_root;
+ json_t *j_details;
+ json_t *j_item;
+ json_t *j_amount;
+ json_t *j_tax_amount;
+ json_t *j_item_price;
+ json_t *j_teatax;
+ json_t *j_id; // trans id
+ json_t *j_pid; // product id
+ json_t *j_quantity;
+ char *str;
+ char *desc = "Fake purchase";
+ struct TALER_Amount amount;
+ int64_t t_id;
+ int64_t p_id;
+ struct GNUNET_CRYPTO_EddsaSignature c_sig;
+
+ db_conn = NULL;
+ keyfile = NULL;
+ privkey = NULL;
+ wire = NULL;
+
+
+ db_conn = MERCHANT_DB_connect (config);
+ if (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_NO))
+ {
+ printf ("no db init'd\n");
+ result = GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile))
+ {
+ printf ("no keyfile entry in cfg file\n");
+ result = GNUNET_SYSERR;
+ }
+
+ privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
+ wire = TALER_MERCHANT_parse_wireformat_sepa (config);
+ shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown, NULL);
+
+ /* Amount */
+ TALER_amount_get_zero ("KUDOS", &amount);
+ j_amount = TALER_json_from_amount (&amount);
+ j_item_price = TALER_json_from_amount (&amount);
+ j_tax_amount = TALER_json_from_amount (&amount);
+
+
+ /* Product ID*/
+ p_id = (int32_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
+
+ if (p_id < 0)
+ j_pid = json_integer ((-1) * p_id);
+ else
+ j_pid = json_integer (p_id);
+
+ /* Quantity */
+ j_quantity = json_integer (3);
+
+ /* Transaction ID*/
+ t_id = (int32_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
+
+ if (t_id < 0)
+ j_id = json_integer ((-1) * t_id);
+ else
+ j_id = json_integer (t_id);
+
+ /* Preparing the 'details' sub-object: an array of 'item' objects */
+
+ j_teatax = json_pack ("{s:o}",
+ "teatax", j_tax_amount);
+ if (NULL == (j_item = json_pack ("{s:s, s:I, s:o, s:I}",
+ "description", desc,
+ "quantity", json_integer_value (j_quantity),
+ "itemprice", j_item_price,
+ "product_id", json_integer_value (j_pid))))
+ {
+ printf ("error in packing [j_item: %p]\n", j_item);
+ return;
+ }
+
+ printf ("[j_item address: %p]\n", j_item);
+
+ str = json_dumps (j_item, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
+ printf ("a %s\n", str);
+ return;
+
+
+ j_fake_contract = json_pack ("{s:o, s:i}",
+ "amount", j_amount,
+ "trans_id", json_integer_value (j_id));
+
+ j_root = MERCHANT_handle_contract (j_fake_contract,
+ db_conn,
+ privkey,
+ wire,
+ &c_sig);
+
+}
+
+
+/**
+ * The main function of the test tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'t', "temp", NULL,
+ gettext_noop ("Use temporary database tables"), GNUNET_NO,
+ &GNUNET_GETOPT_set_one, &dry},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv,
+ "merchant-contract-test",
+ "Test for contracts mgmt",
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+
+
+
+}
diff --git a/src/tests/merchant.c b/src/tests/merchant.c
new file mode 100644
index 00000000..f124a030
--- /dev/null
+++ b/src/tests/merchant.c
@@ -0,0 +1,173 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 merchant/merchant.c
+ * @brief Common utility functions for merchant
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "merchant.h"
+
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * Parses mints from the configuration.
+ *
+ * @param cfg the configuration
+ * @param mints the array of mints upon successful parsing. Will be NULL upon
+ * error.
+ * @return the number of mints in the above array; GNUNET_SYSERR upon error in
+ * parsing.
+ */
+int
+TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct MERCHANT_MintInfo **mints)
+{
+ char *mints_str;
+ char *token_nf; /* do no free (nf) */
+ char *mint_section;
+ char *mint_hostname;
+ char *mint_pubkey_enc;
+ struct MERCHANT_MintInfo *r_mints;
+ struct MERCHANT_MintInfo mint;
+ unsigned long long mint_port;
+ unsigned int cnt;
+ int OK;
+
+ OK = 0;
+ mints_str = NULL;
+ token_nf = NULL;
+ mint_section = NULL;
+ mint_hostname = NULL;
+ mint_pubkey_enc = NULL;
+ r_mints = NULL;
+ cnt = 0;
+ EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
+ "merchant",
+ "TRUSTED_MINTS",
+ &mints_str));
+ for (token_nf = strtok (mints_str, " ");
+ NULL != token_nf;
+ token_nf = strtok (NULL, " "))
+ {
+ GNUNET_assert (0 < GNUNET_asprintf (&mint_section,
+ "mint-%s", token_nf));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ mint_section,
+ "HOSTNAME",
+ &mint_hostname));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ mint_section,
+ "PORT",
+ &mint_port));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ mint_section,
+ "PUBKEY",
+ &mint_pubkey_enc));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc,
+ strlen (mint_pubkey_enc),
+ &mint.pubkey));
+ mint.hostname = mint_hostname;
+ mint.port = (uint16_t) mint_port;
+ GNUNET_array_append (r_mints, cnt, mint);
+ mint_hostname = NULL;
+ GNUNET_free (mint_pubkey_enc);
+ mint_pubkey_enc = NULL;
+ GNUNET_free (mint_section);
+ mint_section = NULL;
+ }
+ OK = 1;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (mints_str);
+ GNUNET_free_non_null (mint_section);
+ GNUNET_free_non_null (mint_hostname);
+ GNUNET_free_non_null (mint_pubkey_enc);
+ if (!OK)
+ {
+ GNUNET_free_non_null (r_mints);
+ return GNUNET_SYSERR;
+ }
+
+ *mints = r_mints;
+ return cnt;
+}
+
+
+/**
+ * Parse the SEPA information from the configuration. If any of the required
+ * fileds is missing return NULL.
+ *
+ * @param cfg the configuration
+ * @return Sepa details as a structure; NULL upon error
+ */
+struct MERCHANT_WIREFORMAT_Sepa *
+TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct MERCHANT_WIREFORMAT_Sepa *wf;
+
+ wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa);
+ EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "IBAN",
+ &wf->iban));
+ EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "NAME",
+ &wf->name));
+ EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "BIC",
+ &wf->bic));
+ return wf;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (wf->iban);
+ GNUNET_free_non_null (wf->name);
+ GNUNET_free_non_null (wf->bic);
+ GNUNET_free (wf);
+ return NULL;
+
+}
+
+
+/**
+ * Destroy and free resouces occupied by the wireformat structure
+ *
+ * @param wf the wireformat structure
+ */
+void
+TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf)
+{
+ GNUNET_free_non_null (wf->iban);
+ GNUNET_free_non_null (wf->name);
+ GNUNET_free_non_null (wf->bic);
+ GNUNET_free (wf);
+}
+
+/* end of merchant.c */
diff --git a/src/tests/merchant.conf b/src/tests/merchant.conf
new file mode 100644
index 00000000..3b637448
--- /dev/null
+++ b/src/tests/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/tests/merchant.priv b/src/tests/merchant.priv
new file mode 100644
index 00000000..2101f677
--- /dev/null
+++ b/src/tests/merchant.priv
@@ -0,0 +1 @@
+{|ic?"򊄏`J0pkvTW \ No newline at end of file
diff --git a/src/tests/merchant_db.c b/src/tests/merchant_db.c
new file mode 100644
index 00000000..274de25a
--- /dev/null
+++ b/src/tests/merchant_db.c
@@ -0,0 +1,354 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 merchant/merchant_db.c
+ * @brief database helper functions used by the merchant
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_pq_lib.h>
+#include "merchant_db.h"
+
+
+#define PQSQL_strerror(kind, cmd, res) \
+ GNUNET_log_from (kind, "merchant-db", \
+ "SQL %s failed at %s:%u with error: %s", \
+ cmd, __FILE__, __LINE__, PQresultErrorMessage (res));
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * Connect to postgresql database
+ *
+ * @param cfg the configuration handle
+ * @return connection to the postgresql database; NULL upon error
+ */
+PGconn *
+MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ return GNUNET_POSTGRES_connect (cfg, "merchant-db");
+}
+
+
+/**
+ * Disconnect from the database
+ *
+ * @param conn database handle to close
+ */
+void
+MERCHANT_DB_disconnect (PGconn *conn)
+{
+ PQfinish (conn);
+}
+
+
+/**
+ * Initialize merchant tables
+ *
+ * @param conn the connection handle to postgres db.
+ * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
+ * contents are dropped when the @a conn is closed
+ * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
+ */
+int
+MERCHANT_DB_initialize (PGconn *conn, int tmp)
+{
+ const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
+ char *sql;
+ PGresult *res;
+ ExecStatusType status;
+ int ret;
+
+ res = NULL;
+ (void) GNUNET_asprintf (&sql,
+ "BEGIN TRANSACTION;"
+ "CREATE %1$s TABLE IF NOT EXISTS contracts ("
+ "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 INT8 NOT NULL,"
+ "expiry INT8 NOT NULL,"
+ "product INT8 NOT NULL);"
+ "CREATE %1$s TABLE IF NOT EXISTS checkouts ("
+ "coin_pub BYTEA PRIMARY KEY,"
+ "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_free (sql);
+ if (GNUNET_OK != ret)
+ return ret;
+
+ while (NULL != (res = PQgetResult (conn)))
+ {
+ status = PQresultStatus (res);
+ PQclear (res);
+ }
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "contract_create",
+ "INSERT INTO contracts"
+ "(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);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "get_contract_product",
+ "SELECT ("
+ "product"
+ ") FROM contracts "
+ "WHERE ("
+ "contract_id=$1"
+ ")",
+ 1, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
+ PQclear (res);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "checkout_create",
+ "INSERT INTO checkouts ("
+ "coin_pub,"
+ "contract_id,"
+ "amount,"
+ "amount_fraction,"
+ "coin_sig"
+ ") VALUES ("
+ "$1, $2, $3, $4, $5"
+ ")",
+ 5, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
+ PQclear (res);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "get_checkout_product",
+ "SELECT ("
+ "product"
+ ") FROM contracts "
+ "WHERE "
+ "contract_id IN ("
+ "SELECT (contract_id) FROM checkouts "
+ "WHERE coin_pub=$1"
+ ")",
+ 1, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
+ PQclear (res);
+
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ if (NULL != res)
+ {
+ PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res);
+ PQclear (res);
+ }
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Inserts a contract record into the database and if successfull returns the
+ * serial number of the inserted row.
+ *
+ * @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 GNUNET_OK on success, GNUNET_SYSERR upon error
+ */
+
+uint32_t
+MERCHANT_DB_contract_create (PGconn *conn,
+ 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;
+ uint64_t value_nbo;
+ uint32_t fraction_nbo;
+ uint64_t nounce_nbo;
+ #endif
+ ExecStatusType status;
+
+ #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_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),
+ TALER_PQ_query_param_absolute_time (expiry),
+ TALER_PQ_query_param_uint64 (&product),
+ TALER_PQ_query_param_end
+ };
+
+ /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */
+ res = TALER_PQ_exec_prepared (conn, "contract_create", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_COMMAND_OK != status);
+ PQclear (res);
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ PQclear (res);
+ return GNUNET_SYSERR;
+}
+
+long long
+MERCHANT_DB_get_contract_product (PGconn *conn,
+ uint64_t contract_id)
+{
+ PGresult *res;
+ uint64_t product;
+ ExecStatusType status;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&contract_id),
+ TALER_PQ_query_param_end
+ };
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("product", &product),
+ TALER_PQ_result_spec_end
+ };
+
+ contract_id = GNUNET_htonll (contract_id);
+ res = TALER_PQ_exec_prepared (conn, "get_contract_product", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_TUPLES_OK != status);
+ EXITIF (1 != PQntuples (res));
+ EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
+ PQclear (res);
+ return GNUNET_ntohll ((uint64_t) product);
+
+ EXITIF_exit:
+ PQclear (res);
+ return -1;
+}
+
+unsigned int
+MERCHANT_DB_checkout_create (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
+ uint64_t transaction_id,
+ struct TALER_Amount *amount,
+ struct GNUNET_CRYPTO_rsa_Signature *coin_sig)
+{
+ PGresult *res;
+ ExecStatusType status;
+ uint32_t value_nbo;
+ uint32_t fraction_nbo;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_rsa_public_key (coin_pub),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_uint32 (&value_nbo),
+ TALER_PQ_query_param_uint32 (&fraction_nbo),
+ TALER_PQ_query_param_rsa_signature (coin_sig),
+ TALER_PQ_query_param_end
+ };
+
+ transaction_id = GNUNET_htonll (transaction_id);
+ value_nbo = htonl (amount->value);
+ fraction_nbo = htonl (amount->fraction);
+ res = TALER_PQ_exec_prepared (conn, "checkout_create", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_COMMAND_OK != status);
+ PQclear (res);
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ PQclear (res);
+ return GNUNET_SYSERR;
+}
+
+
+long long
+MERCHANT_DB_get_checkout_product (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub)
+{
+ PGresult *res;
+ ExecStatusType status;
+ uint64_t product;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_rsa_public_key (coin_pub),
+ TALER_PQ_query_param_end
+ };
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("product", &product),
+ TALER_PQ_result_spec_end
+ };
+
+ product = -1;
+ res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params);
+ status = PQresultStatus (res);
+ EXITIF (PGRES_TUPLES_OK != status);
+ if (0 == PQntuples (res))
+ {
+ TALER_LOG_DEBUG ("Checkout not found for given coin");
+ goto EXITIF_exit;
+ }
+ EXITIF (1 != PQntuples (res));
+ EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
+ PQclear (res);
+ return GNUNET_ntohll ((uint64_t) product);
+
+ EXITIF_exit:
+ PQclear (res);
+ return -1;
+}
+/* end of merchant-db.c */
diff --git a/src/tests/merchant_db.h b/src/tests/merchant_db.h
new file mode 100644
index 00000000..a723b229
--- /dev/null
+++ b/src/tests/merchant_db.h
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ (C) 2014 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 merchant/merchant_db.h
+ * @brief database helper functions used by the merchant
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#ifndef MERCHANT_DB_H
+#define MERCHANT_DB_H
+
+#include <gnunet/gnunet_postgres_lib.h>
+#include <taler/taler_util.h>
+
+/**
+ * Connect to postgresql database
+ *
+ * @param cfg the configuration handle
+ * @return connection to the postgresql database; NULL upon error
+ */
+PGconn *
+MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Disconnect from the database
+ *
+ * @param conn database handle to close
+ */
+void
+MERCHANT_DB_disconnect (PGconn *conn);
+
+
+/**
+ * Initialize merchant tables
+ *
+ * @param conn the connection handle to postgres db.
+ * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
+ * contents are dropped when the @a conn is closed
+ * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
+ */
+int
+MERCHANT_DB_initialize (PGconn *conn, int tmp);
+
+
+/**
+ * Inserts a contract record into the database and if successfull returns the
+ * serial number of the inserted row.
+ *
+ * @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 GNUNET_OK on success, GNUNET_SYSERR upon error
+ */
+
+uint32_t
+MERCHANT_DB_contract_create (PGconn *conn,
+ const struct GNUNET_TIME_Absolute *expiry,
+ const struct TALER_Amount *amount,
+ uint64_t c_id,
+ const char *desc,
+ uint64_t nounce,
+ uint64_t product);
+
+long long
+MERCHANT_DB_get_contract_product (PGconn *conn,
+ uint64_t contract_id);
+
+unsigned int
+MERCHANT_DB_checkout_create (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
+ uint64_t transaction_id,
+ struct TALER_Amount *amount,
+ struct GNUNET_CRYPTO_rsa_Signature *coin_sig);
+
+
+long long
+MERCHANT_DB_get_checkout_product (PGconn *conn,
+ struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub);
+
+#endif /* MERCHANT_DB_H */
+
+/* end of merchant-db.h */
diff --git a/src/tests/test.debug b/src/tests/test.debug
new file mode 100644
index 00000000..1817f7f0
--- /dev/null
+++ b/src/tests/test.debug
@@ -0,0 +1,37 @@
+==5558== Memcheck, a memory error detector
+==5558== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
+==5558== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
+==5558== Command: ./merchant-contract-test -c merchant.conf
+==5558== Parent PID: 483
+==5558==
+==5558==
+==5558== Process terminating with default action of signal 11 (SIGSEGV)
+==5558== Bad permissions for mapped region at address 0x804AB80
+==5558== at 0x4108DAA: ??? (in /usr/lib/i386-linux-gnu/libjansson.so.4.7.0)
+==5558== by 0x4108E7A: ??? (in /usr/lib/i386-linux-gnu/libjansson.so.4.7.0)
+==5558== by 0x410DAFB: json_delete (in /usr/lib/i386-linux-gnu/libjansson.so.4.7.0)
+==5558== by 0x410C28F: ??? (in /usr/lib/i386-linux-gnu/libjansson.so.4.7.0)
+==5558== by 0x410C42B: json_vpack_ex (in /usr/lib/i386-linux-gnu/libjansson.so.4.7.0)
+==5558== by 0x410C530: json_pack (in /usr/lib/i386-linux-gnu/libjansson.so.4.7.0)
+==5558== by 0x80494CE: run (merchant-contract-test.c:148)
+==5558== by 0x41543EE: program_main (program.c:84)
+==5558== by 0x41592ED: run_ready (scheduler.c:587)
+==5558== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==5558== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==5558== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==5558== by 0x8049210: main (merchant-contract-test.c:195)
+==5558==
+==5558== HEAP SUMMARY:
+==5558== in use at exit: 133,271 bytes in 2,334 blocks
+==5558== total heap usage: 5,082 allocs, 2,748 frees, 277,172 bytes allocated
+==5558==
+==5558== LEAK SUMMARY:
+==5558== definitely lost: 149 bytes in 4 blocks
+==5558== indirectly lost: 293 bytes in 10 blocks
+==5558== possibly lost: 359 bytes in 18 blocks
+==5558== still reachable: 132,470 bytes in 2,302 blocks
+==5558== suppressed: 0 bytes in 0 blocks
+==5558== Rerun with --leak-check=full to see details of leaked memory
+==5558==
+==5558== For counts of detected and suppressed errors, rerun with: -v
+==5558== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/src/tests/test.valgrind b/src/tests/test.valgrind
new file mode 100644
index 00000000..751899b1
--- /dev/null
+++ b/src/tests/test.valgrind
@@ -0,0 +1,181 @@
+==1908== Memcheck, a memory error detector
+==1908== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
+==1908== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
+==1908== Command: ./merchant-contract-test -c merchant.conf
+==1908== Parent PID: 483
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x40B084A: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x402DDB8: is_overlap (vg_replace_strmem.c:128)
+==1908== by 0x402DDB8: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x402DDBE: is_overlap (vg_replace_strmem.c:131)
+==1908== by 0x402DDBE: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x402DDCD: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x402DE1C: is_overlap (vg_replace_strmem.c:119)
+==1908== by 0x402DE1C: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x402DF8F: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Conditional jump or move depends on uninitialised value(s)
+==1908== at 0x402E000: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Use of uninitialised value of size 4
+==1908== at 0x402E023: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908==
+==1908== Invalid write of size 2
+==1908== at 0x402E023: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908== Address 0xa is not stack'd, malloc'd or (recently) free'd
+==1908==
+==1908==
+==1908== Process terminating with default action of signal 11 (SIGSEGV)
+==1908== Access not within mapped region at address 0xA
+==1908== at 0x402E023: memcpy (vg_replace_strmem.c:916)
+==1908== by 0x40B0881: ??? (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x404E8E7: gcry_mpi_print (in /lib/i386-linux-gnu/libgcrypt.so.20.0.3)
+==1908== by 0x414117D: GNUNET_CRYPTO_mpi_print_unsigned (crypto_mpi.c:103)
+==1908== by 0x413B27E: GNUNET_CRYPTO_eddsa_sign (crypto_ecc.c:824)
+==1908== by 0x41E57F7: MERCHANT_handle_contract (taler-merchant-httpd_contract.c:142)
+==1908== by 0x80494D6: run (merchant-contract-test.c:126)
+==1908== by 0x41543EE: program_main (program.c:84)
+==1908== by 0x41592ED: run_ready (scheduler.c:587)
+==1908== by 0x41592ED: GNUNET_SCHEDULER_run (scheduler.c:868)
+==1908== by 0x4154B09: GNUNET_PROGRAM_run2 (program.c:302)
+==1908== by 0x4154E01: GNUNET_PROGRAM_run (program.c:341)
+==1908== by 0x8049250: main (merchant-contract-test.c:158)
+==1908== If you believe this happened as a result of a stack
+==1908== overflow in your program's main thread (unlikely but
+==1908== possible), you can try to increase the size of the
+==1908== main thread stack using the --main-stacksize= flag.
+==1908== The main thread stack size used in this run was 8388608.
+==1908==
+==1908== HEAP SUMMARY:
+==1908== in use at exit: 133,296 bytes in 2,329 blocks
+==1908== total heap usage: 38,415 allocs, 36,086 frees, 1,274,533 bytes allocated
+==1908==
+==1908== LEAK SUMMARY:
+==1908== definitely lost: 314 bytes in 3 blocks
+==1908== indirectly lost: 0 bytes in 0 blocks
+==1908== possibly lost: 357 bytes in 17 blocks
+==1908== still reachable: 132,625 bytes in 2,309 blocks
+==1908== suppressed: 0 bytes in 0 blocks
+==1908== Rerun with --leak-check=full to see details of leaked memory
+==1908==
+==1908== For counts of detected and suppressed errors, rerun with: -v
+==1908== Use --track-origins=yes to see where uninitialised values come from
+==1908== ERROR SUMMARY: 10 errors from 9 contexts (suppressed: 0 from 0)