From 867f98457bbcd0cdc86fc9f14549da44176277bb Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 26 Nov 2015 14:20:59 +0100 Subject: revising backenddb-API, towards pluginificiation --- configure.ac | 20 +- src/backenddb/Makefile.am | 29 +- src/backenddb/merchant_db.c | 671 ----------------------------- src/backenddb/merchantdb_plugin.c | 150 +++++++ src/backenddb/plugin_merchantdb_postgres.c | 619 ++++++++++++++++++++++++++ src/include/taler_merchantdb_lib.h | 172 +------- 6 files changed, 813 insertions(+), 848 deletions(-) delete mode 100644 src/backenddb/merchant_db.c create mode 100644 src/backenddb/merchantdb_plugin.c create mode 100644 src/backenddb/plugin_merchantdb_postgres.c diff --git a/configure.ac b/configure.ac index b66b8b00..fd7c9981 100644 --- a/configure.ac +++ b/configure.ac @@ -50,14 +50,12 @@ AS_IF([test $libgnunetutil != 1], *** ]])]) -# check for libpq (postgresql) +# test for postgres AX_LIB_POSTGRESQL([9.3]) -AS_IF([test ! "$found_postgresql" = "yes"], - [AC_MSG_ERROR([[ -*** -*** You need postgresql / libpq to build this program. -*** ]])]) - +if test "$found_postgresql" = "yes"; then + postgres=true +fi +AM_CONDITIONAL(HAVE_POSTGRESQL, test x$postgres = xtrue) # Check for Taler's libtalerpq libtalerpq=0 @@ -81,13 +79,7 @@ AC_CHECK_HEADERS([taler/taler_pq_lib.h], [], [#ifdef HAVE_GNUNET_PLATFORM_H #include #endif]) -AS_IF([test $libtalerpq != 1], - [AC_MSG_ERROR([[ -*** -*** You need libtalerpq to build this program. -*** This library is part of the Taler MINT, available at -*** https://taler.net -*** ]])]) +AM_CONDITIONAL(HAVE_TALERPQ, test x$libtalerpq = x1) # check for libmicrohttpd diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am index 8139e9c8..02656185 100644 --- a/src/backenddb/Makefile.am +++ b/src/backenddb/Makefile.am @@ -1,17 +1,38 @@ # This Makefile.am is in the public domain AM_CPPFLAGS = -I$(top_srcdir)/src/include +plugindir = $(libdir)/taler + +if HAVE_POSTGRESQL +if HAVE_TALERPQ +plugin_LTLIBRARIES = \ + libtaler_plugin_merchantdb_postgres.la +endif +endif + lib_LTLIBRARIES = \ libtalermerchantdb.la libtalermerchantdb_la_SOURCES = \ - merchant_db.c + merchantdb_plugin.c libtalermerchantdb_la_LIBADD = \ $(LIBGCRYPT_LIBS) \ -ltalerutil \ - -lgnunetutil \ + -lgnunetutil + +libtalermerchantdb_la_LDFLAGS = \ + $(POSTGRESQL_LDFLAGS) \ + -version-info 0:0:0 \ + -no-undefined + +libtaler_plugin_merchantdb_postgres_la_SOURCES = \ + plugin_merchantdb_postgres.c +libtaler_plugin_merchantdb_postgres_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_merchantdb_postgres_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ -ltalerpq \ - -lgnunetpostgres \ + -ltalerutil \ -lpq \ - -lpthread + -lgnunetutil $(XLIB) diff --git a/src/backenddb/merchant_db.c b/src/backenddb/merchant_db.c deleted file mode 100644 index d8bef636..00000000 --- a/src/backenddb/merchant_db.c +++ /dev/null @@ -1,671 +0,0 @@ -/* - This file is part of TALER - (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 merchant/merchant_db.c - * @brief database helper functions used by the merchant - * @author Sree Harsha Totakura - */ -#include "platform.h" -#include -#include -#include -#include "taler_merchantdb_lib.h" - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - - -#define PQSQL_strerror(kind, cmd, res) \ - GNUNET_log_from (kind, "merchant-db", \ - "SQL %s failed at %s:%u with error: %s", \ - cmd, __FILE__, __LINE__, PQresultErrorMessage (res)); - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -/** - * Connect to postgresql database - * - * @param cfg the configuration handle - * @return connection to the postgresql database; NULL upon error - */ -PGconn * -TALER_MERCHANTDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - return GNUNET_POSTGRES_connect (cfg, "merchant-db"); -} - - -/** - * Disconnect from the database - * - * @param conn database handle to close - */ -void -TALER_MERCHANTDB_disconnect (PGconn *conn) -{ - PQfinish (conn); -} - - -/** - * Initialize merchant tables - * - * @param conn the connection handle to postgres db. - * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their - * contents are dropped when the @a conn is closed - * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure - */ -int -TALER_MERCHANTDB_initialize (PGconn *conn, int tmp) -{ - const char *tmp_str = (1 == tmp) ? "TEMPORARY" : ""; - char *sql; - PGresult *res; - ExecStatusType status; - int ret; - - res = NULL; - (void) GNUNET_asprintf (&sql, - "BEGIN TRANSACTION;" - "CREATE %1$s TABLE IF NOT EXISTS contracts (" - "contract_id INT8 PRIMARY KEY," - "hash BYTEA NOT NULL," - "amount INT8 NOT NULL," - "amount_fraction INT4 NOT NULL," - "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL," - "description TEXT NOT NULL," - "nounce INT8 NOT NULL," - "timestamp INT8 NOT NULL," - "expiry INT8 NOT NULL," - "edate INT8 NOT NULL," - "refund_deadline INT8 NOT NULL," - "product INT8 NOT NULL);" - "CREATE %1$s TABLE IF NOT EXISTS checkouts (" - "coin_pub BYTEA PRIMARY KEY," - "contract_id INT8 REFERENCES contracts(contract_id)," - "amount INT4 NOT NULL," - "amount_fraction INT4 NOT NULL," - "coin_sig BYTEA NOT NULL);" - "CREATE %1$s TABLE IF NOT EXISTS deposits (" - "dep_perm VARCHAR NOT NULL," - "transaction_id INT8," - "pending INT4 NOT NULL," - "mint_url VARCHAR NOT NULL);", - tmp_str); - ret = GNUNET_POSTGRES_exec (conn, sql); - (void) GNUNET_POSTGRES_exec (conn, - (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;"); - GNUNET_free (sql); - if (GNUNET_OK != ret) - return ret; - - while (NULL != (res = PQgetResult (conn))) - { - status = PQresultStatus (res); - PQclear (res); - } - - EXITIF (NULL == (res = PQprepare - (conn, - "contract_create", - "INSERT INTO contracts" - "(contract_id, hash, timestamp, expiry, edate," - "refund_deadline, amount, amount_fraction, amount_currency," - "description, nounce, product) VALUES" - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", - 12, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - - /* Query aimed to get the contract's nounce and edate which will be - both used for regenerating a 'wire' JSON object to insert into the - deposit permission. Implicitly, this query will tell whether a contract - was created or not */ - - EXITIF (NULL == (res = PQprepare - (conn, - "get_contract_hash", - "SELECT " - "nounce, edate " - "FROM contracts " - "WHERE (" - "hash=$1" - ")", - 1, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "get_contract_set", - "SELECT " - "contract_id, nounce, timestamp, edate, " - "refund_deadline FROM contracts " - "WHERE (" - "hash=$1" - ")", - 1, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "store_deposit_permission", - "INSERT INTO deposits" - "(dep_perm, transaction_id, pending, mint_url) " - "VALUES ($1, $2, $3, $4);", 4, NULL))); - - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - - EXITIF (NULL == (res = PQprepare - (conn, - "update_deposit_permission", - "UPDATE deposits " - "SET pending = $1 " - "WHERE transaction_id = $2", 2, NULL))); - - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - - EXITIF (NULL == (res = PQprepare - (conn, - "get_contract_product", - "SELECT (" - "product" - ") FROM contracts " - "WHERE (" - "contract_id=$1" - ")", - 1, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "checkout_create", - "INSERT INTO checkouts (" - "coin_pub," - "contract_id," - "amount," - "amount_fraction," - "coin_sig" - ") VALUES (" - "$1, $2, $3, $4, $5" - ")", - 5, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "get_checkout_product", - "SELECT (" - "product" - ") FROM contracts " - "WHERE " - "contract_id IN (" - "SELECT (contract_id) FROM checkouts " - "WHERE coin_pub=$1" - ")", - 1, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); - PQclear (res); - - return GNUNET_OK; - - EXITIF_exit: - if (NULL != res) - { - PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res); - PQclear (res); - } - return GNUNET_SYSERR; -} - - -/** - * Update the pending column of a deposit permission - * @param conn handle to DB - * @param transaction_id identification number of the deposit to - * update - * @param pending true if still pending, false otherwise (i.e. the - * mint did respond something) - * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors - */ -uint32_t -TALER_MERCHANTDB_deposit_permission_update (PGconn *conn, - uint64_t transaction_id, - unsigned int pending) -{ - PGresult *res; - ExecStatusType status; - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_uint32 (&pending), - TALER_PQ_query_param_uint64 (&transaction_id), - TALER_PQ_query_param_end - }; - - res = TALER_PQ_exec_prepared (conn, "update_deposit_permission", params); - status = PQresultStatus (res); - - if (PGRES_COMMAND_OK != status) - { - - const char *sqlstate; - - sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); - if (NULL == sqlstate) - { - /* very unexpected... */ - GNUNET_break (0); - PQclear (res); - return GNUNET_SYSERR; - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Database commit failure: %s\n", - sqlstate); - PQclear (res); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Store a deposit permission in DB. To be mainly used if /deposit should - * be retried; also, the merchant can benefit from this information in case - * he needs to later investigate about some @a transaction_id. - * - * @param conn DB handle - * @param transaction_id identification number of this payment (which is the - * same id of the related contract) - * @param pending if true, this payment got to a persistent state - * @param which mint is to get this deposit permission - * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors - */ -uint32_t -TALER_MERCHANTDB_deposit_permission_store (PGconn *conn, - const char *deposit_permission, - uint64_t transaction_id, - unsigned int pending, - const char *mint_url) -{ - PGresult *res; - ExecStatusType status; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)), - TALER_PQ_query_param_uint64 (&transaction_id), - TALER_PQ_query_param_uint32 (&pending), - TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)), - TALER_PQ_query_param_end - }; - - res = TALER_PQ_exec_prepared (conn, "store_deposit_permission", params); - status = PQresultStatus (res); - - if (PGRES_COMMAND_OK != status) - { - const char *sqlstate; - - sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); - if (NULL == sqlstate) - { - /* very unexpected... */ - GNUNET_break (0); - PQclear (res); - return GNUNET_SYSERR; - } - /* 40P01: deadlock, 40001: serialization failure */ - if ( (0 == strcmp (sqlstate, - "23505"))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Inserting same transaction id twice\n"); - /* Primary key violation */ - PQclear (res); - return GNUNET_SYSERR; - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Database commit failure: %s\n", - sqlstate); - PQclear (res); - return GNUNET_SYSERR; - } - - PQclear (res); - return GNUNET_OK; -} - - -/** - * Insert a contract record into the database and if successfull - * return the serial number of the inserted row. - * - * @param conn the database connection - * @param timestamp the timestamp of this contract - * @param expiry the time when the contract will expire - * @param edate when the merchant wants to receive the wire transfer - * corresponding to this deal (this value is also a field inside the - * 'wire' JSON format) - * @param refund deadline until which the merchant can return the paid - * amount - * @param amount the taler amount corresponding to the contract - * @param hash of the stringified JSON corresponding to this contract - * @param c_id contract's id - * @param desc descripition of the contract - * @param nounce a random 64-bit nounce - * @param product description to identify a product - * @return #GNUNET_OK on success, #GNUNET_NO if attempting to insert an - * already inserted @a c_id, #GNUNET_SYSERR for other errors. - */ -uint32_t -TALER_MERCHANTDB_contract_create (PGconn *conn, - const struct GNUNET_TIME_Absolute timestamp, - const struct GNUNET_TIME_Absolute expiry, - struct GNUNET_TIME_Absolute edate, - struct GNUNET_TIME_Absolute refund, - const struct TALER_Amount *amount, - const struct GNUNET_HashCode *h_contract, - uint64_t c_id, - const char *desc, - uint64_t nounce, - uint64_t product) -{ - PGresult *res; -#if 0 - uint64_t expiry_ms_nbo; - uint64_t value_nbo; - uint32_t fraction_nbo; - uint64_t nounce_nbo; -#endif - ExecStatusType status; - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_uint64 (&c_id), - TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), - TALER_PQ_query_param_absolute_time (×tamp), - TALER_PQ_query_param_absolute_time (&expiry), - TALER_PQ_query_param_absolute_time (&edate), - TALER_PQ_query_param_absolute_time (&refund), - TALER_PQ_query_param_amount (amount), - /* A *string* is being put in the following statement, - though the column is declared as *blob*. Will this be - liked by the DB ? */ - TALER_PQ_query_param_fixed_size (desc, strlen (desc)), - TALER_PQ_query_param_uint64 (&nounce), - TALER_PQ_query_param_uint64 (&product), - TALER_PQ_query_param_end - }; - - /* NOTE: the statement is prepared by TALER_MERCHANTDB_initialize function */ - res = TALER_PQ_exec_prepared (conn, "contract_create", params); - status = PQresultStatus (res); - - if (PGRES_COMMAND_OK != status) - { - const char *sqlstate; - - sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); - if (NULL == sqlstate) - { - /* very unexpected... */ - GNUNET_break (0); - PQclear (res); - return GNUNET_SYSERR; - } - /* 40P01: deadlock, 40001: serialization failure */ - if ( (0 == strcmp (sqlstate, - "23505"))) - { - /* Primary key violation */ - PQclear (res); - return GNUNET_NO; - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Database commit failure: %s\n", - sqlstate); - PQclear (res); - return GNUNET_SYSERR; - } - - PQclear (res); - return GNUNET_OK; -} - - -long long -TALER_MERCHANTDB_contract_get_product (PGconn *conn, - uint64_t contract_id) -{ - PGresult *res; - uint64_t product; - ExecStatusType status; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_uint64 (&contract_id), - TALER_PQ_query_param_end - }; - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_uint64 ("product", &product), - TALER_PQ_result_spec_end - }; - - contract_id = GNUNET_htonll (contract_id); - res = TALER_PQ_exec_prepared (conn, "get_contract_product", params); - status = PQresultStatus (res); - EXITIF (PGRES_TUPLES_OK != status); - EXITIF (1 != PQntuples (res)); - EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); - PQclear (res); - return GNUNET_ntohll ((uint64_t) product); - - EXITIF_exit: - PQclear (res); - return -1; -} - - -unsigned int -TALER_MERCHANTDB_checkout_create (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, - uint64_t transaction_id, - struct TALER_Amount *amount, - struct GNUNET_CRYPTO_rsa_Signature *coin_sig) -{ - PGresult *res; - ExecStatusType status; - uint32_t value_nbo; - uint32_t fraction_nbo; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_rsa_public_key (coin_pub), - TALER_PQ_query_param_uint64 (&transaction_id), - TALER_PQ_query_param_uint32 (&value_nbo), - TALER_PQ_query_param_uint32 (&fraction_nbo), - TALER_PQ_query_param_rsa_signature (coin_sig), - TALER_PQ_query_param_end - }; - - transaction_id = GNUNET_htonll (transaction_id); - value_nbo = htonl (amount->value); - fraction_nbo = htonl (amount->fraction); - res = TALER_PQ_exec_prepared (conn, "checkout_create", params); - status = PQresultStatus (res); - EXITIF (PGRES_COMMAND_OK != status); - PQclear (res); - return GNUNET_OK; - - EXITIF_exit: - PQclear (res); - return GNUNET_SYSERR; -} - - -long long -TALER_MERCHANTDB_checkout_get_product (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub) -{ - PGresult *res; - ExecStatusType status; - uint64_t product; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_rsa_public_key (coin_pub), - TALER_PQ_query_param_end - }; - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_uint64 ("product", &product), - TALER_PQ_result_spec_end - }; - - product = -1; - res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params); - status = PQresultStatus (res); - EXITIF (PGRES_TUPLES_OK != status); - if (0 == PQntuples (res)) - { - TALER_LOG_DEBUG ("Checkout not found for given coin"); - goto EXITIF_exit; - } - EXITIF (1 != PQntuples (res)); - EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); - PQclear (res); - return GNUNET_ntohll ((uint64_t) product); - - EXITIF_exit: - PQclear (res); - return -1; -} - - -/** - * The query gets a contract's nounce and edate used to reproduce - * a 'wire' JSON object. This function is also useful to check whether - * a claimed contract existed or not. - * - * @param conn handle to the DB - * @param h_contract the parameter for the row to match against - * @param nounce where to store the found nounce - * @param edate where to store the found edate - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon errors - */ -uint32_t -TALER_MERCHANTDB_contract_get_values (PGconn *conn, - const struct GNUNET_HashCode *h_contract, - uint64_t *nounce, - struct GNUNET_TIME_Absolute *edate) -{ - PGresult *res; - ExecStatusType status; - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), - TALER_PQ_query_param_end - }; - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_uint64 ("nounce", nounce), - TALER_PQ_result_spec_absolute_time ("edate", edate), - TALER_PQ_result_spec_end - }; - - res = TALER_PQ_exec_prepared (conn, "get_contract_hash", params); - - status = PQresultStatus (res); - EXITIF (PGRES_TUPLES_OK != status); - if (0 == PQntuples (res)) - { - TALER_LOG_DEBUG ("Contract not found"); - goto EXITIF_exit; - } - - EXITIF (1 != PQntuples (res)); - EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); - PQclear (res); - return GNUNET_OK; - - EXITIF_exit: - PQclear (res); - return GNUNET_SYSERR; -} - - -/** - * Get a set of values representing a contract. This function is meant - * to obsolete the '_get_contract_values' version. - * - * @param h_contract the hashcode of this contract - * @param contract_handle where to store the results - * @raturn GNUNET_OK in case of success, GNUNET_SYSERR - * upon errors - */ -uint32_t -TALER_MERCHANTDB_contract_get_handle (PGconn *conn, - const struct GNUNET_HashCode *h_contract, - struct TALER_MERCHANTDB_ContractHandle *contract_handle) -{ - struct TALER_MERCHANTDB_ContractHandle ch; - PGresult *res; - ExecStatusType status; - - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), - TALER_PQ_query_param_end - }; - - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_uint64 ("nounce", &ch.nounce), - TALER_PQ_result_spec_absolute_time ("edate", &ch.edate), - TALER_PQ_result_spec_absolute_time ("timestamp", &ch.timestamp), - TALER_PQ_result_spec_absolute_time ("refund_deadline", &ch.refund_deadline), - TALER_PQ_result_spec_uint64 ("contract_id", &ch.contract_id), - TALER_PQ_result_spec_end - }; - - res = TALER_PQ_exec_prepared (conn, "get_contract_set", params); - status = PQresultStatus (res); - EXITIF (PGRES_TUPLES_OK != status); - if (0 == PQntuples (res)) - { - TALER_LOG_DEBUG ("Contract not found"); - goto EXITIF_exit; - } - - EXITIF (1 != PQntuples (res)); - EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); - *contract_handle = ch; - PQclear (res); - return GNUNET_OK; - - EXITIF_exit: - PQclear (res); - return GNUNET_SYSERR; -} diff --git a/src/backenddb/merchantdb_plugin.c b/src/backenddb/merchantdb_plugin.c new file mode 100644 index 00000000..62a1a193 --- /dev/null +++ b/src/backenddb/merchantdb_plugin.c @@ -0,0 +1,150 @@ +/* + 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 merchantdb/merchantdb_plugin.c + * @brief Logic to load database plugin + * @author Christian Grothoff + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include +#include "taler_merchantdb_plugin.h" +#include + + +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +struct TALER_MERCHANTDB_Plugin * +TALER_MERCHANTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *plugin_name; + char *lib_name; + struct GNUNET_CONFIGURATION_Handle *cfg_dup; + struct TALER_MERCHANTDB_Plugin *plugin; + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, + "merchant", + "db", + &plugin_name)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "merchant", + "db"); + return NULL; + } + (void) GNUNET_asprintf (&lib_name, + "libtaler_plugin_merchantdb_%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_MERCHANTDB_plugin_unload (struct TALER_MERCHANTDB_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); +} + + +/** + * Libtool search path before we started. + */ +static char *old_dlsearchpath; + + +/** + * 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 merchantdb_plugin.c */ diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c new file mode 100644 index 00000000..02e6884b --- /dev/null +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -0,0 +1,619 @@ +/* + This file is part of TALER + (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 merchant/merchant_db.c + * @brief database helper functions used by the merchant + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include +#include +#include +#include +#include "taler_merchantdb_plugin.h" + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + + +#define PQSQL_strerror(kind, cmd, res) \ + GNUNET_log_from (kind, "merchant-db", \ + "SQL %s failed at %s:%u with error: %s", \ + cmd, __FILE__, __LINE__, PQresultErrorMessage (res)); + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + + +/** + * Connect to postgresql database + * + * @param cfg the configuration handle + * @return connection to the postgresql database; NULL upon error + */ +PGconn * +TALER_MERCHANTDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + return GNUNET_POSTGRES_connect (cfg, "merchant-db"); +} + + +/** + * Disconnect from the database + * + * @param conn database handle to close + */ +void +TALER_MERCHANTDB_disconnect (PGconn *conn) +{ + PQfinish (conn); +} + + +/** + * Initialize merchant tables + * + * @param conn the connection handle to postgres db. + * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their + * contents are dropped when the @a conn is closed + * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure + */ +int +TALER_MERCHANTDB_initialize (PGconn *conn, int tmp) +{ + const char *tmp_str = (1 == tmp) ? "TEMPORARY" : ""; + char *sql; + PGresult *res; + ExecStatusType status; + int ret; + + res = NULL; + (void) GNUNET_asprintf (&sql, + "BEGIN TRANSACTION;" + "CREATE %1$s TABLE IF NOT EXISTS contracts (" + "contract_id INT8 PRIMARY KEY," + "hash BYTEA NOT NULL," + "amount INT8 NOT NULL," + "amount_fraction INT4 NOT NULL," + "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL," + "description TEXT NOT NULL," + "nounce INT8 NOT NULL," + "timestamp INT8 NOT NULL," + "expiry INT8 NOT NULL," + "edate INT8 NOT NULL," + "refund_deadline INT8 NOT NULL," + "product INT8 NOT NULL);" + "CREATE %1$s TABLE IF NOT EXISTS checkouts (" + "coin_pub BYTEA PRIMARY KEY," + "contract_id INT8 REFERENCES contracts(contract_id)," + "amount INT4 NOT NULL," + "amount_fraction INT4 NOT NULL," + "coin_sig BYTEA NOT NULL);" + "CREATE %1$s TABLE IF NOT EXISTS deposits (" + "dep_perm VARCHAR NOT NULL," + "transaction_id INT8," + "pending INT4 NOT NULL," + "mint_url VARCHAR NOT NULL);", + tmp_str); + ret = GNUNET_POSTGRES_exec (conn, sql); + (void) GNUNET_POSTGRES_exec (conn, + (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;"); + GNUNET_free (sql); + if (GNUNET_OK != ret) + return ret; + + while (NULL != (res = PQgetResult (conn))) + { + status = PQresultStatus (res); + PQclear (res); + } + + EXITIF (NULL == (res = PQprepare + (conn, + "contract_create", + "INSERT INTO contracts" + "(contract_id, hash, timestamp, expiry, edate," + "refund_deadline, amount, amount_fraction, amount_currency," + "description, nounce, product) VALUES" + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + 12, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + + /* Query aimed to get the contract's nounce and edate which will be + both used for regenerating a 'wire' JSON object to insert into the + deposit permission. Implicitly, this query will tell whether a contract + was created or not */ + + EXITIF (NULL == (res = PQprepare + (conn, + "get_contract_hash", + "SELECT " + "nounce, edate " + "FROM contracts " + "WHERE (" + "hash=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "get_contract_set", + "SELECT " + "contract_id, nounce, timestamp, edate, " + "refund_deadline FROM contracts " + "WHERE (" + "hash=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "store_deposit_permission", + "INSERT INTO deposits" + "(dep_perm, transaction_id, pending, mint_url) " + "VALUES ($1, $2, $3, $4);", 4, NULL))); + + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + + EXITIF (NULL == (res = PQprepare + (conn, + "update_deposit_permission", + "UPDATE deposits " + "SET pending = $1 " + "WHERE transaction_id = $2", 2, NULL))); + + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + + EXITIF (NULL == (res = PQprepare + (conn, + "get_contract_product", + "SELECT (" + "product" + ") FROM contracts " + "WHERE (" + "contract_id=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "checkout_create", + "INSERT INTO checkouts (" + "coin_pub," + "contract_id," + "amount," + "amount_fraction," + "coin_sig" + ") VALUES (" + "$1, $2, $3, $4, $5" + ")", + 5, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare + (conn, + "get_checkout_product", + "SELECT (" + "product" + ") FROM contracts " + "WHERE " + "contract_id IN (" + "SELECT (contract_id) FROM checkouts " + "WHERE coin_pub=$1" + ")", + 1, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); + PQclear (res); + + return GNUNET_OK; + + EXITIF_exit: + if (NULL != res) + { + PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res); + PQclear (res); + } + return GNUNET_SYSERR; +} + + +/** + * Update the pending column of a deposit permission + * @param conn handle to DB + * @param transaction_id identification number of the deposit to + * update + * @param pending true if still pending, false otherwise (i.e. the + * mint did respond something) + * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors + */ +uint32_t +TALER_MERCHANTDB_deposit_permission_update (PGconn *conn, + uint64_t transaction_id, + unsigned int pending) +{ + PGresult *res; + ExecStatusType status; + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint32 (&pending), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_end + }; + + res = TALER_PQ_exec_prepared (conn, "update_deposit_permission", params); + status = PQresultStatus (res); + + if (PGRES_COMMAND_OK != status) + { + + const char *sqlstate; + + sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); + if (NULL == sqlstate) + { + /* very unexpected... */ + GNUNET_break (0); + PQclear (res); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database commit failure: %s\n", + sqlstate); + PQclear (res); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Store a deposit permission in DB. To be mainly used if /deposit should + * be retried; also, the merchant can benefit from this information in case + * he needs to later investigate about some @a transaction_id. + * + * @param conn DB handle + * @param transaction_id identification number of this payment (which is the + * same id of the related contract) + * @param pending if true, this payment got to a persistent state + * @param which mint is to get this deposit permission + * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors + */ +uint32_t +TALER_MERCHANTDB_deposit_permission_store (PGconn *conn, + const char *deposit_permission, + uint64_t transaction_id, + unsigned int pending, + const char *mint_url) +{ + PGresult *res; + ExecStatusType status; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_uint32 (&pending), + TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)), + TALER_PQ_query_param_end + }; + + res = TALER_PQ_exec_prepared (conn, "store_deposit_permission", params); + status = PQresultStatus (res); + + if (PGRES_COMMAND_OK != status) + { + const char *sqlstate; + + sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); + if (NULL == sqlstate) + { + /* very unexpected... */ + GNUNET_break (0); + PQclear (res); + return GNUNET_SYSERR; + } + /* 40P01: deadlock, 40001: serialization failure */ + if ( (0 == strcmp (sqlstate, + "23505"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Inserting same transaction id twice\n"); + /* Primary key violation */ + PQclear (res); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database commit failure: %s\n", + sqlstate); + PQclear (res); + return GNUNET_SYSERR; + } + + PQclear (res); + return GNUNET_OK; +} + + +/** + * Insert a contract record into the database and if successfull + * return the serial number of the inserted row. + * + * @param conn the database connection + * @param timestamp the timestamp of this contract + * @param expiry the time when the contract will expire + * @param edate when the merchant wants to receive the wire transfer + * corresponding to this deal (this value is also a field inside the + * 'wire' JSON format) + * @param refund deadline until which the merchant can return the paid + * amount + * @param amount the taler amount corresponding to the contract + * @param hash of the stringified JSON corresponding to this contract + * @param c_id contract's id + * @param desc descripition of the contract + * @param nounce a random 64-bit nounce + * @param product description to identify a product + * @return #GNUNET_OK on success, #GNUNET_NO if attempting to insert an + * already inserted @a c_id, #GNUNET_SYSERR for other errors. + */ +uint32_t +TALER_MERCHANTDB_contract_create (PGconn *conn, + const struct GNUNET_TIME_Absolute timestamp, + const struct GNUNET_TIME_Absolute expiry, + struct GNUNET_TIME_Absolute edate, + struct GNUNET_TIME_Absolute refund, + const struct TALER_Amount *amount, + const struct GNUNET_HashCode *h_contract, + uint64_t c_id, + const char *desc, + uint64_t nounce, + uint64_t product) +{ + PGresult *res; +#if 0 + uint64_t expiry_ms_nbo; + uint64_t value_nbo; + uint32_t fraction_nbo; + uint64_t nounce_nbo; +#endif + ExecStatusType status; + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&c_id), + TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), + TALER_PQ_query_param_absolute_time (×tamp), + TALER_PQ_query_param_absolute_time (&expiry), + TALER_PQ_query_param_absolute_time (&edate), + TALER_PQ_query_param_absolute_time (&refund), + TALER_PQ_query_param_amount (amount), + /* A *string* is being put in the following statement, + though the column is declared as *blob*. Will this be + liked by the DB ? */ + TALER_PQ_query_param_fixed_size (desc, strlen (desc)), + TALER_PQ_query_param_uint64 (&nounce), + TALER_PQ_query_param_uint64 (&product), + TALER_PQ_query_param_end + }; + + /* NOTE: the statement is prepared by TALER_MERCHANTDB_initialize function */ + res = TALER_PQ_exec_prepared (conn, "contract_create", params); + status = PQresultStatus (res); + + if (PGRES_COMMAND_OK != status) + { + const char *sqlstate; + + sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); + if (NULL == sqlstate) + { + /* very unexpected... */ + GNUNET_break (0); + PQclear (res); + return GNUNET_SYSERR; + } + /* 40P01: deadlock, 40001: serialization failure */ + if ( (0 == strcmp (sqlstate, + "23505"))) + { + /* Primary key violation */ + PQclear (res); + return GNUNET_NO; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database commit failure: %s\n", + sqlstate); + PQclear (res); + return GNUNET_SYSERR; + } + + PQclear (res); + return GNUNET_OK; +} + + +long long +TALER_MERCHANTDB_contract_get_product (PGconn *conn, + uint64_t contract_id) +{ + PGresult *res; + uint64_t product; + ExecStatusType status; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&contract_id), + TALER_PQ_query_param_end + }; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("product", &product), + TALER_PQ_result_spec_end + }; + + contract_id = GNUNET_htonll (contract_id); + res = TALER_PQ_exec_prepared (conn, "get_contract_product", params); + status = PQresultStatus (res); + EXITIF (PGRES_TUPLES_OK != status); + EXITIF (1 != PQntuples (res)); + EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); + PQclear (res); + return GNUNET_ntohll ((uint64_t) product); + + EXITIF_exit: + PQclear (res); + return -1; +} + + +unsigned int +TALER_MERCHANTDB_checkout_create (PGconn *conn, + struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, + uint64_t transaction_id, + struct TALER_Amount *amount, + struct GNUNET_CRYPTO_rsa_Signature *coin_sig) +{ + PGresult *res; + ExecStatusType status; + uint32_t value_nbo; + uint32_t fraction_nbo; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_rsa_public_key (coin_pub), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_uint32 (&value_nbo), + TALER_PQ_query_param_uint32 (&fraction_nbo), + TALER_PQ_query_param_rsa_signature (coin_sig), + TALER_PQ_query_param_end + }; + + transaction_id = GNUNET_htonll (transaction_id); + value_nbo = htonl (amount->value); + fraction_nbo = htonl (amount->fraction); + res = TALER_PQ_exec_prepared (conn, "checkout_create", params); + status = PQresultStatus (res); + EXITIF (PGRES_COMMAND_OK != status); + PQclear (res); + return GNUNET_OK; + + EXITIF_exit: + PQclear (res); + return GNUNET_SYSERR; +} + + +long long +TALER_MERCHANTDB_checkout_get_product (PGconn *conn, + struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub) +{ + PGresult *res; + ExecStatusType status; + uint64_t product; + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_rsa_public_key (coin_pub), + TALER_PQ_query_param_end + }; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("product", &product), + TALER_PQ_result_spec_end + }; + + product = -1; + res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params); + status = PQresultStatus (res); + EXITIF (PGRES_TUPLES_OK != status); + if (0 == PQntuples (res)) + { + TALER_LOG_DEBUG ("Checkout not found for given coin"); + goto EXITIF_exit; + } + EXITIF (1 != PQntuples (res)); + EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); + PQclear (res); + return GNUNET_ntohll ((uint64_t) product); + + EXITIF_exit: + PQclear (res); + return -1; +} + + +/** + * The query gets a contract's nounce and edate used to reproduce + * a 'wire' JSON object. This function is also useful to check whether + * a claimed contract existed or not. + * + * @param conn handle to the DB + * @param h_contract the parameter for the row to match against + * @param nounce where to store the found nounce + * @param edate where to store the found edate + * @return #GNUNET_OK on success, #GNUNET_SYSERR upon errors + */ +uint32_t +TALER_MERCHANTDB_contract_get_values (PGconn *conn, + const struct GNUNET_HashCode *h_contract, + uint64_t *nounce, + struct GNUNET_TIME_Absolute *edate) +{ + PGresult *res; + ExecStatusType status; + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_fixed_size (h_contract, sizeof (struct GNUNET_HashCode)), + TALER_PQ_query_param_end + }; + struct TALER_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_uint64 ("nounce", nounce), + TALER_PQ_result_spec_absolute_time ("edate", edate), + TALER_PQ_result_spec_end + }; + + res = TALER_PQ_exec_prepared (conn, "get_contract_hash", params); + + status = PQresultStatus (res); + EXITIF (PGRES_TUPLES_OK != status); + if (0 == PQntuples (res)) + { + TALER_LOG_DEBUG ("Contract not found"); + goto EXITIF_exit; + } + + EXITIF (1 != PQntuples (res)); + EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); + PQclear (res); + return GNUNET_OK; + + EXITIF_exit: + PQclear (res); + return GNUNET_SYSERR; +} diff --git a/src/include/taler_merchantdb_lib.h b/src/include/taler_merchantdb_lib.h index fe5adffc..f64a9bfa 100644 --- a/src/include/taler_merchantdb_lib.h +++ b/src/include/taler_merchantdb_lib.h @@ -19,182 +19,36 @@ * @brief database helper functions used by the merchant backend * @author Sree Harsha Totakura */ +#ifndef TALER_MERCHANTDB_LIB_H +#define TALER_MERCHANTDB_LIB_H -#ifndef MERCHANT_DB_H -#define MERCHANT_DB_H - -#include #include +#include "taler_merchantdb_plugin.h" - - -/* Set of values that represent a contract. To be expanded on an - as-needed basis */ -struct TALER_MERCHANTDB_ContractHandle -{ - /* The nounce used when hashing the wire details - for this contract */ - uint64_t nounce; - - /* The maximum time when the merchant expects the money tranfer - to his bank account to happen */ - struct GNUNET_TIME_Absolute edate; - - /* The time when this contract was generated */ - struct GNUNET_TIME_Absolute timestamp; - - /* The maximum time until which the merchant could issue a - refund to the customer */ - struct GNUNET_TIME_Absolute refund_deadline; - - /* The identification number for this contract */ - uint64_t contract_id; - -}; +/** + * Handle to interact with the database. + */ +struct TALER_MERCHANTDB_Plugin; /** * Connect to postgresql database * * @param cfg the configuration handle - * @return connection to the postgresql database; NULL upon error + * @return connection to the database; NULL upon error */ -PGconn * -TALER_MERCHANTDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg); +struct TALER_MERCHANTDB_Plugin * +TALER_MERCHANTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg); /** * Disconnect from the database * - * @param conn database handle to close + * @param dbh database handle to close */ void -TALER_MERCHANTDB_disconnect (PGconn *conn); - - -/** - * Initialize merchant tables - * - * @param conn the connection handle to postgres db. - * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their - * contents are dropped when the @a conn is closed - * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure - */ -int -TALER_MERCHANTDB_initialize (PGconn *conn, int tmp); - - -/** - * Inserts a contract record into the database and if successfull returns the - * serial number of the inserted row. - * - * @param conn the database connection - * @param timestamp the timestamp of this contract - * @param expiry the time when the contract will expire - * @param edate when the merchant wants to receive the wire transfer corresponding - * to this deal (this value is also a field inside the 'wire' JSON format) - * @param refund deadline until which the merchant can return the paid amount - * @param amount the taler amount corresponding to the contract - * @param hash of the stringified JSON corresponding to this contract - * @param c_id contract's id - * @param desc descripition of the contract - * @param nounce a random 64-bit nounce - * @param product description to identify a product - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error - */ -uint32_t -TALER_MERCHANTDB_contract_create (PGconn *conn, - const struct GNUNET_TIME_Absolute timestamp, - const struct GNUNET_TIME_Absolute expiry, - struct GNUNET_TIME_Absolute edate, - struct GNUNET_TIME_Absolute refund, - const struct TALER_Amount *amount, - const struct GNUNET_HashCode *h_contract, - uint64_t c_id, - const char *desc, - uint64_t nounce, - uint64_t product); +TALER_MERCHANTDB_plugin_unload (struct TALER_MERCHANTDB_Plugin *dbh); -long long -TALER_MERCHANTDB_contract_get_product (PGconn *conn, - uint64_t contract_id); - - -/** - * Update the pending column of a deposit permission - * - * @param conn handle to DB - * @param transaction_id identification number of the deposit to - * update - * @param pending true if still pending, false otherwise (i.e. the - * mint did respond something) - * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors - */ -uint32_t -TALER_MERCHANTDB_deposit_permission_update (PGconn *conn, - uint64_t transaction_id, - unsigned int pending); - -unsigned int -TALER_MERCHANTDB_checkout_create (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, - uint64_t transaction_id, - struct TALER_Amount *amount, - struct GNUNET_CRYPTO_rsa_Signature *coin_sig); - - -long long -TALER_MERCHANTDB_checkout_get_product (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub); - -/** - * The query gets a contract's nounce and edate used to reproduce - * a 'wire' JSON object. This function is also useful to check whether - * a claimed contract existed or not. - * - * @param conn handle to the DB - * @param h_contract the parameter for the row to match against - * @param nounce where to store the found nounce - * @param edate where to store the found expiration date - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon errors - */ -uint32_t -TALER_MERCHANTDB_contract_get_values (PGconn *conn, - const struct GNUNET_HashCode *h_contract, - uint64_t *nounce, - struct GNUNET_TIME_Absolute *edate); - #endif /* MERCHANT_DB_H */ -/** - * Get a set of values representing a contract. This function is meant - * to obsolete the '_get_contract_values' version. - * - * @param h_contract the hashcode of this contract - * @param contract_handle where to store the results - * @raturn #GNUNET_OK in case of success, #GNUNET_SYSERR - * upon errors - */ -uint32_t -TALER_MERCHANTDB_contract_get_handle (PGconn *conn, - const struct GNUNET_HashCode *h_contract, - struct TALER_MERCHANTDB_ContractHandle *contract_handle); - -/** - * Store a deposit permission in DB. To be mainly used if /deposit should - * be retried; also, the merchant can benefit from this information in case - * he needs to later investigate about some transaction_id. - * @param conn DB handle - * @param transaction_id identification number of this payment (which is the - * same id of the related contract) - * @param pending if true, this payment got to a persistent state - * @param which mint is to get this deposit permission - * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors - */ -uint32_t -TALER_MERCHANTDB_deposit_permission_store (PGconn *conn, - const char *deposit_permission, - uint64_t transaction_id, - unsigned int pending, - const char *mint_url); -/* end of merchant-db.h */ +/* end of taler_merchantdb_lib.h */ -- cgit v1.2.3