commit 7d9a40327587ed99fb20f4c4a5669069c7a51e48
parent 0414fb51a9f219c37afc06ad2b309711e360c485
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 20 Mar 2015 23:51:28 +0100
first stab at establishing proper plugin API, main HTTP code compiles, other binaries FTBFS right now
Diffstat:
19 files changed, 4519 insertions(+), 3379 deletions(-)
diff --git a/INSTALL b/INSTALL
@@ -1,7 +1,7 @@
Installation Instructions
*************************
-Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation,
+Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
Inc.
Copying and distribution of this file, with or without modification,
@@ -12,8 +12,8 @@ without warranty of any kind.
Basic Installation
==================
- Briefly, the shell commands `./configure; make; make install' should
-configure, build, and install this package. The following
+ Briefly, the shell command `./configure && make && make install'
+should configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
@@ -309,9 +309,10 @@ causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf bug. Until the bug is fixed you can use this workaround:
+an Autoconf limitation. Until the limitation is lifted, you can use
+this workaround:
- CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
@@ -367,4 +368,3 @@ operates.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
-
diff --git a/configure.ac b/configure.ac
@@ -65,6 +65,13 @@ AS_IF([test $libgnunetutil != 1],
*** ]])])
+TALER_LIB_LDFLAGS="-export-dynamic -no-undefined"
+TALER_PLUGIN_LDFLAGS="-export-dynamic -avoid-version -module -no-undefined"
+
+AC_SUBST(TALER_LIB_LDFLAGS)
+AC_SUBST(TALER_PLUGIN_LDFLAGS)
+
+
# check for libmicrohttpd
microhttpd=0
AC_MSG_CHECKING([for microhttpd])
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
@@ -99,4 +99,16 @@ TALER_config_get_denom (struct GNUNET_CONFIGURATION_Handle *cfg,
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);
+
+
#endif
diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am
@@ -1,18 +1,31 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/pq/ $(POSTGRESQL_CPPFLAGS)
+plugindir = $(libdir)/taler
+
+plugin_LTLIBRARIES = \
+ libtaler_plugin_mintdb_postgres.la
+
+libtaler_plugin_mintdb_postgres_la_SOURCES = \
+ plugin_mintdb_postgres.c
+libtaler_plugin_mintdb_postgres_la_LIBADD = \
+ $(LTLIBINTL)
+libtaler_plugin_mintdb_postgres_la_LDFLAGS = \
+ $(TALER_PLUGIN_LDFLAGS) \
+ -lpq \
+ -lgnunetutil
+
lib_LTLIBRARIES = \
libtalermint_common.la
-# Note: mint_db.c should become a plugin soon! (#3608)
libtalermint_common_la_SOURCES = \
key_io.c key_io.h \
- mint_db.c
+ plugin.c plugin.h \
+ taler_mintdb_plugin.h
libtalermint_common_la_LIBADD = \
$(top_builddir)/src/util/libtalerutil.la \
- -lgnunetutil \
- -lpq
+ -lgnunetutil
libtalermint_common_la_LDFLAGS = \
$(POSTGRESQL_LDFLAGS) \
diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c
@@ -1,2241 +0,0 @@
-/*
- 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_db.c
- * @brief Low-level (statement-level) database access for the mint
- * @author Florian Dold
- * @author Christian Grothoff
- * @author Sree Harsha Totakura
- *
- * TODO:
- * - The mint_db.h-API should ideally be what we need to port
- * when using other databases; so here we should enable
- * alternative implementations by returning
- * a more opaque DB handle.
- */
-#include "platform.h"
-#include "db_pq.h"
-#include "taler_signatures.h"
-#include "taler-mint-httpd_responses.h"
-#include "mint_db.h"
-#include <pthread.h>
-
-
-/**
- * Thread-local database connection.
- * Contains a pointer to PGconn or NULL.
- */
-static pthread_key_t db_conn_threadlocal;
-
-
-#define QUERY_ERR(result) \
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s\n", __FILE__, __LINE__, PQresultErrorMessage (result))
-
-/**
- * Database connection string, as read from
- * the configuration.
- */
-static char *TALER_MINT_db_connection_cfg_str;
-
-#define BREAK_DB_ERR(result) do { \
- GNUNET_break(0); \
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
- } while (0)
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-#define SQLEXEC_(conn, sql, result) \
- do { \
- result = PQexec (conn, sql); \
- if (PGRES_COMMAND_OK != PQresultStatus (result)) \
- { \
- BREAK_DB_ERR (result); \
- PQclear (result); result = NULL; \
- goto SQLEXEC_fail; \
- } \
- PQclear (result); result = NULL; \
- } while (0)
-
-/**
- * This the length of the currency strings (without 0-termination) we use. Note
- * that we need to use this at the DB layer instead of TALER_CURRENCY_LEN as the
- * DB only needs to store 3 bytes instead of 8 bytes.
- */
-#define TALER_DB_CURRENCY_LEN 3
-
-/**
- * Set the given connection to use a temporary schema
- *
- * @param db the database connection
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon error
- */
-static int
-set_temporary_schema (PGconn *db)
-{
- PGresult *result;
-
- SQLEXEC_(db,
- "CREATE SCHEMA IF NOT EXISTS " TALER_TEMP_SCHEMA_NAME ";"
- "SET search_path to " TALER_TEMP_SCHEMA_NAME ";",
- result);
- return GNUNET_OK;
- SQLEXEC_fail:
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Drop the temporary taler schema. This is only useful for testcases
- *
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-int
-TALER_MINT_DB_drop_temporary (PGconn *db)
-{
- PGresult *result;
-
- SQLEXEC_ (db,
- "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;",
- result);
- return GNUNET_OK;
- SQLEXEC_fail:
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Create the necessary tables if they are not present
- *
- * @param temporary should we use a temporary schema
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-int
-TALER_MINT_DB_create_tables (int temporary)
-{
- PGresult *result;
- PGconn *conn;
-
- result = NULL;
- conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
- if (CONNECTION_OK != PQstatus (conn))
- {
- LOG_ERROR ("Database connection failed: %s\n",
- PQerrorMessage (conn));
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if ((GNUNET_YES == temporary)
- && (GNUNET_SYSERR == set_temporary_schema (conn)))
- {
- PQfinish (conn);
- return GNUNET_SYSERR;
- }
-#define SQLEXEC(sql) SQLEXEC_(conn, sql, result);
- /* reserves table is for summarization of a reserve. It is updated when new
- funds are added and existing funds are withdrawn */
- SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves"
- "("
- " reserve_pub BYTEA PRIMARY KEY"
- ",current_balance_value INT4 NOT NULL"
- ",current_balance_fraction INT4 NOT NULL"
- ",balance_currency VARCHAR(4) NOT NULL"
- ",expiration_date INT8 NOT NULL"
- ")");
- /* reserves_in table collects the transactions which transfer funds into the
- reserve. The amount and expiration date for the corresponding reserve are
- updated when new transfer funds are added. The rows of this table
- correspond to each incoming transaction. */
- SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in"
- "("
- " reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
- ",balance_value INT4 NOT NULL"
- ",balance_fraction INT4 NOT NULL"
- ",balance_currency VARCHAR(4) NOT NULL"
- ",expiration_date INT8 NOT NULL"
- ");");
- /* Create an index on the foreign key as it is not created automatically by PSQL */
- SQLEXEC ("CREATE INDEX reserves_in_reserve_pub_index"
- " ON reserves_in (reserve_pub);");
- SQLEXEC ("CREATE TABLE IF NOT EXISTS collectable_blindcoins"
- "("
- "blind_ev BYTEA PRIMARY KEY"
- ",denom_pub BYTEA NOT NULL" /* FIXME: Make this a foreign key? */
- ",denom_sig BYTEA NOT NULL"
- ",reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
- ",reserve_sig BYTEA NOT NULL"
- ");");
- SQLEXEC ("CREATE INDEX collectable_blindcoins_reserve_pub_index ON"
- " collectable_blindcoins (reserve_pub)");
- SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins "
- "("
- " coin_pub BYTEA NOT NULL PRIMARY KEY"
- ",denom_pub BYTEA NOT NULL"
- ",denom_sig BYTEA NOT NULL"
- ",expended_value INT4 NOT NULL"
- ",expended_fraction INT4 NOT NULL"
- ",expended_currency VARCHAR(4) NOT NULL"
- ",refresh_session_pub BYTEA"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
- "("
- " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)"
- ",session_melt_sig BYTEA"
- ",session_commit_sig BYTEA"
- ",noreveal_index INT2 NOT NULL"
- // non-zero if all reveals were ok
- // and the new coin signatures are ready
- ",reveal_ok BOOLEAN NOT NULL DEFAULT false"
- ") ");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order "
- "( "
- " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
- ",newcoin_index INT2 NOT NULL "
- ",denom_pub BYTEA NOT NULL "
- ",PRIMARY KEY (session_pub, newcoin_index)"
- ") ");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link"
- "("
- " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
- ",transfer_pub BYTEA NOT NULL"
- ",link_secret_enc BYTEA NOT NULL"
- // index of the old coin in the customer's request
- ",oldcoin_index INT2 NOT NULL"
- // index for cut and choose,
- // ranges from 0 to kappa-1
- ",cnc_index INT2 NOT NULL"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin"
- "("
- " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
- ",link_vector_enc BYTEA NOT NULL"
- // index of the new coin in the customer's request
- ",newcoin_index INT2 NOT NULL"
- // index for cut and choose,
- ",cnc_index INT2 NOT NULL"
- ",coin_ev BYTEA NOT NULL"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt"
- "("
- " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
- ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) "
- ",denom_pub BYTEA NOT NULL "
- ",oldcoin_index INT2 NOT NULL"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_collectable"
- "("
- " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
- ",ev_sig BYTEA NOT NULL"
- ",newcoin_index INT2 NOT NULL"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
- "( "
- " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
- ",denom_pub BYTEA NOT NULL" /* FIXME: Link this as a foreign key? */
- ",denom_sig BYTEA NOT NULL"
- ",transaction_id INT8 NOT NULL"
- ",amount_currency VARCHAR(4) NOT NULL"
- ",amount_value INT4 NOT NULL"
- ",amount_fraction INT4 NOT NULL"
- ",merchant_pub BYTEA NOT NULL CHECK (length(merchant_pub)=32)"
- ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
- ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
- ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
- ",wire TEXT NOT NULL"
- ")");
-#undef SQLEXEC
- PQfinish (conn);
- return GNUNET_OK;
-
- SQLEXEC_fail:
- PQfinish (conn);
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Setup prepared statements.
- *
- * @param db_conn connection handle to initialize
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
- */
-int
-TALER_MINT_DB_prepare (PGconn *db_conn)
-{
- PGresult *result;
-
-#define PREPARE(name, sql, ...) \
- do { \
- result = PQprepare (db_conn, name, sql, __VA_ARGS__); \
- if (PGRES_COMMAND_OK != PQresultStatus (result)) \
- { \
- BREAK_DB_ERR (result); \
- PQclear (result); result = NULL; \
- return GNUNET_SYSERR; \
- } \
- PQclear (result); result = NULL; \
- } while (0);
-
- PREPARE ("get_reserve",
- "SELECT "
- "current_balance_value"
- ",current_balance_fraction"
- ",balance_currency "
- ",expiration_date "
- "FROM reserves "
- "WHERE reserve_pub=$1 "
- "LIMIT 1; ",
- 1, NULL);
- PREPARE ("create_reserve",
- "INSERT INTO reserves ("
- " reserve_pub,"
- " current_balance_value,"
- " current_balance_fraction,"
- " balance_currency,"
- " expiration_date) VALUES ("
- "$1, $2, $3, $4, $5);",
- 5, NULL);
- PREPARE ("update_reserve",
- "UPDATE reserves "
- "SET"
- " current_balance_value=$2 "
- ",current_balance_fraction=$3 "
- ",expiration_date=$4 "
- "WHERE reserve_pub=$1 ",
- 4, NULL);
- PREPARE ("create_reserves_in_transaction",
- "INSERT INTO reserves_in ("
- " reserve_pub,"
- " balance_value,"
- " balance_fraction,"
- " balance_currency,"
- " expiration_date) VALUES ("
- " $1, $2, $3, $4, $5);",
- 5, NULL);
- PREPARE ("get_reserves_in_transactions",
- "SELECT"
- " balance_value"
- ",balance_fraction"
- ",balance_currency"
- ",expiration_date"
- " FROM reserves_in WHERE reserve_pub=$1",
- 1, NULL);
- PREPARE ("insert_collectable_blindcoin",
- "INSERT INTO collectable_blindcoins ( "
- " blind_ev"
- ",denom_pub, denom_sig"
- ",reserve_pub, reserve_sig) "
- "VALUES ($1, $2, $3, $4, $5)",
- 5, NULL);
- PREPARE ("get_collectable_blindcoin",
- "SELECT "
- " denom_pub, denom_sig"
- ",reserve_sig, reserve_pub "
- "FROM collectable_blindcoins "
- "WHERE blind_ev = $1",
- 1, NULL);
- PREPARE ("get_reserves_blindcoins",
- "select"
- " blind_ev"
- ",denom_pub, denom_sig"
- ",reserve_sig"
- " FROM collectable_blindcoins"
- " WHERE reserve_pub=$1;",
- 1, NULL);
-
- /* FIXME: does it make sense to store these computed values in the DB? */
-#if 0
- PREPARE ("get_refresh_session",
- "SELECT "
- " (SELECT count(*) FROM refresh_melt WHERE session_pub = $1)::INT2 as num_oldcoins "
- ",(SELECT count(*) FROM refresh_blind_session_keys "
- " WHERE session_pub = $1 and cnc_index = 0)::INT2 as num_newcoins "
- ",(SELECT count(*) FROM refresh_blind_session_keys "
- " WHERE session_pub = $1 and newcoin_index = 0)::INT2 as kappa "
- ",noreveal_index"
- ",session_commit_sig "
- ",reveal_ok "
- "FROM refresh_sessions "
- "WHERE session_pub = $1",
- 1, NULL);
-#endif
-
- PREPARE ("get_known_coin",
- "SELECT "
- " coin_pub, denom_pub, denom_sig "
- ",expended_value, expended_fraction, expended_currency "
- ",refresh_session_pub "
- "FROM known_coins "
- "WHERE coin_pub = $1",
- 1, NULL);
- PREPARE ("update_known_coin",
- "UPDATE known_coins "
- "SET "
- " denom_pub = $2 "
- ",denom_sig = $3 "
- ",expended_value = $4 "
- ",expended_fraction = $5 "
- ",expended_currency = $6 "
- ",refresh_session_pub = $7 "
- "WHERE "
- " coin_pub = $1 ",
- 7, NULL);
- PREPARE ("insert_known_coin",
- "INSERT INTO known_coins ("
- " coin_pub"
- ",denom_pub"
- ",denom_sig"
- ",expended_value"
- ",expended_fraction"
- ",expended_currency"
- ",refresh_session_pub"
- ")"
- "VALUES ($1,$2,$3,$4,$5,$6,$7)",
- 7, NULL);
- PREPARE ("get_refresh_commit_link",
- "SELECT "
- " transfer_pub "
- ",link_secret_enc "
- "FROM refresh_commit_link "
- "WHERE session_pub = $1 AND cnc_index = $2 AND oldcoin_index = $3",
- 3, NULL);
- PREPARE ("get_refresh_commit_coin",
- "SELECT "
- " link_vector_enc "
- ",coin_ev "
- "FROM refresh_commit_coin "
- "WHERE session_pub = $1 AND cnc_index = $2 AND newcoin_index = $3",
- 3, NULL);
- PREPARE ("insert_refresh_order",
- "INSERT INTO refresh_order ( "
- " newcoin_index "
- ",session_pub "
- ",denom_pub "
- ") "
- "VALUES ($1, $2, $3) ",
- 3, NULL);
- PREPARE ("insert_refresh_melt",
- "INSERT INTO refresh_melt ( "
- " session_pub "
- ",oldcoin_index "
- ",coin_pub "
- ",denom_pub "
- ") "
- "VALUES ($1, $2, $3, $4) ",
- 3, NULL);
- PREPARE ("get_refresh_order",
- "SELECT denom_pub "
- "FROM refresh_order "
- "WHERE session_pub = $1 AND newcoin_index = $2",
- 2, NULL);
- PREPARE ("get_refresh_collectable",
- "SELECT ev_sig "
- "FROM refresh_collectable "
- "WHERE session_pub = $1 AND newcoin_index = $2",
- 2, NULL);
- PREPARE ("get_refresh_melt",
- "SELECT coin_pub "
- "FROM refresh_melt "
- "WHERE session_pub = $1 AND oldcoin_index = $2",
- 2, NULL);
- PREPARE ("insert_refresh_session",
- "INSERT INTO refresh_sessions ( "
- " session_pub "
- ",noreveal_index "
- ") "
- "VALUES ($1, $2) ",
- 2, NULL);
- PREPARE ("insert_refresh_commit_link",
- "INSERT INTO refresh_commit_link ( "
- " session_pub "
- ",transfer_pub "
- ",cnc_index "
- ",oldcoin_index "
- ",link_secret_enc "
- ") "
- "VALUES ($1, $2, $3, $4, $5) ",
- 5, NULL);
- PREPARE ("insert_refresh_commit_coin",
- "INSERT INTO refresh_commit_coin ( "
- " session_pub "
- ",coin_ev "
- ",cnc_index "
- ",newcoin_index "
- ",link_vector_enc "
- ") "
- "VALUES ($1, $2, $3, $4, $5) ",
- 5, NULL);
- PREPARE ("insert_refresh_collectable",
- "INSERT INTO refresh_collectable ( "
- " session_pub "
- ",newcoin_index "
- ",ev_sig "
- ") "
- "VALUES ($1, $2, $3) ",
- 3, NULL);
- PREPARE ("set_reveal_ok",
- "UPDATE refresh_sessions "
- "SET reveal_ok = TRUE "
- "WHERE session_pub = $1 ",
- 1, NULL);
- PREPARE ("get_link",
- "SELECT link_vector_enc, ro.denom_pub, ev_sig "
- "FROM refresh_melt rm "
- " JOIN refresh_order ro USING (session_pub) "
- " JOIN refresh_commit_coin rcc USING (session_pub) "
- " JOIN refresh_sessions rs USING (session_pub) "
- " JOIN refresh_collectable rc USING (session_pub) "
- "WHERE rm.coin_pub = $1 "
- "AND ro.newcoin_index = rcc.newcoin_index "
- "AND ro.newcoin_index = rc.newcoin_index "
- "AND rcc.cnc_index = rs.noreveal_index % ( "
- " SELECT count(*) FROM refresh_commit_coin rcc2 "
- " WHERE rcc2.newcoin_index = 0 AND rcc2.session_pub = rs.session_pub "
- " ) ",
- 1, NULL);
- PREPARE ("get_transfer",
- "SELECT transfer_pub, link_secret_enc "
- "FROM refresh_melt rm "
- " JOIN refresh_commit_link rcl USING (session_pub) "
- " JOIN refresh_sessions rs USING (session_pub) "
- "WHERE rm.coin_pub = $1 "
- "AND rm.oldcoin_index = rcl.oldcoin_index "
- "AND rcl.cnc_index = rs.noreveal_index % ( "
- " SELECT count(*) FROM refresh_commit_coin rcc2 "
- " WHERE newcoin_index = 0 AND rcc2.session_pub = rm.session_pub "
- " ) ",
- 1, NULL);
- PREPARE ("insert_deposit",
- "INSERT INTO deposits ("
- "coin_pub,"
- "denom_pub,"
- "denom_sig,"
- "transaction_id,"
- "amount_value,"
- "amount_fraction,"
- "amount_currency,"
- "merchant_pub,"
- "h_contract,"
- "h_wire,"
- "coin_sig,"
- "wire"
- ") VALUES ("
- "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12"
- ")",
- 12, NULL);
- PREPARE ("get_deposit",
- "SELECT "
- "coin_pub,"
- "denom_pub,"
- "transaction_id,"
- "amount_value,"
- "amount_fraction,"
- "amount_currency,"
- "merchant_pub,"
- "h_contract,"
- "h_wire,"
- "coin_sig"
- " FROM deposits WHERE ("
- "(coin_pub = $1) AND"
- "(transaction_id = $2) AND"
- "(merchant_pub = $3)"
- ")",
- 3, NULL);
- return GNUNET_OK;
-#undef PREPARE
-}
-
-
-/**
- * Close thread-local database connection when a thread is destroyed.
- *
- * @param closure we get from pthreads (the db handle)
- */
-static void
-db_conn_destroy (void *cls)
-{
- PGconn *db_conn = cls;
-
- if (NULL != db_conn)
- PQfinish (db_conn);
-}
-
-
-/**
- * Initialize database subsystem.
- *
- * @param connection_cfg configuration to use to talk to DB
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_init (const char *connection_cfg)
-{
- if (0 != pthread_key_create (&db_conn_threadlocal,
- &db_conn_destroy))
- {
- LOG_ERROR ("Cannnot create pthread key.\n");
- return GNUNET_SYSERR;
- }
- TALER_MINT_db_connection_cfg_str = GNUNET_strdup (connection_cfg);
- return GNUNET_OK;
-}
-
-
-/**
- * Get the thread-local database-handle.
- * Connect to the db if the connection does not exist yet.
- *
- * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the
- * database default one
- * @return the database connection, or NULL on error
- */
-PGconn *
-TALER_MINT_DB_get_connection (int temporary)
-{
- PGconn *db_conn;
-
- if (NULL != (db_conn = pthread_getspecific (db_conn_threadlocal)))
- return db_conn;
- db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);
- if (CONNECTION_OK !=
- PQstatus (db_conn))
- {
- LOG_ERROR ("Database connection failed: %s\n",
- PQerrorMessage (db_conn));
- GNUNET_break (0);
- return NULL;
- }
- if ((GNUNET_YES == temporary)
- && (GNUNET_SYSERR == set_temporary_schema(db_conn)))
- {
- GNUNET_break (0);
- return NULL;
- }
- if (GNUNET_OK !=
- TALER_MINT_DB_prepare (db_conn))
- {
- GNUNET_break (0);
- return NULL;
- }
- if (0 != pthread_setspecific (db_conn_threadlocal,
- db_conn))
- {
- GNUNET_break (0);
- return NULL;
- }
- return db_conn;
-}
-
-
-/**
- * Start a transaction.
- *
- * @param db_conn the database connection
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_transaction (PGconn *db_conn)
-{
- PGresult *result;
-
- result = PQexec (db_conn,
- "BEGIN");
- if (PGRES_COMMAND_OK !=
- PQresultStatus (result))
- {
- LOG_ERROR ("Failed to start transaction: %s\n",
- PQresultErrorMessage (result));
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Roll back the current transaction of a database connection.
- *
- * @param db_conn the database connection
- * @return #GNUNET_OK on success
- */
-void
-TALER_MINT_DB_rollback (PGconn *db_conn)
-{
- PGresult *result;
-
- result = PQexec (db_conn,
- "ROLLBACK");
- GNUNET_break (PGRES_COMMAND_OK ==
- PQresultStatus (result));
- PQclear (result);
-}
-
-
-/**
- * Commit the current transaction of a database connection.
- *
- * @param db_conn the database connection
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_commit (PGconn *db_conn)
-{
- PGresult *result;
-
- result = PQexec (db_conn,
- "COMMIT");
- if (PGRES_COMMAND_OK !=
- PQresultStatus (result))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Get the summary of a reserve.
- *
- * @param db the database connection handle
- * @param 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
-TALER_MINT_DB_reserve_get (PGconn *db,
- struct Reserve *reserve)
-{
- PGresult *result;
- uint64_t expiration_date_nbo;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(reserve->pub),
- TALER_DB_QUERY_PARAM_END
- };
-
- if (NULL == reserve->pub)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- result = TALER_DB_exec_prepared (db,
- "get_reserve",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("expiration_date", &expiration_date_nbo),
- TALER_DB_RESULT_SPEC_END
- };
- EXITIF (GNUNET_OK != TALER_DB_extract_result (result, rs, 0));
- EXITIF (GNUNET_OK !=
- TALER_DB_extract_amount (result, 0,
- "current_balance_value",
- "current_balance_fraction",
- "balance_currency",
- &reserve->balance));
- reserve->expiry.abs_value_us = GNUNET_ntohll (expiration_date_nbo);
- PQclear (result);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (result);
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Updates a reserve with the data from the given reserve structure.
- *
- * @param db the database connection
- * @param reserve the reserve structure whose data will be used to update the
- * corresponding record in the database.
- * @return #GNUNET_OK upon successful update; #GNUNET_SYSERR upon any error
- */
-int
-reserves_update (PGconn *db,
- struct Reserve *reserve)
-{
- PGresult *result;
- struct TALER_AmountNBO balance_nbo;
- struct GNUNET_TIME_AbsoluteNBO expiry_nbo;
- int ret;
-
- if ((NULL == reserve) || (NULL == reserve->pub))
- return GNUNET_SYSERR;
- ret = GNUNET_OK;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (reserve->pub),
- TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),
- TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),
- TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
- TALER_DB_QUERY_PARAM_END
- };
- TALER_amount_hton (&balance_nbo,
- &reserve->balance);
- expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry);
- result = TALER_DB_exec_prepared (db,
- "update_reserve",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus(result))
- {
- QUERY_ERR (result);
- ret = GNUNET_SYSERR;
- }
- PQclear (result);
- return ret;
-}
-
-
-/**
- * Insert a incoming transaction into reserves. New reserves are also created
- * through this function.
- *
- * @param db the database connection handle
- * @param reserve the reserve structure. The public key of the reserve should
- * be set here. Upon successful execution of this function, the
- * balance and expiration of the reserve will be updated.
- * @param balance the amount that has to be added to the reserve
- * @param expiry the new expiration time for the reserve
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures
- */
-int
-TALER_MINT_DB_reserves_in_insert (PGconn *db,
- struct Reserve *reserve,
- const struct TALER_Amount *balance,
- const struct GNUNET_TIME_Absolute expiry)
-{
- struct TALER_AmountNBO balance_nbo;
- struct GNUNET_TIME_AbsoluteNBO expiry_nbo;
- PGresult *result;
- int reserve_exists;
-
- result = NULL;
- if (NULL == reserve)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK != TALER_MINT_DB_transaction (db))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- reserve_exists = TALER_MINT_DB_reserve_get (db, reserve);
- if (GNUNET_SYSERR == reserve_exists)
- {
- TALER_MINT_DB_rollback (db);
- return GNUNET_SYSERR;
- }
- TALER_amount_hton (&balance_nbo,
- balance);
- expiry_nbo = GNUNET_TIME_absolute_hton (expiry);
- if (GNUNET_NO == reserve_exists)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Reserve does not exist; creating a new one\n");
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (reserve->pub),
- TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),
- TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),
- TALER_DB_QUERY_PARAM_PTR_SIZED (balance_nbo.currency,
- TALER_DB_CURRENCY_LEN),
- TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db,
- "create_reserve",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus(result))
- {
- QUERY_ERR (result);
- goto rollback;
- }
- }
- if (NULL != result)
- PQclear (result);
- result = NULL;
- /* create new incoming transaction */
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (reserve->pub),
- TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),
- TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),
- TALER_DB_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency,
- TALER_DB_CURRENCY_LEN),
- TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db,
- "create_reserves_in_transaction",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus(result))
- {
- QUERY_ERR (result);
- goto rollback;
- }
- PQclear (result);
- result = NULL;
- if (GNUNET_NO == reserve_exists)
- {
- if (GNUNET_OK != TALER_MINT_DB_commit (db))
- return GNUNET_SYSERR;
- reserve->balance = *balance;
- reserve->expiry = expiry;
- return GNUNET_OK;
- }
- /* Update reserve */
- struct Reserve updated_reserve;
- updated_reserve.pub = reserve->pub;
-
- if (GNUNET_OK !=
- TALER_amount_add (&updated_reserve.balance,
- &reserve->balance,
- balance))
- {
- return GNUNET_SYSERR;
- }
- updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry);
- if (GNUNET_OK != reserves_update (db, &updated_reserve))
- goto rollback;
- if (GNUNET_OK != TALER_MINT_DB_commit (db))
- return GNUNET_SYSERR;
- reserve->balance = updated_reserve.balance;
- reserve->expiry = updated_reserve.expiry;
- return GNUNET_OK;
-
- rollback:
- PQclear (result);
- TALER_MINT_DB_rollback (db);
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Locate the response for a /withdraw request under the
- * key of the hash of the blinded message.
- *
- * @param db_conn database connection to use
- * @param h_blind hash of the blinded message
- * @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
-TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,
- const struct GNUNET_HashCode *h_blind,
- struct CollectableBlindcoin *collectable)
-{
- PGresult *result;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (h_blind),
- TALER_DB_QUERY_PARAM_END
- };
- struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
- struct GNUNET_CRYPTO_rsa_Signature *denom_sig;
- char *denom_pub_enc;
- char *denom_sig_enc;
- size_t denom_pub_enc_size;
- size_t denom_sig_enc_size;
- int ret;
-
- ret = GNUNET_SYSERR;
- denom_pub = NULL;
- denom_pub_enc = NULL;
- denom_sig_enc = NULL;
- result = TALER_DB_exec_prepared (db_conn,
- "get_collectable_blindcoin",
- params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result);
- goto cleanup;
- }
- if (0 == PQntuples (result))
- {
- ret = GNUNET_NO;
- goto cleanup;
- }
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC_VAR("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
- TALER_DB_RESULT_SPEC_VAR("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
- TALER_DB_RESULT_SPEC("reserve_sig", &collectable->reserve_sig),
- TALER_DB_RESULT_SPEC("reserve_pub", &collectable->reserve_pub),
- TALER_DB_RESULT_SPEC_END
- };
-
- if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
- {
- GNUNET_break (0);
- goto cleanup;
- }
- denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc,
- denom_pub_enc_size);
- denom_sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc,
- denom_sig_enc_size);
- if ((NULL == denom_pub) || (NULL == denom_sig))
- {
- GNUNET_break (0);
- goto cleanup;
- }
- collectable->denom_pub = denom_pub;
- collectable->sig = denom_sig;
- ret = GNUNET_YES;
-
- cleanup:
- PQclear (result);
- GNUNET_free_non_null (denom_pub_enc);
- GNUNET_free_non_null (denom_sig_enc);
- if (GNUNET_YES != ret)
- { if (NULL != denom_pub)
- GNUNET_CRYPTO_rsa_public_key_free (denom_pub);
- if (NULL != denom_sig)
- GNUNET_CRYPTO_rsa_signature_free (denom_sig);
- }
- return ret;
-}
-
-
-/**
- * Store collectable bit coin under the corresponding
- * hash of the blinded message.
- *
- * @param db_conn database connection to use
- * @param h_blind hash of the blinded message
- * @param withdraw amount by which the reserve will be withdrawn with this
- * transaction
- * @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
-TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,
- const struct GNUNET_HashCode *h_blind,
- struct TALER_Amount withdraw,
- const struct CollectableBlindcoin *collectable)
-{
- PGresult *result;
- struct Reserve reserve;
- char *denom_pub_enc = NULL;
- char *denom_sig_enc = NULL;
- size_t denom_pub_enc_size;
- size_t denom_sig_enc_size;
- int ret;
-
- ret = GNUNET_SYSERR;
- denom_pub_enc_size =
- GNUNET_CRYPTO_rsa_public_key_encode (collectable->denom_pub,
- &denom_pub_enc);
- denom_sig_enc_size =
- GNUNET_CRYPTO_rsa_signature_encode (collectable->sig,
- &denom_sig_enc);
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (h_blind),
- TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1),
- TALER_DB_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size - 1), /* DB doesn't like the trailing \0 */
- TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_pub),
- TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig),
- TALER_DB_QUERY_PARAM_END
- };
- if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn))
- goto cleanup;
- result = TALER_DB_exec_prepared (db_conn,
- "insert_collectable_blindcoin",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- QUERY_ERR (result);
- goto rollback;
- }
- reserve.pub = (struct GNUNET_CRYPTO_EddsaPublicKey *)
- &collectable->reserve_pub;
- if (GNUNET_OK != TALER_MINT_DB_reserve_get (db_conn,
- &reserve))
- goto rollback;
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&reserve.balance,
- &reserve.balance,
- &withdraw))
- goto rollback;
- if (GNUNET_OK != reserves_update (db_conn, &reserve))
- goto rollback;
- if (GNUNET_OK == TALER_MINT_DB_commit (db_conn))
- {
- ret = GNUNET_OK;
- goto cleanup;
- }
-
- rollback:
- TALER_MINT_DB_rollback(db_conn);
- cleanup:
- PQclear (result);
- GNUNET_free_non_null (denom_pub_enc);
- GNUNET_free_non_null (denom_sig_enc);
- return ret;
-}
-
-
-/**
- * Get all of the transaction history associated with the specified
- * reserve.
- *
- * @param db_conn connection to use
- * @param reserve_pub public key of the reserve
- * @return known transaction history (NULL if reserve is unknown)
- */
-struct ReserveHistory *
-TALER_MINT_DB_get_reserve_history (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub)
-{
- PGresult *result;
- struct ReserveHistory *rh;
- struct ReserveHistory *rh_head;
- int rows;
- int ret;
-
- result = NULL;
- rh = NULL;
- rh_head = NULL;
- ret = GNUNET_SYSERR;
- {
- struct BankTransfer *bt;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (reserve_pub),
- TALER_DB_QUERY_PARAM_END
- };
-
- result = TALER_DB_exec_prepared (db_conn,
- "get_reserves_in_transactions",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result);
- goto cleanup;
- }
- if (0 == (rows = PQntuples (result)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Asked to fetch history for an unknown reserve.\n");
- goto cleanup;
- }
- while (0 < rows)
- {
- bt = GNUNET_new (struct BankTransfer);
- if (GNUNET_OK != TALER_DB_extract_amount (result,
- --rows,
- "balance_value",
- "balance_fraction",
- "balance_currency",
- &bt->amount))
- {
- GNUNET_free (bt);
- GNUNET_break (0);
- goto cleanup;
- }
- (void) memcpy (&bt->reserve_pub, reserve_pub, sizeof (bt->reserve_pub));
- if (NULL != rh_head)
- {
- rh_head->next = GNUNET_new (struct ReserveHistory);
- rh_head = rh_head->next;
- }
- else
- {
- rh_head = GNUNET_new (struct ReserveHistory);
- rh = rh_head;
- }
- rh_head->type = TALER_MINT_DB_RO_BANK_TO_MINT;
- rh_head->details.bank = bt;
- }
- }
- PQclear (result);
- result = NULL;
- {
- struct GNUNET_HashCode blind_ev;
- struct GNUNET_CRYPTO_EddsaSignature reserve_sig;
- struct CollectableBlindcoin *cbc;
- char *denom_pub_enc;
- char *denom_sig_enc;
- size_t denom_pub_enc_size;
- size_t denom_sig_enc_size;
-
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (reserve_pub),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db_conn,
- "get_reserves_blindcoins",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- QUERY_ERR (result);
- goto cleanup;
- }
- if (0 == (rows = PQntuples (result)))
- {
- ret = GNUNET_OK; /* Its OK if there are no withdrawls yet */
- goto cleanup;
- }
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC ("blind_ev", &blind_ev),
- TALER_DB_RESULT_SPEC_VAR ("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
- TALER_DB_RESULT_SPEC_VAR ("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
- TALER_DB_RESULT_SPEC ("reserve_sig", &reserve_sig),
- TALER_DB_RESULT_SPEC_END
- };
- GNUNET_assert (NULL != rh);
- GNUNET_assert (NULL != rh_head);
- GNUNET_assert (NULL == rh_head->next);
- while (0 < rows)
- {
- if (GNUNET_YES != TALER_DB_extract_result (result, rs, --rows))
- {
- GNUNET_break (0);
- goto cleanup;
- }
- cbc = GNUNET_new (struct CollectableBlindcoin);
- cbc->sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc,
- denom_sig_enc_size);
- GNUNET_free (denom_sig_enc);
- denom_sig_enc = NULL;
- cbc->denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc,
- denom_pub_enc_size);
- GNUNET_free (denom_pub_enc);
- denom_pub_enc = NULL;
- if ((NULL == cbc->sig) || (NULL == cbc->denom_pub))
- {
- if (NULL != cbc->sig)
- GNUNET_CRYPTO_rsa_signature_free (cbc->sig);
- if (NULL != cbc->denom_pub)
- GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub);
- GNUNET_free (cbc);
- GNUNET_break (0);
- goto cleanup;
- }
- (void) memcpy (&cbc->h_coin_envelope, &blind_ev, sizeof (blind_ev));
- (void) memcpy (&cbc->reserve_pub, reserve_pub, sizeof (cbc->reserve_pub));
- (void) memcpy (&cbc->reserve_sig, &reserve_sig, sizeof (cbc->reserve_sig));
- rh_head->next = GNUNET_new (struct ReserveHistory);
- rh_head = rh_head->next;
- rh_head->type = TALER_MINT_DB_RO_WITHDRAW_COIN;
- rh_head->details.withdraw = cbc;
- }
- }
- ret = GNUNET_OK;
-
- cleanup:
- if (NULL != result)
- PQclear (result);
- if (GNUNET_SYSERR == ret)
- {
- TALER_MINT_DB_free_reserve_history (rh);
- rh = NULL;
- }
- return rh;
-}
-
-
-/**
- * Free memory associated with the given reserve history.
- *
- * @param rh history to free.
- */
-void
-TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh)
-{
- struct BankTransfer *bt;
- struct CollectableBlindcoin *cbc;
- struct ReserveHistory *backref;
-
- while (NULL != rh)
- {
- switch(rh->type)
- {
- case TALER_MINT_DB_RO_BANK_TO_MINT:
- bt = rh->details.bank;
- if (NULL != bt->wire)
- json_decref ((json_t *) bt->wire); /* FIXME: avoid cast? */
- GNUNET_free (bt);
- break;
- case TALER_MINT_DB_RO_WITHDRAW_COIN:
- cbc = rh->details.withdraw;
- GNUNET_CRYPTO_rsa_signature_free (cbc->sig);
- GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub);
- GNUNET_free (cbc);
- break;
- }
- backref = rh;
- rh = rh->next;
- GNUNET_free (backref);
- }
-}
-
-
-/**
- * Check if we have the specified deposit already in the database.
- *
- * @param db_conn 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
- */
-int
-TALER_MINT_DB_have_deposit (PGconn *db_conn,
- const struct Deposit *deposit)
-{
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
- TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id),
- TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),
- TALER_DB_QUERY_PARAM_END
- };
- PGresult *result;
- int ret;
-
- ret = GNUNET_SYSERR;
- result = TALER_DB_exec_prepared (db_conn,
- "get_deposit",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- goto cleanup;
- }
-
- if (0 == PQntuples (result))
- {
- ret = GNUNET_NO;
- goto cleanup;
- }
- ret = GNUNET_YES;
-
- cleanup:
- PQclear (result);
- return ret;
-}
-
-
-/**
- * Insert information about deposited coin into the
- * database.
- *
- * @param db_conn connection to the database
- * @param deposit deposit information to store
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-int
-TALER_MINT_DB_insert_deposit (PGconn *db_conn,
- const struct Deposit *deposit)
-{
- char *denom_pub_enc;
- char *denom_sig_enc;
- char *json_wire_enc;
- PGresult *result;
- struct TALER_AmountNBO amount_nbo;
- size_t denom_pub_enc_size;
- size_t denom_sig_enc_size;
- int ret;
-
- ret = GNUNET_SYSERR;
- denom_pub_enc_size =
- GNUNET_CRYPTO_rsa_public_key_encode (deposit->coin.denom_pub,
- &denom_pub_enc);
- denom_sig_enc_size =
- GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig,
- &denom_sig_enc);
- json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT);
- TALER_amount_hton (&amount_nbo,
- &deposit->amount);
- struct TALER_DB_QueryParam params[]= {
- TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
- TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size),
- TALER_DB_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size),
- TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id),
- TALER_DB_QUERY_PARAM_PTR (&amount_nbo.value),
- TALER_DB_QUERY_PARAM_PTR (&amount_nbo.fraction),
- TALER_DB_QUERY_PARAM_PTR_SIZED (amount_nbo.currency,
- TALER_CURRENCY_LEN - 1),
- TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),
- TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract),
- TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire),
- TALER_DB_QUERY_PARAM_PTR (&deposit->csig),
- TALER_DB_QUERY_PARAM_PTR_SIZED (json_wire_enc,
- strlen (json_wire_enc)),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db_conn, "insert_deposit", params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- goto cleanup;
- }
- ret = GNUNET_OK;
-
- cleanup:
- PQclear (result);
- GNUNET_free_non_null (denom_pub_enc);
- GNUNET_free_non_null (denom_sig_enc);
- GNUNET_free_non_null (json_wire_enc);
- return ret;
-}
-
-
-/**
- * Lookup refresh session data under the given public key.
- *
- * @param db_conn database handle to use
- * @param refresh_session_pub public key to use for the lookup
- * @param session[OUT] where to store the result
- * @return #GNUNET_YES on success,
- * #GNUNET_NO if not found,
- * #GNUNET_SYSERR on DB failure
- */
-int
-TALER_MINT_DB_get_refresh_session (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- struct RefreshSession *session)
-{
- // FIXME: check logic!
- int res;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
- TALER_DB_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_session", params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Query failed: %s\n",
- PQresultErrorMessage (result));
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 == PQntuples (result))
- return GNUNET_NO;
-
- GNUNET_assert (1 == PQntuples (result));
-
- /* We're done if the caller is only interested in
- * whether the session exists or not */
-
- if (NULL == session)
- return GNUNET_YES;
-
- memset (session, 0, sizeof (struct RefreshSession));
-
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("num_oldcoins", &session->num_oldcoins),
- TALER_DB_RESULT_SPEC("num_newcoins", &session->num_newcoins),
- TALER_DB_RESULT_SPEC("kappa", &session->kappa),
- TALER_DB_RESULT_SPEC("noreveal_index", &session->noreveal_index),
- TALER_DB_RESULT_SPEC_END
- };
-
- res = TALER_DB_extract_result (result, rs, 0);
-
- if (GNUNET_OK != res)
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- session->num_oldcoins = ntohs (session->num_oldcoins);
- session->num_newcoins = ntohs (session->num_newcoins);
- session->kappa = ntohs (session->kappa);
- session->noreveal_index = ntohs (session->noreveal_index);
-
- PQclear (result);
- return GNUNET_YES;
-}
-
-
-/**
- * Store new refresh session data under the given public key.
- *
- * @param db_conn database handle to use
- * @param refresh_session_pub public key to use to locate the session
- * @param session session data to store
- * @return #GNUNET_YES on success,
- * #GNUNET_SYSERR on DB failure
- */
-int
-TALER_MINT_DB_create_refresh_session (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- const struct RefreshSession *session)
-{
- // FIXME: actually store session data!
- uint16_t noreveal_index;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(session_pub),
- TALER_DB_QUERY_PARAM_PTR(&noreveal_index),
- TALER_DB_QUERY_PARAM_END
- };
-
- noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);
- noreveal_index = htonl (noreveal_index);
-
- PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_session", params);
-
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Store the given /refresh/melt request in the database.
- *
- * @param db_conn database connection
- * @param session session key of the melt operation
- * @param oldcoin_index index of the coin to store
- * @param melt melt operation
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session,
- uint16_t oldcoin_index,
- const struct RefreshMelt *melt)
-{
- // FIXME: check logic!
- uint16_t oldcoin_index_nbo = htons (oldcoin_index);
- char *buf;
- size_t buf_size;
- PGresult *result;
-
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode (melt->coin.denom_pub,
- &buf);
- {
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(session),
- TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
- TALER_DB_QUERY_PARAM_PTR(&melt->coin.coin_pub),
- TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db_conn, "insert_refresh_melt", params);
- }
- GNUNET_free (buf);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Get information about melted coin details from the database.
- *
- * @param db_conn database connection
- * @param session session key of the melt operation
- * @param oldcoin_index index of the coin to retrieve
- * @param melt melt data to fill in
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session,
- uint16_t oldcoin_index,
- struct RefreshMelt *melt)
-{
- // FIXME: check logic!
- GNUNET_break (0);
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Store in the database which coin(s) we want to create
- * in a given refresh operation.
- *
- * @param db_conn database connection
- * @param session_pub refresh session key
- * @param newcoin_index index of the coin to generate
- * @param denom_pub denomination of the coin to create
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- uint16_t newcoin_index,
- const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub)
-{
- // FIXME: check logic
- uint16_t newcoin_index_nbo = htons (newcoin_index);
- char *buf;
- size_t buf_size;
- PGresult *result;
-
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub,
- &buf);
-
- {
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR (&newcoin_index_nbo),
- TALER_DB_QUERY_PARAM_PTR (session_pub),
- TALER_DB_QUERY_PARAM_PTR_SIZED (buf, buf_size),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db_conn,
- "insert_refresh_order",
- params);
- }
- GNUNET_free (buf);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 != strcmp ("1", PQcmdTuples (result)))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Lookup in the database the @a newcoin_index coin that we want to
- * create in the given refresh operation.
- *
- * @param db_conn database connection
- * @param session_pub refresh session key
- * @param newcoin_index index of the coin to generate
- * @param denom_pub denomination of the coin to create
- * @return NULL on error (not found or internal error)
- */
-struct GNUNET_CRYPTO_rsa_PublicKey *
-TALER_MINT_DB_get_refresh_order (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- uint16_t newcoin_index)
-{
- // FIXME: check logic
- char *buf;
- size_t buf_size;
- struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
- uint16_t newcoin_index_nbo = htons (newcoin_index);
-
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(session_pub),
- TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_DB_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_order", params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return NULL;
- }
-
- if (0 == PQntuples (result))
- {
- PQclear (result);
- /* FIXME: may want to distinguish between different error cases! */
- return NULL;
- }
- GNUNET_assert (1 == PQntuples (result));
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC_VAR ("denom_pub", &buf, &buf_size),
- TALER_DB_RESULT_SPEC_END
- };
- if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return NULL;
- }
- PQclear (result);
- denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size);
- GNUNET_free (buf);
- return denom_pub;
-}
-
-
-
-/**
- * Store information about the commitment of the
- * given coin for the given refresh session in the database.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub refresh session this commitment belongs to
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
- * @param commit_coin coin commitment to store
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on error
- */
-int
-TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int i,
- unsigned int j,
- const struct RefreshCommitCoin *commit_coin)
-{
- // FIXME: check logic!
- uint16_t cnc_index_nbo = htons (i);
- uint16_t newcoin_index_nbo = htons (j);
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
- TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coin->coin_ev, commit_coin->coin_ev_size),
- TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coin->refresh_link->coin_priv_enc,
- commit_coin->refresh_link->blinding_key_enc_size +
- sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)),
- TALER_DB_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_coin", params);
-
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 != strcmp ("1", PQcmdTuples (result)))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Obtain information about the commitment of the
- * given coin of the given refresh session from the database.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub refresh session the commitment belongs to
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
- * @param commit_coin[OUT] coin commitment to return
- * @return #GNUNET_OK on success
- * #GNUNET_NO if not found
- * #GNUNET_SYSERR on error
- */
-int
-TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int cnc_index,
- unsigned int newcoin_index,
- struct RefreshCommitCoin *cc)
-{
- // FIXME: check logic!
- uint16_t cnc_index_nbo = htons (cnc_index);
- uint16_t newcoin_index_nbo = htons (newcoin_index);
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
- TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_DB_QUERY_PARAM_END
- };
- char *c_buf;
- size_t c_buf_size;
- char *rl_buf;
- size_t rl_buf_size;
- struct TALER_RefreshLinkEncrypted *rl;
-
- PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_coin", params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
-
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC_VAR("coin_ev", &c_buf, &c_buf_size),
- TALER_DB_RESULT_SPEC_VAR("link_vector_enc", &rl_buf, &rl_buf_size),
- TALER_DB_RESULT_SPEC_END
- };
- if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
- {
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- if (rl_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
- {
- GNUNET_free (c_buf);
- GNUNET_free (rl_buf);
- return GNUNET_SYSERR;
- }
- rl = TALER_refresh_link_encrypted_decode (rl_buf,
- rl_buf_size);
- GNUNET_free (rl_buf);
- cc->refresh_link = rl;
- cc->coin_ev = c_buf;
- cc->coin_ev_size = c_buf_size;
- return GNUNET_YES;
-}
-
-
-/**
- * Store the commitment to the given (encrypted) refresh link data
- * for the given refresh session.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub public key of the refresh session this
- * commitment belongs with
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to melted (old) coins
- * @param commit_link link information to store
- * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int i,
- unsigned int j,
- const struct RefreshCommitLink *commit_link)
-{
- // FIXME: check logic!
- uint16_t cnc_index_nbo = htons (i);
- uint16_t oldcoin_index_nbo = htons (j);
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
- TALER_DB_QUERY_PARAM_PTR(&commit_link->transfer_pub),
- TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
- TALER_DB_QUERY_PARAM_PTR(&commit_link->shared_secret_enc),
- TALER_DB_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_DB_exec_prepared (db_conn,
- "insert_refresh_commit_link",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 != strcmp ("1", PQcmdTuples (result)))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Obtain the commited (encrypted) refresh link data
- * for the given refresh session.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub public key of the refresh session this
- * commitment belongs with
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to melted (old) coins
- * @param cc[OUT] link information to return
- * @return #GNUNET_SYSERR on internal error,
- * #GNUNET_NO if commitment was not found
- * #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int cnc_index,
- unsigned int oldcoin_index,
- struct RefreshCommitLink *cc)
-{
- // FIXME: check logic!
- uint16_t cnc_index_nbo = htons (cnc_index);
- uint16_t oldcoin_index_nbo = htons (oldcoin_index);
-
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
- TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
- TALER_DB_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_DB_exec_prepared (db_conn,
- "get_refresh_commit_link",
- params);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
-
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("transfer_pub", &cc->transfer_pub),
- TALER_DB_RESULT_SPEC("link_secret_enc", &cc->shared_secret_enc),
- TALER_DB_RESULT_SPEC_END
- };
-
- if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
- {
- PQclear (result);
- GNUNET_free (cc);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * 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 db_conn database connection
- * @param session_pub refresh session
- * @param newcoin_index coin index
- * @param ev_sig coin signature
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- uint16_t newcoin_index,
- const struct GNUNET_CRYPTO_rsa_Signature *ev_sig)
-{
- // FIXME: check logic!
- uint16_t newcoin_index_nbo = htons (newcoin_index);
- char *buf;
- size_t buf_size;
- PGresult *result;
-
- buf_size = GNUNET_CRYPTO_rsa_signature_encode (ev_sig,
- &buf);
- {
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(session_pub),
- TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size),
- TALER_DB_QUERY_PARAM_END
- };
- result = TALER_DB_exec_prepared (db_conn,
- "insert_refresh_collectable",
- params);
- }
- GNUNET_free (buf);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-/**
- * Obtain the link data of a coin, that is the encrypted link
- * information, the denomination keys and the signatures.
- *
- * @param db_conn database connection
- * @param coin_pub public key to use to retrieve linkage data
- * @return all known link data for the coin
- */
-struct LinkDataList *
-TALER_db_get_link (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
-{
- // FIXME: check logic!
- struct LinkDataList *ldl;
- struct LinkDataList *pos;
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(coin_pub),
- TALER_DB_QUERY_PARAM_END
- };
- PGresult *result = TALER_DB_exec_prepared (db_conn, "get_link", params);
-
- ldl = NULL;
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return NULL;
- }
-
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return NULL;
- }
-
-
- int i = 0;
-
- for (i = 0; i < PQntuples (result); i++)
- {
- struct TALER_RefreshLinkEncrypted *link_enc;
- struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
- struct GNUNET_CRYPTO_rsa_Signature *sig;
- char *ld_buf;
- size_t ld_buf_size;
- char *pk_buf;
- size_t pk_buf_size;
- char *sig_buf;
- size_t sig_buf_size;
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC_VAR("link_vector_enc", &ld_buf, &ld_buf_size),
- TALER_DB_RESULT_SPEC_VAR("denom_pub", &pk_buf, &pk_buf_size),
- TALER_DB_RESULT_SPEC_VAR("ev_sig", &sig_buf, &sig_buf_size),
- TALER_DB_RESULT_SPEC_END
- };
-
- if (GNUNET_OK != TALER_DB_extract_result (result, rs, i))
- {
- PQclear (result);
- GNUNET_break (0);
- TALER_db_link_data_list_free (ldl);
- return NULL;
- }
- if (ld_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
- {
- PQclear (result);
- GNUNET_free (pk_buf);
- GNUNET_free (sig_buf);
- GNUNET_free (ld_buf);
- TALER_db_link_data_list_free (ldl);
- return NULL;
- }
- // FIXME: use util API for this!
- link_enc = GNUNET_malloc (sizeof (struct TALER_RefreshLinkEncrypted) +
- ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
- link_enc->blinding_key_enc = (const char *) &link_enc[1];
- link_enc->blinding_key_enc_size = ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
- memcpy (link_enc->coin_priv_enc,
- ld_buf,
- ld_buf_size);
-
- sig = GNUNET_CRYPTO_rsa_signature_decode (sig_buf,
- sig_buf_size);
- denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (pk_buf,
- pk_buf_size);
- GNUNET_free (pk_buf);
- GNUNET_free (sig_buf);
- GNUNET_free (ld_buf);
- if ( (NULL == sig) ||
- (NULL == denom_pub) )
- {
- if (NULL != denom_pub)
- GNUNET_CRYPTO_rsa_public_key_free (denom_pub);
- if (NULL != sig)
- GNUNET_CRYPTO_rsa_signature_free (sig);
- GNUNET_free (link_enc);
- GNUNET_break (0);
- PQclear (result);
- TALER_db_link_data_list_free (ldl);
- return NULL;
- }
- pos = GNUNET_new (struct LinkDataList);
- pos->next = ldl;
- pos->link_data_enc = link_enc;
- pos->denom_pub = denom_pub;
- pos->ev_sig = sig;
- ldl = pos;
- }
- return ldl;
-}
-
-
-/**
- * Free memory of the link data list.
- *
- * @param ldl link data list to release
- */
-void
-TALER_db_link_data_list_free (struct LinkDataList *ldl)
-{
- GNUNET_break (0); // FIXME
-}
-
-
-/**
- * Obtain shared secret and transfer public key from the public key of
- * the coin. This information and the link information returned by
- * #TALER_db_get_link() enable the owner of an old coin to determine
- * the private keys of the new coins after the melt.
- *
- *
- * @param db_conn database connection
- * @param coin_pub public key of the coin
- * @param transfer_pub[OUT] public transfer key
- * @param shared_secret_enc[OUT] set to shared secret
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on failure (not found)
- * #GNUNET_SYSERR on internal failure (database issue)
- */
-int
-TALER_db_get_transfer (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
- struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
- struct TALER_EncryptedLinkSecret *shared_secret_enc)
-{
- // FIXME: check logic!
- struct TALER_DB_QueryParam params[] = {
- TALER_DB_QUERY_PARAM_PTR(coin_pub),
- TALER_DB_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_DB_exec_prepared (db_conn, "get_transfer", params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
-
- if (1 != PQntuples (result))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "got %d tuples for get_transfer\n",
- PQntuples (result));
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- struct TALER_DB_ResultSpec rs[] = {
- TALER_DB_RESULT_SPEC("transfer_pub", transfer_pub),
- TALER_DB_RESULT_SPEC("link_secret_enc", shared_secret_enc),
- TALER_DB_RESULT_SPEC_END
- };
-
- if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
- PQclear (result);
- return GNUNET_OK;
-}
-
-
-
-
-/**
- * Compile a list of all (historic) transactions performed
- * with the given coin (/refresh/melt and /deposit operations).
- *
- * @param db_conn database connection
- * @param coin_pub coin to investigate
- * @return list of transactions, NULL if coin is fresh
- */
-struct TALER_MINT_DB_TransactionList *
-TALER_MINT_DB_get_coin_transactions (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
-{
- // FIXME: check logic!
- GNUNET_break (0); // FIXME: implement!
- return NULL;
-}
-
-
-/**
- * Free linked list of transactions.
- *
- * @param list list to free
- */
-void
-TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list)
-{
- // FIXME: check logic!
- GNUNET_break (0);
-}
-
-
-/* end of mint_db.c */
diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h
@@ -1,976 +0,0 @@
-/*
- 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/mint_db.h
- * @brief Low-level (statement-level) database access for the mint
- * @author Florian Dold
- * @author Christian Grothoff
- */
-#ifndef MINT_DB_H
-#define MINT_DB_H
-
-#include <libpq-fe.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_util.h"
-
-#define TALER_TEMP_SCHEMA_NAME "taler_temporary"
-
-/**
- * Initialize database subsystem.
- *
- * @param connection_cfg configuration for the DB
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_init (const char *connection_cfg);
-
-
-/**
- * Get the thread-local database-handle.
- * Connect to the db if the connection does not exist yet.
- *
- * @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
- */
-PGconn *
-TALER_MINT_DB_get_connection (int temporary);
-
-
-/**
- * Drop the temporary taler schema. This is only useful for testcases
- *
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-int
-TALER_MINT_DB_drop_temporary (PGconn *db);
-
-
-/**
- * Create the necessary tables if they are not present
- *
- * @param temporary should we use a temporary schema
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-int
-TALER_MINT_DB_create_tables (int temporary);
-
-/**
- * Setup prepared statements. FIXME: should this be part of the API,
- * or just internal to "TALER_MINT_DB_get_connection()"?
- *
- * @param db_conn connection handle to initialize
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
- */
-int
-TALER_MINT_DB_prepare (PGconn *db_conn);
-
-
-/**
- * Start a transaction.
- *
- * @param db_conn connection to use
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_transaction (PGconn *db_conn);
-
-
-/**
- * Commit a transaction.
- *
- * @param db_conn connection to use
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_commit (PGconn *db_conn);
-
-
-/**
- * Abort/rollback a transaction.
- *
- * @param db_conn connection to use
- */
-void
-TALER_MINT_DB_rollback (PGconn *db_conn);
-
-
-/**
- * Information we keep on bank transfer(s) that established a reserve.
- */
-struct BankTransfer
-{
-
- /**
- * Public key of the reserve that was filled.
- */
- struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
-
- /**
- * Amount that was transferred to the mint.
- */
- struct TALER_Amount amount;
-
- /**
- * Detailed wire information about the transaction.
- */
- const json_t *wire;
-
-};
-
-
-/* FIXME: add functions to add bank transfers to our DB
- (and to test if we already did add one) (#3633/#3717) */
-
-/**
- * A summary of a Reserve
- */
-struct Reserve
-{
- /**
- * The reserve's public key. This uniquely identifies the reserve
- */
- struct GNUNET_CRYPTO_EddsaPublicKey *pub;
-
- /**
- * The balance amount existing in the reserve
- */
- struct TALER_Amount balance;
-
- /**
- * The expiration date of this reserve
- */
- struct GNUNET_TIME_Absolute expiry;
-};
-
-
-/**
- * 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 CollectableBlindcoin
-{
-
- /**
- * Our signature over the (blinded) coin.
- */
- struct GNUNET_CRYPTO_rsa_Signature *sig;
-
- /**
- * Denomination key (which coin was generated).
- */
- struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
-
- /**
- * Public key of the reserve that was drained.
- */
- struct GNUNET_CRYPTO_EddsaPublicKey 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 GNUNET_CRYPTO_EddsaSignature reserve_sig;
-};
-
-
-/**
- * Get the summary of a reserve.
- *
- * @param db the database connection handle
- * @param 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
-TALER_MINT_DB_reserve_get (PGconn *db,
- struct Reserve *reserve);
-
-
-/**
- * Insert a incoming transaction into reserves. New reserves are also created
- * through this function.
- *
- * @param db the database connection handle
- * @param reserve the reserve structure. The public key of the reserve should
- * be set here. Upon successful execution of this function, the
- * balance and expiration of the reserve will be updated.
- * @param balance the amount that has to be added to the reserve
- * @param expiry the new expiration time for the reserve
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures
- */
-int
-TALER_MINT_DB_reserves_in_insert (PGconn *db,
- struct Reserve *reserve,
- const struct TALER_Amount *balance,
- const struct GNUNET_TIME_Absolute expiry);
-
-
-/**
- * Locate the response for a /withdraw request under the
- * key of the hash of the blinded message.
- *
- * @param db_conn database connection to use
- * @param h_blind hash of the blinded message
- * @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
-TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,
- const struct GNUNET_HashCode *h_blind,
- struct CollectableBlindcoin *collectable);
-
-
-/**
- * Store collectable bit coin under the corresponding
- * hash of the blinded message.
- *
- * @param db_conn database connection to use
- * @param h_blind hash of the blinded message
- * @param withdraw amount by which the reserve will be withdrawn with this
- * transaction
- * @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
-TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,
- const struct GNUNET_HashCode *h_blind,
- struct TALER_Amount withdraw,
- const struct CollectableBlindcoin *collectable);
-
-
-
-/**
- * Types of operations on a reserved.
- */
-enum TALER_MINT_DB_ReserveOperation
-{
- /**
- * Money was deposited into the reserve via a bank transfer.
- */
- TALER_MINT_DB_RO_BANK_TO_MINT = 0,
-
- /**
- * A Coin was withdrawn from the reserve using /withdraw.
- */
- TALER_MINT_DB_RO_WITHDRAW_COIN = 1
-};
-
-
-/**
- * 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 ReserveHistory
-{
-
- /**
- * Next entry in the reserve history.
- */
- struct ReserveHistory *next;
-
- /**
- * Type of the event, determins @e details.
- */
- enum TALER_MINT_DB_ReserveOperation type;
-
- /**
- * Details of the operation, depending on @e type.
- */
- union
- {
-
- /**
- * Details about a bank transfer to the mint.
- */
- struct BankTransfer *bank;
-
- /**
- * Details about a /withdraw operation.
- */
- struct CollectableBlindcoin *withdraw;
-
- } details;
-
-};
-
-
-/**
- * Get all of the transaction history associated with the specified
- * reserve.
- *
- * @param db_conn connection to use
- * @param reserve_pub public key of the reserve
- * @return known transaction history (NULL if reserve is unknown)
- */
-struct ReserveHistory *
-TALER_MINT_DB_get_reserve_history (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub);
-
-
-/**
- * Free memory associated with the given reserve history.
- *
- * @param rh history to free.
- */
-void
-TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh);
-
-
-/**
- * Specification for a /deposit operation.
- */
-struct 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 GNUNET_CRYPTO_EcdsaSignature csig;
-
- /**
- * Public key of the merchant. Enables later identification
- * of the merchant in case of a need to rollback transactions.
- */
- struct GNUNET_CRYPTO_EddsaPublicKey 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.
- */
- const json_t *wire;
-
- /**
- * Merchant-generated transaction ID to detect duplicate
- * transactions.
- */
- uint64_t transaction_id;
-
- /**
- * Fraction of the coin's remaining value to be deposited.
- * The coin is identified by @e coin_pub.
- */
- struct TALER_Amount amount;
-
-};
-
-
-/**
- * Check if we have the specified deposit already in the database.
- *
- * @param db_conn 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 internal error
- */
-int
-TALER_MINT_DB_have_deposit (PGconn *db_conn,
- const struct Deposit *deposit);
-
-
-/**
- * Insert information about deposited coin into the
- * database.
- *
- * @param db_conn connection to the database
- * @param deposit deposit information to store
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-int
-TALER_MINT_DB_insert_deposit (PGconn *db_conn,
- const struct Deposit *deposit);
-
-
-
-/**
- * Global information for a refreshing session. Includes
- * dimensions of the operation, security parameters and
- * client signatures from "/refresh/melt" and "/refresh/commit".
- */
-struct RefreshSession
-{
- /**
- * Signature over the commitments by the client,
- * only valid if @e has_commit_sig is set.
- */
- struct GNUNET_CRYPTO_EddsaSignature commit_sig;
-
- /**
- * Hash over coins to melt and coins to create of the
- * refresh session.
- */
- struct GNUNET_HashCode session_hash;
-
- /**
- * Signature over the melt by the client.
- */
- struct GNUNET_CRYPTO_EddsaSignature melt_sig;
-
- /**
- * Number of coins we are melting.
- */
- uint16_t num_oldcoins;
-
- /**
- * Number of new coins we are creating.
- */
- uint16_t num_newcoins;
-
- /**
- * Number of parallel operations we perform for the cut and choose.
- * (must be greater or equal to three for security). 0 if not yet
- * known.
- */
- uint16_t kappa;
-
- /**
- * Index (smaller @e kappa) which the mint has chosen to not
- * have revealed during cut and choose.
- */
- uint16_t noreveal_index;
-
-};
-
-
-/**
- * Lookup refresh session data under the given public key.
- *
- * @param db_conn database handle to use
- * @param refresh_session_pub public key to use for the lookup
- * @param session[OUT] where to store the result
- * @return #GNUNET_YES on success,
- * #GNUNET_NO if not found,
- * #GNUNET_SYSERR on DB failure
- */
-int
-TALER_MINT_DB_get_refresh_session (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- struct RefreshSession *session);
-
-
-/**
- * Store new refresh session data under the given public key.
- *
- * @param db_conn database handle to use
- * @param refresh_session_pub public key to use to locate the session
- * @param session session data to store
- * @return #GNUNET_YES on success,
- * #GNUNET_SYSERR on DB failure
- */
-int
-TALER_MINT_DB_create_refresh_session (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- const struct RefreshSession *session);
-
-
-/**
- * Specification for coin in a /refresh/melt operation.
- */
-struct RefreshMelt
-{
- /**
- * Information about the coin that is being melted.
- */
- struct TALER_CoinPublicInfo coin;
-
- /**
- * Signature over the melting operation.
- */
- struct GNUNET_CRYPTO_EcdsaSignature coin_sig;
-
- /**
- * Which melting operation should the coin become a part of.
- */
- struct GNUNET_HashCode melt_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.
- */
- struct TALER_Amount amount;
-
-};
-
-
-/**
- * Store the given /refresh/melt request in the database.
- *
- * @param db_conn database connection
- * @param session session key of the melt operation
- * @param oldcoin_index index of the coin to store
- * @param melt coin melt operation details to store
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session,
- uint16_t oldcoin_index,
- const struct RefreshMelt *melt);
-
-
-
-/**
- * Get information about melted coin details from the database.
- *
- * @param db_conn database connection
- * @param session session key of the melt operation
- * @param oldcoin_index index of the coin to retrieve
- * @param melt melt data to fill in
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session,
- uint16_t oldcoin_index,
- struct RefreshMelt *melt);
-
-
-/**
- * Store in the database which coin(s) we want to create
- * in a given refresh operation.
- *
- * @param db_conn database connection
- * @param session_pub refresh session key
- * @param newcoin_index index of the coin to generate
- * @param denom_pub denomination of the coin to create
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- uint16_t newcoin_index,
- const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub);
-
-
-/**
- * Lookup in the database the @a newcoin_index coin that we want to
- * create in the given refresh operation.
- *
- * @param db_conn database connection
- * @param session_pub refresh session key
- * @param newcoin_index index of the coin to generate
- * @param denom_pub denomination of the coin to create
- * @return NULL on error (not found or internal error)
- */
-struct GNUNET_CRYPTO_rsa_PublicKey *
-TALER_MINT_DB_get_refresh_order (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- uint16_t newcoin_index);
-
-
-/**
- * We have as many `struct RefreshCommitCoin` as there are new
- * coins being created by the refresh (for each of the kappa
- * sets). These are the coins we ask the mint to sign if the
- * respective set is selected.
- */
-struct 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;
-
-};
-
-
-/**
- * Store information about the commitment of the
- * given coin for the given refresh session in the database.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub refresh session this commitment belongs to
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
- * @param commit_coin coin commitment to store
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on error
- */
-int
-TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int i,
- unsigned int j,
- const struct RefreshCommitCoin *commit_coin);
-
-
-/**
- * Obtain information about the commitment of the
- * given coin of the given refresh session from the database.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub refresh session the commitment belongs to
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
- * @param commit_coin[OUT] coin commitment to return
- * @return #GNUNET_OK on success
- * #GNUNET_NO if not found
- * #GNUNET_SYSERR on error
- */
-int
-TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int i,
- unsigned int j,
- struct RefreshCommitCoin *commit_coin);
-
-
-/**
- * For each (old) coin being melted, we have a `struct
- * RefreshCommitLink` that allows the user to find the shared secret
- * to decrypt the respective refresh links for the new coins in the
- * `struct RefreshCommitCoin`.
- */
-struct RefreshCommitLink
-{
- /**
- * Transfer public key (FIXME: explain!)
- */
- struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
-
- /**
- * Encrypted shared secret to decrypt the link.
- */
- struct TALER_EncryptedLinkSecret shared_secret_enc;
-};
-
-
-/**
- * Store the commitment to the given (encrypted) refresh link data
- * for the given refresh session.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub public key of the refresh session this
- * commitment belongs with
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to melted (old) coins
- * @param commit_link link information to store
- * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int i,
- unsigned int j,
- const struct RefreshCommitLink *commit_link);
-
-/**
- * Obtain the commited (encrypted) refresh link data
- * for the given refresh session.
- *
- * @param db_conn database connection to use
- * @param refresh_session_pub public key of the refresh session this
- * commitment belongs with
- * @param i set index (1st dimension)
- * @param j coin index (2nd dimension), corresponds to melted (old) coins
- * @param cc[OUT] link information to return
- * @return #GNUNET_SYSERR on internal error,
- * #GNUNET_NO if commitment was not found
- * #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
- unsigned int i,
- unsigned int j,
- struct RefreshCommitLink *cc);
-
-
-/**
- * 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 db_conn database connection
- * @param session_pub refresh session
- * @param newcoin_index coin index
- * @param ev_sig coin signature
- * @return #GNUNET_OK on success
- */
-int
-TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
- uint16_t newcoin_index,
- const struct GNUNET_CRYPTO_rsa_Signature *ev_sig);
-
-
-/**
- * Linked list of refresh information linked to a coin.
- */
-struct LinkDataList
-{
- /**
- * Information is stored in a NULL-terminated linked list.
- */
- struct 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 GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
-
- /**
- * Signature over the blinded envelope.
- */
- struct GNUNET_CRYPTO_rsa_Signature *ev_sig;
-};
-
-
-/**
- * Obtain the link data of a coin, that is the encrypted link
- * information, the denomination keys and the signatures.
- *
- * @param db_conn database connection
- * @param coin_pub public key to use to retrieve linkage data
- * @return all known link data for the coin
- */
-struct LinkDataList *
-TALER_db_get_link (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub);
-
-
-/**
- * Free memory of the link data list.
- *
- * @param ldl link data list to release
- */
-void
-TALER_db_link_data_list_free (struct LinkDataList *ldl);
-
-
-/**
- * Obtain shared secret and transfer public key from the public key of
- * the coin. This information and the link information returned by
- * #TALER_db_get_link() enable the owner of an old coin to determine
- * the private keys of the new coins after the melt.
- *
- *
- * @param db_conn database connection
- * @param coin_pub public key of the coin
- * @param transfer_pub[OUT] public transfer key
- * @param shared_secret_enc[OUT] set to shared secret
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on failure (not found)
- * #GNUNET_SYSERR on internal failure (database issue)
- */
-int
-TALER_db_get_transfer (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
- struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
- struct TALER_EncryptedLinkSecret *shared_secret_enc);
-
-
-/**
- * Specification for a /lock operation.
- */
-struct Lock
-{
- /**
- * Information about the coin that is being melted.
- */
- struct TALER_CoinPublicInfo coin;
-
- /**
- * Signature over the melting operation.
- */
- const struct GNUNET_CRYPTO_EcdsaSignature coin_sig;
-
- /**
- * How much value is being melted?
- */
- struct TALER_Amount amount;
-
- // FIXME: more needed...
-};
-
-
-/**
- * Test if the given /lock request is known to us.
- *
- * @param db_conn database connection
- * @param lock lock operation
- * @return #GNUNET_YES if known,
- * #GNUENT_NO if not,
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_have_lock (PGconn *db_conn,
- const struct Lock *lock);
-
-
-/**
- * Store the given /lock request in the database.
- *
- * @param db_conn database connection
- * @param lock lock operation
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-int
-TALER_MINT_DB_insert_lock (PGconn *db_conn,
- const struct Lock *lock);
-
-
-/**
- * Enumeration to classify the different types of transactions
- * that can be done with a coin.
- */
-enum TALER_MINT_DB_TransactionType
-{
- /**
- * /deposit operation.
- */
- TALER_MINT_DB_TT_DEPOSIT = 0,
-
- /**
- * /refresh/melt operation.
- */
- TALER_MINT_DB_TT_REFRESH_MELT = 1,
-
- /**
- * /lock operation.
- */
- TALER_MINT_DB_TT_LOCK = 2
-};
-
-
-/**
- * List of transactions we performed for a particular coin.
- */
-struct TALER_MINT_DB_TransactionList
-{
-
- /**
- * Next pointer in the NULL-terminated linked list.
- */
- struct TALER_MINT_DB_TransactionList *next;
-
- /**
- * Type of the transaction, determines what is stored in @e details.
- */
- enum TALER_MINT_DB_TransactionType type;
-
- /**
- * Details about the transaction, depending on @e type.
- */
- union
- {
-
- /**
- * Details if transaction was a /deposit operation.
- */
- struct Deposit *deposit;
-
- /**
- * Details if transaction was a /refresh/melt operation.
- */
- struct RefreshMelt *melt;
-
- /**
- * Details if transaction was a /lock operation.
- */
- struct Lock *lock;
-
- } details;
-
-};
-
-
-/**
- * Compile a list of all (historic) transactions performed
- * with the given coin (/refresh/melt and /deposit operations).
- *
- * @param db_conn database connection
- * @param coin_pub coin to investigate
- * @return list of transactions, NULL if coin is fresh
- */
-struct TALER_MINT_DB_TransactionList *
-TALER_MINT_DB_get_coin_transactions (PGconn *db_conn,
- const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub);
-
-
-/**
- * Free linked list of transactions.
- *
- * @param list list to free
- */
-void
-TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list);
-
-
-
-#endif /* _NEURO_MINT_DB_H */
diff --git a/src/mint/plugin.c b/src/mint/plugin.c
@@ -0,0 +1,183 @@
+/*
+ This file is part of TALER
+ Copyright (C) 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/plugin.c
+ * @brief Logic to load database plugin
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "plugin.h"
+#include <ltdl.h>
+
+
+/**
+ * Global variable with the plugin (once loaded).
+ */
+struct TALER_MINTDB_Plugin *plugin;
+
+/**
+ * Libtool search path before we started.
+ */
+static char *old_dlsearchpath;
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_MINT_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Shutdown the plugin.
+ */
+void
+TALER_MINT_plugin_unload ()
+{
+ if (NULL == plugin)
+ return;
+}
+
+
+/**
+ * Setup libtool paths.
+ */
+void __attribute__ ((constructor))
+plugin_init ()
+{
+ int err;
+ const char *opath;
+ char *path;
+ char *cpath;
+
+ err = lt_dlinit ();
+ if (err > 0)
+ {
+ FPRINTF (stderr,
+ _("Initialization of plugin mechanism failed: %s!\n"),
+ lt_dlerror ());
+ return;
+ }
+ opath = lt_dlgetsearchpath ();
+ if (NULL != opath)
+ old_dlsearchpath = GNUNET_strdup (opath);
+ path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+ if (NULL != path)
+ {
+ if (NULL != opath)
+ {
+ GNUNET_asprintf (&cpath, "%s:%s", opath, path);
+ lt_dlsetsearchpath (cpath);
+ GNUNET_free (path);
+ GNUNET_free (cpath);
+ }
+ else
+ {
+ lt_dlsetsearchpath (path);
+ GNUNET_free (path);
+ }
+ }
+}
+
+
+/**
+ * Shutdown libtool.
+ */
+void __attribute__ ((destructor))
+plugin_fini ()
+{
+ lt_dlsetsearchpath (old_dlsearchpath);
+ if (NULL != old_dlsearchpath)
+ {
+ GNUNET_free (old_dlsearchpath);
+ old_dlsearchpath = NULL;
+ }
+ lt_dlexit ();
+}
+
+
+// FIXME: decide if we should keep these in each plugin, here
+// or yet again somewhere else entirely (plugin_common.c?)
+
+/**
+ * Free memory associated with the given reserve history.
+ *
+ * @param rh history to free.
+ */
+void
+TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh)
+{
+ struct BankTransfer *bt;
+ struct CollectableBlindcoin *cbc;
+ struct ReserveHistory *backref;
+
+ while (NULL != rh)
+ {
+ switch(rh->type)
+ {
+ case TALER_MINT_DB_RO_BANK_TO_MINT:
+ bt = rh->details.bank;
+ if (NULL != bt->wire)
+ json_decref ((json_t *) bt->wire); /* FIXME: avoid cast? */
+ GNUNET_free (bt);
+ break;
+ case TALER_MINT_DB_RO_WITHDRAW_COIN:
+ cbc = rh->details.withdraw;
+ GNUNET_CRYPTO_rsa_signature_free (cbc->sig);
+ GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub);
+ GNUNET_free (cbc);
+ break;
+ }
+ backref = rh;
+ rh = rh->next;
+ GNUNET_free (backref);
+ }
+}
+
+
+/**
+ * Free memory of the link data list.
+ *
+ * @param ldl link data list to release
+ */
+void
+TALER_db_link_data_list_free (struct LinkDataList *ldl)
+{
+ GNUNET_break (0); // FIXME
+}
+
+
+/**
+ * Free linked list of transactions.
+ *
+ * @param list list to free
+ */
+void
+TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list)
+{
+ // FIXME: check logic!
+ GNUNET_break (0);
+}
+
+
+
+/* end of plugin.c */
diff --git a/src/mint/plugin.h b/src/mint/plugin.h
@@ -0,0 +1,77 @@
+/*
+ This file is part of TALER
+ Copyright (C) 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/plugin.h
+ * @brief Logic to load database plugins
+ * @author Christian Grothoff
+ */
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mintdb_plugin.h"
+
+/**
+ * Global variable with the plugin (once loaded).
+ */
+extern struct TALER_MINTDB_Plugin *plugin;
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_MINT_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Shutdown the plugin.
+ */
+void
+TALER_MINT_plugin_unload (void);
+
+
+/**
+ * Free memory associated with the given reserve history.
+ *
+ * @param rh history to free.
+ */
+void
+TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh);
+
+
+/**
+ * Free memory of the link data list.
+ *
+ * @param ldl link data list to release
+ */
+void
+TALER_db_link_data_list_free (struct LinkDataList *ldl);
+
+
+/**
+ * Free linked list of transactions.
+ *
+ * @param list list to free
+ */
+void
+TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list);
+
+
+#endif
diff --git a/src/mint/plugin_mintdb_postgres.c b/src/mint/plugin_mintdb_postgres.c
@@ -0,0 +1,2316 @@
+/*
+ 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 plugin_mintdb_postgres.c
+ * @brief Low-level (statement-level) Postgres database access for the mint
+ * @author Florian Dold
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura
+ */
+#include "platform.h"
+#include "db_pq.h"
+#include "taler_signatures.h"
+#include "taler_mintdb_plugin.h"
+#include <pthread.h>
+#include <libpq-fe.h>
+
+
+#define TALER_TEMP_SCHEMA_NAME "taler_temporary"
+
+#define QUERY_ERR(result) \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s\n", __FILE__, __LINE__, PQresultErrorMessage (result))
+
+
+#define BREAK_DB_ERR(result) do { \
+ GNUNET_break(0); \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \
+ } while (0)
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+#define SQLEXEC_(conn, sql, result) \
+ do { \
+ result = PQexec (conn, sql); \
+ if (PGRES_COMMAND_OK != PQresultStatus (result)) \
+ { \
+ BREAK_DB_ERR (result); \
+ PQclear (result); result = NULL; \
+ goto SQLEXEC_fail; \
+ } \
+ PQclear (result); result = NULL; \
+ } while (0)
+
+/**
+ * This the length of the currency strings (without 0-termination) we use. Note
+ * that we need to use this at the DB layer instead of TALER_CURRENCY_LEN as the
+ * DB only needs to store 3 bytes instead of 8 bytes.
+ */
+#define TALER_DB_CURRENCY_LEN 3
+
+
+/**
+ * Handle for a database session (per-thread, for transactions).
+ */
+struct TALER_MINTDB_Session
+{
+ /**
+ * Postgres connection handle.
+ */
+ PGconn *conn;
+};
+
+
+/**
+ * Type of the "cls" argument given to each of the functions in
+ * our API.
+ */
+struct PostgresClosure
+{
+
+ /**
+ * Thread-local database connection.
+ * Contains a pointer to PGconn or NULL.
+ */
+ pthread_key_t db_conn_threadlocal;
+
+ /**
+ * Database connection string, as read from
+ * the configuration.
+ */
+ char *TALER_MINT_db_connection_cfg_str;
+};
+
+
+
+/**
+ * Set the given connection to use a temporary schema
+ *
+ * @param db the database connection
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon error
+ */
+static int
+set_temporary_schema (PGconn *db)
+{
+ PGresult *result;
+
+ SQLEXEC_(db,
+ "CREATE SCHEMA IF NOT EXISTS " TALER_TEMP_SCHEMA_NAME ";"
+ "SET search_path to " TALER_TEMP_SCHEMA_NAME ";",
+ result);
+ return GNUNET_OK;
+ SQLEXEC_fail:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Drop the temporary taler schema. This is only useful for testcases
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+postgres_drop_temporary (void *cls,
+ struct TALER_MINTDB_Session *session)
+{
+ PGresult *result;
+
+ SQLEXEC_ (session->conn,
+ "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;",
+ result);
+ return GNUNET_OK;
+ SQLEXEC_fail:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Create the necessary tables if they are not present
+ *
+ * @param pc our overall context
+ * @param temporary should we use a temporary schema
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+postgres_create_tables (struct PostgresClosure *pc,
+ int temporary)
+{
+ PGresult *result;
+ PGconn *conn;
+
+ result = NULL;
+ conn = PQconnectdb (pc->TALER_MINT_db_connection_cfg_str);
+ if (CONNECTION_OK != PQstatus (conn))
+ {
+ LOG_ERROR ("Database connection failed: %s\n",
+ PQerrorMessage (conn));
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if ((GNUNET_YES == temporary)
+ && (GNUNET_SYSERR == set_temporary_schema (conn)))
+ {
+ PQfinish (conn);
+ return GNUNET_SYSERR;
+ }
+#define SQLEXEC(sql) SQLEXEC_(conn, sql, result);
+ /* reserves table is for summarization of a reserve. It is updated when new
+ funds are added and existing funds are withdrawn */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves"
+ "("
+ " reserve_pub BYTEA PRIMARY KEY"
+ ",current_balance_value INT4 NOT NULL"
+ ",current_balance_fraction INT4 NOT NULL"
+ ",balance_currency VARCHAR(4) NOT NULL"
+ ",expiration_date INT8 NOT NULL"
+ ")");
+ /* reserves_in table collects the transactions which transfer funds into the
+ reserve. The amount and expiration date for the corresponding reserve are
+ updated when new transfer funds are added. The rows of this table
+ correspond to each incoming transaction. */
+ SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in"
+ "("
+ " reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
+ ",balance_value INT4 NOT NULL"
+ ",balance_fraction INT4 NOT NULL"
+ ",balance_currency VARCHAR(4) NOT NULL"
+ ",expiration_date INT8 NOT NULL"
+ ");");
+ /* Create an index on the foreign key as it is not created automatically by PSQL */
+ SQLEXEC ("CREATE INDEX reserves_in_reserve_pub_index"
+ " ON reserves_in (reserve_pub);");
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS collectable_blindcoins"
+ "("
+ "blind_ev BYTEA PRIMARY KEY"
+ ",denom_pub BYTEA NOT NULL" /* FIXME: Make this a foreign key? */
+ ",denom_sig BYTEA NOT NULL"
+ ",reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
+ ",reserve_sig BYTEA NOT NULL"
+ ");");
+ SQLEXEC ("CREATE INDEX collectable_blindcoins_reserve_pub_index ON"
+ " collectable_blindcoins (reserve_pub)");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins "
+ "("
+ " coin_pub BYTEA NOT NULL PRIMARY KEY"
+ ",denom_pub BYTEA NOT NULL"
+ ",denom_sig BYTEA NOT NULL"
+ ",expended_value INT4 NOT NULL"
+ ",expended_fraction INT4 NOT NULL"
+ ",expended_currency VARCHAR(4) NOT NULL"
+ ",refresh_session_pub BYTEA"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
+ "("
+ " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)"
+ ",session_melt_sig BYTEA"
+ ",session_commit_sig BYTEA"
+ ",noreveal_index INT2 NOT NULL"
+ // non-zero if all reveals were ok
+ // and the new coin signatures are ready
+ ",reveal_ok BOOLEAN NOT NULL DEFAULT false"
+ ") ");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order "
+ "( "
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
+ ",newcoin_index INT2 NOT NULL "
+ ",denom_pub BYTEA NOT NULL "
+ ",PRIMARY KEY (session_pub, newcoin_index)"
+ ") ");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)"
+ ",transfer_pub BYTEA NOT NULL"
+ ",link_secret_enc BYTEA NOT NULL"
+ // index of the old coin in the customer's request
+ ",oldcoin_index INT2 NOT NULL"
+ // index for cut and choose,
+ // ranges from 0 to kappa-1
+ ",cnc_index INT2 NOT NULL"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",link_vector_enc BYTEA NOT NULL"
+ // index of the new coin in the customer's request
+ ",newcoin_index INT2 NOT NULL"
+ // index for cut and choose,
+ ",cnc_index INT2 NOT NULL"
+ ",coin_ev BYTEA NOT NULL"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) "
+ ",denom_pub BYTEA NOT NULL "
+ ",oldcoin_index INT2 NOT NULL"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_collectable"
+ "("
+ " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) "
+ ",ev_sig BYTEA NOT NULL"
+ ",newcoin_index INT2 NOT NULL"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
+ "( "
+ " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
+ ",denom_pub BYTEA NOT NULL" /* FIXME: Link this as a foreign key? */
+ ",denom_sig BYTEA NOT NULL"
+ ",transaction_id INT8 NOT NULL"
+ ",amount_currency VARCHAR(4) NOT NULL"
+ ",amount_value INT4 NOT NULL"
+ ",amount_fraction INT4 NOT NULL"
+ ",merchant_pub BYTEA NOT NULL CHECK (length(merchant_pub)=32)"
+ ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
+ ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
+ ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
+ ",wire TEXT NOT NULL"
+ ")");
+#undef SQLEXEC
+ PQfinish (conn);
+ return GNUNET_OK;
+
+ SQLEXEC_fail:
+ PQfinish (conn);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Setup prepared statements.
+ *
+ * @param db_conn connection handle to initialize
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+static int
+postgres_prepare (PGconn *db_conn)
+{
+ PGresult *result;
+
+#define PREPARE(name, sql, ...) \
+ do { \
+ result = PQprepare (db_conn, name, sql, __VA_ARGS__); \
+ if (PGRES_COMMAND_OK != PQresultStatus (result)) \
+ { \
+ BREAK_DB_ERR (result); \
+ PQclear (result); result = NULL; \
+ return GNUNET_SYSERR; \
+ } \
+ PQclear (result); result = NULL; \
+ } while (0);
+
+ PREPARE ("get_reserve",
+ "SELECT "
+ "current_balance_value"
+ ",current_balance_fraction"
+ ",balance_currency "
+ ",expiration_date "
+ "FROM reserves "
+ "WHERE reserve_pub=$1 "
+ "LIMIT 1; ",
+ 1, NULL);
+ PREPARE ("create_reserve",
+ "INSERT INTO reserves ("
+ " reserve_pub,"
+ " current_balance_value,"
+ " current_balance_fraction,"
+ " balance_currency,"
+ " expiration_date) VALUES ("
+ "$1, $2, $3, $4, $5);",
+ 5, NULL);
+ PREPARE ("update_reserve",
+ "UPDATE reserves "
+ "SET"
+ " current_balance_value=$2 "
+ ",current_balance_fraction=$3 "
+ ",expiration_date=$4 "
+ "WHERE reserve_pub=$1 ",
+ 4, NULL);
+ PREPARE ("create_reserves_in_transaction",
+ "INSERT INTO reserves_in ("
+ " reserve_pub,"
+ " balance_value,"
+ " balance_fraction,"
+ " balance_currency,"
+ " expiration_date) VALUES ("
+ " $1, $2, $3, $4, $5);",
+ 5, NULL);
+ PREPARE ("get_reserves_in_transactions",
+ "SELECT"
+ " balance_value"
+ ",balance_fraction"
+ ",balance_currency"
+ ",expiration_date"
+ " FROM reserves_in WHERE reserve_pub=$1",
+ 1, NULL);
+ PREPARE ("insert_collectable_blindcoin",
+ "INSERT INTO collectable_blindcoins ( "
+ " blind_ev"
+ ",denom_pub, denom_sig"
+ ",reserve_pub, reserve_sig) "
+ "VALUES ($1, $2, $3, $4, $5)",
+ 5, NULL);
+ PREPARE ("get_collectable_blindcoin",
+ "SELECT "
+ " denom_pub, denom_sig"
+ ",reserve_sig, reserve_pub "
+ "FROM collectable_blindcoins "
+ "WHERE blind_ev = $1",
+ 1, NULL);
+ PREPARE ("get_reserves_blindcoins",
+ "select"
+ " blind_ev"
+ ",denom_pub, denom_sig"
+ ",reserve_sig"
+ " FROM collectable_blindcoins"
+ " WHERE reserve_pub=$1;",
+ 1, NULL);
+
+ /* FIXME: does it make sense to store these computed values in the DB? */
+#if 0
+ PREPARE ("get_refresh_session",
+ "SELECT "
+ " (SELECT count(*) FROM refresh_melt WHERE session_pub = $1)::INT2 as num_oldcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_pub = $1 and cnc_index = 0)::INT2 as num_newcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_pub = $1 and newcoin_index = 0)::INT2 as kappa "
+ ",noreveal_index"
+ ",session_commit_sig "
+ ",reveal_ok "
+ "FROM refresh_sessions "
+ "WHERE session_pub = $1",
+ 1, NULL);
+#endif
+
+ PREPARE ("get_known_coin",
+ "SELECT "
+ " coin_pub, denom_pub, denom_sig "
+ ",expended_value, expended_fraction, expended_currency "
+ ",refresh_session_pub "
+ "FROM known_coins "
+ "WHERE coin_pub = $1",
+ 1, NULL);
+ PREPARE ("update_known_coin",
+ "UPDATE known_coins "
+ "SET "
+ " denom_pub = $2 "
+ ",denom_sig = $3 "
+ ",expended_value = $4 "
+ ",expended_fraction = $5 "
+ ",expended_currency = $6 "
+ ",refresh_session_pub = $7 "
+ "WHERE "
+ " coin_pub = $1 ",
+ 7, NULL);
+ PREPARE ("insert_known_coin",
+ "INSERT INTO known_coins ("
+ " coin_pub"
+ ",denom_pub"
+ ",denom_sig"
+ ",expended_value"
+ ",expended_fraction"
+ ",expended_currency"
+ ",refresh_session_pub"
+ ")"
+ "VALUES ($1,$2,$3,$4,$5,$6,$7)",
+ 7, NULL);
+ PREPARE ("get_refresh_commit_link",
+ "SELECT "
+ " transfer_pub "
+ ",link_secret_enc "
+ "FROM refresh_commit_link "
+ "WHERE session_pub = $1 AND cnc_index = $2 AND oldcoin_index = $3",
+ 3, NULL);
+ PREPARE ("get_refresh_commit_coin",
+ "SELECT "
+ " link_vector_enc "
+ ",coin_ev "
+ "FROM refresh_commit_coin "
+ "WHERE session_pub = $1 AND cnc_index = $2 AND newcoin_index = $3",
+ 3, NULL);
+ PREPARE ("insert_refresh_order",
+ "INSERT INTO refresh_order ( "
+ " newcoin_index "
+ ",session_pub "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ PREPARE ("insert_refresh_melt",
+ "INSERT INTO refresh_melt ( "
+ " session_pub "
+ ",oldcoin_index "
+ ",coin_pub "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3, $4) ",
+ 3, NULL);
+ PREPARE ("get_refresh_order",
+ "SELECT denom_pub "
+ "FROM refresh_order "
+ "WHERE session_pub = $1 AND newcoin_index = $2",
+ 2, NULL);
+ PREPARE ("get_refresh_collectable",
+ "SELECT ev_sig "
+ "FROM refresh_collectable "
+ "WHERE session_pub = $1 AND newcoin_index = $2",
+ 2, NULL);
+ PREPARE ("get_refresh_melt",
+ "SELECT coin_pub "
+ "FROM refresh_melt "
+ "WHERE session_pub = $1 AND oldcoin_index = $2",
+ 2, NULL);
+ PREPARE ("insert_refresh_session",
+ "INSERT INTO refresh_sessions ( "
+ " session_pub "
+ ",noreveal_index "
+ ") "
+ "VALUES ($1, $2) ",
+ 2, NULL);
+ PREPARE ("insert_refresh_commit_link",
+ "INSERT INTO refresh_commit_link ( "
+ " session_pub "
+ ",transfer_pub "
+ ",cnc_index "
+ ",oldcoin_index "
+ ",link_secret_enc "
+ ") "
+ "VALUES ($1, $2, $3, $4, $5) ",
+ 5, NULL);
+ PREPARE ("insert_refresh_commit_coin",
+ "INSERT INTO refresh_commit_coin ( "
+ " session_pub "
+ ",coin_ev "
+ ",cnc_index "
+ ",newcoin_index "
+ ",link_vector_enc "
+ ") "
+ "VALUES ($1, $2, $3, $4, $5) ",
+ 5, NULL);
+ PREPARE ("insert_refresh_collectable",
+ "INSERT INTO refresh_collectable ( "
+ " session_pub "
+ ",newcoin_index "
+ ",ev_sig "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ PREPARE ("set_reveal_ok",
+ "UPDATE refresh_sessions "
+ "SET reveal_ok = TRUE "
+ "WHERE session_pub = $1 ",
+ 1, NULL);
+ PREPARE ("get_link",
+ "SELECT link_vector_enc, ro.denom_pub, ev_sig "
+ "FROM refresh_melt rm "
+ " JOIN refresh_order ro USING (session_pub) "
+ " JOIN refresh_commit_coin rcc USING (session_pub) "
+ " JOIN refresh_sessions rs USING (session_pub) "
+ " JOIN refresh_collectable rc USING (session_pub) "
+ "WHERE rm.coin_pub = $1 "
+ "AND ro.newcoin_index = rcc.newcoin_index "
+ "AND ro.newcoin_index = rc.newcoin_index "
+ "AND rcc.cnc_index = rs.noreveal_index % ( "
+ " SELECT count(*) FROM refresh_commit_coin rcc2 "
+ " WHERE rcc2.newcoin_index = 0 AND rcc2.session_pub = rs.session_pub "
+ " ) ",
+ 1, NULL);
+ PREPARE ("get_transfer",
+ "SELECT transfer_pub, link_secret_enc "
+ "FROM refresh_melt rm "
+ " JOIN refresh_commit_link rcl USING (session_pub) "
+ " JOIN refresh_sessions rs USING (session_pub) "
+ "WHERE rm.coin_pub = $1 "
+ "AND rm.oldcoin_index = rcl.oldcoin_index "
+ "AND rcl.cnc_index = rs.noreveal_index % ( "
+ " SELECT count(*) FROM refresh_commit_coin rcc2 "
+ " WHERE newcoin_index = 0 AND rcc2.session_pub = rm.session_pub "
+ " ) ",
+ 1, NULL);
+ PREPARE ("insert_deposit",
+ "INSERT INTO deposits ("
+ "coin_pub,"
+ "denom_pub,"
+ "denom_sig,"
+ "transaction_id,"
+ "amount_value,"
+ "amount_fraction,"
+ "amount_currency,"
+ "merchant_pub,"
+ "h_contract,"
+ "h_wire,"
+ "coin_sig,"
+ "wire"
+ ") VALUES ("
+ "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12"
+ ")",
+ 12, NULL);
+ PREPARE ("get_deposit",
+ "SELECT "
+ "coin_pub,"
+ "denom_pub,"
+ "transaction_id,"
+ "amount_value,"
+ "amount_fraction,"
+ "amount_currency,"
+ "merchant_pub,"
+ "h_contract,"
+ "h_wire,"
+ "coin_sig"
+ " FROM deposits WHERE ("
+ "(coin_pub = $1) AND"
+ "(transaction_id = $2) AND"
+ "(merchant_pub = $3)"
+ ")",
+ 3, NULL);
+ return GNUNET_OK;
+#undef PREPARE
+}
+
+
+/**
+ * Close thread-local database connection when a thread is destroyed.
+ *
+ * @param closure we get from pthreads (the db handle)
+ */
+static void
+db_conn_destroy (void *cls)
+{
+ PGconn *db_conn = cls;
+
+ if (NULL != db_conn)
+ PQfinish (db_conn);
+}
+
+
+/**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the
+ * database default one
+ * @return the database connection, or NULL on error
+ */
+static struct TALER_MINTDB_Session *
+postgres_get_connection (void *cls,
+ int temporary)
+{
+ struct PostgresClosure *pc = cls;
+ PGconn *db_conn;
+ struct TALER_MINTDB_Session *session;
+
+ if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
+ return session;
+ db_conn = PQconnectdb (pc->TALER_MINT_db_connection_cfg_str);
+ if (CONNECTION_OK !=
+ PQstatus (db_conn))
+ {
+ LOG_ERROR ("Database connection failed: %s\n",
+ PQerrorMessage (db_conn));
+ GNUNET_break (0);
+ return NULL;
+ }
+ if ((GNUNET_YES == temporary)
+ && (GNUNET_SYSERR == set_temporary_schema(db_conn)))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ postgres_prepare (db_conn))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ session = GNUNET_new (struct TALER_MINTDB_Session);
+ session->conn = db_conn;
+ if (0 != pthread_setspecific (pc->db_conn_threadlocal,
+ session))
+ {
+ GNUNET_break (0);
+ // FIXME: close db_conn!
+ GNUNET_free (session);
+ return NULL;
+ }
+ return session;
+}
+
+
+/**
+ * Start a transaction.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session the database connection
+ * @return #GNUNET_OK on success
+ */
+static int
+postgres_start (void *cls,
+ struct TALER_MINTDB_Session *session)
+{
+ PGresult *result;
+
+ result = PQexec (session->conn,
+ "BEGIN");
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ LOG_ERROR ("Failed to start transaction: %s\n",
+ PQresultErrorMessage (result));
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Roll back the current transaction of a database connection.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session the database connection
+ * @return #GNUNET_OK on success
+ */
+static void
+postgres_rollback (void *cls,
+ struct TALER_MINTDB_Session *session)
+{
+ PGresult *result;
+
+ result = PQexec (session->conn,
+ "ROLLBACK");
+ GNUNET_break (PGRES_COMMAND_OK ==
+ PQresultStatus (result));
+ PQclear (result);
+}
+
+
+/**
+ * Commit the current transaction of a database connection.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session the database connection
+ * @return #GNUNET_OK on success
+ */
+static int
+postgres_commit (void *cls,
+ struct TALER_MINTDB_Session *session)
+{
+ PGresult *result;
+
+ result = PQexec (session->conn,
+ "COMMIT");
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the summary of a reserve.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session the database connection handle
+ * @param 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
+ */
+static int
+postgres_reserve_get (void *cls,
+ struct TALER_MINTDB_Session *session,
+ struct Reserve *reserve)
+{
+ PGresult *result;
+ uint64_t expiration_date_nbo;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(reserve->pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ if (NULL == reserve->pub)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ result = TALER_DB_exec_prepared (session->conn,
+ "get_reserve",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("expiration_date", &expiration_date_nbo),
+ TALER_DB_RESULT_SPEC_END
+ };
+ EXITIF (GNUNET_OK != TALER_DB_extract_result (result, rs, 0));
+ EXITIF (GNUNET_OK !=
+ TALER_DB_extract_amount (result, 0,
+ "current_balance_value",
+ "current_balance_fraction",
+ "balance_currency",
+ &reserve->balance));
+ reserve->expiry.abs_value_us = GNUNET_ntohll (expiration_date_nbo);
+ PQclear (result);
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ PQclear (result);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Updates a reserve with the data from the given reserve structure.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session the database connection
+ * @param reserve the reserve structure whose data will be used to update the
+ * corresponding record in the database.
+ * @return #GNUNET_OK upon successful update; #GNUNET_SYSERR upon any error
+ */
+static int
+postgres_reserves_update (void *cls,
+ struct TALER_MINTDB_Session *session,
+ struct Reserve *reserve)
+{
+ PGresult *result;
+ struct TALER_AmountNBO balance_nbo;
+ struct GNUNET_TIME_AbsoluteNBO expiry_nbo;
+ int ret;
+
+ if ((NULL == reserve) || (NULL == reserve->pub))
+ return GNUNET_SYSERR;
+ ret = GNUNET_OK;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve->pub),
+ TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+ TALER_amount_hton (&balance_nbo,
+ &reserve->balance);
+ expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry);
+ result = TALER_DB_exec_prepared (session->conn,
+ "update_reserve",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ QUERY_ERR (result);
+ ret = GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return ret;
+}
+
+
+/**
+ * Insert a incoming transaction into reserves. New reserves are also created
+ * through this function.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session the database connection handle
+ * @param reserve the reserve structure. The public key of the reserve should
+ * be set here. Upon successful execution of this function, the
+ * balance and expiration of the reserve will be updated.
+ * @param balance the amount that has to be added to the reserve
+ * @param expiry the new expiration time for the reserve
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures
+ */
+static int
+postgres_reserves_in_insert (void *cls,
+ struct TALER_MINTDB_Session *session,
+ struct Reserve *reserve,
+ const struct TALER_Amount *balance,
+ const struct GNUNET_TIME_Absolute expiry)
+{
+ struct TALER_AmountNBO balance_nbo;
+ struct GNUNET_TIME_AbsoluteNBO expiry_nbo;
+ PGresult *result;
+ int reserve_exists;
+
+ result = NULL;
+ if (NULL == reserve)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != postgres_start (cls,
+ session))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ reserve_exists = postgres_reserve_get (cls,
+ session,
+ reserve);
+ if (GNUNET_SYSERR == reserve_exists)
+ {
+ postgres_rollback (cls,
+ session);
+ return GNUNET_SYSERR;
+ }
+ TALER_amount_hton (&balance_nbo,
+ balance);
+ expiry_nbo = GNUNET_TIME_absolute_hton (expiry);
+ if (GNUNET_NO == reserve_exists)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Reserve does not exist; creating a new one\n");
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve->pub),
+ TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (balance_nbo.currency,
+ TALER_DB_CURRENCY_LEN),
+ TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn,
+ "create_reserve",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ QUERY_ERR (result);
+ goto rollback;
+ }
+ }
+ if (NULL != result)
+ PQclear (result);
+ result = NULL;
+ /* create new incoming transaction */
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve->pub),
+ TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency,
+ TALER_DB_CURRENCY_LEN),
+ TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn,
+ "create_reserves_in_transaction",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus(result))
+ {
+ QUERY_ERR (result);
+ goto rollback;
+ }
+ PQclear (result);
+ result = NULL;
+ if (GNUNET_NO == reserve_exists)
+ {
+ if (GNUNET_OK != postgres_commit (cls,
+ session))
+ return GNUNET_SYSERR;
+ reserve->balance = *balance;
+ reserve->expiry = expiry;
+ return GNUNET_OK;
+ }
+ /* Update reserve */
+ struct Reserve updated_reserve;
+ updated_reserve.pub = reserve->pub;
+
+ if (GNUNET_OK !=
+ TALER_amount_add (&updated_reserve.balance,
+ &reserve->balance,
+ balance))
+ {
+ return GNUNET_SYSERR;
+ }
+ updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry);
+ if (GNUNET_OK != postgres_reserves_update (cls,
+ session,
+ &updated_reserve))
+ goto rollback;
+ if (GNUNET_OK != postgres_commit (cls,
+ session))
+ return GNUNET_SYSERR;
+ reserve->balance = updated_reserve.balance;
+ reserve->expiry = updated_reserve.expiry;
+ return GNUNET_OK;
+
+ rollback:
+ PQclear (result);
+ postgres_rollback (cls,
+ session);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Locate the response for a /withdraw request under the
+ * key of the hash of the blinded message.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection to use
+ * @param h_blind hash of the blinded message
+ * @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
+ */
+static int
+postgres_get_collectable_blindcoin (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *h_blind,
+ struct CollectableBlindcoin *collectable)
+{
+ PGresult *result;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (h_blind),
+ TALER_DB_QUERY_PARAM_END
+ };
+ struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
+ struct GNUNET_CRYPTO_rsa_Signature *denom_sig;
+ char *denom_pub_enc;
+ char *denom_sig_enc;
+ size_t denom_pub_enc_size;
+ size_t denom_sig_enc_size;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ denom_pub = NULL;
+ denom_pub_enc = NULL;
+ denom_sig_enc = NULL;
+ result = TALER_DB_exec_prepared (session->conn,
+ "get_collectable_blindcoin",
+ params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ goto cleanup;
+ }
+ if (0 == PQntuples (result))
+ {
+ ret = GNUNET_NO;
+ goto cleanup;
+ }
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC_VAR("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
+ TALER_DB_RESULT_SPEC_VAR("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
+ TALER_DB_RESULT_SPEC("reserve_sig", &collectable->reserve_sig),
+ TALER_DB_RESULT_SPEC("reserve_pub", &collectable->reserve_pub),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc,
+ denom_pub_enc_size);
+ denom_sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc,
+ denom_sig_enc_size);
+ if ((NULL == denom_pub) || (NULL == denom_sig))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ collectable->denom_pub = denom_pub;
+ collectable->sig = denom_sig;
+ ret = GNUNET_YES;
+
+ cleanup:
+ PQclear (result);
+ GNUNET_free_non_null (denom_pub_enc);
+ GNUNET_free_non_null (denom_sig_enc);
+ if (GNUNET_YES != ret)
+ { if (NULL != denom_pub)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pub);
+ if (NULL != denom_sig)
+ GNUNET_CRYPTO_rsa_signature_free (denom_sig);
+ }
+ return ret;
+}
+
+
+/**
+ * Store collectable bit coin under the corresponding
+ * hash of the blinded message.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection to use
+ * @param h_blind hash of the blinded message
+ * @param withdraw amount by which the reserve will be withdrawn with this
+ * transaction
+ * @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
+ */
+static int
+postgres_insert_collectable_blindcoin (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *h_blind,
+ struct TALER_Amount withdraw,
+ const struct CollectableBlindcoin *collectable)
+{
+ PGresult *result;
+ struct Reserve reserve;
+ char *denom_pub_enc = NULL;
+ char *denom_sig_enc = NULL;
+ size_t denom_pub_enc_size;
+ size_t denom_sig_enc_size;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ denom_pub_enc_size =
+ GNUNET_CRYPTO_rsa_public_key_encode (collectable->denom_pub,
+ &denom_pub_enc);
+ denom_sig_enc_size =
+ GNUNET_CRYPTO_rsa_signature_encode (collectable->sig,
+ &denom_sig_enc);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (h_blind),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size - 1), /* DB doesn't like the trailing \0 */
+ TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_pub),
+ TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig),
+ TALER_DB_QUERY_PARAM_END
+ };
+ if (GNUNET_OK != postgres_start (cls,
+ session))
+ goto cleanup;
+ result = TALER_DB_exec_prepared (session->conn,
+ "insert_collectable_blindcoin",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ goto rollback;
+ }
+ reserve.pub = (struct GNUNET_CRYPTO_EddsaPublicKey *)
+ &collectable->reserve_pub;
+ if (GNUNET_OK != postgres_reserve_get (cls,
+ session,
+ &reserve))
+ goto rollback;
+ if (GNUNET_SYSERR ==
+ TALER_amount_subtract (&reserve.balance,
+ &reserve.balance,
+ &withdraw))
+ goto rollback;
+ if (GNUNET_OK != postgres_reserves_update (cls,
+ session,
+ &reserve))
+ goto rollback;
+ if (GNUNET_OK == postgres_commit (cls,
+ session))
+ {
+ ret = GNUNET_OK;
+ goto cleanup;
+ }
+
+ rollback:
+ postgres_rollback (cls,
+ session);
+ cleanup:
+ PQclear (result);
+ GNUNET_free_non_null (denom_pub_enc);
+ GNUNET_free_non_null (denom_sig_enc);
+ return ret;
+}
+
+
+/**
+ * Get all of the transaction history associated with the specified
+ * reserve.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session connection to use
+ * @param reserve_pub public key of the reserve
+ * @return known transaction history (NULL if reserve is unknown)
+ */
+static struct ReserveHistory *
+postgres_get_reserve_history (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub)
+{
+ PGresult *result;
+ struct ReserveHistory *rh;
+ struct ReserveHistory *rh_head;
+ int rows;
+ int ret;
+
+ result = NULL;
+ rh = NULL;
+ rh_head = NULL;
+ ret = GNUNET_SYSERR;
+ {
+ struct BankTransfer *bt;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ result = TALER_DB_exec_prepared (session->conn,
+ "get_reserves_in_transactions",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ goto cleanup;
+ }
+ if (0 == (rows = PQntuples (result)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asked to fetch history for an unknown reserve.\n");
+ goto cleanup;
+ }
+ while (0 < rows)
+ {
+ bt = GNUNET_new (struct BankTransfer);
+ if (GNUNET_OK != TALER_DB_extract_amount (result,
+ --rows,
+ "balance_value",
+ "balance_fraction",
+ "balance_currency",
+ &bt->amount))
+ {
+ GNUNET_free (bt);
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ (void) memcpy (&bt->reserve_pub, reserve_pub, sizeof (bt->reserve_pub));
+ if (NULL != rh_head)
+ {
+ rh_head->next = GNUNET_new (struct ReserveHistory);
+ rh_head = rh_head->next;
+ }
+ else
+ {
+ rh_head = GNUNET_new (struct ReserveHistory);
+ rh = rh_head;
+ }
+ rh_head->type = TALER_MINT_DB_RO_BANK_TO_MINT;
+ rh_head->details.bank = bt;
+ }
+ }
+ PQclear (result);
+ result = NULL;
+ {
+ struct GNUNET_HashCode blind_ev;
+ struct GNUNET_CRYPTO_EddsaSignature reserve_sig;
+ struct CollectableBlindcoin *cbc;
+ char *denom_pub_enc;
+ char *denom_sig_enc;
+ size_t denom_pub_enc_size;
+ size_t denom_sig_enc_size;
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (reserve_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn,
+ "get_reserves_blindcoins",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ goto cleanup;
+ }
+ if (0 == (rows = PQntuples (result)))
+ {
+ ret = GNUNET_OK; /* Its OK if there are no withdrawls yet */
+ goto cleanup;
+ }
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC ("blind_ev", &blind_ev),
+ TALER_DB_RESULT_SPEC_VAR ("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
+ TALER_DB_RESULT_SPEC_VAR ("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
+ TALER_DB_RESULT_SPEC ("reserve_sig", &reserve_sig),
+ TALER_DB_RESULT_SPEC_END
+ };
+ GNUNET_assert (NULL != rh);
+ GNUNET_assert (NULL != rh_head);
+ GNUNET_assert (NULL == rh_head->next);
+ while (0 < rows)
+ {
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, --rows))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ cbc = GNUNET_new (struct CollectableBlindcoin);
+ cbc->sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc,
+ denom_sig_enc_size);
+ GNUNET_free (denom_sig_enc);
+ denom_sig_enc = NULL;
+ cbc->denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc,
+ denom_pub_enc_size);
+ GNUNET_free (denom_pub_enc);
+ denom_pub_enc = NULL;
+ if ((NULL == cbc->sig) || (NULL == cbc->denom_pub))
+ {
+ if (NULL != cbc->sig)
+ GNUNET_CRYPTO_rsa_signature_free (cbc->sig);
+ if (NULL != cbc->denom_pub)
+ GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub);
+ GNUNET_free (cbc);
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ (void) memcpy (&cbc->h_coin_envelope, &blind_ev, sizeof (blind_ev));
+ (void) memcpy (&cbc->reserve_pub, reserve_pub, sizeof (cbc->reserve_pub));
+ (void) memcpy (&cbc->reserve_sig, &reserve_sig, sizeof (cbc->reserve_sig));
+ rh_head->next = GNUNET_new (struct ReserveHistory);
+ rh_head = rh_head->next;
+ rh_head->type = TALER_MINT_DB_RO_WITHDRAW_COIN;
+ rh_head->details.withdraw = cbc;
+ }
+ }
+ ret = GNUNET_OK;
+
+ cleanup:
+ if (NULL != result)
+ PQclear (result);
+ if (GNUNET_SYSERR == ret)
+ {
+ TALER_MINT_DB_free_reserve_history (rh);
+ rh = NULL;
+ }
+ return rh;
+}
+
+
+/**
+ * Check if we have the specified deposit already in the database.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session 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
+ */
+static int
+postgres_have_deposit (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct Deposit *deposit)
+{
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+ PGresult *result;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ result = TALER_DB_exec_prepared (session->conn,
+ "get_deposit",
+ params);
+ if (PGRES_TUPLES_OK !=
+ PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ goto cleanup;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ ret = GNUNET_NO;
+ goto cleanup;
+ }
+ ret = GNUNET_YES;
+
+ cleanup:
+ PQclear (result);
+ return ret;
+}
+
+
+/**
+ * Insert information about deposited coin into the
+ * database.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session connection to the database
+ * @param deposit deposit information to store
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+postgres_insert_deposit (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct Deposit *deposit)
+{
+ char *denom_pub_enc;
+ char *denom_sig_enc;
+ char *json_wire_enc;
+ PGresult *result;
+ struct TALER_AmountNBO amount_nbo;
+ size_t denom_pub_enc_size;
+ size_t denom_sig_enc_size;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ denom_pub_enc_size =
+ GNUNET_CRYPTO_rsa_public_key_encode (deposit->coin.denom_pub,
+ &denom_pub_enc);
+ denom_sig_enc_size =
+ GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig,
+ &denom_sig_enc);
+ json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT);
+ TALER_amount_hton (&amount_nbo,
+ &deposit->amount);
+ struct TALER_DB_QueryParam params[]= {
+ TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id),
+ TALER_DB_QUERY_PARAM_PTR (&amount_nbo.value),
+ TALER_DB_QUERY_PARAM_PTR (&amount_nbo.fraction),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (amount_nbo.currency,
+ TALER_CURRENCY_LEN - 1),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire),
+ TALER_DB_QUERY_PARAM_PTR (&deposit->csig),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (json_wire_enc,
+ strlen (json_wire_enc)),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn, "insert_deposit", params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ goto cleanup;
+ }
+ ret = GNUNET_OK;
+
+ cleanup:
+ PQclear (result);
+ GNUNET_free_non_null (denom_pub_enc);
+ GNUNET_free_non_null (denom_sig_enc);
+ GNUNET_free_non_null (json_wire_enc);
+ return ret;
+}
+
+
+/**
+ * Lookup refresh session data under the given public key.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database handle to use
+ * @param refresh_session_pub public key to use for the lookup
+ * @param refresh_session[OUT] where to store the result
+ * @return #GNUNET_YES on success,
+ * #GNUNET_NO if not found,
+ * #GNUNET_SYSERR on DB failure
+ */
+static int
+postgres_get_refresh_session (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ struct RefreshSession *refresh_session)
+{
+ // FIXME: check logic!
+ int res;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn,
+ "get_refresh_session",
+ params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Query failed: %s\n",
+ PQresultErrorMessage (result));
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ return GNUNET_NO;
+
+ GNUNET_assert (1 == PQntuples (result));
+
+ /* We're done if the caller is only interested in
+ * whether the session exists or not */
+
+ if (NULL == refresh_session)
+ return GNUNET_YES;
+
+ memset (session, 0, sizeof (struct RefreshSession));
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins),
+ TALER_DB_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins),
+ TALER_DB_RESULT_SPEC("kappa", &refresh_session->kappa),
+ TALER_DB_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ res = TALER_DB_extract_result (result, rs, 0);
+
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ refresh_session->num_oldcoins = ntohs (refresh_session->num_oldcoins);
+ refresh_session->num_newcoins = ntohs (refresh_session->num_newcoins);
+ refresh_session->kappa = ntohs (refresh_session->kappa);
+ refresh_session->noreveal_index = ntohs (refresh_session->noreveal_index);
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Store new refresh session data under the given public key.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database handle to use
+ * @param refresh_session_pub public key to use to locate the session
+ * @param refresh_session session data to store
+ * @return #GNUNET_YES on success,
+ * #GNUNET_SYSERR on DB failure
+ */
+static int
+postgres_create_refresh_session (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct RefreshSession *refresh_session)
+{
+ // FIXME: actually store session data!
+ uint16_t noreveal_index;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&noreveal_index),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);
+ noreveal_index = htonl (noreveal_index);
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn,
+ "insert_refresh_session",
+ params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Store the given /refresh/melt request in the database.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param refresh_session session key of the melt operation
+ * @param oldcoin_index index of the coin to store
+ * @param melt melt operation
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+postgres_insert_refresh_melt (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session,
+ uint16_t oldcoin_index,
+ const struct RefreshMelt *melt)
+{
+ // FIXME: check logic!
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+ char *buf;
+ size_t buf_size;
+ PGresult *result;
+
+ buf_size = GNUNET_CRYPTO_rsa_public_key_encode (melt->coin.denom_pub,
+ &buf);
+ {
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&melt->coin.coin_pub),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn,
+ "insert_refresh_melt",
+ params);
+ }
+ GNUNET_free (buf);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get information about melted coin details from the database.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param refresh_session session key of the melt operation
+ * @param oldcoin_index index of the coin to retrieve
+ * @param melt melt data to fill in
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+postgres_get_refresh_melt (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session,
+ uint16_t oldcoin_index,
+ struct RefreshMelt *melt)
+{
+ // FIXME: check logic!
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Store in the database which coin(s) we want to create
+ * in a given refresh operation.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param session_pub refresh session key
+ * @param newcoin_index index of the coin to generate
+ * @param denom_pub denomination of the coin to create
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+postgres_insert_refresh_order (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub)
+{
+ // FIXME: check logic
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ char *buf;
+ size_t buf_size;
+ PGresult *result;
+
+ buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub,
+ &buf);
+
+ {
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR (&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR (session_pub),
+ TALER_DB_QUERY_PARAM_PTR_SIZED (buf, buf_size),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn,
+ "insert_refresh_order",
+ params);
+ }
+ GNUNET_free (buf);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Lookup in the database the @a newcoin_index coin that we want to
+ * create in the given refresh operation.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param session_pub refresh session key
+ * @param newcoin_index index of the coin to generate
+ * @param denom_pub denomination of the coin to create
+ * @return NULL on error (not found or internal error)
+ */
+static struct GNUNET_CRYPTO_rsa_PublicKey *
+postgres_get_refresh_order (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t newcoin_index)
+{
+ // FIXME: check logic
+ char *buf;
+ size_t buf_size;
+ struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn, "get_refresh_order", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return NULL;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ /* FIXME: may want to distinguish between different error cases! */
+ return NULL;
+ }
+ GNUNET_assert (1 == PQntuples (result));
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC_VAR ("denom_pub", &buf, &buf_size),
+ TALER_DB_RESULT_SPEC_END
+ };
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return NULL;
+ }
+ PQclear (result);
+ denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size);
+ GNUNET_free (buf);
+ return denom_pub;
+}
+
+
+
+/**
+ * Store information about the commitment of the
+ * given coin for the given refresh session in the database.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection to use
+ * @param refresh_session_pub refresh session this commitment belongs to
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
+ * @param commit_coin coin commitment to store
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on error
+ */
+static int
+postgres_insert_refresh_commit_coin (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int i,
+ unsigned int j,
+ const struct RefreshCommitCoin *commit_coin)
+{
+ // FIXME: check logic!
+ uint16_t cnc_index_nbo = htons (i);
+ uint16_t newcoin_index_nbo = htons (j);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coin->coin_ev, commit_coin->coin_ev_size),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coin->refresh_link->coin_priv_enc,
+ commit_coin->refresh_link->blinding_key_enc_size +
+ sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn, "insert_refresh_commit_coin", params);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Obtain information about the commitment of the
+ * given coin of the given refresh session from the database.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection to use
+ * @param refresh_session_pub refresh session the commitment belongs to
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
+ * @param commit_coin[OUT] coin commitment to return
+ * @return #GNUNET_OK on success
+ * #GNUNET_NO if not found
+ * #GNUNET_SYSERR on error
+ */
+static int
+postgres_get_refresh_commit_coin (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int cnc_index,
+ unsigned int newcoin_index,
+ struct RefreshCommitCoin *cc)
+{
+ // FIXME: check logic!
+ uint16_t cnc_index_nbo = htons (cnc_index);
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+ char *c_buf;
+ size_t c_buf_size;
+ char *rl_buf;
+ size_t rl_buf_size;
+ struct TALER_RefreshLinkEncrypted *rl;
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn, "get_refresh_commit_coin", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC_VAR("coin_ev", &c_buf, &c_buf_size),
+ TALER_DB_RESULT_SPEC_VAR("link_vector_enc", &rl_buf, &rl_buf_size),
+ TALER_DB_RESULT_SPEC_END
+ };
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ if (rl_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
+ {
+ GNUNET_free (c_buf);
+ GNUNET_free (rl_buf);
+ return GNUNET_SYSERR;
+ }
+ rl = TALER_refresh_link_encrypted_decode (rl_buf,
+ rl_buf_size);
+ GNUNET_free (rl_buf);
+ cc->refresh_link = rl;
+ cc->coin_ev = c_buf;
+ cc->coin_ev_size = c_buf_size;
+ return GNUNET_YES;
+}
+
+
+/**
+ * Store the commitment to the given (encrypted) refresh link data
+ * for the given refresh session.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection to use
+ * @param refresh_session_pub public key of the refresh session this
+ * commitment belongs with
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to melted (old) coins
+ * @param commit_link link information to store
+ * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success
+ */
+static int
+postgres_insert_refresh_commit_link (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int i,
+ unsigned int j,
+ const struct RefreshCommitLink *commit_link)
+{
+ // FIXME: check logic!
+ uint16_t cnc_index_nbo = htons (i);
+ uint16_t oldcoin_index_nbo = htons (j);
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&commit_link->transfer_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&commit_link->shared_secret_enc),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn,
+ "insert_refresh_commit_link",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 != strcmp ("1", PQcmdTuples (result)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Obtain the commited (encrypted) refresh link data
+ * for the given refresh session.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection to use
+ * @param refresh_session_pub public key of the refresh session this
+ * commitment belongs with
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to melted (old) coins
+ * @param cc[OUT] link information to return
+ * @return #GNUNET_SYSERR on internal error,
+ * #GNUNET_NO if commitment was not found
+ * #GNUNET_OK on success
+ */
+static int
+postgres_get_refresh_commit_link (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int cnc_index,
+ unsigned int oldcoin_index,
+ struct RefreshCommitLink *cc)
+{
+ // FIXME: check logic!
+ uint16_t cnc_index_nbo = htons (cnc_index);
+ uint16_t oldcoin_index_nbo = htons (oldcoin_index);
+
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(refresh_session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn,
+ "get_refresh_commit_link",
+ params);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("transfer_pub", &cc->transfer_pub),
+ TALER_DB_RESULT_SPEC("link_secret_enc", &cc->shared_secret_enc),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_free (cc);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * 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 `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param session_pub refresh session
+ * @param newcoin_index coin index
+ * @param ev_sig coin signature
+ * @return #GNUNET_OK on success
+ */
+static int
+postgres_insert_refresh_collectable (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_rsa_Signature *ev_sig)
+{
+ // FIXME: check logic!
+ uint16_t newcoin_index_nbo = htons (newcoin_index);
+ char *buf;
+ size_t buf_size;
+ PGresult *result;
+
+ buf_size = GNUNET_CRYPTO_rsa_signature_encode (ev_sig,
+ &buf);
+ {
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(session_pub),
+ TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size),
+ TALER_DB_QUERY_PARAM_END
+ };
+ result = TALER_DB_exec_prepared (session->conn,
+ "insert_refresh_collectable",
+ params);
+ }
+ GNUNET_free (buf);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Obtain the link data of a coin, that is the encrypted link
+ * information, the denomination keys and the signatures.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param coin_pub public key to use to retrieve linkage data
+ * @return all known link data for the coin
+ */
+static struct LinkDataList *
+postgres_get_link (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
+{
+ // FIXME: check logic!
+ struct LinkDataList *ldl;
+ struct LinkDataList *pos;
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+ PGresult *result = TALER_DB_exec_prepared (session->conn, "get_link", params);
+
+ ldl = NULL;
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return NULL;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return NULL;
+ }
+
+
+ int i = 0;
+
+ for (i = 0; i < PQntuples (result); i++)
+ {
+ struct TALER_RefreshLinkEncrypted *link_enc;
+ struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
+ struct GNUNET_CRYPTO_rsa_Signature *sig;
+ char *ld_buf;
+ size_t ld_buf_size;
+ char *pk_buf;
+ size_t pk_buf_size;
+ char *sig_buf;
+ size_t sig_buf_size;
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC_VAR("link_vector_enc", &ld_buf, &ld_buf_size),
+ TALER_DB_RESULT_SPEC_VAR("denom_pub", &pk_buf, &pk_buf_size),
+ TALER_DB_RESULT_SPEC_VAR("ev_sig", &sig_buf, &sig_buf_size),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, i))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ TALER_db_link_data_list_free (ldl);
+ return NULL;
+ }
+ if (ld_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))
+ {
+ PQclear (result);
+ GNUNET_free (pk_buf);
+ GNUNET_free (sig_buf);
+ GNUNET_free (ld_buf);
+ TALER_db_link_data_list_free (ldl);
+ return NULL;
+ }
+ // FIXME: use util API for this!
+ link_enc = GNUNET_malloc (sizeof (struct TALER_RefreshLinkEncrypted) +
+ ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
+ link_enc->blinding_key_enc = (const char *) &link_enc[1];
+ link_enc->blinding_key_enc_size = ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
+ memcpy (link_enc->coin_priv_enc,
+ ld_buf,
+ ld_buf_size);
+
+ sig = GNUNET_CRYPTO_rsa_signature_decode (sig_buf,
+ sig_buf_size);
+ denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (pk_buf,
+ pk_buf_size);
+ GNUNET_free (pk_buf);
+ GNUNET_free (sig_buf);
+ GNUNET_free (ld_buf);
+ if ( (NULL == sig) ||
+ (NULL == denom_pub) )
+ {
+ if (NULL != denom_pub)
+ GNUNET_CRYPTO_rsa_public_key_free (denom_pub);
+ if (NULL != sig)
+ GNUNET_CRYPTO_rsa_signature_free (sig);
+ GNUNET_free (link_enc);
+ GNUNET_break (0);
+ PQclear (result);
+ TALER_db_link_data_list_free (ldl);
+ return NULL;
+ }
+ pos = GNUNET_new (struct LinkDataList);
+ pos->next = ldl;
+ pos->link_data_enc = link_enc;
+ pos->denom_pub = denom_pub;
+ pos->ev_sig = sig;
+ ldl = pos;
+ }
+ return ldl;
+}
+
+
+/**
+ * Obtain shared secret and transfer public key from the public key of
+ * the coin. This information and the link information returned by
+ * #TALER_db_get_link() enable the owner of an old coin to determine
+ * the private keys of the new coins after the melt.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param coin_pub public key of the coin
+ * @param transfer_pub[OUT] public transfer key
+ * @param shared_secret_enc[OUT] set to shared secret
+ * @return #GNUNET_OK on success,
+ * #GNUNET_NO on failure (not found)
+ * #GNUNET_SYSERR on internal failure (database issue)
+ */
+static int
+postgres_get_transfer (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
+ struct TALER_EncryptedLinkSecret *shared_secret_enc)
+{
+ // FIXME: check logic!
+ struct TALER_DB_QueryParam params[] = {
+ TALER_DB_QUERY_PARAM_PTR(coin_pub),
+ TALER_DB_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_DB_exec_prepared (session->conn, "get_transfer", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ return GNUNET_NO;
+ }
+
+ if (1 != PQntuples (result))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "got %d tuples for get_transfer\n",
+ PQntuples (result));
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ struct TALER_DB_ResultSpec rs[] = {
+ TALER_DB_RESULT_SPEC("transfer_pub", transfer_pub),
+ TALER_DB_RESULT_SPEC("link_secret_enc", shared_secret_enc),
+ TALER_DB_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Compile a list of all (historic) transactions performed
+ * with the given coin (/refresh/melt and /deposit operations).
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database connection
+ * @param coin_pub coin to investigate
+ * @return list of transactions, NULL if coin is fresh
+ */
+static struct TALER_MINT_DB_TransactionList *
+postgres_get_coin_transactions (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
+{
+ // FIXME: check logic!
+ GNUNET_break (0); // FIXME: implement!
+ return NULL;
+}
+
+
+
+/**
+ * Initialize Postgres database subsystem.
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_MINTDB_Plugin`
+ */
+void *
+libtaler_plugin_mintdb_postgres_init (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct PostgresClosure *pg;
+ struct TALER_MINTDB_Plugin *plugin;
+
+ pg = GNUNET_new (struct PostgresClosure);
+
+ if (0 != pthread_key_create (&pg->db_conn_threadlocal,
+ &db_conn_destroy))
+ {
+ LOG_ERROR ("Cannnot create pthread key.\n");
+ return NULL;
+ }
+ /* FIXME: use configuration section with "postgres" in its name... */
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint", "db",
+ &pg->TALER_MINT_db_connection_cfg_str))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "db");
+ return NULL;
+ }
+ plugin = GNUNET_new (struct TALER_MINTDB_Plugin);
+ plugin->cls = pg;
+
+ return plugin;
+}
+
+
+/**
+ * Shutdown Postgres database subsystem.
+ *
+ * @param cls a `struct TALER_MINTDB_Plugin`
+ * @return NULL (always)
+ */
+void *
+libtaler_plugin_mintdb_postgres_done (void *cls)
+{
+ struct TALER_MINTDB_Plugin *plugin = cls;
+ struct PostgresClosure *pg = plugin->cls;
+
+ GNUNET_free (pg);
+ GNUNET_free (plugin);
+ return NULL;
+}
+
+/* end of plugin_mintdb_postgres.c */
diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c
@@ -22,7 +22,7 @@
#include <gnunet/gnunet_util_lib.h>
#include <libpq-fe.h>
#include "taler_util.h"
-#include "mint_db.h"
+#include "taler_mintdb_plugin.h"
/**
diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c
@@ -35,7 +35,7 @@
#include "taler-mint-httpd_withdraw.h"
#include "taler-mint-httpd_refresh.h"
#include "taler-mint-httpd_keystate.h"
-#include "mint_db.h"
+#include "taler_mintdb_plugin.h"
/**
@@ -260,16 +260,7 @@ mint_serve_process_config (const char *mint_directory)
GNUNET_free (master_pub_str);
if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint", "db",
- &db_cfg))
- {
- fprintf (stderr,
- "invalid configuration: mint.db\n");
- return GNUNET_NO;
- }
- if (GNUNET_OK !=
- TALER_MINT_DB_init (db_cfg))
+ TALER_MINT_plugin_load (cfg))
{
fprintf (stderr,
"failed to initialize DB subsystem\n");
diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c
@@ -29,9 +29,9 @@
#include "taler-mint-httpd_db.h"
#include "taler_signatures.h"
#include "taler-mint-httpd_responses.h"
-#include "mint_db.h"
#include "taler_util.h"
#include "taler-mint-httpd_keystate.h"
+#include "plugin.h"
/**
@@ -48,7 +48,7 @@ int
TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
const struct Deposit *deposit)
{
- PGconn *db_conn;
+ struct TALER_MINTDB_Session *session;
struct TALER_MINT_DB_TransactionList *tl;
struct TALER_MINT_DB_TransactionList *pos;
struct TALER_Amount spent;
@@ -59,14 +59,16 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
struct TALER_MINT_DenomKeyIssuePriv *dki;
int ret;
- if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
+ if (NULL == (session = plugin->get_session (plugin->cls,
+ GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
if (GNUNET_YES ==
- TALER_MINT_DB_have_deposit (db_conn,
- deposit))
+ plugin->have_deposit (plugin->cls,
+ session,
+ deposit))
{
return TALER_MINT_reply_deposit_success (connection,
&deposit->coin.coin_pub,
@@ -88,13 +90,15 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
TALER_MINT_key_state_release (mks);
if (GNUNET_OK !=
- TALER_MINT_DB_transaction (db_conn))
+ plugin->start (plugin->cls,
+ session))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
- tl = TALER_MINT_DB_get_coin_transactions (db_conn,
- &deposit->coin.coin_pub);
+ tl = plugin->get_coin_transactions (plugin->cls,
+ session,
+ &deposit->coin.coin_pub);
spent = fee_deposit; /* fee for THIS transaction */
if (GNUNET_OK !=
TALER_amount_add (&spent,
@@ -155,7 +159,8 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
if (0 < TALER_amount_cmp (&spent,
&value))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
ret = TALER_MINT_reply_deposit_insufficient_funds (connection,
tl);
TALER_MINT_DB_free_coin_transaction_list (tl);
@@ -164,16 +169,19 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
TALER_MINT_DB_free_coin_transaction_list (tl);
if (GNUNET_OK !=
- TALER_MINT_DB_insert_deposit (db_conn,
- deposit))
+ plugin->insert_deposit (plugin->cls,
+ session,
+ deposit))
{
LOG_WARNING ("Failed to store /deposit information in database\n");
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- TALER_MINT_DB_commit (db_conn))
+ plugin->commit (plugin->cls,
+ session))
{
LOG_WARNING ("/deposit transaction commit failed\n");
return TALER_MINT_reply_commit_error (connection);
@@ -200,17 +208,19 @@ int
TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection,
const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub)
{
- PGconn *db_conn;
+ struct TALER_MINTDB_Session *session;
struct ReserveHistory *rh;
int res;
- if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
+ if (NULL == (session = plugin->get_session (plugin->cls,
+ GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
- rh = TALER_MINT_DB_get_reserve_history (db_conn,
- reserve_pub);
+ rh = plugin->get_reserve_history (plugin->cls,
+ session,
+ reserve_pub);
if (NULL == rh)
return TALER_MINT_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
@@ -245,7 +255,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
size_t blinded_msg_len,
const struct GNUNET_CRYPTO_EddsaSignature *signature)
{
- PGconn *db_conn;
+ struct TALER_MINTDB_Session *session;
struct ReserveHistory *rh;
const struct ReserveHistory *pos;
struct MintKeyState *key_state;
@@ -266,14 +276,16 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
blinded_msg_len,
&h_blind);
- if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
+ if (NULL == (session = plugin->get_session (plugin->cls,
+ GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
- res = TALER_MINT_DB_get_collectable_blindcoin (db_conn,
- &h_blind,
- &collectable);
+ res = plugin->get_collectable_blindcoin (plugin->cls,
+ session,
+ &h_blind,
+ &collectable);
if (GNUNET_SYSERR == res)
{
GNUNET_break (0);
@@ -305,18 +317,21 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
"Denomination not found");
}
if (GNUNET_OK !=
- TALER_MINT_DB_transaction (db_conn))
+ plugin->start (plugin->cls,
+ session))
{
GNUNET_break (0);
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_internal_db_error (connection);
}
- rh = TALER_MINT_DB_get_reserve_history (db_conn,
- reserve);
+ rh = plugin->get_reserve_history (plugin->cls,
+ session,
+ reserve);
if (NULL == rh)
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
@@ -336,7 +351,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
&value,
&fee_withdraw))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_internal_db_error (connection);
}
@@ -356,7 +372,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
&deposit_total,
&pos->details.bank->amount))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_internal_db_error (connection);
}
@@ -375,7 +392,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
&withdraw_total,
&value))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
TALER_MINT_key_state_release (key_state);
return TALER_MINT_reply_internal_db_error (connection);
}
@@ -392,7 +410,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
&balance))
{
TALER_MINT_key_state_release (key_state);
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
res = TALER_MINT_reply_withdraw_sign_insufficient_funds (connection,
rh);
TALER_MINT_DB_free_reserve_history (rh);
@@ -408,7 +427,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
if (NULL == sig)
{
GNUNET_break (0);
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_error (connection,
"Internal error");
}
@@ -420,18 +440,21 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
&collectable.h_coin_envelope);
collectable.reserve_sig = *signature;
if (GNUNET_OK !=
- TALER_MINT_DB_insert_collectable_blindcoin (db_conn,
- &h_blind,
- amount_required,
- &collectable))
+ plugin->insert_collectable_blindcoin (plugin->cls,
+ session,
+ &h_blind,
+ amount_required,
+ &collectable))
{
GNUNET_break (0);
GNUNET_CRYPTO_rsa_signature_free (sig);
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- TALER_MINT_DB_commit (db_conn))
+ plugin->commit (plugin->cls,
+ session))
{
LOG_WARNING ("/withdraw/sign transaction commit failed\n");
return TALER_MINT_reply_commit_error (connection);
@@ -448,7 +471,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
* the database.
*
* @param connection the connection to send errors to
- * @param db_conn the database connection
+ * @param session the database connection
* @param key_state the mint's key state
* @param session_pub the refresh session's public key
* @param coin_public_info the coin to melt
@@ -460,7 +483,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
*/
static int
refresh_accept_melts (struct MHD_Connection *connection,
- PGconn *db_conn,
+ struct TALER_MINTDB_Session *session,
const struct MintKeyState *key_state,
const struct GNUNET_HashCode *melt_hash,
const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
@@ -489,8 +512,9 @@ refresh_accept_melts (struct MHD_Connection *connection,
TALER_amount_ntoh (&coin_value,
&dki->value);
- tl = TALER_MINT_DB_get_coin_transactions (db_conn,
- &coin_public_info->coin_pub);
+ tl = plugin->get_coin_transactions (plugin->cls,
+ session,
+ &coin_public_info->coin_pub);
/* FIXME: #3636: compute how much value is left with this coin and
compare to `expected_value`! (subtract from "coin_value") */
coin_residual = coin_value;
@@ -518,10 +542,11 @@ refresh_accept_melts (struct MHD_Connection *connection,
melt.melt_hash = *melt_hash;
melt.amount = coin_details->melt_amount;
if (GNUNET_OK !=
- TALER_MINT_DB_insert_refresh_melt (db_conn,
- session_pub,
- oldcoin_index,
- &melt))
+ plugin->insert_refresh_melt (plugin->cls,
+ session,
+ session_pub,
+ oldcoin_index,
+ &melt))
{
GNUNET_break (0);
return GNUNET_SYSERR;
@@ -570,37 +595,42 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
struct RefreshCommitLink *const* commit_link)
{
struct MintKeyState *key_state;
- struct RefreshSession session;
- PGconn *db_conn;
+ struct RefreshSession refresh_session;
+ struct TALER_MINTDB_Session *session;
int res;
unsigned int i;
unsigned int j;
- if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
+ if (NULL == (session = plugin->get_session (plugin->cls,
+ GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- TALER_MINT_DB_transaction (db_conn))
+ plugin->start (plugin->cls,
+ session))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
- res = TALER_MINT_DB_get_refresh_session (db_conn,
- refresh_session_pub,
- &session);
+ res = plugin->get_refresh_session (plugin->cls,
+ session,
+ refresh_session_pub,
+ &refresh_session);
if (GNUNET_YES == res)
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
res = TALER_MINT_reply_refresh_melt_success (connection,
- &session.session_hash,
- session.noreveal_index);
+ &refresh_session.session_hash,
+ refresh_session.noreveal_index);
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
if (GNUNET_SYSERR == res)
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
@@ -610,7 +640,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
{
if (GNUNET_OK !=
(res = refresh_accept_melts (connection,
- db_conn,
+ session,
key_state,
melt_hash,
refresh_session_pub,
@@ -619,7 +649,8 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
i)))
{
TALER_MINT_key_state_release (key_state);
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
}
@@ -629,12 +660,14 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
for (i=0;i<num_new_denoms;i++)
{
if (GNUNET_OK !=
- TALER_MINT_DB_insert_refresh_order (db_conn,
- refresh_session_pub,
- i,
- denom_pubs[i]))
+ plugin->insert_refresh_order (plugin->cls,
+ session,
+ refresh_session_pub,
+ i,
+ denom_pubs[i]))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
}
@@ -644,13 +677,15 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
for (j = 0; j < num_new_denoms; j++)
{
if (GNUNET_OK !=
- TALER_MINT_DB_insert_refresh_commit_coin (db_conn,
- refresh_session_pub,
- i,
- j,
- &commit_coin[i][j]))
+ plugin->insert_refresh_commit_coin (plugin->cls,
+ session,
+ refresh_session_pub,
+ i,
+ j,
+ &commit_coin[i][j]))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
}
@@ -660,13 +695,15 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
for (j = 0; j < coin_count; j++)
{
if (GNUNET_OK !=
- TALER_MINT_DB_insert_refresh_commit_link (db_conn,
- refresh_session_pub,
- i,
- j,
- &commit_link[i][j]))
+ plugin->insert_refresh_commit_link (plugin->cls,
+ session,
+ refresh_session_pub,
+ i,
+ j,
+ &commit_link[i][j]))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
}
@@ -674,34 +711,37 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
/* store 'global' session data */
- session.melt_sig = *client_signature;
- session.session_hash = *melt_hash;
- session.num_oldcoins = coin_count;
- session.num_newcoins = num_new_denoms;
- session.kappa = KAPPA; // FIXME...
- session.noreveal_index
+ refresh_session.melt_sig = *client_signature;
+ refresh_session.session_hash = *melt_hash;
+ refresh_session.num_oldcoins = coin_count;
+ refresh_session.num_newcoins = num_new_denoms;
+ refresh_session.kappa = KAPPA; // FIXME...
+ refresh_session.noreveal_index
= GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
- session.kappa);
+ refresh_session.kappa);
if (GNUNET_OK !=
- (res = TALER_MINT_DB_create_refresh_session (db_conn,
- refresh_session_pub,
- &session)))
+ (res = plugin->create_refresh_session (plugin->cls,
+ session,
+ refresh_session_pub,
+ &refresh_session)))
{
- TALER_MINT_DB_rollback (db_conn);
+ plugin->rollback (plugin->cls,
+ session);
return TALER_MINT_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- TALER_MINT_DB_commit (db_conn))
+ plugin->commit (plugin->cls,
+ session))
{
LOG_WARNING ("/refresh/melt transaction commit failed\n");
return TALER_MINT_reply_commit_error (connection);
}
return TALER_MINT_reply_refresh_melt_success (connection,
- &session.session_hash,
- session.noreveal_index);
+ &refresh_session.session_hash,
+ refresh_session.noreveal_index);
}
@@ -712,7 +752,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
* Then derives the shared secret for each kappa, and check that they match.
*
* @param connection the MHD connection to handle
- * @param db_conn database connection to use
+ * @param session database connection to use
* @param refresh_session session to query
* @param off commitment offset to check
* @param num_oldcoins size of the @a transfer_privs and @a melts arrays
@@ -726,7 +766,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
*/
static int
check_commitment (struct MHD_Connection *connection,
- PGconn *db_conn,
+ struct TALER_MINTDB_Session *session,
const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session,
unsigned int off,
unsigned int num_oldcoins,
@@ -749,11 +789,12 @@ check_commitment (struct MHD_Connection *connection,
struct TALER_LinkSecret shared_secret;
struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check;
- res = TALER_MINT_DB_get_refresh_commit_link (db_conn,
- refresh_session,
- off,
- j,
- &commit_link);
+ res = plugin->get_refresh_commit_link (plugin->cls,
+ session,
+ refresh_session,
+ off,
+ j,
+ &commit_link);
if (GNUNET_OK != res)
{
GNUNET_break (0);
@@ -842,11 +883,12 @@ check_commitment (struct MHD_Connection *connection,
char *buf;
size_t buf_len;
- res = TALER_MINT_DB_get_refresh_commit_coin (db_conn,
- refresh_session,
- off,
- j,
- &commit_coin);
+ res = plugin->get_refresh_commit_coin (plugin->cls,
+ session,
+ refresh_session,
+ off,
+ j,
+ &commit_coin);
if (GNUNET_OK != res)
{
GNUNET_break (0);
@@ -914,7 +956,7 @@ check_commitment (struct MHD_Connection *connection,
* envelope from the database and performs the signing operation.
*
* @param connection the MHD connection to handle
- * @param db_conn database connection to use
+ * @param session database connection to use
* @param refresh_session session to query
* @param key_state key state to lookup denomination pubs
* @param denom_pub denomination key for the coin to create
@@ -925,7 +967,7 @@ check_commitment (struct MHD_Connection *connection,
*/
static struct GNUNET_CRYPTO_rsa_Signature *
refresh_mint_coin (struct MHD_Connection *connection,
- PGconn *db_conn,
+ struct TALER_MINTDB_Session *session,
const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session,
struct MintKeyState *key_state,
const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub,
@@ -937,11 +979,12 @@ refresh_mint_coin (struct MHD_Connection *connection,
struct GNUNET_CRYPTO_rsa_Signature *ev_sig;
int res;
- res = TALER_MINT_DB_get_refresh_commit_coin (db_conn,
- refresh_session,
- noreveal_index,
- coin_off,
- &commit_coin);
+ res = plugin->get_refresh_commit_coin (plugin->cls,
+ session,
+ refresh_session,
+ noreveal_index,
+ coin_off,
+ &commit_coin);
if (GNUNET_OK != res)
{
GNUNET_break (0);
@@ -962,10 +1005,11 @@ refresh_mint_coin (struct MHD_Connection *connection,
return NULL;
}
if (GNUNET_OK !=
- TALER_MINT_DB_insert_refresh_collectable (db_conn,
- refresh_session,
- coin_off,
- ev_sig))
+ plugin->insert_refresh_collectable (plugin->cls,
+ session,
+ refresh_session,
+ coin_off,
+ ev_sig))
{
GNUNET_break (0);
GNUNET_CRYPTO_rsa_signature_free (ev_sig);
@@ -997,7 +1041,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
struct GNUNET_CRYPTO_EcdsaPrivateKey *const*transfer_privs)
{
int res;
- PGconn *db_conn;
+ struct TALER_MINTDB_Session *session;
struct RefreshSession refresh_session;
struct MintKeyState *key_state;
struct RefreshMelt *melts;
@@ -1007,15 +1051,17 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
unsigned int j;
unsigned int off;
- if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
+ if (NULL == (session = plugin->get_session (plugin->cls,
+ GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
- res = TALER_MINT_DB_get_refresh_session (db_conn,
- refresh_session_pub,
- &refresh_session);
+ res = plugin->get_refresh_session (plugin->cls,
+ session,
+ refresh_session_pub,
+ &refresh_session);
if (GNUNET_NO == res)
return TALER_MINT_reply_arg_invalid (connection,
"session_pub");
@@ -1032,10 +1078,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
for (j=0;j<refresh_session.num_oldcoins;j++)
{
if (GNUNET_OK !=
- TALER_MINT_DB_get_refresh_melt (db_conn,
- refresh_session_pub,
- j,
- &melts[j]))
+ plugin->get_refresh_melt (plugin->cls,
+ session,
+ refresh_session_pub,
+ j,
+ &melts[j]))
{
GNUNET_break (0);
GNUNET_free (melts);
@@ -1046,9 +1093,10 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *));
for (j=0;j<refresh_session.num_newcoins;j++)
{
- denom_pubs[j] = TALER_MINT_DB_get_refresh_order (db_conn,
- refresh_session_pub,
- j);
+ denom_pubs[j] = plugin->get_refresh_order (plugin->cls,
+ session,
+ refresh_session_pub,
+ j);
if (NULL == denom_pubs[j])
{
GNUNET_break (0);
@@ -1069,7 +1117,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
off = 1;
if (GNUNET_OK !=
(res = check_commitment (connection,
- db_conn,
+ session,
refresh_session_pub,
i + off,
refresh_session.num_oldcoins,
@@ -1089,7 +1137,8 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
/* Client request OK, start transaction */
if (GNUNET_OK !=
- TALER_MINT_DB_transaction (db_conn))
+ plugin->start (plugin->cls,
+ session))
{
GNUNET_break (0);
for (j=0;j<refresh_session.num_newcoins;j++)
@@ -1104,7 +1153,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
for (j=0;j<refresh_session.num_newcoins;j++)
{
ev_sigs[j] = refresh_mint_coin (connection,
- db_conn,
+ session,
refresh_session_pub,
key_state,
denom_pubs[j],
@@ -1128,7 +1177,8 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,
GNUNET_free (denom_pubs);
if (GNUNET_OK !=
- TALER_MINT_DB_commit (db_conn))
+ plugin->commit (plugin->cls,
+ session))
{
LOG_WARNING ("/refresh/reveal transaction commit failed\n");
for (i=0;i<refresh_session.num_newcoins;i++)
@@ -1161,20 +1211,22 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection,
const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub)
{
int res;
- PGconn *db_conn;
+ struct TALER_MINTDB_Session *session;
struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
struct TALER_EncryptedLinkSecret shared_secret_enc;
struct LinkDataList *ldl;
- if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO)))
+ if (NULL == (session = plugin->get_session (plugin->cls,
+ GNUNET_NO)))
{
GNUNET_break (0);
return TALER_MINT_reply_internal_db_error (connection);
}
- res = TALER_db_get_transfer (db_conn,
- coin_pub,
- &transfer_pub,
- &shared_secret_enc);
+ res = plugin->get_transfer (plugin->cls,
+ session,
+ coin_pub,
+ &transfer_pub,
+ &shared_secret_enc);
if (GNUNET_SYSERR == res)
{
GNUNET_break (0);
@@ -1190,7 +1242,9 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection,
}
GNUNET_assert (GNUNET_OK == res);
- ldl = TALER_db_get_link (db_conn, coin_pub);
+ ldl = plugin->get_link (plugin->cls,
+ session,
+ coin_pub);
if (NULL == ldl)
{
return TALER_MINT_reply_json_pack (connection,
diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h
@@ -25,7 +25,7 @@
#include <microhttpd.h>
#include <gnunet/gnunet_util_lib.h>
#include "taler_util.h"
-#include "mint_db.h"
+#include "taler_mintdb_plugin.h"
/**
diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c
@@ -32,7 +32,7 @@
#include <microhttpd.h>
#include <libpq-fe.h>
#include <pthread.h>
-#include "mint_db.h"
+#include "taler_mintdb_plugin.h"
#include "taler_signatures.h"
#include "taler_util.h"
#include "taler-mint-httpd_parsing.h"
diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c
@@ -24,7 +24,7 @@
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <microhttpd.h>
-#include "mint_db.h"
+#include "taler_mintdb_plugin.h"
#include "taler_signatures.h"
#include "taler_util.h"
#include "taler-mint-httpd_parsing.h"
diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c
@@ -24,7 +24,7 @@
#include <libpq-fe.h>
#include "taler_util.h"
#include "taler_signatures.h"
-#include "mint_db.h"
+#include "taler_mintdb_plugin.h"
#include "db_pq.h"
diff --git a/src/mint/taler_mintdb_plugin.h b/src/mint/taler_mintdb_plugin.h
@@ -0,0 +1,1002 @@
+/*
+ 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/mint_db.h
+ * @brief Low-level (statement-level) database access for the mint
+ * @author Florian Dold
+ * @author Christian Grothoff
+ */
+#ifndef MINT_DB_H
+#define MINT_DB_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+
+
+/**
+ * Information we keep on bank transfer(s) that established a reserve.
+ */
+struct BankTransfer
+{
+
+ /**
+ * Public key of the reserve that was filled.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+
+ /**
+ * Amount that was transferred to the mint.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Detailed wire information about the transaction.
+ */
+ const json_t *wire;
+
+};
+
+
+/**
+ * A summary of a Reserve
+ */
+struct Reserve
+{
+ /**
+ * The reserve's public key. This uniquely identifies the reserve
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey *pub;
+
+ /**
+ * The balance amount existing in the reserve
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * The expiration date of this reserve
+ */
+ struct GNUNET_TIME_Absolute expiry;
+};
+
+
+/**
+ * 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 CollectableBlindcoin
+{
+
+ /**
+ * Our signature over the (blinded) coin.
+ */
+ struct GNUNET_CRYPTO_rsa_Signature *sig;
+
+ /**
+ * Denomination key (which coin was generated).
+ */
+ struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
+
+ /**
+ * Public key of the reserve that was drained.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey 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 GNUNET_CRYPTO_EddsaSignature reserve_sig;
+};
+
+
+
+/**
+ * Types of operations on a reserved.
+ */
+enum TALER_MINT_DB_ReserveOperation
+{
+ /**
+ * Money was deposited into the reserve via a bank transfer.
+ */
+ TALER_MINT_DB_RO_BANK_TO_MINT = 0,
+
+ /**
+ * A Coin was withdrawn from the reserve using /withdraw.
+ */
+ TALER_MINT_DB_RO_WITHDRAW_COIN = 1
+};
+
+
+/**
+ * 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 ReserveHistory
+{
+
+ /**
+ * Next entry in the reserve history.
+ */
+ struct ReserveHistory *next;
+
+ /**
+ * Type of the event, determins @e details.
+ */
+ enum TALER_MINT_DB_ReserveOperation type;
+
+ /**
+ * Details of the operation, depending on @e type.
+ */
+ union
+ {
+
+ /**
+ * Details about a bank transfer to the mint.
+ */
+ struct BankTransfer *bank;
+
+ /**
+ * Details about a /withdraw operation.
+ */
+ struct CollectableBlindcoin *withdraw;
+
+ } details;
+
+};
+
+
+/**
+ * Specification for a /deposit operation.
+ */
+struct 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 GNUNET_CRYPTO_EcdsaSignature csig;
+
+ /**
+ * Public key of the merchant. Enables later identification
+ * of the merchant in case of a need to rollback transactions.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey 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.
+ */
+ const json_t *wire;
+
+ /**
+ * Merchant-generated transaction ID to detect duplicate
+ * transactions.
+ */
+ uint64_t transaction_id;
+
+ /**
+ * Fraction of the coin's remaining value to be deposited.
+ * The coin is identified by @e coin_pub.
+ */
+ struct TALER_Amount amount;
+
+};
+
+
+/**
+ * Global information for a refreshing session. Includes
+ * dimensions of the operation, security parameters and
+ * client signatures from "/refresh/melt" and "/refresh/commit".
+ */
+struct RefreshSession
+{
+ /**
+ * Signature over the commitments by the client,
+ * only valid if @e has_commit_sig is set.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature commit_sig;
+
+ /**
+ * Hash over coins to melt and coins to create of the
+ * refresh session.
+ */
+ struct GNUNET_HashCode session_hash;
+
+ /**
+ * Signature over the melt by the client.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature melt_sig;
+
+ /**
+ * Number of coins we are melting.
+ */
+ uint16_t num_oldcoins;
+
+ /**
+ * Number of new coins we are creating.
+ */
+ uint16_t num_newcoins;
+
+ /**
+ * Number of parallel operations we perform for the cut and choose.
+ * (must be greater or equal to three for security). 0 if not yet
+ * known.
+ */
+ uint16_t kappa;
+
+ /**
+ * Index (smaller @e kappa) which the mint has chosen to not
+ * have revealed during cut and choose.
+ */
+ uint16_t noreveal_index;
+
+};
+
+
+/**
+ * Specification for coin in a /refresh/melt operation.
+ */
+struct RefreshMelt
+{
+ /**
+ * Information about the coin that is being melted.
+ */
+ struct TALER_CoinPublicInfo coin;
+
+ /**
+ * Signature over the melting operation.
+ */
+ struct GNUNET_CRYPTO_EcdsaSignature coin_sig;
+
+ /**
+ * Which melting operation should the coin become a part of.
+ */
+ struct GNUNET_HashCode melt_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.
+ */
+ struct TALER_Amount amount;
+
+};
+
+
+/**
+ * We have as many `struct RefreshCommitCoin` as there are new
+ * coins being created by the refresh (for each of the kappa
+ * sets). These are the coins we ask the mint to sign if the
+ * respective set is selected.
+ */
+struct 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;
+
+};
+
+
+/**
+ * For each (old) coin being melted, we have a `struct
+ * RefreshCommitLink` that allows the user to find the shared secret
+ * to decrypt the respective refresh links for the new coins in the
+ * `struct RefreshCommitCoin`.
+ */
+struct RefreshCommitLink
+{
+ /**
+ * Transfer public key (FIXME: explain!)
+ */
+ struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub;
+
+ /**
+ * Encrypted shared secret to decrypt the link.
+ */
+ struct TALER_EncryptedLinkSecret shared_secret_enc;
+};
+
+
+/**
+ * Linked list of refresh information linked to a coin.
+ */
+struct LinkDataList
+{
+ /**
+ * Information is stored in a NULL-terminated linked list.
+ */
+ struct 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 GNUNET_CRYPTO_rsa_PublicKey *denom_pub;
+
+ /**
+ * Signature over the blinded envelope.
+ */
+ struct GNUNET_CRYPTO_rsa_Signature *ev_sig;
+};
+
+
+/**
+ * Specification for a /lock operation.
+ */
+struct Lock
+{
+ /**
+ * Information about the coin that is being melted.
+ */
+ struct TALER_CoinPublicInfo coin;
+
+ /**
+ * Signature over the melting operation.
+ */
+ const struct GNUNET_CRYPTO_EcdsaSignature coin_sig;
+
+ /**
+ * How much value is being melted?
+ */
+ struct TALER_Amount amount;
+
+ // FIXME: more needed...
+};
+
+
+/**
+ * Enumeration to classify the different types of transactions
+ * that can be done with a coin.
+ */
+enum TALER_MINT_DB_TransactionType
+{
+ /**
+ * /deposit operation.
+ */
+ TALER_MINT_DB_TT_DEPOSIT = 0,
+
+ /**
+ * /refresh/melt operation.
+ */
+ TALER_MINT_DB_TT_REFRESH_MELT = 1,
+
+ /**
+ * /lock operation.
+ */
+ TALER_MINT_DB_TT_LOCK = 2
+};
+
+
+/**
+ * List of transactions we performed for a particular coin.
+ */
+struct TALER_MINT_DB_TransactionList
+{
+
+ /**
+ * Next pointer in the NULL-terminated linked list.
+ */
+ struct TALER_MINT_DB_TransactionList *next;
+
+ /**
+ * Type of the transaction, determines what is stored in @e details.
+ */
+ enum TALER_MINT_DB_TransactionType type;
+
+ /**
+ * Details about the transaction, depending on @e type.
+ */
+ union
+ {
+
+ /**
+ * Details if transaction was a /deposit operation.
+ */
+ struct Deposit *deposit;
+
+ /**
+ * Details if transaction was a /refresh/melt operation.
+ */
+ struct RefreshMelt *melt;
+
+ /**
+ * Details if transaction was a /lock operation.
+ */
+ struct Lock *lock;
+
+ } details;
+
+};
+
+
+/**
+ * Handle for a database session (per-thread, for transactions).
+ */
+struct TALER_MINTDB_Session;
+
+
+/**
+ * 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;
+
+ /**
+ * 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 db_conn connection to use
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*start) (void *cls,
+ struct TALER_MINTDB_Session *db_conn);
+
+
+ /**
+ * Commit a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn connection to use
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*commit) (void *cls,
+ struct TALER_MINTDB_Session *db_conn);
+
+
+ /**
+ * Abort/rollback a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn connection to use
+ */
+ void
+ (*rollback) (void *cls,
+ struct TALER_MINTDB_Session *db_conn);
+
+
+ /**
+ * 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 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 Reserve *reserve);
+
+ /* FIXME: add functions to add bank transfers to our DB
+ (and to test if we already did add one) (#3633/#3717) */
+
+
+ /**
+ * Insert a incoming transaction into reserves. New reserves are also created
+ * through this function.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db the database connection handle
+ * @param reserve the reserve structure. The public key of the reserve should
+ * be set here. Upon successful execution of this function, the
+ * balance and expiration of the reserve will be updated.
+ * @param balance the amount that has to be added to the reserve
+ * @param expiry the new expiration time for the reserve
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures
+ */
+ int
+ (*reserves_in_insert) (void *cls,
+ struct TALER_MINTDB_Session *db,
+ struct Reserve *reserve,
+ const struct TALER_Amount *balance,
+ const struct GNUNET_TIME_Absolute expiry);
+
+
+ /**
+ * 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 db_conn database connection to use
+ * @param h_blind hash of the blinded message
+ * @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_collectable_blindcoin) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_HashCode *h_blind,
+ struct 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 db_conn database connection to use
+ * @param h_blind hash of the blinded message
+ * @param withdraw amount by which the reserve will be withdrawn with this
+ * transaction
+ * @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_collectable_blindcoin) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_HashCode *h_blind,
+ struct TALER_Amount withdraw,
+ const struct 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 db_conn connection to use
+ * @param reserve_pub public key of the reserve
+ * @return known transaction history (NULL if reserve is unknown)
+ */
+ struct ReserveHistory *
+ (*get_reserve_history) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub);
+
+
+ /**
+ * 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 db_conn 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 internal error
+ */
+ int
+ (*have_deposit) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct Deposit *deposit);
+
+
+ /**
+ * Insert information about deposited coin into the
+ * database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn 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 *db_conn,
+ const struct Deposit *deposit);
+
+
+ /**
+ * Lookup refresh session data under the given public key.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn database handle to use
+ * @param refresh_session_pub public key to use for the lookup
+ * @param refresh_session[OUT] 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 *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ struct RefreshSession *refresh_session);
+
+
+ /**
+ * Store new refresh session data under the given public key.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn database handle to use
+ * @param refresh_session_pub public key 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 *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ const struct 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 db_conn database connection
+ * @param refresh_session session key of the melt operation
+ * @param oldcoin_index index of the coin to store
+ * @param melt coin melt operation details to store
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*insert_refresh_melt) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session,
+ uint16_t oldcoin_index,
+ const struct 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 db_conn database connection
+ * @param refresh_session session key of the melt operation
+ * @param oldcoin_index index of the coin to retrieve
+ * @param melt melt data to fill in
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*get_refresh_melt) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session,
+ uint16_t oldcoin_index,
+ struct 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 db_conn database connection
+ * @param session_pub refresh session key
+ * @param newcoin_index index of the coin to generate
+ * @param denom_pub denomination of the coin to create
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*insert_refresh_order) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub);
+
+
+ /**
+ * Lookup in the database the @a newcoin_index coin that we want to
+ * create in the given refresh operation.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn database connection
+ * @param session_pub refresh session key
+ * @param newcoin_index index of the coin to generate
+ * @param denom_pub denomination of the coin to create
+ * @return NULL on error (not found or internal error)
+ */
+ struct GNUNET_CRYPTO_rsa_PublicKey *
+ (*get_refresh_order) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t newcoin_index);
+
+
+ /**
+ * Store information about the commitment of the
+ * given coin for the given refresh session in the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn database connection to use
+ * @param refresh_session_pub refresh session this commitment belongs to
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
+ * @param commit_coin coin commitment to store
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on error
+ */
+ int
+ (*insert_refresh_commit_coin) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int i,
+ unsigned int j,
+ const struct RefreshCommitCoin *commit_coin);
+
+
+ /**
+ * 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 db_conn database connection to use
+ * @param refresh_session_pub refresh session the commitment belongs to
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to refreshed (new) coins
+ * @param commit_coin[OUT] coin commitment to return
+ * @return #GNUNET_OK on success
+ * #GNUNET_NO if not found
+ * #GNUNET_SYSERR on error
+ */
+ int
+ (*get_refresh_commit_coin) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int i,
+ unsigned int j,
+ struct RefreshCommitCoin *commit_coin);
+
+
+ /**
+ * 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 db_conn database connection to use
+ * @param refresh_session_pub public key of the refresh session this
+ * commitment belongs with
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to melted (old) coins
+ * @param commit_link link information to store
+ * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success
+ */
+ int
+ (*insert_refresh_commit_link) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int i,
+ unsigned int j,
+ const struct RefreshCommitLink *commit_link);
+
+ /**
+ * 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 db_conn database connection to use
+ * @param refresh_session_pub public key of the refresh session this
+ * commitment belongs with
+ * @param i set index (1st dimension)
+ * @param j coin index (2nd dimension), corresponds to melted (old) coins
+ * @param cc[OUT] 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_link) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
+ unsigned int i,
+ unsigned int j,
+ struct RefreshCommitLink *cc);
+
+
+ /**
+ * 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 db_conn database connection
+ * @param session_pub refresh session
+ * @param newcoin_index coin index
+ * @param ev_sig coin signature
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*insert_refresh_collectable) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
+ uint16_t newcoin_index,
+ const struct GNUNET_CRYPTO_rsa_Signature *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 db_conn database connection
+ * @param coin_pub public key to use to retrieve linkage data
+ * @return all known link data for the coin
+ */
+ struct LinkDataList *
+ (*get_link) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub);
+
+
+ /**
+ * Obtain shared secret and transfer public key from the public key of
+ * the coin. This information and the link information returned by
+ * #TALER_db_get_link() 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 db_conn database connection
+ * @param coin_pub public key of the coin
+ * @param transfer_pub[OUT] public transfer key
+ * @param shared_secret_enc[OUT] set to shared secret
+ * @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 *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub,
+ struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub,
+ struct TALER_EncryptedLinkSecret *shared_secret_enc);
+
+
+ /**
+ * Test if the given /lock request is known to us.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn database connection
+ * @param lock lock operation
+ * @return #GNUNET_YES if known,
+ * #GNUENT_NO if not,
+ * #GNUNET_SYSERR on internal error
+ */
+ int
+ (*have_lock) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct Lock *lock);
+
+
+ /**
+ * Store the given /lock request in the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param db_conn 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 *db_conn,
+ const struct Lock *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 db_conn database connection
+ * @param coin_pub coin to investigate
+ * @return list of transactions, NULL if coin is fresh
+ */
+ struct TALER_MINT_DB_TransactionList *
+ (*get_coin_transactions) (void *cls,
+ struct TALER_MINTDB_Session *db_conn,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub);
+
+};
+
+
+#endif /* _NEURO_MINT_DB_H */
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
@@ -8,7 +8,8 @@ libtalerutil_la_SOURCES = \
amount.c \
crypto.c \
util.c \
- json.c
+ json.c \
+ os_installation.c
libtalerutil_la_LIBADD = \
-lgnunetutil \
diff --git a/src/util/os_installation.c b/src/util/os_installation.c
@@ -0,0 +1,701 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2006-2014 Christian Grothoff (and other contributing authors)
+
+ GNUnet 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.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file os_installation.c
+ * @brief get paths used by the program; based heavily on the
+ * corresponding GNUnet file, just adapted for Taler.
+ * @author Milan
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#if DARWIN
+#include <mach-o/ldsyms.h>
+#include <mach-o/dyld.h>
+#elif WINDOWS
+#include <windows.h>
+#endif
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+
+#if LINUX
+/**
+ * Try to determine path by reading /proc/PID/exe
+ *
+ * @return NULL on error
+ */
+static char *
+get_path_from_proc_maps ()
+{
+ char fn[64];
+ char line[1024];
+ char dir[1024];
+ FILE *f;
+ char *lgu;
+
+ GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ());
+ if (NULL == (f = FOPEN (fn, "r")))
+ return NULL;
+ while (NULL != fgets (line, sizeof (line), f))
+ {
+ if ((1 ==
+ SSCANF (line, "%*x-%*x %*c%*c%*c%*c %*x %*2x:%*2x %*u%*[ ]%1023s", dir)) &&
+ (NULL != (lgu = strstr (dir, "libtalerutil"))))
+ {
+ lgu[0] = '\0';
+ FCLOSE (f);
+ return GNUNET_strdup (dir);
+ }
+ }
+ FCLOSE (f);
+ return NULL;
+}
+
+
+/**
+ * Try to determine path by reading /proc/PID/exe
+ *
+ * @return NULL on error
+ */
+static char *
+get_path_from_proc_exe ()
+{
+ char fn[64];
+ char lnk[1024];
+ ssize_t size;
+
+ GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/exe", getpid ());
+ size = readlink (fn, lnk, sizeof (lnk) - 1);
+ if (size <= 0)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
+ return NULL;
+ }
+ GNUNET_assert (size < sizeof (lnk));
+ lnk[size] = '\0';
+ while ((lnk[size] != '/') && (size > 0))
+ size--;
+ /* test for being in lib/taler/libexec/ or lib/MULTIARCH/taler/libexec */
+ if ( (size > strlen ("/taler/libexec/")) &&
+ (0 == strcmp ("/taler/libexec/",
+ &lnk[size - strlen ("/taler/libexec/")])) )
+ size -= strlen ("taler/libexec/");
+ if ((size < 4) || (lnk[size - 4] != '/'))
+ {
+ /* not installed in "/bin/" -- binary path probably useless */
+ return NULL;
+ }
+ lnk[size] = '\0';
+ return GNUNET_strdup (lnk);
+}
+#endif
+
+
+#if WINDOWS
+static HINSTANCE dll_instance;
+
+
+/**
+ * GNUNET_util_cl_init() in common_logging.c is preferred.
+ * This function is only for thread-local storage (not used in GNUnet)
+ * and hInstance saving.
+ */
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ dll_instance = hinstDLL;
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+
+/**
+ * Try to determine path with win32-specific function
+ *
+ * @return NULL on error
+ */
+static char *
+get_path_from_module_filename ()
+{
+ size_t pathlen = 512;
+ DWORD real_pathlen;
+ wchar_t *idx;
+ wchar_t *modulepath = NULL;
+ char *upath;
+ uint8_t *u8_string;
+ size_t u8_string_length;
+
+ /* This braindead function won't tell us how much space it needs, so
+ * we start at 1024 and double the space up if it doesn't fit, until
+ * it fits, or we exceed the threshold.
+ */
+ do
+ {
+ pathlen = pathlen * 2;
+ modulepath = GNUNET_realloc (modulepath, pathlen * sizeof (wchar_t));
+ SetLastError (0);
+ real_pathlen = GetModuleFileNameW (dll_instance, modulepath, pathlen * sizeof (wchar_t));
+ } while (real_pathlen >= pathlen && pathlen < 16*1024);
+ if (real_pathlen >= pathlen)
+ GNUNET_assert (0);
+ /* To be safe */
+ modulepath[real_pathlen] = '\0';
+
+ idx = modulepath + real_pathlen;
+ while ((idx > modulepath) && (*idx != L'\\') && (*idx != L'/'))
+ idx--;
+ *idx = L'\0';
+
+ /* Now modulepath holds full path to the directory where libtalerutil is.
+ * This directory should look like <TALER_PREFIX>/bin or <TALER_PREFIX>.
+ */
+ if (wcschr (modulepath, L'/') || wcschr (modulepath, L'\\'))
+ {
+ /* At least one directory component (i.e. we're not in a root directory) */
+ wchar_t *dirname = idx;
+ while ((dirname > modulepath) && (*dirname != L'\\') && (*dirname != L'/'))
+ dirname--;
+ *dirname = L'\0';
+ if (dirname > modulepath)
+ {
+ dirname++;
+ /* Now modulepath holds full path to the parent directory of the directory
+ * where libtalerutil is.
+ * dirname holds the name of the directory where libtalerutil is.
+ */
+ if (wcsicmp (dirname, L"bin") == 0)
+ {
+ /* pass */
+ }
+ else
+ {
+ /* Roll back our changes to modulepath */
+ dirname--;
+ *dirname = L'/';
+ }
+ }
+ }
+
+ /* modulepath is TALER_PREFIX */
+ u8_string = u16_to_u8 (modulepath, wcslen (modulepath), NULL, &u8_string_length);
+ if (NULL == u8_string)
+ GNUNET_assert (0);
+
+ upath = GNUNET_malloc (u8_string_length + 1);
+ memcpy (upath, u8_string, u8_string_length);
+ upath[u8_string_length] = '\0';
+
+ free (u8_string);
+ GNUNET_free (modulepath);
+
+ return upath;
+}
+#endif
+
+
+#if DARWIN
+/**
+ * Signature of the '_NSGetExecutablePath" function.
+ *
+ * @param buf where to write the path
+ * @param number of bytes available in 'buf'
+ * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize'
+ */
+typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize);
+
+
+/**
+ * Try to obtain the path of our executable using '_NSGetExecutablePath'.
+ *
+ * @return NULL on error
+ */
+static char *
+get_path_from_NSGetExecutablePath ()
+{
+ static char zero = '\0';
+ char *path;
+ size_t len;
+ MyNSGetExecutablePathProto func;
+
+ path = NULL;
+ if (NULL == (func =
+ (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath")))
+ return NULL;
+ path = &zero;
+ len = 0;
+ /* get the path len, including the trailing \0 */
+ (void) func (path, &len);
+ if (0 == len)
+ return NULL;
+ path = GNUNET_malloc (len);
+ if (0 != func (path, &len))
+ {
+ GNUNET_free (path);
+ return NULL;
+ }
+ len = strlen (path);
+ while ((path[len] != '/') && (len > 0))
+ len--;
+ path[len] = '\0';
+ return path;
+}
+
+
+/**
+ * Try to obtain the path of our executable using '_dyld_image' API.
+ *
+ * @return NULL on error
+ */
+static char *
+get_path_from_dyld_image ()
+{
+ const char *path;
+ char *p;
+ char *s;
+ unsigned int i;
+ int c;
+
+ c = _dyld_image_count ();
+ for (i = 0; i < c; i++)
+ {
+ if (((const void *) _dyld_get_image_header (i)) != (const void *)&_mh_dylib_header)
+ continue;
+ path = _dyld_get_image_name (i);
+ if ( (NULL == path) || (0 == strlen (path)) )
+ continue;
+ p = GNUNET_strdup (path);
+ s = p + strlen (p);
+ while ((s > p) && ('/' != *s))
+ s--;
+ s++;
+ *s = '\0';
+ return p;
+ }
+ return NULL;
+}
+#endif
+
+
+/**
+ * Return the actual path to a file found in the current
+ * PATH environment variable.
+ *
+ * @param binary the name of the file to find
+ * @return path to binary, NULL if not found
+ */
+static char *
+get_path_from_PATH (const char *binary)
+{
+ char *path;
+ char *pos;
+ char *end;
+ char *buf;
+ const char *p;
+
+ if (NULL == (p = getenv ("PATH")))
+ return NULL;
+#if WINDOWS
+ /* On W32 look in CWD first. */
+ GNUNET_asprintf (&path, ".%c%s", PATH_SEPARATOR, p);
+#else
+ path = GNUNET_strdup (p); /* because we write on it */
+#endif
+ buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1);
+ pos = path;
+ while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
+ {
+ *end = '\0';
+ sprintf (buf, "%s/%s", pos, binary);
+ if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
+ {
+ pos = GNUNET_strdup (pos);
+ GNUNET_free (buf);
+ GNUNET_free (path);
+ return pos;
+ }
+ pos = end + 1;
+ }
+ sprintf (buf, "%s/%s", pos, binary);
+ if (GNUNET_YES == GNUNET_DISK_file_test (buf))
+ {
+ pos = GNUNET_strdup (pos);
+ GNUNET_free (buf);
+ GNUNET_free (path);
+ return pos;
+ }
+ GNUNET_free (buf);
+ GNUNET_free (path);
+ return NULL;
+}
+
+
+/**
+ * Try to obtain the installation path using the "TALER_PREFIX" environment
+ * variable.
+ *
+ * @return NULL on error (environment variable not set)
+ */
+static char *
+get_path_from_TALER_PREFIX ()
+{
+ const char *p;
+
+ if (NULL != (p = getenv ("TALER_PREFIX")))
+ return GNUNET_strdup (p);
+ return NULL;
+}
+
+
+/**
+ * @brief get the path to Taler bin/ or lib/, prefering the lib/ path
+ * @author Milan
+ *
+ * @return a pointer to the executable path, or NULL on error
+ */
+static char *
+os_get_taler_path ()
+{
+ char *ret;
+
+ if (NULL != (ret = get_path_from_TALER_PREFIX ()))
+ return ret;
+#if LINUX
+ if (NULL != (ret = get_path_from_proc_maps ()))
+ return ret;
+ /* try path *first*, before /proc/exe, as /proc/exe can be wrong */
+ if (NULL != (ret = get_path_from_PATH ("taler-mint-httpd")))
+ return ret;
+ if (NULL != (ret = get_path_from_proc_exe ()))
+ return ret;
+#endif
+#if WINDOWS
+ if (NULL != (ret = get_path_from_module_filename ()))
+ return ret;
+#endif
+#if DARWIN
+ if (NULL != (ret = get_path_from_dyld_image ()))
+ return ret;
+ if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
+ return ret;
+#endif
+ if (NULL != (ret = get_path_from_PATH ("taler-mint-httpd")))
+ return ret;
+ /* other attempts here */
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not determine installation path for %s. Set `%s' environment variable.\n"),
+ "Taler", "TALER_PREFIX");
+ return NULL;
+}
+
+
+/**
+ * @brief get the path to current app's bin/
+ * @author Milan
+ *
+ * @return a pointer to the executable path, or NULL on error
+ */
+static char *
+os_get_exec_path ()
+{
+ char *ret = NULL;
+
+#if LINUX
+ if (NULL != (ret = get_path_from_proc_exe ()))
+ return ret;
+#endif
+#if WINDOWS
+ if (NULL != (ret = get_path_from_module_filename ()))
+ return ret;
+#endif
+#if DARWIN
+ if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
+ return ret;
+#endif
+ /* other attempts here */
+ return ret;
+}
+
+
+/**
+ * @brief get the path to a specific Taler installation directory or,
+ * with #TALER_OS_IPK_SELF_PREFIX, the current running apps installation directory
+ * @author Milan
+ * @return a pointer to the dir path (to be freed by the caller)
+ */
+char *
+TALER_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
+{
+ size_t n;
+ const char *dirname;
+ char *execpath = NULL;
+ char *tmp;
+ char *multiarch;
+ char *libdir;
+ int isbasedir;
+
+ /* if wanted, try to get the current app's bin/ */
+ if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
+ execpath = os_get_exec_path ();
+
+ /* try to get Taler's bin/ or lib/, or if previous was unsuccessful some
+ * guess for the current app */
+ if (NULL == execpath)
+ execpath = os_get_taler_path ();
+
+ if (NULL == execpath)
+ return NULL;
+
+ n = strlen (execpath);
+ if (0 == n)
+ {
+ /* should never happen, but better safe than sorry */
+ GNUNET_free (execpath);
+ return NULL;
+ }
+ /* remove filename itself */
+ while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1]))
+ execpath[--n] = '\0';
+
+ isbasedir = 1;
+ if ((n > 6) &&
+ ((0 == strcasecmp (&execpath[n - 6], "/lib32")) ||
+ (0 == strcasecmp (&execpath[n - 6], "/lib64"))))
+ {
+ if ( (GNUNET_OS_IPK_LIBDIR != dirkind) &&
+ (GNUNET_OS_IPK_LIBEXECDIR != dirkind) )
+ {
+ /* strip '/lib32' or '/lib64' */
+ execpath[n - 6] = '\0';
+ n -= 6;
+ }
+ else
+ isbasedir = 0;
+ }
+ else if ((n > 4) &&
+ ((0 == strcasecmp (&execpath[n - 4], "/bin")) ||
+ (0 == strcasecmp (&execpath[n - 4], "/lib"))))
+ {
+ /* strip '/bin' or '/lib' */
+ execpath[n - 4] = '\0';
+ n -= 4;
+ }
+ multiarch = NULL;
+ if (NULL != (libdir = strstr (execpath, "/lib/")))
+ {
+ /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/";
+ here we need to re-add 'multiarch' to lib and libexec paths later! */
+ multiarch = &libdir[5];
+ if (NULL == strchr (multiarch, '/'))
+ libdir[0] = '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */
+ else
+ multiarch = NULL; /* maybe not, multiarch still has a '/', which is not OK */
+ }
+ /* in case this was a directory named foo-bin, remove "foo-" */
+ while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
+ execpath[--n] = '\0';
+ switch (dirkind)
+ {
+ case GNUNET_OS_IPK_PREFIX:
+ case GNUNET_OS_IPK_SELF_PREFIX:
+ dirname = DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_BINDIR:
+ dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_LIBDIR:
+ if (isbasedir)
+ {
+ GNUNET_asprintf (&tmp,
+ "%s%s%s%s%s",
+ execpath,
+ DIR_SEPARATOR_STR "lib",
+ (NULL != multiarch) ? DIR_SEPARATOR_STR : "",
+ (NULL != multiarch) ? multiarch : "",
+ DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR);
+ if (GNUNET_YES ==
+ GNUNET_DISK_directory_test (tmp, GNUNET_YES))
+ {
+ GNUNET_free (execpath);
+ return tmp;
+ }
+ GNUNET_free (tmp);
+ tmp = NULL;
+ if (4 == sizeof (void *))
+ {
+ dirname =
+ DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR;
+ GNUNET_asprintf (&tmp,
+ "%s%s",
+ execpath,
+ dirname);
+ }
+ if (8 == sizeof (void *))
+ {
+ dirname =
+ DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR;
+ GNUNET_asprintf (&tmp,
+ "%s%s",
+ execpath,
+ dirname);
+ }
+
+ if ( (NULL != tmp) &&
+ (GNUNET_YES ==
+ GNUNET_DISK_directory_test (tmp, GNUNET_YES)) )
+ {
+ GNUNET_free (execpath);
+ return tmp;
+ }
+ GNUNET_free (tmp);
+ }
+ dirname = DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_DATADIR:
+ dirname =
+ DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_LOCALEDIR:
+ dirname =
+ DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_ICONDIR:
+ dirname =
+ DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_DOCDIR:
+ dirname =
+ DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR \
+ "gnunet" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_LIBEXECDIR:
+ if (isbasedir)
+ {
+ dirname =
+ DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR;
+ GNUNET_asprintf (&tmp,
+ "%s%s%s%s",
+ execpath,
+ DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR,
+ (NULL != multiarch) ? multiarch : "",
+ dirname);
+ if (GNUNET_YES ==
+ GNUNET_DISK_directory_test (tmp, GNUNET_YES))
+ {
+ GNUNET_free (execpath);
+ return tmp;
+ }
+ GNUNET_free (tmp);
+ tmp = NULL;
+ if (4 == sizeof (void *))
+ {
+ dirname =
+ DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR \
+ "libexec" DIR_SEPARATOR_STR;
+ GNUNET_asprintf (&tmp,
+ "%s%s",
+ execpath,
+ dirname);
+ }
+ if (8 == sizeof (void *))
+ {
+ dirname =
+ DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR \
+ "libexec" DIR_SEPARATOR_STR;
+ GNUNET_asprintf (&tmp,
+ "%s%s",
+ execpath,
+ dirname);
+ }
+ if ( (NULL != tmp) &&
+ (GNUNET_YES ==
+ GNUNET_DISK_directory_test (tmp, GNUNET_YES)) )
+ {
+ GNUNET_free (execpath);
+ return tmp;
+ }
+
+ GNUNET_free (tmp);
+ }
+ dirname =
+ DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR \
+ "libexec" DIR_SEPARATOR_STR;
+ break;
+ default:
+ GNUNET_free (execpath);
+ return NULL;
+ }
+ GNUNET_asprintf (&tmp,
+ "%s%s",
+ execpath,
+ dirname);
+ GNUNET_free (execpath);
+ return tmp;
+}
+
+
+/**
+ * Given the name of a taler-helper, taler-service or taler-daemon
+ * binary, try to prefix it with the libexec/-directory to get the
+ * full path.
+ *
+ * @param progname name of the binary
+ * @return full path to the binary, if possible, otherwise copy of 'progname'
+ */
+char *
+TALER_OS_get_libexec_binary_path (const char *progname)
+{
+ static char *cache;
+ char *libexecdir;
+ char *binary;
+
+ if ( (DIR_SEPARATOR == progname[0]) ||
+ (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL)) )
+ return GNUNET_strdup (progname);
+ if (NULL != cache)
+ libexecdir = cache;
+ else
+ libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
+ if (NULL == libexecdir)
+ return GNUNET_strdup (progname);
+ GNUNET_asprintf (&binary,
+ "%s%s",
+ libexecdir,
+ progname);
+ cache = libexecdir;
+ return binary;
+}
+
+
+
+/* end of os_installation.c */