diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c index 786d04ac7..91d6705c3 100644 --- a/src/mint/mint_db.c +++ b/src/mint/mint_db.c @@ -13,11 +13,13 @@ You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, If not, see */ + /** * @file mint_db.c * @brief Low-level (statement-level) database access for the mint * @author Florian Dold * @author Christian Grothoff + * @author Sree Harsha Totakura * * TODO: * - The mint_db.h-API should ideally be what we need to port @@ -32,6 +34,7 @@ #include "mint_db.h" #include + /** * Thread-local database connection. * Contains a pointer to PGconn or NULL. @@ -45,7 +48,6 @@ static pthread_key_t db_conn_threadlocal; */ 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)); \ @@ -60,6 +62,213 @@ static char *TALER_MINT_db_connection_cfg_str; } while (0) +#define SQLEXEC_(conn, sql, result) \ + do { \ + result = PQexec (conn, sql); \ + if (PGRES_COMMAND_OK != PQresultStatus (result)) \ + { \ + break_db_err (result); \ + PQclear (result); result = NULL; \ + goto SQLEXEC_fail; \ + } \ + PQclear (result); result = NULL; \ + } while (0) + + +/** + * Set the given connection to use a temporary schema + * + * @param db the database connection + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon error + */ +static int +set_temporary_schema (PGconn *db) +{ + PGresult *result; + + SQLEXEC_(db, + "CREATE SCHEMA IF NOT EXISTS " TALER_TEMP_SCHEMA_NAME ";" + "SET search_path to " TALER_TEMP_SCHEMA_NAME ";", + result); + return GNUNET_OK; + SQLEXEC_fail: + return GNUNET_SYSERR; +} + + +/** + * Drop the temporary taler schema. This is only useful for testcases + * + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +int +TALER_MINT_DB_drop_temporary (PGconn *db) +{ + PGresult *result; + + SQLEXEC_ (db, + "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;", + result); + return GNUNET_OK; + SQLEXEC_fail: + return GNUNET_SYSERR; +} + + +/** + * Create the necessary tables if they are not present + * + * @param temporary should we use a temporary schema + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +int +TALER_MINT_DB_create_tables (int temporary) +{ + PGresult *result; + PGconn *conn; + + result = NULL; + conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); + if (CONNECTION_OK != PQstatus (conn)) + { + LOG_ERROR ("Database connection failed: %s\n", + PQerrorMessage (conn)); + GNUNET_break (0); + return GNUNET_SYSERR; + } + if ((GNUNET_YES == temporary) + && (GNUNET_SYSERR == set_temporary_schema (conn))) + { + PQfinish (conn); + return GNUNET_SYSERR; + } +#define SQLEXEC(sql) SQLEXEC_(conn, sql, result); + /* reserves table is for summarization of a reserve. It is updated when new + funds are added and existing funds are withdrawn */ + SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves" + "(" + " reserve_pub BYTEA PRIMARY KEY" + ",current_balance_value INT4 NOT NULL" + ",current_balance_fraction INT4 NOT NULL" + ",balance_currency VARCHAR(4) NOT NULL" + ",expiration_date INT8 NOT NULL" + ")"); + /* reserves_in table collects the transactions which transfer funds into the + reserve. The amount and expiration date for the corresponding reserve are + updated when new transfer funds are added. The rows of this table + correspond to each incoming transaction. */ + SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in" + "(" + " reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE" + ",balance_value INT4 NOT NULL" + ",balance_fraction INT4 NOT NULL" + ",expiration_date INT8 NOT NULL" + ");"); + result = PQexec (conn, + "CREATE INDEX reserves_in_index ON reserves_in (reserve_pub);"); + if (PGRES_COMMAND_OK != PQresultStatus (result)) + { + ExecStatusType status = PQresultStatus (result); + PQclear (result); + result = NULL; + goto SQLEXEC_fail; + } + PQclear (result); + SQLEXEC ("CREATE TABLE IF NOT EXISTS collectable_blindcoins" + "(" + "blind_ev BYTEA PRIMARY KEY" + ",blind_ev_sig BYTEA NOT NULL" + ",denom_pub BYTEA NOT NULL" + ",reserve_sig BYTEA NOT NULL" + ",reserve_pub BYTEA REFERENCES reserves (reserve_pub) ON DELETE CASCADE" + ");" + "CREATE INDEX collectable_blindcoins_index ON" + " collectable_blindcoins(reserve_pub)"); + SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins " + "(" + " coin_pub BYTEA NOT NULL PRIMARY KEY" + ",denom_pub BYTEA NOT NULL" + ",denom_sig BYTEA NOT NULL" + ",expended_value INT4 NOT NULL" + ",expended_fraction INT4 NOT NULL" + ",expended_currency VARCHAR(4) NOT NULL" + ",refresh_session_pub BYTEA" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions " + "(" + " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)" + ",session_melt_sig BYTEA" + ",session_commit_sig BYTEA" + ",noreveal_index INT2 NOT NULL" + // non-zero if all reveals were ok + // and the new coin signatures are ready + ",reveal_ok BOOLEAN NOT NULL DEFAULT false" + ") "); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order " + "( " + " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)" + ",newcoin_index INT2 NOT NULL " + ",denom_pub BYTEA NOT NULL " + ",PRIMARY KEY (session_pub, newcoin_index)" + ") "); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_link" + "(" + " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)" + ",transfer_pub BYTEA NOT NULL" + ",link_secret_enc BYTEA NOT NULL" + // index of the old coin in the customer's request + ",oldcoin_index INT2 NOT NULL" + // index for cut and choose, + // ranges from 0 to kappa-1 + ",cnc_index INT2 NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin" + "(" + " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " + ",link_vector_enc BYTEA NOT NULL" + // index of the new coin in the customer's request + ",newcoin_index INT2 NOT NULL" + // index for cut and choose, + ",cnc_index INT2 NOT NULL" + ",coin_ev BYTEA NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melt" + "(" + " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " + ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) " + ",denom_pub BYTEA NOT NULL " + ",oldcoin_index INT2 NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_collectable" + "(" + " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " + ",ev_sig BYTEA NOT NULL" + ",newcoin_index INT2 NOT NULL" + ")"); + SQLEXEC("CREATE TABLE IF NOT EXISTS deposits " + "( " + " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)" + ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)" + ",transaction_id INT8 NOT NULL" + ",amount_currency VARCHAR(4) NOT NULL" + ",amount_value INT4 NOT NULL" + ",amount_fraction INT4 NOT NULL" + ",merchant_pub BYTEA NOT NULL" + ",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)" + ",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)" + ",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)" + ",wire TEXT NOT NULL" + ")"); +#undef SQLEXEC + PQfinish (conn); + return GNUNET_OK; + + SQLEXEC_fail: + PQfinish (conn); + return GNUNET_SYSERR; +} + + /** * Setup prepared statements. * diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index a32cbd773..b36823803 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -27,6 +27,7 @@ #include #include "taler_util.h" +#define TALER_TEMP_SCHEMA_NAME "taler_temporary" /** * Initialize database subsystem. @@ -42,12 +43,32 @@ TALER_MINT_DB_init (const char *connection_cfg); * Get the thread-local database-handle. * Connect to the db if the connection does not exist yet. * + * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the + * database default one * @param the database connection, or NULL on error */ PGconn * -TALER_MINT_DB_get_connection (void); +TALER_MINT_DB_get_connection (int temporary); +/** + * Drop the temporary taler schema. This is only useful for testcases + * + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +int +TALER_MINT_DB_drop_temporary (PGconn *db); + + +/** + * Create the necessary tables if they are not present + * + * @param temporary should we use a temporary schema + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +int +TALER_MINT_DB_create_tables (int temporary); + /** * Setup prepared statements. FIXME: should this be part of the API, * or just internal to "TALER_MINT_DB_get_connection()"? diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c index 7611802ea..c5b0ae4c3 100644 --- a/src/mint/taler-mint-dbinit.c +++ b/src/mint/taler-mint-dbinit.c @@ -36,192 +36,7 @@ static PGconn *db_conn; static char *TALER_MINT_db_connection_cfg_str; -static int -TALER_MINT_init_withdraw_tables (PGconn *conn) -{ - PGresult *result; - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS reserves" - "(" - " reserve_pub BYTEA PRIMARY KEY" - ",balance_value INT4 NOT NULL" - ",balance_fraction INT4 NOT NULL" - ",balance_currency VARCHAR(4) NOT NULL" - ",status_sig BYTEA" - ",status_sign_pub BYTEA" - ",expiration_date INT8 NOT NULL" - ")"); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS collectable_blindcoins" - "(" - "blind_ev BYTEA PRIMARY KEY" - ",blind_ev_sig BYTEA NOT NULL" - ",denom_pub BYTEA NOT NULL" - ",reserve_sig BYTEA NOT NULL" - ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub)" - ")"); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS known_coins " - "(" - " coin_pub BYTEA NOT NULL PRIMARY KEY" - ",denom_pub BYTEA NOT NULL" - ",denom_sig BYTEA NOT NULL" - ",expended_value INT4 NOT NULL" - ",expended_fraction INT4 NOT NULL" - ",expended_currency VARCHAR(4) NOT NULL" - ",refresh_session_pub BYTEA" - ")"); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS refresh_sessions " - "(" - " session_pub BYTEA PRIMARY KEY CHECK (length(session_pub) = 32)" - ",session_melt_sig BYTEA" - ",session_commit_sig BYTEA" - ",noreveal_index INT2 NOT NULL" - // non-zero if all reveals were ok - // and the new coin signatures are ready - ",reveal_ok BOOLEAN NOT NULL DEFAULT false" - ") "); - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS refresh_order " - "( " - " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)" - ",newcoin_index INT2 NOT NULL " - ",denom_pub BYTEA NOT NULL " - ",PRIMARY KEY (session_pub, newcoin_index)" - ") "); - - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS refresh_commit_link" - "(" - " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub)" - ",transfer_pub BYTEA NOT NULL" - ",link_secret_enc BYTEA NOT NULL" - // index of the old coin in the customer's request - ",oldcoin_index INT2 NOT NULL" - // index for cut and choose, - // ranges from 0 to kappa-1 - ",cnc_index INT2 NOT NULL" - ")"); - - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS refresh_commit_coin" - "(" - " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " - ",link_vector_enc BYTEA NOT NULL" - // index of the new coin in the customer's request - ",newcoin_index INT2 NOT NULL" - // index for cut and choose, - ",cnc_index INT2 NOT NULL" - ",coin_ev BYTEA NOT NULL" - ")"); - - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS refresh_melt" - "(" - " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " - ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) " - ",denom_pub BYTEA NOT NULL " - ",oldcoin_index INT2 NOT NULL" - ")"); - - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS refresh_collectable" - "(" - " session_pub BYTEA NOT NULL REFERENCES refresh_sessions (session_pub) " - ",ev_sig BYTEA NOT NULL" - ",newcoin_index INT2 NOT NULL" - ")"); - - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - result = PQexec (conn, - "CREATE TABLE IF NOT EXISTS deposits " - "( " - " coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)" - ",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)" - ",transaction_id INT8 NOT NULL" - ",amount_currency VARCHAR(4) NOT NULL" - ",amount_value INT4 NOT NULL" - ",amount_fraction INT4 NOT NULL" - ",merchant_pub BYTEA NOT NULL" - ",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" - ")"); - - if (PGRES_COMMAND_OK != PQresultStatus(result)) - { - break_db_err (result); - return GNUNET_SYSERR; - } - PQclear (result); - - return GNUNET_OK; -} /** @@ -271,7 +86,7 @@ main (int argc, char *const *argv) return 1; } - if (GNUNET_OK != TALER_MINT_init_withdraw_tables (db_conn)) + if (GNUNET_OK != TALER_MINT_DB_create_tables (db_conn)) { fprintf (stderr, "Failed to initialize database.\n"); return 1; diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 19fcb1c12..d5613c0dc 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -85,7 +85,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, struct TALER_MINT_DenomKeyIssuePriv *dki; int ret; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); @@ -203,7 +203,7 @@ TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, struct ReserveHistory *rh; int res; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); @@ -264,7 +264,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, blinded_msg_len, &h_blind); - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); @@ -521,7 +521,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, int res; unsigned int i; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); @@ -650,7 +650,7 @@ TALER_MINT_db_execute_refresh_commit (struct MHD_Connection *connection, unsigned int j; int res; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); @@ -1034,7 +1034,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, unsigned int j; unsigned int off; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); @@ -1202,7 +1202,7 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection, struct TALER_EncryptedLinkSecret shared_secret_enc; struct LinkDataList *ldl; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection);