summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/auditor/Makefile.am39
-rw-r--r--src/auditor/auditor.conf (renamed from src/exchange-tools/auditor.conf)6
-rw-r--r--src/auditor/taler-auditor-sign.c (renamed from src/exchange-tools/taler-auditor-sign.c)0
-rw-r--r--src/auditor/taler-auditor.c117
-rw-r--r--src/auditordb/Makefile.am53
-rw-r--r--src/auditordb/auditordb-postgres.conf3
-rw-r--r--src/auditordb/auditordb_plugin.c88
-rw-r--r--src/auditordb/plugin_auditordb_postgres.c776
-rw-r--r--src/exchange-tools/Makefile.am10
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c12
-rw-r--r--src/include/Makefile.am2
-rw-r--r--src/include/taler_auditordb_lib.h47
-rw-r--r--src/include/taler_auditordb_plugin.h135
14 files changed, 1272 insertions, 18 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 45ff87a09..2f158a1da 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,7 +22,7 @@ pkgcfg_DATA = \
EXTRA_DIST = \
taler.conf
-SUBDIRS = include util json $(PQ_DIR) $(BANK_LIB) wire exchangedb exchange exchange-tools
+SUBDIRS = include util json $(PQ_DIR) $(BANK_LIB) wire exchangedb exchange exchange-tools auditordb auditor
if HAVE_LIBCURL
SUBDIRS += exchange-lib benchmark
else
diff --git a/src/auditor/Makefile.am b/src/auditor/Makefile.am
new file mode 100644
index 000000000..724b43205
--- /dev/null
+++ b/src/auditor/Makefile.am
@@ -0,0 +1,39 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+pkgcfgdir = $(prefix)/share/taler/config.d/
+
+pkgcfg_DATA = \
+ auditor.conf
+
+bin_PROGRAMS = \
+ taler-auditor \
+ taler-auditor-sign
+
+taler_auditor_SOURCES = \
+ taler-auditor.c
+taler_auditor_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/wire/libtalerwire.la \
+ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+ $(top_builddir)/src/auditordb/libtalerauditordb.la \
+ -lgnunetutil
+
+taler_auditor_sign_SOURCES = \
+ taler-auditor-sign.c
+taler_auditor_sign_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+ -lgnunetutil $(XLIB)
+
+
+
+EXTRA_DIST = \
+ auditor.conf
diff --git a/src/exchange-tools/auditor.conf b/src/auditor/auditor.conf
index 7eb5f8ae9..22395cc6e 100644
--- a/src/exchange-tools/auditor.conf
+++ b/src/auditor/auditor.conf
@@ -1,8 +1,8 @@
-# This configuration file is in the public domain
+# This file is in the public domain.
#
-# It cointains options for the auditor.
-
[auditor]
+# Which database backend do we use for the auditor?
+DB = postgres
# Where do we store the auditor's private key?
AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
diff --git a/src/exchange-tools/taler-auditor-sign.c b/src/auditor/taler-auditor-sign.c
index 6e4fda754..6e4fda754 100644
--- a/src/exchange-tools/taler-auditor-sign.c
+++ b/src/auditor/taler-auditor-sign.c
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
new file mode 100644
index 000000000..1e9d1c2ef
--- /dev/null
+++ b/src/auditor/taler-auditor.c
@@ -0,0 +1,117 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 Inria
+
+ 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, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file auditor/taler-auditor.c
+ * @brief audits an exchange database.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * Handle to access the exchange's database.
+ */
+static struct TALER_EXCHANGEDB_Plugin *edb;
+
+/**
+ * Handle to access the auditor's database.
+ */
+static struct TALER_AUDITORDB_Plugin *adb;
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ if (NULL ==
+ (edb = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ fprintf (stderr,
+ "Failed to initialize exchange database plugin.\n");
+ global_ret = 1;
+ return;
+ }
+ if (NULL ==
+ (adb = TALER_AUDITORDB_plugin_load (cfg)))
+ {
+ fprintf (stderr,
+ "Failed to initialize auditor database plugin.\n");
+ global_ret = 1;
+ TALER_EXCHANGEDB_plugin_unload (edb);
+ return;
+ }
+
+
+ TALER_AUDITORDB_plugin_unload (adb);
+ TALER_EXCHANGEDB_plugin_unload (edb);
+}
+
+
+/**
+ * The main function of the database initialization tool.
+ * Used to initialize the Taler Exchange's database.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ /* force linker to link against libtalerutil; if we do
+ not do this, the linker may "optimize" libtalerutil
+ away and skip #TALER_OS_init(), which we do need */
+ (void) TALER_project_data_default ();
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-auditor",
+ "INFO",
+ NULL));
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc,
+ argv,
+ "taler-auditor",
+ "Audit Taler exchange database",
+ options,
+ &run,
+ NULL))
+ return 1;
+ return global_ret;
+}
+
+
+/* end of taler-auditor.c */
diff --git a/src/auditordb/Makefile.am b/src/auditordb/Makefile.am
new file mode 100644
index 000000000..e8ec40272
--- /dev/null
+++ b/src/auditordb/Makefile.am
@@ -0,0 +1,53 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/pq/ $(POSTGRESQL_CPPFLAGS)
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+pkgcfgdir = $(prefix)/share/taler/config.d/
+
+pkgcfg_DATA = \
+ auditordb-postgres.conf
+
+EXTRA_DIST = \
+ auditordb-postgres.conf
+
+plugindir = $(libdir)/taler
+
+if HAVE_POSTGRESQL
+plugin_LTLIBRARIES = \
+ libtaler_plugin_auditordb_postgres.la
+endif
+
+libtaler_plugin_auditordb_postgres_la_SOURCES = \
+ plugin_auditordb_postgres.c
+libtaler_plugin_auditordb_postgres_la_LIBADD = \
+ $(LTLIBINTL)
+libtaler_plugin_auditordb_postgres_la_LDFLAGS = \
+ $(TALER_PLUGIN_LDFLAGS) \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lpq \
+ -lgnunetpq \
+ -lgnunetutil $(XLIB)
+
+lib_LTLIBRARIES = \
+ libtalerauditordb.la
+
+libtalerauditordb_la_SOURCES = \
+ auditordb_plugin.c
+
+libtalerauditordb_la_LIBADD = \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil $(XLIB)
+
+libtalerauditordb_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -no-undefined
+
+
+EXTRA_test_auditordb_postgres_DEPENDENCIES = \
+ libtaler_plugin_auditordb_postgres.la
diff --git a/src/auditordb/auditordb-postgres.conf b/src/auditordb/auditordb-postgres.conf
new file mode 100644
index 000000000..4fe2064f1
--- /dev/null
+++ b/src/auditordb/auditordb-postgres.conf
@@ -0,0 +1,3 @@
+[auditordb-postgres]
+# Argument for Postgres for how to connect to the database.
+DB_CONN_STR = "postgres:///taler"
diff --git a/src/auditordb/auditordb_plugin.c b/src/auditordb/auditordb_plugin.c
new file mode 100644
index 000000000..6aa0274f8
--- /dev/null
+++ b/src/auditordb/auditordb_plugin.c
@@ -0,0 +1,88 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015, 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file auditordb/auditordb_plugin.c
+ * @brief Logic to load database plugin
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include "taler_auditordb_plugin.h"
+#include <ltdl.h>
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+struct TALER_AUDITORDB_Plugin *
+TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char *plugin_name;
+ char *lib_name;
+ struct GNUNET_CONFIGURATION_Handle *cfg_dup;
+ struct TALER_AUDITORDB_Plugin *plugin;
+
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "auditor",
+ "db",
+ &plugin_name))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "auditor",
+ "db");
+ return NULL;
+ }
+ (void) GNUNET_asprintf (&lib_name,
+ "libtaler_plugin_auditordb_%s",
+ plugin_name);
+ GNUNET_free (plugin_name);
+ cfg_dup = GNUNET_CONFIGURATION_dup (cfg);
+ plugin = GNUNET_PLUGIN_load (lib_name,
+ cfg_dup);
+ if (NULL != plugin)
+ plugin->library_name = lib_name;
+ else
+ GNUNET_free (lib_name);
+ GNUNET_CONFIGURATION_destroy (cfg_dup);
+ return plugin;
+}
+
+
+/**
+ * Shutdown the plugin.
+ *
+ * @param plugin the plugin to unload
+ */
+void
+TALER_AUDITORDB_plugin_unload (struct TALER_AUDITORDB_Plugin *plugin)
+{
+ char *lib_name;
+
+ if (NULL == plugin)
+ return;
+ lib_name = plugin->library_name;
+ GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
+ plugin));
+ GNUNET_free (lib_name);
+}
+
+
+
+/* end of auditordb_plugin.c */
diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c
new file mode 100644
index 000000000..290af9dbc
--- /dev/null
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -0,0 +1,776 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file plugin_auditordb_postgres.c
+ * @brief Low-level (statement-level) Postgres database access for the auditor
+ * @author Florian Dold
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura
+ */
+#include "platform.h"
+#include "taler_pq_lib.h"
+#include "taler_auditordb_plugin.h"
+#include <pthread.h>
+#include <libpq-fe.h>
+
+/**
+ * Log a query error.
+ *
+ * @param result PQ result object of the query that failed
+ */
+#define QUERY_ERR(result) \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s (%s)\n", __FILE__, __LINE__, PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)))
+
+
+/**
+ * Log a really unexpected PQ error.
+ *
+ * @param result PQ result object of the PQ operation that failed
+ */
+#define BREAK_DB_ERR(result) do { \
+ GNUNET_break (0); \
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s (%s)\n", PQresultErrorMessage (result), PQresStatus (PQresultStatus (result))); \
+ } while (0)
+
+
+/**
+ * Shorthand for exit jumps. Logs the current line number
+ * and jumps to the "EXITIF_exit" label.
+ *
+ * @param cond condition that must be TRUE to exit with an error
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * Execute an SQL statement and log errors on failure. Must be
+ * run in a function that has an "SQLEXEC_fail" label to jump
+ * to in case the SQL statement failed.
+ *
+ * @param conn database connection
+ * @param sql SQL statement to run
+ */
+#define SQLEXEC_(conn, sql) \
+ do { \
+ PGresult *result = PQexec (conn, sql); \
+ if (PGRES_COMMAND_OK != PQresultStatus (result)) \
+ { \
+ BREAK_DB_ERR (result); \
+ PQclear (result); \
+ goto SQLEXEC_fail; \
+ } \
+ PQclear (result); \
+ } while (0)
+
+
+/**
+ * Run an SQL statement, ignoring errors and clearing the result.
+ *
+ * @param conn database connection
+ * @param sql SQL statement to run
+ */
+#define SQLEXEC_IGNORE_ERROR_(conn, sql) \
+ do { \
+ PGresult *result = PQexec (conn, sql); \
+ PQclear (result); \
+ } while (0)
+
+
+/**
+ * Handle for a database session (per-thread, for transactions).
+ */
+struct TALER_AUDITORDB_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 *connection_cfg_str;
+};
+
+
+/**
+ * Function called by libpq whenever it wants to log something.
+ * We already log whenever we care, so this function does nothing
+ * and merely exists to silence the libpq logging.
+ *
+ * @param arg NULL
+ * @param res information about some libpq event
+ */
+static void
+pq_notice_receiver_cb (void *arg,
+ const PGresult *res)
+{
+ /* do nothing, intentionally */
+}
+
+
+/**
+ * Function called by libpq whenever it wants to log something.
+ * We log those using the Taler logger.
+ *
+ * @param arg NULL
+ * @param message information about some libpq event
+ */
+static void
+pq_notice_processor_cb (void *arg,
+ const char *message)
+{
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
+ "pq",
+ "%s",
+ message);
+}
+
+
+/**
+ * Establish connection to the Postgres database
+ * and initialize callbacks for logging.
+ *
+ * @param pc configuration to use
+ * @return NULL on error
+ */
+static PGconn *
+connect_to_postgres (struct PostgresClosure *pc)
+{
+ PGconn *conn;
+
+ conn = PQconnectdb (pc->connection_cfg_str);
+ if (CONNECTION_OK !=
+ PQstatus (conn))
+ {
+ TALER_LOG_ERROR ("Database connection failed: %s\n",
+ PQerrorMessage (conn));
+ GNUNET_break (0);
+ return NULL;
+ }
+ PQsetNoticeReceiver (conn,
+ &pq_notice_receiver_cb,
+ NULL);
+ PQsetNoticeProcessor (conn,
+ &pq_notice_processor_cb,
+ NULL);
+ return conn;
+}
+
+
+/**
+ * Drop all Taler tables. This should only be used by testcases.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+postgres_drop_tables (void *cls)
+{
+ struct PostgresClosure *pc = cls;
+ PGconn *conn;
+
+ conn = connect_to_postgres (pc);
+ if (NULL == conn)
+ return GNUNET_SYSERR;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Dropping ALL tables\n");
+ SQLEXEC_ (conn,
+ "DROP TABLE IF EXISTS test;");
+ PQfinish (conn);
+ return GNUNET_OK;
+ SQLEXEC_fail:
+ PQfinish (conn);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Create the necessary tables if they are not present
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+postgres_create_tables (void *cls)
+{
+ struct PostgresClosure *pc = cls;
+ PGconn *conn;
+
+ conn = connect_to_postgres (pc);
+ if (NULL == conn)
+ return GNUNET_SYSERR;
+#define SQLEXEC(sql) SQLEXEC_(conn, sql);
+#define SQLEXEC_INDEX(sql) SQLEXEC_IGNORE_ERROR_(conn, sql);
+
+ /* Table with all of the denomination keys that the auditor
+ is aware of. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_denominations"
+ "(denom_pub BYTEA PRIMARY KEY"
+ ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",valid_from INT8 NOT NULL"
+ ",expire_withdraw INT8 NOT NULL"
+ ",expire_deposit INT8 NOT NULL"
+ ",expire_legal INT8 NOT NULL"
+ ",coin_val INT8 NOT NULL" /* value of this denom */
+ ",coin_frac INT4 NOT NULL" /* fractional value of this denom */
+ ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" /* assuming same currency for fees */
+ ",fee_withdraw_val INT8 NOT NULL"
+ ",fee_withdraw_frac INT4 NOT NULL"
+ ",fee_withdraw_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_deposit_val INT8 NOT NULL"
+ ",fee_deposit_frac INT4 NOT NULL"
+ ",fee_deposit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refresh_val INT8 NOT NULL"
+ ",fee_refresh_frac INT4 NOT NULL"
+ ",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",fee_refund_val INT8 NOT NULL"
+ ",fee_refund_frac INT4 NOT NULL"
+ ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with all of the customer reserves and their respective
+ balances that the auditor is aware of.
+ "last_reserve_out_serial_id" marks the last withdrawal from
+ "reserves_out" about this reserve that the auditor is aware of,
+ and "last_reserve_in_serial_id" is the last "reserve_in"
+ operation about this reserve that the auditor is aware of. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_reserves"
+ "(reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)"
+ ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",reserve_balance_val INT8 NOT NULL"
+ ",reserve_balance_frac INT4 NOT NULL"
+ ",reserve_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",withdraw_fee_balance_val INT8 NOT NULL"
+ ",withdraw_fee_balance_frac INT4 NOT NULL"
+ ",withdraw_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",expiration_date INT8 NOT NULL"
+ ",last_reserve_in_serial_id INT8 NOT NULL"
+ ",last_reserve_out_serial_id INT8 NOT NULL"
+ ")");
+
+ /* Table with the sum of the balances of all customer reserves
+ (by exchange's master public key) */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_reserve_balance"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",reserve_balance_val INT8 NOT NULL"
+ ",reserve_balance_frac INT4 NOT NULL"
+ ",reserve_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",withdraw_fee_balance_val INT8 NOT NULL"
+ ",withdraw_fee_balance_frac INT4 NOT NULL"
+ ",withdraw_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with all of the outstanding denomination coins that the
+ exchange is aware of. "last_deposit_serial_id" marks the
+ deposit_serial_id from "deposits" about this denomination key
+ that the auditor is aware of; "last_melt_serial_id" marks the
+ last melt from "refresh_sessions" that the auditor is aware
+ of; "refund_serial_id" tells us the last entry in "refunds"
+ for this denom_pub that the auditor is aware of. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS denomination_pending"
+ "(denom_pub BYTEA NOT NULL REFERENCES denominations (denom_pub) ON DELETE CASCADE"
+ ",denom_balance_val INT8 NOT NULL"
+ ",denom_balance_frac INT4 NOT NULL"
+ ",denom_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",deposit_fee_balance_val INT8 NOT NULL"
+ ",deposit_fee_balance_frac INT4 NOT NULL"
+ ",deposit_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",melt_fee_balance_val INT8 NOT NULL"
+ ",melt_fee_balance_frac INT4 NOT NULL"
+ ",melt_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",refund_fee_balance_val INT8 NOT NULL"
+ ",refund_fee_balance_frac INT4 NOT NULL"
+ ",refund_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",last_deposit_serial_id INT8 NOT NULL"
+ ",last_melt_serial_id INT8 NOT NULL"
+ ",last_refund INT8 NOT NULL"
+ ")");
+
+ /* Table with the sum of the outstanding coins from
+ "denomination_pending" (denom_pubs must belong
+ to the respective's exchange's master public key);
+ it represents the total_liabilities of the exchange
+ at this point (modulo unexpected historic_loss-style
+ events where denomination keys are compromised) */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS total_liabilities"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",denom_balance_val INT8 NOT NULL"
+ ",denom_balance_frac INT4 NOT NULL"
+ ",denom_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",deposit_fee_balance_val INT8 NOT NULL"
+ ",deposit_fee_balance_frac INT4 NOT NULL"
+ ",deposit_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",melt_fee_balance_val INT8 NOT NULL"
+ ",melt_fee_balance_frac INT4 NOT NULL"
+ ",melt_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with the sum of the generated coins all
+ denomination keys. This represents the maximum
+ additional total financial risk of the exchange
+ in case that all denomination keys are compromised
+ (and all of the deposits so far were done by
+ the successful attacker). So this is strictly an
+ upper bound on the risk exposure of the exchange.
+ (Note that this risk is in addition to the known
+ total_liabilities) */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS total_risk"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",risk_val INT8 NOT NULL"
+ ",risk_frac INT4 NOT NULL"
+ ",risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+
+ /* Table with historic profits; basically, when a denom_pub
+ is expired and everything associated with it is garbage
+ collected, the final profits end up in here; note that
+ the "denom_pub" here is not a foreign key, we just keep
+ it as a reference point. "revenue_balance" is the sum
+ of all of the profits we made on the coin except for
+ withdraw fees (which are in historic_reserve_revenue);
+ the deposit and melt fees are given individually; the
+ delta to the revenue_balance is from coins that were withdrawn
+ but never deposited prior to expiration. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_denomination_revenue"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",denom_pub BYTEA NOT NULL"
+ ",revenue_timestamp INT8 NOT NULL"
+ ",revenue_balance_val INT8 NOT NULL"
+ ",revenue_balance_frac INT4 NOT NULL"
+ ",revenue_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",deposit_fee_balance_val INT8 NOT NULL"
+ ",deposit_fee_balance_frac INT4 NOT NULL"
+ ",deposit_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ",melt_fee_balance_val INT8 NOT NULL"
+ ",melt_fee_balance_frac INT4 NOT NULL"
+ ",melt_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" ")");
+
+ /* Table with historic losses; basically, when we need to
+ invalidate a denom_pub because the denom_priv was
+ compromised, we incur a loss. These losses are totaled
+ up here. (NOTE: the 'bankrupcy' protocol is not yet
+ implemented, so right now this table is not used.) */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_losses"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",denom_pub BYTEA NOT NULL"
+ ",loss_timestamp INT8 NOT NULL"
+ ",loss_balance_val INT8 NOT NULL"
+ ",loss_balance_frac INT4 NOT NULL"
+ ",loss_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with historic profits by reserve; basically, when a
+ reserve expires, we transmit the balance back to the user, but
+ rounding gains and withdraw fees are listed here. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_reserve_revenue"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)"
+ ",expiration_date INT8 NOT NULL"
+ ",reserve_profit_val INT8 NOT NULL"
+ ",reserve_profit_frac INT4 NOT NULL"
+ ",reserve_profit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with historic profits from reserves; we eventually
+ GC "historic_reserve_revenue", and then store the totals
+ in here (by time intervals). */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_reserve_summary"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",start_date INT8 NOT NULL"
+ ",end_date INT8 NOT NULL"
+ ",reserve_profits_val INT8 NOT NULL"
+ ",reserve_profits_frac INT4 NOT NULL"
+ ",reserve_profits_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with historic business ledger; basically, when the exchange
+ operator decides to use operating costs for anything but wire
+ transfers to merchants, it goes in here. This happens when the
+ operator users transaction fees for business expenses. "purpose"
+ is free-form but should be a human-readable wire transfer
+ identifier. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_ledger"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",purpose VARCHAR NOT NULL"
+ ",timestamp INT8 NOT NULL"
+ ",balance_val INT8 NOT NULL"
+ ",balance_frac INT4 NOT NULL"
+ ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+ /* Table with the sum of the ledger, historic_revenue,
+ historic_losses and the auditor_reserve_balance.
+ This is the final amount that the exchange should have
+ in its bank account right now. */
+ SQLEXEC ("CREATE TABLE IF NOT EXISTS predicted_result"
+ "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
+ ",balance_val INT8 NOT NULL"
+ ",balance_frac INT4 NOT NULL"
+ ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+ ")");
+
+
+ SQLEXEC_INDEX("CREATE INDEX testx "
+ "ON test(test_pub)");
+#undef SQLEXEC
+#undef SQLEXEC_INDEX
+
+ 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);
+
+ /* Used in #postgres_XXX() */
+ PREPARE ("test_insert",
+ "INSERT INTO test "
+ "(test_pub"
+ ") VALUES "
+ "($1);",
+ 1, NULL);
+ return GNUNET_OK;
+#undef PREPARE
+}
+
+
+/**
+ * Close thread-local database connection when a thread is destroyed.
+ *
+ * @param cls closure we get from pthreads (the db handle)
+ */
+static void
+db_conn_destroy (void *cls)
+{
+ struct TALER_AUDITORDB_Session *session = cls;
+ PGconn *db_conn = session->conn;
+
+ if (NULL != db_conn)
+ PQfinish (db_conn);
+ GNUNET_free (session);
+}
+
+
+/**
+ * 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
+ * @return the database connection, or NULL on error
+ */
+static struct TALER_AUDITORDB_Session *
+postgres_get_session (void *cls)
+{
+ struct PostgresClosure *pc = cls;
+ PGconn *db_conn;
+ struct TALER_AUDITORDB_Session *session;
+
+ if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
+ return session;
+ db_conn = connect_to_postgres (pc);
+ if (NULL == db_conn)
+ return NULL;
+ if (GNUNET_OK !=
+ postgres_prepare (db_conn))
+ {
+ GNUNET_break (0);
+ PQfinish (db_conn);
+ return NULL;
+ }
+ session = GNUNET_new (struct TALER_AUDITORDB_Session);
+ session->conn = db_conn;
+ if (0 != pthread_setspecific (pc->db_conn_threadlocal,
+ session))
+ {
+ GNUNET_break (0);
+ PQfinish (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_AUDITORDB_Session *session)
+{
+ PGresult *result;
+
+ result = PQexec (session->conn,
+ "START TRANSACTION ISOLATION LEVEL SERIALIZABLE");
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ TALER_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_AUDITORDB_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_AUDITORDB_Session *session)
+{
+ PGresult *result;
+
+ result = PQexec (session->conn,
+ "COMMIT");
+ if (PGRES_COMMAND_OK !=
+ PQresultStatus (result))
+ {
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (result,
+ PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ /* 40P01: deadlock, 40001: serialization failure */
+ if ( (0 == strcmp (sqlstate,
+ "40P01")) ||
+ (0 == strcmp (sqlstate,
+ "40001")) )
+ {
+ /* These two can be retried and have a fair chance of working
+ the next time */
+ PQclear (result);
+ return GNUNET_NO;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called to perform "garbage collection" on the
+ * database, expiring records we no longer require.
+ *
+ * @param cls closure
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on DB errors
+ */
+static int
+postgres_gc (void *cls)
+{
+ struct PostgresClosure *pc = cls;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_PQ_QueryParam params_time[] = {
+ GNUNET_PQ_query_param_absolute_time (&now),
+ GNUNET_PQ_query_param_end
+ };
+ PGconn *conn;
+ PGresult *result;
+
+ now = GNUNET_TIME_absolute_get ();
+ conn = connect_to_postgres (pc);
+ if (NULL == conn)
+ return GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ postgres_prepare (conn))
+ {
+ PQfinish (conn);
+ return GNUNET_SYSERR;
+ }
+ result = GNUNET_PQ_exec_prepared (conn,
+ "gc_auditor",
+ params_time);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ PQfinish (conn);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ PQfinish (conn);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Initialize Postgres database subsystem.
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_AUDITORDB_Plugin`
+ */
+void *
+libtaler_plugin_auditordb_postgres_init (void *cls)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct PostgresClosure *pg;
+ struct TALER_AUDITORDB_Plugin *plugin;
+ const char *ec;
+
+ pg = GNUNET_new (struct PostgresClosure);
+
+ if (0 != pthread_key_create (&pg->db_conn_threadlocal,
+ &db_conn_destroy))
+ {
+ TALER_LOG_ERROR ("Cannnot create pthread key.\n");
+ GNUNET_free (pg);
+ return NULL;
+ }
+ ec = getenv ("TALER_AUDITORDB_POSTGRES_CONFIG");
+ if (NULL != ec)
+ {
+ pg->connection_cfg_str = GNUNET_strdup (ec);
+ }
+ else
+ {
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "auditordb-postgres",
+ "db_conn_str",
+ &pg->connection_cfg_str))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "auditordb-postgres",
+ "db_conn_str");
+ GNUNET_free (pg);
+ return NULL;
+ }
+ }
+ plugin = GNUNET_new (struct TALER_AUDITORDB_Plugin);
+ plugin->cls = pg;
+ plugin->get_session = &postgres_get_session;
+ plugin->drop_tables = &postgres_drop_tables;
+ plugin->create_tables = &postgres_create_tables;
+ plugin->start = &postgres_start;
+ plugin->commit = &postgres_commit;
+ plugin->rollback = &postgres_rollback;
+ plugin->gc = &postgres_gc;
+ return plugin;
+}
+
+
+/**
+ * Shutdown Postgres database subsystem.
+ *
+ * @param cls a `struct TALER_AUDITORDB_Plugin`
+ * @return NULL (always)
+ */
+void *
+libtaler_plugin_auditordb_postgres_done (void *cls)
+{
+ struct TALER_AUDITORDB_Plugin *plugin = cls;
+ struct PostgresClosure *pg = plugin->cls;
+
+ GNUNET_free (pg->connection_cfg_str);
+ GNUNET_free (pg);
+ GNUNET_free (plugin);
+ return NULL;
+}
+
+/* end of plugin_auditordb_postgres.c */
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index a1e4f67a2..2fd4177ed 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -4,7 +4,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include
pkgcfgdir = $(prefix)/share/taler/config.d/
pkgcfg_DATA = \
- auditor.conf \
exchange-signkeys.conf \
coins.conf
@@ -14,7 +13,6 @@ if USE_COVERAGE
endif
bin_PROGRAMS = \
- taler-auditor-sign \
taler-exchange-keyup \
taler-exchange-keycheck \
taler-exchange-reservemod \
@@ -31,14 +29,6 @@ taler_exchange_keyup_LDADD = \
-lgnunetutil $(XLIB)
taler_exchange_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
-taler_auditor_sign_SOURCES = \
- taler-auditor-sign.c
-taler_auditor_sign_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
- -lgnunetutil $(XLIB)
-
taler_exchange_wire_SOURCES = \
taler-exchange-wire.c
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index a90ff849d..9229b10de 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -311,7 +311,8 @@ postgres_create_tables (void *cls)
into the reserve. The rows of this table correspond to each
incoming transaction. */
SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in"
- "(reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
+ "(reserve_in_serial_id BIGSERIAL"
+ ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
",credit_val INT8 NOT NULL"
",credit_frac INT4 NOT NULL"
",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
@@ -329,7 +330,8 @@ postgres_create_tables (void *cls)
should fail to even withdraw, as otherwise the coins will fail to deposit
(as they really must be unique). */
SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves_out"
- "(h_blind_ev BYTEA PRIMARY KEY"
+ "(reserve_out_serial_id BIGSERIAL"
+ ",h_blind_ev BYTEA PRIMARY KEY"
",denom_pub BYTEA NOT NULL REFERENCES denominations (denom_pub) ON DELETE CASCADE"
",denom_sig BYTEA NOT NULL"
",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
@@ -360,7 +362,8 @@ postgres_create_tables (void *cls)
* NOTE: maybe we should instead forbid values >= 2^15 categorically?
*/
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
- "(session_hash BYTEA PRIMARY KEY CHECK (LENGTH(session_hash)=64)"
+ "(melt_serial_id BIGSERIAL"
+ ",session_hash BYTEA PRIMARY KEY CHECK (LENGTH(session_hash)=64)"
",old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
",old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)"
",amount_with_fee_val INT8 NOT NULL"
@@ -446,7 +449,8 @@ postgres_create_tables (void *cls)
/* Table with information about coins that have been refunded. (Technically
one of the deposit operations that a coin was involved with is refunded.)*/
SQLEXEC("CREATE TABLE IF NOT EXISTS refunds "
- "(coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
+ "(refund_serial_id BIGSERIAL"
+ ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE"
",merchant_pub BYTEA NOT NULL CHECK(LENGTH(merchant_pub)=32)"
",merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)"
",h_contract BYTEA NOT NULL CHECK(LENGTH(h_contract)=64)"
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index dfecf4694..7a8f6e071 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -13,6 +13,8 @@ else
talerinclude_HEADERS = \
platform.h \
taler_amount_lib.h \
+ taler_auditordb_lib.h \
+ taler_auditordb_plugin.h \
taler_bank_service.h \
taler_crypto_lib.h \
taler_error_codes.h \
diff --git a/src/include/taler_auditordb_lib.h b/src/include/taler_auditordb_lib.h
new file mode 100644
index 000000000..0953cf110
--- /dev/null
+++ b/src/include/taler_auditordb_lib.h
@@ -0,0 +1,47 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2016 Inria & GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_auditordb_lib.h
+ * @brief high-level interface for the auditor's database
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_AUDITORDB_LIB_H
+#define TALER_AUDITORDB_LIB_H
+
+#include "taler_auditordb_plugin.h"
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return NULL on failure
+ */
+struct TALER_AUDITORDB_Plugin *
+TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Shutdown the plugin.
+ *
+ * @param plugin plugin to unload
+ */
+void
+TALER_AUDITORDB_plugin_unload (struct TALER_AUDITORDB_Plugin *plugin);
+
+
+#endif
diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h
new file mode 100644
index 000000000..ec489c62f
--- /dev/null
+++ b/src/include/taler_auditordb_plugin.h
@@ -0,0 +1,135 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_auditordb_plugin.h
+ * @brief Low-level (statement-level) database access for the auditor
+ * @author Florian Dold
+ * @author Christian Grothoff
+ */
+#ifndef TALER_AUDITORDB_PLUGIN_H
+#define TALER_AUDITORDB_PLUGIN_H
+
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_auditordb_lib.h"
+
+
+/**
+ * Handle for one session with the database.
+ */
+struct TALER_AUDITORDB_Session;
+
+
+/**
+ * @brief The plugin API, returned from the plugin's "init" function.
+ * The argument given to "init" is simply a configuration handle.
+ */
+struct TALER_AUDITORDB_Plugin
+{
+
+ /**
+ * Closure for all callbacks.
+ */
+ void *cls;
+
+ /**
+ * Name of the library which generated this plugin. Set by the
+ * plugin loader.
+ */
+ char *library_name;
+
+ /**
+ * Get the thread-local database-handle.
+ * Connect to the db if the connection does not exist yet.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param the database connection, or NULL on error
+ */
+ struct TALER_AUDITORDB_Session *
+ (*get_session) (void *cls);
+
+
+ /**
+ * Drop the Taler tables. This should only be used in 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_tables) (void *cls);
+
+
+ /**
+ * Create the necessary tables if they are not present
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+ int
+ (*create_tables) (void *cls);
+
+
+ /**
+ * Start a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @return #GNUNET_OK on success
+ */
+ int
+ (*start) (void *cls,
+ struct TALER_AUDITORDB_Session *session);
+
+
+ /**
+ * Commit a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @return #GNUNET_OK on success, #GNUNET_NO if the transaction
+ * can be retried, #GNUNET_SYSERR on hard failures
+ */
+ int
+ (*commit) (void *cls,
+ struct TALER_AUDITORDB_Session *session);
+
+
+ /**
+ * Abort/rollback a transaction.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ */
+ void
+ (*rollback) (void *cls,
+ struct TALER_AUDITORDB_Session *session);
+
+
+ /**
+ * Function called to perform "garbage collection" on the
+ * database, expiring records we no longer require.
+ *
+ * @param cls closure
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on DB errors
+ */
+ int
+ (*gc) (void *cls);
+
+};
+
+
+#endif /* _TALER_AUDITOR_DB_H */