diff options
Diffstat (limited to 'src/mint')
31 files changed, 4598 insertions, 3258 deletions
| diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 19fba62f..1eba7d61 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -1,17 +1,33 @@  # 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 \ -  mint_db.c +  plugin.c plugin.h \ +  taler_mintdb_plugin.h  libtalermint_common_la_LIBADD = \    $(top_builddir)/src/util/libtalerutil.la \ -  -lgnunetutil \ -  -lpq +  -lgnunetutil  libtalermint_common_la_LDFLAGS = \    $(POSTGRESQL_LDFLAGS) \ @@ -70,7 +86,6 @@ taler_mint_httpd_SOURCES = \    taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \    taler-mint-httpd_responses.c taler-mint-httpd_responses.h \    taler-mint-httpd_mhd.c \ -  taler-mint-httpd_keys.c \    taler-mint-httpd_deposit.c \    taler-mint-httpd_withdraw.c \    taler-mint-httpd_refresh.c @@ -128,4 +143,4 @@ test_mint_db_LDADD = \    libtalermint_common.la \    $(top_srcdir)/src/util/libtalerutil.la \    $(top_srcdir)/src/pq/libtalerpq.la \ -  -lgnunetutil +  -lgnunetutil -ljansson diff --git a/src/mint/key_io.c b/src/mint/key_io.c index 6b70e980..182d6f3d 100644 --- a/src/mint/key_io.c +++ b/src/mint/key_io.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 Christian Grothoff (and other contributing authors) +  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 @@ -13,9 +13,8 @@    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 key_io.c + * @file mint/key_io.c   * @brief I/O operations for the Mint's private keys   * @author Florian Dold   * @author Benedikt Mueller @@ -26,26 +25,39 @@  #include "key_io.h" +/** + * Closure for the #signkeys_iterate_dir_iter(). + */  struct SignkeysIterateContext  { -  TALER_MINT_SignkeyIterator it; -  void *it_cls; -}; +  /** +   * Function to call on each signing key. +   */ +  TALER_MINT_SignkeyIterator it; -struct DenomkeysIterateContext -{ -  const char *alias; -  TALER_MINT_DenomkeyIterator 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_MINT_SignKeyIssuePriv issue; @@ -55,37 +67,58 @@ signkeys_iterate_dir_iter (void *cls,                                 sizeof (struct TALER_MINT_SignKeyIssuePriv));    if (nread != sizeof (struct TALER_MINT_SignKeyIssuePriv))    { -    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid signkey file: '%s'\n", filename); +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "Invalid signkey file `%s': wrong size\n", +                filename);      return GNUNET_OK;    } -  return skc->it (skc->it_cls, &issue); +  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 *cls) +                             TALER_MINT_SignkeyIterator it, +                             void *it_cls)  {    char *signkey_dir; -  size_t len;    struct SignkeysIterateContext skc; +  int ret; -  len = GNUNET_asprintf (&signkey_dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mint_base_dir); -  GNUNET_assert (len > 0); - +  GNUNET_asprintf (&signkey_dir, +                   "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, +                   mint_base_dir);    skc.it = it; -  skc.it_cls = cls; - -  return GNUNET_DISK_directory_scan (signkey_dir, &signkeys_iterate_dir_iter, &skc); +  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 + * Import a denomination key from the given file.   *   * @param filename the file to import the key from - * @param dki pointer to return the imported denomination key - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + * @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, @@ -95,42 +128,55 @@ TALER_MINT_read_denom_key (const char *filename,    size_t offset;    void *data;    struct GNUNET_CRYPTO_rsa_PrivateKey *priv; -  int ret; -  ret = GNUNET_SYSERR; -  data = NULL; -  offset = sizeof (struct TALER_MINT_DenomKeyIssuePriv) -      - offsetof (struct TALER_MINT_DenomKeyIssuePriv, issue.signature);    if (GNUNET_OK != GNUNET_DISK_file_size (filename,                                            &size,                                            GNUNET_YES,                                            GNUNET_YES)) -    goto cleanup; +  { +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Skipping inaccessable denomination key file `%s'\n", +                filename); +    return GNUNET_SYSERR; +  } +  offset = sizeof (struct TALER_MINT_DenomKeyIssue);    if (size <= offset)    {      GNUNET_break (0); -    goto cleanup; +    return GNUNET_SYSERR;    }    data = GNUNET_malloc (size); -  if (size != GNUNET_DISK_fn_read (filename, -                                   data, -                                   size)) -    goto cleanup; -  if (NULL == (priv = GNUNET_CRYPTO_rsa_private_key_decode (data + offset, -                                                            size - offset))) -    goto cleanup; -  dki->denom_priv = priv; -  memcpy (&dki->issue.signature, data, offset); -  ret = GNUNET_OK; - - cleanup: -  GNUNET_free_non_null (data); -  return ret; +  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 + * Exports a denomination key to the given file.   *   * @param filename the file where to write the denomination key   * @param dki the denomination key @@ -148,25 +194,26 @@ TALER_MINT_write_denom_key (const char *filename,    int ret;    fh = NULL; -  priv_enc_size = GNUNET_CRYPTO_rsa_private_key_encode (dki->denom_priv, -                                                        &priv_enc); +  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_MINT_DenomKeyIssuePriv) -      - offsetof (struct TALER_MINT_DenomKeyIssuePriv, issue.signature); +  wsize = sizeof (struct TALER_MINT_DenomKeyIssue);    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))) +  if (GNUNET_SYSERR == +      (wrote = GNUNET_DISK_file_write (fh, +                                       priv_enc, +                                       priv_enc_size)))      goto cleanup;    if (wrote != priv_enc_size)      goto cleanup; @@ -179,25 +226,74 @@ TALER_MINT_write_denom_key (const char *filename,  } +/** + * 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_MINT_DenomKeyIssuePriv issue; -  if (GNUNET_OK != TALER_MINT_read_denom_key (filename, &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); +  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) @@ -205,36 +301,47 @@ denomkeys_iterate_topdir_iter (void *cls,    struct DenomkeysIterateContext *dic = cls;    dic->alias = GNUNET_STRINGS_get_short_name (filename); - -  // FIXME: differentiate between error case and normal iteration abortion -  if (0 > GNUNET_DISK_directory_scan (filename, &denomkeys_iterate_keydir_iter, dic)) +  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 *cls) +                              TALER_MINT_DenomkeyIterator it, +                              void *it_cls)  {    char *dir; -  size_t len;    struct DenomkeysIterateContext dic; +  int ret; -  len = GNUNET_asprintf (&dir, -                         "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS, -                         mint_base_dir); -  GNUNET_assert (len > 0); - +  GNUNET_asprintf (&dir, +                   "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS, +                   mint_base_dir);    dic.it = it; -  dic.it_cls = cls; - -  // scan over alias dirs -  return GNUNET_DISK_directory_scan (dir, -                                     &denomkeys_iterate_topdir_iter, -                                     &dic); +  dic.it_cls = it_cls; +  ret = GNUNET_DISK_directory_scan (dir, +                                    &denomkeys_iterate_topdir_iter, +                                    &dic); +  GNUNET_free (dir); +  return ret;  } - -/* end of mint_common.c */ +/* end of key_io.c */ diff --git a/src/mint/key_io.h b/src/mint/key_io.h index c9fd5762..6c6722a8 100644 --- a/src/mint/key_io.h +++ b/src/mint/key_io.h @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 Christian Grothoff (and other contributing authors) +  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 @@ -14,72 +14,103 @@    TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/>  */  /** - * @file key_io.h + * @file mint/key_io.h   * @brief IO operations for the mint's private keys   * @author Florian Dold   * @author Benedikt Mueller   * @author Christian Grothoff - * - * TODO: - * - document better   */  #ifndef KEY_IO_H  #define KEY_IO_H  #include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_common.h> -#include "taler_util.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. - * Includes the private key followed by the signed - * issue message. + * 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_MINT_SignKeyIssuePriv  { -  struct GNUNET_CRYPTO_EddsaPrivateKey signkey_priv; +  /** +   * Private key part of the mint's signing key. +   */ +  struct TALER_MintPrivateKey signkey_priv; +  /** +   * Public information about a mint signing key. +   */    struct TALER_MINT_SignKeyIssue issue;  }; +GNUNET_NETWORK_STRUCT_END + +/** + * All information about a denomination key (which is used to + * sign coins into existence). + */  struct TALER_MINT_DenomKeyIssuePriv  {    /** -   * The private key of the denomination.  Will be NULL if the private key is -   * not available. +   * 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 GNUNET_CRYPTO_rsa_PrivateKey *denom_priv; +  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_MINT_DenomKeyIssue issue;  }; - -  /** - * Iterator for sign keys. + * Iterator over signing keys.   *   * @param cls closure - * @param ski the sign key issue + * @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_MINT_SignKeyIssuePriv *ski); +  /** - * Iterator for denomination keys. + * Iterator over denomination keys.   *   * @param cls closure - * @param dki the denomination key issue + * @param dki the denomination key   * @param alias coin alias   * @return #GNUNET_OK to continue to iterate,   *  #GNUNET_NO to stop iteration with no error, @@ -93,23 +124,44 @@ typedef int  /** - * FIXME + * 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 *cls); +                             TALER_MINT_SignkeyIterator it, +                             void *it_cls);  /** - * FIXME + * 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 *cls); +                              TALER_MINT_DenomkeyIterator it, +                              void *it_cls);  /** - * Exports a denomination key to the given file + * Exports a denomination key to the given file.   *   * @param filename the file where to write the denomination key   * @param dki the denomination key @@ -121,10 +173,10 @@ TALER_MINT_write_denom_key (const char *filename,  /** - * Import a denomination key from the given file + * Import a denomination key from the given file.   *   * @param filename the file to import the key from - * @param dki pointer to return the imported denomination key + * @param[OUT] dki set to the imported denomination key   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure   */  int @@ -132,7 +184,4 @@ TALER_MINT_read_denom_key (const char *filename,                             struct TALER_MINT_DenomKeyIssuePriv *dki); - - -  #endif diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h deleted file mode 100644 index 9818172a..00000000 --- a/src/mint/mint_db.h +++ /dev/null @@ -1,974 +0,0 @@ -/* -  This file is part of TALER -  Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - -  TALER is free software; you can redistribute it and/or modify it under the -  terms of the GNU General Public License as published by the Free Software -  Foundation; either version 3, or (at your option) any later version. - -  TALER is distributed in the hope that it will be useful, but WITHOUT ANY -  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -  A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - -  You should have received a copy of the GNU General Public License along with -  TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file mint/mint_db.h - * @brief Low-level (statement-level) database access for the mint - * @author Florian Dold - * @author Christian Grothoff - */ -#ifndef MINT_DB_H -#define MINT_DB_H - -#include <libpq-fe.h> -#include <microhttpd.h> -#include <gnunet/gnunet_util_lib.h> -#include "taler_util.h" - -#define TALER_TEMP_SCHEMA_NAME "taler_temporary" - -/** - * Initialize database subsystem. - * - * @param connection_cfg configuration for the DB - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_init (const char *connection_cfg); - - -/** - * Get the thread-local database-handle. - * Connect to the db if the connection does not exist yet. - * - * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the - *        database default one - * @param the database connection, or NULL on error - */ -PGconn * -TALER_MINT_DB_get_connection (int temporary); - - -/** - * Drop the temporary taler schema.  This is only useful for testcases - * - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_DB_drop_temporary (PGconn *db); - - -/** - * Create the necessary tables if they are not present - * - * @param temporary should we use a temporary schema - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_DB_create_tables (int temporary); - -/** - * Setup prepared statements.  FIXME: should this be part of the API, - * or just internal to "TALER_MINT_DB_get_connection()"? - * - * @param db_conn connection handle to initialize - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure - */ -int -TALER_MINT_DB_prepare (PGconn *db_conn); - - -/** - * Start a transaction. - * - * @param db_conn connection to use - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_transaction (PGconn *db_conn); - - -/** - * Commit a transaction. - * - * @param db_conn connection to use - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_commit (PGconn *db_conn); - - -/** - * Abort/rollback a transaction. - * - * @param db_conn connection to use - */ -void -TALER_MINT_DB_rollback (PGconn *db_conn); - - -/** - * Information we keep on a bank transfer that - * established a reserve. - */ -struct BankTransfer -{ - -  /** -   * Public key of the reserve that was filled. -   */ -  struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - -  /** -   * Amount that was transferred to the mint. -   */ -  struct TALER_Amount amount; - -  /** -   * Detailed wire information about the transaction. -   */ -  const json_t *wire; - -}; - - -/* FIXME: add functions to add bank transfers to our DB -   (and to test if we already did add one) (#3633) */ - -/** - * A summary of a Reserve - */ -struct Reserve -{ -  /** -   * The reserve's public key.  This uniquely identifies the reserve -   */ -  struct GNUNET_CRYPTO_EddsaPublicKey *pub; - -  /** -   * The balance amount existing in the reserve -   */ -  struct TALER_Amount balance; - -  /** -   * The expiration date of this reserve -   */ -  struct GNUNET_TIME_Absolute expiry; -}; - - -/** - * Information we keep for a withdrawn coin to reproduce - * the /withdraw operation if needed, and to have proof - * that a reserve was drained by this amount. - */ -struct CollectableBlindcoin -{ - -  /** -   * Our signature over the (blinded) coin. -   */ -  struct GNUNET_CRYPTO_rsa_Signature *sig; - -  /** -   * Denomination key (which coin was generated). -   */ -  struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - -  /** -   * Public key of the reserve that was drained. -   */ -  struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - -  /** -   * Hash over the blinded message, needed to verify -   * the @e reserve_sig. -   */ -  struct GNUNET_HashCode h_coin_envelope; - -  /** -   * Signature confirming the withdrawl, matching @e reserve_pub, -   * @e denom_pub and @e h_coin_envelope. -   */ -  struct GNUNET_CRYPTO_EddsaSignature reserve_sig; -}; - - -/** - * Get the summary of a reserve. - * - * @param db the database connection handle - * @param reserve the reserve data.  The public key of the reserve should be set - *          in this structure; it is used to query the database.  The balance - *          and expiration are then filled accordingly. - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_DB_reserve_get (PGconn *db, -                           struct Reserve *reserve); - - -/** - * Insert a incoming transaction into reserves.  New reserves are also created - * through this function. - * - * @param db the database connection handle - * @param reserve the reserve structure.  The public key of the reserve should - *          be set here.  Upon successful execution of this function, the - *          balance and expiration of the reserve will be updated. - * @param balance the amount that has to be added to the reserve - * @param expiry the new expiration time for the reserve - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures - */ -int -TALER_MINT_DB_reserves_in_insert (PGconn *db, -                                  struct Reserve *reserve, -                                  const struct TALER_Amount balance, -                                  const struct GNUNET_TIME_Absolute expiry); - - -/** - * Locate the response for a /withdraw request under the - * key of the hash of the blinded message. - * - * @param db_conn database connection to use - * @param h_blind hash of the blinded message - * @param collectable corresponding collectable coin (blind signature) - *                    if a coin is found - * @return #GNUNET_SYSERR on internal error - *         #GNUNET_NO if the collectable was not found - *         #GNUNET_YES on success - */ -int -TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, -                                         const struct GNUNET_HashCode *h_blind, -                                         struct CollectableBlindcoin *collectable); - - -/** - * Store collectable bit coin under the corresponding - * hash of the blinded message. - * - * @param db_conn database connection to use - * @param h_blind hash of the blinded message - * @param collectable corresponding collectable coin (blind signature) - *                    if a coin is found - * @return #GNUNET_SYSERR on internal error - *         #GNUNET_NO if the collectable was not found - *         #GNUNET_YES on success - */ -int -TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, -                                            const struct GNUNET_HashCode *h_blind, -                                            const struct CollectableBlindcoin *collectable); - - - -/** - * Types of operations on a reserved. - */ -enum TALER_MINT_DB_ReserveOperation -{ -  /** -   * Money was deposited into the reserve via a bank transfer. -   */ -  TALER_MINT_DB_RO_BANK_TO_MINT = 0, - -  /** -   * A Coin was withdrawn from the reserve using /withdraw. -   */ -  TALER_MINT_DB_RO_WITHDRAW_COIN = 1 -}; - - -/** - * Reserve history as a linked list.  Lists all of the transactions - * associated with this reserve (such as the bank transfers that - * established the reserve and all /withdraw operations we have done - * since). - */ -struct ReserveHistory -{ - -  /** -   * Next entry in the reserve history. -   */ -  struct ReserveHistory *next; - -  /** -   * Type of the event, determins @e details. -   */ -  enum TALER_MINT_DB_ReserveOperation type; - -  /** -   * Details of the operation, depending on @e type. -   */ -  union -  { - -    /** -     * Details about a bank transfer to the mint. -     */ -    struct BankTransfer *bank; - -    /** -     * Details about a /withdraw operation. -     */ -    struct CollectableBlindcoin *withdraw; - -  } details; - -}; - - -/** - * Get all of the transaction history associated with the specified - * reserve. - * - * @param db_conn connection to use - * @param reserve_pub public key of the reserve - * @return known transaction history (NULL if reserve is unknown) - */ -struct ReserveHistory * -TALER_MINT_DB_get_reserve_history (PGconn *db_conn, -                                   const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub); - - -/** - * Free memory associated with the given reserve history. - * - * @param rh history to free. - */ -void -TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh); - - -/** - * Specification for a /deposit operation. - */ -struct Deposit -{ -  /** -   * Information about the coin that is being deposited. -   */ -  struct TALER_CoinPublicInfo coin; - -  /** -   * ECDSA signature affirming that the customer intends -   * this coin to be deposited at the merchant identified -   * by @e h_wire in relation to the contract identified -   * by @e h_contract. -   */ -  struct GNUNET_CRYPTO_EcdsaSignature csig; - -  /** -   * Public key of the merchant.  Enables later identification -   * of the merchant in case of a need to rollback transactions. -   */ -  struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; - -  /** -   * Hash over the contract between merchant and customer -   * (remains unknown to the Mint). -   */ -  struct GNUNET_HashCode h_contract; - -  /** -   * Hash of the (canonical) representation of @e wire, used -   * to check the signature on the request.  Generated by -   * the mint from the detailed wire data provided by the -   * merchant. -   */ -  struct GNUNET_HashCode h_wire; - -  /** -   * Detailed wire information for executing the transaction. -   */ -  const json_t *wire; - -  /** -   * Merchant-generated transaction ID to detect duplicate -   * transactions. -   */ -  uint64_t transaction_id; - -  /** -   * Fraction of the coin's remaining value to be deposited. -   * The coin is identified by @e coin_pub. -   */ -  struct TALER_Amount amount; - -}; - - -/** - * Check if we have the specified deposit already in the database. - * - * @param db_conn database connection - * @param deposit deposit to search for - * @return #GNUNET_YES if we know this operation, - *         #GNUNET_NO if this deposit is unknown to us, - *         #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_have_deposit (PGconn *db_conn, -                            const struct Deposit *deposit); - - -/** - * Insert information about deposited coin into the - * database. - * - * @param db_conn connection to the database - * @param deposit deposit information to store - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -int -TALER_MINT_DB_insert_deposit (PGconn *db_conn, -                              const struct Deposit *deposit); - - - -/** - * Global information for a refreshing session.  Includes - * dimensions of the operation, security parameters and - * client signatures from "/refresh/melt" and "/refresh/commit". - */ -struct RefreshSession -{ -  /** -   * Signature over the commitments by the client, -   * only valid if @e has_commit_sig is set. -   */ -  struct GNUNET_CRYPTO_EddsaSignature commit_sig; - -  /** -   * Hash over coins to melt and coins to create of the -   * refresh session. -   */ -  struct GNUNET_HashCode session_hash; - -  /** -   * Signature over the melt by the client. -   */ -  struct GNUNET_CRYPTO_EddsaSignature melt_sig; - -  /** -   * Number of coins we are melting. -   */ -  uint16_t num_oldcoins; - -  /** -   * Number of new coins we are creating. -   */ -  uint16_t num_newcoins; - -  /** -   * Number of parallel operations we perform for the cut and choose. -   * (must be greater or equal to three for security).  0 if not yet -   * known. -   */ -  uint16_t kappa; - -  /** -   * Index (smaller @e kappa) which the mint has chosen to not -   * have revealed during cut and choose. -   */ -  uint16_t noreveal_index; - -}; - - -/** - * Lookup refresh session data under the given public key. - * - * @param db_conn database handle to use - * @param refresh_session_pub public key to use for the lookup - * @param session[OUT] where to store the result - * @return #GNUNET_YES on success, - *         #GNUNET_NO if not found, - *         #GNUNET_SYSERR on DB failure - */ -int -TALER_MINT_DB_get_refresh_session (PGconn *db_conn, -                                   const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                   struct RefreshSession *session); - - -/** - * Store new refresh session data under the given public key. - * - * @param db_conn database handle to use - * @param refresh_session_pub public key to use to locate the session - * @param session session data to store - * @return #GNUNET_YES on success, - *         #GNUNET_SYSERR on DB failure - */ -int -TALER_MINT_DB_create_refresh_session (PGconn *db_conn, -                                      const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                      const struct RefreshSession *session); - - -/** - * Specification for coin in a /refresh/melt operation. - */ -struct RefreshMelt -{ -  /** -   * Information about the coin that is being melted. -   */ -  struct TALER_CoinPublicInfo coin; - -  /** -   * Signature over the melting operation. -   */ -  struct GNUNET_CRYPTO_EcdsaSignature coin_sig; - -  /** -   * Which melting operation should the coin become a part of. -   */ -  struct GNUNET_HashCode melt_hash; - -  /** -   * How much value is being melted? -   * This amount includes the fees, so the final amount contributed -   * to the melt is this value minus the fee for melting the coin. -   */ -  struct TALER_Amount amount; - -}; - - -/** - * Store the given /refresh/melt request in the database. - * - * @param db_conn database connection - * @param session session key of the melt operation - * @param oldcoin_index index of the coin to store - * @param melt coin melt operation details to store - * @return #GNUNET_OK on success - *         #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, -                                   const struct GNUNET_CRYPTO_EddsaPublicKey *session, -                                   uint16_t oldcoin_index, -                                   const struct RefreshMelt *melt); - - - -/** - * Get information about melted coin details from the database. - * - * @param db_conn database connection - * @param session session key of the melt operation - * @param oldcoin_index index of the coin to retrieve - * @param melt melt data to fill in - * @return #GNUNET_OK on success - *         #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, -                                const struct GNUNET_CRYPTO_EddsaPublicKey *session, -                                uint16_t oldcoin_index, -                                struct RefreshMelt *melt); - - -/** - * Store in the database which coin(s) we want to create - * in a given refresh operation. - * - * @param db_conn database connection - * @param session_pub refresh session key - * @param newcoin_index index of the coin to generate - * @param denom_pub denomination of the coin to create - * @return #GNUNET_OK on success - *         #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, -                                    const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                    uint16_t newcoin_index, -                                    const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub); - - -/** - * Lookup in the database the @a newcoin_index coin that we want to - * create in the given refresh operation. - * - * @param db_conn database connection - * @param session_pub refresh session key - * @param newcoin_index index of the coin to generate - * @param denom_pub denomination of the coin to create - * @return NULL on error (not found or internal error) - */ -struct GNUNET_CRYPTO_rsa_PublicKey * -TALER_MINT_DB_get_refresh_order (PGconn *db_conn, -                                 const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                 uint16_t newcoin_index); - - -/** - * We have as many `struct RefreshCommitCoin` as there are new - * coins being created by the refresh (for each of the kappa - * sets).  These are the coins we ask the mint to sign if the - * respective set is selected. - */ -struct RefreshCommitCoin -{ - -  /** -   * Encrypted data allowing those able to decrypt it to derive -   * the private keys of the new coins created by the refresh. -   */ -  struct TALER_RefreshLinkEncrypted *refresh_link; - -  /** -   * Blinded message to be signed (in envelope), with @e coin_env_size bytes. -   */ -  char *coin_ev; - -  /** -   * Number of bytes in @e coin_ev. -   */ -  size_t coin_ev_size; - -}; - - -/** - * Store information about the commitment of the - * given coin for the given refresh session in the database. - * - * @param db_conn database connection to use - * @param refresh_session_pub refresh session this commitment belongs to - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to refreshed (new) coins - * @param commit_coin coin commitment to store - * @return #GNUNET_OK on success - *         #GNUNET_SYSERR on error - */ -int -TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, -                                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                          unsigned int i, -                                          unsigned int j, -                                          const struct RefreshCommitCoin *commit_coin); - - -/** - * Obtain information about the commitment of the - * given coin of the given refresh session from the database. - * - * @param db_conn database connection to use - * @param refresh_session_pub refresh session the commitment belongs to - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to refreshed (new) coins - * @param commit_coin[OUT] coin commitment to return - * @return #GNUNET_OK on success - *         #GNUNET_NO if not found - *         #GNUNET_SYSERR on error - */ -int -TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, -                                       const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                       unsigned int i, -                                       unsigned int j, -                                       struct RefreshCommitCoin *commit_coin); - - -/** - * For each (old) coin being melted, we have a `struct - * RefreshCommitLink` that allows the user to find the shared secret - * to decrypt the respective refresh links for the new coins in the - * `struct RefreshCommitCoin`. - */ -struct RefreshCommitLink -{ -  /** -   * Transfer public key (FIXME: explain!) -   */ -  struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub; - -  /** -   * Encrypted shared secret to decrypt the link. -   */ -  struct TALER_EncryptedLinkSecret shared_secret_enc; -}; - - -/** - * Store the commitment to the given (encrypted) refresh link data - * for the given refresh session. - * - * @param db_conn database connection to use - * @param refresh_session_pub public key of the refresh session this - *        commitment belongs with - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to melted (old) coins - * @param commit_link link information to store - * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success - */ -int -TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, -                                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                          unsigned int i, -                                          unsigned int j, -                                          const struct RefreshCommitLink *commit_link); - -/** - * Obtain the commited (encrypted) refresh link data - * for the given refresh session. - * - * @param db_conn database connection to use - * @param refresh_session_pub public key of the refresh session this - *        commitment belongs with - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to melted (old) coins - * @param cc[OUT] link information to return - * @return #GNUNET_SYSERR on internal error, - *         #GNUNET_NO if commitment was not found - *         #GNUNET_OK on success - */ -int -TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, -                                       const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                       unsigned int i, -                                       unsigned int j, -                                       struct RefreshCommitLink *cc); - - -/** - * Insert signature of a new coin generated during refresh into - * the database indexed by the refresh session and the index - * of the coin.  This data is later used should an old coin - * be used to try to obtain the private keys during "/refresh/link". - * - * @param db_conn database connection - * @param session_pub refresh session - * @param newcoin_index coin index - * @param ev_sig coin signature - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, -                                          const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                          uint16_t newcoin_index, -                                          const struct GNUNET_CRYPTO_rsa_Signature *ev_sig); - - -/** - * Linked list of refresh information linked to a coin. - */ -struct LinkDataList -{ -  /** -   * Information is stored in a NULL-terminated linked list. -   */ -  struct LinkDataList *next; - -  /** -   * Link data, used to recover the private key of the coin -   * by the owner of the old coin. -   */ -  struct TALER_RefreshLinkEncrypted *link_data_enc; - -  /** -   * Denomination public key, determines the value of the coin. -   */ -  struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - -  /** -   * Signature over the blinded envelope. -   */ -  struct GNUNET_CRYPTO_rsa_Signature *ev_sig; -}; - - -/** - * Obtain the link data of a coin, that is the encrypted link - * information, the denomination keys and the signatures. - * - * @param db_conn database connection - * @param coin_pub public key to use to retrieve linkage data - * @return all known link data for the coin - */ -struct LinkDataList * -TALER_db_get_link (PGconn *db_conn, -                   const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); - - -/** - * Free memory of the link data list. - * - * @param ldl link data list to release - */ -void -TALER_db_link_data_list_free (struct LinkDataList *ldl); - - -/** - * Obtain shared secret and transfer public key from the public key of - * the coin.  This information and the link information returned by - * #TALER_db_get_link() enable the owner of an old coin to determine - * the private keys of the new coins after the melt. - * - * - * @param db_conn database connection - * @param coin_pub public key of the coin - * @param transfer_pub[OUT] public transfer key - * @param shared_secret_enc[OUT] set to shared secret - * @return #GNUNET_OK on success, - *         #GNUNET_NO on failure (not found) - *         #GNUNET_SYSERR on internal failure (database issue) - */ -int -TALER_db_get_transfer (PGconn *db_conn, -                       const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, -                       struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, -                       struct TALER_EncryptedLinkSecret *shared_secret_enc); - - -/** - * Specification for a /lock operation. - */ -struct Lock -{ -  /** -   * Information about the coin that is being melted. -   */ -  struct TALER_CoinPublicInfo coin; - -  /** -   * Signature over the melting operation. -   */ -  const struct GNUNET_CRYPTO_EcdsaSignature coin_sig; - -  /** -   * How much value is being melted? -   */ -  struct TALER_Amount amount; - -  // FIXME: more needed... -}; - - -/** - * Test if the given /lock request is known to us. - * - * @param db_conn database connection - * @param lock lock operation - * @return #GNUNET_YES if known, - *         #GNUENT_NO if not, - *         #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_have_lock (PGconn *db_conn, -                         const struct Lock *lock); - - -/** - * Store the given /lock request in the database. - * - * @param db_conn database connection - * @param lock lock operation - * @return #GNUNET_OK on success - *         #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_insert_lock (PGconn *db_conn, -                           const struct Lock *lock); - - -/** - * Enumeration to classify the different types of transactions - * that can be done with a coin. - */ -enum TALER_MINT_DB_TransactionType -{ -  /** -   * /deposit operation. -   */ -  TALER_MINT_DB_TT_DEPOSIT = 0, - -  /** -   * /refresh/melt operation. -   */ -  TALER_MINT_DB_TT_REFRESH_MELT = 1, - -  /** -   * /lock operation. -   */ -  TALER_MINT_DB_TT_LOCK = 2 -}; - - -/** - * List of transactions we performed for a particular coin. - */ -struct TALER_MINT_DB_TransactionList -{ - -  /** -   * Next pointer in the NULL-terminated linked list. -   */ -  struct TALER_MINT_DB_TransactionList *next; - -  /** -   * Type of the transaction, determines what is stored in @e details. -   */ -  enum TALER_MINT_DB_TransactionType type; - -  /** -   * Details about the transaction, depending on @e type. -   */ -  union -  { - -    /** -     * Details if transaction was a /deposit operation. -     */ -    struct Deposit *deposit; - -    /** -     * Details if transaction was a /refresh/melt operation. -     */ -    struct RefreshMelt *melt; - -    /** -     * Details if transaction was a /lock operation. -     */ -    struct Lock *lock; - -  } details; - -}; - - -/** - * Compile a list of all (historic) transactions performed - * with the given coin (/refresh/melt and /deposit operations). - * - * @param db_conn database connection - * @param coin_pub coin to investigate - * @return list of transactions, NULL if coin is fresh - */ -struct TALER_MINT_DB_TransactionList * -TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, -                                     const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); - - -/** - * Free linked list of transactions. - * - * @param list list to free - */ -void -TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list); - - - -#endif /* _NEURO_MINT_DB_H */ diff --git a/src/mint/plugin.c b/src/mint/plugin.c new file mode 100644 index 00000000..4fb75f87 --- /dev/null +++ b/src/mint/plugin.c @@ -0,0 +1,118 @@ +/* +  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) +{ +  return GNUNET_SYSERR; +} + + +/** + * Shutdown the plugin. + */ +void +TALER_MINT_plugin_unload () +{ +  if (NULL == plugin) +    return; +} + + +/** + * Setup libtool paths. + */ +void __attribute__ ((constructor)) +plugin_init () +{ +  int err; +  const char *opath; +  char *path; +  char *cpath; + +  err = lt_dlinit (); +  if (err > 0) +  { +    FPRINTF (stderr, +             _("Initialization of plugin mechanism failed: %s!\n"), +             lt_dlerror ()); +    return; +  } +  opath = lt_dlgetsearchpath (); +  if (NULL != opath) +    old_dlsearchpath = GNUNET_strdup (opath); +  path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); +  if (NULL != path) +  { +    if (NULL != opath) +    { +      GNUNET_asprintf (&cpath, "%s:%s", opath, path); +      lt_dlsetsearchpath (cpath); +      GNUNET_free (path); +      GNUNET_free (cpath); +    } +    else +    { +      lt_dlsetsearchpath (path); +      GNUNET_free (path); +    } +  } +} + + +/** + * Shutdown libtool. + */ +void __attribute__ ((destructor)) +plugin_fini () +{ +  lt_dlsetsearchpath (old_dlsearchpath); +  if (NULL != old_dlsearchpath) +  { +    GNUNET_free (old_dlsearchpath); +    old_dlsearchpath = NULL; +  } +  lt_dlexit (); +} + + +/* end of plugin.c */ diff --git a/src/mint/plugin.h b/src/mint/plugin.h new file mode 100644 index 00000000..0dfb866d --- /dev/null +++ b/src/mint/plugin.h @@ -0,0 +1,50 @@ +/* +  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_common.c b/src/mint/plugin_mintdb_common.c new file mode 100644 index 00000000..a95cf4be --- /dev/null +++ b/src/mint/plugin_mintdb_common.c @@ -0,0 +1,118 @@ +/* +  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_mintdb_common.c + * @brief Functions shared across plugins, this file is meant to be + *        #include-d in each plugin. + * @author Christian Grothoff + */ + +/** + * Free memory associated with the given reserve history. + * + * @param cls the @e cls of this struct with the plugin-specific state (unused) + * @param rh history to free. + */ +static void +common_free_reserve_history (void *cls, +                             struct ReserveHistory *rh) +{ +  struct BankTransfer *bt; +  struct CollectableBlindcoin *cbc; +  struct ReserveHistory *backref; + +  while (NULL != rh) +  { +    switch(rh->type) +    { +    case TALER_MINT_DB_RO_BANK_TO_MINT: +      bt = rh->details.bank; +      if (NULL != bt->wire) +        json_decref (bt->wire); +      GNUNET_free (bt); +      break; +    case TALER_MINT_DB_RO_WITHDRAW_COIN: +      cbc = rh->details.withdraw; +      GNUNET_CRYPTO_rsa_signature_free (cbc->sig.rsa_signature); +      GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub.rsa_public_key); +      GNUNET_free (cbc); +      break; +    } +    backref = rh; +    rh = rh->next; +    GNUNET_free (backref); +  } +} + + +/** + * Free memory of the link data list. + * + * @param cls the @e cls of this struct with the plugin-specific state (unused) + * @param ldl link data list to release + */ +static void +common_free_link_data_list (void *cls, +                            struct LinkDataList *ldl) +{ +  struct LinkDataList *next; + +  while (NULL != ldl) +  { +    next = ldl->next; +    GNUNET_free (ldl->link_data_enc); +    GNUNET_free (ldl); +    ldl = next; +  } +} + + +/** + * Free linked list of transactions. + * + * @param cls the @e cls of this struct with the plugin-specific state (unused) + * @param list list to free + */ +static void +common_free_coin_transaction_list (void *cls, +                                   struct TALER_MINT_DB_TransactionList *list) +{ +  struct TALER_MINT_DB_TransactionList *next; + +  while (NULL != list) +  { +    next = list->next; + +    switch (list->type) +    { +    case TALER_MINT_DB_TT_DEPOSIT: +      json_decref (list->details.deposit->wire); +      GNUNET_free (list->details.deposit); +      break; +    case TALER_MINT_DB_TT_REFRESH_MELT: +      GNUNET_free (list->details.melt); +      break; +    case TALER_MINT_DB_TT_LOCK: +      GNUNET_free (list->details.lock); +      /* FIXME: look at this again once locking is implemented (#3625) */ +      break; +    } +    GNUNET_free (list); +    list = next; +  } +} + +/* end of plugin_mintdb_common.c */ diff --git a/src/mint/mint_db.c b/src/mint/plugin_mintdb_postgres.c index 848f9e04..5a1ff8c0 100644 --- a/src/mint/mint_db.c +++ b/src/mint/plugin_mintdb_postgres.c @@ -15,41 +15,26 @@  */  /** - * @file mint_db.c - * @brief Low-level (statement-level) database access for the mint + * @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 - * - * TODO: - * - The mint_db.h-API should ideally be what we need to port - *   when using other databases; so here we should enable - *   alternative implementations by returning - *   a more opaque DB handle.   */  #include "platform.h"  #include "db_pq.h"  #include "taler_signatures.h" -#include "taler-mint-httpd_responses.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h"  #include <pthread.h> +#include <libpq-fe.h> +#include "plugin_mintdb_common.c" -/** - * Thread-local database connection. - * Contains a pointer to PGconn or NULL. - */ -static pthread_key_t db_conn_threadlocal; - +#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)) -/** - * Database connection string, as read from - * the configuration. - */ -static char *TALER_MINT_db_connection_cfg_str;  #define BREAK_DB_ERR(result) do { \      GNUNET_break(0); \ @@ -84,6 +69,41 @@ static char *TALER_MINT_db_connection_cfg_str;   */  #define TALER_DB_CURRENCY_LEN 3 + +/** + * Handle for a database session (per-thread, for transactions). + */ +struct TALER_MINTDB_Session +{ +  /** +   * Postgres connection handle. +   */ +  PGconn *conn; +}; + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct PostgresClosure +{ + +  /** +   * Thread-local database connection. +   * Contains a pointer to PGconn or NULL. +   */ +  pthread_key_t db_conn_threadlocal; + +  /** +   * Database connection string, as read from +   * the configuration. +   */ +  char *TALER_MINT_db_connection_cfg_str; +}; + + +  /**   * Set the given connection to use a temporary schema   * @@ -108,14 +128,16 @@ set_temporary_schema (PGconn *db)  /**   * 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   */ -int -TALER_MINT_DB_drop_temporary (PGconn *db) +static int +postgres_drop_temporary (void *cls, +                         struct TALER_MINTDB_Session *session)  {    PGresult *result; -  SQLEXEC_ (db, +  SQLEXEC_ (session->conn,              "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;",              result);    return GNUNET_OK; @@ -127,17 +149,20 @@ TALER_MINT_DB_drop_temporary (PGconn *db)  /**   * 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   */ -int -TALER_MINT_DB_create_tables (int temporary) +static int +postgres_create_tables (void *cls, +                        int temporary)  { +  struct PostgresClosure *pc = cls;    PGresult *result;    PGconn *conn;    result = NULL; -  conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); +  conn = PQconnectdb (pc->TALER_MINT_db_connection_cfg_str);    if (CONNECTION_OK != PQstatus (conn))    {      LOG_ERROR ("Database connection failed: %s\n", @@ -145,8 +170,8 @@ TALER_MINT_DB_create_tables (int temporary)      GNUNET_break (0);      return GNUNET_SYSERR;    } -  if ((GNUNET_YES == temporary) -      && (GNUNET_SYSERR == set_temporary_schema (conn))) +  if ( (GNUNET_YES == temporary) && +       (GNUNET_SYSERR == set_temporary_schema (conn)))    {      PQfinish (conn);      return GNUNET_SYSERR; @@ -195,11 +220,11 @@ TALER_MINT_DB_create_tables (int temporary)            ",expended_value INT4 NOT NULL"            ",expended_fraction INT4 NOT NULL"            ",expended_currency VARCHAR(4) NOT NULL" -          ",refresh_session_pub BYTEA" +          ",refresh_session_hash BYTEA"            ")");    SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "            "(" -          " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)" +          " session_hash BYTEA PRIMARY KEY CHECK (length(session_hash) = 32)"            ",session_melt_sig BYTEA"            ",session_commit_sig BYTEA"            ",noreveal_index INT2 NOT NULL" @@ -209,25 +234,25 @@ TALER_MINT_DB_create_tables (int temporary)            ") ");    SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order "            "( " -          " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)" +          " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"            ",newcoin_index INT2 NOT NULL "            ",denom_pub BYTEA NOT NULL " -          ",PRIMARY KEY (session_pub, newcoin_index)" +          ",PRIMARY KEY (session_hash, newcoin_index)"            ") ");    SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link"            "(" -          " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)" +          " 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 kappa-1 +          // ranges from 0 to #KAPPA-1            ",cnc_index INT2 NOT NULL"            ")");    SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin"            "(" -          " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " +          " 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" @@ -237,32 +262,34 @@ TALER_MINT_DB_create_tables (int temporary)            ")");    SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt"            "(" -          " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " +          " 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_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " +          " 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 CHECK (length(denom_pub)=32)" +          ",denom_pub BYTEA NOT NULL" /* FIXME: Link this as a foreign key? */ +          ",denom_sig BYTEA NOT NULL"            ",transaction_id INT8 NOT NULL"            ",amount_currency VARCHAR(4) NOT NULL"            ",amount_value INT4 NOT NULL"            ",amount_fraction INT4 NOT NULL" -          ",merchant_pub BYTEA NOT NULL" +          ",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; @@ -278,8 +305,8 @@ TALER_MINT_DB_create_tables (int temporary)   * @param db_conn connection handle to initialize   * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure   */ -int -TALER_MINT_DB_prepare (PGconn *db_conn) +static int +postgres_prepare (PGconn *db_conn)  {    PGresult *result; @@ -366,16 +393,16 @@ TALER_MINT_DB_prepare (PGconn *db_conn)  #if 0    PREPARE ("get_refresh_session",             "SELECT " -           " (SELECT count(*) FROM refresh_melt WHERE session_pub = $1)::INT2 as num_oldcoins " +           " (SELECT count(*) FROM refresh_melt WHERE session_hash = $1)::INT2 as num_oldcoins "             ",(SELECT count(*) FROM refresh_blind_session_keys " -           "  WHERE session_pub = $1 and cnc_index = 0)::INT2 as num_newcoins " +           "  WHERE session_hash = $1 and cnc_index = 0)::INT2 as num_newcoins "             ",(SELECT count(*) FROM refresh_blind_session_keys " -           "  WHERE session_pub = $1 and newcoin_index = 0)::INT2 as kappa " +           "  WHERE session_hash = $1 and newcoin_index = 0)::INT2 as kappa "             ",noreveal_index"             ",session_commit_sig "             ",reveal_ok "             "FROM refresh_sessions " -           "WHERE session_pub = $1", +           "WHERE session_hash = $1",             1, NULL);  #endif @@ -383,7 +410,7 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             "SELECT "             " coin_pub, denom_pub, denom_sig "             ",expended_value, expended_fraction, expended_currency " -           ",refresh_session_pub " +           ",refresh_session_hash "             "FROM known_coins "             "WHERE coin_pub = $1",             1, NULL); @@ -395,7 +422,7 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             ",expended_value = $4 "             ",expended_fraction = $5 "             ",expended_currency = $6 " -           ",refresh_session_pub = $7 " +           ",refresh_session_hash = $7 "             "WHERE "             " coin_pub = $1 ",             7, NULL); @@ -407,7 +434,7 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             ",expended_value"             ",expended_fraction"             ",expended_currency" -           ",refresh_session_pub" +           ",refresh_session_hash"             ")"             "VALUES ($1,$2,$3,$4,$5,$6,$7)",             7, NULL); @@ -416,26 +443,26 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             " transfer_pub "             ",link_secret_enc "             "FROM refresh_commit_link " -           "WHERE session_pub = $1 AND cnc_index = $2 AND oldcoin_index = $3", +           "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_pub = $1 AND cnc_index = $2 AND newcoin_index = $3", +           "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_pub " +           ",session_hash "             ",denom_pub "             ") "             "VALUES ($1, $2, $3) ",             3, NULL);    PREPARE ("insert_refresh_melt",             "INSERT INTO refresh_melt ( " -           " session_pub " +           " session_hash "             ",oldcoin_index "             ",coin_pub "             ",denom_pub " @@ -445,28 +472,28 @@ TALER_MINT_DB_prepare (PGconn *db_conn)    PREPARE ("get_refresh_order",             "SELECT denom_pub "             "FROM refresh_order " -           "WHERE session_pub = $1 AND newcoin_index = $2", +           "WHERE session_hash = $1 AND newcoin_index = $2",             2, NULL);    PREPARE ("get_refresh_collectable",             "SELECT ev_sig "             "FROM refresh_collectable " -           "WHERE session_pub = $1 AND newcoin_index = $2", +           "WHERE session_hash = $1 AND newcoin_index = $2",             2, NULL);    PREPARE ("get_refresh_melt",             "SELECT coin_pub "             "FROM refresh_melt " -           "WHERE session_pub = $1 AND oldcoin_index = $2", +           "WHERE session_hash = $1 AND oldcoin_index = $2",             2, NULL);    PREPARE ("insert_refresh_session",             "INSERT INTO refresh_sessions ( " -           " session_pub " +           " session_hash "             ",noreveal_index "             ") "             "VALUES ($1, $2) ",             2, NULL);    PREPARE ("insert_refresh_commit_link",             "INSERT INTO refresh_commit_link ( " -           " session_pub " +           " session_hash "             ",transfer_pub "             ",cnc_index "             ",oldcoin_index " @@ -476,7 +503,7 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             5, NULL);    PREPARE ("insert_refresh_commit_coin",             "INSERT INTO refresh_commit_coin ( " -           " session_pub " +           " session_hash "             ",coin_ev "             ",cnc_index "             ",newcoin_index " @@ -486,7 +513,7 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             5, NULL);    PREPARE ("insert_refresh_collectable",             "INSERT INTO refresh_collectable ( " -           " session_pub " +           " session_hash "             ",newcoin_index "             ",ev_sig "             ") " @@ -495,39 +522,40 @@ TALER_MINT_DB_prepare (PGconn *db_conn)    PREPARE ("set_reveal_ok",             "UPDATE refresh_sessions "             "SET reveal_ok = TRUE " -           "WHERE session_pub = $1 ", +           "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_pub) " -           "     JOIN refresh_commit_coin rcc USING (session_pub) " -           "     JOIN refresh_sessions rs USING (session_pub) " -           "     JOIN refresh_collectable rc USING (session_pub) " +           "     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_pub = rs.session_pub " +           "         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_pub) " -           "     JOIN refresh_sessions rs USING (session_pub) " +           "     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_pub = rm.session_pub " +           "         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," @@ -538,9 +566,9 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             "coin_sig,"             "wire"             ") VALUES (" -           "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11" +           "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12"             ")", -           11, NULL); +           12, NULL);    PREPARE ("get_deposit",             "SELECT "             "coin_pub," @@ -554,9 +582,11 @@ TALER_MINT_DB_prepare (PGconn *db_conn)             "h_wire,"             "coin_sig"             " FROM deposits WHERE (" -           "coin_pub = $1" +           "(coin_pub = $1) AND" +           "(transaction_id = $2) AND" +           "(merchant_pub = $3)"             ")", -           1, NULL); +           3, NULL);    return GNUNET_OK;  #undef PREPARE  } @@ -578,41 +608,25 @@ db_conn_destroy (void *cls)  /** - * Initialize database subsystem. - * - * @param connection_cfg configuration to use to talk to DB - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_init (const char *connection_cfg) -{ -  if (0 != pthread_key_create (&db_conn_threadlocal, -                               &db_conn_destroy)) -  { -    LOG_ERROR ("Cannnot create pthread key.\n"); -    return GNUNET_SYSERR; -  } -  TALER_MINT_db_connection_cfg_str = GNUNET_strdup (connection_cfg); -  return GNUNET_OK; -} - - -/**   * Get the thread-local database-handle.   * Connect to the db if the connection does not exist yet.   * + * @param 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   */ -PGconn * -TALER_MINT_DB_get_connection (int temporary) +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 != (db_conn = pthread_getspecific (db_conn_threadlocal))) -    return db_conn; -  db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); +  if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal))) +    return session; +  db_conn = PQconnectdb (pc->TALER_MINT_db_connection_cfg_str);    if (CONNECTION_OK !=        PQstatus (db_conn))    { @@ -628,33 +642,39 @@ TALER_MINT_DB_get_connection (int temporary)      return NULL;    }    if (GNUNET_OK != -      TALER_MINT_DB_prepare (db_conn)) +      postgres_prepare (db_conn))    {      GNUNET_break (0);      return NULL;    } -  if (0 != pthread_setspecific (db_conn_threadlocal, -                                db_conn)) +  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 db_conn; +  return session;  }  /**   * Start a transaction.   * - * @param db_conn the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection   * @return #GNUNET_OK on success   */ -int -TALER_MINT_DB_transaction (PGconn *db_conn) +static int +postgres_start (void *cls, +                struct TALER_MINTDB_Session *session)  {    PGresult *result; -  result = PQexec (db_conn, +  result = PQexec (session->conn,                     "BEGIN");    if (PGRES_COMMAND_OK !=        PQresultStatus (result)) @@ -674,15 +694,17 @@ TALER_MINT_DB_transaction (PGconn *db_conn)  /**   * Roll back the current transaction of a database connection.   * - * @param db_conn the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection   * @return #GNUNET_OK on success   */ -void -TALER_MINT_DB_rollback (PGconn *db_conn) +static void +postgres_rollback (void *cls, +                   struct TALER_MINTDB_Session *session)  {    PGresult *result; -  result = PQexec (db_conn, +  result = PQexec (session->conn,                     "ROLLBACK");    GNUNET_break (PGRES_COMMAND_OK ==                  PQresultStatus (result)); @@ -693,15 +715,17 @@ TALER_MINT_DB_rollback (PGconn *db_conn)  /**   * Commit the current transaction of a database connection.   * - * @param db_conn the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection   * @return #GNUNET_OK on success   */ -int -TALER_MINT_DB_commit (PGconn *db_conn) +static int +postgres_commit (void *cls, +                 struct TALER_MINTDB_Session *session)  {    PGresult *result; -  result = PQexec (db_conn, +  result = PQexec (session->conn,                     "COMMIT");    if (PGRES_COMMAND_OK !=        PQresultStatus (result)) @@ -719,29 +743,26 @@ TALER_MINT_DB_commit (PGconn *db_conn)  /**   * Get the summary of a reserve.   * - * @param db the database connection handle + * @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   */ -int -TALER_MINT_DB_reserve_get (PGconn *db, -                           struct Reserve *reserve) +static int +postgres_reserve_get (void *cls, +                      struct TALER_MINTDB_Session *session, +                      struct Reserve *reserve)  {    PGresult *result;    uint64_t expiration_date_nbo;    struct TALER_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(reserve->pub), +    TALER_DB_QUERY_PARAM_PTR(&reserve->pub),      TALER_DB_QUERY_PARAM_END    }; -  if (NULL == reserve->pub) -  { -    GNUNET_break (0); -    return GNUNET_SYSERR; -  } -  result = TALER_DB_exec_prepared (db, +  result = TALER_DB_exec_prepared (session->conn,                                     "get_reserve",                                     params);    if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -779,33 +800,36 @@ TALER_MINT_DB_reserve_get (PGconn *db,  /**   * Updates a reserve with the data from the given reserve structure.   * - * @param db the database connection + * @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   */ -int -reserves_update (PGconn *db, -                 struct Reserve *reserve) +static int +postgres_reserves_update (void *cls, +                          struct TALER_MINTDB_Session *session, +                          struct Reserve *reserve)  {    PGresult *result;    struct TALER_AmountNBO balance_nbo;    struct GNUNET_TIME_AbsoluteNBO expiry_nbo;    int ret; -  if ((NULL == reserve) || (NULL == reserve->pub)) +  if (NULL == reserve)      return GNUNET_SYSERR;    ret = GNUNET_OK;    struct TALER_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR (reserve->pub), +    TALER_DB_QUERY_PARAM_PTR (&reserve->pub),      TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),      TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),      TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),      TALER_DB_QUERY_PARAM_END    }; -  balance_nbo = TALER_amount_hton (reserve->balance); +  TALER_amount_hton (&balance_nbo, +                     &reserve->balance);    expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry); -  result = TALER_DB_exec_prepared (db, +  result = TALER_DB_exec_prepared (session->conn,                                     "update_reserve",                                     params);    if (PGRES_COMMAND_OK != PQresultStatus(result)) @@ -822,7 +846,8 @@ reserves_update (PGconn *db,   * Insert a incoming transaction into reserves.  New reserves are also created   * through this function.   * - * @param db the database connection handle + * @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. @@ -830,11 +855,12 @@ reserves_update (PGconn *db,   * @param expiry the new expiration time for the reserve   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures   */ -int -TALER_MINT_DB_reserves_in_insert (PGconn *db, -                                  struct Reserve *reserve, -                                  const struct TALER_Amount balance, -                                  const struct GNUNET_TIME_Absolute expiry) +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; @@ -847,25 +873,30 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,      GNUNET_break (0);      return GNUNET_SYSERR;    } -  if (GNUNET_OK != TALER_MINT_DB_transaction (db)) +  if (GNUNET_OK != postgres_start (cls, +                                   session))    {      GNUNET_break (0);      return GNUNET_SYSERR;    } -  reserve_exists = TALER_MINT_DB_reserve_get (db, reserve); +  reserve_exists = postgres_reserve_get (cls, +                                         session, +                                         reserve);    if (GNUNET_SYSERR == reserve_exists)    { -    TALER_MINT_DB_rollback (db); +    postgres_rollback (cls, +                       session);      return GNUNET_SYSERR;    } -  balance_nbo = TALER_amount_hton (balance); +  TALER_amount_hton (&balance_nbo, +                     balance);    expiry_nbo = GNUNET_TIME_absolute_hton (expiry);    if (GNUNET_NO == reserve_exists)    {      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,                  "Reserve does not exist; creating a new one\n");      struct TALER_DB_QueryParam params[] = { -      TALER_DB_QUERY_PARAM_PTR (reserve->pub), +      TALER_DB_QUERY_PARAM_PTR (&reserve->pub),        TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),        TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),        TALER_DB_QUERY_PARAM_PTR_SIZED (balance_nbo.currency, @@ -873,7 +904,7 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,        TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),        TALER_DB_QUERY_PARAM_END      }; -    result = TALER_DB_exec_prepared (db, +    result = TALER_DB_exec_prepared (session->conn,                                       "create_reserve",                                       params);      if (PGRES_COMMAND_OK != PQresultStatus(result)) @@ -887,7 +918,7 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,    result = NULL;    /* create new incoming transaction */    struct TALER_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR (reserve->pub), +    TALER_DB_QUERY_PARAM_PTR (&reserve->pub),      TALER_DB_QUERY_PARAM_PTR (&balance_nbo.value),      TALER_DB_QUERY_PARAM_PTR (&balance_nbo.fraction),      TALER_DB_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency, @@ -895,7 +926,7 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,      TALER_DB_QUERY_PARAM_PTR (&expiry_nbo),      TALER_DB_QUERY_PARAM_END    }; -  result = TALER_DB_exec_prepared (db, +  result = TALER_DB_exec_prepared (session->conn,                                     "create_reserves_in_transaction",                                     params);    if (PGRES_COMMAND_OK != PQresultStatus(result)) @@ -903,23 +934,35 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,      QUERY_ERR (result);      goto rollback;    } -  PQclear (result); result = NULL; +  PQclear (result); +  result = NULL;    if (GNUNET_NO == reserve_exists)    { -    if (GNUNET_OK != TALER_MINT_DB_commit (db)) +    if (GNUNET_OK != postgres_commit (cls, +                                      session))        return GNUNET_SYSERR; -    reserve->balance = balance; +    reserve->balance = *balance;      reserve->expiry = expiry;      return GNUNET_OK;    }    /* Update reserve */    struct Reserve updated_reserve;    updated_reserve.pub = reserve->pub; -  updated_reserve.balance = TALER_amount_add (reserve->balance, balance); + +  if (GNUNET_OK != +      TALER_amount_add (&updated_reserve.balance, +                        &reserve->balance, +                        balance)) +  { +    return GNUNET_SYSERR; +  }    updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry); -  if (GNUNET_OK != reserves_update (db, &updated_reserve)) +  if (GNUNET_OK != postgres_reserves_update (cls, +                                             session, +                                             &updated_reserve))      goto rollback; -  if (GNUNET_OK != TALER_MINT_DB_commit (db)) +  if (GNUNET_OK != postgres_commit (cls, +                                    session))      return GNUNET_SYSERR;    reserve->balance = updated_reserve.balance;    reserve->expiry = updated_reserve.expiry; @@ -927,7 +970,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,   rollback:    PQclear (result); -  TALER_MINT_DB_rollback (db); +  postgres_rollback (cls, +                     session);    return GNUNET_SYSERR;  } @@ -936,7 +980,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,   * Locate the response for a /withdraw request under the   * key of the hash of the blinded message.   * - * @param db_conn database connection to use + * @param 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 @@ -944,10 +989,11 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,   *         #GNUNET_NO if the collectable was not found   *         #GNUNET_YES on success   */ -int -TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, -                                         const struct GNUNET_HashCode *h_blind, -                                         struct CollectableBlindcoin *collectable) +static int +postgres_get_collectable_blindcoin (void *cls, +                                    struct TALER_MINTDB_Session *session, +                                    const struct GNUNET_HashCode *h_blind, +                                    struct CollectableBlindcoin *collectable)  {    PGresult *result;    struct TALER_DB_QueryParam params[] = { @@ -966,7 +1012,7 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,    denom_pub = NULL;    denom_pub_enc = NULL;    denom_sig_enc = NULL; -  result = TALER_DB_exec_prepared (db_conn, +  result = TALER_DB_exec_prepared (session->conn,                                     "get_collectable_blindcoin",                                     params); @@ -1002,8 +1048,8 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,      GNUNET_break (0);      goto cleanup;    } -  collectable->denom_pub = denom_pub; -  collectable->sig = denom_sig; +  collectable->denom_pub.rsa_public_key = denom_pub; +  collectable->sig.rsa_signature = denom_sig;    ret = GNUNET_YES;   cleanup: @@ -1024,20 +1070,26 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn,   * Store collectable bit coin under the corresponding   * hash of the blinded message.   * - * @param db_conn database connection to use + * @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   */ -int -TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, -                                            const struct GNUNET_HashCode *h_blind, -                                            const struct CollectableBlindcoin *collectable) +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; @@ -1046,10 +1098,11 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,    ret = GNUNET_SYSERR;    denom_pub_enc_size = -      GNUNET_CRYPTO_rsa_public_key_encode (collectable->denom_pub, +      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, &denom_sig_enc); +      GNUNET_CRYPTO_rsa_signature_encode (collectable->sig.rsa_signature, +                                          &denom_sig_enc);    struct TALER_DB_QueryParam params[] = {      TALER_DB_QUERY_PARAM_PTR (h_blind),      TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1), @@ -1058,16 +1111,41 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,      TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig),      TALER_DB_QUERY_PARAM_END    }; -  result = TALER_DB_exec_prepared (db_conn, +  if (GNUNET_OK != postgres_start (cls, +                                   session)) +    goto cleanup; +  result = TALER_DB_exec_prepared (session->conn,                                     "insert_collectable_blindcoin",                                     params);    if (PGRES_COMMAND_OK != PQresultStatus (result))    {      QUERY_ERR (result); +    goto rollback; +  } +  reserve.pub = 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;    } -  ret = GNUNET_OK; + rollback: +  postgres_rollback (cls, +                     session);   cleanup:    PQclear (result);    GNUNET_free_non_null (denom_pub_enc); @@ -1080,13 +1158,15 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn,   * Get all of the transaction history associated with the specified   * reserve.   * - * @param db_conn connection to use + * @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)   */ -struct ReserveHistory * -TALER_MINT_DB_get_reserve_history (PGconn *db_conn, -                                   const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) +static struct ReserveHistory * +postgres_get_reserve_history (void *cls, +                              struct TALER_MINTDB_Session *session, +                              const struct TALER_ReservePublicKey *reserve_pub)  {    PGresult *result;    struct ReserveHistory *rh; @@ -1105,7 +1185,7 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,        TALER_DB_QUERY_PARAM_END      }; -    result = TALER_DB_exec_prepared (db_conn, +    result = TALER_DB_exec_prepared (session->conn,                                       "get_reserves_in_transactions",                                       params);      if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -1133,7 +1213,7 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,          GNUNET_break (0);          goto cleanup;        } -      (void) memcpy (&bt->reserve_pub, reserve_pub, sizeof (bt->reserve_pub)); +      bt->reserve_pub = *reserve_pub;        if (NULL != rh_head)        {          rh_head->next = GNUNET_new (struct ReserveHistory); @@ -1152,7 +1232,7 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,    result = NULL;    {      struct GNUNET_HashCode blind_ev; -    struct GNUNET_CRYPTO_EddsaSignature reserve_sig; +    struct TALER_ReserveSignature reserve_sig;      struct CollectableBlindcoin *cbc;      char *denom_pub_enc;      char *denom_sig_enc; @@ -1163,7 +1243,7 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,        TALER_DB_QUERY_PARAM_PTR (reserve_pub),        TALER_DB_QUERY_PARAM_END      }; -    result = TALER_DB_exec_prepared (db_conn, +    result = TALER_DB_exec_prepared (session->conn,                                       "get_reserves_blindcoins",                                       params);      if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -1194,20 +1274,23 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,          goto cleanup;        }        cbc = GNUNET_new (struct CollectableBlindcoin); -      cbc->sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc, -                                                    denom_sig_enc_size); +      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 = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc, -                                                            denom_pub_enc_size); +      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) || (NULL == cbc->denom_pub)) +      if ( (NULL == cbc->sig.rsa_signature) || +           (NULL == cbc->denom_pub.rsa_public_key) )        { -        if (NULL != cbc->sig) -          GNUNET_CRYPTO_rsa_signature_free (cbc->sig); -        if (NULL != cbc->denom_pub) -          GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub); +        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; @@ -1228,7 +1311,8 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,      PQclear (result);    if (GNUNET_SYSERR == ret)    { -    TALER_MINT_DB_free_reserve_history (rh); +    common_free_reserve_history (cls, +                                 rh);      rh = NULL;    }    return rh; @@ -1236,77 +1320,49 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn,  /** - * Free memory associated with the given reserve history. - * - * @param rh history to free. - */ -void -TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh) -{ -  struct BankTransfer *bt; -  struct CollectableBlindcoin *cbc; -  struct ReserveHistory *backref; - -  while (NULL != rh) -  { -    switch(rh->type) -    { -    case TALER_MINT_DB_RO_BANK_TO_MINT: -      bt = rh->details.bank; -      if (NULL != bt->wire) -        json_decref ((json_t *) bt->wire); /* FIXME: avoid cast? */ -      GNUNET_free (bt); -      break; -    case TALER_MINT_DB_RO_WITHDRAW_COIN: -      cbc = rh->details.withdraw; -      GNUNET_CRYPTO_rsa_signature_free (cbc->sig); -      GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub); -      GNUNET_free (cbc); -      break; -    } -    backref = rh; -    rh = rh->next; -    GNUNET_free (backref); -  } -} - - -/**   * Check if we have the specified deposit already in the database.   * - * @param db_conn database connection + * @param 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   */ -int -TALER_MINT_DB_have_deposit (PGconn *db_conn, -                            const struct Deposit *deposit) +static int +postgres_have_deposit (void *cls, +                       struct TALER_MINTDB_Session *session, +                       const struct Deposit *deposit)  { -  // FIXME: check logic!    struct TALER_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), // FIXME +    TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), +    TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id), +    TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),      TALER_DB_QUERY_PARAM_END    };    PGresult *result; +  int ret; -  result = TALER_DB_exec_prepared (db_conn, +  ret = GNUNET_SYSERR; +  result = TALER_DB_exec_prepared (session->conn,                                     "get_deposit",                                     params);    if (PGRES_TUPLES_OK !=        PQresultStatus (result))    {      BREAK_DB_ERR (result); -    PQclear (result); -    return GNUNET_SYSERR; +    goto cleanup;    }    if (0 == PQntuples (result))    { -    PQclear (result); -    return GNUNET_NO; +    ret = GNUNET_NO; +    goto cleanup;    } -  return GNUNET_YES; +  ret = GNUNET_YES; + + cleanup: +  PQclear (result); +  return ret;  } @@ -1314,69 +1370,96 @@ TALER_MINT_DB_have_deposit (PGconn *db_conn,   * Insert information about deposited coin into the   * database.   * - * @param db_conn connection to 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   */ -int -TALER_MINT_DB_insert_deposit (PGconn *db_conn, -                              const struct Deposit *deposit) +static int +postgres_insert_deposit (void *cls, +                         struct TALER_MINTDB_Session *session, +                         const struct Deposit *deposit)  { -  // FIXME: check logic! +  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_DB_QueryParam params[]= {      TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), -    TALER_DB_QUERY_PARAM_PTR (&deposit->coin.denom_pub), // FIXME! -    TALER_DB_QUERY_PARAM_PTR (&deposit->coin.denom_sig), // FIXME! +    TALER_DB_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size), +    TALER_DB_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size),      TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id), -    TALER_DB_QUERY_PARAM_PTR (&deposit->amount.value), -    TALER_DB_QUERY_PARAM_PTR (&deposit->amount.fraction), -    TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->amount.currency, -                                    strlen (deposit->amount.currency)), +    TALER_DB_QUERY_PARAM_PTR (&amount_nbo.value), +    TALER_DB_QUERY_PARAM_PTR (&amount_nbo.fraction), +    TALER_DB_QUERY_PARAM_PTR_SIZED (amount_nbo.currency, +                                    TALER_CURRENCY_LEN - 1),      TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub),      TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract),      TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire),      TALER_DB_QUERY_PARAM_PTR (&deposit->csig), -    TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->wire, -                                    strlen ("FIXME")), // FIXME! json! +    TALER_DB_QUERY_PARAM_PTR_SIZED (json_wire_enc, +                                    strlen (json_wire_enc)),      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result; - -  result = TALER_DB_exec_prepared (db_conn, "insert_deposit", params); +  result = TALER_DB_exec_prepared (session->conn, "insert_deposit", params);    if (PGRES_COMMAND_OK != PQresultStatus (result))    {      BREAK_DB_ERR (result); -    PQclear (result); -    return GNUNET_SYSERR; +    goto cleanup;    } +  ret = GNUNET_OK; + + cleanup:    PQclear (result); -  return GNUNET_OK; +  GNUNET_free_non_null (denom_pub_enc); +  GNUNET_free_non_null (denom_sig_enc); +  GNUNET_free_non_null (json_wire_enc); +  return ret;  }  /** - * Lookup refresh session data under the given public key. + * Lookup refresh session data under the given @a session_hash.   * - * @param db_conn database handle to use - * @param refresh_session_pub public key to use for the lookup - * @param session[OUT] where to store the result + * @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   */ -int -TALER_MINT_DB_get_refresh_session (PGconn *db_conn, -                                   const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                   struct RefreshSession *session) +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_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), +    TALER_DB_QUERY_PARAM_PTR(session_hash),      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_session", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, +                                             "get_refresh_session", +                                             params);    if (PGRES_TUPLES_OK != PQresultStatus (result))    { @@ -1395,16 +1478,15 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn,    /* We're done if the caller is only interested in     * whether the session exists or not */ -  if (NULL == session) +  if (NULL == refresh_session)      return GNUNET_YES;    memset (session, 0, sizeof (struct RefreshSession));    struct TALER_DB_ResultSpec rs[] = { -    TALER_DB_RESULT_SPEC("num_oldcoins", &session->num_oldcoins), -    TALER_DB_RESULT_SPEC("num_newcoins", &session->num_newcoins), -    TALER_DB_RESULT_SPEC("kappa", &session->kappa), -    TALER_DB_RESULT_SPEC("noreveal_index", &session->noreveal_index), +    TALER_DB_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins), +    TALER_DB_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins), +    TALER_DB_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index),      TALER_DB_RESULT_SPEC_END    }; @@ -1417,10 +1499,9 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn,      return GNUNET_SYSERR;    } -  session->num_oldcoins = ntohs (session->num_oldcoins); -  session->num_newcoins = ntohs (session->num_newcoins); -  session->kappa = ntohs (session->kappa); -  session->noreveal_index = ntohs (session->noreveal_index); +  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; @@ -1428,23 +1509,25 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn,  /** - * Store new refresh session data under the given public key. + * Store new refresh session data under the given @a session_hash.   * - * @param db_conn database handle to use - * @param refresh_session_pub public key to use to locate the session - * @param session session data to store + * @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   */ -int -TALER_MINT_DB_create_refresh_session (PGconn *db_conn, -                                      const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                      const struct RefreshSession *session) +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_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(session_pub), +    TALER_DB_QUERY_PARAM_PTR(session_hash),      TALER_DB_QUERY_PARAM_PTR(&noreveal_index),      TALER_DB_QUERY_PARAM_END    }; @@ -1452,7 +1535,9 @@ TALER_MINT_DB_create_refresh_session (PGconn *db_conn,    noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15);    noreveal_index = htonl (noreveal_index); -  PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_session", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, +                                             "insert_refresh_session", +                                             params);    if (PGRES_COMMAND_OK != PQresultStatus (result))    { @@ -1469,18 +1554,19 @@ TALER_MINT_DB_create_refresh_session (PGconn *db_conn,  /**   * Store the given /refresh/melt request in the database.   * - * @param db_conn database connection - * @param session session key of the melt operation + * @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 + * @param melt melt operation details to store; includes +   *             the session hash of the melt   * @return #GNUNET_OK on success   *         #GNUNET_SYSERR on internal error   */ -int -TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, -                                   const struct GNUNET_CRYPTO_EddsaPublicKey *session, -                                   uint16_t oldcoin_index, -                                   const struct RefreshMelt *melt) +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); @@ -1488,17 +1574,19 @@ TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,    size_t buf_size;    PGresult *result; -  buf_size = GNUNET_CRYPTO_rsa_public_key_encode (melt->coin.denom_pub, +  buf_size = GNUNET_CRYPTO_rsa_public_key_encode (melt->coin.denom_pub.rsa_public_key,                                                    &buf);    {      struct TALER_DB_QueryParam params[] = { -      TALER_DB_QUERY_PARAM_PTR(session), +      TALER_DB_QUERY_PARAM_PTR(&melt->session_hash),        TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),        TALER_DB_QUERY_PARAM_PTR(&melt->coin.coin_pub),        TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size),        TALER_DB_QUERY_PARAM_END      }; -    result = TALER_DB_exec_prepared (db_conn, "insert_refresh_melt", params); +    result = TALER_DB_exec_prepared (session->conn, +                                     "insert_refresh_melt", +                                     params);    }    GNUNET_free (buf);    if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -1515,18 +1603,20 @@ TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn,  /**   * Get information about melted coin details from the database.   * - * @param db_conn database connection - * @param session session key of the melt operation + * @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   */ -int -TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, -                                const struct GNUNET_CRYPTO_EddsaPublicKey *session, -                                uint16_t oldcoin_index, -                                struct RefreshMelt *melt) +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); @@ -1538,36 +1628,38 @@ TALER_MINT_DB_get_refresh_melt (PGconn *db_conn,   * Store in the database which coin(s) we want to create   * in a given refresh operation.   * - * @param db_conn database connection - * @param session_pub refresh session key - * @param newcoin_index index of the coin to generate - * @param denom_pub denomination of the coin to create + * @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   */ -int -TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, -                                    const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                    uint16_t newcoin_index, -                                    const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub) +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 -  uint16_t newcoin_index_nbo = htons (newcoin_index); +  // 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_pub, +  buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs->rsa_public_key,                                                    &buf);    {      struct TALER_DB_QueryParam params[] = {        TALER_DB_QUERY_PARAM_PTR (&newcoin_index_nbo), -      TALER_DB_QUERY_PARAM_PTR (session_pub), +      TALER_DB_QUERY_PARAM_PTR (session_hash),        TALER_DB_QUERY_PARAM_PTR_SIZED (buf, buf_size),        TALER_DB_QUERY_PARAM_END      }; -    result = TALER_DB_exec_prepared (db_conn, +    result = TALER_DB_exec_prepared (session->conn,                                       "insert_refresh_order",                                       params);    } @@ -1589,46 +1681,50 @@ TALER_MINT_DB_insert_refresh_order (PGconn *db_conn,  /** - * Lookup in the database the @a newcoin_index coin that we want to + * Lookup in the database the coins that we want to   * create in the given refresh operation.   * - * @param db_conn database connection - * @param session_pub refresh session key - * @param newcoin_index index of the coin to generate - * @param denom_pub denomination of the coin to create - * @return NULL on error (not found or internal error) + * @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   */ -struct GNUNET_CRYPTO_rsa_PublicKey * -TALER_MINT_DB_get_refresh_order (PGconn *db_conn, -                                 const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                 uint16_t newcoin_index) +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 +  // FIXME: check logic -- was written for just one coin!    char *buf;    size_t buf_size; -  struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; -  uint16_t newcoin_index_nbo = htons (newcoin_index); +  uint16_t newcoin_index_nbo = htons (num_newcoins);    struct TALER_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(session_pub), +    TALER_DB_QUERY_PARAM_PTR(session_hash),      TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_order", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, +                                             "get_refresh_order", params);    if (PGRES_TUPLES_OK != PQresultStatus (result))    {      BREAK_DB_ERR (result);      PQclear (result); -    return NULL; +    return GNUNET_SYSERR;    }    if (0 == PQntuples (result))    {      PQclear (result);      /* FIXME: may want to distinguish between different error cases! */ -    return NULL; +    return GNUNET_SYSERR;    }    GNUNET_assert (1 == PQntuples (result));    struct TALER_DB_ResultSpec rs[] = { @@ -1639,12 +1735,14 @@ TALER_MINT_DB_get_refresh_order (PGconn *db_conn,    {      PQclear (result);      GNUNET_break (0); -    return NULL; +    return GNUNET_SYSERR;    }    PQclear (result); -  denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); +  denom_pubs->rsa_public_key +    = GNUNET_CRYPTO_rsa_public_key_decode (buf, +                                           buf_size);    GNUNET_free (buf); -  return denom_pub; +  return GNUNET_OK;  } @@ -1653,36 +1751,40 @@ TALER_MINT_DB_get_refresh_order (PGconn *db_conn,   * Store information about the commitment of the   * given coin for the given refresh session in the database.   * - * @param db_conn database connection to use - * @param refresh_session_pub refresh session this commitment belongs to + * @param 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 coin commitment to store + * @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   */ -int -TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, -                                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                          unsigned int i, -                                          unsigned int j, -                                          const struct RefreshCommitCoin *commit_coin) +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! +  // FIXME: check logic! -- was written for single commit_coin!    uint16_t cnc_index_nbo = htons (i); -  uint16_t newcoin_index_nbo = htons (j); +  uint16_t newcoin_index_nbo = htons (num_newcoins);    struct TALER_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), -    TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coin->coin_ev, commit_coin->coin_ev_size), +    TALER_DB_QUERY_PARAM_PTR(session_hash), +    TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coins->coin_ev, commit_coins->coin_ev_size),      TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),      TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), -    TALER_DB_QUERY_PARAM_PTR_SIZED(commit_coin->refresh_link->coin_priv_enc, -                                   commit_coin->refresh_link->blinding_key_enc_size + -                                   sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)), +    TALER_DB_QUERY_PARAM_PTR_SIZED (commit_coins->refresh_link->coin_priv_enc, +                                    commit_coins->refresh_link->blinding_key_enc_size + +                                    sizeof (struct TALER_CoinSpendPrivateKey)),      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_coin", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, +                                             "insert_refresh_commit_coin", +                                             params);    if (PGRES_COMMAND_OK != PQresultStatus (result))    { @@ -1706,8 +1808,9 @@ TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn,   * Obtain information about the commitment of the   * given coin of the given refresh session from the database.   * - * @param db_conn database connection to use - * @param refresh_session_pub refresh session the commitment belongs to + * @param 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 @@ -1715,18 +1818,19 @@ TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn,   *         #GNUNET_NO if not found   *         #GNUNET_SYSERR on error   */ -int -TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, -                                       const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                       unsigned int cnc_index, -                                       unsigned int newcoin_index, -                                       struct RefreshCommitCoin *cc) +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_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), +    TALER_DB_QUERY_PARAM_PTR(session_hash),      TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),      TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),      TALER_DB_QUERY_PARAM_END @@ -1737,7 +1841,9 @@ TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,    size_t rl_buf_size;    struct TALER_RefreshLinkEncrypted *rl; -  PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_coin", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, +                                             "get_refresh_commit_coin", +                                             params);    if (PGRES_TUPLES_OK != PQresultStatus (result))    { @@ -1763,7 +1869,7 @@ TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,      return GNUNET_SYSERR;    }    PQclear (result); -  if (rl_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)) +  if (rl_buf_size < sizeof (struct TALER_CoinSpendPrivateKey))    {      GNUNET_free (c_buf);      GNUNET_free (rl_buf); @@ -1783,26 +1889,27 @@ TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn,   * Store the commitment to the given (encrypted) refresh link data   * for the given refresh session.   * - * @param db_conn database connection to use - * @param refresh_session_pub public key of the refresh session this - *        commitment belongs with + * @param 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   */ -int -TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, -                                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                          unsigned int i, -                                          unsigned int j, -                                          const struct RefreshCommitLink *commit_link) +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_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), +    TALER_DB_QUERY_PARAM_PTR(session_hash),      TALER_DB_QUERY_PARAM_PTR(&commit_link->transfer_pub),      TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),      TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo), @@ -1810,7 +1917,7 @@ TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn,      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, +  PGresult *result = TALER_DB_exec_prepared (session->conn,                                               "insert_refresh_commit_link",                                               params);    if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -1835,35 +1942,36 @@ TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn,   * Obtain the commited (encrypted) refresh link data   * for the given refresh session.   * - * @param db_conn database connection to use - * @param refresh_session_pub public key of the refresh session this - *        commitment belongs with + * @param 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 cc[OUT] link information to return + * @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   */ -int -TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, -                                       const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                       unsigned int cnc_index, -                                       unsigned int oldcoin_index, -                                       struct RefreshCommitLink *cc) +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! -  uint16_t cnc_index_nbo = htons (cnc_index); -  uint16_t oldcoin_index_nbo = htons (oldcoin_index); +  // 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_DB_QueryParam params[] = { -    TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), +    TALER_DB_QUERY_PARAM_PTR(session_hash),      TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo),      TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo),      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, +  PGresult *result = TALER_DB_exec_prepared (session->conn,                                               "get_refresh_commit_link",                                               params);    if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -1880,15 +1988,14 @@ TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,    }    struct TALER_DB_ResultSpec rs[] = { -    TALER_DB_RESULT_SPEC("transfer_pub", &cc->transfer_pub), -    TALER_DB_RESULT_SPEC("link_secret_enc", &cc->shared_secret_enc), +    TALER_DB_RESULT_SPEC("transfer_pub", &links->transfer_pub), +    TALER_DB_RESULT_SPEC("link_secret_enc", &links->shared_secret_enc),      TALER_DB_RESULT_SPEC_END    };    if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0))    {      PQclear (result); -    GNUNET_free (cc);      return GNUNET_SYSERR;    } @@ -1903,17 +2010,19 @@ TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn,   * of the coin.  This data is later used should an old coin   * be used to try to obtain the private keys during "/refresh/link".   * - * @param db_conn database connection - * @param session_pub refresh session + * @param 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   */ -int -TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, -                                          const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, -                                          uint16_t newcoin_index, -                                          const struct GNUNET_CRYPTO_rsa_Signature *ev_sig) +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); @@ -1921,16 +2030,16 @@ TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,    size_t buf_size;    PGresult *result; -  buf_size = GNUNET_CRYPTO_rsa_signature_encode (ev_sig, +  buf_size = GNUNET_CRYPTO_rsa_signature_encode (ev_sig->rsa_signature,                                                   &buf);    {      struct TALER_DB_QueryParam params[] = { -      TALER_DB_QUERY_PARAM_PTR(session_pub), +      TALER_DB_QUERY_PARAM_PTR(session_hash),        TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo),        TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size),        TALER_DB_QUERY_PARAM_END      }; -    result = TALER_DB_exec_prepared (db_conn, +    result = TALER_DB_exec_prepared (session->conn,                                       "insert_refresh_collectable",                                       params);    } @@ -1950,13 +2059,15 @@ TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn,   * Obtain the link data of a coin, that is the encrypted link   * information, the denomination keys and the signatures.   * - * @param db_conn database connection + * @param 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   */ -struct LinkDataList * -TALER_db_get_link (PGconn *db_conn, -                   const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) +static struct LinkDataList * +postgres_get_link_data_list (void *cls, +                             struct TALER_MINTDB_Session *session, +                             const struct TALER_CoinSpendPublicKey *coin_pub)  {    // FIXME: check logic!    struct LinkDataList *ldl; @@ -1965,7 +2076,7 @@ TALER_db_get_link (PGconn *db_conn,      TALER_DB_QUERY_PARAM_PTR(coin_pub),      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, "get_link", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, "get_link", params);    ldl = NULL;    if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -2006,7 +2117,8 @@ TALER_db_get_link (PGconn *db_conn,      {        PQclear (result);        GNUNET_break (0); -      TALER_db_link_data_list_free (ldl); +      common_free_link_data_list (cls, +                                  ldl);        return NULL;      }      if (ld_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)) @@ -2015,7 +2127,8 @@ TALER_db_get_link (PGconn *db_conn,        GNUNET_free (pk_buf);        GNUNET_free (sig_buf);        GNUNET_free (ld_buf); -      TALER_db_link_data_list_free (ldl); +      common_free_link_data_list (cls, +                                  ldl);        return NULL;      }      // FIXME: use util API for this! @@ -2027,10 +2140,12 @@ TALER_db_get_link (PGconn *db_conn,              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); +    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); @@ -2044,14 +2159,15 @@ TALER_db_get_link (PGconn *db_conn,        GNUNET_free (link_enc);        GNUNET_break (0);        PQclear (result); -      TALER_db_link_data_list_free (ldl); +      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 = denom_pub; -    pos->ev_sig = sig; +    pos->denom_pub.rsa_public_key = denom_pub; +    pos->ev_sig.rsa_signature = sig;      ldl = pos;    }    return ldl; @@ -2059,25 +2175,13 @@ TALER_db_get_link (PGconn *db_conn,  /** - * Free memory of the link data list. - * - * @param ldl link data list to release - */ -void -TALER_db_link_data_list_free (struct LinkDataList *ldl) -{ -  GNUNET_break (0); // FIXME -} - - -/**   * Obtain shared secret and transfer public key from the public key of   * the coin.  This information and the link information returned by - * #TALER_db_get_link() enable the owner of an old coin to determine - * the private keys of the new coins after the melt. - * + * #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 db_conn database connection + * @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 @@ -2085,10 +2189,11 @@ TALER_db_link_data_list_free (struct LinkDataList *ldl)   *         #GNUNET_NO on failure (not found)   *         #GNUNET_SYSERR on internal failure (database issue)   */ -int -TALER_db_get_transfer (PGconn *db_conn, -                       const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, -                       struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, +static int +postgres_get_transfer (void *cls, +                       struct TALER_MINTDB_Session *session, +                       const struct TALER_CoinSpendPublicKey *coin_pub, +                       struct TALER_TransferPublicKey *transfer_pub,                         struct TALER_EncryptedLinkSecret *shared_secret_enc)  {    // FIXME: check logic! @@ -2097,7 +2202,7 @@ TALER_db_get_transfer (PGconn *db_conn,      TALER_DB_QUERY_PARAM_END    }; -  PGresult *result = TALER_DB_exec_prepared (db_conn, "get_transfer", params); +  PGresult *result = TALER_DB_exec_prepared (session->conn, "get_transfer", params);    if (PGRES_TUPLES_OK != PQresultStatus (result))    { @@ -2139,19 +2244,19 @@ TALER_db_get_transfer (PGconn *db_conn,  } - -  /**   * Compile a list of all (historic) transactions performed   * with the given coin (/refresh/melt and /deposit operations).   * - * @param db_conn database connection + * @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   */ -struct TALER_MINT_DB_TransactionList * -TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, -                                     const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) +static struct TALER_MINT_DB_TransactionList * +postgres_get_coin_transactions (void *cls, +                                struct TALER_MINTDB_Session *session, +                                const struct TALER_CoinSpendPublicKey *coin_pub)  {    // FIXME: check logic!    GNUNET_break (0); // FIXME: implement! @@ -2159,17 +2264,92 @@ TALER_MINT_DB_get_coin_transactions (PGconn *db_conn,  } +  /** - * Free linked list of transactions. + * Initialize Postgres database subsystem.   * - * @param list list to free + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_MINTDB_Plugin`   */ -void -TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list) +void * +libtaler_plugin_mintdb_postgres_init (void *cls)  { -  // FIXME: check logic! -  GNUNET_break (0); +  struct GNUNET_CONFIGURATION_Handle *cfg = cls; +  struct PostgresClosure *pg; +  struct TALER_MINTDB_Plugin *plugin; + +  pg = GNUNET_new (struct PostgresClosure); + +  if (0 != pthread_key_create (&pg->db_conn_threadlocal, +                               &db_conn_destroy)) +  { +    LOG_ERROR ("Cannnot create pthread key.\n"); +    return NULL; +  } +  /* FIXME: use configuration section with "postgres" in its name... */ +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_string (cfg, +                                             "mint", "db", +                                             &pg->TALER_MINT_db_connection_cfg_str)) +  { +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "mint", +                               "db"); +    return NULL; +  } +  plugin = GNUNET_new (struct TALER_MINTDB_Plugin); +  plugin->cls = pg; +  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;  } -/* end of mint_db.c */ +/** + * Shutdown Postgres database subsystem. + * + * @param cls a `struct TALER_MINTDB_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_mintdb_postgres_done (void *cls) +{ +  struct TALER_MINTDB_Plugin *plugin = cls; +  struct PostgresClosure *pg = plugin->cls; + +  GNUNET_free (pg); +  GNUNET_free (plugin); +  return NULL; +} + +/* end of plugin_mintdb_postgres.c */ diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c index 8106b54c..8056b7df 100644 --- a/src/mint/taler-mint-dbinit.c +++ b/src/mint/taler-mint-dbinit.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 Christian Grothoff (and other contributing authors) +  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 @@ -14,7 +14,7 @@    TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/>  */  /** - * @file taler-mint-dbinit.c + * @file mint/taler-mint-dbinit.c   * @brief Create tables for the mint database.   * @author Florian Dold   */ @@ -22,76 +22,80 @@  #include <gnunet/gnunet_util_lib.h>  #include <libpq-fe.h>  #include "taler_util.h" -#include "mint_db.h" - -#define break_db_err(result) do { \ -    GNUNET_break(0); \ -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \ -    PQclear (result); \ -  } while (0) - +#include "taler_mintdb_plugin.h" +#include "plugin.h" +/** + * Mint directory with the keys. + */  static char *mint_base_dir; -static struct GNUNET_CONFIGURATION_Handle *cfg; -static PGconn *db_conn; -static char *TALER_MINT_db_connection_cfg_str; - - +/** + * Our configuration. + */ +static struct GNUNET_CONFIGURATION_Handle *cfg;  /** - * The main function of the serve tool + * 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) +main (int argc, +      char *const *argv)  {    static const struct GNUNET_GETOPT_CommandLineOption options[] = { -    GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup 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-serve", options, argc, argv) < 0) +  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)); - +  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"); +    fprintf (stderr, +             "Mint base directory not given.\n");      return 1;    } -    cfg = TALER_config_load (mint_base_dir);    if (NULL == cfg)    { -    fprintf (stderr, "Can't load mint configuration.\n"); +    fprintf (stderr, +             "Failed to load mint configuration.\n");      return 1;    } -  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "mint", "db", &TALER_MINT_db_connection_cfg_str)) -  { -    fprintf (stderr, "Configuration 'mint.db' not found.\n"); -    return 42; -  } -  db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); -  if (CONNECTION_OK != PQstatus (db_conn)) +  if (GNUNET_OK != +      TALER_MINT_plugin_load (cfg))    { -    fprintf (stderr, "Database connection failed: %s\n", PQerrorMessage (db_conn)); +    fprintf (stderr, +             "Failed to initialize database plugin.\n");      return 1;    } - -  if (GNUNET_OK != TALER_MINT_DB_create_tables (GNUNET_NO)) +  if (GNUNET_OK != +      plugin->create_tables (plugin->cls, +                             GNUNET_NO))    { -    fprintf (stderr, "Failed to initialize database.\n"); +    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 index 41685106..70d3b64f 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -31,12 +31,12 @@  #include "taler_util.h"  #include "taler-mint-httpd_parsing.h"  #include "taler-mint-httpd_mhd.h" -#include "taler-mint-httpd_keys.h"  #include "taler-mint-httpd_deposit.h"  #include "taler-mint-httpd_withdraw.h"  #include "taler-mint-httpd_refresh.h"  #include "taler-mint-httpd_keystate.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h" +#include "plugin.h"  /** @@ -230,7 +230,6 @@ mint_serve_process_config (const char *mint_directory)    unsigned long long port;    unsigned long long kappa;    char *master_pub_str; -  char *db_cfg;    cfg = TALER_config_load (mint_directory);    if (NULL == cfg) @@ -261,16 +260,7 @@ mint_serve_process_config (const char *mint_directory)    GNUNET_free (master_pub_str);    if (GNUNET_OK != -      GNUNET_CONFIGURATION_get_value_string (cfg, -                                             "mint", "db", -                                             &db_cfg)) -  { -    fprintf (stderr, -             "invalid configuration: mint.db\n"); -    return GNUNET_NO; -  } -  if (GNUNET_OK != -      TALER_MINT_DB_init (db_cfg)) +      TALER_MINT_plugin_load (cfg))    {      fprintf (stderr,               "failed to initialize DB subsystem\n"); diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h index a86b06e4..50b74570 100644 --- a/src/mint/taler-mint-httpd.h +++ b/src/mint/taler-mint-httpd.h @@ -23,12 +23,7 @@  #ifndef TALER_MINT_HTTPD_H  #define TALER_MINT_HTTPD_H - -/** - * Cut-and-choose size for refreshing. - * FIXME: maybe make it a config option? - */ -#define KAPPA 3 +#include <microhttpd.h>  /**   * For now, we just do EUR.  Should become configurable diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 63bca2ec..17f44c9c 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -17,41 +17,73 @@   * @file taler-mint-httpd_db.c   * @brief High-level (transactional-layer) database operations for the mint.   * @author Christian Grothoff - * - * TODO: - * - actually abstract DB implementation (i.e. via plugin logic) - *   (this file should remain largely unchanged with the exception - *    of the PQ-specific DB handle types)   */  #include "platform.h"  #include <pthread.h>  #include <jansson.h>  #include "taler-mint-httpd_db.h"  #include "taler_signatures.h" -#include "taler-mint-httpd_keys.h"  #include "taler-mint-httpd_responses.h" -#include "mint_db.h"  #include "taler_util.h"  #include "taler-mint-httpd_keystate.h" +#include "plugin.h"  /** - * Get an amount in the mint's currency that is zero. + * Calculate the total value of all transactions performed. + * Stores @a off plus the cost of all transactions in @a tl + * in @a ret.   * - * @return zero amount in the mint's currency + * @param pos transaction list to process + * @param off offset to use as the starting value + * @param ret where the resulting total is to be stored + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors   */ -static struct TALER_Amount -mint_amount_native_zero () +static int +calculate_transaction_list_totals (struct TALER_MINT_DB_TransactionList *tl, +                                   const struct TALER_Amount *off, +                                   struct TALER_Amount *ret)  { -  struct TALER_Amount amount; - -  memset (&amount, -          0, -          sizeof (amount)); -  memcpy (amount.currency, -          MINT_CURRENCY, -          strlen (MINT_CURRENCY) + 1); -  return amount; +  struct TALER_Amount spent = *off; +  struct TALER_MINT_DB_TransactionList *pos; + +  for (pos = tl; NULL != pos; pos = pos->next) +  { +    switch (pos->type) +    { +    case TALER_MINT_DB_TT_DEPOSIT: +      if (GNUNET_OK != +          TALER_amount_add (&spent, +                            &spent, +                            &pos->details.deposit->amount_with_fee)) +      { +        GNUNET_break (0); +        return GNUNET_SYSERR; +      } +      break; +    case TALER_MINT_DB_TT_REFRESH_MELT: +      if (GNUNET_OK != +          TALER_amount_add (&spent, +                            &spent, +                            &pos->details.melt->amount_with_fee)) +      { +        GNUNET_break (0); +        return GNUNET_SYSERR; +      } +      break; +    case TALER_MINT_DB_TT_LOCK: +      /* should check if lock is still active, +         and if it is for THIS operation; if +         lock is inactive, delete it; if lock +         is for THIS operation, ignore it; +         if lock is for another operation, +         count it! */ +      GNUNET_assert (0);  // FIXME: not implemented! (#3625) +      return GNUNET_SYSERR; +    } +  } +  *ret = spent; +  return GNUNET_OK;  } @@ -69,26 +101,24 @@ int  TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,                                 const struct Deposit *deposit)  { -  PGconn *db_conn; +  struct TALER_MINTDB_Session *session;    struct TALER_MINT_DB_TransactionList *tl; -  struct TALER_MINT_DB_TransactionList *pos;    struct TALER_Amount spent;    struct TALER_Amount value; -  struct TALER_Amount fee_deposit; -  struct TALER_Amount fee_withdraw; -  struct TALER_Amount fee_refresh;    struct MintKeyState *mks;    struct TALER_MINT_DenomKeyIssuePriv *dki;    int ret; -  if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) +  if (NULL == (session = plugin->get_session (plugin->cls, +                                              GNUNET_NO)))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    }    if (GNUNET_YES == -      TALER_MINT_DB_have_deposit (db_conn, -                                  deposit)) +      plugin->have_deposit (plugin->cls, +                            session, +                            deposit))    {      return TALER_MINT_reply_deposit_success (connection,                                               &deposit->coin.coin_pub, @@ -96,79 +126,67 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,                                               &deposit->h_contract,                                               deposit->transaction_id,                                               &deposit->merchant_pub, -                                             &deposit->amount); +                                             &deposit->amount_with_fee);    }    mks = TALER_MINT_key_state_acquire ();    dki = TALER_MINT_get_denom_key (mks, -                                  deposit->coin.denom_pub); -  value = TALER_amount_ntoh (dki->issue.value); -  fee_deposit = TALER_amount_ntoh (dki->issue.fee_deposit); -  fee_refresh = TALER_amount_ntoh (dki->issue.fee_refresh); +                                  &deposit->coin.denom_pub); +  TALER_amount_ntoh (&value, +                     &dki->issue.value);    TALER_MINT_key_state_release (mks);    if (GNUNET_OK != -      TALER_MINT_DB_transaction (db_conn)) +      plugin->start (plugin->cls, +                     session))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    } -  tl = TALER_MINT_DB_get_coin_transactions (db_conn, -                                            &deposit->coin.coin_pub); -  spent = fee_withdraw; /* fee for THIS transaction */ -  /* FIXME: need to deal better with integer overflows -     in the logic that follows! (change amount.c API! -- #3637) */ -  spent = TALER_amount_add (spent, -                            deposit->amount); - -  for (pos = tl; NULL != pos; pos = pos->next) +  /* fee for THIS transaction */ +  spent = deposit->amount_with_fee; +  /* add cost of all previous transactions */ +  tl = plugin->get_coin_transactions (plugin->cls, +                                      session, +                                      &deposit->coin.coin_pub); +  if (GNUNET_OK != +      calculate_transaction_list_totals (tl, +                                         &spent, +                                         &spent))    { -    switch (pos->type) -    { -    case TALER_MINT_DB_TT_DEPOSIT: -      spent = TALER_amount_add (spent, -                                pos->details.deposit->amount); -      spent = TALER_amount_add (spent, -                                fee_deposit); -      break; -    case TALER_MINT_DB_TT_REFRESH_MELT: -      spent = TALER_amount_add (spent, -                                pos->details.melt->amount); -      spent = TALER_amount_add (spent, -                                fee_refresh); -      break; -    case TALER_MINT_DB_TT_LOCK: -      /* should check if lock is still active, -         and if it is for THIS operation; if -         lock is inactive, delete it; if lock -         is for THIS operation, ignore it; -         if lock is for another operation, -         count it! */ -      GNUNET_assert (0);  // FIXME: not implemented! (#3625) -      break; -    } +    plugin->free_coin_transaction_list (plugin->cls, +                                        tl); +    return TALER_MINT_reply_internal_db_error (connection);    } - -  if (0 < TALER_amount_cmp (spent, value)) +  /* Check that cost of all transactions is smaller than +     the value of the coin. */ +  if (0 < TALER_amount_cmp (&spent, +                            &value))    { -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      ret = TALER_MINT_reply_deposit_insufficient_funds (connection, -                                               tl); -    TALER_MINT_DB_free_coin_transaction_list (tl); +                                                       tl); +    plugin->free_coin_transaction_list (plugin->cls, +                                        tl);      return ret;    } -  TALER_MINT_DB_free_coin_transaction_list (tl); +  plugin->free_coin_transaction_list (plugin->cls, +                                      tl);    if (GNUNET_OK != -      TALER_MINT_DB_insert_deposit (db_conn, -                                    deposit)) +      plugin->insert_deposit (plugin->cls, +                              session, +                              deposit))    {      LOG_WARNING ("Failed to store /deposit information in database\n"); -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      return TALER_MINT_reply_internal_db_error (connection);    }    if (GNUNET_OK != -      TALER_MINT_DB_commit (db_conn)) +      plugin->commit (plugin->cls, +                      session))    {      LOG_WARNING ("/deposit transaction commit failed\n");      return TALER_MINT_reply_commit_error (connection); @@ -179,7 +197,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,                                             &deposit->h_contract,                                             deposit->transaction_id,                                             &deposit->merchant_pub, -                                           &deposit->amount); +                                           &deposit->amount_with_fee);  } @@ -193,19 +211,21 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,   */  int  TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, -                                       const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) +                                       const struct TALER_ReservePublicKey *reserve_pub)  { -  PGconn *db_conn; +  struct TALER_MINTDB_Session *session;    struct ReserveHistory *rh;    int res; -  if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) +  if (NULL == (session = plugin->get_session (plugin->cls, +                                              GNUNET_NO)))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    } -  rh = TALER_MINT_DB_get_reserve_history (db_conn, -                                          reserve_pub); +  rh = plugin->get_reserve_history (plugin->cls, +                                    session, +                                    reserve_pub);    if (NULL == rh)      return TALER_MINT_reply_json_pack (connection,                                         MHD_HTTP_NOT_FOUND, @@ -213,7 +233,8 @@ TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection,                                         "error", "Reserve not found");    res = TALER_MINT_reply_withdraw_status_success (connection,                                                    rh); -  TALER_MINT_DB_free_reserve_history (rh); +  plugin->free_reserve_history (plugin->cls, +                                rh);    return res;  } @@ -234,13 +255,13 @@ TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection,   */  int  TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, -                                     const struct GNUNET_CRYPTO_EddsaPublicKey *reserve, -                                     const struct GNUNET_CRYPTO_rsa_PublicKey *denomination_pub, +                                     const struct TALER_ReservePublicKey *reserve, +                                     const struct TALER_DenominationPublicKey *denomination_pub,                                       const char *blinded_msg,                                       size_t blinded_msg_len, -                                     const struct GNUNET_CRYPTO_EddsaSignature *signature) +                                     const struct TALER_ReserveSignature *signature)  { -  PGconn *db_conn; +  struct TALER_MINTDB_Session *session;    struct ReserveHistory *rh;    const struct ReserveHistory *pos;    struct MintKeyState *key_state; @@ -253,6 +274,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,    struct TALER_Amount withdraw_total;    struct TALER_Amount balance;    struct TALER_Amount value; +  struct TALER_Amount fee_withdraw;    struct GNUNET_HashCode h_blind;    int res; @@ -260,14 +282,16 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,                        blinded_msg_len,                        &h_blind); -  if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) +  if (NULL == (session = plugin->get_session (plugin->cls, +                                              GNUNET_NO)))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    } -  res = TALER_MINT_DB_get_collectable_blindcoin (db_conn, -                                                 &h_blind, -                                                 &collectable); +  res = plugin->get_collectable_blindcoin (plugin->cls, +                                           session, +                                           &h_blind, +                                           &collectable);    if (GNUNET_SYSERR == res)    {      GNUNET_break (0); @@ -279,8 +303,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,    {      res = TALER_MINT_reply_withdraw_sign_success (connection,                                                    &collectable); -    GNUNET_CRYPTO_rsa_signature_free (collectable.sig); -    GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub); +    GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature); +    GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key);      return res;    }    GNUNET_assert (GNUNET_NO == res); @@ -299,18 +323,21 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,                                         "Denomination not found");    }    if (GNUNET_OK != -      TALER_MINT_DB_transaction (db_conn)) +      plugin->start (plugin->cls, +                     session))    {      GNUNET_break (0);      TALER_MINT_key_state_release (key_state);      return TALER_MINT_reply_internal_db_error (connection);    } -  rh = TALER_MINT_DB_get_reserve_history (db_conn, -                                          reserve); +  rh = plugin->get_reserve_history (plugin->cls, +                                    session, +                                    reserve);    if (NULL == rh)    { -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      TALER_MINT_key_state_release (key_state);      return TALER_MINT_reply_json_pack (connection,                                         MHD_HTTP_NOT_FOUND, @@ -320,8 +347,21 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,    }    /* calculate amount required including fees */ -  amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), -                                      TALER_amount_ntoh (dki->issue.fee_withdraw)); +  TALER_amount_ntoh (&value, +                     &dki->issue.value); +  TALER_amount_ntoh (&fee_withdraw, +                     &dki->issue.fee_withdraw); + +  if (GNUNET_OK != +      TALER_amount_add (&amount_required, +                        &value, +                        &fee_withdraw)) +  { +    plugin->rollback (plugin->cls, +                      session); +    TALER_MINT_key_state_release (key_state); +    return TALER_MINT_reply_internal_db_error (connection); +  }    /* calculate balance of the reserve */    res = 0; @@ -333,70 +373,96 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,        if (0 == (res & 1))          deposit_total = pos->details.bank->amount;        else -        deposit_total = TALER_amount_add (deposit_total, -                                          pos->details.bank->amount); +        if (GNUNET_OK != +            TALER_amount_add (&deposit_total, +                              &deposit_total, +                              &pos->details.bank->amount)) +        { +          plugin->rollback (plugin->cls, +                            session); +          TALER_MINT_key_state_release (key_state); +          return TALER_MINT_reply_internal_db_error (connection); +        }        res |= 1;        break;      case TALER_MINT_DB_RO_WITHDRAW_COIN:        tdki = TALER_MINT_get_denom_key (key_state, -                                       pos->details.withdraw->denom_pub); -      value = TALER_amount_ntoh (tdki->issue.value); +                                       &pos->details.withdraw->denom_pub); +      TALER_amount_ntoh (&value, +                         &tdki->issue.value);        if (0 == (res & 2))          withdraw_total = value;        else -        withdraw_total = TALER_amount_add (withdraw_total, -                                           value); +        if (GNUNET_OK != +            TALER_amount_add (&withdraw_total, +                              &withdraw_total, +                              &value)) +        { +          plugin->rollback (plugin->cls, +                            session); +          TALER_MINT_key_state_release (key_state); +          return TALER_MINT_reply_internal_db_error (connection); +        }        res |= 2;        break;      }    } -  GNUNET_break (0 > TALER_amount_cmp (withdraw_total, -                                      deposit_total)); -  balance = TALER_amount_subtract (deposit_total, -                                   withdraw_total); -  if (0 < TALER_amount_cmp (amount_required, -                            balance)) +  /* All reserve balances should be non-negative */ +  GNUNET_break (GNUNET_SYSERR != +                TALER_amount_subtract (&balance, +                                       &deposit_total, +                                       &withdraw_total)); +  if (0 < TALER_amount_cmp (&amount_required, +                            &balance))    {      TALER_MINT_key_state_release (key_state); -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      res = TALER_MINT_reply_withdraw_sign_insufficient_funds (connection,                                                               rh); -    TALER_MINT_DB_free_reserve_history (rh); +    plugin->free_reserve_history (plugin->cls, +                                  rh);      return res;    } -  TALER_MINT_DB_free_reserve_history (rh); +  plugin->free_reserve_history (plugin->cls, +                                rh);    /* Balance is good, sign the coin! */ -  sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv, +  sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key,                                  blinded_msg,                                  blinded_msg_len);    TALER_MINT_key_state_release (key_state);    if (NULL == sig)    {      GNUNET_break (0); -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      return TALER_MINT_reply_internal_error (connection,                                              "Internal error");    } -  collectable.sig = sig; -  collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; +  collectable.sig.rsa_signature = sig; +  collectable.denom_pub = *denomination_pub;    collectable.reserve_pub = *reserve;    GNUNET_CRYPTO_hash (blinded_msg,                        blinded_msg_len,                        &collectable.h_coin_envelope);    collectable.reserve_sig = *signature;    if (GNUNET_OK != -      TALER_MINT_DB_insert_collectable_blindcoin (db_conn, -                                                  &h_blind, -                                                  &collectable)) +      plugin->insert_collectable_blindcoin (plugin->cls, +                                            session, +                                            &h_blind, +                                            amount_required, +                                            &collectable))    {      GNUNET_break (0);      GNUNET_CRYPTO_rsa_signature_free (sig); -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      return TALER_MINT_reply_internal_db_error (connection);    }    if (GNUNET_OK != -      TALER_MINT_DB_commit (db_conn)) +      plugin->commit (plugin->cls, +                      session))    {      LOG_WARNING ("/withdraw/sign transaction commit failed\n");      return TALER_MINT_reply_commit_error (connection); @@ -413,9 +479,9 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,   * the database.   *   * @param connection the connection to send errors to - * @param db_conn the database connection + * @param session the database connection   * @param key_state the mint's key state - * @param session_pub the refresh session's public key + * @param session_hash hash identifying the refresh session   * @param coin_public_info the coin to melt   * @param coin_details details about the coin being melted   * @param oldcoin_index what is the number assigned to this coin @@ -425,10 +491,9 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,   */  static int  refresh_accept_melts (struct MHD_Connection *connection, -                      PGconn *db_conn, +                      struct TALER_MINTDB_Session *session,                        const struct MintKeyState *key_state, -                      const struct GNUNET_HashCode *melt_hash, -                      const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, +                      const struct GNUNET_HashCode *session_hash,                        const struct TALER_CoinPublicInfo *coin_public_info,                        const struct MeltDetails *coin_details,                        uint16_t oldcoin_index) @@ -437,11 +502,12 @@ refresh_accept_melts (struct MHD_Connection *connection,    struct TALER_MINT_DB_TransactionList *tl;    struct TALER_Amount coin_value;    struct TALER_Amount coin_residual; +  struct TALER_Amount spent;    struct RefreshMelt melt;    int res;    dki = &TALER_MINT_get_denom_key (key_state, -                                   coin_public_info->denom_pub)->issue; +                                   &coin_public_info->denom_pub)->issue;    if (NULL == dki)      return (MHD_YES == @@ -452,40 +518,57 @@ refresh_accept_melts (struct MHD_Connection *connection,                                          "denom not found"))        ? GNUNET_NO : GNUNET_SYSERR; -  coin_value = TALER_amount_ntoh (dki->value); -  tl = TALER_MINT_DB_get_coin_transactions (db_conn, -                                            &coin_public_info->coin_pub); -  /* FIXME: #3636: compute how much value is left with this coin and -     compare to `expected_value`! (subtract from "coin_value") */ -  coin_residual = coin_value; -  /* Refuse to refresh when the coin does not have enough money left to -   * pay the refreshing fees of the coin. */ - -  if (TALER_amount_cmp (coin_residual, -                        coin_details->melt_amount) < 0) +  TALER_amount_ntoh (&coin_value, +                     &dki->value); +  /* fee for THIS transaction; the melt amount includes the fee! */ +  spent = coin_details->melt_amount_with_fee; +  /* add historic transaction costs of this coin */ +  tl = plugin->get_coin_transactions (plugin->cls, +                                      session, +                                      &coin_public_info->coin_pub); +  if (GNUNET_OK != +      calculate_transaction_list_totals (tl, +                                         &spent, +                                         &spent)) +  { +    GNUNET_break (0); +    plugin->free_coin_transaction_list (plugin->cls, +                                        tl); +    return TALER_MINT_reply_internal_db_error (connection); +  } +  /* Refuse to refresh when the coin's value is insufficient +     for the cost of all transactions. */ +  if (TALER_amount_cmp (&coin_value, +                        &spent) < 0)    { +    GNUNET_assert (GNUNET_OK == +                   TALER_amount_subtract (&coin_residual, +                                          &spent, +                                          &coin_details->melt_amount_with_fee));      res = (MHD_YES ==             TALER_MINT_reply_refresh_melt_insufficient_funds (connection,                                                               &coin_public_info->coin_pub,                                                               coin_value,                                                               tl, -                                                             coin_details->melt_amount, +                                                             coin_details->melt_amount_with_fee,                                                               coin_residual))        ? GNUNET_NO : GNUNET_SYSERR; -    TALER_MINT_DB_free_coin_transaction_list (tl); +    plugin->free_coin_transaction_list (plugin->cls, +                                        tl);      return res;    } -  TALER_MINT_DB_free_coin_transaction_list (tl); +  plugin->free_coin_transaction_list (plugin->cls, +                                      tl);    melt.coin = *coin_public_info;    melt.coin_sig = coin_details->melt_sig; -  melt.melt_hash = *melt_hash; -  melt.amount = coin_details->melt_amount; +  melt.session_hash = *session_hash; +  melt.amount_with_fee = coin_details->melt_amount_with_fee;    if (GNUNET_OK != -      TALER_MINT_DB_insert_refresh_melt (db_conn, -                                         session_pub, -                                         oldcoin_index, -                                         &melt)) +      plugin->insert_refresh_melt (plugin->cls, +                                   session, +                                   oldcoin_index, +                                   &melt))    {      GNUNET_break (0);      return GNUNET_SYSERR; @@ -502,69 +585,68 @@ refresh_accept_melts (struct MHD_Connection *connection,   * melted and confirm the melting operation to the client.   *   * @param connection the MHD connection to handle - * @param melt_hash hash code of the session the coins are melted into - * @param refresh_session_pub public key of the refresh session - * @param client_signature signature of the client (matching @a refresh_session_pub) - *         over the melting request + * @param session_hash hash code of the session the coins are melted into   * @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @commit_coin array   * @param denum_pubs public keys of the coins we want to withdraw in the end   * @param coin_count number of entries in @a coin_public_infos and @a coin_melt_details, size of y-dimension of @commit_link array   * @param coin_public_infos information about the coins to melt   * @param coin_melt_details signatures and (residual) value of the respective coin should be melted - * @param kappa size of x-dimension of @commit_coin and @commit_link arrays   * @param commit_coin 2d array of coin commitments (what the mint is to sign - *                    once the "/refres/reveal" of cut and choose is done) + *                    once the "/refres/reveal" of cut and choose is done), + *                    x-dimension must be #KAPPA   * @param commit_link 2d array of coin link commitments (what the mint is   *                    to return via "/refresh/link" to enable linkage in the   *                    future) + *                    x-dimension must be #KAPPA   * @return MHD result code   */  int  TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, -                                    const struct GNUNET_HashCode *melt_hash, -                                    const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                    const struct GNUNET_CRYPTO_EddsaSignature *client_signature, +                                    const struct GNUNET_HashCode *session_hash,                                      unsigned int num_new_denoms, -                                    struct GNUNET_CRYPTO_rsa_PublicKey *const*denom_pubs, +                                    const struct TALER_DenominationPublicKey *denom_pubs,                                      unsigned int coin_count,                                      const struct TALER_CoinPublicInfo *coin_public_infos,                                      const struct MeltDetails *coin_melt_details, -                                    unsigned int kappa,                                      struct RefreshCommitCoin *const* commit_coin,                                      struct RefreshCommitLink *const* commit_link)  {    struct MintKeyState *key_state; -  struct RefreshSession session; -  PGconn *db_conn; +  struct RefreshSession refresh_session; +  struct TALER_MINTDB_Session *session;    int res;    unsigned int i; -  unsigned int j; -  if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) +  if (NULL == (session = plugin->get_session (plugin->cls, +                                              GNUNET_NO)))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    }    if (GNUNET_OK != -      TALER_MINT_DB_transaction (db_conn)) +      plugin->start (plugin->cls, +                     session))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    } -  res = TALER_MINT_DB_get_refresh_session (db_conn, -                                           refresh_session_pub, -                                           &session); +  res = plugin->get_refresh_session (plugin->cls, +                                     session, +                                     session_hash, +                                     &refresh_session);    if (GNUNET_YES == res)    { -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      res = TALER_MINT_reply_refresh_melt_success (connection, -                                                 &session.session_hash, -                                                 session.noreveal_index); +                                                 session_hash, +                                                 refresh_session.noreveal_index);      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;    }    if (GNUNET_SYSERR == res)    { -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      return TALER_MINT_reply_internal_db_error (connection);    } @@ -574,98 +656,95 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,    {      if (GNUNET_OK !=          (res = refresh_accept_melts (connection, -                                     db_conn, +                                     session,                                       key_state, -                                     melt_hash, -                                     refresh_session_pub, +                                     session_hash,                                       &coin_public_infos[i],                                       &coin_melt_details[i],                                       i)))      {        TALER_MINT_key_state_release (key_state); -      TALER_MINT_DB_rollback (db_conn); +      plugin->rollback (plugin->cls, +                        session);        return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;      }    }    TALER_MINT_key_state_release (key_state);    /* store requested new denominations */ -  for (i=0;i<num_new_denoms;i++) +  if (GNUNET_OK != +      plugin->insert_refresh_order (plugin->cls, +                                    session, +                                    session_hash, +                                    num_new_denoms, +                                    denom_pubs))    { -    if (GNUNET_OK != -        TALER_MINT_DB_insert_refresh_order (db_conn, -                                            refresh_session_pub, -                                            i, -                                            denom_pubs[i])) -    { -      TALER_MINT_DB_rollback (db_conn); -      return TALER_MINT_reply_internal_db_error (connection); -    } +    plugin->rollback (plugin->cls, +                      session); +    return TALER_MINT_reply_internal_db_error (connection);    } -  for (i = 0; i < kappa; i++) +  for (i = 0; i < KAPPA; i++)    { -    for (j = 0; j < num_new_denoms; j++) +    if (GNUNET_OK != +        plugin->insert_refresh_commit_coins (plugin->cls, +                                             session, +                                             session_hash, +                                             i, +                                             num_new_denoms, +                                             commit_coin[i]))      { -      if (GNUNET_OK != -          TALER_MINT_DB_insert_refresh_commit_coin (db_conn, -                                                    refresh_session_pub, -                                                    i, -                                                    j, -                                                    &commit_coin[i][j])) -      { -        TALER_MINT_DB_rollback (db_conn); -        return TALER_MINT_reply_internal_db_error (connection); -      } +      plugin->rollback (plugin->cls, +                        session); +      return TALER_MINT_reply_internal_db_error (connection);      }    } -  for (i = 0; i < kappa; i++) +  for (i = 0; i < KAPPA; i++)    { -    for (j = 0; j < coin_count; j++) +    if (GNUNET_OK != +        plugin->insert_refresh_commit_links (plugin->cls, +                                             session, +                                             session_hash, +                                             i, +                                             coin_count, +                                             commit_link[i]))      { -      if (GNUNET_OK != -          TALER_MINT_DB_insert_refresh_commit_link (db_conn, -                                                    refresh_session_pub, -                                                    i, -                                                    j, -                                                    &commit_link[i][j])) -      { -        TALER_MINT_DB_rollback (db_conn); -        return TALER_MINT_reply_internal_db_error (connection); -      } +      plugin->rollback (plugin->cls, +                        session); +      return TALER_MINT_reply_internal_db_error (connection);      }    }    /* store 'global' session data */ -  session.melt_sig = *client_signature; -  session.session_hash = *melt_hash; -  session.num_oldcoins = coin_count; -  session.num_newcoins = num_new_denoms; -  session.kappa = KAPPA; // FIXME... -  session.noreveal_index +  refresh_session.num_oldcoins = coin_count; +  refresh_session.num_newcoins = num_new_denoms; +  refresh_session.noreveal_index      = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, -                                session.kappa); +                                KAPPA);    if (GNUNET_OK != -      (res = TALER_MINT_DB_create_refresh_session (db_conn, -                                                   refresh_session_pub, -                                                   &session))) +      (res = plugin->create_refresh_session (plugin->cls, +                                             session, +                                             session_hash, +                                             &refresh_session)))    { -    TALER_MINT_DB_rollback (db_conn); +    plugin->rollback (plugin->cls, +                      session);      return TALER_MINT_reply_internal_db_error (connection);    }    if (GNUNET_OK != -      TALER_MINT_DB_commit (db_conn)) +      plugin->commit (plugin->cls, +                      session))    {      LOG_WARNING ("/refresh/melt transaction commit failed\n");      return TALER_MINT_reply_commit_error (connection);    }    return TALER_MINT_reply_refresh_melt_success (connection, -                                                &session.session_hash, -                                                session.noreveal_index); +                                                session_hash, +                                                refresh_session.noreveal_index);  } @@ -673,67 +752,74 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,   * Check if the given @a transfer_privs correspond to an honest   * commitment for the given session.   * Checks that the transfer private keys match their commitments. - * Then derives the shared secret for each kappa, and check that they match. + * Then derives the shared secret for each #KAPPA, and check that they match.   *   * @param connection the MHD connection to handle - * @param db_conn database connection to use - * @param refresh_session session to query + * @param session database connection to use + * @param session_hash hash of session to query   * @param off commitment offset to check   * @param num_oldcoins size of the @a transfer_privs and @a melts arrays   * @param transfer_privs private transfer keys   * @param melts array of melted coins   * @param num_newcoins number of newcoins being generated - * @param denom_pub array of @a num_newcoins keys for the new coins + * @param denom_pubs array of @a num_newcoins keys for the new coins   * @return #GNUNET_OK if the committment was honest,   *         #GNUNET_NO if there was a problem and we generated an error message   *         #GNUNET_SYSERR if we could not even generate an error message   */  static int  check_commitment (struct MHD_Connection *connection, -                  PGconn *db_conn, -                  const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, +                  struct TALER_MINTDB_Session *session, +                  const struct GNUNET_HashCode *session_hash,                    unsigned int off,                    unsigned int num_oldcoins, -                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *transfer_privs, +                  const struct TALER_TransferPrivateKey *transfer_privs,                    const struct RefreshMelt *melts,                    unsigned int num_newcoins, -                  struct GNUNET_CRYPTO_rsa_PublicKey *const*denom_pubs) +                  const struct TALER_DenominationPublicKey *denom_pubs)  {    unsigned int j; -  int res;    struct TALER_LinkSecret last_shared_secret;    int secret_initialized = GNUNET_NO; +  struct GNUNET_CRYPTO_EcdhePublicKey coin_ecdhe; +  struct GNUNET_CRYPTO_EcdhePrivateKey transfer_ecdhe; +  struct RefreshCommitLink *commit_links; +  struct RefreshCommitCoin *commit_coins; + +  commit_links = GNUNET_malloc (num_oldcoins * +                                sizeof (struct RefreshCommitLink)); +  if (GNUNET_OK != +      plugin->get_refresh_commit_links (plugin->cls, +                                        session, +                                        session_hash, +                                        off, +                                        num_oldcoins, +                                        commit_links)) +  { +    GNUNET_break (0); +    GNUNET_free (commit_links); +    return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) +      ? GNUNET_NO : GNUNET_SYSERR; +  }    for (j = 0; j < num_oldcoins; j++)    { -    struct RefreshCommitLink commit_link;      struct TALER_TransferSecret transfer_secret;      struct TALER_LinkSecret shared_secret; -    struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check; - -    res = TALER_MINT_DB_get_refresh_commit_link (db_conn, -                                                 refresh_session, -                                                 off, -                                                 j, -                                                 &commit_link); -    if (GNUNET_OK != res) -    { -      GNUNET_break (0); -      return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) -        ? GNUNET_NO : GNUNET_SYSERR; -    } +    struct TALER_TransferPublicKey transfer_pub_check; -    GNUNET_CRYPTO_ecdsa_key_get_public (&transfer_privs[j], -                                        &transfer_pub_check); +    GNUNET_CRYPTO_ecdsa_key_get_public (&transfer_privs[j].ecdsa_priv, +                                        &transfer_pub_check.ecdsa_pub);      if (0 !=          memcmp (&transfer_pub_check, -                &commit_link.transfer_pub, -                sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) +                &commit_links[j].transfer_pub, +                sizeof (struct TALER_TransferPublicKey)))      {        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,                    "transfer keys do not match\n"); +      GNUNET_free (commit_links);        /* FIXME: return more specific error with original signature (#3712) */ -      return (MHD_YES ==  +      return (MHD_YES ==  	      TALER_MINT_reply_refresh_reveal_missmatch (connection,  							 off,  							 j, @@ -743,25 +829,31 @@ check_commitment (struct MHD_Connection *connection,      /* We're converting key types here, which is not very nice       * but necessary and harmless (keys will be thrown away later). */ -    /* FIXME: ECDHE/ECDSA-key type confusion! Can we reduce/avoid this? */ +    GNUNET_CRYPTO_ecdsa_public_to_ecdhe (&melts[j].coin.coin_pub.ecdsa_pub, +                                         &coin_ecdhe); +    GNUNET_CRYPTO_ecdsa_private_to_ecdhe (&transfer_privs[j].ecdsa_priv, +                                          &transfer_ecdhe);      if (GNUNET_OK != -        GNUNET_CRYPTO_ecc_ecdh ((const struct GNUNET_CRYPTO_EcdhePrivateKey *) &transfer_privs[j], -                                (const struct GNUNET_CRYPTO_EcdhePublicKey *) &melts[j].coin.coin_pub, +        GNUNET_CRYPTO_ecc_ecdh (&transfer_ecdhe, +                                &coin_ecdhe,                                  &transfer_secret.key))      {        GNUNET_break (0); +      GNUNET_CRYPTO_ecdhe_key_clear (&transfer_ecdhe); +      GNUNET_free (commit_links);        return (MHD_YES == TALER_MINT_reply_internal_error (connection,                                                            "ECDH error"))          ? GNUNET_NO : GNUNET_SYSERR;      } - +    GNUNET_CRYPTO_ecdhe_key_clear (&transfer_ecdhe);      if (GNUNET_OK != -        TALER_transfer_decrypt (&commit_link.shared_secret_enc, +        TALER_transfer_decrypt (&commit_links[j].shared_secret_enc,                                  &transfer_secret,                                  &shared_secret))      {        GNUNET_break (0); -      return (MHD_YES ==  +      GNUNET_free (commit_links); +      return (MHD_YES ==  	      TALER_MINT_reply_internal_error (connection,  					       "Decryption error"))          ? GNUNET_NO : GNUNET_SYSERR; @@ -778,6 +870,7 @@ check_commitment (struct MHD_Connection *connection,      {        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,                    "shared secrets do not match\n"); +      GNUNET_free (commit_links);        /* FIXME: return more specific error with original signature (#3712) */        return (MHD_YES ==  	      TALER_MINT_reply_refresh_reveal_missmatch (connection, @@ -788,71 +881,75 @@ check_commitment (struct MHD_Connection *connection,      }    }    GNUNET_break (GNUNET_YES == secret_initialized); - +  GNUNET_free (commit_links);    /* Check that the commitments for all new coins were correct */ +  commit_coins = GNUNET_malloc (num_newcoins * +                                sizeof (struct RefreshCommitCoin)); + +  if (GNUNET_OK != +      plugin->get_refresh_commit_coins (plugin->cls, +                                        session, +                                        session_hash, +                                        off, +                                        num_newcoins, +                                        commit_coins)) +  { +    GNUNET_break (0); +    GNUNET_free (commit_coins); +    return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) +      ? GNUNET_NO : GNUNET_SYSERR; +  } +    for (j = 0; j < num_newcoins; j++)    { -    struct RefreshCommitCoin commit_coin;      struct TALER_RefreshLinkDecrypted *link_data; -    struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; +    struct TALER_CoinSpendPublicKey coin_pub;      struct GNUNET_HashCode h_msg;      char *buf;      size_t buf_len; -    res = TALER_MINT_DB_get_refresh_commit_coin (db_conn, -                                                 refresh_session, -                                                 off, -                                                 j, -                                                 &commit_coin); -    if (GNUNET_OK != res) -    { -      GNUNET_break (0); -      return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) -        ? GNUNET_NO : GNUNET_SYSERR; -    } - -    link_data = TALER_refresh_decrypt (commit_coin.refresh_link, +    link_data = TALER_refresh_decrypt (commit_coins[j].refresh_link,                                         &last_shared_secret);      if (NULL == link_data)      {        GNUNET_break (0); +      GNUNET_free (commit_coins);        return (MHD_YES == TALER_MINT_reply_internal_error (connection,                                                            "Decryption error"))          ? GNUNET_NO : GNUNET_SYSERR;      } -    GNUNET_CRYPTO_ecdsa_key_get_public (&link_data->coin_priv, -                                        &coin_pub); -    /* FIXME: we had envisioned a more complex scheme to derive -       the message to sign for a blinded coin... -       FIXME: we should have a function in util/ to do this! */ +    GNUNET_CRYPTO_ecdsa_key_get_public (&link_data->coin_priv.ecdsa_priv, +                                        &coin_pub.ecdsa_pub);      GNUNET_CRYPTO_hash (&coin_pub, -                        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), +                        sizeof (struct TALER_CoinSpendPublicKey),                          &h_msg);      if (0 == (buf_len =                GNUNET_CRYPTO_rsa_blind (&h_msg, -                                       link_data->blinding_key, -                                       denom_pubs[j], +                                       link_data->blinding_key.rsa_blinding_key, +                                       denom_pubs[j].rsa_public_key,                                         &buf)))      {        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,                    "blind failed\n"); +      GNUNET_free (commit_coins);        return (MHD_YES == TALER_MINT_reply_internal_error (connection,                                                            "Blinding error"))          ? GNUNET_NO : GNUNET_SYSERR;      } -    if ( (buf_len != commit_coin.coin_ev_size) || +    if ( (buf_len != commit_coins[j].coin_ev_size) ||           (0 != memcmp (buf, -                       commit_coin.coin_ev, +                       commit_coins[j].coin_ev,                         buf_len)) )      {        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                  "blind envelope does not match for kappa=%u, old=%d\n", +                  "blind envelope does not match for k=%u, old=%d\n",                    off,                    (int) j);        /* FIXME: return more specific error with original signature (#3712) */ +      GNUNET_free (commit_coins);        return (MHD_YES ==  	      TALER_MINT_reply_refresh_reveal_missmatch (connection,  							 off, @@ -862,6 +959,7 @@ check_commitment (struct MHD_Connection *connection,      }      GNUNET_free (buf);    } +  GNUNET_free (commit_coins);    return GNUNET_OK;  } @@ -872,62 +970,53 @@ check_commitment (struct MHD_Connection *connection,   * envelope from the database and performs the signing operation.   *   * @param connection the MHD connection to handle - * @param db_conn database connection to use - * @param refresh_session session to query + * @param session database connection to use + * @param session_hash hash of session to query   * @param key_state key state to lookup denomination pubs   * @param denom_pub denomination key for the coin to create - * @param noreveal_index which index should we use to obtain the - *                  envelope for the coin, based on cut-and-choose + * @param commit_coin the coin that was committed   * @param coin_off number of the coin   * @return NULL on error, otherwise signature over the coin   */ -static struct GNUNET_CRYPTO_rsa_Signature * +static struct TALER_DenominationSignature  refresh_mint_coin (struct MHD_Connection *connection, -                   PGconn *db_conn, -                   const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, +                   struct TALER_MINTDB_Session *session, +                   const struct GNUNET_HashCode *session_hash,                     struct MintKeyState *key_state, -                   const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub, -                   unsigned int noreveal_index, +                   const struct TALER_DenominationPublicKey *denom_pub, +                   const struct RefreshCommitCoin *commit_coin,                     unsigned int coin_off)  { -  struct RefreshCommitCoin commit_coin;    struct TALER_MINT_DenomKeyIssuePriv *dki; -  struct GNUNET_CRYPTO_rsa_Signature *ev_sig; -  int res; +  struct TALER_DenominationSignature ev_sig; -  res = TALER_MINT_DB_get_refresh_commit_coin (db_conn, -                                               refresh_session, -                                               noreveal_index, -                                               coin_off, -                                               &commit_coin); -  if (GNUNET_OK != res) -  { -    GNUNET_break (0); -    return NULL; -  } -  dki = TALER_MINT_get_denom_key (key_state, denom_pub); +  dki = TALER_MINT_get_denom_key (key_state, +                                  denom_pub);    if (NULL == dki)    {      GNUNET_break (0); -    return NULL; +    ev_sig.rsa_signature = NULL; +    return ev_sig;    } -  ev_sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv, -                                   commit_coin.coin_ev, -                                   commit_coin.coin_ev_size); -  if (NULL == ev_sig) +  ev_sig.rsa_signature +    = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key, +                              commit_coin->coin_ev, +                              commit_coin->coin_ev_size); +  if (NULL == ev_sig.rsa_signature)    {      GNUNET_break (0); -    return NULL; +    return ev_sig;    }    if (GNUNET_OK != -      TALER_MINT_DB_insert_refresh_collectable (db_conn, -                                                refresh_session, -                                                coin_off, -                                                ev_sig)) +      plugin->insert_refresh_collectable (plugin->cls, +                                          session, +                                          session_hash, +                                          coin_off, +                                          &ev_sig))    {      GNUNET_break (0); -    GNUNET_CRYPTO_rsa_signature_free (ev_sig); -    return NULL; +    GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature); +    ev_sig.rsa_signature = NULL;    }    return ev_sig;  } @@ -935,48 +1024,50 @@ refresh_mint_coin (struct MHD_Connection *connection,  /**   * Execute a "/refresh/reveal".  The client is revealing to us the - * transfer keys for @a kappa-1 sets of coins.  Verify that the + * transfer keys for @a #KAPPA-1 sets of coins.  Verify that the   * revealed transfer keys would allow linkage to the blinded coins,   * and if so, return the signed coins for corresponding to the set of   * coins that was not chosen.   *   * @param connection the MHD connection to handle - * @param refresh_session_pub public key of the refresh session - * @param kappa size of x-dimension of @transfer_privs array plus one (!) + * @param session_hash hash identifying the refresh session   * @param num_oldcoins size of y-dimension of @transfer_privs array - * @param transfer_pubs array with the revealed transfer keys + * @param transfer_pubs array with the revealed transfer keys, + *                      x-dimension must be #KAPPA - 1   * @return MHD result code   */  int  TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, -                                      const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                      unsigned int kappa, +                                      const struct GNUNET_HashCode *session_hash,                                        unsigned int num_oldcoins, -                                      struct GNUNET_CRYPTO_EcdsaPrivateKey *const*transfer_privs) +                                      struct TALER_TransferPrivateKey **transfer_privs)  {    int res; -  PGconn *db_conn; +  struct TALER_MINTDB_Session *session;    struct RefreshSession refresh_session;    struct MintKeyState *key_state;    struct RefreshMelt *melts; -  struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; -  struct GNUNET_CRYPTO_rsa_Signature **ev_sigs; +  struct TALER_DenominationPublicKey *denom_pubs; +  struct TALER_DenominationSignature *ev_sigs; +  struct RefreshCommitCoin *commit_coins;    unsigned int i;    unsigned int j;    unsigned int off; -  if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) +  if (NULL == (session = plugin->get_session (plugin->cls, +                                              GNUNET_NO)))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    } -  res = TALER_MINT_DB_get_refresh_session (db_conn, -                                           refresh_session_pub, -                                           &refresh_session); +  res = plugin->get_refresh_session (plugin->cls, +                                     session, +                                     session_hash, +                                     &refresh_session);    if (GNUNET_NO == res)      return TALER_MINT_reply_arg_invalid (connection, -                                         "session_pub"); +                                         "session_hash");    if (GNUNET_SYSERR == res)      return TALER_MINT_reply_internal_db_error (connection);    if (0 == refresh_session.num_oldcoins) @@ -990,10 +1081,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,    for (j=0;j<refresh_session.num_oldcoins;j++)    {      if (GNUNET_OK != -        TALER_MINT_DB_get_refresh_melt (db_conn, -                                        refresh_session_pub, -                                        j, -                                        &melts[j])) +        plugin->get_refresh_melt (plugin->cls, +                                  session, +                                  session_hash, +                                  j, +                                  &melts[j]))      {        GNUNET_break (0);        GNUNET_free (melts); @@ -1001,34 +1093,31 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,      }    }    denom_pubs = GNUNET_malloc (refresh_session.num_newcoins * -                              sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); -  for (j=0;j<refresh_session.num_newcoins;j++) +                              sizeof (struct TALER_DenominationPublicKey)); +  if (GNUNET_OK != +      plugin->get_refresh_order (plugin->cls, +                                 session, +                                 session_hash, +                                 refresh_session.num_newcoins, +                                 denom_pubs))    { -    denom_pubs[j] = TALER_MINT_DB_get_refresh_order (db_conn, -                                                     refresh_session_pub, -                                                     j); -    if (NULL == denom_pubs[j]) -    { -      GNUNET_break (0); -      for (i=0;i<j;i++) -        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i]); -      GNUNET_free (denom_pubs); -      GNUNET_free (melts); -      return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) -        ? GNUNET_NO : GNUNET_SYSERR; -    } +    GNUNET_break (0); +    GNUNET_free (denom_pubs); +    GNUNET_free (melts); +    return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) +      ? GNUNET_NO : GNUNET_SYSERR;    }    off = 0; -  for (i=0;i<refresh_session.kappa - 1;i++) +  for (i=0;i<KAPPA - 1;i++)    {      if (i == refresh_session.noreveal_index)        off = 1;      if (GNUNET_OK !=          (res = check_commitment (connection, -                                 db_conn, -                                 refresh_session_pub, +                                 session, +                                 session_hash,                                   i + off,                                   refresh_session.num_oldcoins,                                   transfer_privs[i + off], @@ -1037,7 +1126,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,                                   denom_pubs)))      {        for (j=0;j<refresh_session.num_newcoins;j++) -        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);        GNUNET_free (denom_pubs);        GNUNET_free (melts);        return (GNUNET_NO == res) ? MHD_YES : MHD_NO; @@ -1047,50 +1136,71 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,    /* Client request OK, start transaction */    if (GNUNET_OK != -      TALER_MINT_DB_transaction (db_conn)) +      plugin->start (plugin->cls, +                     session))    {      GNUNET_break (0);      for (j=0;j<refresh_session.num_newcoins;j++) -      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);      GNUNET_free (denom_pubs);      return TALER_MINT_reply_internal_db_error (connection);    } +  commit_coins = GNUNET_malloc (refresh_session.num_newcoins * +                                sizeof (struct RefreshCommitCoin)); +  if (GNUNET_OK != +      plugin->get_refresh_commit_coins (plugin->cls, +                                        session, +                                        session_hash, +                                        refresh_session.noreveal_index, +                                        refresh_session.num_newcoins, +                                        commit_coins)) +  { +    GNUNET_break (0); +    GNUNET_free (commit_coins); +    for (j=0;j<refresh_session.num_newcoins;j++) +      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key); +    GNUNET_free (denom_pubs); +    return TALER_MINT_reply_internal_db_error (connection); +  }    ev_sigs = GNUNET_malloc (refresh_session.num_newcoins * -                           sizeof (struct GNUNET_CRYPTO_rsa_Signature *)); +                           sizeof (struct TALER_DenominationSignature));    key_state = TALER_MINT_key_state_acquire ();    for (j=0;j<refresh_session.num_newcoins;j++)    {      ev_sigs[j] = refresh_mint_coin (connection, -                                    db_conn, -                                    refresh_session_pub, +                                    session, +                                    session_hash,                                      key_state, -                                    denom_pubs[j], -                                    refresh_session.noreveal_index, +                                    &denom_pubs[j], +                                    &commit_coins[j],                                      j); -    if (NULL == ev_sigs[j]) +    if (NULL == ev_sigs[j].rsa_signature)      {        TALER_MINT_key_state_release (key_state);        for (i=0;i<j;i++) -        GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i]); +        GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);        GNUNET_free (ev_sigs);        for (j=0;j<refresh_session.num_newcoins;j++) -        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);        GNUNET_free (denom_pubs); +      GNUNET_free (commit_coins);        return TALER_MINT_reply_internal_db_error (connection);      }    }    TALER_MINT_key_state_release (key_state);    for (j=0;j<refresh_session.num_newcoins;j++) -    GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +    GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);    GNUNET_free (denom_pubs); +  GNUNET_free (commit_coins);    if (GNUNET_OK != -      TALER_MINT_DB_commit (db_conn)) +      plugin->commit (plugin->cls, +                      session))    {      LOG_WARNING ("/refresh/reveal transaction commit failed\n");      for (i=0;i<refresh_session.num_newcoins;i++) -      GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i]); +      GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);      GNUNET_free (ev_sigs);      return TALER_MINT_reply_commit_error (connection);    } @@ -1099,7 +1209,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,                                                   refresh_session.num_newcoins,                                                   ev_sigs);    for (i=0;i<refresh_session.num_newcoins;i++) -    GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i]); +    GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);    GNUNET_free (ev_sigs);    return res;  } @@ -1116,23 +1226,25 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,   */  int  TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection, -                                    const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) +                                    const struct TALER_CoinSpendPublicKey *coin_pub)  {    int res; -  PGconn *db_conn; -  struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub; +  struct TALER_MINTDB_Session *session; +  struct TALER_TransferPublicKey transfer_pub;    struct TALER_EncryptedLinkSecret shared_secret_enc;    struct LinkDataList *ldl; -  if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) +  if (NULL == (session = plugin->get_session (plugin->cls, +                                              GNUNET_NO)))    {      GNUNET_break (0);      return TALER_MINT_reply_internal_db_error (connection);    } -  res = TALER_db_get_transfer (db_conn, -                               coin_pub, -                               &transfer_pub, -                               &shared_secret_enc); +  res = plugin->get_transfer (plugin->cls, +                              session, +                              coin_pub, +                              &transfer_pub, +                              &shared_secret_enc);    if (GNUNET_SYSERR == res)    {      GNUNET_break (0); @@ -1148,7 +1260,9 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection,    }    GNUNET_assert (GNUNET_OK == res); -  ldl = TALER_db_get_link (db_conn, coin_pub); +  ldl = plugin->get_link_data_list (plugin->cls, +                                    session, +                                    coin_pub);    if (NULL == ldl)    {      return TALER_MINT_reply_json_pack (connection, @@ -1161,7 +1275,8 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection,                                                 &transfer_pub,                                                 &shared_secret_enc,                                                 ldl); -  TALER_db_link_data_list_free (ldl); +  plugin->free_link_data_list (plugin->cls, +                               ldl);    return res;  } diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index 84e65eef..b2061850 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -25,8 +25,7 @@  #include <microhttpd.h>  #include <gnunet/gnunet_util_lib.h>  #include "taler_util.h" -#include "taler-mint-httpd_keys.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h"  /** @@ -54,7 +53,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,   */  int  TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, -                                       const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub); +                                       const struct TALER_ReservePublicKey *reserve_pub);  /** @@ -73,11 +72,11 @@ TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection,   */  int  TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, -                                     const struct GNUNET_CRYPTO_EddsaPublicKey *reserve, -                                     const struct GNUNET_CRYPTO_rsa_PublicKey *denomination_pub, +                                     const struct TALER_ReservePublicKey *reserve, +                                     const struct TALER_DenominationPublicKey *denomination_pub,                                       const char *blinded_msg,                                       size_t blinded_msg_len, -                                     const struct GNUNET_CRYPTO_EddsaSignature *signature); +                                     const struct TALER_ReserveSignature *signature);  /** @@ -89,14 +88,14 @@ struct MeltDetails     * Signature allowing the melt (using     * a `struct RefreshMeltConfirmSignRequestBody`) to sign over.     */ -  struct GNUNET_CRYPTO_EcdsaSignature melt_sig; +  struct TALER_CoinSpendSignature melt_sig;    /**     * How much of the coin's value did the client allow to be melted?     * This amount includes the fees, so the final amount contributed     * to the melt is this value minus the fee for melting the coin.     */ -  struct TALER_Amount melt_amount; +  struct TALER_Amount melt_amount_with_fee;  }; @@ -107,19 +106,13 @@ struct MeltDetails   * required value left and if so, store that they have been   * melted and confirm the melting operation to the client.   * - * FIXME: some arguments are redundant here... - *   * @param connection the MHD connection to handle - * @param melt_hash hash code of the session the coins are melted into - * @param refresh_session_pub public key of the refresh session - * @param client_signature signature of the client (matching @a refresh_session_pub) - *         over the melting request + * @param session_hash hash code of the session the coins are melted into   * @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @commit_coin array   * @param denum_pubs array of public denomination keys for the refresh (?)   * @param coin_count number of entries in @a coin_public_infos and @ a coin_melt_details, size of y-dimension of @commit_link array   * @param coin_public_infos information about the coins to melt   * @param coin_melt_details signatures and (residual) value of the respective coin should be melted - * @param kappa size of x-dimension of @commit_coin and @commit_link arrays   * @param commit_coin 2d array of coin commitments (what the mint is to sign   *                    once the "/refres/reveal" of cut and choose is done)   * @param commit_link 2d array of coin link commitments (what the mint is @@ -127,42 +120,36 @@ struct MeltDetails   *                    future)   * @return MHD result code   */ -// FIXME: see #3635.  int  TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, -                                    const struct GNUNET_HashCode *melt_hash, -                                    const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                    const struct GNUNET_CRYPTO_EddsaSignature *client_signature, +                                    const struct GNUNET_HashCode *session_hash,                                      unsigned int num_new_denoms, -                                    struct GNUNET_CRYPTO_rsa_PublicKey *const*denom_pubs, +                                    const struct TALER_DenominationPublicKey *denom_pubs,                                      unsigned int coin_count,                                      const struct TALER_CoinPublicInfo *coin_public_infos,                                      const struct MeltDetails *coin_melt_details, -                                    unsigned int kappa,                                      struct RefreshCommitCoin *const* commit_coin,                                      struct RefreshCommitLink *const* commit_link);  /**   * Execute a "/refresh/reveal".  The client is revealing to us the - * transfer keys for @a kappa-1 sets of coins.  Verify that the + * transfer keys for #KAPPA-1 sets of coins.  Verify that the   * revealed transfer keys would allow linkage to the blinded coins,   * and if so, return the signed coins for corresponding to the set of   * coins that was not chosen.   *   * @param connection the MHD connection to handle - * @param refresh_session_pub public key of the refresh session - * @param kappa size of x-dimension of @transfer_privs array plus one (!) + * @param session_hash hash over the refresh session   * @param num_oldcoins size of y-dimension of @transfer_privs array - * @param transfer_pubs array with the revealed transfer keys + * @param transfer_pubs array with the revealed transfer keys, #KAPPA is 1st-dimension   * @return MHD result code   */  int  TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, -                                      const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                                      unsigned int kappa, +                                      const struct GNUNET_HashCode *session_hash,                                        unsigned int num_oldcoins, -                                      struct GNUNET_CRYPTO_EcdsaPrivateKey *const*transfer_privs); +                                      struct TALER_TransferPrivateKey **transfer_privs);  /** @@ -176,7 +163,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection,   */  int  TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection, -                                    const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); +                                    const struct TALER_CoinSpendPublicKey *coin_pub);  #endif diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index 915a7389..7ecf8bfe 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -32,11 +32,10 @@  #include <microhttpd.h>  #include <libpq-fe.h>  #include <pthread.h> -#include "mint_db.h" +#include "taler_mintdb_plugin.h"  #include "taler_signatures.h"  #include "taler_util.h"  #include "taler-mint-httpd_parsing.h" -#include "taler-mint-httpd_keys.h"  #include "taler-mint-httpd_db.h"  #include "taler-mint-httpd_deposit.h"  #include "taler-mint-httpd_responses.h" @@ -60,19 +59,21 @@ verify_and_execute_deposit (struct MHD_Connection *connection,    struct MintKeyState *key_state;    struct TALER_DepositRequest dr;    struct TALER_MINT_DenomKeyIssuePriv *dki; +  struct TALER_Amount fee_deposit;    dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_DEPOSIT);    dr.purpose.size = htonl (sizeof (struct TALER_DepositRequest));    dr.h_contract = deposit->h_contract;    dr.h_wire = deposit->h_wire;    dr.transaction_id = GNUNET_htonll (deposit->transaction_id); -  dr.amount = TALER_amount_hton (deposit->amount); +  TALER_amount_hton (&dr.amount_with_fee, +                     &deposit->amount_with_fee);    dr.coin_pub = deposit->coin.coin_pub;    if (GNUNET_OK !=        GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_WALLET_DEPOSIT,                                    &dr.purpose, -                                  &deposit->csig, -                                  &deposit->coin.coin_pub)) +                                  &deposit->csig.ecdsa_signature, +                                  &deposit->coin.coin_pub.ecdsa_pub))    {      LOG_WARNING ("Invalid signature on /deposit request\n");      return TALER_MINT_reply_arg_invalid (connection, @@ -81,7 +82,7 @@ verify_and_execute_deposit (struct MHD_Connection *connection,    /* check denomination exists and is valid */    key_state = TALER_MINT_key_state_acquire ();    dki = TALER_MINT_get_denom_key (key_state, -                                  deposit->coin.denom_pub); +                                  &deposit->coin.denom_pub);    if (NULL == dki)    {      TALER_MINT_key_state_release (key_state); @@ -97,6 +98,17 @@ verify_and_execute_deposit (struct MHD_Connection *connection,      TALER_MINT_key_state_release (key_state);      return TALER_MINT_reply_coin_invalid (connection);    } +  TALER_amount_ntoh (&fee_deposit, +                     &dki->issue.fee_deposit); +  if (TALER_amount_cmp (&fee_deposit, +                        &deposit->amount_with_fee) < 0) +  { +    TALER_MINT_key_state_release (key_state); +    return (MHD_YES == +            TALER_MINT_reply_external_error (connection, +                                             "deposited amount smaller than depositing fee")) +      ? GNUNET_NO : GNUNET_SYSERR; +  }    TALER_MINT_key_state_release (key_state);    return TALER_MINT_db_execute_deposit (connection, @@ -119,7 +131,7 @@ static int  parse_and_handle_deposit_request (struct MHD_Connection *connection,                                    const json_t *root,                                    const struct TALER_Amount *amount, -                                  const json_t *wire) +                                  json_t *wire)  {    int res;    struct Deposit deposit; @@ -167,7 +179,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,    GNUNET_free (wire_enc);    deposit.wire = wire; -  deposit.amount = *amount; +  deposit.amount_with_fee = *amount;    res = verify_and_execute_deposit (connection,                                      &deposit);    TALER_MINT_release_parsed_data (spec); diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c deleted file mode 100644 index c22040d0..00000000 --- a/src/mint/taler-mint-httpd_keys.c +++ /dev/null @@ -1,74 +0,0 @@ -/* -  This file is part of TALER -  Copyright (C) 2014 GNUnet e.V. - -  TALER is free software; you can redistribute it and/or modify it under the -  terms of the GNU Affero General Public License as published by the Free Software -  Foundation; either version 3, or (at your option) any later version. - -  TALER is distributed in the hope that it will be useful, but WITHOUT ANY -  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details. - -  You should have received a copy of the GNU Affero General Public License along with -  TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-mint-httpd_keys.c - * @brief Handle /keys requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler-mint-httpd_keys.h" -#include "taler-mint-httpd_keystate.h" - - -/** - * Function to call to handle the request by sending - * back static data from the @a rh. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[IN|OUT] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TALER_MINT_handler_keys (struct RequestHandler *rh, -                         struct MHD_Connection *connection, -                         void **connection_cls, -                         const char *upload_data, -                         size_t *upload_data_size) -{ -  struct MintKeyState *key_state; -  struct MHD_Response *response; -  int ret; - -  key_state = TALER_MINT_key_state_acquire (); -  response = MHD_create_response_from_buffer (strlen (key_state->keys_json), -                                              key_state->keys_json, -                                              MHD_RESPMEM_MUST_COPY); -  TALER_MINT_key_state_release (key_state); -  if (NULL == response) -  { -    GNUNET_break (0); -    return MHD_NO; -  } -  (void) MHD_add_response_header (response, -                                  "Content-Type", -                                  rh->mime_type); -  ret = MHD_queue_response (connection, -                            rh->response_code, -                            response); -  MHD_destroy_response (response); -  return ret; -} - - -/* end of taler-mint-httpd_keys.c */ diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h deleted file mode 100644 index bb1bc721..00000000 --- a/src/mint/taler-mint-httpd_keys.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -  This file is part of TALER -  Copyright (C) 2014 GNUnet e.V. - -  TALER is free software; you can redistribute it and/or modify it under the -  terms of the GNU Affero General Public License as published by the Free Software -  Foundation; either version 3, or (at your option) any later version. - -  TALER is distributed in the hope that it will be useful, but WITHOUT ANY -  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details. - -  You should have received a copy of the GNU Affero General Public License along with -  TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-mint-httpd_keys.h - * @brief Handle /keys requests and manage key state - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef TALER_MINT_HTTPD_KEYS_H -#define TALER_MINT_HTTPD_KEYS_H - -#include <gnunet/gnunet_util_lib.h> -#include <microhttpd.h> -#include <jansson.h> -#include "taler-mint-httpd.h" - - -/** - * Handle a "/keys" request - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[IN|OUT] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code -  */ -int -TALER_MINT_handler_keys (struct RequestHandler *rh, -                         struct MHD_Connection *connection, -                         void **connection_cls, -                         const char *upload_data, -                         size_t *upload_data_size); - - - -#endif diff --git a/src/mint/taler-mint-httpd_keystate.c b/src/mint/taler-mint-httpd_keystate.c index 7edae9f7..c29c5c51 100644 --- a/src/mint/taler-mint-httpd_keystate.c +++ b/src/mint/taler-mint-httpd_keystate.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 GNUnet e.V. +  Copyright (C) 2014, 2015 GNUnet e.V.    TALER is free software; you can redistribute it and/or modify it under the    terms of the GNU Affero General Public License as published by the Free Software @@ -21,17 +21,73 @@   * @author Christian Grothoff   */  #include "platform.h" -#include <gnunet/gnunet_util_lib.h>  #include <pthread.h> -#include "taler_signatures.h"  #include "taler-mint-httpd_keystate.h" -#include "taler_util.h" -#include "taler-mint-httpd_parsing.h" + + +/** + * Snapshot of the (coin and signing) keys (including private keys) of + * the mint.  There can be multiple instances of this struct, as it is + * reference counted and only destroyed once the last user is done + * with it.  The current instance is acquired using + * #TALER_MINT_key_state_acquire().  Using this function increases the + * reference count.  The contents of this structure (except for the + * reference counter) should be considered READ-ONLY until it is + * ultimately destroyed (as there can be many concurrent users). + */ +struct MintKeyState +{ +  /** +   * JSON array with denomination keys.  (Currently not really used +   * after initialization.) +   */ +  json_t *denom_keys_array; + +  /** +   * JSON array with signing keys. (Currently not really used +   * after initialization.) +   */ +  json_t *sign_keys_array; + +  /** +   * Cached JSON text that the mint will send for a "/keys" request. +   * Includes our @e master_pub public key, the signing and +   * denomination keys as well as the @e reload_time. +   */ +  char *keys_json; + +  /** +   * Mapping from denomination keys to denomination key issue struct. +   * Used to lookup the key by hash. +   */ +  struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; + +  /** +   * When did we initiate the key reloading? +   */ +  struct GNUNET_TIME_Absolute reload_time; + +  /** +   * When is the next key invalid and we have to reload? (We also +   * reload on SIGUSR1.) +   */ +  struct GNUNET_TIME_Absolute next_reload; + +  /** +   * Mint signing key that should be used currently. +   */ +  struct TALER_MINT_SignKeyIssuePriv current_sign_key_issue; + +  /** +   * Reference count.  The struct is released when the RC hits zero. +   */ +  unsigned int refcnt; +};  /**   * Mint key state.  Never use directly, instead access via - * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release. + * #TALER_MINT_key_state_acquire() and #TALER_MINT_key_state_release().   */  static struct MintKeyState *internal_key_state; @@ -50,79 +106,48 @@ static int reload_pipe[2];   * Convert the public part of a denomination key issue to a JSON   * object.   * + * @param pk public key of the denomination key   * @param dki the denomination key issue   * @return a JSON object describing the denomination key isue (public part)   */  static json_t * -denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki) -{ -  char *buf; -  size_t buf_len; -  json_t *dk_json = json_object (); - -  json_object_set_new (dk_json, -                       "master_sig", -                       TALER_JSON_from_data (&dki->signature, -                                             sizeof (struct GNUNET_CRYPTO_EddsaSignature))); -  json_object_set_new (dk_json, -                       "stamp_start", -                       TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start))); -  json_object_set_new (dk_json, -                       "stamp_expire_withdraw", -                       TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw))); -  json_object_set_new (dk_json, -                       "stamp_expire_deposit", -                       TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend))); - -  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dki->denom_pub, -                                                 &buf); -  json_object_set_new (dk_json, -                       "denom_pub", -                       TALER_JSON_from_data (buf, -                                             buf_len)); -  GNUNET_free (buf); -  json_object_set_new (dk_json, -                       "value", -                       TALER_JSON_from_amount (TALER_amount_ntoh (dki->value))); -  json_object_set_new (dk_json, -                       "fee_withdraw", -                       TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_withdraw))); -  json_object_set_new (dk_json, -                       "fee_deposit", -                       TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_deposit))); -  json_object_set_new (dk_json, -                       "fee_refresh", -                       TALER_JSON_from_amount (TALER_amount_ntoh (dki->fee_refresh))); -  return dk_json; -} - - -/** - * Convert the public part of a sign key issue to a JSON object. - * - * @param ski the sign key issue - * @return a JSON object describing the sign key isue (public part) - */ -static json_t * -sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski) +denom_key_issue_to_json (const struct TALER_DenominationPublicKey *pk, +                         const struct TALER_MINT_DenomKeyIssue *dki)  { -  json_t *sk_json = json_object (); - -  json_object_set_new (sk_json, -                       "stamp_start", -                       TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start))); -  json_object_set_new (sk_json, -                       "stamp_expire", -                       TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire))); -  json_object_set_new (sk_json, -                       "master_sig", -                       TALER_JSON_from_data (&ski->signature, -                                             sizeof (struct GNUNET_CRYPTO_EddsaSignature))); -  json_object_set_new (sk_json, -                       "key", -                       TALER_JSON_from_data (&ski->signkey_pub, -                                             sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))); -  return sk_json; +  struct TALER_Amount value; +  struct TALER_Amount fee_withdraw; +  struct TALER_Amount fee_deposit; +  struct TALER_Amount fee_refresh; + +  TALER_amount_ntoh (&value, +                     &dki->value); +  TALER_amount_ntoh (&fee_withdraw, +                     &dki->fee_withdraw); +  TALER_amount_ntoh (&fee_deposit, +                     &dki->fee_deposit); +  TALER_amount_ntoh (&fee_refresh, +                     &dki->fee_refresh); +  return +    json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}", +               "master_sig", +               TALER_JSON_from_data (&dki->signature, +                                     sizeof (struct GNUNET_CRYPTO_EddsaSignature)), +               "stamp_start", +               TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start)), +               "stamp_expire_withdraw", +               TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw)), +               "stamp_expire_deposit", +               TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend)), +               "denom_pub", +               TALER_JSON_from_rsa_public_key (pk->rsa_public_key), +               "value", +               TALER_JSON_from_amount (&value), +               "fee_withdraw", +               TALER_JSON_from_amount (&fee_withdraw), +               "fee_deposit", +               TALER_JSON_from_amount (&fee_deposit), +               "fee_refresh", +               TALER_JSON_from_amount (&fee_refresh));  } @@ -152,7 +177,7 @@ TALER_MINT_conf_duration_provide ()  /** - * Iterator for denomination keys. + * Iterator for (re)loading/initializing denomination keys.   *   * @param cls closure   * @param dki the denomination key issue @@ -167,47 +192,83 @@ reload_keys_denom_iter (void *cls,                          const struct TALER_MINT_DenomKeyIssuePriv *dki)  {    struct MintKeyState *ctx = cls; -  struct GNUNET_TIME_Absolute stamp_provide; +  struct GNUNET_TIME_Absolute now; +  struct GNUNET_TIME_Absolute horizon;    struct GNUNET_HashCode denom_key_hash; +  struct TALER_MINT_DenomKeyIssuePriv *d2;    int res; -  stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, -                                            TALER_MINT_conf_duration_provide ()); - -  if (GNUNET_TIME_absolute_ntoh (dki->issue.expire_spend).abs_value_us < ctx->reload_time.abs_value_us) +  horizon = GNUNET_TIME_relative_to_absolute (TALER_MINT_conf_duration_provide ()); +  if (GNUNET_TIME_absolute_ntoh (dki->issue.expire_spend).abs_value_us > +      horizon.abs_value_us)    { -    // this key is expired +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Skipping future denomination key `%s'\n", +                alias);      return GNUNET_OK;    } -  if (GNUNET_TIME_absolute_ntoh (dki->issue.start).abs_value_us > stamp_provide.abs_value_us) +  now = GNUNET_TIME_absolute_get (); +  if (GNUNET_TIME_absolute_ntoh (dki->issue.expire_spend).abs_value_us < +      now.abs_value_us)    { -    // we are to early for this key +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Skipping expired denomination key `%s'\n", +                alias);      return GNUNET_OK;    } -  GNUNET_CRYPTO_hash (&dki->issue.denom_pub, -                      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), -                      &denom_key_hash); - +  GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, +                                     &denom_key_hash); +  d2 = GNUNET_memdup (dki, +                      sizeof (struct TALER_MINT_DenomKeyIssuePriv));    res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map,                                             &denom_key_hash, -                                           GNUNET_memdup (dki, sizeof (struct TALER_MINT_DenomKeyIssuePriv)), +                                           d2,                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);    if (GNUNET_OK != res) +  {      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -                "Duplicate denomination key\n"); - +                "Duplicate denomination key `%s'\n", +                alias); +    GNUNET_free (d2); +    return GNUNET_OK; +  }    json_array_append_new (ctx->denom_keys_array, -                         denom_key_issue_to_json (&dki->issue)); - +                         denom_key_issue_to_json (&dki->denom_pub, +                                                  &dki->issue));    return GNUNET_OK;  }  /** + * Convert the public part of a sign key issue to a JSON object. + * + * @param ski the sign key issue + * @return a JSON object describing the sign key isue (public part) + */ +static json_t * +sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski) +{ +  return +    json_pack ("{s:o, s:o, s:o, s:o}", +               "stamp_start", +               TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start)), +               "stamp_expire", +               TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire)), +               "master_sig", +               TALER_JSON_from_data (&ski->signature, +                                     sizeof (struct GNUNET_CRYPTO_EddsaSignature)), +               "key", +               TALER_JSON_from_data (&ski->signkey_pub, +                                     sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))); +} + + +/**   * Iterator for sign keys.   *   * @param cls closure + * @param filename name of the file the key came from   * @param ski the sign key issue   * @return #GNUNET_OK to continue to iterate,   *  #GNUNET_NO to stop iteration with no error, @@ -215,36 +276,40 @@ reload_keys_denom_iter (void *cls,   */  static int  reload_keys_sign_iter (void *cls, +                       const char *filename,                         const struct TALER_MINT_SignKeyIssuePriv *ski)  {    struct MintKeyState *ctx = cls; -  struct GNUNET_TIME_Absolute stamp_provide; - -  stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, -                                            TALER_MINT_conf_duration_provide (cfg)); +  struct GNUNET_TIME_Absolute now; +  struct GNUNET_TIME_Absolute horizon; -  if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < ctx->reload_time.abs_value_us) +  horizon = GNUNET_TIME_relative_to_absolute (TALER_MINT_conf_duration_provide ()); +  if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > +      horizon.abs_value_us)    { -    // this key is expired +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Skipping future signing key `%s'\n", +                filename);      return GNUNET_OK;    } - -  if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > stamp_provide.abs_value_us) +  now = GNUNET_TIME_absolute_get (); +  if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < +      now.abs_value_us)    { -    // we are to early for this key +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Skipping expired signing key `%s'\n", +                filename);      return GNUNET_OK;    } -  // the signkey is valid for now, check -  // if it's more recent than the current one! -  if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us > +  /* The signkey is valid at this time, check if it's more recent than +     what we have so far! */ +  if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us <        GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us) +  { +    /* We keep the most recent one around */      ctx->current_sign_key_issue = *ski; - - -  ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload, -                                               GNUNET_TIME_absolute_ntoh (ski->issue.expire)); - +  }    json_array_append_new (ctx->sign_keys_array,                           sign_key_issue_to_json (&ski->issue)); @@ -253,46 +318,24 @@ reload_keys_sign_iter (void *cls,  /** - * Load the mint's key state from disk. + * Iterator for freeing denomination keys.   * - * @return fresh key state (with reference count 1) + * @param cls closure with the `struct MintKeyState` + * @param key key for 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 struct MintKeyState * -reload_keys () +static int +free_denom_key (void *cls, +                const struct GNUNET_HashCode *key, +                void *value)  { -  struct MintKeyState *key_state; -  json_t *keys; - -  key_state = GNUNET_new (struct MintKeyState); -  key_state->refcnt = 1; - -  key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS; - -  key_state->denom_keys_array = json_array (); -  GNUNET_assert (NULL != key_state->denom_keys_array); - -  key_state->sign_keys_array = json_array (); -  GNUNET_assert (NULL != key_state->sign_keys_array); - -  key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, -                                                                  GNUNET_NO); -  GNUNET_assert (NULL != key_state->denomkey_map); +  struct TALER_MINT_DenomKeyIssuePriv *dki = value; -  key_state->reload_time = GNUNET_TIME_absolute_get (); - -  TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state); -  TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state); - -  keys = json_pack ("{s:o, s:o, s:o, s:o}", -                    "master_pub", TALER_JSON_from_data (&master_pub, -                                                        sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)), -                    "signkeys", key_state->sign_keys_array, -                    "denoms", key_state->denom_keys_array, -                    "list_issue_date", TALER_JSON_from_abs (key_state->reload_time)); - -  key_state->keys_json = json_dumps (keys, JSON_INDENT(2)); - -  return key_state; +  GNUNET_free (dki); +  return GNUNET_OK;  } @@ -309,6 +352,13 @@ TALER_MINT_key_state_release (struct MintKeyState *key_state)    key_state->refcnt--;    if (0 == key_state->refcnt)    { +    json_decref (key_state->denom_keys_array); +    json_decref (key_state->sign_keys_array); +    GNUNET_CONTAINER_multihashmap_iterate (key_state->denomkey_map, +                                           &free_denom_key, +                                           key_state); +    GNUNET_CONTAINER_multihashmap_destroy (key_state->denomkey_map); +    GNUNET_free (key_state->keys_json);      GNUNET_free (key_state);    }    GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); @@ -317,8 +367,8 @@ TALER_MINT_key_state_release (struct MintKeyState *key_state)  /**   * Acquire the key state of the mint.  Updates keys if necessary. - * For every call to #TALER_MINT_key_state_acquire, a matching call - * to #TALER_MINT_key_state_release must be made. + * For every call to #TALER_MINT_key_state_acquire(), a matching call + * to #TALER_MINT_key_state_release() must be made.   *   * @return the key state   */ @@ -327,19 +377,64 @@ TALER_MINT_key_state_acquire (void)  {    struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();    struct MintKeyState *key_state; +  json_t *keys; +  char *inner; +  struct TALER_MINT_KeySetSignature ks; +  struct TALER_MintSignature sig;    GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); -  if (NULL == internal_key_state) +  if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us)    { -    internal_key_state = reload_keys (); +    TALER_MINT_key_state_release (internal_key_state); +    internal_key_state = NULL;    } -  else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us) +  if (NULL == internal_key_state)    { -    GNUNET_assert (0 < internal_key_state->refcnt); -    internal_key_state->refcnt--; -    if (0 == internal_key_state->refcnt) -      GNUNET_free (internal_key_state); -    internal_key_state = reload_keys (); +    key_state = GNUNET_new (struct MintKeyState); +    key_state->denom_keys_array = json_array (); +    GNUNET_assert (NULL != key_state->denom_keys_array); +    key_state->sign_keys_array = json_array (); +    GNUNET_assert (NULL != key_state->sign_keys_array); +    key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, +                                                                    GNUNET_NO); +    key_state->reload_time = GNUNET_TIME_absolute_get (); +    TALER_MINT_denomkeys_iterate (mintdir, +                                  &reload_keys_denom_iter, +                                  key_state); +    TALER_MINT_signkeys_iterate (mintdir, +                                 &reload_keys_sign_iter, +                                 key_state); +    key_state->next_reload = GNUNET_TIME_absolute_ntoh (key_state->current_sign_key_issue.issue.expire); +    if (0 == key_state->next_reload.abs_value_us) +      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +                  "No valid signing key found!\n"); + +    keys = json_pack ("{s:o, s:o, s:o, s:o}", +                      "master_pub", +                      TALER_JSON_from_data (&master_pub, +                                            sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)), +                      "signkeys", key_state->sign_keys_array, +                      "denoms", key_state->denom_keys_array, +                      "list_issue_date", TALER_JSON_from_abs (key_state->reload_time)); +    inner = json_dumps (keys, +                        JSON_INDENT(2)); +    ks.purpose.size = htonl (sizeof (ks)); +    ks.purpose.purpose = htonl (TALER_SIGNATURE_KEYS_SET); +    ks.list_issue_date = GNUNET_TIME_absolute_hton (key_state->reload_time); +    GNUNET_CRYPTO_hash (inner, +                        strlen (inner), +                        &ks.hc); +    GNUNET_free (inner); +    TALER_MINT_keys_sign (&ks.purpose, +                          &sig); +    keys = json_pack ("{s:o, s:o}", +                      "keys", keys, +                      "eddsa-signature", TALER_JSON_from_eddsa_sig (&ks.purpose, +                                                                    &sig.eddsa_signature)); +    key_state->keys_json = json_dumps (keys, +                                       JSON_INDENT (2)); +    json_decref (keys); +    internal_key_state = key_state;    }    key_state = internal_key_state;    key_state->refcnt++; @@ -359,20 +454,14 @@ TALER_MINT_key_state_acquire (void)   */  struct TALER_MINT_DenomKeyIssuePriv *  TALER_MINT_get_denom_key (const struct MintKeyState *key_state, -                          const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub) +                          const struct TALER_DenominationPublicKey *denom_pub)  { -  struct GNUNET_HashCode hash; -  char *buf; -  size_t buf_len; - -  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub, -                                                 &buf); -  GNUNET_CRYPTO_hash (buf, -                      buf_len, -                      &hash); -  GNUNET_free (buf); +  struct GNUNET_HashCode hc; + +  GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key, +                                     &hc);    return GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, -                                            &hash); +                                            &hc);  } @@ -390,9 +479,11 @@ handle_signal (int signal_number)    if (SIGUSR1 == signal_number)    { -    errno = 0; -    res = write (reload_pipe[1], &c, 1); -    if ((res < 0) && (EINTR != errno)) +    res = write (reload_pipe[1], +                 &c, +                 1); +    if ( (res < 0) && +         (EINTR != errno) )      {        GNUNET_break (0);        return; @@ -409,11 +500,16 @@ handle_signal (int signal_number)  /**   * Read signals from a pipe in a loop, and reload keys from disk if   * SIGUSR1 is read from the pipe. + * + * @return #GNUNET_SYSERR on errors, otherwise does not return + *          (FIXME: #3474)   */  int  TALER_MINT_key_reload_loop (void)  {    struct sigaction act; +  struct sigaction rec; +  int ret;    if (0 != pipe (reload_pipe))    { @@ -421,16 +517,21 @@ TALER_MINT_key_reload_loop (void)               "Failed to create pipe.\n");      return GNUNET_SYSERR;    } -  memset (&act, 0, sizeof (struct sigaction)); +  memset (&act, +          0, +          sizeof (struct sigaction));    act.sa_handler = &handle_signal; - -  if (0 != sigaction (SIGUSR1, &act, NULL)) +  if (0 != sigaction (SIGUSR1, +                      &act, +                      &rec))    {      fprintf (stderr,               "Failed to set signal handler.\n");      return GNUNET_SYSERR;    } +  ret = GNUNET_OK; +  /* FIXME: allow for 'clean' termination or restart (#3474) */    while (1)    {      char c; @@ -438,52 +539,105 @@ TALER_MINT_key_reload_loop (void)      GNUNET_log (GNUNET_ERROR_TYPE_INFO,                  "(re-)loading keys\n"); -    GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));      if (NULL != internal_key_state)      { -      GNUNET_assert (0 != internal_key_state->refcnt); -      internal_key_state->refcnt -= 1; -      if (0 == internal_key_state->refcnt) -        GNUNET_free (internal_key_state); +      TALER_MINT_key_state_release (internal_key_state); +      internal_key_state = NULL;      } -    internal_key_state = reload_keys (); -    GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); +    /* This will re-initialize 'internal_key_state' with +       an initial refcnt of 1 */ +    (void) TALER_MINT_key_state_acquire (); +  read_again:      errno = 0; -    res = read (reload_pipe[0], &c, 1); +    res = read (reload_pipe[0], +                &c, +                1);      if ((res < 0) && (EINTR != errno))      {        GNUNET_break (0); -      return GNUNET_SYSERR; +      ret = GNUNET_SYSERR; +      break;      }      if (EINTR == errno)        goto read_again;    } -  return GNUNET_OK; + +  if (0 != sigaction (SIGUSR1, +                      &rec, +                      &act)) +  { +    fprintf (stderr, +             "Failed to restore signal handler.\n"); +    return GNUNET_SYSERR; +  } +  return ret;  }  /** - * Sign the message in @a purpose with the mint's signing - * key. + * Sign the message in @a purpose with the mint's signing key.   *   * @param purpose the message to sign   * @param[OUT] sig signature over purpose using current signing key   */  void  TALER_MINT_keys_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, -                      struct GNUNET_CRYPTO_EddsaSignature *sig) +                      struct TALER_MintSignature *sig)  {    struct MintKeyState *key_state;    key_state = TALER_MINT_key_state_acquire ();    GNUNET_assert (GNUNET_OK == -                 GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv, +                 GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv.eddsa_priv,                                             purpose, -                                           sig)); +                                           &sig->eddsa_signature));    TALER_MINT_key_state_release (key_state);  } +/** + * Function to call to handle the request by sending + * back static data from the @a rh. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[IN|OUT] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TALER_MINT_handler_keys (struct RequestHandler *rh, +                         struct MHD_Connection *connection, +                         void **connection_cls, +                         const char *upload_data, +                         size_t *upload_data_size) +{ +  struct MintKeyState *key_state; +  struct MHD_Response *response; +  int ret; + +  key_state = TALER_MINT_key_state_acquire (); +  response = MHD_create_response_from_buffer (strlen (key_state->keys_json), +                                              key_state->keys_json, +                                              MHD_RESPMEM_MUST_COPY); +  TALER_MINT_key_state_release (key_state); +  if (NULL == response) +  { +    GNUNET_break (0); +    return MHD_NO; +  } +  (void) MHD_add_response_header (response, +                                  "Content-Type", +                                  rh->mime_type); +  ret = MHD_queue_response (connection, +                            rh->response_code, +                            response); +  MHD_destroy_response (response); +  return ret; +} + +  /* end of taler-mint-httpd_keystate.c */ diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h index 4b700d1c..faccc8f0 100644 --- a/src/mint/taler-mint-httpd_keystate.h +++ b/src/mint/taler-mint-httpd_keystate.h @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 GNUnet e.V. +  Copyright (C) 2014, 2015 GNUnet e.V.    TALER is free software; you can redistribute it and/or modify it under the    terms of the GNU Affero General Public License as published by the Free Software @@ -14,7 +14,7 @@    TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/>  */  /** - * @file taler-mint-httpd_keystate.h + * @file mint/taler-mint-httpd_keystate.h   * @brief management of our private signing keys (denomination keys)   * @author Florian Dold   * @author Benedikt Mueller @@ -23,10 +23,8 @@  #ifndef TALER_MINT_HTTPD_KEYSTATE_H  #define TALER_MINT_HTTPD_KEYSTATE_H -  #include <gnunet/gnunet_util_lib.h>  #include <microhttpd.h> -#include <jansson.h>  #include "taler-mint-httpd.h"  #include "key_io.h" @@ -35,49 +33,7 @@   * Snapshot of the (coin and signing)   * keys (including private keys) of the mint.   */ -struct MintKeyState -{ -  /** -   * When did we initiate the key reloading? -   */ -  struct GNUNET_TIME_Absolute reload_time; - -  /** -   * JSON array with denomination keys. -   */ -  json_t *denom_keys_array; - -  /** -   * JSON array with signing keys. -   */ -  json_t *sign_keys_array; - -  /** -   * Mapping from denomination keys to denomination key issue struct. -   */ -  struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; - -  /** -   * When is the next key invalid and we have to reload? -   */ -  struct GNUNET_TIME_Absolute next_reload; - -  /** -   * Mint signing key that should be used currently. -   */ -  struct TALER_MINT_SignKeyIssuePriv current_sign_key_issue; - -  /** -   * Cached JSON text that the mint will send for -   * a /keys request. -   */ -  char *keys_json; - -  /** -   * Reference count. -   */ -  unsigned int refcnt; -}; +struct MintKeyState;  /** @@ -101,7 +57,8 @@ TALER_MINT_key_state_release (struct MintKeyState *key_state);  /** - * Look up the issue for a denom public key. + * Look up the issue for a denom public key.  Note that the result + * is only valid while the @a key_state is not released!   *   * @param key state to look in   * @param denom_pub denomination public key @@ -110,14 +67,15 @@ TALER_MINT_key_state_release (struct MintKeyState *key_state);   */  struct TALER_MINT_DenomKeyIssuePriv *  TALER_MINT_get_denom_key (const struct MintKeyState *key_state, -                          const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub); +                          const struct TALER_DenominationPublicKey *denom_pub);  /**   * Read signals from a pipe in a loop, and reload keys from disk if   * SIGUSR1 is read from the pipe.   * - * @return #GNUNET_OK if we terminated normally, #GNUNET_SYSERR on error + * @return #GNUNET_OK if we terminated normally, + *         #GNUNET_SYSERR on error   */  int  TALER_MINT_key_reload_loop (void); @@ -132,8 +90,25 @@ TALER_MINT_key_reload_loop (void);   */  void  TALER_MINT_keys_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, -                      struct GNUNET_CRYPTO_EddsaSignature *sig); +                      struct TALER_MintSignature *sig); + +/** + * Handle a "/keys" request + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[IN|OUT] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code +  */ +int +TALER_MINT_handler_keys (struct RequestHandler *rh, +                         struct MHD_Connection *connection, +                         void **connection_cls, +                         const char *upload_data, +                         size_t *upload_data_size);  #endif diff --git a/src/mint/taler-mint-httpd_parsing.c b/src/mint/taler-mint-httpd_parsing.c index 6c5f72b3..dedf4af9 100644 --- a/src/mint/taler-mint-httpd_parsing.c +++ b/src/mint/taler-mint-httpd_parsing.c @@ -680,6 +680,16 @@ GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection,          break;        } +    case JNAV_RET_AMOUNT: +      { +        struct TALER_Amount *where = va_arg (argp, void *); + +        ret = TALER_MINT_parse_amount_json (connection, +                                            (json_t *) root, +                                            where); +        break; +      } +      default:        GNUNET_break (0);        ret = (MHD_YES == @@ -721,6 +731,8 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,    ret = GNUNET_YES;    for (i=0; NULL != spec[i].field_name; i++)    { +    if (GNUNET_YES != ret) +      break;      switch (spec[i].command)      {      case JNAV_FIELD: @@ -730,8 +742,6 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,        GNUNET_break (0);        return GNUNET_SYSERR;      case JNAV_RET_DATA: -      if (GNUNET_YES != ret) -        break;        ret = GNUNET_MINT_parse_navigate_json (connection,                                               root,                                               JNAV_FIELD, @@ -741,8 +751,6 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,                                               spec[i].destination_size_in);        break;      case JNAV_RET_DATA_VAR: -      if (GNUNET_YES != ret) -        break;        ptr = NULL;        ret = GNUNET_MINT_parse_navigate_json (connection,                                               root, @@ -754,8 +762,6 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,        spec[i].destination = ptr;        break;      case JNAV_RET_TYPED_JSON: -      if (GNUNET_YES != ret) -        break;        ptr = NULL;        ret = GNUNET_MINT_parse_navigate_json (connection,                                               root, @@ -767,8 +773,6 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,        *((void**)spec[i].destination) = ptr;        break;      case JNAV_RET_RSA_PUBLIC_KEY: -      if (GNUNET_YES != ret) -        break;        ptr = NULL;        ret = GNUNET_MINT_parse_navigate_json (connection,                                               root, @@ -779,8 +783,6 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,        spec[i].destination = ptr;        break;      case JNAV_RET_RSA_SIGNATURE: -      if (GNUNET_YES != ret) -        break;        ptr = NULL;        ret = GNUNET_MINT_parse_navigate_json (connection,                                               root, @@ -790,6 +792,16 @@ TALER_MINT_parse_json_data (struct MHD_Connection *connection,                                               &ptr);        spec[i].destination = ptr;        break; +    case JNAV_RET_AMOUNT: +      GNUNET_assert (sizeof (struct TALER_Amount) == +                     spec[i].destination_size_in); +      ret = GNUNET_MINT_parse_navigate_json (connection, +                                             root, +                                             JNAV_FIELD, +                                             spec[i].field_name, +                                             JNAV_RET_AMOUNT, +                                             &spec[i].destination); +      break;      }    }    if (GNUNET_YES != ret) @@ -854,6 +866,11 @@ TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec)          *(void**) spec[i].destination = NULL;        }        break; +    case JNAV_RET_AMOUNT: +      memset (spec[i].destination, +              0, +              sizeof (struct TALER_Amount)); +      break;      }    }  } @@ -878,8 +895,10 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection,    json_int_t value;    json_int_t fraction;    const char *currency; -  struct TALER_Amount a; +  memset (amount, +          0, +          sizeof (struct TALER_Amount));    if (-1 == json_unpack (f,                           "{s:I, s:I, s:s}",                           "value", &value, @@ -897,7 +916,7 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection,    }    if ( (value < 0) ||         (fraction < 0) || -       (value > UINT32_MAX) || +       (value > UINT64_MAX) ||         (fraction > UINT32_MAX) )    {      LOG_WARNING ("Amount specified not in allowed range\n"); @@ -922,11 +941,11 @@ TALER_MINT_parse_amount_json (struct MHD_Connection *connection,        return GNUNET_SYSERR;      return GNUNET_NO;    } -  a.value = (uint32_t) value; -  a.fraction = (uint32_t) fraction; +  amount->value = (uint64_t) value; +  amount->fraction = (uint32_t) fraction;    GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN); -  strcpy (a.currency, MINT_CURRENCY); -  *amount = TALER_amount_normalize (a); +  strcpy (amount->currency, MINT_CURRENCY); +  TALER_amount_normalize (amount);    return GNUNET_OK;  } diff --git a/src/mint/taler-mint-httpd_parsing.h b/src/mint/taler-mint-httpd_parsing.h index 94e2927d..7a322d4d 100644 --- a/src/mint/taler-mint-httpd_parsing.h +++ b/src/mint/taler-mint-httpd_parsing.h @@ -120,7 +120,13 @@ enum TALER_MINT_JsonNavigationCommand     * Return a `struct GNUNET_CRYPTO_rsa_Signature` which was     * encoded as variable-size base32crockford encoded data.     */ -  JNAV_RET_RSA_SIGNATURE +  JNAV_RET_RSA_SIGNATURE, + +  /** +   * Return a `struct TALER_Amount` which was +   * encoded within its own json object. +   */ +  JNAV_RET_AMOUNT  }; @@ -271,6 +277,14 @@ TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec);  #define TALER_MINT_PARSE_RSA_SIGNATURE(field,ptrsig) { field, ptrsig, 0, 0, JNAV_RET_RSA_SIGNATURE, 0 }  /** + * Generate line in parser specification for an amount. + * + * @param field name of the field + * @param amount a `struct TALER_Amount *` to initialize + */ +#define TALER_MINT_PARSE_AMOUNT(field,amount) { field, amount, sizeof(*amount), 0, JNAV_RET_AMOUNT, 0 } + +/**   * Generate line in parser specification indicating the end of the spec.   */  #define TALER_MINT_PARSE_END { NULL, NULL, 0, 0, JNAV_FIELD, 0 } diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index a5d609ed..5fc8fd5b 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -24,11 +24,10 @@  #include <gnunet/gnunet_util_lib.h>  #include <jansson.h>  #include <microhttpd.h> -#include "mint_db.h" +#include "taler_mintdb_plugin.h"  #include "taler_signatures.h"  #include "taler_util.h"  #include "taler-mint-httpd_parsing.h" -#include "taler-mint-httpd_keys.h"  #include "taler-mint-httpd_mhd.h"  #include "taler-mint-httpd_refresh.h"  #include "taler-mint-httpd_responses.h" @@ -41,15 +40,13 @@   * and then hand things of to execute the melt operation.   *   * @param connection the MHD connection to handle - * @param refresh_session_pub public key of the melt operation   * @param num_new_denoms number of coins to be created, size of y-dimension of @commit_link array   * @param denom_pubs array of @a num_new_denoms keys   * @param coin_count number of coins to be melted, size of y-dimension of @commit_coin array   * @param coin_public_infos array with @a coin_count entries about the coins   * @param coin_melt_details array with @a coin_count entries with melting details - * @param commit_hash hash over the data that the client commits to + * @param session_hash hash over the data that the client commits to   * @param commit_client_sig signature of the client over this commitment - * @param kappa size of x-dimension of @commit_coin and @commit_link arrays   * @param commit_coin 2d array of coin commitments (what the mint is to sign   *                    once the "/refres/reveal" of cut and choose is done)   * @param commit_link 2d array of coin link commitments (what the mint is @@ -59,101 +56,88 @@   */  static int  handle_refresh_melt_binary (struct MHD_Connection *connection, -                            const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,                              unsigned int num_new_denoms, -                            struct GNUNET_CRYPTO_rsa_PublicKey *const*denom_pubs, +                            const struct TALER_DenominationPublicKey *denom_pubs,                              unsigned int coin_count,                              struct TALER_CoinPublicInfo *coin_public_infos,                              const struct MeltDetails *coin_melt_details, -                            const struct GNUNET_HashCode *commit_hash, -                            const struct GNUNET_CRYPTO_EddsaSignature *commit_client_sig, -                            unsigned int kappa, +                            const struct GNUNET_HashCode *session_hash,                              struct RefreshCommitCoin *const* commit_coin,                              struct RefreshCommitLink *const* commit_link)  {    unsigned int i; -  struct GNUNET_HashContext *hash_context; -  struct GNUNET_HashCode melt_hash; -  struct RefreshMeltSessionSignature body; -  char *buf; -  size_t buf_size;    struct MintKeyState *key_state;    struct TALER_MINT_DenomKeyIssue *dki;    struct TALER_Amount cost;    struct TALER_Amount total_cost;    struct TALER_Amount melt; +  struct TALER_Amount value; +  struct TALER_Amount fee_withdraw; +  struct TALER_Amount fee_melt;    struct TALER_Amount total_melt; -  /* check that signature from the session public key is ok */ -  hash_context = GNUNET_CRYPTO_hash_context_start (); -  /* FIXME: also hash session public key here!? */ -  for (i = 0; i < num_new_denoms; i++) -  { -    buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs[i], -                                                    &buf); -    GNUNET_CRYPTO_hash_context_read (hash_context, -                                     buf, -                                     buf_size); -    GNUNET_free (buf); -  } -  for (i = 0; i < coin_count; i++) -    GNUNET_CRYPTO_hash_context_read (hash_context, -                                     &coin_public_infos[i].coin_pub, -                                     sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); -  GNUNET_CRYPTO_hash_context_finish (hash_context, -                                     &melt_hash); -  // FIXME: what about the `commit_hash`? - -  body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_SESSION); -  body.purpose.size = htonl (sizeof (struct RefreshMeltSessionSignature)); -  body.melt_hash = melt_hash; -  body.amount = TALER_amount_hton (coin_melt_details->melt_amount); - -  if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_REFRESH_MELT_SESSION, -                                               &body.purpose, -                                               commit_client_sig, -                                               refresh_session_pub)) -  { -    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -                "signature invalid (did not verify)\n"); -    return TALER_MINT_reply_json_pack (connection, -                                       MHD_HTTP_UNAUTHORIZED, -                                       "{s:s}", -                                       "error", -                                       "invalid signature (verification)"); -  } - -  // FIXME: badness, use proper way to set to zero... +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_get_zero (MINT_CURRENCY, +                                        &total_cost));    key_state = TALER_MINT_key_state_acquire (); -  memset (&total_cost, 0, sizeof (struct TALER_Amount));    for (i=0;i<num_new_denoms;i++)    {      dki = &TALER_MINT_get_denom_key (key_state, -                                     denom_pubs[i])->issue; -    cost = TALER_amount_add (TALER_amount_ntoh (dki->value), -                             TALER_amount_ntoh (dki->fee_withdraw)); -    // FIXME: #3637 -    total_cost = TALER_amount_add (cost, -                                   total_cost); +                                     &denom_pubs[i])->issue; +    TALER_amount_ntoh (&value, +                       &dki->value); +    TALER_amount_ntoh (&fee_withdraw, +                       &dki->fee_withdraw); +    if ( (GNUNET_OK != +          TALER_amount_add (&cost, +                            &value, +                            &fee_withdraw)) || +         (GNUNET_OK != +          TALER_amount_add (&total_cost, +                            &cost, +                            &total_cost)) ) +    { +      TALER_MINT_key_state_release (key_state); +      return TALER_MINT_reply_internal_error (connection, +                                              "cost calculation failure"); +    }    } -  // FIXME: badness, use proper way to set to zero... -  memset (&total_melt, 0, sizeof (struct TALER_Amount)); +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_get_zero (MINT_CURRENCY, +                                        &total_melt));    for (i=0;i<coin_count;i++)    { -    memset (&melt, 0, sizeof (struct TALER_Amount)); -    // FIXME: reduce coin value by melting fee! -    // melt = coin_values[i]; // FIXME: #3636! - -    // FIXME: #3637 -    total_melt = TALER_amount_add (melt, -                                   total_melt); +    /* calculate contribution of the i-th melt by subtracting +       the fee; add the rest to the total_melt value */ +    dki = &TALER_MINT_get_denom_key (key_state, +                                     &coin_public_infos[i].denom_pub)->issue; +    TALER_amount_ntoh (&fee_melt, +                       &dki->fee_refresh); +    if (GNUNET_OK != +        TALER_amount_subtract (&melt, +                               &coin_melt_details->melt_amount_with_fee, +                               &fee_melt)) +    { +      TALER_MINT_key_state_release (key_state); +      return TALER_MINT_reply_external_error (connection, +                                              "Melt contribution below melting fee"); +    } +    if (GNUNET_OK != +        TALER_amount_add (&total_melt, +                          &melt, +                          &total_melt)) +    { +      TALER_MINT_key_state_release (key_state); +      return TALER_MINT_reply_internal_error (connection, +                                              "balance calculation failure"); +    }    }    TALER_MINT_key_state_release (key_state);    if (0 != -      TALER_amount_cmp (total_cost, -                        total_melt) ) +      TALER_amount_cmp (&total_cost, +                        &total_melt))    {      /* We require total value of coins being melted and         total value of coins being generated to match! */ @@ -163,15 +147,12 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,                                         "error", "value mismatch");    }    return TALER_MINT_db_execute_refresh_melt (connection, -                                             &melt_hash, -                                             refresh_session_pub, -                                             commit_client_sig, +                                             session_hash,                                               num_new_denoms,                                               denom_pubs,                                               coin_count,                                               coin_public_infos,                                               coin_melt_details, -                                             kappa,                                               commit_coin,                                               commit_link);  } @@ -195,28 +176,24 @@ get_coin_public_info (struct MHD_Connection *connection,                        struct MeltDetails *r_melt_detail)  {    int ret; -  struct GNUNET_CRYPTO_EcdsaSignature melt_sig; -  struct GNUNET_CRYPTO_rsa_Signature *sig; -  struct GNUNET_CRYPTO_rsa_PublicKey *pk; +  struct TALER_CoinSpendSignature melt_sig; +  struct TALER_DenominationSignature sig; +  struct TALER_DenominationPublicKey pk;    struct TALER_Amount amount;    struct GNUNET_MINT_ParseFieldSpec spec[] = {      TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), -    TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig), -    TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk), +    TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig.rsa_signature), +    TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk.rsa_public_key),      TALER_MINT_PARSE_FIXED ("confirm_sig", &melt_sig), -    /* FIXME: #3636! */ +    TALER_MINT_PARSE_AMOUNT ("value_with_fee", &amount),      TALER_MINT_PARSE_END    }; -  memset (&amount, 0, sizeof (amount)); // FIXME: #3636!    ret = TALER_MINT_parse_json_data (connection,                                      coin_info,                                      spec);    if (GNUNET_OK != ret)      return ret; -  /* FIXME: include amount of coin value to be melted here (#3636!) and -    in what we return!? */ -    /* check mint signature on the coin */    r_public_info->denom_sig = sig;    r_public_info->denom_pub = pk; @@ -224,8 +201,8 @@ get_coin_public_info (struct MHD_Connection *connection,        TALER_test_coin_valid (r_public_info))    {      TALER_MINT_release_parsed_data (spec); -    r_public_info->denom_sig = NULL; -    r_public_info->denom_pub = NULL; +    r_public_info->denom_sig.rsa_signature = NULL; +    r_public_info->denom_pub.rsa_public_key = NULL;      return (MHD_YES ==              TALER_MINT_reply_json_pack (connection,                                          MHD_HTTP_NOT_FOUND, @@ -234,7 +211,7 @@ get_coin_public_info (struct MHD_Connection *connection,        ? GNUNET_NO : GNUNET_SYSERR;    }    r_melt_detail->melt_sig = melt_sig; -  r_melt_detail->melt_amount = amount; +  r_melt_detail->melt_amount_with_fee = amount;    TALER_MINT_release_parsed_data (spec);    return GNUNET_OK;  } @@ -248,7 +225,7 @@ get_coin_public_info (struct MHD_Connection *connection,   * be done before the transaction starts.   *   * @param connection the connection to send error responses to - * @param melt_hash hash over refresh session the coin is melted into + * @param session_hash hash over refresh session the coin is melted into   * @param r_public_info the coin's public information   * @param r_melt_detail details about the coin's melting permission (if valid)   * @return #GNUNET_YES if coin public info in JSON was valid @@ -257,26 +234,26 @@ get_coin_public_info (struct MHD_Connection *connection,   */  static int  verify_coin_public_info (struct MHD_Connection *connection, -                         const struct GNUNET_HashCode *melt_hash, +                         const struct GNUNET_HashCode *session_hash,                           const struct TALER_CoinPublicInfo *r_public_info,                           const struct MeltDetails *r_melt_detail)  {    struct RefreshMeltCoinSignature body;    struct MintKeyState *key_state;    struct TALER_MINT_DenomKeyIssuePriv *dki; +  struct TALER_Amount fee_refresh; -  /* FIXME: include amount of coin value to be melted here (#3636!) and -    in what we return!? */    body.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature));    body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN); -  body.melt_hash = *melt_hash; -  body.amount = TALER_amount_hton (r_melt_detail->melt_amount); +  body.session_hash = *session_hash; +  TALER_amount_hton (&body.amount_with_fee, +                     &r_melt_detail->melt_amount_with_fee);    body.coin_pub = r_public_info->coin_pub;    if (GNUNET_OK !=        GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_COIN,                                    &body.purpose, -                                  &r_melt_detail->melt_sig, -                                  &r_public_info->coin_pub)) +                                  &r_melt_detail->melt_sig.ecdsa_signature, +                                  &r_public_info->coin_pub.ecdsa_pub))    {      if (MHD_YES !=          TALER_MINT_reply_json_pack (connection, @@ -288,9 +265,7 @@ verify_coin_public_info (struct MHD_Connection *connection,    }    key_state = TALER_MINT_key_state_acquire ();    dki = TALER_MINT_get_denom_key (key_state, -                                  r_public_info->denom_pub); -  /* FIXME: need to check if denomination key is still -     valid for issuing! (#3634) */ +                                  &r_public_info->denom_pub);    if (NULL == dki)    {      TALER_MINT_key_state_release (key_state); @@ -298,6 +273,20 @@ verify_coin_public_info (struct MHD_Connection *connection,      return TALER_MINT_reply_arg_invalid (connection,                                           "denom_pub");    } +  /* FIXME: need to check if denomination key is still +     valid for issuing! (#3634) */ +  TALER_amount_ntoh (&fee_refresh, +                     &dki->issue.fee_refresh); +  if (TALER_amount_cmp (&fee_refresh, +                        &r_melt_detail->melt_amount_with_fee) < 0) +  { +    TALER_MINT_key_state_release (key_state); +    return (MHD_YES == +            TALER_MINT_reply_external_error (connection, +                                             "melt amount smaller than melting fee")) +      ? GNUNET_NO : GNUNET_SYSERR; +  } +    TALER_MINT_key_state_release (key_state);    return GNUNET_OK;  } @@ -363,28 +352,24 @@ free_commit_links (struct RefreshCommitLink **commit_link,   * #handle_refresh_melt_binary().   *   * @param connection the MHD connection to handle - * @param refresh_session_pub public key of the melt operation   * @param new_denoms array of denomination keys   * @param melt_coins array of coins to melt   * @param melt_sig_json signature affirming the melt operation   * @param commit_signature signature over the commit - * @param kappa security parameter for cut and choose   * @param num_oldcoins number of coins that are being melted - * @param transfer_pubs @a kappa-dimensional array of @a num_oldcoins transfer keys - * @param secret_encs @a kappa-dimensional array of @a num_oldcoins secrets + * @param transfer_pubs #KAPPA-dimensional array of @a num_oldcoins transfer keys + * @param secret_encs #KAPPA-dimensional array of @a num_oldcoins secrets   * @param num_newcoins number of coins that the refresh will generate - * @param coin_envs @a kappa-dimensional array of @a num_newcoins envelopes to sign - * @param link_encs @a kappa-dimensional array of @a num_newcoins encrypted links + * @param coin_envs #KAPPA-dimensional array of @a num_newcoins envelopes to sign + * @param link_encs #KAPPA-dimensional array of @a num_newcoins encrypted links   * @return MHD result code   */  static int  handle_refresh_melt_json (struct MHD_Connection *connection, -                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,                            const json_t *new_denoms,                            const json_t *melt_coins,                            const json_t *melt_sig_json,                            const json_t *commit_signature, -                          unsigned int kappa,                            unsigned int num_oldcoins,                            const json_t *transfer_pubs,                            const json_t *secret_encs, @@ -396,37 +381,50 @@ handle_refresh_melt_json (struct MHD_Connection *connection,    int res;    unsigned int i;    unsigned int j; -  struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; +  struct TALER_DenominationPublicKey *denom_pubs;    unsigned int num_new_denoms;    struct TALER_CoinPublicInfo *coin_public_infos;    struct MeltDetails *coin_melt_details;    unsigned int coin_count; -  struct GNUNET_HashCode commit_hash; +  struct GNUNET_HashCode session_hash;    struct GNUNET_HashContext *hash_context; -  struct RefreshCommitCoin *commit_coin[kappa]; -  struct RefreshCommitLink *commit_link[kappa]; -  const struct GNUNET_CRYPTO_EddsaSignature commit_client_sig; +  struct RefreshCommitCoin *commit_coin[KAPPA]; +  struct RefreshCommitLink *commit_link[KAPPA]; +  /* For the signature check, we hash most of the inputs together +     (except for the signatures on the coins). */ +  hash_context = GNUNET_CRYPTO_hash_context_start ();    num_new_denoms = json_array_size (new_denoms);    denom_pubs = GNUNET_malloc (num_new_denoms * -                              sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); +                              sizeof (struct TALER_DenominationPublicKey));    for (i=0;i<num_new_denoms;i++)    { -    res = GNUNET_MINT_parse_navigate_json (connection, new_denoms, +    char *buf; +    size_t buf_size; + +    res = GNUNET_MINT_parse_navigate_json (connection, +                                           new_denoms,                                             JNAV_INDEX, (int) i, -                                           JNAV_RET_RSA_PUBLIC_KEY, &denom_pubs[i]); +                                           JNAV_RET_RSA_PUBLIC_KEY, +                                           &denom_pubs[i].rsa_public_key);      if (GNUNET_OK != res)      {        for (j=0;j<i;j++) -        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);        GNUNET_free (denom_pubs);        return res;      } -  } +    buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs[i].rsa_public_key, +                                                    &buf); +    GNUNET_CRYPTO_hash_context_read (hash_context, +                                     buf, +                                     buf_size); +    GNUNET_free (buf); + }    coin_count = json_array_size (melt_coins);    /* FIXME: make 'struct TALER_CoinPublicInfo' part of `struct MeltDetails` -     and combine these two arrays/arguments! */ +     and combine these two arrays/arguments! (#3726) */    coin_public_infos = GNUNET_malloc (coin_count *                                       sizeof (struct TALER_CoinPublicInfo));    coin_melt_details = GNUNET_malloc (coin_count * @@ -434,6 +432,8 @@ handle_refresh_melt_json (struct MHD_Connection *connection,    for (i=0;i<coin_count;i++)    {      /* decode JSON data on coin to melt */ +    struct TALER_AmountNBO melt_amount; +      res = get_coin_public_info (connection,                                  json_array_get (melt_coins, i),                                  &coin_public_infos[i], @@ -442,25 +442,54 @@ handle_refresh_melt_json (struct MHD_Connection *connection,      {        for (j=0;j<i;j++)        { -        GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub); -        GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig); +        GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub.rsa_public_key); +        GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig.rsa_signature);        }        GNUNET_free (coin_public_infos);        for (j=0;j<num_new_denoms;j++) -        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);        GNUNET_free (coin_melt_details);        GNUNET_free (denom_pubs);        return (GNUNET_NO == res) ? MHD_YES : MHD_NO;      } -  } +    /* Check that the client does not try to melt the same coin twice +       into the same session! */ +    for (j=0;j<i;j++) +    { +      if (0 == memcmp (&coin_public_infos[i].coin_pub, +                       &coin_public_infos[j].coin_pub, +                       sizeof (struct TALER_CoinSpendPublicKey))) +      { +        for (j=0;j<i;j++) +        { +          GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub.rsa_public_key); +          GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig.rsa_signature); +        } +        GNUNET_free (coin_public_infos); +        for (j=0;j<num_new_denoms;j++) +          GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key); +        GNUNET_free (coin_melt_details); +        GNUNET_free (denom_pubs); +        return TALER_MINT_reply_external_error (connection, +                                                "melting same coin twice in same session is not allowed"); +      } +    } +    TALER_amount_hton (&melt_amount, +                       &coin_melt_details[i].melt_amount_with_fee); +    GNUNET_CRYPTO_hash_context_read (hash_context, +                                     &coin_public_infos[i].coin_pub, +                                     sizeof (struct TALER_CoinSpendPublicKey)); +    GNUNET_CRYPTO_hash_context_read (hash_context, +                                     &melt_amount, +                                     sizeof (struct TALER_AmountNBO)); +  }    /* parse JSON arrays into 2d binary arrays and hash everything       together for the signature check */    memset (commit_coin, 0, sizeof (commit_coin));    memset (commit_link, 0, sizeof (commit_link)); -  hash_context = GNUNET_CRYPTO_hash_context_start (); -  for (i = 0; i < kappa; i++) +  for (i = 0; i < KAPPA; i++)    {      commit_coin[i] = GNUNET_malloc (num_newcoins *                                      sizeof (struct RefreshCommitCoin)); @@ -468,24 +497,27 @@ handle_refresh_melt_json (struct MHD_Connection *connection,      {        char *link_enc;        size_t link_enc_size; +      struct RefreshCommitCoin *rcc = &commit_coin[i][j];        res = GNUNET_MINT_parse_navigate_json (connection,                                               coin_evs,                                               JNAV_INDEX, (int) i,                                               JNAV_INDEX, (int) j,                                               JNAV_RET_DATA_VAR, -                                             &commit_coin[i][j].coin_ev, -                                             &commit_coin[i][j].coin_ev_size); +                                             &rcc->coin_ev, +                                             &rcc->coin_ev_size);        if (GNUNET_OK != res)        {          GNUNET_CRYPTO_hash_context_abort (hash_context); -        free_commit_coins (commit_coin, kappa, num_newcoins); +        free_commit_coins (commit_coin, +                           KAPPA, +                           num_newcoins);          return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;        }        GNUNET_CRYPTO_hash_context_read (hash_context, -                                       commit_coin[i][j].coin_ev, -                                       commit_coin[i][j].coin_ev_size); +                                       rcc->coin_ev, +                                       rcc->coin_ev_size);        res = GNUNET_MINT_parse_navigate_json (connection,                                               link_encs,                                               JNAV_INDEX, (int) i, @@ -496,87 +528,83 @@ handle_refresh_melt_json (struct MHD_Connection *connection,        if (GNUNET_OK != res)        {          GNUNET_CRYPTO_hash_context_abort (hash_context); -        free_commit_coins (commit_coin, kappa, num_newcoins); +        free_commit_coins (commit_coin, +                           KAPPA, +                           num_newcoins);          return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;        } -      commit_coin[i][j].refresh_link = TALER_refresh_link_encrypted_decode (link_enc, -                                                                            link_enc_size); - +      rcc->refresh_link +        = TALER_refresh_link_encrypted_decode (link_enc, +                                               link_enc_size);        GNUNET_CRYPTO_hash_context_read (hash_context,                                         link_enc,                                         link_enc_size);      }    } -  for (i = 0; i < kappa; i++) +  for (i = 0; i < KAPPA; i++)    {      commit_link[i] = GNUNET_malloc (num_oldcoins *                                      sizeof (struct RefreshCommitLink));      for (j = 0; j < num_oldcoins; j++)      { +      struct RefreshCommitLink *rcl = &commit_link[i][j]; +        res = GNUNET_MINT_parse_navigate_json (connection,                                               transfer_pubs,                                               JNAV_INDEX, (int) i,                                               JNAV_INDEX, (int) j,                                               JNAV_RET_DATA, -                                             &commit_link[i][j].transfer_pub, -                                             sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); +                                             &rcl->transfer_pub, +                                             sizeof (struct TALER_TransferPublicKey));        if (GNUNET_OK != res)        {          GNUNET_break (GNUNET_SYSERR != res);          GNUNET_CRYPTO_hash_context_abort (hash_context); -        free_commit_coins (commit_coin, kappa, num_newcoins); -        free_commit_links (commit_link, kappa, num_oldcoins); +        free_commit_coins (commit_coin, +                           KAPPA, +                           num_newcoins); +        free_commit_links (commit_link, +                           KAPPA, +                           num_oldcoins);          return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;        } - -      GNUNET_CRYPTO_hash_context_read (hash_context, -                                       &commit_link[i][j].transfer_pub, -                                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); -        res = GNUNET_MINT_parse_navigate_json (connection,                                               secret_encs,                                               JNAV_INDEX, (int) i,                                               JNAV_INDEX, (int) j,                                               JNAV_RET_DATA, -                                             &commit_link[i][j].shared_secret_enc, +                                             &rcl->shared_secret_enc,                                               sizeof (struct GNUNET_HashCode));        if (GNUNET_OK != res)        {          GNUNET_break (GNUNET_SYSERR != res);          GNUNET_CRYPTO_hash_context_abort (hash_context); -        free_commit_coins (commit_coin, kappa, num_newcoins); -        free_commit_links (commit_link, kappa, num_oldcoins); +        free_commit_coins (commit_coin, +                           KAPPA, +                           num_newcoins); +        free_commit_links (commit_link, +                           KAPPA, +                           num_oldcoins);          return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;        }        GNUNET_CRYPTO_hash_context_read (hash_context, -                                       &commit_link[i][j].shared_secret_enc, -                                       sizeof (struct GNUNET_HashCode)); +                                       rcl, +                                       sizeof (struct RefreshCommitLink));      } -  } -  GNUNET_CRYPTO_hash_context_finish (hash_context, &commit_hash); - - -  res = GNUNET_MINT_parse_navigate_json (connection, -                                         commit_signature, -                                         JNAV_FIELD, -                                         "sig", -                                         JNAV_RET_DATA, -                                         &commit_client_sig, -                                         sizeof (struct GNUNET_CRYPTO_EddsaSignature)); - -  if (GNUNET_OK != res) -    return (GNUNET_NO == res) ? MHD_YES : MHD_NO; +  } +  GNUNET_CRYPTO_hash_context_finish (hash_context, +                                     &session_hash);    for (i=0;i<coin_count;i++)    { -    /* verify signatures ons coin to melt */ +    /* verify signatures on coins to melt */      res = verify_coin_public_info (connection, -                                   &commit_hash, +                                   &session_hash,                                     &coin_public_infos[i],                                     &coin_melt_details[i]);      if (GNUNET_OK != res) @@ -588,30 +616,29 @@ handle_refresh_melt_json (struct MHD_Connection *connection,    /* execute commit */    res = handle_refresh_melt_binary (connection, -                                    refresh_session_pub,                                      num_new_denoms,                                      denom_pubs,                                      coin_count,                                      coin_public_infos,                                      coin_melt_details, -                                    &commit_hash, -                                    &commit_client_sig, -                                    kappa, +                                    &session_hash,                                      commit_coin,                                      commit_link);   cleanup: -  free_commit_coins (commit_coin, kappa, num_newcoins); -  free_commit_links (commit_link, kappa, num_oldcoins); +  free_commit_coins (commit_coin, +                     KAPPA, +                     num_newcoins); +  free_commit_links (commit_link, +                     KAPPA, +                     num_oldcoins);    for (j=0;j<coin_count;j++)    { -    GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub); -    GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig); +    GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub.rsa_public_key); +    GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig.rsa_signature);    }    GNUNET_free (coin_public_infos);    for (j=0;j<num_new_denoms;j++) -  { -    GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); -  } +    GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);    GNUNET_free (coin_melt_details);    GNUNET_free (denom_pubs);    return res; @@ -647,14 +674,11 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,    json_t *transfer_pubs;    json_t *secret_encs;    json_t *commit_sig_json; -  unsigned int kappa;    unsigned int num_oldcoins;    unsigned int num_newcoins;    json_t *coin_detail; -  struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;    int res;    struct GNUNET_MINT_ParseFieldSpec spec[] = { -    TALER_MINT_PARSE_FIXED ("session_pub", &refresh_session_pub),      TALER_MINT_PARSE_ARRAY ("new_denoms", &new_denoms),      TALER_MINT_PARSE_ARRAY ("melt_coins", &melt_coins),      TALER_MINT_PARSE_ARRAY ("melt_signature", &melt_sig_json), @@ -684,15 +708,14 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;    /* Determine dimensionality of the request (kappa, #old and #new coins) */ -  kappa = json_array_size (coin_evs); -  if ( (3 > kappa) || (kappa > 32) ) +  if (KAPPA != json_array_size (coin_evs))    {      GNUNET_break_op (0);      TALER_MINT_release_parsed_data (spec);      return TALER_MINT_reply_arg_invalid (connection,                                           "coin_evs");    } -  if (json_array_size (transfer_pubs) != kappa) +  if (KAPPA != json_array_size (transfer_pubs))    {      GNUNET_break_op (0);      TALER_MINT_release_parsed_data (spec); @@ -722,12 +745,10 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,    num_oldcoins = json_array_size (coin_detail);    res = handle_refresh_melt_json (connection, -                                  &refresh_session_pub,                                    new_denoms,                                    melt_coins,                                    melt_sig_json,                                    commit_sig_json, -                                  kappa,                                    num_oldcoins,                                    transfer_pubs,                                    secret_encs, @@ -748,29 +769,27 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,   * coins.   *   * @param connection the MHD connection to handle - * @param refresh_session_pub public key of the session - * @param kappa length of the 1st dimension of @a transfer_privs array PLUS ONE + * @param session_hash hash identifying the melting session   * @param num_oldcoins length of the 2nd dimension of @a transfer_privs array   * @param tp_json private transfer keys in JSON format   * @return MHD result code    */  static int  handle_refresh_reveal_json (struct MHD_Connection *connection, -                            const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                            unsigned int kappa, +                            const struct GNUNET_HashCode *session_hash,                              unsigned int num_oldcoins,                              const json_t *tp_json)  { -  struct GNUNET_CRYPTO_EcdsaPrivateKey *transfer_privs[kappa - 1]; +  struct TALER_TransferPrivateKey *transfer_privs[KAPPA - 1];    unsigned int i;    unsigned int j;    int res; -  for (i = 0; i < kappa - 1; i++) +  for (i = 0; i < KAPPA - 1; i++)      transfer_privs[i] = GNUNET_malloc (num_oldcoins * -                                       sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)); +                                       sizeof (struct TALER_TransferPrivateKey));    res = GNUNET_OK; -  for (i = 0; i < kappa - 1; i++) +  for (i = 0; i < KAPPA - 1; i++)    {      if (GNUNET_OK != res)        break; @@ -784,18 +803,17 @@ handle_refresh_reveal_json (struct MHD_Connection *connection,                                               JNAV_INDEX, (int) j,                                               JNAV_RET_DATA,                                               &transfer_privs[i][j], -                                             sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); +                                             sizeof (struct TALER_TransferPrivateKey));      }    }    if (GNUNET_OK != res)      res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;    else      res = TALER_MINT_db_execute_refresh_reveal (connection, -                                                refresh_session_pub, -                                                kappa, +                                                session_hash,                                                  num_oldcoins,                                                  transfer_privs); -  for (i = 0; i < kappa - 1; i++) +  for (i = 0; i < KAPPA - 1; i++)      GNUNET_free (transfer_privs[i]);    return res;  } @@ -824,15 +842,14 @@ TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,                                     const char *upload_data,                                     size_t *upload_data_size)  { -  struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub; +  struct GNUNET_HashCode session_hash;    int res; -  unsigned int kappa;    unsigned int num_oldcoins;    json_t *reveal_detail;    json_t *root;    json_t *transfer_privs;    struct GNUNET_MINT_ParseFieldSpec spec[] = { -    TALER_MINT_PARSE_FIXED ("session_pub", &refresh_session_pub), +    TALER_MINT_PARSE_FIXED ("session_hash", &session_hash),      TALER_MINT_PARSE_ARRAY ("transfer_privs", &transfer_privs),      TALER_MINT_PARSE_END    }; @@ -855,15 +872,13 @@ TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;    /* Determine dimensionality of the request (kappa and #old coins) */ -  kappa = json_array_size (transfer_privs) + 1; -  if ( (2 > kappa) || (kappa > 31) ) +  if (KAPPA != json_array_size (transfer_privs) + 1)    {      TALER_MINT_release_parsed_data (spec);      return TALER_MINT_reply_arg_invalid (connection,                                           "transfer_privs");    }    /* Note we do +1 as 1 row (cut-and-choose!) is missing! */ -  kappa++;    res = GNUNET_MINT_parse_navigate_json (connection,                                           transfer_privs,                                           JNAV_INDEX, 0, @@ -877,8 +892,7 @@ TALER_MINT_handler_refresh_reveal (struct RequestHandler *rh,    }    num_oldcoins = json_array_size (reveal_detail);    res = handle_refresh_reveal_json (connection, -                                    &refresh_session_pub, -                                    kappa, +                                    &session_hash,                                      num_oldcoins,                                      transfer_privs);    TALER_MINT_release_parsed_data (spec); @@ -903,13 +917,13 @@ TALER_MINT_handler_refresh_link (struct RequestHandler *rh,                                   const char *upload_data,                                   size_t *upload_data_size)  { -  struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; +  struct TALER_CoinSpendPublicKey coin_pub;    int res;    res = TALER_MINT_mhd_request_arg_data (connection,                                           "coin_pub",                                           &coin_pub, -                                         sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); +                                         sizeof (struct TALER_CoinSpendPublicKey));    if (GNUNET_SYSERR == res)      return MHD_NO;    if (GNUNET_OK != res) diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 9cc92cd6..667ce792 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -282,15 +282,15 @@ TALER_MINT_reply_invalid_json (struct MHD_Connection *connection)   */  int  TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, -                                  const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, +                                  const struct TALER_CoinSpendPublicKey *coin_pub,                                    const struct GNUNET_HashCode *h_wire,                                    const struct GNUNET_HashCode *h_contract,                                    uint64_t transaction_id, -                                  const struct GNUNET_CRYPTO_EddsaPublicKey *merchant, +                                  const struct TALER_MerchantPublicKey *merchant,                                    const struct TALER_Amount *amount)  {    struct TALER_DepositConfirmation dc; -  struct GNUNET_CRYPTO_EddsaSignature sig; +  struct TALER_MintSignature sig;    json_t *sig_json;    int ret; @@ -299,12 +299,14 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection,    dc.h_contract = *h_contract;    dc.h_wire = *h_wire;    dc.transaction_id = GNUNET_htonll (transaction_id); -  dc.amount = TALER_amount_hton (*amount); +  TALER_amount_hton (&dc.amount_with_fee, +                     amount);    dc.coin_pub = *coin_pub;    dc.merchant = *merchant;    TALER_MINT_keys_sign (&dc.purpose,                          &sig); -  sig_json = TALER_JSON_from_eddsa_sig (&dc.purpose, &sig); +  sig_json = TALER_JSON_from_eddsa_sig (&dc.purpose, +                                        &sig.eddsa_signature);    ret = TALER_MINT_reply_json_pack (connection,                                      MHD_HTTP_OK,                                      "{s:s, s:o}", @@ -341,16 +343,17 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl)          const struct Deposit *deposit = pos->details.deposit;          type = "deposit"; -        value = deposit->amount; +        value = deposit->amount_with_fee;          dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_DEPOSIT);          dr.purpose.size = htonl (sizeof (struct TALER_DepositRequest));          dr.h_contract = deposit->h_contract;          dr.h_wire = deposit->h_wire;          dr.transaction_id = GNUNET_htonll (deposit->transaction_id); -        dr.amount = TALER_amount_hton (deposit->amount); +        TALER_amount_hton (&dr.amount_with_fee, +                           &deposit->amount_with_fee);          dr.coin_pub = deposit->coin.coin_pub;          transaction = TALER_JSON_from_ecdsa_sig (&dr.purpose, -                                                 &deposit->csig); +                                                 &deposit->csig.ecdsa_signature);          break;        }      case TALER_MINT_DB_TT_REFRESH_MELT: @@ -359,14 +362,15 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl)          const struct RefreshMelt *melt = pos->details.melt;          type = "melt"; -        value = melt->amount; +        value = melt->amount_with_fee;          ms.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN);          ms.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature)); -        ms.melt_hash = melt->melt_hash; -        ms.amount = TALER_amount_hton (melt->amount); +        ms.session_hash = melt->session_hash; +        TALER_amount_hton (&ms.amount_with_fee, +                           &melt->amount_with_fee);          ms.coin_pub = melt->coin.coin_pub;          transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose, -                                                 &melt->coin_sig); +                                                 &melt->coin_sig.ecdsa_signature);        }        break;      case TALER_MINT_DB_TT_LOCK: @@ -383,7 +387,7 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl)      json_array_append_new (history,                             json_pack ("{s:s, s:o}",                                        "type", type, -                                      "amount", TALER_JSON_from_amount (value), +                                      "amount", TALER_JSON_from_amount (&value),                                        "signature", transaction));    }    return history; @@ -420,7 +424,7 @@ TALER_MINT_reply_deposit_insufficient_funds (struct MHD_Connection *connection,   *   * @param rh reserve history to JSON-ify   * @param balance[OUT] set to current reserve balance - * @return json representation of the @a rh + * @return json representation of the @a rh, NULL on error   */  static json_t *  compile_reserve_history (const struct ReserveHistory *rh, @@ -447,14 +451,20 @@ compile_reserve_history (const struct ReserveHistory *rh,        if (0 == ret)          deposit_total = pos->details.bank->amount;        else -        deposit_total = TALER_amount_add (deposit_total, -                                          pos->details.bank->amount); +        if (GNUNET_OK != +            TALER_amount_add (&deposit_total, +                              &deposit_total, +                              &pos->details.bank->amount)) +        { +          json_decref (json_history); +          return NULL; +        }        ret = 1;        json_array_append_new (json_history,                               json_pack ("{s:s, s:o, s:o}",                                          "type", "DEPOSIT",                                          "wire", pos->details.bank->wire, -                                        "amount", TALER_JSON_from_amount (pos->details.bank->amount))); +                                        "amount", TALER_JSON_from_amount (&pos->details.bank->amount)));        break;      case TALER_MINT_DB_RO_WITHDRAW_COIN:        break; @@ -472,37 +482,52 @@ compile_reserve_history (const struct ReserveHistory *rh,      case TALER_MINT_DB_RO_WITHDRAW_COIN:        dki = TALER_MINT_get_denom_key (key_state, -                                      pos->details.withdraw->denom_pub); -      value = TALER_amount_ntoh (dki->issue.value); +                                      &pos->details.withdraw->denom_pub); +      TALER_amount_ntoh (&value, +                         &dki->issue.value);        if (0 == ret)          withdraw_total = value;        else -        withdraw_total = TALER_amount_add (withdraw_total, -                                           value); +        if (GNUNET_OK != +            TALER_amount_add (&withdraw_total, +                              &withdraw_total, +                              &value)) +        { +          TALER_MINT_key_state_release (key_state); +          json_decref (json_history); +          return NULL; +        }        ret = 1;        wr.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW);        wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest));        wr.reserve_pub = pos->details.withdraw->reserve_pub; -      GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub, +      GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key,                                           &wr.h_denomination_pub);        wr.h_coin_envelope = pos->details.withdraw->h_coin_envelope;        transaction = TALER_JSON_from_eddsa_sig (&wr.purpose, -                                               &pos->details.withdraw->reserve_sig); +                                               &pos->details.withdraw->reserve_sig.eddsa_signature);        json_array_append_new (json_history,                               json_pack ("{s:s, s:o, s:o}",                                          "type", "WITHDRAW",                                          "signature", transaction, -                                        "amount", TALER_JSON_from_amount (value))); +                                        "amount", TALER_JSON_from_amount (&value)));        break;      }    }    TALER_MINT_key_state_release (key_state); -  *balance = TALER_amount_subtract (deposit_total, -                                    withdraw_total); +  if (GNUNET_SYSERR == +      TALER_amount_subtract (balance, +                             &deposit_total, +                             &withdraw_total)) +  { +    GNUNET_break (0); +    json_decref (json_history); +    return NULL; +  }    return json_history;  } @@ -525,7 +550,10 @@ TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection,    json_history = compile_reserve_history (rh,                                            &balance); -  json_balance = TALER_JSON_from_amount (balance); +  if (NULL == json_history) +    return TALER_MINT_reply_internal_error (connection, +                                            "balance calculation failure"); +  json_balance = TALER_JSON_from_amount (&balance);    ret = TALER_MINT_reply_json_pack (connection,                                      MHD_HTTP_OK,                                      "{s:o, s:o}", @@ -557,7 +585,10 @@ TALER_MINT_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connec    json_history = compile_reserve_history (rh,                                            &balance); -  json_balance = TALER_JSON_from_amount (balance); +  if (NULL == json_history) +    return TALER_MINT_reply_internal_error (connection, +                                            "balance calculation failure"); +  json_balance = TALER_JSON_from_amount (&balance);    ret = TALER_MINT_reply_json_pack (connection,                                      MHD_HTTP_PAYMENT_REQUIRED,                                      "{s:s, s:o, s:o}", @@ -582,15 +613,9 @@ TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection,                                          const struct CollectableBlindcoin *collectable)  {    json_t *sig_json; -  size_t sig_buf_size; -  char *sig_buf;    int ret; -  sig_buf_size = GNUNET_CRYPTO_rsa_signature_encode (collectable->sig, -                                                     &sig_buf); -  sig_json = TALER_JSON_from_data (sig_buf, -                                   sig_buf_size); -  GNUNET_free (sig_buf); +  sig_json = TALER_JSON_from_rsa_signature (collectable->sig.rsa_signature);    ret = TALER_MINT_reply_json_pack (connection,                                      MHD_HTTP_OK,                                      "{s:o}", @@ -617,7 +642,7 @@ TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection,   */  int  TALER_MINT_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection, -                                                  const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, +                                                  const struct TALER_CoinSpendPublicKey *coin_pub,                                                    struct TALER_Amount coin_value,                                                    struct TALER_MINT_DB_TransactionList *tl,                                                    struct TALER_Amount requested, @@ -631,10 +656,10 @@ TALER_MINT_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connect                                       "{s:s, s:o, s:o, s:o, s:o, s:o}",                                       "error", "insufficient funds",                                       "coin-pub", TALER_JSON_from_data (coin_pub, -                                                                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)), -                                     "original-value", TALER_JSON_from_amount (coin_value), -                                     "residual-value", TALER_JSON_from_amount (residual), -                                     "requested-value", TALER_JSON_from_amount (requested), +                                                                       sizeof (struct TALER_CoinSpendPublicKey)), +                                     "original-value", TALER_JSON_from_amount (&coin_value), +                                     "residual-value", TALER_JSON_from_amount (&residual), +                                     "requested-value", TALER_JSON_from_amount (&requested),                                       "history", history);  } @@ -653,7 +678,7 @@ TALER_MINT_reply_refresh_melt_success (struct MHD_Connection *connection,                                         uint16_t noreveal_index)  {    struct RefreshMeltResponseSignatureBody body; -  struct GNUNET_CRYPTO_EddsaSignature sig; +  struct TALER_MintSignature sig;    json_t *sig_json;    int ret; @@ -664,7 +689,7 @@ TALER_MINT_reply_refresh_melt_success (struct MHD_Connection *connection,    TALER_MINT_keys_sign (&body.purpose,                          &sig);    sig_json = TALER_JSON_from_eddsa_sig (&body.purpose, -                                        &sig); +                                        &sig.eddsa_signature);    GNUNET_assert (NULL != sig_json);    ret = TALER_MINT_reply_json_pack (connection,                                       MHD_HTTP_OK, @@ -687,27 +712,21 @@ TALER_MINT_reply_refresh_melt_success (struct MHD_Connection *connection,  int  TALER_MINT_reply_refresh_reveal_success (struct MHD_Connection *connection,                                           unsigned int num_newcoins, -                                         struct GNUNET_CRYPTO_rsa_Signature **sigs) +                                         const struct TALER_DenominationSignature *sigs)  {    int newcoin_index;    json_t *root;    json_t *list; -  char *buf; -  size_t buf_size;    int ret;    root = json_object ();    list = json_array (); -  json_object_set_new (root, "ev_sigs", list); +  json_object_set_new (root, +                       "ev_sigs", +                       list);    for (newcoin_index = 0; newcoin_index < num_newcoins; newcoin_index++) -  { -    buf_size = GNUNET_CRYPTO_rsa_signature_encode (sigs[newcoin_index], -                                                   &buf);      json_array_append_new (list, -                           TALER_JSON_from_data (buf, -                                                 buf_size)); -    GNUNET_free (buf); -  } +                           TALER_JSON_from_rsa_signature (sigs[newcoin_index].rsa_signature));    ret = TALER_MINT_reply_json (connection,                                 root,                                 MHD_HTTP_OK); @@ -760,7 +779,7 @@ TALER_MINT_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,   */  int  TALER_MINT_reply_refresh_link_success (struct MHD_Connection *connection, -                                       const struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, +                                       const struct TALER_TransferPublicKey *transfer_pub,                                         const struct TALER_EncryptedLinkSecret *shared_secret_enc,                                         const struct LinkDataList *ldl)  { @@ -773,26 +792,18 @@ TALER_MINT_reply_refresh_link_success (struct MHD_Connection *connection,    for (pos = ldl; NULL != pos; pos = pos->next)    {      json_t *obj; -    char *buf; -    size_t buf_len;      obj = json_object ();      json_object_set_new (obj, "link_enc",                           TALER_JSON_from_data (ldl->link_data_enc->coin_priv_enc, -                                               sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey) + +                                               sizeof (struct TALER_CoinSpendPrivateKey) +                                                 ldl->link_data_enc->blinding_key_enc_size)); -    buf_len = GNUNET_CRYPTO_rsa_public_key_encode (ldl->denom_pub, -                                                   &buf); -    json_object_set_new (obj, "denom_pub", -                         TALER_JSON_from_data (buf, -                                               buf_len)); -    GNUNET_free (buf); -    buf_len = GNUNET_CRYPTO_rsa_signature_encode (ldl->ev_sig, -                                                  &buf); -    json_object_set_new (obj, "ev_sig", -                         TALER_JSON_from_data (buf, -                                               buf_len)); -    GNUNET_free (buf); +    json_object_set_new (obj, +                         "denom_pub", +                         TALER_JSON_from_rsa_public_key (ldl->denom_pub.rsa_public_key)); +    json_object_set_new (obj, +                         "ev_sig", +                         TALER_JSON_from_rsa_signature (ldl->ev_sig.rsa_signature));      json_array_append_new (list, obj);    } @@ -803,7 +814,7 @@ TALER_MINT_reply_refresh_link_success (struct MHD_Connection *connection,    json_object_set_new (root,                         "transfer_pub",                         TALER_JSON_from_data (transfer_pub, -                                             sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))); +                                             sizeof (struct TALER_TransferPublicKey)));    json_object_set_new (root,                         "secret_enc",                         TALER_JSON_from_data (shared_secret_enc, diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index d42aa29b..d7e56350 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -185,11 +185,11 @@ TALER_MINT_reply_invalid_json (struct MHD_Connection *connection);   */  int  TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, -                                  const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, +                                  const struct TALER_CoinSpendPublicKey *coin_pub,                                    const struct GNUNET_HashCode *h_wire,                                    const struct GNUNET_HashCode *h_contract,                                    uint64_t transaction_id, -                                  const struct GNUNET_CRYPTO_EddsaPublicKey *merchant, +                                  const struct TALER_MerchantPublicKey *merchant,                                    const struct TALER_Amount *amount); @@ -276,7 +276,7 @@ TALER_MINT_reply_refresh_melt_success (struct MHD_Connection *connection,   */  int  TALER_MINT_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection, -                                                  const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, +                                                  const struct TALER_CoinSpendPublicKey *coin_pub,                                                    struct TALER_Amount coin_value,                                                    struct TALER_MINT_DB_TransactionList *tl,                                                    struct TALER_Amount requested, @@ -294,7 +294,7 @@ TALER_MINT_reply_refresh_melt_insufficient_funds (struct MHD_Connection *connect  int  TALER_MINT_reply_refresh_reveal_success (struct MHD_Connection *connection,                                           unsigned int num_newcoins, -                                         struct GNUNET_CRYPTO_rsa_Signature **sigs); +                                         const struct TALER_DenominationSignature *sigs);  /** @@ -332,7 +332,7 @@ TALER_MINT_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,   */  int  TALER_MINT_reply_refresh_link_success (struct MHD_Connection *connection, -                                       const struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, +                                       const struct TALER_TransferPublicKey *transfer_pub,                                         const struct TALER_EncryptedLinkSecret *shared_secret_enc,                                         const struct LinkDataList *ldl); diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c index b796af94..7d7ca806 100644 --- a/src/mint/taler-mint-httpd_withdraw.c +++ b/src/mint/taler-mint-httpd_withdraw.c @@ -49,13 +49,13 @@ TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,                                      const char *upload_data,                                      size_t *upload_data_size)  { -  struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; +  struct TALER_ReservePublicKey reserve_pub;    int res;    res = TALER_MINT_mhd_request_arg_data (connection,                                           "reserve_pub",                                           &reserve_pub, -                                         sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); +                                         sizeof (struct TALER_ReservePublicKey));    if (GNUNET_SYSERR == res)      return MHD_NO; /* internal error */    if (GNUNET_NO == res) @@ -90,17 +90,17 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,  {    struct TALER_WithdrawRequest wsrd;    int res; -  struct GNUNET_CRYPTO_rsa_PublicKey *denomination_pub; +  struct TALER_DenominationPublicKey denomination_pub;    char *denomination_pub_data;    size_t denomination_pub_data_size;    char *blinded_msg;    size_t blinded_msg_len; -  struct GNUNET_CRYPTO_EddsaSignature signature; +  struct TALER_ReserveSignature signature;    res = TALER_MINT_mhd_request_arg_data (connection,                                           "reserve_pub",                                           &wsrd.reserve_pub, -                                         sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); +                                         sizeof (struct TALER_ReservePublicKey));    if (GNUNET_SYSERR == res)      return MHD_NO; /* internal error */    if (GNUNET_NO == res) @@ -108,7 +108,7 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,    res = TALER_MINT_mhd_request_arg_data (connection,                                           "reserve_sig",                                           &signature, -                                         sizeof (struct GNUNET_CRYPTO_EddsaSignature)); +                                         sizeof (struct TALER_ReserveSignature));    if (GNUNET_SYSERR == res)      return MHD_NO; /* internal error */    if (GNUNET_NO == res) @@ -148,8 +148,8 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,    if (GNUNET_OK !=        GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WITHDRAW,                                    &wsrd.purpose, -                                  &signature, -                                  &wsrd.reserve_pub)) +                                  &signature.eddsa_signature, +                                  &wsrd.reserve_pub.eddsa_pub))    {      LOG_WARNING ("Client supplied invalid signature for /withdraw/sign request\n");      GNUNET_free (denomination_pub_data); @@ -157,10 +157,11 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,      return TALER_MINT_reply_arg_invalid (connection,                                           "reserve_sig");    } -  denomination_pub = GNUNET_CRYPTO_rsa_public_key_decode (denomination_pub_data, -                                                          denomination_pub_data_size); +  denomination_pub.rsa_public_key +    = GNUNET_CRYPTO_rsa_public_key_decode (denomination_pub_data, +                                           denomination_pub_data_size);    GNUNET_free (denomination_pub_data); -  if (NULL == denomination_pub) +  if (NULL == denomination_pub.rsa_public_key)    {      LOG_WARNING ("Client supplied ill-formed denomination public key for /withdraw/sign request\n");      GNUNET_free (blinded_msg); @@ -169,12 +170,12 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,    }    res = TALER_MINT_db_execute_withdraw_sign (connection,                                               &wsrd.reserve_pub, -                                             denomination_pub, +                                             &denomination_pub,                                               blinded_msg,                                               blinded_msg_len,                                               &signature);    GNUNET_free (blinded_msg); -  GNUNET_CRYPTO_rsa_public_key_free (denomination_pub); +  GNUNET_CRYPTO_rsa_public_key_free (denomination_pub.rsa_public_key);    return res;  } diff --git a/src/mint/taler-mint-keycheck.c b/src/mint/taler-mint-keycheck.c index c934d08f..370b1c51 100644 --- a/src/mint/taler-mint-keycheck.c +++ b/src/mint/taler-mint-keycheck.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 Christian Grothoff (and other contributing authors) +  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 @@ -13,127 +13,166 @@    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. + * @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_signatures.h"  #include "key_io.h" - +/** + * Mint directory with the keys. + */  static char *mintdir; + +/** + * 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 struct TALER_MINT_SignKeyIssuePriv *ski) +signkeys_iter (void *cls, +               const char *filename, +               const struct TALER_MINT_SignKeyIssuePriv *ski)  { -  struct GNUNET_TIME_Absolute start; - -  printf ("iterating over key for start time %s\n", -          GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (ski->issue.start))); - -  start = GNUNET_TIME_absolute_ntoh (ski->issue.start); +  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_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose))) +      (sizeof (struct TALER_MINT_SignKeyIssue) - +       offsetof (struct TALER_MINT_SignKeyIssue, purpose)))    { -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Signkey with start %s has invalid purpose field (timestamp: %llu)\n", -                GNUNET_STRINGS_absolute_time_to_string (start), -                (long long) start.abs_value_us); +    fprintf (stderr, +             "Signing key `%s' has invalid purpose size\n", +             filename);      return GNUNET_SYSERR;    } - - -  if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNKEY, -                                               &ski->issue.purpose, -                                               &ski->issue.signature, -                                               &ski->issue.master_pub)) +  if (GNUNET_OK != +      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNKEY, +                                  &ski->issue.purpose, +                                  &ski->issue.signature.eddsa_signature, +                                  &ski->issue.master_pub.eddsa_pub))    { -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signkey with start %s has invalid signature (timestamp: %llu)\n", -                GNUNET_STRINGS_absolute_time_to_string (start), -                (long long) start.abs_value_us); +    fprintf (stderr, +             "Signing key `%s' has invalid signature\n", +             filename);      return GNUNET_SYSERR;    } -  /* FIXME: what about private key matching the public key? */ -  printf ("key valid\n"); +  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 (mintdir, signkeys_iter, NULL)) +  if (0 > TALER_MINT_signkeys_iterate (mintdir, +                                       &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_MINT_DenomKeyIssuePriv *dki)  { -  struct GNUNET_TIME_Absolute start; - -  start = GNUNET_TIME_absolute_ntoh (dki->issue.start); +  struct GNUNET_HashCode hc;    if (ntohl (dki->issue.purpose.size) != -      (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - offsetof (struct TALER_MINT_DenomKeyIssuePriv, issue.purpose))) +      sizeof (struct TALER_MINT_DenomKeyIssue))    { -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Denomkey for '%s' with start %s has invalid purpose field (timestamp: %llu)\n", -                alias, -                GNUNET_STRINGS_absolute_time_to_string (start), -                (long long) start.abs_value_us); +    fprintf (stderr, +             "Denomination key for `%s' has invalid purpose size\n", +             alias);      return GNUNET_SYSERR;    }    if (GNUNET_OK !=        GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOM,                                    &dki->issue.purpose, -                                  &dki->issue.signature, -                                  &dki->issue.master)) +                                  &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)))    { -    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, -                "Denomkey for '%s'with start %s has invalid signature (timestamp: %llu)\n", -                alias, -                GNUNET_STRINGS_absolute_time_to_string (start), -                (long long) start.abs_value_us); +    fprintf (stderr, +             "Public key for `%s' does not match signature\n", +             alias);      return GNUNET_SYSERR;    } -  printf ("denom key valid\n"); +  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 (mintdir, -                                        &denomkeys_iter, NULL)) +                                        &denomkeys_iter, +                                        NULL))      return GNUNET_NO;    return GNUNET_OK;  } -static int -mint_keys_check (void) -{ -  if (GNUNET_OK != mint_signkeys_check ()) -    return GNUNET_NO; -  return mint_denomkeys_check (); -} - -  /**   * The main function of the keyup tool   * @@ -145,30 +184,44 @@ int  main (int argc, char *const *argv)  {    static const struct GNUNET_GETOPT_CommandLineOption options[] = { -    GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"), -    {'d', "mint-dir", "DIR", -     "mint directory with keys to update", 1, +    GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keycheck OPTIONS"), +    {'d', "directory", "DIRECTORY", +     "mint directory with keys to check", 1,       &GNUNET_GETOPT_set_filename, &mintdir},      GNUNET_GETOPT_OPTION_END    }; -  GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keycheck", "WARNING", NULL)); +  GNUNET_assert (GNUNET_OK == +                 GNUNET_log_setup ("taler-mint-keycheck", +                                   "WARNING", +                                   NULL)); -  if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0) +  if (GNUNET_GETOPT_run ("taler-mint-keycheck", +                         options, +                         argc, argv) < 0)      return 1;    if (NULL == mintdir)    { -    fprintf (stderr, "mint directory not given\n"); +    fprintf (stderr, +             "Mint directory not given\n");      return 1;    }    kcfg = TALER_config_load (mintdir);    if (NULL == kcfg)    { -    fprintf (stderr, "can't load mint configuration\n"); +    fprintf (stderr, +             "Failed to load mint configuration\n");      return 1;    } -  if (GNUNET_OK != mint_keys_check ()) +  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 index c4e15347..603154f6 100644 --- a/src/mint/taler-mint-keyup.c +++ b/src/mint/taler-mint-keyup.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 Christian Grothoff (and other contributing authors) +  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 @@ -13,58 +13,140 @@    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_signatures.h"  #include "key_io.h"  /** - * FIXME: allow user to specify (within reason). + * 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 RSA_KEYSIZE 2048 -  #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 CoinTypeNBO  { +  /** +   * 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;  }; @@ -97,12 +179,12 @@ static struct GNUNET_TIME_Absolute now;  /**   * Master private key of the mint.   */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *master_priv; +static struct TALER_MasterPrivateKey master_priv;  /**   * Master public key of the mint.   */ -static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub; +static struct TALER_MasterPublicKey master_pub;  /**   * Until what time do we provide keys? @@ -110,152 +192,183 @@ static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub;  static struct GNUNET_TIME_Absolute lookahead_sign_stamp; -static int -config_get_denom (const char *section, const char *option, struct TALER_Amount *denom) -{ -  char *str; -  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, section, option, &str)) -    return GNUNET_NO; -  if (GNUNET_OK != TALER_string_to_amount (str, denom)) -    return GNUNET_SYSERR; -  return GNUNET_OK; -} - - -static char * -get_signkey_dir () -{ -  char *dir; -  size_t len; -  len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mintdir); -  GNUNET_assert (len > 0); -  return dir; -} - - -static char * +/** + * 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)  { -  char *dir; -  size_t len; -  len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu"), -                         mintdir, (long long) start.abs_value_us); -  GNUNET_assert (len > 0); +  static char dir[4096]; + +  GNUNET_snprintf (dir, +                   sizeof (dir), +                   "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu", +                   mintdir, +                   (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). + * 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) +hash_coin_type (const struct CoinTypeParams *p, +                struct GNUNET_HashCode *hash)  {    struct CoinTypeNBO p_nbo; -  memset (&p_nbo, 0, sizeof (struct CoinTypeNBO)); - +  memset (&p_nbo, +          0, +          sizeof (struct CoinTypeNBO));    p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);    p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); -  p_nbo.value = TALER_amount_hton (p->value); -  p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw); -  p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit); -  p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh); - -  GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), hash); +  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 CoinTypeNBO), +                      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]; -  size_t len;    struct GNUNET_HashCode hash;    char *hash_str;    char *val_str; -  unsigned int i; +  size_t i;    hash_coin_type (p, &hash);    hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash,                                                    sizeof (struct GNUNET_HashCode)); -  GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);    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); +  val_str = TALER_amount_to_string (&p->value);    for (i = 0; i < strlen (val_str); i++) -    if (':' == val_str[i] || '.' == val_str[i]) +    if ( (':' == val_str[i]) || +         ('.' == val_str[i]) )        val_str[i] = '_'; -  len = GNUNET_snprintf (dir, sizeof (dir), -                         ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s"), -                         mintdir, val_str, hash_str); -  GNUNET_assert (len > 0); +  GNUNET_snprintf (dir, +                   sizeof (dir), +                   "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s", +                   mintdir, +                   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 (struct CoinTypeParams *p, +get_cointype_file (const struct CoinTypeParams *p,                     struct GNUNET_TIME_Absolute start)  { -  const char *dir;    static char filename[4096]; -  size_t len; +  const char *dir; +    dir = get_cointype_dir (p); -  len = GNUNET_snprintf (filename, sizeof (filename), ("%s" DIR_SEPARATOR_STR "%llu"), -                         dir, (unsigned long long) start.abs_value_us); -  GNUNET_assert (len > 0); +  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 the past. + * 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 + * @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, - *  #GNUNET_NO to stop iteration with no error, - *  #GNUNET_SYSERR to abort iteration with error! + * @return #GNUNET_OK (to continue to iterate)   */  static int  get_anchor_iter (void *cls,                   const char *filename)  { -  struct GNUNET_TIME_Absolute stamp;    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); - +  stamp.abs_value_us = strtol (base, +                               &end, +                               10);    if ((NULL == end) || (0 != *end))    { -    fprintf(stderr, "Ignoring unexpected file '%s'.\n", filename); +    fprintf(stderr, +            "Ignoring unexpected file `%s'.\n", +            filename);      return GNUNET_OK;    } - -  // TODO: check if it's actually a valid key file - -  if ((stamp.abs_value_us <= now.abs_value_us) && (stamp.abs_value_us > anchor->abs_value_us)) -    *anchor = stamp; - +  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. + * 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 with the signed stuff - * @param duration how long is one key valid? + * @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   */ @@ -267,36 +380,57 @@ get_anchor (const char *dir,  {    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)) +  if (GNUNET_YES != +      GNUNET_DISK_directory_test (dir, +                                  GNUNET_YES))    {      *anchor = now; -    printf ("Can't look for anchor (%s)\n", dir); +    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)) +  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;    } -  if ((GNUNET_TIME_absolute_add (*anchor, duration)).abs_value_us < now.abs_value_us) +  /* 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)    { -    // there's no good anchor, start from now -    // (existing keys are too old) +    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) +  else if (anchor->abs_value_us != now.abs_value_us) // Also odd...    { -    // we have a good anchor -    *anchor = GNUNET_TIME_absolute_add (*anchor, duration); -    *anchor = GNUNET_TIME_absolute_subtract (*anchor, overlap); +    /* 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 +  /* 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, @@ -306,33 +440,32 @@ create_signkey_issue_priv (struct GNUNET_TIME_Absolute start,    struct TALER_MINT_SignKeyIssue *issue = &pi->issue;    priv = GNUNET_CRYPTO_eddsa_key_create (); -  GNUNET_assert (NULL != priv); -  pi->signkey_priv = *priv; +  pi->signkey_priv.eddsa_priv = *priv;    GNUNET_free (priv); -  issue->master_pub = *master_pub; +  issue->master_pub = master_pub;    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, &issue->signkey_pub); - +  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_MASTER_SIGNKEY); -  issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose)); +  issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - +                               offsetof (struct TALER_MINT_SignKeyIssue, +                                         purpose));    GNUNET_assert (GNUNET_OK == -                 GNUNET_CRYPTO_eddsa_sign (master_priv, +                 GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,                                             &issue->purpose, -                                           &issue->signature)); -} - - -static int -check_signkey_valid (const char *signkey_filename) -{ -  // FIXME: do real checks -  return GNUNET_OK; +                                           &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 ()  { @@ -340,110 +473,219 @@ mint_keys_update_signkeys ()    struct GNUNET_TIME_Absolute anchor;    char *signkey_dir; -  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "signkey_duration", &signkey_duration)) +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_time (kcfg, +                                           "mint_keys", +                                           "signkey_duration", +                                           &signkey_duration))    { -    fprintf (stderr, "Can't read config value mint_keys.signkey_duration\n"); +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               "mint_keys", +                               "signkey_duration");      return GNUNET_SYSERR;    } -  ROUND_TO_SECS (signkey_duration, rel_value_us); -  signkey_dir = get_signkey_dir (); -  // make sure the directory exists -  if (GNUNET_OK != GNUNET_DISK_directory_create (signkey_dir)) +  ROUND_TO_SECS (signkey_duration, +                 rel_value_us); +  GNUNET_asprintf (&signkey_dir, +                   "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, +                   mintdir); +  /* make sure the directory exists */ +  if (GNUNET_OK != +      GNUNET_DISK_directory_create (signkey_dir))    { -    fprintf (stderr, "Cant create signkey dir\n"); +    fprintf (stderr, +             "Failed to create signing key directory\n");      return GNUNET_SYSERR;    } -  get_anchor (signkey_dir, signkey_duration, GNUNET_TIME_UNIT_ZERO, &anchor); +  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_MINT_SignKeyIssuePriv signkey_issue; +    ssize_t nwrite; -  while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) { -    char *skf;      skf = get_signkey_file (anchor); -    if (GNUNET_YES != GNUNET_DISK_file_test (skf)) -    { -      struct TALER_MINT_SignKeyIssuePriv signkey_issue; -      ssize_t nwrite; -      printf ("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_MINT_SignKeyIssue), -                                     (GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ)); -      if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue)) -      { -        fprintf (stderr, "Can't write to file '%s'\n", skf); -        return GNUNET_SYSERR; -      } -    } -    else if (GNUNET_OK != check_signkey_valid (skf)) +    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_MINT_SignKeyIssue), +                                   GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ); +    if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue))      { +      fprintf (stderr, +               "Failed to write to file `%s': %s\n", +               skf, +               STRERROR (errno));        return GNUNET_SYSERR;      } -    anchor = GNUNET_TIME_absolute_add (anchor, signkey_duration); +    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) +get_cointype_params (const char *ct, +                     struct CoinTypeParams *params)  {    const char *dir; -  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_withdraw", ct, ¶ms->duration_withdraw)) +  unsigned long long rsa_keysize; + +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_time (kcfg, +                                           ct, +                                           "duration_withdraw", +                                           ¶ms->duration_withdraw))    { -    fprintf (stderr, "Withdraw duration not given for coin type '%s'\n", ct); +    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, "mint_denom_duration_spend", ct, ¶ms->duration_spend)) +  ROUND_TO_SECS (params->duration_withdraw, +                 rel_value_us); +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_time (kcfg, +                                           ct, +                                           "duration_spend", +                                           ¶ms->duration_spend))    { -    fprintf (stderr, "Spend duration not given for coin type '%s'\n", ct); +    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, "mint_denom_duration_overlap", ct, ¶ms->duration_overlap)) +  ROUND_TO_SECS (params->duration_spend, +                 rel_value_us); +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_time (kcfg, +                                           ct, +                                           "duration_overlap", +                                           ¶ms->duration_overlap))    { -    fprintf (stderr, "Overlap duration not given for coin type '%s'\n", ct); +    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 != config_get_denom ("mint_denom_value", ct, ¶ms->value)) +  ROUND_TO_SECS (params->duration_overlap, +                 rel_value_us); +  if (GNUNET_OK != +      GNUNET_CONFIGURATION_get_value_number (kcfg, +                                             ct, +                                             "rsa_keysize", +                                             &rsa_keysize))    { -    fprintf (stderr, "Value not given for coin type '%s'\n", ct); +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               ct, +                               "rsa_keysize");      return GNUNET_SYSERR;    } - -  if (GNUNET_OK != config_get_denom ("mint_denom_fee_withdraw", ct, ¶ms->fee_withdraw)) +  if ( (rsa_keysize > 4 * 2048) || +       (rsa_keysize < 1024) )    { -    fprintf (stderr, "Withdraw fee not given for coin type '%s'\n", ct); +    fprintf (stderr, +             "Given RSA keysize %llu outside of permitted range\n", +             rsa_keysize);      return GNUNET_SYSERR;    } - -  if (GNUNET_OK != config_get_denom ("mint_denom_fee_deposit", ct, ¶ms->fee_deposit)) +  params->rsa_keysize = (unsigned int) rsa_keysize; +  if (GNUNET_OK != +      TALER_config_get_denom (kcfg, +                              ct, +                              "value", +                              ¶ms->value))    { -    fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct); +    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                               ct, +                               "value");      return GNUNET_SYSERR;    } - -  if (GNUNET_OK != config_get_denom ("mint_denom_fee_refresh", ct, ¶ms->fee_refresh)) +  if (GNUNET_OK != +      TALER_config_get_denom (kcfg, +                              ct, +                              "fee_withdraw", +                              ¶ms->fee_withdraw))    { -    fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct); +    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); +  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 (struct CoinTypeParams *params, +create_denomkey_issue (const struct CoinTypeParams *params,                         struct TALER_MINT_DenomKeyIssuePriv *dki)  { -  GNUNET_assert (NULL != (dki->denom_priv = GNUNET_CRYPTO_rsa_private_key_create (RSA_KEYSIZE))); -  dki->issue.denom_pub = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv); -  dki->issue.master = *master_pub; +  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_pub;    dki->issue.start = GNUNET_TIME_absolute_hton (params->anchor);    dki->issue.expire_withdraw =        GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, @@ -451,137 +693,125 @@ create_denomkey_issue (struct CoinTypeParams *params,    dki->issue.expire_spend =      GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,                                                             params->duration_spend)); -  dki->issue.value = TALER_amount_hton (params->value); -  dki->issue.fee_withdraw = TALER_amount_hton (params->fee_withdraw); -  dki->issue.fee_deposit = TALER_amount_hton (params->fee_deposit); -  dki->issue.fee_refresh = TALER_amount_hton (params->fee_refresh); - +  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_MASTER_DENOM); -  dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - offsetof (struct TALER_MINT_DenomKeyIssuePriv, issue.purpose)); - +  dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - +                                   offsetof (struct TALER_MINT_DenomKeyIssuePriv, +                                             issue.purpose));    GNUNET_assert (GNUNET_OK == -                 GNUNET_CRYPTO_eddsa_sign (master_priv, +                 GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,                                             &dki->issue.purpose, -                                           &dki->issue.signature)); -} - - -static int -check_cointype_valid (const char *filename, struct CoinTypeParams *params) -{ -  // FIXME: add real checks -  return GNUNET_OK; +                                           &dki->issue.signature.eddsa_signature));  } -static int -mint_keys_update_cointype (const char *coin_alias) +/** + * 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 *cointype_dir; - -  if (GNUNET_OK != get_cointype_params (coin_alias, &p)) -    return GNUNET_SYSERR; - -  cointype_dir = get_cointype_dir (&p); -  if (GNUNET_OK != GNUNET_DISK_directory_create (cointype_dir)) -    return GNUNET_SYSERR; - -  while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) { -    const char *dkf; -    dkf = get_cointype_file (&p, p.anchor); - -    if (GNUNET_YES != GNUNET_DISK_file_test (dkf)) -    { -      struct TALER_MINT_DenomKeyIssuePriv denomkey_issue; -      int ret; -      printf ("Generating denomination key for type '%s', start %s.\n", -              coin_alias, -              GNUNET_STRINGS_absolute_time_to_string (p.anchor)); -      printf ("Target path: %s\n", dkf); -      create_denomkey_issue (&p, &denomkey_issue); -      ret = TALER_MINT_write_denom_key (dkf, &denomkey_issue); -      GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv); -      if (GNUNET_OK != ret) -      { -        fprintf (stderr, "Can't write to file '%s'\n", dkf); -        return GNUNET_SYSERR; -      } -    } -    else if (GNUNET_OK != check_cointype_valid (dkf, &p)) -    { -      return GNUNET_SYSERR; -    } -    p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend); -    p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, p.duration_overlap); +  const char *dkf; +  struct TALER_MINT_DenomKeyIssuePriv 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;    } -  return GNUNET_OK; -} - - -static int -mint_keys_update_denomkeys () -{ -  char *coin_types; -  char *ct; -  char *tok_ctx; - -  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint_keys", "coin_types", &coin_types)) +  if (GNUNET_OK != +      GNUNET_DISK_directory_create (get_cointype_dir (&p)))    { -    fprintf (stderr, "mint_keys.coin_types not in configuration\n"); -    return GNUNET_SYSERR; +    *ret = GNUNET_SYSERR; +    return;    } -  for (ct = strtok_r (coin_types, " ", &tok_ctx); -       ct != NULL; -       ct = strtok_r (NULL, " ", &tok_ctx)) +  while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)    { -    if (GNUNET_OK != mint_keys_update_cointype (ct)) +    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))      { -      GNUNET_free (coin_types); -      return GNUNET_SYSERR; +      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);    } -  GNUNET_free (coin_types); -  return GNUNET_OK;  } +/** + * Update all of the denomination keys of the mint. + * + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */  static int -mint_keys_update () +mint_keys_update_denomkeys ()  { -  int ret; -  struct GNUNET_TIME_Relative lookahead_sign; -  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "lookahead_sign", &lookahead_sign)) -  { -    fprintf (stderr, "mint_keys.lookahead_sign not found\n"); -    return GNUNET_SYSERR; -  } -  if (lookahead_sign.rel_value_us == 0) -  { -    fprintf (stderr, "mint_keys.lookahead_sign must not be zero\n"); -    return GNUNET_SYSERR; -  } -  ROUND_TO_SECS (lookahead_sign, rel_value_us); -  lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, lookahead_sign); +  int ok; -  ret = mint_keys_update_signkeys (); -  if (GNUNET_OK != ret) -    return GNUNET_SYSERR; - -  return mint_keys_update_denomkeys (); +  ok = GNUNET_OK; +  GNUNET_CONFIGURATION_iterate_sections (kcfg, +                                         &mint_keys_update_cointype, +                                         &ok); +  return ok;  }  /** - * The main function of the keyup tool + * 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) +main (int argc, +      char *const *argv)  {    static const struct GNUNET_GETOPT_CommandLineOption options[] = {      GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"), @@ -596,22 +826,33 @@ main (int argc, char *const *argv)       &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)); +  GNUNET_assert (GNUNET_OK == +                 GNUNET_log_setup ("taler-mint-keyup", +                                   "WARNING", +                                   NULL)); -  if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0) +  if (GNUNET_GETOPT_run ("taler-mint-keyup", +                         options, +                         argc, argv) < 0)      return 1;    if (NULL == mintdir)    { -    fprintf (stderr, "mint directory not given\n"); +    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)) +    if (GNUNET_OK != +        GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, +                                               &now))      { -      fprintf (stderr, "timestamp invalid\n"); +      fprintf (stderr, +               "timestamp `%s' invalid\n", +               pretend_time_str);        return 1;      }    } @@ -624,44 +865,90 @@ main (int argc, char *const *argv)    kcfg = TALER_config_load (mintdir);    if (NULL == kcfg)    { -    fprintf (stderr, "can't load mint configuration\n"); +    fprintf (stderr, +             "Failed to load mint configuration\n");      return 1;    } -    if (NULL == masterkeyfile)    { -    fprintf (stderr, "master key file not given\n"); +    fprintf (stderr, +             "Master key file not given\n");      return 1;    } -  master_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); -  if (NULL == master_priv) +  eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); +  if (NULL == eddsa_priv)    { -    fprintf (stderr, "master key invalid\n"); +    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_pub.eddsa_pub); -  master_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey); -  GNUNET_CRYPTO_eddsa_key_get_public (master_priv, master_pub); - -  // check if key from file matches the one from the configuration +  /* check if key from file matches the one from the configuration */    {      struct GNUNET_CRYPTO_EddsaPublicKey master_pub_from_cfg; +      if (GNUNET_OK != -        GNUNET_CONFIGURATION_get_data (kcfg, "mint", "master_pub", +        GNUNET_CONFIGURATION_get_data (kcfg, +                                       "mint", +                                       "master_pub",                                         &master_pub_from_cfg,                                         sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))      { -      fprintf (stderr, "master key missing in configuration (mint.master_pub)\n"); +      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, +                                 "mint", +                                 "master_pub");        return 1;      } -    if (0 != memcmp (master_pub, &master_pub_from_cfg, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) +    if (0 != +        memcmp (&master_pub, +                &master_pub_from_cfg, +                sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))      { -      fprintf (stderr, "Mismatch between key from mint configuration and master private key file from command line.\n"); +      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, +                                 "mint", +                                 "master_pub", +                                 _("does not match with private key"));        return 1;      }    } -  if (GNUNET_OK != mint_keys_update ()) +  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 index e7795e67..68f736ff 100644 --- a/src/mint/taler-mint-reservemod.c +++ b/src/mint/taler-mint-reservemod.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2014 Christian Grothoff (and other contributing authors) +  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 @@ -13,63 +13,93 @@    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. + * @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 "mint_db.h" +#include "taler_mintdb_plugin.h"  #include "db_pq.h" -char *mintdir; +/** + * Director of the mint, containing the keys. + */ +static char *mintdir; + +/** + * Public key of the reserve to manipulate. + */  static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub; -struct GNUNET_CONFIGURATION_Handle *cfg; +/** + * 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. + * Create a new or add to existing reserve.  Fails if currencies do + * not match.   *   * @param denom denomination to add - * - * @return ... + * @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}; -    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); -  } +  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)); +    fprintf (stderr, +             "Select failed: %s\n", +             PQresultErrorMessage (result));      return GNUNET_SYSERR;    }    if (0 == PQntuples (result))    {      struct GNUNET_TIME_AbsoluteNBO exnbo; -    exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add ( GNUNET_TIME_absolute_get (), GNUNET_TIME_UNIT_YEARS)); -      uint32_t value = htonl (denom.value);      uint32_t fraction = htonl (denom.fraction);      const void *param_values[] = { @@ -77,33 +107,53 @@ reservemod_add (struct TALER_Amount denom)        &value,        &fraction,        denom.currency, -      &exnbo}; -    int param_lengths[] = {32, 4, 4, strlen(denom.currency), 8}; -    int param_formats[] = {1, 1, 1, 1, 1}; +      &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); +                           "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)); +      fprintf (stderr, +               "Insert failed: %s\n", +               PQresultErrorMessage (result));        return GNUNET_SYSERR;      }    }    else    { -    struct TALER_Amount old_denom; -    struct TALER_Amount new_denom; -    struct TALER_AmountNBO new_denom_nbo; -    int param_lengths[] = {4, 4, 32}; -    int param_formats[] = {1, 1, 1};      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_DB_extract_amount (result, 0, @@ -111,28 +161,38 @@ reservemod_add (struct TALER_Amount denom)                                              "balance_fraction",                                              "balance_currency",                                              &old_denom)); -    new_denom = TALER_amount_add (old_denom, denom); -    new_denom_nbo = TALER_amount_hton (new_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); +                           "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)); +      fprintf (stderr, +               "Update failed: %s\n", +               PQresultErrorMessage (result));        return GNUNET_SYSERR;      } - -    if (0 != strcmp ("1", PQcmdTuples (result))) +    /* 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", +      fprintf (stderr, +               "Update failed (updated `%s' tupes instead of '1')\n",                 PQcmdTuples (result));        return GNUNET_SYSERR;      } -    }    return GNUNET_OK;  } @@ -151,7 +211,7 @@ 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-keyup OPTIONS"), +    GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-reservemod OPTIONS"),      {'d', "mint-dir", "DIR",       "mint directory with keys to update", 1,       &GNUNET_GETOPT_set_filename, &mintdir}, @@ -165,13 +225,19 @@ main (int argc, char *const *argv)    };    char *TALER_MINT_db_connection_cfg_str; -  GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keycheck", "WARNING", NULL)); +  GNUNET_assert (GNUNET_OK == +                 GNUNET_log_setup ("taler-mint-reservemod", +                                   "WARNING", +                                   NULL)); -  if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0) +  if (GNUNET_GETOPT_run ("taler-mint-reservemod", +                         options, +                         argc, argv) < 0)      return 1;    if (NULL == mintdir)    { -    fprintf (stderr, "mint directory not given\n"); +    fprintf (stderr, +             "Mint directory not given\n");      return 1;    } @@ -183,14 +249,15 @@ main (int argc, char *const *argv)                                        reserve_pub,                                        sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))))    { -    fprintf (stderr, "reserve key invalid\n"); +    fprintf (stderr, +             "Parsing reserve key invalid\n");      return 1;    } -    cfg = TALER_config_load (mintdir);    if (NULL == cfg)    { -    fprintf (stderr, "can't load mint configuration\n"); +    fprintf (stderr, +             "Failed to load mint configuration\n");      return 1;    }    if (GNUNET_OK != @@ -199,29 +266,40 @@ main (int argc, char *const *argv)                                               "db",                                               &TALER_MINT_db_connection_cfg_str))    { -    fprintf (stderr, "db configuration string not found\n"); -    return 42; +    fprintf (stderr, +             "Database configuration string not found\n"); +    return 1;    }    db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str);    if (CONNECTION_OK != PQstatus (db_conn))    { -    fprintf (stderr, "db connection failed: %s\n", PQerrorMessage (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)) + +    if (GNUNET_OK != +        TALER_string_to_amount (add_str, +                                &add_value))      { -      fprintf (stderr, "could not read value\n"); +      fprintf (stderr, +               "Failed to parse currency amount `%s'\n", +               add_str);        return 1;      } -    if (GNUNET_OK != reservemod_add (add_value)) +    if (GNUNET_OK != +        reservemod_add (add_value))      { -      fprintf (stderr, "adding value failed\n"); +      fprintf (stderr, +               "Failed to update reserve.\n");        return 1;      }    }    return 0;  } + +/* end taler-mint-reservemod.c */ diff --git a/src/mint/taler_mintdb_plugin.h b/src/mint/taler_mintdb_plugin.h new file mode 100644 index 00000000..08a73479 --- /dev/null +++ b/src/mint/taler_mintdb_plugin.h @@ -0,0 +1,1024 @@ +/* +  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_mintdb_plugin.h + * @brief Low-level (statement-level) database access for the mint + * @author Florian Dold + * @author Christian Grothoff + */ +#ifndef TALER_MINTDB_PLUGIN_H +#define TALER_MINTDB_PLUGIN_H + +#include <gnunet/gnunet_util_lib.h> +#include "taler_util.h" + + +/** + * Information we keep on bank transfer(s) that established a reserve. + */ +struct BankTransfer +{ + +  /** +   * Public key of the reserve that was filled. +   */ +  struct TALER_ReservePublicKey reserve_pub; + +  /** +   * Amount that was transferred to the mint. +   */ +  struct TALER_Amount amount; + +  /** +   * Detailed wire information about the transaction. +   */ +  json_t *wire; + +}; + + +/** + * A summary of a Reserve + */ +struct Reserve +{ +  /** +   * The reserve's public key.  This uniquely identifies the reserve +   */ +  struct TALER_ReservePublicKey pub; + +  /** +   * The balance amount existing in the reserve +   */ +  struct TALER_Amount balance; + +  /** +   * The expiration date of this reserve +   */ +  struct GNUNET_TIME_Absolute expiry; +}; + + +/** + * Information we keep for a withdrawn coin to reproduce + * the /withdraw operation if needed, and to have proof + * that a reserve was drained by this amount. + */ +struct CollectableBlindcoin +{ + +  /** +   * Our signature over the (blinded) coin. +   */ +  struct TALER_DenominationSignature sig; + +  /** +   * Denomination key (which coin was generated). +   * FIXME: we should probably instead have the +   * AMOUNT *including* fee in what is being signed +   * as well! +   */ +  struct TALER_DenominationPublicKey denom_pub; + +  /** +   * Public key of the reserve that was drained. +   */ +  struct TALER_ReservePublicKey reserve_pub; + +  /** +   * Hash over the blinded message, needed to verify +   * the @e reserve_sig. +   */ +  struct GNUNET_HashCode h_coin_envelope; + +  /** +   * Signature confirming the withdrawl, matching @e reserve_pub, +   * @e denom_pub and @e h_coin_envelope. +   */ +  struct TALER_ReserveSignature reserve_sig; +}; + + + +/** + * Types of operations on a reserved. + */ +enum TALER_MINT_DB_ReserveOperation +{ +  /** +   * Money was deposited into the reserve via a bank transfer. +   */ +  TALER_MINT_DB_RO_BANK_TO_MINT = 0, + +  /** +   * A Coin was withdrawn from the reserve using /withdraw. +   */ +  TALER_MINT_DB_RO_WITHDRAW_COIN = 1 +}; + + +/** + * Reserve history as a linked list.  Lists all of the transactions + * associated with this reserve (such as the bank transfers that + * established the reserve and all /withdraw operations we have done + * since). + */ +struct ReserveHistory +{ + +  /** +   * Next entry in the reserve history. +   */ +  struct ReserveHistory *next; + +  /** +   * Type of the event, determins @e details. +   */ +  enum TALER_MINT_DB_ReserveOperation type; + +  /** +   * Details of the operation, depending on @e type. +   */ +  union +  { + +    /** +     * Details about a bank transfer to the mint. +     */ +    struct BankTransfer *bank; + +    /** +     * Details about a /withdraw operation. +     */ +    struct CollectableBlindcoin *withdraw; + +  } details; + +}; + + +/** + * Specification for a /deposit operation. + */ +struct Deposit +{ +  /** +   * Information about the coin that is being deposited. +   */ +  struct TALER_CoinPublicInfo coin; + +  /** +   * ECDSA signature affirming that the customer intends +   * this coin to be deposited at the merchant identified +   * by @e h_wire in relation to the contract identified +   * by @e h_contract. +   */ +  struct TALER_CoinSpendSignature csig; + +  /** +   * Public key of the merchant.  Enables later identification +   * of the merchant in case of a need to rollback transactions. +   */ +  struct TALER_MerchantPublicKey merchant_pub; + +  /** +   * Hash over the contract between merchant and customer +   * (remains unknown to the Mint). +   */ +  struct GNUNET_HashCode h_contract; + +  /** +   * Hash of the (canonical) representation of @e wire, used +   * to check the signature on the request.  Generated by +   * the mint from the detailed wire data provided by the +   * merchant. +   */ +  struct GNUNET_HashCode h_wire; + +  /** +   * Detailed wire information for executing the transaction. +   */ +  json_t *wire; + +  /** +   * Merchant-generated transaction ID to detect duplicate +   * transactions. +   */ +  uint64_t transaction_id; + +  /** +   * Fraction of the coin's remaining value to be deposited, including +   * depositing fee (if any).  The coin is identified by @e coin_pub. +   */ +  struct TALER_Amount amount_with_fee; + +}; + + +/** + * Global information for a refreshing session.  Includes + * dimensions of the operation, security parameters and + * client signatures from "/refresh/melt" and "/refresh/commit". + */ +struct RefreshSession +{ + +  /** +   * Number of coins we are melting. +   */ +  uint16_t num_oldcoins; + +  /** +   * Number of new coins we are creating. +   */ +  uint16_t num_newcoins; + +  /** +   * Index (smaller #KAPPA) which the mint has chosen to not +   * have revealed during cut and choose. +   */ +  uint16_t noreveal_index; + +}; + + +/** + * Specification for coin in a /refresh/melt operation. + */ +struct RefreshMelt +{ +  /** +   * Information about the coin that is being melted. +   */ +  struct TALER_CoinPublicInfo coin; + +  /** +   * Signature over the melting operation. +   */ +  struct TALER_CoinSpendSignature coin_sig; + +  /** +   * Hash of the refresh session this coin is melted into. +   */ +  struct GNUNET_HashCode session_hash; + +  /** +   * How much value is being melted?  This amount includes the fees, +   * so the final amount contributed to the melt is this value minus +   * the fee for melting the coin.  We include the fee in what is +   * being signed so that we can verify a reserve's remaining total +   * balance without needing to access the respective denomination key +   * information each time. +   */ +  struct TALER_Amount amount_with_fee; + +}; + + +/** + * We have as many `struct RefreshCommitCoin` as there are new + * coins being created by the refresh (for each of the #KAPPA + * sets).  These are the coins we ask the mint to sign if the + * respective set is selected. + */ +struct RefreshCommitCoin +{ + +  /** +   * Encrypted data allowing those able to decrypt it to derive +   * the private keys of the new coins created by the refresh. +   */ +  struct TALER_RefreshLinkEncrypted *refresh_link; + +  /** +   * Blinded message to be signed (in envelope), with @e coin_env_size bytes. +   */ +  char *coin_ev; + +  /** +   * Number of bytes in @e coin_ev. +   */ +  size_t coin_ev_size; + +}; + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * For each (old) coin being melted, we have a `struct + * RefreshCommitLink` that allows the user to find the shared secret + * to decrypt the respective refresh links for the new coins in the + * `struct RefreshCommitCoin`. + */ +struct RefreshCommitLink +{ +  /** +   * Transfer public key, used to decrypt the @e shared_secret_enc +   * in combintation with the corresponding private key of the +   * coin. +   */ +  struct TALER_TransferPublicKey transfer_pub; + +  /** +   * Encrypted shared secret to decrypt the link. +   */ +  struct TALER_EncryptedLinkSecret shared_secret_enc; +}; + +GNUNET_NETWORK_STRUCT_END + + + +/** + * Linked list of refresh information linked to a coin. + */ +struct LinkDataList +{ +  /** +   * Information is stored in a NULL-terminated linked list. +   */ +  struct LinkDataList *next; + +  /** +   * Link data, used to recover the private key of the coin +   * by the owner of the old coin. +   */ +  struct TALER_RefreshLinkEncrypted *link_data_enc; + +  /** +   * Denomination public key, determines the value of the coin. +   */ +  struct TALER_DenominationPublicKey denom_pub; + +  /** +   * Signature over the blinded envelope. +   */ +  struct TALER_DenominationSignature ev_sig; +}; + + +/** + * Specification for a /lock operation. + */ +struct Lock +{ +  /** +   * Information about the coin that is being locked. +   */ +  struct TALER_CoinPublicInfo coin; + +  /** +   * Signature over the locking operation. +   */ +  struct TALER_CoinSpendSignature coin_sig; + +  /** +   * How much value is being locked? +   */ +  struct TALER_Amount amount; + +  // FIXME: more needed... +}; + + +/** + * Enumeration to classify the different types of transactions + * that can be done with a coin. + */ +enum TALER_MINT_DB_TransactionType +{ +  /** +   * /deposit operation. +   */ +  TALER_MINT_DB_TT_DEPOSIT = 0, + +  /** +   * /refresh/melt operation. +   */ +  TALER_MINT_DB_TT_REFRESH_MELT = 1, + +  /** +   * /lock operation. +   */ +  TALER_MINT_DB_TT_LOCK = 2 +}; + + +/** + * List of transactions we performed for a particular coin. + */ +struct TALER_MINT_DB_TransactionList +{ + +  /** +   * Next pointer in the NULL-terminated linked list. +   */ +  struct TALER_MINT_DB_TransactionList *next; + +  /** +   * Type of the transaction, determines what is stored in @e details. +   */ +  enum TALER_MINT_DB_TransactionType type; + +  /** +   * Details about the transaction, depending on @e type. +   */ +  union +  { + +    /** +     * Details if transaction was a /deposit operation. +     */ +    struct Deposit *deposit; + +    /** +     * Details if transaction was a /refresh/melt operation. +     */ +    struct RefreshMelt *melt; + +    /** +     * Details if transaction was a /lock operation. +     */ +    struct Lock *lock; + +  } details; + +}; + + +/** + * Handle for a database session (per-thread, for transactions). + */ +struct TALER_MINTDB_Session; + + +/** + * The plugin API, returned from the plugin's "init" function. + * The argument given to "init" is simply a configuration handle. + */ +struct TALER_MINTDB_Plugin +{ + +  /** +   * Closure for all callbacks. +   */ +  void *cls; + +  /** +   * Get the thread-local database-handle. +   * Connect to the db if the connection does not exist yet. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the +   *        database default one +   * @param the database connection, or NULL on error +   */ +  struct TALER_MINTDB_Session * +  (*get_session) (void *cls, +                  int temporary); + + +  /** +   * Drop the temporary taler schema.  This is only useful for testcases. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure +   */ +  int +  (*drop_temporary) (void *cls, +                     struct TALER_MINTDB_Session *db); + + +  /** +   * Create the necessary tables if they are not present +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param temporary should we use a temporary schema +   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure +   */ +  int +  (*create_tables) (void *cls, +                    int temporary); + + +  /** +   * Start a transaction. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion connection to use +   * @return #GNUNET_OK on success +   */ +  int +  (*start) (void *cls, +            struct TALER_MINTDB_Session *sesssion); + + +  /** +   * Commit a transaction. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion connection to use +   * @return #GNUNET_OK on success +   */ +  int +  (*commit) (void *cls, +             struct TALER_MINTDB_Session *sesssion); + + +  /** +   * Abort/rollback a transaction. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion connection to use +   */ +  void +  (*rollback) (void *cls, +               struct TALER_MINTDB_Session *sesssion); + + +  /** +   * Get the summary of a reserve. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param db the database connection handle +   * @param reserve the reserve data.  The public key of the reserve should be set +   *          in this structure; it is used to query the database.  The balance +   *          and expiration are then filled accordingly. +   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure +   */ +  int +  (*reserve_get) (void *cls, +                  struct TALER_MINTDB_Session *db, +                  struct Reserve *reserve); + +  /* FIXME: add functions to add bank transfers to our DB +     (and to test if we already did add one) (#3633/#3717) */ + + +  /** +   * Insert a incoming transaction into reserves.  New reserves are also created +   * through this function. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param db the database connection handle +   * @param reserve the reserve structure.  The public key of the reserve should +   *          be set here.  Upon successful execution of this function, the +   *          balance and expiration of the reserve will be updated. +   * @param balance the amount that has to be added to the reserve +   * @param expiry the new expiration time for the reserve +   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures +   */ +  int +  (*reserves_in_insert) (void *cls, +                         struct TALER_MINTDB_Session *db, +                         struct Reserve *reserve, +                         const struct TALER_Amount *balance, +                         const struct GNUNET_TIME_Absolute expiry); + + +  /** +   * Locate the response for a /withdraw request under the +   * key of the hash of the blinded message. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection to use +   * @param h_blind hash of the blinded message +   * @param collectable corresponding collectable coin (blind signature) +   *                    if a coin is found +   * @return #GNUNET_SYSERR on internal error +   *         #GNUNET_NO if the collectable was not found +   *         #GNUNET_YES on success +   */ +  int +  (*get_collectable_blindcoin) (void *cls, +                                struct TALER_MINTDB_Session *sesssion, +                                const struct GNUNET_HashCode *h_blind, +                                struct CollectableBlindcoin *collectable); + + +  /** +   * Store collectable bit coin under the corresponding +   * hash of the blinded message. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection to use +   * @param h_blind hash of the blinded message +   * @param withdraw amount by which the reserve will be withdrawn with this +   *          transaction +   * @param collectable corresponding collectable coin (blind signature) +   *                    if a coin is found +   * @return #GNUNET_SYSERR on internal error +   *         #GNUNET_NO if the collectable was not found +   *         #GNUNET_YES on success +   */ +  int +  (*insert_collectable_blindcoin) (void *cls, +                                   struct TALER_MINTDB_Session *sesssion, +                                   const struct GNUNET_HashCode *h_blind, +                                   struct TALER_Amount withdraw, +                                   const struct CollectableBlindcoin *collectable); + + +  /** +   * Get all of the transaction history associated with the specified +   * reserve. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion connection to use +   * @param reserve_pub public key of the reserve +   * @return known transaction history (NULL if reserve is unknown) +   */ +  struct ReserveHistory * +  (*get_reserve_history) (void *cls, +                          struct TALER_MINTDB_Session *sesssion, +                          const struct TALER_ReservePublicKey *reserve_pub); + + +  /** +   * Free memory associated with the given reserve history. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param rh history to free. +   */ +  void +  (*free_reserve_history) (void *cls, +                           struct ReserveHistory *rh); + + +  /** +   * Check if we have the specified deposit already in the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param deposit deposit to search for +   * @return #GNUNET_YES if we know this operation, +   *         #GNUNET_NO if this deposit is unknown to us, +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*have_deposit) (void *cls, +                   struct TALER_MINTDB_Session *sesssion, +                   const struct Deposit *deposit); + + +  /** +   * Insert information about deposited coin into the +   * database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion connection to the database +   * @param deposit deposit information to store +   * @return #GNUNET_OK on success, #GNUNET_SYSERR on error +   */ +  int +  (*insert_deposit) (void *cls, +                     struct TALER_MINTDB_Session *sesssion, +                     const struct Deposit *deposit); + + +  /** +   * Lookup refresh session data under the given @a session_hash. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database handle to use +   * @param session_hash hash over the melt to use for the lookup +   * @param refresh_session[OUT] where to store the result +   * @return #GNUNET_YES on success, +   *         #GNUNET_NO if not found, +   *         #GNUNET_SYSERR on DB failure +   */ +  int +  (*get_refresh_session) (void *cls, +                          struct TALER_MINTDB_Session *sesssion, +                          const struct GNUNET_HashCode *session_hash, +                          struct RefreshSession *refresh_session); + + +  /** +   * Store new refresh session data under the given @a session_hash. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database handle to use +   * @param session_hash hash over the melt to use to locate the session +   * @param refresh_session session data to store +   * @return #GNUNET_YES on success, +   *         #GNUNET_SYSERR on DB failure +   */ +  int +  (*create_refresh_session) (void *cls, +                             struct TALER_MINTDB_Session *sesssion, +                             const struct GNUNET_HashCode *session_hash, +                             const struct RefreshSession *refresh_session); + + +  /** +   * Store the given /refresh/melt request in the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param oldcoin_index index of the coin to store +   * @param melt coin melt operation details to store; includes +   *             the session hash of the melt +   * @return #GNUNET_OK on success +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*insert_refresh_melt) (void *cls, +                          struct TALER_MINTDB_Session *sesssion, +                          uint16_t oldcoin_index, +                          const struct RefreshMelt *melt); + + +  /** +   * Get information about melted coin details from the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param session_hash hash to identify refresh session +   * @param oldcoin_index index of the coin to retrieve +   * @param melt melt data to fill in +   * @return #GNUNET_OK on success +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*get_refresh_melt) (void *cls, +                       struct TALER_MINTDB_Session *sesssion, +                       const struct GNUNET_HashCode *session_hash, +                       uint16_t oldcoin_index, +                       struct RefreshMelt *melt); + + +  /** +   * Store in the database which coin(s) we want to create +   * in a given refresh operation. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param session_hash hash to identify refresh session +   * @param num_newcoins number of coins to generate, size of the @a denom_pubs array +   * @param denom_pubs array denominations of the coins to create +   * @return #GNUNET_OK on success +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*insert_refresh_order) (void *cls, +                           struct TALER_MINTDB_Session *sesssion, +                           const struct GNUNET_HashCode *session_hash, +                           uint16_t num_newcoins, +                           const struct TALER_DenominationPublicKey *denom_pubs); + + +  /** +   * Lookup in the database for the @a num_newcoins coins that we want to +   * create in the given refresh operation. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param session_hash hash to identify refresh session +   * @param num_newcoins size of the @a denom_pubs array +   * @param denom_pubs[OUT] where to write @a num_newcoins denomination keys +   * @return #GNUNET_OK on success +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*get_refresh_order) (void *cls, +                        struct TALER_MINTDB_Session *sesssion, +                        const struct GNUNET_HashCode *session_hash, +                        uint16_t num_newcoins, +                        struct TALER_DenominationPublicKey *denom_pubs); + + +  /** +   * Store information about the commitments of the given index @a i +   * for the given refresh session in the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection to use +   * @param session_hash hash to identify refresh session +   * @param i set index (1st dimension), relating to #KAPPA +   * @param num_newcoins coin index size of the @a commit_coins array +   * @param commit_coin array of coin commitments to store +   * @return #GNUNET_OK on success +   *         #GNUNET_SYSERR on error +   */ +  int +  (*insert_refresh_commit_coins) (void *cls, +                                  struct TALER_MINTDB_Session *sesssion, +                                  const struct GNUNET_HashCode *session_hash, +                                  unsigned int i, +                                  unsigned int num_newcoins, +                                  const struct RefreshCommitCoin *commit_coins); + + +  /** +   * Obtain information about the commitment of the +   * given coin of the given refresh session from the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection to use +   * @param session_hash hash to identify refresh session +   * @param i set index (1st dimension) +   * @param num_coins size of the @a commit_coins array +   * @param commit_coin[OUT] array of coin commitments to return +   * @return #GNUNET_OK on success +   *         #GNUNET_NO if not found +   *         #GNUNET_SYSERR on error +   */ +  int +  (*get_refresh_commit_coins) (void *cls, +                               struct TALER_MINTDB_Session *sesssion, +                               const struct GNUNET_HashCode *session_hash, +                               unsigned int i, +                               unsigned int num_coins, +                               struct RefreshCommitCoin *commit_coins); + + +  /** +   * Store the commitment to the given (encrypted) refresh link data +   * for the given refresh session. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection to use +   * @param session_hash hash to identify refresh session +   * @param i set index (1st dimension), relating to #KAPPA +   * @param num_links size of the @a commit_link array +   * @param commit_links array of link information to store +   * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success +   */ +  int +  (*insert_refresh_commit_links) (void *cls, +                                  struct TALER_MINTDB_Session *sesssion, +                                  const struct GNUNET_HashCode *session_hash, +                                  unsigned int i, +                                  unsigned int num_links, +                                  const struct RefreshCommitLink *commit_links); + +  /** +   * Obtain the commited (encrypted) refresh link data +   * for the given refresh session. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection to use +   * @param session_hash hash to identify refresh session +   * @param i set index (1st dimension) +   * @param num_links size of the @links array to return +   * @param links[OUT] array link information to return +   * @return #GNUNET_SYSERR on internal error, +   *         #GNUNET_NO if commitment was not found +   *         #GNUNET_OK on success +   */ +  int +  (*get_refresh_commit_links) (void *cls, +                               struct TALER_MINTDB_Session *sesssion, +                               const struct GNUNET_HashCode *session_hash, +                               unsigned int i, +                               unsigned int num_links, +                               struct RefreshCommitLink *links); + + +  /** +   * Insert signature of a new coin generated during refresh into +   * the database indexed by the refresh session and the index +   * of the coin.  This data is later used should an old coin +   * be used to try to obtain the private keys during "/refresh/link". +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param session_hash hash to identify refresh session +   * @param newcoin_index coin index +   * @param ev_sig coin signature +   * @return #GNUNET_OK on success +   */ +  int +  (*insert_refresh_collectable) (void *cls, +                                 struct TALER_MINTDB_Session *sesssion, +                                 const struct GNUNET_HashCode *session_hash, +                                 uint16_t newcoin_index, +                                 const struct TALER_DenominationSignature *ev_sig); + + +  /** +   * Obtain the link data of a coin, that is the encrypted link +   * information, the denomination keys and the signatures. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param coin_pub public key to use to retrieve linkage data +   * @return all known link data for the coin +   */ +  struct LinkDataList * +  (*get_link_data_list) (void *cls, +                         struct TALER_MINTDB_Session *sesssion, +                         const struct TALER_CoinSpendPublicKey *coin_pub); + + +  /** +   * Free memory of the link data list. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param ldl link data list to release +   */ +  void +  (*free_link_data_list) (void *cls, +                          struct LinkDataList *ldl); + + +  /** +   * Obtain shared secret and transfer public key from the public key of +   * the coin.  This information and the link information returned by +   * #TALER_db_get_link() enable the owner of an old coin to determine +   * the private keys of the new coins after the melt. +   * +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param coin_pub public key of the coin +   * @param transfer_pub[OUT] public transfer key +   * @param shared_secret_enc[OUT] set to shared secret +   * @return #GNUNET_OK on success, +   *         #GNUNET_NO on failure (not found) +   *         #GNUNET_SYSERR on internal failure (database issue) +   */ +  int +  (*get_transfer) (void *cls, +                   struct TALER_MINTDB_Session *sesssion, +                   const struct TALER_CoinSpendPublicKey *coin_pub, +                   struct TALER_TransferPublicKey *transfer_pub, +                   struct TALER_EncryptedLinkSecret *shared_secret_enc); + + +  /** +   * Test if the given /lock request is known to us. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param lock lock operation +   * @return #GNUNET_YES if known, +   *         #GNUENT_NO if not, +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*have_lock) (void *cls, +                struct TALER_MINTDB_Session *sesssion, +                const struct Lock *lock); + + +  /** +   * Store the given /lock request in the database. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param lock lock operation +   * @return #GNUNET_OK on success +   *         #GNUNET_SYSERR on internal error +   */ +  int +  (*insert_lock) (void *cls, +                  struct TALER_MINTDB_Session *sesssion, +                  const struct Lock *lock); + + +  /** +   * Compile a list of all (historic) transactions performed +   * with the given coin (/refresh/melt and /deposit operations). +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param sesssion database connection +   * @param coin_pub coin to investigate +   * @return list of transactions, NULL if coin is fresh +   */ +  struct TALER_MINT_DB_TransactionList * +  (*get_coin_transactions) (void *cls, +                            struct TALER_MINTDB_Session *sesssion, +                            const struct TALER_CoinSpendPublicKey *coin_pub); + + +  /** +   * Free linked list of transactions. +   * +   * @param cls the @e cls of this struct with the plugin-specific state +   * @param list list to free +   */ +  void +  (*free_coin_transaction_list) (void *cls, +                                 struct TALER_MINT_DB_TransactionList *list); + + +}; + + +#endif /* _NEURO_MINT_DB_H */ diff --git a/src/mint/test_mint_common.c b/src/mint/test_mint_common.c index aa72dfdc..f6771474 100644 --- a/src/mint/test_mint_common.c +++ b/src/mint/test_mint_common.c @@ -31,8 +31,10 @@      if (cond) { GNUNET_break (0); goto EXITIF_exit; }             \    } while (0) +  int -main (int argc, const char *const argv[]) +main (int argc, +      const char *const argv[])  {    struct TALER_MINT_DenomKeyIssuePriv dki;    char *enc; @@ -41,25 +43,26 @@ main (int argc, const char *const argv[])    char *enc_read;    size_t enc_read_size;    char *tmpfile; -    int ret;    ret = 1;    enc = NULL;    enc_read = NULL;    tmpfile = NULL; -  dki.denom_priv = NULL; -  dki_read.denom_priv = NULL; +  dki.denom_priv.rsa_private_key = NULL; +  dki_read.denom_priv.rsa_private_key = NULL;    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,                                &dki.issue.signature,                                sizeof (dki) - offsetof (struct TALER_MINT_DenomKeyIssue,                                                         signature)); -  dki.denom_priv = GNUNET_CRYPTO_rsa_private_key_create (RSA_KEY_SIZE); -  enc_size = GNUNET_CRYPTO_rsa_private_key_encode (dki.denom_priv, &enc); +  dki.denom_priv.rsa_private_key +    = GNUNET_CRYPTO_rsa_private_key_create (RSA_KEY_SIZE); +  enc_size = GNUNET_CRYPTO_rsa_private_key_encode (dki.denom_priv.rsa_private_key, +                                                   &enc);    EXITIF (NULL == (tmpfile = GNUNET_DISK_mktemp ("test_mint_common")));    EXITIF (GNUNET_OK != TALER_MINT_write_denom_key (tmpfile, &dki));    EXITIF (GNUNET_OK != TALER_MINT_read_denom_key (tmpfile, &dki_read)); -  enc_read_size = GNUNET_CRYPTO_rsa_private_key_encode (dki_read.denom_priv, +  enc_read_size = GNUNET_CRYPTO_rsa_private_key_encode (dki_read.denom_priv.rsa_private_key,                                                          &enc_read);    EXITIF (enc_size != enc_read_size);    EXITIF (0 != memcmp (enc, @@ -75,9 +78,9 @@ main (int argc, const char *const argv[])      GNUNET_free (tmpfile);    }    GNUNET_free_non_null (enc_read); -  if (NULL != dki.denom_priv) -    GNUNET_CRYPTO_rsa_private_key_free (dki.denom_priv); -  if (NULL != dki_read.denom_priv) -    GNUNET_CRYPTO_rsa_private_key_free (dki_read.denom_priv); +  if (NULL != dki.denom_priv.rsa_private_key) +    GNUNET_CRYPTO_rsa_private_key_free (dki.denom_priv.rsa_private_key); +  if (NULL != dki_read.denom_priv.rsa_private_key) +    GNUNET_CRYPTO_rsa_private_key_free (dki_read.denom_priv.rsa_private_key);    return ret;  } diff --git a/src/mint/test_mint_db.c b/src/mint/test_mint_db.c index c18ad2e8..0b61818f 100644 --- a/src/mint/test_mint_db.c +++ b/src/mint/test_mint_db.c @@ -13,15 +13,13 @@    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/test_mint_db.c   * @brief test cases for DB interaction functions   * @author Sree Harsha Totakura <sreeharsha@totakura.in>   */ -  #include "platform.h" -#include "mint_db.h" +#include "plugin.h"  static int result; @@ -45,7 +43,7 @@ static int result;  /**   * Checks if the given reserve has the given amount of balance and expiry   * - * @param db the database connection + * @param session the database connection   * @param pub the public key of the reserve   * @param value balance value   * @param fraction balance fraction @@ -54,16 +52,22 @@ static int result;   * @return #GNUNET_OK if the given reserve has the same balance and expiration   *           as the given parameters; #GNUNET_SYSERR if not   */ -int -check_reserve (PGconn *db, -               struct GNUNET_CRYPTO_EddsaPublicKey *pub, -               uint32_t value, uint32_t fraction, const char *currency, +static int +check_reserve (struct TALER_MINTDB_Session *session, +               const struct TALER_ReservePublicKey *pub, +               uint32_t value, +               uint32_t fraction, +               const char *currency,                 uint64_t expiry)  {    struct Reserve reserve; -  reserve.pub = pub; -  FAILIF (GNUNET_OK != TALER_MINT_DB_reserve_get (db, &reserve)); +  reserve.pub = *pub; + +  FAILIF (GNUNET_OK != +          plugin->reserve_get (plugin->cls, +                               session, +                               &reserve));    FAILIF (value != reserve.balance.value);    FAILIF (fraction != reserve.balance.fraction);    FAILIF (0 != strcmp (currency, reserve.balance.currency)); @@ -77,27 +81,30 @@ check_reserve (PGconn *db,  struct DenomKeyPair  { -  struct GNUNET_CRYPTO_rsa_PrivateKey *priv; -  struct GNUNET_CRYPTO_rsa_PublicKey *pub; +  struct TALER_DenominationPrivateKey priv; +  struct TALER_DenominationPublicKey pub;  }; -struct DenomKeyPair * + +static struct DenomKeyPair *  create_denom_key_pair (unsigned int size)  {    struct DenomKeyPair *dkp;    dkp = GNUNET_new (struct DenomKeyPair); -  dkp->priv = GNUNET_CRYPTO_rsa_private_key_create (size); -  GNUNET_assert (NULL != dkp->priv); -  dkp->pub = GNUNET_CRYPTO_rsa_private_key_get_public (dkp->priv); +  dkp->priv.rsa_private_key = GNUNET_CRYPTO_rsa_private_key_create (size); +  GNUNET_assert (NULL != dkp->priv.rsa_private_key); +  dkp->pub.rsa_public_key +    = GNUNET_CRYPTO_rsa_private_key_get_public (dkp->priv.rsa_private_key);    return dkp;  } +  static void  destroy_denon_key_pair (struct DenomKeyPair *dkp)  { -  GNUNET_CRYPTO_rsa_public_key_free (dkp->pub); -  GNUNET_CRYPTO_rsa_private_key_free (dkp->priv); +  GNUNET_CRYPTO_rsa_public_key_free (dkp->pub.rsa_public_key); +  GNUNET_CRYPTO_rsa_private_key_free (dkp->priv.rsa_private_key);    GNUNET_free (dkp);  } @@ -107,14 +114,16 @@ destroy_denon_key_pair (struct DenomKeyPair *dkp)   * @param cls closure   * @param args remaining command-line arguments   * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param config configuration + * @param cfg configuration   */  static void -run (void *cls, char *const *args, const char *cfgfile, -     const struct GNUNET_CONFIGURATION_Handle *config) +run (void *cls, +     char *const *args, +     const char *cfgfile, +     const struct GNUNET_CONFIGURATION_Handle *cfg)  { -  PGconn *db; -  struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; +  struct TALER_MINTDB_Session *session; +  struct TALER_ReservePublicKey reserve_pub;    struct Reserve reserve;    struct GNUNET_TIME_Absolute expiry;    struct TALER_Amount amount; @@ -126,81 +135,134 @@ run (void *cls, char *const *args, const char *cfgfile,    struct ReserveHistory *rh_head;    struct BankTransfer *bt;    struct CollectableBlindcoin *withdraw; +  struct Deposit deposit; +  struct Deposit deposit2; +  struct json_t *wire; +  const char * const json_wire_str = +      "{ \"type\":\"SEPA\", \ +\"IBAN\":\"DE67830654080004822650\",                    \ +\"name\":\"GNUnet e.V.\",                               \ +\"bic\":\"GENODEF1SLR\",                                 \ +\"edate\":\"1449930207000\",                                \ +\"r\":123456789,                                     \ +\"address\": \"foobar\"}";    unsigned int cnt; -  db = NULL;    dkp = NULL;    rh = NULL; +  wire = NULL;    ZR_BLK (&cbc);    ZR_BLK (&cbc2); -  if (GNUNET_OK != TALER_MINT_DB_init ("postgres:///taler")) +  if (GNUNET_OK != +      TALER_MINT_plugin_load (cfg))    {      result = 1;      return;    } -  if (GNUNET_OK != TALER_MINT_DB_create_tables (GNUNET_YES)) +  if (GNUNET_OK != +      plugin->create_tables (plugin->cls, +                             GNUNET_YES))    {      result = 2;      goto drop;    } -  if (NULL == (db = TALER_MINT_DB_get_connection(GNUNET_YES))) +  if (NULL == +      (session = plugin->get_session (plugin->cls, +                                      GNUNET_YES)))    {      result = 3;      goto drop;    }    RND_BLK (&reserve_pub); -  reserve.pub = &reserve_pub; +  reserve.pub = reserve_pub;    amount.value = 1;    amount.fraction = 1;    strcpy (amount.currency, CURRENCY);    expiry = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),                                       GNUNET_TIME_UNIT_HOURS);    result = 4; -  FAILIF (GNUNET_OK != TALER_MINT_DB_reserves_in_insert (db, -                                                         &reserve, -                                                         amount, -                                                         expiry)); -  FAILIF (GNUNET_OK != check_reserve (db, -                                      &reserve_pub, -                                      amount.value, -                                      amount.fraction, -                                      amount.currency, -                                      expiry.abs_value_us)); -  FAILIF (GNUNET_OK != TALER_MINT_DB_reserves_in_insert (db, -                                                         &reserve, -                                                         amount, -                                                         expiry)); -  FAILIF (GNUNET_OK != check_reserve (db, -                                      &reserve_pub, -                                      ++amount.value, -                                      ++amount.fraction, -                                      amount.currency, -                                      expiry.abs_value_us)); +  FAILIF (GNUNET_OK != +          plugin->reserves_in_insert (plugin->cls, +                                      session, +                                      &reserve, +                                      &amount, +                                      expiry)); +  FAILIF (GNUNET_OK != +          check_reserve (session, +                         &reserve_pub, +                         amount.value, +                         amount.fraction, +                         amount.currency, +                         expiry.abs_value_us)); +  FAILIF (GNUNET_OK != +          plugin->reserves_in_insert (plugin->cls, +                                      session, +                                      &reserve, +                                      &amount, +                                      expiry)); +  FAILIF (GNUNET_OK != +          check_reserve (session, +                         &reserve_pub, +                         ++amount.value, +                         ++amount.fraction, +                         amount.currency, +                         expiry.abs_value_us));    dkp = create_denom_key_pair (1024);    RND_BLK(&h_blind);    RND_BLK(&cbc.reserve_sig);    cbc.denom_pub = dkp->pub; -  cbc.sig = GNUNET_CRYPTO_rsa_sign (dkp->priv, &h_blind, sizeof (h_blind)); -  (void) memcpy (&cbc.reserve_pub, &reserve_pub, sizeof (reserve_pub)); -  FAILIF (GNUNET_OK != TALER_MINT_DB_insert_collectable_blindcoin (db, -                                                                   &h_blind, -                                                                   &cbc)); -  FAILIF (GNUNET_YES != TALER_MINT_DB_get_collectable_blindcoin (db, -                                                                 &h_blind, -                                                                 &cbc2)); -  FAILIF (NULL == cbc2.denom_pub); -  FAILIF (0 != memcmp (&cbc2.reserve_sig, &cbc.reserve_sig, sizeof (cbc2.reserve_sig))); -  FAILIF (0 != memcmp (&cbc2.reserve_pub, &cbc.reserve_pub, sizeof (cbc2.reserve_pub))); -  FAILIF (GNUNET_OK != GNUNET_CRYPTO_rsa_verify (&h_blind, cbc2.sig, dkp->pub)); -  rh_head = rh = TALER_MINT_DB_get_reserve_history (db, &reserve_pub); +  cbc.sig.rsa_signature +    = GNUNET_CRYPTO_rsa_sign (dkp->priv.rsa_private_key, +                              &h_blind, +                              sizeof (h_blind)); +  (void) memcpy (&cbc.reserve_pub, +                 &reserve_pub, +                 sizeof (reserve_pub)); +  amount.value--; +  amount.fraction--; +  FAILIF (GNUNET_OK != +          plugin->insert_collectable_blindcoin (plugin->cls, +                                                session, +                                                &h_blind, +                                                amount, +                                                &cbc)); +  FAILIF (GNUNET_OK != +          check_reserve (session, +                         &reserve_pub, +                         amount.value, +                         amount.fraction, +                         amount.currency, +                         expiry.abs_value_us)); +  FAILIF (GNUNET_YES != +          plugin->get_collectable_blindcoin (plugin->cls, +                                             session, +                                             &h_blind, +                                             &cbc2)); +  FAILIF (NULL == cbc2.denom_pub.rsa_public_key); +  FAILIF (0 != memcmp (&cbc2.reserve_sig, +                       &cbc.reserve_sig, +                       sizeof (cbc2.reserve_sig))); +  FAILIF (0 != memcmp (&cbc2.reserve_pub, +                       &cbc.reserve_pub, +                       sizeof (cbc2.reserve_pub))); +  FAILIF (GNUNET_OK != +          GNUNET_CRYPTO_rsa_verify (&h_blind, +                                    cbc2.sig.rsa_signature, +                                    dkp->pub.rsa_public_key)); +  rh = plugin->get_reserve_history (plugin->cls, +                                    session, +                                    &reserve_pub);    FAILIF (NULL == rh); +  rh_head = rh;    for (cnt=0; NULL != rh_head; rh_head=rh_head->next, cnt++)    {      switch (rh_head->type)      {      case TALER_MINT_DB_RO_BANK_TO_MINT:        bt = rh_head->details.bank; -      FAILIF (0 != memcmp (&bt->reserve_pub, &reserve_pub, sizeof (reserve_pub))); +      FAILIF (0 != memcmp (&bt->reserve_pub, +                           &reserve_pub, +                           sizeof (reserve_pub)));        FAILIF (1 != bt->amount.value);        FAILIF (1 != bt->amount.fraction);        FAILIF (0 != strcmp (CURRENCY, bt->amount.currency)); @@ -217,28 +279,76 @@ run (void *cls, char *const *args, const char *cfgfile,      }    }    FAILIF (3 != cnt); +  /* Tests for deposits */ +  RND_BLK (&deposit.coin.coin_pub); +  deposit.coin.denom_pub = dkp->pub; +  deposit.coin.denom_sig = cbc.sig; +  RND_BLK (&deposit.csig); +  RND_BLK (&deposit.merchant_pub); +  RND_BLK (&deposit.h_contract); +  RND_BLK (&deposit.h_wire); +  wire = json_loads (json_wire_str, 0, NULL); +  deposit.wire = wire; +  deposit.transaction_id = +      GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); +  deposit.amount_with_fee = amount; +  FAILIF (GNUNET_OK != +          plugin->insert_deposit (plugin->cls, +                                  session, &deposit)); +  FAILIF (GNUNET_YES != +          plugin->have_deposit (plugin->cls, +                                session, +                                &deposit)); +  (void) memcpy (&deposit2, +                 &deposit, +                 sizeof (deposit)); +  deposit2.transaction_id++;     /* should fail if transaction id is different */ +  FAILIF (GNUNET_NO != +          plugin->have_deposit (plugin->cls, +                                session, +                                &deposit2)); +  deposit2.transaction_id = deposit.transaction_id; +  RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */ +  FAILIF (GNUNET_NO != +          plugin->have_deposit (plugin->cls, +                                session, +                                &deposit2)); +  (void) memcpy (&deposit2.merchant_pub, +                 &deposit.merchant_pub, +                 sizeof (deposit.merchant_pub)); +  RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */ +  FAILIF (GNUNET_NO != +          plugin->have_deposit (plugin->cls, +                                session, +                                &deposit2));    result = 0;   drop: +  if (NULL != wire) +    json_decref (wire);    if (NULL != rh) -    TALER_MINT_DB_free_reserve_history (rh); +    plugin->free_reserve_history (plugin->cls, +                                  rh);    rh = NULL; -  if (NULL != db) -    GNUNET_break (GNUNET_OK == TALER_MINT_DB_drop_temporary (db)); +  if (NULL != session) +    GNUNET_break (GNUNET_OK == +                  plugin->drop_temporary (plugin->cls, +                                          session));    if (NULL != dkp)      destroy_denon_key_pair (dkp); -  if (NULL != cbc.sig) -    GNUNET_CRYPTO_rsa_signature_free (cbc.sig); -  if (NULL != cbc2.denom_pub) -    GNUNET_CRYPTO_rsa_public_key_free (cbc2.denom_pub); -  if (NULL != cbc2.sig) -    GNUNET_CRYPTO_rsa_signature_free (cbc2.sig); +  if (NULL != cbc.sig.rsa_signature) +    GNUNET_CRYPTO_rsa_signature_free (cbc.sig.rsa_signature); +  if (NULL != cbc2.denom_pub.rsa_public_key) +    GNUNET_CRYPTO_rsa_public_key_free (cbc2.denom_pub.rsa_public_key); +  if (NULL != cbc2.sig.rsa_signature) +    GNUNET_CRYPTO_rsa_signature_free (cbc2.sig.rsa_signature);    dkp = NULL;  }  int -main (int argc, char *const argv[]) +main (int argc, +      char *const argv[])  {     static const struct GNUNET_GETOPT_CommandLineOption options[] = {      GNUNET_GETOPT_OPTION_END diff --git a/src/mint/test_mint_deposits.c b/src/mint/test_mint_deposits.c index 5ad8006e..4107c1ae 100644 --- a/src/mint/test_mint_deposits.c +++ b/src/mint/test_mint_deposits.c @@ -13,17 +13,15 @@    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/test_mint_deposits.c   * @brief testcase for mint deposits   * @author Sree Harsha Totakura <sreeharsha@totakura.in>   */ -  #include "platform.h"  #include <libpq-fe.h>  #include <gnunet/gnunet_util_lib.h> -#include "mint_db.h" +#include "plugin.h"  #include "db_pq.h"  #include "taler-mint-httpd.h" @@ -44,11 +42,6 @@  /** - * DB connection handle - */ -static PGconn *conn; - -/**   * Should we not interact with a temporary table?   */  static int persistent; @@ -59,64 +52,19 @@ static int persistent;  static int result; -int -TALER_MINT_DB_init_deposits (PGconn *conn, int tmp) -{ -  const char *tmp_str = (1 == tmp) ? "TEMPORARY" : ""; -  char *sql; -  PGresult *res; -  int ret; - -  res = NULL; -  (void) GNUNET_asprintf (&sql, -                          "CREATE %1$s TABLE IF NOT EXISTS deposits (" -                          " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)" -                          ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)" -                          ",transaction_id INT8 NOT NULL" -                          ",amount_value INT4 NOT NULL" -                          ",amount_fraction INT4 NOT NULL" -                          ",amount_currency VARCHAR(4) NOT NULL" -                          ",merchant_pub BYTEA NOT NULL" -                          ",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" -                          ")", -                          tmp_str); -  res = PQexec (conn, sql); -  GNUNET_free (sql); -  if (PGRES_COMMAND_OK != PQresultStatus (res)) -  { -    break_db_err (res); -    ret = GNUNET_SYSERR; -  } -  else -    ret = GNUNET_OK; -  PQclear (res); -  return ret; -} - - -static void -do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ -  if (NULL != conn) -    PQfinish (conn); -  conn = NULL; -} - -  /**   * Main function that will be run by the scheduler.   *   * @param cls closure   * @param args remaining command-line arguments   * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param config configuration + * @param cfg configuration   */  static void -run (void *cls, char *const *args, const char *cfgfile, -     const struct GNUNET_CONFIGURATION_Handle *config) +run (void *cls, +     char *const *args, +     const char *cfgfile, +     const struct GNUNET_CONFIGURATION_Handle *cfg)  {    static const char wire[] = "{"        "\"type\":\"SEPA\"," @@ -126,13 +74,16 @@ run (void *cls, char *const *args, const char *cfgfile,        "}";    struct Deposit *deposit;    uint64_t transaction_id; +  struct TALER_MINTDB_Session *session;    deposit = NULL; -  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, -                                &do_shutdown, NULL); -  EXITIF (NULL == (conn = PQconnectdb(DB_URI))); -  EXITIF (GNUNET_OK != TALER_MINT_DB_init_deposits (conn, !persistent)); -  EXITIF (GNUNET_OK != TALER_MINT_DB_prepare (conn)); +  EXITIF (GNUNET_OK != TALER_MINT_plugin_load (cfg)); +  EXITIF (GNUNET_OK != +          plugin->create_tables (plugin->cls, +                                 ! persistent)); +  session = plugin->get_session (plugin->cls, +                                 ! persistent); +  EXITIF (NULL == session);    deposit = GNUNET_malloc (sizeof (struct Deposit) + sizeof (wire));    /* Makeup a random coin public key */    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, @@ -143,28 +94,33 @@ run (void *cls, char *const *args, const char *cfgfile,                                               UINT64_MAX);    deposit->transaction_id = GNUNET_htonll (transaction_id);    /* Random amount */ -  deposit->amount.value = +  deposit->amount_with_fee.value =        htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)); -  deposit->amount.fraction = +  deposit->amount_with_fee.fraction =        htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)); -  GNUNET_assert (strlen (MINT_CURRENCY) < sizeof (deposit->amount.currency)); -  strcpy (deposit->amount.currency, MINT_CURRENCY); +  GNUNET_assert (strlen (MINT_CURRENCY) < sizeof (deposit->amount_with_fee.currency)); +  strcpy (deposit->amount_with_fee.currency, MINT_CURRENCY);    /* Copy wireformat */    deposit->wire = json_loads (wire, 0, NULL); -  EXITIF (GNUNET_OK != TALER_MINT_DB_insert_deposit (conn, -                                                     deposit)); -  EXITIF (GNUNET_OK != TALER_MINT_DB_have_deposit (conn, -                                                   deposit)); +  EXITIF (GNUNET_OK != +          plugin->insert_deposit (plugin->cls, +                                  session, +                                  deposit)); +  EXITIF (GNUNET_OK != +          plugin->have_deposit (plugin->cls, +                                session, +                                deposit));    result = GNUNET_OK;   EXITIF_exit:    GNUNET_free_non_null (deposit); -  GNUNET_SCHEDULER_shutdown ();    return;  } -int main(int argc, char *const argv[]) +int +main (int argc, +      char *const argv[])  {    static const struct GNUNET_GETOPT_CommandLineOption options[] = {      {'T', "persist", NULL, | 
