From c626ccac51dc968725b2ead1ecb0cebedd9987fc Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 28 Mar 2015 11:06:00 +0100 Subject: second round of renamefest --- configure.ac | 4 +- src/Makefile.am | 2 +- src/include/Makefile.am | 1 + src/include/taler_mintdb_lib.h | 207 +++ src/include/taler_mintdb_plugin.h | 5 +- src/lib/Makefile.am | 30 - src/lib/mint_api.c | 1154 ---------------- src/lib/test_mint_api.c | 211 --- src/mint-lib/Makefile.am | 30 + src/mint-lib/mint_api.c | 1154 ++++++++++++++++ src/mint-lib/test_mint_api.c | 211 +++ src/mint-tools/Makefile.am | 63 + src/mint-tools/taler-mint-dbinit.c | 106 ++ src/mint-tools/taler-mint-keycheck.c | 227 +++ src/mint-tools/taler-mint-keyup.c | 954 +++++++++++++ src/mint-tools/taler-mint-reservemod.c | 305 +++++ src/mint/Makefile.am | 128 +- src/mint/key_io.c | 347 ----- src/mint/key_io.h | 187 --- src/mint/plugin.c | 147 -- src/mint/plugin.h | 50 - src/mint/plugin_mintdb_common.c | 118 -- src/mint/plugin_mintdb_postgres.c | 2357 -------------------------------- src/mint/taler-mint-dbinit.c | 101 -- src/mint/taler-mint-httpd.c | 12 +- src/mint/taler-mint-httpd.h | 5 + src/mint/taler-mint-httpd_db.c | 128 +- src/mint/taler-mint-httpd_db.h | 1 - src/mint/taler-mint-httpd_deposit.c | 1 - src/mint/taler-mint-httpd_keystate.h | 2 +- src/mint/taler-mint-httpd_mhd.c | 1 - src/mint/taler-mint-keycheck.c | 227 --- src/mint/taler-mint-keyup.c | 954 ------------- src/mint/taler-mint-reservemod.c | 305 ----- src/mint/test_mint_common.c | 86 -- src/mint/test_mint_db.c | 393 ------ src/mint/test_mint_deposits.c | 142 -- src/mintdb/Makefile.am | 68 + src/mintdb/mintdb_keyio.c | 347 +++++ src/mintdb/mintdb_plugin.c | 149 ++ src/mintdb/plugin_mintdb_common.c | 118 ++ src/mintdb/plugin_mintdb_postgres.c | 2356 +++++++++++++++++++++++++++++++ src/mintdb/test_mintdb.c | 393 ++++++ src/mintdb/test_mintdb_deposits.c | 142 ++ src/mintdb/test_mintdb_keyio.c | 86 ++ 45 files changed, 7005 insertions(+), 7010 deletions(-) create mode 100644 src/include/taler_mintdb_lib.h delete mode 100644 src/lib/Makefile.am delete mode 100644 src/lib/mint_api.c delete mode 100644 src/lib/test_mint_api.c create mode 100644 src/mint-lib/Makefile.am create mode 100644 src/mint-lib/mint_api.c create mode 100644 src/mint-lib/test_mint_api.c create mode 100644 src/mint-tools/Makefile.am create mode 100644 src/mint-tools/taler-mint-dbinit.c create mode 100644 src/mint-tools/taler-mint-keycheck.c create mode 100644 src/mint-tools/taler-mint-keyup.c create mode 100644 src/mint-tools/taler-mint-reservemod.c delete mode 100644 src/mint/key_io.c delete mode 100644 src/mint/key_io.h delete mode 100644 src/mint/plugin.c delete mode 100644 src/mint/plugin.h delete mode 100644 src/mint/plugin_mintdb_common.c delete mode 100644 src/mint/plugin_mintdb_postgres.c delete mode 100644 src/mint/taler-mint-dbinit.c delete mode 100644 src/mint/taler-mint-keycheck.c delete mode 100644 src/mint/taler-mint-keyup.c delete mode 100644 src/mint/taler-mint-reservemod.c delete mode 100644 src/mint/test_mint_common.c delete mode 100644 src/mint/test_mint_db.c delete mode 100644 src/mint/test_mint_deposits.c create mode 100644 src/mintdb/Makefile.am create mode 100644 src/mintdb/mintdb_keyio.c create mode 100644 src/mintdb/mintdb_plugin.c create mode 100644 src/mintdb/plugin_mintdb_common.c create mode 100644 src/mintdb/plugin_mintdb_postgres.c create mode 100644 src/mintdb/test_mintdb.c create mode 100644 src/mintdb/test_mintdb_deposits.c create mode 100644 src/mintdb/test_mintdb_keyio.c diff --git a/configure.ac b/configure.ac index f69bcefd2..abc23c1f2 100644 --- a/configure.ac +++ b/configure.ac @@ -174,7 +174,9 @@ AC_CONFIG_FILES([Makefile src/include/Makefile src/util/Makefile src/pq/Makefile + src/mintdb/Makefile src/mint/Makefile - src/lib/Makefile + src/mint-tools/Makefile + src/mint-lib/Makefile ]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 0315beadc..a2d3ebcd9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,3 @@ # This Makefile.am is in the public domain AM_CPPFLAGS = -I$(top_srcdir)/src/include -SUBDIRS = include util pq mint lib +SUBDIRS = include util pq mintdb mint mint-tools mint-lib diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 4d4c6771e..5bb4cfa20 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -8,6 +8,7 @@ talerinclude_HEADERS = \ taler_json_lib.h \ taler_util.h \ taler_mint_service.h \ + taler_mintdb_lib.h \ taler_mintdb_plugin.h \ taler_pq_lib.h \ taler_signatures.h diff --git a/src/include/taler_mintdb_lib.h b/src/include/taler_mintdb_lib.h new file mode 100644 index 000000000..cceec3307 --- /dev/null +++ b/src/include/taler_mintdb_lib.h @@ -0,0 +1,207 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file include/taler_mintdb_lib.h + * @brief IO operations for the mint's private keys + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_MINTDB_LIB_H +#define TALER_MINTDB_LIB_H + +#include +#include "taler_signatures.h" + +/** + * Subdirectroy under the mint's base directory which contains + * the mint's signing keys. + */ +#define DIR_SIGNKEYS "signkeys" + +/** + * Subdirectory under the mint's base directory which contains + * the mint's denomination keys. + */ +#define DIR_DENOMKEYS "denomkeys" + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * On disk format used for a mint signing key. Signing keys are used + * by the mint to affirm its messages, but not to create coins. + * Includes the private key followed by the public information about + * the signing key. + */ +struct TALER_MintSigningKeyValidityPSPriv +{ + /** + * Private key part of the mint's signing key. + */ + struct TALER_MintPrivateKeyP signkey_priv; + + /** + * Public information about a mint signing key. + */ + struct TALER_MintSigningKeyValidityPS issue; +}; + + +GNUNET_NETWORK_STRUCT_END + + +/** + * All information about a denomination key (which is used to + * sign coins into existence). + */ +struct TALER_DenominationKeyIssueInformation +{ + /** + * The private key of the denomination. Will be NULL if the private + * key is not available (this is the case after the key has expired + * for signing coins, but is still valid for depositing coins). + */ + struct TALER_DenominationPrivateKey denom_priv; + + /** + * Decoded denomination public key (the hash of it is in + * @e issue, but we sometimes need the full public key as well). + */ + struct TALER_DenominationPublicKey denom_pub; + + /** + * Signed public information about a denomination key. + */ + struct TALER_DenominationKeyValidityPS issue; +}; + + +/** + * Iterator over signing keys. + * + * @param cls closure + * @param filename name of the file the key came from + * @param ski the sign key + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +typedef int +(*TALER_MINT_SignkeyIterator)(void *cls, + const char *filename, + const struct TALER_MintSigningKeyValidityPSPriv *ski); + + +/** + * Iterator over denomination keys. + * + * @param cls closure + * @param dki the denomination key + * @param alias coin alias + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +typedef int +(*TALER_MINT_DenomkeyIterator)(void *cls, + const char *alias, + const struct TALER_DenominationKeyIssueInformation *dki); + + + +/** + * Call @a it for each signing key found in the @a mint_base_dir. + * + * @param mint_base_dir base directory for the mint, + * the signing keys must be in the #DIR_SIGNKEYS + * subdirectory + * @param it function to call on each signing key + * @param it_cls closure for @a it + * @return number of files found (may not match + * number of keys given to @a it as malformed + * files are simply skipped), -1 on error + */ +int +TALER_MINT_signkeys_iterate (const char *mint_base_dir, + TALER_MINT_SignkeyIterator it, + void *it_cls); + + +/** + * Call @a it for each denomination key found in the @a mint_base_dir. + * + * @param mint_base_dir base directory for the mint, + * the signing keys must be in the #DIR_DENOMKEYS + * subdirectory + * @param it function to call on each denomination key found + * @param it_cls closure for @a it + * @return -1 on error, 0 if no files were found, otherwise + * a positive number (however, even with a positive + * number it is possible that @a it was never called + * as maybe none of the files were well-formed) + */ +int +TALER_MINT_denomkeys_iterate (const char *mint_base_dir, + TALER_MINT_DenomkeyIterator it, + void *it_cls); + + +/** + * Exports a denomination key to the given file. + * + * @param filename the file where to write the denomination key + * @param dki the denomination key + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure. + */ +int +TALER_MINT_write_denom_key (const char *filename, + const struct TALER_DenominationKeyIssueInformation *dki); + + +/** + * Import a denomination key from the given file. + * + * @param filename the file to import the key from + * @param[OUT] dki set to the imported denomination key + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +int +TALER_MINT_read_denom_key (const char *filename, + struct TALER_DenominationKeyIssueInformation *dki); + + +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return NULL on failure + */ +struct TALER_MINTDB_Plugin * +TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Shutdown the plugin. + * + * @param plugin plugin to unload + */ +void +TALER_MINT_plugin_unload (struct TALER_MINTDB_Plugin *plugin); + + + +#endif diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 8c9f4af23..7cd3e9202 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -479,9 +479,10 @@ struct TALER_MINTDB_Plugin void *cls; /** - * Name of the library which generated this plugin + * Name of the library which generated this plugin. Set by the + * plugin loader. */ - const char *library_name; + char *library_name; /** * Get the thread-local database-handle. diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am deleted file mode 100644 index f91ca32c1..000000000 --- a/src/lib/Makefile.am +++ /dev/null @@ -1,30 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -lib_LTLIBRARIES = \ - libtalermint.la - -libtalermint_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined - -libtalermint_la_SOURCES = \ - mint_api.c - -libtalermint_la_LIBADD = \ - -lgnunetutil \ - -ljansson \ - -lcurl - -check_PROGRAMS = \ - test_mint_api - -test_mint_api_SOURCES = \ - test_mint_api.c -test_mint_api_LDADD = \ - libtalermint.la \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil \ - -ljansson - diff --git a/src/lib/mint_api.c b/src/lib/mint_api.c deleted file mode 100644 index 019793b78..000000000 --- a/src/lib/mint_api.c +++ /dev/null @@ -1,1154 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see - -*/ - -/** - * @file mint/mint_api.c - * @brief Implementation of the client interface to mint's HTTP API - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include -#include -#include -#include "taler_mint_service.h" -#include "taler_signatures.h" - - -#define CURL_STRERROR(TYPE, FUNCTION, CODE) \ - GNUNET_log (TYPE, "cURL function `%s' has failed at `%s:%d' with error: %s", \ - FUNCTION, __FILE__, __LINE__, curl_easy_strerror (CODE)); - - - -/** - * Print JSON parsing related error information - */ -#define JSON_WARN(error) \ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ - "JSON parsing failed at %s:%u: %s (%s)", \ - __FILE__, __LINE__, error.text, error.source) - -/** - * Failsafe flag - */ -static int fail; - -/** - * Context - */ -struct TALER_MINT_Context -{ - /** - * CURL multi handle - */ - CURLM *multi; - - /** - * CURL share handle - */ - CURLSH *share; - - /** - * Perform task handle - */ - struct GNUNET_SCHEDULER_Task *perform_task; -}; - -/** - * Type of requests we currently have - */ -enum RequestType -{ - /** - * No request - */ - REQUEST_TYPE_NONE, - - /** - * Current request is to receive mint's keys - */ - REQUEST_TYPE_KEYSGET, - - /** - * Current request is to submit a deposit permission and get its status - */ - REQUEST_TYPE_DEPOSIT -}; - - -/** - * Handle to the mint - */ -struct TALER_MINT_Handle -{ - /** - * The context of this handle - */ - struct TALER_MINT_Context *ctx; - - /** - * The hostname of the mint - */ - char *hostname; - - /** - * The CURL handle - */ - CURL *curl; - - /** - * Error buffer for CURL - */ - char emsg[CURL_ERROR_SIZE]; - - /** - * Download buffer - */ - void *buf; - - /** - * The currently active request - */ - union { - /** - * Used to denote no request if set to NULL - */ - void *none; - - /** - * Denom keys get request if REQUEST_TYPE_KEYSGET - */ - struct TALER_MINT_KeysGetHandle *keys_get; - - /** - * Deposit request if REQUEST_TYPE_DEPOSIT - */ - struct TALER_MINT_DepositHandle *deposit; - } req; - - /** - * The size of the download buffer - */ - size_t buf_size; - - /** - * Active request type - */ - enum RequestType req_type; - - /** - * The service port of the mint - */ - uint16_t port; - - /** - * Are we connected to the mint? - */ - uint8_t connected; - -}; - - -/** - * A handle to get the keys of a mint - */ -struct TALER_MINT_KeysGetHandle -{ - /** - * The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this handle - */ - char *url; - - TALER_MINT_KeysGetCallback cb; - - void *cb_cls; - - TALER_MINT_ContinuationCallback cont_cb; - - void *cont_cls; -}; - - -/** - * A handle to submit a deposit permission and get its status - */ -struct TALER_MINT_DepositHandle -{ - /** - *The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this handle - */ - char *url; - - TALER_MINT_DepositResultCallback cb; - - void *cb_cls; - - char *json_enc; - - struct curl_slist *headers; - -}; - - -/** - * Parses the timestamp encoded as ASCII string as UNIX timstamp. - * - * @param abs successfully parsed timestamp will be returned thru this parameter - * @param tstamp_enc the ASCII encoding of the timestamp - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static int -parse_timestamp (struct GNUNET_TIME_Absolute *abs, - const char *tstamp_enc) -{ - unsigned long tstamp; - - if (1 != sscanf (tstamp_enc, "%lu", &tstamp)) - return GNUNET_SYSERR; - *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (), - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, tstamp)); - return GNUNET_OK; -} - - -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - - -static int -parse_json_signkey (struct TALER_MINT_SigningPublicKey **_sign_key, - json_t *sign_key_obj, - struct TALER_MasterPublicKeyP *master_key) -{ - json_t *valid_from_obj; - json_t *valid_until_obj; - json_t *key_obj; - json_t *sig_obj; - const char *valid_from_enc; - const char *valid_until_enc; - const char *key_enc; - const char *sig_enc; - struct TALER_MINT_SigningPublicKey *sign_key; - struct TALER_MintSigningKeyValidityPS sign_key_issue; - struct GNUNET_CRYPTO_EddsaSignature sig; - struct GNUNET_TIME_Absolute valid_from; - struct GNUNET_TIME_Absolute valid_until; - - EXITIF (JSON_OBJECT != json_typeof (sign_key_obj)); - EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj, - "stamp_start"))); - EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj, - "stamp_expire"))); - EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key"))); - EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig"))); - EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj))); - EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj))); - EXITIF (NULL == (key_enc = json_string_value (key_obj))); - EXITIF (NULL == (sig_enc = json_string_value (sig_obj))); - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, - valid_from_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until, - valid_until_enc)); - EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */ - EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */ - EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, - &sig, sizeof (sig))); - (void) memset (&sign_key_issue, 0, sizeof (sign_key_issue)); - EXITIF (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc, - 52, - &sign_key_issue.signkey_pub.eddsa_pub)); - sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY); - sign_key_issue.purpose.size = - htonl (sizeof (sign_key_issue) - - offsetof (struct TALER_MintSigningKeyValidityPS, purpose)); - sign_key_issue.master_public_key = *master_key; - sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from); - sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until); - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY, - &sign_key_issue.purpose, - &sig, - &master_key->eddsa_pub)); - sign_key = GNUNET_new (struct TALER_MINT_SigningPublicKey); - sign_key->valid_from = valid_from; - sign_key->valid_until = valid_until; - sign_key->key = sign_key_issue.signkey_pub; - *_sign_key = sign_key; - return GNUNET_OK; - - EXITIF_exit: - return GNUNET_SYSERR; -} - - -static int -parse_json_amount (json_t *amount_obj, struct TALER_Amount *amt) -{ - json_t *obj; - const char *currency_str; - int value; - int fraction; - - EXITIF (NULL == (obj = json_object_get (amount_obj, "currency"))); - EXITIF (NULL == (currency_str = json_string_value (obj))); - EXITIF (NULL == (obj = json_object_get (amount_obj, "value"))); - EXITIF (JSON_INTEGER != json_typeof (obj)); - EXITIF (0 > (value = json_integer_value (obj))); - EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction"))); - EXITIF (JSON_INTEGER != json_typeof (obj)); - EXITIF (0 > (fraction = json_integer_value (obj))); - (void) memset (amt->currency, 0, sizeof (amt->currency)); - (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1); - amt->value = (uint32_t) value; - amt->fraction = (uint32_t) fraction; - return GNUNET_OK; - - EXITIF_exit: - return GNUNET_SYSERR; -} - - -/* FIXME: avoid useless ** for _denom_key! */ -static int -parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key, - json_t *denom_key_obj, - struct TALER_MasterPublicKeyP *master_key) -{ - json_t *obj; - const char *sig_enc; - const char *deposit_valid_until_enc; - const char *withdraw_valid_until_enc; - const char *valid_from_enc; - const char *key_enc; - char *buf; - size_t buf_size; - struct TALER_MINT_DenomPublicKey *denom_key; - struct GNUNET_TIME_Absolute valid_from; - struct GNUNET_TIME_Absolute withdraw_valid_until; - struct GNUNET_TIME_Absolute deposit_valid_until; - struct TALER_Amount value; - struct TALER_Amount fee_withdraw; - struct TALER_Amount fee_deposit; - struct TALER_Amount fee_refresh; - struct TALER_DenominationKeyValidityPS denom_key_issue; - struct GNUNET_CRYPTO_rsa_PublicKey *pk; - struct GNUNET_CRYPTO_EddsaSignature sig; - - EXITIF (JSON_OBJECT != json_typeof (denom_key_obj)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig"))); - EXITIF (NULL == (sig_enc = json_string_value (obj))); - EXITIF (103 != strlen (sig_enc)); - EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, - &sig, sizeof (sig))); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit"))); - EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj))); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw"))); - EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj))); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start"))); - EXITIF (NULL == (valid_from_enc = json_string_value (obj))); - - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub"))); - EXITIF (NULL == (key_enc = json_string_value (obj))); - - EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until, - withdraw_valid_until_enc)); - EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until, - deposit_valid_until_enc)); - - memset (&denom_key_issue, 0, sizeof (denom_key_issue)); - - buf_size = (strlen (key_enc) * 5) / 8; - buf = GNUNET_malloc (buf_size); - - EXITIF (GNUNET_OK != - GNUNET_STRINGS_string_to_data (key_enc, strlen (key_enc), - buf, - buf_size)); - pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); - GNUNET_free (buf); - - EXITIF (NULL == pk); - GNUNET_CRYPTO_rsa_public_key_hash (pk, - &denom_key_issue.denom_hash); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit)); - EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh"))); - EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh)); - denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY); - denom_key_issue.purpose.size = htonl - (sizeof (struct TALER_DenominationKeyValidityPS) - - offsetof (struct TALER_DenominationKeyValidityPS, purpose)); - denom_key_issue.master = *master_key; - denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from); - denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until); - denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until); - TALER_amount_hton (&denom_key_issue.value, - &value); - TALER_amount_hton (&denom_key_issue.fee_withdraw, - &fee_withdraw); - TALER_amount_hton (&denom_key_issue.fee_deposit, - &fee_deposit); - TALER_amount_hton (&denom_key_issue.fee_refresh, - &fee_refresh); - EXITIF (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY, - &denom_key_issue.purpose, - &sig, - &master_key->eddsa_pub)); - denom_key = GNUNET_new (struct TALER_MINT_DenomPublicKey); - denom_key->key.rsa_public_key = pk; - denom_key->valid_from = valid_from; - denom_key->withdraw_valid_until = withdraw_valid_until; - denom_key->deposit_valid_until = deposit_valid_until; - denom_key->value = value; - denom_key->fee_withdraw = fee_withdraw; - denom_key->fee_deposit = fee_deposit; - denom_key->fee_refresh = fee_refresh; - *_denom_key = denom_key; - return GNUNET_OK; - - EXITIF_exit: - return GNUNET_SYSERR; -} - - -static int -parse_response_keys_get (const char *in, size_t size, - struct TALER_MINT_SigningPublicKey ***_sign_keys, - unsigned int *_n_sign_keys, - struct TALER_MINT_DenomPublicKey ***_denom_keys, - unsigned int *_n_denom_keys) -{ - json_t *resp_obj; - struct TALER_MINT_DenomPublicKey **denom_keys; - struct TALER_MasterPublicKeyP master_key; - struct GNUNET_TIME_Absolute list_issue_date; - struct TALER_MINT_SigningPublicKey **sign_keys; - unsigned int n_denom_keys; - unsigned int n_sign_keys; - json_error_t error; - unsigned int index; - int OK; - - denom_keys = NULL; - n_denom_keys = 0; - sign_keys = NULL; - n_sign_keys = 0; - OK = 0; - resp_obj = json_loadb (in, size, - JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, - &error); - if (NULL == resp_obj) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse received data as JSON object\n"); - return GNUNET_SYSERR; - } - - EXITIF (JSON_OBJECT != json_typeof (resp_obj)); - { - /* parse the master public key */ - json_t *master_key_obj; - const char *master_key_enc; - - EXITIF (NULL == (master_key_obj = json_object_get (resp_obj, "TMH_master_public_key"))); - EXITIF (NULL == (master_key_enc = json_string_value (master_key_obj))); - EXITIF (52 != strlen (master_key_enc)); /* strlen(base32(char[32])) = 52 */ - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc, - 52, - &master_key.eddsa_pub)); - } - { - /* parse the issue date of the response */ - json_t *list_issue_date_obj; - const char *tstamp_enc; - - EXITIF (NULL == (list_issue_date_obj = - json_object_get(resp_obj, "list_issue_date"))); - EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj))); - EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date, tstamp_enc)); - } - { - /* parse the signing keys */ - json_t *sign_keys_array; - json_t *sign_key_obj; - - EXITIF (NULL == (sign_keys_array = - json_object_get (resp_obj, "signkeys"))); - EXITIF (JSON_ARRAY != json_typeof (sign_keys_array)); - EXITIF (0 == (n_sign_keys = json_array_size (sign_keys_array))); - sign_keys = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey *) - * (n_sign_keys + 1)); - index = 0; - json_array_foreach (sign_keys_array, index, sign_key_obj) { - EXITIF (GNUNET_SYSERR == parse_json_signkey (&sign_keys[index], - sign_key_obj, - &master_key)); - } - } - { - /* parse the denomination keys */ - json_t *denom_keys_array; - json_t *denom_key_obj; - - EXITIF (NULL == (denom_keys_array = json_object_get (resp_obj, "denoms"))); - EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); - EXITIF (0 == (n_denom_keys = json_array_size (denom_keys_array))); - denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey *) - * (n_denom_keys + 1)); - index = 0; - json_array_foreach (denom_keys_array, index, denom_key_obj) { - EXITIF (GNUNET_SYSERR == parse_json_denomkey (&denom_keys[index], - denom_key_obj, - &master_key)); - } - } - OK = 1; - - EXITIF_exit: - json_decref (resp_obj); - if (!OK) - { - if (NULL != sign_keys) - { - for (index=0; NULL != sign_keys[index]; index++) - GNUNET_free_non_null (sign_keys[index]); - GNUNET_free (sign_keys); - } - if (NULL != denom_keys) - { - for (index=0; NULL != denom_keys[index]; index++) - GNUNET_free_non_null (denom_keys[index]); - GNUNET_free (denom_keys); - } - return GNUNET_SYSERR; - } - - *_sign_keys = sign_keys; - *_n_sign_keys = n_sign_keys; - *_denom_keys = denom_keys; - *_n_denom_keys = n_denom_keys; - return GNUNET_OK; -} - - -int -parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj) -{ - json_t *obj; - const char *status_str; - json_error_t error; - - status_str = NULL; - obj = NULL; - obj = json_loadb (buf, size, - JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, &error); - if (NULL == obj) - { - JSON_WARN (error); - return GNUNET_SYSERR; - } - EXITIF (-1 == json_unpack (obj, "{s:s}", "status", &status_str)); - TALER_LOG_DEBUG ("Received deposit response: %s from mint\n", status_str); - if (0 == strcmp ("DEPOSIT_OK", status_str)) - *r_status = 1; - else if (0 == strcmp ("DEPOSIT_QUEUED", status_str)) - *r_status = 2; - else - *r_status = 0; - *r_obj = obj; - - return GNUNET_OK; - EXITIF_exit: - json_decref (obj); - return GNUNET_SYSERR; -} - -#undef EXITIF - -static void -mint_connect (struct TALER_MINT_Handle *mint) -{ - struct TALER_MINT_Context *ctx = mint->ctx; - - GNUNET_assert (0 == mint->connected); - GNUNET_assert (CURLM_OK == curl_multi_add_handle (ctx->multi, mint->curl)); - mint->connected = GNUNET_YES; -} - -static void -mint_disconnect (struct TALER_MINT_Handle *mint) -{ - struct TALER_MINT_Context *ctx = mint->ctx; - - GNUNET_assert (GNUNET_YES == mint->connected); - GNUNET_break (CURLM_OK == curl_multi_remove_handle (ctx->multi, - mint->curl)); - mint->connected = GNUNET_NO; - GNUNET_free_non_null (mint->buf); - mint->buf = NULL; - mint->buf_size = 0; - mint->req_type = REQUEST_TYPE_NONE; - mint->req.none = NULL; -} - -static void -cleanup_keys_get (struct TALER_MINT_KeysGetHandle *gh) -{ - GNUNET_free (gh->url); - GNUNET_free (gh); -} - -static void -cleanup_deposit (struct TALER_MINT_DepositHandle *dh) -{ - curl_slist_free_all (dh->headers); - GNUNET_free_non_null (dh->json_enc); - GNUNET_free (dh->url); - GNUNET_free (dh); -} - -static void -request_failed (struct TALER_MINT_Handle *mint, long resp_code) -{ - switch (mint->req_type) - { - case REQUEST_TYPE_NONE: - GNUNET_assert (0); - break; - case REQUEST_TYPE_KEYSGET: - { - struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get; - TALER_MINT_ContinuationCallback cont_cb; - void *cont_cls; - GNUNET_assert (NULL != gh); - cont_cb = gh->cont_cb; - cont_cls = gh->cont_cls; - cleanup_keys_get (gh); - mint_disconnect (mint); - cont_cb (cont_cls, mint->emsg); - } - break; - case REQUEST_TYPE_DEPOSIT: - { - struct TALER_MINT_DepositHandle *dh = mint->req.deposit; - TALER_MINT_DepositResultCallback cb = dh->cb; - void *cls = dh->cb_cls; - - GNUNET_assert (NULL != dh); - cleanup_deposit (dh); - mint_disconnect (mint); - cb (cls, 0, NULL, mint->emsg); - } - break; - } -} - -static void -request_succeeded (struct TALER_MINT_Handle *mint, long resp_code) -{ - char *emsg; - - emsg = NULL; - switch (mint->req_type) - { - case REQUEST_TYPE_NONE: - GNUNET_assert (0); - break; - case REQUEST_TYPE_KEYSGET: - { - struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get; - TALER_MINT_ContinuationCallback cont_cb; - void *cont_cls; - struct TALER_MINT_SigningPublicKey **sign_keys; - struct TALER_MINT_DenomPublicKey **denom_keys; - unsigned int n_sign_keys; - unsigned int n_denom_keys; - - GNUNET_assert (NULL != gh); - cont_cb = gh->cont_cb; - cont_cls = gh->cont_cls; - if (200 == resp_code) - { - /* parse JSON object from the mint->buf which is of size mint->buf_size */ - if (GNUNET_OK == - parse_response_keys_get (mint->buf, mint->buf_size, - &sign_keys, &n_sign_keys, - &denom_keys, &n_denom_keys)) - gh->cb (gh->cb_cls, sign_keys, denom_keys); - else - emsg = GNUNET_strdup ("Error parsing response"); - } - else - GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code); - cleanup_keys_get (gh); - mint_disconnect (mint); - cont_cb (cont_cls, emsg); - } - break; - case REQUEST_TYPE_DEPOSIT: - { - struct TALER_MINT_DepositHandle *dh = mint->req.deposit; - TALER_MINT_DepositResultCallback cb; - void *cls; - int status; - json_t *obj; - - GNUNET_assert (NULL != dh); - obj = NULL; - cb = dh->cb; - cls = dh->cb_cls; - status = 0; - if (200 == resp_code) - { - /* parse JSON object from the mint->buf which is of size mint->buf_size */ - if (GNUNET_OK != - parse_deposit_response (mint->buf, mint->buf_size, - &status, &obj)) - emsg = GNUNET_strdup ("Error parsing response"); - } - else - GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code); - cleanup_deposit (dh); - mint_disconnect (mint); - cb (cls, status, obj, emsg); - } - break; - } - GNUNET_free_non_null (emsg); -} - - -static void -do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); - -static void -perform (struct TALER_MINT_Context *ctx) -{ - fd_set fd_rs; - fd_set fd_ws; - struct GNUNET_NETWORK_FDSet rs; - struct GNUNET_NETWORK_FDSet ws; - CURLMsg *cmsg; - struct TALER_MINT_Handle *mint; - long timeout; - long resp_code; - static unsigned int n_old; - int n_running; - int n_completed; - int max_fd; - - n_completed = 0; - curl_multi_perform (ctx->multi, &n_running); - GNUNET_assert (0 <= n_running); - if ((0 == n_running) || (n_running < n_old)) - { - /* some requests were completed -- handle them */ - while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed))) - { - GNUNET_break (CURLMSG_DONE == cmsg->msg); /* curl only has CURLMSG_DONE */ - GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, - CURLINFO_PRIVATE, - (char *) &mint)); - GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, - CURLINFO_RESPONSE_CODE, - &resp_code)); - GNUNET_assert (ctx == mint->ctx); /* did we get the correct one? */ - if (CURLE_OK == cmsg->data.result) - request_succeeded (mint, resp_code); - else - request_failed (mint, resp_code); - } - } - n_old = n_running; - /* reschedule perform() */ - if (0 != n_old) - { - FD_ZERO (&fd_rs); - FD_ZERO (&fd_ws); - GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi, - &fd_rs, - &fd_ws, - NULL, - &max_fd)); - if (-1 == max_fd) - { - ctx->perform_task = GNUNET_SCHEDULER_add_delayed - (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100), - &do_perform, ctx); - return; - } - GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, &timeout)); - if (-1 == timeout) - { - timeout = 1000 * 60 * 5; - } - GNUNET_NETWORK_fdset_zero (&rs); - GNUNET_NETWORK_fdset_zero (&ws); - GNUNET_NETWORK_fdset_copy_native (&rs, &fd_rs, max_fd + 1); - GNUNET_NETWORK_fdset_copy_native (&ws, &fd_ws, max_fd + 1); - ctx->perform_task = GNUNET_SCHEDULER_add_select - (GNUNET_SCHEDULER_PRIORITY_KEEP, - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout), - &rs, &ws, - &do_perform, ctx); - } -} - - -static void -do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct TALER_MINT_Context *ctx = cls; - - GNUNET_assert (NULL != ctx->perform_task); - ctx->perform_task = NULL; - perform (ctx); -} - -static void -perform_now (struct TALER_MINT_Context *ctx) -{ - if (NULL != ctx->perform_task) - { - GNUNET_SCHEDULER_cancel (ctx->perform_task); - ctx->perform_task = NULL; - } - ctx->perform_task = GNUNET_SCHEDULER_add_now (&do_perform, ctx); -} - - -/* This function gets called by libcurl as soon as there is data received that */ -/* needs to be saved. The size of the data pointed to by ptr is size */ -/* multiplied with nmemb, it will not be zero terminated. Return the number */ -/* of bytes actually taken care of. If that amount differs from the amount passed */ -/* to your function, it'll signal an error to the library. This will abort the */ -/* transfer and return CURLE_WRITE_ERROR. */ - -/* From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will */ -/* cause writing to this connection to become paused. See */ -/* curl_easy_pause(3) for further details. */ - -/* This function may be called with zero bytes data if the transferred file is */ -/* empty. */ - -/* Set this option to NULL to get the internal default function. The internal */ -/* default function will write the data to the FILE * given with */ -/* CURLOPT_WRITEDATA. */ - -/* Set the userdata argument with the CURLOPT_WRITEDATA option. */ - -/* The callback function will be passed as much data as possible in all invokes, */ -/* but you cannot possibly make any assumptions. It may be one byte, it may be */ -/* thousands. The maximum amount of body data that can be passed to the write */ -/* callback is defined in the curl.h header file: CURL_MAX_WRITE_SIZE (the usual */ -/* default is 16K). If you however have CURLOPT_HEADER set, which sends */ -/* header data to the write callback, you can get up to */ -/* CURL_MAX_HTTP_HEADER bytes of header data passed into it. This usually */ -/* means 100K. */ -static size_t -download (char *bufptr, size_t size, size_t nitems, void *cls) -{ - struct TALER_MINT_Handle *mint = cls; - size_t msize; - void *buf; - - if (0 == size * nitems) - { - /* file is empty */ - return 0; - } - msize = size * nitems; - mint->buf = GNUNET_realloc (mint->buf, mint->buf_size + msize); - buf = mint->buf + mint->buf_size; - memcpy (buf, bufptr, msize); - mint->buf_size += msize; - return msize; -} - - -/** - * Initialise a connection to the mint. - * - * @param ctx the context - * @param hostname the hostname of the mint - * @param port the point where the mint's HTTP service is running. - * @param mint_key the offline master public key of the mint. - * This is used to verify the responses of the mint. - * @return the mint handle; NULL upon error - */ -struct TALER_MINT_Handle * -TALER_MINT_connect (struct TALER_MINT_Context *ctx, - const char *hostname, - uint16_t port, - const struct TALER_MasterPublicKeyP *mint_key) -{ - struct TALER_MINT_Handle *mint; - - mint = GNUNET_new (struct TALER_MINT_Handle); - mint->ctx = ctx; - mint->hostname = GNUNET_strdup (hostname); - mint->port = (0 != port) ? port : 80; - mint->curl = curl_easy_init (); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_SHARE, ctx->share)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_ERRORBUFFER, mint->emsg)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_WRITEFUNCTION, &download)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_WRITEDATA, mint)); - GNUNET_assert (CURLE_OK == curl_easy_setopt (mint->curl, CURLOPT_PRIVATE, mint)); - return mint; -} - -/** - * Disconnect from the mint - * - * @param mint the mint handle - */ -void -TALER_MINT_disconnect (struct TALER_MINT_Handle *mint) -{ - if (GNUNET_YES == mint->connected) - mint_disconnect (mint); - curl_easy_cleanup (mint->curl); - GNUNET_free (mint->hostname); - GNUNET_free (mint); -} - -/** - * Get the signing and denomination key of the mint. - * - * @param mint handle to the mint - * @param cb the callback to call with each retrieved denomination key - * @param cb_cls closure for the above callback - * @param cont_cb the callback to call after completing this asynchronous call - * @param cont_cls the closure for the continuation callback - * @return a handle to this asynchronous call; NULL upon eror - */ -struct TALER_MINT_KeysGetHandle * -TALER_MINT_keys_get (struct TALER_MINT_Handle *mint, - TALER_MINT_KeysGetCallback cb, - void *cb_cls, - TALER_MINT_ContinuationCallback cont_cb, - void *cont_cls) -{ - struct TALER_MINT_KeysGetHandle *gh; - - GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type); - gh = GNUNET_new (struct TALER_MINT_KeysGetHandle); - gh->mint = mint; - mint->req_type = REQUEST_TYPE_KEYSGET; - mint->req.keys_get = gh; - gh->cb = cb; - gh->cb_cls = cb_cls; - gh->cont_cb = cont_cb; - gh->cont_cls = cont_cls; - GNUNET_asprintf (&gh->url, - "http://%s:%hu/keys", - mint->hostname, - mint->port); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, - CURLOPT_URL, - gh->url)); - if (GNUNET_NO == mint->connected) - mint_connect (mint); - perform_now (mint->ctx); - return gh; -} - - -/** - * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This - * should not be called if either of the @a TALER_MINT_KeysGetCallback or - * @a TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have - * been called. - * - * @param get the handle for retrieving the keys - */ -void -TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get) -{ - struct TALER_MINT_Handle *mint = get->mint; - - mint_disconnect (mint); - cleanup_keys_get (get); -} - -/** - * Submit a deposit permission to the mint and get the mint's response - * - * @param mint the mint handle - * @param cb the callback to call when a reply for this request is available - * @param cb_cls closure for the above callback - * @param deposit_obj the deposit permission received from the customer along - * with the wireformat JSON object - * @return a handle for this request; NULL if the JSON object could not be - * parsed or is of incorrect format or any other error. In this case, - * the callback is not called. - */ -struct TALER_MINT_DepositHandle * -TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint, - TALER_MINT_DepositResultCallback cb, - void *cb_cls, - json_t *deposit_obj) -{ - struct TALER_MINT_DepositHandle *dh; - - GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type); - dh = GNUNET_new (struct TALER_MINT_DepositHandle); - dh->mint = mint; - mint->req_type = REQUEST_TYPE_DEPOSIT; - mint->req.deposit = dh; - dh->cb = cb; - dh->cb_cls = cb_cls; - GNUNET_asprintf (&dh->url, "http://%s:%hu/deposit", mint->hostname, mint->port); - GNUNET_assert (NULL != (dh->json_enc = json_dumps (deposit_obj, JSON_COMPACT))); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_URL, dh->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDS, - dh->json_enc)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDSIZE, - strlen (dh->json_enc))); - GNUNET_assert (NULL != (dh->headers = - curl_slist_append (dh->headers, "Content-Type: application/json"))); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (mint->curl, CURLOPT_HTTPHEADER, dh->headers)); - if (GNUNET_NO == mint->connected) - mint_connect (mint); - perform_now (mint->ctx); - return dh; -} - - -/** - * Cancel a deposit permission request. This function cannot be used on a - * request handle if a response is already served for it. - * - * @param the deposit permission request handle - */ -void -TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit) -{ - struct TALER_MINT_Handle *mint = deposit->mint; - - mint_disconnect (mint); - cleanup_deposit (deposit); -} - - -/** - * Initialise this library. This function should be called before using any of - * the following functions. - * - * @return library context - */ -struct TALER_MINT_Context * -TALER_MINT_init () -{ - struct TALER_MINT_Context *ctx; - CURLM *multi; - CURLSH *share; - - if (fail) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "cURL was not initialised properly\n"); - return NULL; - } - if (NULL == (multi = curl_multi_init ())) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL multi handle\n"); - return NULL; - } - if (NULL == (share = curl_share_init ())) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL share handle\n"); - return NULL; - } - ctx = GNUNET_new (struct TALER_MINT_Context); - ctx->multi = multi; - ctx->share = share; - return ctx; -} - - -/** - * Cleanup library initialisation resources. This function should be called - * after using this library to cleanup the resources occupied during library's - * initialisation. - * - * @param ctx the library context - */ -void -TALER_MINT_cleanup (struct TALER_MINT_Context *ctx) -{ - curl_share_cleanup (ctx->share); - curl_multi_cleanup (ctx->multi); - if (NULL != ctx->perform_task) - { - GNUNET_break (0); /* investigate why this happens */ - GNUNET_SCHEDULER_cancel (ctx->perform_task); - } - GNUNET_free (ctx); -} - - -__attribute__ ((constructor)) -void -TALER_MINT_constructor__ (void) -{ - CURLcode ret; - if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT))) - { - CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, "curl_global_init", ret); - fail = 1; - } -} - -__attribute__ ((destructor)) -void -TALER_MINT_destructor__ (void) -{ - if (fail) - return; - curl_global_cleanup (); -} diff --git a/src/lib/test_mint_api.c b/src/lib/test_mint_api.c deleted file mode 100644 index a068dde93..000000000 --- a/src/lib/test_mint_api.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file mint/test_mint_api.c - * @brief testcase to test mint's HTTP API interface - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include "taler_util.h" -#include "taler_mint_service.h" - -struct TALER_MINT_Context *ctx; - -struct TALER_MINT_Handle *mint; - -struct TALER_MINT_KeysGetHandle *dkey_get; - -struct TALER_MINT_DepositHandle *dh; - -static struct GNUNET_SCHEDULER_Task *shutdown_task; - -static int result; - - -static void -do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - shutdown_task = NULL; - if (NULL != dkey_get) - TALER_MINT_keys_get_cancel (dkey_get); - dkey_get = NULL; - if (NULL != dh) - TALER_MINT_deposit_submit_cancel (dh); - dh = NULL; - TALER_MINT_disconnect (mint); - mint = NULL; - TALER_MINT_cleanup (ctx); - ctx = NULL; -} - - -/** - * Callbacks of this type are used to serve the result of submitting a deposit - * permission object to a mint - * - * @param cls closure - * @param status 1 for successful deposit, 2 for retry, 0 for failure - * @param obj the received JSON object; can be NULL if it cannot be constructed - * from the reply - * @param emsg in case of unsuccessful deposit, this contains a human readable - * explanation. - */ -static void -deposit_status (void *cls, - int status, - json_t *obj, - char *emsg) -{ - char *json_enc; - - dh = NULL; - json_enc = NULL; - if (NULL != obj) - { - json_enc = json_dumps (obj, JSON_INDENT(2)); - fprintf (stderr, "%s", json_enc); - } - if (1 == status) - result = GNUNET_OK; - else - GNUNET_break (0); - if (NULL != emsg) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deposit failed: %s\n", emsg); - GNUNET_SCHEDULER_shutdown (); -} -/** - * Functions of this type are called to signal completion of an asynchronous call. - * - * @param cls closure - * @param emsg if the asynchronous call could not be completed due to an error, - * this parameter contains a human readable error message - */ -static void -cont (void *cls, const char *emsg) -{ - json_t *dp; - char rnd_32[32]; - char rnd_64[64]; - char *enc_32; - char *enc_64; - - GNUNET_assert (NULL == cls); - dkey_get = NULL; - if (NULL != emsg) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg); - - enc_32 = GNUNET_STRINGS_data_to_string_alloc (rnd_32, sizeof (rnd_32)); - enc_64 = GNUNET_STRINGS_data_to_string_alloc (rnd_64, sizeof (rnd_64)); - dp = json_pack ("{s:s s:o s:s s:s s:s s:s s:s s:s s:s s:s}", - "type", "DIRECT_DEPOSIT", - "wire", json_pack ("{s:s}", "type", "SEPA"), - "C", enc_32, - "K", enc_32, - "ubsig", enc_64, - "M", enc_32, - "H_a", enc_64, - "H_wire", enc_64, - "csig", enc_64, - "m", "B1C5GP2RB1C5G"); - GNUNET_free (enc_32); - GNUNET_free (enc_64); - dh = TALER_MINT_deposit_submit_json (mint, - deposit_status, - NULL, - dp); - json_decref (dp); -} - - -/** - * Functions of this type are called to provide the retrieved signing and - * denomination keys of the mint. No TALER_MINT_*() functions should be called - * in this callback. - * - * @param cls closure passed to TALER_MINT_keys_get() - * @param sign_keys NULL-terminated array of pointers to the mint's signing - * keys. NULL if no signing keys are retrieved. - * @param denom_keys NULL-terminated array of pointers to the mint's - * denomination keys; will be NULL if no signing keys are retrieved. - */ -static void -read_denom_key (void *cls, - struct TALER_MINT_SigningPublicKey **sign_keys, - struct TALER_MINT_DenomPublicKey **denom_keys) -{ - unsigned int cnt; - GNUNET_assert (NULL == cls); -#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); return; } while (0) - ERR (NULL == sign_keys); - ERR (NULL == denom_keys); - for (cnt = 0; NULL != sign_keys[cnt]; cnt++) - GNUNET_free (sign_keys[cnt]); - ERR (0 == cnt); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u signing keys\n", cnt); - GNUNET_free (sign_keys); - for (cnt = 0; NULL != denom_keys[cnt]; cnt++) - GNUNET_free (denom_keys[cnt]); - ERR (0 == cnt); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u denomination keys\n", cnt); - GNUNET_free (denom_keys); -#undef ERR - return; -} - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param config configuration - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *config) -{ - ctx = TALER_MINT_init (); - mint = TALER_MINT_connect (ctx, "localhost", 4241, NULL); - GNUNET_assert (NULL != mint); - dkey_get = TALER_MINT_keys_get (mint, - &read_denom_key, NULL, - &cont, NULL); - GNUNET_assert (NULL != dkey_get); - shutdown_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), - &do_shutdown, NULL); -} - -int -main (int argc, char * const *argv) -{ - static struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - result = GNUNET_SYSERR; - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, argv, "test-mint-api", - gettext_noop - ("Testcase to test mint's HTTP API interface"), - options, &run, NULL)) - return 3; - return (GNUNET_OK == result) ? 0 : 1; -} diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am new file mode 100644 index 000000000..f91ca32c1 --- /dev/null +++ b/src/mint-lib/Makefile.am @@ -0,0 +1,30 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +lib_LTLIBRARIES = \ + libtalermint.la + +libtalermint_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined + +libtalermint_la_SOURCES = \ + mint_api.c + +libtalermint_la_LIBADD = \ + -lgnunetutil \ + -ljansson \ + -lcurl + +check_PROGRAMS = \ + test_mint_api + +test_mint_api_SOURCES = \ + test_mint_api.c +test_mint_api_LDADD = \ + libtalermint.la \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil \ + -ljansson + diff --git a/src/mint-lib/mint_api.c b/src/mint-lib/mint_api.c new file mode 100644 index 000000000..019793b78 --- /dev/null +++ b/src/mint-lib/mint_api.c @@ -0,0 +1,1154 @@ +/* + This file is part of TALER + Copyright (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ + +/** + * @file mint/mint_api.c + * @brief Implementation of the client interface to mint's HTTP API + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include +#include +#include +#include "taler_mint_service.h" +#include "taler_signatures.h" + + +#define CURL_STRERROR(TYPE, FUNCTION, CODE) \ + GNUNET_log (TYPE, "cURL function `%s' has failed at `%s:%d' with error: %s", \ + FUNCTION, __FILE__, __LINE__, curl_easy_strerror (CODE)); + + + +/** + * Print JSON parsing related error information + */ +#define JSON_WARN(error) \ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ + "JSON parsing failed at %s:%u: %s (%s)", \ + __FILE__, __LINE__, error.text, error.source) + +/** + * Failsafe flag + */ +static int fail; + +/** + * Context + */ +struct TALER_MINT_Context +{ + /** + * CURL multi handle + */ + CURLM *multi; + + /** + * CURL share handle + */ + CURLSH *share; + + /** + * Perform task handle + */ + struct GNUNET_SCHEDULER_Task *perform_task; +}; + +/** + * Type of requests we currently have + */ +enum RequestType +{ + /** + * No request + */ + REQUEST_TYPE_NONE, + + /** + * Current request is to receive mint's keys + */ + REQUEST_TYPE_KEYSGET, + + /** + * Current request is to submit a deposit permission and get its status + */ + REQUEST_TYPE_DEPOSIT +}; + + +/** + * Handle to the mint + */ +struct TALER_MINT_Handle +{ + /** + * The context of this handle + */ + struct TALER_MINT_Context *ctx; + + /** + * The hostname of the mint + */ + char *hostname; + + /** + * The CURL handle + */ + CURL *curl; + + /** + * Error buffer for CURL + */ + char emsg[CURL_ERROR_SIZE]; + + /** + * Download buffer + */ + void *buf; + + /** + * The currently active request + */ + union { + /** + * Used to denote no request if set to NULL + */ + void *none; + + /** + * Denom keys get request if REQUEST_TYPE_KEYSGET + */ + struct TALER_MINT_KeysGetHandle *keys_get; + + /** + * Deposit request if REQUEST_TYPE_DEPOSIT + */ + struct TALER_MINT_DepositHandle *deposit; + } req; + + /** + * The size of the download buffer + */ + size_t buf_size; + + /** + * Active request type + */ + enum RequestType req_type; + + /** + * The service port of the mint + */ + uint16_t port; + + /** + * Are we connected to the mint? + */ + uint8_t connected; + +}; + + +/** + * A handle to get the keys of a mint + */ +struct TALER_MINT_KeysGetHandle +{ + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this handle + */ + char *url; + + TALER_MINT_KeysGetCallback cb; + + void *cb_cls; + + TALER_MINT_ContinuationCallback cont_cb; + + void *cont_cls; +}; + + +/** + * A handle to submit a deposit permission and get its status + */ +struct TALER_MINT_DepositHandle +{ + /** + *The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this handle + */ + char *url; + + TALER_MINT_DepositResultCallback cb; + + void *cb_cls; + + char *json_enc; + + struct curl_slist *headers; + +}; + + +/** + * Parses the timestamp encoded as ASCII string as UNIX timstamp. + * + * @param abs successfully parsed timestamp will be returned thru this parameter + * @param tstamp_enc the ASCII encoding of the timestamp + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +static int +parse_timestamp (struct GNUNET_TIME_Absolute *abs, + const char *tstamp_enc) +{ + unsigned long tstamp; + + if (1 != sscanf (tstamp_enc, "%lu", &tstamp)) + return GNUNET_SYSERR; + *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (), + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, tstamp)); + return GNUNET_OK; +} + + +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + + +static int +parse_json_signkey (struct TALER_MINT_SigningPublicKey **_sign_key, + json_t *sign_key_obj, + struct TALER_MasterPublicKeyP *master_key) +{ + json_t *valid_from_obj; + json_t *valid_until_obj; + json_t *key_obj; + json_t *sig_obj; + const char *valid_from_enc; + const char *valid_until_enc; + const char *key_enc; + const char *sig_enc; + struct TALER_MINT_SigningPublicKey *sign_key; + struct TALER_MintSigningKeyValidityPS sign_key_issue; + struct GNUNET_CRYPTO_EddsaSignature sig; + struct GNUNET_TIME_Absolute valid_from; + struct GNUNET_TIME_Absolute valid_until; + + EXITIF (JSON_OBJECT != json_typeof (sign_key_obj)); + EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj, + "stamp_start"))); + EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj, + "stamp_expire"))); + EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key"))); + EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig"))); + EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj))); + EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj))); + EXITIF (NULL == (key_enc = json_string_value (key_obj))); + EXITIF (NULL == (sig_enc = json_string_value (sig_obj))); + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, + valid_from_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until, + valid_until_enc)); + EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */ + EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */ + EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, + &sig, sizeof (sig))); + (void) memset (&sign_key_issue, 0, sizeof (sign_key_issue)); + EXITIF (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc, + 52, + &sign_key_issue.signkey_pub.eddsa_pub)); + sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY); + sign_key_issue.purpose.size = + htonl (sizeof (sign_key_issue) + - offsetof (struct TALER_MintSigningKeyValidityPS, purpose)); + sign_key_issue.master_public_key = *master_key; + sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from); + sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until); + EXITIF (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY, + &sign_key_issue.purpose, + &sig, + &master_key->eddsa_pub)); + sign_key = GNUNET_new (struct TALER_MINT_SigningPublicKey); + sign_key->valid_from = valid_from; + sign_key->valid_until = valid_until; + sign_key->key = sign_key_issue.signkey_pub; + *_sign_key = sign_key; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +static int +parse_json_amount (json_t *amount_obj, struct TALER_Amount *amt) +{ + json_t *obj; + const char *currency_str; + int value; + int fraction; + + EXITIF (NULL == (obj = json_object_get (amount_obj, "currency"))); + EXITIF (NULL == (currency_str = json_string_value (obj))); + EXITIF (NULL == (obj = json_object_get (amount_obj, "value"))); + EXITIF (JSON_INTEGER != json_typeof (obj)); + EXITIF (0 > (value = json_integer_value (obj))); + EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction"))); + EXITIF (JSON_INTEGER != json_typeof (obj)); + EXITIF (0 > (fraction = json_integer_value (obj))); + (void) memset (amt->currency, 0, sizeof (amt->currency)); + (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1); + amt->value = (uint32_t) value; + amt->fraction = (uint32_t) fraction; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +/* FIXME: avoid useless ** for _denom_key! */ +static int +parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key, + json_t *denom_key_obj, + struct TALER_MasterPublicKeyP *master_key) +{ + json_t *obj; + const char *sig_enc; + const char *deposit_valid_until_enc; + const char *withdraw_valid_until_enc; + const char *valid_from_enc; + const char *key_enc; + char *buf; + size_t buf_size; + struct TALER_MINT_DenomPublicKey *denom_key; + struct GNUNET_TIME_Absolute valid_from; + struct GNUNET_TIME_Absolute withdraw_valid_until; + struct GNUNET_TIME_Absolute deposit_valid_until; + struct TALER_Amount value; + struct TALER_Amount fee_withdraw; + struct TALER_Amount fee_deposit; + struct TALER_Amount fee_refresh; + struct TALER_DenominationKeyValidityPS denom_key_issue; + struct GNUNET_CRYPTO_rsa_PublicKey *pk; + struct GNUNET_CRYPTO_EddsaSignature sig; + + EXITIF (JSON_OBJECT != json_typeof (denom_key_obj)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig"))); + EXITIF (NULL == (sig_enc = json_string_value (obj))); + EXITIF (103 != strlen (sig_enc)); + EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103, + &sig, sizeof (sig))); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit"))); + EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj))); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw"))); + EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj))); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start"))); + EXITIF (NULL == (valid_from_enc = json_string_value (obj))); + + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub"))); + EXITIF (NULL == (key_enc = json_string_value (obj))); + + EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until, + withdraw_valid_until_enc)); + EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until, + deposit_valid_until_enc)); + + memset (&denom_key_issue, 0, sizeof (denom_key_issue)); + + buf_size = (strlen (key_enc) * 5) / 8; + buf = GNUNET_malloc (buf_size); + + EXITIF (GNUNET_OK != + GNUNET_STRINGS_string_to_data (key_enc, strlen (key_enc), + buf, + buf_size)); + pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); + GNUNET_free (buf); + + EXITIF (NULL == pk); + GNUNET_CRYPTO_rsa_public_key_hash (pk, + &denom_key_issue.denom_hash); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit)); + EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh"))); + EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh)); + denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY); + denom_key_issue.purpose.size = htonl + (sizeof (struct TALER_DenominationKeyValidityPS) - + offsetof (struct TALER_DenominationKeyValidityPS, purpose)); + denom_key_issue.master = *master_key; + denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from); + denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until); + denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until); + TALER_amount_hton (&denom_key_issue.value, + &value); + TALER_amount_hton (&denom_key_issue.fee_withdraw, + &fee_withdraw); + TALER_amount_hton (&denom_key_issue.fee_deposit, + &fee_deposit); + TALER_amount_hton (&denom_key_issue.fee_refresh, + &fee_refresh); + EXITIF (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY, + &denom_key_issue.purpose, + &sig, + &master_key->eddsa_pub)); + denom_key = GNUNET_new (struct TALER_MINT_DenomPublicKey); + denom_key->key.rsa_public_key = pk; + denom_key->valid_from = valid_from; + denom_key->withdraw_valid_until = withdraw_valid_until; + denom_key->deposit_valid_until = deposit_valid_until; + denom_key->value = value; + denom_key->fee_withdraw = fee_withdraw; + denom_key->fee_deposit = fee_deposit; + denom_key->fee_refresh = fee_refresh; + *_denom_key = denom_key; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +static int +parse_response_keys_get (const char *in, size_t size, + struct TALER_MINT_SigningPublicKey ***_sign_keys, + unsigned int *_n_sign_keys, + struct TALER_MINT_DenomPublicKey ***_denom_keys, + unsigned int *_n_denom_keys) +{ + json_t *resp_obj; + struct TALER_MINT_DenomPublicKey **denom_keys; + struct TALER_MasterPublicKeyP master_key; + struct GNUNET_TIME_Absolute list_issue_date; + struct TALER_MINT_SigningPublicKey **sign_keys; + unsigned int n_denom_keys; + unsigned int n_sign_keys; + json_error_t error; + unsigned int index; + int OK; + + denom_keys = NULL; + n_denom_keys = 0; + sign_keys = NULL; + n_sign_keys = 0; + OK = 0; + resp_obj = json_loadb (in, size, + JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, + &error); + if (NULL == resp_obj) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse received data as JSON object\n"); + return GNUNET_SYSERR; + } + + EXITIF (JSON_OBJECT != json_typeof (resp_obj)); + { + /* parse the master public key */ + json_t *master_key_obj; + const char *master_key_enc; + + EXITIF (NULL == (master_key_obj = json_object_get (resp_obj, "TMH_master_public_key"))); + EXITIF (NULL == (master_key_enc = json_string_value (master_key_obj))); + EXITIF (52 != strlen (master_key_enc)); /* strlen(base32(char[32])) = 52 */ + EXITIF (GNUNET_OK != + GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc, + 52, + &master_key.eddsa_pub)); + } + { + /* parse the issue date of the response */ + json_t *list_issue_date_obj; + const char *tstamp_enc; + + EXITIF (NULL == (list_issue_date_obj = + json_object_get(resp_obj, "list_issue_date"))); + EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj))); + EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date, tstamp_enc)); + } + { + /* parse the signing keys */ + json_t *sign_keys_array; + json_t *sign_key_obj; + + EXITIF (NULL == (sign_keys_array = + json_object_get (resp_obj, "signkeys"))); + EXITIF (JSON_ARRAY != json_typeof (sign_keys_array)); + EXITIF (0 == (n_sign_keys = json_array_size (sign_keys_array))); + sign_keys = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey *) + * (n_sign_keys + 1)); + index = 0; + json_array_foreach (sign_keys_array, index, sign_key_obj) { + EXITIF (GNUNET_SYSERR == parse_json_signkey (&sign_keys[index], + sign_key_obj, + &master_key)); + } + } + { + /* parse the denomination keys */ + json_t *denom_keys_array; + json_t *denom_key_obj; + + EXITIF (NULL == (denom_keys_array = json_object_get (resp_obj, "denoms"))); + EXITIF (JSON_ARRAY != json_typeof (denom_keys_array)); + EXITIF (0 == (n_denom_keys = json_array_size (denom_keys_array))); + denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey *) + * (n_denom_keys + 1)); + index = 0; + json_array_foreach (denom_keys_array, index, denom_key_obj) { + EXITIF (GNUNET_SYSERR == parse_json_denomkey (&denom_keys[index], + denom_key_obj, + &master_key)); + } + } + OK = 1; + + EXITIF_exit: + json_decref (resp_obj); + if (!OK) + { + if (NULL != sign_keys) + { + for (index=0; NULL != sign_keys[index]; index++) + GNUNET_free_non_null (sign_keys[index]); + GNUNET_free (sign_keys); + } + if (NULL != denom_keys) + { + for (index=0; NULL != denom_keys[index]; index++) + GNUNET_free_non_null (denom_keys[index]); + GNUNET_free (denom_keys); + } + return GNUNET_SYSERR; + } + + *_sign_keys = sign_keys; + *_n_sign_keys = n_sign_keys; + *_denom_keys = denom_keys; + *_n_denom_keys = n_denom_keys; + return GNUNET_OK; +} + + +int +parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj) +{ + json_t *obj; + const char *status_str; + json_error_t error; + + status_str = NULL; + obj = NULL; + obj = json_loadb (buf, size, + JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, &error); + if (NULL == obj) + { + JSON_WARN (error); + return GNUNET_SYSERR; + } + EXITIF (-1 == json_unpack (obj, "{s:s}", "status", &status_str)); + TALER_LOG_DEBUG ("Received deposit response: %s from mint\n", status_str); + if (0 == strcmp ("DEPOSIT_OK", status_str)) + *r_status = 1; + else if (0 == strcmp ("DEPOSIT_QUEUED", status_str)) + *r_status = 2; + else + *r_status = 0; + *r_obj = obj; + + return GNUNET_OK; + EXITIF_exit: + json_decref (obj); + return GNUNET_SYSERR; +} + +#undef EXITIF + +static void +mint_connect (struct TALER_MINT_Handle *mint) +{ + struct TALER_MINT_Context *ctx = mint->ctx; + + GNUNET_assert (0 == mint->connected); + GNUNET_assert (CURLM_OK == curl_multi_add_handle (ctx->multi, mint->curl)); + mint->connected = GNUNET_YES; +} + +static void +mint_disconnect (struct TALER_MINT_Handle *mint) +{ + struct TALER_MINT_Context *ctx = mint->ctx; + + GNUNET_assert (GNUNET_YES == mint->connected); + GNUNET_break (CURLM_OK == curl_multi_remove_handle (ctx->multi, + mint->curl)); + mint->connected = GNUNET_NO; + GNUNET_free_non_null (mint->buf); + mint->buf = NULL; + mint->buf_size = 0; + mint->req_type = REQUEST_TYPE_NONE; + mint->req.none = NULL; +} + +static void +cleanup_keys_get (struct TALER_MINT_KeysGetHandle *gh) +{ + GNUNET_free (gh->url); + GNUNET_free (gh); +} + +static void +cleanup_deposit (struct TALER_MINT_DepositHandle *dh) +{ + curl_slist_free_all (dh->headers); + GNUNET_free_non_null (dh->json_enc); + GNUNET_free (dh->url); + GNUNET_free (dh); +} + +static void +request_failed (struct TALER_MINT_Handle *mint, long resp_code) +{ + switch (mint->req_type) + { + case REQUEST_TYPE_NONE: + GNUNET_assert (0); + break; + case REQUEST_TYPE_KEYSGET: + { + struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get; + TALER_MINT_ContinuationCallback cont_cb; + void *cont_cls; + GNUNET_assert (NULL != gh); + cont_cb = gh->cont_cb; + cont_cls = gh->cont_cls; + cleanup_keys_get (gh); + mint_disconnect (mint); + cont_cb (cont_cls, mint->emsg); + } + break; + case REQUEST_TYPE_DEPOSIT: + { + struct TALER_MINT_DepositHandle *dh = mint->req.deposit; + TALER_MINT_DepositResultCallback cb = dh->cb; + void *cls = dh->cb_cls; + + GNUNET_assert (NULL != dh); + cleanup_deposit (dh); + mint_disconnect (mint); + cb (cls, 0, NULL, mint->emsg); + } + break; + } +} + +static void +request_succeeded (struct TALER_MINT_Handle *mint, long resp_code) +{ + char *emsg; + + emsg = NULL; + switch (mint->req_type) + { + case REQUEST_TYPE_NONE: + GNUNET_assert (0); + break; + case REQUEST_TYPE_KEYSGET: + { + struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get; + TALER_MINT_ContinuationCallback cont_cb; + void *cont_cls; + struct TALER_MINT_SigningPublicKey **sign_keys; + struct TALER_MINT_DenomPublicKey **denom_keys; + unsigned int n_sign_keys; + unsigned int n_denom_keys; + + GNUNET_assert (NULL != gh); + cont_cb = gh->cont_cb; + cont_cls = gh->cont_cls; + if (200 == resp_code) + { + /* parse JSON object from the mint->buf which is of size mint->buf_size */ + if (GNUNET_OK == + parse_response_keys_get (mint->buf, mint->buf_size, + &sign_keys, &n_sign_keys, + &denom_keys, &n_denom_keys)) + gh->cb (gh->cb_cls, sign_keys, denom_keys); + else + emsg = GNUNET_strdup ("Error parsing response"); + } + else + GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code); + cleanup_keys_get (gh); + mint_disconnect (mint); + cont_cb (cont_cls, emsg); + } + break; + case REQUEST_TYPE_DEPOSIT: + { + struct TALER_MINT_DepositHandle *dh = mint->req.deposit; + TALER_MINT_DepositResultCallback cb; + void *cls; + int status; + json_t *obj; + + GNUNET_assert (NULL != dh); + obj = NULL; + cb = dh->cb; + cls = dh->cb_cls; + status = 0; + if (200 == resp_code) + { + /* parse JSON object from the mint->buf which is of size mint->buf_size */ + if (GNUNET_OK != + parse_deposit_response (mint->buf, mint->buf_size, + &status, &obj)) + emsg = GNUNET_strdup ("Error parsing response"); + } + else + GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code); + cleanup_deposit (dh); + mint_disconnect (mint); + cb (cls, status, obj, emsg); + } + break; + } + GNUNET_free_non_null (emsg); +} + + +static void +do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + +static void +perform (struct TALER_MINT_Context *ctx) +{ + fd_set fd_rs; + fd_set fd_ws; + struct GNUNET_NETWORK_FDSet rs; + struct GNUNET_NETWORK_FDSet ws; + CURLMsg *cmsg; + struct TALER_MINT_Handle *mint; + long timeout; + long resp_code; + static unsigned int n_old; + int n_running; + int n_completed; + int max_fd; + + n_completed = 0; + curl_multi_perform (ctx->multi, &n_running); + GNUNET_assert (0 <= n_running); + if ((0 == n_running) || (n_running < n_old)) + { + /* some requests were completed -- handle them */ + while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed))) + { + GNUNET_break (CURLMSG_DONE == cmsg->msg); /* curl only has CURLMSG_DONE */ + GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, + CURLINFO_PRIVATE, + (char *) &mint)); + GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle, + CURLINFO_RESPONSE_CODE, + &resp_code)); + GNUNET_assert (ctx == mint->ctx); /* did we get the correct one? */ + if (CURLE_OK == cmsg->data.result) + request_succeeded (mint, resp_code); + else + request_failed (mint, resp_code); + } + } + n_old = n_running; + /* reschedule perform() */ + if (0 != n_old) + { + FD_ZERO (&fd_rs); + FD_ZERO (&fd_ws); + GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi, + &fd_rs, + &fd_ws, + NULL, + &max_fd)); + if (-1 == max_fd) + { + ctx->perform_task = GNUNET_SCHEDULER_add_delayed + (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100), + &do_perform, ctx); + return; + } + GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, &timeout)); + if (-1 == timeout) + { + timeout = 1000 * 60 * 5; + } + GNUNET_NETWORK_fdset_zero (&rs); + GNUNET_NETWORK_fdset_zero (&ws); + GNUNET_NETWORK_fdset_copy_native (&rs, &fd_rs, max_fd + 1); + GNUNET_NETWORK_fdset_copy_native (&ws, &fd_ws, max_fd + 1); + ctx->perform_task = GNUNET_SCHEDULER_add_select + (GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout), + &rs, &ws, + &do_perform, ctx); + } +} + + +static void +do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TALER_MINT_Context *ctx = cls; + + GNUNET_assert (NULL != ctx->perform_task); + ctx->perform_task = NULL; + perform (ctx); +} + +static void +perform_now (struct TALER_MINT_Context *ctx) +{ + if (NULL != ctx->perform_task) + { + GNUNET_SCHEDULER_cancel (ctx->perform_task); + ctx->perform_task = NULL; + } + ctx->perform_task = GNUNET_SCHEDULER_add_now (&do_perform, ctx); +} + + +/* This function gets called by libcurl as soon as there is data received that */ +/* needs to be saved. The size of the data pointed to by ptr is size */ +/* multiplied with nmemb, it will not be zero terminated. Return the number */ +/* of bytes actually taken care of. If that amount differs from the amount passed */ +/* to your function, it'll signal an error to the library. This will abort the */ +/* transfer and return CURLE_WRITE_ERROR. */ + +/* From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will */ +/* cause writing to this connection to become paused. See */ +/* curl_easy_pause(3) for further details. */ + +/* This function may be called with zero bytes data if the transferred file is */ +/* empty. */ + +/* Set this option to NULL to get the internal default function. The internal */ +/* default function will write the data to the FILE * given with */ +/* CURLOPT_WRITEDATA. */ + +/* Set the userdata argument with the CURLOPT_WRITEDATA option. */ + +/* The callback function will be passed as much data as possible in all invokes, */ +/* but you cannot possibly make any assumptions. It may be one byte, it may be */ +/* thousands. The maximum amount of body data that can be passed to the write */ +/* callback is defined in the curl.h header file: CURL_MAX_WRITE_SIZE (the usual */ +/* default is 16K). If you however have CURLOPT_HEADER set, which sends */ +/* header data to the write callback, you can get up to */ +/* CURL_MAX_HTTP_HEADER bytes of header data passed into it. This usually */ +/* means 100K. */ +static size_t +download (char *bufptr, size_t size, size_t nitems, void *cls) +{ + struct TALER_MINT_Handle *mint = cls; + size_t msize; + void *buf; + + if (0 == size * nitems) + { + /* file is empty */ + return 0; + } + msize = size * nitems; + mint->buf = GNUNET_realloc (mint->buf, mint->buf_size + msize); + buf = mint->buf + mint->buf_size; + memcpy (buf, bufptr, msize); + mint->buf_size += msize; + return msize; +} + + +/** + * Initialise a connection to the mint. + * + * @param ctx the context + * @param hostname the hostname of the mint + * @param port the point where the mint's HTTP service is running. + * @param mint_key the offline master public key of the mint. + * This is used to verify the responses of the mint. + * @return the mint handle; NULL upon error + */ +struct TALER_MINT_Handle * +TALER_MINT_connect (struct TALER_MINT_Context *ctx, + const char *hostname, + uint16_t port, + const struct TALER_MasterPublicKeyP *mint_key) +{ + struct TALER_MINT_Handle *mint; + + mint = GNUNET_new (struct TALER_MINT_Handle); + mint->ctx = ctx; + mint->hostname = GNUNET_strdup (hostname); + mint->port = (0 != port) ? port : 80; + mint->curl = curl_easy_init (); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_SHARE, ctx->share)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_ERRORBUFFER, mint->emsg)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_WRITEFUNCTION, &download)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_WRITEDATA, mint)); + GNUNET_assert (CURLE_OK == curl_easy_setopt (mint->curl, CURLOPT_PRIVATE, mint)); + return mint; +} + +/** + * Disconnect from the mint + * + * @param mint the mint handle + */ +void +TALER_MINT_disconnect (struct TALER_MINT_Handle *mint) +{ + if (GNUNET_YES == mint->connected) + mint_disconnect (mint); + curl_easy_cleanup (mint->curl); + GNUNET_free (mint->hostname); + GNUNET_free (mint); +} + +/** + * Get the signing and denomination key of the mint. + * + * @param mint handle to the mint + * @param cb the callback to call with each retrieved denomination key + * @param cb_cls closure for the above callback + * @param cont_cb the callback to call after completing this asynchronous call + * @param cont_cls the closure for the continuation callback + * @return a handle to this asynchronous call; NULL upon eror + */ +struct TALER_MINT_KeysGetHandle * +TALER_MINT_keys_get (struct TALER_MINT_Handle *mint, + TALER_MINT_KeysGetCallback cb, + void *cb_cls, + TALER_MINT_ContinuationCallback cont_cb, + void *cont_cls) +{ + struct TALER_MINT_KeysGetHandle *gh; + + GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type); + gh = GNUNET_new (struct TALER_MINT_KeysGetHandle); + gh->mint = mint; + mint->req_type = REQUEST_TYPE_KEYSGET; + mint->req.keys_get = gh; + gh->cb = cb; + gh->cb_cls = cb_cls; + gh->cont_cb = cont_cb; + gh->cont_cls = cont_cls; + GNUNET_asprintf (&gh->url, + "http://%s:%hu/keys", + mint->hostname, + mint->port); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, + CURLOPT_URL, + gh->url)); + if (GNUNET_NO == mint->connected) + mint_connect (mint); + perform_now (mint->ctx); + return gh; +} + + +/** + * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This + * should not be called if either of the @a TALER_MINT_KeysGetCallback or + * @a TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have + * been called. + * + * @param get the handle for retrieving the keys + */ +void +TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get) +{ + struct TALER_MINT_Handle *mint = get->mint; + + mint_disconnect (mint); + cleanup_keys_get (get); +} + +/** + * Submit a deposit permission to the mint and get the mint's response + * + * @param mint the mint handle + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @param deposit_obj the deposit permission received from the customer along + * with the wireformat JSON object + * @return a handle for this request; NULL if the JSON object could not be + * parsed or is of incorrect format or any other error. In this case, + * the callback is not called. + */ +struct TALER_MINT_DepositHandle * +TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint, + TALER_MINT_DepositResultCallback cb, + void *cb_cls, + json_t *deposit_obj) +{ + struct TALER_MINT_DepositHandle *dh; + + GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type); + dh = GNUNET_new (struct TALER_MINT_DepositHandle); + dh->mint = mint; + mint->req_type = REQUEST_TYPE_DEPOSIT; + mint->req.deposit = dh; + dh->cb = cb; + dh->cb_cls = cb_cls; + GNUNET_asprintf (&dh->url, "http://%s:%hu/deposit", mint->hostname, mint->port); + GNUNET_assert (NULL != (dh->json_enc = json_dumps (deposit_obj, JSON_COMPACT))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_URL, dh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDS, + dh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_POSTFIELDSIZE, + strlen (dh->json_enc))); + GNUNET_assert (NULL != (dh->headers = + curl_slist_append (dh->headers, "Content-Type: application/json"))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (mint->curl, CURLOPT_HTTPHEADER, dh->headers)); + if (GNUNET_NO == mint->connected) + mint_connect (mint); + perform_now (mint->ctx); + return dh; +} + + +/** + * Cancel a deposit permission request. This function cannot be used on a + * request handle if a response is already served for it. + * + * @param the deposit permission request handle + */ +void +TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit) +{ + struct TALER_MINT_Handle *mint = deposit->mint; + + mint_disconnect (mint); + cleanup_deposit (deposit); +} + + +/** + * Initialise this library. This function should be called before using any of + * the following functions. + * + * @return library context + */ +struct TALER_MINT_Context * +TALER_MINT_init () +{ + struct TALER_MINT_Context *ctx; + CURLM *multi; + CURLSH *share; + + if (fail) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "cURL was not initialised properly\n"); + return NULL; + } + if (NULL == (multi = curl_multi_init ())) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL multi handle\n"); + return NULL; + } + if (NULL == (share = curl_share_init ())) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL share handle\n"); + return NULL; + } + ctx = GNUNET_new (struct TALER_MINT_Context); + ctx->multi = multi; + ctx->share = share; + return ctx; +} + + +/** + * Cleanup library initialisation resources. This function should be called + * after using this library to cleanup the resources occupied during library's + * initialisation. + * + * @param ctx the library context + */ +void +TALER_MINT_cleanup (struct TALER_MINT_Context *ctx) +{ + curl_share_cleanup (ctx->share); + curl_multi_cleanup (ctx->multi); + if (NULL != ctx->perform_task) + { + GNUNET_break (0); /* investigate why this happens */ + GNUNET_SCHEDULER_cancel (ctx->perform_task); + } + GNUNET_free (ctx); +} + + +__attribute__ ((constructor)) +void +TALER_MINT_constructor__ (void) +{ + CURLcode ret; + if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT))) + { + CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, "curl_global_init", ret); + fail = 1; + } +} + +__attribute__ ((destructor)) +void +TALER_MINT_destructor__ (void) +{ + if (fail) + return; + curl_global_cleanup (); +} diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c new file mode 100644 index 000000000..a068dde93 --- /dev/null +++ b/src/mint-lib/test_mint_api.c @@ -0,0 +1,211 @@ +/* + This file is part of TALER + Copyright (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file mint/test_mint_api.c + * @brief testcase to test mint's HTTP API interface + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "taler_util.h" +#include "taler_mint_service.h" + +struct TALER_MINT_Context *ctx; + +struct TALER_MINT_Handle *mint; + +struct TALER_MINT_KeysGetHandle *dkey_get; + +struct TALER_MINT_DepositHandle *dh; + +static struct GNUNET_SCHEDULER_Task *shutdown_task; + +static int result; + + +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + shutdown_task = NULL; + if (NULL != dkey_get) + TALER_MINT_keys_get_cancel (dkey_get); + dkey_get = NULL; + if (NULL != dh) + TALER_MINT_deposit_submit_cancel (dh); + dh = NULL; + TALER_MINT_disconnect (mint); + mint = NULL; + TALER_MINT_cleanup (ctx); + ctx = NULL; +} + + +/** + * Callbacks of this type are used to serve the result of submitting a deposit + * permission object to a mint + * + * @param cls closure + * @param status 1 for successful deposit, 2 for retry, 0 for failure + * @param obj the received JSON object; can be NULL if it cannot be constructed + * from the reply + * @param emsg in case of unsuccessful deposit, this contains a human readable + * explanation. + */ +static void +deposit_status (void *cls, + int status, + json_t *obj, + char *emsg) +{ + char *json_enc; + + dh = NULL; + json_enc = NULL; + if (NULL != obj) + { + json_enc = json_dumps (obj, JSON_INDENT(2)); + fprintf (stderr, "%s", json_enc); + } + if (1 == status) + result = GNUNET_OK; + else + GNUNET_break (0); + if (NULL != emsg) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deposit failed: %s\n", emsg); + GNUNET_SCHEDULER_shutdown (); +} +/** + * Functions of this type are called to signal completion of an asynchronous call. + * + * @param cls closure + * @param emsg if the asynchronous call could not be completed due to an error, + * this parameter contains a human readable error message + */ +static void +cont (void *cls, const char *emsg) +{ + json_t *dp; + char rnd_32[32]; + char rnd_64[64]; + char *enc_32; + char *enc_64; + + GNUNET_assert (NULL == cls); + dkey_get = NULL; + if (NULL != emsg) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg); + + enc_32 = GNUNET_STRINGS_data_to_string_alloc (rnd_32, sizeof (rnd_32)); + enc_64 = GNUNET_STRINGS_data_to_string_alloc (rnd_64, sizeof (rnd_64)); + dp = json_pack ("{s:s s:o s:s s:s s:s s:s s:s s:s s:s s:s}", + "type", "DIRECT_DEPOSIT", + "wire", json_pack ("{s:s}", "type", "SEPA"), + "C", enc_32, + "K", enc_32, + "ubsig", enc_64, + "M", enc_32, + "H_a", enc_64, + "H_wire", enc_64, + "csig", enc_64, + "m", "B1C5GP2RB1C5G"); + GNUNET_free (enc_32); + GNUNET_free (enc_64); + dh = TALER_MINT_deposit_submit_json (mint, + deposit_status, + NULL, + dp); + json_decref (dp); +} + + +/** + * Functions of this type are called to provide the retrieved signing and + * denomination keys of the mint. No TALER_MINT_*() functions should be called + * in this callback. + * + * @param cls closure passed to TALER_MINT_keys_get() + * @param sign_keys NULL-terminated array of pointers to the mint's signing + * keys. NULL if no signing keys are retrieved. + * @param denom_keys NULL-terminated array of pointers to the mint's + * denomination keys; will be NULL if no signing keys are retrieved. + */ +static void +read_denom_key (void *cls, + struct TALER_MINT_SigningPublicKey **sign_keys, + struct TALER_MINT_DenomPublicKey **denom_keys) +{ + unsigned int cnt; + GNUNET_assert (NULL == cls); +#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); return; } while (0) + ERR (NULL == sign_keys); + ERR (NULL == denom_keys); + for (cnt = 0; NULL != sign_keys[cnt]; cnt++) + GNUNET_free (sign_keys[cnt]); + ERR (0 == cnt); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u signing keys\n", cnt); + GNUNET_free (sign_keys); + for (cnt = 0; NULL != denom_keys[cnt]; cnt++) + GNUNET_free (denom_keys[cnt]); + ERR (0 == cnt); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u denomination keys\n", cnt); + GNUNET_free (denom_keys); +#undef ERR + return; +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param config configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + ctx = TALER_MINT_init (); + mint = TALER_MINT_connect (ctx, "localhost", 4241, NULL); + GNUNET_assert (NULL != mint); + dkey_get = TALER_MINT_keys_get (mint, + &read_denom_key, NULL, + &cont, NULL); + GNUNET_assert (NULL != dkey_get); + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), + &do_shutdown, NULL); +} + +int +main (int argc, char * const *argv) +{ + static struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + result = GNUNET_SYSERR; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, "test-mint-api", + gettext_noop + ("Testcase to test mint's HTTP API interface"), + options, &run, NULL)) + return 3; + return (GNUNET_OK == result) ? 0 : 1; +} diff --git a/src/mint-tools/Makefile.am b/src/mint-tools/Makefile.am new file mode 100644 index 000000000..7ae6f355a --- /dev/null +++ b/src/mint-tools/Makefile.am @@ -0,0 +1,63 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +bin_PROGRAMS = \ + taler-mint-keyup \ + taler-mint-keycheck \ + taler-mint-reservemod \ + taler-mint-dbinit + +taler_mint_keyup_SOURCES = \ + taler-mint-keyup.c + +taler_mint_keyup_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/pq/libtalerpq.la \ + $(top_builddir)/src/mint/libtalermint_common.la \ + -lgnunetutil +taler_mint_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS) + + +taler_mint_keycheck_SOURCES = \ + taler-mint-keycheck.c + +taler_mint_keycheck_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/pq/libtalerpq.la \ + $(top_builddir)/src/mint/libtalermint_common.la \ + -lgnunetutil +taler_mint_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS) + +taler_mint_reservemod_SOURCES = \ + taler-mint-reservemod.c +taler_mint_reservemod_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/pq/libtalerpq.la \ + $(top_builddir)/src/mint/libtalermint_common.la \ + -lpq \ + -lgnunetutil +taler_mint_reservemod_LDFLAGS = \ + $(POSTGRESQL_LDFLAGS) +taler_mint_reservemod_CPPFLAGS = \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/pq/ \ + $(POSTGRESQL_CPPFLAGS) + +taler_mint_dbinit_SOURCES = \ + taler-mint-dbinit.c +taler_mint_dbinit_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/pq/libtalerpq.la \ + $(top_builddir)/src/mint/libtalermint_common.la \ + -lpq \ + -lgnunetutil +taler_mint_dbinit_LDFLAGS = \ + $(POSTGRESQL_LDFLAGS) +taler_mint_dbinit_CPPFLAGS = \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/pq/ \ + $(POSTGRESQL_CPPFLAGS) diff --git a/src/mint-tools/taler-mint-dbinit.c b/src/mint-tools/taler-mint-dbinit.c new file mode 100644 index 000000000..ec08369b8 --- /dev/null +++ b/src/mint-tools/taler-mint-dbinit.c @@ -0,0 +1,106 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file mint/taler-mint-dbinit.c + * @brief Create tables for the mint database. + * @author Florian Dold + */ +#include "platform.h" +#include +#include +#include "taler_util.h" +#include "taler_mintdb_plugin.h" +#include "taler_mintdb_lib.h" + +/** + * Mint directory with the keys. + */ +static char *mint_base_dir; + +/** + * Our configuration. + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our DB plugin. + */ +static struct TALER_MINTDB_Plugin *plugin; + + +/** + * The main function of the database initialization tool. + * Used to initialize the Taler Mint's database. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, + char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-dbinit OPTIONS"), + {'d', "mint-dir", "DIR", + "mint directory", 1, + &GNUNET_GETOPT_set_filename, &mint_base_dir}, + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_GETOPT_run ("taler-mint-dbinit", + options, + argc, argv) < 0) + return 1; + + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-mint-dbinit", + "INFO", + NULL)); + if (NULL == mint_base_dir) + { + fprintf (stderr, + "Mint base directory not given.\n"); + return 1; + } + cfg = TALER_config_load (mint_base_dir); + if (NULL == cfg) + { + fprintf (stderr, + "Failed to load mint configuration.\n"); + return 1; + } + if (NULL == + (plugin = TALER_MINT_plugin_load (cfg))) + { + fprintf (stderr, + "Failed to initialize database plugin.\n"); + return 1; + } + if (GNUNET_OK != + plugin->create_tables (plugin->cls, + GNUNET_NO)) + { + fprintf (stderr, + "Failed to initialize database.\n"); + TALER_MINT_plugin_unload (plugin); + return 1; + } + TALER_MINT_plugin_unload (plugin); + return 0; +} + +/* end of taler-mint-dbinit.c */ diff --git a/src/mint-tools/taler-mint-keycheck.c b/src/mint-tools/taler-mint-keycheck.c new file mode 100644 index 000000000..b18ebdf7a --- /dev/null +++ b/src/mint-tools/taler-mint-keycheck.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-keycheck.c + * @brief Check mint keys for validity. Reads the signing and denomination + * keys from the mint directory and checks to make sure they are + * well-formed. This is purely a diagnostic tool. + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include +#include +#include "taler_mintdb_lib.h" + +/** + * Mint directory with the keys. + */ +static char *mint_directory; + +/** + * Our configuration. + */ +static struct GNUNET_CONFIGURATION_Handle *kcfg; + + +/** + * Function called on each signing key. + * + * @param cls closure (NULL) + * @param filename name of the file the key came from + * @param ski the sign key + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +static int +signkeys_iter (void *cls, + const char *filename, + const struct TALER_MintSigningKeyValidityPSPriv *ski) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Iterating over key `%s' for start time %s\n", + filename, + GNUNET_STRINGS_absolute_time_to_string + (GNUNET_TIME_absolute_ntoh (ski->issue.start))); + + if (ntohl (ski->issue.purpose.size) != + (sizeof (struct TALER_MintSigningKeyValidityPS) - + offsetof (struct TALER_MintSigningKeyValidityPS, purpose))) + { + fprintf (stderr, + "Signing key `%s' has invalid purpose size\n", + filename); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY, + &ski->issue.purpose, + &ski->issue.signature.eddsa_signature, + &ski->issue.master_public_key.eddsa_pub)) + { + fprintf (stderr, + "Signing key `%s' has invalid signature\n", + filename); + return GNUNET_SYSERR; + } + printf ("Signing key `%s' valid\n", + filename); + return GNUNET_OK; +} + + +/** + * Check signing keys. + * + * @return #GNUNET_OK if the keys are OK + * #GNUNET_NO if not + */ +static int +mint_signkeys_check () +{ + if (0 > TALER_MINT_signkeys_iterate (mint_directory, + &signkeys_iter, + NULL)) + return GNUNET_NO; + return GNUNET_OK; +} + + +/** + * Function called on each denomination key. + * + * @param cls closure (NULL) + * @param dki the denomination key + * @param alias coin alias + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +static int +denomkeys_iter (void *cls, + const char *alias, + const struct TALER_DenominationKeyIssueInformation *dki) +{ + struct GNUNET_HashCode hc; + + if (ntohl (dki->issue.purpose.size) != + sizeof (struct TALER_DenominationKeyValidityPS)) + { + fprintf (stderr, + "Denomination key for `%s' has invalid purpose size\n", + alias); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY, + &dki->issue.purpose, + &dki->issue.signature.eddsa_signature, + &dki->issue.master.eddsa_pub)) + { + fprintf (stderr, + "Denomination key for `%s' has invalid signature\n", + alias); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, + &hc); + if (0 != memcmp (&hc, + &dki->issue.denom_hash, + sizeof (struct GNUNET_HashCode))) + { + fprintf (stderr, + "Public key for `%s' does not match signature\n", + alias); + return GNUNET_SYSERR; + } + printf ("Denomination key `%s' is valid\n", + alias); + + return GNUNET_OK; +} + + +/** + * Check denomination keys. + * + * @return #GNUNET_OK if the keys are OK + * #GNUNET_NO if not + */ +static int +mint_denomkeys_check () +{ + if (0 > TALER_MINT_denomkeys_iterate (mint_directory, + &denomkeys_iter, + NULL)) + return GNUNET_NO; + return GNUNET_OK; +} + + +/** + * The main function of the keyup tool + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keycheck OPTIONS"), + {'d', "directory", "DIRECTORY", + "mint directory with keys to check", 1, + &GNUNET_GETOPT_set_filename, &mint_directory}, + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-mint-keycheck", + "WARNING", + NULL)); + + if (GNUNET_GETOPT_run ("taler-mint-keycheck", + options, + argc, argv) < 0) + return 1; + if (NULL == mint_directory) + { + fprintf (stderr, + "Mint directory not given\n"); + return 1; + } + + kcfg = TALER_config_load (mint_directory); + if (NULL == kcfg) + { + fprintf (stderr, + "Failed to load mint configuration\n"); + return 1; + } + if ( (GNUNET_OK != mint_signkeys_check ()) || + (GNUNET_OK != mint_denomkeys_check ()) ) + { + GNUNET_CONFIGURATION_destroy (kcfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (kcfg); + return 0; +} + +/* end of taler-mint-keycheck.c */ diff --git a/src/mint-tools/taler-mint-keyup.c b/src/mint-tools/taler-mint-keyup.c new file mode 100644 index 000000000..8ffe7d165 --- /dev/null +++ b/src/mint-tools/taler-mint-keyup.c @@ -0,0 +1,954 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @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 +#include +#include "taler_util.h" +#include "taler_mintdb_lib.h" + +/** + * When generating filenames from a cryptographic hash, we do not use + * all 512 bits but cut off after this number of characters (in + * base32-encoding). Base32 is 5 bit per character, and given that we + * have very few coin types we hash, at 100 bits the chance of + * collision (by accident over tiny set -- birthday paradox does not + * apply here!) is negligible. + */ +#define HASH_CUTOFF 20 + +/** + * Macro to round microseconds to seconds in GNUNET_TIME_* structs. + * + * @param name value to round + * @param field rel_value_us or abs_value_us + */ +#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000); + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Struct with all of the key information for a kind of coin. Hashed + * to generate a unique directory name per coin type. + */ +struct CoinTypeNBOP +{ + /** + * How long can the coin be spend? + */ + struct GNUNET_TIME_RelativeNBO duration_spend; + + /** + * How long can the coin be withdrawn (generated)? + */ + struct GNUNET_TIME_RelativeNBO duration_withdraw; + + /** + * What is the value of the coin? + */ + struct TALER_AmountNBO value; + + /** + * What is the fee charged for withdrawl? + */ + struct TALER_AmountNBO fee_withdraw; + + /** + * What is the fee charged for deposits? + */ + struct TALER_AmountNBO fee_deposit; + + /** + * What is the fee charged for melting? + */ + struct TALER_AmountNBO fee_refresh; + + /** + * Key size in NBO. + */ + uint32_t rsa_keysize; +}; + +GNUNET_NETWORK_STRUCT_END + +/** + * Set of all of the parameters that chracterize a coin. + */ +struct CoinTypeParams +{ + + /** + * How long can the coin be spend? Should be significantly + * larger than @e duration_withdraw (i.e. years). + */ + struct GNUNET_TIME_Relative duration_spend; + + /** + * How long can the coin be withdrawn (generated)? Should be small + * enough to limit how many coins will be signed into existence with + * the same key, but large enough to still provide a reasonable + * anonymity set. + */ + struct GNUNET_TIME_Relative duration_withdraw; + + /** + * How much should coin creation (@e duration_withdraw) duration + * overlap with the next coin? Basically, the starting time of two + * coins is always @e duration_withdraw - @e duration_overlap apart. + */ + struct GNUNET_TIME_Relative duration_overlap; + + /** + * What is the value of the coin? + */ + struct TALER_Amount value; + + /** + * What is the fee charged for withdrawl? + */ + struct TALER_Amount fee_withdraw; + + /** + * What is the fee charged for deposits? + */ + struct TALER_Amount fee_deposit; + + /** + * What is the fee charged for melting? + */ + struct TALER_Amount fee_refresh; + + /** + * Time at which this coin is supposed to become valid. + */ + struct GNUNET_TIME_Absolute anchor; + + /** + * Length of the RSA key in bits. + */ + uint32_t rsa_keysize; +}; + + +/** + * Filename of the master private key. + */ +static char *masterkeyfile; + +/** + * Director of the mint, containing the keys. + */ +static char *mint_directory; + +/** + * Time to pretend when the key update is executed. + */ +static char *pretend_time_str; + +/** + * Handle to the mint's configuration + */ +static struct GNUNET_CONFIGURATION_Handle *kcfg; + +/** + * Time when the key update is executed. Either the actual current time, or a + * pretended time. + */ +static struct GNUNET_TIME_Absolute now; + +/** + * Master private key of the mint. + */ +static struct TALER_MasterPrivateKeyP master_priv; + +/** + * Master public key of the mint. + */ +static struct TALER_MasterPublicKeyP master_public_key; + +/** + * Until what time do we provide keys? + */ +static struct GNUNET_TIME_Absolute lookahead_sign_stamp; + + +/** + * Obtain the name of the directory we use to store signing + * keys created at time @a start. + * + * @param start time at which we create the signing key + * @return name of the directory we should use, basically "$MINTDIR/$TIME/"; + * (valid until next call to this function) + */ +static const char * +get_signkey_file (struct GNUNET_TIME_Absolute start) +{ + static char dir[4096]; + + GNUNET_snprintf (dir, + sizeof (dir), + "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu", + mint_directory, + (unsigned long long) start.abs_value_us); + return dir; +} + + +/** + * Hash the data defining the coin type. Exclude information that may + * not be the same for all instances of the coin type (i.e. the + * anchor, overlap). + * + * @param p coin parameters to convert to a hash + * @param hash[OUT] set to the hash matching @a p + */ +static void +hash_coin_type (const struct CoinTypeParams *p, + struct GNUNET_HashCode *hash) +{ + struct CoinTypeNBOP p_nbo; + + memset (&p_nbo, + 0, + sizeof (struct CoinTypeNBOP)); + p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend); + p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); + TALER_amount_hton (&p_nbo.value, + &p->value); + TALER_amount_hton (&p_nbo.fee_withdraw, + &p->fee_withdraw); + TALER_amount_hton (&p_nbo.fee_deposit, + &p->fee_deposit); + TALER_amount_hton (&p_nbo.fee_refresh, + &p->fee_refresh); + p_nbo.rsa_keysize = htonl (p->rsa_keysize); + GNUNET_CRYPTO_hash (&p_nbo, + sizeof (struct CoinTypeNBOP), + hash); +} + + +/** + * Obtain the name of the directory we should use to store coins of + * the given type. The directory name has the format + * "$MINTDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the + * coin and "$HASH" encodes all of the coin's parameters, generating a + * unique string for each type of coin. Note that the "$HASH" + * includes neither the absolute creation time nor the key of the + * coin, thus the files in the subdirectory really just refer to the + * same type of coins, not the same coin. + * + * @param p coin parameters to convert to a directory name + * @return directory name (valid until next call to this function) + */ +static const char * +get_cointype_dir (const struct CoinTypeParams *p) +{ + static char dir[4096]; + struct GNUNET_HashCode hash; + char *hash_str; + char *val_str; + size_t i; + + hash_coin_type (p, &hash); + hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash, + sizeof (struct GNUNET_HashCode)); + GNUNET_assert (NULL != hash_str); + GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); + hash_str[HASH_CUTOFF] = 0; + + val_str = TALER_amount_to_string (&p->value); + for (i = 0; i < strlen (val_str); i++) + if ( (':' == val_str[i]) || + ('.' == val_str[i]) ) + val_str[i] = '_'; + + GNUNET_snprintf (dir, + sizeof (dir), + "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s", + mint_directory, + val_str, + hash_str); + GNUNET_free (hash_str); + GNUNET_free (val_str); + return dir; +} + + +/** + * Obtain the name of the file we would use to store the key + * information for a coin of the given type @a p and validity + * start time @a start + * + * @param p parameters for the coin + * @param start when would the coin begin to be issued + * @return name of the file to use for this coin + * (valid until next call to this function) + */ +static const char * +get_cointype_file (const struct CoinTypeParams *p, + struct GNUNET_TIME_Absolute start) +{ + static char filename[4096]; + const char *dir; + + dir = get_cointype_dir (p); + GNUNET_snprintf (filename, + sizeof (filename), + "%s" DIR_SEPARATOR_STR "%llu", + dir, + (unsigned long long) start.abs_value_us); + return filename; +} + + +/** + * Get the latest key file from a past run of the key generation + * tool. Used to calculate the starting time for the keys we + * generate during this invocation. This function is used to + * handle both signing keys and coin keys, as in both cases + * the filenames correspond to the timestamps we need. + * + * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated + * to contain the highest timestamp (below #now) + * that was found + * @param filename complete filename (absolute path) + * @return #GNUNET_OK (to continue to iterate) + */ +static int +get_anchor_iter (void *cls, + const char *filename) +{ + struct GNUNET_TIME_Absolute *anchor = cls; + struct GNUNET_TIME_Absolute stamp; + const char *base; + char *end = NULL; + + base = GNUNET_STRINGS_get_short_name (filename); + stamp.abs_value_us = strtol (base, + &end, + 10); + if ((NULL == end) || (0 != *end)) + { + fprintf(stderr, + "Ignoring unexpected file `%s'.\n", + filename); + return GNUNET_OK; + } + if (stamp.abs_value_us <= now.abs_value_us) + *anchor = GNUNET_TIME_absolute_max (stamp, + *anchor); + return GNUNET_OK; +} + + +/** + * Get the timestamp where the first new key should be generated. + * Relies on correctly named key files (as we do not parse them, + * but just look at the filenames to "guess" at their contents). + * + * @param dir directory that should contain the existing keys + * @param duration how long is one key valid (for signing)? + * @param overlap what's the overlap between the keys validity period? + * @param[out] anchor the timestamp where the first new key should be generated + */ +static void +get_anchor (const char *dir, + struct GNUNET_TIME_Relative duration, + struct GNUNET_TIME_Relative overlap, + struct GNUNET_TIME_Absolute *anchor) +{ + GNUNET_assert (0 == duration.rel_value_us % 1000000); + GNUNET_assert (0 == overlap.rel_value_us % 1000000); + if (GNUNET_YES != + GNUNET_DISK_directory_test (dir, + GNUNET_YES)) + { + *anchor = now; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No existing keys found, starting with fresh key set.\n"); + return; + } + *anchor = GNUNET_TIME_UNIT_ZERO_ABS; + if (-1 == + GNUNET_DISK_directory_scan (dir, + &get_anchor_iter, + anchor)) + { + *anchor = now; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No existing keys found, starting with fresh key set.\n"); + return; + } + + /* FIXME: this check is a bit dubious, as 'now' + may be way into the future if we want to generate + many keys... #3727*/ + if ((GNUNET_TIME_absolute_add (*anchor, + duration)).abs_value_us < now.abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Existing keys are way too old, starting with fresh key set.\n"); + *anchor = now; + } + else if (anchor->abs_value_us != now.abs_value_us) // Also odd... + { + /* Real starting time is the last start time + duration - overlap */ + *anchor = GNUNET_TIME_absolute_add (*anchor, + duration); + *anchor = GNUNET_TIME_absolute_subtract (*anchor, + overlap); + } + /* anchor is now the stamp where we need to create a new key */ +} + + +/** + * Create a mint signing key (for signing mint messages, not for coins) + * and assert its correctness by signing it with the master key. + * + * @param start start time of the validity period for the key + * @param duration how long should the key be valid + * @param pi[OUT] set to the signing key information + */ +static void +create_signkey_issue_priv (struct GNUNET_TIME_Absolute start, + struct GNUNET_TIME_Relative duration, + struct TALER_MintSigningKeyValidityPSPriv *pi) +{ + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + struct TALER_MintSigningKeyValidityPS *issue = &pi->issue; + + priv = GNUNET_CRYPTO_eddsa_key_create (); + pi->signkey_priv.eddsa_priv = *priv; + GNUNET_free (priv); + issue->master_public_key = master_public_key; + issue->start = GNUNET_TIME_absolute_hton (start); + issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, + duration)); + GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv.eddsa_priv, + &issue->signkey_pub.eddsa_pub); + issue->purpose.purpose = htonl (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY); + issue->purpose.size = htonl (sizeof (struct TALER_MintSigningKeyValidityPS) - + offsetof (struct TALER_MintSigningKeyValidityPS, + purpose)); + + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv, + &issue->purpose, + &issue->signature.eddsa_signature)); +} + + +/** + * Generate signing keys starting from the last key found to + * the lookahead time. + * + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +mint_keys_update_signkeys () +{ + struct GNUNET_TIME_Relative signkey_duration; + struct GNUNET_TIME_Absolute anchor; + char *signkey_dir; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + "mint_keys", + "signkey_duration", + &signkey_duration)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint_keys", + "signkey_duration"); + return GNUNET_SYSERR; + } + ROUND_TO_SECS (signkey_duration, + rel_value_us); + GNUNET_asprintf (&signkey_dir, + "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, + mint_directory); + /* make sure the directory exists */ + if (GNUNET_OK != + GNUNET_DISK_directory_create (signkey_dir)) + { + fprintf (stderr, + "Failed to create signing key directory\n"); + return GNUNET_SYSERR; + } + + get_anchor (signkey_dir, + signkey_duration, + GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */, + &anchor); + + while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) + { + const char *skf; + struct TALER_MintSigningKeyValidityPSPriv signkey_issue; + ssize_t nwrite; + + skf = get_signkey_file (anchor); + GNUNET_break (GNUNET_YES != + GNUNET_DISK_file_test (skf)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Generating signing key for %s.\n", + GNUNET_STRINGS_absolute_time_to_string (anchor)); + create_signkey_issue_priv (anchor, + signkey_duration, + &signkey_issue); + nwrite = GNUNET_DISK_fn_write (skf, + &signkey_issue, + sizeof (struct TALER_MintSigningKeyValidityPS), + GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ); + if (nwrite != sizeof (struct TALER_MintSigningKeyValidityPS)) + { + fprintf (stderr, + "Failed to write to file `%s': %s\n", + skf, + STRERROR (errno)); + return GNUNET_SYSERR; + } + anchor = GNUNET_TIME_absolute_add (anchor, + signkey_duration); + } + return GNUNET_OK; +} + + +/** + * Parse configuration for coin type parameters. Also determines + * our anchor by looking at the existing coins of the same type. + * + * @param ct section in the configuration file giving the coin type parameters + * @param params[OUT] set to the coin parameters from the configuration + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid + */ +static int +get_cointype_params (const char *ct, + struct CoinTypeParams *params) +{ + const char *dir; + unsigned long long rsa_keysize; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + ct, + "duration_withdraw", + ¶ms->duration_withdraw)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "duration_withdraw"); + return GNUNET_SYSERR; + } + ROUND_TO_SECS (params->duration_withdraw, + rel_value_us); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + ct, + "duration_spend", + ¶ms->duration_spend)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "duration_spend"); + return GNUNET_SYSERR; + } + ROUND_TO_SECS (params->duration_spend, + rel_value_us); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + ct, + "duration_overlap", + ¶ms->duration_overlap)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "mint_denom_duration_overlap"); + return GNUNET_SYSERR; + } + ROUND_TO_SECS (params->duration_overlap, + rel_value_us); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (kcfg, + ct, + "rsa_keysize", + &rsa_keysize)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "rsa_keysize"); + return GNUNET_SYSERR; + } + if ( (rsa_keysize > 4 * 2048) || + (rsa_keysize < 1024) ) + { + fprintf (stderr, + "Given RSA keysize %llu outside of permitted range\n", + rsa_keysize); + return GNUNET_SYSERR; + } + params->rsa_keysize = (unsigned int) rsa_keysize; + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "value", + ¶ms->value)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "value"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "fee_withdraw", + ¶ms->fee_withdraw)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "fee_withdraw"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "fee_deposit", + ¶ms->fee_deposit)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "fee_deposit"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "fee_refresh", + ¶ms->fee_refresh)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "fee_refresh"); + return GNUNET_SYSERR; + } + + dir = get_cointype_dir (params); + get_anchor (dir, + params->duration_spend, + params->duration_overlap, + ¶ms->anchor); + return GNUNET_OK; +} + + +/** + * Initialize the private and public key information structure for + * signing coins into existence. Generates the private signing key + * and signes it together with the coin's meta data using the master + * signing key. + * + * @param params parameters used to initialize the @a dki + * @param dki[OUT] initialized according to @a params + */ +static void +create_denomkey_issue (const struct CoinTypeParams *params, + struct TALER_DenominationKeyIssueInformation *dki) +{ + dki->denom_priv.rsa_private_key + = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize); + GNUNET_assert (NULL != dki->denom_priv.rsa_private_key); + dki->denom_pub.rsa_public_key + = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, + &dki->issue.denom_hash); + dki->issue.master = master_public_key; + dki->issue.start = GNUNET_TIME_absolute_hton (params->anchor); + dki->issue.expire_withdraw = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, + params->duration_withdraw)); + dki->issue.expire_spend = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, + params->duration_spend)); + TALER_amount_hton (&dki->issue.value, + ¶ms->value); + TALER_amount_hton (&dki->issue.fee_withdraw, + ¶ms->fee_withdraw); + TALER_amount_hton (&dki->issue.fee_deposit, + ¶ms->fee_deposit); + TALER_amount_hton (&dki->issue.fee_refresh, + ¶ms->fee_refresh); + dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY); + dki->issue.purpose.size = htonl (sizeof (struct TALER_DenominationKeyIssueInformation) - + offsetof (struct TALER_DenominationKeyIssueInformation, + issue.purpose)); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv, + &dki->issue.purpose, + &dki->issue.signature.eddsa_signature)); +} + + +/** + * Generate new coin signing keys for the coin type of the given @a + * coin_alias. + * + * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure + * @param coin_alias name of the coin's section in the configuration + */ +static void +mint_keys_update_cointype (void *cls, + const char *coin_alias) +{ + int *ret = cls; + struct CoinTypeParams p; + const char *dkf; + struct TALER_DenominationKeyIssueInformation denomkey_issue; + + if (0 != strncasecmp (coin_alias, + "coin_", + strlen ("coin_"))) + return; /* not a coin definition */ + if (GNUNET_OK != + get_cointype_params (coin_alias, + &p)) + { + *ret = GNUNET_SYSERR; + return; + } + if (GNUNET_OK != + GNUNET_DISK_directory_create (get_cointype_dir (&p))) + { + *ret = GNUNET_SYSERR; + return; + } + + while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) + { + dkf = get_cointype_file (&p, + p.anchor); + GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Generating denomination key for type `%s', start %s at %s\n", + coin_alias, + GNUNET_STRINGS_absolute_time_to_string (p.anchor), + dkf); + create_denomkey_issue (&p, + &denomkey_issue); + if (GNUNET_OK != + TALER_MINT_write_denom_key (dkf, + &denomkey_issue)) + { + fprintf (stderr, + "Failed to write denomination key information to file `%s'.\n", + dkf); + *ret = GNUNET_SYSERR; + GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + return; + } + GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + p.anchor = GNUNET_TIME_absolute_add (p.anchor, + p.duration_spend); + p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, + p.duration_overlap); + } +} + + +/** + * Update all of the denomination keys of the mint. + * + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +mint_keys_update_denomkeys () +{ + int ok; + + ok = GNUNET_OK; + GNUNET_CONFIGURATION_iterate_sections (kcfg, + &mint_keys_update_cointype, + &ok); + return ok; +} + + +/** + * The main function of the taler-mint-keyup tool. This tool is used + * to create the signing and denomination keys for the mint. It uses + * the long-term offline private key and writes the (additional) key + * files to the respective mint directory (from where they can then be + * copied to the online server). Note that we need (at least) the + * most recent generated previous keys so as to align the validity + * periods. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, + char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"), + {'m', "master-key", "FILE", + "master key file (private key)", 1, + &GNUNET_GETOPT_set_filename, &masterkeyfile}, + {'d', "mint-dir", "DIR", + "mint directory with keys to update", 1, + &GNUNET_GETOPT_set_filename, &mint_directory}, + {'t', "time", "TIMESTAMP", + "pretend it is a different time for the update", 0, + &GNUNET_GETOPT_set_string, &pretend_time_str}, + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_TIME_Relative lookahead_sign; + struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; + + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-mint-keyup", + "WARNING", + NULL)); + + if (GNUNET_GETOPT_run ("taler-mint-keyup", + options, + argc, argv) < 0) + return 1; + if (NULL == mint_directory) + { + fprintf (stderr, + "Mint directory not given\n"); + return 1; + } + if (NULL != pretend_time_str) + { + if (GNUNET_OK != + GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, + &now)) + { + fprintf (stderr, + "timestamp `%s' invalid\n", + pretend_time_str); + return 1; + } + } + else + { + now = GNUNET_TIME_absolute_get (); + } + ROUND_TO_SECS (now, abs_value_us); + + kcfg = TALER_config_load (mint_directory); + if (NULL == kcfg) + { + fprintf (stderr, + "Failed to load mint configuration\n"); + return 1; + } + if (NULL == masterkeyfile) + { + fprintf (stderr, + "Master key file not given\n"); + return 1; + } + eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); + if (NULL == eddsa_priv) + { + fprintf (stderr, + "Failed to initialize master key from file `%s'\n", + masterkeyfile); + return 1; + } + master_priv.eddsa_priv = *eddsa_priv; + GNUNET_free (eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, + &master_public_key.eddsa_pub); + + /* check if key from file matches the one from the configuration */ + { + struct GNUNET_CRYPTO_EddsaPublicKey master_public_key_from_cfg; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_data (kcfg, + "mint", + "master_public_key", + &master_public_key_from_cfg, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "master_public_key"); + return 1; + } + if (0 != + memcmp (&master_public_key, + &master_public_key_from_cfg, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "mint", + "master_public_key", + _("does not match with private key")); + return 1; + } + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + "mint_keys", + "lookahead_sign", + &lookahead_sign)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint_keys", + "lookahead_sign"); + return GNUNET_SYSERR; + } + if (0 == lookahead_sign.rel_value_us) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "mint_keys", + "lookahead_sign", + _("must not be zero")); + return GNUNET_SYSERR; + } + ROUND_TO_SECS (lookahead_sign, + rel_value_us); + lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, + lookahead_sign); + + + /* finally, do actual work */ + if (GNUNET_OK != mint_keys_update_signkeys ()) + return 1; + + if (GNUNET_OK != mint_keys_update_denomkeys ()) + return 1; + return 0; +} + +/* end of taler-mint-keyup.c */ diff --git a/src/mint-tools/taler-mint-reservemod.c b/src/mint-tools/taler-mint-reservemod.c new file mode 100644 index 000000000..3261f47cf --- /dev/null +++ b/src/mint-tools/taler-mint-reservemod.c @@ -0,0 +1,305 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-reservemod.c + * @brief Modify reserves. Allows manipulation of reserve balances. + * @author Florian Dold + * @author Benedikt Mueller + */ +#include "platform.h" +#include +#include +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_mintdb_plugin.h" +#include "taler_mintdb_lib.h" + + +/** + * Director of the mint, containing the keys. + */ +static char *mint_directory; + +/** + * Public key of the reserve to manipulate. + */ +static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub; + +/** + * Handle to the mint's configuration + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Database connection handle. + */ +static PGconn *db_conn; + + +/** + * Create a new or add to existing reserve. Fails if currencies do + * not match. + * + * @param denom denomination to add + * @return #GNUNET_OK on success, + * #GNUNET_SYSERR on error + */ +// FIXME: this should use the DB abstraction layer. (#3717) +// FIXME: this should be done by adding an inbound transaction +// to the table with the transactions for this reserve, +// not by modifying some 'total' value for the reserve! +// (we should in fact probably never modify, always just append!) (#3633) +static int +reservemod_add (struct TALER_Amount denom) +{ + PGresult *result; + const void *param_values[] = { + reserve_pub + }; + int param_lengths[] = { + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) + }; + int param_formats[] = { + 1 + }; + struct TALER_Amount old_denom; + struct TALER_Amount new_denom; + struct TALER_AmountNBO new_denom_nbo; + + result = PQexecParams (db_conn, + "SELECT balance_value, balance_fraction, balance_currency" + " FROM reserves" + " WHERE reserve_pub=$1" + " LIMIT 1;", + 1, + NULL, + (const char * const *) param_values, + param_lengths, + param_formats, + 1); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + fprintf (stderr, + "Select failed: %s\n", + PQresultErrorMessage (result)); + return GNUNET_SYSERR; + } + if (0 == PQntuples (result)) + { + struct GNUNET_TIME_AbsoluteNBO exnbo; + uint32_t value = htonl (denom.value); + uint32_t fraction = htonl (denom.fraction); + const void *param_values[] = { + reserve_pub, + &value, + &fraction, + denom.currency, + &exnbo + }; + int param_lengths[] = { + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), + sizeof (uint32_t), + sizeof (uint32_t), + strlen (denom.currency), + sizeof (struct GNUNET_TIME_AbsoluteNBO) + }; + int param_formats[] = { + 1, 1, 1, 1, 1 + }; + + exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS)); + result = PQexecParams (db_conn, + "INSERT INTO reserves (reserve_pub, balance_value, balance_fraction, balance_currency, expiration_date)" + " VALUES ($1,$2,$3,$4,$5);", + 5, + NULL, + (const char **) param_values, + param_lengths, + param_formats, + 1); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + fprintf (stderr, + "Insert failed: %s\n", + PQresultErrorMessage (result)); + return GNUNET_SYSERR; + } + } + else + { + const void *param_values[] = { + &new_denom_nbo.value, + &new_denom_nbo.fraction, + reserve_pub + }; + int param_lengths[] = { + sizeof (new_denom_nbo.value), + sizeof (new_denom_nbo.fraction), + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) + }; + int param_formats[] = { + 1, 1, 1 + }; + + GNUNET_assert (GNUNET_OK == + TALER_PQ_extract_amount (result, 0, + "balance_value", + "balance_fraction", + "balance_currency", + &old_denom)); + TALER_amount_add (&new_denom, + &old_denom, + &denom); + TALER_amount_hton (&new_denom_nbo, + &new_denom); + result = PQexecParams (db_conn, + "UPDATE reserves" + " SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL" + " WHERE reserve_pub = $3;", + 3, + NULL, + (const char **) param_values, + param_lengths, + param_formats, + 1); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + fprintf (stderr, + "Update failed: %s\n", + PQresultErrorMessage (result)); + return GNUNET_SYSERR; + } + /* Yes, for historic reasons libpq returns a 'const char *'... */ + if (0 != strcmp ("1", + PQcmdTuples (result))) + { + fprintf (stderr, + "Update failed (updated `%s' tupes instead of '1')\n", + PQcmdTuples (result)); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +/** + * The main function of the reservemod tool + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + static char *reserve_pub_str; + static char *add_str; + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-reservemod OPTIONS"), + {'d', "mint-dir", "DIR", + "mint directory with keys to update", 1, + &GNUNET_GETOPT_set_filename, &mint_directory}, + {'R', "reserve", "KEY", + "reserve (public key) to modify", 1, + &GNUNET_GETOPT_set_string, &reserve_pub_str}, + {'a', "add", "DENOM", + "value to add", 1, + &GNUNET_GETOPT_set_string, &add_str}, + GNUNET_GETOPT_OPTION_END + }; + char *connection_cfg_str; + + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-mint-reservemod", + "WARNING", + NULL)); + + if (GNUNET_GETOPT_run ("taler-mint-reservemod", + options, + argc, argv) < 0) + return 1; + if (NULL == mint_directory) + { + fprintf (stderr, + "Mint directory not given\n"); + return 1; + } + + reserve_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey); + if ((NULL == reserve_pub_str) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (reserve_pub_str, + strlen (reserve_pub_str), + reserve_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))) + { + fprintf (stderr, + "Parsing reserve key invalid\n"); + return 1; + } + cfg = TALER_config_load (mint_directory); + if (NULL == cfg) + { + fprintf (stderr, + "Failed to load mint configuration\n"); + return 1; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "db", + &connection_cfg_str)) + { + fprintf (stderr, + "Database configuration string not found\n"); + return 1; + } + db_conn = PQconnectdb (connection_cfg_str); + if (CONNECTION_OK != PQstatus (db_conn)) + { + fprintf (stderr, + "Database connection failed: %s\n", + PQerrorMessage (db_conn)); + return 1; + } + if (NULL != add_str) + { + struct TALER_Amount add_value; + + if (GNUNET_OK != + TALER_string_to_amount (add_str, + &add_value)) + { + fprintf (stderr, + "Failed to parse currency amount `%s'\n", + add_str); + return 1; + } + if (GNUNET_OK != + reservemod_add (add_value)) + { + fprintf (stderr, + "Failed to update reserve.\n"); + return 1; + } + } + return 0; +} + +/* end taler-mint-reservemod.c */ diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index f8e5696ff..538020e9e 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -1,82 +1,8 @@ # This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/pq/ $(POSTGRESQL_CPPFLAGS) - -plugindir = $(libdir)/taler - -plugin_LTLIBRARIES = \ - libtaler_plugin_mintdb_postgres.la - -EXTRA_DIST = plugin_mintdb_common.c - -libtaler_plugin_mintdb_postgres_la_SOURCES = \ - plugin_mintdb_postgres.c -libtaler_plugin_mintdb_postgres_la_LIBADD = \ - $(LTLIBINTL) -libtaler_plugin_mintdb_postgres_la_LDFLAGS = \ - $(TALER_PLUGIN_LDFLAGS) \ - -lpq \ - -lgnunetutil - -lib_LTLIBRARIES = \ - libtalermint_common.la - -libtalermint_common_la_SOURCES = \ - key_io.c key_io.h \ - plugin.c plugin.h - -libtalermint_common_la_LIBADD = \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil - -libtalermint_common_la_LDFLAGS = \ - $(POSTGRESQL_LDFLAGS) \ - -version-info 0:0:0 \ - -no-undefined - +AM_CPPFLAGS = -I$(top_srcdir)/src/include bin_PROGRAMS = \ - taler-mint-keyup \ - taler-mint-keycheck \ - taler-mint-reservemod \ - taler-mint-httpd \ - taler-mint-dbinit - -taler_mint_keyup_SOURCES = \ - taler-mint-keyup.c - -taler_mint_keyup_LDADD = \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - $(top_builddir)/src/pq/libtalerpq.la \ - $(top_builddir)/src/mint/libtalermint_common.la \ - -lpq \ - -lgnunetutil -taler_mint_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS) - - -taler_mint_keycheck_SOURCES = \ - taler-mint-keycheck.c - -taler_mint_keycheck_LDADD = \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - $(top_builddir)/src/pq/libtalerpq.la \ - $(top_builddir)/src/mint/libtalermint_common.la \ - -lgnunetutil \ - -lpq -taler_mint_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS) - -taler_mint_reservemod_SOURCES = \ - taler-mint-reservemod.c -taler_mint_reservemod_LDADD = \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - $(top_builddir)/src/pq/libtalerpq.la \ - $(top_builddir)/src/mint/libtalermint_common.la \ - -lpq \ - -lgnunetutil -taler_mint_reservemod_LDFLAGS = \ - $(POSTGRESQL_LDFLAGS) + taler-mint-httpd taler_mint_httpd_SOURCES = \ taler-mint-httpd.c \ @@ -92,56 +18,8 @@ taler_mint_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/pq/libtalerpq.la \ - $(top_builddir)/src/mint/libtalermint_common.la \ - -lpq \ + $(top_builddir)/src/mintdb/libtalermintdb.la \ -lmicrohttpd \ -ljansson \ -lgnunetutil \ -lpthread -taler_mint_httpd_LDFLAGS = \ - $(POSTGRESQL_LDFLAGS) - - -taler_mint_dbinit_SOURCES = \ - taler-mint-dbinit.c -taler_mint_dbinit_LDADD = \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - $(top_builddir)/src/pq/libtalerpq.la \ - $(top_builddir)/src/mint/libtalermint_common.la \ - -lpq \ - -lgnunetutil -taler_mint_dbinit_LDFLAGS = $(POSTGRESQL_LDFLAGS) - -check_PROGRAMS = \ - test-mint-deposits \ - test-mint-common \ - test-mint-db-postgres - -test_mint_deposits_SOURCES = \ - test_mint_deposits.c -test_mint_deposits_LDADD = \ - libtalermint_common.la \ - $(top_srcdir)/src/util/libtalerutil.la \ - $(top_srcdir)/src/pq/libtalerpq.la \ - -lgnunetutil \ - -ljansson \ - -lpq - -test_mint_common_SOURCES = \ - test_mint_common.c -test_mint_common_LDADD = \ - libtalermint_common.la \ - $(top_srcdir)/src/util/libtalerutil.la \ - $(top_srcdir)/src/pq/libtalerpq.la \ - -lgnunetutil - -test_mint_db_postgres_SOURCES = \ - test_mint_db.c -test_mint_db_postgres_LDADD = \ - libtalermint_common.la \ - $(top_srcdir)/src/util/libtalerutil.la \ - $(top_srcdir)/src/pq/libtalerpq.la \ - -lgnunetutil -ljansson -EXTRA_test_mint_db_postgres_DEPENDENCIES = \ - libtaler_plugin_mintdb_postgres.la diff --git a/src/mint/key_io.c b/src/mint/key_io.c deleted file mode 100644 index 02d888d5d..000000000 --- a/src/mint/key_io.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file mint/key_io.c - * @brief I/O operations for the Mint's private keys - * @author Florian Dold - * @author Benedikt Mueller - * @author Sree Harsha Totakura - * @author Christian Grothoff - */ -#include "platform.h" -#include "key_io.h" - - -/** - * Closure for the #signkeys_iterate_dir_iter(). - */ -struct SignkeysIterateContext -{ - - /** - * Function to call on each signing key. - */ - TALER_MINT_SignkeyIterator it; - - /** - * Closure for @e it. - */ - void *it_cls; -}; - - -/** - * Function called on each file in the directory with - * our signing keys. Parses the file and calls the - * iterator from @a cls. - * - * @param cls the `struct SignkeysIterateContext *` - * @param filename name of the file to parse - * @return #GNUNET_OK to continue, - * #GNUNET_NO to stop iteration without error, - * #GNUNET_SYSERR to stop iteration with error - */ -static int -signkeys_iterate_dir_iter (void *cls, - const char *filename) -{ - struct SignkeysIterateContext *skc = cls; - ssize_t nread; - struct TALER_MintSigningKeyValidityPSPriv issue; - - nread = GNUNET_DISK_fn_read (filename, - &issue, - sizeof (struct TALER_MintSigningKeyValidityPSPriv)); - if (nread != sizeof (struct TALER_MintSigningKeyValidityPSPriv)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid signkey file `%s': wrong size\n", - filename); - return GNUNET_OK; - } - return skc->it (skc->it_cls, - filename, - &issue); -} - - -/** - * Call @a it for each signing key found in the @a mint_base_dir. - * - * @param mint_base_dir base directory for the mint, - * the signing keys must be in the #DIR_SIGNKEYS - * subdirectory - * @param it function to call on each signing key - * @param it_cls closure for @a it - * @return number of files found (may not match - * number of keys given to @a it as malformed - * files are simply skipped), -1 on error - */ -int -TALER_MINT_signkeys_iterate (const char *mint_base_dir, - TALER_MINT_SignkeyIterator it, - void *it_cls) -{ - char *signkey_dir; - struct SignkeysIterateContext skc; - int ret; - - GNUNET_asprintf (&signkey_dir, - "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, - mint_base_dir); - skc.it = it; - skc.it_cls = it_cls; - ret = GNUNET_DISK_directory_scan (signkey_dir, - &signkeys_iterate_dir_iter, - &skc); - GNUNET_free (signkey_dir); - return ret; -} - - -/** - * Import a denomination key from the given file. - * - * @param filename the file to import the key from - * @param[OUT] dki set to the imported denomination key - * @return #GNUNET_OK upon success; - * #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_read_denom_key (const char *filename, - struct TALER_DenominationKeyIssueInformation *dki) -{ - uint64_t size; - size_t offset; - void *data; - struct GNUNET_CRYPTO_rsa_PrivateKey *priv; - - if (GNUNET_OK != GNUNET_DISK_file_size (filename, - &size, - GNUNET_YES, - GNUNET_YES)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Skipping inaccessable denomination key file `%s'\n", - filename); - return GNUNET_SYSERR; - } - offset = sizeof (struct TALER_DenominationKeyValidityPS); - if (size <= offset) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - data = GNUNET_malloc (size); - if (size != - GNUNET_DISK_fn_read (filename, - data, - size)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "read", - filename); - GNUNET_free (data); - return GNUNET_SYSERR; - } - if (NULL == - (priv = GNUNET_CRYPTO_rsa_private_key_decode (data + offset, - size - offset))) - { - GNUNET_free (data); - return GNUNET_SYSERR; - } - dki->denom_priv.rsa_private_key = priv; - dki->denom_pub.rsa_public_key - = GNUNET_CRYPTO_rsa_private_key_get_public (priv); - memcpy (&dki->issue, - data, - offset); - GNUNET_free (data); - return GNUNET_OK; -} - - -/** - * Exports a denomination key to the given file. - * - * @param filename the file where to write the denomination key - * @param dki the denomination key - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure. - */ -int -TALER_MINT_write_denom_key (const char *filename, - const struct TALER_DenominationKeyIssueInformation *dki) -{ - char *priv_enc; - size_t priv_enc_size; - struct GNUNET_DISK_FileHandle *fh; - ssize_t wrote; - size_t wsize; - int ret; - - fh = NULL; - priv_enc_size - = GNUNET_CRYPTO_rsa_private_key_encode (dki->denom_priv.rsa_private_key, - &priv_enc); - ret = GNUNET_SYSERR; - if (NULL == (fh = GNUNET_DISK_file_open - (filename, - GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE, - GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE))) - goto cleanup; - wsize = sizeof (struct TALER_DenominationKeyValidityPS); - if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh, - &dki->issue.signature, - wsize))) - goto cleanup; - if (wrote != wsize) - goto cleanup; - if (GNUNET_SYSERR == - (wrote = GNUNET_DISK_file_write (fh, - priv_enc, - priv_enc_size))) - goto cleanup; - if (wrote != priv_enc_size) - goto cleanup; - ret = GNUNET_OK; - cleanup: - GNUNET_free_non_null (priv_enc); - if (NULL != fh) - (void) GNUNET_DISK_file_close (fh); - return ret; -} - - -/** - * Closure for #denomkeys_iterate_keydir_iter() and - * #denomkeys_iterate_topdir_iter(). - */ -struct DenomkeysIterateContext -{ - - /** - * Set to the name of the directory below the top-level directory - * during the call to #denomkeys_iterate_keydir_iter(). - */ - const char *alias; - - /** - * Function to call on each denomination key. - */ - TALER_MINT_DenomkeyIterator it; - - /** - * Closure for @e it. - */ - void *it_cls; -}; - - -/** - * Decode the denomination key in the given file @a filename and call - * the callback in @a cls with the information. - * - * @param cls the `struct DenomkeysIterateContext *` - * @param filename name of a file that should contain - * a denomination key - * @return #GNUNET_OK to continue to iterate - * #GNUNET_NO to abort iteration with success - * #GNUNET_SYSERR to abort iteration with failure - */ -static int -denomkeys_iterate_keydir_iter (void *cls, - const char *filename) -{ - struct DenomkeysIterateContext *dic = cls; - struct TALER_DenominationKeyIssueInformation issue; - - if (GNUNET_OK != - TALER_MINT_read_denom_key (filename, - &issue)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid denomkey file: '%s'\n", - filename); - return GNUNET_OK; - } - return dic->it (dic->it_cls, - dic->alias, - &issue); -} - - -/** - * Function called on each subdirectory in the #DIR_DENOMKEYS. Will - * call the #denomkeys_iterate_keydir_iter() on each file in the - * subdirectory. - * - * @param cls the `struct DenomkeysIterateContext *` - * @param filename name of the subdirectory to scan - * @return #GNUNET_OK on success, - * #GNUNET_SYSERR if we need to abort - */ -static int -denomkeys_iterate_topdir_iter (void *cls, - const char *filename) -{ - struct DenomkeysIterateContext *dic = cls; - - dic->alias = GNUNET_STRINGS_get_short_name (filename); - if (0 > GNUNET_DISK_directory_scan (filename, - &denomkeys_iterate_keydir_iter, - dic)) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Call @a it for each denomination key found in the @a mint_base_dir. - * - * @param mint_base_dir base directory for the mint, - * the signing keys must be in the #DIR_DENOMKEYS - * subdirectory - * @param it function to call on each denomination key found - * @param it_cls closure for @a it - * @return -1 on error, 0 if no files were found, otherwise - * a positive number (however, even with a positive - * number it is possible that @a it was never called - * as maybe none of the files were well-formed) - */ -int -TALER_MINT_denomkeys_iterate (const char *mint_base_dir, - TALER_MINT_DenomkeyIterator it, - void *it_cls) -{ - char *dir; - struct DenomkeysIterateContext dic; - int ret; - - GNUNET_asprintf (&dir, - "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS, - mint_base_dir); - dic.it = it; - dic.it_cls = it_cls; - ret = GNUNET_DISK_directory_scan (dir, - &denomkeys_iterate_topdir_iter, - &dic); - GNUNET_free (dir); - return ret; -} - - -/* end of key_io.c */ diff --git a/src/mint/key_io.h b/src/mint/key_io.h deleted file mode 100644 index 0ff968976..000000000 --- a/src/mint/key_io.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file mint/key_io.h - * @brief IO operations for the mint's private keys - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef KEY_IO_H -#define KEY_IO_H - -#include -#include "taler_signatures.h" - -/** - * Subdirectroy under the mint's base directory which contains - * the mint's signing keys. - */ -#define DIR_SIGNKEYS "signkeys" - -/** - * Subdirectory under the mint's base directory which contains - * the mint's denomination keys. - */ -#define DIR_DENOMKEYS "denomkeys" - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * On disk format used for a mint signing key. Signing keys are used - * by the mint to affirm its messages, but not to create coins. - * Includes the private key followed by the public information about - * the signing key. - */ -struct TALER_MintSigningKeyValidityPSPriv -{ - /** - * Private key part of the mint's signing key. - */ - struct TALER_MintPrivateKeyP signkey_priv; - - /** - * Public information about a mint signing key. - */ - struct TALER_MintSigningKeyValidityPS issue; -}; - - -GNUNET_NETWORK_STRUCT_END - - -/** - * All information about a denomination key (which is used to - * sign coins into existence). - */ -struct TALER_DenominationKeyIssueInformation -{ - /** - * The private key of the denomination. Will be NULL if the private - * key is not available (this is the case after the key has expired - * for signing coins, but is still valid for depositing coins). - */ - struct TALER_DenominationPrivateKey denom_priv; - - /** - * Decoded denomination public key (the hash of it is in - * @e issue, but we sometimes need the full public key as well). - */ - struct TALER_DenominationPublicKey denom_pub; - - /** - * Signed public information about a denomination key. - */ - struct TALER_DenominationKeyValidityPS issue; -}; - - -/** - * Iterator over signing keys. - * - * @param cls closure - * @param filename name of the file the key came from - * @param ski the sign key - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -typedef int -(*TALER_MINT_SignkeyIterator)(void *cls, - const char *filename, - const struct TALER_MintSigningKeyValidityPSPriv *ski); - - -/** - * Iterator over denomination keys. - * - * @param cls closure - * @param dki the denomination key - * @param alias coin alias - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -typedef int -(*TALER_MINT_DenomkeyIterator)(void *cls, - const char *alias, - const struct TALER_DenominationKeyIssueInformation *dki); - - - -/** - * Call @a it for each signing key found in the @a mint_base_dir. - * - * @param mint_base_dir base directory for the mint, - * the signing keys must be in the #DIR_SIGNKEYS - * subdirectory - * @param it function to call on each signing key - * @param it_cls closure for @a it - * @return number of files found (may not match - * number of keys given to @a it as malformed - * files are simply skipped), -1 on error - */ -int -TALER_MINT_signkeys_iterate (const char *mint_base_dir, - TALER_MINT_SignkeyIterator it, - void *it_cls); - - -/** - * Call @a it for each denomination key found in the @a mint_base_dir. - * - * @param mint_base_dir base directory for the mint, - * the signing keys must be in the #DIR_DENOMKEYS - * subdirectory - * @param it function to call on each denomination key found - * @param it_cls closure for @a it - * @return -1 on error, 0 if no files were found, otherwise - * a positive number (however, even with a positive - * number it is possible that @a it was never called - * as maybe none of the files were well-formed) - */ -int -TALER_MINT_denomkeys_iterate (const char *mint_base_dir, - TALER_MINT_DenomkeyIterator it, - void *it_cls); - - -/** - * Exports a denomination key to the given file. - * - * @param filename the file where to write the denomination key - * @param dki the denomination key - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure. - */ -int -TALER_MINT_write_denom_key (const char *filename, - const struct TALER_DenominationKeyIssueInformation *dki); - - -/** - * Import a denomination key from the given file. - * - * @param filename the file to import the key from - * @param[OUT] dki set to the imported denomination key - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_read_denom_key (const char *filename, - struct TALER_DenominationKeyIssueInformation *dki); - - -#endif diff --git a/src/mint/plugin.c b/src/mint/plugin.c deleted file mode 100644 index 147d4c4c7..000000000 --- a/src/mint/plugin.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file mint/plugin.c - * @brief Logic to load database plugin - * @author Christian Grothoff - */ -#include "platform.h" -#include "plugin.h" -#include - - -/** - * Global variable with the plugin (once loaded). - */ -struct TALER_MINTDB_Plugin *plugin; - -/** - * Libtool search path before we started. - */ -static char *old_dlsearchpath; - - -/** - * Initialize the plugin. - * - * @param cfg configuration to use - * @return #GNUNET_OK on success - */ -int -TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char *plugin_name; - char *lib_name; - struct GNUNET_CONFIGURATION_Handle *cfg_dup; - - if (NULL != plugin) - return GNUNET_OK; - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint", - "db", - &plugin_name)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint", - "db"); - return GNUNET_SYSERR; - } - (void) GNUNET_asprintf (&lib_name, - "libtaler_plugin_mintdb_%s", - plugin_name); - GNUNET_free (plugin_name); - cfg_dup = GNUNET_CONFIGURATION_dup (cfg); - plugin = GNUNET_PLUGIN_load (lib_name, cfg_dup); - GNUNET_CONFIGURATION_destroy (cfg_dup); - GNUNET_free (lib_name); - if (NULL == plugin) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Shutdown the plugin. - */ -void -TALER_MINT_plugin_unload () -{ - if (NULL == plugin) - return; - GNUNET_assert (NULL == GNUNET_PLUGIN_unload (plugin->library_name, - plugin)); -} - - -/** - * Setup libtool paths. - */ -void __attribute__ ((constructor)) -plugin_init () -{ - int err; - const char *opath; - char *path; - char *cpath; - - err = lt_dlinit (); - if (err > 0) - { - FPRINTF (stderr, - _("Initialization of plugin mechanism failed: %s!\n"), - lt_dlerror ()); - return; - } - opath = lt_dlgetsearchpath (); - if (NULL != opath) - old_dlsearchpath = GNUNET_strdup (opath); - path = TALER_os_installation_get_path (GNUNET_OS_IPK_LIBDIR); - if (NULL != path) - { - if (NULL != opath) - { - GNUNET_asprintf (&cpath, "%s:%s", opath, path); - lt_dlsetsearchpath (cpath); - GNUNET_free (path); - GNUNET_free (cpath); - } - else - { - lt_dlsetsearchpath (path); - GNUNET_free (path); - } - } -} - - -/** - * Shutdown libtool. - */ -void __attribute__ ((destructor)) -plugin_fini () -{ - lt_dlsetsearchpath (old_dlsearchpath); - if (NULL != old_dlsearchpath) - { - GNUNET_free (old_dlsearchpath); - old_dlsearchpath = NULL; - } - lt_dlexit (); -} - - -/* end of plugin.c */ diff --git a/src/mint/plugin.h b/src/mint/plugin.h deleted file mode 100644 index 0dfb866da..000000000 --- a/src/mint/plugin.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file mint/plugin.h - * @brief Logic to load database plugins - * @author Christian Grothoff - */ -#ifndef PLUGIN_H -#define PLUGIN_H - -#include -#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 deleted file mode 100644 index a95cf4be2..000000000 --- a/src/mint/plugin_mintdb_common.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @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/plugin_mintdb_postgres.c b/src/mint/plugin_mintdb_postgres.c deleted file mode 100644 index 9846d1720..000000000 --- a/src/mint/plugin_mintdb_postgres.c +++ /dev/null @@ -1,2357 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file plugin_mintdb_postgres.c - * @brief Low-level (statement-level) Postgres database access for the mint - * @author Florian Dold - * @author Christian Grothoff - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include "taler_pq_lib.h" -#include "taler_signatures.h" -#include "taler_mintdb_plugin.h" -#include -#include - -#include "plugin_mintdb_common.c" - -#define TALER_TEMP_SCHEMA_NAME "taler_temporary" - -#define QUERY_ERR(result) \ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s\n", __FILE__, __LINE__, PQresultErrorMessage (result)) - - -#define BREAK_DB_ERR(result) do { \ - GNUNET_break(0); \ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \ - } while (0) - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -#define SQLEXEC_(conn, sql, result) \ - do { \ - result = PQexec (conn, sql); \ - if (PGRES_COMMAND_OK != PQresultStatus (result)) \ - { \ - BREAK_DB_ERR (result); \ - PQclear (result); result = NULL; \ - goto SQLEXEC_fail; \ - } \ - PQclear (result); result = NULL; \ - } while (0) - -/** - * This the length of the currency strings (without 0-termination) we use. Note - * that we need to use this at the DB layer instead of TALER_CURRENCY_LEN as the - * DB only needs to store 3 bytes instead of 8 bytes. - */ -#define TALER_PQ_CURRENCY_LEN 3 - - -/** - * Handle for a database session (per-thread, for transactions). - */ -struct TALER_MINTDB_Session -{ - /** - * Postgres connection handle. - */ - PGconn *conn; -}; - - -/** - * Type of the "cls" argument given to each of the functions in - * our API. - */ -struct PostgresClosure -{ - - /** - * Thread-local database connection. - * Contains a pointer to PGconn or NULL. - */ - pthread_key_t db_conn_threadlocal; - - /** - * Database connection string, as read from - * the configuration. - */ - char *connection_cfg_str; -}; - - - -/** - * Set the given connection to use a temporary schema - * - * @param db the database connection - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon error - */ -static int -set_temporary_schema (PGconn *db) -{ - PGresult *result; - - SQLEXEC_(db, - "CREATE SCHEMA IF NOT EXISTS " TALER_TEMP_SCHEMA_NAME ";" - "SET search_path to " TALER_TEMP_SCHEMA_NAME ";", - result); - return GNUNET_OK; - SQLEXEC_fail: - return GNUNET_SYSERR; -} - - -/** - * Drop the temporary taler schema. This is only useful for testcases - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static int -postgres_drop_temporary (void *cls, - struct TALER_MINTDB_Session *session) -{ - PGresult *result; - - SQLEXEC_ (session->conn, - "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;", - result); - return GNUNET_OK; - SQLEXEC_fail: - return GNUNET_SYSERR; -} - - -/** - * Create the necessary tables if they are not present - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param temporary should we use a temporary schema - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static int -postgres_create_tables (void *cls, - int temporary) -{ - struct PostgresClosure *pc = cls; - PGresult *result; - PGconn *conn; - - result = NULL; - conn = PQconnectdb (pc->connection_cfg_str); - if (CONNECTION_OK != PQstatus (conn)) - { - TALER_LOG_ERROR ("Database connection failed: %s\n", - PQerrorMessage (conn)); - GNUNET_break (0); - return GNUNET_SYSERR; - } - if ( (GNUNET_YES == temporary) && - (GNUNET_SYSERR == set_temporary_schema (conn))) - { - PQfinish (conn); - return GNUNET_SYSERR; - } -#define SQLEXEC(sql) SQLEXEC_(conn, sql, result); - /* reserves table is for summarization of a reserve. It is updated when new - funds are added and existing funds are withdrawn */ - SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves" - "(" - " reserve_pub BYTEA PRIMARY KEY" - ",current_balance_value INT8 NOT NULL" - ",current_balance_fraction INT4 NOT NULL" - ",balance_currency VARCHAR(4) NOT NULL" - ",expiration_date INT8 NOT NULL" - ")"); - /* reserves_in table collects the transactions which transfer funds into the - reserve. The amount and expiration date for the corresponding reserve are - updated when new transfer funds are added. The rows of this table - correspond to each incoming transaction. */ - SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in" - "(" - " reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE" - ",balance_value INT8 NOT NULL" - ",balance_fraction INT4 NOT NULL" - ",balance_currency VARCHAR(4) NOT NULL" - ",expiration_date INT8 NOT NULL" - ");"); - /* Create an index on the foreign key as it is not created automatically by PSQL */ - SQLEXEC ("CREATE INDEX reserves_in_reserve_pub_index" - " ON reserves_in (reserve_pub);"); - SQLEXEC ("CREATE TABLE IF NOT EXISTS collectable_blindcoins" - "(" - "blind_ev BYTEA PRIMARY KEY" - ",denom_pub BYTEA NOT NULL" /* FIXME: Make this a foreign key? */ - ",denom_sig BYTEA NOT NULL" - ",reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE" - ",reserve_sig BYTEA NOT NULL" - ");"); - SQLEXEC ("CREATE INDEX collectable_blindcoins_reserve_pub_index ON" - " collectable_blindcoins (reserve_pub)"); - SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins " - "(" - " coin_pub BYTEA NOT NULL PRIMARY KEY" - ",denom_pub BYTEA NOT NULL" - ",denom_sig BYTEA NOT NULL" - ",expended_value INT8 NOT NULL" - ",expended_fraction INT4 NOT NULL" - ",expended_currency VARCHAR(4) NOT NULL" - ",refresh_session_hash BYTEA" - ")"); - SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions " - "(" - " session_hash BYTEA PRIMARY KEY CHECK (length(session_hash) = 32)" - ",session_melt_sig BYTEA" - ",session_commit_sig BYTEA" - ",noreveal_index INT2 NOT NULL" - // non-zero if all reveals were ok - // and the new coin signatures are ready - ",reveal_ok BOOLEAN NOT NULL DEFAULT false" - ") "); - SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order " - "( " - " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)" - ",newcoin_index INT2 NOT NULL " - ",denom_pub BYTEA NOT NULL " - ",PRIMARY KEY (session_hash, newcoin_index)" - ") "); - SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link" - "(" - " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)" - ",transfer_pub BYTEA NOT NULL" - ",link_secret_enc BYTEA NOT NULL" - // index of the old coin in the customer's request - ",oldcoin_index INT2 NOT NULL" - // index for cut and choose, - // ranges from 0 to #TALER_CNC_KAPPA-1 - ",cnc_index INT2 NOT NULL" - ")"); - SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin" - "(" - " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) " - ",link_vector_enc BYTEA NOT NULL" - // index of the new coin in the customer's request - ",newcoin_index INT2 NOT NULL" - // index for cut and choose, - ",cnc_index INT2 NOT NULL" - ",coin_ev BYTEA NOT NULL" - ")"); - SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt" - "(" - " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) " - ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) " - ",denom_pub BYTEA NOT NULL " - ",oldcoin_index INT2 NOT NULL" - ")"); - SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_collectable" - "(" - " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) " - ",ev_sig BYTEA NOT NULL" - ",newcoin_index INT2 NOT NULL" - ")"); - SQLEXEC("CREATE TABLE IF NOT EXISTS deposits " - "( " - " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)" - ",denom_pub BYTEA NOT NULL" /* FIXME: Link this as a foreign key? */ - ",denom_sig BYTEA NOT NULL" - ",transaction_id INT8 NOT NULL" - ",amount_currency VARCHAR(4) NOT NULL" - ",amount_value INT8 NOT NULL" - ",amount_fraction INT4 NOT NULL" - ",merchant_pub BYTEA NOT NULL CHECK (length(merchant_pub)=32)" - ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)" - ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)" - ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)" - ",wire TEXT NOT NULL" - ")"); -#undef SQLEXEC - - PQfinish (conn); - return GNUNET_OK; - - SQLEXEC_fail: - PQfinish (conn); - return GNUNET_SYSERR; -} - - -/** - * Setup prepared statements. - * - * @param db_conn connection handle to initialize - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure - */ -static int -postgres_prepare (PGconn *db_conn) -{ - PGresult *result; - -#define PREPARE(name, sql, ...) \ - do { \ - result = PQprepare (db_conn, name, sql, __VA_ARGS__); \ - if (PGRES_COMMAND_OK != PQresultStatus (result)) \ - { \ - BREAK_DB_ERR (result); \ - PQclear (result); result = NULL; \ - return GNUNET_SYSERR; \ - } \ - PQclear (result); result = NULL; \ - } while (0); - - PREPARE ("get_reserve", - "SELECT " - "current_balance_value" - ",current_balance_fraction" - ",balance_currency " - ",expiration_date " - "FROM reserves " - "WHERE reserve_pub=$1 " - "LIMIT 1; ", - 1, NULL); - PREPARE ("create_reserve", - "INSERT INTO reserves (" - " reserve_pub," - " current_balance_value," - " current_balance_fraction," - " balance_currency," - " expiration_date) VALUES (" - "$1, $2, $3, $4, $5);", - 5, NULL); - PREPARE ("update_reserve", - "UPDATE reserves " - "SET" - " current_balance_value=$2 " - ",current_balance_fraction=$3 " - ",expiration_date=$4 " - "WHERE reserve_pub=$1 ", - 4, NULL); - PREPARE ("create_reserves_in_transaction", - "INSERT INTO reserves_in (" - " reserve_pub," - " balance_value," - " balance_fraction," - " balance_currency," - " expiration_date) VALUES (" - " $1, $2, $3, $4, $5);", - 5, NULL); - PREPARE ("get_reserves_in_transactions", - "SELECT" - " balance_value" - ",balance_fraction" - ",balance_currency" - ",expiration_date" - " FROM reserves_in WHERE reserve_pub=$1", - 1, NULL); - PREPARE ("insert_collectable_blindcoin", - "INSERT INTO collectable_blindcoins ( " - " blind_ev" - ",denom_pub, denom_sig" - ",reserve_pub, reserve_sig) " - "VALUES ($1, $2, $3, $4, $5)", - 5, NULL); - PREPARE ("get_collectable_blindcoin", - "SELECT " - " denom_pub, denom_sig" - ",reserve_sig, reserve_pub " - "FROM collectable_blindcoins " - "WHERE blind_ev = $1", - 1, NULL); - PREPARE ("get_reserves_blindcoins", - "select" - " blind_ev" - ",denom_pub, denom_sig" - ",reserve_sig" - " FROM collectable_blindcoins" - " WHERE reserve_pub=$1;", - 1, NULL); - - /* FIXME: does it make sense to store these computed values in the DB? */ -#if 0 - PREPARE ("get_refresh_session", - "SELECT " - " (SELECT count(*) FROM refresh_melt WHERE session_hash = $1)::INT2 as num_oldcoins " - ",(SELECT count(*) FROM refresh_blind_session_keys " - " WHERE session_hash = $1 and cnc_index = 0)::INT2 as num_newcoins " - ",(SELECT count(*) FROM refresh_blind_session_keys " - " WHERE session_hash = $1 and newcoin_index = 0)::INT2 as kappa " - ",noreveal_index" - ",session_commit_sig " - ",reveal_ok " - "FROM refresh_sessions " - "WHERE session_hash = $1", - 1, NULL); -#endif - - PREPARE ("get_known_coin", - "SELECT " - " coin_pub, denom_pub, denom_sig " - ",expended_value, expended_fraction, expended_currency " - ",refresh_session_hash " - "FROM known_coins " - "WHERE coin_pub = $1", - 1, NULL); - PREPARE ("update_known_coin", - "UPDATE known_coins " - "SET " - " denom_pub = $2 " - ",denom_sig = $3 " - ",expended_value = $4 " - ",expended_fraction = $5 " - ",expended_currency = $6 " - ",refresh_session_hash = $7 " - "WHERE " - " coin_pub = $1 ", - 7, NULL); - PREPARE ("insert_known_coin", - "INSERT INTO known_coins (" - " coin_pub" - ",denom_pub" - ",denom_sig" - ",expended_value" - ",expended_fraction" - ",expended_currency" - ",refresh_session_hash" - ")" - "VALUES ($1,$2,$3,$4,$5,$6,$7)", - 7, NULL); - PREPARE ("get_refresh_commit_link", - "SELECT " - " transfer_pub " - ",link_secret_enc " - "FROM refresh_commit_link " - "WHERE session_hash = $1 AND cnc_index = $2 AND oldcoin_index = $3", - 3, NULL); - PREPARE ("get_refresh_commit_coin", - "SELECT " - " link_vector_enc " - ",coin_ev " - "FROM refresh_commit_coin " - "WHERE session_hash = $1 AND cnc_index = $2 AND newcoin_index = $3", - 3, NULL); - PREPARE ("insert_refresh_order", - "INSERT INTO refresh_order ( " - " newcoin_index " - ",session_hash " - ",denom_pub " - ") " - "VALUES ($1, $2, $3) ", - 3, NULL); - PREPARE ("insert_refresh_melt", - "INSERT INTO refresh_melt ( " - " session_hash " - ",oldcoin_index " - ",coin_pub " - ",denom_pub " - ") " - "VALUES ($1, $2, $3, $4) ", - 3, NULL); - PREPARE ("get_refresh_order", - "SELECT denom_pub " - "FROM refresh_order " - "WHERE session_hash = $1 AND newcoin_index = $2", - 2, NULL); - PREPARE ("get_refresh_collectable", - "SELECT ev_sig " - "FROM refresh_collectable " - "WHERE session_hash = $1 AND newcoin_index = $2", - 2, NULL); - PREPARE ("get_refresh_melt", - "SELECT coin_pub " - "FROM refresh_melt " - "WHERE session_hash = $1 AND oldcoin_index = $2", - 2, NULL); - PREPARE ("insert_refresh_session", - "INSERT INTO refresh_sessions ( " - " session_hash " - ",noreveal_index " - ") " - "VALUES ($1, $2) ", - 2, NULL); - PREPARE ("insert_refresh_commit_link", - "INSERT INTO refresh_commit_link ( " - " session_hash " - ",transfer_pub " - ",cnc_index " - ",oldcoin_index " - ",link_secret_enc " - ") " - "VALUES ($1, $2, $3, $4, $5) ", - 5, NULL); - PREPARE ("insert_refresh_commit_coin", - "INSERT INTO refresh_commit_coin ( " - " session_hash " - ",coin_ev " - ",cnc_index " - ",newcoin_index " - ",link_vector_enc " - ") " - "VALUES ($1, $2, $3, $4, $5) ", - 5, NULL); - PREPARE ("insert_refresh_collectable", - "INSERT INTO refresh_collectable ( " - " session_hash " - ",newcoin_index " - ",ev_sig " - ") " - "VALUES ($1, $2, $3) ", - 3, NULL); - PREPARE ("set_reveal_ok", - "UPDATE refresh_sessions " - "SET reveal_ok = TRUE " - "WHERE session_hash = $1 ", - 1, NULL); - PREPARE ("get_link", - "SELECT link_vector_enc, ro.denom_pub, ev_sig " - "FROM refresh_melt rm " - " JOIN refresh_order ro USING (session_hash) " - " JOIN refresh_commit_coin rcc USING (session_hash) " - " JOIN refresh_sessions rs USING (session_hash) " - " JOIN refresh_collectable rc USING (session_hash) " - "WHERE rm.coin_pub = $1 " - "AND ro.newcoin_index = rcc.newcoin_index " - "AND ro.newcoin_index = rc.newcoin_index " - "AND rcc.cnc_index = rs.noreveal_index % ( " - " SELECT count(*) FROM refresh_commit_coin rcc2 " - " WHERE rcc2.newcoin_index = 0 AND rcc2.session_hash = rs.session_hash " - " ) ", - 1, NULL); - PREPARE ("get_transfer", - "SELECT transfer_pub, link_secret_enc " - "FROM refresh_melt rm " - " JOIN refresh_commit_link rcl USING (session_hash) " - " JOIN refresh_sessions rs USING (session_hash) " - "WHERE rm.coin_pub = $1 " - "AND rm.oldcoin_index = rcl.oldcoin_index " - "AND rcl.cnc_index = rs.noreveal_index % ( " - " SELECT count(*) FROM refresh_commit_coin rcc2 " - " WHERE newcoin_index = 0 AND rcc2.session_hash = rm.session_hash " - " ) ", - 1, NULL); - PREPARE ("insert_deposit", - "INSERT INTO deposits (" - "coin_pub," - "denom_pub," - "denom_sig," - "transaction_id," - "amount_value," - "amount_fraction," - "amount_currency," - "merchant_pub," - "h_contract," - "h_wire," - "coin_sig," - "wire" - ") VALUES (" - "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12" - ")", - 12, NULL); - PREPARE ("get_deposit", - "SELECT " - "coin_pub," - "denom_pub," - "transaction_id," - "amount_value," - "amount_fraction," - "amount_currency," - "merchant_pub," - "h_contract," - "h_wire," - "coin_sig" - " FROM deposits WHERE (" - "(coin_pub = $1) AND" - "(transaction_id = $2) AND" - "(merchant_pub = $3)" - ")", - 3, NULL); - return GNUNET_OK; -#undef PREPARE -} - - -/** - * Close thread-local database connection when a thread is destroyed. - * - * @param closure we get from pthreads (the db handle) - */ -static void -db_conn_destroy (void *cls) -{ - PGconn *db_conn = cls; - - if (NULL != db_conn) - PQfinish (db_conn); -} - - -/** - * Get the thread-local database-handle. - * Connect to the db if the connection does not exist yet. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the - * database default one - * @return the database connection, or NULL on error - */ -static struct TALER_MINTDB_Session * -postgres_get_session (void *cls, - int temporary) -{ - struct PostgresClosure *pc = cls; - PGconn *db_conn; - struct TALER_MINTDB_Session *session; - - if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal))) - return session; - db_conn = PQconnectdb (pc->connection_cfg_str); - if (CONNECTION_OK != - PQstatus (db_conn)) - { - TALER_LOG_ERROR ("Database connection failed: %s\n", - PQerrorMessage (db_conn)); - GNUNET_break (0); - return NULL; - } - if ((GNUNET_YES == temporary) - && (GNUNET_SYSERR == set_temporary_schema(db_conn))) - { - GNUNET_break (0); - return NULL; - } - if (GNUNET_OK != - postgres_prepare (db_conn)) - { - GNUNET_break (0); - return NULL; - } - session = GNUNET_new (struct TALER_MINTDB_Session); - session->conn = db_conn; - if (0 != pthread_setspecific (pc->db_conn_threadlocal, - session)) - { - GNUNET_break (0); - // FIXME: close db_conn! - GNUNET_free (session); - return NULL; - } - return session; -} - - -/** - * Start a transaction. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session the database connection - * @return #GNUNET_OK on success - */ -static int -postgres_start (void *cls, - struct TALER_MINTDB_Session *session) -{ - PGresult *result; - - result = PQexec (session->conn, - "BEGIN"); - if (PGRES_COMMAND_OK != - PQresultStatus (result)) - { - TALER_LOG_ERROR ("Failed to start transaction: %s\n", - PQresultErrorMessage (result)); - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Roll back the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session the database connection - * @return #GNUNET_OK on success - */ -static void -postgres_rollback (void *cls, - struct TALER_MINTDB_Session *session) -{ - PGresult *result; - - result = PQexec (session->conn, - "ROLLBACK"); - GNUNET_break (PGRES_COMMAND_OK == - PQresultStatus (result)); - PQclear (result); -} - - -/** - * Commit the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session the database connection - * @return #GNUNET_OK on success - */ -static int -postgres_commit (void *cls, - struct TALER_MINTDB_Session *session) -{ - PGresult *result; - - result = PQexec (session->conn, - "COMMIT"); - if (PGRES_COMMAND_OK != - PQresultStatus (result)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Get the summary of a reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session the database connection handle - * @param reserve the reserve data. The public key of the reserve should be set - * in this structure; it is used to query the database. The balance - * and expiration are then filled accordingly. - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static int -postgres_reserve_get (void *cls, - struct TALER_MINTDB_Session *session, - struct Reserve *reserve) -{ - PGresult *result; - uint64_t expiration_date_nbo; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(&reserve->pub), - TALER_PQ_QUERY_PARAM_END - }; - - result = TALER_PQ_exec_prepared (session->conn, - "get_reserve", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC("expiration_date", &expiration_date_nbo), - TALER_PQ_RESULT_SPEC_END - }; - EXITIF (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)); - EXITIF (GNUNET_OK != - TALER_PQ_extract_amount (result, 0, - "current_balance_value", - "current_balance_fraction", - "balance_currency", - &reserve->balance)); - reserve->expiry.abs_value_us = GNUNET_ntohll (expiration_date_nbo); - PQclear (result); - return GNUNET_OK; - - EXITIF_exit: - PQclear (result); - return GNUNET_SYSERR; -} - - -/** - * Updates a reserve with the data from the given reserve structure. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session the database connection - * @param reserve the reserve structure whose data will be used to update the - * corresponding record in the database. - * @return #GNUNET_OK upon successful update; #GNUNET_SYSERR upon any error - */ -static int -postgres_reserves_update (void *cls, - struct TALER_MINTDB_Session *session, - struct Reserve *reserve) -{ - PGresult *result; - struct TALER_AmountNBO balance_nbo; - struct GNUNET_TIME_AbsoluteNBO expiry_nbo; - int ret; - - if (NULL == reserve) - return GNUNET_SYSERR; - ret = GNUNET_OK; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (&reserve->pub), - TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value), - TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction), - TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo), - TALER_PQ_QUERY_PARAM_END - }; - TALER_amount_hton (&balance_nbo, - &reserve->balance); - expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry); - result = TALER_PQ_exec_prepared (session->conn, - "update_reserve", - params); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - QUERY_ERR (result); - ret = GNUNET_SYSERR; - } - PQclear (result); - return ret; -} - - -/** - * Insert a incoming transaction into reserves. New reserves are also created - * through this function. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session the database connection handle - * @param reserve the reserve structure. The public key of the reserve should - * be set here. Upon successful execution of this function, the - * balance and expiration of the reserve will be updated. - * @param balance the amount that has to be added to the reserve - * @param expiry the new expiration time for the reserve - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures - */ -static int -postgres_reserves_in_insert (void *cls, - struct TALER_MINTDB_Session *session, - struct Reserve *reserve, - const struct TALER_Amount *balance, - const struct GNUNET_TIME_Absolute expiry) -{ - struct TALER_AmountNBO balance_nbo; - struct GNUNET_TIME_AbsoluteNBO expiry_nbo; - PGresult *result; - int reserve_exists; - - result = NULL; - if (NULL == reserve) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != postgres_start (cls, - session)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - reserve_exists = postgres_reserve_get (cls, - session, - reserve); - if (GNUNET_SYSERR == reserve_exists) - { - postgres_rollback (cls, - session); - return GNUNET_SYSERR; - } - TALER_amount_hton (&balance_nbo, - balance); - expiry_nbo = GNUNET_TIME_absolute_hton (expiry); - if (GNUNET_NO == reserve_exists) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Reserve does not exist; creating a new one\n"); - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (&reserve->pub), - TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value), - TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction), - TALER_PQ_QUERY_PARAM_PTR_SIZED (balance_nbo.currency, - TALER_PQ_CURRENCY_LEN), - TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, - "create_reserve", - params); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - QUERY_ERR (result); - goto rollback; - } - } - if (NULL != result) - PQclear (result); - result = NULL; - /* create new incoming transaction */ - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (&reserve->pub), - TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value), - TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction), - TALER_PQ_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency, - TALER_PQ_CURRENCY_LEN), - TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, - "create_reserves_in_transaction", - params); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - QUERY_ERR (result); - goto rollback; - } - PQclear (result); - result = NULL; - if (GNUNET_NO == reserve_exists) - { - if (GNUNET_OK != postgres_commit (cls, - session)) - return GNUNET_SYSERR; - reserve->balance = *balance; - reserve->expiry = expiry; - return GNUNET_OK; - } - /* Update reserve */ - struct Reserve updated_reserve; - updated_reserve.pub = reserve->pub; - - if (GNUNET_OK != - TALER_amount_add (&updated_reserve.balance, - &reserve->balance, - balance)) - { - return GNUNET_SYSERR; - } - updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry); - if (GNUNET_OK != postgres_reserves_update (cls, - session, - &updated_reserve)) - goto rollback; - if (GNUNET_OK != postgres_commit (cls, - session)) - return GNUNET_SYSERR; - reserve->balance = updated_reserve.balance; - reserve->expiry = updated_reserve.expiry; - return GNUNET_OK; - - rollback: - PQclear (result); - postgres_rollback (cls, - session); - return GNUNET_SYSERR; -} - - -/** - * Locate the response for a /withdraw request under the - * key of the hash of the blinded message. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param h_blind hash of the blinded message - * @param collectable corresponding collectable coin (blind signature) - * if a coin is found - * @return #GNUNET_SYSERR on internal error - * #GNUNET_NO if the collectable was not found - * #GNUNET_YES on success - */ -static int -postgres_get_collectable_blindcoin (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *h_blind, - struct CollectableBlindcoin *collectable) -{ - PGresult *result; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (h_blind), - TALER_PQ_QUERY_PARAM_END - }; - struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - struct GNUNET_CRYPTO_rsa_Signature *denom_sig; - char *denom_pub_enc; - char *denom_sig_enc; - size_t denom_pub_enc_size; - size_t denom_sig_enc_size; - int ret; - - ret = GNUNET_SYSERR; - denom_pub = NULL; - denom_pub_enc = NULL; - denom_sig_enc = NULL; - result = TALER_PQ_exec_prepared (session->conn, - "get_collectable_blindcoin", - params); - - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result); - goto cleanup; - } - if (0 == PQntuples (result)) - { - ret = GNUNET_NO; - goto cleanup; - } - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_VAR("denom_pub", &denom_pub_enc, &denom_pub_enc_size), - TALER_PQ_RESULT_SPEC_VAR("denom_sig", &denom_sig_enc, &denom_sig_enc_size), - TALER_PQ_RESULT_SPEC("reserve_sig", &collectable->reserve_sig), - TALER_PQ_RESULT_SPEC("reserve_pub", &collectable->reserve_pub), - TALER_PQ_RESULT_SPEC_END - }; - - if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) - { - GNUNET_break (0); - goto cleanup; - } - denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc, - denom_pub_enc_size); - denom_sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc, - denom_sig_enc_size); - if ((NULL == denom_pub) || (NULL == denom_sig)) - { - GNUNET_break (0); - goto cleanup; - } - collectable->denom_pub.rsa_public_key = denom_pub; - collectable->sig.rsa_signature = denom_sig; - ret = GNUNET_YES; - - cleanup: - PQclear (result); - GNUNET_free_non_null (denom_pub_enc); - GNUNET_free_non_null (denom_sig_enc); - if (GNUNET_YES != ret) - { if (NULL != denom_pub) - GNUNET_CRYPTO_rsa_public_key_free (denom_pub); - if (NULL != denom_sig) - GNUNET_CRYPTO_rsa_signature_free (denom_sig); - } - return ret; -} - - -/** - * Store collectable bit coin under the corresponding - * hash of the blinded message. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param h_blind hash of the blinded message - * @param withdraw amount by which the reserve will be withdrawn with this - * transaction - * @param collectable corresponding collectable coin (blind signature) - * if a coin is found - * @return #GNUNET_SYSERR on internal error - * #GNUNET_NO if the collectable was not found - * #GNUNET_YES on success - */ -static int -postgres_insert_collectable_blindcoin (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *h_blind, - struct TALER_Amount withdraw, - const struct CollectableBlindcoin *collectable) -{ - PGresult *result; - struct Reserve reserve; - char *denom_pub_enc = NULL; - char *denom_sig_enc = NULL; - size_t denom_pub_enc_size; - size_t denom_sig_enc_size; - int ret; - - ret = GNUNET_SYSERR; - denom_pub_enc_size = - GNUNET_CRYPTO_rsa_public_key_encode (collectable->denom_pub.rsa_public_key, - &denom_pub_enc); - denom_sig_enc_size = - GNUNET_CRYPTO_rsa_signature_encode (collectable->sig.rsa_signature, - &denom_sig_enc); - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (h_blind), - TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1), - TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size - 1), /* DB doesn't like the trailing \0 */ - TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_pub), - TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_sig), - TALER_PQ_QUERY_PARAM_END - }; - if (GNUNET_OK != postgres_start (cls, - session)) - goto cleanup; - result = TALER_PQ_exec_prepared (session->conn, - "insert_collectable_blindcoin", - params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - QUERY_ERR (result); - goto rollback; - } - reserve.pub = collectable->reserve_pub; - if (GNUNET_OK != postgres_reserve_get (cls, - session, - &reserve)) - goto rollback; - if (GNUNET_SYSERR == - TALER_amount_subtract (&reserve.balance, - &reserve.balance, - &withdraw)) - goto rollback; - if (GNUNET_OK != postgres_reserves_update (cls, - session, - &reserve)) - goto rollback; - if (GNUNET_OK == postgres_commit (cls, - session)) - { - ret = GNUNET_OK; - goto cleanup; - } - - rollback: - postgres_rollback (cls, - session); - cleanup: - PQclear (result); - GNUNET_free_non_null (denom_pub_enc); - GNUNET_free_non_null (denom_sig_enc); - return ret; -} - - -/** - * Get all of the transaction history associated with the specified - * reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session connection to use - * @param reserve_pub public key of the reserve - * @return known transaction history (NULL if reserve is unknown) - */ -static struct ReserveHistory * -postgres_get_reserve_history (void *cls, - struct TALER_MINTDB_Session *session, - const struct TALER_ReservePublicKeyP *reserve_pub) -{ - PGresult *result; - struct ReserveHistory *rh; - struct ReserveHistory *rh_head; - int rows; - int ret; - - result = NULL; - rh = NULL; - rh_head = NULL; - ret = GNUNET_SYSERR; - { - struct BankTransfer *bt; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (reserve_pub), - TALER_PQ_QUERY_PARAM_END - }; - - result = TALER_PQ_exec_prepared (session->conn, - "get_reserves_in_transactions", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result); - goto cleanup; - } - if (0 == (rows = PQntuples (result))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Asked to fetch history for an unknown reserve.\n"); - goto cleanup; - } - while (0 < rows) - { - bt = GNUNET_new (struct BankTransfer); - if (GNUNET_OK != TALER_PQ_extract_amount (result, - --rows, - "balance_value", - "balance_fraction", - "balance_currency", - &bt->amount)) - { - GNUNET_free (bt); - GNUNET_break (0); - goto cleanup; - } - bt->reserve_pub = *reserve_pub; - if (NULL != rh_head) - { - rh_head->next = GNUNET_new (struct ReserveHistory); - rh_head = rh_head->next; - } - else - { - rh_head = GNUNET_new (struct ReserveHistory); - rh = rh_head; - } - rh_head->type = TALER_MINT_DB_RO_BANK_TO_MINT; - rh_head->details.bank = bt; - } - } - PQclear (result); - result = NULL; - { - struct GNUNET_HashCode blind_ev; - struct TALER_ReserveSignatureP reserve_sig; - struct CollectableBlindcoin *cbc; - char *denom_pub_enc; - char *denom_sig_enc; - size_t denom_pub_enc_size; - size_t denom_sig_enc_size; - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (reserve_pub), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, - "get_reserves_blindcoins", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result); - goto cleanup; - } - if (0 == (rows = PQntuples (result))) - { - ret = GNUNET_OK; /* Its OK if there are no withdrawls yet */ - goto cleanup; - } - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC ("blind_ev", &blind_ev), - TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &denom_pub_enc, &denom_pub_enc_size), - TALER_PQ_RESULT_SPEC_VAR ("denom_sig", &denom_sig_enc, &denom_sig_enc_size), - TALER_PQ_RESULT_SPEC ("reserve_sig", &reserve_sig), - TALER_PQ_RESULT_SPEC_END - }; - GNUNET_assert (NULL != rh); - GNUNET_assert (NULL != rh_head); - GNUNET_assert (NULL == rh_head->next); - while (0 < rows) - { - if (GNUNET_YES != TALER_PQ_extract_result (result, rs, --rows)) - { - GNUNET_break (0); - goto cleanup; - } - cbc = GNUNET_new (struct CollectableBlindcoin); - cbc->sig.rsa_signature - = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc, - denom_sig_enc_size); - GNUNET_free (denom_sig_enc); - denom_sig_enc = NULL; - cbc->denom_pub.rsa_public_key - = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc, - denom_pub_enc_size); - GNUNET_free (denom_pub_enc); - denom_pub_enc = NULL; - if ( (NULL == cbc->sig.rsa_signature) || - (NULL == cbc->denom_pub.rsa_public_key) ) - { - if (NULL != cbc->sig.rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (cbc->sig.rsa_signature); - if (NULL != cbc->denom_pub.rsa_public_key) - GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub.rsa_public_key); - GNUNET_free (cbc); - GNUNET_break (0); - goto cleanup; - } - (void) memcpy (&cbc->h_coin_envelope, &blind_ev, sizeof (blind_ev)); - (void) memcpy (&cbc->reserve_pub, reserve_pub, sizeof (cbc->reserve_pub)); - (void) memcpy (&cbc->reserve_sig, &reserve_sig, sizeof (cbc->reserve_sig)); - rh_head->next = GNUNET_new (struct ReserveHistory); - rh_head = rh_head->next; - rh_head->type = TALER_MINT_DB_RO_WITHDRAW_COIN; - rh_head->details.withdraw = cbc; - } - } - ret = GNUNET_OK; - - cleanup: - if (NULL != result) - PQclear (result); - if (GNUNET_SYSERR == ret) - { - common_free_reserve_history (cls, - rh); - rh = NULL; - } - return rh; -} - - -/** - * Check if we have the specified deposit already in the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param deposit deposit to search for - * @return #GNUNET_YES if we know this operation, - * #GNUNET_NO if this deposit is unknown to us - */ -static int -postgres_have_deposit (void *cls, - struct TALER_MINTDB_Session *session, - const struct Deposit *deposit) -{ - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub), - TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id), - TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub), - TALER_PQ_QUERY_PARAM_END - }; - PGresult *result; - int ret; - - ret = GNUNET_SYSERR; - result = TALER_PQ_exec_prepared (session->conn, - "get_deposit", - params); - if (PGRES_TUPLES_OK != - PQresultStatus (result)) - { - BREAK_DB_ERR (result); - goto cleanup; - } - - if (0 == PQntuples (result)) - { - ret = GNUNET_NO; - goto cleanup; - } - ret = GNUNET_YES; - - cleanup: - PQclear (result); - return ret; -} - - -/** - * Insert information about deposited coin into the - * database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session connection to the database - * @param deposit deposit information to store - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -postgres_insert_deposit (void *cls, - struct TALER_MINTDB_Session *session, - const struct Deposit *deposit) -{ - char *denom_pub_enc; - char *denom_sig_enc; - char *json_wire_enc; - PGresult *result; - struct TALER_AmountNBO amount_nbo; - size_t denom_pub_enc_size; - size_t denom_sig_enc_size; - int ret; - - ret = GNUNET_SYSERR; - denom_pub_enc_size = - GNUNET_CRYPTO_rsa_public_key_encode (deposit->coin.denom_pub.rsa_public_key, - &denom_pub_enc); - denom_sig_enc_size = - GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig.rsa_signature, - &denom_sig_enc); - json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT); - TALER_amount_hton (&amount_nbo, - &deposit->amount_with_fee); - struct TALER_PQ_QueryParam params[]= { - TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub), - TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size), - TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size), - TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id), - TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.value), - TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.fraction), - TALER_PQ_QUERY_PARAM_PTR_SIZED (amount_nbo.currency, - TALER_CURRENCY_LEN - 1), - TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub), - TALER_PQ_QUERY_PARAM_PTR (&deposit->h_contract), - TALER_PQ_QUERY_PARAM_PTR (&deposit->h_wire), - TALER_PQ_QUERY_PARAM_PTR (&deposit->csig), - TALER_PQ_QUERY_PARAM_PTR_SIZED (json_wire_enc, - strlen (json_wire_enc)), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, "insert_deposit", params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - goto cleanup; - } - ret = GNUNET_OK; - - cleanup: - PQclear (result); - GNUNET_free_non_null (denom_pub_enc); - GNUNET_free_non_null (denom_sig_enc); - GNUNET_free_non_null (json_wire_enc); - return ret; -} - - -/** - * Lookup refresh session data under the given @a session_hash. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database handle to use - * @param session_hash hash over the melt to use to locate the session - * @param refresh_session[OUT] where to store the result - * @return #GNUNET_YES on success, - * #GNUNET_NO if not found, - * #GNUNET_SYSERR on DB failure - */ -static int -postgres_get_refresh_session (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct RefreshSession *refresh_session) -{ - // FIXME: check logic! - int res; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_END - }; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "get_refresh_session", - params); - - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Query failed: %s\n", - PQresultErrorMessage (result)); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 == PQntuples (result)) - return GNUNET_NO; - - GNUNET_assert (1 == PQntuples (result)); - - /* We're done if the caller is only interested in - * whether the session exists or not */ - - if (NULL == refresh_session) - return GNUNET_YES; - - memset (session, 0, sizeof (struct RefreshSession)); - - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins), - TALER_PQ_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins), - TALER_PQ_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index), - TALER_PQ_RESULT_SPEC_END - }; - - res = TALER_PQ_extract_result (result, rs, 0); - - if (GNUNET_OK != res) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - - refresh_session->num_oldcoins = ntohs (refresh_session->num_oldcoins); - refresh_session->num_newcoins = ntohs (refresh_session->num_newcoins); - refresh_session->noreveal_index = ntohs (refresh_session->noreveal_index); - - PQclear (result); - return GNUNET_YES; -} - - -/** - * Store new refresh session data under the given @a session_hash. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database handle to use - * @param session_hash hash over the melt to use to locate the session - * @param refresh_session session data to store - * @return #GNUNET_YES on success, - * #GNUNET_SYSERR on DB failure - */ -static int -postgres_create_refresh_session (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - const struct RefreshSession *refresh_session) -{ - // FIXME: actually store session data! - uint16_t noreveal_index; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR(&noreveal_index), - TALER_PQ_QUERY_PARAM_END - }; - - noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15); - noreveal_index = htonl (noreveal_index); - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "insert_refresh_session", - params); - - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Store the given /refresh/melt request in the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param oldcoin_index index of the coin to store - * @param melt melt operation details to store; includes - * the session hash of the melt - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -static int -postgres_insert_refresh_melt (void *cls, - struct TALER_MINTDB_Session *session, - uint16_t oldcoin_index, - const struct RefreshMelt *melt) -{ - // FIXME: check logic! - uint16_t oldcoin_index_nbo = htons (oldcoin_index); - char *buf; - size_t buf_size; - PGresult *result; - - buf_size = GNUNET_CRYPTO_rsa_public_key_encode (melt->coin.denom_pub.rsa_public_key, - &buf); - { - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(&melt->session_hash), - TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo), - TALER_PQ_QUERY_PARAM_PTR(&melt->coin.coin_pub), - TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, - "insert_refresh_melt", - params); - } - GNUNET_free (buf); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - PQclear (result); - return GNUNET_OK; -} - - -/** - * Get information about melted coin details from the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param refresh_session session key of the melt operation - * @param oldcoin_index index of the coin to retrieve - * @param melt melt data to fill in - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -static int -postgres_get_refresh_melt (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t oldcoin_index, - struct RefreshMelt *melt) -{ - // FIXME: check logic! - GNUNET_break (0); - return GNUNET_SYSERR; -} - - -/** - * Store in the database which coin(s) we want to create - * in a given refresh operation. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param session_hash hash to identify refresh session - * @param num_newcoins number of coins to generate, size of the @a denom_pubs array - * @param denom_pubs array denominations of the coins to create - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -static int -postgres_insert_refresh_order (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - const struct TALER_DenominationPublicKey *denom_pubs) -{ - // FIXME: check logic: was written for just one COIN! - uint16_t newcoin_index_nbo = htons (num_newcoins); - char *buf; - size_t buf_size; - PGresult *result; - - buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs->rsa_public_key, - &buf); - - { - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR (&newcoin_index_nbo), - TALER_PQ_QUERY_PARAM_PTR (session_hash), - TALER_PQ_QUERY_PARAM_PTR_SIZED (buf, buf_size), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, - "insert_refresh_order", - params); - } - GNUNET_free (buf); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 != strcmp ("1", PQcmdTuples (result))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - PQclear (result); - return GNUNET_OK; -} - - -/** - * Lookup in the database the coins that we want to - * create in the given refresh operation. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param session_hash hash to identify refresh session - * @param newcoin_index array of the @a denom_pubs array - * @param denom_pubs where to store the deomination keys - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -static int -postgres_get_refresh_order (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - struct TALER_DenominationPublicKey *denom_pubs) -{ - // FIXME: check logic -- was written for just one coin! - char *buf; - size_t buf_size; - uint16_t newcoin_index_nbo = htons (num_newcoins); - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), - TALER_PQ_QUERY_PARAM_END - }; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "get_refresh_order", params); - - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 == PQntuples (result)) - { - PQclear (result); - /* FIXME: may want to distinguish between different error cases! */ - return GNUNET_SYSERR; - } - GNUNET_assert (1 == PQntuples (result)); - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &buf, &buf_size), - TALER_PQ_RESULT_SPEC_END - }; - if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) - { - PQclear (result); - GNUNET_break (0); - return GNUNET_SYSERR; - } - PQclear (result); - denom_pubs->rsa_public_key - = GNUNET_CRYPTO_rsa_public_key_decode (buf, - buf_size); - GNUNET_free (buf); - return GNUNET_OK; -} - - - -/** - * Store information about the commitment of the - * given coin for the given refresh session in the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param i set index (1st dimension) - * @param num_newcoins coin index size of the @a commit_coins array - * @param commit_coins array of coin commitments to store - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on error - */ -static int -postgres_insert_refresh_commit_coins (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - unsigned int i, - unsigned int num_newcoins, - const struct RefreshCommitCoin *commit_coins) -{ - // FIXME: check logic! -- was written for single commit_coin! - uint16_t cnc_index_nbo = htons (i); - uint16_t newcoin_index_nbo = htons (num_newcoins); - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR_SIZED(commit_coins->coin_ev, commit_coins->coin_ev_size), - TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), - TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), - TALER_PQ_QUERY_PARAM_PTR_SIZED (commit_coins->refresh_link->coin_priv_enc, - commit_coins->refresh_link->blinding_key_enc_size + - sizeof (union TALER_CoinSpendPrivateKeyP)), - TALER_PQ_QUERY_PARAM_END - }; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "insert_refresh_commit_coin", - params); - - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 != strcmp ("1", PQcmdTuples (result))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Obtain information about the commitment of the - * given coin of the given refresh session from the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to refreshed (new) coins - * @param commit_coin[OUT] coin commitment to return - * @return #GNUNET_OK on success - * #GNUNET_NO if not found - * #GNUNET_SYSERR on error - */ -static int -postgres_get_refresh_commit_coins (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - unsigned int cnc_index, - unsigned int newcoin_index, - struct RefreshCommitCoin *cc) -{ - // FIXME: check logic! - uint16_t cnc_index_nbo = htons (cnc_index); - uint16_t newcoin_index_nbo = htons (newcoin_index); - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), - TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), - TALER_PQ_QUERY_PARAM_END - }; - char *c_buf; - size_t c_buf_size; - char *rl_buf; - size_t rl_buf_size; - struct TALER_RefreshLinkEncrypted *rl; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "get_refresh_commit_coin", - params); - - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_VAR("coin_ev", &c_buf, &c_buf_size), - TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &rl_buf, &rl_buf_size), - TALER_PQ_RESULT_SPEC_END - }; - if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0)) - { - PQclear (result); - return GNUNET_SYSERR; - } - PQclear (result); - if (rl_buf_size < sizeof (union TALER_CoinSpendPrivateKeyP)) - { - GNUNET_free (c_buf); - GNUNET_free (rl_buf); - return GNUNET_SYSERR; - } - rl = TALER_refresh_link_encrypted_decode (rl_buf, - rl_buf_size); - GNUNET_free (rl_buf); - cc->refresh_link = rl; - cc->coin_ev = c_buf; - cc->coin_ev_size = c_buf_size; - return GNUNET_YES; -} - - -/** - * Store the commitment to the given (encrypted) refresh link data - * for the given refresh session. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to melted (old) coins - * @param commit_link link information to store - * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success - */ -static int -postgres_insert_refresh_commit_links (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - unsigned int i, - unsigned int j, - const struct RefreshCommitLink *commit_link) -{ - // FIXME: check logic! - uint16_t cnc_index_nbo = htons (i); - uint16_t oldcoin_index_nbo = htons (j); - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR(&commit_link->transfer_pub), - TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), - TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo), - TALER_PQ_QUERY_PARAM_PTR(&commit_link->shared_secret_enc), - TALER_PQ_QUERY_PARAM_END - }; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "insert_refresh_commit_link", - params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 != strcmp ("1", PQcmdTuples (result))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Obtain the commited (encrypted) refresh link data - * for the given refresh session. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param i set index (1st dimension) - * @param num_links size of the @a commit_link array - * @param links[OUT] array of link information to return - * @return #GNUNET_SYSERR on internal error, - * #GNUNET_NO if commitment was not found - * #GNUNET_OK on success - */ -static int -postgres_get_refresh_commit_links (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - unsigned int i, - unsigned int num_links, - struct RefreshCommitLink *links) -{ - // FIXME: check logic: was written for a single link! - uint16_t cnc_index_nbo = htons (i); - uint16_t oldcoin_index_nbo = htons (num_links); - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), - TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo), - TALER_PQ_QUERY_PARAM_END - }; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, - "get_refresh_commit_link", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC("transfer_pub", &links->transfer_pub), - TALER_PQ_RESULT_SPEC("link_secret_enc", &links->shared_secret_enc), - TALER_PQ_RESULT_SPEC_END - }; - - if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0)) - { - PQclear (result); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Insert signature of a new coin generated during refresh into - * the database indexed by the refresh session and the index - * of the coin. This data is later used should an old coin - * be used to try to obtain the private keys during "/refresh/link". - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param session_hash hash to identify refresh session - * @param newcoin_index coin index - * @param ev_sig coin signature - * @return #GNUNET_OK on success - */ -static int -postgres_insert_refresh_collectable (void *cls, - struct TALER_MINTDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t newcoin_index, - const struct TALER_DenominationSignature *ev_sig) -{ - // FIXME: check logic! - uint16_t newcoin_index_nbo = htons (newcoin_index); - char *buf; - size_t buf_size; - PGresult *result; - - buf_size = GNUNET_CRYPTO_rsa_signature_encode (ev_sig->rsa_signature, - &buf); - { - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(session_hash), - TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), - TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size), - TALER_PQ_QUERY_PARAM_END - }; - result = TALER_PQ_exec_prepared (session->conn, - "insert_refresh_collectable", - params); - } - GNUNET_free (buf); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - PQclear (result); - return GNUNET_OK; -} - - -/** - * Obtain the link data of a coin, that is the encrypted link - * information, the denomination keys and the signatures. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param coin_pub public key to use to retrieve linkage data - * @return all known link data for the coin - */ -static struct LinkDataList * -postgres_get_link_data_list (void *cls, - struct TALER_MINTDB_Session *session, - const union TALER_CoinSpendPublicKeyP *coin_pub) -{ - // FIXME: check logic! - struct LinkDataList *ldl; - struct LinkDataList *pos; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(coin_pub), - TALER_PQ_QUERY_PARAM_END - }; - PGresult *result = TALER_PQ_exec_prepared (session->conn, "get_link", params); - - ldl = NULL; - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return NULL; - } - - if (0 == PQntuples (result)) - { - PQclear (result); - return NULL; - } - - - int i = 0; - - for (i = 0; i < PQntuples (result); i++) - { - struct TALER_RefreshLinkEncrypted *link_enc; - struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - struct GNUNET_CRYPTO_rsa_Signature *sig; - char *ld_buf; - size_t ld_buf_size; - char *pk_buf; - size_t pk_buf_size; - char *sig_buf; - size_t sig_buf_size; - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &ld_buf, &ld_buf_size), - TALER_PQ_RESULT_SPEC_VAR("denom_pub", &pk_buf, &pk_buf_size), - TALER_PQ_RESULT_SPEC_VAR("ev_sig", &sig_buf, &sig_buf_size), - TALER_PQ_RESULT_SPEC_END - }; - - if (GNUNET_OK != TALER_PQ_extract_result (result, rs, i)) - { - PQclear (result); - GNUNET_break (0); - common_free_link_data_list (cls, - ldl); - return NULL; - } - if (ld_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)) - { - PQclear (result); - GNUNET_free (pk_buf); - GNUNET_free (sig_buf); - GNUNET_free (ld_buf); - common_free_link_data_list (cls, - ldl); - return NULL; - } - // FIXME: use util API for this! - link_enc = GNUNET_malloc (sizeof (struct TALER_RefreshLinkEncrypted) + - ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)); - link_enc->blinding_key_enc = (const char *) &link_enc[1]; - link_enc->blinding_key_enc_size = ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey); - memcpy (link_enc->coin_priv_enc, - ld_buf, - ld_buf_size); - - sig - = GNUNET_CRYPTO_rsa_signature_decode (sig_buf, - sig_buf_size); - denom_pub - = GNUNET_CRYPTO_rsa_public_key_decode (pk_buf, - pk_buf_size); - GNUNET_free (pk_buf); - GNUNET_free (sig_buf); - GNUNET_free (ld_buf); - if ( (NULL == sig) || - (NULL == denom_pub) ) - { - if (NULL != denom_pub) - GNUNET_CRYPTO_rsa_public_key_free (denom_pub); - if (NULL != sig) - GNUNET_CRYPTO_rsa_signature_free (sig); - GNUNET_free (link_enc); - GNUNET_break (0); - PQclear (result); - common_free_link_data_list (cls, - ldl); - return NULL; - } - pos = GNUNET_new (struct LinkDataList); - pos->next = ldl; - pos->link_data_enc = link_enc; - pos->denom_pub.rsa_public_key = denom_pub; - pos->ev_sig.rsa_signature = sig; - ldl = pos; - } - return ldl; -} - - -/** - * Obtain shared secret and transfer public key from the public key of - * the coin. This information and the link information returned by - * #postgres_get_link_data_list() enable the owner of an old coin to - * determine the private keys of the new coins after the melt. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param coin_pub public key of the coin - * @param transfer_pub[OUT] public transfer key - * @param shared_secret_enc[OUT] set to shared secret - * @return #GNUNET_OK on success, - * #GNUNET_NO on failure (not found) - * #GNUNET_SYSERR on internal failure (database issue) - */ -static int -postgres_get_transfer (void *cls, - struct TALER_MINTDB_Session *session, - const union TALER_CoinSpendPublicKeyP *coin_pub, - struct TALER_TransferPublicKeyP *transfer_pub, - struct TALER_EncryptedLinkSecretP *shared_secret_enc) -{ - // FIXME: check logic! - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_QUERY_PARAM_PTR(coin_pub), - TALER_PQ_QUERY_PARAM_END - }; - - PGresult *result = TALER_PQ_exec_prepared (session->conn, "get_transfer", params); - - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - - if (1 != PQntuples (result)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "got %d tuples for get_transfer\n", - PQntuples (result)); - GNUNET_break (0); - return GNUNET_SYSERR; - } - - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC("transfer_pub", transfer_pub), - TALER_PQ_RESULT_SPEC("link_secret_enc", shared_secret_enc), - TALER_PQ_RESULT_SPEC_END - }; - - if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) - { - PQclear (result); - GNUNET_break (0); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - -/** - * Compile a list of all (historic) transactions performed - * with the given coin (/refresh/melt and /deposit operations). - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param coin_pub coin to investigate - * @return list of transactions, NULL if coin is fresh - */ -static struct TALER_MINT_DB_TransactionList * -postgres_get_coin_transactions (void *cls, - struct TALER_MINTDB_Session *session, - const union TALER_CoinSpendPublicKeyP *coin_pub) -{ - // FIXME: check logic! - GNUNET_break (0); // FIXME: implement! - return NULL; -} - - - -/** - * Initialize Postgres database subsystem. - * - * @param cls a configuration instance - * @return NULL on error, otherwise a `struct TALER_MINTDB_Plugin` - */ -void * -libtaler_plugin_mintdb_postgres_init (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct PostgresClosure *pg; - struct TALER_MINTDB_Plugin *plugin; - - pg = GNUNET_new (struct PostgresClosure); - - if (0 != pthread_key_create (&pg->db_conn_threadlocal, - &db_conn_destroy)) - { - TALER_LOG_ERROR ("Cannnot create pthread key.\n"); - return NULL; - } - /* FIXME: use configuration section with "postgres" in its name... */ - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint", "db_conn_str", - &pg->connection_cfg_str)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint", - "db_conn_str"); - return NULL; - } - plugin = GNUNET_new (struct TALER_MINTDB_Plugin); - plugin->cls = pg; - plugin->library_name = "libtaler_plugin_mintdb_postgres"; - plugin->get_session = &postgres_get_session; - plugin->drop_temporary = &postgres_drop_temporary; - plugin->create_tables = &postgres_create_tables; - plugin->start = &postgres_start; - plugin->commit = &postgres_commit; - plugin->rollback = &postgres_rollback; - plugin->reserve_get = &postgres_reserve_get; - plugin->reserves_in_insert = &postgres_reserves_in_insert; - plugin->get_collectable_blindcoin = &postgres_get_collectable_blindcoin; - plugin->insert_collectable_blindcoin = &postgres_insert_collectable_blindcoin; - plugin->get_reserve_history = &postgres_get_reserve_history; - plugin->free_reserve_history = &common_free_reserve_history; - plugin->have_deposit = &postgres_have_deposit; - plugin->insert_deposit = &postgres_insert_deposit; - plugin->get_refresh_session = &postgres_get_refresh_session; - plugin->create_refresh_session = &postgres_create_refresh_session; - plugin->insert_refresh_melt = &postgres_insert_refresh_melt; - plugin->get_refresh_melt = &postgres_get_refresh_melt; - plugin->insert_refresh_order = &postgres_insert_refresh_order; - plugin->get_refresh_order = &postgres_get_refresh_order; - plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins; - plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins; - plugin->insert_refresh_commit_links = &postgres_insert_refresh_commit_links; - plugin->get_refresh_commit_links = &postgres_get_refresh_commit_links; - plugin->insert_refresh_collectable = &postgres_insert_refresh_collectable; - plugin->get_link_data_list = &postgres_get_link_data_list; - plugin->free_link_data_list = &common_free_link_data_list; - plugin->get_transfer = &postgres_get_transfer; - // plugin->have_lock = &postgres_have_lock; - // plugin->insert_lock = &postgres_insert_lock; - plugin->get_coin_transactions = &postgres_get_coin_transactions; - plugin->free_coin_transaction_list = &common_free_coin_transaction_list; - return plugin; -} - - -/** - * Shutdown Postgres database subsystem. - * - * @param cls a `struct TALER_MINTDB_Plugin` - * @return NULL (always) - */ -void * -libtaler_plugin_mintdb_postgres_done (void *cls) -{ - struct TALER_MINTDB_Plugin *plugin = cls; - struct PostgresClosure *pg = plugin->cls; - - GNUNET_free (pg->connection_cfg_str); - GNUNET_free (pg); - GNUNET_free (plugin); - return NULL; -} - -/* end of plugin_mintdb_postgres.c */ diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c deleted file mode 100644 index 8056b7df0..000000000 --- a/src/mint/taler-mint-dbinit.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file mint/taler-mint-dbinit.c - * @brief Create tables for the mint database. - * @author Florian Dold - */ -#include "platform.h" -#include -#include -#include "taler_util.h" -#include "taler_mintdb_plugin.h" -#include "plugin.h" - -/** - * Mint directory with the keys. - */ -static char *mint_base_dir; - -/** - * Our configuration. - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * The main function of the database initialization tool. - * Used to initialize the Taler Mint's database. - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, - char *const *argv) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-dbinit OPTIONS"), - {'d', "mint-dir", "DIR", - "mint directory", 1, - &GNUNET_GETOPT_set_filename, &mint_base_dir}, - GNUNET_GETOPT_OPTION_END - }; - - if (GNUNET_GETOPT_run ("taler-mint-dbinit", - options, - argc, argv) < 0) - return 1; - - GNUNET_assert (GNUNET_OK == - GNUNET_log_setup ("taler-mint-dbinit", - "INFO", - NULL)); - if (NULL == mint_base_dir) - { - fprintf (stderr, - "Mint base directory not given.\n"); - return 1; - } - cfg = TALER_config_load (mint_base_dir); - if (NULL == cfg) - { - fprintf (stderr, - "Failed to load mint configuration.\n"); - return 1; - } - if (GNUNET_OK != - TALER_MINT_plugin_load (cfg)) - { - fprintf (stderr, - "Failed to initialize database plugin.\n"); - return 1; - } - if (GNUNET_OK != - plugin->create_tables (plugin->cls, - GNUNET_NO)) - { - fprintf (stderr, - "Failed to initialize database.\n"); - TALER_MINT_plugin_unload (); - return 1; - } - TALER_MINT_plugin_unload (); - return 0; -} - -/* end of taler-mint-dbinit.c */ diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index 13c77b033..a86735963 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "taler_signatures.h" #include "taler_util.h" @@ -36,7 +35,6 @@ #include "taler-mint-httpd_refresh.h" #include "taler-mint-httpd_keystate.h" #include "taler_mintdb_plugin.h" -#include "plugin.h" /** @@ -60,6 +58,11 @@ struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key; */ char *TMH_expected_wire_format = "sepa"; +/** + * Our DB plugin. + */ +struct TALER_MINTDB_Plugin *TMH_plugin; + /** * The HTTP Daemon. */ @@ -260,8 +263,8 @@ mint_serve_process_config (const char *mint_directory) } GNUNET_free (TMH_master_public_key_str); - if (GNUNET_OK != - TALER_MINT_plugin_load (cfg)) + if (NULL == + (TMH_plugin = TALER_MINT_plugin_load (cfg))) { fprintf (stderr, "failed to initialize DB subsystem\n"); @@ -355,5 +358,6 @@ main (int argc, char *const *argv) ret = TMH_KS_loop (); MHD_stop_daemon (mydaemon); + TALER_MINT_plugin_unload (TMH_plugin); return (GNUNET_OK == ret) ? 0 : 1; } diff --git a/src/mint/taler-mint-httpd.h b/src/mint/taler-mint-httpd.h index 92ce54b84..11f2f3658 100644 --- a/src/mint/taler-mint-httpd.h +++ b/src/mint/taler-mint-httpd.h @@ -59,6 +59,11 @@ extern struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key; */ extern struct GNUNET_CRYPTO_EddsaPrivateKey TMH_mint_private_signing_key; +/** + * Our DB plugin. + */ +extern struct TALER_MINTDB_Plugin *TMH_plugin; + /** * Struct describing an URL and the handler for it. diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 2fdd87188..13fde7240 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -26,7 +26,7 @@ #include "taler-mint-httpd_responses.h" #include "taler_util.h" #include "taler-mint-httpd_keystate.h" -#include "plugin.h" +#include "taler_mintdb_lib.h" /** @@ -109,14 +109,14 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, struct TALER_DenominationKeyIssueInformation *dki; int ret; - if (NULL == (session = plugin->get_session (plugin->cls, + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, GNUNET_NO))) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } if (GNUNET_YES == - plugin->have_deposit (plugin->cls, + TMH_plugin->have_deposit (TMH_plugin->cls, session, deposit)) { @@ -136,7 +136,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, TMH_KS_release (mks); if (GNUNET_OK != - plugin->start (plugin->cls, + TMH_plugin->start (TMH_plugin->cls, session)) { GNUNET_break (0); @@ -145,7 +145,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, /* fee for THIS transaction */ spent = deposit->amount_with_fee; /* add cost of all previous transactions */ - tl = plugin->get_coin_transactions (plugin->cls, + tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls, session, &deposit->coin.coin_pub); if (GNUNET_OK != @@ -153,7 +153,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, &spent, &spent)) { - plugin->free_coin_transaction_list (plugin->cls, + TMH_plugin->free_coin_transaction_list (TMH_plugin->cls, tl); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -162,30 +162,30 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, if (0 < TALER_amount_cmp (&spent, &value)) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); ret = TMH_RESPONSE_reply_deposit_insufficient_funds (connection, tl); - plugin->free_coin_transaction_list (plugin->cls, + TMH_plugin->free_coin_transaction_list (TMH_plugin->cls, tl); return ret; } - plugin->free_coin_transaction_list (plugin->cls, + TMH_plugin->free_coin_transaction_list (TMH_plugin->cls, tl); if (GNUNET_OK != - plugin->insert_deposit (plugin->cls, + TMH_plugin->insert_deposit (TMH_plugin->cls, session, deposit)) { TALER_LOG_WARNING ("Failed to store /deposit information in database\n"); - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } if (GNUNET_OK != - plugin->commit (plugin->cls, + TMH_plugin->commit (TMH_plugin->cls, session)) { TALER_LOG_WARNING ("/deposit transaction commit failed\n"); @@ -217,13 +217,13 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, struct ReserveHistory *rh; int res; - if (NULL == (session = plugin->get_session (plugin->cls, + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, GNUNET_NO))) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } - rh = plugin->get_reserve_history (plugin->cls, + rh = TMH_plugin->get_reserve_history (TMH_plugin->cls, session, reserve_pub); if (NULL == rh) @@ -233,7 +233,7 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, "error", "Reserve not found"); res = TMH_RESPONSE_reply_withdraw_status_success (connection, rh); - plugin->free_reserve_history (plugin->cls, + TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); return res; } @@ -282,13 +282,13 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, blinded_msg_len, &h_blind); - if (NULL == (session = plugin->get_session (plugin->cls, + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, GNUNET_NO))) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } - res = plugin->get_collectable_blindcoin (plugin->cls, + res = TMH_plugin->get_collectable_blindcoin (TMH_plugin->cls, session, &h_blind, &collectable); @@ -323,7 +323,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, "Denomination not found"); } if (GNUNET_OK != - plugin->start (plugin->cls, + TMH_plugin->start (TMH_plugin->cls, session)) { GNUNET_break (0); @@ -331,12 +331,12 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, return TMH_RESPONSE_reply_internal_db_error (connection); } - rh = plugin->get_reserve_history (plugin->cls, + rh = TMH_plugin->get_reserve_history (TMH_plugin->cls, session, reserve); if (NULL == rh) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); TMH_KS_release (key_state); return TMH_RESPONSE_reply_json_pack (connection, @@ -357,7 +357,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, &value, &fee_withdraw)) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); TMH_KS_release (key_state); return TMH_RESPONSE_reply_internal_db_error (connection); @@ -378,7 +378,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, &deposit_total, &pos->details.bank->amount)) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); TMH_KS_release (key_state); return TMH_RESPONSE_reply_internal_db_error (connection); @@ -398,7 +398,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, &withdraw_total, &value)) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); TMH_KS_release (key_state); return TMH_RESPONSE_reply_internal_db_error (connection); @@ -416,15 +416,15 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, &balance)) { TMH_KS_release (key_state); - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); res = TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (connection, rh); - plugin->free_reserve_history (plugin->cls, + TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); return res; } - plugin->free_reserve_history (plugin->cls, + TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); /* Balance is good, sign the coin! */ @@ -435,7 +435,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, if (NULL == sig) { GNUNET_break (0); - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_error (connection, "Internal error"); @@ -448,7 +448,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, &collectable.h_coin_envelope); collectable.reserve_sig = *signature; if (GNUNET_OK != - plugin->insert_collectable_blindcoin (plugin->cls, + TMH_plugin->insert_collectable_blindcoin (TMH_plugin->cls, session, &h_blind, amount_required, @@ -456,12 +456,12 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, { GNUNET_break (0); GNUNET_CRYPTO_rsa_signature_free (sig); - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } if (GNUNET_OK != - plugin->commit (plugin->cls, + TMH_plugin->commit (TMH_plugin->cls, session)) { TALER_LOG_WARNING ("/withdraw/sign transaction commit failed\n"); @@ -523,7 +523,7 @@ refresh_accept_melts (struct MHD_Connection *connection, /* fee for THIS transaction; the melt amount includes the fee! */ spent = coin_details->melt_amount_with_fee; /* add historic transaction costs of this coin */ - tl = plugin->get_coin_transactions (plugin->cls, + tl = TMH_plugin->get_coin_transactions (TMH_plugin->cls, session, &coin_public_info->coin_pub); if (GNUNET_OK != @@ -532,7 +532,7 @@ refresh_accept_melts (struct MHD_Connection *connection, &spent)) { GNUNET_break (0); - plugin->free_coin_transaction_list (plugin->cls, + TMH_plugin->free_coin_transaction_list (TMH_plugin->cls, tl); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -553,11 +553,11 @@ refresh_accept_melts (struct MHD_Connection *connection, coin_details->melt_amount_with_fee, coin_residual)) ? GNUNET_NO : GNUNET_SYSERR; - plugin->free_coin_transaction_list (plugin->cls, + TMH_plugin->free_coin_transaction_list (TMH_plugin->cls, tl); return res; } - plugin->free_coin_transaction_list (plugin->cls, + TMH_plugin->free_coin_transaction_list (TMH_plugin->cls, tl); melt.coin = *coin_public_info; @@ -565,7 +565,7 @@ refresh_accept_melts (struct MHD_Connection *connection, melt.session_hash = *session_hash; melt.amount_with_fee = coin_details->melt_amount_with_fee; if (GNUNET_OK != - plugin->insert_refresh_melt (plugin->cls, + TMH_plugin->insert_refresh_melt (TMH_plugin->cls, session, oldcoin_index, &melt)) @@ -617,26 +617,26 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, int res; unsigned int i; - if (NULL == (session = plugin->get_session (plugin->cls, + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, GNUNET_NO))) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } if (GNUNET_OK != - plugin->start (plugin->cls, + TMH_plugin->start (TMH_plugin->cls, session)) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } - res = plugin->get_refresh_session (plugin->cls, + res = TMH_plugin->get_refresh_session (TMH_plugin->cls, session, session_hash, &refresh_session); if (GNUNET_YES == res) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); res = TMH_RESPONSE_reply_refresh_melt_success (connection, session_hash, @@ -645,7 +645,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, } if (GNUNET_SYSERR == res) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -664,7 +664,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, i))) { TMH_KS_release (key_state); - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } @@ -673,13 +673,13 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, /* store requested new denominations */ if (GNUNET_OK != - plugin->insert_refresh_order (plugin->cls, + TMH_plugin->insert_refresh_order (TMH_plugin->cls, session, session_hash, num_new_denoms, denom_pubs)) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -687,14 +687,14 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, for (i = 0; i < TALER_CNC_KAPPA; i++) { if (GNUNET_OK != - plugin->insert_refresh_commit_coins (plugin->cls, + TMH_plugin->insert_refresh_commit_coins (TMH_plugin->cls, session, session_hash, i, num_new_denoms, commit_coin[i])) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -702,14 +702,14 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, for (i = 0; i < TALER_CNC_KAPPA; i++) { if (GNUNET_OK != - plugin->insert_refresh_commit_links (plugin->cls, + TMH_plugin->insert_refresh_commit_links (TMH_plugin->cls, session, session_hash, i, coin_count, commit_link[i])) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -723,12 +723,12 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, TALER_CNC_KAPPA); if (GNUNET_OK != - (res = plugin->create_refresh_session (plugin->cls, + (res = TMH_plugin->create_refresh_session (TMH_plugin->cls, session, session_hash, &refresh_session))) { - plugin->rollback (plugin->cls, + TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -736,7 +736,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, if (GNUNET_OK != - plugin->commit (plugin->cls, + TMH_plugin->commit (TMH_plugin->cls, session)) { TALER_LOG_WARNING ("/refresh/melt transaction commit failed\n"); @@ -789,7 +789,7 @@ check_commitment (struct MHD_Connection *connection, commit_links = GNUNET_malloc (num_oldcoins * sizeof (struct RefreshCommitLink)); if (GNUNET_OK != - plugin->get_refresh_commit_links (plugin->cls, + TMH_plugin->get_refresh_commit_links (TMH_plugin->cls, session, session_hash, off, @@ -888,7 +888,7 @@ check_commitment (struct MHD_Connection *connection, sizeof (struct RefreshCommitCoin)); if (GNUNET_OK != - plugin->get_refresh_commit_coins (plugin->cls, + TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls, session, session_hash, off, @@ -1008,7 +1008,7 @@ refresh_mint_coin (struct MHD_Connection *connection, return ev_sig; } if (GNUNET_OK != - plugin->insert_refresh_collectable (plugin->cls, + TMH_plugin->insert_refresh_collectable (TMH_plugin->cls, session, session_hash, coin_off, @@ -1054,14 +1054,14 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, unsigned int j; unsigned int off; - if (NULL == (session = plugin->get_session (plugin->cls, + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, GNUNET_NO))) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } - res = plugin->get_refresh_session (plugin->cls, + res = TMH_plugin->get_refresh_session (TMH_plugin->cls, session, session_hash, &refresh_session); @@ -1081,7 +1081,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, for (j=0;jget_refresh_melt (plugin->cls, + TMH_plugin->get_refresh_melt (TMH_plugin->cls, session, session_hash, j, @@ -1095,7 +1095,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, denom_pubs = GNUNET_malloc (refresh_session.num_newcoins * sizeof (struct TALER_DenominationPublicKey)); if (GNUNET_OK != - plugin->get_refresh_order (plugin->cls, + TMH_plugin->get_refresh_order (TMH_plugin->cls, session, session_hash, refresh_session.num_newcoins, @@ -1136,7 +1136,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, /* Client request OK, start transaction */ if (GNUNET_OK != - plugin->start (plugin->cls, + TMH_plugin->start (TMH_plugin->cls, session)) { GNUNET_break (0); @@ -1149,7 +1149,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, commit_coins = GNUNET_malloc (refresh_session.num_newcoins * sizeof (struct RefreshCommitCoin)); if (GNUNET_OK != - plugin->get_refresh_commit_coins (plugin->cls, + TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls, session, session_hash, refresh_session.noreveal_index, @@ -1195,7 +1195,7 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, GNUNET_free (commit_coins); if (GNUNET_OK != - plugin->commit (plugin->cls, + TMH_plugin->commit (TMH_plugin->cls, session)) { TALER_LOG_WARNING ("/refresh/reveal transaction commit failed\n"); @@ -1234,13 +1234,13 @@ TMH_DB_execute_refresh_link (struct MHD_Connection *connection, struct TALER_EncryptedLinkSecretP shared_secret_enc; struct LinkDataList *ldl; - if (NULL == (session = plugin->get_session (plugin->cls, + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, GNUNET_NO))) { GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } - res = plugin->get_transfer (plugin->cls, + res = TMH_plugin->get_transfer (TMH_plugin->cls, session, coin_pub, &transfer_pub, @@ -1260,7 +1260,7 @@ TMH_DB_execute_refresh_link (struct MHD_Connection *connection, } GNUNET_assert (GNUNET_OK == res); - ldl = plugin->get_link_data_list (plugin->cls, + ldl = TMH_plugin->get_link_data_list (TMH_plugin->cls, session, coin_pub); if (NULL == ldl) @@ -1275,7 +1275,7 @@ TMH_DB_execute_refresh_link (struct MHD_Connection *connection, &transfer_pub, &shared_secret_enc, ldl); - plugin->free_link_data_list (plugin->cls, + TMH_plugin->free_link_data_list (TMH_plugin->cls, ldl); return res; } diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index 44d11ffc9..3e8a7df7f 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -21,7 +21,6 @@ #ifndef TALER_MINT_HTTPD_DB_H #define TALER_MINT_HTTPD_DB_H -#include #include #include #include "taler_util.h" diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index a1d64be45..ee2d24845 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "taler_mintdb_plugin.h" #include "taler_signatures.h" diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h index 4946ec1a3..26c9ccad6 100644 --- a/src/mint/taler-mint-httpd_keystate.h +++ b/src/mint/taler-mint-httpd_keystate.h @@ -26,7 +26,7 @@ #include #include #include "taler-mint-httpd.h" -#include "key_io.h" +#include "taler_mintdb_lib.h" /** diff --git a/src/mint/taler-mint-httpd_mhd.c b/src/mint/taler-mint-httpd_mhd.c index 934a2f9d2..a12158731 100644 --- a/src/mint/taler-mint-httpd_mhd.c +++ b/src/mint/taler-mint-httpd_mhd.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "taler-mint-httpd_responses.h" #include "taler-mint-httpd.h" diff --git a/src/mint/taler-mint-keycheck.c b/src/mint/taler-mint-keycheck.c deleted file mode 100644 index 4e8885fd3..000000000 --- a/src/mint/taler-mint-keycheck.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file taler-mint-keycheck.c - * @brief Check mint keys for validity. Reads the signing and denomination - * keys from the mint directory and checks to make sure they are - * well-formed. This is purely a diagnostic tool. - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include -#include -#include "key_io.h" - -/** - * Mint directory with the keys. - */ -static char *mint_directory; - -/** - * Our configuration. - */ -static struct GNUNET_CONFIGURATION_Handle *kcfg; - - -/** - * Function called on each signing key. - * - * @param cls closure (NULL) - * @param filename name of the file the key came from - * @param ski the sign key - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -signkeys_iter (void *cls, - const char *filename, - const struct TALER_MintSigningKeyValidityPSPriv *ski) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Iterating over key `%s' for start time %s\n", - filename, - GNUNET_STRINGS_absolute_time_to_string - (GNUNET_TIME_absolute_ntoh (ski->issue.start))); - - if (ntohl (ski->issue.purpose.size) != - (sizeof (struct TALER_MintSigningKeyValidityPS) - - offsetof (struct TALER_MintSigningKeyValidityPS, purpose))) - { - fprintf (stderr, - "Signing key `%s' has invalid purpose size\n", - filename); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY, - &ski->issue.purpose, - &ski->issue.signature.eddsa_signature, - &ski->issue.master_public_key.eddsa_pub)) - { - fprintf (stderr, - "Signing key `%s' has invalid signature\n", - filename); - return GNUNET_SYSERR; - } - printf ("Signing key `%s' valid\n", - filename); - return GNUNET_OK; -} - - -/** - * Check signing keys. - * - * @return #GNUNET_OK if the keys are OK - * #GNUNET_NO if not - */ -static int -mint_signkeys_check () -{ - if (0 > TALER_MINT_signkeys_iterate (mint_directory, - &signkeys_iter, - NULL)) - return GNUNET_NO; - return GNUNET_OK; -} - - -/** - * Function called on each denomination key. - * - * @param cls closure (NULL) - * @param dki the denomination key - * @param alias coin alias - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -denomkeys_iter (void *cls, - const char *alias, - const struct TALER_DenominationKeyIssueInformation *dki) -{ - struct GNUNET_HashCode hc; - - if (ntohl (dki->issue.purpose.size) != - sizeof (struct TALER_DenominationKeyValidityPS)) - { - fprintf (stderr, - "Denomination key for `%s' has invalid purpose size\n", - alias); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY, - &dki->issue.purpose, - &dki->issue.signature.eddsa_signature, - &dki->issue.master.eddsa_pub)) - { - fprintf (stderr, - "Denomination key for `%s' has invalid signature\n", - alias); - return GNUNET_SYSERR; - } - GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, - &hc); - if (0 != memcmp (&hc, - &dki->issue.denom_hash, - sizeof (struct GNUNET_HashCode))) - { - fprintf (stderr, - "Public key for `%s' does not match signature\n", - alias); - return GNUNET_SYSERR; - } - printf ("Denomination key `%s' is valid\n", - alias); - - return GNUNET_OK; -} - - -/** - * Check denomination keys. - * - * @return #GNUNET_OK if the keys are OK - * #GNUNET_NO if not - */ -static int -mint_denomkeys_check () -{ - if (0 > TALER_MINT_denomkeys_iterate (mint_directory, - &denomkeys_iter, - NULL)) - return GNUNET_NO; - return GNUNET_OK; -} - - -/** - * The main function of the keyup tool - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keycheck OPTIONS"), - {'d', "directory", "DIRECTORY", - "mint directory with keys to check", 1, - &GNUNET_GETOPT_set_filename, &mint_directory}, - GNUNET_GETOPT_OPTION_END - }; - - GNUNET_assert (GNUNET_OK == - GNUNET_log_setup ("taler-mint-keycheck", - "WARNING", - NULL)); - - if (GNUNET_GETOPT_run ("taler-mint-keycheck", - options, - argc, argv) < 0) - return 1; - if (NULL == mint_directory) - { - fprintf (stderr, - "Mint directory not given\n"); - return 1; - } - - kcfg = TALER_config_load (mint_directory); - if (NULL == kcfg) - { - fprintf (stderr, - "Failed to load mint configuration\n"); - return 1; - } - if ( (GNUNET_OK != mint_signkeys_check ()) || - (GNUNET_OK != mint_denomkeys_check ()) ) - { - GNUNET_CONFIGURATION_destroy (kcfg); - return 1; - } - GNUNET_CONFIGURATION_destroy (kcfg); - return 0; -} - -/* end of taler-mint-keycheck.c */ diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c deleted file mode 100644 index ab457426c..000000000 --- a/src/mint/taler-mint-keyup.c +++ /dev/null @@ -1,954 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @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 -#include -#include "taler_util.h" -#include "key_io.h" - -/** - * When generating filenames from a cryptographic hash, we do not use - * all 512 bits but cut off after this number of characters (in - * base32-encoding). Base32 is 5 bit per character, and given that we - * have very few coin types we hash, at 100 bits the chance of - * collision (by accident over tiny set -- birthday paradox does not - * apply here!) is negligible. - */ -#define HASH_CUTOFF 20 - -/** - * Macro to round microseconds to seconds in GNUNET_TIME_* structs. - * - * @param name value to round - * @param field rel_value_us or abs_value_us - */ -#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000); - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Struct with all of the key information for a kind of coin. Hashed - * to generate a unique directory name per coin type. - */ -struct CoinTypeNBOP -{ - /** - * How long can the coin be spend? - */ - struct GNUNET_TIME_RelativeNBO duration_spend; - - /** - * How long can the coin be withdrawn (generated)? - */ - struct GNUNET_TIME_RelativeNBO duration_withdraw; - - /** - * What is the value of the coin? - */ - struct TALER_AmountNBO value; - - /** - * What is the fee charged for withdrawl? - */ - struct TALER_AmountNBO fee_withdraw; - - /** - * What is the fee charged for deposits? - */ - struct TALER_AmountNBO fee_deposit; - - /** - * What is the fee charged for melting? - */ - struct TALER_AmountNBO fee_refresh; - - /** - * Key size in NBO. - */ - uint32_t rsa_keysize; -}; - -GNUNET_NETWORK_STRUCT_END - -/** - * Set of all of the parameters that chracterize a coin. - */ -struct CoinTypeParams -{ - - /** - * How long can the coin be spend? Should be significantly - * larger than @e duration_withdraw (i.e. years). - */ - struct GNUNET_TIME_Relative duration_spend; - - /** - * How long can the coin be withdrawn (generated)? Should be small - * enough to limit how many coins will be signed into existence with - * the same key, but large enough to still provide a reasonable - * anonymity set. - */ - struct GNUNET_TIME_Relative duration_withdraw; - - /** - * How much should coin creation (@e duration_withdraw) duration - * overlap with the next coin? Basically, the starting time of two - * coins is always @e duration_withdraw - @e duration_overlap apart. - */ - struct GNUNET_TIME_Relative duration_overlap; - - /** - * What is the value of the coin? - */ - struct TALER_Amount value; - - /** - * What is the fee charged for withdrawl? - */ - struct TALER_Amount fee_withdraw; - - /** - * What is the fee charged for deposits? - */ - struct TALER_Amount fee_deposit; - - /** - * What is the fee charged for melting? - */ - struct TALER_Amount fee_refresh; - - /** - * Time at which this coin is supposed to become valid. - */ - struct GNUNET_TIME_Absolute anchor; - - /** - * Length of the RSA key in bits. - */ - uint32_t rsa_keysize; -}; - - -/** - * Filename of the master private key. - */ -static char *masterkeyfile; - -/** - * Director of the mint, containing the keys. - */ -static char *mint_directory; - -/** - * Time to pretend when the key update is executed. - */ -static char *pretend_time_str; - -/** - * Handle to the mint's configuration - */ -static struct GNUNET_CONFIGURATION_Handle *kcfg; - -/** - * Time when the key update is executed. Either the actual current time, or a - * pretended time. - */ -static struct GNUNET_TIME_Absolute now; - -/** - * Master private key of the mint. - */ -static struct TALER_MasterPrivateKeyP master_priv; - -/** - * Master public key of the mint. - */ -static struct TALER_MasterPublicKeyP master_public_key; - -/** - * Until what time do we provide keys? - */ -static struct GNUNET_TIME_Absolute lookahead_sign_stamp; - - -/** - * Obtain the name of the directory we use to store signing - * keys created at time @a start. - * - * @param start time at which we create the signing key - * @return name of the directory we should use, basically "$MINTDIR/$TIME/"; - * (valid until next call to this function) - */ -static const char * -get_signkey_file (struct GNUNET_TIME_Absolute start) -{ - static char dir[4096]; - - GNUNET_snprintf (dir, - sizeof (dir), - "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu", - mint_directory, - (unsigned long long) start.abs_value_us); - return dir; -} - - -/** - * Hash the data defining the coin type. Exclude information that may - * not be the same for all instances of the coin type (i.e. the - * anchor, overlap). - * - * @param p coin parameters to convert to a hash - * @param hash[OUT] set to the hash matching @a p - */ -static void -hash_coin_type (const struct CoinTypeParams *p, - struct GNUNET_HashCode *hash) -{ - struct CoinTypeNBOP p_nbo; - - memset (&p_nbo, - 0, - sizeof (struct CoinTypeNBOP)); - p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend); - p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); - TALER_amount_hton (&p_nbo.value, - &p->value); - TALER_amount_hton (&p_nbo.fee_withdraw, - &p->fee_withdraw); - TALER_amount_hton (&p_nbo.fee_deposit, - &p->fee_deposit); - TALER_amount_hton (&p_nbo.fee_refresh, - &p->fee_refresh); - p_nbo.rsa_keysize = htonl (p->rsa_keysize); - GNUNET_CRYPTO_hash (&p_nbo, - sizeof (struct CoinTypeNBOP), - hash); -} - - -/** - * Obtain the name of the directory we should use to store coins of - * the given type. The directory name has the format - * "$MINTDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the - * coin and "$HASH" encodes all of the coin's parameters, generating a - * unique string for each type of coin. Note that the "$HASH" - * includes neither the absolute creation time nor the key of the - * coin, thus the files in the subdirectory really just refer to the - * same type of coins, not the same coin. - * - * @param p coin parameters to convert to a directory name - * @return directory name (valid until next call to this function) - */ -static const char * -get_cointype_dir (const struct CoinTypeParams *p) -{ - static char dir[4096]; - struct GNUNET_HashCode hash; - char *hash_str; - char *val_str; - size_t i; - - hash_coin_type (p, &hash); - hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash, - sizeof (struct GNUNET_HashCode)); - GNUNET_assert (NULL != hash_str); - GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); - hash_str[HASH_CUTOFF] = 0; - - val_str = TALER_amount_to_string (&p->value); - for (i = 0; i < strlen (val_str); i++) - if ( (':' == val_str[i]) || - ('.' == val_str[i]) ) - val_str[i] = '_'; - - GNUNET_snprintf (dir, - sizeof (dir), - "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s", - mint_directory, - val_str, - hash_str); - GNUNET_free (hash_str); - GNUNET_free (val_str); - return dir; -} - - -/** - * Obtain the name of the file we would use to store the key - * information for a coin of the given type @a p and validity - * start time @a start - * - * @param p parameters for the coin - * @param start when would the coin begin to be issued - * @return name of the file to use for this coin - * (valid until next call to this function) - */ -static const char * -get_cointype_file (const struct CoinTypeParams *p, - struct GNUNET_TIME_Absolute start) -{ - static char filename[4096]; - const char *dir; - - dir = get_cointype_dir (p); - GNUNET_snprintf (filename, - sizeof (filename), - "%s" DIR_SEPARATOR_STR "%llu", - dir, - (unsigned long long) start.abs_value_us); - return filename; -} - - -/** - * Get the latest key file from a past run of the key generation - * tool. Used to calculate the starting time for the keys we - * generate during this invocation. This function is used to - * handle both signing keys and coin keys, as in both cases - * the filenames correspond to the timestamps we need. - * - * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated - * to contain the highest timestamp (below #now) - * that was found - * @param filename complete filename (absolute path) - * @return #GNUNET_OK (to continue to iterate) - */ -static int -get_anchor_iter (void *cls, - const char *filename) -{ - struct GNUNET_TIME_Absolute *anchor = cls; - struct GNUNET_TIME_Absolute stamp; - const char *base; - char *end = NULL; - - base = GNUNET_STRINGS_get_short_name (filename); - stamp.abs_value_us = strtol (base, - &end, - 10); - if ((NULL == end) || (0 != *end)) - { - fprintf(stderr, - "Ignoring unexpected file `%s'.\n", - filename); - return GNUNET_OK; - } - if (stamp.abs_value_us <= now.abs_value_us) - *anchor = GNUNET_TIME_absolute_max (stamp, - *anchor); - return GNUNET_OK; -} - - -/** - * Get the timestamp where the first new key should be generated. - * Relies on correctly named key files (as we do not parse them, - * but just look at the filenames to "guess" at their contents). - * - * @param dir directory that should contain the existing keys - * @param duration how long is one key valid (for signing)? - * @param overlap what's the overlap between the keys validity period? - * @param[out] anchor the timestamp where the first new key should be generated - */ -static void -get_anchor (const char *dir, - struct GNUNET_TIME_Relative duration, - struct GNUNET_TIME_Relative overlap, - struct GNUNET_TIME_Absolute *anchor) -{ - GNUNET_assert (0 == duration.rel_value_us % 1000000); - GNUNET_assert (0 == overlap.rel_value_us % 1000000); - if (GNUNET_YES != - GNUNET_DISK_directory_test (dir, - GNUNET_YES)) - { - *anchor = now; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No existing keys found, starting with fresh key set.\n"); - return; - } - *anchor = GNUNET_TIME_UNIT_ZERO_ABS; - if (-1 == - GNUNET_DISK_directory_scan (dir, - &get_anchor_iter, - anchor)) - { - *anchor = now; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No existing keys found, starting with fresh key set.\n"); - return; - } - - /* FIXME: this check is a bit dubious, as 'now' - may be way into the future if we want to generate - many keys... #3727*/ - if ((GNUNET_TIME_absolute_add (*anchor, - duration)).abs_value_us < now.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Existing keys are way too old, starting with fresh key set.\n"); - *anchor = now; - } - else if (anchor->abs_value_us != now.abs_value_us) // Also odd... - { - /* Real starting time is the last start time + duration - overlap */ - *anchor = GNUNET_TIME_absolute_add (*anchor, - duration); - *anchor = GNUNET_TIME_absolute_subtract (*anchor, - overlap); - } - /* anchor is now the stamp where we need to create a new key */ -} - - -/** - * Create a mint signing key (for signing mint messages, not for coins) - * and assert its correctness by signing it with the master key. - * - * @param start start time of the validity period for the key - * @param duration how long should the key be valid - * @param pi[OUT] set to the signing key information - */ -static void -create_signkey_issue_priv (struct GNUNET_TIME_Absolute start, - struct GNUNET_TIME_Relative duration, - struct TALER_MintSigningKeyValidityPSPriv *pi) -{ - struct GNUNET_CRYPTO_EddsaPrivateKey *priv; - struct TALER_MintSigningKeyValidityPS *issue = &pi->issue; - - priv = GNUNET_CRYPTO_eddsa_key_create (); - pi->signkey_priv.eddsa_priv = *priv; - GNUNET_free (priv); - issue->master_public_key = master_public_key; - issue->start = GNUNET_TIME_absolute_hton (start); - issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, - duration)); - GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv.eddsa_priv, - &issue->signkey_pub.eddsa_pub); - issue->purpose.purpose = htonl (TALER_SIGNATURE_MINT_SIGNING_KEY_VALIDITY); - issue->purpose.size = htonl (sizeof (struct TALER_MintSigningKeyValidityPS) - - offsetof (struct TALER_MintSigningKeyValidityPS, - purpose)); - - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv, - &issue->purpose, - &issue->signature.eddsa_signature)); -} - - -/** - * Generate signing keys starting from the last key found to - * the lookahead time. - * - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -mint_keys_update_signkeys () -{ - struct GNUNET_TIME_Relative signkey_duration; - struct GNUNET_TIME_Absolute anchor; - char *signkey_dir; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - "mint_keys", - "signkey_duration", - &signkey_duration)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint_keys", - "signkey_duration"); - return GNUNET_SYSERR; - } - ROUND_TO_SECS (signkey_duration, - rel_value_us); - GNUNET_asprintf (&signkey_dir, - "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, - mint_directory); - /* make sure the directory exists */ - if (GNUNET_OK != - GNUNET_DISK_directory_create (signkey_dir)) - { - fprintf (stderr, - "Failed to create signing key directory\n"); - return GNUNET_SYSERR; - } - - get_anchor (signkey_dir, - signkey_duration, - GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */, - &anchor); - - while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) - { - const char *skf; - struct TALER_MintSigningKeyValidityPSPriv signkey_issue; - ssize_t nwrite; - - skf = get_signkey_file (anchor); - GNUNET_break (GNUNET_YES != - GNUNET_DISK_file_test (skf)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Generating signing key for %s.\n", - GNUNET_STRINGS_absolute_time_to_string (anchor)); - create_signkey_issue_priv (anchor, - signkey_duration, - &signkey_issue); - nwrite = GNUNET_DISK_fn_write (skf, - &signkey_issue, - sizeof (struct TALER_MintSigningKeyValidityPS), - GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ); - if (nwrite != sizeof (struct TALER_MintSigningKeyValidityPS)) - { - fprintf (stderr, - "Failed to write to file `%s': %s\n", - skf, - STRERROR (errno)); - return GNUNET_SYSERR; - } - anchor = GNUNET_TIME_absolute_add (anchor, - signkey_duration); - } - return GNUNET_OK; -} - - -/** - * Parse configuration for coin type parameters. Also determines - * our anchor by looking at the existing coins of the same type. - * - * @param ct section in the configuration file giving the coin type parameters - * @param params[OUT] set to the coin parameters from the configuration - * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid - */ -static int -get_cointype_params (const char *ct, - struct CoinTypeParams *params) -{ - const char *dir; - unsigned long long rsa_keysize; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - ct, - "duration_withdraw", - ¶ms->duration_withdraw)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "duration_withdraw"); - return GNUNET_SYSERR; - } - ROUND_TO_SECS (params->duration_withdraw, - rel_value_us); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - ct, - "duration_spend", - ¶ms->duration_spend)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "duration_spend"); - return GNUNET_SYSERR; - } - ROUND_TO_SECS (params->duration_spend, - rel_value_us); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - ct, - "duration_overlap", - ¶ms->duration_overlap)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "mint_denom_duration_overlap"); - return GNUNET_SYSERR; - } - ROUND_TO_SECS (params->duration_overlap, - rel_value_us); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (kcfg, - ct, - "rsa_keysize", - &rsa_keysize)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "rsa_keysize"); - return GNUNET_SYSERR; - } - if ( (rsa_keysize > 4 * 2048) || - (rsa_keysize < 1024) ) - { - fprintf (stderr, - "Given RSA keysize %llu outside of permitted range\n", - rsa_keysize); - return GNUNET_SYSERR; - } - params->rsa_keysize = (unsigned int) rsa_keysize; - if (GNUNET_OK != - TALER_config_get_denom (kcfg, - ct, - "value", - ¶ms->value)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "value"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_config_get_denom (kcfg, - ct, - "fee_withdraw", - ¶ms->fee_withdraw)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "fee_withdraw"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_config_get_denom (kcfg, - ct, - "fee_deposit", - ¶ms->fee_deposit)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "fee_deposit"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_config_get_denom (kcfg, - ct, - "fee_refresh", - ¶ms->fee_refresh)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - ct, - "fee_refresh"); - return GNUNET_SYSERR; - } - - dir = get_cointype_dir (params); - get_anchor (dir, - params->duration_spend, - params->duration_overlap, - ¶ms->anchor); - return GNUNET_OK; -} - - -/** - * Initialize the private and public key information structure for - * signing coins into existence. Generates the private signing key - * and signes it together with the coin's meta data using the master - * signing key. - * - * @param params parameters used to initialize the @a dki - * @param dki[OUT] initialized according to @a params - */ -static void -create_denomkey_issue (const struct CoinTypeParams *params, - struct TALER_DenominationKeyIssueInformation *dki) -{ - dki->denom_priv.rsa_private_key - = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize); - GNUNET_assert (NULL != dki->denom_priv.rsa_private_key); - dki->denom_pub.rsa_public_key - = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv.rsa_private_key); - GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, - &dki->issue.denom_hash); - dki->issue.master = master_public_key; - dki->issue.start = GNUNET_TIME_absolute_hton (params->anchor); - dki->issue.expire_withdraw = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, - params->duration_withdraw)); - dki->issue.expire_spend = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, - params->duration_spend)); - TALER_amount_hton (&dki->issue.value, - ¶ms->value); - TALER_amount_hton (&dki->issue.fee_withdraw, - ¶ms->fee_withdraw); - TALER_amount_hton (&dki->issue.fee_deposit, - ¶ms->fee_deposit); - TALER_amount_hton (&dki->issue.fee_refresh, - ¶ms->fee_refresh); - dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MINT_DENOMINATION_KEY_VALIDITY); - dki->issue.purpose.size = htonl (sizeof (struct TALER_DenominationKeyIssueInformation) - - offsetof (struct TALER_DenominationKeyIssueInformation, - issue.purpose)); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv, - &dki->issue.purpose, - &dki->issue.signature.eddsa_signature)); -} - - -/** - * Generate new coin signing keys for the coin type of the given @a - * coin_alias. - * - * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure - * @param coin_alias name of the coin's section in the configuration - */ -static void -mint_keys_update_cointype (void *cls, - const char *coin_alias) -{ - int *ret = cls; - struct CoinTypeParams p; - const char *dkf; - struct TALER_DenominationKeyIssueInformation denomkey_issue; - - if (0 != strncasecmp (coin_alias, - "coin_", - strlen ("coin_"))) - return; /* not a coin definition */ - if (GNUNET_OK != - get_cointype_params (coin_alias, - &p)) - { - *ret = GNUNET_SYSERR; - return; - } - if (GNUNET_OK != - GNUNET_DISK_directory_create (get_cointype_dir (&p))) - { - *ret = GNUNET_SYSERR; - return; - } - - while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) - { - dkf = get_cointype_file (&p, - p.anchor); - GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Generating denomination key for type `%s', start %s at %s\n", - coin_alias, - GNUNET_STRINGS_absolute_time_to_string (p.anchor), - dkf); - create_denomkey_issue (&p, - &denomkey_issue); - if (GNUNET_OK != - TALER_MINT_write_denom_key (dkf, - &denomkey_issue)) - { - fprintf (stderr, - "Failed to write denomination key information to file `%s'.\n", - dkf); - *ret = GNUNET_SYSERR; - GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); - return; - } - GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); - p.anchor = GNUNET_TIME_absolute_add (p.anchor, - p.duration_spend); - p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, - p.duration_overlap); - } -} - - -/** - * Update all of the denomination keys of the mint. - * - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -mint_keys_update_denomkeys () -{ - int ok; - - ok = GNUNET_OK; - GNUNET_CONFIGURATION_iterate_sections (kcfg, - &mint_keys_update_cointype, - &ok); - return ok; -} - - -/** - * The main function of the taler-mint-keyup tool. This tool is used - * to create the signing and denomination keys for the mint. It uses - * the long-term offline private key and writes the (additional) key - * files to the respective mint directory (from where they can then be - * copied to the online server). Note that we need (at least) the - * most recent generated previous keys so as to align the validity - * periods. - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, - char *const *argv) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"), - {'m', "master-key", "FILE", - "master key file (private key)", 1, - &GNUNET_GETOPT_set_filename, &masterkeyfile}, - {'d', "mint-dir", "DIR", - "mint directory with keys to update", 1, - &GNUNET_GETOPT_set_filename, &mint_directory}, - {'t', "time", "TIMESTAMP", - "pretend it is a different time for the update", 0, - &GNUNET_GETOPT_set_string, &pretend_time_str}, - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_TIME_Relative lookahead_sign; - struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; - - GNUNET_assert (GNUNET_OK == - GNUNET_log_setup ("taler-mint-keyup", - "WARNING", - NULL)); - - if (GNUNET_GETOPT_run ("taler-mint-keyup", - options, - argc, argv) < 0) - return 1; - if (NULL == mint_directory) - { - fprintf (stderr, - "Mint directory not given\n"); - return 1; - } - if (NULL != pretend_time_str) - { - if (GNUNET_OK != - GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, - &now)) - { - fprintf (stderr, - "timestamp `%s' invalid\n", - pretend_time_str); - return 1; - } - } - else - { - now = GNUNET_TIME_absolute_get (); - } - ROUND_TO_SECS (now, abs_value_us); - - kcfg = TALER_config_load (mint_directory); - if (NULL == kcfg) - { - fprintf (stderr, - "Failed to load mint configuration\n"); - return 1; - } - if (NULL == masterkeyfile) - { - fprintf (stderr, - "Master key file not given\n"); - return 1; - } - eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); - if (NULL == eddsa_priv) - { - fprintf (stderr, - "Failed to initialize master key from file `%s'\n", - masterkeyfile); - return 1; - } - master_priv.eddsa_priv = *eddsa_priv; - GNUNET_free (eddsa_priv); - GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, - &master_public_key.eddsa_pub); - - /* check if key from file matches the one from the configuration */ - { - struct GNUNET_CRYPTO_EddsaPublicKey master_public_key_from_cfg; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_data (kcfg, - "mint", - "master_public_key", - &master_public_key_from_cfg, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint", - "master_public_key"); - return 1; - } - if (0 != - memcmp (&master_public_key, - &master_public_key_from_cfg, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "mint", - "master_public_key", - _("does not match with private key")); - return 1; - } - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (kcfg, - "mint_keys", - "lookahead_sign", - &lookahead_sign)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "mint_keys", - "lookahead_sign"); - return GNUNET_SYSERR; - } - if (0 == lookahead_sign.rel_value_us) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "mint_keys", - "lookahead_sign", - _("must not be zero")); - return GNUNET_SYSERR; - } - ROUND_TO_SECS (lookahead_sign, - rel_value_us); - lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, - lookahead_sign); - - - /* finally, do actual work */ - if (GNUNET_OK != mint_keys_update_signkeys ()) - return 1; - - if (GNUNET_OK != mint_keys_update_denomkeys ()) - return 1; - return 0; -} - -/* end of taler-mint-keyup.c */ diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c deleted file mode 100644 index 125748704..000000000 --- a/src/mint/taler-mint-reservemod.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file taler-mint-reservemod.c - * @brief Modify reserves. Allows manipulation of reserve balances. - * @author Florian Dold - * @author Benedikt Mueller - */ -#include "platform.h" -#include -#include -#include "taler_util.h" -#include "taler_signatures.h" -#include "taler_mintdb_plugin.h" -#include "taler_pq_lib.h" - - -/** - * Director of the mint, containing the keys. - */ -static char *mint_directory; - -/** - * Public key of the reserve to manipulate. - */ -static struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub; - -/** - * Handle to the mint's configuration - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Database connection handle. - */ -static PGconn *db_conn; - - -/** - * Create a new or add to existing reserve. Fails if currencies do - * not match. - * - * @param denom denomination to add - * @return #GNUNET_OK on success, - * #GNUNET_SYSERR on error - */ -// FIXME: this should use the DB abstraction layer. (#3717) -// FIXME: this should be done by adding an inbound transaction -// to the table with the transactions for this reserve, -// not by modifying some 'total' value for the reserve! -// (we should in fact probably never modify, always just append!) (#3633) -static int -reservemod_add (struct TALER_Amount denom) -{ - PGresult *result; - const void *param_values[] = { - reserve_pub - }; - int param_lengths[] = { - sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) - }; - int param_formats[] = { - 1 - }; - struct TALER_Amount old_denom; - struct TALER_Amount new_denom; - struct TALER_AmountNBO new_denom_nbo; - - result = PQexecParams (db_conn, - "SELECT balance_value, balance_fraction, balance_currency" - " FROM reserves" - " WHERE reserve_pub=$1" - " LIMIT 1;", - 1, - NULL, - (const char * const *) param_values, - param_lengths, - param_formats, - 1); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - fprintf (stderr, - "Select failed: %s\n", - PQresultErrorMessage (result)); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - struct GNUNET_TIME_AbsoluteNBO exnbo; - uint32_t value = htonl (denom.value); - uint32_t fraction = htonl (denom.fraction); - const void *param_values[] = { - reserve_pub, - &value, - &fraction, - denom.currency, - &exnbo - }; - int param_lengths[] = { - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), - sizeof (uint32_t), - sizeof (uint32_t), - strlen (denom.currency), - sizeof (struct GNUNET_TIME_AbsoluteNBO) - }; - int param_formats[] = { - 1, 1, 1, 1, 1 - }; - - exnbo = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS)); - result = PQexecParams (db_conn, - "INSERT INTO reserves (reserve_pub, balance_value, balance_fraction, balance_currency, expiration_date)" - " VALUES ($1,$2,$3,$4,$5);", - 5, - NULL, - (const char **) param_values, - param_lengths, - param_formats, - 1); - - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - fprintf (stderr, - "Insert failed: %s\n", - PQresultErrorMessage (result)); - return GNUNET_SYSERR; - } - } - else - { - const void *param_values[] = { - &new_denom_nbo.value, - &new_denom_nbo.fraction, - reserve_pub - }; - int param_lengths[] = { - sizeof (new_denom_nbo.value), - sizeof (new_denom_nbo.fraction), - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) - }; - int param_formats[] = { - 1, 1, 1 - }; - - GNUNET_assert (GNUNET_OK == - TALER_PQ_extract_amount (result, 0, - "balance_value", - "balance_fraction", - "balance_currency", - &old_denom)); - TALER_amount_add (&new_denom, - &old_denom, - &denom); - TALER_amount_hton (&new_denom_nbo, - &new_denom); - result = PQexecParams (db_conn, - "UPDATE reserves" - " SET balance_value = $1, balance_fraction = $2, status_sig = NULL, status_sign_pub = NULL" - " WHERE reserve_pub = $3;", - 3, - NULL, - (const char **) param_values, - param_lengths, - param_formats, - 1); - - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - fprintf (stderr, - "Update failed: %s\n", - PQresultErrorMessage (result)); - return GNUNET_SYSERR; - } - /* Yes, for historic reasons libpq returns a 'const char *'... */ - if (0 != strcmp ("1", - PQcmdTuples (result))) - { - fprintf (stderr, - "Update failed (updated `%s' tupes instead of '1')\n", - PQcmdTuples (result)); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * The main function of the reservemod tool - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - static char *reserve_pub_str; - static char *add_str; - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-reservemod OPTIONS"), - {'d', "mint-dir", "DIR", - "mint directory with keys to update", 1, - &GNUNET_GETOPT_set_filename, &mint_directory}, - {'R', "reserve", "KEY", - "reserve (public key) to modify", 1, - &GNUNET_GETOPT_set_string, &reserve_pub_str}, - {'a', "add", "DENOM", - "value to add", 1, - &GNUNET_GETOPT_set_string, &add_str}, - GNUNET_GETOPT_OPTION_END - }; - char *connection_cfg_str; - - GNUNET_assert (GNUNET_OK == - GNUNET_log_setup ("taler-mint-reservemod", - "WARNING", - NULL)); - - if (GNUNET_GETOPT_run ("taler-mint-reservemod", - options, - argc, argv) < 0) - return 1; - if (NULL == mint_directory) - { - fprintf (stderr, - "Mint directory not given\n"); - return 1; - } - - reserve_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey); - if ((NULL == reserve_pub_str) || - (GNUNET_OK != - GNUNET_STRINGS_string_to_data (reserve_pub_str, - strlen (reserve_pub_str), - reserve_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))) - { - fprintf (stderr, - "Parsing reserve key invalid\n"); - return 1; - } - cfg = TALER_config_load (mint_directory); - if (NULL == cfg) - { - fprintf (stderr, - "Failed to load mint configuration\n"); - return 1; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint", - "db", - &connection_cfg_str)) - { - fprintf (stderr, - "Database configuration string not found\n"); - return 1; - } - db_conn = PQconnectdb (connection_cfg_str); - if (CONNECTION_OK != PQstatus (db_conn)) - { - fprintf (stderr, - "Database connection failed: %s\n", - PQerrorMessage (db_conn)); - return 1; - } - if (NULL != add_str) - { - struct TALER_Amount add_value; - - if (GNUNET_OK != - TALER_string_to_amount (add_str, - &add_value)) - { - fprintf (stderr, - "Failed to parse currency amount `%s'\n", - add_str); - return 1; - } - if (GNUNET_OK != - reservemod_add (add_value)) - { - fprintf (stderr, - "Failed to update reserve.\n"); - return 1; - } - } - return 0; -} - -/* end taler-mint-reservemod.c */ diff --git a/src/mint/test_mint_common.c b/src/mint/test_mint_common.c deleted file mode 100644 index 83df25046..000000000 --- a/src/mint/test_mint_common.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014 GNUnet e. V. (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 -*/ -/** - * @file mint/test_mint_common.c - * @brief test cases for some functions in mint/mint_common.c - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include "gnunet/gnunet_util_lib.h" -#include "taler_signatures.h" -#include "key_io.h" - -#define RSA_KEY_SIZE 1024 - - -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -int -main (int argc, - const char *const argv[]) -{ - struct TALER_DenominationKeyIssueInformation dki; - char *enc; - size_t enc_size; - struct TALER_DenominationKeyIssueInformation dki_read; - char *enc_read; - size_t enc_read_size; - char *tmpfile; - int ret; - - ret = 1; - enc = NULL; - enc_read = NULL; - tmpfile = 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_DenominationKeyValidityPS, - signature)); - 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.rsa_private_key, - &enc_read); - EXITIF (enc_size != enc_read_size); - EXITIF (0 != memcmp (enc, - enc_read, - enc_size)); - ret = 0; - - EXITIF_exit: - GNUNET_free_non_null (enc); - if (NULL != tmpfile) - { - (void) unlink (tmpfile); - GNUNET_free (tmpfile); - } - GNUNET_free_non_null (enc_read); - 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 deleted file mode 100644 index a82094075..000000000 --- a/src/mint/test_mint_db.c +++ /dev/null @@ -1,393 +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 -*/ -/** - * @file mint/test_mint_db.c - * @brief test cases for DB interaction functions - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include "plugin.h" - -static int result; - -#define FAILIF(cond) \ - do { \ - if (!(cond)){ break;} \ - GNUNET_break (0); \ - goto drop; \ - } while (0) - - -#define RND_BLK(ptr) \ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) - -#define ZR_BLK(ptr) \ - memset (ptr, 0, sizeof (*ptr)) - - -#define CURRENCY "EUR" - -/** - * Checks if the given reserve has the given amount of balance and expiry - * - * @param session the database connection - * @param pub the public key of the reserve - * @param value balance value - * @param fraction balance fraction - * @param currency currency of the reserve - * @param expiry expiration of the reserve - * @return #GNUNET_OK if the given reserve has the same balance and expiration - * as the given parameters; #GNUNET_SYSERR if not - */ -static int -check_reserve (struct TALER_MINTDB_Session *session, - const struct TALER_ReservePublicKeyP *pub, - uint64_t value, - uint32_t fraction, - const char *currency, - uint64_t expiry) -{ - struct Reserve 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)); - FAILIF (expiry != reserve.expiry.abs_value_us); - - return GNUNET_OK; - drop: - return GNUNET_SYSERR; -} - - -struct DenomKeyPair -{ - struct TALER_DenominationPrivateKey priv; - struct TALER_DenominationPublicKey pub; -}; - - -static struct DenomKeyPair * -create_denom_key_pair (unsigned int size) -{ - struct DenomKeyPair *dkp; - - dkp = GNUNET_new (struct DenomKeyPair); - 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.rsa_public_key); - GNUNET_CRYPTO_rsa_private_key_free (dkp->priv.rsa_private_key); - GNUNET_free (dkp); -} - -/** - * 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 cfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct TALER_MINTDB_Session *session; - struct TALER_ReservePublicKeyP reserve_pub; - struct Reserve reserve; - struct GNUNET_TIME_Absolute expiry; - struct TALER_Amount amount; - struct DenomKeyPair *dkp; - struct GNUNET_HashCode h_blind; - struct CollectableBlindcoin cbc; - struct CollectableBlindcoin cbc2; - struct ReserveHistory *rh; - 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; - - dkp = NULL; - rh = NULL; - wire = NULL; - session = NULL; - ZR_BLK (&cbc); - ZR_BLK (&cbc2); - if (GNUNET_OK != - TALER_MINT_plugin_load (cfg)) - { - result = 1; - return; - } - if (GNUNET_OK != - plugin->create_tables (plugin->cls, - GNUNET_YES)) - { - result = 2; - goto drop; - } - if (NULL == - (session = plugin->get_session (plugin->cls, - GNUNET_YES))) - { - result = 3; - goto drop; - } - RND_BLK (&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 != - 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.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 (1 != bt->amount.value); - FAILIF (1 != bt->amount.fraction); - FAILIF (0 != strcmp (CURRENCY, bt->amount.currency)); - FAILIF (NULL != bt->wire); /* FIXME: write wire details to db */ - break; - case TALER_MINT_DB_RO_WITHDRAW_COIN: - withdraw = rh_head->details.withdraw; - FAILIF (0 != memcmp (&withdraw->reserve_pub, - &reserve_pub, - sizeof (reserve_pub))); - FAILIF (0 != memcmp (&withdraw->h_coin_envelope, - &h_blind, sizeof (h_blind))); - break; - } - } - 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) - plugin->free_reserve_history (plugin->cls, - rh); - rh = NULL; - 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.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; - TALER_MINT_plugin_unload (); -} - - -int -main (int argc, - char *const argv[]) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - char *argv2[] = { - "test-mint-db-", /* will be replaced later */ - "-c", "test-mint-db-.conf", /* will be replaced later */ - NULL, - }; - const char *plugin_name; - char *config_filename; - char *testname; - - result = -1; - if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) - { - GNUNET_break (0); - return -1; - } - plugin_name++; - (void) GNUNET_asprintf (&testname, - "test-mint-db-%s", plugin_name); - (void) GNUNET_asprintf (&config_filename, - "%s.conf", testname); - argv2[0] = argv[0]; - argv2[2] = config_filename; - if (GNUNET_OK != - GNUNET_PROGRAM_run ((sizeof (argv2)/sizeof (char *)) - 1, argv2, - testname, - "Test cases for mint database helper functions.", - options, &run, NULL)) - { - GNUNET_free (config_filename); - GNUNET_free (testname); - return 3; - } - GNUNET_free (config_filename); - GNUNET_free (testname); - return result; -} diff --git a/src/mint/test_mint_deposits.c b/src/mint/test_mint_deposits.c deleted file mode 100644 index dbe12e88d..000000000 --- a/src/mint/test_mint_deposits.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file mint/test_mint_deposits.c - * @brief testcase for mint deposits - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include -#include -#include "plugin.h" -#include "taler_pq_lib.h" -#include "taler-mint-httpd.h" - -#define DB_URI "postgres:///taler" - -#define break_db_err(result) do { \ - GNUNET_break(0); \ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \ - } while (0) - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -/** - * Should we not interact with a temporary table? - */ -static int persistent; - -/** - * Testcase result - */ -static int result; - - -/** - * 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 cfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - static const char wire[] = "{" - "\"type\":\"SEPA\"," - "\"IBAN\":\"DE67830654080004822650\"," - "\"NAME\":\"GNUNET E.V\"," - "\"BIC\":\"GENODEF1SRL\"" - "}"; - struct Deposit *deposit; - uint64_t transaction_id; - struct TALER_MINTDB_Session *session; - - deposit = NULL; - 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, - deposit, - sizeof (struct Deposit)); - /* Makeup a random 64bit transaction ID */ - transaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT64_MAX); - deposit->transaction_id = GNUNET_htonll (transaction_id); - /* Random amount */ - deposit->amount_with_fee.value = - htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)); - deposit->amount_with_fee.fraction = - htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)); - GNUNET_assert (strlen (TMH_MINT_CURRENCY) < sizeof (deposit->amount_with_fee.currency)); - strcpy (deposit->amount_with_fee.currency, TMH_MINT_CURRENCY); - /* Copy wireformat */ - deposit->wire = json_loads (wire, 0, NULL); - 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); - return; -} - - -int -main (int argc, - char *const argv[]) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - {'T', "persist", NULL, - gettext_noop ("Use a persistent database table instead of a temporary one"), - GNUNET_NO, &GNUNET_GETOPT_set_one, &persistent}, - GNUNET_GETOPT_OPTION_END - }; - - - persistent = GNUNET_NO; - result = GNUNET_SYSERR; - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, argv, - "test-mint-deposits", - "testcase for mint deposits", - options, &run, NULL)) - return 3; - return (GNUNET_OK == result) ? 0 : 1; -} diff --git a/src/mintdb/Makefile.am b/src/mintdb/Makefile.am new file mode 100644 index 000000000..ceaa2e210 --- /dev/null +++ b/src/mintdb/Makefile.am @@ -0,0 +1,68 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/pq/ $(POSTGRESQL_CPPFLAGS) + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_plugin_mintdb_postgres.la + +EXTRA_DIST = plugin_mintdb_common.c + +libtaler_plugin_mintdb_postgres_la_SOURCES = \ + plugin_mintdb_postgres.c +libtaler_plugin_mintdb_postgres_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_mintdb_postgres_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + -lpq \ + -lgnunetutil + +lib_LTLIBRARIES = \ + libtalermintdb.la + +libtalermintdb_la_SOURCES = \ + mintdb_keyio.c \ + mintdb_plugin.c + +libtalermintdb_la_LIBADD = \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil + +libtalermintdb_la_LDFLAGS = \ + $(POSTGRESQL_LDFLAGS) \ + -version-info 0:0:0 \ + -no-undefined + + +check_PROGRAMS = \ + test-mintdb-deposits \ + test-mintdb-keyio \ + test-mintdb-postgres + +test_mintdb_deposits_SOURCES = \ + test_mintdb_deposits.c +test_mintdb_deposits_LDADD = \ + libtalermintdb.la \ + $(top_srcdir)/src/util/libtalerutil.la \ + $(top_srcdir)/src/pq/libtalerpq.la \ + -lgnunetutil \ + -ljansson \ + -lpq + +test_mintdb_keyio_SOURCES = \ + test_mintdb_keyio.c +test_mintdb_keyio_LDADD = \ + libtalermintdb.la \ + $(top_srcdir)/src/util/libtalerutil.la \ + $(top_srcdir)/src/pq/libtalerpq.la \ + -lgnunetutil + +test_mintdb_postgres_SOURCES = \ + test_mintdb.c +test_mintdb_postgres_LDADD = \ + libtalermintdb.la \ + $(top_srcdir)/src/util/libtalerutil.la \ + $(top_srcdir)/src/pq/libtalerpq.la \ + -lgnunetutil -ljansson +EXTRA_test_mintdb_postgres_DEPENDENCIES = \ + libtaler_plugin_mintdb_postgres.la diff --git a/src/mintdb/mintdb_keyio.c b/src/mintdb/mintdb_keyio.c new file mode 100644 index 000000000..321b890c3 --- /dev/null +++ b/src/mintdb/mintdb_keyio.c @@ -0,0 +1,347 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file mintdb/mintdb_keyio.c + * @brief I/O operations for the Mint's private keys + * @author Florian Dold + * @author Benedikt Mueller + * @author Sree Harsha Totakura + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_mintdb_lib.h" + + +/** + * Closure for the #signkeys_iterate_dir_iter(). + */ +struct SignkeysIterateContext +{ + + /** + * Function to call on each signing key. + */ + TALER_MINT_SignkeyIterator it; + + /** + * Closure for @e it. + */ + void *it_cls; +}; + + +/** + * Function called on each file in the directory with + * our signing keys. Parses the file and calls the + * iterator from @a cls. + * + * @param cls the `struct SignkeysIterateContext *` + * @param filename name of the file to parse + * @return #GNUNET_OK to continue, + * #GNUNET_NO to stop iteration without error, + * #GNUNET_SYSERR to stop iteration with error + */ +static int +signkeys_iterate_dir_iter (void *cls, + const char *filename) +{ + struct SignkeysIterateContext *skc = cls; + ssize_t nread; + struct TALER_MintSigningKeyValidityPSPriv issue; + + nread = GNUNET_DISK_fn_read (filename, + &issue, + sizeof (struct TALER_MintSigningKeyValidityPSPriv)); + if (nread != sizeof (struct TALER_MintSigningKeyValidityPSPriv)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid signkey file `%s': wrong size\n", + filename); + return GNUNET_OK; + } + return skc->it (skc->it_cls, + filename, + &issue); +} + + +/** + * Call @a it for each signing key found in the @a mint_base_dir. + * + * @param mint_base_dir base directory for the mint, + * the signing keys must be in the #DIR_SIGNKEYS + * subdirectory + * @param it function to call on each signing key + * @param it_cls closure for @a it + * @return number of files found (may not match + * number of keys given to @a it as malformed + * files are simply skipped), -1 on error + */ +int +TALER_MINT_signkeys_iterate (const char *mint_base_dir, + TALER_MINT_SignkeyIterator it, + void *it_cls) +{ + char *signkey_dir; + struct SignkeysIterateContext skc; + int ret; + + GNUNET_asprintf (&signkey_dir, + "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, + mint_base_dir); + skc.it = it; + skc.it_cls = it_cls; + ret = GNUNET_DISK_directory_scan (signkey_dir, + &signkeys_iterate_dir_iter, + &skc); + GNUNET_free (signkey_dir); + return ret; +} + + +/** + * Import a denomination key from the given file. + * + * @param filename the file to import the key from + * @param[OUT] dki set to the imported denomination key + * @return #GNUNET_OK upon success; + * #GNUNET_SYSERR upon failure + */ +int +TALER_MINT_read_denom_key (const char *filename, + struct TALER_DenominationKeyIssueInformation *dki) +{ + uint64_t size; + size_t offset; + void *data; + struct GNUNET_CRYPTO_rsa_PrivateKey *priv; + + if (GNUNET_OK != GNUNET_DISK_file_size (filename, + &size, + GNUNET_YES, + GNUNET_YES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Skipping inaccessable denomination key file `%s'\n", + filename); + return GNUNET_SYSERR; + } + offset = sizeof (struct TALER_DenominationKeyValidityPS); + if (size <= offset) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + data = GNUNET_malloc (size); + if (size != + GNUNET_DISK_fn_read (filename, + data, + size)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "read", + filename); + GNUNET_free (data); + return GNUNET_SYSERR; + } + if (NULL == + (priv = GNUNET_CRYPTO_rsa_private_key_decode (data + offset, + size - offset))) + { + GNUNET_free (data); + return GNUNET_SYSERR; + } + dki->denom_priv.rsa_private_key = priv; + dki->denom_pub.rsa_public_key + = GNUNET_CRYPTO_rsa_private_key_get_public (priv); + memcpy (&dki->issue, + data, + offset); + GNUNET_free (data); + return GNUNET_OK; +} + + +/** + * Exports a denomination key to the given file. + * + * @param filename the file where to write the denomination key + * @param dki the denomination key + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure. + */ +int +TALER_MINT_write_denom_key (const char *filename, + const struct TALER_DenominationKeyIssueInformation *dki) +{ + char *priv_enc; + size_t priv_enc_size; + struct GNUNET_DISK_FileHandle *fh; + ssize_t wrote; + size_t wsize; + int ret; + + fh = NULL; + priv_enc_size + = GNUNET_CRYPTO_rsa_private_key_encode (dki->denom_priv.rsa_private_key, + &priv_enc); + ret = GNUNET_SYSERR; + if (NULL == (fh = GNUNET_DISK_file_open + (filename, + GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_TRUNCATE, + GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE))) + goto cleanup; + wsize = sizeof (struct TALER_DenominationKeyValidityPS); + if (GNUNET_SYSERR == (wrote = GNUNET_DISK_file_write (fh, + &dki->issue.signature, + wsize))) + goto cleanup; + if (wrote != wsize) + goto cleanup; + if (GNUNET_SYSERR == + (wrote = GNUNET_DISK_file_write (fh, + priv_enc, + priv_enc_size))) + goto cleanup; + if (wrote != priv_enc_size) + goto cleanup; + ret = GNUNET_OK; + cleanup: + GNUNET_free_non_null (priv_enc); + if (NULL != fh) + (void) GNUNET_DISK_file_close (fh); + return ret; +} + + +/** + * Closure for #denomkeys_iterate_keydir_iter() and + * #denomkeys_iterate_topdir_iter(). + */ +struct DenomkeysIterateContext +{ + + /** + * Set to the name of the directory below the top-level directory + * during the call to #denomkeys_iterate_keydir_iter(). + */ + const char *alias; + + /** + * Function to call on each denomination key. + */ + TALER_MINT_DenomkeyIterator it; + + /** + * Closure for @e it. + */ + void *it_cls; +}; + + +/** + * Decode the denomination key in the given file @a filename and call + * the callback in @a cls with the information. + * + * @param cls the `struct DenomkeysIterateContext *` + * @param filename name of a file that should contain + * a denomination key + * @return #GNUNET_OK to continue to iterate + * #GNUNET_NO to abort iteration with success + * #GNUNET_SYSERR to abort iteration with failure + */ +static int +denomkeys_iterate_keydir_iter (void *cls, + const char *filename) +{ + struct DenomkeysIterateContext *dic = cls; + struct TALER_DenominationKeyIssueInformation issue; + + if (GNUNET_OK != + TALER_MINT_read_denom_key (filename, + &issue)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid denomkey file: '%s'\n", + filename); + return GNUNET_OK; + } + return dic->it (dic->it_cls, + dic->alias, + &issue); +} + + +/** + * Function called on each subdirectory in the #DIR_DENOMKEYS. Will + * call the #denomkeys_iterate_keydir_iter() on each file in the + * subdirectory. + * + * @param cls the `struct DenomkeysIterateContext *` + * @param filename name of the subdirectory to scan + * @return #GNUNET_OK on success, + * #GNUNET_SYSERR if we need to abort + */ +static int +denomkeys_iterate_topdir_iter (void *cls, + const char *filename) +{ + struct DenomkeysIterateContext *dic = cls; + + dic->alias = GNUNET_STRINGS_get_short_name (filename); + if (0 > GNUNET_DISK_directory_scan (filename, + &denomkeys_iterate_keydir_iter, + dic)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Call @a it for each denomination key found in the @a mint_base_dir. + * + * @param mint_base_dir base directory for the mint, + * the signing keys must be in the #DIR_DENOMKEYS + * subdirectory + * @param it function to call on each denomination key found + * @param it_cls closure for @a it + * @return -1 on error, 0 if no files were found, otherwise + * a positive number (however, even with a positive + * number it is possible that @a it was never called + * as maybe none of the files were well-formed) + */ +int +TALER_MINT_denomkeys_iterate (const char *mint_base_dir, + TALER_MINT_DenomkeyIterator it, + void *it_cls) +{ + char *dir; + struct DenomkeysIterateContext dic; + int ret; + + GNUNET_asprintf (&dir, + "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS, + mint_base_dir); + dic.it = it; + dic.it_cls = it_cls; + ret = GNUNET_DISK_directory_scan (dir, + &denomkeys_iterate_topdir_iter, + &dic); + GNUNET_free (dir); + return ret; +} + + +/* end of key_io.c */ diff --git a/src/mintdb/mintdb_plugin.c b/src/mintdb/mintdb_plugin.c new file mode 100644 index 000000000..b109ff3d1 --- /dev/null +++ b/src/mintdb/mintdb_plugin.c @@ -0,0 +1,149 @@ +/* + This file is part of TALER + Copyright (C) 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file mintdb/mintdb_plugin.c + * @brief Logic to load database plugin + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_mintdb_lib.h" +#include "taler_mintdb_plugin.h" +#include + + +/** + * Libtool search path before we started. + */ +static char *old_dlsearchpath; + + +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +struct TALER_MINTDB_Plugin * +TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *plugin_name; + char *lib_name; + struct GNUNET_CONFIGURATION_Handle *cfg_dup; + struct TALER_MINTDB_Plugin *plugin; + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", + "db", + &plugin_name)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "db"); + return NULL; + } + (void) GNUNET_asprintf (&lib_name, + "libtaler_plugin_mintdb_%s", + plugin_name); + GNUNET_free (plugin_name); + cfg_dup = GNUNET_CONFIGURATION_dup (cfg); + plugin = GNUNET_PLUGIN_load (lib_name, cfg_dup); + if (NULL != plugin) + plugin->library_name = lib_name; + else + GNUNET_free (lib_name); + GNUNET_CONFIGURATION_destroy (cfg_dup); + return plugin; +} + + +/** + * Shutdown the plugin. + * + * @param plugin the plugin to unload + */ +void +TALER_MINT_plugin_unload (struct TALER_MINTDB_Plugin *plugin) +{ + char *lib_name; + + if (NULL == plugin) + return; + lib_name = plugin->library_name; + GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, + plugin)); + GNUNET_free (lib_name); +} + + +/** + * Setup libtool paths. + */ +void __attribute__ ((constructor)) +plugin_init () +{ + int err; + const char *opath; + char *path; + char *cpath; + + err = lt_dlinit (); + if (err > 0) + { + FPRINTF (stderr, + _("Initialization of plugin mechanism failed: %s!\n"), + lt_dlerror ()); + return; + } + opath = lt_dlgetsearchpath (); + if (NULL != opath) + old_dlsearchpath = GNUNET_strdup (opath); + path = TALER_os_installation_get_path (GNUNET_OS_IPK_LIBDIR); + if (NULL != path) + { + if (NULL != opath) + { + GNUNET_asprintf (&cpath, "%s:%s", opath, path); + lt_dlsetsearchpath (cpath); + GNUNET_free (path); + GNUNET_free (cpath); + } + else + { + lt_dlsetsearchpath (path); + GNUNET_free (path); + } + } +} + + +/** + * Shutdown libtool. + */ +void __attribute__ ((destructor)) +plugin_fini () +{ + lt_dlsetsearchpath (old_dlsearchpath); + if (NULL != old_dlsearchpath) + { + GNUNET_free (old_dlsearchpath); + old_dlsearchpath = NULL; + } + lt_dlexit (); +} + + +/* end of plugin.c */ diff --git a/src/mintdb/plugin_mintdb_common.c b/src/mintdb/plugin_mintdb_common.c new file mode 100644 index 000000000..a95cf4be2 --- /dev/null +++ b/src/mintdb/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 +*/ +/** + * @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/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c new file mode 100644 index 000000000..fa2c19db0 --- /dev/null +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -0,0 +1,2356 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file plugin_mintdb_postgres.c + * @brief Low-level (statement-level) Postgres database access for the mint + * @author Florian Dold + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "taler_pq_lib.h" +#include "taler_signatures.h" +#include "taler_mintdb_plugin.h" +#include +#include + +#include "plugin_mintdb_common.c" + +#define TALER_TEMP_SCHEMA_NAME "taler_temporary" + +#define QUERY_ERR(result) \ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s\n", __FILE__, __LINE__, PQresultErrorMessage (result)) + + +#define BREAK_DB_ERR(result) do { \ + GNUNET_break(0); \ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \ + } while (0) + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + +#define SQLEXEC_(conn, sql, result) \ + do { \ + result = PQexec (conn, sql); \ + if (PGRES_COMMAND_OK != PQresultStatus (result)) \ + { \ + BREAK_DB_ERR (result); \ + PQclear (result); result = NULL; \ + goto SQLEXEC_fail; \ + } \ + PQclear (result); result = NULL; \ + } while (0) + +/** + * This the length of the currency strings (without 0-termination) we use. Note + * that we need to use this at the DB layer instead of TALER_CURRENCY_LEN as the + * DB only needs to store 3 bytes instead of 8 bytes. + */ +#define TALER_PQ_CURRENCY_LEN 3 + + +/** + * Handle for a database session (per-thread, for transactions). + */ +struct TALER_MINTDB_Session +{ + /** + * Postgres connection handle. + */ + PGconn *conn; +}; + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct PostgresClosure +{ + + /** + * Thread-local database connection. + * Contains a pointer to PGconn or NULL. + */ + pthread_key_t db_conn_threadlocal; + + /** + * Database connection string, as read from + * the configuration. + */ + char *connection_cfg_str; +}; + + + +/** + * Set the given connection to use a temporary schema + * + * @param db the database connection + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon error + */ +static int +set_temporary_schema (PGconn *db) +{ + PGresult *result; + + SQLEXEC_(db, + "CREATE SCHEMA IF NOT EXISTS " TALER_TEMP_SCHEMA_NAME ";" + "SET search_path to " TALER_TEMP_SCHEMA_NAME ";", + result); + return GNUNET_OK; + SQLEXEC_fail: + return GNUNET_SYSERR; +} + + +/** + * Drop the temporary taler schema. This is only useful for testcases + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +static int +postgres_drop_temporary (void *cls, + struct TALER_MINTDB_Session *session) +{ + PGresult *result; + + SQLEXEC_ (session->conn, + "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;", + result); + return GNUNET_OK; + SQLEXEC_fail: + return GNUNET_SYSERR; +} + + +/** + * Create the necessary tables if they are not present + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param temporary should we use a temporary schema + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +static int +postgres_create_tables (void *cls, + int temporary) +{ + struct PostgresClosure *pc = cls; + PGresult *result; + PGconn *conn; + + result = NULL; + conn = PQconnectdb (pc->connection_cfg_str); + if (CONNECTION_OK != PQstatus (conn)) + { + TALER_LOG_ERROR ("Database connection failed: %s\n", + PQerrorMessage (conn)); + GNUNET_break (0); + return GNUNET_SYSERR; + } + if ( (GNUNET_YES == temporary) && + (GNUNET_SYSERR == set_temporary_schema (conn))) + { + PQfinish (conn); + return GNUNET_SYSERR; + } +#define SQLEXEC(sql) SQLEXEC_(conn, sql, result); + /* reserves table is for summarization of a reserve. It is updated when new + funds are added and existing funds are withdrawn */ + SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves" + "(" + " reserve_pub BYTEA PRIMARY KEY" + ",current_balance_value INT8 NOT NULL" + ",current_balance_fraction INT4 NOT NULL" + ",balance_currency VARCHAR(4) NOT NULL" + ",expiration_date INT8 NOT NULL" + ")"); + /* reserves_in table collects the transactions which transfer funds into the + reserve. The amount and expiration date for the corresponding reserve are + updated when new transfer funds are added. The rows of this table + correspond to each incoming transaction. */ + SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in" + "(" + " reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE" + ",balance_value INT8 NOT NULL" + ",balance_fraction INT4 NOT NULL" + ",balance_currency VARCHAR(4) NOT NULL" + ",expiration_date INT8 NOT NULL" + ");"); + /* Create an index on the foreign key as it is not created automatically by PSQL */ + SQLEXEC ("CREATE INDEX reserves_in_reserve_pub_index" + " ON reserves_in (reserve_pub);"); + SQLEXEC ("CREATE TABLE IF NOT EXISTS collectable_blindcoins" + "(" + "blind_ev BYTEA PRIMARY KEY" + ",denom_pub BYTEA NOT NULL" /* FIXME: Make this a foreign key? */ + ",denom_sig BYTEA NOT NULL" + ",reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE" + ",reserve_sig BYTEA NOT NULL" + ");"); + SQLEXEC ("CREATE INDEX collectable_blindcoins_reserve_pub_index ON" + " collectable_blindcoins (reserve_pub)"); + SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins " + "(" + " coin_pub BYTEA NOT NULL PRIMARY KEY" + ",denom_pub BYTEA NOT NULL" + ",denom_sig BYTEA NOT NULL" + ",expended_value INT8 NOT NULL" + ",expended_fraction INT4 NOT NULL" + ",expended_currency VARCHAR(4) NOT NULL" + ",refresh_session_hash BYTEA" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions " + "(" + " session_hash BYTEA PRIMARY KEY CHECK (length(session_hash) = 32)" + ",session_melt_sig BYTEA" + ",session_commit_sig BYTEA" + ",noreveal_index INT2 NOT NULL" + // non-zero if all reveals were ok + // and the new coin signatures are ready + ",reveal_ok BOOLEAN NOT NULL DEFAULT false" + ") "); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order " + "( " + " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)" + ",newcoin_index INT2 NOT NULL " + ",denom_pub BYTEA NOT NULL " + ",PRIMARY KEY (session_hash, newcoin_index)" + ") "); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link" + "(" + " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)" + ",transfer_pub BYTEA NOT NULL" + ",link_secret_enc BYTEA NOT NULL" + // index of the old coin in the customer's request + ",oldcoin_index INT2 NOT NULL" + // index for cut and choose, + // ranges from 0 to #TALER_CNC_KAPPA-1 + ",cnc_index INT2 NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin" + "(" + " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) " + ",link_vector_enc BYTEA NOT NULL" + // index of the new coin in the customer's request + ",newcoin_index INT2 NOT NULL" + // index for cut and choose, + ",cnc_index INT2 NOT NULL" + ",coin_ev BYTEA NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt" + "(" + " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) " + ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) " + ",denom_pub BYTEA NOT NULL " + ",oldcoin_index INT2 NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_collectable" + "(" + " session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) " + ",ev_sig BYTEA NOT NULL" + ",newcoin_index INT2 NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS deposits " + "( " + " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)" + ",denom_pub BYTEA NOT NULL" /* FIXME: Link this as a foreign key? */ + ",denom_sig BYTEA NOT NULL" + ",transaction_id INT8 NOT NULL" + ",amount_currency VARCHAR(4) NOT NULL" + ",amount_value INT8 NOT NULL" + ",amount_fraction INT4 NOT NULL" + ",merchant_pub BYTEA NOT NULL CHECK (length(merchant_pub)=32)" + ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)" + ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)" + ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)" + ",wire TEXT NOT NULL" + ")"); +#undef SQLEXEC + + PQfinish (conn); + return GNUNET_OK; + + SQLEXEC_fail: + PQfinish (conn); + return GNUNET_SYSERR; +} + + +/** + * Setup prepared statements. + * + * @param db_conn connection handle to initialize + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +static int +postgres_prepare (PGconn *db_conn) +{ + PGresult *result; + +#define PREPARE(name, sql, ...) \ + do { \ + result = PQprepare (db_conn, name, sql, __VA_ARGS__); \ + if (PGRES_COMMAND_OK != PQresultStatus (result)) \ + { \ + BREAK_DB_ERR (result); \ + PQclear (result); result = NULL; \ + return GNUNET_SYSERR; \ + } \ + PQclear (result); result = NULL; \ + } while (0); + + PREPARE ("get_reserve", + "SELECT " + "current_balance_value" + ",current_balance_fraction" + ",balance_currency " + ",expiration_date " + "FROM reserves " + "WHERE reserve_pub=$1 " + "LIMIT 1; ", + 1, NULL); + PREPARE ("create_reserve", + "INSERT INTO reserves (" + " reserve_pub," + " current_balance_value," + " current_balance_fraction," + " balance_currency," + " expiration_date) VALUES (" + "$1, $2, $3, $4, $5);", + 5, NULL); + PREPARE ("update_reserve", + "UPDATE reserves " + "SET" + " current_balance_value=$2 " + ",current_balance_fraction=$3 " + ",expiration_date=$4 " + "WHERE reserve_pub=$1 ", + 4, NULL); + PREPARE ("create_reserves_in_transaction", + "INSERT INTO reserves_in (" + " reserve_pub," + " balance_value," + " balance_fraction," + " balance_currency," + " expiration_date) VALUES (" + " $1, $2, $3, $4, $5);", + 5, NULL); + PREPARE ("get_reserves_in_transactions", + "SELECT" + " balance_value" + ",balance_fraction" + ",balance_currency" + ",expiration_date" + " FROM reserves_in WHERE reserve_pub=$1", + 1, NULL); + PREPARE ("insert_collectable_blindcoin", + "INSERT INTO collectable_blindcoins ( " + " blind_ev" + ",denom_pub, denom_sig" + ",reserve_pub, reserve_sig) " + "VALUES ($1, $2, $3, $4, $5)", + 5, NULL); + PREPARE ("get_collectable_blindcoin", + "SELECT " + " denom_pub, denom_sig" + ",reserve_sig, reserve_pub " + "FROM collectable_blindcoins " + "WHERE blind_ev = $1", + 1, NULL); + PREPARE ("get_reserves_blindcoins", + "select" + " blind_ev" + ",denom_pub, denom_sig" + ",reserve_sig" + " FROM collectable_blindcoins" + " WHERE reserve_pub=$1;", + 1, NULL); + + /* FIXME: does it make sense to store these computed values in the DB? */ +#if 0 + PREPARE ("get_refresh_session", + "SELECT " + " (SELECT count(*) FROM refresh_melt WHERE session_hash = $1)::INT2 as num_oldcoins " + ",(SELECT count(*) FROM refresh_blind_session_keys " + " WHERE session_hash = $1 and cnc_index = 0)::INT2 as num_newcoins " + ",(SELECT count(*) FROM refresh_blind_session_keys " + " WHERE session_hash = $1 and newcoin_index = 0)::INT2 as kappa " + ",noreveal_index" + ",session_commit_sig " + ",reveal_ok " + "FROM refresh_sessions " + "WHERE session_hash = $1", + 1, NULL); +#endif + + PREPARE ("get_known_coin", + "SELECT " + " coin_pub, denom_pub, denom_sig " + ",expended_value, expended_fraction, expended_currency " + ",refresh_session_hash " + "FROM known_coins " + "WHERE coin_pub = $1", + 1, NULL); + PREPARE ("update_known_coin", + "UPDATE known_coins " + "SET " + " denom_pub = $2 " + ",denom_sig = $3 " + ",expended_value = $4 " + ",expended_fraction = $5 " + ",expended_currency = $6 " + ",refresh_session_hash = $7 " + "WHERE " + " coin_pub = $1 ", + 7, NULL); + PREPARE ("insert_known_coin", + "INSERT INTO known_coins (" + " coin_pub" + ",denom_pub" + ",denom_sig" + ",expended_value" + ",expended_fraction" + ",expended_currency" + ",refresh_session_hash" + ")" + "VALUES ($1,$2,$3,$4,$5,$6,$7)", + 7, NULL); + PREPARE ("get_refresh_commit_link", + "SELECT " + " transfer_pub " + ",link_secret_enc " + "FROM refresh_commit_link " + "WHERE session_hash = $1 AND cnc_index = $2 AND oldcoin_index = $3", + 3, NULL); + PREPARE ("get_refresh_commit_coin", + "SELECT " + " link_vector_enc " + ",coin_ev " + "FROM refresh_commit_coin " + "WHERE session_hash = $1 AND cnc_index = $2 AND newcoin_index = $3", + 3, NULL); + PREPARE ("insert_refresh_order", + "INSERT INTO refresh_order ( " + " newcoin_index " + ",session_hash " + ",denom_pub " + ") " + "VALUES ($1, $2, $3) ", + 3, NULL); + PREPARE ("insert_refresh_melt", + "INSERT INTO refresh_melt ( " + " session_hash " + ",oldcoin_index " + ",coin_pub " + ",denom_pub " + ") " + "VALUES ($1, $2, $3, $4) ", + 3, NULL); + PREPARE ("get_refresh_order", + "SELECT denom_pub " + "FROM refresh_order " + "WHERE session_hash = $1 AND newcoin_index = $2", + 2, NULL); + PREPARE ("get_refresh_collectable", + "SELECT ev_sig " + "FROM refresh_collectable " + "WHERE session_hash = $1 AND newcoin_index = $2", + 2, NULL); + PREPARE ("get_refresh_melt", + "SELECT coin_pub " + "FROM refresh_melt " + "WHERE session_hash = $1 AND oldcoin_index = $2", + 2, NULL); + PREPARE ("insert_refresh_session", + "INSERT INTO refresh_sessions ( " + " session_hash " + ",noreveal_index " + ") " + "VALUES ($1, $2) ", + 2, NULL); + PREPARE ("insert_refresh_commit_link", + "INSERT INTO refresh_commit_link ( " + " session_hash " + ",transfer_pub " + ",cnc_index " + ",oldcoin_index " + ",link_secret_enc " + ") " + "VALUES ($1, $2, $3, $4, $5) ", + 5, NULL); + PREPARE ("insert_refresh_commit_coin", + "INSERT INTO refresh_commit_coin ( " + " session_hash " + ",coin_ev " + ",cnc_index " + ",newcoin_index " + ",link_vector_enc " + ") " + "VALUES ($1, $2, $3, $4, $5) ", + 5, NULL); + PREPARE ("insert_refresh_collectable", + "INSERT INTO refresh_collectable ( " + " session_hash " + ",newcoin_index " + ",ev_sig " + ") " + "VALUES ($1, $2, $3) ", + 3, NULL); + PREPARE ("set_reveal_ok", + "UPDATE refresh_sessions " + "SET reveal_ok = TRUE " + "WHERE session_hash = $1 ", + 1, NULL); + PREPARE ("get_link", + "SELECT link_vector_enc, ro.denom_pub, ev_sig " + "FROM refresh_melt rm " + " JOIN refresh_order ro USING (session_hash) " + " JOIN refresh_commit_coin rcc USING (session_hash) " + " JOIN refresh_sessions rs USING (session_hash) " + " JOIN refresh_collectable rc USING (session_hash) " + "WHERE rm.coin_pub = $1 " + "AND ro.newcoin_index = rcc.newcoin_index " + "AND ro.newcoin_index = rc.newcoin_index " + "AND rcc.cnc_index = rs.noreveal_index % ( " + " SELECT count(*) FROM refresh_commit_coin rcc2 " + " WHERE rcc2.newcoin_index = 0 AND rcc2.session_hash = rs.session_hash " + " ) ", + 1, NULL); + PREPARE ("get_transfer", + "SELECT transfer_pub, link_secret_enc " + "FROM refresh_melt rm " + " JOIN refresh_commit_link rcl USING (session_hash) " + " JOIN refresh_sessions rs USING (session_hash) " + "WHERE rm.coin_pub = $1 " + "AND rm.oldcoin_index = rcl.oldcoin_index " + "AND rcl.cnc_index = rs.noreveal_index % ( " + " SELECT count(*) FROM refresh_commit_coin rcc2 " + " WHERE newcoin_index = 0 AND rcc2.session_hash = rm.session_hash " + " ) ", + 1, NULL); + PREPARE ("insert_deposit", + "INSERT INTO deposits (" + "coin_pub," + "denom_pub," + "denom_sig," + "transaction_id," + "amount_value," + "amount_fraction," + "amount_currency," + "merchant_pub," + "h_contract," + "h_wire," + "coin_sig," + "wire" + ") VALUES (" + "$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12" + ")", + 12, NULL); + PREPARE ("get_deposit", + "SELECT " + "coin_pub," + "denom_pub," + "transaction_id," + "amount_value," + "amount_fraction," + "amount_currency," + "merchant_pub," + "h_contract," + "h_wire," + "coin_sig" + " FROM deposits WHERE (" + "(coin_pub = $1) AND" + "(transaction_id = $2) AND" + "(merchant_pub = $3)" + ")", + 3, NULL); + return GNUNET_OK; +#undef PREPARE +} + + +/** + * Close thread-local database connection when a thread is destroyed. + * + * @param closure we get from pthreads (the db handle) + */ +static void +db_conn_destroy (void *cls) +{ + PGconn *db_conn = cls; + + if (NULL != db_conn) + PQfinish (db_conn); +} + + +/** + * Get the thread-local database-handle. + * Connect to the db if the connection does not exist yet. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the + * database default one + * @return the database connection, or NULL on error + */ +static struct TALER_MINTDB_Session * +postgres_get_session (void *cls, + int temporary) +{ + struct PostgresClosure *pc = cls; + PGconn *db_conn; + struct TALER_MINTDB_Session *session; + + if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal))) + return session; + db_conn = PQconnectdb (pc->connection_cfg_str); + if (CONNECTION_OK != + PQstatus (db_conn)) + { + TALER_LOG_ERROR ("Database connection failed: %s\n", + PQerrorMessage (db_conn)); + GNUNET_break (0); + return NULL; + } + if ((GNUNET_YES == temporary) + && (GNUNET_SYSERR == set_temporary_schema(db_conn))) + { + GNUNET_break (0); + return NULL; + } + if (GNUNET_OK != + postgres_prepare (db_conn)) + { + GNUNET_break (0); + return NULL; + } + session = GNUNET_new (struct TALER_MINTDB_Session); + session->conn = db_conn; + if (0 != pthread_setspecific (pc->db_conn_threadlocal, + session)) + { + GNUNET_break (0); + // FIXME: close db_conn! + GNUNET_free (session); + return NULL; + } + return session; +} + + +/** + * Start a transaction. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection + * @return #GNUNET_OK on success + */ +static int +postgres_start (void *cls, + struct TALER_MINTDB_Session *session) +{ + PGresult *result; + + result = PQexec (session->conn, + "BEGIN"); + if (PGRES_COMMAND_OK != + PQresultStatus (result)) + { + TALER_LOG_ERROR ("Failed to start transaction: %s\n", + PQresultErrorMessage (result)); + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Roll back the current transaction of a database connection. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection + * @return #GNUNET_OK on success + */ +static void +postgres_rollback (void *cls, + struct TALER_MINTDB_Session *session) +{ + PGresult *result; + + result = PQexec (session->conn, + "ROLLBACK"); + GNUNET_break (PGRES_COMMAND_OK == + PQresultStatus (result)); + PQclear (result); +} + + +/** + * Commit the current transaction of a database connection. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection + * @return #GNUNET_OK on success + */ +static int +postgres_commit (void *cls, + struct TALER_MINTDB_Session *session) +{ + PGresult *result; + + result = PQexec (session->conn, + "COMMIT"); + if (PGRES_COMMAND_OK != + PQresultStatus (result)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Get the summary of a reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection handle + * @param reserve the reserve data. The public key of the reserve should be set + * in this structure; it is used to query the database. The balance + * and expiration are then filled accordingly. + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +static int +postgres_reserve_get (void *cls, + struct TALER_MINTDB_Session *session, + struct Reserve *reserve) +{ + PGresult *result; + uint64_t expiration_date_nbo; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(&reserve->pub), + TALER_PQ_QUERY_PARAM_END + }; + + result = TALER_PQ_exec_prepared (session->conn, + "get_reserve", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + QUERY_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC("expiration_date", &expiration_date_nbo), + TALER_PQ_RESULT_SPEC_END + }; + EXITIF (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)); + EXITIF (GNUNET_OK != + TALER_PQ_extract_amount (result, 0, + "current_balance_value", + "current_balance_fraction", + "balance_currency", + &reserve->balance)); + reserve->expiry.abs_value_us = GNUNET_ntohll (expiration_date_nbo); + PQclear (result); + return GNUNET_OK; + + EXITIF_exit: + PQclear (result); + return GNUNET_SYSERR; +} + + +/** + * Updates a reserve with the data from the given reserve structure. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection + * @param reserve the reserve structure whose data will be used to update the + * corresponding record in the database. + * @return #GNUNET_OK upon successful update; #GNUNET_SYSERR upon any error + */ +static int +postgres_reserves_update (void *cls, + struct TALER_MINTDB_Session *session, + struct Reserve *reserve) +{ + PGresult *result; + struct TALER_AmountNBO balance_nbo; + struct GNUNET_TIME_AbsoluteNBO expiry_nbo; + int ret; + + if (NULL == reserve) + return GNUNET_SYSERR; + ret = GNUNET_OK; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (&reserve->pub), + TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value), + TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction), + TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo), + TALER_PQ_QUERY_PARAM_END + }; + TALER_amount_hton (&balance_nbo, + &reserve->balance); + expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry); + result = TALER_PQ_exec_prepared (session->conn, + "update_reserve", + params); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + QUERY_ERR (result); + ret = GNUNET_SYSERR; + } + PQclear (result); + return ret; +} + + +/** + * Insert a incoming transaction into reserves. New reserves are also created + * through this function. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection handle + * @param reserve the reserve structure. The public key of the reserve should + * be set here. Upon successful execution of this function, the + * balance and expiration of the reserve will be updated. + * @param balance the amount that has to be added to the reserve + * @param expiry the new expiration time for the reserve + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures + */ +static int +postgres_reserves_in_insert (void *cls, + struct TALER_MINTDB_Session *session, + struct Reserve *reserve, + const struct TALER_Amount *balance, + const struct GNUNET_TIME_Absolute expiry) +{ + struct TALER_AmountNBO balance_nbo; + struct GNUNET_TIME_AbsoluteNBO expiry_nbo; + PGresult *result; + int reserve_exists; + + result = NULL; + if (NULL == reserve) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != postgres_start (cls, + session)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + reserve_exists = postgres_reserve_get (cls, + session, + reserve); + if (GNUNET_SYSERR == reserve_exists) + { + postgres_rollback (cls, + session); + return GNUNET_SYSERR; + } + TALER_amount_hton (&balance_nbo, + balance); + expiry_nbo = GNUNET_TIME_absolute_hton (expiry); + if (GNUNET_NO == reserve_exists) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Reserve does not exist; creating a new one\n"); + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (&reserve->pub), + TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value), + TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction), + TALER_PQ_QUERY_PARAM_PTR_SIZED (balance_nbo.currency, + TALER_PQ_CURRENCY_LEN), + TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, + "create_reserve", + params); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + QUERY_ERR (result); + goto rollback; + } + } + if (NULL != result) + PQclear (result); + result = NULL; + /* create new incoming transaction */ + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (&reserve->pub), + TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.value), + TALER_PQ_QUERY_PARAM_PTR (&balance_nbo.fraction), + TALER_PQ_QUERY_PARAM_PTR_SIZED (&balance_nbo.currency, + TALER_PQ_CURRENCY_LEN), + TALER_PQ_QUERY_PARAM_PTR (&expiry_nbo), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, + "create_reserves_in_transaction", + params); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + QUERY_ERR (result); + goto rollback; + } + PQclear (result); + result = NULL; + if (GNUNET_NO == reserve_exists) + { + if (GNUNET_OK != postgres_commit (cls, + session)) + return GNUNET_SYSERR; + reserve->balance = *balance; + reserve->expiry = expiry; + return GNUNET_OK; + } + /* Update reserve */ + struct Reserve updated_reserve; + updated_reserve.pub = reserve->pub; + + if (GNUNET_OK != + TALER_amount_add (&updated_reserve.balance, + &reserve->balance, + balance)) + { + return GNUNET_SYSERR; + } + updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry); + if (GNUNET_OK != postgres_reserves_update (cls, + session, + &updated_reserve)) + goto rollback; + if (GNUNET_OK != postgres_commit (cls, + session)) + return GNUNET_SYSERR; + reserve->balance = updated_reserve.balance; + reserve->expiry = updated_reserve.expiry; + return GNUNET_OK; + + rollback: + PQclear (result); + postgres_rollback (cls, + session); + return GNUNET_SYSERR; +} + + +/** + * Locate the response for a /withdraw request under the + * key of the hash of the blinded message. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use + * @param h_blind hash of the blinded message + * @param collectable corresponding collectable coin (blind signature) + * if a coin is found + * @return #GNUNET_SYSERR on internal error + * #GNUNET_NO if the collectable was not found + * #GNUNET_YES on success + */ +static int +postgres_get_collectable_blindcoin (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *h_blind, + struct CollectableBlindcoin *collectable) +{ + PGresult *result; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (h_blind), + TALER_PQ_QUERY_PARAM_END + }; + struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; + struct GNUNET_CRYPTO_rsa_Signature *denom_sig; + char *denom_pub_enc; + char *denom_sig_enc; + size_t denom_pub_enc_size; + size_t denom_sig_enc_size; + int ret; + + ret = GNUNET_SYSERR; + denom_pub = NULL; + denom_pub_enc = NULL; + denom_sig_enc = NULL; + result = TALER_PQ_exec_prepared (session->conn, + "get_collectable_blindcoin", + params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + QUERY_ERR (result); + goto cleanup; + } + if (0 == PQntuples (result)) + { + ret = GNUNET_NO; + goto cleanup; + } + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC_VAR("denom_pub", &denom_pub_enc, &denom_pub_enc_size), + TALER_PQ_RESULT_SPEC_VAR("denom_sig", &denom_sig_enc, &denom_sig_enc_size), + TALER_PQ_RESULT_SPEC("reserve_sig", &collectable->reserve_sig), + TALER_PQ_RESULT_SPEC("reserve_pub", &collectable->reserve_pub), + TALER_PQ_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) + { + GNUNET_break (0); + goto cleanup; + } + denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc, + denom_pub_enc_size); + denom_sig = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc, + denom_sig_enc_size); + if ((NULL == denom_pub) || (NULL == denom_sig)) + { + GNUNET_break (0); + goto cleanup; + } + collectable->denom_pub.rsa_public_key = denom_pub; + collectable->sig.rsa_signature = denom_sig; + ret = GNUNET_YES; + + cleanup: + PQclear (result); + GNUNET_free_non_null (denom_pub_enc); + GNUNET_free_non_null (denom_sig_enc); + if (GNUNET_YES != ret) + { if (NULL != denom_pub) + GNUNET_CRYPTO_rsa_public_key_free (denom_pub); + if (NULL != denom_sig) + GNUNET_CRYPTO_rsa_signature_free (denom_sig); + } + return ret; +} + + +/** + * Store collectable bit coin under the corresponding + * hash of the blinded message. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use + * @param h_blind hash of the blinded message + * @param withdraw amount by which the reserve will be withdrawn with this + * transaction + * @param collectable corresponding collectable coin (blind signature) + * if a coin is found + * @return #GNUNET_SYSERR on internal error + * #GNUNET_NO if the collectable was not found + * #GNUNET_YES on success + */ +static int +postgres_insert_collectable_blindcoin (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *h_blind, + struct TALER_Amount withdraw, + const struct CollectableBlindcoin *collectable) +{ + PGresult *result; + struct Reserve reserve; + char *denom_pub_enc = NULL; + char *denom_sig_enc = NULL; + size_t denom_pub_enc_size; + size_t denom_sig_enc_size; + int ret; + + ret = GNUNET_SYSERR; + denom_pub_enc_size = + GNUNET_CRYPTO_rsa_public_key_encode (collectable->denom_pub.rsa_public_key, + &denom_pub_enc); + denom_sig_enc_size = + GNUNET_CRYPTO_rsa_signature_encode (collectable->sig.rsa_signature, + &denom_sig_enc); + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (h_blind), + TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size - 1), + TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size - 1), /* DB doesn't like the trailing \0 */ + TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_pub), + TALER_PQ_QUERY_PARAM_PTR (&collectable->reserve_sig), + TALER_PQ_QUERY_PARAM_END + }; + if (GNUNET_OK != postgres_start (cls, + session)) + goto cleanup; + result = TALER_PQ_exec_prepared (session->conn, + "insert_collectable_blindcoin", + params); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + QUERY_ERR (result); + goto rollback; + } + reserve.pub = collectable->reserve_pub; + if (GNUNET_OK != postgres_reserve_get (cls, + session, + &reserve)) + goto rollback; + if (GNUNET_SYSERR == + TALER_amount_subtract (&reserve.balance, + &reserve.balance, + &withdraw)) + goto rollback; + if (GNUNET_OK != postgres_reserves_update (cls, + session, + &reserve)) + goto rollback; + if (GNUNET_OK == postgres_commit (cls, + session)) + { + ret = GNUNET_OK; + goto cleanup; + } + + rollback: + postgres_rollback (cls, + session); + cleanup: + PQclear (result); + GNUNET_free_non_null (denom_pub_enc); + GNUNET_free_non_null (denom_sig_enc); + return ret; +} + + +/** + * Get all of the transaction history associated with the specified + * reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session connection to use + * @param reserve_pub public key of the reserve + * @return known transaction history (NULL if reserve is unknown) + */ +static struct ReserveHistory * +postgres_get_reserve_history (void *cls, + struct TALER_MINTDB_Session *session, + const struct TALER_ReservePublicKeyP *reserve_pub) +{ + PGresult *result; + struct ReserveHistory *rh; + struct ReserveHistory *rh_head; + int rows; + int ret; + + result = NULL; + rh = NULL; + rh_head = NULL; + ret = GNUNET_SYSERR; + { + struct BankTransfer *bt; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (reserve_pub), + TALER_PQ_QUERY_PARAM_END + }; + + result = TALER_PQ_exec_prepared (session->conn, + "get_reserves_in_transactions", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + QUERY_ERR (result); + goto cleanup; + } + if (0 == (rows = PQntuples (result))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asked to fetch history for an unknown reserve.\n"); + goto cleanup; + } + while (0 < rows) + { + bt = GNUNET_new (struct BankTransfer); + if (GNUNET_OK != TALER_PQ_extract_amount (result, + --rows, + "balance_value", + "balance_fraction", + "balance_currency", + &bt->amount)) + { + GNUNET_free (bt); + GNUNET_break (0); + goto cleanup; + } + bt->reserve_pub = *reserve_pub; + if (NULL != rh_head) + { + rh_head->next = GNUNET_new (struct ReserveHistory); + rh_head = rh_head->next; + } + else + { + rh_head = GNUNET_new (struct ReserveHistory); + rh = rh_head; + } + rh_head->type = TALER_MINT_DB_RO_BANK_TO_MINT; + rh_head->details.bank = bt; + } + } + PQclear (result); + result = NULL; + { + struct GNUNET_HashCode blind_ev; + struct TALER_ReserveSignatureP reserve_sig; + struct CollectableBlindcoin *cbc; + char *denom_pub_enc; + char *denom_sig_enc; + size_t denom_pub_enc_size; + size_t denom_sig_enc_size; + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (reserve_pub), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, + "get_reserves_blindcoins", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + QUERY_ERR (result); + goto cleanup; + } + if (0 == (rows = PQntuples (result))) + { + ret = GNUNET_OK; /* Its OK if there are no withdrawls yet */ + goto cleanup; + } + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC ("blind_ev", &blind_ev), + TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &denom_pub_enc, &denom_pub_enc_size), + TALER_PQ_RESULT_SPEC_VAR ("denom_sig", &denom_sig_enc, &denom_sig_enc_size), + TALER_PQ_RESULT_SPEC ("reserve_sig", &reserve_sig), + TALER_PQ_RESULT_SPEC_END + }; + GNUNET_assert (NULL != rh); + GNUNET_assert (NULL != rh_head); + GNUNET_assert (NULL == rh_head->next); + while (0 < rows) + { + if (GNUNET_YES != TALER_PQ_extract_result (result, rs, --rows)) + { + GNUNET_break (0); + goto cleanup; + } + cbc = GNUNET_new (struct CollectableBlindcoin); + cbc->sig.rsa_signature + = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_enc, + denom_sig_enc_size); + GNUNET_free (denom_sig_enc); + denom_sig_enc = NULL; + cbc->denom_pub.rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_decode (denom_pub_enc, + denom_pub_enc_size); + GNUNET_free (denom_pub_enc); + denom_pub_enc = NULL; + if ( (NULL == cbc->sig.rsa_signature) || + (NULL == cbc->denom_pub.rsa_public_key) ) + { + if (NULL != cbc->sig.rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (cbc->sig.rsa_signature); + if (NULL != cbc->denom_pub.rsa_public_key) + GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub.rsa_public_key); + GNUNET_free (cbc); + GNUNET_break (0); + goto cleanup; + } + (void) memcpy (&cbc->h_coin_envelope, &blind_ev, sizeof (blind_ev)); + (void) memcpy (&cbc->reserve_pub, reserve_pub, sizeof (cbc->reserve_pub)); + (void) memcpy (&cbc->reserve_sig, &reserve_sig, sizeof (cbc->reserve_sig)); + rh_head->next = GNUNET_new (struct ReserveHistory); + rh_head = rh_head->next; + rh_head->type = TALER_MINT_DB_RO_WITHDRAW_COIN; + rh_head->details.withdraw = cbc; + } + } + ret = GNUNET_OK; + + cleanup: + if (NULL != result) + PQclear (result); + if (GNUNET_SYSERR == ret) + { + common_free_reserve_history (cls, + rh); + rh = NULL; + } + return rh; +} + + +/** + * Check if we have the specified deposit already in the database. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param deposit deposit to search for + * @return #GNUNET_YES if we know this operation, + * #GNUNET_NO if this deposit is unknown to us + */ +static int +postgres_have_deposit (void *cls, + struct TALER_MINTDB_Session *session, + const struct Deposit *deposit) +{ + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub), + TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id), + TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub), + TALER_PQ_QUERY_PARAM_END + }; + PGresult *result; + int ret; + + ret = GNUNET_SYSERR; + result = TALER_PQ_exec_prepared (session->conn, + "get_deposit", + params); + if (PGRES_TUPLES_OK != + PQresultStatus (result)) + { + BREAK_DB_ERR (result); + goto cleanup; + } + + if (0 == PQntuples (result)) + { + ret = GNUNET_NO; + goto cleanup; + } + ret = GNUNET_YES; + + cleanup: + PQclear (result); + return ret; +} + + +/** + * Insert information about deposited coin into the + * database. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session connection to the database + * @param deposit deposit information to store + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +postgres_insert_deposit (void *cls, + struct TALER_MINTDB_Session *session, + const struct Deposit *deposit) +{ + char *denom_pub_enc; + char *denom_sig_enc; + char *json_wire_enc; + PGresult *result; + struct TALER_AmountNBO amount_nbo; + size_t denom_pub_enc_size; + size_t denom_sig_enc_size; + int ret; + + ret = GNUNET_SYSERR; + denom_pub_enc_size = + GNUNET_CRYPTO_rsa_public_key_encode (deposit->coin.denom_pub.rsa_public_key, + &denom_pub_enc); + denom_sig_enc_size = + GNUNET_CRYPTO_rsa_signature_encode (deposit->coin.denom_sig.rsa_signature, + &denom_sig_enc); + json_wire_enc = json_dumps (deposit->wire, JSON_COMPACT); + TALER_amount_hton (&amount_nbo, + &deposit->amount_with_fee); + struct TALER_PQ_QueryParam params[]= { + TALER_PQ_QUERY_PARAM_PTR (&deposit->coin.coin_pub), + TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_pub_enc, denom_pub_enc_size), + TALER_PQ_QUERY_PARAM_PTR_SIZED (denom_sig_enc, denom_sig_enc_size), + TALER_PQ_QUERY_PARAM_PTR (&deposit->transaction_id), + TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.value), + TALER_PQ_QUERY_PARAM_PTR (&amount_nbo.fraction), + TALER_PQ_QUERY_PARAM_PTR_SIZED (amount_nbo.currency, + TALER_CURRENCY_LEN - 1), + TALER_PQ_QUERY_PARAM_PTR (&deposit->merchant_pub), + TALER_PQ_QUERY_PARAM_PTR (&deposit->h_contract), + TALER_PQ_QUERY_PARAM_PTR (&deposit->h_wire), + TALER_PQ_QUERY_PARAM_PTR (&deposit->csig), + TALER_PQ_QUERY_PARAM_PTR_SIZED (json_wire_enc, + strlen (json_wire_enc)), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, "insert_deposit", params); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + goto cleanup; + } + ret = GNUNET_OK; + + cleanup: + PQclear (result); + GNUNET_free_non_null (denom_pub_enc); + GNUNET_free_non_null (denom_sig_enc); + GNUNET_free_non_null (json_wire_enc); + return ret; +} + + +/** + * Lookup refresh session data under the given @a session_hash. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database handle to use + * @param session_hash hash over the melt to use to locate the session + * @param refresh_session[OUT] where to store the result + * @return #GNUNET_YES on success, + * #GNUNET_NO if not found, + * #GNUNET_SYSERR on DB failure + */ +static int +postgres_get_refresh_session (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + struct RefreshSession *refresh_session) +{ + // FIXME: check logic! + int res; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_END + }; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "get_refresh_session", + params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Query failed: %s\n", + PQresultErrorMessage (result)); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + return GNUNET_NO; + + GNUNET_assert (1 == PQntuples (result)); + + /* We're done if the caller is only interested in + * whether the session exists or not */ + + if (NULL == refresh_session) + return GNUNET_YES; + + memset (session, 0, sizeof (struct RefreshSession)); + + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins), + TALER_PQ_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins), + TALER_PQ_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index), + TALER_PQ_RESULT_SPEC_END + }; + + res = TALER_PQ_extract_result (result, rs, 0); + + if (GNUNET_OK != res) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + refresh_session->num_oldcoins = ntohs (refresh_session->num_oldcoins); + refresh_session->num_newcoins = ntohs (refresh_session->num_newcoins); + refresh_session->noreveal_index = ntohs (refresh_session->noreveal_index); + + PQclear (result); + return GNUNET_YES; +} + + +/** + * Store new refresh session data under the given @a session_hash. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database handle to use + * @param session_hash hash over the melt to use to locate the session + * @param refresh_session session data to store + * @return #GNUNET_YES on success, + * #GNUNET_SYSERR on DB failure + */ +static int +postgres_create_refresh_session (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + const struct RefreshSession *refresh_session) +{ + // FIXME: actually store session data! + uint16_t noreveal_index; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR(&noreveal_index), + TALER_PQ_QUERY_PARAM_END + }; + + noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15); + noreveal_index = htonl (noreveal_index); + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "insert_refresh_session", + params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Store the given /refresh/melt request in the database. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param oldcoin_index index of the coin to store + * @param melt melt operation details to store; includes + * the session hash of the melt + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ +static int +postgres_insert_refresh_melt (void *cls, + struct TALER_MINTDB_Session *session, + uint16_t oldcoin_index, + const struct RefreshMelt *melt) +{ + // FIXME: check logic! + uint16_t oldcoin_index_nbo = htons (oldcoin_index); + char *buf; + size_t buf_size; + PGresult *result; + + buf_size = GNUNET_CRYPTO_rsa_public_key_encode (melt->coin.denom_pub.rsa_public_key, + &buf); + { + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(&melt->session_hash), + TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_PQ_QUERY_PARAM_PTR(&melt->coin.coin_pub), + TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, + "insert_refresh_melt", + params); + } + GNUNET_free (buf); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + + +/** + * Get information about melted coin details from the database. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param refresh_session session key of the melt operation + * @param oldcoin_index index of the coin to retrieve + * @param melt melt data to fill in + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ +static int +postgres_get_refresh_melt (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + uint16_t oldcoin_index, + struct RefreshMelt *melt) +{ + // FIXME: check logic! + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Store in the database which coin(s) we want to create + * in a given refresh operation. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param session_hash hash to identify refresh session + * @param num_newcoins number of coins to generate, size of the @a denom_pubs array + * @param denom_pubs array denominations of the coins to create + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ +static int +postgres_insert_refresh_order (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + uint16_t num_newcoins, + const struct TALER_DenominationPublicKey *denom_pubs) +{ + // FIXME: check logic: was written for just one COIN! + uint16_t newcoin_index_nbo = htons (num_newcoins); + char *buf; + size_t buf_size; + PGresult *result; + + buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs->rsa_public_key, + &buf); + + { + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR (&newcoin_index_nbo), + TALER_PQ_QUERY_PARAM_PTR (session_hash), + TALER_PQ_QUERY_PARAM_PTR_SIZED (buf, buf_size), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, + "insert_refresh_order", + params); + } + GNUNET_free (buf); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + + +/** + * Lookup in the database the coins that we want to + * create in the given refresh operation. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param session_hash hash to identify refresh session + * @param newcoin_index array of the @a denom_pubs array + * @param denom_pubs where to store the deomination keys + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ +static int +postgres_get_refresh_order (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + uint16_t num_newcoins, + struct TALER_DenominationPublicKey *denom_pubs) +{ + // FIXME: check logic -- was written for just one coin! + char *buf; + size_t buf_size; + uint16_t newcoin_index_nbo = htons (num_newcoins); + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_PQ_QUERY_PARAM_END + }; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "get_refresh_order", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + /* FIXME: may want to distinguish between different error cases! */ + return GNUNET_SYSERR; + } + GNUNET_assert (1 == PQntuples (result)); + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC_VAR ("denom_pub", &buf, &buf_size), + TALER_PQ_RESULT_SPEC_END + }; + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + PQclear (result); + denom_pubs->rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_decode (buf, + buf_size); + GNUNET_free (buf); + return GNUNET_OK; +} + + + +/** + * Store information about the commitment of the + * given coin for the given refresh session in the database. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use + * @param session_hash hash to identify refresh session + * @param i set index (1st dimension) + * @param num_newcoins coin index size of the @a commit_coins array + * @param commit_coins array of coin commitments to store + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on error + */ +static int +postgres_insert_refresh_commit_coins (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + unsigned int i, + unsigned int num_newcoins, + const struct RefreshCommitCoin *commit_coins) +{ + // FIXME: check logic! -- was written for single commit_coin! + uint16_t cnc_index_nbo = htons (i); + uint16_t newcoin_index_nbo = htons (num_newcoins); + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR_SIZED(commit_coins->coin_ev, commit_coins->coin_ev_size), + TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_PQ_QUERY_PARAM_PTR_SIZED (commit_coins->refresh_link->coin_priv_enc, + commit_coins->refresh_link->blinding_key_enc_size + + sizeof (union TALER_CoinSpendPrivateKeyP)), + TALER_PQ_QUERY_PARAM_END + }; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "insert_refresh_commit_coin", + params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Obtain information about the commitment of the + * given coin of the given refresh session from the database. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use + * @param session_hash hash to identify refresh session + * @param i set index (1st dimension) + * @param j coin index (2nd dimension), corresponds to refreshed (new) coins + * @param commit_coin[OUT] coin commitment to return + * @return #GNUNET_OK on success + * #GNUNET_NO if not found + * #GNUNET_SYSERR on error + */ +static int +postgres_get_refresh_commit_coins (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + unsigned int cnc_index, + unsigned int newcoin_index, + struct RefreshCommitCoin *cc) +{ + // FIXME: check logic! + uint16_t cnc_index_nbo = htons (cnc_index); + uint16_t newcoin_index_nbo = htons (newcoin_index); + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_PQ_QUERY_PARAM_END + }; + char *c_buf; + size_t c_buf_size; + char *rl_buf; + size_t rl_buf_size; + struct TALER_RefreshLinkEncrypted *rl; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "get_refresh_commit_coin", + params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC_VAR("coin_ev", &c_buf, &c_buf_size), + TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &rl_buf, &rl_buf_size), + TALER_PQ_RESULT_SPEC_END + }; + if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0)) + { + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + if (rl_buf_size < sizeof (union TALER_CoinSpendPrivateKeyP)) + { + GNUNET_free (c_buf); + GNUNET_free (rl_buf); + return GNUNET_SYSERR; + } + rl = TALER_refresh_link_encrypted_decode (rl_buf, + rl_buf_size); + GNUNET_free (rl_buf); + cc->refresh_link = rl; + cc->coin_ev = c_buf; + cc->coin_ev_size = c_buf_size; + return GNUNET_YES; +} + + +/** + * Store the commitment to the given (encrypted) refresh link data + * for the given refresh session. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use + * @param session_hash hash to identify refresh session + * @param i set index (1st dimension) + * @param j coin index (2nd dimension), corresponds to melted (old) coins + * @param commit_link link information to store + * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success + */ +static int +postgres_insert_refresh_commit_links (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + unsigned int i, + unsigned int j, + const struct RefreshCommitLink *commit_link) +{ + // FIXME: check logic! + uint16_t cnc_index_nbo = htons (i); + uint16_t oldcoin_index_nbo = htons (j); + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR(&commit_link->transfer_pub), + TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_PQ_QUERY_PARAM_PTR(&commit_link->shared_secret_enc), + TALER_PQ_QUERY_PARAM_END + }; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "insert_refresh_commit_link", + params); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Obtain the commited (encrypted) refresh link data + * for the given refresh session. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use + * @param session_hash hash to identify refresh session + * @param i set index (1st dimension) + * @param num_links size of the @a commit_link array + * @param links[OUT] array of link information to return + * @return #GNUNET_SYSERR on internal error, + * #GNUNET_NO if commitment was not found + * #GNUNET_OK on success + */ +static int +postgres_get_refresh_commit_links (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + unsigned int i, + unsigned int num_links, + struct RefreshCommitLink *links) +{ + // FIXME: check logic: was written for a single link! + uint16_t cnc_index_nbo = htons (i); + uint16_t oldcoin_index_nbo = htons (num_links); + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_PQ_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_PQ_QUERY_PARAM_END + }; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, + "get_refresh_commit_link", + params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC("transfer_pub", &links->transfer_pub), + TALER_PQ_RESULT_SPEC("link_secret_enc", &links->shared_secret_enc), + TALER_PQ_RESULT_SPEC_END + }; + + if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0)) + { + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Insert signature of a new coin generated during refresh into + * the database indexed by the refresh session and the index + * of the coin. This data is later used should an old coin + * be used to try to obtain the private keys during "/refresh/link". + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param session_hash hash to identify refresh session + * @param newcoin_index coin index + * @param ev_sig coin signature + * @return #GNUNET_OK on success + */ +static int +postgres_insert_refresh_collectable (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + uint16_t newcoin_index, + const struct TALER_DenominationSignature *ev_sig) +{ + // FIXME: check logic! + uint16_t newcoin_index_nbo = htons (newcoin_index); + char *buf; + size_t buf_size; + PGresult *result; + + buf_size = GNUNET_CRYPTO_rsa_signature_encode (ev_sig->rsa_signature, + &buf); + { + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(session_hash), + TALER_PQ_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_PQ_QUERY_PARAM_PTR_SIZED(buf, buf_size), + TALER_PQ_QUERY_PARAM_END + }; + result = TALER_PQ_exec_prepared (session->conn, + "insert_refresh_collectable", + params); + } + GNUNET_free (buf); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + + +/** + * Obtain the link data of a coin, that is the encrypted link + * information, the denomination keys and the signatures. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param coin_pub public key to use to retrieve linkage data + * @return all known link data for the coin + */ +static struct LinkDataList * +postgres_get_link_data_list (void *cls, + struct TALER_MINTDB_Session *session, + const union TALER_CoinSpendPublicKeyP *coin_pub) +{ + // FIXME: check logic! + struct LinkDataList *ldl; + struct LinkDataList *pos; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(coin_pub), + TALER_PQ_QUERY_PARAM_END + }; + PGresult *result = TALER_PQ_exec_prepared (session->conn, "get_link", params); + + ldl = NULL; + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return NULL; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return NULL; + } + + + int i = 0; + + for (i = 0; i < PQntuples (result); i++) + { + struct TALER_RefreshLinkEncrypted *link_enc; + struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; + struct GNUNET_CRYPTO_rsa_Signature *sig; + char *ld_buf; + size_t ld_buf_size; + char *pk_buf; + size_t pk_buf_size; + char *sig_buf; + size_t sig_buf_size; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC_VAR("link_vector_enc", &ld_buf, &ld_buf_size), + TALER_PQ_RESULT_SPEC_VAR("denom_pub", &pk_buf, &pk_buf_size), + TALER_PQ_RESULT_SPEC_VAR("ev_sig", &sig_buf, &sig_buf_size), + TALER_PQ_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, i)) + { + PQclear (result); + GNUNET_break (0); + common_free_link_data_list (cls, + ldl); + return NULL; + } + if (ld_buf_size < sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)) + { + PQclear (result); + GNUNET_free (pk_buf); + GNUNET_free (sig_buf); + GNUNET_free (ld_buf); + common_free_link_data_list (cls, + ldl); + return NULL; + } + // FIXME: use util API for this! + link_enc = GNUNET_malloc (sizeof (struct TALER_RefreshLinkEncrypted) + + ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)); + link_enc->blinding_key_enc = (const char *) &link_enc[1]; + link_enc->blinding_key_enc_size = ld_buf_size - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey); + memcpy (link_enc->coin_priv_enc, + ld_buf, + ld_buf_size); + + sig + = GNUNET_CRYPTO_rsa_signature_decode (sig_buf, + sig_buf_size); + denom_pub + = GNUNET_CRYPTO_rsa_public_key_decode (pk_buf, + pk_buf_size); + GNUNET_free (pk_buf); + GNUNET_free (sig_buf); + GNUNET_free (ld_buf); + if ( (NULL == sig) || + (NULL == denom_pub) ) + { + if (NULL != denom_pub) + GNUNET_CRYPTO_rsa_public_key_free (denom_pub); + if (NULL != sig) + GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_free (link_enc); + GNUNET_break (0); + PQclear (result); + common_free_link_data_list (cls, + ldl); + return NULL; + } + pos = GNUNET_new (struct LinkDataList); + pos->next = ldl; + pos->link_data_enc = link_enc; + pos->denom_pub.rsa_public_key = denom_pub; + pos->ev_sig.rsa_signature = sig; + ldl = pos; + } + return ldl; +} + + +/** + * Obtain shared secret and transfer public key from the public key of + * the coin. This information and the link information returned by + * #postgres_get_link_data_list() enable the owner of an old coin to + * determine the private keys of the new coins after the melt. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param coin_pub public key of the coin + * @param transfer_pub[OUT] public transfer key + * @param shared_secret_enc[OUT] set to shared secret + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure (not found) + * #GNUNET_SYSERR on internal failure (database issue) + */ +static int +postgres_get_transfer (void *cls, + struct TALER_MINTDB_Session *session, + const union TALER_CoinSpendPublicKeyP *coin_pub, + struct TALER_TransferPublicKeyP *transfer_pub, + struct TALER_EncryptedLinkSecretP *shared_secret_enc) +{ + // FIXME: check logic! + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_QUERY_PARAM_PTR(coin_pub), + TALER_PQ_QUERY_PARAM_END + }; + + PGresult *result = TALER_PQ_exec_prepared (session->conn, "get_transfer", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + if (1 != PQntuples (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "got %d tuples for get_transfer\n", + PQntuples (result)); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_RESULT_SPEC("transfer_pub", transfer_pub), + TALER_PQ_RESULT_SPEC("link_secret_enc", shared_secret_enc), + TALER_PQ_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Compile a list of all (historic) transactions performed + * with the given coin (/refresh/melt and /deposit operations). + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param coin_pub coin to investigate + * @return list of transactions, NULL if coin is fresh + */ +static struct TALER_MINT_DB_TransactionList * +postgres_get_coin_transactions (void *cls, + struct TALER_MINTDB_Session *session, + const union TALER_CoinSpendPublicKeyP *coin_pub) +{ + // FIXME: check logic! + GNUNET_break (0); // FIXME: implement! + return NULL; +} + + + +/** + * Initialize Postgres database subsystem. + * + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_MINTDB_Plugin` + */ +void * +libtaler_plugin_mintdb_postgres_init (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct PostgresClosure *pg; + struct TALER_MINTDB_Plugin *plugin; + + pg = GNUNET_new (struct PostgresClosure); + + if (0 != pthread_key_create (&pg->db_conn_threadlocal, + &db_conn_destroy)) + { + TALER_LOG_ERROR ("Cannnot create pthread key.\n"); + return NULL; + } + /* FIXME: use configuration section with "postgres" in its name... */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", "db_conn_str", + &pg->connection_cfg_str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "db_conn_str"); + return NULL; + } + plugin = GNUNET_new (struct TALER_MINTDB_Plugin); + plugin->cls = pg; + plugin->get_session = &postgres_get_session; + plugin->drop_temporary = &postgres_drop_temporary; + plugin->create_tables = &postgres_create_tables; + plugin->start = &postgres_start; + plugin->commit = &postgres_commit; + plugin->rollback = &postgres_rollback; + plugin->reserve_get = &postgres_reserve_get; + plugin->reserves_in_insert = &postgres_reserves_in_insert; + plugin->get_collectable_blindcoin = &postgres_get_collectable_blindcoin; + plugin->insert_collectable_blindcoin = &postgres_insert_collectable_blindcoin; + plugin->get_reserve_history = &postgres_get_reserve_history; + plugin->free_reserve_history = &common_free_reserve_history; + plugin->have_deposit = &postgres_have_deposit; + plugin->insert_deposit = &postgres_insert_deposit; + plugin->get_refresh_session = &postgres_get_refresh_session; + plugin->create_refresh_session = &postgres_create_refresh_session; + plugin->insert_refresh_melt = &postgres_insert_refresh_melt; + plugin->get_refresh_melt = &postgres_get_refresh_melt; + plugin->insert_refresh_order = &postgres_insert_refresh_order; + plugin->get_refresh_order = &postgres_get_refresh_order; + plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins; + plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins; + plugin->insert_refresh_commit_links = &postgres_insert_refresh_commit_links; + plugin->get_refresh_commit_links = &postgres_get_refresh_commit_links; + plugin->insert_refresh_collectable = &postgres_insert_refresh_collectable; + plugin->get_link_data_list = &postgres_get_link_data_list; + plugin->free_link_data_list = &common_free_link_data_list; + plugin->get_transfer = &postgres_get_transfer; + // plugin->have_lock = &postgres_have_lock; + // plugin->insert_lock = &postgres_insert_lock; + plugin->get_coin_transactions = &postgres_get_coin_transactions; + plugin->free_coin_transaction_list = &common_free_coin_transaction_list; + return plugin; +} + + +/** + * Shutdown Postgres database subsystem. + * + * @param cls a `struct TALER_MINTDB_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_mintdb_postgres_done (void *cls) +{ + struct TALER_MINTDB_Plugin *plugin = cls; + struct PostgresClosure *pg = plugin->cls; + + GNUNET_free (pg->connection_cfg_str); + GNUNET_free (pg); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_mintdb_postgres.c */ diff --git a/src/mintdb/test_mintdb.c b/src/mintdb/test_mintdb.c new file mode 100644 index 000000000..a82094075 --- /dev/null +++ b/src/mintdb/test_mintdb.c @@ -0,0 +1,393 @@ +/* + 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 +*/ +/** + * @file mint/test_mint_db.c + * @brief test cases for DB interaction functions + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "plugin.h" + +static int result; + +#define FAILIF(cond) \ + do { \ + if (!(cond)){ break;} \ + GNUNET_break (0); \ + goto drop; \ + } while (0) + + +#define RND_BLK(ptr) \ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) + +#define ZR_BLK(ptr) \ + memset (ptr, 0, sizeof (*ptr)) + + +#define CURRENCY "EUR" + +/** + * Checks if the given reserve has the given amount of balance and expiry + * + * @param session the database connection + * @param pub the public key of the reserve + * @param value balance value + * @param fraction balance fraction + * @param currency currency of the reserve + * @param expiry expiration of the reserve + * @return #GNUNET_OK if the given reserve has the same balance and expiration + * as the given parameters; #GNUNET_SYSERR if not + */ +static int +check_reserve (struct TALER_MINTDB_Session *session, + const struct TALER_ReservePublicKeyP *pub, + uint64_t value, + uint32_t fraction, + const char *currency, + uint64_t expiry) +{ + struct Reserve 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)); + FAILIF (expiry != reserve.expiry.abs_value_us); + + return GNUNET_OK; + drop: + return GNUNET_SYSERR; +} + + +struct DenomKeyPair +{ + struct TALER_DenominationPrivateKey priv; + struct TALER_DenominationPublicKey pub; +}; + + +static struct DenomKeyPair * +create_denom_key_pair (unsigned int size) +{ + struct DenomKeyPair *dkp; + + dkp = GNUNET_new (struct DenomKeyPair); + 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.rsa_public_key); + GNUNET_CRYPTO_rsa_private_key_free (dkp->priv.rsa_private_key); + GNUNET_free (dkp); +} + +/** + * 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 cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct TALER_MINTDB_Session *session; + struct TALER_ReservePublicKeyP reserve_pub; + struct Reserve reserve; + struct GNUNET_TIME_Absolute expiry; + struct TALER_Amount amount; + struct DenomKeyPair *dkp; + struct GNUNET_HashCode h_blind; + struct CollectableBlindcoin cbc; + struct CollectableBlindcoin cbc2; + struct ReserveHistory *rh; + 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; + + dkp = NULL; + rh = NULL; + wire = NULL; + session = NULL; + ZR_BLK (&cbc); + ZR_BLK (&cbc2); + if (GNUNET_OK != + TALER_MINT_plugin_load (cfg)) + { + result = 1; + return; + } + if (GNUNET_OK != + plugin->create_tables (plugin->cls, + GNUNET_YES)) + { + result = 2; + goto drop; + } + if (NULL == + (session = plugin->get_session (plugin->cls, + GNUNET_YES))) + { + result = 3; + goto drop; + } + RND_BLK (&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 != + 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.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 (1 != bt->amount.value); + FAILIF (1 != bt->amount.fraction); + FAILIF (0 != strcmp (CURRENCY, bt->amount.currency)); + FAILIF (NULL != bt->wire); /* FIXME: write wire details to db */ + break; + case TALER_MINT_DB_RO_WITHDRAW_COIN: + withdraw = rh_head->details.withdraw; + FAILIF (0 != memcmp (&withdraw->reserve_pub, + &reserve_pub, + sizeof (reserve_pub))); + FAILIF (0 != memcmp (&withdraw->h_coin_envelope, + &h_blind, sizeof (h_blind))); + break; + } + } + 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) + plugin->free_reserve_history (plugin->cls, + rh); + rh = NULL; + 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.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; + TALER_MINT_plugin_unload (); +} + + +int +main (int argc, + char *const argv[]) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + char *argv2[] = { + "test-mint-db-", /* will be replaced later */ + "-c", "test-mint-db-.conf", /* will be replaced later */ + NULL, + }; + const char *plugin_name; + char *config_filename; + char *testname; + + result = -1; + if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) + { + GNUNET_break (0); + return -1; + } + plugin_name++; + (void) GNUNET_asprintf (&testname, + "test-mint-db-%s", plugin_name); + (void) GNUNET_asprintf (&config_filename, + "%s.conf", testname); + argv2[0] = argv[0]; + argv2[2] = config_filename; + if (GNUNET_OK != + GNUNET_PROGRAM_run ((sizeof (argv2)/sizeof (char *)) - 1, argv2, + testname, + "Test cases for mint database helper functions.", + options, &run, NULL)) + { + GNUNET_free (config_filename); + GNUNET_free (testname); + return 3; + } + GNUNET_free (config_filename); + GNUNET_free (testname); + return result; +} diff --git a/src/mintdb/test_mintdb_deposits.c b/src/mintdb/test_mintdb_deposits.c new file mode 100644 index 000000000..dbe12e88d --- /dev/null +++ b/src/mintdb/test_mintdb_deposits.c @@ -0,0 +1,142 @@ +/* + This file is part of TALER + Copyright (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file mint/test_mint_deposits.c + * @brief testcase for mint deposits + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include +#include +#include "plugin.h" +#include "taler_pq_lib.h" +#include "taler-mint-httpd.h" + +#define DB_URI "postgres:///taler" + +#define break_db_err(result) do { \ + GNUNET_break(0); \ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \ + } while (0) + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + +/** + * Should we not interact with a temporary table? + */ +static int persistent; + +/** + * Testcase result + */ +static int result; + + +/** + * 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 cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + static const char wire[] = "{" + "\"type\":\"SEPA\"," + "\"IBAN\":\"DE67830654080004822650\"," + "\"NAME\":\"GNUNET E.V\"," + "\"BIC\":\"GENODEF1SRL\"" + "}"; + struct Deposit *deposit; + uint64_t transaction_id; + struct TALER_MINTDB_Session *session; + + deposit = NULL; + 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, + deposit, + sizeof (struct Deposit)); + /* Makeup a random 64bit transaction ID */ + transaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); + deposit->transaction_id = GNUNET_htonll (transaction_id); + /* Random amount */ + deposit->amount_with_fee.value = + htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)); + deposit->amount_with_fee.fraction = + htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)); + GNUNET_assert (strlen (TMH_MINT_CURRENCY) < sizeof (deposit->amount_with_fee.currency)); + strcpy (deposit->amount_with_fee.currency, TMH_MINT_CURRENCY); + /* Copy wireformat */ + deposit->wire = json_loads (wire, 0, NULL); + 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); + return; +} + + +int +main (int argc, + char *const argv[]) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'T', "persist", NULL, + gettext_noop ("Use a persistent database table instead of a temporary one"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &persistent}, + GNUNET_GETOPT_OPTION_END + }; + + + persistent = GNUNET_NO; + result = GNUNET_SYSERR; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, + "test-mint-deposits", + "testcase for mint deposits", + options, &run, NULL)) + return 3; + return (GNUNET_OK == result) ? 0 : 1; +} diff --git a/src/mintdb/test_mintdb_keyio.c b/src/mintdb/test_mintdb_keyio.c new file mode 100644 index 000000000..83df25046 --- /dev/null +++ b/src/mintdb/test_mintdb_keyio.c @@ -0,0 +1,86 @@ +/* + This file is part of TALER + Copyright (C) 2014 GNUnet e. V. (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 +*/ +/** + * @file mint/test_mint_common.c + * @brief test cases for some functions in mint/mint_common.c + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "gnunet/gnunet_util_lib.h" +#include "taler_signatures.h" +#include "key_io.h" + +#define RSA_KEY_SIZE 1024 + + +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + +int +main (int argc, + const char *const argv[]) +{ + struct TALER_DenominationKeyIssueInformation dki; + char *enc; + size_t enc_size; + struct TALER_DenominationKeyIssueInformation dki_read; + char *enc_read; + size_t enc_read_size; + char *tmpfile; + int ret; + + ret = 1; + enc = NULL; + enc_read = NULL; + tmpfile = 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_DenominationKeyValidityPS, + signature)); + 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.rsa_private_key, + &enc_read); + EXITIF (enc_size != enc_read_size); + EXITIF (0 != memcmp (enc, + enc_read, + enc_size)); + ret = 0; + + EXITIF_exit: + GNUNET_free_non_null (enc); + if (NULL != tmpfile) + { + (void) unlink (tmpfile); + GNUNET_free (tmpfile); + } + GNUNET_free_non_null (enc_read); + 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; +} -- cgit v1.2.3