diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-01-08 18:37:20 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-01-08 18:37:20 +0100 |
commit | 57d1f08dbca256f5fe16d57b29bfa523dec8f6c4 (patch) | |
tree | 3b3ee5f3b8c174887217e5c465048dea4e79bae2 /src/mint/mint_db.c |
-initial import for mint
Diffstat (limited to 'src/mint/mint_db.c')
-rw-r--r-- | src/mint/mint_db.c | 1838 |
1 files changed, 1838 insertions, 0 deletions
diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c new file mode 100644 index 000000000..6dc025877 --- /dev/null +++ b/src/mint/mint_db.c @@ -0,0 +1,1838 @@ +/* + This file is part of TALER + (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 <http://www.gnu.org/licenses/> +*/ + +/** + * @file mint_db.c + * @brief Database access for the mint + * @author Florian Dold + */ +#include "platform.h" +#include "taler_db_lib.h" +#include "taler_signatures.h" +#include "mint_db.h" +#include "mint.h" +#include <pthread.h> + +/** + * Thread-local database connection. + * Contains a pointer to PGconn or NULL. + */ +static pthread_key_t db_conn_threadlocal; + + +/** + * Database connection string, as read from + * the configuration. + */ +static char *TALER_MINT_db_connection_cfg_str; + + +#define break_db_err(result) do { \ + GNUNET_break(0); \ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s\n", PQresultErrorMessage (result)); \ + } while (0) + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + +int +TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, + struct TALER_RSA_BlindedSignaturePurpose *blind_ev, + struct CollectableBlindcoin *collectable) +{ + PGresult *result; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR (blind_ev), + TALER_DB_QUERY_PARAM_END + }; + result = TALER_DB_exec_prepared (db_conn, "get_collectable_blindcoins", 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)) + { + PQclear (result); + return GNUNET_NO; + } + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("blind_ev_sig", &collectable->ev_sig), + TALER_DB_RESULT_SPEC("denom_pub", &collectable->denom_pub), + TALER_DB_RESULT_SPEC("reserve_sig", &collectable->reserve_sig), + TALER_DB_RESULT_SPEC("reserve_pub", &collectable->reserve_pub), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + (void) memcpy (&collectable->ev, blind_ev, sizeof (struct TALER_RSA_BlindedSignaturePurpose)); + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, + const struct CollectableBlindcoin *collectable) +{ + PGresult *result; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR (&collectable->ev), + TALER_DB_QUERY_PARAM_PTR (&collectable->ev_sig), + TALER_DB_QUERY_PARAM_PTR (&collectable->denom_pub), + TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_pub), + TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig), + TALER_DB_QUERY_PARAM_END + }; + result = TALER_DB_exec_prepared (db_conn, "insert_collectable_blindcoins", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result)); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Insert failed (updated '%s' tupes instead of '1')\n", + PQcmdTuples (result)); + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_get_reserve (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub, + struct Reserve *reserve) +{ + PGresult *result; + int res; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR (reserve_pub), + TALER_DB_QUERY_PARAM_END + }; + + result = TALER_DB_exec_prepared (db_conn, "get_reserve", 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)) + { + PQclear (result); + return GNUNET_NO; + } + + reserve->reserve_pub = *reserve_pub; + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("status_sig", &reserve->status_sig), + TALER_DB_RESULT_SPEC("status_sign_pub", &reserve->status_sign_pub), + TALER_DB_RESULT_SPEC_END + }; + + res = TALER_DB_extract_result (result, rs, 0); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + { + int fnums[] = { + PQfnumber (result, "balance_value"), + PQfnumber (result, "balance_fraction"), + PQfnumber (result, "balance_currency"), + }; + if (GNUNET_OK != TALER_TALER_DB_extract_amount_nbo (result, 0, fnums, &reserve->balance)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + } + + /* FIXME: Add expiration?? */ + + PQclear (result); + return GNUNET_OK; +} + + +/* If fresh is GNUNET_YES, set some fields to NULL as they are not actually valid */ +int +TALER_MINT_DB_update_reserve (PGconn *db_conn, + const struct Reserve *reserve, + int fresh) +{ + PGresult *result; + uint64_t stamp_sec; + + stamp_sec = GNUNET_ntohll (GNUNET_TIME_absolute_ntoh (reserve->expiration).abs_value_us / 1000000); + + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR (&reserve->reserve_pub), + TALER_DB_QUERY_PARAM_PTR (&reserve->balance.value), + TALER_DB_QUERY_PARAM_PTR (&reserve->balance.fraction), + TALER_DB_QUERY_PARAM_PTR_SIZED (&reserve->balance.currency, + strlen (reserve->balance.currency)), + TALER_DB_QUERY_PARAM_PTR (&reserve->status_sig), + TALER_DB_QUERY_PARAM_PTR (&reserve->status_sign_pub), + TALER_DB_QUERY_PARAM_PTR (&stamp_sec), + TALER_DB_QUERY_PARAM_END + }; + + /* set some fields to NULL if they are not actually valid */ + + if (GNUNET_YES == fresh) + { + unsigned i; + for (i = 4; i <= 7; i += 1) + { + params[i].data = NULL; + params[i].size = 0; + } + } + + result = TALER_DB_exec_prepared (db_conn, "update_reserve", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result)); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Update failed (updated '%s' tupes instead of '1')\n", + PQcmdTuples (result)); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + + +int +TALER_MINT_DB_prepare (PGconn *db_conn) +{ + PGresult *result; + + result = PQprepare (db_conn, "get_reserve", + "SELECT " + " balance_value, balance_fraction, balance_currency " + ",expiration_date, blind_session_pub, blind_session_priv" + ",status_sig, status_sign_pub " + "FROM reserves " + "WHERE reserve_pub=$1 " + "LIMIT 1; ", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "update_reserve", + "UPDATE reserves " + "SET" + " balance_value=$2 " + ",balance_fraction=$3 " + ",balance_currency=$4 " + ",status_sig=$5 " + ",status_sign_pub=$6 " + ",expiration_date=$7 " + "WHERE reserve_pub=$1 ", + 9, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + result = PQprepare (db_conn, "insert_collectable_blindcoins", + "INSERT INTO collectable_blindcoins ( " + " blind_ev, blind_ev_sig " + ",denom_pub, reserve_pub, reserve_sig) " + "VALUES ($1, $2, $3, $4, $5)", + 6, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_collectable_blindcoins", + "SELECT " + "blind_ev_sig, denom_pub, reserve_sig, reserve_pub " + "FROM collectable_blindcoins " + "WHERE blind_ev = $1", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_reserve_order", + "SELECT " + " blind_ev, blind_ev_sig, denom_pub, reserve_sig, reserve_pub " + "FROM collectable_blindcoins " + "WHERE blind_session_pub = $1", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + /* FIXME: does it make sense to store these computed values in the DB? */ + result = PQprepare (db_conn, "get_refresh_session", + "SELECT " + " (SELECT count(*) FROM refresh_melt WHERE session_pub = $1)::INT2 as num_oldcoins " + ",(SELECT count(*) FROM refresh_blind_session_keys " + " WHERE session_pub = $1 and cnc_index = 0)::INT2 as num_newcoins " + ",(SELECT count(*) FROM refresh_blind_session_keys " + " WHERE session_pub = $1 and newcoin_index = 0)::INT2 as kappa " + ",noreveal_index" + ",session_commit_sig " + ",reveal_ok " + "FROM refresh_sessions " + "WHERE session_pub = $1", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_known_coin", + "SELECT " + " coin_pub, denom_pub, denom_sig " + ",expended_value, expended_fraction, expended_currency " + ",refresh_session_pub " + "FROM known_coins " + "WHERE coin_pub = $1", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "update_known_coin", + "UPDATE known_coins " + "SET " + " denom_pub = $2 " + ",denom_sig = $3 " + ",expended_value = $4 " + ",expended_fraction = $5 " + ",expended_currency = $6 " + ",refresh_session_pub = $7 " + "WHERE " + " coin_pub = $1 ", + 7, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_known_coin", + "INSERT INTO known_coins (" + " coin_pub" + ",denom_pub" + ",denom_sig" + ",expended_value" + ",expended_fraction" + ",expended_currency" + ",refresh_session_pub" + ")" + "VALUES ($1,$2,$3,$4,$5,$6,$7)", + 7, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_refresh_commit_link", + "SELECT " + " transfer_pub " + ",link_secret_enc " + "FROM refresh_commit_link " + "WHERE session_pub = $1 AND cnc_index = $2 AND oldcoin_index = $3", + 3, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_refresh_commit_coin", + "SELECT " + " link_vector_enc " + ",coin_ev " + "FROM refresh_commit_coin " + "WHERE session_pub = $1 AND cnc_index = $2 AND newcoin_index = $3", + 3, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_refresh_order", + "INSERT INTO refresh_order ( " + " newcoin_index " + ",session_pub " + ",denom_pub " + ") " + "VALUES ($1, $2, $3) ", + 3, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_refresh_melt", + "INSERT INTO refresh_melt ( " + " session_pub " + ",oldcoin_index " + ",coin_pub " + ",denom_pub " + ") " + "VALUES ($1, $2, $3, $4) ", + 3, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_refresh_order", + "SELECT denom_pub " + "FROM refresh_order " + "WHERE session_pub = $1 AND newcoin_index = $2", + 2, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_refresh_collectable", + "SELECT ev_sig " + "FROM refresh_collectable " + "WHERE session_pub = $1 AND newcoin_index = $2", + 2, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_refresh_melt", + "SELECT coin_pub " + "FROM refresh_melt " + "WHERE session_pub = $1 AND oldcoin_index = $2", + 2, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_refresh_session", + "INSERT INTO refresh_sessions ( " + " session_pub " + ",noreveal_index " + ") " + "VALUES ($1, $2) ", + 2, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_refresh_commit_link", + "INSERT INTO refresh_commit_link ( " + " session_pub " + ",transfer_pub " + ",cnc_index " + ",oldcoin_index " + ",link_secret_enc " + ") " + "VALUES ($1, $2, $3, $4, $5) ", + 5, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_refresh_commit_coin", + "INSERT INTO refresh_commit_coin ( " + " session_pub " + ",coin_ev " + ",cnc_index " + ",newcoin_index " + ",link_vector_enc " + ") " + "VALUES ($1, $2, $3, $4, $5) ", + 5, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "insert_refresh_collectable", + "INSERT INTO refresh_collectable ( " + " session_pub " + ",newcoin_index " + ",ev_sig " + ") " + "VALUES ($1, $2, $3) ", + 3, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "set_reveal_ok", + "UPDATE refresh_sessions " + "SET reveal_ok = TRUE " + "WHERE session_pub = $1 ", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_link", + "SELECT link_vector_enc, ro.denom_pub, ev_sig " + "FROM refresh_melt rm " + " JOIN refresh_order ro USING (session_pub) " + " JOIN refresh_commit_coin rcc USING (session_pub) " + " JOIN refresh_sessions rs USING (session_pub) " + " JOIN refresh_collectable rc USING (session_pub) " + "WHERE rm.coin_pub = $1 " + "AND ro.newcoin_index = rcc.newcoin_index " + "AND ro.newcoin_index = rc.newcoin_index " + "AND rcc.cnc_index = rs.noreveal_index % ( " + " SELECT count(*) FROM refresh_commit_coin rcc2 " + " WHERE rcc2.newcoin_index = 0 AND rcc2.session_pub = rs.session_pub " + " ) ", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + result = PQprepare (db_conn, "get_transfer", + "SELECT transfer_pub, link_secret_enc " + "FROM refresh_melt rm " + " JOIN refresh_commit_link rcl USING (session_pub) " + " JOIN refresh_sessions rs USING (session_pub) " + "WHERE rm.coin_pub = $1 " + "AND rm.oldcoin_index = rcl.oldcoin_index " + "AND rcl.cnc_index = rs.noreveal_index % ( " + " SELECT count(*) FROM refresh_commit_coin rcc2 " + " WHERE newcoin_index = 0 AND rcc2.session_pub = rm.session_pub " + " ) ", + 1, NULL); + if (PGRES_COMMAND_OK != PQresultStatus(result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + + if (GNUNET_OK != TALER_MINT_DB_prepare_deposits (db_conn)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Roll back the current transaction of a database connection. + * + * @param db_conn the database connection + * @return GNUNET_OK on success + */ +int +TALER_MINT_DB_rollback (PGconn *db_conn) +{ + PGresult *result; + + result = PQexec(db_conn, "ROLLBACK"); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Roll back the current transaction of a database connection. + * + * @param db_conn the database connection + * @return GNUNET_OK on success + */ +int +TALER_MINT_DB_commit (PGconn *db_conn) +{ + PGresult *result; + + result = PQexec(db_conn, "COMMIT"); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Start a transaction. + * + * @param db_conn the database connection + * @return GNUNET_OK on success + */ +int +TALER_MINT_DB_transaction (PGconn *db_conn) +{ + PGresult *result; + + result = PQexec(db_conn, "BEGIN"); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't start transaction: %s\n", PQresultErrorMessage (result)); + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +/** + * Insert a refresh order into the database. + */ +int +TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub) +{ + uint16_t newcoin_index_nbo = htons (newcoin_index); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(denom_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_order", 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; +} + + +int +TALER_MINT_DB_get_refresh_session (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + struct RefreshSession *session) +{ + int res; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_session", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result)); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + return GNUNET_NO; + + GNUNET_assert (1 == PQntuples (result)); + + /* We're done if the caller is only interested in + * whether the session exists or not */ + + if (NULL == session) + return GNUNET_YES; + + memset (session, 0, sizeof (struct RefreshSession)); + + session->session_pub = *refresh_session_pub; + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("num_oldcoins", &session->num_oldcoins), + TALER_DB_RESULT_SPEC("num_newcoins", &session->num_newcoins), + TALER_DB_RESULT_SPEC("kappa", &session->kappa), + TALER_DB_RESULT_SPEC("noreveal_index", &session->noreveal_index), + TALER_DB_RESULT_SPEC("reveal_ok", &session->reveal_ok), + TALER_DB_RESULT_SPEC_END + }; + + res = TALER_DB_extract_result (result, rs, 0); + + if (GNUNET_OK != res) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + if (TALER_DB_field_isnull (result, 0, "session_commit_sig")) + session->has_commit_sig = GNUNET_NO; + else + session->has_commit_sig = GNUNET_YES; + + session->num_oldcoins = ntohs (session->num_oldcoins); + session->num_newcoins = ntohs (session->num_newcoins); + session->kappa = ntohs (session->kappa); + session->noreveal_index = ntohs (session->noreveal_index); + + PQclear (result); + return GNUNET_YES; +} + + +int +TALER_MINT_DB_get_known_coin (PGconn *db_conn, struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + struct KnownCoin *known_coin) +{ + int res; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(coin_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_known_coin", 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)); + + /* extract basic information about the known coin */ + + { + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("coin_pub", &known_coin->public_info.coin_pub), + TALER_DB_RESULT_SPEC("denom_pub", &known_coin->public_info.denom_pub), + TALER_DB_RESULT_SPEC("denom_sig", &known_coin->public_info.denom_sig), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != (res = TALER_DB_extract_result (result, rs, 0))) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + } + + /* extract the expended amount of the coin */ + + if (GNUNET_OK != TALER_DB_extract_amount (result, 0, + "expended_value", + "expended_fraction", + "expended_currency", + &known_coin->expended_balance)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + /* extract the refresh session of the coin or mark it as missing */ + + { + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("refresh_session_pub", &known_coin->refresh_session_pub), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_SYSERR == (res = TALER_DB_extract_result (result, rs, 0))) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + if (GNUNET_NO == res) + { + known_coin->is_refreshed = GNUNET_NO; + memset (&known_coin->refresh_session_pub, 0, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + } + else + { + known_coin->is_refreshed = GNUNET_YES; + } + } + + PQclear (result); + return GNUNET_YES; +} + + +int +TALER_MINT_DB_create_refresh_session (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub) +{ + uint16_t noreveal_index; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(&noreveal_index), + TALER_DB_QUERY_PARAM_END + }; + + noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15); + noreveal_index = htonl (noreveal_index); + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_session", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_set_commit_signature (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + const struct GNUNET_CRYPTO_EddsaSignature *commit_sig) +{ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +int +TALER_MINT_DB_set_reveal_ok (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub) +{ + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "set_reveal_ok", 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; +} + + +int +TALER_MINT_DB_update_known_coin (PGconn *db_conn, + const struct KnownCoin *known_coin) +{ + struct TALER_AmountNBO expended_nbo = TALER_amount_hton (known_coin->expended_balance); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.coin_pub), + TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_pub), + TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_sig), + TALER_DB_QUERY_PARAM_PTR(&expended_nbo.value), + TALER_DB_QUERY_PARAM_PTR(&expended_nbo.fraction), + TALER_DB_QUERY_PARAM_PTR_SIZED(expended_nbo.currency, strlen (expended_nbo.currency)), + TALER_DB_QUERY_PARAM_PTR(&known_coin->refresh_session_pub), + TALER_DB_QUERY_PARAM_END + }; + + if (GNUNET_NO == known_coin->is_refreshed) + { + // Mind the magic index! + params[6].data = NULL; + params[6].size = 0; + } + + PGresult *result = TALER_DB_exec_prepared (db_conn, "update_known_coin", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + PQclear (result); + // return 'no' here (don't fail) so that we can + // insert if update fails (=> "upsert") + return GNUNET_NO; + } + + PQclear (result); + return GNUNET_YES; +} + +int +TALER_MINT_DB_insert_known_coin (PGconn *db_conn, + const struct KnownCoin *known_coin) +{ + struct TALER_AmountNBO expended_nbo = TALER_amount_hton (known_coin->expended_balance); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.coin_pub), + TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_pub), + TALER_DB_QUERY_PARAM_PTR(&known_coin->public_info.denom_sig), + TALER_DB_QUERY_PARAM_PTR(&expended_nbo.value), + TALER_DB_QUERY_PARAM_PTR(&expended_nbo.fraction), + TALER_DB_QUERY_PARAM_PTR_SIZED(&expended_nbo.currency, strlen (expended_nbo.currency)), + TALER_DB_QUERY_PARAM_PTR(&known_coin->refresh_session_pub), + TALER_DB_QUERY_PARAM_END + }; + + if (GNUNET_NO == known_coin->is_refreshed) + { + // Mind the magic index! + params[6].data = NULL; + params[6].size = 0; + } + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_known_coin", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + PQclear (result); + // return 'no' here (don't fail) so that we can + // update if insert fails (=> "upsert") + return GNUNET_NO; + } + + PQclear (result); + return GNUNET_YES; +} + + +int +TALER_MINT_DB_upsert_known_coin (PGconn *db_conn, struct KnownCoin *known_coin) +{ + int ret; + ret = TALER_MINT_DB_update_known_coin (db_conn, known_coin); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_YES == ret) + return GNUNET_YES; + return TALER_MINT_DB_insert_known_coin (db_conn, known_coin); +} + + +int +TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, struct RefreshCommitLink *commit_link) +{ + uint16_t cnc_index_nbo = htons (commit_link->cnc_index); + uint16_t oldcoin_index_nbo = htons (commit_link->oldcoin_index); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(&commit_link->session_pub), + TALER_DB_QUERY_PARAM_PTR(&commit_link->transfer_pub), + TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_DB_QUERY_PARAM_PTR_SIZED(&commit_link->shared_secret_enc, sizeof (struct GNUNET_HashCode)), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_link", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, struct RefreshCommitCoin *commit_coin) +{ + uint16_t cnc_index_nbo = htons (commit_coin->cnc_index); + uint16_t newcoin_index_nbo = htons (commit_coin->newcoin_index); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(&commit_coin->session_pub), + TALER_DB_QUERY_PARAM_PTR(&commit_coin->coin_ev), + TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_DB_QUERY_PARAM_PTR_SIZED(&commit_coin->link_enc, sizeof (struct LinkData)), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_coin", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 != strcmp ("1", PQcmdTuples (result))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + int cnc_index, int oldcoin_index, + struct RefreshCommitLink *cc) +{ + uint16_t cnc_index_nbo = htons (cnc_index); + uint16_t oldcoin_index_nbo = htons (oldcoin_index); + + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), + TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_DB_QUERY_PARAM_END + }; + + cc->cnc_index = cnc_index; + cc->oldcoin_index = oldcoin_index; + cc->session_pub = *refresh_session_pub; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_link", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("transfer_pub", &cc->transfer_pub), + TALER_DB_RESULT_SPEC_SIZED("link_secret_enc", &cc->shared_secret_enc, + TALER_REFRESH_SHARED_SECRET_LENGTH), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_free (cc); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_YES; +} + + +int +TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + int cnc_index, int newcoin_index, + struct RefreshCommitCoin *cc) +{ + uint16_t cnc_index_nbo = htons (cnc_index); + uint16_t newcoin_index_nbo = htons (newcoin_index); + + cc->cnc_index = cnc_index; + cc->newcoin_index = newcoin_index; + cc->session_pub = *refresh_session_pub; + + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(refresh_session_pub), + TALER_DB_QUERY_PARAM_PTR(&cnc_index_nbo), + TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_coin", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("coin_ev", &cc->coin_ev), + TALER_DB_RESULT_SPEC_SIZED("link_vector_enc", &cc->link_enc, + TALER_REFRESH_LINK_LENGTH), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_YES != TALER_DB_extract_result (result, rs, 0)) + { + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_YES; +} + + +int +TALER_MINT_DB_get_refresh_order (PGconn *db_conn, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub) +{ + uint16_t newcoin_index_nbo = htons (newcoin_index); + + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_order", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + GNUNET_assert (1 == PQntuples (result)); + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("denom_pub", denom_pub), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + const struct TALER_RSA_Signature *ev_sig) +{ + uint16_t newcoin_index_nbo = htons (newcoin_index); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_DB_QUERY_PARAM_PTR(ev_sig), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_collectable", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_get_refresh_collectable (PGconn *db_conn, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + struct TALER_RSA_Signature *ev_sig) +{ + + uint16_t newcoin_index_nbo = htons (newcoin_index); + + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(&newcoin_index_nbo), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_collectable", 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; + } + + GNUNET_assert (1 == PQntuples (result)); + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("ev_sig", ev_sig), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + + +int +TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t oldcoin_index, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + const struct TALER_RSA_PublicKeyBinaryEncoded *denom_pub) +{ + uint16_t oldcoin_index_nbo = htons (oldcoin_index); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_DB_QUERY_PARAM_PTR(coin_pub), + TALER_DB_QUERY_PARAM_PTR(denom_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_melt", params); + + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + + + +int +TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t oldcoin_index, + struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) +{ + uint16_t oldcoin_index_nbo = htons (oldcoin_index); + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(session_pub), + TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_melt", 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; + } + + GNUNET_assert (1 == PQntuples (result)); + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("coin_pub", coin_pub), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_db_get_link (PGconn *db_conn, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + LinkIterator link_iter, + void *cls) +{ + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(coin_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_link", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + + int i = 0; + int res; + + for (i = 0; i < PQntuples (result); i++) + { + struct LinkDataEnc link_data_enc; + struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; + struct TALER_RSA_Signature ev_sig; + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("link_vector_enc", &link_data_enc), + TALER_DB_RESULT_SPEC("denom_pub", &denom_pub), + TALER_DB_RESULT_SPEC("ev_sig", &ev_sig), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_DB_extract_result (result, rs, i)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != (res = link_iter (cls, &link_data_enc, &denom_pub, &ev_sig))) + { + GNUNET_assert (GNUNET_SYSERR != res); + PQclear (result); + return res; + } + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_db_get_transfer (PGconn *db_conn, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, + struct SharedSecretEnc *shared_secret_enc) +{ + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR(coin_pub), + TALER_DB_QUERY_PARAM_END + }; + + PGresult *result = TALER_DB_exec_prepared (db_conn, "get_transfer", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + if (1 != PQntuples (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "got %d tuples for get_transfer\n", PQntuples (result)); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("transfer_pub", transfer_pub), + TALER_DB_RESULT_SPEC("link_secret_enc", shared_secret_enc), + TALER_DB_RESULT_SPEC_END + }; + + if (GNUNET_OK != TALER_DB_extract_result (result, rs, 0)) + { + PQclear (result); + GNUNET_break (0); + return GNUNET_SYSERR; + } + + PQclear (result); + return GNUNET_OK; +} + + +int +TALER_MINT_DB_init_deposits (PGconn *conn, int tmp) +{ + const char *tmp_str = (1 == tmp) ? "TEMPORARY" : ""; + char *sql; + PGresult *res; + int ret; + + res = NULL; + (void) GNUNET_asprintf (&sql, + "CREATE %1$s TABLE IF NOT EXISTS deposits (" + " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)" + ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)" + ",transaction_id INT8 NOT NULL" + ",amount_value INT4 NOT NULL" + ",amount_fraction INT4 NOT NULL" + ",amount_currency VARCHAR(4) NOT NULL" + ",merchant_pub BYTEA NOT NULL" + ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)" + ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)" + ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)" + ",wire TEXT NOT NULL" + ")", + tmp_str); + res = PQexec (conn, sql); + GNUNET_free (sql); + if (PGRES_COMMAND_OK != PQresultStatus (res)) + { + break_db_err (res); + ret = GNUNET_SYSERR; + } + else + ret = GNUNET_OK; + PQclear (res); + return ret; +} + +int +TALER_MINT_DB_prepare_deposits (PGconn *db_conn) +{ + PGresult *result; + + result = PQprepare (db_conn, "insert_deposit", + "INSERT INTO deposits (" + "coin_pub," + "denom_pub," + "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" + ")", + 11, NULL); + EXITIF (PGRES_COMMAND_OK != PQresultStatus(result)); + PQclear (result); + + result = PQprepare (db_conn, "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" + ")", + 1, NULL); + EXITIF (PGRES_COMMAND_OK != PQresultStatus(result)); + PQclear (result); + + return GNUNET_OK; + + EXITIF_exit: + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; +} + + +int +TALER_MINT_DB_insert_deposit (PGconn *db_conn, + const struct Deposit *deposit) +{ + struct TALER_DB_QueryParam params[]= { + TALER_DB_QUERY_PARAM_PTR (&deposit->coin_pub), + TALER_DB_QUERY_PARAM_PTR (&deposit->denom_pub), + TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id), + TALER_DB_QUERY_PARAM_PTR (&deposit->amount.value), + TALER_DB_QUERY_PARAM_PTR (&deposit->amount.fraction), + TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->amount.currency, strlen (deposit->amount.currency)), + TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub), + TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract), + TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire), + TALER_DB_QUERY_PARAM_PTR (&deposit->coin_sig), + TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->wire, strlen(deposit->wire)), + TALER_DB_QUERY_PARAM_END + }; + PGresult *result; + + result = TALER_DB_exec_prepared (db_conn, "insert_deposit", params); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + break_db_err (result); + PQclear (result); + return GNUNET_SYSERR; + } + PQclear (result); + return GNUNET_OK; +} + +int +TALER_MINT_DB_get_deposit (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub, + struct Deposit **r_deposit) +{ + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR (coin_pub), + TALER_DB_QUERY_PARAM_END + }; + PGresult *result; + struct Deposit *deposit; + + deposit = NULL; + result = TALER_DB_exec_prepared (db_conn, "get_deposit", params); + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + break_db_err (result); + goto EXITIF_exit; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return GNUNET_NO; + } + + if (1 != PQntuples (result)) + { + GNUNET_break (0); + goto EXITIF_exit; + } + + { + deposit = GNUNET_malloc (sizeof (struct Deposit)); /* Without wire data */ + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC ("coin_pub", &deposit->coin_pub), + TALER_DB_RESULT_SPEC ("denom_pub", &deposit->denom_pub), + TALER_DB_RESULT_SPEC ("coin_sig", &deposit->coin_sig), + TALER_DB_RESULT_SPEC ("transaction_id", &deposit->transaction_id), + TALER_DB_RESULT_SPEC ("merchant_pub", &deposit->merchant_pub), + TALER_DB_RESULT_SPEC ("h_contract", &deposit->h_contract), + TALER_DB_RESULT_SPEC ("h_wire", &deposit->h_wire), + TALER_DB_RESULT_SPEC_END + }; + EXITIF (GNUNET_OK != TALER_DB_extract_result (result, rs, 0)); + EXITIF (GNUNET_OK != TALER_DB_extract_amount_nbo (result, 0, + "amount_value", + "amount_fraction", + "amount_currency", + &deposit->amount)); + deposit->purpose.purpose = htonl (TALER_SIGNATURE_DEPOSIT); + deposit->purpose.size = htonl (sizeof (struct Deposit) + - offsetof (struct Deposit, purpose)); + } + + PQclear (result); + *r_deposit = deposit; + return GNUNET_OK; + +EXITIF_exit: + PQclear (result); + GNUNET_free_non_null (deposit); + deposit = NULL; + return GNUNET_SYSERR; +} + + + +/** + * Get the thread-local database-handle. + * Connect to the db if the connection does not exist yet. + * + * @param the database connection, or NULL on error + */ +PGconn * +TALER_MINT_DB_get_connection (void) +{ + PGconn *db_conn; + + if (NULL != (db_conn = pthread_getspecific (db_conn_threadlocal))) + return db_conn; + + db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); + + if (CONNECTION_OK != PQstatus (db_conn)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "db connection failed: %s\n", + PQerrorMessage (db_conn)); + GNUNET_break (0); + return NULL; + } + + if (GNUNET_OK != TALER_MINT_DB_prepare (db_conn)) + { + GNUNET_break (0); + return NULL; + } + if (0 != pthread_setspecific (db_conn_threadlocal, db_conn)) + { + GNUNET_break (0); + return NULL; + } + return db_conn; +} + + +/** + * Close thread-local database connection when a thread is destroyed. + * + * @param closure we get from pthreads (the db handle) + */ +static void +db_conn_destroy (void *cls) +{ + PGconn *db_conn = cls; + if (NULL != db_conn) + PQfinish (db_conn); +} + + +/** + * Initialize database subsystem. + * + * @param connection_cfg configuration to use to talk to DB + * @return GNUNET_OK on success + */ +int +TALER_MINT_DB_init (const char *connection_cfg) +{ + + if (0 != pthread_key_create (&db_conn_threadlocal, &db_conn_destroy)) + { + fprintf (stderr, + "Can't create pthread key.\n"); + return GNUNET_SYSERR; + } + TALER_MINT_db_connection_cfg_str = GNUNET_strdup (connection_cfg); + return GNUNET_OK; +} |