commit c626ccac51dc968725b2ead1ecb0cebedd9987fc
parent eae1896a4bfc02b5d7586f81bbedfea69b29acc8
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 28 Mar 2015 11:06:00 +0100
second round of renamefest
Diffstat:
38 files changed, 4871 insertions(+), 4876 deletions(-)
diff --git a/configure.ac b/configure.ac
@@ -174,7 +174,9 @@ AC_CONFIG_FILES([Makefile
src/include/Makefile
src/util/Makefile
src/pq/Makefile
+ src/mintdb/Makefile
src/mint/Makefile
- src/lib/Makefile
+ src/mint-tools/Makefile
+ src/mint-lib/Makefile
])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
@@ -1,3 +1,3 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include
-SUBDIRS = include util pq mint lib
+SUBDIRS = include util pq mintdb mint mint-tools mint-lib
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
@@ -8,6 +8,7 @@ talerinclude_HEADERS = \
taler_json_lib.h \
taler_util.h \
taler_mint_service.h \
+ taler_mintdb_lib.h \
taler_mintdb_plugin.h \
taler_pq_lib.h \
taler_signatures.h
diff --git a/src/include/taler_mintdb_lib.h b/src/include/taler_mintdb_lib.h
@@ -0,0 +1,207 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/taler_mintdb_lib.h
+ * @brief IO operations for the mint's private keys
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINTDB_LIB_H
+#define TALER_MINTDB_LIB_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_signatures.h"
+
+/**
+ * Subdirectroy under the mint's base directory which contains
+ * the mint's signing keys.
+ */
+#define DIR_SIGNKEYS "signkeys"
+
+/**
+ * Subdirectory under the mint's base directory which contains
+ * the mint's denomination keys.
+ */
+#define DIR_DENOMKEYS "denomkeys"
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * On disk format used for a mint signing key. Signing keys are used
+ * by the mint to affirm its messages, but not to create coins.
+ * Includes the private key followed by the public information about
+ * the signing key.
+ */
+struct TALER_MintSigningKeyValidityPSPriv
+{
+ /**
+ * Private key part of the mint's signing key.
+ */
+ struct TALER_MintPrivateKeyP signkey_priv;
+
+ /**
+ * Public information about a mint signing key.
+ */
+ struct TALER_MintSigningKeyValidityPS issue;
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * All information about a denomination key (which is used to
+ * sign coins into existence).
+ */
+struct TALER_DenominationKeyIssueInformation
+{
+ /**
+ * The private key of the denomination. Will be NULL if the private
+ * key is not available (this is the case after the key has expired
+ * for signing coins, but is still valid for depositing coins).
+ */
+ struct TALER_DenominationPrivateKey denom_priv;
+
+ /**
+ * Decoded denomination public key (the hash of it is in
+ * @e issue, but we sometimes need the full public key as well).
+ */
+ struct TALER_DenominationPublicKey denom_pub;
+
+ /**
+ * Signed public information about a denomination key.
+ */
+ struct TALER_DenominationKeyValidityPS issue;
+};
+
+
+/**
+ * Iterator over signing keys.
+ *
+ * @param cls closure
+ * @param filename name of the file the key came from
+ * @param ski the sign key
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int
+(*TALER_MINT_SignkeyIterator)(void *cls,
+ const char *filename,
+ const struct TALER_MintSigningKeyValidityPSPriv *ski);
+
+
+/**
+ * Iterator over denomination keys.
+ *
+ * @param cls closure
+ * @param dki the denomination key
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+typedef int
+(*TALER_MINT_DenomkeyIterator)(void *cls,
+ const char *alias,
+ const struct TALER_DenominationKeyIssueInformation *dki);
+
+
+
+/**
+ * Call @a it for each signing key found in the @a mint_base_dir.
+ *
+ * @param mint_base_dir base directory for the mint,
+ * the signing keys must be in the #DIR_SIGNKEYS
+ * subdirectory
+ * @param it function to call on each signing key
+ * @param it_cls closure for @a it
+ * @return number of files found (may not match
+ * number of keys given to @a it as malformed
+ * files are simply skipped), -1 on error
+ */
+int
+TALER_MINT_signkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_SignkeyIterator it,
+ void *it_cls);
+
+
+/**
+ * Call @a it for each denomination key found in the @a mint_base_dir.
+ *
+ * @param mint_base_dir base directory for the mint,
+ * the signing keys must be in the #DIR_DENOMKEYS
+ * subdirectory
+ * @param it function to call on each denomination key found
+ * @param it_cls closure for @a it
+ * @return -1 on error, 0 if no files were found, otherwise
+ * a positive number (however, even with a positive
+ * number it is possible that @a it was never called
+ * as maybe none of the files were well-formed)
+ */
+int
+TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_DenomkeyIterator it,
+ void *it_cls);
+
+
+/**
+ * Exports a denomination key to the given file.
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINT_write_denom_key (const char *filename,
+ const struct TALER_DenominationKeyIssueInformation *dki);
+
+
+/**
+ * Import a denomination key from the given file.
+ *
+ * @param filename the file to import the key from
+ * @param[OUT] dki set to the imported denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINT_read_denom_key (const char *filename,
+ struct TALER_DenominationKeyIssueInformation *dki);
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return NULL on failure
+ */
+struct TALER_MINTDB_Plugin *
+TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Shutdown the plugin.
+ *
+ * @param plugin plugin to unload
+ */
+void
+TALER_MINT_plugin_unload (struct TALER_MINTDB_Plugin *plugin);
+
+
+
+#endif
diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h
@@ -479,9 +479,10 @@ struct TALER_MINTDB_Plugin
void *cls;
/**
- * Name of the library which generated this plugin
+ * Name of the library which generated this plugin. Set by the
+ * plugin loader.
*/
- const char *library_name;
+ char *library_name;
/**
* Get the thread-local database-handle.
diff --git a/src/lib/Makefile.am b/src/mint-lib/Makefile.am
diff --git a/src/lib/mint_api.c b/src/mint-lib/mint_api.c
diff --git a/src/lib/test_mint_api.c b/src/mint-lib/test_mint_api.c
diff --git a/src/mint-tools/Makefile.am b/src/mint-tools/Makefile.am
@@ -0,0 +1,63 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+bin_PROGRAMS = \
+ taler-mint-keyup \
+ taler-mint-keycheck \
+ taler-mint-reservemod \
+ taler-mint-dbinit
+
+taler_mint_keyup_SOURCES = \
+ taler-mint-keyup.c
+
+taler_mint_keyup_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/mint/libtalermint_common.la \
+ -lgnunetutil
+taler_mint_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+
+taler_mint_keycheck_SOURCES = \
+ taler-mint-keycheck.c
+
+taler_mint_keycheck_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/mint/libtalermint_common.la \
+ -lgnunetutil
+taler_mint_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+taler_mint_reservemod_SOURCES = \
+ taler-mint-reservemod.c
+taler_mint_reservemod_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/mint/libtalermint_common.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_reservemod_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+taler_mint_reservemod_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/pq/ \
+ $(POSTGRESQL_CPPFLAGS)
+
+taler_mint_dbinit_SOURCES = \
+ taler-mint-dbinit.c
+taler_mint_dbinit_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/mint/libtalermint_common.la \
+ -lpq \
+ -lgnunetutil
+taler_mint_dbinit_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+taler_mint_dbinit_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/pq/ \
+ $(POSTGRESQL_CPPFLAGS)
diff --git a/src/mint-tools/taler-mint-dbinit.c b/src/mint-tools/taler-mint-dbinit.c
@@ -0,0 +1,106 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint/taler-mint-dbinit.c
+ * @brief Create tables for the mint database.
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "taler_util.h"
+#include "taler_mintdb_plugin.h"
+#include "taler_mintdb_lib.h"
+
+/**
+ * Mint directory with the keys.
+ */
+static char *mint_base_dir;
+
+/**
+ * Our configuration.
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Our DB plugin.
+ */
+static struct TALER_MINTDB_Plugin *plugin;
+
+
+/**
+ * The main function of the database initialization tool.
+ * Used to initialize the Taler Mint'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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-dbinit OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory", 1,
+ &GNUNET_GETOPT_set_filename, &mint_base_dir},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (GNUNET_GETOPT_run ("taler-mint-dbinit",
+ options,
+ argc, argv) < 0)
+ return 1;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-dbinit",
+ "INFO",
+ NULL));
+ if (NULL == mint_base_dir)
+ {
+ fprintf (stderr,
+ "Mint base directory not given.\n");
+ return 1;
+ }
+ cfg = TALER_config_load (mint_base_dir);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "Failed to load mint configuration.\n");
+ return 1;
+ }
+ if (NULL ==
+ (plugin = TALER_MINT_plugin_load (cfg)))
+ {
+ fprintf (stderr,
+ "Failed to initialize database plugin.\n");
+ return 1;
+ }
+ if (GNUNET_OK !=
+ plugin->create_tables (plugin->cls,
+ GNUNET_NO))
+ {
+ fprintf (stderr,
+ "Failed to initialize database.\n");
+ TALER_MINT_plugin_unload (plugin);
+ return 1;
+ }
+ TALER_MINT_plugin_unload (plugin);
+ return 0;
+}
+
+/* end of taler-mint-dbinit.c */
diff --git a/src/mint-tools/taler-mint-keycheck.c b/src/mint-tools/taler-mint-keycheck.c
@@ -0,0 +1,227 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-keycheck.c
+ * @brief Check mint keys for validity. Reads the signing and denomination
+ * keys from the mint directory and checks to make sure they are
+ * well-formed. This is purely a diagnostic tool.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mintdb_lib.h"
+
+/**
+ * Mint directory with the keys.
+ */
+static char *mint_directory;
+
+/**
+ * Our configuration.
+ */
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+
+/**
+ * Function called on each signing key.
+ *
+ * @param cls closure (NULL)
+ * @param filename name of the file the key came from
+ * @param ski the sign key
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+signkeys_iter (void *cls,
+ const char *filename,
+ const struct TALER_MintSigningKeyValidityPSPriv *ski)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Iterating over key `%s' for start time %s\n",
+ filename,
+ GNUNET_STRINGS_absolute_time_to_string
+ (GNUNET_TIME_absolute_ntoh (ski->issue.start)));
+
+ if (ntohl (ski->issue.purpose.size) !=
+ (sizeof (struct TALER_MintSigningKeyValidityPS) -
+ offsetof (struct TALER_MintSigningKeyValidityPS, purpose)))
+ {
+ fprintf (stderr,
+ "Signing key `%s' has invalid purpose size\n",
+ filename);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY,
+ &ski->issue.purpose,
+ &ski->issue.signature.eddsa_signature,
+ &ski->issue.master_public_key.eddsa_pub))
+ {
+ fprintf (stderr,
+ "Signing key `%s' has invalid signature\n",
+ filename);
+ return GNUNET_SYSERR;
+ }
+ printf ("Signing key `%s' valid\n",
+ filename);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Check signing keys.
+ *
+ * @return #GNUNET_OK if the keys are OK
+ * #GNUNET_NO if not
+ */
+static int
+mint_signkeys_check ()
+{
+ if (0 > TALER_MINT_signkeys_iterate (mint_directory,
+ &signkeys_iter,
+ NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called on each denomination key.
+ *
+ * @param cls closure (NULL)
+ * @param dki the denomination key
+ * @param alias coin alias
+ * @return #GNUNET_OK to continue to iterate,
+ * #GNUNET_NO to stop iteration with no error,
+ * #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+denomkeys_iter (void *cls,
+ const char *alias,
+ const struct TALER_DenominationKeyIssueInformation *dki)
+{
+ struct GNUNET_HashCode hc;
+
+ if (ntohl (dki->issue.purpose.size) !=
+ sizeof (struct TALER_DenominationKeyValidityPS))
+ {
+ fprintf (stderr,
+ "Denomination key for `%s' has invalid purpose size\n",
+ alias);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY,
+ &dki->issue.purpose,
+ &dki->issue.signature.eddsa_signature,
+ &dki->issue.master.eddsa_pub))
+ {
+ fprintf (stderr,
+ "Denomination key for `%s' has invalid signature\n",
+ alias);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
+ &hc);
+ if (0 != memcmp (&hc,
+ &dki->issue.denom_hash,
+ sizeof (struct GNUNET_HashCode)))
+ {
+ fprintf (stderr,
+ "Public key for `%s' does not match signature\n",
+ alias);
+ return GNUNET_SYSERR;
+ }
+ printf ("Denomination key `%s' is valid\n",
+ alias);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Check denomination keys.
+ *
+ * @return #GNUNET_OK if the keys are OK
+ * #GNUNET_NO if not
+ */
+static int
+mint_denomkeys_check ()
+{
+ if (0 > TALER_MINT_denomkeys_iterate (mint_directory,
+ &denomkeys_iter,
+ NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the keyup tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keycheck OPTIONS"),
+ {'d', "directory", "DIRECTORY",
+ "mint directory with keys to check", 1,
+ &GNUNET_GETOPT_set_filename, &mint_directory},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-keycheck",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keycheck",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == mint_directory)
+ {
+ fprintf (stderr,
+ "Mint directory not given\n");
+ return 1;
+ }
+
+ kcfg = TALER_config_load (mint_directory);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr,
+ "Failed to load mint configuration\n");
+ return 1;
+ }
+ if ( (GNUNET_OK != mint_signkeys_check ()) ||
+ (GNUNET_OK != mint_denomkeys_check ()) )
+ {
+ GNUNET_CONFIGURATION_destroy (kcfg);
+ return 1;
+ }
+ GNUNET_CONFIGURATION_destroy (kcfg);
+ return 0;
+}
+
+/* end of taler-mint-keycheck.c */
diff --git a/src/mint-tools/taler-mint-keyup.c b/src/mint-tools/taler-mint-keyup.c
@@ -0,0 +1,954 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-keyup.c
+ * @brief Update the mint's keys for coins and signatures,
+ * using the mint's offline master key.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_mintdb_lib.h"
+
+/**
+ * When generating filenames from a cryptographic hash, we do not use
+ * all 512 bits but cut off after this number of characters (in
+ * base32-encoding). Base32 is 5 bit per character, and given that we
+ * have very few coin types we hash, at 100 bits the chance of
+ * collision (by accident over tiny set -- birthday paradox does not
+ * apply here!) is negligible.
+ */
+#define HASH_CUTOFF 20
+
+/**
+ * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
+ *
+ * @param name value to round
+ * @param field rel_value_us or abs_value_us
+ */
+#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000);
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Struct with all of the key information for a kind of coin. Hashed
+ * to generate a unique directory name per coin type.
+ */
+struct CoinTypeNBOP
+{
+ /**
+ * How long can the coin be spend?
+ */
+ struct GNUNET_TIME_RelativeNBO duration_spend;
+
+ /**
+ * How long can the coin be withdrawn (generated)?
+ */
+ struct GNUNET_TIME_RelativeNBO duration_withdraw;
+
+ /**
+ * What is the value of the coin?
+ */
+ struct TALER_AmountNBO value;
+
+ /**
+ * What is the fee charged for withdrawl?
+ */
+ struct TALER_AmountNBO fee_withdraw;
+
+ /**
+ * What is the fee charged for deposits?
+ */
+ struct TALER_AmountNBO fee_deposit;
+
+ /**
+ * What is the fee charged for melting?
+ */
+ struct TALER_AmountNBO fee_refresh;
+
+ /**
+ * Key size in NBO.
+ */
+ uint32_t rsa_keysize;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Set of all of the parameters that chracterize a coin.
+ */
+struct CoinTypeParams
+{
+
+ /**
+ * How long can the coin be spend? Should be significantly
+ * larger than @e duration_withdraw (i.e. years).
+ */
+ struct GNUNET_TIME_Relative duration_spend;
+
+ /**
+ * How long can the coin be withdrawn (generated)? Should be small
+ * enough to limit how many coins will be signed into existence with
+ * the same key, but large enough to still provide a reasonable
+ * anonymity set.
+ */
+ struct GNUNET_TIME_Relative duration_withdraw;
+
+ /**
+ * How much should coin creation (@e duration_withdraw) duration
+ * overlap with the next coin? Basically, the starting time of two
+ * coins is always @e duration_withdraw - @e duration_overlap apart.
+ */
+ struct GNUNET_TIME_Relative duration_overlap;
+
+ /**
+ * What is the value of the coin?
+ */
+ struct TALER_Amount value;
+
+ /**
+ * What is the fee charged for withdrawl?
+ */
+ struct TALER_Amount fee_withdraw;
+
+ /**
+ * What is the fee charged for deposits?
+ */
+ struct TALER_Amount fee_deposit;
+
+ /**
+ * What is the fee charged for melting?
+ */
+ struct TALER_Amount fee_refresh;
+
+ /**
+ * Time at which this coin is supposed to become valid.
+ */
+ struct GNUNET_TIME_Absolute anchor;
+
+ /**
+ * Length of the RSA key in bits.
+ */
+ uint32_t rsa_keysize;
+};
+
+
+/**
+ * Filename of the master private key.
+ */
+static char *masterkeyfile;
+
+/**
+ * Director of the mint, containing the keys.
+ */
+static char *mint_directory;
+
+/**
+ * Time to pretend when the key update is executed.
+ */
+static char *pretend_time_str;
+
+/**
+ * Handle to the mint's configuration
+ */
+static struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+/**
+ * Time when the key update is executed. Either the actual current time, or a
+ * pretended time.
+ */
+static struct GNUNET_TIME_Absolute now;
+
+/**
+ * Master private key of the mint.
+ */
+static struct TALER_MasterPrivateKeyP master_priv;
+
+/**
+ * Master public key of the mint.
+ */
+static struct TALER_MasterPublicKeyP master_public_key;
+
+/**
+ * Until what time do we provide keys?
+ */
+static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
+
+
+/**
+ * Obtain the name of the directory we use to store signing
+ * keys created at time @a start.
+ *
+ * @param start time at which we create the signing key
+ * @return name of the directory we should use, basically "$MINTDIR/$TIME/";
+ * (valid until next call to this function)
+ */
+static const char *
+get_signkey_file (struct GNUNET_TIME_Absolute start)
+{
+ static char dir[4096];
+
+ GNUNET_snprintf (dir,
+ sizeof (dir),
+ "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu",
+ mint_directory,
+ (unsigned long long) start.abs_value_us);
+ return dir;
+}
+
+
+/**
+ * Hash the data defining the coin type. Exclude information that may
+ * not be the same for all instances of the coin type (i.e. the
+ * anchor, overlap).
+ *
+ * @param p coin parameters to convert to a hash
+ * @param hash[OUT] set to the hash matching @a p
+ */
+static void
+hash_coin_type (const struct CoinTypeParams *p,
+ struct GNUNET_HashCode *hash)
+{
+ struct CoinTypeNBOP p_nbo;
+
+ memset (&p_nbo,
+ 0,
+ sizeof (struct CoinTypeNBOP));
+ p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
+ p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
+ TALER_amount_hton (&p_nbo.value,
+ &p->value);
+ TALER_amount_hton (&p_nbo.fee_withdraw,
+ &p->fee_withdraw);
+ TALER_amount_hton (&p_nbo.fee_deposit,
+ &p->fee_deposit);
+ TALER_amount_hton (&p_nbo.fee_refresh,
+ &p->fee_refresh);
+ p_nbo.rsa_keysize = htonl (p->rsa_keysize);
+ GNUNET_CRYPTO_hash (&p_nbo,
+ sizeof (struct CoinTypeNBOP),
+ hash);
+}
+
+
+/**
+ * Obtain the name of the directory we should use to store coins of
+ * the given type. The directory name has the format
+ * "$MINTDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the
+ * coin and "$HASH" encodes all of the coin's parameters, generating a
+ * unique string for each type of coin. Note that the "$HASH"
+ * includes neither the absolute creation time nor the key of the
+ * coin, thus the files in the subdirectory really just refer to the
+ * same type of coins, not the same coin.
+ *
+ * @param p coin parameters to convert to a directory name
+ * @return directory name (valid until next call to this function)
+ */
+static const char *
+get_cointype_dir (const struct CoinTypeParams *p)
+{
+ static char dir[4096];
+ struct GNUNET_HashCode hash;
+ char *hash_str;
+ char *val_str;
+ size_t i;
+
+ hash_coin_type (p, &hash);
+ hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash,
+ sizeof (struct GNUNET_HashCode));
+ GNUNET_assert (NULL != hash_str);
+ GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
+ hash_str[HASH_CUTOFF] = 0;
+
+ val_str = TALER_amount_to_string (&p->value);
+ for (i = 0; i < strlen (val_str); i++)
+ if ( (':' == val_str[i]) ||
+ ('.' == val_str[i]) )
+ val_str[i] = '_';
+
+ GNUNET_snprintf (dir,
+ sizeof (dir),
+ "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s",
+ mint_directory,
+ val_str,
+ hash_str);
+ GNUNET_free (hash_str);
+ GNUNET_free (val_str);
+ return dir;
+}
+
+
+/**
+ * Obtain the name of the file we would use to store the key
+ * information for a coin of the given type @a p and validity
+ * start time @a start
+ *
+ * @param p parameters for the coin
+ * @param start when would the coin begin to be issued
+ * @return name of the file to use for this coin
+ * (valid until next call to this function)
+ */
+static const char *
+get_cointype_file (const struct CoinTypeParams *p,
+ struct GNUNET_TIME_Absolute start)
+{
+ static char filename[4096];
+ const char *dir;
+
+ dir = get_cointype_dir (p);
+ GNUNET_snprintf (filename,
+ sizeof (filename),
+ "%s" DIR_SEPARATOR_STR "%llu",
+ dir,
+ (unsigned long long) start.abs_value_us);
+ return filename;
+}
+
+
+/**
+ * Get the latest key file from a past run of the key generation
+ * tool. Used to calculate the starting time for the keys we
+ * generate during this invocation. This function is used to
+ * handle both signing keys and coin keys, as in both cases
+ * the filenames correspond to the timestamps we need.
+ *
+ * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated
+ * to contain the highest timestamp (below #now)
+ * that was found
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK (to continue to iterate)
+ */
+static int
+get_anchor_iter (void *cls,
+ const char *filename)
+{
+ struct GNUNET_TIME_Absolute *anchor = cls;
+ struct GNUNET_TIME_Absolute stamp;
+ const char *base;
+ char *end = NULL;
+
+ base = GNUNET_STRINGS_get_short_name (filename);
+ stamp.abs_value_us = strtol (base,
+ &end,
+ 10);
+ if ((NULL == end) || (0 != *end))
+ {
+ fprintf(stderr,
+ "Ignoring unexpected file `%s'.\n",
+ filename);
+ return GNUNET_OK;
+ }
+ if (stamp.abs_value_us <= now.abs_value_us)
+ *anchor = GNUNET_TIME_absolute_max (stamp,
+ *anchor);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the timestamp where the first new key should be generated.
+ * Relies on correctly named key files (as we do not parse them,
+ * but just look at the filenames to "guess" at their contents).
+ *
+ * @param dir directory that should contain the existing keys
+ * @param duration how long is one key valid (for signing)?
+ * @param overlap what's the overlap between the keys validity period?
+ * @param[out] anchor the timestamp where the first new key should be generated
+ */
+static void
+get_anchor (const char *dir,
+ struct GNUNET_TIME_Relative duration,
+ struct GNUNET_TIME_Relative overlap,
+ struct GNUNET_TIME_Absolute *anchor)
+{
+ GNUNET_assert (0 == duration.rel_value_us % 1000000);
+ GNUNET_assert (0 == overlap.rel_value_us % 1000000);
+ if (GNUNET_YES !=
+ GNUNET_DISK_directory_test (dir,
+ GNUNET_YES))
+ {
+ *anchor = now;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No existing keys found, starting with fresh key set.\n");
+ return;
+ }
+ *anchor = GNUNET_TIME_UNIT_ZERO_ABS;
+ if (-1 ==
+ GNUNET_DISK_directory_scan (dir,
+ &get_anchor_iter,
+ anchor))
+ {
+ *anchor = now;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No existing keys found, starting with fresh key set.\n");
+ return;
+ }
+
+ /* FIXME: this check is a bit dubious, as 'now'
+ may be way into the future if we want to generate
+ many keys... #3727*/
+ if ((GNUNET_TIME_absolute_add (*anchor,
+ duration)).abs_value_us < now.abs_value_us)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Existing keys are way too old, starting with fresh key set.\n");
+ *anchor = now;
+ }
+ else if (anchor->abs_value_us != now.abs_value_us) // Also odd...
+ {
+ /* Real starting time is the last start time + duration - overlap */
+ *anchor = GNUNET_TIME_absolute_add (*anchor,
+ duration);
+ *anchor = GNUNET_TIME_absolute_subtract (*anchor,
+ overlap);
+ }
+ /* anchor is now the stamp where we need to create a new key */
+}
+
+
+/**
+ * Create a mint signing key (for signing mint messages, not for coins)
+ * and assert its correctness by signing it with the master key.
+ *
+ * @param start start time of the validity period for the key
+ * @param duration how long should the key be valid
+ * @param pi[OUT] set to the signing key information
+ */
+static void
+create_signkey_issue_priv (struct GNUNET_TIME_Absolute start,
+ struct GNUNET_TIME_Relative duration,
+ struct TALER_MintSigningKeyValidityPSPriv *pi)
+{
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+ struct TALER_MintSigningKeyValidityPS *issue = &pi->issue;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ pi->signkey_priv.eddsa_priv = *priv;
+ GNUNET_free (priv);
+ issue->master_public_key = master_public_key;
+ issue->start = GNUNET_TIME_absolute_hton (start);
+ issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start,
+ duration));
+ GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv.eddsa_priv,
+ &issue->signkey_pub.eddsa_pub);
+ issue->purpose.purpose = htonl (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY);
+ issue->purpose.size = htonl (sizeof (struct TALER_MintSigningKeyValidityPS) -
+ offsetof (struct TALER_MintSigningKeyValidityPS,
+ purpose));
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
+ &issue->purpose,
+ &issue->signature.eddsa_signature));
+}
+
+
+/**
+ * Generate signing keys starting from the last key found to
+ * the lookahead time.
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+mint_keys_update_signkeys ()
+{
+ struct GNUNET_TIME_Relative signkey_duration;
+ struct GNUNET_TIME_Absolute anchor;
+ char *signkey_dir;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ "mint_keys",
+ "signkey_duration",
+ &signkey_duration))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint_keys",
+ "signkey_duration");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (signkey_duration,
+ rel_value_us);
+ GNUNET_asprintf (&signkey_dir,
+ "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS,
+ mint_directory);
+ /* make sure the directory exists */
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create (signkey_dir))
+ {
+ fprintf (stderr,
+ "Failed to create signing key directory\n");
+ return GNUNET_SYSERR;
+ }
+
+ get_anchor (signkey_dir,
+ signkey_duration,
+ GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */,
+ &anchor);
+
+ while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
+ {
+ const char *skf;
+ struct TALER_MintSigningKeyValidityPSPriv signkey_issue;
+ ssize_t nwrite;
+
+ skf = get_signkey_file (anchor);
+ GNUNET_break (GNUNET_YES !=
+ GNUNET_DISK_file_test (skf));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Generating signing key for %s.\n",
+ GNUNET_STRINGS_absolute_time_to_string (anchor));
+ create_signkey_issue_priv (anchor,
+ signkey_duration,
+ &signkey_issue);
+ nwrite = GNUNET_DISK_fn_write (skf,
+ &signkey_issue,
+ sizeof (struct TALER_MintSigningKeyValidityPS),
+ GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ);
+ if (nwrite != sizeof (struct TALER_MintSigningKeyValidityPS))
+ {
+ fprintf (stderr,
+ "Failed to write to file `%s': %s\n",
+ skf,
+ STRERROR (errno));
+ return GNUNET_SYSERR;
+ }
+ anchor = GNUNET_TIME_absolute_add (anchor,
+ signkey_duration);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse configuration for coin type parameters. Also determines
+ * our anchor by looking at the existing coins of the same type.
+ *
+ * @param ct section in the configuration file giving the coin type parameters
+ * @param params[OUT] set to the coin parameters from the configuration
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
+ */
+static int
+get_cointype_params (const char *ct,
+ struct CoinTypeParams *params)
+{
+ const char *dir;
+ unsigned long long rsa_keysize;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ ct,
+ "duration_withdraw",
+ ¶ms->duration_withdraw))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "duration_withdraw");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_withdraw,
+ rel_value_us);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ ct,
+ "duration_spend",
+ ¶ms->duration_spend))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "duration_spend");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_spend,
+ rel_value_us);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ ct,
+ "duration_overlap",
+ ¶ms->duration_overlap))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "mint_denom_duration_overlap");
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (params->duration_overlap,
+ rel_value_us);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (kcfg,
+ ct,
+ "rsa_keysize",
+ &rsa_keysize))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "rsa_keysize");
+ return GNUNET_SYSERR;
+ }
+ if ( (rsa_keysize > 4 * 2048) ||
+ (rsa_keysize < 1024) )
+ {
+ fprintf (stderr,
+ "Given RSA keysize %llu outside of permitted range\n",
+ rsa_keysize);
+ return GNUNET_SYSERR;
+ }
+ params->rsa_keysize = (unsigned int) rsa_keysize;
+ if (GNUNET_OK !=
+ TALER_config_get_denom (kcfg,
+ ct,
+ "value",
+ ¶ms->value))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "value");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_denom (kcfg,
+ ct,
+ "fee_withdraw",
+ ¶ms->fee_withdraw))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "fee_withdraw");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_denom (kcfg,
+ ct,
+ "fee_deposit",
+ ¶ms->fee_deposit))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "fee_deposit");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_denom (kcfg,
+ ct,
+ "fee_refresh",
+ ¶ms->fee_refresh))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "fee_refresh");
+ return GNUNET_SYSERR;
+ }
+
+ dir = get_cointype_dir (params);
+ get_anchor (dir,
+ params->duration_spend,
+ params->duration_overlap,
+ ¶ms->anchor);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Initialize the private and public key information structure for
+ * signing coins into existence. Generates the private signing key
+ * and signes it together with the coin's meta data using the master
+ * signing key.
+ *
+ * @param params parameters used to initialize the @a dki
+ * @param dki[OUT] initialized according to @a params
+ */
+static void
+create_denomkey_issue (const struct CoinTypeParams *params,
+ struct TALER_DenominationKeyIssueInformation *dki)
+{
+ dki->denom_priv.rsa_private_key
+ = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize);
+ GNUNET_assert (NULL != dki->denom_priv.rsa_private_key);
+ dki->denom_pub.rsa_public_key
+ = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv.rsa_private_key);
+ GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
+ &dki->issue.denom_hash);
+ dki->issue.master = master_public_key;
+ dki->issue.start = GNUNET_TIME_absolute_hton (params->anchor);
+ dki->issue.expire_withdraw =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
+ params->duration_withdraw));
+ dki->issue.expire_spend =
+ GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
+ params->duration_spend));
+ TALER_amount_hton (&dki->issue.value,
+ ¶ms->value);
+ TALER_amount_hton (&dki->issue.fee_withdraw,
+ ¶ms->fee_withdraw);
+ TALER_amount_hton (&dki->issue.fee_deposit,
+ ¶ms->fee_deposit);
+ TALER_amount_hton (&dki->issue.fee_refresh,
+ ¶ms->fee_refresh);
+ dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY);
+ dki->issue.purpose.size = htonl (sizeof (struct TALER_DenominationKeyIssueInformation) -
+ offsetof (struct TALER_DenominationKeyIssueInformation,
+ issue.purpose));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
+ &dki->issue.purpose,
+ &dki->issue.signature.eddsa_signature));
+}
+
+
+/**
+ * Generate new coin signing keys for the coin type of the given @a
+ * coin_alias.
+ *
+ * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
+ * @param coin_alias name of the coin's section in the configuration
+ */
+static void
+mint_keys_update_cointype (void *cls,
+ const char *coin_alias)
+{
+ int *ret = cls;
+ struct CoinTypeParams p;
+ const char *dkf;
+ struct TALER_DenominationKeyIssueInformation denomkey_issue;
+
+ if (0 != strncasecmp (coin_alias,
+ "coin_",
+ strlen ("coin_")))
+ return; /* not a coin definition */
+ if (GNUNET_OK !=
+ get_cointype_params (coin_alias,
+ &p))
+ {
+ *ret = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create (get_cointype_dir (&p)))
+ {
+ *ret = GNUNET_SYSERR;
+ return;
+ }
+
+ while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
+ {
+ dkf = get_cointype_file (&p,
+ p.anchor);
+ GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Generating denomination key for type `%s', start %s at %s\n",
+ coin_alias,
+ GNUNET_STRINGS_absolute_time_to_string (p.anchor),
+ dkf);
+ create_denomkey_issue (&p,
+ &denomkey_issue);
+ if (GNUNET_OK !=
+ TALER_MINT_write_denom_key (dkf,
+ &denomkey_issue))
+ {
+ fprintf (stderr,
+ "Failed to write denomination key information to file `%s'.\n",
+ dkf);
+ *ret = GNUNET_SYSERR;
+ GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
+ return;
+ }
+ GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
+ p.anchor = GNUNET_TIME_absolute_add (p.anchor,
+ p.duration_spend);
+ p.anchor = GNUNET_TIME_absolute_subtract (p.anchor,
+ p.duration_overlap);
+ }
+}
+
+
+/**
+ * Update all of the denomination keys of the mint.
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+mint_keys_update_denomkeys ()
+{
+ int ok;
+
+ ok = GNUNET_OK;
+ GNUNET_CONFIGURATION_iterate_sections (kcfg,
+ &mint_keys_update_cointype,
+ &ok);
+ return ok;
+}
+
+
+/**
+ * The main function of the taler-mint-keyup tool. This tool is used
+ * to create the signing and denomination keys for the mint. It uses
+ * the long-term offline private key and writes the (additional) key
+ * files to the respective mint directory (from where they can then be
+ * copied to the online server). Note that we need (at least) the
+ * most recent generated previous keys so as to align the validity
+ * periods.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
+ {'m', "master-key", "FILE",
+ "master key file (private key)", 1,
+ &GNUNET_GETOPT_set_filename, &masterkeyfile},
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mint_directory},
+ {'t', "time", "TIMESTAMP",
+ "pretend it is a different time for the update", 0,
+ &GNUNET_GETOPT_set_string, &pretend_time_str},
+ GNUNET_GETOPT_OPTION_END
+ };
+ struct GNUNET_TIME_Relative lookahead_sign;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-keyup",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-keyup",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == mint_directory)
+ {
+ fprintf (stderr,
+ "Mint directory not given\n");
+ return 1;
+ }
+ if (NULL != pretend_time_str)
+ {
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str,
+ &now))
+ {
+ fprintf (stderr,
+ "timestamp `%s' invalid\n",
+ pretend_time_str);
+ return 1;
+ }
+ }
+ else
+ {
+ now = GNUNET_TIME_absolute_get ();
+ }
+ ROUND_TO_SECS (now, abs_value_us);
+
+ kcfg = TALER_config_load (mint_directory);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr,
+ "Failed to load mint configuration\n");
+ return 1;
+ }
+ if (NULL == masterkeyfile)
+ {
+ fprintf (stderr,
+ "Master key file not given\n");
+ return 1;
+ }
+ eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
+ if (NULL == eddsa_priv)
+ {
+ fprintf (stderr,
+ "Failed to initialize master key from file `%s'\n",
+ masterkeyfile);
+ return 1;
+ }
+ master_priv.eddsa_priv = *eddsa_priv;
+ GNUNET_free (eddsa_priv);
+ GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
+ &master_public_key.eddsa_pub);
+
+ /* check if key from file matches the one from the configuration */
+ {
+ struct GNUNET_CRYPTO_EddsaPublicKey master_public_key_from_cfg;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_data (kcfg,
+ "mint",
+ "master_public_key",
+ &master_public_key_from_cfg,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "master_public_key");
+ return 1;
+ }
+ if (0 !=
+ memcmp (&master_public_key,
+ &master_public_key_from_cfg,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "master_public_key",
+ _("does not match with private key"));
+ return 1;
+ }
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ "mint_keys",
+ "lookahead_sign",
+ &lookahead_sign))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint_keys",
+ "lookahead_sign");
+ return GNUNET_SYSERR;
+ }
+ if (0 == lookahead_sign.rel_value_us)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "mint_keys",
+ "lookahead_sign",
+ _("must not be zero"));
+ return GNUNET_SYSERR;
+ }
+ ROUND_TO_SECS (lookahead_sign,
+ rel_value_us);
+ lookahead_sign_stamp = GNUNET_TIME_absolute_add (now,
+ lookahead_sign);
+
+
+ /* finally, do actual work */
+ if (GNUNET_OK != mint_keys_update_signkeys ())
+ return 1;
+
+ if (GNUNET_OK != mint_keys_update_denomkeys ())
+ return 1;
+ return 0;
+}
+
+/* end of taler-mint-keyup.c */
diff --git a/src/mint-tools/taler-mint-reservemod.c b/src/mint-tools/taler-mint-reservemod.c
@@ -0,0 +1,305 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-reservemod.c
+ * @brief Modify reserves. Allows manipulation of reserve balances.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_mintdb_plugin.h"
+#include "taler_mintdb_lib.h"
+
+
+/**
+ * Director of the mint, containing the keys.
+ */
+static char *mint_directory;
+
+/**
+ * Public key of the reserve to manipulate.
+ */
+static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub;
+
+/**
+ * Handle to the mint's configuration
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Database connection handle.
+ */
+static PGconn *db_conn;
+
+
+/**
+ * Create a new or add to existing reserve. Fails if currencies do
+ * not match.
+ *
+ * @param denom denomination to add
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on error
+ */
+// FIXME: this should use the DB abstraction layer. (#3717)
+// FIXME: this should be done by adding an inbound transaction
+// to the table with the transactions for this reserve,
+// not by modifying some 'total' value for the reserve!
+// (we should in fact probably never modify, always just append!) (#3633)
+static int
+reservemod_add (struct TALER_Amount denom)
+{
+ PGresult *result;
+ const void *param_values[] = {
+ reserve_pub
+ };
+ int param_lengths[] = {
+ sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
+ };
+ int param_formats[] = {
+ 1
+ };
+ struct TALER_Amount old_denom;
+ struct TALER_Amount new_denom;
+ struct TALER_AmountNBO new_denom_nbo;
+
+ result = PQexecParams (db_conn,
+ "SELECT balance_value, balance_fraction, balance_currency"
+ " FROM reserves"
+ " WHERE reserve_pub=$1"
+ " LIMIT 1;",
+ 1,
+ NULL,
+ (const char * const *) param_values,
+ param_lengths,
+ param_formats,
+ 1);
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ fprintf (stderr,
+ "Select failed: %s\n",
+ PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ if (0 == PQntuples (result))
+ {
+ struct GNUNET_TIME_AbsoluteNBO exnbo;
+ uint32_t value = htonl (denom.value);
+ uint32_t fraction = htonl (denom.fraction);
+ const void *param_values[] = {
+ reserve_pub,
+ &value,
+ &fraction,
+ denom.currency,
+ &exnbo
+ };
+ int param_lengths[] = {
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
+ sizeof (uint32_t),
+ sizeof (uint32_t),
+ strlen (denom.currency),
+ sizeof (struct GNUNET_TIME_AbsoluteNBO)
+ };
+ int param_formats[] = {
+ 1, 1, 1, 1, 1
+ };
+
+ exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS));
+ result = PQexecParams (db_conn,
+ "INSERT INTO reserves (reserve_pub, balance_value, balance_fraction, balance_currency, expiration_date)"
+ " VALUES ($1,$2,$3,$4,$5);",
+ 5,
+ NULL,
+ (const char **) param_values,
+ param_lengths,
+ param_formats,
+ 1);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ fprintf (stderr,
+ "Insert failed: %s\n",
+ PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ const void *param_values[] = {
+ &new_denom_nbo.value,
+ &new_denom_nbo.fraction,
+ reserve_pub
+ };
+ int param_lengths[] = {
+ sizeof (new_denom_nbo.value),
+ sizeof (new_denom_nbo.fraction),
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)
+ };
+ int param_formats[] = {
+ 1, 1, 1
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_PQ_extract_amount (result, 0,
+ "balance_value",
+ "balance_fraction",
+ "balance_currency",
+ &old_denom));
+ TALER_amount_add (&new_denom,
+ &old_denom,
+ &denom);
+ TALER_amount_hton (&new_denom_nbo,
+ &new_denom);
+ result = PQexecParams (db_conn,
+ "UPDATE reserves"
+ " SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL"
+ " WHERE reserve_pub = $3;",
+ 3,
+ NULL,
+ (const char **) param_values,
+ param_lengths,
+ param_formats,
+ 1);
+
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ fprintf (stderr,
+ "Update failed: %s\n",
+ PQresultErrorMessage (result));
+ return GNUNET_SYSERR;
+ }
+ /* Yes, for historic reasons libpq returns a 'const char *'... */
+ if (0 != strcmp ("1",
+ PQcmdTuples (result)))
+ {
+ fprintf (stderr,
+ "Update failed (updated `%s' tupes instead of '1')\n",
+ PQcmdTuples (result));
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * The main function of the reservemod tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static char *reserve_pub_str;
+ static char *add_str;
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-reservemod OPTIONS"),
+ {'d', "mint-dir", "DIR",
+ "mint directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &mint_directory},
+ {'R', "reserve", "KEY",
+ "reserve (public key) to modify", 1,
+ &GNUNET_GETOPT_set_string, &reserve_pub_str},
+ {'a', "add", "DENOM",
+ "value to add", 1,
+ &GNUNET_GETOPT_set_string, &add_str},
+ GNUNET_GETOPT_OPTION_END
+ };
+ char *connection_cfg_str;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-mint-reservemod",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-mint-reservemod",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == mint_directory)
+ {
+ fprintf (stderr,
+ "Mint directory not given\n");
+ return 1;
+ }
+
+ reserve_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
+ if ((NULL == reserve_pub_str) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (reserve_pub_str,
+ strlen (reserve_pub_str),
+ reserve_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))))
+ {
+ fprintf (stderr,
+ "Parsing reserve key invalid\n");
+ return 1;
+ }
+ cfg = TALER_config_load (mint_directory);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "Failed to load mint configuration\n");
+ return 1;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "db",
+ &connection_cfg_str))
+ {
+ fprintf (stderr,
+ "Database configuration string not found\n");
+ return 1;
+ }
+ db_conn = PQconnectdb (connection_cfg_str);
+ if (CONNECTION_OK != PQstatus (db_conn))
+ {
+ fprintf (stderr,
+ "Database connection failed: %s\n",
+ PQerrorMessage (db_conn));
+ return 1;
+ }
+ if (NULL != add_str)
+ {
+ struct TALER_Amount add_value;
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (add_str,
+ &add_value))
+ {
+ fprintf (stderr,
+ "Failed to parse currency amount `%s'\n",
+ add_str);
+ return 1;
+ }
+ if (GNUNET_OK !=
+ reservemod_add (add_value))
+ {
+ fprintf (stderr,
+ "Failed to update reserve.\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* end taler-mint-reservemod.c */
diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am
@@ -1,82 +1,8 @@
# 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
-
-EXTRA_DIST = plugin_mintdb_common.c
-
-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
-
-libtalermint_common_la_SOURCES = \
- key_io.c key_io.h \
- plugin.c plugin.h
-
-libtalermint_common_la_LIBADD = \
- $(top_builddir)/src/util/libtalerutil.la \
- -lgnunetutil
-
-libtalermint_common_la_LDFLAGS = \
- $(POSTGRESQL_LDFLAGS) \
- -version-info 0:0:0 \
- -no-undefined
-
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
bin_PROGRAMS = \
- taler-mint-keyup \
- taler-mint-keycheck \
- taler-mint-reservemod \
- taler-mint-httpd \
- taler-mint-dbinit
-
-taler_mint_keyup_SOURCES = \
- taler-mint-keyup.c
-
-taler_mint_keyup_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/mint/libtalermint_common.la \
- -lpq \
- -lgnunetutil
-taler_mint_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
-
-
-taler_mint_keycheck_SOURCES = \
- taler-mint-keycheck.c
-
-taler_mint_keycheck_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/mint/libtalermint_common.la \
- -lgnunetutil \
- -lpq
-taler_mint_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
-
-taler_mint_reservemod_SOURCES = \
- taler-mint-reservemod.c
-taler_mint_reservemod_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/mint/libtalermint_common.la \
- -lpq \
- -lgnunetutil
-taler_mint_reservemod_LDFLAGS = \
- $(POSTGRESQL_LDFLAGS)
+ taler-mint-httpd
taler_mint_httpd_SOURCES = \
taler-mint-httpd.c \
@@ -92,56 +18,8 @@ taler_mint_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/mint/libtalermint_common.la \
- -lpq \
+ $(top_builddir)/src/mintdb/libtalermintdb.la \
-lmicrohttpd \
-ljansson \
-lgnunetutil \
-lpthread
-taler_mint_httpd_LDFLAGS = \
- $(POSTGRESQL_LDFLAGS)
-
-
-taler_mint_dbinit_SOURCES = \
- taler-mint-dbinit.c
-taler_mint_dbinit_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/mint/libtalermint_common.la \
- -lpq \
- -lgnunetutil
-taler_mint_dbinit_LDFLAGS = $(POSTGRESQL_LDFLAGS)
-
-check_PROGRAMS = \
- test-mint-deposits \
- test-mint-common \
- test-mint-db-postgres
-
-test_mint_deposits_SOURCES = \
- test_mint_deposits.c
-test_mint_deposits_LDADD = \
- libtalermint_common.la \
- $(top_srcdir)/src/util/libtalerutil.la \
- $(top_srcdir)/src/pq/libtalerpq.la \
- -lgnunetutil \
- -ljansson \
- -lpq
-
-test_mint_common_SOURCES = \
- test_mint_common.c
-test_mint_common_LDADD = \
- libtalermint_common.la \
- $(top_srcdir)/src/util/libtalerutil.la \
- $(top_srcdir)/src/pq/libtalerpq.la \
- -lgnunetutil
-
-test_mint_db_postgres_SOURCES = \
- test_mint_db.c
-test_mint_db_postgres_LDADD = \
- libtalermint_common.la \
- $(top_srcdir)/src/util/libtalerutil.la \
- $(top_srcdir)/src/pq/libtalerpq.la \
- -lgnunetutil -ljansson
-EXTRA_test_mint_db_postgres_DEPENDENCIES = \
- libtaler_plugin_mintdb_postgres.la
diff --git a/src/mint/key_io.c b/src/mint/key_io.c
@@ -1,347 +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/key_io.c
- * @brief I/O operations for the Mint's private keys
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Sree Harsha Totakura
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "key_io.h"
-
-
-/**
- * Closure for the #signkeys_iterate_dir_iter().
- */
-struct SignkeysIterateContext
-{
-
- /**
- * Function to call on each signing key.
- */
- TALER_MINT_SignkeyIterator it;
-
- /**
- * Closure for @e it.
- */
- void *it_cls;
-};
-
-
-/**
- * Function called on each file in the directory with
- * our signing keys. Parses the file and calls the
- * iterator from @a cls.
- *
- * @param cls the `struct SignkeysIterateContext *`
- * @param filename name of the file to parse
- * @return #GNUNET_OK to continue,
- * #GNUNET_NO to stop iteration without error,
- * #GNUNET_SYSERR to stop iteration with error
- */
-static int
-signkeys_iterate_dir_iter (void *cls,
- const char *filename)
-{
- struct SignkeysIterateContext *skc = cls;
- ssize_t nread;
- struct TALER_MintSigningKeyValidityPSPriv issue;
-
- nread = GNUNET_DISK_fn_read (filename,
- &issue,
- sizeof (struct TALER_MintSigningKeyValidityPSPriv));
- if (nread != sizeof (struct TALER_MintSigningKeyValidityPSPriv))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid signkey file `%s': wrong size\n",
- filename);
- return GNUNET_OK;
- }
- return skc->it (skc->it_cls,
- filename,
- &issue);
-}
-
-
-/**
- * Call @a it for each signing key found in the @a mint_base_dir.
- *
- * @param mint_base_dir base directory for the mint,
- * the signing keys must be in the #DIR_SIGNKEYS
- * subdirectory
- * @param it function to call on each signing key
- * @param it_cls closure for @a it
- * @return number of files found (may not match
- * number of keys given to @a it as malformed
- * files are simply skipped), -1 on error
- */
-int
-TALER_MINT_signkeys_iterate (const char *mint_base_dir,
- TALER_MINT_SignkeyIterator it,
- void *it_cls)
-{
- char *signkey_dir;
- struct SignkeysIterateContext skc;
- int ret;
-
- GNUNET_asprintf (&signkey_dir,
- "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS,
- mint_base_dir);
- skc.it = it;
- skc.it_cls = it_cls;
- ret = GNUNET_DISK_directory_scan (signkey_dir,
- &signkeys_iterate_dir_iter,
- &skc);
- GNUNET_free (signkey_dir);
- return ret;
-}
-
-
-/**
- * Import a denomination key from the given file.
- *
- * @param filename the file to import the key from
- * @param[OUT] dki set to the imported denomination key
- * @return #GNUNET_OK upon success;
- * #GNUNET_SYSERR upon failure
- */
-int
-TALER_MINT_read_denom_key (const char *filename,
- struct TALER_DenominationKeyIssueInformation *dki)
-{
- uint64_t size;
- size_t offset;
- void *data;
- struct GNUNET_CRYPTO_rsa_PrivateKey *priv;
-
- if (GNUNET_OK != GNUNET_DISK_file_size (filename,
- &size,
- GNUNET_YES,
- GNUNET_YES))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Skipping inaccessable denomination key file `%s'\n",
- filename);
- return GNUNET_SYSERR;
- }
- offset = sizeof (struct TALER_DenominationKeyValidityPS);
- if (size <= offset)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- data = GNUNET_malloc (size);
- if (size !=
- GNUNET_DISK_fn_read (filename,
- data,
- size))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
- "read",
- filename);
- GNUNET_free (data);
- return GNUNET_SYSERR;
- }
- if (NULL ==
- (priv = GNUNET_CRYPTO_rsa_private_key_decode (data + offset,
- size - offset)))
- {
- GNUNET_free (data);
- return GNUNET_SYSERR;
- }
- dki->denom_priv.rsa_private_key = priv;
- dki->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
- memcpy (&dki->issue,
- data,
- offset);
- GNUNET_free (data);
- return GNUNET_OK;
-}
-
-
-/**
- * Exports a denomination key to the given file.
- *
- * @param filename the file where to write the denomination key
- * @param dki the denomination key
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
- */
-int
-TALER_MINT_write_denom_key (const char *filename,
- const struct TALER_DenominationKeyIssueInformation *dki)
-{
- char *priv_enc;
- size_t priv_enc_size;
- struct GNUNET_DISK_FileHandle *fh;
- ssize_t wrote;
- size_t wsize;
- int ret;
-
- fh = NULL;
- priv_enc_size
- = GNUNET_CRYPTO_rsa_private_key_encode (dki->denom_priv.rsa_private_key,
- &priv_enc);
- ret = GNUNET_SYSERR;
- if (NULL == (fh = GNUNET_DISK_file_open
- (filename,
- GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE,
- GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE)))
- goto cleanup;
- wsize = sizeof (struct TALER_DenominationKeyValidityPS);
- if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh,
- &dki->issue.signature,
- wsize)))
- goto cleanup;
- if (wrote != wsize)
- goto cleanup;
- if (GNUNET_SYSERR ==
- (wrote = GNUNET_DISK_file_write (fh,
- priv_enc,
- priv_enc_size)))
- goto cleanup;
- if (wrote != priv_enc_size)
- goto cleanup;
- ret = GNUNET_OK;
- cleanup:
- GNUNET_free_non_null (priv_enc);
- if (NULL != fh)
- (void) GNUNET_DISK_file_close (fh);
- return ret;
-}
-
-
-/**
- * Closure for #denomkeys_iterate_keydir_iter() and
- * #denomkeys_iterate_topdir_iter().
- */
-struct DenomkeysIterateContext
-{
-
- /**
- * Set to the name of the directory below the top-level directory
- * during the call to #denomkeys_iterate_keydir_iter().
- */
- const char *alias;
-
- /**
- * Function to call on each denomination key.
- */
- TALER_MINT_DenomkeyIterator it;
-
- /**
- * Closure for @e it.
- */
- void *it_cls;
-};
-
-
-/**
- * Decode the denomination key in the given file @a filename and call
- * the callback in @a cls with the information.
- *
- * @param cls the `struct DenomkeysIterateContext *`
- * @param filename name of a file that should contain
- * a denomination key
- * @return #GNUNET_OK to continue to iterate
- * #GNUNET_NO to abort iteration with success
- * #GNUNET_SYSERR to abort iteration with failure
- */
-static int
-denomkeys_iterate_keydir_iter (void *cls,
- const char *filename)
-{
- struct DenomkeysIterateContext *dic = cls;
- struct TALER_DenominationKeyIssueInformation issue;
-
- if (GNUNET_OK !=
- TALER_MINT_read_denom_key (filename,
- &issue))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid denomkey file: '%s'\n",
- filename);
- return GNUNET_OK;
- }
- return dic->it (dic->it_cls,
- dic->alias,
- &issue);
-}
-
-
-/**
- * Function called on each subdirectory in the #DIR_DENOMKEYS. Will
- * call the #denomkeys_iterate_keydir_iter() on each file in the
- * subdirectory.
- *
- * @param cls the `struct DenomkeysIterateContext *`
- * @param filename name of the subdirectory to scan
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR if we need to abort
- */
-static int
-denomkeys_iterate_topdir_iter (void *cls,
- const char *filename)
-{
- struct DenomkeysIterateContext *dic = cls;
-
- dic->alias = GNUNET_STRINGS_get_short_name (filename);
- if (0 > GNUNET_DISK_directory_scan (filename,
- &denomkeys_iterate_keydir_iter,
- dic))
- return GNUNET_SYSERR;
- return GNUNET_OK;
-}
-
-
-/**
- * Call @a it for each denomination key found in the @a mint_base_dir.
- *
- * @param mint_base_dir base directory for the mint,
- * the signing keys must be in the #DIR_DENOMKEYS
- * subdirectory
- * @param it function to call on each denomination key found
- * @param it_cls closure for @a it
- * @return -1 on error, 0 if no files were found, otherwise
- * a positive number (however, even with a positive
- * number it is possible that @a it was never called
- * as maybe none of the files were well-formed)
- */
-int
-TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
- TALER_MINT_DenomkeyIterator it,
- void *it_cls)
-{
- char *dir;
- struct DenomkeysIterateContext dic;
- int ret;
-
- GNUNET_asprintf (&dir,
- "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS,
- mint_base_dir);
- dic.it = it;
- dic.it_cls = it_cls;
- ret = GNUNET_DISK_directory_scan (dir,
- &denomkeys_iterate_topdir_iter,
- &dic);
- GNUNET_free (dir);
- return ret;
-}
-
-
-/* end of key_io.c */
diff --git a/src/mint/key_io.h b/src/mint/key_io.h
@@ -1,187 +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/key_io.h
- * @brief IO operations for the mint's private keys
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef KEY_IO_H
-#define KEY_IO_H
-
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_signatures.h"
-
-/**
- * Subdirectroy under the mint's base directory which contains
- * the mint's signing keys.
- */
-#define DIR_SIGNKEYS "signkeys"
-
-/**
- * Subdirectory under the mint's base directory which contains
- * the mint's denomination keys.
- */
-#define DIR_DENOMKEYS "denomkeys"
-
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-/**
- * On disk format used for a mint signing key. Signing keys are used
- * by the mint to affirm its messages, but not to create coins.
- * Includes the private key followed by the public information about
- * the signing key.
- */
-struct TALER_MintSigningKeyValidityPSPriv
-{
- /**
- * Private key part of the mint's signing key.
- */
- struct TALER_MintPrivateKeyP signkey_priv;
-
- /**
- * Public information about a mint signing key.
- */
- struct TALER_MintSigningKeyValidityPS issue;
-};
-
-
-GNUNET_NETWORK_STRUCT_END
-
-
-/**
- * All information about a denomination key (which is used to
- * sign coins into existence).
- */
-struct TALER_DenominationKeyIssueInformation
-{
- /**
- * The private key of the denomination. Will be NULL if the private
- * key is not available (this is the case after the key has expired
- * for signing coins, but is still valid for depositing coins).
- */
- struct TALER_DenominationPrivateKey denom_priv;
-
- /**
- * Decoded denomination public key (the hash of it is in
- * @e issue, but we sometimes need the full public key as well).
- */
- struct TALER_DenominationPublicKey denom_pub;
-
- /**
- * Signed public information about a denomination key.
- */
- struct TALER_DenominationKeyValidityPS issue;
-};
-
-
-/**
- * Iterator over signing keys.
- *
- * @param cls closure
- * @param filename name of the file the key came from
- * @param ski the sign key
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-typedef int
-(*TALER_MINT_SignkeyIterator)(void *cls,
- const char *filename,
- const struct TALER_MintSigningKeyValidityPSPriv *ski);
-
-
-/**
- * Iterator over denomination keys.
- *
- * @param cls closure
- * @param dki the denomination key
- * @param alias coin alias
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-typedef int
-(*TALER_MINT_DenomkeyIterator)(void *cls,
- const char *alias,
- const struct TALER_DenominationKeyIssueInformation *dki);
-
-
-
-/**
- * Call @a it for each signing key found in the @a mint_base_dir.
- *
- * @param mint_base_dir base directory for the mint,
- * the signing keys must be in the #DIR_SIGNKEYS
- * subdirectory
- * @param it function to call on each signing key
- * @param it_cls closure for @a it
- * @return number of files found (may not match
- * number of keys given to @a it as malformed
- * files are simply skipped), -1 on error
- */
-int
-TALER_MINT_signkeys_iterate (const char *mint_base_dir,
- TALER_MINT_SignkeyIterator it,
- void *it_cls);
-
-
-/**
- * Call @a it for each denomination key found in the @a mint_base_dir.
- *
- * @param mint_base_dir base directory for the mint,
- * the signing keys must be in the #DIR_DENOMKEYS
- * subdirectory
- * @param it function to call on each denomination key found
- * @param it_cls closure for @a it
- * @return -1 on error, 0 if no files were found, otherwise
- * a positive number (however, even with a positive
- * number it is possible that @a it was never called
- * as maybe none of the files were well-formed)
- */
-int
-TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
- TALER_MINT_DenomkeyIterator it,
- void *it_cls);
-
-
-/**
- * Exports a denomination key to the given file.
- *
- * @param filename the file where to write the denomination key
- * @param dki the denomination key
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
- */
-int
-TALER_MINT_write_denom_key (const char *filename,
- const struct TALER_DenominationKeyIssueInformation *dki);
-
-
-/**
- * Import a denomination key from the given file.
- *
- * @param filename the file to import the key from
- * @param[OUT] dki set to the imported denomination key
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-int
-TALER_MINT_read_denom_key (const char *filename,
- struct TALER_DenominationKeyIssueInformation *dki);
-
-
-#endif
diff --git a/src/mint/plugin.c b/src/mint/plugin.c
@@ -1,147 +0,0 @@
-/*
- 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 (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- char *plugin_name;
- char *lib_name;
- struct GNUNET_CONFIGURATION_Handle *cfg_dup;
-
- if (NULL != plugin)
- return GNUNET_OK;
- if (GNUNET_SYSERR ==
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint",
- "db",
- &plugin_name))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "db");
- return GNUNET_SYSERR;
- }
- (void) GNUNET_asprintf (&lib_name,
- "libtaler_plugin_mintdb_%s",
- plugin_name);
- GNUNET_free (plugin_name);
- cfg_dup = GNUNET_CONFIGURATION_dup (cfg);
- plugin = GNUNET_PLUGIN_load (lib_name, cfg_dup);
- GNUNET_CONFIGURATION_destroy (cfg_dup);
- GNUNET_free (lib_name);
- if (NULL == plugin)
- return GNUNET_SYSERR;
- return GNUNET_OK;
-}
-
-
-/**
- * Shutdown the plugin.
- */
-void
-TALER_MINT_plugin_unload ()
-{
- if (NULL == plugin)
- return;
- GNUNET_assert (NULL == GNUNET_PLUGIN_unload (plugin->library_name,
- plugin));
-}
-
-
-/**
- * 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 ();
-}
-
-
-/* end of plugin.c */
diff --git a/src/mint/plugin.h b/src/mint/plugin.h
@@ -1,50 +0,0 @@
-/*
- 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 (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Shutdown the plugin.
- */
-void
-TALER_MINT_plugin_unload (void);
-
-
-#endif
diff --git a/src/mint/plugin_mintdb_postgres.c b/src/mint/plugin_mintdb_postgres.c
@@ -1,2357 +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 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 "taler_pq_lib.h"
-#include "taler_signatures.h"
-#include "taler_mintdb_plugin.h"
-#include <pthread.h>
-#include <libpq-fe.h>
-
-#include "plugin_mintdb_common.c"
-
-#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_PQ_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 *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 cls the `struct PostgresClosure` with the plugin-specific state
- * @param temporary should we use a temporary schema
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-static int
-postgres_create_tables (void *cls,
- int temporary)
-{
- struct PostgresClosure *pc = cls;
- PGresult *result;
- PGconn *conn;
-
- result = NULL;
- 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 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 INT8 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 INT8 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 INT8 NOT NULL"
- ",expended_fraction INT4 NOT NULL"
- ",expended_currency VARCHAR(4) NOT NULL"
- ",refresh_session_hash BYTEA"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
- "("
- " session_hash BYTEA PRIMARY KEY CHECK (length(session_hash) = 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_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"
- ",newcoin_index INT2 NOT NULL "
- ",denom_pub BYTEA NOT NULL "
- ",PRIMARY KEY (session_hash, newcoin_index)"
- ") ");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link"
- "("
- " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"
- ",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 #TALER_CNC_KAPPA-1
- ",cnc_index INT2 NOT NULL"
- ")");
- SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin"
- "("
- " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) "
- ",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_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) "
- ",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_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) "
- ",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 INT8 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_hash = $1)::INT2 as num_oldcoins "
- ",(SELECT count(*) FROM refresh_blind_session_keys "
- " WHERE session_hash = $1 and cnc_index = 0)::INT2 as num_newcoins "
- ",(SELECT count(*) FROM refresh_blind_session_keys "
- " WHERE session_hash = $1 and newcoin_index = 0)::INT2 as kappa "
- ",noreveal_index"
- ",session_commit_sig "
- ",reveal_ok "
- "FROM refresh_sessions "
- "WHERE session_hash = $1",
- 1, NULL);
-#endif
-
- PREPARE ("get_known_coin",
- "SELECT "
- " coin_pub, denom_pub, denom_sig "
- ",expended_value, expended_fraction, expended_currency "
- ",refresh_session_hash "
- "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_hash = $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_hash"
- ")"
- "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_hash = $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_hash = $1 AND cnc_index = $2 AND newcoin_index = $3",
- 3, NULL);
- PREPARE ("insert_refresh_order",
- "INSERT INTO refresh_order ( "
- " newcoin_index "
- ",session_hash "
- ",denom_pub "
- ") "
- "VALUES ($1, $2, $3) ",
- 3, NULL);
- PREPARE ("insert_refresh_melt",
- "INSERT INTO refresh_melt ( "
- " session_hash "
- ",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_hash = $1 AND newcoin_index = $2",
- 2, NULL);
- PREPARE ("get_refresh_collectable",
- "SELECT ev_sig "
- "FROM refresh_collectable "
- "WHERE session_hash = $1 AND newcoin_index = $2",
- 2, NULL);
- PREPARE ("get_refresh_melt",
- "SELECT coin_pub "
- "FROM refresh_melt "
- "WHERE session_hash = $1 AND oldcoin_index = $2",
- 2, NULL);
- PREPARE ("insert_refresh_session",
- "INSERT INTO refresh_sessions ( "
- " session_hash "
- ",noreveal_index "
- ") "
- "VALUES ($1, $2) ",
- 2, NULL);
- PREPARE ("insert_refresh_commit_link",
- "INSERT INTO refresh_commit_link ( "
- " session_hash "
- ",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_hash "
- ",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_hash "
- ",newcoin_index "
- ",ev_sig "
- ") "
- "VALUES ($1, $2, $3) ",
- 3, NULL);
- PREPARE ("set_reveal_ok",
- "UPDATE refresh_sessions "
- "SET reveal_ok = TRUE "
- "WHERE session_hash = $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_hash) "
- " JOIN refresh_commit_coin rcc USING (session_hash) "
- " JOIN refresh_sessions rs USING (session_hash) "
- " JOIN refresh_collectable rc USING (session_hash) "
- "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_hash = rs.session_hash "
- " ) ",
- 1, NULL);
- PREPARE ("get_transfer",
- "SELECT transfer_pub, link_secret_enc "
- "FROM refresh_melt rm "
- " JOIN refresh_commit_link rcl USING (session_hash) "
- " JOIN refresh_sessions rs USING (session_hash) "
- "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_hash = rm.session_hash "
- " ) ",
- 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_session (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->connection_cfg_str);
- if (CONNECTION_OK !=
- PQstatus (db_conn))
- {
- TALER_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))
- {
- 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_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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(&reserve->pub),
- TALER_PQ_QUERY_PARAM_END
- };
-
- result = TALER_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC("expiration_date", &expiration_date_nbo),
- TALER_PQ_RESULT_SPEC_END
- };
- EXITIF (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0));
- EXITIF (GNUNET_OK !=
- TALER_PQ_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)
- return GNUNET_SYSERR;
- ret = GNUNET_OK;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (&reserve->pub),
- TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value),
- TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction),
- TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo),
- TALER_PQ_QUERY_PARAM_END
- };
- TALER_amount_hton (&balance_nbo,
- &reserve->balance);
- expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry);
- result = TALER_PQ_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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (&reserve->pub),
- TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value),
- TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (balance_nbo.currency,
- TALER_PQ_CURRENCY_LEN),
- TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (&reserve->pub),
- TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value),
- TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency,
- TALER_PQ_CURRENCY_LEN),
- TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (h_blind),
- TALER_PQ_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_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_VAR("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
- TALER_PQ_RESULT_SPEC_VAR("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
- TALER_PQ_RESULT_SPEC("reserve_sig", &collectable->reserve_sig),
- TALER_PQ_RESULT_SPEC("reserve_pub", &collectable->reserve_pub),
- TALER_PQ_RESULT_SPEC_END
- };
-
- if (GNUNET_OK != TALER_PQ_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.rsa_public_key = denom_pub;
- collectable->sig.rsa_signature = 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.rsa_public_key,
- &denom_pub_enc);
- denom_sig_enc_size =
- GNUNET_CRYPTO_rsa_signature_encode (collectable->sig.rsa_signature,
- &denom_sig_enc);
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (h_blind),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size - 1), /* DB doesn't like the trailing \0 */
- TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_pub),
- TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_sig),
- TALER_PQ_QUERY_PARAM_END
- };
- if (GNUNET_OK != postgres_start (cls,
- session))
- goto cleanup;
- result = TALER_PQ_exec_prepared (session->conn,
- "insert_collectable_blindcoin",
- params);
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- QUERY_ERR (result);
- goto rollback;
- }
- reserve.pub = 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 TALER_ReservePublicKeyP *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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (reserve_pub),
- TALER_PQ_QUERY_PARAM_END
- };
-
- result = TALER_PQ_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_PQ_extract_amount (result,
- --rows,
- "balance_value",
- "balance_fraction",
- "balance_currency",
- &bt->amount))
- {
- GNUNET_free (bt);
- GNUNET_break (0);
- goto cleanup;
- }
- bt->reserve_pub = *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 TALER_ReserveSignatureP 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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (reserve_pub),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC ("blind_ev", &blind_ev),
- TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
- TALER_PQ_RESULT_SPEC_VAR ("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
- TALER_PQ_RESULT_SPEC ("reserve_sig", &reserve_sig),
- TALER_PQ_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_PQ_extract_result (result, rs, --rows))
- {
- GNUNET_break (0);
- goto cleanup;
- }
- cbc = GNUNET_new (struct CollectableBlindcoin);
- cbc->sig.rsa_signature
- = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc,
- denom_sig_enc_size);
- GNUNET_free (denom_sig_enc);
- denom_sig_enc = NULL;
- cbc->denom_pub.rsa_public_key
- = 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.rsa_signature) ||
- (NULL == cbc->denom_pub.rsa_public_key) )
- {
- if (NULL != cbc->sig.rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (cbc->sig.rsa_signature);
- if (NULL != cbc->denom_pub.rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub.rsa_public_key);
- 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)
- {
- common_free_reserve_history (cls,
- 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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub),
- TALER_PQ_QUERY_PARAM_END
- };
- PGresult *result;
- int ret;
-
- ret = GNUNET_SYSERR;
- result = TALER_PQ_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.rsa_public_key,
- &denom_pub_enc);
- denom_sig_enc_size =
- GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig.rsa_signature,
- &denom_sig_enc);
- json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT);
- TALER_amount_hton (&amount_nbo,
- &deposit->amount_with_fee);
- struct TALER_PQ_QueryParam params[]= {
- TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id),
- TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.value),
- TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.fraction),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (amount_nbo.currency,
- TALER_CURRENCY_LEN - 1),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->h_contract),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->h_wire),
- TALER_PQ_QUERY_PARAM_PTR (&deposit->csig),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (json_wire_enc,
- strlen (json_wire_enc)),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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 @a session_hash.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database handle to use
- * @param session_hash hash over the melt to use to locate the session
- * @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_HashCode *session_hash,
- struct RefreshSession *refresh_session)
-{
- // FIXME: check logic!
- int res;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins),
- TALER_PQ_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins),
- TALER_PQ_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index),
- TALER_PQ_RESULT_SPEC_END
- };
-
- res = TALER_PQ_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->noreveal_index = ntohs (refresh_session->noreveal_index);
-
- PQclear (result);
- return GNUNET_YES;
-}
-
-
-/**
- * Store new refresh session data under the given @a session_hash.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database handle to use
- * @param session_hash hash over the melt to use to locate the session
- * @param refresh_session session data to store
- * @return #GNUNET_YES on success,
- * #GNUNET_SYSERR on DB failure
- */
-static int
-postgres_create_refresh_session (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- const struct RefreshSession *refresh_session)
-{
- // FIXME: actually store session data!
- uint16_t noreveal_index;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&noreveal_index),
- TALER_PQ_QUERY_PARAM_END
- };
-
- noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);
- noreveal_index = htonl (noreveal_index);
-
- PGresult *result = TALER_PQ_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 oldcoin_index index of the coin to store
- * @param melt melt operation details to store; includes
- * the session hash of the melt
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-static int
-postgres_insert_refresh_melt (void *cls,
- struct TALER_MINTDB_Session *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.rsa_public_key,
- &buf);
- {
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(&melt->session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR(&melt->coin.coin_pub),
- TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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_HashCode *session_hash,
- 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_hash hash to identify refresh session
- * @param num_newcoins number of coins to generate, size of the @a denom_pubs array
- * @param denom_pubs array denominations of the coins to create
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-static int
-postgres_insert_refresh_order (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- uint16_t num_newcoins,
- const struct TALER_DenominationPublicKey *denom_pubs)
-{
- // FIXME: check logic: was written for just one COIN!
- uint16_t newcoin_index_nbo = htons (num_newcoins);
- char *buf;
- size_t buf_size;
- PGresult *result;
-
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs->rsa_public_key,
- &buf);
-
- {
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR (&newcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR (session_hash),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (buf, buf_size),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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 coins 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_hash hash to identify refresh session
- * @param newcoin_index array of the @a denom_pubs array
- * @param denom_pubs where to store the deomination keys
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on internal error
- */
-static int
-postgres_get_refresh_order (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- uint16_t num_newcoins,
- struct TALER_DenominationPublicKey *denom_pubs)
-{
- // FIXME: check logic -- was written for just one coin!
- char *buf;
- size_t buf_size;
- uint16_t newcoin_index_nbo = htons (num_newcoins);
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_PQ_exec_prepared (session->conn,
- "get_refresh_order", params);
-
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- BREAK_DB_ERR (result);
- PQclear (result);
- return GNUNET_SYSERR;
- }
-
- if (0 == PQntuples (result))
- {
- PQclear (result);
- /* FIXME: may want to distinguish between different error cases! */
- return GNUNET_SYSERR;
- }
- GNUNET_assert (1 == PQntuples (result));
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &buf, &buf_size),
- TALER_PQ_RESULT_SPEC_END
- };
- if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
- {
- PQclear (result);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- denom_pubs->rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_decode (buf,
- buf_size);
- GNUNET_free (buf);
- return GNUNET_OK;
-}
-
-
-
-/**
- * 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 session_hash hash to identify refresh session
- * @param i set index (1st dimension)
- * @param num_newcoins coin index size of the @a commit_coins array
- * @param commit_coins array of coin commitments to store
- * @return #GNUNET_OK on success
- * #GNUNET_SYSERR on error
- */
-static int
-postgres_insert_refresh_commit_coins (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- unsigned int i,
- unsigned int num_newcoins,
- const struct RefreshCommitCoin *commit_coins)
-{
- // FIXME: check logic! -- was written for single commit_coin!
- uint16_t cnc_index_nbo = htons (i);
- uint16_t newcoin_index_nbo = htons (num_newcoins);
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR_SIZED(commit_coins->coin_ev, commit_coins->coin_ev_size),
- TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR_SIZED (commit_coins->refresh_link->coin_priv_enc,
- commit_coins->refresh_link->blinding_key_enc_size +
- sizeof (union TALER_CoinSpendPrivateKeyP)),
- TALER_PQ_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_PQ_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 session_hash hash to identify refresh session
- * @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_coins (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- 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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_PQ_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_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_VAR("coin_ev", &c_buf, &c_buf_size),
- TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &rl_buf, &rl_buf_size),
- TALER_PQ_RESULT_SPEC_END
- };
- if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0))
- {
- PQclear (result);
- return GNUNET_SYSERR;
- }
- PQclear (result);
- if (rl_buf_size < sizeof (union TALER_CoinSpendPrivateKeyP))
- {
- 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 session_hash hash to identify refresh session
- * @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_links (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- 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_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&commit_link->transfer_pub),
- TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR(&commit_link->shared_secret_enc),
- TALER_PQ_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_PQ_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 session_hash hash to identify refresh session
- * @param i set index (1st dimension)
- * @param num_links size of the @a commit_link array
- * @param links[OUT] array of 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_links (void *cls,
- struct TALER_MINTDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- unsigned int i,
- unsigned int num_links,
- struct RefreshCommitLink *links)
-{
- // FIXME: check logic: was written for a single link!
- uint16_t cnc_index_nbo = htons (i);
- uint16_t oldcoin_index_nbo = htons (num_links);
-
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC("transfer_pub", &links->transfer_pub),
- TALER_PQ_RESULT_SPEC("link_secret_enc", &links->shared_secret_enc),
- TALER_PQ_RESULT_SPEC_END
- };
-
- if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0))
- {
- PQclear (result);
- 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_hash hash to identify 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_HashCode *session_hash,
- uint16_t newcoin_index,
- const struct TALER_DenominationSignature *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->rsa_signature,
- &buf);
- {
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(session_hash),
- TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
- TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size),
- TALER_PQ_QUERY_PARAM_END
- };
- result = TALER_PQ_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_data_list (void *cls,
- struct TALER_MINTDB_Session *session,
- const union TALER_CoinSpendPublicKeyP *coin_pub)
-{
- // FIXME: check logic!
- struct LinkDataList *ldl;
- struct LinkDataList *pos;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(coin_pub),
- TALER_PQ_QUERY_PARAM_END
- };
- PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &ld_buf, &ld_buf_size),
- TALER_PQ_RESULT_SPEC_VAR("denom_pub", &pk_buf, &pk_buf_size),
- TALER_PQ_RESULT_SPEC_VAR("ev_sig", &sig_buf, &sig_buf_size),
- TALER_PQ_RESULT_SPEC_END
- };
-
- if (GNUNET_OK != TALER_PQ_extract_result (result, rs, i))
- {
- PQclear (result);
- GNUNET_break (0);
- common_free_link_data_list (cls,
- 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);
- common_free_link_data_list (cls,
- 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);
- common_free_link_data_list (cls,
- ldl);
- return NULL;
- }
- pos = GNUNET_new (struct LinkDataList);
- pos->next = ldl;
- pos->link_data_enc = link_enc;
- pos->denom_pub.rsa_public_key = denom_pub;
- pos->ev_sig.rsa_signature = 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
- * #postgres_get_link_data_list() enable the owner of an old coin to
- * determine the private keys of the new coins after the melt.
- *
- * @param cls the `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 union TALER_CoinSpendPublicKeyP *coin_pub,
- struct TALER_TransferPublicKeyP *transfer_pub,
- struct TALER_EncryptedLinkSecretP *shared_secret_enc)
-{
- // FIXME: check logic!
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_QUERY_PARAM_PTR(coin_pub),
- TALER_PQ_QUERY_PARAM_END
- };
-
- PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC("transfer_pub", transfer_pub),
- TALER_PQ_RESULT_SPEC("link_secret_enc", shared_secret_enc),
- TALER_PQ_RESULT_SPEC_END
- };
-
- if (GNUNET_OK != TALER_PQ_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 union TALER_CoinSpendPublicKeyP *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))
- {
- TALER_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_conn_str",
- &pg->connection_cfg_str))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "db_conn_str");
- return NULL;
- }
- plugin = GNUNET_new (struct TALER_MINTDB_Plugin);
- plugin->cls = pg;
- plugin->library_name = "libtaler_plugin_mintdb_postgres";
- plugin->get_session = &postgres_get_session;
- plugin->drop_temporary = &postgres_drop_temporary;
- plugin->create_tables = &postgres_create_tables;
- plugin->start = &postgres_start;
- plugin->commit = &postgres_commit;
- plugin->rollback = &postgres_rollback;
- plugin->reserve_get = &postgres_reserve_get;
- plugin->reserves_in_insert = &postgres_reserves_in_insert;
- plugin->get_collectable_blindcoin = &postgres_get_collectable_blindcoin;
- plugin->insert_collectable_blindcoin = &postgres_insert_collectable_blindcoin;
- plugin->get_reserve_history = &postgres_get_reserve_history;
- plugin->free_reserve_history = &common_free_reserve_history;
- plugin->have_deposit = &postgres_have_deposit;
- plugin->insert_deposit = &postgres_insert_deposit;
- plugin->get_refresh_session = &postgres_get_refresh_session;
- plugin->create_refresh_session = &postgres_create_refresh_session;
- plugin->insert_refresh_melt = &postgres_insert_refresh_melt;
- plugin->get_refresh_melt = &postgres_get_refresh_melt;
- plugin->insert_refresh_order = &postgres_insert_refresh_order;
- plugin->get_refresh_order = &postgres_get_refresh_order;
- plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins;
- plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins;
- plugin->insert_refresh_commit_links = &postgres_insert_refresh_commit_links;
- plugin->get_refresh_commit_links = &postgres_get_refresh_commit_links;
- plugin->insert_refresh_collectable = &postgres_insert_refresh_collectable;
- plugin->get_link_data_list = &postgres_get_link_data_list;
- plugin->free_link_data_list = &common_free_link_data_list;
- plugin->get_transfer = &postgres_get_transfer;
- // plugin->have_lock = &postgres_have_lock;
- // plugin->insert_lock = &postgres_insert_lock;
- plugin->get_coin_transactions = &postgres_get_coin_transactions;
- plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
- 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->connection_cfg_str);
- 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
@@ -1,101 +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/taler-mint-dbinit.c
- * @brief Create tables for the mint database.
- * @author Florian Dold
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <libpq-fe.h>
-#include "taler_util.h"
-#include "taler_mintdb_plugin.h"
-#include "plugin.h"
-
-/**
- * Mint directory with the keys.
- */
-static char *mint_base_dir;
-
-/**
- * Our configuration.
- */
-static struct GNUNET_CONFIGURATION_Handle *cfg;
-
-
-/**
- * The main function of the database initialization tool.
- * Used to initialize the Taler Mint'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)
-{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-dbinit OPTIONS"),
- {'d', "mint-dir", "DIR",
- "mint directory", 1,
- &GNUNET_GETOPT_set_filename, &mint_base_dir},
- GNUNET_GETOPT_OPTION_END
- };
-
- if (GNUNET_GETOPT_run ("taler-mint-dbinit",
- options,
- argc, argv) < 0)
- return 1;
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-mint-dbinit",
- "INFO",
- NULL));
- if (NULL == mint_base_dir)
- {
- fprintf (stderr,
- "Mint base directory not given.\n");
- return 1;
- }
- cfg = TALER_config_load (mint_base_dir);
- if (NULL == cfg)
- {
- fprintf (stderr,
- "Failed to load mint configuration.\n");
- return 1;
- }
- if (GNUNET_OK !=
- TALER_MINT_plugin_load (cfg))
- {
- fprintf (stderr,
- "Failed to initialize database plugin.\n");
- return 1;
- }
- if (GNUNET_OK !=
- plugin->create_tables (plugin->cls,
- GNUNET_NO))
- {
- fprintf (stderr,
- "Failed to initialize database.\n");
- TALER_MINT_plugin_unload ();
- return 1;
- }
- TALER_MINT_plugin_unload ();
- return 0;
-}
-
-/* end of taler-mint-dbinit.c */
diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c
@@ -25,7 +25,6 @@
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <microhttpd.h>
-#include <libpq-fe.h>
#include <pthread.h>
#include "taler_signatures.h"
#include "taler_util.h"
@@ -36,7 +35,6 @@
#include "taler-mint-httpd_refresh.h"
#include "taler-mint-httpd_keystate.h"
#include "taler_mintdb_plugin.h"
-#include "plugin.h"
/**
@@ -61,6 +59,11 @@ struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
char *TMH_expected_wire_format = "sepa";
/**
+ * Our DB plugin.
+ */
+struct TALER_MINTDB_Plugin *TMH_plugin;
+
+/**
* The HTTP Daemon.
*/
static struct MHD_Daemon *mydaemon;
@@ -260,8 +263,8 @@ mint_serve_process_config (const char *mint_directory)
}
GNUNET_free (TMH_master_public_key_str);
- if (GNUNET_OK !=
- TALER_MINT_plugin_load (cfg))
+ if (NULL ==
+ (TMH_plugin = TALER_MINT_plugin_load (cfg)))
{
fprintf (stderr,
"failed to initialize DB subsystem\n");
@@ -355,5 +358,6 @@ main (int argc, char *const *argv)
ret = TMH_KS_loop ();
MHD_stop_daemon (mydaemon);
+ TALER_MINT_plugin_unload (TMH_plugin);
return (GNUNET_OK == ret) ? 0 : 1;
}
diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h
@@ -59,6 +59,11 @@ extern struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
*/
extern struct GNUNET_CRYPTO_EddsaPrivateKey TMH_mint_private_signing_key;
+/**
+ * Our DB plugin.
+ */
+extern struct TALER_MINTDB_Plugin *TMH_plugin;
+
/**
* Struct describing an URL and the handler for it.
diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c
@@ -26,7 +26,7 @@
#include "taler-mint-httpd_responses.h"
#include "taler_util.h"
#include "taler-mint-httpd_keystate.h"
-#include "plugin.h"
+#include "taler_mintdb_lib.h"
/**
@@ -109,14 +109,14 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
struct TALER_DenominationKeyIssueInformation *dki;
int ret;
- if (NULL == (session = plugin->get_session (plugin->cls,
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_YES ==
- plugin->have_deposit (plugin->cls,
+ TMH_plugin->have_deposit (TMH_plugin->cls,
session,
deposit))
{
@@ -136,7 +136,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
TMH_KS_release (mks);
if (GNUNET_OK !=
- plugin->start (plugin->cls,
+ TMH_plugin->start (TMH_plugin->cls,
session))
{
GNUNET_break (0);
@@ -145,7 +145,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
/* fee for THIS transaction */
spent = deposit->amount_with_fee;
/* add cost of all previous transactions */
- tl = plugin->get_coin_transactions (plugin->cls,
+ tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls,
session,
&deposit->coin.coin_pub);
if (GNUNET_OK !=
@@ -153,7 +153,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
&spent,
&spent))
{
- plugin->free_coin_transaction_list (plugin->cls,
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
tl);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -162,30 +162,30 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
if (0 < TALER_amount_cmp (&spent,
&value))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
ret = TMH_RESPONSE_reply_deposit_insufficient_funds (connection,
tl);
- plugin->free_coin_transaction_list (plugin->cls,
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
tl);
return ret;
}
- plugin->free_coin_transaction_list (plugin->cls,
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
tl);
if (GNUNET_OK !=
- plugin->insert_deposit (plugin->cls,
+ TMH_plugin->insert_deposit (TMH_plugin->cls,
session,
deposit))
{
TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- plugin->commit (plugin->cls,
+ TMH_plugin->commit (TMH_plugin->cls,
session))
{
TALER_LOG_WARNING ("/deposit transaction commit failed\n");
@@ -217,13 +217,13 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection,
struct ReserveHistory *rh;
int res;
- if (NULL == (session = plugin->get_session (plugin->cls,
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
- rh = plugin->get_reserve_history (plugin->cls,
+ rh = TMH_plugin->get_reserve_history (TMH_plugin->cls,
session,
reserve_pub);
if (NULL == rh)
@@ -233,7 +233,7 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection,
"error", "Reserve not found");
res = TMH_RESPONSE_reply_withdraw_status_success (connection,
rh);
- plugin->free_reserve_history (plugin->cls,
+ TMH_plugin->free_reserve_history (TMH_plugin->cls,
rh);
return res;
}
@@ -282,13 +282,13 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
blinded_msg_len,
&h_blind);
- if (NULL == (session = plugin->get_session (plugin->cls,
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
- res = plugin->get_collectable_blindcoin (plugin->cls,
+ res = TMH_plugin->get_collectable_blindcoin (TMH_plugin->cls,
session,
&h_blind,
&collectable);
@@ -323,7 +323,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
"Denomination not found");
}
if (GNUNET_OK !=
- plugin->start (plugin->cls,
+ TMH_plugin->start (TMH_plugin->cls,
session))
{
GNUNET_break (0);
@@ -331,12 +331,12 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
return TMH_RESPONSE_reply_internal_db_error (connection);
}
- rh = plugin->get_reserve_history (plugin->cls,
+ rh = TMH_plugin->get_reserve_history (TMH_plugin->cls,
session,
reserve);
if (NULL == rh)
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
TMH_KS_release (key_state);
return TMH_RESPONSE_reply_json_pack (connection,
@@ -357,7 +357,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
&value,
&fee_withdraw))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
TMH_KS_release (key_state);
return TMH_RESPONSE_reply_internal_db_error (connection);
@@ -378,7 +378,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
&deposit_total,
&pos->details.bank->amount))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
TMH_KS_release (key_state);
return TMH_RESPONSE_reply_internal_db_error (connection);
@@ -398,7 +398,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
&withdraw_total,
&value))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
TMH_KS_release (key_state);
return TMH_RESPONSE_reply_internal_db_error (connection);
@@ -416,15 +416,15 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
&balance))
{
TMH_KS_release (key_state);
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
res = TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (connection,
rh);
- plugin->free_reserve_history (plugin->cls,
+ TMH_plugin->free_reserve_history (TMH_plugin->cls,
rh);
return res;
}
- plugin->free_reserve_history (plugin->cls,
+ TMH_plugin->free_reserve_history (TMH_plugin->cls,
rh);
/* Balance is good, sign the coin! */
@@ -435,7 +435,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
if (NULL == sig)
{
GNUNET_break (0);
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_error (connection,
"Internal error");
@@ -448,7 +448,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
&collectable.h_coin_envelope);
collectable.reserve_sig = *signature;
if (GNUNET_OK !=
- plugin->insert_collectable_blindcoin (plugin->cls,
+ TMH_plugin->insert_collectable_blindcoin (TMH_plugin->cls,
session,
&h_blind,
amount_required,
@@ -456,12 +456,12 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection,
{
GNUNET_break (0);
GNUNET_CRYPTO_rsa_signature_free (sig);
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- plugin->commit (plugin->cls,
+ TMH_plugin->commit (TMH_plugin->cls,
session))
{
TALER_LOG_WARNING ("/withdraw/sign transaction commit failed\n");
@@ -523,7 +523,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
/* fee for THIS transaction; the melt amount includes the fee! */
spent = coin_details->melt_amount_with_fee;
/* add historic transaction costs of this coin */
- tl = plugin->get_coin_transactions (plugin->cls,
+ tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls,
session,
&coin_public_info->coin_pub);
if (GNUNET_OK !=
@@ -532,7 +532,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
&spent))
{
GNUNET_break (0);
- plugin->free_coin_transaction_list (plugin->cls,
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
tl);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -553,11 +553,11 @@ refresh_accept_melts (struct MHD_Connection *connection,
coin_details->melt_amount_with_fee,
coin_residual))
? GNUNET_NO : GNUNET_SYSERR;
- plugin->free_coin_transaction_list (plugin->cls,
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
tl);
return res;
}
- plugin->free_coin_transaction_list (plugin->cls,
+ TMH_plugin->free_coin_transaction_list (TMH_plugin->cls,
tl);
melt.coin = *coin_public_info;
@@ -565,7 +565,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
melt.session_hash = *session_hash;
melt.amount_with_fee = coin_details->melt_amount_with_fee;
if (GNUNET_OK !=
- plugin->insert_refresh_melt (plugin->cls,
+ TMH_plugin->insert_refresh_melt (TMH_plugin->cls,
session,
oldcoin_index,
&melt))
@@ -617,26 +617,26 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
int res;
unsigned int i;
- if (NULL == (session = plugin->get_session (plugin->cls,
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_OK !=
- plugin->start (plugin->cls,
+ TMH_plugin->start (TMH_plugin->cls,
session))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
- res = plugin->get_refresh_session (plugin->cls,
+ res = TMH_plugin->get_refresh_session (TMH_plugin->cls,
session,
session_hash,
&refresh_session);
if (GNUNET_YES == res)
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
res = TMH_RESPONSE_reply_refresh_melt_success (connection,
session_hash,
@@ -645,7 +645,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
}
if (GNUNET_SYSERR == res)
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -664,7 +664,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
i)))
{
TMH_KS_release (key_state);
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
@@ -673,13 +673,13 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
/* store requested new denominations */
if (GNUNET_OK !=
- plugin->insert_refresh_order (plugin->cls,
+ TMH_plugin->insert_refresh_order (TMH_plugin->cls,
session,
session_hash,
num_new_denoms,
denom_pubs))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -687,14 +687,14 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
for (i = 0; i < TALER_CNC_KAPPA; i++)
{
if (GNUNET_OK !=
- plugin->insert_refresh_commit_coins (plugin->cls,
+ TMH_plugin->insert_refresh_commit_coins (TMH_plugin->cls,
session,
session_hash,
i,
num_new_denoms,
commit_coin[i]))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -702,14 +702,14 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
for (i = 0; i < TALER_CNC_KAPPA; i++)
{
if (GNUNET_OK !=
- plugin->insert_refresh_commit_links (plugin->cls,
+ TMH_plugin->insert_refresh_commit_links (TMH_plugin->cls,
session,
session_hash,
i,
coin_count,
commit_link[i]))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -723,12 +723,12 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
= GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
TALER_CNC_KAPPA);
if (GNUNET_OK !=
- (res = plugin->create_refresh_session (plugin->cls,
+ (res = TMH_plugin->create_refresh_session (TMH_plugin->cls,
session,
session_hash,
&refresh_session)))
{
- plugin->rollback (plugin->cls,
+ TMH_plugin->rollback (TMH_plugin->cls,
session);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
@@ -736,7 +736,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
if (GNUNET_OK !=
- plugin->commit (plugin->cls,
+ TMH_plugin->commit (TMH_plugin->cls,
session))
{
TALER_LOG_WARNING ("/refresh/melt transaction commit failed\n");
@@ -789,7 +789,7 @@ check_commitment (struct MHD_Connection *connection,
commit_links = GNUNET_malloc (num_oldcoins *
sizeof (struct RefreshCommitLink));
if (GNUNET_OK !=
- plugin->get_refresh_commit_links (plugin->cls,
+ TMH_plugin->get_refresh_commit_links (TMH_plugin->cls,
session,
session_hash,
off,
@@ -888,7 +888,7 @@ check_commitment (struct MHD_Connection *connection,
sizeof (struct RefreshCommitCoin));
if (GNUNET_OK !=
- plugin->get_refresh_commit_coins (plugin->cls,
+ TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls,
session,
session_hash,
off,
@@ -1008,7 +1008,7 @@ refresh_mint_coin (struct MHD_Connection *connection,
return ev_sig;
}
if (GNUNET_OK !=
- plugin->insert_refresh_collectable (plugin->cls,
+ TMH_plugin->insert_refresh_collectable (TMH_plugin->cls,
session,
session_hash,
coin_off,
@@ -1054,14 +1054,14 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
unsigned int j;
unsigned int off;
- if (NULL == (session = plugin->get_session (plugin->cls,
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
- res = plugin->get_refresh_session (plugin->cls,
+ res = TMH_plugin->get_refresh_session (TMH_plugin->cls,
session,
session_hash,
&refresh_session);
@@ -1081,7 +1081,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
for (j=0;j<refresh_session.num_oldcoins;j++)
{
if (GNUNET_OK !=
- plugin->get_refresh_melt (plugin->cls,
+ TMH_plugin->get_refresh_melt (TMH_plugin->cls,
session,
session_hash,
j,
@@ -1095,7 +1095,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
denom_pubs = GNUNET_malloc (refresh_session.num_newcoins *
sizeof (struct TALER_DenominationPublicKey));
if (GNUNET_OK !=
- plugin->get_refresh_order (plugin->cls,
+ TMH_plugin->get_refresh_order (TMH_plugin->cls,
session,
session_hash,
refresh_session.num_newcoins,
@@ -1136,7 +1136,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
/* Client request OK, start transaction */
if (GNUNET_OK !=
- plugin->start (plugin->cls,
+ TMH_plugin->start (TMH_plugin->cls,
session))
{
GNUNET_break (0);
@@ -1149,7 +1149,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
commit_coins = GNUNET_malloc (refresh_session.num_newcoins *
sizeof (struct RefreshCommitCoin));
if (GNUNET_OK !=
- plugin->get_refresh_commit_coins (plugin->cls,
+ TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls,
session,
session_hash,
refresh_session.noreveal_index,
@@ -1195,7 +1195,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
GNUNET_free (commit_coins);
if (GNUNET_OK !=
- plugin->commit (plugin->cls,
+ TMH_plugin->commit (TMH_plugin->cls,
session))
{
TALER_LOG_WARNING ("/refresh/reveal transaction commit failed\n");
@@ -1234,13 +1234,13 @@ TMH_DB_execute_refresh_link (struct MHD_Connection *connection,
struct TALER_EncryptedLinkSecretP shared_secret_enc;
struct LinkDataList *ldl;
- if (NULL == (session = plugin->get_session (plugin->cls,
+ if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
- res = plugin->get_transfer (plugin->cls,
+ res = TMH_plugin->get_transfer (TMH_plugin->cls,
session,
coin_pub,
&transfer_pub,
@@ -1260,7 +1260,7 @@ TMH_DB_execute_refresh_link (struct MHD_Connection *connection,
}
GNUNET_assert (GNUNET_OK == res);
- ldl = plugin->get_link_data_list (plugin->cls,
+ ldl = TMH_plugin->get_link_data_list (TMH_plugin->cls,
session,
coin_pub);
if (NULL == ldl)
@@ -1275,7 +1275,7 @@ TMH_DB_execute_refresh_link (struct MHD_Connection *connection,
&transfer_pub,
&shared_secret_enc,
ldl);
- plugin->free_link_data_list (plugin->cls,
+ TMH_plugin->free_link_data_list (TMH_plugin->cls,
ldl);
return res;
}
diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h
@@ -21,7 +21,6 @@
#ifndef TALER_MINT_HTTPD_DB_H
#define TALER_MINT_HTTPD_DB_H
-#include <libpq-fe.h>
#include <microhttpd.h>
#include <gnunet/gnunet_util_lib.h>
#include "taler_util.h"
diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c
@@ -30,7 +30,6 @@
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <microhttpd.h>
-#include <libpq-fe.h>
#include <pthread.h>
#include "taler_mintdb_plugin.h"
#include "taler_signatures.h"
diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h
@@ -26,7 +26,7 @@
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
#include "taler-mint-httpd.h"
-#include "key_io.h"
+#include "taler_mintdb_lib.h"
/**
diff --git a/src/mint/taler-mint-httpd_mhd.c b/src/mint/taler-mint-httpd_mhd.c
@@ -27,7 +27,6 @@
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <microhttpd.h>
-#include <libpq-fe.h>
#include <pthread.h>
#include "taler-mint-httpd_responses.h"
#include "taler-mint-httpd.h"
diff --git a/src/mint/taler-mint-keycheck.c b/src/mint/taler-mint-keycheck.c
@@ -1,227 +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 taler-mint-keycheck.c
- * @brief Check mint keys for validity. Reads the signing and denomination
- * keys from the mint directory and checks to make sure they are
- * well-formed. This is purely a diagnostic tool.
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include <platform.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "key_io.h"
-
-/**
- * Mint directory with the keys.
- */
-static char *mint_directory;
-
-/**
- * Our configuration.
- */
-static struct GNUNET_CONFIGURATION_Handle *kcfg;
-
-
-/**
- * Function called on each signing key.
- *
- * @param cls closure (NULL)
- * @param filename name of the file the key came from
- * @param ski the sign key
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-signkeys_iter (void *cls,
- const char *filename,
- const struct TALER_MintSigningKeyValidityPSPriv *ski)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Iterating over key `%s' for start time %s\n",
- filename,
- GNUNET_STRINGS_absolute_time_to_string
- (GNUNET_TIME_absolute_ntoh (ski->issue.start)));
-
- if (ntohl (ski->issue.purpose.size) !=
- (sizeof (struct TALER_MintSigningKeyValidityPS) -
- offsetof (struct TALER_MintSigningKeyValidityPS, purpose)))
- {
- fprintf (stderr,
- "Signing key `%s' has invalid purpose size\n",
- filename);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY,
- &ski->issue.purpose,
- &ski->issue.signature.eddsa_signature,
- &ski->issue.master_public_key.eddsa_pub))
- {
- fprintf (stderr,
- "Signing key `%s' has invalid signature\n",
- filename);
- return GNUNET_SYSERR;
- }
- printf ("Signing key `%s' valid\n",
- filename);
- return GNUNET_OK;
-}
-
-
-/**
- * Check signing keys.
- *
- * @return #GNUNET_OK if the keys are OK
- * #GNUNET_NO if not
- */
-static int
-mint_signkeys_check ()
-{
- if (0 > TALER_MINT_signkeys_iterate (mint_directory,
- &signkeys_iter,
- NULL))
- return GNUNET_NO;
- return GNUNET_OK;
-}
-
-
-/**
- * Function called on each denomination key.
- *
- * @param cls closure (NULL)
- * @param dki the denomination key
- * @param alias coin alias
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-denomkeys_iter (void *cls,
- const char *alias,
- const struct TALER_DenominationKeyIssueInformation *dki)
-{
- struct GNUNET_HashCode hc;
-
- if (ntohl (dki->issue.purpose.size) !=
- sizeof (struct TALER_DenominationKeyValidityPS))
- {
- fprintf (stderr,
- "Denomination key for `%s' has invalid purpose size\n",
- alias);
- return GNUNET_SYSERR;
- }
-
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY,
- &dki->issue.purpose,
- &dki->issue.signature.eddsa_signature,
- &dki->issue.master.eddsa_pub))
- {
- fprintf (stderr,
- "Denomination key for `%s' has invalid signature\n",
- alias);
- return GNUNET_SYSERR;
- }
- GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
- &hc);
- if (0 != memcmp (&hc,
- &dki->issue.denom_hash,
- sizeof (struct GNUNET_HashCode)))
- {
- fprintf (stderr,
- "Public key for `%s' does not match signature\n",
- alias);
- return GNUNET_SYSERR;
- }
- printf ("Denomination key `%s' is valid\n",
- alias);
-
- return GNUNET_OK;
-}
-
-
-/**
- * Check denomination keys.
- *
- * @return #GNUNET_OK if the keys are OK
- * #GNUNET_NO if not
- */
-static int
-mint_denomkeys_check ()
-{
- if (0 > TALER_MINT_denomkeys_iterate (mint_directory,
- &denomkeys_iter,
- NULL))
- return GNUNET_NO;
- return GNUNET_OK;
-}
-
-
-/**
- * The main function of the keyup tool
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc, char *const *argv)
-{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keycheck OPTIONS"),
- {'d', "directory", "DIRECTORY",
- "mint directory with keys to check", 1,
- &GNUNET_GETOPT_set_filename, &mint_directory},
- GNUNET_GETOPT_OPTION_END
- };
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-mint-keycheck",
- "WARNING",
- NULL));
-
- if (GNUNET_GETOPT_run ("taler-mint-keycheck",
- options,
- argc, argv) < 0)
- return 1;
- if (NULL == mint_directory)
- {
- fprintf (stderr,
- "Mint directory not given\n");
- return 1;
- }
-
- kcfg = TALER_config_load (mint_directory);
- if (NULL == kcfg)
- {
- fprintf (stderr,
- "Failed to load mint configuration\n");
- return 1;
- }
- if ( (GNUNET_OK != mint_signkeys_check ()) ||
- (GNUNET_OK != mint_denomkeys_check ()) )
- {
- GNUNET_CONFIGURATION_destroy (kcfg);
- return 1;
- }
- GNUNET_CONFIGURATION_destroy (kcfg);
- return 0;
-}
-
-/* end of taler-mint-keycheck.c */
diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c
@@ -1,954 +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 taler-mint-keyup.c
- * @brief Update the mint's keys for coins and signatures,
- * using the mint's offline master key.
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include <platform.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_util.h"
-#include "key_io.h"
-
-/**
- * When generating filenames from a cryptographic hash, we do not use
- * all 512 bits but cut off after this number of characters (in
- * base32-encoding). Base32 is 5 bit per character, and given that we
- * have very few coin types we hash, at 100 bits the chance of
- * collision (by accident over tiny set -- birthday paradox does not
- * apply here!) is negligible.
- */
-#define HASH_CUTOFF 20
-
-/**
- * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
- *
- * @param name value to round
- * @param field rel_value_us or abs_value_us
- */
-#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000);
-
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-/**
- * Struct with all of the key information for a kind of coin. Hashed
- * to generate a unique directory name per coin type.
- */
-struct CoinTypeNBOP
-{
- /**
- * How long can the coin be spend?
- */
- struct GNUNET_TIME_RelativeNBO duration_spend;
-
- /**
- * How long can the coin be withdrawn (generated)?
- */
- struct GNUNET_TIME_RelativeNBO duration_withdraw;
-
- /**
- * What is the value of the coin?
- */
- struct TALER_AmountNBO value;
-
- /**
- * What is the fee charged for withdrawl?
- */
- struct TALER_AmountNBO fee_withdraw;
-
- /**
- * What is the fee charged for deposits?
- */
- struct TALER_AmountNBO fee_deposit;
-
- /**
- * What is the fee charged for melting?
- */
- struct TALER_AmountNBO fee_refresh;
-
- /**
- * Key size in NBO.
- */
- uint32_t rsa_keysize;
-};
-
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * Set of all of the parameters that chracterize a coin.
- */
-struct CoinTypeParams
-{
-
- /**
- * How long can the coin be spend? Should be significantly
- * larger than @e duration_withdraw (i.e. years).
- */
- struct GNUNET_TIME_Relative duration_spend;
-
- /**
- * How long can the coin be withdrawn (generated)? Should be small
- * enough to limit how many coins will be signed into existence with
- * the same key, but large enough to still provide a reasonable
- * anonymity set.
- */
- struct GNUNET_TIME_Relative duration_withdraw;
-
- /**
- * How much should coin creation (@e duration_withdraw) duration
- * overlap with the next coin? Basically, the starting time of two
- * coins is always @e duration_withdraw - @e duration_overlap apart.
- */
- struct GNUNET_TIME_Relative duration_overlap;
-
- /**
- * What is the value of the coin?
- */
- struct TALER_Amount value;
-
- /**
- * What is the fee charged for withdrawl?
- */
- struct TALER_Amount fee_withdraw;
-
- /**
- * What is the fee charged for deposits?
- */
- struct TALER_Amount fee_deposit;
-
- /**
- * What is the fee charged for melting?
- */
- struct TALER_Amount fee_refresh;
-
- /**
- * Time at which this coin is supposed to become valid.
- */
- struct GNUNET_TIME_Absolute anchor;
-
- /**
- * Length of the RSA key in bits.
- */
- uint32_t rsa_keysize;
-};
-
-
-/**
- * Filename of the master private key.
- */
-static char *masterkeyfile;
-
-/**
- * Director of the mint, containing the keys.
- */
-static char *mint_directory;
-
-/**
- * Time to pretend when the key update is executed.
- */
-static char *pretend_time_str;
-
-/**
- * Handle to the mint's configuration
- */
-static struct GNUNET_CONFIGURATION_Handle *kcfg;
-
-/**
- * Time when the key update is executed. Either the actual current time, or a
- * pretended time.
- */
-static struct GNUNET_TIME_Absolute now;
-
-/**
- * Master private key of the mint.
- */
-static struct TALER_MasterPrivateKeyP master_priv;
-
-/**
- * Master public key of the mint.
- */
-static struct TALER_MasterPublicKeyP master_public_key;
-
-/**
- * Until what time do we provide keys?
- */
-static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
-
-
-/**
- * Obtain the name of the directory we use to store signing
- * keys created at time @a start.
- *
- * @param start time at which we create the signing key
- * @return name of the directory we should use, basically "$MINTDIR/$TIME/";
- * (valid until next call to this function)
- */
-static const char *
-get_signkey_file (struct GNUNET_TIME_Absolute start)
-{
- static char dir[4096];
-
- GNUNET_snprintf (dir,
- sizeof (dir),
- "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu",
- mint_directory,
- (unsigned long long) start.abs_value_us);
- return dir;
-}
-
-
-/**
- * Hash the data defining the coin type. Exclude information that may
- * not be the same for all instances of the coin type (i.e. the
- * anchor, overlap).
- *
- * @param p coin parameters to convert to a hash
- * @param hash[OUT] set to the hash matching @a p
- */
-static void
-hash_coin_type (const struct CoinTypeParams *p,
- struct GNUNET_HashCode *hash)
-{
- struct CoinTypeNBOP p_nbo;
-
- memset (&p_nbo,
- 0,
- sizeof (struct CoinTypeNBOP));
- p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
- p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
- TALER_amount_hton (&p_nbo.value,
- &p->value);
- TALER_amount_hton (&p_nbo.fee_withdraw,
- &p->fee_withdraw);
- TALER_amount_hton (&p_nbo.fee_deposit,
- &p->fee_deposit);
- TALER_amount_hton (&p_nbo.fee_refresh,
- &p->fee_refresh);
- p_nbo.rsa_keysize = htonl (p->rsa_keysize);
- GNUNET_CRYPTO_hash (&p_nbo,
- sizeof (struct CoinTypeNBOP),
- hash);
-}
-
-
-/**
- * Obtain the name of the directory we should use to store coins of
- * the given type. The directory name has the format
- * "$MINTDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the
- * coin and "$HASH" encodes all of the coin's parameters, generating a
- * unique string for each type of coin. Note that the "$HASH"
- * includes neither the absolute creation time nor the key of the
- * coin, thus the files in the subdirectory really just refer to the
- * same type of coins, not the same coin.
- *
- * @param p coin parameters to convert to a directory name
- * @return directory name (valid until next call to this function)
- */
-static const char *
-get_cointype_dir (const struct CoinTypeParams *p)
-{
- static char dir[4096];
- struct GNUNET_HashCode hash;
- char *hash_str;
- char *val_str;
- size_t i;
-
- hash_coin_type (p, &hash);
- hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash,
- sizeof (struct GNUNET_HashCode));
- GNUNET_assert (NULL != hash_str);
- GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
- hash_str[HASH_CUTOFF] = 0;
-
- val_str = TALER_amount_to_string (&p->value);
- for (i = 0; i < strlen (val_str); i++)
- if ( (':' == val_str[i]) ||
- ('.' == val_str[i]) )
- val_str[i] = '_';
-
- GNUNET_snprintf (dir,
- sizeof (dir),
- "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s",
- mint_directory,
- val_str,
- hash_str);
- GNUNET_free (hash_str);
- GNUNET_free (val_str);
- return dir;
-}
-
-
-/**
- * Obtain the name of the file we would use to store the key
- * information for a coin of the given type @a p and validity
- * start time @a start
- *
- * @param p parameters for the coin
- * @param start when would the coin begin to be issued
- * @return name of the file to use for this coin
- * (valid until next call to this function)
- */
-static const char *
-get_cointype_file (const struct CoinTypeParams *p,
- struct GNUNET_TIME_Absolute start)
-{
- static char filename[4096];
- const char *dir;
-
- dir = get_cointype_dir (p);
- GNUNET_snprintf (filename,
- sizeof (filename),
- "%s" DIR_SEPARATOR_STR "%llu",
- dir,
- (unsigned long long) start.abs_value_us);
- return filename;
-}
-
-
-/**
- * Get the latest key file from a past run of the key generation
- * tool. Used to calculate the starting time for the keys we
- * generate during this invocation. This function is used to
- * handle both signing keys and coin keys, as in both cases
- * the filenames correspond to the timestamps we need.
- *
- * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated
- * to contain the highest timestamp (below #now)
- * that was found
- * @param filename complete filename (absolute path)
- * @return #GNUNET_OK (to continue to iterate)
- */
-static int
-get_anchor_iter (void *cls,
- const char *filename)
-{
- struct GNUNET_TIME_Absolute *anchor = cls;
- struct GNUNET_TIME_Absolute stamp;
- const char *base;
- char *end = NULL;
-
- base = GNUNET_STRINGS_get_short_name (filename);
- stamp.abs_value_us = strtol (base,
- &end,
- 10);
- if ((NULL == end) || (0 != *end))
- {
- fprintf(stderr,
- "Ignoring unexpected file `%s'.\n",
- filename);
- return GNUNET_OK;
- }
- if (stamp.abs_value_us <= now.abs_value_us)
- *anchor = GNUNET_TIME_absolute_max (stamp,
- *anchor);
- return GNUNET_OK;
-}
-
-
-/**
- * Get the timestamp where the first new key should be generated.
- * Relies on correctly named key files (as we do not parse them,
- * but just look at the filenames to "guess" at their contents).
- *
- * @param dir directory that should contain the existing keys
- * @param duration how long is one key valid (for signing)?
- * @param overlap what's the overlap between the keys validity period?
- * @param[out] anchor the timestamp where the first new key should be generated
- */
-static void
-get_anchor (const char *dir,
- struct GNUNET_TIME_Relative duration,
- struct GNUNET_TIME_Relative overlap,
- struct GNUNET_TIME_Absolute *anchor)
-{
- GNUNET_assert (0 == duration.rel_value_us % 1000000);
- GNUNET_assert (0 == overlap.rel_value_us % 1000000);
- if (GNUNET_YES !=
- GNUNET_DISK_directory_test (dir,
- GNUNET_YES))
- {
- *anchor = now;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No existing keys found, starting with fresh key set.\n");
- return;
- }
- *anchor = GNUNET_TIME_UNIT_ZERO_ABS;
- if (-1 ==
- GNUNET_DISK_directory_scan (dir,
- &get_anchor_iter,
- anchor))
- {
- *anchor = now;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No existing keys found, starting with fresh key set.\n");
- return;
- }
-
- /* FIXME: this check is a bit dubious, as 'now'
- may be way into the future if we want to generate
- many keys... #3727*/
- if ((GNUNET_TIME_absolute_add (*anchor,
- duration)).abs_value_us < now.abs_value_us)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Existing keys are way too old, starting with fresh key set.\n");
- *anchor = now;
- }
- else if (anchor->abs_value_us != now.abs_value_us) // Also odd...
- {
- /* Real starting time is the last start time + duration - overlap */
- *anchor = GNUNET_TIME_absolute_add (*anchor,
- duration);
- *anchor = GNUNET_TIME_absolute_subtract (*anchor,
- overlap);
- }
- /* anchor is now the stamp where we need to create a new key */
-}
-
-
-/**
- * Create a mint signing key (for signing mint messages, not for coins)
- * and assert its correctness by signing it with the master key.
- *
- * @param start start time of the validity period for the key
- * @param duration how long should the key be valid
- * @param pi[OUT] set to the signing key information
- */
-static void
-create_signkey_issue_priv (struct GNUNET_TIME_Absolute start,
- struct GNUNET_TIME_Relative duration,
- struct TALER_MintSigningKeyValidityPSPriv *pi)
-{
- struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
- struct TALER_MintSigningKeyValidityPS *issue = &pi->issue;
-
- priv = GNUNET_CRYPTO_eddsa_key_create ();
- pi->signkey_priv.eddsa_priv = *priv;
- GNUNET_free (priv);
- issue->master_public_key = master_public_key;
- issue->start = GNUNET_TIME_absolute_hton (start);
- issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start,
- duration));
- GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv.eddsa_priv,
- &issue->signkey_pub.eddsa_pub);
- issue->purpose.purpose = htonl (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY);
- issue->purpose.size = htonl (sizeof (struct TALER_MintSigningKeyValidityPS) -
- offsetof (struct TALER_MintSigningKeyValidityPS,
- purpose));
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
- &issue->purpose,
- &issue->signature.eddsa_signature));
-}
-
-
-/**
- * Generate signing keys starting from the last key found to
- * the lookahead time.
- *
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-mint_keys_update_signkeys ()
-{
- struct GNUNET_TIME_Relative signkey_duration;
- struct GNUNET_TIME_Absolute anchor;
- char *signkey_dir;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- "mint_keys",
- "signkey_duration",
- &signkey_duration))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint_keys",
- "signkey_duration");
- return GNUNET_SYSERR;
- }
- ROUND_TO_SECS (signkey_duration,
- rel_value_us);
- GNUNET_asprintf (&signkey_dir,
- "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS,
- mint_directory);
- /* make sure the directory exists */
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create (signkey_dir))
- {
- fprintf (stderr,
- "Failed to create signing key directory\n");
- return GNUNET_SYSERR;
- }
-
- get_anchor (signkey_dir,
- signkey_duration,
- GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */,
- &anchor);
-
- while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
- {
- const char *skf;
- struct TALER_MintSigningKeyValidityPSPriv signkey_issue;
- ssize_t nwrite;
-
- skf = get_signkey_file (anchor);
- GNUNET_break (GNUNET_YES !=
- GNUNET_DISK_file_test (skf));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Generating signing key for %s.\n",
- GNUNET_STRINGS_absolute_time_to_string (anchor));
- create_signkey_issue_priv (anchor,
- signkey_duration,
- &signkey_issue);
- nwrite = GNUNET_DISK_fn_write (skf,
- &signkey_issue,
- sizeof (struct TALER_MintSigningKeyValidityPS),
- GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ);
- if (nwrite != sizeof (struct TALER_MintSigningKeyValidityPS))
- {
- fprintf (stderr,
- "Failed to write to file `%s': %s\n",
- skf,
- STRERROR (errno));
- return GNUNET_SYSERR;
- }
- anchor = GNUNET_TIME_absolute_add (anchor,
- signkey_duration);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Parse configuration for coin type parameters. Also determines
- * our anchor by looking at the existing coins of the same type.
- *
- * @param ct section in the configuration file giving the coin type parameters
- * @param params[OUT] set to the coin parameters from the configuration
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
- */
-static int
-get_cointype_params (const char *ct,
- struct CoinTypeParams *params)
-{
- const char *dir;
- unsigned long long rsa_keysize;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- ct,
- "duration_withdraw",
- ¶ms->duration_withdraw))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "duration_withdraw");
- return GNUNET_SYSERR;
- }
- ROUND_TO_SECS (params->duration_withdraw,
- rel_value_us);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- ct,
- "duration_spend",
- ¶ms->duration_spend))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "duration_spend");
- return GNUNET_SYSERR;
- }
- ROUND_TO_SECS (params->duration_spend,
- rel_value_us);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- ct,
- "duration_overlap",
- ¶ms->duration_overlap))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "mint_denom_duration_overlap");
- return GNUNET_SYSERR;
- }
- ROUND_TO_SECS (params->duration_overlap,
- rel_value_us);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (kcfg,
- ct,
- "rsa_keysize",
- &rsa_keysize))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "rsa_keysize");
- return GNUNET_SYSERR;
- }
- if ( (rsa_keysize > 4 * 2048) ||
- (rsa_keysize < 1024) )
- {
- fprintf (stderr,
- "Given RSA keysize %llu outside of permitted range\n",
- rsa_keysize);
- return GNUNET_SYSERR;
- }
- params->rsa_keysize = (unsigned int) rsa_keysize;
- if (GNUNET_OK !=
- TALER_config_get_denom (kcfg,
- ct,
- "value",
- ¶ms->value))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "value");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_denom (kcfg,
- ct,
- "fee_withdraw",
- ¶ms->fee_withdraw))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "fee_withdraw");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_denom (kcfg,
- ct,
- "fee_deposit",
- ¶ms->fee_deposit))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "fee_deposit");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_denom (kcfg,
- ct,
- "fee_refresh",
- ¶ms->fee_refresh))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "fee_refresh");
- return GNUNET_SYSERR;
- }
-
- dir = get_cointype_dir (params);
- get_anchor (dir,
- params->duration_spend,
- params->duration_overlap,
- ¶ms->anchor);
- return GNUNET_OK;
-}
-
-
-/**
- * Initialize the private and public key information structure for
- * signing coins into existence. Generates the private signing key
- * and signes it together with the coin's meta data using the master
- * signing key.
- *
- * @param params parameters used to initialize the @a dki
- * @param dki[OUT] initialized according to @a params
- */
-static void
-create_denomkey_issue (const struct CoinTypeParams *params,
- struct TALER_DenominationKeyIssueInformation *dki)
-{
- dki->denom_priv.rsa_private_key
- = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize);
- GNUNET_assert (NULL != dki->denom_priv.rsa_private_key);
- dki->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv.rsa_private_key);
- GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
- &dki->issue.denom_hash);
- dki->issue.master = master_public_key;
- dki->issue.start = GNUNET_TIME_absolute_hton (params->anchor);
- dki->issue.expire_withdraw =
- GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
- params->duration_withdraw));
- dki->issue.expire_spend =
- GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
- params->duration_spend));
- TALER_amount_hton (&dki->issue.value,
- ¶ms->value);
- TALER_amount_hton (&dki->issue.fee_withdraw,
- ¶ms->fee_withdraw);
- TALER_amount_hton (&dki->issue.fee_deposit,
- ¶ms->fee_deposit);
- TALER_amount_hton (&dki->issue.fee_refresh,
- ¶ms->fee_refresh);
- dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY);
- dki->issue.purpose.size = htonl (sizeof (struct TALER_DenominationKeyIssueInformation) -
- offsetof (struct TALER_DenominationKeyIssueInformation,
- issue.purpose));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
- &dki->issue.purpose,
- &dki->issue.signature.eddsa_signature));
-}
-
-
-/**
- * Generate new coin signing keys for the coin type of the given @a
- * coin_alias.
- *
- * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
- * @param coin_alias name of the coin's section in the configuration
- */
-static void
-mint_keys_update_cointype (void *cls,
- const char *coin_alias)
-{
- int *ret = cls;
- struct CoinTypeParams p;
- const char *dkf;
- struct TALER_DenominationKeyIssueInformation denomkey_issue;
-
- if (0 != strncasecmp (coin_alias,
- "coin_",
- strlen ("coin_")))
- return; /* not a coin definition */
- if (GNUNET_OK !=
- get_cointype_params (coin_alias,
- &p))
- {
- *ret = GNUNET_SYSERR;
- return;
- }
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create (get_cointype_dir (&p)))
- {
- *ret = GNUNET_SYSERR;
- return;
- }
-
- while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
- {
- dkf = get_cointype_file (&p,
- p.anchor);
- GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Generating denomination key for type `%s', start %s at %s\n",
- coin_alias,
- GNUNET_STRINGS_absolute_time_to_string (p.anchor),
- dkf);
- create_denomkey_issue (&p,
- &denomkey_issue);
- if (GNUNET_OK !=
- TALER_MINT_write_denom_key (dkf,
- &denomkey_issue))
- {
- fprintf (stderr,
- "Failed to write denomination key information to file `%s'.\n",
- dkf);
- *ret = GNUNET_SYSERR;
- GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
- return;
- }
- GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
- p.anchor = GNUNET_TIME_absolute_add (p.anchor,
- p.duration_spend);
- p.anchor = GNUNET_TIME_absolute_subtract (p.anchor,
- p.duration_overlap);
- }
-}
-
-
-/**
- * Update all of the denomination keys of the mint.
- *
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-mint_keys_update_denomkeys ()
-{
- int ok;
-
- ok = GNUNET_OK;
- GNUNET_CONFIGURATION_iterate_sections (kcfg,
- &mint_keys_update_cointype,
- &ok);
- return ok;
-}
-
-
-/**
- * The main function of the taler-mint-keyup tool. This tool is used
- * to create the signing and denomination keys for the mint. It uses
- * the long-term offline private key and writes the (additional) key
- * files to the respective mint directory (from where they can then be
- * copied to the online server). Note that we need (at least) the
- * most recent generated previous keys so as to align the validity
- * periods.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
- char *const *argv)
-{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
- {'m', "master-key", "FILE",
- "master key file (private key)", 1,
- &GNUNET_GETOPT_set_filename, &masterkeyfile},
- {'d', "mint-dir", "DIR",
- "mint directory with keys to update", 1,
- &GNUNET_GETOPT_set_filename, &mint_directory},
- {'t', "time", "TIMESTAMP",
- "pretend it is a different time for the update", 0,
- &GNUNET_GETOPT_set_string, &pretend_time_str},
- GNUNET_GETOPT_OPTION_END
- };
- struct GNUNET_TIME_Relative lookahead_sign;
- struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-mint-keyup",
- "WARNING",
- NULL));
-
- if (GNUNET_GETOPT_run ("taler-mint-keyup",
- options,
- argc, argv) < 0)
- return 1;
- if (NULL == mint_directory)
- {
- fprintf (stderr,
- "Mint directory not given\n");
- return 1;
- }
- if (NULL != pretend_time_str)
- {
- if (GNUNET_OK !=
- GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str,
- &now))
- {
- fprintf (stderr,
- "timestamp `%s' invalid\n",
- pretend_time_str);
- return 1;
- }
- }
- else
- {
- now = GNUNET_TIME_absolute_get ();
- }
- ROUND_TO_SECS (now, abs_value_us);
-
- kcfg = TALER_config_load (mint_directory);
- if (NULL == kcfg)
- {
- fprintf (stderr,
- "Failed to load mint configuration\n");
- return 1;
- }
- if (NULL == masterkeyfile)
- {
- fprintf (stderr,
- "Master key file not given\n");
- return 1;
- }
- eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
- if (NULL == eddsa_priv)
- {
- fprintf (stderr,
- "Failed to initialize master key from file `%s'\n",
- masterkeyfile);
- return 1;
- }
- master_priv.eddsa_priv = *eddsa_priv;
- GNUNET_free (eddsa_priv);
- GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
- &master_public_key.eddsa_pub);
-
- /* check if key from file matches the one from the configuration */
- {
- struct GNUNET_CRYPTO_EddsaPublicKey master_public_key_from_cfg;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_data (kcfg,
- "mint",
- "master_public_key",
- &master_public_key_from_cfg,
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "master_public_key");
- return 1;
- }
- if (0 !=
- memcmp (&master_public_key,
- &master_public_key_from_cfg,
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "master_public_key",
- _("does not match with private key"));
- return 1;
- }
- }
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- "mint_keys",
- "lookahead_sign",
- &lookahead_sign))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint_keys",
- "lookahead_sign");
- return GNUNET_SYSERR;
- }
- if (0 == lookahead_sign.rel_value_us)
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "mint_keys",
- "lookahead_sign",
- _("must not be zero"));
- return GNUNET_SYSERR;
- }
- ROUND_TO_SECS (lookahead_sign,
- rel_value_us);
- lookahead_sign_stamp = GNUNET_TIME_absolute_add (now,
- lookahead_sign);
-
-
- /* finally, do actual work */
- if (GNUNET_OK != mint_keys_update_signkeys ())
- return 1;
-
- if (GNUNET_OK != mint_keys_update_denomkeys ())
- return 1;
- return 0;
-}
-
-/* end of taler-mint-keyup.c */
diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c
@@ -1,305 +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 taler-mint-reservemod.c
- * @brief Modify reserves. Allows manipulation of reserve balances.
- * @author Florian Dold
- * @author Benedikt Mueller
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <libpq-fe.h>
-#include "taler_util.h"
-#include "taler_signatures.h"
-#include "taler_mintdb_plugin.h"
-#include "taler_pq_lib.h"
-
-
-/**
- * Director of the mint, containing the keys.
- */
-static char *mint_directory;
-
-/**
- * Public key of the reserve to manipulate.
- */
-static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub;
-
-/**
- * Handle to the mint's configuration
- */
-static struct GNUNET_CONFIGURATION_Handle *cfg;
-
-/**
- * Database connection handle.
- */
-static PGconn *db_conn;
-
-
-/**
- * Create a new or add to existing reserve. Fails if currencies do
- * not match.
- *
- * @param denom denomination to add
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on error
- */
-// FIXME: this should use the DB abstraction layer. (#3717)
-// FIXME: this should be done by adding an inbound transaction
-// to the table with the transactions for this reserve,
-// not by modifying some 'total' value for the reserve!
-// (we should in fact probably never modify, always just append!) (#3633)
-static int
-reservemod_add (struct TALER_Amount denom)
-{
- PGresult *result;
- const void *param_values[] = {
- reserve_pub
- };
- int param_lengths[] = {
- sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
- };
- int param_formats[] = {
- 1
- };
- struct TALER_Amount old_denom;
- struct TALER_Amount new_denom;
- struct TALER_AmountNBO new_denom_nbo;
-
- result = PQexecParams (db_conn,
- "SELECT balance_value, balance_fraction, balance_currency"
- " FROM reserves"
- " WHERE reserve_pub=$1"
- " LIMIT 1;",
- 1,
- NULL,
- (const char * const *) param_values,
- param_lengths,
- param_formats,
- 1);
- if (PGRES_TUPLES_OK != PQresultStatus (result))
- {
- fprintf (stderr,
- "Select failed: %s\n",
- PQresultErrorMessage (result));
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- struct GNUNET_TIME_AbsoluteNBO exnbo;
- uint32_t value = htonl (denom.value);
- uint32_t fraction = htonl (denom.fraction);
- const void *param_values[] = {
- reserve_pub,
- &value,
- &fraction,
- denom.currency,
- &exnbo
- };
- int param_lengths[] = {
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
- sizeof (uint32_t),
- sizeof (uint32_t),
- strlen (denom.currency),
- sizeof (struct GNUNET_TIME_AbsoluteNBO)
- };
- int param_formats[] = {
- 1, 1, 1, 1, 1
- };
-
- exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS));
- result = PQexecParams (db_conn,
- "INSERT INTO reserves (reserve_pub, balance_value, balance_fraction, balance_currency, expiration_date)"
- " VALUES ($1,$2,$3,$4,$5);",
- 5,
- NULL,
- (const char **) param_values,
- param_lengths,
- param_formats,
- 1);
-
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- fprintf (stderr,
- "Insert failed: %s\n",
- PQresultErrorMessage (result));
- return GNUNET_SYSERR;
- }
- }
- else
- {
- const void *param_values[] = {
- &new_denom_nbo.value,
- &new_denom_nbo.fraction,
- reserve_pub
- };
- int param_lengths[] = {
- sizeof (new_denom_nbo.value),
- sizeof (new_denom_nbo.fraction),
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)
- };
- int param_formats[] = {
- 1, 1, 1
- };
-
- GNUNET_assert (GNUNET_OK ==
- TALER_PQ_extract_amount (result, 0,
- "balance_value",
- "balance_fraction",
- "balance_currency",
- &old_denom));
- TALER_amount_add (&new_denom,
- &old_denom,
- &denom);
- TALER_amount_hton (&new_denom_nbo,
- &new_denom);
- result = PQexecParams (db_conn,
- "UPDATE reserves"
- " SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL"
- " WHERE reserve_pub = $3;",
- 3,
- NULL,
- (const char **) param_values,
- param_lengths,
- param_formats,
- 1);
-
- if (PGRES_COMMAND_OK != PQresultStatus (result))
- {
- fprintf (stderr,
- "Update failed: %s\n",
- PQresultErrorMessage (result));
- return GNUNET_SYSERR;
- }
- /* Yes, for historic reasons libpq returns a 'const char *'... */
- if (0 != strcmp ("1",
- PQcmdTuples (result)))
- {
- fprintf (stderr,
- "Update failed (updated `%s' tupes instead of '1')\n",
- PQcmdTuples (result));
- return GNUNET_SYSERR;
- }
- }
- return GNUNET_OK;
-}
-
-
-/**
- * The main function of the reservemod tool
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc, char *const *argv)
-{
- static char *reserve_pub_str;
- static char *add_str;
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-reservemod OPTIONS"),
- {'d', "mint-dir", "DIR",
- "mint directory with keys to update", 1,
- &GNUNET_GETOPT_set_filename, &mint_directory},
- {'R', "reserve", "KEY",
- "reserve (public key) to modify", 1,
- &GNUNET_GETOPT_set_string, &reserve_pub_str},
- {'a', "add", "DENOM",
- "value to add", 1,
- &GNUNET_GETOPT_set_string, &add_str},
- GNUNET_GETOPT_OPTION_END
- };
- char *connection_cfg_str;
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-mint-reservemod",
- "WARNING",
- NULL));
-
- if (GNUNET_GETOPT_run ("taler-mint-reservemod",
- options,
- argc, argv) < 0)
- return 1;
- if (NULL == mint_directory)
- {
- fprintf (stderr,
- "Mint directory not given\n");
- return 1;
- }
-
- reserve_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
- if ((NULL == reserve_pub_str) ||
- (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (reserve_pub_str,
- strlen (reserve_pub_str),
- reserve_pub,
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))))
- {
- fprintf (stderr,
- "Parsing reserve key invalid\n");
- return 1;
- }
- cfg = TALER_config_load (mint_directory);
- if (NULL == cfg)
- {
- fprintf (stderr,
- "Failed to load mint configuration\n");
- return 1;
- }
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint",
- "db",
- &connection_cfg_str))
- {
- fprintf (stderr,
- "Database configuration string not found\n");
- return 1;
- }
- db_conn = PQconnectdb (connection_cfg_str);
- if (CONNECTION_OK != PQstatus (db_conn))
- {
- fprintf (stderr,
- "Database connection failed: %s\n",
- PQerrorMessage (db_conn));
- return 1;
- }
- if (NULL != add_str)
- {
- struct TALER_Amount add_value;
-
- if (GNUNET_OK !=
- TALER_string_to_amount (add_str,
- &add_value))
- {
- fprintf (stderr,
- "Failed to parse currency amount `%s'\n",
- add_str);
- return 1;
- }
- if (GNUNET_OK !=
- reservemod_add (add_value))
- {
- fprintf (stderr,
- "Failed to update reserve.\n");
- return 1;
- }
- }
- return 0;
-}
-
-/* end taler-mint-reservemod.c */
diff --git a/src/mintdb/Makefile.am b/src/mintdb/Makefile.am
@@ -0,0 +1,68 @@
+# 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
+
+EXTRA_DIST = plugin_mintdb_common.c
+
+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 = \
+ libtalermintdb.la
+
+libtalermintdb_la_SOURCES = \
+ mintdb_keyio.c \
+ mintdb_plugin.c
+
+libtalermintdb_la_LIBADD = \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil
+
+libtalermintdb_la_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS) \
+ -version-info 0:0:0 \
+ -no-undefined
+
+
+check_PROGRAMS = \
+ test-mintdb-deposits \
+ test-mintdb-keyio \
+ test-mintdb-postgres
+
+test_mintdb_deposits_SOURCES = \
+ test_mintdb_deposits.c
+test_mintdb_deposits_LDADD = \
+ libtalermintdb.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ $(top_srcdir)/src/pq/libtalerpq.la \
+ -lgnunetutil \
+ -ljansson \
+ -lpq
+
+test_mintdb_keyio_SOURCES = \
+ test_mintdb_keyio.c
+test_mintdb_keyio_LDADD = \
+ libtalermintdb.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ $(top_srcdir)/src/pq/libtalerpq.la \
+ -lgnunetutil
+
+test_mintdb_postgres_SOURCES = \
+ test_mintdb.c
+test_mintdb_postgres_LDADD = \
+ libtalermintdb.la \
+ $(top_srcdir)/src/util/libtalerutil.la \
+ $(top_srcdir)/src/pq/libtalerpq.la \
+ -lgnunetutil -ljansson
+EXTRA_test_mintdb_postgres_DEPENDENCIES = \
+ libtaler_plugin_mintdb_postgres.la
diff --git a/src/mintdb/mintdb_keyio.c b/src/mintdb/mintdb_keyio.c
@@ -0,0 +1,347 @@
+/*
+ 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 mintdb/mintdb_keyio.c
+ * @brief I/O operations for the Mint's private keys
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Sree Harsha Totakura
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_mintdb_lib.h"
+
+
+/**
+ * Closure for the #signkeys_iterate_dir_iter().
+ */
+struct SignkeysIterateContext
+{
+
+ /**
+ * Function to call on each signing key.
+ */
+ TALER_MINT_SignkeyIterator it;
+
+ /**
+ * Closure for @e it.
+ */
+ void *it_cls;
+};
+
+
+/**
+ * Function called on each file in the directory with
+ * our signing keys. Parses the file and calls the
+ * iterator from @a cls.
+ *
+ * @param cls the `struct SignkeysIterateContext *`
+ * @param filename name of the file to parse
+ * @return #GNUNET_OK to continue,
+ * #GNUNET_NO to stop iteration without error,
+ * #GNUNET_SYSERR to stop iteration with error
+ */
+static int
+signkeys_iterate_dir_iter (void *cls,
+ const char *filename)
+{
+ struct SignkeysIterateContext *skc = cls;
+ ssize_t nread;
+ struct TALER_MintSigningKeyValidityPSPriv issue;
+
+ nread = GNUNET_DISK_fn_read (filename,
+ &issue,
+ sizeof (struct TALER_MintSigningKeyValidityPSPriv));
+ if (nread != sizeof (struct TALER_MintSigningKeyValidityPSPriv))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid signkey file `%s': wrong size\n",
+ filename);
+ return GNUNET_OK;
+ }
+ return skc->it (skc->it_cls,
+ filename,
+ &issue);
+}
+
+
+/**
+ * Call @a it for each signing key found in the @a mint_base_dir.
+ *
+ * @param mint_base_dir base directory for the mint,
+ * the signing keys must be in the #DIR_SIGNKEYS
+ * subdirectory
+ * @param it function to call on each signing key
+ * @param it_cls closure for @a it
+ * @return number of files found (may not match
+ * number of keys given to @a it as malformed
+ * files are simply skipped), -1 on error
+ */
+int
+TALER_MINT_signkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_SignkeyIterator it,
+ void *it_cls)
+{
+ char *signkey_dir;
+ struct SignkeysIterateContext skc;
+ int ret;
+
+ GNUNET_asprintf (&signkey_dir,
+ "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS,
+ mint_base_dir);
+ skc.it = it;
+ skc.it_cls = it_cls;
+ ret = GNUNET_DISK_directory_scan (signkey_dir,
+ &signkeys_iterate_dir_iter,
+ &skc);
+ GNUNET_free (signkey_dir);
+ return ret;
+}
+
+
+/**
+ * Import a denomination key from the given file.
+ *
+ * @param filename the file to import the key from
+ * @param[OUT] dki set to the imported denomination key
+ * @return #GNUNET_OK upon success;
+ * #GNUNET_SYSERR upon failure
+ */
+int
+TALER_MINT_read_denom_key (const char *filename,
+ struct TALER_DenominationKeyIssueInformation *dki)
+{
+ uint64_t size;
+ size_t offset;
+ void *data;
+ struct GNUNET_CRYPTO_rsa_PrivateKey *priv;
+
+ if (GNUNET_OK != GNUNET_DISK_file_size (filename,
+ &size,
+ GNUNET_YES,
+ GNUNET_YES))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Skipping inaccessable denomination key file `%s'\n",
+ filename);
+ return GNUNET_SYSERR;
+ }
+ offset = sizeof (struct TALER_DenominationKeyValidityPS);
+ if (size <= offset)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ data = GNUNET_malloc (size);
+ if (size !=
+ GNUNET_DISK_fn_read (filename,
+ data,
+ size))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+ "read",
+ filename);
+ GNUNET_free (data);
+ return GNUNET_SYSERR;
+ }
+ if (NULL ==
+ (priv = GNUNET_CRYPTO_rsa_private_key_decode (data + offset,
+ size - offset)))
+ {
+ GNUNET_free (data);
+ return GNUNET_SYSERR;
+ }
+ dki->denom_priv.rsa_private_key = priv;
+ dki->denom_pub.rsa_public_key
+ = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
+ memcpy (&dki->issue,
+ data,
+ offset);
+ GNUNET_free (data);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Exports a denomination key to the given file.
+ *
+ * @param filename the file where to write the denomination key
+ * @param dki the denomination key
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure.
+ */
+int
+TALER_MINT_write_denom_key (const char *filename,
+ const struct TALER_DenominationKeyIssueInformation *dki)
+{
+ char *priv_enc;
+ size_t priv_enc_size;
+ struct GNUNET_DISK_FileHandle *fh;
+ ssize_t wrote;
+ size_t wsize;
+ int ret;
+
+ fh = NULL;
+ priv_enc_size
+ = GNUNET_CRYPTO_rsa_private_key_encode (dki->denom_priv.rsa_private_key,
+ &priv_enc);
+ ret = GNUNET_SYSERR;
+ if (NULL == (fh = GNUNET_DISK_file_open
+ (filename,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE,
+ GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE)))
+ goto cleanup;
+ wsize = sizeof (struct TALER_DenominationKeyValidityPS);
+ if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh,
+ &dki->issue.signature,
+ wsize)))
+ goto cleanup;
+ if (wrote != wsize)
+ goto cleanup;
+ if (GNUNET_SYSERR ==
+ (wrote = GNUNET_DISK_file_write (fh,
+ priv_enc,
+ priv_enc_size)))
+ goto cleanup;
+ if (wrote != priv_enc_size)
+ goto cleanup;
+ ret = GNUNET_OK;
+ cleanup:
+ GNUNET_free_non_null (priv_enc);
+ if (NULL != fh)
+ (void) GNUNET_DISK_file_close (fh);
+ return ret;
+}
+
+
+/**
+ * Closure for #denomkeys_iterate_keydir_iter() and
+ * #denomkeys_iterate_topdir_iter().
+ */
+struct DenomkeysIterateContext
+{
+
+ /**
+ * Set to the name of the directory below the top-level directory
+ * during the call to #denomkeys_iterate_keydir_iter().
+ */
+ const char *alias;
+
+ /**
+ * Function to call on each denomination key.
+ */
+ TALER_MINT_DenomkeyIterator it;
+
+ /**
+ * Closure for @e it.
+ */
+ void *it_cls;
+};
+
+
+/**
+ * Decode the denomination key in the given file @a filename and call
+ * the callback in @a cls with the information.
+ *
+ * @param cls the `struct DenomkeysIterateContext *`
+ * @param filename name of a file that should contain
+ * a denomination key
+ * @return #GNUNET_OK to continue to iterate
+ * #GNUNET_NO to abort iteration with success
+ * #GNUNET_SYSERR to abort iteration with failure
+ */
+static int
+denomkeys_iterate_keydir_iter (void *cls,
+ const char *filename)
+{
+ struct DenomkeysIterateContext *dic = cls;
+ struct TALER_DenominationKeyIssueInformation issue;
+
+ if (GNUNET_OK !=
+ TALER_MINT_read_denom_key (filename,
+ &issue))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid denomkey file: '%s'\n",
+ filename);
+ return GNUNET_OK;
+ }
+ return dic->it (dic->it_cls,
+ dic->alias,
+ &issue);
+}
+
+
+/**
+ * Function called on each subdirectory in the #DIR_DENOMKEYS. Will
+ * call the #denomkeys_iterate_keydir_iter() on each file in the
+ * subdirectory.
+ *
+ * @param cls the `struct DenomkeysIterateContext *`
+ * @param filename name of the subdirectory to scan
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR if we need to abort
+ */
+static int
+denomkeys_iterate_topdir_iter (void *cls,
+ const char *filename)
+{
+ struct DenomkeysIterateContext *dic = cls;
+
+ dic->alias = GNUNET_STRINGS_get_short_name (filename);
+ if (0 > GNUNET_DISK_directory_scan (filename,
+ &denomkeys_iterate_keydir_iter,
+ dic))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Call @a it for each denomination key found in the @a mint_base_dir.
+ *
+ * @param mint_base_dir base directory for the mint,
+ * the signing keys must be in the #DIR_DENOMKEYS
+ * subdirectory
+ * @param it function to call on each denomination key found
+ * @param it_cls closure for @a it
+ * @return -1 on error, 0 if no files were found, otherwise
+ * a positive number (however, even with a positive
+ * number it is possible that @a it was never called
+ * as maybe none of the files were well-formed)
+ */
+int
+TALER_MINT_denomkeys_iterate (const char *mint_base_dir,
+ TALER_MINT_DenomkeyIterator it,
+ void *it_cls)
+{
+ char *dir;
+ struct DenomkeysIterateContext dic;
+ int ret;
+
+ GNUNET_asprintf (&dir,
+ "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS,
+ mint_base_dir);
+ dic.it = it;
+ dic.it_cls = it_cls;
+ ret = GNUNET_DISK_directory_scan (dir,
+ &denomkeys_iterate_topdir_iter,
+ &dic);
+ GNUNET_free (dir);
+ return ret;
+}
+
+
+/* end of key_io.c */
diff --git a/src/mintdb/mintdb_plugin.c b/src/mintdb/mintdb_plugin.c
@@ -0,0 +1,149 @@
+/*
+ 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 mintdb/mintdb_plugin.c
+ * @brief Logic to load database plugin
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_mintdb_lib.h"
+#include "taler_mintdb_plugin.h"
+#include <ltdl.h>
+
+
+/**
+ * Libtool search path before we started.
+ */
+static char *old_dlsearchpath;
+
+
+/**
+ * Initialize the plugin.
+ *
+ * @param cfg configuration to use
+ * @return #GNUNET_OK on success
+ */
+struct TALER_MINTDB_Plugin *
+TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char *plugin_name;
+ char *lib_name;
+ struct GNUNET_CONFIGURATION_Handle *cfg_dup;
+ struct TALER_MINTDB_Plugin *plugin;
+
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "mint",
+ "db",
+ &plugin_name))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "db");
+ return NULL;
+ }
+ (void) GNUNET_asprintf (&lib_name,
+ "libtaler_plugin_mintdb_%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_MINT_plugin_unload (struct TALER_MINTDB_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);
+}
+
+
+/**
+ * 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 ();
+}
+
+
+/* end of plugin.c */
diff --git a/src/mint/plugin_mintdb_common.c b/src/mintdb/plugin_mintdb_common.c
diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c
@@ -0,0 +1,2356 @@
+/*
+ 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 "taler_pq_lib.h"
+#include "taler_signatures.h"
+#include "taler_mintdb_plugin.h"
+#include <pthread.h>
+#include <libpq-fe.h>
+
+#include "plugin_mintdb_common.c"
+
+#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_PQ_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 *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 cls the `struct PostgresClosure` with the plugin-specific state
+ * @param temporary should we use a temporary schema
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+postgres_create_tables (void *cls,
+ int temporary)
+{
+ struct PostgresClosure *pc = cls;
+ PGresult *result;
+ PGconn *conn;
+
+ result = NULL;
+ 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 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 INT8 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 INT8 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 INT8 NOT NULL"
+ ",expended_fraction INT4 NOT NULL"
+ ",expended_currency VARCHAR(4) NOT NULL"
+ ",refresh_session_hash BYTEA"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
+ "("
+ " session_hash BYTEA PRIMARY KEY CHECK (length(session_hash) = 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_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"
+ ",newcoin_index INT2 NOT NULL "
+ ",denom_pub BYTEA NOT NULL "
+ ",PRIMARY KEY (session_hash, newcoin_index)"
+ ") ");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link"
+ "("
+ " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"
+ ",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 #TALER_CNC_KAPPA-1
+ ",cnc_index INT2 NOT NULL"
+ ")");
+ SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin"
+ "("
+ " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) "
+ ",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_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) "
+ ",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_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) "
+ ",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 INT8 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_hash = $1)::INT2 as num_oldcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_hash = $1 and cnc_index = 0)::INT2 as num_newcoins "
+ ",(SELECT count(*) FROM refresh_blind_session_keys "
+ " WHERE session_hash = $1 and newcoin_index = 0)::INT2 as kappa "
+ ",noreveal_index"
+ ",session_commit_sig "
+ ",reveal_ok "
+ "FROM refresh_sessions "
+ "WHERE session_hash = $1",
+ 1, NULL);
+#endif
+
+ PREPARE ("get_known_coin",
+ "SELECT "
+ " coin_pub, denom_pub, denom_sig "
+ ",expended_value, expended_fraction, expended_currency "
+ ",refresh_session_hash "
+ "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_hash = $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_hash"
+ ")"
+ "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_hash = $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_hash = $1 AND cnc_index = $2 AND newcoin_index = $3",
+ 3, NULL);
+ PREPARE ("insert_refresh_order",
+ "INSERT INTO refresh_order ( "
+ " newcoin_index "
+ ",session_hash "
+ ",denom_pub "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ PREPARE ("insert_refresh_melt",
+ "INSERT INTO refresh_melt ( "
+ " session_hash "
+ ",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_hash = $1 AND newcoin_index = $2",
+ 2, NULL);
+ PREPARE ("get_refresh_collectable",
+ "SELECT ev_sig "
+ "FROM refresh_collectable "
+ "WHERE session_hash = $1 AND newcoin_index = $2",
+ 2, NULL);
+ PREPARE ("get_refresh_melt",
+ "SELECT coin_pub "
+ "FROM refresh_melt "
+ "WHERE session_hash = $1 AND oldcoin_index = $2",
+ 2, NULL);
+ PREPARE ("insert_refresh_session",
+ "INSERT INTO refresh_sessions ( "
+ " session_hash "
+ ",noreveal_index "
+ ") "
+ "VALUES ($1, $2) ",
+ 2, NULL);
+ PREPARE ("insert_refresh_commit_link",
+ "INSERT INTO refresh_commit_link ( "
+ " session_hash "
+ ",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_hash "
+ ",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_hash "
+ ",newcoin_index "
+ ",ev_sig "
+ ") "
+ "VALUES ($1, $2, $3) ",
+ 3, NULL);
+ PREPARE ("set_reveal_ok",
+ "UPDATE refresh_sessions "
+ "SET reveal_ok = TRUE "
+ "WHERE session_hash = $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_hash) "
+ " JOIN refresh_commit_coin rcc USING (session_hash) "
+ " JOIN refresh_sessions rs USING (session_hash) "
+ " JOIN refresh_collectable rc USING (session_hash) "
+ "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_hash = rs.session_hash "
+ " ) ",
+ 1, NULL);
+ PREPARE ("get_transfer",
+ "SELECT transfer_pub, link_secret_enc "
+ "FROM refresh_melt rm "
+ " JOIN refresh_commit_link rcl USING (session_hash) "
+ " JOIN refresh_sessions rs USING (session_hash) "
+ "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_hash = rm.session_hash "
+ " ) ",
+ 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_session (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->connection_cfg_str);
+ if (CONNECTION_OK !=
+ PQstatus (db_conn))
+ {
+ TALER_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))
+ {
+ 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_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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(&reserve->pub),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ result = TALER_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC("expiration_date", &expiration_date_nbo),
+ TALER_PQ_RESULT_SPEC_END
+ };
+ EXITIF (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0));
+ EXITIF (GNUNET_OK !=
+ TALER_PQ_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)
+ return GNUNET_SYSERR;
+ ret = GNUNET_OK;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (&reserve->pub),
+ TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value),
+ TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction),
+ TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ TALER_amount_hton (&balance_nbo,
+ &reserve->balance);
+ expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry);
+ result = TALER_PQ_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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (&reserve->pub),
+ TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value),
+ TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (balance_nbo.currency,
+ TALER_PQ_CURRENCY_LEN),
+ TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (&reserve->pub),
+ TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value),
+ TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency,
+ TALER_PQ_CURRENCY_LEN),
+ TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (h_blind),
+ TALER_PQ_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_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_VAR("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
+ TALER_PQ_RESULT_SPEC_VAR("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
+ TALER_PQ_RESULT_SPEC("reserve_sig", &collectable->reserve_sig),
+ TALER_PQ_RESULT_SPEC("reserve_pub", &collectable->reserve_pub),
+ TALER_PQ_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_PQ_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.rsa_public_key = denom_pub;
+ collectable->sig.rsa_signature = 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.rsa_public_key,
+ &denom_pub_enc);
+ denom_sig_enc_size =
+ GNUNET_CRYPTO_rsa_signature_encode (collectable->sig.rsa_signature,
+ &denom_sig_enc);
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (h_blind),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size - 1), /* DB doesn't like the trailing \0 */
+ TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_pub),
+ TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_sig),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ if (GNUNET_OK != postgres_start (cls,
+ session))
+ goto cleanup;
+ result = TALER_PQ_exec_prepared (session->conn,
+ "insert_collectable_blindcoin",
+ params);
+ if (PGRES_COMMAND_OK != PQresultStatus (result))
+ {
+ QUERY_ERR (result);
+ goto rollback;
+ }
+ reserve.pub = 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 TALER_ReservePublicKeyP *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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (reserve_pub),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ result = TALER_PQ_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_PQ_extract_amount (result,
+ --rows,
+ "balance_value",
+ "balance_fraction",
+ "balance_currency",
+ &bt->amount))
+ {
+ GNUNET_free (bt);
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ bt->reserve_pub = *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 TALER_ReserveSignatureP 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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (reserve_pub),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC ("blind_ev", &blind_ev),
+ TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &denom_pub_enc, &denom_pub_enc_size),
+ TALER_PQ_RESULT_SPEC_VAR ("denom_sig", &denom_sig_enc, &denom_sig_enc_size),
+ TALER_PQ_RESULT_SPEC ("reserve_sig", &reserve_sig),
+ TALER_PQ_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_PQ_extract_result (result, rs, --rows))
+ {
+ GNUNET_break (0);
+ goto cleanup;
+ }
+ cbc = GNUNET_new (struct CollectableBlindcoin);
+ cbc->sig.rsa_signature
+ = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc,
+ denom_sig_enc_size);
+ GNUNET_free (denom_sig_enc);
+ denom_sig_enc = NULL;
+ cbc->denom_pub.rsa_public_key
+ = 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.rsa_signature) ||
+ (NULL == cbc->denom_pub.rsa_public_key) )
+ {
+ if (NULL != cbc->sig.rsa_signature)
+ GNUNET_CRYPTO_rsa_signature_free (cbc->sig.rsa_signature);
+ if (NULL != cbc->denom_pub.rsa_public_key)
+ GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub.rsa_public_key);
+ 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)
+ {
+ common_free_reserve_history (cls,
+ 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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ PGresult *result;
+ int ret;
+
+ ret = GNUNET_SYSERR;
+ result = TALER_PQ_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.rsa_public_key,
+ &denom_pub_enc);
+ denom_sig_enc_size =
+ GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig.rsa_signature,
+ &denom_sig_enc);
+ json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT);
+ TALER_amount_hton (&amount_nbo,
+ &deposit->amount_with_fee);
+ struct TALER_PQ_QueryParam params[]= {
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id),
+ TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.value),
+ TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.fraction),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (amount_nbo.currency,
+ TALER_CURRENCY_LEN - 1),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->h_contract),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->h_wire),
+ TALER_PQ_QUERY_PARAM_PTR (&deposit->csig),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (json_wire_enc,
+ strlen (json_wire_enc)),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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 @a session_hash.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database handle to use
+ * @param session_hash hash over the melt to use to locate the session
+ * @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_HashCode *session_hash,
+ struct RefreshSession *refresh_session)
+{
+ // FIXME: check logic!
+ int res;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins),
+ TALER_PQ_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins),
+ TALER_PQ_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index),
+ TALER_PQ_RESULT_SPEC_END
+ };
+
+ res = TALER_PQ_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->noreveal_index = ntohs (refresh_session->noreveal_index);
+
+ PQclear (result);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Store new refresh session data under the given @a session_hash.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param session database handle to use
+ * @param session_hash hash over the melt to use to locate the session
+ * @param refresh_session session data to store
+ * @return #GNUNET_YES on success,
+ * #GNUNET_SYSERR on DB failure
+ */
+static int
+postgres_create_refresh_session (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ const struct RefreshSession *refresh_session)
+{
+ // FIXME: actually store session data!
+ uint16_t noreveal_index;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&noreveal_index),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);
+ noreveal_index = htonl (noreveal_index);
+
+ PGresult *result = TALER_PQ_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 oldcoin_index index of the coin to store
+ * @param melt melt operation details to store; includes
+ * the session hash of the melt
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+postgres_insert_refresh_melt (void *cls,
+ struct TALER_MINTDB_Session *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.rsa_public_key,
+ &buf);
+ {
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(&melt->session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR(&melt->coin.coin_pub),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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_HashCode *session_hash,
+ 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_hash hash to identify refresh session
+ * @param num_newcoins number of coins to generate, size of the @a denom_pubs array
+ * @param denom_pubs array denominations of the coins to create
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+postgres_insert_refresh_order (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t num_newcoins,
+ const struct TALER_DenominationPublicKey *denom_pubs)
+{
+ // FIXME: check logic: was written for just one COIN!
+ uint16_t newcoin_index_nbo = htons (num_newcoins);
+ char *buf;
+ size_t buf_size;
+ PGresult *result;
+
+ buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs->rsa_public_key,
+ &buf);
+
+ {
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR (&newcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR (session_hash),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (buf, buf_size),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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 coins 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_hash hash to identify refresh session
+ * @param newcoin_index array of the @a denom_pubs array
+ * @param denom_pubs where to store the deomination keys
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on internal error
+ */
+static int
+postgres_get_refresh_order (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ uint16_t num_newcoins,
+ struct TALER_DenominationPublicKey *denom_pubs)
+{
+ // FIXME: check logic -- was written for just one coin!
+ char *buf;
+ size_t buf_size;
+ uint16_t newcoin_index_nbo = htons (num_newcoins);
+
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_PQ_exec_prepared (session->conn,
+ "get_refresh_order", params);
+
+ if (PGRES_TUPLES_OK != PQresultStatus (result))
+ {
+ BREAK_DB_ERR (result);
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == PQntuples (result))
+ {
+ PQclear (result);
+ /* FIXME: may want to distinguish between different error cases! */
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (1 == PQntuples (result));
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &buf, &buf_size),
+ TALER_PQ_RESULT_SPEC_END
+ };
+ if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ denom_pubs->rsa_public_key
+ = GNUNET_CRYPTO_rsa_public_key_decode (buf,
+ buf_size);
+ GNUNET_free (buf);
+ return GNUNET_OK;
+}
+
+
+
+/**
+ * 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 session_hash hash to identify refresh session
+ * @param i set index (1st dimension)
+ * @param num_newcoins coin index size of the @a commit_coins array
+ * @param commit_coins array of coin commitments to store
+ * @return #GNUNET_OK on success
+ * #GNUNET_SYSERR on error
+ */
+static int
+postgres_insert_refresh_commit_coins (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int i,
+ unsigned int num_newcoins,
+ const struct RefreshCommitCoin *commit_coins)
+{
+ // FIXME: check logic! -- was written for single commit_coin!
+ uint16_t cnc_index_nbo = htons (i);
+ uint16_t newcoin_index_nbo = htons (num_newcoins);
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED(commit_coins->coin_ev, commit_coins->coin_ev_size),
+ TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED (commit_coins->refresh_link->coin_priv_enc,
+ commit_coins->refresh_link->blinding_key_enc_size +
+ sizeof (union TALER_CoinSpendPrivateKeyP)),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_PQ_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 session_hash hash to identify refresh session
+ * @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_coins (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ 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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_PQ_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_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_VAR("coin_ev", &c_buf, &c_buf_size),
+ TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &rl_buf, &rl_buf_size),
+ TALER_PQ_RESULT_SPEC_END
+ };
+ if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ return GNUNET_SYSERR;
+ }
+ PQclear (result);
+ if (rl_buf_size < sizeof (union TALER_CoinSpendPrivateKeyP))
+ {
+ 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 session_hash hash to identify refresh session
+ * @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_links (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ 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_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&commit_link->transfer_pub),
+ TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR(&commit_link->shared_secret_enc),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_PQ_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 session_hash hash to identify refresh session
+ * @param i set index (1st dimension)
+ * @param num_links size of the @a commit_link array
+ * @param links[OUT] array of 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_links (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const struct GNUNET_HashCode *session_hash,
+ unsigned int i,
+ unsigned int num_links,
+ struct RefreshCommitLink *links)
+{
+ // FIXME: check logic: was written for a single link!
+ uint16_t cnc_index_nbo = htons (i);
+ uint16_t oldcoin_index_nbo = htons (num_links);
+
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC("transfer_pub", &links->transfer_pub),
+ TALER_PQ_RESULT_SPEC("link_secret_enc", &links->shared_secret_enc),
+ TALER_PQ_RESULT_SPEC_END
+ };
+
+ if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0))
+ {
+ PQclear (result);
+ 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_hash hash to identify 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_HashCode *session_hash,
+ uint16_t newcoin_index,
+ const struct TALER_DenominationSignature *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->rsa_signature,
+ &buf);
+ {
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(session_hash),
+ TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo),
+ TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ result = TALER_PQ_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_data_list (void *cls,
+ struct TALER_MINTDB_Session *session,
+ const union TALER_CoinSpendPublicKeyP *coin_pub)
+{
+ // FIXME: check logic!
+ struct LinkDataList *ldl;
+ struct LinkDataList *pos;
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(coin_pub),
+ TALER_PQ_QUERY_PARAM_END
+ };
+ PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &ld_buf, &ld_buf_size),
+ TALER_PQ_RESULT_SPEC_VAR("denom_pub", &pk_buf, &pk_buf_size),
+ TALER_PQ_RESULT_SPEC_VAR("ev_sig", &sig_buf, &sig_buf_size),
+ TALER_PQ_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_PQ_extract_result (result, rs, i))
+ {
+ PQclear (result);
+ GNUNET_break (0);
+ common_free_link_data_list (cls,
+ 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);
+ common_free_link_data_list (cls,
+ 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);
+ common_free_link_data_list (cls,
+ ldl);
+ return NULL;
+ }
+ pos = GNUNET_new (struct LinkDataList);
+ pos->next = ldl;
+ pos->link_data_enc = link_enc;
+ pos->denom_pub.rsa_public_key = denom_pub;
+ pos->ev_sig.rsa_signature = 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
+ * #postgres_get_link_data_list() enable the owner of an old coin to
+ * determine the private keys of the new coins after the melt.
+ *
+ * @param cls the `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 union TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_TransferPublicKeyP *transfer_pub,
+ struct TALER_EncryptedLinkSecretP *shared_secret_enc)
+{
+ // FIXME: check logic!
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_QUERY_PARAM_PTR(coin_pub),
+ TALER_PQ_QUERY_PARAM_END
+ };
+
+ PGresult *result = TALER_PQ_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_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC("transfer_pub", transfer_pub),
+ TALER_PQ_RESULT_SPEC("link_secret_enc", shared_secret_enc),
+ TALER_PQ_RESULT_SPEC_END
+ };
+
+ if (GNUNET_OK != TALER_PQ_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 union TALER_CoinSpendPublicKeyP *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))
+ {
+ TALER_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_conn_str",
+ &pg->connection_cfg_str))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "mint",
+ "db_conn_str");
+ return NULL;
+ }
+ plugin = GNUNET_new (struct TALER_MINTDB_Plugin);
+ plugin->cls = pg;
+ plugin->get_session = &postgres_get_session;
+ plugin->drop_temporary = &postgres_drop_temporary;
+ plugin->create_tables = &postgres_create_tables;
+ plugin->start = &postgres_start;
+ plugin->commit = &postgres_commit;
+ plugin->rollback = &postgres_rollback;
+ plugin->reserve_get = &postgres_reserve_get;
+ plugin->reserves_in_insert = &postgres_reserves_in_insert;
+ plugin->get_collectable_blindcoin = &postgres_get_collectable_blindcoin;
+ plugin->insert_collectable_blindcoin = &postgres_insert_collectable_blindcoin;
+ plugin->get_reserve_history = &postgres_get_reserve_history;
+ plugin->free_reserve_history = &common_free_reserve_history;
+ plugin->have_deposit = &postgres_have_deposit;
+ plugin->insert_deposit = &postgres_insert_deposit;
+ plugin->get_refresh_session = &postgres_get_refresh_session;
+ plugin->create_refresh_session = &postgres_create_refresh_session;
+ plugin->insert_refresh_melt = &postgres_insert_refresh_melt;
+ plugin->get_refresh_melt = &postgres_get_refresh_melt;
+ plugin->insert_refresh_order = &postgres_insert_refresh_order;
+ plugin->get_refresh_order = &postgres_get_refresh_order;
+ plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins;
+ plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins;
+ plugin->insert_refresh_commit_links = &postgres_insert_refresh_commit_links;
+ plugin->get_refresh_commit_links = &postgres_get_refresh_commit_links;
+ plugin->insert_refresh_collectable = &postgres_insert_refresh_collectable;
+ plugin->get_link_data_list = &postgres_get_link_data_list;
+ plugin->free_link_data_list = &common_free_link_data_list;
+ plugin->get_transfer = &postgres_get_transfer;
+ // plugin->have_lock = &postgres_have_lock;
+ // plugin->insert_lock = &postgres_insert_lock;
+ plugin->get_coin_transactions = &postgres_get_coin_transactions;
+ plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
+ 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->connection_cfg_str);
+ GNUNET_free (pg);
+ GNUNET_free (plugin);
+ return NULL;
+}
+
+/* end of plugin_mintdb_postgres.c */
diff --git a/src/mint/test_mint_db.c b/src/mintdb/test_mintdb.c
diff --git a/src/mint/test_mint_deposits.c b/src/mintdb/test_mintdb_deposits.c
diff --git a/src/mint/test_mint_common.c b/src/mintdb/test_mintdb_keyio.c